mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add visual json editor
This commit is contained in:
parent
c97786e12c
commit
e3e0378857
@ -739,7 +739,16 @@
|
|||||||
},
|
},
|
||||||
"jsonEditor": {
|
"jsonEditor": {
|
||||||
"title": "JSON editor",
|
"title": "JSON editor",
|
||||||
"format": "format JSON"
|
"format": "format JSON",
|
||||||
|
"rawMode": "Edit JSON",
|
||||||
|
"uiMode": "Visual editor",
|
||||||
|
"insertAbove": "Insert above",
|
||||||
|
"insertBelow": "Insert below",
|
||||||
|
"addItem": "Add item",
|
||||||
|
"copyPath": "Copy path to item",
|
||||||
|
"expandItems": "Expand items",
|
||||||
|
"collapseItems": "Collapse items",
|
||||||
|
"duplicate": "Duplicate"
|
||||||
},
|
},
|
||||||
"markdownEditor": {
|
"markdownEditor": {
|
||||||
"title": "Markdown editor",
|
"title": "Markdown editor",
|
||||||
|
@ -185,6 +185,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
_destroy: function() {
|
_destroy: function() {
|
||||||
|
this.topContainer.remove();
|
||||||
},
|
},
|
||||||
_refreshFilter: function() {
|
_refreshFilter: function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
@ -230,7 +231,7 @@
|
|||||||
this.uiHeight = desiredHeight;
|
this.uiHeight = desiredHeight;
|
||||||
this._resize();
|
this._resize();
|
||||||
},
|
},
|
||||||
addItem: function(data) {
|
insertItemAt: function(data,index) {
|
||||||
var that = this;
|
var that = this;
|
||||||
data = data || {};
|
data = data || {};
|
||||||
var li = $('<li>');
|
var li = $('<li>');
|
||||||
@ -248,7 +249,13 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!added) {
|
if (!added) {
|
||||||
li.appendTo(this.element);
|
if (index <= 0) {
|
||||||
|
li.prependTo(this.element);
|
||||||
|
} else if (index > that.element.children().length-1) {
|
||||||
|
li.appendTo(this.element);
|
||||||
|
} else {
|
||||||
|
li.insertBefore(this.element.children().eq(index));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var row = $('<div/>').addClass("red-ui-editableList-item-content").appendTo(li);
|
var row = $('<div/>').addClass("red-ui-editableList-item-content").appendTo(li);
|
||||||
row.data('data',data);
|
row.data('data',data);
|
||||||
@ -293,6 +300,9 @@
|
|||||||
},0);
|
},0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
addItem: function(data) {
|
||||||
|
this.insertItemAt(data,this.element.children().length)
|
||||||
|
},
|
||||||
addItems: function(items) {
|
addItems: function(items) {
|
||||||
for (var i=0; i<items.length;i++) {
|
for (var i=0; i<items.length;i++) {
|
||||||
this.addItem(items[i]);
|
this.addItem(items[i]);
|
||||||
|
@ -20,6 +20,10 @@
|
|||||||
* - data : array - initial items to display in tree
|
* - data : array - initial items to display in tree
|
||||||
* - multi : boolean - if true, .selected will return an array of results
|
* - multi : boolean - if true, .selected will return an array of results
|
||||||
* otherwise, returns the first selected item
|
* otherwise, returns the first selected item
|
||||||
|
* - sortable: boolean/string - TODO: see editableList
|
||||||
|
* - rootSortable: boolean - if 'sortable' is set, then setting this to
|
||||||
|
* false, prevents items being sorted to the
|
||||||
|
* top level of the tree
|
||||||
*
|
*
|
||||||
* methods:
|
* methods:
|
||||||
* - data(items) - clears existing items and replaces with new data
|
* - data(items) - clears existing items and replaces with new data
|
||||||
@ -27,25 +31,57 @@
|
|||||||
* events:
|
* events:
|
||||||
* - treelistselect : function(event, item) {}
|
* - treelistselect : function(event, item) {}
|
||||||
* - treelistconfirm : function(event,item) {}
|
* - treelistconfirm : function(event,item) {}
|
||||||
|
* - treelistchangeparent: function(event,item, oldParent, newParent) {}
|
||||||
*
|
*
|
||||||
* data:
|
* data:
|
||||||
* [
|
* [
|
||||||
* {
|
* {
|
||||||
* label: 'Local', // label for the item
|
* label: 'Local', // label for the item
|
||||||
|
* sublabel: 'Local', // a sub-label for the item
|
||||||
* icon: 'fa fa-rocket', // (optional) icon for the item
|
* icon: 'fa fa-rocket', // (optional) icon for the item
|
||||||
* selected: true/false, // (optional) if present, display checkbox accordingly
|
* selected: true/false, // (optional) if present, display checkbox accordingly
|
||||||
* children: [] | function(done,item) // (optional) an array of child items, or a function
|
* children: [] | function(done,item) // (optional) an array of child items, or a function
|
||||||
* // that will call the `done` callback with an array
|
* // that will call the `done` callback with an array
|
||||||
* // of child items
|
* // of child items
|
||||||
|
* expanded: true/false, // show the child items by default
|
||||||
|
* deferBuild: true/false, // don't build any ui elements for the item's children
|
||||||
|
* until it is expanded by the user.
|
||||||
|
* element: // custom dom element to use for the item - ignored if `label` is set
|
||||||
* }
|
* }
|
||||||
* ]
|
* ]
|
||||||
*
|
*
|
||||||
*
|
|
||||||
*
|
|
||||||
* var treeList = $("<div>").css({width: "100%", height: "100%"}).treeList({data:[...]})
|
* var treeList = $("<div>").css({width: "100%", height: "100%"}).treeList({data:[...]})
|
||||||
* treeList.on('treelistselect', function(e,item) { console.log(item)})
|
* treeList.on('treelistselect', function(e,item) { console.log(item)})
|
||||||
* treeList.treeList('data',[ ... ] )
|
* treeList.treeList('data',[ ... ] )
|
||||||
*
|
*
|
||||||
|
*
|
||||||
|
* After `data` has been added to the tree, each item is augmented the following
|
||||||
|
* properties and functions:
|
||||||
|
*
|
||||||
|
* item.parent - set to the parent item
|
||||||
|
* item.treeList.container
|
||||||
|
* item.treeList.label - the label element for the item
|
||||||
|
* item.treeList.depth - the depth in the tree (0 == root)
|
||||||
|
* item.treeList.parentList - the editableList instance this item is in
|
||||||
|
* item.treeList.remove() - removes the item from the tree
|
||||||
|
* item.treeList.makeLeaf(detachChildElements) - turns an element with children into a leaf node,
|
||||||
|
* removing the UI decoration etc.
|
||||||
|
* detachChildElements - any children with custom
|
||||||
|
* elements will be detached rather than removed
|
||||||
|
* so jQuery event handlers are preserved in case
|
||||||
|
* the child elements need to be reattached later
|
||||||
|
* item.treeList.makeParent(children) - turns an element into a parent node, adding the necessary
|
||||||
|
* UI decoration.
|
||||||
|
* item.treeList.insertChildAt(newItem,position,select) - adds a child item an the specified position.
|
||||||
|
* Optionally selects the item after adding.
|
||||||
|
* item.treeList.addChild(newItem,select) - appends a child item.
|
||||||
|
* Optionally selects the item after adding.
|
||||||
|
* item.treeList.expand(done) - expands the parent item to show children. Optional 'done' callback.
|
||||||
|
* item.treeList.collapse() - collapse the parent item to hide children.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$.widget( "nodered.treeList", {
|
$.widget( "nodered.treeList", {
|
||||||
@ -116,20 +152,30 @@
|
|||||||
});
|
});
|
||||||
this._data = [];
|
this._data = [];
|
||||||
|
|
||||||
this._topList = $('<ol>').css({
|
this._topList = $('<ol class="red-ui-treeList-list">').css({
|
||||||
position:'absolute',
|
position:'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
left:0,
|
left:0,
|
||||||
right:0,
|
right:0,
|
||||||
bottom:0
|
bottom:0
|
||||||
}).appendTo(wrapper).editableList({
|
}).appendTo(wrapper);
|
||||||
|
|
||||||
|
var topListOptions = {
|
||||||
addButton: false,
|
addButton: false,
|
||||||
scrollOnAdd: false,
|
scrollOnAdd: false,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
addItem: function(container,i,item) {
|
addItem: function(container,i,item) {
|
||||||
that._addSubtree(that._topList,container,item,0);
|
that._addSubtree(that._topList,container,item,0);
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
|
if (this.options.rootSortable !== false && !!this.options.sortable) {
|
||||||
|
topListOptions.sortable = this.options.sortable;
|
||||||
|
topListOptions.connectWith = '.red-ui-treeList-sortable';
|
||||||
|
this._topList.addClass('red-ui-treeList-sortable');
|
||||||
|
}
|
||||||
|
this._topList.editableList(topListOptions)
|
||||||
|
|
||||||
|
|
||||||
if (this.options.data) {
|
if (this.options.data) {
|
||||||
this.data(this.options.data);
|
this.data(this.options.data);
|
||||||
}
|
}
|
||||||
@ -171,23 +217,82 @@
|
|||||||
},
|
},
|
||||||
_addChildren: function(container,parent,children,depth) {
|
_addChildren: function(container,parent,children,depth) {
|
||||||
var that = this;
|
var that = this;
|
||||||
var subtree = $('<ol>').appendTo(container).editableList({
|
var subtree = $('<ol class="red-ui-treeList-list">').appendTo(container).editableList({
|
||||||
|
connectWith: ".red-ui-treeList-sortable",
|
||||||
|
sortable: that.options.sortable,
|
||||||
addButton: false,
|
addButton: false,
|
||||||
scrollOnAdd: false,
|
scrollOnAdd: false,
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
addItem: function(container,i,item) {
|
addItem: function(container,i,item) {
|
||||||
that._addSubtree(subtree,container,item,depth+1);
|
that._addSubtree(subtree,container,item,depth+1);
|
||||||
|
},
|
||||||
|
sortItems: function(data) {
|
||||||
|
var children = [];
|
||||||
|
var reparented = [];
|
||||||
|
data.each(function() {
|
||||||
|
var child = $(this).data('data');
|
||||||
|
children.push(child);
|
||||||
|
var evt = that._fixDepths(parent,child);
|
||||||
|
if (evt) {
|
||||||
|
reparented.push(evt);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (Array.isArray(parent.children)) {
|
||||||
|
parent.children = children;
|
||||||
|
}
|
||||||
|
reparented.forEach(function(evt) {
|
||||||
|
that._trigger("changeparent",null,evt);
|
||||||
|
});
|
||||||
|
that._trigger("sort",null,parent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (!!that.options.sortable) {
|
||||||
|
subtree.addClass('red-ui-treeList-sortable');
|
||||||
|
}
|
||||||
for (var i=0;i<children.length;i++) {
|
for (var i=0;i<children.length;i++) {
|
||||||
children[i].parent = parent;
|
children[i].parent = parent;
|
||||||
subtree.editableList('addItem',children[i])
|
subtree.editableList('addItem',children[i])
|
||||||
}
|
}
|
||||||
return subtree;
|
return subtree;
|
||||||
},
|
},
|
||||||
|
_fixDepths: function(parent,child) {
|
||||||
|
// If child has just been moved into parent in the UI
|
||||||
|
// this will fix up the internal data structures to match.
|
||||||
|
// The calling function must take care of getting child
|
||||||
|
// into the parent.children array. The rest is up to us.
|
||||||
|
var that = this;
|
||||||
|
var reparentedEvent = null;
|
||||||
|
if (child.parent !== parent) {
|
||||||
|
reparented = true;
|
||||||
|
var oldParent = child.parent;
|
||||||
|
child.parent = parent;
|
||||||
|
reparentedEvent = {
|
||||||
|
item: child,
|
||||||
|
old: oldParent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (child.depth !== parent.depth+1) {
|
||||||
|
child.depth = parent.depth+1;
|
||||||
|
var labelPaddingWidth = ((child.gutter?child.gutter.width()+2:0)+(child.depth*20));
|
||||||
|
child.treeList.labelPadding.width(labelPaddingWidth+'px');
|
||||||
|
if (child.element) {
|
||||||
|
$(child.element).css({
|
||||||
|
width: "calc(100% - "+(labelPaddingWidth+20+(child.icon?20:0))+"px)"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// This corrects all child item depths
|
||||||
|
if (child.children && Array.isArray(child.children)) {
|
||||||
|
child.children.forEach(function(item) {
|
||||||
|
that._fixDepths(child,item);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reparentedEvent;
|
||||||
|
},
|
||||||
_addSubtree: function(parentList, container, item, depth) {
|
_addSubtree: function(parentList, container, item, depth) {
|
||||||
var that = this;
|
var that = this;
|
||||||
item.treeList = {};
|
item.treeList = {};
|
||||||
|
item.treeList.depth = depth;
|
||||||
item.treeList.container = container;
|
item.treeList.container = container;
|
||||||
|
|
||||||
item.treeList.parentList = parentList;
|
item.treeList.parentList = parentList;
|
||||||
@ -196,87 +301,80 @@
|
|||||||
if (item.parent) {
|
if (item.parent) {
|
||||||
var index = item.parent.children.indexOf(item);
|
var index = item.parent.children.indexOf(item);
|
||||||
item.parent.children.splice(index,1)
|
item.parent.children.splice(index,1)
|
||||||
|
that._trigger("sort",null,item.parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var labelNodeType = "<div>";
|
|
||||||
// if (item.children && item.hasOwnProperty('selected')) {
|
var label = $("<div>",{class:"red-ui-treeList-label"}).appendTo(container);
|
||||||
// labelNodeType = "<div>";
|
|
||||||
// }
|
|
||||||
var label = $(labelNodeType,{class:"red-ui-treeList-label"}).appendTo(container);
|
|
||||||
item.treeList.label = label;
|
item.treeList.label = label;
|
||||||
if (item.class) {
|
if (item.class) {
|
||||||
label.addClass(item.class);
|
label.addClass(item.class);
|
||||||
}
|
}
|
||||||
label.css({
|
if (item.gutter) {
|
||||||
paddingLeft: (depth*15)+'px'
|
item.gutter.css({
|
||||||
})
|
position: 'absolute'
|
||||||
|
}).appendTo(label)
|
||||||
|
|
||||||
|
}
|
||||||
|
var labelPaddingWidth = (item.gutter?item.gutter.width()+2:0)+(depth*20);
|
||||||
|
item.treeList.labelPadding = $('<span>').css({
|
||||||
|
display: "inline-block",
|
||||||
|
width: labelPaddingWidth+'px'
|
||||||
|
}).appendTo(label);
|
||||||
|
|
||||||
label.on('mouseover',function(e) { that._trigger('itemmouseover',e,item); })
|
label.on('mouseover',function(e) { that._trigger('itemmouseover',e,item); })
|
||||||
label.on('mouseout',function(e) { that._trigger('itemmouseout',e,item); })
|
label.on('mouseout',function(e) { that._trigger('itemmouseout',e,item); })
|
||||||
label.on('mouseenter',function(e) { that._trigger('itemmouseenter',e,item); })
|
label.on('mouseenter',function(e) { that._trigger('itemmouseenter',e,item); })
|
||||||
label.on('mouseleave',function(e) { that._trigger('itemmouseleave',e,item); })
|
label.on('mouseleave',function(e) { that._trigger('itemmouseleave',e,item); })
|
||||||
|
|
||||||
if (item.children) {
|
item.treeList.makeLeaf = function(detachChildElements) {
|
||||||
item.treeList.addChild = function(newItem,select) {
|
if (!treeListIcon.children().length) {
|
||||||
item.treeList.childList.editableList('addItem',newItem)
|
// Already a leaf
|
||||||
newItem.parent = item;
|
return
|
||||||
item.children.push(newItem);
|
|
||||||
if (select) {
|
|
||||||
setTimeout(function() {
|
|
||||||
that.select(newItem)
|
|
||||||
},100);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
item.treeList.expand = function(done) {
|
if (detachChildElements && item.children) {
|
||||||
if (container.hasClass("expanded")) {
|
var detachChildren = function(item) {
|
||||||
done && done();
|
if (item.children) {
|
||||||
return;
|
item.children.forEach(function(child) {
|
||||||
}
|
if (child.element) {
|
||||||
if (!container.hasClass("built") && typeof item.children === 'function') {
|
child.element.detach();
|
||||||
container.addClass('built');
|
|
||||||
var childrenAdded = false;
|
|
||||||
var spinner;
|
|
||||||
var startTime = 0;
|
|
||||||
item.children(function(children) {
|
|
||||||
childrenAdded = true;
|
|
||||||
item.treeList.childList = that._addChildren(container,item,children,depth).hide();
|
|
||||||
var delta = Date.now() - startTime;
|
|
||||||
if (delta < 400) {
|
|
||||||
setTimeout(function() {
|
|
||||||
item.treeList.childList.slideDown('fast');
|
|
||||||
if (spinner) {
|
|
||||||
spinner.remove();
|
|
||||||
}
|
|
||||||
},400-delta);
|
|
||||||
} else {
|
|
||||||
item.treeList.childList.slideDown('fast');
|
|
||||||
if (spinner) {
|
|
||||||
spinner.remove();
|
|
||||||
}
|
}
|
||||||
}
|
if (child.gutter) {
|
||||||
done && done();
|
child.gutter.detach();
|
||||||
that._trigger("childrenloaded",null,item)
|
}
|
||||||
},item);
|
detachChildren(child);
|
||||||
if (!childrenAdded) {
|
});
|
||||||
startTime = Date.now();
|
|
||||||
spinner = $('<div class="red-ui-treeList-spinner">').css({
|
|
||||||
"background-position": (35+depth*15)+'px 50%'
|
|
||||||
}).appendTo(container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
item.treeList.childList.slideDown('fast');
|
|
||||||
done && done();
|
|
||||||
}
|
}
|
||||||
container.addClass("expanded");
|
detachChildren(item);
|
||||||
}
|
}
|
||||||
item.treeList.collapse = function() {
|
treeListIcon.empty();
|
||||||
item.treeList.childList.slideUp('fast');
|
if (!item.deferBuild) {
|
||||||
container.removeClass("expanded");
|
item.treeList.childList.remove();
|
||||||
|
delete item.treeList.childList;
|
||||||
}
|
}
|
||||||
|
label.off("click.red-ui-treeList-expand");
|
||||||
$('<span class="red-ui-treeList-icon"><i class="fa fa-angle-right" /></span>').appendTo(label);
|
treeListIcon.off("click.red-ui-treeList-expand");
|
||||||
|
delete item.children;
|
||||||
|
container.removeClass("expanded");
|
||||||
|
}
|
||||||
|
item.treeList.makeParent = function(children) {
|
||||||
|
if (treeListIcon.children().length) {
|
||||||
|
// Already a parent because we've got the angle-right icon
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('<i class="fa fa-angle-right" />').appendTo(treeListIcon);
|
||||||
|
treeListIcon.on("click.red-ui-treeList-expand", function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
if (container.hasClass("expanded")) {
|
||||||
|
item.treeList.collapse();
|
||||||
|
} else {
|
||||||
|
item.treeList.expand();
|
||||||
|
}
|
||||||
|
});
|
||||||
// $('<span class="red-ui-treeList-icon"><i class="fa fa-folder-o" /></span>').appendTo(label);
|
// $('<span class="red-ui-treeList-icon"><i class="fa fa-folder-o" /></span>').appendTo(label);
|
||||||
label.on("click", function(e) {
|
label.on("click.red-ui-treeList-expand", function(e) {
|
||||||
if (container.hasClass("expanded")) {
|
if (container.hasClass("expanded")) {
|
||||||
if (item.hasOwnProperty('selected') || label.hasClass("selected")) {
|
if (item.hasOwnProperty('selected') || label.hasClass("selected")) {
|
||||||
item.treeList.collapse();
|
item.treeList.collapse();
|
||||||
@ -285,9 +383,97 @@
|
|||||||
item.treeList.expand();
|
item.treeList.expand();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
if (!item.children) {
|
||||||
$('<span class="red-ui-treeList-icon"></span>').appendTo(label);
|
item.children = children||[];
|
||||||
|
item.treeList.childList = that._addChildren(container,item,item.children,depth).hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
item.treeList.insertChildAt = function(newItem,position,select) {
|
||||||
|
newItem.parent = item;
|
||||||
|
item.children.splice(position,0,newItem);
|
||||||
|
|
||||||
|
if (!item.deferBuild) {
|
||||||
|
item.treeList.childList.editableList('insertItemAt',newItem,position)
|
||||||
|
if (select) {
|
||||||
|
setTimeout(function() {
|
||||||
|
that.select(newItem)
|
||||||
|
},100);
|
||||||
|
}
|
||||||
|
that._trigger("sort",null,item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.treeList.addChild = function(newItem,select) {
|
||||||
|
item.treeList.insertChildAt(newItem,item.children.length,select);
|
||||||
|
}
|
||||||
|
item.treeList.expand = function(done) {
|
||||||
|
if (!item.children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (container.hasClass("expanded")) {
|
||||||
|
done && done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!container.hasClass("built") && (item.deferBuild || typeof item.children === 'function')) {
|
||||||
|
container.addClass('built');
|
||||||
|
var childrenAdded = false;
|
||||||
|
var spinner;
|
||||||
|
var startTime = 0;
|
||||||
|
var completeBuild = function(children) {
|
||||||
|
childrenAdded = true;
|
||||||
|
item.treeList.childList = that._addChildren(container,item,children,depth).hide();
|
||||||
|
var delta = Date.now() - startTime;
|
||||||
|
if (delta < 400) {
|
||||||
|
setTimeout(function() {
|
||||||
|
item.treeList.childList.slideDown('fast');
|
||||||
|
if (spinner) {
|
||||||
|
spinner.remove();
|
||||||
|
}
|
||||||
|
},400-delta);
|
||||||
|
} else {
|
||||||
|
item.treeList.childList.slideDown('fast');
|
||||||
|
if (spinner) {
|
||||||
|
spinner.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done && done();
|
||||||
|
that._trigger("childrenloaded",null,item)
|
||||||
|
}
|
||||||
|
if (typeof item.children === 'function') {
|
||||||
|
item.children(completeBuild,item);
|
||||||
|
} else {
|
||||||
|
delete item.deferBuild;
|
||||||
|
completeBuild(item.children);
|
||||||
|
}
|
||||||
|
if (!childrenAdded) {
|
||||||
|
startTime = Date.now();
|
||||||
|
spinner = $('<div class="red-ui-treeList-spinner">').css({
|
||||||
|
"background-position": (35+depth*20)+'px 50%'
|
||||||
|
}).appendTo(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (that._loadingData) {
|
||||||
|
item.treeList.childList.show();
|
||||||
|
} else {
|
||||||
|
item.treeList.childList.slideDown('fast');
|
||||||
|
}
|
||||||
|
done && done();
|
||||||
|
}
|
||||||
|
container.addClass("expanded");
|
||||||
|
}
|
||||||
|
item.treeList.collapse = function() {
|
||||||
|
if (!item.children) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
item.treeList.childList.slideUp('fast');
|
||||||
|
container.removeClass("expanded");
|
||||||
|
}
|
||||||
|
|
||||||
|
var treeListIcon = $('<span class="red-ui-treeList-icon"></span>').appendTo(label);
|
||||||
|
if (item.children) {
|
||||||
|
item.treeList.makeParent();
|
||||||
|
}
|
||||||
|
|
||||||
if (item.hasOwnProperty('selected')) {
|
if (item.hasOwnProperty('selected')) {
|
||||||
var selectWrapper = $('<span class="red-ui-treeList-icon"></span>').appendTo(label);
|
var selectWrapper = $('<span class="red-ui-treeList-icon"></span>').appendTo(label);
|
||||||
var cb = $('<input class="red-ui-treeList-checkbox" type="checkbox">').prop('checked',item.selected).appendTo(selectWrapper);
|
var cb = $('<input class="red-ui-treeList-checkbox" type="checkbox">').prop('checked',item.selected).appendTo(selectWrapper);
|
||||||
@ -326,19 +512,22 @@
|
|||||||
if (item.icon) {
|
if (item.icon) {
|
||||||
$('<span class="red-ui-treeList-icon"><i class="'+item.icon+'" /></span>').appendTo(label);
|
$('<span class="red-ui-treeList-icon"><i class="'+item.icon+'" /></span>').appendTo(label);
|
||||||
}
|
}
|
||||||
if (item.label || item.sublabel) {
|
if (item.hasOwnProperty('label') || item.hasOwnProperty('sublabel')) {
|
||||||
if (item.label) {
|
if (item.hasOwnProperty('label')) {
|
||||||
$('<span class="red-ui-treeList-label-text"></span>').text(item.label).appendTo(label);
|
$('<span class="red-ui-treeList-label-text"></span>').text(item.label).appendTo(label);
|
||||||
}
|
}
|
||||||
if (item.sublabel) {
|
if (item.hasOwnProperty('sublabel')) {
|
||||||
$('<span class="red-ui-treeList-sublabel-text"></span>').text(item.sublabel).appendTo(label);
|
$('<span class="red-ui-treeList-sublabel-text"></span>').text(item.sublabel).appendTo(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (item.element) {
|
} else if (item.element) {
|
||||||
$(item.element).appendTo(label);
|
$(item.element).appendTo(label);
|
||||||
|
$(item.element).css({
|
||||||
|
width: "calc(100% - "+(labelPaddingWidth+20+(item.icon?20:0))+"px)"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (item.children) {
|
if (item.children) {
|
||||||
if (Array.isArray(item.children)) {
|
if (Array.isArray(item.children) && !item.deferBuild) {
|
||||||
item.treeList.childList = that._addChildren(container,item,item.children,depth).hide();
|
item.treeList.childList = that._addChildren(container,item,item.children,depth).hide();
|
||||||
}
|
}
|
||||||
if (item.expanded) {
|
if (item.expanded) {
|
||||||
@ -350,12 +539,17 @@
|
|||||||
this._topList.editableList('empty');
|
this._topList.editableList('empty');
|
||||||
},
|
},
|
||||||
data: function(items) {
|
data: function(items) {
|
||||||
|
var that = this;
|
||||||
if (items !== undefined) {
|
if (items !== undefined) {
|
||||||
this._data = items;
|
this._data = items;
|
||||||
this._topList.editableList('empty');
|
this._topList.editableList('empty');
|
||||||
|
this._loadingData = true;
|
||||||
for (var i=0; i<items.length;i++) {
|
for (var i=0; i<items.length;i++) {
|
||||||
this._topList.editableList('addItem',items[i]);
|
this._topList.editableList('addItem',items[i]);
|
||||||
}
|
}
|
||||||
|
setTimeout(function() {
|
||||||
|
delete that._loadingData;
|
||||||
|
},200);
|
||||||
this._trigger("select")
|
this._trigger("select")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -249,9 +249,15 @@
|
|||||||
that.validate();
|
that.validate();
|
||||||
that.element.val(that.value());
|
that.element.val(that.value());
|
||||||
that.element.trigger('change',that.propertyType,that.value());
|
that.element.trigger('change',that.propertyType,that.value());
|
||||||
|
});
|
||||||
|
this.input.on('keydown', function(evt) {
|
||||||
|
if (evt.keyCode >= 37 && evt.keyCode <= 40) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
this.selectTrigger.on("click", function(event) {
|
this.selectTrigger.on("click", function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
that._showTypeMenu();
|
that._showTypeMenu();
|
||||||
});
|
});
|
||||||
this.selectTrigger.on('keydown',function(evt) {
|
this.selectTrigger.on('keydown',function(evt) {
|
||||||
@ -259,6 +265,7 @@
|
|||||||
// Down
|
// Down
|
||||||
that._showTypeMenu();
|
that._showTypeMenu();
|
||||||
}
|
}
|
||||||
|
evt.stopPropagation();
|
||||||
}).on('focus', function() {
|
}).on('focus', function() {
|
||||||
that.uiSelect.addClass('red-ui-typedInput-focus');
|
that.uiSelect.addClass('red-ui-typedInput-focus');
|
||||||
})
|
})
|
||||||
@ -271,12 +278,14 @@
|
|||||||
});
|
});
|
||||||
this.optionSelectTrigger.on("click", function(event) {
|
this.optionSelectTrigger.on("click", function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
that._showOptionSelectMenu();
|
that._showOptionSelectMenu();
|
||||||
}).on('keydown', function(evt) {
|
}).on('keydown', function(evt) {
|
||||||
if (evt.keyCode === 40) {
|
if (evt.keyCode === 40) {
|
||||||
// Down
|
// Down
|
||||||
that._showOptionSelectMenu();
|
that._showOptionSelectMenu();
|
||||||
}
|
}
|
||||||
|
evt.stopPropagation();
|
||||||
}).on('blur', function() {
|
}).on('blur', function() {
|
||||||
that.uiSelect.removeClass('red-ui-typedInput-focus');
|
that.uiSelect.removeClass('red-ui-typedInput-focus');
|
||||||
}).on('focus', function() {
|
}).on('focus', function() {
|
||||||
@ -357,6 +366,7 @@
|
|||||||
|
|
||||||
op.on("click", function(event) {
|
op.on("click", function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
callback(opt.value);
|
callback(opt.value);
|
||||||
that._hideMenu(menu);
|
that._hideMenu(menu);
|
||||||
});
|
});
|
||||||
@ -376,9 +386,11 @@
|
|||||||
// UP
|
// UP
|
||||||
$(this).children(":focus").prev().trigger("focus");
|
$(this).children(":focus").prev().trigger("focus");
|
||||||
} else if (evt.keyCode === 27) {
|
} else if (evt.keyCode === 27) {
|
||||||
|
// ESCAPE
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
that._hideMenu(menu);
|
that._hideMenu(menu);
|
||||||
}
|
}
|
||||||
|
evt.stopPropagation();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -516,6 +528,7 @@
|
|||||||
this.optionMenu.remove();
|
this.optionMenu.remove();
|
||||||
}
|
}
|
||||||
this.menu.remove();
|
this.menu.remove();
|
||||||
|
this.uiSelect.remove();
|
||||||
},
|
},
|
||||||
types: function(types) {
|
types: function(types) {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
@ -16,7 +16,419 @@
|
|||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
|
|
||||||
var template = '<script type="text/x-red" data-template-name="_json"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><button id="node-input-json-reformat" class="red-ui-button red-ui-button-small"><span data-i18n="jsonEditor.format"></span></button></div><div class="form-row node-text-editor-row"><div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div></div></script>';
|
// var template = '<script type="text/x-red" data-template-name="_json"></script>';
|
||||||
|
var template = '<script type="text/x-red" data-template-name="_json">'+
|
||||||
|
'<ul id="red-ui-editor-type-json-tabs"></ul>'+
|
||||||
|
'<div id="red-ui-editor-type-json-tab-raw" class="red-ui-editor-type-json-tab-content hide">'+
|
||||||
|
'<div class="form-row" style="margin-bottom: 3px; text-align: right;">'+
|
||||||
|
'<button id="node-input-json-reformat" class="red-ui-button red-ui-button-small"><span data-i18n="jsonEditor.format"></span></button>'+
|
||||||
|
'</div>'+
|
||||||
|
'<div class="form-row node-text-editor-row">'+
|
||||||
|
'<div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div>'+
|
||||||
|
'</div>'+
|
||||||
|
'</div>'+
|
||||||
|
'<div id="red-ui-editor-type-json-tab-ui" class="red-ui-editor-type-json-tab-content hide">'+
|
||||||
|
'<div id="red-ui-editor-type-json-tab-ui-container"></div>'+
|
||||||
|
'</div>'+
|
||||||
|
'</script>';
|
||||||
|
|
||||||
|
var activeTab;
|
||||||
|
|
||||||
|
function insertNewItem(parent,index,copyIndex) {
|
||||||
|
var newValue = "";
|
||||||
|
|
||||||
|
if (parent.children.length > 0) {
|
||||||
|
switch (parent.children[Math.max(0,Math.min(parent.children.length-1,copyIndex))].type) {
|
||||||
|
case 'string': newValue = ""; break;
|
||||||
|
case 'number': newValue = 0; break;
|
||||||
|
case 'boolean': newValue = true; break;
|
||||||
|
case 'null': newValue = null; break;
|
||||||
|
case 'object': newValue = {}; break;
|
||||||
|
case 'array': newValue = []; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newKey;
|
||||||
|
if (parent.type === 'array') {
|
||||||
|
newKey = parent.children.length;
|
||||||
|
} else {
|
||||||
|
var usedKeys = {};
|
||||||
|
parent.children.forEach(function(child) { usedKeys[child.key] = true })
|
||||||
|
var keyRoot = "item";
|
||||||
|
var keySuffix = 2;
|
||||||
|
newKey = keyRoot;
|
||||||
|
while(usedKeys[newKey]) {
|
||||||
|
newKey = keyRoot+"-"+(keySuffix++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newItem = handleItem(newKey,newValue,parent.depth+1,parent);
|
||||||
|
parent.treeList.insertChildAt(newItem, index, true);
|
||||||
|
parent.treeList.expand();
|
||||||
|
}
|
||||||
|
function showObjectMenu(button,item) {
|
||||||
|
var elementPos = button.offset();
|
||||||
|
var options = [];
|
||||||
|
if (item.parent) {
|
||||||
|
options.push({id:"red-ui-editor-type-json-menu-insert-above", icon:"fa fa-toggle-up", label:RED._('jsonEditor.insertAbove'),onselect:function(){
|
||||||
|
var index = item.parent.children.indexOf(item);
|
||||||
|
insertNewItem(item.parent,index,index);
|
||||||
|
}});
|
||||||
|
options.push({id:"red-ui-editor-type-json-menu-insert-below", icon:"fa fa-toggle-down", label:RED._('jsonEditor.insertBelow'),onselect:function(){
|
||||||
|
var index = item.parent.children.indexOf(item)+1;
|
||||||
|
insertNewItem(item.parent,index,index-1);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
if (item.type === 'array' || item.type === 'object') {
|
||||||
|
options.push({id:"red-ui-editor-type-json-menu-add-child", icon:"fa fa-plus", label:RED._('jsonEditor.addItem'),onselect:function(){
|
||||||
|
insertNewItem(item,item.children.length,item.children.length-1);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
if (item.parent) {
|
||||||
|
options.push({id:"red-ui-editor-type-json-menu-copy-path", icon:"fa fa-terminal", label:RED._('jsonEditor.copyPath'),onselect:function(){
|
||||||
|
var i = item;
|
||||||
|
var path = "";
|
||||||
|
var newPath;
|
||||||
|
while(i.parent) {
|
||||||
|
if (i.parent.type === "array") {
|
||||||
|
newPath = "["+i.key+"]";
|
||||||
|
} else {
|
||||||
|
if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(i.key)) {
|
||||||
|
newPath = i.key;
|
||||||
|
} else {
|
||||||
|
newPath = "[\""+i.key.replace(/"/,"\\\"")+"\"]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path = newPath+(path.length>0 && path[0] !== "["?".":"")+path;
|
||||||
|
i = i.parent;
|
||||||
|
}
|
||||||
|
RED.clipboard.copyText(path,item.element,"clipboard.copyMessagePath");
|
||||||
|
}});
|
||||||
|
|
||||||
|
options.push({id:"red-ui-editor-type-json-menu-duplicate", icon:"fa fa-copy", label:RED._("jsonEditor.duplicate"),onselect:function(){
|
||||||
|
var newKey = item.key;
|
||||||
|
if (item.parent.type === 'array') {
|
||||||
|
newKey = parent.children.length;
|
||||||
|
} else {
|
||||||
|
var m = /^(.*?)(-(\d+))?$/.exec(newKey);
|
||||||
|
var usedKeys = {};
|
||||||
|
item.parent.children.forEach(function(child) { usedKeys[child.key] = true })
|
||||||
|
var keyRoot = m[1];
|
||||||
|
var keySuffix = 2;
|
||||||
|
if (m[3] !== undefined) {
|
||||||
|
keySuffix = parseInt(m[3]);
|
||||||
|
}
|
||||||
|
newKey = keyRoot;
|
||||||
|
while(usedKeys[newKey]) {
|
||||||
|
newKey = keyRoot+"-"+(keySuffix++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newItem = handleItem(newKey,convertToObject(item),item.parent.depth+1,item.parent);
|
||||||
|
var index = item.parent.children.indexOf(item)+1;
|
||||||
|
|
||||||
|
item.parent.treeList.insertChildAt(newItem, index, true);
|
||||||
|
item.parent.treeList.expand();
|
||||||
|
}});
|
||||||
|
|
||||||
|
options.push({id:"red-ui-editor-type-json-menu-delete", icon:"fa fa-times", label:RED._('common.label.delete'),onselect:function(){
|
||||||
|
item.treeList.remove();
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
if (item.type === 'array' || item.type === 'object') {
|
||||||
|
options.push(null)
|
||||||
|
options.push({id:"red-ui-editor-type-json-menu-expand-children",icon:"fa fa-angle-double-down", label:RED._('jsonEditor.expandItems'),onselect:function(){
|
||||||
|
item.treeList.expand();
|
||||||
|
item.children.forEach(function(child) {
|
||||||
|
child.treeList.expand();
|
||||||
|
})
|
||||||
|
}});
|
||||||
|
options.push({id:"red-ui-editor-type-json-menu-collapse-children",icon:"fa fa-angle-double-up", label:RED._('jsonEditor.collapseItems'),onselect:function(){
|
||||||
|
item.children.forEach(function(child) {
|
||||||
|
child.treeList.collapse();
|
||||||
|
})
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
var menuOptionMenu = RED.menu.init({
|
||||||
|
id:"red-ui-editor-type-json-menu",
|
||||||
|
options: options
|
||||||
|
});
|
||||||
|
menuOptionMenu.css({
|
||||||
|
position: "absolute"
|
||||||
|
})
|
||||||
|
menuOptionMenu.on('mouseleave', function(){ $(this).hide() });
|
||||||
|
menuOptionMenu.on('mouseup', function() { $(this).hide() });
|
||||||
|
menuOptionMenu.appendTo("body");
|
||||||
|
var top = elementPos.top;
|
||||||
|
var height = menuOptionMenu.height();
|
||||||
|
var winHeight = $(window).height();
|
||||||
|
if (top+height > winHeight) {
|
||||||
|
top -= (top+height)-winHeight + 20;
|
||||||
|
}
|
||||||
|
menuOptionMenu.css({
|
||||||
|
top: top+"px",
|
||||||
|
left: elementPos.left+"px"
|
||||||
|
})
|
||||||
|
menuOptionMenu.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseObject(obj,depth,parent) {
|
||||||
|
var result = [];
|
||||||
|
for (var prop in obj) {
|
||||||
|
if (obj.hasOwnProperty(prop)) {
|
||||||
|
result.push(handleItem(prop,obj[prop],depth,parent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
function parseArray(obj,depth,parent) {
|
||||||
|
var result = [];
|
||||||
|
var l = obj.length;
|
||||||
|
for (var i=0;i<l;i++) {
|
||||||
|
result.push(handleItem(i,obj[i],depth,parent));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
function handleItem(key,val,depth,parent) {
|
||||||
|
var item = {depth:depth, type: typeof val};
|
||||||
|
var container = $('<span class="red-ui-editor-type-json-editor-label">');
|
||||||
|
if (key != null) {
|
||||||
|
item.key = key;
|
||||||
|
var keyText;
|
||||||
|
if (typeof key === 'string') {
|
||||||
|
keyText = '"'+key+'"';
|
||||||
|
} else {
|
||||||
|
keyText = key;
|
||||||
|
}
|
||||||
|
var keyLabel = $('<span class="red-ui-debug-msg-object-key red-ui-editor-type-json-editor-label-key">').text(keyText).appendTo(container);
|
||||||
|
keyLabel.addClass('red-ui-debug-msg-type-'+(typeof key));
|
||||||
|
if (parent && parent.type === "array") {
|
||||||
|
keyLabel.addClass("red-ui-editor-type-json-editor-label-array-key")
|
||||||
|
}
|
||||||
|
|
||||||
|
keyLabel.on("click", function(evt) {
|
||||||
|
if (item.parent.type === 'array') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
evt.preventDefault();
|
||||||
|
evt.stopPropagation();
|
||||||
|
var w = Math.max(150,keyLabel.width());
|
||||||
|
var keyInput = $('<input type="text" class="red-ui-editor-type-json-editor-key">').css({width:w+"px"}).val(""+item.key).insertAfter(keyLabel).typedInput({types:['str']});
|
||||||
|
$(document).on("mousedown.nr-ui-json-editor", function(evt) {
|
||||||
|
var typedInputElement = keyInput.next(".red-ui-typedInput-container")[0];
|
||||||
|
var target = evt.target;
|
||||||
|
while (target.nodeName !== 'BODY' && target !== typedInputElement && !$(target).hasClass("red-ui-typedInput-options")) {
|
||||||
|
target = target.parentElement;
|
||||||
|
}
|
||||||
|
if (target.nodeName === 'BODY') {
|
||||||
|
var newKey = keyInput.typedInput("value");
|
||||||
|
item.key = newKey;
|
||||||
|
var keyText;
|
||||||
|
if (typeof newKey === 'string') {
|
||||||
|
keyText = '"'+newKey+'"';
|
||||||
|
} else {
|
||||||
|
keyText = newKey;
|
||||||
|
}
|
||||||
|
keyLabel.text(keyText);
|
||||||
|
keyInput.remove();
|
||||||
|
keyLabel.show();
|
||||||
|
$(document).off("mousedown.nr-ui-json-editor");
|
||||||
|
$(document).off("keydown.nr-ui-json-editor");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$(document).on("keydown.nr-ui-json-editor",function(evt) {
|
||||||
|
if (evt.keyCode === 27) {
|
||||||
|
// Escape
|
||||||
|
keyInput.remove();
|
||||||
|
keyLabel.show();
|
||||||
|
$(document).off("mousedown.nr-ui-json-editor");
|
||||||
|
$(document).off("keydown.nr-ui-json-editor");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
keyLabel.hide();
|
||||||
|
});
|
||||||
|
$('<span>').text(" : ").appendTo(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
item.expanded = depth < 2;
|
||||||
|
item.type = "array";
|
||||||
|
item.deferBuild = depth >= 2;
|
||||||
|
item.children = parseArray(val,depth+1,item);
|
||||||
|
} else if (val !== null && item.type === "object") {
|
||||||
|
item.expanded = depth < 2;
|
||||||
|
item.children = parseObject(val,depth+1,item);
|
||||||
|
item.deferBuild = depth >= 2;
|
||||||
|
} else {
|
||||||
|
item.value = val;
|
||||||
|
if (val === null) {
|
||||||
|
item.type = 'null'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var valType;
|
||||||
|
var valValue = "";
|
||||||
|
var valClass;
|
||||||
|
switch(item.type) {
|
||||||
|
case 'string': valType = 'str'; valValue = '"'+item.value+'"'; valClass = "red-ui-debug-msg-type-string"; break;
|
||||||
|
case 'number': valType = 'num'; valValue = item.value; valClass = "red-ui-debug-msg-type-number";break;
|
||||||
|
case 'boolean': valType = 'bool'; valValue = item.value; valClass = "red-ui-debug-msg-type-other";break;
|
||||||
|
case 'null': valType = item.type; valValue = item.type; valClass = "red-ui-debug-msg-type-null";break;
|
||||||
|
case 'object':
|
||||||
|
valType = item.type;
|
||||||
|
valValue = item.type;//+"{"+item.children.length+"}";
|
||||||
|
valClass = "red-ui-debug-msg-type-meta";
|
||||||
|
break;
|
||||||
|
case 'array':
|
||||||
|
valType = item.type;
|
||||||
|
valValue = item.type+"["+item.children.length+"]";
|
||||||
|
valClass = "red-ui-debug-msg-type-meta";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
var orphanedChildren;
|
||||||
|
var valueLabel = $('<span class="red-ui-editor-type-json-editor-label-value">').addClass(valClass).text(valValue).appendTo(container);
|
||||||
|
valueLabel.on("click", function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
evt.stopPropagation();
|
||||||
|
if (valType === 'str') {
|
||||||
|
valValue = valValue.substring(1,valValue.length-1);
|
||||||
|
} else if (valType === 'array') {
|
||||||
|
valValue = "";
|
||||||
|
} else if (valType === 'object') {
|
||||||
|
valValue = "";
|
||||||
|
}
|
||||||
|
var w = Math.max(150,valueLabel.width());
|
||||||
|
var val = $('<input type="text" class="red-ui-editor-type-json-editor-value">').css({width:w+"px"}).val(""+valValue).insertAfter(valueLabel).typedInput({
|
||||||
|
types:[
|
||||||
|
'str','num','bool',
|
||||||
|
{value:"null",label:"null",hasValue:false},
|
||||||
|
{value:"array",label:"array",hasValue:false},
|
||||||
|
{value:"object",label:"object",hasValue:false}
|
||||||
|
],
|
||||||
|
default: valType
|
||||||
|
});
|
||||||
|
$(document).on("mousedown.nr-ui-json-editor", function(evt) {
|
||||||
|
var typedInputElement = val.next(".red-ui-typedInput-container")[0];
|
||||||
|
var target = evt.target;
|
||||||
|
while (target.nodeName !== 'BODY' && target !== typedInputElement && !$(target).hasClass("red-ui-typedInput-options")) {
|
||||||
|
target = target.parentElement;
|
||||||
|
}
|
||||||
|
if (target.nodeName === 'BODY') {
|
||||||
|
valType = val.typedInput("type");
|
||||||
|
valValue = val.typedInput("value");
|
||||||
|
if (valType === 'num') {
|
||||||
|
valValue = valValue.trim();
|
||||||
|
if (isNaN(valValue)) {
|
||||||
|
valType = 'str';
|
||||||
|
} else if (valValue === "") {
|
||||||
|
valValue = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.value = valValue;
|
||||||
|
var valClass;
|
||||||
|
switch(valType) {
|
||||||
|
case 'str': item.children && (orphanedChildren = item.children); item.treeList.makeLeaf(true); item.type = "string"; valClass = "red-ui-debug-msg-type-string"; valValue = '"'+valValue+'"'; break;
|
||||||
|
case 'num': item.children && (orphanedChildren = item.children); item.treeList.makeLeaf(true); item.type = "number"; valClass = "red-ui-debug-msg-type-number"; break;
|
||||||
|
case 'bool': item.children && (orphanedChildren = item.children); item.treeList.makeLeaf(true); item.type = "boolean"; valClass = "red-ui-debug-msg-type-other"; item.value = (valValue === "true"); break;
|
||||||
|
case 'null': item.children && (orphanedChildren = item.children); item.treeList.makeLeaf(true); item.type = "null"; valClass = "red-ui-debug-msg-type-null"; item.value = valValue = "null"; break;
|
||||||
|
case 'object':
|
||||||
|
item.treeList.makeParent(orphanedChildren);
|
||||||
|
item.type = "object";
|
||||||
|
valClass = "red-ui-debug-msg-type-meta";
|
||||||
|
item.value = valValue = "object";
|
||||||
|
item.children.forEach(function(child,i) {
|
||||||
|
if (child.hasOwnProperty('_key')) {
|
||||||
|
child.key = child._key;
|
||||||
|
delete child._key;
|
||||||
|
var keyText;
|
||||||
|
var keyLabel = child.element.find(".red-ui-editor-type-json-editor-label-key");
|
||||||
|
keyLabel.removeClass("red-ui-editor-type-json-editor-label-array-key");
|
||||||
|
if (typeof child.key === 'string') {
|
||||||
|
keyText = '"'+child.key+'"';
|
||||||
|
keyLabel.addClass('red-ui-debug-msg-type-string');
|
||||||
|
keyLabel.removeClass('red-ui-debug-msg-type-number');
|
||||||
|
} else {
|
||||||
|
keyText = child.key;
|
||||||
|
keyLabel.removeClass('red-ui-debug-msg-type-string');
|
||||||
|
keyLabel.addClass('red-ui-debug-msg-type-number');
|
||||||
|
}
|
||||||
|
keyLabel.text(keyText);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
case 'array':
|
||||||
|
item.treeList.makeParent(orphanedChildren);
|
||||||
|
item.type = "array";
|
||||||
|
valClass = "red-ui-debug-msg-type-meta";
|
||||||
|
item.value = valValue = "array["+(item.children.length)+"]";
|
||||||
|
item.children.forEach(function(child,i) {
|
||||||
|
child._key = child.key;
|
||||||
|
child.key = i;
|
||||||
|
child.element.find(".red-ui-editor-type-json-editor-label-key")
|
||||||
|
.addClass("red-ui-editor-type-json-editor-label-array-key")
|
||||||
|
.text(""+child.key)
|
||||||
|
.removeClass('red-ui-debug-msg-type-string')
|
||||||
|
.addClass('red-ui-debug-msg-type-number');
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
valueLabel.text(valValue).removeClass().addClass("red-ui-editor-type-json-editor-label-value "+valClass);
|
||||||
|
val.remove();
|
||||||
|
valueLabel.show();
|
||||||
|
$(document).off("mousedown.nr-ui-json-editor");
|
||||||
|
$(document).off("keydown.nr-ui-json-editor");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$(document).on("keydown.nr-ui-json-editor",function(evt) {
|
||||||
|
if (evt.keyCode === 27) {
|
||||||
|
// Escape
|
||||||
|
val.remove();
|
||||||
|
valueLabel.show();
|
||||||
|
if (valType === 'str') {
|
||||||
|
valValue = '"'+valValue+'"';
|
||||||
|
}
|
||||||
|
$(document).off("mousedown.nr-ui-json-editor");
|
||||||
|
$(document).off("keydown.nr-ui-json-editor");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
valueLabel.hide();
|
||||||
|
})
|
||||||
|
item.gutter = $('<span class="red-ui-editor-type-json-editor-item-gutter"></span>');
|
||||||
|
|
||||||
|
if (parent) {//red-ui-editor-type-json-editor-item-handle
|
||||||
|
$('<span class="red-ui-editor-type-json-editor-item-handle"><i class="fa fa-bars"></span>').appendTo(item.gutter);
|
||||||
|
} else {
|
||||||
|
$('<span></span>').appendTo(item.gutter);
|
||||||
|
}
|
||||||
|
$('<button type="button" class="editor-button editor-button-small"><i class="fa fa-caret-down"></button>').appendTo(item.gutter).on("click", function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
evt.stopPropagation();
|
||||||
|
showObjectMenu($(this), item);
|
||||||
|
});
|
||||||
|
item.element = container;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
function convertToObject(item) {
|
||||||
|
var element;
|
||||||
|
switch (item.type) {
|
||||||
|
case 'string': element = item.value; break;
|
||||||
|
case 'number': element = Number(item.value); break;
|
||||||
|
case 'boolean': element = item.value; break;
|
||||||
|
case 'null': element = null; break;
|
||||||
|
case 'object':
|
||||||
|
element = {};
|
||||||
|
item.children.forEach(function(child) {
|
||||||
|
element[child.key] = convertToObject(child);
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
case 'array':
|
||||||
|
element = item.children.map(function(child) {
|
||||||
|
return convertToObject(child);
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
var definition = {
|
var definition = {
|
||||||
show: function(options) {
|
show: function(options) {
|
||||||
@ -41,9 +453,11 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var rootNode;
|
||||||
|
|
||||||
var trayOptions = {
|
var trayOptions = {
|
||||||
title: options.title,
|
title: options.title,
|
||||||
width: "inherit",
|
width: options.width||700,
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
id: "node-dialog-cancel",
|
id: "node-dialog-cancel",
|
||||||
@ -60,25 +474,60 @@
|
|||||||
if (options.requireValid && !checkValid()) {
|
if (options.requireValid && !checkValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onComplete(expressionEditor.getValue());
|
var result;
|
||||||
|
if (activeTab === "json-ui") {
|
||||||
|
if (rootNode) {
|
||||||
|
result = JSON.stringify(convertToObject(rootNode),null,4);
|
||||||
|
} else {
|
||||||
|
result = expressionEditor.getValue();
|
||||||
|
}
|
||||||
|
} else if (activeTab === "json-raw") {
|
||||||
|
result = expressionEditor.getValue();
|
||||||
|
}
|
||||||
|
onComplete && onComplete(result);
|
||||||
RED.tray.close();
|
RED.tray.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
resize: function(dimensions) {
|
resize: function(dimensions) {
|
||||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
var height = $(".red-ui-editor-type-json-tab-content").height();
|
||||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
$(".node-text-editor").css("height",(height-45)+"px");
|
||||||
var height = $("#dialog-form").height();
|
|
||||||
for (var i=0;i<rows.size();i++) {
|
|
||||||
height -= $(rows[i]).outerHeight(true);
|
|
||||||
}
|
|
||||||
height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
|
|
||||||
$(".node-text-editor").css("height",height+"px");
|
|
||||||
expressionEditor.resize();
|
expressionEditor.resize();
|
||||||
},
|
},
|
||||||
open: function(tray) {
|
open: function(tray) {
|
||||||
var trayBody = tray.find('.red-ui-tray-body');
|
var trayBody = tray.find('.red-ui-tray-body');
|
||||||
var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor');
|
var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor');
|
||||||
|
|
||||||
|
var container = $("#red-ui-editor-type-json-tab-ui-container").css({"height":"100%"});
|
||||||
|
var filterDepth = Infinity;
|
||||||
|
var list = $('<div class="red-ui-debug-msg-payload red-ui-editor-type-json-editor">').appendTo(container).treeList({
|
||||||
|
rootSortable: false,
|
||||||
|
sortable: ".red-ui-editor-type-json-editor-item-handle",
|
||||||
|
}).on("treelistchangeparent", function(event, evt) {
|
||||||
|
if (evt.old.type === 'array') {
|
||||||
|
evt.old.element.find(".red-ui-editor-type-json-editor-label-type").text("array["+evt.old.children.length+"]");
|
||||||
|
}
|
||||||
|
if (evt.item.parent.type === 'array') {
|
||||||
|
evt.item.parent.element.find(".red-ui-editor-type-json-editor-label-type").text("array["+evt.item.parent.children.length+"]");
|
||||||
|
}
|
||||||
|
}).on("treelistsort", function(event, item) {
|
||||||
|
item.children.forEach(function(child,i) {
|
||||||
|
if (item.type === 'array') {
|
||||||
|
child.key = i;
|
||||||
|
child.element.find(".red-ui-editor-type-json-editor-label-key")
|
||||||
|
.text(child.key)
|
||||||
|
.removeClass('red-ui-debug-msg-type-string')
|
||||||
|
.addClass('red-ui-debug-msg-type-number');
|
||||||
|
} else {
|
||||||
|
child.element.find(".red-ui-editor-type-json-editor-label-key")
|
||||||
|
.text('"'+child.key+'"')
|
||||||
|
.removeClass('red-ui-debug-msg-type-number')
|
||||||
|
.addClass('red-ui-debug-msg-type-string');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
expressionEditor = RED.editor.createEditor({
|
expressionEditor = RED.editor.createEditor({
|
||||||
id: 'node-input-json',
|
id: 'node-input-json',
|
||||||
value: "",
|
value: "",
|
||||||
@ -103,9 +552,56 @@
|
|||||||
expressionEditor.getSession().setValue(v||"",-1);
|
expressionEditor.getSession().setValue(v||"",-1);
|
||||||
});
|
});
|
||||||
dialogForm.i18n();
|
dialogForm.i18n();
|
||||||
|
|
||||||
|
var finishedBuild = false;
|
||||||
|
var tabs = RED.tabs.create({
|
||||||
|
element: $("#red-ui-editor-type-json-tabs"),
|
||||||
|
onchange:function(tab) {
|
||||||
|
activeTab = tab.id;
|
||||||
|
$(".red-ui-editor-type-json-tab-content").hide();
|
||||||
|
if (finishedBuild) {
|
||||||
|
if (tab.id === "json-raw") {
|
||||||
|
if (rootNode) {
|
||||||
|
var result = JSON.stringify(convertToObject(rootNode),null,4);
|
||||||
|
expressionEditor.getSession().setValue(result||"",-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (tab.id === "json-ui") {
|
||||||
|
var raw = expressionEditor.getValue().trim() ||"{}";
|
||||||
|
try {
|
||||||
|
var parsed = JSON.parse(raw);
|
||||||
|
rootNode = handleItem(null,parsed,0,null);
|
||||||
|
rootNode.class = "red-ui-editor-type-json-root-node"
|
||||||
|
list.treeList('data',[rootNode]);
|
||||||
|
} catch(err) {
|
||||||
|
rootNode = null;
|
||||||
|
list.treeList('data',[{
|
||||||
|
label: "Invalid JSON: "+err.toString()
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tab.content.show();
|
||||||
|
trayOptions.resize();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
tabs.addTab({
|
||||||
|
id: 'json-raw',
|
||||||
|
label: RED._('jsonEditor.rawMode'),
|
||||||
|
content: $("#red-ui-editor-type-json-tab-raw")
|
||||||
|
});
|
||||||
|
tabs.addTab({
|
||||||
|
id: 'json-ui',
|
||||||
|
label: RED._('jsonEditor.uiMode'),
|
||||||
|
content: $("#red-ui-editor-type-json-tab-ui")
|
||||||
|
});
|
||||||
|
finishedBuild = true;
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
close: function() {
|
close: function() {
|
||||||
expressionEditor.destroy();
|
// expressionEditor.destroy();
|
||||||
if (options.onclose) {
|
if (options.onclose) {
|
||||||
options.onclose();
|
options.onclose();
|
||||||
}
|
}
|
||||||
|
@ -461,3 +461,114 @@ button.red-ui-button-small
|
|||||||
margin: 2px;
|
margin: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.red-ui-editor input.red-ui-editor-type-json-editor-key {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red-ui-editor-type-json-editor {
|
||||||
|
height: calc(100% - 10px);
|
||||||
|
.red-ui-treeList-container {
|
||||||
|
background: $secondary-background;
|
||||||
|
}
|
||||||
|
.red-ui-treeList-label {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-height: 35px;
|
||||||
|
.red-ui-treeList-icon:before {
|
||||||
|
content:'';
|
||||||
|
display: inline-block;
|
||||||
|
height: 35px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
> span, > span > span {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
&:hover, &:hover .red-ui-treeList-sublabel-text {
|
||||||
|
background: $secondary-background-disabled;
|
||||||
|
.red-ui-editor-type-json-editor-item-gutter {
|
||||||
|
> span, > button {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.selected {
|
||||||
|
.red-ui-editor-type-json-editor-item-gutter {
|
||||||
|
background: $secondary-background-hover;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
.red-ui-editor-type-json-editor-item-gutter {
|
||||||
|
background: $secondary-background-selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.red-ui-editor-type-json-root-node {
|
||||||
|
.red-ui-editor-type-json-editor-item-gutter {
|
||||||
|
> span, > button {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.red-ui-editor-type-json-editor-controls {
|
||||||
|
height: 34px;
|
||||||
|
line-height: 34px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.red-ui-editor-type-json-editor-key {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
.red-ui-editor-type-json-editor-label {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
.red-ui-editor-type-json-editor-label-value {
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
.red-ui-editor-type-json-editor-label-value,
|
||||||
|
.red-ui-editor-type-json-editor-label-key {
|
||||||
|
display: inline-block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-height: 34px;
|
||||||
|
line-height: 30px;
|
||||||
|
padding: 0 2px;
|
||||||
|
border: 2px solid rgba(0,0,0,0);
|
||||||
|
border-radius: 3px;
|
||||||
|
&:not(.red-ui-editor-type-json-editor-label-array-key):hover {
|
||||||
|
border-color: $list-item-background-hover;
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.red-ui-editor-type-json-editor-item-gutter {
|
||||||
|
width: 48px;
|
||||||
|
padding-left: 4px;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
line-height: 35px;
|
||||||
|
color: $tertiary-text-color;
|
||||||
|
background: $secondary-background-disabled;
|
||||||
|
> span {
|
||||||
|
display: inline-block;
|
||||||
|
height: 35px;
|
||||||
|
line-height: 35px;
|
||||||
|
width: 20px;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
> span, > button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.red-ui-editor-type-json-editor-item-handle {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
.red-ui-editor-type-json-tab-content {
|
||||||
|
position: relative;
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user