@dualbox/editor 1.0.92 → 1.0.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43766,47 +43766,174 @@ class GraphModel {
43766
43766
  paste(json) {
43767
43767
  var currentMetanode = this.getCurrentMetanode();
43768
43768
 
43769
- this.batch(() => {
43770
- // paste metanodes
43769
+ var checkValidPaste = () => {
43770
+ let valid = true;
43771
+
43771
43772
  lodash.each(json.metamodules, (mmDef, mmName) => {
43772
43773
  var metamodule = lodash.get(currentMetanode, ["metanodes", mmName]);
43773
- if (metamodule === undefined) {
43774
- lodash.set(currentMetanode, ["metanodes", mmName], mmDef);
43774
+ if (metamodule !== undefined) {
43775
+ valid = false;
43776
+ const Toast = sweetalert2_all.mixin({
43777
+ toast: true,
43778
+ position: 'bottom-end',
43779
+ showConfirmButton: false,
43780
+ timer: 5000,
43781
+ timerProgressBar: true,
43782
+ html: "",
43783
+ onOpen: (toast) => {
43784
+ toast.addEventListener('mouseenter', sweetalert2_all.stopTimer);
43785
+ toast.addEventListener('mouseleave', sweetalert2_all.resumeTimer);
43786
+ }
43787
+ });
43788
+ Toast.fire({
43789
+ icon: 'error',
43790
+ title: '<span class="text-danger">Invalid paste : metanode ' + mmName + ' already exists here</span>'
43791
+ });
43775
43792
  }
43776
43793
  });
43777
-
43778
- // paste inputs
43779
43794
  lodash.each(json.input, (inputDef, inputName) => {
43780
43795
  var input = lodash.get(currentMetanode, ["input", inputName]);
43781
- if (input === undefined) {
43782
- lodash.set(currentMetanode, ["input", inputName], inputDef);
43796
+ if (input !== undefined) {
43797
+ valid = false;
43798
+ const Toast = sweetalert2_all.mixin({
43799
+ toast: true,
43800
+ position: 'bottom-end',
43801
+ showConfirmButton: false,
43802
+ timer: 5000,
43803
+ timerProgressBar: true,
43804
+ html: "",
43805
+ onOpen: (toast) => {
43806
+ toast.addEventListener('mouseenter', sweetalert2_all.stopTimer);
43807
+ toast.addEventListener('mouseleave', sweetalert2_all.resumeTimer);
43808
+ }
43809
+ });
43810
+ Toast.fire({
43811
+ icon: 'error',
43812
+ title: '<span class="text-danger">Invalid paste : input ' + inputName + ' already exists here</span>'
43813
+ });
43783
43814
  }
43784
43815
  });
43785
-
43786
43816
  // paste outputs
43787
43817
  lodash.each(json.output, (outputDef, outputName) => {
43788
43818
  var output = lodash.get(currentMetanode, ["output", outputName]);
43789
- if (output === undefined) {
43790
- lodash.set(currentMetanode, ["output", outputName], outputDef);
43819
+ if (output !== undefined) {
43820
+ valid = false;
43821
+ const Toast = sweetalert2_all.mixin({
43822
+ toast: true,
43823
+ position: 'bottom-end',
43824
+ showConfirmButton: false,
43825
+ timer: 5000,
43826
+ timerProgressBar: true,
43827
+ html: "",
43828
+ onOpen: (toast) => {
43829
+ toast.addEventListener('mouseenter', sweetalert2_all.stopTimer);
43830
+ toast.addEventListener('mouseleave', sweetalert2_all.resumeTimer);
43831
+ }
43832
+ });
43833
+ Toast.fire({
43834
+ icon: 'error',
43835
+ title: '<span class="text-danger">Invalid paste : output ' + outputName + ' already exists here</span>'
43836
+ });
43791
43837
  }
43792
43838
  });
43793
43839
 
43794
43840
  // paste modules
43795
43841
  lodash.each(json.modules, (moduleDef, moduleName) => {
43796
43842
  var node = lodash.get(currentMetanode, ["modules", moduleName]);
43797
- if (node === undefined) {
43798
- lodash.set(currentMetanode, ["modules", moduleName], moduleDef);
43843
+ if (node !== undefined) {
43844
+ valid = false;
43845
+ const Toast = sweetalert2_all.mixin({
43846
+ toast: true,
43847
+ position: 'bottom-end',
43848
+ showConfirmButton: false,
43849
+ timer: 5000,
43850
+ timerProgressBar: true,
43851
+ html: "",
43852
+ onOpen: (toast) => {
43853
+ toast.addEventListener('mouseenter', sweetalert2_all.stopTimer);
43854
+ toast.addEventListener('mouseleave', sweetalert2_all.resumeTimer);
43855
+ }
43856
+ });
43857
+ Toast.fire({
43858
+ icon: 'error',
43859
+ title: '<span class="text-danger">Invalid paste : compute box ' + moduleName + ' already exists here</span>'
43860
+ });
43799
43861
  }
43800
43862
  });
43801
43863
 
43802
43864
  // paste uis
43803
43865
  lodash.each(json.ui, (uiDef, uiName) => {
43804
43866
  var ui = lodash.get(currentMetanode, ["ui", uiName]);
43805
- if (ui === undefined) {
43806
- lodash.set(currentMetanode, ["ui", uiName], uiDef);
43867
+ if (ui !== undefined) {
43868
+ valid = false;
43869
+ const Toast = sweetalert2_all.mixin({
43870
+ toast: true,
43871
+ position: 'bottom-end',
43872
+ showConfirmButton: false,
43873
+ timer: 5000,
43874
+ timerProgressBar: true,
43875
+ html: "",
43876
+ onOpen: (toast) => {
43877
+ toast.addEventListener('mouseenter', sweetalert2_all.stopTimer);
43878
+ toast.addEventListener('mouseleave', sweetalert2_all.resumeTimer);
43879
+ }
43880
+ });
43881
+ Toast.fire({
43882
+ icon: 'error',
43883
+ title: '<span class="text-danger">Invalid paste : ui ' + uiName + ' already exists here</span>'
43884
+ });
43807
43885
  }
43808
43886
  });
43809
- });
43887
+
43888
+ return valid;
43889
+ };
43890
+
43891
+ let validPaste = checkValidPaste();
43892
+ if (validPaste) {
43893
+ this.batch(() => {
43894
+ // paste metanodes
43895
+ lodash.each(json.metamodules, (mmDef, mmName) => {
43896
+ var metamodule = lodash.get(currentMetanode, ["metanodes", mmName]);
43897
+ if (metamodule === undefined) {
43898
+ lodash.set(currentMetanode, ["metanodes", mmName], mmDef);
43899
+ }
43900
+ });
43901
+
43902
+ // paste inputs
43903
+ lodash.each(json.input, (inputDef, inputName) => {
43904
+ var input = lodash.get(currentMetanode, ["input", inputName]);
43905
+ if (input === undefined) {
43906
+ lodash.set(currentMetanode, ["input", inputName], inputDef);
43907
+ }
43908
+ });
43909
+
43910
+ // paste outputs
43911
+ lodash.each(json.output, (outputDef, outputName) => {
43912
+ var output = lodash.get(currentMetanode, ["output", outputName]);
43913
+ if (output === undefined) {
43914
+ lodash.set(currentMetanode, ["output", outputName], outputDef);
43915
+ }
43916
+ });
43917
+
43918
+ // paste modules
43919
+ lodash.each(json.modules, (moduleDef, moduleName) => {
43920
+ var node = lodash.get(currentMetanode, ["modules", moduleName]);
43921
+ if (node === undefined) {
43922
+ lodash.set(currentMetanode, ["modules", moduleName], moduleDef);
43923
+ }
43924
+ });
43925
+
43926
+ // paste uis
43927
+ lodash.each(json.ui, (uiDef, uiName) => {
43928
+ var ui = lodash.get(currentMetanode, ["ui", uiName]);
43929
+ if (ui === undefined) {
43930
+ lodash.set(currentMetanode, ["ui", uiName], uiDef);
43931
+ }
43932
+ });
43933
+ });
43934
+ }
43935
+
43936
+ return validPaste;
43810
43937
  }
43811
43938
 
43812
43939
  /**
@@ -43901,14 +44028,16 @@ class GraphModel {
43901
44028
 
43902
44029
  // add pkg dependencies (lib, types, etc) to app graph
43903
44030
  async addDependencies(pkg) {
43904
- for (const dependencyName of Object.keys(pkg.dependencies)) {
43905
- if (utils.isLibrary(dependencyName) || utils.isType(dependencyName)) {
43906
- console.log('[*] Adding dependency: ' + dependencyName);
43907
- // await this.e.loadPackage(dependencyName, dependencyVersion); // Is this better ?
43908
- await this.e.loadPackage(dependencyName); // or should we just load the last version ?
43909
- let pkg = this.e.packages[dependencyName];
43910
- this.data.root.libs[pkg.name] = pkg.version;
43911
- await this.addDependencies(pkg);
44031
+ if (pkg.dependencies) {
44032
+ for (const dependencyName of Object.keys(pkg.dependencies)) {
44033
+ if (utils.isLibrary(dependencyName) || utils.isType(dependencyName)) {
44034
+ console.log('[*] Adding dependency: ' + dependencyName);
44035
+ // await this.e.loadPackage(dependencyName, dependencyVersion); // Is this better ?
44036
+ await this.e.loadPackage(dependencyName); // or should we just load the last version ?
44037
+ let pkg = this.e.packages[dependencyName];
44038
+ this.data.root.libs[pkg.name] = pkg.version;
44039
+ await this.addDependencies(pkg);
44040
+ }
43912
44041
  }
43913
44042
  }
43914
44043
  }
@@ -63541,6 +63670,7 @@ var __vue_render__$4 = function() {
63541
63670
  return _c(
63542
63671
  "option",
63543
63672
  {
63673
+ key: typeName,
63544
63674
  domProps: {
63545
63675
  value: typeName,
63546
63676
  selected: typeName == _vm.selectedType
@@ -63594,7 +63724,7 @@ __vue_render__$4._withStripped = true;
63594
63724
  /* style */
63595
63725
  const __vue_inject_styles__$4 = function (inject) {
63596
63726
  if (!inject) return
63597
- inject("data-v-0750b83e_0", { source: "\n.list-types {\n max-height: 518px;\n overflow-y: auto;\n padding-top: 10px;\n}\n.list-types li {\n cursor: pointer;\n}\n.list-types li:hover {\n background-color: rgba(0,0,0,0.1);\n}\nspan.type-display {\n font-size: 16px;\n font-weight: bold;\n}\n.choose-type {\n font-weight: bold;\n font-size: 18px;\n}\n.bs3 {\n display: none!important;\n}\n", map: {"version":3,"sources":["/home/seb/dev/dualbox/editor/js/src/v/templates/editType.vue"],"names":[],"mappings":";AACA;IACA,iBAAA;IACA,gBAAA;IACA,iBAAA;AACA;AAEA;IACA,eAAA;AACA;AAEA;IACA,iCAAA;AACA;AAEA;IACA,eAAA;IACA,iBAAA;AACA;AAEA;IACA,iBAAA;IACA,eAAA;AACA;AAEA;IACA,uBAAA;AACA","file":"editType.vue","sourcesContent":["<style>\n .list-types {\n max-height: 518px;\n overflow-y: auto;\n padding-top: 10px;\n }\n\n .list-types li {\n cursor: pointer;\n }\n\n .list-types li:hover {\n background-color: rgba(0,0,0,0.1);\n }\n\n span.type-display {\n font-size: 16px;\n font-weight: bold;\n }\n\n .choose-type {\n font-weight: bold;\n font-size: 18px;\n }\n\n .bs3 {\n display: none!important;\n }\n</style>\n\n<template>\n <div style=\"width: 100%;\">\n <span class=\"d-inline-block\"><span class=\"type-display\">{{typePrefix}}</span></span>\n <select class=\"selectpicker d-inline-block\">\n <option disabled value='default' :selected=\"!selectedType || selectedType=='*'\" >Select a type</option>\n <option v-for=\"typeName in getMatchingTypes()\" :value=\"typeName\" :selected=\"typeName == selectedType\">{{typeName}}</option>\n </select>\n <span class=\"d-inline-block type-display\">{{typeSuffix}}</span>\n <button v-if=\"displayOKButton!==false\" class=\"btn btn-success d-inline-block\" @click=\"emitType\">Ok</button>\n <button v-if=\"displayResetButton!==false\" class=\"btn btn-outline-secondary d-inline-block\" @click=\"resetChanges\">Reset</button>\n <small v-if=\"showExtendedTypeMessage\" class=\"form-text text-muted\">\n You are building a complex type (types with Array or Map). Continue to select the '*' type until all star are resolved.\n </small>\n </div>\n</template>\n\n<script>\nimport _ from 'lodash-es';\n\n// including bootstrap-select doesn't work\n// made a PR for this: see https://github.com/snapappointments/bootstrap-select/issues/2374\nimport 'bootstrap-select/dist/css/bootstrap-select.min.css';\nimport 'bootstrap-select';\nif( $.fn.selectpicker ) {\n $.fn.selectpicker.Constructor.BootstrapVersion = '4';\n}\n\nexport default {\n name : \"edit-type\",\n props: [\n \"type\", // the dualbox type of the value\n \"displayOKButton\",\n \"displayResetButton\"\n ],\n data: function () {\n return {\n \"types\" : null,\n \"searchText\" : null,\n \"selectedType\": null,\n\n \"typePrefix\" : \"\",\n \"typeSuffix\" : \"\",\n \"showExtendedTypeMessage\": false,\n };\n },\n created: async function() {\n this.view = window.dualboxEditor.v;\n this.types = await this.getTypes(); // will reactively update type list when done\n this.initToTypeValue();\n },\n mounted: function() {\n var self = this;\n this.initSelectPicker();\n this.bindSelectPicker();\n },\n updated: function() {\n console.log('updated');\n this.refreshSelectPicker();\n },\n methods: {\n initToTypeValue() {\n if( this.type ) {\n var a = this.type.split('<');\n var fragment = a[ a.length -1 ];\n a = fragment.split('>');\n fragment = a[0];\n a = fragment.split(',');\n fragment = a[ a.length - 1 ];\n\n this.typePrefix = this.type.substr(0, this.type.indexOf(fragment));\n this.selectedType = fragment;\n this.typeSuffix = this.type.substr(this.type.indexOf(fragment) + fragment.length, this.type.length -1);\n }\n },\n\n isSelected(typeName) {\n return typeName == this.selectedType;\n },\n\n initSelectPicker: function() {\n this.selectDiv = $(this.$el).find('select.selectpicker');\n this.selectDiv.selectpicker({\n liveSearch: true,\n liveSearchPlaceholder: \"Search through types\",\n });\n },\n\n removeSelectPicler: function() {\n this.selectDiv.selectpicker('destroy');\n },\n\n unbindSelectPicker: function() {\n this.selectDiv.off('changed.bs.select');\n },\n\n bindSelectPicker: function() {\n this.selectDiv.on('changed.bs.select', (event, clickedIndex, isSelected, previousValue) => {\n var val = this.selectDiv.selectpicker('val');\n this.selectType( val );\n });\n },\n\n destroySelectPicker: function() {\n this.selectDiv.off('changed.bs.select');\n this.selectDiv.selectpicker('destroy');\n },\n\n resetSelectPicker: function() {\n this.unbindSelectPicker();\n this.selectDiv.selectpicker('val', 'default');\n this.bindSelectPicker();\n },\n\n refreshSelectPicker: function() {\n this.selectDiv.selectpicker('refresh');\n },\n\n sleep: function(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n },\n\n getTypes: async function() {\n var rootTypesPkg = await this.view.e.getRootTypes();\n var rootTypes = _.map(rootTypesPkg, pkg => _.get(pkg, \"dualbox.type.name\"));\n\n // Remove array and maps, insert them on top\n rootTypes = rootTypes.filter(n => n.toLowerCase()!=='array' && n.toLowerCase()!=='map');\n rootTypes.unshift(\"Array<*>\", \"Map<String, *>\");\n return rootTypes;\n },\n\n getMatchingTypes : function() {\n if( this.searchText ) {\n return _.filter(this.types, t => { return t.indexOf(this.searchText) !== -1; });\n }\n else {\n return this.types;\n }\n },\n\n selectType: function(typeName) {\n // We selected a star-type. Now, continue the selection to resolve the star\n if( typeName.indexOf('*') !== -1 ) {\n this.showExtendedTypeMessage = true;\n\n var prefix, suffix;\n [prefix, suffix] = typeName.split('*');\n this.typePrefix = this.typePrefix + prefix;\n this.typeSuffix = suffix + this.typeSuffix;\n\n // reset the select\n this.resetSelectPicker();\n }\n else {\n this.selectedType = this.typePrefix + typeName + this.typeSuffix;\n }\n },\n\n emitType: function(e) {\n this.$emit('edited', this.get())\n },\n\n get: function() {\n return this.selectedType ? this.selectedType.replace(/\\s/g, \"\") : undefined;\n },\n\n resetChanges: function() {\n this.typePrefix = \"\";\n this.typeSuffix = \"\";\n this.selectedType = null;\n this.showExtendedTypeMessage = false;\n this.searchText = \"\";\n this.resetSelectPicker();\n },\n }\n}\n</script>\n"]}, media: undefined });
63727
+ inject("data-v-b5dab45e_0", { source: "\n.list-types {\n max-height: 518px;\n overflow-y: auto;\n padding-top: 10px;\n}\n.list-types li {\n cursor: pointer;\n}\n.list-types li:hover {\n background-color: rgba(0,0,0,0.1);\n}\nspan.type-display {\n font-size: 16px;\n font-weight: bold;\n}\n.choose-type {\n font-weight: bold;\n font-size: 18px;\n}\n.bs3 {\n display: none!important;\n}\n", map: {"version":3,"sources":["/home/seb/dev/dualbox/editor/js/src/v/templates/editType.vue"],"names":[],"mappings":";AACA;IACA,iBAAA;IACA,gBAAA;IACA,iBAAA;AACA;AAEA;IACA,eAAA;AACA;AAEA;IACA,iCAAA;AACA;AAEA;IACA,eAAA;IACA,iBAAA;AACA;AAEA;IACA,iBAAA;IACA,eAAA;AACA;AAEA;IACA,uBAAA;AACA","file":"editType.vue","sourcesContent":["<style>\n .list-types {\n max-height: 518px;\n overflow-y: auto;\n padding-top: 10px;\n }\n\n .list-types li {\n cursor: pointer;\n }\n\n .list-types li:hover {\n background-color: rgba(0,0,0,0.1);\n }\n\n span.type-display {\n font-size: 16px;\n font-weight: bold;\n }\n\n .choose-type {\n font-weight: bold;\n font-size: 18px;\n }\n\n .bs3 {\n display: none!important;\n }\n</style>\n\n<template>\n <div style=\"width: 100%;\">\n <span class=\"d-inline-block\"><span class=\"type-display\">{{typePrefix}}</span></span>\n <select class=\"selectpicker d-inline-block\">\n <option disabled value='default' :selected=\"!selectedType || selectedType=='*'\" >Select a type</option>\n <option v-for=\"typeName in getMatchingTypes()\" :key=\"typeName\" :value=\"typeName\" :selected=\"typeName == selectedType\">{{typeName}}</option>\n </select>\n <span class=\"d-inline-block type-display\">{{typeSuffix}}</span>\n <button v-if=\"displayOKButton!==false\" class=\"btn btn-success d-inline-block\" @click=\"emitType\">Ok</button>\n <button v-if=\"displayResetButton!==false\" class=\"btn btn-outline-secondary d-inline-block\" @click=\"resetChanges\">Reset</button>\n <small v-if=\"showExtendedTypeMessage\" class=\"form-text text-muted\">\n You are building a complex type (types with Array or Map). Continue to select the '*' type until all star are resolved.\n </small>\n </div>\n</template>\n\n<script>\nimport _ from 'lodash-es';\n\n// including bootstrap-select doesn't work\n// made a PR for this: see https://github.com/snapappointments/bootstrap-select/issues/2374\nimport 'bootstrap-select/dist/css/bootstrap-select.min.css';\nimport 'bootstrap-select';\nif( $.fn.selectpicker ) {\n $.fn.selectpicker.Constructor.BootstrapVersion = '4';\n}\n\nexport default {\n name : \"edit-type\",\n props: [\n \"type\", // the dualbox type of the value\n \"displayOKButton\",\n \"displayResetButton\"\n ],\n data: function () {\n return {\n \"types\" : null,\n \"searchText\" : null,\n \"selectedType\": null,\n\n \"typePrefix\" : \"\",\n \"typeSuffix\" : \"\",\n \"showExtendedTypeMessage\": false,\n };\n },\n created: async function() {\n this.view = window.dualboxEditor.v;\n this.types = await this.getTypes(); // will reactively update type list when done\n this.initToTypeValue();\n },\n mounted: function() {\n var self = this;\n this.initSelectPicker();\n this.bindSelectPicker();\n },\n updated: function() {\n console.log('updated');\n this.refreshSelectPicker();\n },\n methods: {\n initToTypeValue() {\n if( this.type ) {\n var a = this.type.split('<');\n var fragment = a[ a.length -1 ];\n a = fragment.split('>');\n fragment = a[0];\n a = fragment.split(',');\n fragment = a[ a.length - 1 ];\n\n this.typePrefix = this.type.substr(0, this.type.indexOf(fragment));\n this.selectedType = fragment;\n this.typeSuffix = this.type.substr(this.type.indexOf(fragment) + fragment.length, this.type.length -1);\n }\n },\n\n isSelected(typeName) {\n return typeName == this.selectedType;\n },\n\n initSelectPicker: function() {\n this.selectDiv = $(this.$el).find('select.selectpicker');\n this.selectDiv.selectpicker({\n liveSearch: true,\n liveSearchPlaceholder: \"Search through types\",\n });\n },\n\n removeSelectPicler: function() {\n this.selectDiv.selectpicker('destroy');\n },\n\n unbindSelectPicker: function() {\n this.selectDiv.off('changed.bs.select');\n },\n\n bindSelectPicker: function() {\n this.selectDiv.on('changed.bs.select', (event, clickedIndex, isSelected, previousValue) => {\n var val = this.selectDiv.selectpicker('val');\n this.selectType( val );\n });\n },\n\n destroySelectPicker: function() {\n this.selectDiv.off('changed.bs.select');\n this.selectDiv.selectpicker('destroy');\n },\n\n resetSelectPicker: function() {\n this.unbindSelectPicker();\n this.selectDiv.selectpicker('val', 'default');\n this.bindSelectPicker();\n },\n\n refreshSelectPicker: function() {\n this.selectDiv.selectpicker('refresh');\n },\n\n sleep: function(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n },\n\n getTypes: async function() {\n var rootTypesPkg = await this.view.e.getRootTypes();\n var rootTypes = _.map(rootTypesPkg, pkg => _.get(pkg, \"dualbox.type.name\"));\n\n // Remove array and maps, insert them on top\n rootTypes = rootTypes.filter(n => n.toLowerCase()!=='array' && n.toLowerCase()!=='map');\n rootTypes.unshift(\"Array<*>\", \"Map<String, *>\");\n return rootTypes;\n },\n\n getMatchingTypes : function() {\n if( this.searchText ) {\n return _.filter(this.types, t => { return t.indexOf(this.searchText) !== -1; });\n }\n else {\n return this.types;\n }\n },\n\n selectType: function(typeName) {\n // We selected a star-type. Now, continue the selection to resolve the star\n if( typeName.indexOf('*') !== -1 ) {\n this.showExtendedTypeMessage = true;\n\n var prefix, suffix;\n [prefix, suffix] = typeName.split('*');\n this.typePrefix = this.typePrefix + prefix;\n this.typeSuffix = suffix + this.typeSuffix;\n\n // reset the select\n this.resetSelectPicker();\n }\n else {\n this.selectedType = this.typePrefix + typeName + this.typeSuffix;\n }\n },\n\n emitType: function(e) {\n this.$emit('edited', this.get())\n },\n\n get: function() {\n return this.selectedType ? this.selectedType.replace(/\\s/g, \"\") : undefined;\n },\n\n resetChanges: function() {\n this.typePrefix = \"\";\n this.typeSuffix = \"\";\n this.selectedType = null;\n this.showExtendedTypeMessage = false;\n this.searchText = \"\";\n this.resetSelectPicker();\n },\n }\n}\n</script>\n"]}, media: undefined });
63598
63728
 
63599
63729
  };
63600
63730
  /* scoped */
@@ -63638,7 +63768,7 @@ var script$5 = {
63638
63768
  return {
63639
63769
  error: null,
63640
63770
 
63641
- searchText: {}, // the search text, indexed by selectedType
63771
+ searchText: { "module": "", "ui": "" }, // the search text, indexed by selectedType
63642
63772
  results: {}, // dictionary of arrays, indexed by selectedType
63643
63773
  selectedResult: {}, // dictionary of string, indexed by selectedType
63644
63774
  selectedType: "module",
@@ -63648,6 +63778,7 @@ var script$5 = {
63648
63778
  },
63649
63779
  created: function() {
63650
63780
  this.view = window.dualboxEditor.v;
63781
+ this.searchModules();
63651
63782
  },
63652
63783
  updated: function() {
63653
63784
  console.log("AddNode updated !");
@@ -63722,6 +63853,9 @@ var script$5 = {
63722
63853
  let searchText = this.searchText[this.selectedType];
63723
63854
  this.selectedType = t;
63724
63855
  this.searchText[t] = searchText;
63856
+ if( t === "module" || t === "ui" ) {
63857
+ this.searchModules();
63858
+ }
63725
63859
  },
63726
63860
 
63727
63861
  addSelectedNode: async function(e) {
@@ -63824,17 +63958,17 @@ var script$5 = {
63824
63958
  searchModules: async function(e) {
63825
63959
  //var text = $(e.target).val();
63826
63960
  var text = this.searchText[this.selectedType];
63827
- try {
63961
+ //try {
63828
63962
  this.error = null;
63829
63963
  this.results[this.selectedType] = await this.view.e.search(
63830
63964
  text,
63831
63965
  this.selectedType
63832
63966
  );
63833
63967
  this.$forceUpdate();
63834
- } catch (e) {
63835
- this.error = e;
63836
- sweetalert2_all("error searching packages", e.reason, "error");
63837
- }
63968
+ //} catch (e) {
63969
+ // this.error = e;
63970
+ // swal("error searching packages", e, "error");
63971
+ //}
63838
63972
  },
63839
63973
 
63840
63974
  getSearchText: function() {
@@ -64077,76 +64211,55 @@ var __vue_render__$5 = function() {
64077
64211
  }
64078
64212
  },
64079
64213
  [
64080
- _c(
64081
- "div",
64082
- { staticClass: "row h100" },
64083
- [
64084
- _vm.getSearchText().length == 0
64085
- ? _c("div", { staticClass: "message-container" }, [
64086
- _vm._m(4)
64087
- ])
64088
- : _vm.getNbResults() == 0
64089
- ? _c("div", [_vm._m(5)])
64090
- : [
64091
- _c(
64092
- "div",
64093
- {
64094
- staticClass: "col-md-4 h100",
64095
- staticStyle: {
64096
- "padding-left": "0",
64097
- "padding-right": "0",
64098
- "overflow-y": "auto",
64099
- "overflow-x": "hidden"
64100
- }
64101
- },
64102
- [
64103
- _c("search-results", {
64104
- ref: "searchResults",
64105
- attrs: {
64106
- results: _vm.getResults(),
64107
- err: _vm.error
64108
- },
64109
- on: {
64110
- resultSelected: _vm.setSelectedResult
64111
- }
64112
- })
64113
- ],
64114
- 1
64115
- ),
64116
- _vm._v(" "),
64117
- _c(
64118
- "div",
64119
- {
64120
- staticClass: "col-md-8 h100",
64121
- staticStyle: {
64122
- "padding-left": "15px",
64123
- "padding-right": "0"
64124
- }
64125
- },
64126
- [
64127
- _vm.getSelectedResult()
64128
- ? _c("display-result", {
64129
- ref: "displayResults",
64130
- attrs: {
64131
- result: _vm.getSelectedResult()
64132
- }
64133
- })
64134
- : _c(
64135
- "div",
64136
- { staticClass: "message-container" },
64137
- [
64138
- _c("p", { staticClass: "message" }, [
64139
- _vm._v("Select a result to display")
64140
- ])
64141
- ]
64142
- )
64143
- ],
64144
- 1
64145
- )
64146
- ]
64147
- ],
64148
- 2
64149
- )
64214
+ _c("div", { staticClass: "row h100" }, [
64215
+ _c(
64216
+ "div",
64217
+ {
64218
+ staticClass: "col-md-4 h100",
64219
+ staticStyle: {
64220
+ "padding-left": "0",
64221
+ "padding-right": "0",
64222
+ "overflow-y": "auto",
64223
+ "overflow-x": "hidden"
64224
+ }
64225
+ },
64226
+ [
64227
+ _c("search-results", {
64228
+ ref: "searchResults",
64229
+ attrs: {
64230
+ results: _vm.getResults(),
64231
+ err: _vm.error
64232
+ },
64233
+ on: { resultSelected: _vm.setSelectedResult }
64234
+ })
64235
+ ],
64236
+ 1
64237
+ ),
64238
+ _vm._v(" "),
64239
+ _c(
64240
+ "div",
64241
+ {
64242
+ staticClass: "col-md-8 h100",
64243
+ staticStyle: {
64244
+ "padding-left": "15px",
64245
+ "padding-right": "0"
64246
+ }
64247
+ },
64248
+ [
64249
+ _vm.getSelectedResult()
64250
+ ? _c("display-result", {
64251
+ ref: "displayResults",
64252
+ attrs: { result: _vm.getSelectedResult() }
64253
+ })
64254
+ : _c("div", { staticClass: "message-container" }, [
64255
+ _c("p", { staticClass: "message" }, [
64256
+ _vm._v("Select a search result to display")
64257
+ ])
64258
+ ])
64259
+ ],
64260
+ 1
64261
+ )
64262
+ ])
64150
64263
  ]
64151
64264
  ),
64152
64265
  _vm._v(" "),
@@ -64173,7 +64286,7 @@ var __vue_render__$5 = function() {
64173
64286
  _vm._v("Add a new input")
64174
64287
  ]),
64175
64288
  _vm._v(" "),
64176
- _vm._m(6),
64289
+ _vm._m(4),
64177
64290
  _vm._v(" "),
64178
64291
  _c(
64179
64292
  "div",
@@ -64199,7 +64312,7 @@ var __vue_render__$5 = function() {
64199
64312
  1
64200
64313
  ),
64201
64314
  _vm._v(" "),
64202
- _vm._m(7)
64315
+ _vm._m(5)
64203
64316
  ]
64204
64317
  )
64205
64318
  ])
@@ -64229,7 +64342,7 @@ var __vue_render__$5 = function() {
64229
64342
  _vm._v("Add a new output")
64230
64343
  ]),
64231
64344
  _vm._v(" "),
64232
- _vm._m(8),
64345
+ _vm._m(6),
64233
64346
  _vm._v(" "),
64234
64347
  _c(
64235
64348
  "div",
@@ -64255,7 +64368,7 @@ var __vue_render__$5 = function() {
64255
64368
  1
64256
64369
  ),
64257
64370
  _vm._v(" "),
64258
- _vm._m(9)
64371
+ _vm._m(7)
64259
64372
  ]
64260
64373
  )
64261
64374
  ])
@@ -64287,7 +64400,7 @@ var __vue_render__$5 = function() {
64287
64400
  )
64288
64401
  ]),
