v3.0.0

<bmx-edi-editor>

An EDI workbench for UN/EDIFACT, ANSI X12 and TRADACOMS: an interchange shown as a tree of interchanges, groups, messages, segment groups and segments, with the chosen segment's elements laid out by name, code meaning and format to read and change; as text coloured by its own delimiters; and as JSON or XML to take into other systems.

21 properties · 5 events · 18 methods · 10 parts

Example

Try it with your own files in confidence: they are opened by your browser, on your device. Nothing is uploaded, no copy reaches us or anyone else, and nothing is kept once you close this tab.

Checking...

Show markup
<bmx-edi-editor id="ex-edi" label="Inbound orders" description="A made-up supplier's order interchange, checked against the made-up buyer's own order guide (written for this example). One line's quantity is not a number and a party role is not one the guide allows: choose them in the tree, or press Acknowledge to see the CONTRL the supplier would get back."></bmx-edi-editor>
<p class="note" id="ex-edi-out" role="status" style="margin: 12px 0 0">Checking...</p>

<script type="module">
  await customElements.whenDefined('bmx-edi-editor');
  const editor = document.getElementById('ex-edi');
  const out = document.getElementById('ex-edi-out');

  // A made-up buyer's order guide: the segments it uses, in its own words. Your partners' guides
  // (or a UN/EDIFACT directory you hold) are given to the editor in this same JSON shape.
  const guide = {
    standard: 'edifact',
    version: 'D:96A:UN',
    name: "Harbour Lane Studio order guide 1.2",
    messages: {
      ORDERS: {
        type: 'ORDERS',
        name: 'Purchase order',
        structure: [
          { segment: 'UNH', status: 'M' },
          { segment: 'BGM', status: 'M' },
          { segment: 'DTM', status: 'M', max: 5 },
          { segment: 'FTX', status: 'C', max: 3 },
          { group: 'SG2', status: 'M', max: 5, name: 'Parties', children: [{ segment: 'NAD', status: 'M' }] },
          { group: 'SG25', status: 'M', max: 999, name: 'Order lines', children: [{ segment: 'LIN', status: 'M' }, { segment: 'QTY', status: 'M' }, { segment: 'PRI', status: 'C' }] },
          { segment: 'UNS', status: 'M' },
          { segment: 'CNT', status: 'C' },
          { segment: 'UNT', status: 'M' },
        ],
      },
    },
    segments: {
      BGM: { tag: 'BGM', name: 'Order start', elements: [{ id: 'C002', status: 'M' }, { id: '1004', status: 'M' }, { id: '1225', status: 'C' }] },
      DTM: { tag: 'DTM', name: 'Date', elements: [{ id: 'C507', status: 'M' }] },
      FTX: { tag: 'FTX', name: 'Note', elements: [{ id: '4451', status: 'M' }, { id: '4453', status: 'C' }, { id: 'C107', status: 'C' }, { id: 'C108', status: 'C' }] },
      NAD: { tag: 'NAD', name: 'Party', elements: [{ id: '3035', status: 'M' }, { id: 'C082', status: 'M' }] },
      LIN: { tag: 'LIN', name: 'Order line', elements: [{ id: '1082', status: 'M' }, { id: '1229', status: 'C' }, { id: 'C212', status: 'M' }] },
      QTY: { tag: 'QTY', name: 'Quantity', elements: [{ id: 'C186', status: 'M' }] },
      PRI: { tag: 'PRI', name: 'Price', elements: [{ id: 'C509', status: 'M' }] },
      UNS: { tag: 'UNS', name: 'End of lines', elements: [{ id: '0081', status: 'M' }] },
      CNT: { tag: 'CNT', name: 'Check total', elements: [{ id: 'C270', status: 'M' }] },
    },
    composites: {
      C002: { id: 'C002', name: 'Kind of document', components: [{ id: '1001', status: 'M' }] },
      C507: { id: 'C507', name: 'Date', components: [{ id: '2005', status: 'M' }, { id: '2380', status: 'M' }, { id: '2379', status: 'M' }] },
      C107: { id: 'C107', name: 'Note code', components: [{ id: '4441', status: 'M' }] },
      C108: { id: 'C108', name: 'Note text', components: [{ id: '4440', status: 'M' }, { id: '4440', status: 'C' }] },
      C082: { id: 'C082', name: 'Party code', components: [{ id: '3039', status: 'M' }, { id: '1131', status: 'C' }, { id: '3055', status: 'M' }] },
      C212: { id: 'C212', name: 'Item code', components: [{ id: '7140', status: 'M' }, { id: '7143', status: 'M' }] },
      C186: { id: 'C186', name: 'Quantity', components: [{ id: '6063', status: 'M' }, { id: '6060', status: 'M' }] },
      C509: { id: 'C509', name: 'Price', components: [{ id: '5125', status: 'M' }, { id: '5118', status: 'M' }] },
      C270: { id: 'C270', name: 'Check total', components: [{ id: '6069', status: 'M' }, { id: '6066', status: 'M' }] },
    },
    elements: {
      '1001': { id: '1001', name: 'Document', format: 'an..3', codes: 'documents' },
      '1004': { id: '1004', name: 'Order number', format: 'an..35' },
      '1225': { id: '1225', name: 'Purpose', format: 'an..3', codes: 'purposes' },
      '2005': { id: '2005', name: 'Which date', format: 'an..3', codes: 'dates' },
      '2380': { id: '2380', name: 'Date', format: 'an..35' },
      '2379': { id: '2379', name: 'Date format', format: 'an..3', codes: ['102'] },
      '4451': { id: '4451', name: 'Note subject', format: 'an..3', codes: ['DEL', 'PUR'] },
      '4453': { id: '4453', name: 'Note function', format: 'an..3' },
      '4441': { id: '4441', name: 'Note code', format: 'an..3' },
      '4440': { id: '4440', name: 'Text', format: 'an..70' },
      '3035': { id: '3035', name: 'Party role', format: 'an..3', codes: 'roles' },
      '3039': { id: '3039', name: 'Party code', format: 'an..35' },
      '1131': { id: '1131', name: 'Code list', format: 'an..3' },
      '3055': { id: '3055', name: 'Who issued the code', format: 'an..3', codes: ['9'] },
      '1082': { id: '1082', name: 'Line number', format: 'n..6' },
      '1229': { id: '1229', name: 'Line action', format: 'an..3' },
      '7140': { id: '7140', name: 'Item number', format: 'an..35' },
      '7143': { id: '7143', name: 'Item number kind', format: 'an..3', codes: ['EN', 'SA'] },
      '6063': { id: '6063', name: 'Kind of quantity', format: 'an..3', codes: ['21'] },
      '6060': { id: '6060', name: 'Quantity', format: 'n..15' },
      '5125': { id: '5125', name: 'Kind of price', format: 'an..3', codes: ['AAA'] },
      '5118': { id: '5118', name: 'Price', format: 'n..15' },
      '0081': { id: '0081', name: 'Section', format: 'a1', codes: ['S'] },
      '6069': { id: '6069', name: 'What is counted', format: 'an..3', codes: ['2'] },
      '6066': { id: '6066', name: 'Count', format: 'n..18' },
    },
    codes: {
      documents: { '220': 'Order', '221': 'Standing order', '105': 'Sample order' },
      purposes: { '9': 'Original', '5': 'Replacement', '1': 'Cancellation' },
      dates: { '137': 'Order date', '2': 'Deliver on', '64': 'Deliver no earlier than', '63': 'Deliver no later than' },
      roles: { BY: 'Buyer', SU: 'Supplier', DP: 'Deliver to', IV: 'Invoice to' },
    },
  };

  const interchange = `UNA:+.? '
UNB+UNOC:3+5012345000004:14+5098765000005:14+261020:0915+HLS000421++ORDERS'
UNH+1+ORDERS:D:96A:UN'
BGM+220+PO-004217+9'
DTM+137:20261020:102'
DTM+2:20261030:102'
FTX+DEL+++Deliveries to the side entrance on Quay Street?'s yard'
NAD+BY+5012345000004::9'
NAD+SU+5098765000005::9'
NAD+XX+5012345000011::9'
LIN+1++5098765100014:EN'
QTY+21:8'
PRI+AAA:18.40'
LIN+2++5098765100021:EN'
QTY+21:two'
PRI+AAA:11.25'
LIN+3++5098765100038:EN'
QTY+21:12'
PRI+AAA:2.10'
UNS+S'
CNT+2:3'
UNT+20+1'
UNZ+1+HLS000421'
`;

  editor.guides = [guide];
  editor.value = interchange;
  // Start at the line with the bad quantity.
  await editor.select(13);

  const report = ({ valid, problems }) => {
    const errors = problems.filter(p => p.severity === 'error');
    out.textContent = valid ? 'Valid against the order guide - ready to process.' : `${errors.length} error${errors.length === 1 ? '' : 's'}: ${errors[0].path} - ${errors[0].message}`;
  };
  editor.addEventListener('bmxValidate', event => report(event.detail));
  const problems = await editor.validate();
  report({ valid: !problems.some(p => p.severity === 'error'), problems });
