From 04dabc901ea01b968dced589c353f42f7e46be04 Mon Sep 17 00:00:00 2001 From: Sylvain Mariel Date: Sun, 29 Sep 2024 15:49:20 +0200 Subject: [PATCH] Add custom data in sequence step --- css/sequenceEditor.css | 2 +- html/sequenceEditor.html | 6 +-- js/class.Sequence.js | 69 ++++++++++++++++++++---- js/class.SequenceEditor.js | 105 ++++++++++++++++++++++++++++++------- sass/sequenceEditor.scss | 20 ++++++- 5 files changed, 167 insertions(+), 35 deletions(-) diff --git a/css/sequenceEditor.css b/css/sequenceEditor.css index 0ff8575..d38d503 100644 --- a/css/sequenceEditor.css +++ b/css/sequenceEditor.css @@ -1 +1 @@ -#sequence_edition,#sequence_display{display:none;margin:10px}#sequence_display_control{background:#eceff1;margin-top:10px;padding:10px}#sequence_display_control label,#sequence_display_control button{margin:5px}#sequence_display_control button{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#00bfa5;color:#fff}#sequence_display_control button:hover{background-color:#00a68f;color:#fff;cursor:pointer;text-decoration:none}#sequence_display_control button:active,#sequence_display_control button:focus{outline:none;color:#fff;background-color:#008c79}body{padding-top:30px}#sequence_selection{position:fixed;top:0;left:0;right:0;background:#212121;height:30px;display:flex;align-items:center;z-index:999}#sequence_select{margin:0 6px 0 26px}#sequence_onoff{margin:0 6px}#sequence_selection ul{display:flex;margin:0;padding:0}#sequence_selection ul#sequence_control{box-shadow:inset 0 -3px 0 0 #ff1744}#sequence_selection li{list-style:none}#sequence_selection label{margin:0;padding:0 6px;color:#fafafa;font-weight:normal;font-size:12px}#sequence_selection a{margin:0;padding:0 6px;font-size:12px;line-height:30px;color:#fafafa;background:none;border:none;user-select:none;text-decoration:none;white-space:nowrap}#sequence_selection a:hover{cursor:pointer}#step_table th{padding:2px 8px}#step_table td{padding:4px 8px}#step_table ul{list-style:none;margin:0;padding:0}.step-up,.step-down{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#fff;color:#424242;padding:2px 6px}.step-up:hover,.step-down:hover{background-color:#f2f2f2;color:#424242;cursor:pointer;text-decoration:none}.step-up:active,.step-up:focus,.step-down:active,.step-down:focus{outline:none;color:#424242;background-color:#e6e6e6}.step-up:hover,.step-up:focus,.step-down:hover,.step-down:focus{background-color:#424242;color:#fff}.step-up:active,.step-down:active{background-color:#1c1c1c;color:#fff}#step_table>tbody>tr:first-child .step-up,#step_table>tbody>tr:nth-last-child(2) .step-down{display:none}#step_table>tbody>tr:not(:last-child) td:last-child{text-align:right}.step-remove{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#fff;color:#ff1744;padding:2px 7px}.step-remove:hover{background-color:#f2f2f2;color:#ff1744;cursor:pointer;text-decoration:none}.step-remove:active,.step-remove:focus{outline:none;color:#ff1744;background-color:#e6e6e6}.step-remove:hover,.step-remove:focus{background-color:#ff1744;color:#fff}.step-remove:active{background-color:#ca0027;color:#fff}.signal-custom{font-style:italic}.signal-remove{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#fff;color:#ff1744}.signal-remove:hover{background-color:#f2f2f2;color:#ff1744;cursor:pointer;text-decoration:none}.signal-remove:active,.signal-remove:focus{outline:none;color:#ff1744;background-color:#e6e6e6}.signal-remove:active{color:#ca0027}.signal-up,.signal-down{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#fff;color:#424242}.signal-up:hover,.signal-down:hover{background-color:#f2f2f2;color:#424242;cursor:pointer;text-decoration:none}.signal-up:active,.signal-up:focus,.signal-down:active,.signal-down:focus{outline:none;color:#424242;background-color:#e6e6e6}.signal-up:active,.signal-down:active{color:#1c1c1c}.signal-remove,.signal-up,.signal-down{padding:0px;visibility:hidden}.signal-remove:hover,.signal-remove:focus,.signal-up:hover,.signal-up:focus,.signal-down:hover,.signal-down:focus{background-color:#fff}#step_table li:hover .signal-remove,#step_table li:hover .signal-up,#step_table li:hover .signal-down{visibility:visible}#step_table li:first-child .signal-up,#step_table li:last-child .signal-down{display:none}.asserted-add,.awaited-add{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#fff;color:#00bfa5;margin-top:4px;padding:0px 5px}.asserted-add:hover,.awaited-add:hover{background-color:#f2f2f2;color:#00bfa5;cursor:pointer;text-decoration:none}.asserted-add:active,.asserted-add:focus,.awaited-add:active,.awaited-add:focus{outline:none;color:#00bfa5;background-color:#e6e6e6}.asserted-add:hover,.asserted-add:focus,.awaited-add:hover,.awaited-add:focus{background-color:#00bfa5;color:#fff}.asserted-add:active,.awaited-add:active{background-color:#007363;color:#fff}.step-add{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#00bfa5;color:#fff}.step-add:hover{background-color:#00a68f;color:#fff;cursor:pointer;text-decoration:none}.step-add:active,.step-add:focus{outline:none;color:#fff;background-color:#008c79}.step-add:hover{background-color:#00a68f}.step-add:active,.step-add:focus{background-color:#008c79} +#sequence_edition,#sequence_display{display:none;margin:10px}#sequence_display_control{background:#eceff1;margin-top:10px;padding:10px}#sequence_display_control label,#sequence_display_control button{margin:5px}#sequence_display_control button{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#00bfa5;color:#fff}#sequence_display_control button:hover{background-color:#00a68f;color:#fff;cursor:pointer;text-decoration:none}#sequence_display_control button:active,#sequence_display_control button:focus{outline:none;color:#fff;background-color:#008c79}body{padding-top:30px}#sequence_selection{position:fixed;top:0;left:0;right:0;background:#212121;height:30px;display:flex;align-items:center;z-index:999}#sequence_select{margin:0 6px 0 26px}#sequence_onoff{margin:0 6px}#sequence_selection ul{display:flex;margin:0;padding:0}#sequence_selection ul#sequence_control{box-shadow:inset 0 -3px 0 0 #ff1744}#sequence_selection li{list-style:none}#sequence_selection label{margin:0;padding:0 6px;color:#fafafa;font-weight:normal;font-size:12px}#sequence_selection a{margin:0;padding:0 6px;font-size:12px;line-height:30px;color:#fafafa;background:none;border:none;user-select:none;text-decoration:none;white-space:nowrap}#sequence_selection a:hover{cursor:pointer}#step_table th{padding:2px 8px}#step_table td{padding:4px 8px}#step_table ul{list-style:none;margin:0;padding:0}.step-up,.step-down{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#fff;color:#424242;padding:2px 6px}.step-up:hover,.step-down:hover{background-color:#f2f2f2;color:#424242;cursor:pointer;text-decoration:none}.step-up:active,.step-up:focus,.step-down:active,.step-down:focus{outline:none;color:#424242;background-color:#e6e6e6}.step-up:hover,.step-up:focus,.step-down:hover,.step-down:focus{background-color:#424242;color:#fff}.step-up:active,.step-down:active{background-color:#1c1c1c;color:#fff}#step_table>tbody>tr:first-child .step-up,#step_table>tbody>tr:nth-last-child(2) .step-down{display:none}#step_table>tbody>tr:not(:last-child) td:last-child{text-align:right}.step-remove{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#fff;color:#ff1744;padding:2px 7px}.step-remove:hover{background-color:#f2f2f2;color:#ff1744;cursor:pointer;text-decoration:none}.step-remove:active,.step-remove:focus{outline:none;color:#ff1744;background-color:#e6e6e6}.step-remove:hover,.step-remove:focus{background-color:#ff1744;color:#fff}.step-remove:active{background-color:#ca0027;color:#fff}.signal-custom{font-style:italic}.signal-remove,.stepdata-remove{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#fff;color:#ff1744}.signal-remove:hover,.stepdata-remove:hover{background-color:#f2f2f2;color:#ff1744;cursor:pointer;text-decoration:none}.signal-remove:active,.signal-remove:focus,.stepdata-remove:active,.stepdata-remove:focus{outline:none;color:#ff1744;background-color:#e6e6e6}.signal-remove:active,.stepdata-remove:active{color:#ca0027}.signal-up,.signal-down{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#fff;color:#424242}.signal-up:hover,.signal-down:hover{background-color:#f2f2f2;color:#424242;cursor:pointer;text-decoration:none}.signal-up:active,.signal-up:focus,.signal-down:active,.signal-down:focus{outline:none;color:#424242;background-color:#e6e6e6}.signal-up:active,.signal-down:active{color:#1c1c1c}.signal-remove,.stepdata-remove,.signal-up,.signal-down{padding:0px;visibility:hidden}.signal-remove:hover,.signal-remove:focus,.stepdata-remove:hover,.stepdata-remove:focus,.signal-up:hover,.signal-up:focus,.signal-down:hover,.signal-down:focus{background-color:#fff}#step_table li:hover .signal-remove,#step_table li:hover .signal-up,#step_table li:hover .signal-down{visibility:visible}#step_table li:first-child .signal-up,#step_table li:last-child .signal-down{display:none}#step_table th:hover .stepdata-remove{visibility:visible}#step_table th:last-child{text-align:right;padding-right:10px}.asserted-add,.awaited-add,.stepdata-add{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#fff;color:#00bfa5;margin-top:4px;padding:0px 5px}.asserted-add:hover,.awaited-add:hover,.stepdata-add:hover{background-color:#f2f2f2;color:#00bfa5;cursor:pointer;text-decoration:none}.asserted-add:active,.asserted-add:focus,.awaited-add:active,.awaited-add:focus,.stepdata-add:active,.stepdata-add:focus{outline:none;color:#00bfa5;background-color:#e6e6e6}.asserted-add:hover,.asserted-add:focus,.awaited-add:hover,.awaited-add:focus,.stepdata-add:hover,.stepdata-add:focus{background-color:#00bfa5;color:#fff}.asserted-add:active,.awaited-add:active,.stepdata-add:active{background-color:#007363;color:#fff}.step-add{text-transform:uppercase;background:none;border:none;border-radius:2px;padding:6px 20px;transition:150ms ease;background-color:#00bfa5;color:#fff}.step-add:hover{background-color:#00a68f;color:#fff;cursor:pointer;text-decoration:none}.step-add:active,.step-add:focus{outline:none;color:#fff;background-color:#008c79}.step-add:hover{background-color:#00a68f}.step-add:active,.step-add:focus{background-color:#008c79} diff --git a/html/sequenceEditor.html b/html/sequenceEditor.html index 139413a..da49b66 100644 --- a/html/sequenceEditor.html +++ b/html/sequenceEditor.html @@ -42,9 +42,9 @@ Step Name Asserted Signal Awaited Signal - Min time (ms) - Max time (ms) - + + + diff --git a/js/class.Sequence.js b/js/class.Sequence.js index 96f77a7..15bc9a1 100644 --- a/js/class.Sequence.js +++ b/js/class.Sequence.js @@ -102,6 +102,7 @@ class Sequence { this.name = name; this.steps = []; this.onoff = 1; // 1 = ON, 0 = 0FF + this.dataName = []; Object.defineProperties(this, { length: { get: function() { @@ -113,7 +114,7 @@ class Sequence { // add an empty step to the sequence addStep(name = `STEP ${this.length}`) { - let newStep = new SequenceStep(this.length, name); + let newStep = new SequenceStep(this.length, name, this.dataName.length); this.steps.push(newStep); return newStep; } @@ -177,6 +178,28 @@ class Sequence { return hasSignal; } + // add a new step data with a given name + addStepData(name='', value='') { + // first, add the name + this.dataName.push(name); + + // then, add the value to each step + this.forEachStep((step) => { + step.data.push(value); + }); + } + + // remove a step data from all step + removeStepData(stepdataid) { + // first, remove the data name + this.dataName.splice(stepdataid,1); + + // then, remove the data from each step + this.forEachStep((step) => { + step.data.splice(stepdataid,1); + }); + } + // generate a JSON object using WaveDrom synthax toWavedromObject(config = {}) { // init the wavedrom object @@ -218,6 +241,7 @@ class Sequence { name: this.name, onoff: this.onoff, steps: [], + dataName: this.dataName }; this.forEachStep((step) => { json.steps.push(step.toString()); @@ -236,6 +260,17 @@ class Sequence { for(let step of json.steps) { sequence.steps.push(SequenceStep.fromString(step)); } + + // import data (>= v2.3) + if (undefined !== json.dataName) { + sequence.dataName = json.dataName; + } + + // import tmin/tmax (< v2.3) + if(sequence.steps.length > 0 && sequence.steps[0].data.length == 2 && sequence.dataName.length == 0) { + sequence.dataName = ['tmin', 'tmax']; + } + return sequence; } @@ -286,12 +321,11 @@ class Sequence { // ----------------------------------------------------------------------------- class SequenceStep { - constructor(id, name=`STEP ${id}`, tmin=0, tmax=0) { + constructor(id, name=`STEP ${id}`, datalength=0) { this.id = id; this.name = name; - this.tmin = tmin; - this.tmax = tmax; this.signals = []; + this.data = []; Object.defineProperties(this, { length: { @@ -300,6 +334,10 @@ class SequenceStep { }, } }); + + for(let i=0; i= v2.3) + if (undefined !== json.data) { + step.data = json.data; + } + + // import tmin/tmax (< v2.3) + if(undefined !== json.tmin) { + step.data.push(json.tmin); + } + if(undefined !== json.tmax) { + step.data.push(json.tmax); + } + return step; } } diff --git a/js/class.SequenceEditor.js b/js/class.SequenceEditor.js index 0a78201..8a0f373 100644 --- a/js/class.SequenceEditor.js +++ b/js/class.SequenceEditor.js @@ -58,6 +58,17 @@ class SequenceEditor { // show the step table (hidden by default in css) $('#sequence_edition, #sequence_display').show(); + // add custom step data names + $('.stepdataname').parents('th').remove(); + let dataNameHTML = ''; + for(let i=0; i + ${this.selectedSequence.dataName[i]} + + `; + } + $('#step_table>thead>tr>th:last-child').before(dataNameHTML); + // for each step of the selected sequence let id=0; let stepHTML; @@ -83,10 +94,11 @@ class SequenceEditor { `; }); - stepHTML += ` - ${step.tmin} - ${step.tmax} - + stepHTML += ``; + for(let i=0; i${step.data[i]}`; + } + stepHTML += ` @@ -95,24 +107,46 @@ class SequenceEditor { }); stepHTML += ''; $('#step_table>tbody').html(stepHTML); + + $('#step_table>tbody>tr:last-child>td').attr('colspan',5+this.selectedSequence.dataName.length); } // cancel any edition of the step name cancelStepDataEdition() { if($('.stepdata-edited').length <= 0) return; - let step = $('.stepdata-edited').data('step'); - let stepdata = $('.stepdata-edited').data('stepdata'); - $('.stepdata-edited').removeClass('stepdata-edited').html(step[stepdata]); + let step = $('.stepdata-edited').data('step'); + let stepdata = $('.stepdata-edited').data('stepdata'); + + if($('.stepdata-edited').hasClass('stepname')){ + $('.stepdata-edited').removeClass('stepdata-edited').html(step.name); + } + else if($('.stepdata-edited').hasClass('stepdataname')){ + $('.stepdata-edited').removeClass('stepdata-edited').html(this.selectedSequence.dataName[stepdata]); + } + else { + $('.stepdata-edited').removeClass('stepdata-edited').html(step.data[stepdata]); + } + } // validate the current edition of the step name and redraw validateStepDataEdition() { if($('.stepdata-edited').length <= 0) return; - let step = $('.stepdata-edited').data('step'); - let stepdata = $('.stepdata-edited').data('stepdata'); - step[stepdata] = $('.stepdata-edited input').val(); + let step = $('.stepdata-edited').data('step'); + let stepdata = $('.stepdata-edited').data('stepdata'); + + if($('.stepdata-edited').hasClass('stepname')){ + step.name = $('.stepdata-edited input').val(); + } + else if($('.stepdata-edited').hasClass('stepdataname')){ + this.selectedSequence.dataName[stepdata] = $('.stepdata-edited input').val(); + } + else { + step.data[stepdata] = $('.stepdata-edited input').val(); + } + this.cancelStepDataEdition(); this.drawWavedromDiagram(); } @@ -281,7 +315,7 @@ class SequenceEditor { } // export the selected sequence to a JSON file (async function) - exportWavedromToJSON() { + exportSequenceToJSON() { if(null === this.selectedSequence) return; // init the sequence data to be reformated for export @@ -297,10 +331,13 @@ class SequenceEditor { let exportedStep = { step: step.name, signals_in: [], - signals_out: [], - tmin: isNaN(step.tmin) ? step.tmin : parseInt(step.tmin), - tmax: isNaN(step.tmax) ? step.tmax : parseInt(step.tmax) - }; + signals_out: [] + }; + + // add data to the step with the name stored in the sequence + step.data.forEach((data, id) => { + exportedStep[this.selectedSequence.dataName[id]] = data; + }); // for each signal of this step step.forEachSignal((signal) => { @@ -323,7 +360,8 @@ class SequenceEditor { }); // download the Sequence to a JSON file - let file_content = JSON.stringify(exportedSequence, null, 4).replace(/(\n|\r|\s)+/g,'')+'\n'; + let file_content = JSON.stringify(exportedSequence, null, 4)+'\n'; + // let file_content = JSON.stringify(exportedSequence, null, 4).replace(/(\n|\r|\s)+/g,'')+'\n'; let file_name = `${this.selectedSequence.name}.json`; let blob = new Blob([file_content]); let dataURL = URL.createObjectURL(blob); @@ -424,12 +462,39 @@ class SequenceEditor { this.drawWavedromDiagram(); }); - // edit the name of a step (only if not already edited) and cancel other edition + // add a new step data with a default name + $('.stepdata-add').on('click', () => { + this.selectedSequence.addStepData('New Data', 'data value'); + this.refreshStepTable(); + this.drawWavedromDiagram(); + }); + + // remove a step data from all steps + $('#step_table').on('click', '.stepdata-remove', (event) => { + let stepdataid = $(event.target).parents('th').children('.stepdataname').data('stepdata'); + this.selectedSequence.removeStepData(stepdataid); + this.refreshStepTable(); + this.drawWavedromDiagram(); + }); + + // edit a data (only if not already edited) and cancel other edition $('#step_table').on('click', '.stepdata:not(.stepdata-edited)', (event) => { let stepid = $(event.target).parents('tr').data('stepid'); let stepdata = $(event.target).data('stepdata'); let step = this.selectedSequence.getStep(stepid); - $(event.target).addClass('stepdata-edited').data('step',step).html(``).children('input').focus().select(); + let value; + + if($(event.target).hasClass('stepname')){ + value = step.name; + } + else if($(event.target).hasClass('stepdataname')){ + value = this.selectedSequence.dataName[stepdata]; + } + else { + value = step.data[stepdata]; + } + + $(event.target).addClass('stepdata-edited').data('step',step).html(``).children('input').focus().select(); }); // cancel or validate the edition of the step name depending on the pressed key @@ -555,7 +620,7 @@ class SequenceEditor { // ESCAPE if (27 == event.keyCode) { this.cancelSeqNameEdition(); - // TODO: Escape not working ??? + // TODO: Escape not working ??? escape does not trigger keypress ??? } // ENTER else if (13 == event.keyCode) { @@ -575,7 +640,7 @@ class SequenceEditor { // export the diagram to jpg $('#sequence_export_json').click(() => { - this.exportWavedromToJSON(); + this.exportSequenceToJSON(); }); // change the diagram scale diff --git a/sass/sequenceEditor.scss b/sass/sequenceEditor.scss index 13fe6cc..912938c 100644 --- a/sass/sequenceEditor.scss +++ b/sass/sequenceEditor.scss @@ -158,7 +158,8 @@ body { font-style: italic; } -.signal-remove { +.signal-remove, +.stepdata-remove { @include square-btn(white, $red); &:active { @@ -176,6 +177,7 @@ body { } .signal-remove, +.stepdata-remove, .signal-up, .signal-down { padding: 0px; @@ -202,8 +204,22 @@ body { } } +#step_table th { + &:hover { + & .stepdata-remove { + visibility: visible; + } + } + + &:last-child { + text-align: right; + padding-right: 10px; + } +} + .asserted-add, -.awaited-add { +.awaited-add, +.stepdata-add { @include square-btn(white, $teal); margin-top: 4px; padding: 0px 5px;