64289
64402
  _vm._v(" "),
64290
- _vm._m(10),
64403
+ _vm._m(8),
64291
64404
  _vm._v(" "),
64292
64405
  _c("div", { staticClass: "form-group" }, [
64293
64406
  _c("label", { attrs: { for: "metanode-def" } }, [
@@ -64301,7 +64414,7 @@ var __vue_render__$5 = function() {
64301
64414
  })
64302
64415
  ]),
64303
64416
  _vm._v(" "),
64304
- _vm._m(11)
64417
+ _vm._m(9)
64305
64418
  ]
64306
64419
  )
64307
64420
  ])
@@ -64401,24 +64514,6 @@ var __vue_staticRenderFns__$5 = [
64401
64514
  [_c("span", { attrs: { "aria-hidden": "true" } }, [_vm._v("×")])]
64402
64515
  )
64403
64516
  },
64404
- function() {
64405
- var _vm = this;
64406
- var _h = _vm.$createElement;
64407
- var _c = _vm._self._c || _h;
64408
- return _c("p", { staticClass: "message" }, [
64409
- _c("i", { staticClass: "fas fa-search" }),
64410
- _vm._v(" Type something to search through the Dualbox library.")
64411
- ])
64412
- },
64413
- function() {
64414
- var _vm = this;
64415
- var _h = _vm.$createElement;
64416
- var _c = _vm._self._c || _h;
64417
- return _c("p", { staticClass: "message" }, [
64418
- _c("i", { staticClass: "fas fa-search" }),
64419
- _vm._v(" No results.")
64420
- ])
64421
- },
64422
64517
  function() {
64423
64518
  var _vm = this;
64424
64519
  var _h = _vm.$createElement;
@@ -64532,11 +64627,11 @@ __vue_render__$5._withStripped = true;
64532
64627
  /* style */
64533
64628
  const __vue_inject_styles__$5 = function (inject) {
64534
64629
  if (!inject) return
64535
- inject("data-v-4feaa5c6_0", { source: "\n.add-node-modal .modal-body[data-v-4feaa5c6] {\n height: 70vh;\n}\n.h100[data-v-4feaa5c6] {\n height: 100% !important;\n min-height: 100% !important;\n}\n.table-desc th[data-v-4feaa5c6],\n.table-desc td[data-v-4feaa5c6] {\n margin-right: 10px;\n}\n#add-node-modal .modal-dialog[data-v-4feaa5c6] {\n min-width: calc(100% - 40px) !important;\n height: calc(100% - 40px);\n margin: 20px;\n padding: 0;\n}\n#add-node-modal .modal-content[data-v-4feaa5c6] {\n height: auto;\n min-height: 100%;\n border-radius: 0;\n}\n.nav-item[data-v-4feaa5c6] {\n margin-left: 15px;\n margin-right: 15px;\n}\n.nav-link[data-v-4feaa5c6] {\n padding-left: 4px;\n padding-right: 4px;\n padding-bottom: 4px;\n padding-top: 4px;\n vertical-align: middle;\n position: relative;\n user-select: none;\n}\n.nav-link.active[data-v-4feaa5c6] {\n top: 0px;\n border-top-left-radius: 5px;\n border-bottom-left-radius: 5px;\n}\n.nav-link > span[data-v-4feaa5c6] {\n padding-left: 10px;\n padding-right: 10px;\n}\n.nav-link.active > span[data-v-4feaa5c6] {\n color: black;\n}\n.input-search[data-v-4feaa5c6] {\n display: none;\n width: 1px;\n height: 24px;\n box-shadow: none !important;\n border-color: rgba(0, 0, 0, 0.3) !important;\n}\n.nav-link.active .input-search[data-v-4feaa5c6] {\n display: inline-block;\n border-top-left-radius: 0px;\n border-bottom-left-radius: 0px;\n transition: width 1s;\n width: 200px;\n}\n.nav-compute > a.active > input[data-v-4feaa5c6] {\n background-color: rgba(0, 0, 0, 0.3) !important;\n}\n.nav-compute > a.active[data-v-4feaa5c6],\n.nav-compute > a.active > input[data-v-4feaa5c6] {\n background-color: rgba(0, 0, 0, 0.3) !important;\n color: rgba(255, 255, 255, 0.5);\n}\n.nav-compute > a.active > .input-search[data-v-4feaa5c6]::placeholder {\n color: rgba(255, 255, 255, 0.5);\n}\n.nav-uis > a.active[data-v-4feaa5c6],\n.nav-uis > a.active > input[data-v-4feaa5c6] {\n background-color: #bff2ca !important;\n}\n.nav-inputs > a.active[data-v-4feaa5c6] {\n background-color: #f5d76e !important;\n}\n.nav-compute > a.active > input[data-v-4feaa5c6] {\n background-color: rgba(0, 0, 0, 0.8);\n}\n.nav-outputs > a.active[data-v-4feaa5c6],\n.nav-outputs > a.active > input[data-v-4feaa5c6] {\n background-color: #ffb3a7 !important;\n}\n.message-container[data-v-4feaa5c6] {\n position: relative;\n width: 100%;\n height: 100%;\n}\n.message[data-v-4feaa5c6] {\n position: absolute;\n top: 50%;\n transform: translateY(-80%);\n left: 0;\n right: 0;\n opacity: 0.6;\n text-align: center;\n}\n.tab-content[data-v-4feaa5c6],\n.tab-pane[data-v-4feaa5c6] {\n width: 100%;\n height: 100%;\n}\nlabel[data-v-4feaa5c6] {\n font-size: 20px;\n margin-top: 20px;\n}\nfieldset[data-v-4feaa5c6] {\n border: 1px solid rgba(0, 0, 0, 0.1);\n margin: 10px;\n padding: 20px;\n min-width: 600px;\n}\n", map: {"version":3,"sources":["/home/seb/dev/dualbox/editor/js/src/v/templates/addNode.vue"],"names":[],"mappings":";AACA;IACA,YAAA;AACA;AAEA;IACA,uBAAA;IACA,2BAAA;AACA;AAEA;;IAEA,kBAAA;AACA;AAEA;IACA,uCAAA;IACA,yBAAA;IACA,YAAA;IACA,UAAA;AACA;AAEA;IACA,YAAA;IACA,gBAAA;IACA,gBAAA;AACA;AAEA;IACA,iBAAA;IACA,kBAAA;AACA;AAEA;IACA,iBAAA;IACA,kBAAA;IACA,mBAAA;IACA,gBAAA;IACA,sBAAA;IACA,kBAAA;IACA,iBAAA;AACA;AAEA;IACA,QAAA;IACA,2BAAA;IACA,8BAAA;AACA;AAEA;IACA,kBAAA;IACA,mBAAA;AACA;AAEA;IACA,YAAA;AACA;AAEA;IACA,aAAA;IACA,UAAA;IACA,YAAA;IACA,2BAAA;IACA,2CAAA;AACA;AAEA;IACA,qBAAA;IACA,2BAAA;IACA,8BAAA;IACA,oBAAA;IACA,YAAA;AACA;AACA;IACA,+CAAA;AACA;AAEA;;IAEA,+CAAA;IACA,+BAAA;AACA;AAEA;IACA,+BAAA;AACA;AAEA;;IAEA,oCAAA;AACA;AAEA;IACA,oCAAA;AACA;AAEA;IACA,oCAAA;AACA;AAEA;;IAEA,oCAAA;AACA;AAEA;IACA,kBAAA;IACA,WAAA;IACA,YAAA;AACA;AAEA;IACA,kBAAA;IACA,QAAA;IACA,2BAAA;IACA,OAAA;IACA,QAAA;IACA,YAAA;IACA,kBAAA;AACA;AAEA;;IAEA,WAAA;IACA,YAAA;AACA;AAEA;IACA,eAAA;IACA,gBAAA;AACA;AAEA;IACA,oCAAA;IACA,YAAA;IACA,aAAA;IACA,gBAAA;AACA","file":"addNode.vue","sourcesContent":["<style scoped>\n.add-node-modal .modal-body {\n height: 70vh;\n}\n\n.h100 {\n height: 100% !important;\n min-height: 100% !important;\n}\n\n.table-desc th,\n.table-desc td {\n margin-right: 10px;\n}\n\n#add-node-modal .modal-dialog {\n min-width: calc(100% - 40px) !important;\n height: calc(100% - 40px);\n margin: 20px;\n padding: 0;\n}\n\n#add-node-modal .modal-content {\n height: auto;\n min-height: 100%;\n border-radius: 0;\n}\n\n.nav-item {\n margin-left: 15px;\n margin-right: 15px;\n}\n\n.nav-link {\n padding-left: 4px;\n padding-right: 4px;\n padding-bottom: 4px;\n padding-top: 4px;\n vertical-align: middle;\n position: relative;\n user-select: none;\n}\n\n.nav-link.active {\n top: 0px;\n border-top-left-radius: 5px;\n border-bottom-left-radius: 5px;\n}\n\n.nav-link > span {\n padding-left: 10px;\n padding-right: 10px;\n}\n\n.nav-link.active > span {\n color: black;\n}\n\n.input-search {\n display: none;\n width: 1px;\n height: 24px;\n box-shadow: none !important;\n border-color: rgba(0, 0, 0, 0.3) !important;\n}\n\n.nav-link.active .input-search {\n display: inline-block;\n border-top-left-radius: 0px;\n border-bottom-left-radius: 0px;\n transition: width 1s;\n width: 200px;\n}\n.nav-compute > a.active > input {\n background-color: rgba(0, 0, 0, 0.3) !important;\n}\n\n.nav-compute > a.active,\n.nav-compute > a.active > input {\n background-color: rgba(0, 0, 0, 0.3) !important;\n color: rgba(255, 255, 255, 0.5);\n}\n\n.nav-compute > a.active > .input-search::placeholder {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.nav-uis > a.active,\n.nav-uis > a.active > input {\n background-color: #bff2ca !important;\n}\n\n.nav-inputs > a.active {\n background-color: #f5d76e !important;\n}\n\n.nav-compute > a.active > input {\n background-color: rgba(0, 0, 0, 0.8);\n}\n\n.nav-outputs > a.active,\n.nav-outputs > a.active > input {\n background-color: #ffb3a7 !important;\n}\n\n.message-container {\n position: relative;\n width: 100%;\n height: 100%;\n}\n\n.message {\n position: absolute;\n top: 50%;\n transform: translateY(-80%);\n left: 0;\n right: 0;\n opacity: 0.6;\n text-align: center;\n}\n\n.tab-content,\n.tab-pane {\n width: 100%;\n height: 100%;\n}\n\nlabel {\n font-size: 20px;\n margin-top: 20px;\n}\n\nfieldset {\n border: 1px solid rgba(0, 0, 0, 0.1);\n margin: 10px;\n padding: 20px;\n min-width: 600px;\n}\n</style>\n\n<template>\n <div class=\"modal fade add-node-modal\" :class=\"{ 'hide' : !display }\" id=\"add-node-modal\" tabindex=\"-1\" role=\"dialog\">\n <div class=\"modal-dialog\" role=\"document\">\n <div class=\"modal-content h100\">\n <div class=\"modal-header\">\n <h5 class=\"modal-title\">Add Box</h5>\n <ul class=\"nav nav-pills mr-auto ml-2\">\n <li class=\"nav-item nav-compute\">\n <a class=\"nav-link active\" href=\"#add-module-or-ui\" data-toggle=\"pill\" @click=\"selectType('module')\" draggable=\"false\">\n <span>Compute</span>\n <input class=\"form-control input-sm input-search search-modules\" type=\"text\" placeholder=\"Search modules...\" id=\"search-modules\" v-model=\"searchText['module']\" @input=\"searchModules\" autofocus />\n </a>\n </li>\n <li class=\"nav-item nav-uis\">\n <a class=\"nav-link\" href=\"#add-module-or-ui\" data-toggle=\"pill\" @click=\"selectType('ui')\" draggable=\"false\">\n <span>UI</span>\n <input class=\"form-control input-sm input-search search-ui\" type=\"text\" placeholder=\"Search UIs...\" id=\"search-uis\" @input=\"searchModules\" v-model=\"searchText['ui']\" autofocus />\n </a>\n </li>\n <li class=\"nav-item nav-inputs\" @click=\"selectType('input')\">\n <a class=\"nav-link\" href=\"#add-input\" data-toggle=\"pill\" draggable=\"false\"><span>Input</span></a>\n </li>\n <li class=\"nav-item nav-outputs\" @click=\"selectType('output')\">\n <a class=\"nav-link\" href=\"#add-output\" data-toggle=\"pill\" draggable=\"false\"><span>Output</span></a>\n </li>\n <li class=\"nav-item nav-outputs\" @click=\"selectType('metanode')\">\n <a class=\"nav-link\" href=\"#add-metanode\" data-toggle=\"pill\" draggable=\"false\"><span>Metabox</span></a>\n </li>\n </ul>\n\n <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div class=\"modal-body\">\n <div class=\"container-fluid h100\">\n\n <div class=\"tab-content\">\n <div class=\"tab-pane active\" id=\"add-module-or-ui\" role=\"tabpanel\" aria-labelledby=\"add-module-or-ui-tab\">\n <div class=\"row h100\">\n <div v-if=\"getSearchText().length == 0\" class=\"message-container\">\n <p class=\"message\"><i class=\"fas fa-search\"></i> Type something to search through the Dualbox library.</p>\n </div>\n <div v-else-if=\"getNbResults() == 0\">\n <p class=\"message\"><i class=\"fas fa-search\"></i> No results.</p>\n </div>\n <template v-else>\n <div class=\"col-md-4 h100\" style=\"padding-left: 0; padding-right: 0; overflow-y: auto; overflow-x: hidden;\">\n <search-results ref=\"searchResults\" :results=\"getResults()\" :err=\"error\" @resultSelected=\"setSelectedResult\"></search-results>\n </div>\n <div class=\"col-md-8 h100\" style=\"padding-left: 15px; padding-right: 0;\">\n <display-result ref=\"displayResults\" v-if=\"getSelectedResult()\" :result=\"getSelectedResult()\"></display-result>\n <div v-else class=\"message-container\">\n <p class=\"message\">Select a result to display</p>\n </div>\n </div>\n </template>\n </div>\n </div>\n <div class=\"tab-pane\" id=\"add-input\" role=\"tabpanel\" aria-labelledby=\"add-input-tab\">\n <div class=\"row\">\n <fieldset class=\"mx-auto\" style=\"margin-top: 50px;\">\n <h2 class=\"mt-2\">Add a new input</h2>\n\n <div class=\"form-group\">\n <label for=\"input-name\">Input name:</label>\n <input type=\"text\" class=\"form-control input-name\" id=\"input-name\" placeholder=\"Enter name...\" autofocus required>\n </div>\n\n <div class=\"form-group\">\n <label for=\"input-name\">Input type:</label>\n <input style=\"display: none;\" required />\n <edit-type ref=\"inputType\" :type=\"inputType\" :displayOKButton=\"false\"></edit-type>\n </div>\n\n <div class=\"form-group\">\n <label for=\"input-description\">Input description:</label>\n <textarea class=\"input-description\" rows=4 style=\"width: 100%;\" placeholder=\"Enter description\" autofocus>\n </textarea>\n </div>\n </fieldset>\n </div>\n </div>\n <div class=\"tab-pane\" id=\"add-output\" role=\"tabpanel\" aria-labelledby=\"add-output-tab\">\n <div class=\"row\">\n <fieldset class=\"mx-auto\" style=\"margin-top: 50px;\">\n <h2 class=\"mt-2\">Add a new output</h2>\n\n <div class=\"form-group\">\n <label for=\"output-name\">Output name:</label>\n <input type=\"text\" class=\"form-control output-name\" id=\"output-name\" placeholder=\"Enter name...\" autofocus required>\n </div>\n\n <div class=\"form-group\">\n <label for=\"output-name\">Output type:</label>\n <input style=\"display: none;\" required />\n <edit-type ref=\"outputType\" :type=\"outputType\" :displayOKButton=\"false\"></edit-type>\n </div>\n\n <div class=\"form-group\">\n <label for=\"output-description\">Output description:</label>\n <textarea class=\"output-description\" rows=4 style=\"width: 100%;\" placeholder=\"Enter description\" autofocus>\n </textarea>\n </div>\n </fieldset>\n </div>\n </div>\n <div class=\"tab-pane\" id=\"add-metanode\" role=\"tabpanel\" aria-labelledby=\"add-metanode-tab\">\n <div class=\"row\">\n <fieldset class=\"mx-auto\" style=\"margin-top: 50px;\">\n <h2 class=\"mt-2\">Add a new Metabox\n <!--<button class=\"btn btn-outline-secondary float-right\" @click=\"importMetabox\"><i class=\"fas fa-upload\"></i></button>-->\n </h2>\n\n <div class=\"form-group\">\n <label for=\"metanode-name\">Metabox name:</label>\n <input type=\"text\" class=\"form-control metanode-name\" id=\"metanode-name\" placeholder=\"Enter name...\" autofocus required>\n </div>\n\n <div class=\"form-group\">\n <label for=\"metanode-def\">Metabox definition (optional):</label>\n <input type=\"file\" class=\"form-control-file\" id=\"metanode-def\" @change=\"onMetaboxFileSelect\">\n </div>\n\n <div class=\"form-group\">\n <label for=\"metanode-description\">Metabox description:</label>\n <textarea class=\"metanode-description\" rows=4 style=\"width: 100%;\" placeholder=\"Enter description\" autofocus>\n </textarea>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">Close</button>\n <button type=\"button\" class=\"btn btn-primary add-node-btn\" @click=\"addSelectedNode\">Add node</button>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script>\nimport _ from \"lodash-es\";\nimport swal from \"sweetalert2\";\nimport SearchResultsVue from \"./searchResults.vue\";\nimport DisplayResultVue from \"./displayResult.vue\";\nimport EditTypeVue from \"./editType.vue\";\n\nexport default {\n props: [\n \"display\", // original state (true = show, false=hide) of the modal\n \"mousePosition\" // mouse position when this panel was opened\n ],\n components: {\n \"search-results\": SearchResultsVue,\n \"display-result\": DisplayResultVue,\n \"edit-type\": EditTypeVue\n },\n data: function() {\n return {\n error: null,\n\n searchText: {}, // the search text, indexed by selectedType\n results: {}, // dictionary of arrays, indexed by selectedType\n selectedResult: {}, // dictionary of string, indexed by selectedType\n selectedType: \"module\",\n inputType: \"*\",\n outputType: \"*\"\n };\n },\n created: function() {\n this.view = window.dualboxEditor.v;\n },\n updated: function() {\n console.log(\"AddNode updated !\");\n this.updateVisibility();\n this.setSearchFocus();\n\n if (!this.display) {\n // make sure the modal disappeared\n $(this.$el).modal(\"hide\");\n $(\"body\").removeClass(\"modal-open\");\n $(\".modal-backdrop\").remove();\n }\n },\n mounted: function() {\n var self = this;\n this.updateVisibility(); // to init our modal\n\n $(this.$el).on(\"shown.bs.modal\", () => {\n this.setSearchFocus();\n });\n $(this.$el).on(\"hide.bs.modal\", () => {\n self.$emit(\"closed\");\n });\n\n $(this.$el)\n .find('[data-toggle=\"pill\"]')\n .on(\"shown.bs.tab\", e => {\n this.setSearchFocus();\n var selectedResult = this.getSelectedResult();\n if (selectedResult) {\n this.$refs.searchResults.selected = selectedResult.name;\n }\n });\n },\n methods: {\n updateVisibility: function() {\n if (this.display) {\n $(this.$el).modal(\"show\");\n } else {\n $(this.$el).modal(\"hide\");\n }\n },\n\n closeModal: function() {\n // don't mutate prop directly, mute the parent\n // this.display = false;\n\n return new Promise((resolve, reject) => {\n this.$emit(\"closed\");\n\n let forceCloseModal = () => {\n if (!this.display) {\n resolve();\n } else {\n this.$emit(\"closed\");\n setTimeout(forceCloseModal, 100);\n }\n };\n\n this.$nextTick(forceCloseModal);\n });\n },\n\n setSearchFocus: function() {\n $(this.$el)\n .find(\".nav-link.active\")\n .find(\"input\")\n .focus();\n },\n\n selectType(t) {\n let searchText = this.searchText[this.selectedType];\n this.selectedType = t;\n this.searchText[t] = searchText;\n },\n\n addSelectedNode: async function(e) {\n switch (this.selectedType) {\n case \"module\":\n case \"ui\":\n var selectedNode = $(\".node-result.selected\")[0];\n if (selectedNode) {\n await this.closeModal();\n // add node to the controller\n var packageName = $(selectedNode).data(\"package\");\n this.view.c.addNewBox(packageName, this.mousePosition);\n } else {\n swal(\"Please select a node\", \"\", \"error\");\n return;\n }\n break;\n\n case \"input\":\n var type = this.$refs.inputType.get();\n var name = $(this.$el)\n .find(\".input-name\")\n .val();\n var desc = $(this.$el)\n .find(\".input-description\")\n .val();\n if (type !== undefined && name) {\n await this.closeModal();\n this.view.c.addInput(\n name,\n type,\n desc,\n this.mousePosition\n );\n // reset fields\n this.$refs.inputType.resetChanges();\n $(this.$el)\n .find(\".input-name\")\n .val(\"\");\n $(this.$el)\n .find(\".input-description\")\n .val(\"\");\n } else {\n swal(\"Please complete all fields\", \"\", \"error\");\n return;\n }\n break;\n\n case \"output\":\n var name = $(this.$el)\n .find(\".output-name\")\n .val();\n var type = this.$refs.outputType.get();\n var desc = $(this.$el)\n .find(\".output-description\")\n .val();\n if (type !== undefined && name) {\n await this.closeModal();\n this.view.c.addOutput(\n name,\n type,\n desc,\n this.mousePosition\n );\n // reset fields\n this.$refs.outputType.resetChanges();\n $(this.$el)\n .find(\".output-name\")\n .val(\"\");\n $(this.$el)\n .find(\".output-description\")\n .val(\"\");\n } else {\n swal(\"Please complete all fields\", \"\", \"error\");\n return;\n }\n break;\n\n case \"metanode\":\n var name = $(this.$el)\n .find(\".metanode-name\")\n .val();\n var desc = $(this.$el)\n .find(\".metanode-description\")\n .val();\n if (!name) {\n swal(\"Please complete name field\", \"\", \"error\");\n return;\n }\n\n var json = this.metanodeJSON || {};\n json.description = desc;\n\n await this.closeModal();\n this.view.c.addNewMetabox(name, json, this.mousePosition);\n default:\n }\n },\n\n searchModules: async function(e) {\n //var text = $(e.target).val();\n var text = this.searchText[this.selectedType];\n try {\n this.error = null;\n this.results[this.selectedType] = await this.view.e.search(\n text,\n this.selectedType\n );\n this.$forceUpdate();\n } catch (e) {\n this.error = e;\n swal(\"error searching packages\", e.reason, \"error\");\n }\n },\n\n getSearchText: function() {\n return this.searchText[this.selectedType] || \"\";\n },\n\n setSelectedResult: function(packageName) {\n // first, find the selected result in the list\n var selectedPackage = null;\n _.each(this.getResults(), r => {\n if (r.name == packageName) {\n selectedPackage = r;\n return false;\n }\n });\n\n if (!selectedPackage) {\n throw \"Could not find package named \" + packageName;\n }\n\n this.selectedResult[this.selectedType] = selectedPackage;\n this.$forceUpdate();\n },\n\n getSelectedResult: function() {\n return this.selectedResult[this.selectedType];\n },\n\n getResults: function() {\n return this.results[this.selectedType] || null;\n },\n\n getNbResults: function() {\n var results = this.getResults();\n return results && results.length ? results.length : 0;\n },\n\n onMetaboxFileSelect: function(e) {\n var file = e.target.files[0];\n const reader = new FileReader();\n reader.onload = e => {\n this.metanodeJSON = JSON.parse(e.target.result);\n if (this.metanodeJSON.description) {\n $(this.$el)\n .find(\".metanode-description\")\n .val(this.metanodeJSON.description);\n }\n };\n reader.readAsText(file);\n }\n }\n};\n</script>\n\n"]}, media: undefined });
64630
+ inject("data-v-095049aa_0", { source: "\n.add-node-modal .modal-body[data-v-095049aa] {\n height: 70vh;\n}\n.h100[data-v-095049aa] {\n height: 100% !important;\n min-height: 100% !important;\n}\n.table-desc th[data-v-095049aa],\n.table-desc td[data-v-095049aa] {\n margin-right: 10px;\n}\n#add-node-modal .modal-dialog[data-v-095049aa] {\n min-width: calc(100% - 40px) !important;\n height: calc(100% - 40px);\n margin: 20px;\n padding: 0;\n}\n#add-node-modal .modal-content[data-v-095049aa] {\n height: auto;\n min-height: 100%;\n border-radius: 0;\n}\n.nav-item[data-v-095049aa] {\n margin-left: 15px;\n margin-right: 15px;\n}\n.nav-link[data-v-095049aa] {\n padding-left: 4px;\n padding-right: 4px;\n padding-bottom: 4px;\n padding-top: 4px;\n vertical-align: middle;\n position: relative;\n user-select: none;\n}\n.nav-link.active[data-v-095049aa] {\n top: 0px;\n border-top-left-radius: 5px;\n border-bottom-left-radius: 5px;\n}\n.nav-link > span[data-v-095049aa] {\n padding-left: 10px;\n padding-right: 10px;\n}\n.nav-link.active > span[data-v-095049aa] {\n color: black;\n}\n.input-search[data-v-095049aa] {\n display: none;\n width: 1px;\n height: 24px;\n box-shadow: none !important;\n border-color: rgba(0, 0, 0, 0.3) !important;\n}\n.nav-link.active .input-search[data-v-095049aa] {\n display: inline-block;\n border-top-left-radius: 0px;\n border-bottom-left-radius: 0px;\n transition: width 1s;\n width: 200px;\n}\n.nav-compute > a.active > input[data-v-095049aa] {\n background-color: rgba(0, 0, 0, 0.3) !important;\n}\n.nav-compute > a.active[data-v-095049aa],\n.nav-compute > a.active > input[data-v-095049aa] {\n background-color: rgba(0, 0, 0, 0.3) !important;\n color: rgba(255, 255, 255, 0.5);\n}\n.nav-compute > a.active > .input-search[data-v-095049aa]::placeholder {\n color: rgba(255, 255, 255, 0.5);\n}\n.nav-uis > a.active[data-v-095049aa],\n.nav-uis > a.active > input[data-v-095049aa] {\n background-color: #bff2ca !important;\n}\n.nav-inputs > a.active[data-v-095049aa] {\n background-color: #f5d76e !important;\n}\n.nav-compute > a.active > input[data-v-095049aa] {\n background-color: rgba(0, 0, 0, 0.8);\n}\n.nav-outputs > a.active[data-v-095049aa],\n.nav-outputs > a.active > input[data-v-095049aa] {\n background-color: #ffb3a7 !important;\n}\n.message-container[data-v-095049aa] {\n position: relative;\n width: 100%;\n height: 100%;\n}\n.message[data-v-095049aa] {\n position: absolute;\n top: 50%;\n transform: translateY(-80%);\n left: 0;\n right: 0;\n opacity: 0.6;\n text-align: center;\n}\n.tab-content[data-v-095049aa],\n.tab-pane[data-v-095049aa] {\n width: 100%;\n height: 100%;\n}\nlabel[data-v-095049aa] {\n font-size: 20px;\n margin-top: 20px;\n}\nfieldset[data-v-095049aa] {\n border: 1px solid rgba(0, 0, 0, 0.1);\n margin: 10px;\n padding: 20px;\n min-width: 600px;\n}\n", map: {"version":3,"sources":["/home/seb/dev/dualbox/editor/js/src/v/templates/addNode.vue"],"names":[],"mappings":";AACA;IACA,YAAA;AACA;AAEA;IACA,uBAAA;IACA,2BAAA;AACA;AAEA;;IAEA,kBAAA;AACA;AAEA;IACA,uCAAA;IACA,yBAAA;IACA,YAAA;IACA,UAAA;AACA;AAEA;IACA,YAAA;IACA,gBAAA;IACA,gBAAA;AACA;AAEA;IACA,iBAAA;IACA,kBAAA;AACA;AAEA;IACA,iBAAA;IACA,kBAAA;IACA,mBAAA;IACA,gBAAA;IACA,sBAAA;IACA,kBAAA;IACA,iBAAA;AACA;AAEA;IACA,QAAA;IACA,2BAAA;IACA,8BAAA;AACA;AAEA;IACA,kBAAA;IACA,mBAAA;AACA;AAEA;IACA,YAAA;AACA;AAEA;IACA,aAAA;IACA,UAAA;IACA,YAAA;IACA,2BAAA;IACA,2CAAA;AACA;AAEA;IACA,qBAAA;IACA,2BAAA;IACA,8BAAA;IACA,oBAAA;IACA,YAAA;AACA;AACA;IACA,+CAAA;AACA;AAEA;;IAEA,+CAAA;IACA,+BAAA;AACA;AAEA;IACA,+BAAA;AACA;AAEA;;IAEA,oCAAA;AACA;AAEA;IACA,oCAAA;AACA;AAEA;IACA,oCAAA;AACA;AAEA;;IAEA,oCAAA;AACA;AAEA;IACA,kBAAA;IACA,WAAA;IACA,YAAA;AACA;AAEA;IACA,kBAAA;IACA,QAAA;IACA,2BAAA;IACA,OAAA;IACA,QAAA;IACA,YAAA;IACA,kBAAA;AACA;AAEA;;IAEA,WAAA;IACA,YAAA;AACA;AAEA;IACA,eAAA;IACA,gBAAA;AACA;AAEA;IACA,oCAAA;IACA,YAAA;IACA,aAAA;IACA,gBAAA;AACA","file":"addNode.vue","sourcesContent":["<style scoped>\n.add-node-modal .modal-body {\n height: 70vh;\n}\n\n.h100 {\n height: 100% !important;\n min-height: 100% !important;\n}\n\n.table-desc th,\n.table-desc td {\n margin-right: 10px;\n}\n\n#add-node-modal .modal-dialog {\n min-width: calc(100% - 40px) !important;\n height: calc(100% - 40px);\n margin: 20px;\n padding: 0;\n}\n\n#add-node-modal .modal-content {\n height: auto;\n min-height: 100%;\n border-radius: 0;\n}\n\n.nav-item {\n margin-left: 15px;\n margin-right: 15px;\n}\n\n.nav-link {\n padding-left: 4px;\n padding-right: 4px;\n padding-bottom: 4px;\n padding-top: 4px;\n vertical-align: middle;\n position: relative;\n user-select: none;\n}\n\n.nav-link.active {\n top: 0px;\n border-top-left-radius: 5px;\n border-bottom-left-radius: 5px;\n}\n\n.nav-link > span {\n padding-left: 10px;\n padding-right: 10px;\n}\n\n.nav-link.active > span {\n color: black;\n}\n\n.input-search {\n display: none;\n width: 1px;\n height: 24px;\n box-shadow: none !important;\n border-color: rgba(0, 0, 0, 0.3) !important;\n}\n\n.nav-link.active .input-search {\n display: inline-block;\n border-top-left-radius: 0px;\n border-bottom-left-radius: 0px;\n transition: width 1s;\n width: 200px;\n}\n.nav-compute > a.active > input {\n background-color: rgba(0, 0, 0, 0.3) !important;\n}\n\n.nav-compute > a.active,\n.nav-compute > a.active > input {\n background-color: rgba(0, 0, 0, 0.3) !important;\n color: rgba(255, 255, 255, 0.5);\n}\n\n.nav-compute > a.active > .input-search::placeholder {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.nav-uis > a.active,\n.nav-uis > a.active > input {\n background-color: #bff2ca !important;\n}\n\n.nav-inputs > a.active {\n background-color: #f5d76e !important;\n}\n\n.nav-compute > a.active > input {\n background-color: rgba(0, 0, 0, 0.8);\n}\n\n.nav-outputs > a.active,\n.nav-outputs > a.active > input {\n background-color: #ffb3a7 !important;\n}\n\n.message-container {\n position: relative;\n width: 100%;\n height: 100%;\n}\n\n.message {\n position: absolute;\n top: 50%;\n transform: translateY(-80%);\n left: 0;\n right: 0;\n opacity: 0.6;\n text-align: center;\n}\n\n.tab-content,\n.tab-pane {\n width: 100%;\n height: 100%;\n}\n\nlabel {\n font-size: 20px;\n margin-top: 20px;\n}\n\nfieldset {\n border: 1px solid rgba(0, 0, 0, 0.1);\n margin: 10px;\n padding: 20px;\n min-width: 600px;\n}\n</style>\n\n<template>\n <div class=\"modal fade add-node-modal\" :class=\"{ 'hide' : !display }\" id=\"add-node-modal\" tabindex=\"-1\" role=\"dialog\">\n <div class=\"modal-dialog\" role=\"document\">\n <div class=\"modal-content h100\">\n <div class=\"modal-header\">\n <h5 class=\"modal-title\">Add Box</h5>\n <ul class=\"nav nav-pills mr-auto ml-2\">\n <li class=\"nav-item nav-compute\">\n <a class=\"nav-link active\" href=\"#add-module-or-ui\" data-toggle=\"pill\" @click=\"selectType('module')\" draggable=\"false\">\n <span>Compute</span>\n <input class=\"form-control input-sm input-search search-modules\" type=\"text\" placeholder=\"Search modules...\" id=\"search-modules\" v-model=\"searchText['module']\" @input=\"searchModules\" autofocus />\n </a>\n </li>\n <li class=\"nav-item nav-uis\">\n <a class=\"nav-link\" href=\"#add-module-or-ui\" data-toggle=\"pill\" @click=\"selectType('ui')\" draggable=\"false\">\n <span>UI</span>\n <input class=\"form-control input-sm input-search search-ui\" type=\"text\" placeholder=\"Search UIs...\" id=\"search-uis\" @input=\"searchModules\" v-model=\"searchText['ui']\" autofocus />\n </a>\n </li>\n <li class=\"nav-item nav-inputs\" @click=\"selectType('input')\">\n <a class=\"nav-link\" href=\"#add-input\" data-toggle=\"pill\" draggable=\"false\"><span>Input</span></a>\n </li>\n <li class=\"nav-item nav-outputs\" @click=\"selectType('output')\">\n <a class=\"nav-link\" href=\"#add-output\" data-toggle=\"pill\" draggable=\"false\"><span>Output</span></a>\n </li>\n <li class=\"nav-item nav-outputs\" @click=\"selectType('metanode')\">\n <a class=\"nav-link\" href=\"#add-metanode\" data-toggle=\"pill\" draggable=\"false\"><span>Metabox</span></a>\n </li>\n </ul>\n\n <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div class=\"modal-body\">\n <div class=\"container-fluid h100\">\n\n <div class=\"tab-content\">\n <div class=\"tab-pane active\" id=\"add-module-or-ui\" role=\"tabpanel\" aria-labelledby=\"add-module-or-ui-tab\">\n <div class=\"row h100\">\n <div class=\"col-md-4 h100\" style=\"padding-left: 0; padding-right: 0; overflow-y: auto; overflow-x: hidden;\">\n <search-results ref=\"searchResults\" :results=\"getResults()\" :err=\"error\" @resultSelected=\"setSelectedResult\"></search-results>\n </div>\n <div class=\"col-md-8 h100\" style=\"padding-left: 15px; padding-right: 0;\">\n <display-result ref=\"displayResults\" v-if=\"getSelectedResult()\" :result=\"getSelectedResult()\"></display-result>\n <div v-else class=\"message-container\">\n <p class=\"message\">Select a search result to display</p>\n </div>\n </div>\n </div>\n </div>\n <div class=\"tab-pane\" id=\"add-input\" role=\"tabpanel\" aria-labelledby=\"add-input-tab\">\n <div class=\"row\">\n <fieldset class=\"mx-auto\" style=\"margin-top: 50px;\">\n <h2 class=\"mt-2\">Add a new input</h2>\n\n <div class=\"form-group\">\n <label for=\"input-name\">Input name:</label>\n <input type=\"text\" class=\"form-control input-name\" id=\"input-name\" placeholder=\"Enter name...\" autofocus required>\n </div>\n\n <div class=\"form-group\">\n <label for=\"input-name\">Input type:</label>\n <input style=\"display: none;\" required />\n <edit-type ref=\"inputType\" :type=\"inputType\" :displayOKButton=\"false\"></edit-type>\n </div>\n\n <div class=\"form-group\">\n <label for=\"input-description\">Input description:</label>\n <textarea class=\"input-description\" rows=4 style=\"width: 100%;\" placeholder=\"Enter description\" autofocus>\n </textarea>\n </div>\n </fieldset>\n </div>\n </div>\n <div class=\"tab-pane\" id=\"add-output\" role=\"tabpanel\" aria-labelledby=\"add-output-tab\">\n <div class=\"row\">\n <fieldset class=\"mx-auto\" style=\"margin-top: 50px;\">\n <h2 class=\"mt-2\">Add a new output</h2>\n\n <div class=\"form-group\">\n <label for=\"output-name\">Output name:</label>\n <input type=\"text\" class=\"form-control output-name\" id=\"output-name\" placeholder=\"Enter name...\" autofocus required>\n </div>\n\n <div class=\"form-group\">\n <label for=\"output-name\">Output type:</label>\n <input style=\"display: none;\" required />\n <edit-type ref=\"outputType\" :type=\"outputType\" :displayOKButton=\"false\"></edit-type>\n </div>\n\n <div class=\"form-group\">\n <label for=\"output-description\">Output description:</label>\n <textarea class=\"output-description\" rows=4 style=\"width: 100%;\" placeholder=\"Enter description\" autofocus>\n </textarea>\n </div>\n </fieldset>\n </div>\n </div>\n <div class=\"tab-pane\" id=\"add-metanode\" role=\"tabpanel\" aria-labelledby=\"add-metanode-tab\">\n <div class=\"row\">\n <fieldset class=\"mx-auto\" style=\"margin-top: 50px;\">\n <h2 class=\"mt-2\">Add a new Metabox\n <!--<button class=\"btn btn-outline-secondary float-right\" @click=\"importMetabox\"><i class=\"fas fa-upload\"></i></button>-->\n </h2>\n\n <div class=\"form-group\">\n <label for=\"metanode-name\">Metabox name:</label>\n <input type=\"text\" class=\"form-control metanode-name\" id=\"metanode-name\" placeholder=\"Enter name...\" autofocus required>\n </div>\n\n <div class=\"form-group\">\n <label for=\"metanode-def\">Metabox definition (optional):</label>\n <input type=\"file\" class=\"form-control-file\" id=\"metanode-def\" @change=\"onMetaboxFileSelect\">\n </div>\n\n <div class=\"form-group\">\n <label for=\"metanode-description\">Metabox description:</label>\n <textarea class=\"metanode-description\" rows=4 style=\"width: 100%;\" placeholder=\"Enter description\" autofocus>\n </textarea>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">Close</button>\n <button type=\"button\" class=\"btn btn-primary add-node-btn\" @click=\"addSelectedNode\">Add node</button>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script>\nimport _ from \"lodash-es\";\nimport swal from \"sweetalert2\";\nimport SearchResultsVue from \"./searchResults.vue\";\nimport DisplayResultVue from \"./displayResult.vue\";\nimport EditTypeVue from \"./editType.vue\";\n\nexport default {\n props: [\n \"display\", // original state (true = show, false=hide) of the modal\n \"mousePosition\" // mouse position when this panel was opened\n ],\n components: {\n \"search-results\": SearchResultsVue,\n \"display-result\": DisplayResultVue,\n \"edit-type\": EditTypeVue\n },\n data: function() {\n return {\n error: null,\n\n searchText: { \"module\": \"\", \"ui\": \"\" }, // the search text, indexed by selectedType\n results: {}, // dictionary of arrays, indexed by selectedType\n selectedResult: {}, // dictionary of string, indexed by selectedType\n selectedType: \"module\",\n inputType: \"*\",\n outputType: \"*\"\n };\n },\n created: function() {\n this.view = window.dualboxEditor.v;\n this.searchModules();\n },\n updated: function() {\n console.log(\"AddNode updated !\");\n this.updateVisibility();\n this.setSearchFocus();\n\n if (!this.display) {\n // make sure the modal disappeared\n $(this.$el).modal(\"hide\");\n $(\"body\").removeClass(\"modal-open\");\n $(\".modal-backdrop\").remove();\n }\n },\n mounted: function() {\n var self = this;\n this.updateVisibility(); // to init our modal\n\n $(this.$el).on(\"shown.bs.modal\", () => {\n this.setSearchFocus();\n });\n $(this.$el).on(\"hide.bs.modal\", () => {\n self.$emit(\"closed\");\n });\n\n $(this.$el)\n .find('[data-toggle=\"pill\"]')\n .on(\"shown.bs.tab\", e => {\n this.setSearchFocus();\n var selectedResult = this.getSelectedResult();\n if (selectedResult) {\n this.$refs.searchResults.selected = selectedResult.name;\n }\n });\n },\n methods: {\n updateVisibility: function() {\n if (this.display) {\n $(this.$el).modal(\"show\");\n } else {\n $(this.$el).modal(\"hide\");\n }\n },\n\n closeModal: function() {\n // don't mutate prop directly, mute the parent\n // this.display = false;\n\n return new Promise((resolve, reject) => {\n this.$emit(\"closed\");\n\n let forceCloseModal = () => {\n if (!this.display) {\n resolve();\n } else {\n this.$emit(\"closed\");\n setTimeout(forceCloseModal, 100);\n }\n };\n\n this.$nextTick(forceCloseModal);\n });\n },\n\n setSearchFocus: function() {\n $(this.$el)\n .find(\".nav-link.active\")\n .find(\"input\")\n .focus();\n },\n\n selectType(t) {\n let searchText = this.searchText[this.selectedType];\n this.selectedType = t;\n this.searchText[t] = searchText;\n if( t === \"module\" || t === \"ui\" ) {\n this.searchModules();\n }\n },\n\n addSelectedNode: async function(e) {\n switch (this.selectedType) {\n case \"module\":\n case \"ui\":\n var selectedNode = $(\".node-result.selected\")[0];\n if (selectedNode) {\n await this.closeModal();\n // add node to the controller\n var packageName = $(selectedNode).data(\"package\");\n this.view.c.addNewBox(packageName, this.mousePosition);\n } else {\n swal(\"Please select a node\", \"\", \"error\");\n return;\n }\n break;\n\n case \"input\":\n var type = this.$refs.inputType.get();\n var name = $(this.$el)\n .find(\".input-name\")\n .val();\n var desc = $(this.$el)\n .find(\".input-description\")\n .val();\n if (type !== undefined && name) {\n await this.closeModal();\n this.view.c.addInput(\n name,\n type,\n desc,\n this.mousePosition\n );\n // reset fields\n this.$refs.inputType.resetChanges();\n $(this.$el)\n .find(\".input-name\")\n .val(\"\");\n $(this.$el)\n .find(\".input-description\")\n .val(\"\");\n } else {\n swal(\"Please complete all fields\", \"\", \"error\");\n return;\n }\n break;\n\n case \"output\":\n var name = $(this.$el)\n .find(\".output-name\")\n .val();\n var type = this.$refs.outputType.get();\n var desc = $(this.$el)\n .find(\".output-description\")\n .val();\n if (type !== undefined && name) {\n await this.closeModal();\n this.view.c.addOutput(\n name,\n type,\n desc,\n this.mousePosition\n );\n // reset fields\n this.$refs.outputType.resetChanges();\n $(this.$el)\n .find(\".output-name\")\n .val(\"\");\n $(this.$el)\n .find(\".output-description\")\n .val(\"\");\n } else {\n swal(\"Please complete all fields\", \"\", \"error\");\n return;\n }\n break;\n\n case \"metanode\":\n var name = $(this.$el)\n .find(\".metanode-name\")\n .val();\n var desc = $(this.$el)\n .find(\".metanode-description\")\n .val();\n if (!name) {\n swal(\"Please complete name field\", \"\", \"error\");\n return;\n }\n\n var json = this.metanodeJSON || {};\n json.description = desc;\n\n await this.closeModal();\n this.view.c.addNewMetabox(name, json, this.mousePosition);\n default:\n }\n },\n\n searchModules: async function(e) {\n //var text = $(e.target).val();\n var text = this.searchText[this.selectedType];\n //try {\n this.error = null;\n this.results[this.selectedType] = await this.view.e.search(\n text,\n this.selectedType\n );\n this.$forceUpdate();\n //} catch (e) {\n // this.error = e;\n // swal(\"error searching packages\", e, \"error\");\n //}\n },\n\n getSearchText: function() {\n return this.searchText[this.selectedType] || \"\";\n },\n\n setSelectedResult: function(packageName) {\n // first, find the selected result in the list\n var selectedPackage = null;\n _.each(this.getResults(), r => {\n if (r.name == packageName) {\n selectedPackage = r;\n return false;\n }\n });\n\n if (!selectedPackage) {\n throw \"Could not find package named \" + packageName;\n }\n\n this.selectedResult[this.selectedType] = selectedPackage;\n this.$forceUpdate();\n },\n\n getSelectedResult: function() {\n return this.selectedResult[this.selectedType];\n },\n\n getResults: function() {\n return this.results[this.selectedType] || null;\n },\n\n getNbResults: function() {\n var results = this.getResults();\n return results && results.length ? results.length : 0;\n },\n\n onMetaboxFileSelect: function(e) {\n var file = e.target.files[0];\n const reader = new FileReader();\n reader.onload = e => {\n this.metanodeJSON = JSON.parse(e.target.result);\n if (this.metanodeJSON.description) {\n $(this.$el)\n .find(\".metanode-description\")\n .val(this.metanodeJSON.description);\n }\n };\n reader.readAsText(file);\n }\n }\n};\n</script>\n\n"]}, media: undefined });
64536
64631
 
64537
64632
  };
64538
64633
  /* scoped */
64539
- const __vue_scope_id__$5 = "data-v-4feaa5c6";
64634
+ const __vue_scope_id__$5 = "data-v-095049aa";
64540
64635
  /* module identifier */
64541
64636
  const __vue_module_identifier__$5 = undefined;
64542
64637
  /* functional template */
@@ -88840,13 +88935,14 @@ var script$d = {
88840
88935
  };
88841
88936
 
88842
88937
  // add the value to our select, and load it
88843
- $(self.$el)
88844
- .find(".app-interface-select")
88845
- .append(
88846
- $("<option/>", {
88847
- value: name
88848
- }).append(name)
88849
- );
88938
+ // $(self.$el)
88939
+ // .find(".app-interface-select")
88940
+ // .append(
88941
+ // $("<option/>", {
88942
+ // value: name
88943
+ // }).append(name)
88944
+ // );
88945
+ self.$forceUpdate();
88850
88946
  $(document).ready(() => {
88851
88947
  $(self.$el)
88852
88948
  .find(".app-interface-select")
@@ -88885,17 +88981,10 @@ var script$d = {
88885
88981
  }).then(result => {
88886
88982
  if (result.value) {
88887
88983
  // set the interface back to first value
88888
- $(this.$el)
88889
- .find(
88890
- ".app-interface-select option[value='" + name + "']"
88891
- )
88892
- .remove();
88893
- $(this.$el)
88894
- .find(".app-interface-select")
88895
- .change();
88896
88984
  delete self.view.m.data.root.interface[name];
88897
88985
  self.htmlCode.setValue("");
88898
88986
  self.view.m.save();
88987
+ self.view.m.repaint();
88899
88988
  self.view.runApp(self.getOptions());
88900
88989
  }
88901
88990
  });
@@ -89491,7 +89580,10 @@ var __vue_render__$d = function() {
89491
89580
  ) {
89492
89581
  return _c(
89493
89582
  "option",
89494
- { domProps: { value: interfaceName } },
89583
+ {
89584
+ key: interfaceName,
89585
+ domProps: { value: interfaceName }
89586
+ },
89495
89587
  [_vm._v(_vm._s(interfaceName))]
89496
89588
  )
89497
89589
  })
@@ -89769,7 +89861,7 @@ __vue_render__$d._withStripped = true;
89769
89861
  /* style */
89770
89862
  const __vue_inject_styles__$d = function (inject) {
89771
89863
  if (!inject) return
89772
- inject("data-v-2e37bd5a_0", { source: "\n.code-panel {\n width: 30%;\n height: 100%;\n vertical-align: top;\n float: left;\n}\n.application-container {\n width: calc(70% - 5px);\n height: 100%;\n display: inline-block;\n vertical-align: top;\n float: right;\n}\n.code-controls,\n.run-options {\n width: 100%;\n height: 60px;\n}\n.application {\n width: 100%;\n height: calc(100% - 60px);\n background-color: #ccc;\n}\n.dragbar {\n height: 100%;\n width: 5px;\n background-color: #e7e7e7;\n display: inline-block;\n cursor: col-resize;\n}\n.code-content {\n position: relative;\n height: calc(100% - 56px);\n}\n.code-html-container {\n position: relative;\n height: 50%;\n border-bottom: 1px solid #e7e7e7;\n}\n.code-css-container {\n position: relative;\n height: calc(50% - 1px);\n overflow: hidden;\n}\n.code-css {\n width: 100%;\n height: 100%;\n}\n.code-html {\n height: calc(100% - 40px);\n width: 100%;\n}\n.CodeMirror {\n height: 100% !important;\n}\n.code-badge {\n position: absolute;\n top: 10px;\n right: 10px;\n padding: 10px;\n}\n.btn-light.focus,\n.btn-light:focus {\n box-shadow: none !important;\n}\n.button-bar {\n width: 60px;\n padding: 10px;\n border-bottom: 1px solid #e7e7e7;\n width: 100%;\n background-color: white;\n}\n.noselect {\n -webkit-user-select: none;\n /* Chrome/Safari */\n -moz-user-select: none;\n /* Firefox */\n -ms-user-select: none;\n /* IE10+ */\n -o-user-select: none;\n user-select: none;\n}\n.btn + .btn {\n margin-left: 5px;\n}\n.btn-graph-goto {\n box-shadow: none !important;\n}\n.nav-tabs {\n border-bottom: none;\n}\n.main-navigation a.nav-link {\n color: white;\n}\n.main-navigation a.nav-link.active {\n color: #212529;\n}\n.dualbox-editor-body {\n width: 100%;\n height: 100%;\n overflow: hidden;\n background-color: rgb(85, 85, 85);\n}\n.dualbox-graph-left-section {\n height: 100%;\n width: 500px;\n float: left;\n position: relative;\n overflow: hidden;\n margin-left: -465px;\n transition: margin-left 0.3s ease;\n}\n.dualbox-graph-left-window {\n width: calc(100% - 35px);\n height: calc(100% - 10px);\n margin-top: 10px;\n /* background-color: #ECF2F8; */\n background-color: #f8f9fa;\n /* border: 1px solid grey; */\n border-radius: 5px;\n position: relative;\n float: left;\n}\n.dualbox-graph-left-panel {\n width: 100%;\n height: 100%;\n}\n.btn-toggle-left-window {\n position: absolute;\n right: 0;\n top: 0;\n margin-right: -35px;\n z-index: 100;\n border-top-left-radius: 0px;\n border-bottom-left-radius: 0px;\n}\n.btn-toggle-left-window:hover,\n.btn-toggle-right-window:hover,\n.btn-toggle-left-window:focus,\n.btn-toggle-right-window:focus,\n.btn-toggle-left-window:active,\n.btn-toggle-right-window:active,\n.btn-toggle-left-window:active:hover,\n.btn-toggle-right-window:active:hover {\n color: #212529 !important;\n background-color: #f8f9fa !important;\n border-color: #f8f9fa !important;\n}\n.dualbox-graph-right-section {\n height: 100%;\n width: 500px;\n float: right;\n position: relative;\n overflow: hidden;\n margin-right: -465px;\n transition: margin-right 0.3s ease;\n}\n.dualbox-graph-right-window {\n width: calc(100% - 35px);\n height: calc(100% - 20px);\n margin-top: 10px;\n margin-bottom: 30px;\n /* background-color: #ECF2F8; */\n background-color: #f8f9fa;\n /* border: 1px solid grey; */\n border-radius: 5px;\n position: relative;\n float: right;\n}\n.dualbox-graph-right-panel {\n width: 100%;\n height: 100%;\n}\n.btn-toggle-right-window {\n position: absolute;\n left: 0;\n top: 0;\n margin-left: -35px;\n z-index: 100;\n border-top-right-radius: 0px;\n border-bottom-right-radius: 0px;\n}\n.dualbox-graph-tab {\n height: 100%;\n width: 100%;\n background-color: #555 !important;\n}\n.dualbox-graph-main {\n height: 100%;\n width: calc(100% - 70px);\n float: right;\n background-color: #555 !important;\n transition: width 0.3s ease;\n}\n.dualbox-graph-main.left-panel-expanded {\n width: calc(100% - 535px);\n margin-left: -465px;\n}\n.dualbox-graph-main.right-panel-expanded {\n width: calc(100% - 535px);\n}\n.dualbox-graph-main.left-panel-expanded.right-panel-expanded {\n width: calc(100% - 1000px);\n margin-left: -465px;\n}\n.opacity0 {\n opacity: 0;\n}\n.btn-editor-xs {\n width: 18px;\n padding: 3px !important;\n line-height: 0.5;\n border-radius: 2px;\n}\n.btn-editor-xs > i {\n font-size: 10px;\n}\n.btn-outline-discrete {\n border-color: rgba(0, 0, 0, 0.05);\n border-color: transparent;\n color: rgba(0, 0, 0, 0.3);\n}\n.dualbox-app-navigation {\n background-color: transparent;\n margin-bottom: 0;\n vertical-align: middle;\n padding-top: 7px;\n padding-bottom: 7px;\n font-weight: bold;\n user-select: none;\n}\n.app-topbar {\n border-bottom: none;\n padding-left: 15px;\n padding-right: 15px;\n display: flex;\n width: 100%;\n height: 58px;\n padding-top: 10px;\n padding-bottom: 10px;\n}\n.dark,\n.graph-tab.active,\n.nav-item.active .graph-tab {\n color: white !important;\n background-color: #555 !important;\n}\n.light {\n color: #4d4d4d !important;\n background-color: #f8f9fa !important;\n}\n.main-navigation .nav-link,\n.main-navigation .nav-link.active,\n.nav-item.active .nav-link {\n border-bottom: none;\n border-left: none;\n border-top: none;\n border-right: none;\n position: relative;\n top: 1px;\n}\n.btn:focus,\nbutton:focus {\n outline: none;\n box-shadow: none;\n}\n.btn-transparent,\n.btn-transparent:hover,\n.btn-transparent:focus {\n background-color: transparent;\n}\n.connection-control {\n position: absolute;\n height: 14px;\n width: 14px;\n background-color: transparent;\n z-index: 19;\n cursor: move;\n}\n.connection-control.selected {\n border-color: #0066ff;\n box-shadow: 1px 1px 10px #0066ff;\n}\n.connection-label {\n z-index: 22 !important;\n color: #004d00 !important;\n background-color: white !important;\n padding: 4px 4px;\n border: 2px solid #004d00;\n font-size: 16px !important;\n border-radius: 5px;\n}\n.input-color-tag {\n background-color: #f2d600;\n}\n.output-color-tag {\n background-color: #ffab4a;\n}\n.ui-color-tag {\n background-color: #61bd4f;\n}\n.metanode-color-tag {\n background-color: #dddddd;\n}\n.input {\n}\n.output {\n}\n.fileUpload {\n position: relative;\n overflow: hidden;\n margin: 10px;\n}\n.fileUpload input.upload {\n position: absolute;\n top: 0;\n right: 0;\n margin: 0;\n padding: 0;\n font-size: 20px;\n cursor: pointer;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.module-edit-modal-body .btn + .btn {\n margin-bottom: 0;\n margin-left: 5px;\n}\n.btn-add-node {\n width: 46%;\n margin: 1%;\n}\nbutton.close {\n position: absolute;\n right: 0;\n margin-right: 5px !important;\n}\n.CodeMirror {\n height: auto;\n min-height: 300px;\n}\n.load-app,\n.save-app {\n margin-left: 5px;\n margin-right: 5px;\n}\n.btn-xs {\n padding: 0px 4px;\n font-size: 12px;\n}\n@media screen and (max-width: 1152px) {\n.code-panel {\n display: none;\n}\n.dragbar {\n display: none !important;\n}\n.application-container {\n width: 100%;\n}\n.run-options {\n display: none;\n}\n.application {\n height: 100%;\n}\n.navbar-right {\n display: none;\n}\n}\n", map: {"version":3,"sources":["/home/seb/dev/dualbox/editor/js/src/v/templates/main.vue"],"names":[],"mappings":";AACA;IACA,UAAA;IACA,YAAA;IACA,mBAAA;IACA,WAAA;AACA;AAEA;IACA,sBAAA;IACA,YAAA;IACA,qBAAA;IACA,mBAAA;IACA,YAAA;AACA;AAEA;;IAEA,WAAA;IACA,YAAA;AACA;AAEA;IACA,WAAA;IACA,yBAAA;IACA,sBAAA;AACA;AAEA;IACA,YAAA;IACA,UAAA;IACA,yBAAA;IACA,qBAAA;IACA,kBAAA;AACA;AAEA;IACA,kBAAA;IACA,yBAAA;AACA;AAEA;IACA,kBAAA;IACA,WAAA;IACA,gCAAA;AACA;AAEA;IACA,kBAAA;IACA,uBAAA;IACA,gBAAA;AACA;AAEA;IACA,WAAA;IACA,YAAA;AACA;AAEA;IACA,yBAAA;IACA,WAAA;AACA;AAEA;IACA,uBAAA;AACA;AAEA;IACA,kBAAA;IACA,SAAA;IACA,WAAA;IACA,aAAA;AACA;AAEA;;IAEA,2BAAA;AACA;AAEA;IACA,WAAA;IACA,aAAA;IACA,gCAAA;IACA,WAAA;IACA,uBAAA;AACA;AAEA;IACA,yBAAA;IACA,kBAAA;IACA,sBAAA;IACA,YAAA;IACA,qBAAA;IACA,UAAA;IACA,oBAAA;IACA,iBAAA;AACA;AAEA;IACA,gBAAA;AACA;AAEA;IACA,2BAAA;AACA;AAEA;IACA,mBAAA;AACA;AAEA;IACA,YAAA;AACA;AAEA;IACA,cAAA;AACA;AAEA;IACA,WAAA;IACA,YAAA;IACA,gBAAA;IACA,iCAAA;AACA;AAEA;IACA,YAAA;IACA,YAAA;IACA,WAAA;IACA,kBAAA;IACA,gBAAA;IACA,mBAAA;IACA,iCAAA;AACA;AAEA;IACA,wBAAA;IACA,yBAAA;IACA,gBAAA;IACA,+BAAA;IACA,yBAAA;IACA,4BAAA;IACA,kBAAA;IACA,kBAAA;IACA,WAAA;AACA;AAEA;IACA,WAAA;IACA,YAAA;AACA;AAEA;IACA,kBAAA;IACA,QAAA;IACA,MAAA;IACA,mBAAA;IACA,YAAA;IACA,2BAAA;IACA,8BAAA;AACA;AAEA;;;;;;;;IAQA,yBAAA;IACA,oCAAA;IACA,gCAAA;AACA;AAEA;IACA,YAAA;IACA,YAAA;IACA,YAAA;IACA,kBAAA;IACA,gBAAA;IACA,oBAAA;IACA,kCAAA;AACA;AAEA;IACA,wBAAA;IACA,yBAAA;IACA,gBAAA;IACA,mBAAA;IACA,+BAAA;IACA,yBAAA;IACA,4BAAA;IACA,kBAAA;IACA,kBAAA;IACA,YAAA;AACA;AAEA;IACA,WAAA;IACA,YAAA;AACA;AAEA;IACA,kBAAA;IACA,OAAA;IACA,MAAA;IACA,kBAAA;IACA,YAAA;IACA,4BAAA;IACA,+BAAA;AACA;AAEA;IACA,YAAA;IACA,WAAA;IACA,iCAAA;AACA;AAEA;IACA,YAAA;IACA,wBAAA;IACA,YAAA;IACA,iCAAA;IACA,2BAAA;AACA;AAEA;IACA,yBAAA;IACA,mBAAA;AACA;AAEA;IACA,yBAAA;AACA;AAEA;IACA,0BAAA;IACA,mBAAA;AACA;AAEA;IACA,UAAA;AACA;AAEA;IACA,WAAA;IACA,uBAAA;IACA,gBAAA;IACA,kBAAA;AACA;AAEA;IACA,eAAA;AACA;AAEA;IACA,iCAAA;IACA,yBAAA;IACA,yBAAA;AACA;AAEA;IACA,6BAAA;IACA,gBAAA;IACA,sBAAA;IACA,gBAAA;IACA,mBAAA;IACA,iBAAA;IACA,iBAAA;AACA;AAEA;IACA,mBAAA;IACA,kBAAA;IACA,mBAAA;IACA,aAAA;IACA,WAAA;IACA,YAAA;IACA,iBAAA;IACA,oBAAA;AACA;AAEA;;;IAGA,uBAAA;IACA,iCAAA;AACA;AAEA;IACA,yBAAA;IACA,oCAAA;AACA;AAEA;;;IAGA,mBAAA;IACA,iBAAA;IACA,gBAAA;IACA,kBAAA;IACA,kBAAA;IACA,QAAA;AACA;AAEA;;IAEA,aAAA;IACA,gBAAA;AACA;AAEA;;;IAGA,6BAAA;AACA;AAEA;IACA,kBAAA;IACA,YAAA;IACA,WAAA;IACA,6BAAA;IACA,WAAA;IACA,YAAA;AACA;AAEA;IACA,qBAAA;IACA,gCAAA;AACA;AAEA;IACA,sBAAA;IACA,yBAAA;IACA,kCAAA;IACA,gBAAA;IACA,yBAAA;IACA,0BAAA;IACA,kBAAA;AACA;AAEA;IACA,yBAAA;AACA;AAEA;IACA,yBAAA;AACA;AAEA;IACA,yBAAA;AACA;AAEA;IACA,yBAAA;AACA;AAEA;AACA;AAEA;AACA;AAEA;IACA,kBAAA;IACA,gBAAA;IACA,YAAA;AACA;AAEA;IACA,kBAAA;IACA,MAAA;IACA,QAAA;IACA,SAAA;IACA,UAAA;IACA,eAAA;IACA,eAAA;IACA,UAAA;IACA,wBAAA;AACA;AAEA;IACA,gBAAA;IACA,gBAAA;AACA;AAEA;IACA,UAAA;IACA,UAAA;AACA;AAEA;IACA,kBAAA;IACA,QAAA;IACA,4BAAA;AACA;AAEA;IACA,YAAA;IACA,iBAAA;AACA;AAEA;;IAEA,gBAAA;IACA,iBAAA;AACA;AAEA;IACA,gBAAA;IACA,eAAA;AACA;AAEA;AACA;QACA,aAAA;AACA;AAEA;QACA,wBAAA;AACA;AAEA;QACA,WAAA;AACA;AAEA;QACA,aAAA;AACA;AAEA;QACA,YAAA;AACA;AAEA;QACA,aAAA;AACA;AACA","file":"main.vue","sourcesContent":["<style>\n.code-panel {\n width: 30%;\n height: 100%;\n vertical-align: top;\n float: left;\n}\n\n.application-container {\n width: calc(70% - 5px);\n height: 100%;\n display: inline-block;\n vertical-align: top;\n float: right;\n}\n\n.code-controls,\n.run-options {\n width: 100%;\n height: 60px;\n}\n\n.application {\n width: 100%;\n height: calc(100% - 60px);\n background-color: #ccc;\n}\n\n.dragbar {\n height: 100%;\n width: 5px;\n background-color: #e7e7e7;\n display: inline-block;\n cursor: col-resize;\n}\n\n.code-content {\n position: relative;\n height: calc(100% - 56px);\n}\n\n.code-html-container {\n position: relative;\n height: 50%;\n border-bottom: 1px solid #e7e7e7;\n}\n\n.code-css-container {\n position: relative;\n height: calc(50% - 1px);\n overflow: hidden;\n}\n\n.code-css {\n width: 100%;\n height: 100%;\n}\n\n.code-html {\n height: calc(100% - 40px);\n width: 100%;\n}\n\n.CodeMirror {\n height: 100% !important;\n}\n\n.code-badge {\n position: absolute;\n top: 10px;\n right: 10px;\n padding: 10px;\n}\n\n.btn-light.focus,\n.btn-light:focus {\n box-shadow: none !important;\n}\n\n.button-bar {\n width: 60px;\n padding: 10px;\n border-bottom: 1px solid #e7e7e7;\n width: 100%;\n background-color: white;\n}\n\n.noselect {\n -webkit-user-select: none;\n /* Chrome/Safari */\n -moz-user-select: none;\n /* Firefox */\n -ms-user-select: none;\n /* IE10+ */\n -o-user-select: none;\n user-select: none;\n}\n\n.btn + .btn {\n margin-left: 5px;\n}\n\n.btn-graph-goto {\n box-shadow: none !important;\n}\n\n.nav-tabs {\n border-bottom: none;\n}\n\n.main-navigation a.nav-link {\n color: white;\n}\n\n.main-navigation a.nav-link.active {\n color: #212529;\n}\n\n.dualbox-editor-body {\n width: 100%;\n height: 100%;\n overflow: hidden;\n background-color: rgb(85, 85, 85);\n}\n\n.dualbox-graph-left-section {\n height: 100%;\n width: 500px;\n float: left;\n position: relative;\n overflow: hidden;\n margin-left: -465px;\n transition: margin-left 0.3s ease;\n}\n\n.dualbox-graph-left-window {\n width: calc(100% - 35px);\n height: calc(100% - 10px);\n margin-top: 10px;\n /* background-color: #ECF2F8; */\n background-color: #f8f9fa;\n /* border: 1px solid grey; */\n border-radius: 5px;\n position: relative;\n float: left;\n}\n\n.dualbox-graph-left-panel {\n width: 100%;\n height: 100%;\n}\n\n.btn-toggle-left-window {\n position: absolute;\n right: 0;\n top: 0;\n margin-right: -35px;\n z-index: 100;\n border-top-left-radius: 0px;\n border-bottom-left-radius: 0px;\n}\n\n.btn-toggle-left-window:hover,\n.btn-toggle-right-window:hover,\n.btn-toggle-left-window:focus,\n.btn-toggle-right-window:focus,\n.btn-toggle-left-window:active,\n.btn-toggle-right-window:active,\n.btn-toggle-left-window:active:hover,\n.btn-toggle-right-window:active:hover {\n color: #212529 !important;\n background-color: #f8f9fa !important;\n border-color: #f8f9fa !important;\n}\n\n.dualbox-graph-right-section {\n height: 100%;\n width: 500px;\n float: right;\n position: relative;\n overflow: hidden;\n margin-right: -465px;\n transition: margin-right 0.3s ease;\n}\n\n.dualbox-graph-right-window {\n width: calc(100% - 35px);\n height: calc(100% - 20px);\n margin-top: 10px;\n margin-bottom: 30px;\n /* background-color: #ECF2F8; */\n background-color: #f8f9fa;\n /* border: 1px solid grey; */\n border-radius: 5px;\n position: relative;\n float: right;\n}\n\n.dualbox-graph-right-panel {\n width: 100%;\n height: 100%;\n}\n\n.btn-toggle-right-window {\n position: absolute;\n left: 0;\n top: 0;\n margin-left: -35px;\n z-index: 100;\n border-top-right-radius: 0px;\n border-bottom-right-radius: 0px;\n}\n\n.dualbox-graph-tab {\n height: 100%;\n width: 100%;\n background-color: #555 !important;\n}\n\n.dualbox-graph-main {\n height: 100%;\n width: calc(100% - 70px);\n float: right;\n background-color: #555 !important;\n transition: width 0.3s ease;\n}\n\n.dualbox-graph-main.left-panel-expanded {\n width: calc(100% - 535px);\n margin-left: -465px;\n}\n\n.dualbox-graph-main.right-panel-expanded {\n width: calc(100% - 535px);\n}\n\n.dualbox-graph-main.left-panel-expanded.right-panel-expanded {\n width: calc(100% - 1000px);\n margin-left: -465px;\n}\n\n.opacity0 {\n opacity: 0;\n}\n\n.btn-editor-xs {\n width: 18px;\n padding: 3px !important;\n line-height: 0.5;\n border-radius: 2px;\n}\n\n.btn-editor-xs > i {\n font-size: 10px;\n}\n\n.btn-outline-discrete {\n border-color: rgba(0, 0, 0, 0.05);\n border-color: transparent;\n color: rgba(0, 0, 0, 0.3);\n}\n\n.dualbox-app-navigation {\n background-color: transparent;\n margin-bottom: 0;\n vertical-align: middle;\n padding-top: 7px;\n padding-bottom: 7px;\n font-weight: bold;\n user-select: none;\n}\n\n.app-topbar {\n border-bottom: none;\n padding-left: 15px;\n padding-right: 15px;\n display: flex;\n width: 100%;\n height: 58px;\n padding-top: 10px;\n padding-bottom: 10px;\n}\n\n.dark,\n.graph-tab.active,\n.nav-item.active .graph-tab {\n color: white !important;\n background-color: #555 !important;\n}\n\n.light {\n color: #4d4d4d !important;\n background-color: #f8f9fa !important;\n}\n\n.main-navigation .nav-link,\n.main-navigation .nav-link.active,\n.nav-item.active .nav-link {\n border-bottom: none;\n border-left: none;\n border-top: none;\n border-right: none;\n position: relative;\n top: 1px;\n}\n\n.btn:focus,\nbutton:focus {\n outline: none;\n box-shadow: none;\n}\n\n.btn-transparent,\n.btn-transparent:hover,\n.btn-transparent:focus {\n background-color: transparent;\n}\n\n.connection-control {\n position: absolute;\n height: 14px;\n width: 14px;\n background-color: transparent;\n z-index: 19;\n cursor: move;\n}\n\n.connection-control.selected {\n border-color: #0066ff;\n box-shadow: 1px 1px 10px #0066ff;\n}\n\n.connection-label {\n z-index: 22 !important;\n color: #004d00 !important;\n background-color: white !important;\n padding: 4px 4px;\n border: 2px solid #004d00;\n font-size: 16px !important;\n border-radius: 5px;\n}\n\n.input-color-tag {\n background-color: #f2d600;\n}\n\n.output-color-tag {\n background-color: #ffab4a;\n}\n\n.ui-color-tag {\n background-color: #61bd4f;\n}\n\n.metanode-color-tag {\n background-color: #dddddd;\n}\n\n.input {\n}\n\n.output {\n}\n\n.fileUpload {\n position: relative;\n overflow: hidden;\n margin: 10px;\n}\n\n.fileUpload input.upload {\n position: absolute;\n top: 0;\n right: 0;\n margin: 0;\n padding: 0;\n font-size: 20px;\n cursor: pointer;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n\n.module-edit-modal-body .btn + .btn {\n margin-bottom: 0;\n margin-left: 5px;\n}\n\n.btn-add-node {\n width: 46%;\n margin: 1%;\n}\n\nbutton.close {\n position: absolute;\n right: 0;\n margin-right: 5px !important;\n}\n\n.CodeMirror {\n height: auto;\n min-height: 300px;\n}\n\n.load-app,\n.save-app {\n margin-left: 5px;\n margin-right: 5px;\n}\n\n.btn-xs {\n padding: 0px 4px;\n font-size: 12px;\n}\n\n@media screen and (max-width: 1152px) {\n .code-panel {\n display: none;\n }\n\n .dragbar {\n display: none !important;\n }\n\n .application-container {\n width: 100%;\n }\n\n .run-options {\n display: none;\n }\n\n .application {\n height: 100%;\n }\n\n .navbar-right {\n display: none;\n }\n}\n</style>\n\n<template>\n <div class=\"dualbox-editor-body\">\n <nav class=\"main-navigation navbar navbar-default\" style=\"position: relative; margin-bottom: 0px; padding: 0; padding-top: 5px; background-color: #0B6D80;\">\n <ul class=\"nav nav-tabs\">\n <li class=\"nav-item active\">\n <a class=\"nav-link graph-tab\" href=\"#editor-graph-tab\" data-toggle=\"tab\">Application Graph</a>\n </li>\n <li class=\"nav-item\">\n <a class=\"nav-link interface-tab\" href=\"#editor-interface-tab\" data-toggle=\"tab\">Interface Editor</a>\n </li>\n </ul>\n\n <div class=\"nav navbar-right\" style=\"vertical-align: top; margin-top: 5px; margin-right: 10px; margin-bottom: 5px;\">\n <div v-if=\"showLoadButton === true\">\n <button class=\"load-app btn btn-sm btn-light\" style=\"position: relative; bottom: 3px;\" @click=\"loadApp\">Load App</button>\n </div>\n <div v-if=\"showSaveButton\">\n <button class=\"save-app btn btn-sm btn-light\" style=\"position: relative; bottom: 3px;\" @click=\"saveApp\">Save App</button>\n </div>\n </div>\n </nav>\n <div class=\"tab-content dualbox-graph-tab\" style=\"width: 100%; height: calc(100% - 46px);\">\n <div class=\"tab-pane active\" id=\"editor-graph-tab\" style=\"width: 100%; height: 100%;\">\n <div class=\"dualbox-graph-left-section dark\">\n <div class=\"dualbox-graph-left-window\">\n <button class=\"btn btn-light btn-toggle-left-window\" @click=\"toggleLeftWindow\" title=\"shrink window\" data-expanded=\"false\"><i class=\"fa fa-angle-double-right\"></i></button>\n <div class=\"dualbox-graph-left-panel light\">\n <edit-node-settings v-if=\"leftPanelMode == 'node' && selectedNodeId !== null\" :id=\"selectedNodeId\" :key=\"selectedNodeId\"></edit-node-settings>\n <edit-main-settings v-else-if=\"leftPanelMode == 'main'\" :app=\"getCurrentApplication()\" @edited=\"onEdited()\"></edit-main-settings>\n </div>\n </div>\n </div>\n <div class=\"dualbox-graph-right-section dark\">\n <div class=\"dualbox-graph-right-window\">\n <button class=\"btn btn-light btn-toggle-right-window\" @click=\"toggleRightWindow\" title=\"shrink window\" data-expanded=\"false\"><i class=\"fa fa-angle-double-left\"></i></button>\n <div class=\"dualbox-graph-right-panel light\">\n <debug-node-infos v-if=\"debugNodeId !== null\" :id=\"debugNodeId\"></debug-node-infos>\n </div>\n </div>\n </div>\n <div class=\"container-fluid dualbox-graph-main\" @click=\"setMainMenu\">\n <div class=\"row\">\n <div class=\"app-topbar dark\">\n <div class=\"justify-content-left\">\n <div class=\"dropdown\">\n <button class=\"btn btn-primary dropdown-toggle d-none\" type=\"button\" id=\"add-node-dropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\" style=\"margin-right: 5px;\">\n Add\n </button>\n <div class=\"dropdown-menu\" aria-labelledby=\"add-node-dropdown\">\n <a class=\"dropdown-item add-box\" @click=\"addBox\" href=\"#\">Add box</a>\n <a class=\"dropdown-item add-metabox\" @click=\"addMetabox\" href=\"#\">Add metabox</a>\n <a class=\"dropdown-item add-input\" @click=\"addInput\" href=\"#\">Add input</a>\n <a class=\"dropdown-item add-output\" @click=\"addOutput\" href=\"#\">Add output</a>\n <a class=\"dropdown-item import-metabox\" @click=\"importMetabox\" href=\"#\">Import metabox</a>\n </div>\n <button class=\"btn btn-light btn-undo\" title=\"undo (ctrl-z)\" @click=\"undo\"><i class=\"fa fa-undo\"></i></button>\n <button class=\"btn btn-light btn-redo\" title=\"redo (ctrl-y)\" @click=\"redo\"><i class=\"fa fa-repeat fa-redo\"></i></button>\n <div class=\"form-check d-inline-block ml-2\">\n <label class=\"form-check-label\">\n <input class=\"form-check-input show-events\" type=\"checkbox\" value=\"false\" @click=\"showEvents\">\n <span class=\"noselect\">Show events</span>\n </label>\n </div>\n </div>\n </div>\n\n <div class=\"justify-content-center mx-auto\">\n <ol class=\"dualbox-app-navigation breadcrumb\"></ol>\n </div>\n\n <div class=\"justify-content-right\">\n <div class=\"dropdown selection-menu\" style=\"display: none;\">\n <button class=\"btn btn-primary dropdown-toggle\" type=\"button\" id=\"selection-dropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\">\n Selection\n </button>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"add-node-dropdown\">\n <a class=\"dropdown-item dualbox-merge-selection\" @click=\"mergeSelection\" href=\"#\">Merge</a>\n <a class=\"dropdown-item dualbox-remove-selection\" @click=\"removeSelection\" href=\"#\" style=\"color: red;\">Delete</a>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"row\" style=\"height: calc(100% - 58px);\">\n <graph-vue :app=\"app\" ref=\"graph\" :displayEvents=\"displayEvents\"></graph-vue>\n </div>\n </div>\n </div>\n <div class=\"tab-pane\" id=\"editor-interface-tab\" style=\"width 100%; height: 100%; background-color: white;\">\n <div class=\"db-editor-main\" style=\"width: 100%; height: 100%; overflow: hidden;\">\n <div class=\"code-panel\">\n <div class=\"button-bar form-inline code-controls\" style=\"position: relative;\">\n <button class=\"btn btn-primary btn-save-interface-element\" @click=\"saveInterfaceElement\">Save changes</button>\n </div>\n <div class=\"code-content\">\n <div class=\"code-html-container\">\n <div style=\"padding-top: 5px; padding-bottom: 5px; height: 50px; position: relative;\">\n <h3 style=\"position: absolute; top: 0; margin: 5px; font-size: 16px; z-index: 100; display: inline-block; margin-top: 5px;\">HTML</h3>\n <div class=\"d-inline-block ml-auto form-inline mr-2 mt-2\" style=\"position: absolute; top: 0; right: 0;\">\n <button class=\"btn btn-success btn-sm btn-add-interface\" @click=\"addInterface\"><i class=\"fas fa-plus\"></i></button>\n <select class=\"form-control btn-sm app-interface-select\" @change=\"selectInterface\" style=\"width: 150px; height: 32px;\">\n <option>Load UI...</option>\n <option v-for=\"interfaceName in getInterfacesNames()\" :value=\"interfaceName\">{{interfaceName}}</option>\n </select>\n <button class=\"btn btn-sm btn-light btn-edit-panel-description\" @click=\"editPanelDescription\"><i class=\"fas fa-info\" style=\"padding-left: 8px; padding-right: 8px;\"></i></button>\n <button class=\"btn btn-danger btn-sm btn-remove-interface\" @click=\"removeInterface\"><i class=\"fas fa-minus\"></i></button>\n </div>\n </div>\n <div class=\"code-html-text-container\" style=\"position: relative; height: calc(100% - 50px);\">\n <textarea class=\"code-html\" v-html=\"getSelectedInterfaceCode()\"></textarea>\n </div>\n </div>\n <div class=\"code-css-container\">\n <div style=\"height: 30px;\">\n <h3 style=\"position: absolute; top: 0; margin: 5px; font-size: 16px; z-index: 100;\">CSS</h3>\n </div>\n <div class=\"colde-css-text-container\" style=\"position: relative; height: calc(100% - 30px);\">\n <textarea class=\"code-css\" v-model=\"appCss\"></textarea>\n </div>\n </div>\n </div>\n </div>\n <div class=\"dragbar\"></div>\n <div class=\"application-container\">\n <div class=\"button-bar form-inline run-options\" style=\"position: relative;\">\n <button class=\"btn btn-success btn-run mr-2\" @click=\"runApp\">Run</button>\n\n <select class=\"form-control run-loglevel mr-1 ml-2\">\n <option>error</option>\n <option>warn</option>\n <option>info</option>\n <option>log</option>\n <option>debug</option>\n </select>\n\n <input class=\"form-control run-noversioncheck mr-1 ml-3\" type=\"checkbox\" checked>No version checking</input>\n <input class=\"form-control run-removetrycatch mr-1 ml-3\" type=\"checkbox\">Remove Try/catch</input>\n <input class=\"form-control run-makesynchrone mr-1 ml-3\" type=\"checkbox\">Make Synchrone</input>\n <input class=\"form-control run-ressourcecaching mr-1 ml-3\" type=\"checkbox\">Ressource caching</input>\n\n <div class=\"ml-auto\">\n <input class=\"form-control run-profiler mr-1 ml-3\" type=\"checkbox\">Profiler</input>\n <input class=\"form-control run-record mr-1 ml-3\" type=\"checkbox\">Record</input>\n <button class=\"btn btn-secondary btn-snapshot\" @click=\"takeAndLoadSnapshot\">Snapshot</button>\n </div>\n </div>\n <div class=\"application capture-left-click capture-right-click\"></div>\n </div>\n </div>\n </div>\n </div>\n <add-node-vue :display=\"addingBox\" :mousePosition=\"this.newBoxPosition\" @closed=\"onAddBoxClosed\"></add-node-vue>\n </div>\n</template>\n\n<script>\nimport _ from \"lodash-es\";\nimport swal from \"sweetalert2\";\n\nimport graphVue from \"./graph.vue\";\nimport addNodeVue from \"./addNode.vue\";\nimport editMainSettingsVue from \"./editMainSettings.vue\";\nimport editNodeSettingsVue from \"./editNodeSettings.vue\";\nimport debugNodeInfosVue from \"./debugNodeInfos.vue\";\n\n// CodeMirror\nimport CodeMirror from \"codemirror/lib/codemirror.js\";\nimport \"codemirror/mode/xml/xml.js\";\nimport \"codemirror/mode/css/css.js\";\nimport \"codemirror/mode/javascript/javascript.js\";\nimport \"codemirror/mode/htmlmixed/htmlmixed.js\";\n\nimport htmltool from \"@dualbox/dualbox-lib-htmltool\";\n\nexport default {\n props: [\n \"app\", // the app\n\n // Display configuration (online or offline ?)\n \"showLoadButton\",\n \"showSaveButton\",\n \"saveButtonFunction\",\n\n // modes\n \"eventVisibility\" // are events visible\n ],\n components: {\n \"graph-vue\": graphVue,\n \"add-node-vue\": addNodeVue,\n \"edit-main-settings\": editMainSettingsVue,\n \"edit-node-settings\": editNodeSettingsVue,\n \"debug-node-infos\": debugNodeInfosVue\n },\n data() {\n return {\n // modes\n addingBox: false,\n leftPanelMode: \"main\", // 'main' to display main menu, 'node' for a node\n selectedNodeId: null, // id of the node to display in left menu\n debugNodeId: null, // on a snapshot, id of the node to display debug infos\n newBoxPosition: null, // track mouse position relative to the graph when adding a node\n displayEvents: false,\n selectedInterface: null,\n\n // the specific app css\n appCss: \"\"\n };\n },\n created: function() {\n this.view = window.dualboxEditor.v;\n // TODO: restore this when all menus are migrated to Vue.js\n\n this.cssCode = null;\n this.htmlCode = null;\n },\n updated: function() {\n //console.log('updating main view with: ' + JSON.stringify(this.app));\n this.appCss = (this.app && this.app.css) || \"\";\n\n // Update html and css code editor\n this.updateCode();\n },\n mounted: function() {\n //this.view.setMainMenu();\n\n $(this.$el).bind(\"expandSettings\", function(e) {\n // expand\n $(this)\n .find(\".dualbox-graph-left-section\")\n .css(\"margin-left\", \"0\");\n $(this)\n .find(\".dualbox-graph-main\")\n .addClass(\"left-panel-expanded\");\n $(this)\n .find(\".btn-toggle-left-window\")\n .data(\"expanded\", true)\n .find(\"i\")\n .removeClass(\"fa-angle-double-right\")\n .addClass(\"fa-angle-double-left\")\n .attr(\"title\", \"shrink window\");\n });\n\n $(this.$el).bind(\"shrinkSettings\", function(e) {\n // shrink\n $(this)\n .find(\".dualbox-graph-left-section\")\n .css(\"margin-left\", \"-465px\");\n $(this)\n .find(\".dualbox-graph-main\")\n .removeClass(\"left-panel-expanded\");\n $(this)\n .find(\".btn-toggle-left-window\")\n .data(\"expanded\", false)\n .find(\"i\")\n .removeClass(\"fa-angle-double-left\")\n .addClass(\"fa-angle-double-right\")\n .attr(\"title\", \"expand window\");\n });\n\n $(this.$el).bind(\"expandDebug\", function(e) {\n // expand\n $(this)\n .find(\".dualbox-graph-right-section\")\n .css(\"margin-right\", \"0\");\n $(this)\n .find(\".dualbox-graph-main\")\n .addClass(\"right-panel-expanded\");\n $(this)\n .find(\".btn-toggle-right-window\")\n .data(\"expanded\", true)\n .find(\"i\")\n .removeClass(\"fa-angle-double-left\")\n .addClass(\"fa-angle-double-right\")\n .attr(\"title\", \"shrink window\");\n });\n\n $(this.$el).bind(\"shrinkDebug\", function(e) {\n // shrink\n $(this)\n .find(\".dualbox-graph-right-section\")\n .css(\"margin-right\", \"-465px\");\n $(this)\n .find(\".dualbox-graph-main\")\n .removeClass(\"right-panel-expanded\");\n $(this)\n .find(\".btn-toggle-right-window\")\n .data(\"expanded\", false)\n .find(\"i\")\n .removeClass(\"fa-angle-double-right\")\n .addClass(\"fa-angle-double-left\")\n .attr(\"title\", \"expand window\");\n });\n\n // bind tabs\n var self = this;\n $(this.$el)\n .find(\"a[data-toggle='tab']\")\n .on(\"shown.bs.tab\", function(e) {\n var target = $(e.target).attr(\"href\"); // activated tab\n if (target == \"#editor-graph-tab\") {\n self.view.killApp();\n } else if (target == \"#editor-interface-tab\") {\n self.htmlCode.refresh();\n self.cssCode.refresh();\n self.view.runApp(self.getOptions());\n }\n });\n\n $(this.$el)\n .find(\"a[data-toggle='tab']\")\n .on(\"click\", function(e) {\n e.preventDefault();\n $(this).tab(\"show\");\n });\n\n // Update html and css code editor\n this.updateCode();\n\n // Resize horizontally\n $(this.$el)\n .find(\".dragbar\")\n .mousedown(function(e) {\n e.preventDefault();\n $(document).mouseup(function(e) {\n $(document).unbind(\"mousemove\");\n });\n $(document).mousemove(function(e) {\n $(\".code-panel\").css(\"width\", e.pageX + \"px\");\n $(\".application-container\").css(\n \"width\",\n $(\".db-editor-main\").width() - e.pageX - 5 + \"px\"\n );\n });\n });\n\n // prevent default behavior for ctrl+s / cmd+s\n $(document).keydown(function(event) {\n if ((event.ctrlKey || event.metaKey) && event.which == 83) {\n event.preventDefault();\n return false;\n }\n });\n },\n destroyed: function() {\n console.log(\"Graph vue destroyed\");\n $(\".ContextMenu\").remove();\n },\n methods: {\n getOptions: function() {\n return {\n profiler: $(this.$el)\n .find(\".run-profiler\")\n .is(\":checked\"),\n logLevel: $(this.$el)\n .find(\".run-loglevel\")\n .val(),\n options: {\n noVersionCheck: $(this.$el)\n .find(\".run-noversioncheck\")\n .is(\":checked\"),\n debug: {\n removeTryCatch: $(this.$el)\n .find(\".run-removetrycatch\")\n .is(\":checked\"),\n makeSynchrone: $(this.$el)\n .find(\".run-makesynchrone\")\n .is(\":checked\"),\n record: $(this.$el)\n .find(\".run-record\")\n .is(\":checked\"),\n ressourceCaching: $(this.$el)\n .find(\".run-ressourcecaching\")\n .is(\":checked\")\n }\n }\n };\n },\n\n updateCode: function() {\n // instanciate codemirror for html and css\n if (!this.htmlCode) {\n this.htmlCode = CodeMirror.fromTextArea(\n $(this.$el).find(\".code-html\")[0],\n {\n lineNumbers: true,\n mode: \"htmlmixed\",\n lineWrapping: true,\n extraKeys: {\n \"Ctrl-S\": () => {\n this.saveInterfaceElement();\n this.runApp();\n },\n \"Cmd-S\": () => {\n this.saveInterfaceElement();\n this.runApp();\n }\n }\n }\n );\n }\n if (!this.cssCode) {\n this.cssCode = CodeMirror.fromTextArea(\n $(this.$el).find(\".code-css\")[0],\n {\n lineNumbers: true,\n mode: \"css\",\n lineWrapping: true,\n extraKeys: {\n \"Ctrl-S\": () => {\n // save css code into app\n this.view.m.data.root.css = this.cssCode.getValue();\n this.view.m.save();\n this.runApp();\n },\n \"Cmd-S\": () => {\n // save css code into app\n this.view.m.data.root.css = this.cssCode.getValue();\n this.view.m.save();\n this.runApp();\n }\n }\n }\n );\n }\n\n this.htmlCode.setValue(this.getSelectedInterfaceCode());\n this.cssCode.setValue(this.view.m.get().css || \"\");\n },\n\n loadApp: function() {\n var self = this;\n\n // create a fake input and click it to select a file\n var input = $(\"<input/>\", {\n type: \"file\",\n class: \"upload\",\n accept: \".json\"\n });\n input.change(function(e) {\n // if we're not here, go to 1st tab\n $(\"a[data-toggle='tab'][href='#editor-graph-tab']\").click();\n\n // parse the file JSON and load it\n var files = e.target.files; // FileList object\n var r = new FileReader();\n r.onload = function(e) {\n var contents = e.target.result;\n var json = JSON.parse(contents);\n self.view.e.setApp(json);\n };\n\n r.readAsText(files[0]);\n });\n input.click();\n },\n saveApp: function() {\n // bind the app load\n if (this.saveButtonFunction) {\n var saveButtonFunction = e => {\n var json = this.view.m.getCleanJson();\n return this.saveButtonFunction(json);\n };\n } else {\n var saveButtonFunction = e => {\n var app = this.view.m.getCleanJson();\n var text = JSON.stringify(app, null, 2);\n var blob = new Blob([text], {\n type: \"application/octet-stream\"\n });\n\n var a = document.createElement(\"a\");\n a.href = window.URL.createObjectURL(blob);\n a.download = \"app.json\";\n\n // simulate a click on the link\n if (document.createEvent) {\n var event = document.createEvent(\"MouseEvents\");\n event.initEvent(\"click\", true, true);\n a.dispatchEvent(event);\n } else {\n a.click();\n }\n };\n }\n saveButtonFunction();\n },\n getCurrentMousePosition: function(e) {\n return this.$refs.graph.getCurrentMousePosition(e);\n },\n addBox: function(e) {\n console.log('opening addbox modal');\n this.addingBox = true;\n this.newBoxPosition = this.getCurrentMousePosition(e);\n this.$forceUpdate();\n },\n onAddBoxClosed: function() {\n console.log('closing modal');\n this.addingBox = false;\n this.$forceUpdate();\n },\n addMetabox: function(e) {\n var self = this;\n e.preventDefault();\n e.stopPropagation();\n\n swal({\n input: \"text\",\n title: \"Choose a name for the metabox\"\n }).then(function(result) {\n if (result.value) {\n self.view.c.addNewMetabox(result.value);\n }\n });\n },\n addInput: function(e) {\n this.view.c.createInput();\n },\n addOutput: function(e) {\n this.view.c.createOutput();\n },\n importMetabox: async function(e) {\n var self = this;\n\n const { value: file } = await swal({\n title: \"Select your metabox file\",\n input: \"file\",\n inputAttributes: {\n accept: \"application/json\",\n \"aria-label\": \"Select your metabox file\"\n }\n });\n\n if (file) {\n const reader = new FileReader();\n reader.onload = e => {\n var json = JSON.parse(e.target.result);\n\n self.view.e\n .loadPackages(json)\n .then(async () => {\n const { value: name } = await swal({\n title: \"Choose a name for your metabox\",\n input: \"text\",\n showCancelButton: true,\n inputValidator: value => {\n return (\n !value && \"You need to write something!\"\n );\n }\n });\n\n self.view.c.addNewMetabox(name, json);\n })\n .catch(err => {\n console.error(err);\n });\n };\n reader.readAsText(file);\n }\n },\n\n getInterfacesNames: function() {\n return _.keys(\n _.get(this.view.m, [\"data\", \"root\", \"interface\"]) || {}\n );\n },\n\n getInterfaceCode: function(uiName) {\n var itf = this.view.m.data.root.interface;\n var htmlString = htmltool.json2html(itf[uiName]);\n var prettyString = htmltool.htmlPrettyPrint(htmlString);\n return prettyString;\n },\n\n getSelectedInterfaceCode: function() {\n return this.selectedInterface\n ? this.getInterfaceCode(this.selectedInterface)\n : \"\";\n },\n\n selectInterface: function(e) {\n // Load the interface HTML when selected\n var uiName = $(e.target).val();\n this.selectedInterface = uiName;\n if (uiName !== \"\" && uiName !== \"Load UI...\") {\n var prettyString = this.getInterfaceCode(uiName);\n this.htmlCode.setValue(prettyString);\n } else {\n this.htmlCode.setValue(\"\");\n }\n },\n saveInterfaceElement: function(e) {\n var currentInterface = $(this.$el)\n .find(\".app-interface-select\")\n .val();\n if (currentInterface !== \"\") {\n var currentHTML = this.htmlCode.getValue();\n\n // save html code into app\n this.view.m.data.root.interface[\n currentInterface\n ] = htmltool.html2json(currentHTML);\n }\n\n // save css code into app\n this.view.m.data.root.css = this.cssCode.getValue();\n this.view.m.save();\n },\n addInterface: function(e) {\n var self = this;\n\n swal.mixin({\n confirmButtonText: \"Next &rarr;\",\n showCancelButton: true,\n progressSteps: [\"1\", \"2\", \"3\"]\n })\n .queue([\n {\n input: \"text\",\n title: \"Choose a name\",\n text: \"Enter a name for the new interface\"\n },\n {\n input: \"select\",\n title: \"Choose the type\",\n text: \"Is it a viewer panel or a control panel?\",\n inputOptions: {\n control: \"A control panel\",\n viewer: \"A viewer\"\n }\n },\n {\n input: \"select\",\n title: \"Choose the position\",\n text: \"Where do you want to position your panel?\",\n inputOptions: {\n \"top-left\": \"At the top-left\",\n \"top-center\": \"At the top-center\",\n \"top-right\": \"At the top-right\",\n \"center-left\": \"At the center-left\",\n center: \"At the center\",\n \"center-right\": \"At the center-right\",\n \"bottom-left\": \"At the bottom-left\",\n \"bottom-center\": \"At the bottom-center\",\n \"bottom-right\": \"At the bottom-right\",\n \"whole-screen\": \"I want my panel in full-screen\"\n }\n }\n ])\n .then(result => {\n if (result.value) {\n var name = result.value[0];\n var type = result.value[1];\n var position = result.value[2];\n\n if (name === \"\") {\n swal.showInputError(\"the name is empty!\");\n return false;\n }\n\n var appInterface = self.view.m.data.root.interface;\n if (appInterface[name]) {\n swal.showInputError(\n \"Interface \" + name + \" already exists!\"\n );\n return false;\n } else {\n var style = {};\n var cssPositionClass = \"dualbox-panel-\" + position;\n\n // add a basic control\n appInterface[name] = {\n type: \"Element\",\n tagName: \"div\",\n attributes: {\n className: [\n \"dualbox\",\n \"dualbox-container\",\n \"dualbox-container-\" + name,\n type == \"viewer\"\n ? \"dualbox-viewer dualbox-mobile-h60\"\n : \"dualbox-controls\",\n \"dualbox-panel\",\n cssPositionClass\n ]\n },\n children: []\n };\n\n // add the value to our select, and load it\n $(self.$el)\n .find(\".app-interface-select\")\n .append(\n $(\"<option/>\", {\n value: name\n }).append(name)\n );\n $(document).ready(() => {\n $(self.$el)\n .find(\".app-interface-select\")\n .val(name)\n .change();\n\n // set it into the editor\n var htmlString = htmltool.json2html(\n appInterface[name]\n );\n var prettyString = htmltool.htmlPrettyPrint(\n htmlString\n );\n this.htmlCode.setValue(prettyString);\n self.updateCode();\n });\n\n self.view.m.save();\n self.view.runApp(self.getOptions());\n }\n }\n });\n },\n removeInterface: function(e) {\n var self = this;\n var name = $(\".app-interface-select\").val();\n\n swal({\n title: \"Confirm deleting \" + name + \" ?\",\n type: \"warning\",\n showCancelButton: true,\n confirmButtonColor: \"#DD6B55\",\n confirmButtonText: \"Yes, delete it!\",\n closeOnConfirm: true,\n closeOnCancel: true\n }).then(result => {\n if (result.value) {\n // set the interface back to first value\n $(this.$el)\n .find(\n \".app-interface-select option[value='\" + name + \"']\"\n )\n .remove();\n $(this.$el)\n .find(\".app-interface-select\")\n .change();\n delete self.view.m.data.root.interface[name];\n self.htmlCode.setValue(\"\");\n self.view.m.save();\n self.view.runApp(self.getOptions());\n }\n });\n },\n editPanelDescription: function(e) {\n var self = this;\n var name = $(\".app-interface-select\").val();\n\n swal({\n title: \"Enter a description for this panel!\",\n input: \"textarea\",\n inputValue:\n this.view.m.data.root.interface[name].description || \"\",\n showCancelButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true,\n animation: \"slide-from-top\",\n inputPlaceholder: \"Write something\"\n }).then(result => {\n if (result.value === \"\") {\n swal.showInputError(\"You need to write something!\");\n return false;\n } else {\n self.view.m.data.root.interface[name].description =\n result.value;\n }\n });\n },\n toggleLeftWindow: function(e) {\n var expanded = $(e.target)\n .closest(\"button\")\n .data(\"expanded\");\n if (expanded) {\n $(this.$el).trigger(\"shrinkSettings\");\n } else {\n $(this.$el).trigger(\"expandSettings\");\n }\n },\n toggleRightWindow: function(e) {\n var expanded = $(e.target)\n .closest(\"button\")\n .data(\"expanded\");\n if (expanded) {\n $(this.$el).trigger(\"shrinkDebug\");\n } else {\n $(this.$el).trigger(\"expandDebug\");\n }\n },\n\n showEvents: function(e) {\n //this.view.setEventsVisibility( $(e.target).is(':checked') );\n this.displayEvents = $(e.target).is(\":checked\");\n },\n\n runApp: function(e) {\n this.view.runApp(this.getOptions());\n },\n\n takeAndLoadSnapshot: function(e) {\n this.view.takeAndLoadSnapshot();\n },\n\n undo: function(e) {\n this.view.c.undo();\n },\n\n redo: function(e) {\n this.view.c.redo();\n },\n\n removeSelection: function(e) {\n this.view.c.deleteSelection();\n },\n\n mergeSelection: function(e) {\n this.view.c.mergeSelection();\n },\n\n setMainMenu: async function(e) {\n await this.view.setMainMenu();\n },\n\n getCurrentApplication: function(e) {\n // trick to make this function reactive to \"app\" field update\n if (this.app) {\n return this.view.m.getCurrentApp(true);\n } else {\n throw \"Error: no app found\";\n }\n },\n\n onEdited: function(e) {\n console.log(\"Something was edited, forcing new render\");\n this.$forceUpdate();\n },\n\n ready: function() {\n return new Promise(resolve => {\n this.$nextTick(() => {\n window.requestAnimationFrame(() => {\n $(this.$el).ready(resolve);\n });\n });\n });\n }\n }\n};\n</script>\n"]}, media: undefined });
89864
+ inject("data-v-4f46e4b2_0", { source: "\n.code-panel {\n width: 30%;\n height: 100%;\n vertical-align: top;\n float: left;\n}\n.application-container {\n width: calc(70% - 5px);\n height: 100%;\n display: inline-block;\n vertical-align: top;\n float: right;\n}\n.code-controls,\n.run-options {\n width: 100%;\n height: 60px;\n}\n.application {\n width: 100%;\n height: calc(100% - 60px);\n background-color: #ccc;\n}\n.dragbar {\n height: 100%;\n width: 5px;\n background-color: #e7e7e7;\n display: inline-block;\n cursor: col-resize;\n}\n.code-content {\n position: relative;\n height: calc(100% - 56px);\n}\n.code-html-container {\n position: relative;\n height: 50%;\n border-bottom: 1px solid #e7e7e7;\n}\n.code-css-container {\n position: relative;\n height: calc(50% - 1px);\n overflow: hidden;\n}\n.code-css {\n width: 100%;\n height: 100%;\n}\n.code-html {\n height: calc(100% - 40px);\n width: 100%;\n}\n.CodeMirror {\n height: 100% !important;\n}\n.code-badge {\n position: absolute;\n top: 10px;\n right: 10px;\n padding: 10px;\n}\n.btn-light.focus,\n.btn-light:focus {\n box-shadow: none !important;\n}\n.button-bar {\n width: 60px;\n padding: 10px;\n border-bottom: 1px solid #e7e7e7;\n width: 100%;\n background-color: white;\n}\n.noselect {\n -webkit-user-select: none;\n /* Chrome/Safari */\n -moz-user-select: none;\n /* Firefox */\n -ms-user-select: none;\n /* IE10+ */\n -o-user-select: none;\n user-select: none;\n}\n.btn + .btn {\n margin-left: 5px;\n}\n.btn-graph-goto {\n box-shadow: none !important;\n}\n.nav-tabs {\n border-bottom: none;\n}\n.main-navigation a.nav-link {\n color: white;\n}\n.main-navigation a.nav-link.active {\n color: #212529;\n}\n.dualbox-editor-body {\n width: 100%;\n height: 100%;\n overflow: hidden;\n background-color: rgb(85, 85, 85);\n}\n.dualbox-graph-left-section {\n height: 100%;\n width: 500px;\n float: left;\n position: relative;\n overflow: hidden;\n margin-left: -465px;\n transition: margin-left 0.3s ease;\n}\n.dualbox-graph-left-window {\n width: calc(100% - 35px);\n height: calc(100% - 10px);\n margin-top: 10px;\n /* background-color: #ECF2F8; */\n background-color: #f8f9fa;\n /* border: 1px solid grey; */\n border-radius: 5px;\n position: relative;\n float: left;\n}\n.dualbox-graph-left-panel {\n width: 100%;\n height: 100%;\n}\n.btn-toggle-left-window {\n position: absolute;\n right: 0;\n top: 0;\n margin-right: -35px;\n z-index: 100;\n border-top-left-radius: 0px;\n border-bottom-left-radius: 0px;\n}\n.btn-toggle-left-window:hover,\n.btn-toggle-right-window:hover,\n.btn-toggle-left-window:focus,\n.btn-toggle-right-window:focus,\n.btn-toggle-left-window:active,\n.btn-toggle-right-window:active,\n.btn-toggle-left-window:active:hover,\n.btn-toggle-right-window:active:hover {\n color: #212529 !important;\n background-color: #f8f9fa !important;\n border-color: #f8f9fa !important;\n}\n.dualbox-graph-right-section {\n height: 100%;\n width: 500px;\n float: right;\n position: relative;\n overflow: hidden;\n margin-right: -465px;\n transition: margin-right 0.3s ease;\n}\n.dualbox-graph-right-window {\n width: calc(100% - 35px);\n height: calc(100% - 20px);\n margin-top: 10px;\n margin-bottom: 30px;\n /* background-color: #ECF2F8; */\n background-color: #f8f9fa;\n /* border: 1px solid grey; */\n border-radius: 5px;\n position: relative;\n float: right;\n}\n.dualbox-graph-right-panel {\n width: 100%;\n height: 100%;\n}\n.btn-toggle-right-window {\n position: absolute;\n left: 0;\n top: 0;\n margin-left: -35px;\n z-index: 100;\n border-top-right-radius: 0px;\n border-bottom-right-radius: 0px;\n}\n.dualbox-graph-tab {\n height: 100%;\n width: 100%;\n background-color: #555 !important;\n}\n.dualbox-graph-main {\n height: 100%;\n width: calc(100% - 70px);\n float: right;\n background-color: #555 !important;\n transition: width 0.3s ease;\n}\n.dualbox-graph-main.left-panel-expanded {\n width: calc(100% - 535px);\n margin-left: -465px;\n}\n.dualbox-graph-main.right-panel-expanded {\n width: calc(100% - 535px);\n}\n.dualbox-graph-main.left-panel-expanded.right-panel-expanded {\n width: calc(100% - 1000px);\n margin-left: -465px;\n}\n.opacity0 {\n opacity: 0;\n}\n.btn-editor-xs {\n width: 18px;\n padding: 3px !important;\n line-height: 0.5;\n border-radius: 2px;\n}\n.btn-editor-xs > i {\n font-size: 10px;\n}\n.btn-outline-discrete {\n border-color: rgba(0, 0, 0, 0.05);\n border-color: transparent;\n color: rgba(0, 0, 0, 0.3);\n}\n.dualbox-app-navigation {\n background-color: transparent;\n margin-bottom: 0;\n vertical-align: middle;\n padding-top: 7px;\n padding-bottom: 7px;\n font-weight: bold;\n user-select: none;\n}\n.app-topbar {\n border-bottom: none;\n padding-left: 15px;\n padding-right: 15px;\n display: flex;\n width: 100%;\n height: 58px;\n padding-top: 10px;\n padding-bottom: 10px;\n}\n.dark,\n.graph-tab.active,\n.nav-item.active .graph-tab {\n color: white !important;\n background-color: #555 !important;\n}\n.light {\n color: #4d4d4d !important;\n background-color: #f8f9fa !important;\n}\n.main-navigation .nav-link,\n.main-navigation .nav-link.active,\n.nav-item.active .nav-link {\n border-bottom: none;\n border-left: none;\n border-top: none;\n border-right: none;\n position: relative;\n top: 1px;\n}\n.btn:focus,\nbutton:focus {\n outline: none;\n box-shadow: none;\n}\n.btn-transparent,\n.btn-transparent:hover,\n.btn-transparent:focus {\n background-color: transparent;\n}\n.connection-control {\n position: absolute;\n height: 14px;\n width: 14px;\n background-color: transparent;\n z-index: 19;\n cursor: move;\n}\n.connection-control.selected {\n border-color: #0066ff;\n box-shadow: 1px 1px 10px #0066ff;\n}\n.connection-label {\n z-index: 22 !important;\n color: #004d00 !important;\n background-color: white !important;\n padding: 4px 4px;\n border: 2px solid #004d00;\n font-size: 16px !important;\n border-radius: 5px;\n}\n.input-color-tag {\n background-color: #f2d600;\n}\n.output-color-tag {\n background-color: #ffab4a;\n}\n.ui-color-tag {\n background-color: #61bd4f;\n}\n.metanode-color-tag {\n background-color: #dddddd;\n}\n.input {\n}\n.output {\n}\n.fileUpload {\n position: relative;\n overflow: hidden;\n margin: 10px;\n}\n.fileUpload input.upload {\n position: absolute;\n top: 0;\n right: 0;\n margin: 0;\n padding: 0;\n font-size: 20px;\n cursor: pointer;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.module-edit-modal-body .btn + .btn {\n margin-bottom: 0;\n margin-left: 5px;\n}\n.btn-add-node {\n width: 46%;\n margin: 1%;\n}\nbutton.close {\n position: absolute;\n right: 0;\n margin-right: 5px !important;\n}\n.CodeMirror {\n height: auto;\n min-height: 300px;\n}\n.load-app,\n.save-app {\n margin-left: 5px;\n margin-right: 5px;\n}\n.btn-xs {\n padding: 0px 4px;\n font-size: 12px;\n}\n@media screen and (max-width: 1152px) {\n.code-panel {\n display: none;\n}\n.dragbar {\n display: none !important;\n}\n.application-container {\n width: 100%;\n}\n.run-options {\n display: none;\n}\n.application {\n height: 100%;\n}\n.navbar-right {\n display: none;\n}\n}\n", map: {"version":3,"sources":["/home/seb/dev/dualbox/editor/js/src/v/templates/main.vue"],"names":[],"mappings":";AACA;IACA,UAAA;IACA,YAAA;IACA,mBAAA;IACA,WAAA;AACA;AAEA;IACA,sBAAA;IACA,YAAA;IACA,qBAAA;IACA,mBAAA;IACA,YAAA;AACA;AAEA;;IAEA,WAAA;IACA,YAAA;AACA;AAEA;IACA,WAAA;IACA,yBAAA;IACA,sBAAA;AACA;AAEA;IACA,YAAA;IACA,UAAA;IACA,yBAAA;IACA,qBAAA;IACA,kBAAA;AACA;AAEA;IACA,kBAAA;IACA,yBAAA;AACA;AAEA;IACA,kBAAA;IACA,WAAA;IACA,gCAAA;AACA;AAEA;IACA,kBAAA;IACA,uBAAA;IACA,gBAAA;AACA;AAEA;IACA,WAAA;IACA,YAAA;AACA;AAEA;IACA,yBAAA;IACA,WAAA;AACA;AAEA;IACA,uBAAA;AACA;AAEA;IACA,kBAAA;IACA,SAAA;IACA,WAAA;IACA,aAAA;AACA;AAEA;;IAEA,2BAAA;AACA;AAEA;IACA,WAAA;IACA,aAAA;IACA,gCAAA;IACA,WAAA;IACA,uBAAA;AACA;AAEA;IACA,yBAAA;IACA,kBAAA;IACA,sBAAA;IACA,YAAA;IACA,qBAAA;IACA,UAAA;IACA,oBAAA;IACA,iBAAA;AACA;AAEA;IACA,gBAAA;AACA;AAEA;IACA,2BAAA;AACA;AAEA;IACA,mBAAA;AACA;AAEA;IACA,YAAA;AACA;AAEA;IACA,cAAA;AACA;AAEA;IACA,WAAA;IACA,YAAA;IACA,gBAAA;IACA,iCAAA;AACA;AAEA;IACA,YAAA;IACA,YAAA;IACA,WAAA;IACA,kBAAA;IACA,gBAAA;IACA,mBAAA;IACA,iCAAA;AACA;AAEA;IACA,wBAAA;IACA,yBAAA;IACA,gBAAA;IACA,+BAAA;IACA,yBAAA;IACA,4BAAA;IACA,kBAAA;IACA,kBAAA;IACA,WAAA;AACA;AAEA;IACA,WAAA;IACA,YAAA;AACA;AAEA;IACA,kBAAA;IACA,QAAA;IACA,MAAA;IACA,mBAAA;IACA,YAAA;IACA,2BAAA;IACA,8BAAA;AACA;AAEA;;;;;;;;IAQA,yBAAA;IACA,oCAAA;IACA,gCAAA;AACA;AAEA;IACA,YAAA;IACA,YAAA;IACA,YAAA;IACA,kBAAA;IACA,gBAAA;IACA,oBAAA;IACA,kCAAA;AACA;AAEA;IACA,wBAAA;IACA,yBAAA;IACA,gBAAA;IACA,mBAAA;IACA,+BAAA;IACA,yBAAA;IACA,4BAAA;IACA,kBAAA;IACA,kBAAA;IACA,YAAA;AACA;AAEA;IACA,WAAA;IACA,YAAA;AACA;AAEA;IACA,kBAAA;IACA,OAAA;IACA,MAAA;IACA,kBAAA;IACA,YAAA;IACA,4BAAA;IACA,+BAAA;AACA;AAEA;IACA,YAAA;IACA,WAAA;IACA,iCAAA;AACA;AAEA;IACA,YAAA;IACA,wBAAA;IACA,YAAA;IACA,iCAAA;IACA,2BAAA;AACA;AAEA;IACA,yBAAA;IACA,mBAAA;AACA;AAEA;IACA,yBAAA;AACA;AAEA;IACA,0BAAA;IACA,mBAAA;AACA;AAEA;IACA,UAAA;AACA;AAEA;IACA,WAAA;IACA,uBAAA;IACA,gBAAA;IACA,kBAAA;AACA;AAEA;IACA,eAAA;AACA;AAEA;IACA,iCAAA;IACA,yBAAA;IACA,yBAAA;AACA;AAEA;IACA,6BAAA;IACA,gBAAA;IACA,sBAAA;IACA,gBAAA;IACA,mBAAA;IACA,iBAAA;IACA,iBAAA;AACA;AAEA;IACA,mBAAA;IACA,kBAAA;IACA,mBAAA;IACA,aAAA;IACA,WAAA;IACA,YAAA;IACA,iBAAA;IACA,oBAAA;AACA;AAEA;;;IAGA,uBAAA;IACA,iCAAA;AACA;AAEA;IACA,yBAAA;IACA,oCAAA;AACA;AAEA;;;IAGA,mBAAA;IACA,iBAAA;IACA,gBAAA;IACA,kBAAA;IACA,kBAAA;IACA,QAAA;AACA;AAEA;;IAEA,aAAA;IACA,gBAAA;AACA;AAEA;;;IAGA,6BAAA;AACA;AAEA;IACA,kBAAA;IACA,YAAA;IACA,WAAA;IACA,6BAAA;IACA,WAAA;IACA,YAAA;AACA;AAEA;IACA,qBAAA;IACA,gCAAA;AACA;AAEA;IACA,sBAAA;IACA,yBAAA;IACA,kCAAA;IACA,gBAAA;IACA,yBAAA;IACA,0BAAA;IACA,kBAAA;AACA;AAEA;IACA,yBAAA;AACA;AAEA;IACA,yBAAA;AACA;AAEA;IACA,yBAAA;AACA;AAEA;IACA,yBAAA;AACA;AAEA;AACA;AAEA;AACA;AAEA;IACA,kBAAA;IACA,gBAAA;IACA,YAAA;AACA;AAEA;IACA,kBAAA;IACA,MAAA;IACA,QAAA;IACA,SAAA;IACA,UAAA;IACA,eAAA;IACA,eAAA;IACA,UAAA;IACA,wBAAA;AACA;AAEA;IACA,gBAAA;IACA,gBAAA;AACA;AAEA;IACA,UAAA;IACA,UAAA;AACA;AAEA;IACA,kBAAA;IACA,QAAA;IACA,4BAAA;AACA;AAEA;IACA,YAAA;IACA,iBAAA;AACA;AAEA;;IAEA,gBAAA;IACA,iBAAA;AACA;AAEA;IACA,gBAAA;IACA,eAAA;AACA;AAEA;AACA;QACA,aAAA;AACA;AAEA;QACA,wBAAA;AACA;AAEA;QACA,WAAA;AACA;AAEA;QACA,aAAA;AACA;AAEA;QACA,YAAA;AACA;AAEA;QACA,aAAA;AACA;AACA","file":"main.vue","sourcesContent":["<style>\n.code-panel {\n width: 30%;\n height: 100%;\n vertical-align: top;\n float: left;\n}\n\n.application-container {\n width: calc(70% - 5px);\n height: 100%;\n display: inline-block;\n vertical-align: top;\n float: right;\n}\n\n.code-controls,\n.run-options {\n width: 100%;\n height: 60px;\n}\n\n.application {\n width: 100%;\n height: calc(100% - 60px);\n background-color: #ccc;\n}\n\n.dragbar {\n height: 100%;\n width: 5px;\n background-color: #e7e7e7;\n display: inline-block;\n cursor: col-resize;\n}\n\n.code-content {\n position: relative;\n height: calc(100% - 56px);\n}\n\n.code-html-container {\n position: relative;\n height: 50%;\n border-bottom: 1px solid #e7e7e7;\n}\n\n.code-css-container {\n position: relative;\n height: calc(50% - 1px);\n overflow: hidden;\n}\n\n.code-css {\n width: 100%;\n height: 100%;\n}\n\n.code-html {\n height: calc(100% - 40px);\n width: 100%;\n}\n\n.CodeMirror {\n height: 100% !important;\n}\n\n.code-badge {\n position: absolute;\n top: 10px;\n right: 10px;\n padding: 10px;\n}\n\n.btn-light.focus,\n.btn-light:focus {\n box-shadow: none !important;\n}\n\n.button-bar {\n width: 60px;\n padding: 10px;\n border-bottom: 1px solid #e7e7e7;\n width: 100%;\n background-color: white;\n}\n\n.noselect {\n -webkit-user-select: none;\n /* Chrome/Safari */\n -moz-user-select: none;\n /* Firefox */\n -ms-user-select: none;\n /* IE10+ */\n -o-user-select: none;\n user-select: none;\n}\n\n.btn + .btn {\n margin-left: 5px;\n}\n\n.btn-graph-goto {\n box-shadow: none !important;\n}\n\n.nav-tabs {\n border-bottom: none;\n}\n\n.main-navigation a.nav-link {\n color: white;\n}\n\n.main-navigation a.nav-link.active {\n color: #212529;\n}\n\n.dualbox-editor-body {\n width: 100%;\n height: 100%;\n overflow: hidden;\n background-color: rgb(85, 85, 85);\n}\n\n.dualbox-graph-left-section {\n height: 100%;\n width: 500px;\n float: left;\n position: relative;\n overflow: hidden;\n margin-left: -465px;\n transition: margin-left 0.3s ease;\n}\n\n.dualbox-graph-left-window {\n width: calc(100% - 35px);\n height: calc(100% - 10px);\n margin-top: 10px;\n /* background-color: #ECF2F8; */\n background-color: #f8f9fa;\n /* border: 1px solid grey; */\n border-radius: 5px;\n position: relative;\n float: left;\n}\n\n.dualbox-graph-left-panel {\n width: 100%;\n height: 100%;\n}\n\n.btn-toggle-left-window {\n position: absolute;\n right: 0;\n top: 0;\n margin-right: -35px;\n z-index: 100;\n border-top-left-radius: 0px;\n border-bottom-left-radius: 0px;\n}\n\n.btn-toggle-left-window:hover,\n.btn-toggle-right-window:hover,\n.btn-toggle-left-window:focus,\n.btn-toggle-right-window:focus,\n.btn-toggle-left-window:active,\n.btn-toggle-right-window:active,\n.btn-toggle-left-window:active:hover,\n.btn-toggle-right-window:active:hover {\n color: #212529 !important;\n background-color: #f8f9fa !important;\n border-color: #f8f9fa !important;\n}\n\n.dualbox-graph-right-section {\n height: 100%;\n width: 500px;\n float: right;\n position: relative;\n overflow: hidden;\n margin-right: -465px;\n transition: margin-right 0.3s ease;\n}\n\n.dualbox-graph-right-window {\n width: calc(100% - 35px);\n height: calc(100% - 20px);\n margin-top: 10px;\n margin-bottom: 30px;\n /* background-color: #ECF2F8; */\n background-color: #f8f9fa;\n /* border: 1px solid grey; */\n border-radius: 5px;\n position: relative;\n float: right;\n}\n\n.dualbox-graph-right-panel {\n width: 100%;\n height: 100%;\n}\n\n.btn-toggle-right-window {\n position: absolute;\n left: 0;\n top: 0;\n margin-left: -35px;\n z-index: 100;\n border-top-right-radius: 0px;\n border-bottom-right-radius: 0px;\n}\n\n.dualbox-graph-tab {\n height: 100%;\n width: 100%;\n background-color: #555 !important;\n}\n\n.dualbox-graph-main {\n height: 100%;\n width: calc(100% - 70px);\n float: right;\n background-color: #555 !important;\n transition: width 0.3s ease;\n}\n\n.dualbox-graph-main.left-panel-expanded {\n width: calc(100% - 535px);\n margin-left: -465px;\n}\n\n.dualbox-graph-main.right-panel-expanded {\n width: calc(100% - 535px);\n}\n\n.dualbox-graph-main.left-panel-expanded.right-panel-expanded {\n width: calc(100% - 1000px);\n margin-left: -465px;\n}\n\n.opacity0 {\n opacity: 0;\n}\n\n.btn-editor-xs {\n width: 18px;\n padding: 3px !important;\n line-height: 0.5;\n border-radius: 2px;\n}\n\n.btn-editor-xs > i {\n font-size: 10px;\n}\n\n.btn-outline-discrete {\n border-color: rgba(0, 0, 0, 0.05);\n border-color: transparent;\n color: rgba(0, 0, 0, 0.3);\n}\n\n.dualbox-app-navigation {\n background-color: transparent;\n margin-bottom: 0;\n vertical-align: middle;\n padding-top: 7px;\n padding-bottom: 7px;\n font-weight: bold;\n user-select: none;\n}\n\n.app-topbar {\n border-bottom: none;\n padding-left: 15px;\n padding-right: 15px;\n display: flex;\n width: 100%;\n height: 58px;\n padding-top: 10px;\n padding-bottom: 10px;\n}\n\n.dark,\n.graph-tab.active,\n.nav-item.active .graph-tab {\n color: white !important;\n background-color: #555 !important;\n}\n\n.light {\n color: #4d4d4d !important;\n background-color: #f8f9fa !important;\n}\n\n.main-navigation .nav-link,\n.main-navigation .nav-link.active,\n.nav-item.active .nav-link {\n border-bottom: none;\n border-left: none;\n border-top: none;\n border-right: none;\n position: relative;\n top: 1px;\n}\n\n.btn:focus,\nbutton:focus {\n outline: none;\n box-shadow: none;\n}\n\n.btn-transparent,\n.btn-transparent:hover,\n.btn-transparent:focus {\n background-color: transparent;\n}\n\n.connection-control {\n position: absolute;\n height: 14px;\n width: 14px;\n background-color: transparent;\n z-index: 19;\n cursor: move;\n}\n\n.connection-control.selected {\n border-color: #0066ff;\n box-shadow: 1px 1px 10px #0066ff;\n}\n\n.connection-label {\n z-index: 22 !important;\n color: #004d00 !important;\n background-color: white !important;\n padding: 4px 4px;\n border: 2px solid #004d00;\n font-size: 16px !important;\n border-radius: 5px;\n}\n\n.input-color-tag {\n background-color: #f2d600;\n}\n\n.output-color-tag {\n background-color: #ffab4a;\n}\n\n.ui-color-tag {\n background-color: #61bd4f;\n}\n\n.metanode-color-tag {\n background-color: #dddddd;\n}\n\n.input {\n}\n\n.output {\n}\n\n.fileUpload {\n position: relative;\n overflow: hidden;\n margin: 10px;\n}\n\n.fileUpload input.upload {\n position: absolute;\n top: 0;\n right: 0;\n margin: 0;\n padding: 0;\n font-size: 20px;\n cursor: pointer;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n\n.module-edit-modal-body .btn + .btn {\n margin-bottom: 0;\n margin-left: 5px;\n}\n\n.btn-add-node {\n width: 46%;\n margin: 1%;\n}\n\nbutton.close {\n position: absolute;\n right: 0;\n margin-right: 5px !important;\n}\n\n.CodeMirror {\n height: auto;\n min-height: 300px;\n}\n\n.load-app,\n.save-app {\n margin-left: 5px;\n margin-right: 5px;\n}\n\n.btn-xs {\n padding: 0px 4px;\n font-size: 12px;\n}\n\n@media screen and (max-width: 1152px) {\n .code-panel {\n display: none;\n }\n\n .dragbar {\n display: none !important;\n }\n\n .application-container {\n width: 100%;\n }\n\n .run-options {\n display: none;\n }\n\n .application {\n height: 100%;\n }\n\n .navbar-right {\n display: none;\n }\n}\n</style>\n\n<template>\n <div class=\"dualbox-editor-body\">\n <nav class=\"main-navigation navbar navbar-default\" style=\"position: relative; margin-bottom: 0px; padding: 0; padding-top: 5px; background-color: #0B6D80;\">\n <ul class=\"nav nav-tabs\">\n <li class=\"nav-item active\">\n <a class=\"nav-link graph-tab\" href=\"#editor-graph-tab\" data-toggle=\"tab\">Application Graph</a>\n </li>\n <li class=\"nav-item\">\n <a class=\"nav-link interface-tab\" href=\"#editor-interface-tab\" data-toggle=\"tab\">Interface Editor</a>\n </li>\n </ul>\n\n <div class=\"nav navbar-right\" style=\"vertical-align: top; margin-top: 5px; margin-right: 10px; margin-bottom: 5px;\">\n <div v-if=\"showLoadButton === true\">\n <button class=\"load-app btn btn-sm btn-light\" style=\"position: relative; bottom: 3px;\" @click=\"loadApp\">Load App</button>\n </div>\n <div v-if=\"showSaveButton\">\n <button class=\"save-app btn btn-sm btn-light\" style=\"position: relative; bottom: 3px;\" @click=\"saveApp\">Save App</button>\n </div>\n </div>\n </nav>\n <div class=\"tab-content dualbox-graph-tab\" style=\"width: 100%; height: calc(100% - 46px);\">\n <div class=\"tab-pane active\" id=\"editor-graph-tab\" style=\"width: 100%; height: 100%;\">\n <div class=\"dualbox-graph-left-section dark\">\n <div class=\"dualbox-graph-left-window\">\n <button class=\"btn btn-light btn-toggle-left-window\" @click=\"toggleLeftWindow\" title=\"shrink window\" data-expanded=\"false\"><i class=\"fa fa-angle-double-right\"></i></button>\n <div class=\"dualbox-graph-left-panel light\">\n <edit-node-settings v-if=\"leftPanelMode == 'node' && selectedNodeId !== null\" :id=\"selectedNodeId\" :key=\"selectedNodeId\"></edit-node-settings>\n <edit-main-settings v-else-if=\"leftPanelMode == 'main'\" :app=\"getCurrentApplication()\" @edited=\"onEdited()\"></edit-main-settings>\n </div>\n </div>\n </div>\n <div class=\"dualbox-graph-right-section dark\">\n <div class=\"dualbox-graph-right-window\">\n <button class=\"btn btn-light btn-toggle-right-window\" @click=\"toggleRightWindow\" title=\"shrink window\" data-expanded=\"false\"><i class=\"fa fa-angle-double-left\"></i></button>\n <div class=\"dualbox-graph-right-panel light\">\n <debug-node-infos v-if=\"debugNodeId !== null\" :id=\"debugNodeId\"></debug-node-infos>\n </div>\n </div>\n </div>\n <div class=\"container-fluid dualbox-graph-main\" @click=\"setMainMenu\">\n <div class=\"row\">\n <div class=\"app-topbar dark\">\n <div class=\"justify-content-left\">\n <div class=\"dropdown\">\n <button class=\"btn btn-primary dropdown-toggle d-none\" type=\"button\" id=\"add-node-dropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\" style=\"margin-right: 5px;\">\n Add\n </button>\n <div class=\"dropdown-menu\" aria-labelledby=\"add-node-dropdown\">\n <a class=\"dropdown-item add-box\" @click=\"addBox\" href=\"#\">Add box</a>\n <a class=\"dropdown-item add-metabox\" @click=\"addMetabox\" href=\"#\">Add metabox</a>\n <a class=\"dropdown-item add-input\" @click=\"addInput\" href=\"#\">Add input</a>\n <a class=\"dropdown-item add-output\" @click=\"addOutput\" href=\"#\">Add output</a>\n <a class=\"dropdown-item import-metabox\" @click=\"importMetabox\" href=\"#\">Import metabox</a>\n </div>\n <button class=\"btn btn-light btn-undo\" title=\"undo (ctrl-z)\" @click=\"undo\"><i class=\"fa fa-undo\"></i></button>\n <button class=\"btn btn-light btn-redo\" title=\"redo (ctrl-y)\" @click=\"redo\"><i class=\"fa fa-repeat fa-redo\"></i></button>\n <div class=\"form-check d-inline-block ml-2\">\n <label class=\"form-check-label\">\n <input class=\"form-check-input show-events\" type=\"checkbox\" value=\"false\" @click=\"showEvents\">\n <span class=\"noselect\">Show events</span>\n </label>\n </div>\n </div>\n </div>\n\n <div class=\"justify-content-center mx-auto\">\n <ol class=\"dualbox-app-navigation breadcrumb\"></ol>\n </div>\n\n <div class=\"justify-content-right\">\n <div class=\"dropdown selection-menu\" style=\"display: none;\">\n <button class=\"btn btn-primary dropdown-toggle\" type=\"button\" id=\"selection-dropdown\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\">\n Selection\n </button>\n <div class=\"dropdown-menu dropdown-menu-right\" aria-labelledby=\"add-node-dropdown\">\n <a class=\"dropdown-item dualbox-merge-selection\" @click=\"mergeSelection\" href=\"#\">Merge</a>\n <a class=\"dropdown-item dualbox-remove-selection\" @click=\"removeSelection\" href=\"#\" style=\"color: red;\">Delete</a>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"row\" style=\"height: calc(100% - 58px);\">\n <graph-vue :app=\"app\" ref=\"graph\" :displayEvents=\"displayEvents\"></graph-vue>\n </div>\n </div>\n </div>\n <div class=\"tab-pane\" id=\"editor-interface-tab\" style=\"width 100%; height: 100%; background-color: white;\">\n <div class=\"db-editor-main\" style=\"width: 100%; height: 100%; overflow: hidden;\">\n <div class=\"code-panel\">\n <div class=\"button-bar form-inline code-controls\" style=\"position: relative;\">\n <button class=\"btn btn-primary btn-save-interface-element\" @click=\"saveInterfaceElement\">Save changes</button>\n </div>\n <div class=\"code-content\">\n <div class=\"code-html-container\">\n <div style=\"padding-top: 5px; padding-bottom: 5px; height: 50px; position: relative;\">\n <h3 style=\"position: absolute; top: 0; margin: 5px; font-size: 16px; z-index: 100; display: inline-block; margin-top: 5px;\">HTML</h3>\n <div class=\"d-inline-block ml-auto form-inline mr-2 mt-2\" style=\"position: absolute; top: 0; right: 0;\">\n <button class=\"btn btn-success btn-sm btn-add-interface\" @click=\"addInterface\"><i class=\"fas fa-plus\"></i></button>\n <select class=\"form-control btn-sm app-interface-select\" @change=\"selectInterface\" style=\"width: 150px; height: 32px;\">\n <option>Load UI...</option>\n <option v-for=\"interfaceName in getInterfacesNames()\" :key=\"interfaceName\" :value=\"interfaceName\">{{interfaceName}}</option>\n </select>\n <button class=\"btn btn-sm btn-light btn-edit-panel-description\" @click=\"editPanelDescription\"><i class=\"fas fa-info\" style=\"padding-left: 8px; padding-right: 8px;\"></i></button>\n <button class=\"btn btn-danger btn-sm btn-remove-interface\" @click=\"removeInterface\"><i class=\"fas fa-minus\"></i></button>\n </div>\n </div>\n <div class=\"code-html-text-container\" style=\"position: relative; height: calc(100% - 50px);\">\n <textarea class=\"code-html\" v-html=\"getSelectedInterfaceCode()\"></textarea>\n </div>\n </div>\n <div class=\"code-css-container\">\n <div style=\"height: 30px;\">\n <h3 style=\"position: absolute; top: 0; margin: 5px; font-size: 16px; z-index: 100;\">CSS</h3>\n </div>\n <div class=\"colde-css-text-container\" style=\"position: relative; height: calc(100% - 30px);\">\n <textarea class=\"code-css\" v-model=\"appCss\"></textarea>\n </div>\n </div>\n </div>\n </div>\n <div class=\"dragbar\"></div>\n <div class=\"application-container\">\n <div class=\"button-bar form-inline run-options\" style=\"position: relative;\">\n <button class=\"btn btn-success btn-run mr-2\" @click=\"runApp\">Run</button>\n\n <select class=\"form-control run-loglevel mr-1 ml-2\">\n <option>error</option>\n <option>warn</option>\n <option>info</option>\n <option>log</option>\n <option>debug</option>\n </select>\n\n <input class=\"form-control run-noversioncheck mr-1 ml-3\" type=\"checkbox\" checked>No version checking</input>\n <input class=\"form-control run-removetrycatch mr-1 ml-3\" type=\"checkbox\">Remove Try/catch</input>\n <input class=\"form-control run-makesynchrone mr-1 ml-3\" type=\"checkbox\">Make Synchrone</input>\n <input class=\"form-control run-ressourcecaching mr-1 ml-3\" type=\"checkbox\">Ressource caching</input>\n\n <div class=\"ml-auto\">\n <input class=\"form-control run-profiler mr-1 ml-3\" type=\"checkbox\">Profiler</input>\n <input class=\"form-control run-record mr-1 ml-3\" type=\"checkbox\">Record</input>\n <button class=\"btn btn-secondary btn-snapshot\" @click=\"takeAndLoadSnapshot\">Snapshot</button>\n </div>\n </div>\n <div class=\"application capture-left-click capture-right-click\"></div>\n </div>\n </div>\n </div>\n </div>\n <add-node-vue :display=\"addingBox\" :mousePosition=\"this.newBoxPosition\" @closed=\"onAddBoxClosed\"></add-node-vue>\n </div>\n</template>\n\n<script>\nimport _ from \"lodash-es\";\nimport swal from \"sweetalert2\";\n\nimport graphVue from \"./graph.vue\";\nimport addNodeVue from \"./addNode.vue\";\nimport editMainSettingsVue from \"./editMainSettings.vue\";\nimport editNodeSettingsVue from \"./editNodeSettings.vue\";\nimport debugNodeInfosVue from \"./debugNodeInfos.vue\";\n\n// CodeMirror\nimport CodeMirror from \"codemirror/lib/codemirror.js\";\nimport \"codemirror/mode/xml/xml.js\";\nimport \"codemirror/mode/css/css.js\";\nimport \"codemirror/mode/javascript/javascript.js\";\nimport \"codemirror/mode/htmlmixed/htmlmixed.js\";\n\nimport htmltool from \"@dualbox/dualbox-lib-htmltool\";\n\nexport default {\n props: [\n \"app\", // the app\n\n // Display configuration (online or offline ?)\n \"showLoadButton\",\n \"showSaveButton\",\n \"saveButtonFunction\",\n\n // modes\n \"eventVisibility\" // are events visible\n ],\n components: {\n \"graph-vue\": graphVue,\n \"add-node-vue\": addNodeVue,\n \"edit-main-settings\": editMainSettingsVue,\n \"edit-node-settings\": editNodeSettingsVue,\n \"debug-node-infos\": debugNodeInfosVue\n },\n data() {\n return {\n // modes\n addingBox: false,\n leftPanelMode: \"main\", // 'main' to display main menu, 'node' for a node\n selectedNodeId: null, // id of the node to display in left menu\n debugNodeId: null, // on a snapshot, id of the node to display debug infos\n newBoxPosition: null, // track mouse position relative to the graph when adding a node\n displayEvents: false,\n selectedInterface: null,\n\n // the specific app css\n appCss: \"\"\n };\n },\n created: function() {\n this.view = window.dualboxEditor.v;\n // TODO: restore this when all menus are migrated to Vue.js\n\n this.cssCode = null;\n this.htmlCode = null;\n },\n updated: function() {\n //console.log('updating main view with: ' + JSON.stringify(this.app));\n this.appCss = (this.app && this.app.css) || \"\";\n\n // Update html and css code editor\n this.updateCode();\n },\n mounted: function() {\n //this.view.setMainMenu();\n\n $(this.$el).bind(\"expandSettings\", function(e) {\n // expand\n $(this)\n .find(\".dualbox-graph-left-section\")\n .css(\"margin-left\", \"0\");\n $(this)\n .find(\".dualbox-graph-main\")\n .addClass(\"left-panel-expanded\");\n $(this)\n .find(\".btn-toggle-left-window\")\n .data(\"expanded\", true)\n .find(\"i\")\n .removeClass(\"fa-angle-double-right\")\n .addClass(\"fa-angle-double-left\")\n .attr(\"title\", \"shrink window\");\n });\n\n $(this.$el).bind(\"shrinkSettings\", function(e) {\n // shrink\n $(this)\n .find(\".dualbox-graph-left-section\")\n .css(\"margin-left\", \"-465px\");\n $(this)\n .find(\".dualbox-graph-main\")\n .removeClass(\"left-panel-expanded\");\n $(this)\n .find(\".btn-toggle-left-window\")\n .data(\"expanded\", false)\n .find(\"i\")\n .removeClass(\"fa-angle-double-left\")\n .addClass(\"fa-angle-double-right\")\n .attr(\"title\", \"expand window\");\n });\n\n $(this.$el).bind(\"expandDebug\", function(e) {\n // expand\n $(this)\n .find(\".dualbox-graph-right-section\")\n .css(\"margin-right\", \"0\");\n $(this)\n .find(\".dualbox-graph-main\")\n .addClass(\"right-panel-expanded\");\n $(this)\n .find(\".btn-toggle-right-window\")\n .data(\"expanded\", true)\n .find(\"i\")\n .removeClass(\"fa-angle-double-left\")\n .addClass(\"fa-angle-double-right\")\n .attr(\"title\", \"shrink window\");\n });\n\n $(this.$el).bind(\"shrinkDebug\", function(e) {\n // shrink\n $(this)\n .find(\".dualbox-graph-right-section\")\n .css(\"margin-right\", \"-465px\");\n $(this)\n .find(\".dualbox-graph-main\")\n .removeClass(\"right-panel-expanded\");\n $(this)\n .find(\".btn-toggle-right-window\")\n .data(\"expanded\", false)\n .find(\"i\")\n .removeClass(\"fa-angle-double-right\")\n .addClass(\"fa-angle-double-left\")\n .attr(\"title\", \"expand window\");\n });\n\n // bind tabs\n var self = this;\n $(this.$el)\n .find(\"a[data-toggle='tab']\")\n .on(\"shown.bs.tab\", function(e) {\n var target = $(e.target).attr(\"href\"); // activated tab\n if (target == \"#editor-graph-tab\") {\n self.view.killApp();\n } else if (target == \"#editor-interface-tab\") {\n self.htmlCode.refresh();\n self.cssCode.refresh();\n self.view.runApp(self.getOptions());\n }\n });\n\n $(this.$el)\n .find(\"a[data-toggle='tab']\")\n .on(\"click\", function(e) {\n e.preventDefault();\n $(this).tab(\"show\");\n });\n\n // Update html and css code editor\n this.updateCode();\n\n // Resize horizontally\n $(this.$el)\n .find(\".dragbar\")\n .mousedown(function(e) {\n e.preventDefault();\n $(document).mouseup(function(e) {\n $(document).unbind(\"mousemove\");\n });\n $(document).mousemove(function(e) {\n $(\".code-panel\").css(\"width\", e.pageX + \"px\");\n $(\".application-container\").css(\n \"width\",\n $(\".db-editor-main\").width() - e.pageX - 5 + \"px\"\n );\n });\n });\n\n // prevent default behavior for ctrl+s / cmd+s\n $(document).keydown(function(event) {\n if ((event.ctrlKey || event.metaKey) && event.which == 83) {\n event.preventDefault();\n return false;\n }\n });\n },\n destroyed: function() {\n console.log(\"Graph vue destroyed\");\n $(\".ContextMenu\").remove();\n },\n methods: {\n getOptions: function() {\n return {\n profiler: $(this.$el)\n .find(\".run-profiler\")\n .is(\":checked\"),\n logLevel: $(this.$el)\n .find(\".run-loglevel\")\n .val(),\n options: {\n noVersionCheck: $(this.$el)\n .find(\".run-noversioncheck\")\n .is(\":checked\"),\n debug: {\n removeTryCatch: $(this.$el)\n .find(\".run-removetrycatch\")\n .is(\":checked\"),\n makeSynchrone: $(this.$el)\n .find(\".run-makesynchrone\")\n .is(\":checked\"),\n record: $(this.$el)\n .find(\".run-record\")\n .is(\":checked\"),\n ressourceCaching: $(this.$el)\n .find(\".run-ressourcecaching\")\n .is(\":checked\")\n }\n }\n };\n },\n\n updateCode: function() {\n // instanciate codemirror for html and css\n if (!this.htmlCode) {\n this.htmlCode = CodeMirror.fromTextArea(\n $(this.$el).find(\".code-html\")[0],\n {\n lineNumbers: true,\n mode: \"htmlmixed\",\n lineWrapping: true,\n extraKeys: {\n \"Ctrl-S\": () => {\n this.saveInterfaceElement();\n this.runApp();\n },\n \"Cmd-S\": () => {\n this.saveInterfaceElement();\n this.runApp();\n }\n }\n }\n );\n }\n if (!this.cssCode) {\n this.cssCode = CodeMirror.fromTextArea(\n $(this.$el).find(\".code-css\")[0],\n {\n lineNumbers: true,\n mode: \"css\",\n lineWrapping: true,\n extraKeys: {\n \"Ctrl-S\": () => {\n // save css code into app\n this.view.m.data.root.css = this.cssCode.getValue();\n this.view.m.save();\n this.runApp();\n },\n \"Cmd-S\": () => {\n // save css code into app\n this.view.m.data.root.css = this.cssCode.getValue();\n this.view.m.save();\n this.runApp();\n }\n }\n }\n );\n }\n\n this.htmlCode.setValue(this.getSelectedInterfaceCode());\n this.cssCode.setValue(this.view.m.get().css || \"\");\n },\n\n loadApp: function() {\n var self = this;\n\n // create a fake input and click it to select a file\n var input = $(\"<input/>\", {\n type: \"file\",\n class: \"upload\",\n accept: \".json\"\n });\n input.change(function(e) {\n // if we're not here, go to 1st tab\n $(\"a[data-toggle='tab'][href='#editor-graph-tab']\").click();\n\n // parse the file JSON and load it\n var files = e.target.files; // FileList object\n var r = new FileReader();\n r.onload = function(e) {\n var contents = e.target.result;\n var json = JSON.parse(contents);\n self.view.e.setApp(json);\n };\n\n r.readAsText(files[0]);\n });\n input.click();\n },\n saveApp: function() {\n // bind the app load\n if (this.saveButtonFunction) {\n var saveButtonFunction = e => {\n var json = this.view.m.getCleanJson();\n return this.saveButtonFunction(json);\n };\n } else {\n var saveButtonFunction = e => {\n var app = this.view.m.getCleanJson();\n var text = JSON.stringify(app, null, 2);\n var blob = new Blob([text], {\n type: \"application/octet-stream\"\n });\n\n var a = document.createElement(\"a\");\n a.href = window.URL.createObjectURL(blob);\n a.download = \"app.json\";\n\n // simulate a click on the link\n if (document.createEvent) {\n var event = document.createEvent(\"MouseEvents\");\n event.initEvent(\"click\", true, true);\n a.dispatchEvent(event);\n } else {\n a.click();\n }\n };\n }\n saveButtonFunction();\n },\n getCurrentMousePosition: function(e) {\n return this.$refs.graph.getCurrentMousePosition(e);\n },\n addBox: function(e) {\n console.log('opening addbox modal');\n this.addingBox = true;\n this.newBoxPosition = this.getCurrentMousePosition(e);\n this.$forceUpdate();\n },\n onAddBoxClosed: function() {\n console.log('closing modal');\n this.addingBox = false;\n this.$forceUpdate();\n },\n addMetabox: function(e) {\n var self = this;\n e.preventDefault();\n e.stopPropagation();\n\n swal({\n input: \"text\",\n title: \"Choose a name for the metabox\"\n }).then(function(result) {\n if (result.value) {\n self.view.c.addNewMetabox(result.value);\n }\n });\n },\n addInput: function(e) {\n this.view.c.createInput();\n },\n addOutput: function(e) {\n this.view.c.createOutput();\n },\n importMetabox: async function(e) {\n var self = this;\n\n const { value: file } = await swal({\n title: \"Select your metabox file\",\n input: \"file\",\n inputAttributes: {\n accept: \"application/json\",\n \"aria-label\": \"Select your metabox file\"\n }\n });\n\n if (file) {\n const reader = new FileReader();\n reader.onload = e => {\n var json = JSON.parse(e.target.result);\n\n self.view.e\n .loadPackages(json)\n .then(async () => {\n const { value: name } = await swal({\n title: \"Choose a name for your metabox\",\n input: \"text\",\n showCancelButton: true,\n inputValidator: value => {\n return (\n !value && \"You need to write something!\"\n );\n }\n });\n\n self.view.c.addNewMetabox(name, json);\n })\n .catch(err => {\n console.error(err);\n });\n };\n reader.readAsText(file);\n }\n },\n\n getInterfacesNames: function() {\n return _.keys(\n _.get(this.view.m, [\"data\", \"root\", \"interface\"]) || {}\n );\n },\n\n getInterfaceCode: function(uiName) {\n var itf = this.view.m.data.root.interface;\n var htmlString = htmltool.json2html(itf[uiName]);\n var prettyString = htmltool.htmlPrettyPrint(htmlString);\n return prettyString;\n },\n\n getSelectedInterfaceCode: function() {\n return this.selectedInterface\n ? this.getInterfaceCode(this.selectedInterface)\n : \"\";\n },\n\n selectInterface: function(e) {\n // Load the interface HTML when selected\n var uiName = $(e.target).val();\n this.selectedInterface = uiName;\n if (uiName !== \"\" && uiName !== \"Load UI...\") {\n var prettyString = this.getInterfaceCode(uiName);\n this.htmlCode.setValue(prettyString);\n } else {\n this.htmlCode.setValue(\"\");\n }\n },\n saveInterfaceElement: function(e) {\n var currentInterface = $(this.$el)\n .find(\".app-interface-select\")\n .val();\n if (currentInterface !== \"\") {\n var currentHTML = this.htmlCode.getValue();\n\n // save html code into app\n this.view.m.data.root.interface[\n currentInterface\n ] = htmltool.html2json(currentHTML);\n }\n\n // save css code into app\n this.view.m.data.root.css = this.cssCode.getValue();\n this.view.m.save();\n },\n addInterface: function(e) {\n var self = this;\n\n swal.mixin({\n confirmButtonText: \"Next &rarr;\",\n showCancelButton: true,\n progressSteps: [\"1\", \"2\", \"3\"]\n })\n .queue([\n {\n input: \"text\",\n title: \"Choose a name\",\n text: \"Enter a name for the new interface\"\n },\n {\n input: \"select\",\n title: \"Choose the type\",\n text: \"Is it a viewer panel or a control panel?\",\n inputOptions: {\n control: \"A control panel\",\n viewer: \"A viewer\"\n }\n },\n {\n input: \"select\",\n title: \"Choose the position\",\n text: \"Where do you want to position your panel?\",\n inputOptions: {\n \"top-left\": \"At the top-left\",\n \"top-center\": \"At the top-center\",\n \"top-right\": \"At the top-right\",\n \"center-left\": \"At the center-left\",\n center: \"At the center\",\n \"center-right\": \"At the center-right\",\n \"bottom-left\": \"At the bottom-left\",\n \"bottom-center\": \"At the bottom-center\",\n \"bottom-right\": \"At the bottom-right\",\n \"whole-screen\": \"I want my panel in full-screen\"\n }\n }\n ])\n .then(result => {\n if (result.value) {\n var name = result.value[0];\n var type = result.value[1];\n var position = result.value[2];\n\n if (name === \"\") {\n swal.showInputError(\"the name is empty!\");\n return false;\n }\n\n var appInterface = self.view.m.data.root.interface;\n if (appInterface[name]) {\n swal.showInputError(\n \"Interface \" + name + \" already exists!\"\n );\n return false;\n } else {\n var style = {};\n var cssPositionClass = \"dualbox-panel-\" + position;\n\n // add a basic control\n appInterface[name] = {\n type: \"Element\",\n tagName: \"div\",\n attributes: {\n className: [\n \"dualbox\",\n \"dualbox-container\",\n \"dualbox-container-\" + name,\n type == \"viewer\"\n ? \"dualbox-viewer dualbox-mobile-h60\"\n : \"dualbox-controls\",\n \"dualbox-panel\",\n cssPositionClass\n ]\n },\n children: []\n };\n\n // add the value to our select, and load it\n // $(self.$el)\n // .find(\".app-interface-select\")\n // .append(\n // $(\"<option/>\", {\n // value: name\n // }).append(name)\n // );\n self.$forceUpdate();\n $(document).ready(() => {\n $(self.$el)\n .find(\".app-interface-select\")\n .val(name)\n .change();\n\n // set it into the editor\n var htmlString = htmltool.json2html(\n appInterface[name]\n );\n var prettyString = htmltool.htmlPrettyPrint(\n htmlString\n );\n this.htmlCode.setValue(prettyString);\n self.updateCode();\n });\n\n self.view.m.save();\n self.view.runApp(self.getOptions());\n }\n }\n });\n },\n removeInterface: function(e) {\n var self = this;\n var name = $(\".app-interface-select\").val();\n\n swal({\n title: \"Confirm deleting \" + name + \" ?\",\n type: \"warning\",\n showCancelButton: true,\n confirmButtonColor: \"#DD6B55\",\n confirmButtonText: \"Yes, delete it!\",\n closeOnConfirm: true,\n closeOnCancel: true\n }).then(result => {\n if (result.value) {\n // set the interface back to first value\n delete self.view.m.data.root.interface[name];\n self.htmlCode.setValue(\"\");\n self.view.m.save();\n self.view.m.repaint();\n self.view.runApp(self.getOptions());\n }\n });\n },\n editPanelDescription: function(e) {\n var self = this;\n var name = $(\".app-interface-select\").val();\n\n swal({\n title: \"Enter a description for this panel!\",\n input: \"textarea\",\n inputValue:\n this.view.m.data.root.interface[name].description || \"\",\n showCancelButton: true,\n closeOnConfirm: false,\n showLoaderOnConfirm: true,\n animation: \"slide-from-top\",\n inputPlaceholder: \"Write something\"\n }).then(result => {\n if (result.value === \"\") {\n swal.showInputError(\"You need to write something!\");\n return false;\n } else {\n self.view.m.data.root.interface[name].description =\n result.value;\n }\n });\n },\n toggleLeftWindow: function(e) {\n var expanded = $(e.target)\n .closest(\"button\")\n .data(\"expanded\");\n if (expanded) {\n $(this.$el).trigger(\"shrinkSettings\");\n } else {\n $(this.$el).trigger(\"expandSettings\");\n }\n },\n toggleRightWindow: function(e) {\n var expanded = $(e.target)\n .closest(\"button\")\n .data(\"expanded\");\n if (expanded) {\n $(this.$el).trigger(\"shrinkDebug\");\n } else {\n $(this.$el).trigger(\"expandDebug\");\n }\n },\n\n showEvents: function(e) {\n //this.view.setEventsVisibility( $(e.target).is(':checked') );\n this.displayEvents = $(e.target).is(\":checked\");\n },\n\n runApp: function(e) {\n this.view.runApp(this.getOptions());\n },\n\n takeAndLoadSnapshot: function(e) {\n this.view.takeAndLoadSnapshot();\n },\n\n undo: function(e) {\n this.view.c.undo();\n },\n\n redo: function(e) {\n this.view.c.redo();\n },\n\n removeSelection: function(e) {\n this.view.c.deleteSelection();\n },\n\n mergeSelection: function(e) {\n this.view.c.mergeSelection();\n },\n\n setMainMenu: async function(e) {\n await this.view.setMainMenu();\n },\n\n getCurrentApplication: function(e) {\n // trick to make this function reactive to \"app\" field update\n if (this.app) {\n return this.view.m.getCurrentApp(true);\n } else {\n throw \"Error: no app found\";\n }\n },\n\n onEdited: function(e) {\n console.log(\"Something was edited, forcing new render\");\n this.$forceUpdate();\n },\n\n ready: function() {\n return new Promise(resolve => {\n this.$nextTick(() => {\n window.requestAnimationFrame(() => {\n $(this.$el).ready(resolve);\n });\n });\n });\n }\n }\n};\n</script>\n"]}, media: undefined });
89773
89865
 
89774
89866
  };
89775
89867
  /* scoped */
@@ -91361,16 +91453,11 @@ class GraphController {
91361
91453
  var clipboard = await this.getClipboard();
91362
91454
  try {
91363
91455
  var json = JSON.parse(clipboard);
91364
- sweetalert2_all.queue([{
91365
- title: 'Dependencies required',
91366
- text: 'Dualbox will load additional required dependencies to paste your selection',
91367
- showLoaderOnConfirm: true,
91368
- preConfirm: async () => {
91369
- await this.e.loadPackages(json);
91370
- this.m.paste(json);
91371
- await this.v.repaint();
91372
- }
91373
- }]);
91456
+ await this.e.loadPackages(json);
91457
+ let validPaste = this.m.paste(json);
91458
+ if (validPaste) {
91459
+ await this.v.repaint();
91460
+ }
91374
91461
  } catch (e) {
91375
91462
  sweetalert2_all.fire("Invalid content for copy/paste", "You must first copy boxes from Dualbox", "error");
91376
91463
  return;
@@ -108186,6 +108273,281 @@ var defaultApp = {
108186
108273
  }
108187
108274
  };
108188
108275
 
108276
+ const FuzzySet = function(arr, useLevenshtein, gramSizeLower, gramSizeUpper) {
108277
+ var fuzzyset = {
108278
+
108279
+ };
108280
+
108281
+ // default options
108282
+ arr = arr || [];
108283
+ fuzzyset.gramSizeLower = gramSizeLower || 2;
108284
+ fuzzyset.gramSizeUpper = gramSizeUpper || 3;
108285
+ fuzzyset.useLevenshtein = (typeof useLevenshtein !== 'boolean') ? true : useLevenshtein;
108286
+
108287
+ // define all the object functions and attributes
108288
+ fuzzyset.exactSet = {};
108289
+ fuzzyset.matchDict = {};
108290
+ fuzzyset.items = {};
108291
+
108292
+ // helper functions
108293
+ var levenshtein = function(str1, str2) {
108294
+ var current = [], prev, value;
108295
+
108296
+ for (var i = 0; i <= str2.length; i++)
108297
+ for (var j = 0; j <= str1.length; j++) {
108298
+ if (i && j)
108299
+ if (str1.charAt(j - 1) === str2.charAt(i - 1))
108300
+ value = prev;
108301
+ else
108302
+ value = Math.min(current[j], current[j - 1], prev) + 1;
108303
+ else
108304
+ value = i + j;
108305
+
108306
+ prev = current[j];
108307
+ current[j] = value;
108308
+ }
108309
+
108310
+ return current.pop();
108311
+ };
108312
+
108313
+ // return an edit distance from 0 to 1
108314
+ var _distance = function(str1, str2) {
108315
+ if (str1 === null && str2 === null) throw 'Trying to compare two null values';
108316
+ if (str1 === null || str2 === null) return 0;
108317
+ str1 = String(str1); str2 = String(str2);
108318
+
108319
+ var distance = levenshtein(str1, str2);
108320
+ if (str1.length > str2.length) {
108321
+ return 1 - distance / str1.length;
108322
+ } else {
108323
+ return 1 - distance / str2.length;
108324
+ }
108325
+ };
108326
+ var _nonWordRe = /[^a-zA-Z0-9\u00C0-\u00FF, ]+/g;
108327
+
108328
+ var _iterateGrams = function(value, gramSize) {
108329
+ gramSize = gramSize || 2;
108330
+ var simplified = '-' + value.toLowerCase().replace(_nonWordRe, '') + '-',
108331
+ lenDiff = gramSize - simplified.length,
108332
+ results = [];
108333
+ if (lenDiff > 0) {
108334
+ for (var i = 0; i < lenDiff; ++i) {
108335
+ simplified += '-';
108336
+ }
108337
+ }
108338
+ for (var i = 0; i < simplified.length - gramSize + 1; ++i) {
108339
+ results.push(simplified.slice(i, i + gramSize));
108340
+ }
108341
+ return results;
108342
+ };
108343
+
108344
+ var _gramCounter = function(value, gramSize) {
108345
+ // return an object where key=gram, value=number of occurrences
108346
+ gramSize = gramSize || 2;
108347
+ var result = {},
108348
+ grams = _iterateGrams(value, gramSize),
108349
+ i = 0;
108350
+ for (i; i < grams.length; ++i) {
108351
+ if (grams[i] in result) {
108352
+ result[grams[i]] += 1;
108353
+ } else {
108354
+ result[grams[i]] = 1;
108355
+ }
108356
+ }
108357
+ return result;
108358
+ };
108359
+
108360
+ // the main functions
108361
+ fuzzyset.get = function(value, defaultValue, minMatchScore) {
108362
+ // check for value in set, returning defaultValue or null if none found
108363
+ if (minMatchScore === undefined) {
108364
+ minMatchScore = .33;
108365
+ }
108366
+ var result = this._get(value, minMatchScore);
108367
+ if (!result && typeof defaultValue !== 'undefined') {
108368
+ return defaultValue;
108369
+ }
108370
+ return result;
108371
+ };
108372
+
108373
+ fuzzyset._get = function(value, minMatchScore) {
108374
+ var results = [];
108375
+ // start with high gram size and if there are no results, go to lower gram sizes
108376
+ for (var gramSize = this.gramSizeUpper; gramSize >= this.gramSizeLower; --gramSize) {
108377
+ results = this.__get(value, gramSize, minMatchScore);
108378
+ if (results && results.length > 0) {
108379
+ return results;
108380
+ }
108381
+ }
108382
+ return null;
108383
+ };
108384
+
108385
+ fuzzyset.__get = function(value, gramSize, minMatchScore) {
108386
+ var normalizedValue = this._normalizeStr(value),
108387
+ matches = {},
108388
+ gramCounts = _gramCounter(normalizedValue, gramSize),
108389
+ items = this.items[gramSize],
108390
+ sumOfSquareGramCounts = 0,
108391
+ gram,
108392
+ gramCount,
108393
+ i,
108394
+ index,
108395
+ otherGramCount;
108396
+
108397
+ for (gram in gramCounts) {
108398
+ gramCount = gramCounts[gram];
108399
+ sumOfSquareGramCounts += Math.pow(gramCount, 2);
108400
+ if (gram in this.matchDict) {
108401
+ for (i = 0; i < this.matchDict[gram].length; ++i) {
108402
+ index = this.matchDict[gram][i][0];
108403
+ otherGramCount = this.matchDict[gram][i][1];
108404
+ if (index in matches) {
108405
+ matches[index] += gramCount * otherGramCount;
108406
+ } else {
108407
+ matches[index] = gramCount * otherGramCount;
108408
+ }
108409
+ }
108410
+ }
108411
+ }
108412
+
108413
+ function isEmptyObject(obj) {
108414
+ for(var prop in obj) {
108415
+ if(obj.hasOwnProperty(prop))
108416
+ return false;
108417
+ }
108418
+ return true;
108419
+ }
108420
+
108421
+ if (isEmptyObject(matches)) {
108422
+ return null;
108423
+ }
108424
+
108425
+ var vectorNormal = Math.sqrt(sumOfSquareGramCounts),
108426
+ results = [],
108427
+ matchScore;
108428
+ // build a results list of [score, str]
108429
+ for (var matchIndex in matches) {
108430
+ matchScore = matches[matchIndex];
108431
+ results.push([matchScore / (vectorNormal * items[matchIndex][0]), items[matchIndex][1]]);
108432
+ }
108433
+ var sortDescending = function(a, b) {
108434
+ if (a[0] < b[0]) {
108435
+ return 1;
108436
+ } else if (a[0] > b[0]) {
108437
+ return -1;
108438
+ } else {
108439
+ return 0;
108440
+ }
108441
+ };
108442
+ results.sort(sortDescending);
108443
+ if (this.useLevenshtein) {
108444
+ var newResults = [],
108445
+ endIndex = Math.min(50, results.length);
108446
+ // truncate somewhat arbitrarily to 50
108447
+ for (var i = 0; i < endIndex; ++i) {
108448
+ newResults.push([_distance(results[i][1], normalizedValue), results[i][1]]);
108449
+ }
108450
+ results = newResults;
108451
+ results.sort(sortDescending);
108452
+ }
108453
+ newResults = [];
108454
+ results.forEach(function(scoreWordPair) {
108455
+ if (scoreWordPair[0] >= minMatchScore) {
108456
+ newResults.push([scoreWordPair[0], this.exactSet[scoreWordPair[1]]]);
108457
+ }
108458
+ }.bind(this));
108459
+ return newResults;
108460
+ };
108461
+
108462
+ fuzzyset.add = function(value) {
108463
+ var normalizedValue = this._normalizeStr(value);
108464
+ if (normalizedValue in this.exactSet) {
108465
+ return false;
108466
+ }
108467
+
108468
+ var i = this.gramSizeLower;
108469
+ for (i; i < this.gramSizeUpper + 1; ++i) {
108470
+ this._add(value, i);
108471
+ }
108472
+ };
108473
+
108474
+ fuzzyset._add = function(value, gramSize) {
108475
+ var normalizedValue = this._normalizeStr(value),
108476
+ items = this.items[gramSize] || [],
108477
+ index = items.length;
108478
+
108479
+ items.push(0);
108480
+ var gramCounts = _gramCounter(normalizedValue, gramSize),
108481
+ sumOfSquareGramCounts = 0,
108482
+ gram, gramCount;
108483
+ for (gram in gramCounts) {
108484
+ gramCount = gramCounts[gram];
108485
+ sumOfSquareGramCounts += Math.pow(gramCount, 2);
108486
+ if (gram in this.matchDict) {
108487
+ this.matchDict[gram].push([index, gramCount]);
108488
+ } else {
108489
+ this.matchDict[gram] = [[index, gramCount]];
108490
+ }
108491
+ }
108492
+ var vectorNormal = Math.sqrt(sumOfSquareGramCounts);
108493
+ items[index] = [vectorNormal, normalizedValue];
108494
+ this.items[gramSize] = items;
108495
+ this.exactSet[normalizedValue] = value;
108496
+ };
108497
+
108498
+ fuzzyset._normalizeStr = function(str) {
108499
+ if (Object.prototype.toString.call(str) !== '[object String]') throw 'Must use a string as argument to FuzzySet functions';
108500
+ return str.toLowerCase();
108501
+ };
108502
+
108503
+ // return length of items in set
108504
+ fuzzyset.length = function() {
108505
+ var count = 0,
108506
+ prop;
108507
+ for (prop in this.exactSet) {
108508
+ if (this.exactSet.hasOwnProperty(prop)) {
108509
+ count += 1;
108510
+ }
108511
+ }
108512
+ return count;
108513
+ };
108514
+
108515
+ // return is set is empty
108516
+ fuzzyset.isEmpty = function() {
108517
+ for (var prop in this.exactSet) {
108518
+ if (this.exactSet.hasOwnProperty(prop)) {
108519
+ return false;
108520
+ }
108521
+ }
108522
+ return true;
108523
+ };
108524
+
108525
+ // return list of values loaded into set
108526
+ fuzzyset.values = function() {
108527
+ var values = [],
108528
+ prop;
108529
+ for (prop in this.exactSet) {
108530
+ if (this.exactSet.hasOwnProperty(prop)) {
108531
+ values.push(this.exactSet[prop]);
108532
+ }
108533
+ }
108534
+ return values;
108535
+ };
108536
+
108537
+
108538
+ // initialization
108539
+ var i = fuzzyset.gramSizeLower;
108540
+ for (i; i < fuzzyset.gramSizeUpper + 1; ++i) {
108541
+ fuzzyset.items[i] = [];
108542
+ }
108543
+ // add all the items to the set
108544
+ for (i = 0; i < arr.length; ++i) {
108545
+ fuzzyset.add(arr[i]);
108546
+ }
108547
+
108548
+ return fuzzyset;
108549
+ };
108550
+
108189
108551
  //import fa from '@dualbox/dualbox-lib-font-awesome';
108190
108552
  //console.log('FontAwesome loaded: ' + fa);
108191
108553
  // Display dependencies
@@ -108216,8 +108578,15 @@ class DualboxEditor {
108216
108578
  this.v.c = this.m.c = this.c;
108217
108579
  this.c.v = this.m.v = this.v;
108218
108580
 
108219
- // cache for packages
108581
+ // packages can be passed at init time or searched dynamically
108220
108582
  this.packages = {};
108583
+ if (attrs.packages) {
108584
+ for (let p of Object.values(attrs.packages)) {
108585
+ this.packages[p.name] = p;
108586
+ }
108587
+ }
108588
+ this.allPackagesFetched = !!attrs.packages; // assume ALL packages are passed if this argument exists
108589
+
108221
108590
  this.DualBox = null; // local dualbox, for editor use
108222
108591
 
108223
108592
  this.appDescription = attrs.appDescription || null;
@@ -108226,31 +108595,137 @@ class DualboxEditor {
108226
108595
  // the function to search for modules
108227
108596
  // attr.search signature: search( text, cb )
108228
108597
  // callback signature: cb( err, packages ), packages is an array of package.json
108229
- this.search = (text, type) => {
108230
- return new Promise((resolve, reject) => {
108231
- attrs.search(text, (err, packages) => {
108232
- if (err) {
108233
- reject(err);
108234
- } else {
108235
- // cache packages in this.packages
108236
- lodash.each(packages, r => {
108237
- if (!this.packages[r.name]) {
108238
- this.packages[r.name] = r;
108239
- }
108240
- });
108598
+ this.search = async (text, type) => {
108599
+ if (this.allPackagesFetched) {
108600
+ let packages = lodash.cloneDeep(this.packages);
108601
+
108602
+ if (type) {
108603
+ packages = lodash.filter(packages, o => {
108604
+ return (
108605
+ o.name.startsWith("@dualbox/dualbox-" + type) ||
108606
+ (type == "module" && o.name.startsWith("dualbox-core"))
108607
+ );
108608
+ });
108609
+ }
108241
108610
 
108242
- if (type) {
108243
- packages = lodash.filter(packages, o => {
108244
- return (
108245
- o.name.startsWith("@dualbox/dualbox-" + type) ||
108246
- (type == "module" && o.name.startsWith("dualbox-core"))
108247
- );
108611
+ if (text) {
108612
+ packages = this.filterPackages(text, packages);
108613
+ }
108614
+
108615
+ return Promise.resolve(packages);
108616
+ } else {
108617
+ return new Promise((resolve, reject) => {
108618
+ attrs.search(text, (err, packages) => {
108619
+ if (err) {
108620
+ reject(err);
108621
+ } else {
108622
+ // cache packages in this.packages
108623
+ lodash.each(packages, r => {
108624
+ if (!this.packages[r.name]) {
108625
+ this.packages[r.name] = r;
108626
+ }
108248
108627
  });
108628
+
108629
+ if (type) {
108630
+ packages = lodash.filter(packages, o => {
108631
+ return (
108632
+ o.name.startsWith("@dualbox/dualbox-" + type) ||
108633
+ (type == "module" && o.name.startsWith("dualbox-core"))
108634
+ );
108635
+ });
108636
+ }
108637
+ resolve(packages);
108638
+ }
108639
+ });
108640
+ });
108641
+ }
108642
+ };
108643
+
108644
+ // filter results from text
108645
+ this.filterPackages = function(text, packages) {
108646
+ text = text.toLowerCase();
108647
+
108648
+ // ponderate search
108649
+ let ponderation = {
108650
+ "shortName": 0.7,
108651
+ "description": 0.3
108652
+ };
108653
+
108654
+ var splitWords = function(text) {
108655
+ return text.trim().replace(/[\W_]+/g, ' ').split(' ');
108656
+ };
108657
+
108658
+ let words = splitWords(text);
108659
+
108660
+ var isObject = function(o) {
108661
+ return typeof o === 'object' && o !== null;
108662
+ };
108663
+
108664
+ var getFuzzySearchResult = function(word, field) {
108665
+ var arr = [];
108666
+ lodash.each(packages, (r) => {
108667
+ if (r[field]) {
108668
+ let fieldWords = r[field].trim().split('-').join(' ') || "";
108669
+ r.searchString = fieldWords;
108670
+ arr.push(fieldWords);
108671
+ }
108672
+ });
108673
+ var set = new FuzzySet(arr, false);
108674
+ return set.get(word, null, 0.05);
108675
+ };
108676
+
108677
+ var setFuzzySearchResultScore = function(word, field) {
108678
+ var fuzzyResults = getFuzzySearchResult(word, field);
108679
+ lodash.each(fuzzyResults, (fuzzyResult) => {
108680
+ let resultIndexes = lodash.keys(lodash.pickBy(packages, r => r.searchString == fuzzyResult[1]));
108681
+ if (resultIndexes.length == 0) {
108682
+ console.error("Could not retrieve result for: " + fuzzyResult);
108683
+ }
108684
+ for (let resultIndex of resultIndexes) {
108685
+ let result = packages[resultIndex];
108686
+
108687
+ if (!isObject(result.wordscore)) {
108688
+ result.wordscore = {};
108249
108689
  }
108250
- resolve(packages);
108690
+
108691
+ if (!isObject(result.wordscore[field])) {
108692
+ result.wordscore[field] = {};
108693
+ }
108694
+
108695
+ result.wordscore[field][word] = result.wordscore[field][word] || 0;
108696
+ result.wordscore[field][word] += fuzzyResult[0];
108251
108697
  }
108698
+
108699
+ // let resultIndex = _.findIndex(results, r => r[field] == fuzzyResult[1]);
108700
+ // if (resultIndex == -1) {
108701
+ // throw `Could not find result ${field}:${fuzzyResult[1]}`;
108702
+ // }
108703
+ });
108704
+ };
108705
+
108706
+ // create FuzzySets and search them
108707
+ for (let word of words) {
108708
+ setFuzzySearchResultScore(word, "shortName");
108709
+ setFuzzySearchResultScore(word, "description");
108710
+ }
108711
+
108712
+ // aggregate scores
108713
+ lodash.each(packages, r => {
108714
+ r.score = 0;
108715
+ lodash.each(r.wordscore, (wordScores, field) => {
108716
+ let fieldScore = 0;
108717
+ lodash.each(wordScores, (val, key) => {
108718
+ fieldScore += val;
108719
+ });
108720
+ fieldScore /= words.length;
108721
+ r.score += fieldScore * ponderation[field];
108252
108722
  });
108253
108723
  });
108724
+
108725
+ packages = lodash.filter(packages, r => r.score > 0);
108726
+
108727
+ // return result sorted by score
108728
+ return lodash.sortBy(packages, r => 1 - (r.score || 0));
108254
108729
  };
108255
108730
 
108256
108731
  this.types = [];