</script>
<bmx-edi-editor label="Inbound orders"></bmx-edi-editor>
<script>
  editor.guides = [partnerOrdersGuide];
  editor.value = interchangeText;
</script>

The envelopes are always checked: headers and trailers paired, counts and control references right, each envelope element of the right form. Messages are checked against the guides given - a trading partner's implementation guide, an X12 guide or a UN/EDIFACT directory the customer holds, as JSON in the documented shape: segments in order, groups repeated no more than allowed, mandatory elements present, values of the right kind and length, codes from their lists and rules between elements. Every problem has its path (ORDERS 1/SG2[1]/NAD/3035) and takes you to it.

Changes made in the tree replace only their own characters, release characters added as needed; adding, duplicating, deleting and moving segments puts the control counts right. Acknowledge writes the answer a receiver sends: a CONTRL for EDIFACT, a 997, 999 or TA1 for X12.

Nothing leaves the page: files and guides are read in the browser. In a form, name receives the text; require-valid keeps the form from submitting while there are errors.

Tree keyboard: arrows move and open/close, Home/End, Page Up/Down, Enter goes to the selected segment's first value, Delete deletes the segment, Alt+Up/Down move it.

Properties

PropertyAttributeTypeDefaultDescription
ackKind ack-kind '997' | '999' | 'TA1' '997' X12 acknowledgement written by Acknowledge: 997 (default), 999 or TA1.
autoRecount auto-recount boolean true Correct control counts after segments are added, deleted or moved.
codesAsWarnings codes-as-warnings boolean false Report codes missing from their lists as warnings rather than errors.
description description string — Help text below the editor.
disabled disabled boolean false Disable the editor.
errorText error-text string — An error supplied by the page.
guideSrc guide-src string — Addresses of guide JSON files to fetch (space or comma separated).
guides guides BmxEdiGuide | BmxEdiGuide[] | string — Guides describing messages: one, a list, or JSON of either.
hideLabel hide-label boolean false Hide the label visually while keeping it as the accessible name.
hideStatus hide-status boolean false Hide the status bar.
hideToolbar hide-toolbar boolean false Hide the toolbar.
label label string — The field's label: names the whole control.
name name string — The field's name in a form: it receives the text.
readonly readonly boolean false Allow reading, searching and copying, but not changing.
requireValid require-valid boolean false Keep the form from submitting while there are errors.
src src string — An address to load the interchange from (the page's own server).
strict strict boolean false Count warnings as errors in acknowledgements and validity.
strings strings Partial<BmxEdiEditorStrings> | string — Words to show instead of the English ones, as an object or JSON.
value value string '' The interchange's text. Two-way.
view view BmxEdiEditorView 'tree' The view shown. Two-way.
views views BmxEdiEditorView[] | string — The views offered, as an array, JSON or a comma list. Default: all four.

Events

EventDetailDescription
bmxAcknowledge BmxEdiAcknowledgeDetail Fired when an acknowledgement has been written.
bmxChange BmxEdiChangeDetail Fired after the text changes.
bmxEdit BmxEdiEditDetail Fired before a change made with the tree or a command. Cancel it to refuse the change.
bmxSelect BmxEdiSelectDetail Fired when another segment or envelope is selected.
bmxValidate { valid: boolean; problems: readonly BmxEdiProblem[]; } Fired after each check.

Methods

MethodSignatureDescription
acknowledge acknowledge(kind?: "997" | "999" | "TA1" | "CONTRL") => Promise<BmxEdiAck[]> Write acknowledgements (CONTRL for EDIFACT, the kind given or ack-kind for X12) without showing them.
addGuides addGuides(guides: BmxEdiGuide | BmxEdiGuide[] | string) => Promise<number> Add guides to those given by guides.
collapseAll collapseAll() => Promise<void>
expandAll expandAll() => Promise<void> Expand every interchange, group, message and segment group.
getValidation getValidation() => Promise<BmxEdiValidation | null> The whole validation: envelopes, message trees and problems.
getValue getValue() => Promise<string> The text now (including typing not yet read).
loadFiles loadFiles(files: readonly File[] | FileList) => Promise<void> Read files the person chose or dropped: an interchange (EDI, or JSON or XML this editor wrote) and guide JSON files.
recount recount() => Promise<boolean> Correct every control count. Returns whether anything changed.
redo redo() => Promise<boolean>
save save(fileName?: string) => Promise<void> Download the interchange (default name from the file opened, or interchange.edi).
select select(segment: number) => Promise<boolean> Select a segment by its index in the file.
setDelimiters setDelimiters(delimiters: Partial<BmxEdiDelimiters>) => Promise<boolean> Write the interchange again with other delimiters.
setLayout setLayout(layout: "lines" | "compact") => Promise<boolean> Lay the interchange out one segment a line, or run together.
setValue setValue(text: string, options?: { keepHistory?: boolean; }) => Promise<void> Replace the whole text. With keepHistory, it can be undone.
toJson toJson(options?: { exact?: boolean; }) => Promise<unknown> The interchange as JSON: readable (default) or exact (writes back with fromEdiJson).
toXml toXml() => Promise<string> The interchange as XML (written back exactly by fromEdiXml).
undo undo() => Promise<boolean>
validate validate() => Promise<BmxEdiProblem[]> Check now and return every problem.

Slots

SlotDescription
description Rich help text, in place of the description property.
label Rich label content, in place of the label property.

CSS shadow parts

PartDescription
ack The acknowledgement panel.
description The help text under the field.
detail The selected segment's elements.
label The label element.
menu The More menu.
problems The problems panel.
row A tree row; also row-selected, row-problem.
status The bar under the views.
toolbar The bar of views and commands.
tree The tree.

CSS custom properties

PropertyDescription
--bmx-edi-editor-background The views' background.
--bmx-edi-editor-detail-width The element panel's width beside the tree.
--bmx-edi-editor-envelope-color Interchanges, groups and messages in the tree.
--bmx-edi-editor-height The height of the views' working area.
--bmx-edi-editor-indent How far each level of the tree is indented.
--bmx-edi-editor-label-font-size The label's font size.
--bmx-edi-editor-radius Corner radius of the frame.
--bmx-edi-editor-row-height The height of one tree row.
--bmx-edi-editor-selected-background The selected row.
--bmx-edi-editor-support-font-size Font size of the description and error message.
--bmx-edi-editor-tag-color Segment tags in the tree.