<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
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
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
| Event | Detail | Description |
|---|---|---|
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
| Method | Signature | Description |
|---|---|---|
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
| Slot | Description |
|---|---|
description |
Rich help text, in place of the description property. |
label |
Rich label content, in place of the label property. |
CSS shadow parts
| Part | Description |
|---|---|
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
| Property | Description |
|---|---|
--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. |