diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a73106c4..ee591143f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,65 @@ +#### 0.19: Milestone Release + +Editor + + - Add editorTheme.palette.theme to allow overriding colours + - Index all node properties when searching Fixes #1446 + - Handle NaN and Infinity properly in debug sidebar Fixes #1778 #1779 + - Prevent horizontal scroll when palette name cannot wrap + - Ignore middle-click on node/ports to enable panning + - Better wire layout when looping back + - fix appearence of retry button of remote branch management dialog + - Handle releasing ctrl when using quick-add node dialog + - Add $env function to JSONata expressions + - Widen support for env var to use ${} or $() syntax + - Add env-var support to TypedInput + - Show unknown node properties in info tab + - Add node icon picker widget + - Only edit nodes on dbl click on primary button with no modifiers + - Allow subflows to be put in any palette category + - Add flow navigator widget + - Cache flow library result to improve response time Fixes #1753 + - Add middle-button-drag to pan the workspace + - allow multi-line category name in editor + - Redesign sidebar tabs + - Do not disable the export-clipboard menu option with empty selection + +Nodes + + - Change: Ensure runtime errors in Change node can be caught Fixes #1769 + - File: Add output to File Out node + - Function: add expandable JavaScript editor pane + - Function: allow id and name reference in function node code (#1731) + - HTTP Request: Move to request module + - HTTP: Ensure apiMaxLength applies to HTTP Nodes Fixes #1278 + - Join: accumulate top level properties + - Join: allow environment variable as reduce init value + - JSON: add JSON schema validation via msg.schema + - Pi: Let nrgpio code work with python 3 + - Pi: let Pi nodes be visible/editable on all platforms + - Switch: add isEmpty rule + - TCP: queue messages while connecting; closes #1414 + - TLS: Add servername option to TLS config node for SNI Fixes #1805 + - UDP: Don't accidentally re-use udp port when set to not do so + +Persistent Context + + - Add Context data sidebar + - Add persistable context option + - Add default memory store + - Add file-based context store + - Add async mode to evaluateJSONataExpression + - Update RED.util.evaluateNodeProperty to support context stores + +Runtime + + - Support flow.disabled and .info in /flow API + - Node errors should be Strings not Errors Fixes #1781 + - Add detection of connection timeout in git communication Fixes #1770 + - Handle loading empty nodesDir + - Add 'private' property to userDir generated package.json + - Add RED.require to allow nodes to access other modules + #### 0.18.7: Maintenance Release Editor Fixes diff --git a/Gruntfile.js b/Gruntfile.js index fb609c992..15bd4afd7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -24,6 +24,10 @@ module.exports = function(grunt) { nodemonArgs.push(flowFile); } + var nonHeadless = grunt.option('non-headless'); + if (nonHeadless) { + process.env.NODE_RED_NON_HEADLESS = 'true'; + } grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), paths: { @@ -144,10 +148,12 @@ module.exports = function(grunt) { "editor/js/ui/keyboard.js", "editor/js/ui/workspaces.js", "editor/js/ui/view.js", + "editor/js/ui/view-navigator.js", "editor/js/ui/sidebar.js", "editor/js/ui/palette.js", "editor/js/ui/tab-info.js", "editor/js/ui/tab-config.js", + "editor/js/ui/tab-context.js", "editor/js/ui/palette-editor.js", "editor/js/ui/editor.js", "editor/js/ui/editors/*.js", diff --git a/editor/icons/file-in.png b/editor/icons/file-in.png new file mode 100644 index 000000000..d3e5cd7fe Binary files /dev/null and b/editor/icons/file-in.png differ diff --git a/editor/icons/file-out.png b/editor/icons/file-out.png new file mode 100644 index 000000000..051f819c8 Binary files /dev/null and b/editor/icons/file-out.png differ diff --git a/editor/js/keymap.json b/editor/js/keymap.json index 08f83b4a5..dc37232e2 100644 --- a/editor/js/keymap.json +++ b/editor/js/keymap.json @@ -10,6 +10,7 @@ "ctrl-g i": "core:show-info-tab", "ctrl-g d": "core:show-debug-tab", "ctrl-g c": "core:show-config-tab", + "ctrl-g x": "core:show-context-tab", "ctrl-e": "core:show-export-dialog", "ctrl-i": "core:show-import-dialog", "ctrl-space": "core:toggle-sidebar", diff --git a/editor/js/nodes.js b/editor/js/nodes.js index 433e400a2..12ff3e682 100644 --- a/editor/js/nodes.js +++ b/editor/js/nodes.js @@ -133,7 +133,7 @@ RED.nodes = (function() { registerNodeType: function(nt,def) { nodeDefinitions[nt] = def; def.type = nt; - if (def.category != "subflows") { + if (nt.substring(0,8) != "subflow:") { def.set = nodeSets[typeToId[nt]]; nodeSets[typeToId[nt]].added = true; nodeSets[typeToId[nt]].enabled = true; @@ -356,7 +356,7 @@ RED.nodes = (function() { defaults:{name:{value:""}}, info: sf.info, icon: function() { return sf.icon||"subflow.png" }, - category: "subflows", + category: sf.category || "subflows", inputs: sf.in.length, outputs: sf.out.length, color: "#da9", @@ -519,6 +519,7 @@ RED.nodes = (function() { node.type = n.type; node.name = n.name; node.info = n.info; + node.category = n.category; node.in = []; node.out = []; diff --git a/editor/js/ui/clipboard.js b/editor/js/ui/clipboard.js index 0d7519e10..74df8dce9 100644 --- a/editor/js/ui/clipboard.js +++ b/editor/js/ui/clipboard.js @@ -276,9 +276,20 @@ RED.clipboard = (function() { if (typeof value !== "string" ) { value = JSON.stringify(value, function(key,value) { if (value !== null && typeof value === 'object') { - if (value.__encoded__ && value.hasOwnProperty('data') && value.hasOwnProperty('length')) { - truncated = value.data.length !== value.length; - return value.data; + if (value.__enc__) { + if (value.hasOwnProperty('data') && value.hasOwnProperty('length')) { + truncated = value.data.length !== value.length; + return value.data; + } + if (value.type === 'function' || value.type === 'internal') { + return undefined + } + if (value.type === 'number') { + // Handle NaN and Infinity - they are not permitted + // in JSON. We can either substitute with a String + // representation or null + return null; + } } } return value; diff --git a/editor/js/ui/common/popover.js b/editor/js/ui/common/popover.js index 0170f7070..4e2a0b0ef 100644 --- a/editor/js/ui/common/popover.js +++ b/editor/js/ui/common/popover.js @@ -48,23 +48,31 @@ RED.popover = (function() { var openPopup = function(instant) { if (active) { - div = $('
').appendTo("body"); + div = $('
'); if (size !== "default") { div.addClass("red-ui-popover-size-"+size); } if (typeof content === 'function') { - content.call(res).appendTo(div); + var result = content.call(res); + if (result === null) { + return; + } + if (typeof result === 'string') { + div.text(result); + } else { + div.append(result); + } } else { div.html(content); } if (width !== "auto") { div.width(width); } - + div.appendTo("body"); var targetPos = target.offset(); - var targetWidth = target.width(); - var targetHeight = target.height(); + var targetWidth = target.outerWidth(); + var targetHeight = target.outerHeight(); var divHeight = div.height(); var divWidth = div.width(); if (direction === 'right') { @@ -147,7 +155,17 @@ RED.popover = (function() { } return { - create: createPopover + create: createPopover, + tooltip: function(target,content) { + RED.popover.create({ + target:target, + trigger: "hover", + size: "small", + direction: "bottom", + content: content, + delay: { show: 550, hide: 10 } + }); + } } })(); diff --git a/editor/js/ui/common/tabs.js b/editor/js/ui/common/tabs.js index 23b69f5d1..d664235be 100644 --- a/editor/js/ui/common/tabs.js +++ b/editor/js/ui/common/tabs.js @@ -17,6 +17,9 @@ RED.tabs = (function() { + + var defaultTabIcon = "fa fa-lemon-o"; + function createTabs(options) { var tabs = {}; var pinnedTabsCount = 0; @@ -71,6 +74,7 @@ RED.tabs = (function() { var id = $(el).data('tabId'); var opt = { id:"red-ui-tabs-menu-option-"+id, + icon: tabs[id].iconClass || defaultTabIcon, label: tabs[id].name, onselect: function() { activateTab(id); @@ -90,12 +94,12 @@ RED.tabs = (function() { collapsibleMenu.on('mouseleave', function(){ $(this).hide() }); collapsibleMenu.on('mouseup', function() { $(this).hide() }); collapsibleMenu.appendTo("body"); - var elementPos = selectButton.offset(); - collapsibleMenu.css({ - top: (elementPos.top+selectButton.height()-20)+"px", - left: (elementPos.left - collapsibleMenu.width() + selectButton.width())+"px" - }) } + var elementPos = selectButton.offset(); + collapsibleMenu.css({ + top: (elementPos.top+selectButton.height()-20)+"px", + left: (elementPos.left - collapsibleMenu.width() + selectButton.width())+"px" + }) collapsibleMenu.toggle(); }) @@ -170,8 +174,8 @@ RED.tabs = (function() { ul.children().css({"transition": "width 100ms"}); link.parent().addClass("active"); var parentId = link.parent().attr('id'); - wrapper.find(".red-ui-tab-link-button").removeClass("active"); - $("#"+parentId+"-link-button").addClass("active"); + wrapper.find(".red-ui-tab-link-button").removeClass("active selected"); + $("#"+parentId+"-link-button").addClass("active selected"); if (options.scrollable) { var pos = link.parent().position().left; if (pos-21 < 0) { @@ -339,7 +343,7 @@ RED.tabs = (function() { if (tab.iconClass) { $('',{class:tab.iconClass}).appendTo(pinnedLink); } else { - $('',{class:"fa fa-lemon-o"}).appendTo(pinnedLink); + $('',{class:defaultTabIcon}).appendTo(pinnedLink); } pinnedLink.click(function(evt) { evt.preventDefault(); @@ -349,14 +353,7 @@ RED.tabs = (function() { pinnedLink.addClass("red-ui-tab-link-button-pinned"); pinnedTabsCount++; } - RED.popover.create({ - target:$(pinnedLink), - trigger: "hover", - size: "small", - direction: "bottom", - content: tab.name, - delay: { show: 550, hide: 10 } - }); + RED.popover.tooltip($(pinnedLink), tab.name); } link.on("click",onTabClick); @@ -370,7 +367,6 @@ RED.tabs = (function() { removeTab(tab.id); }); } - updateTabWidths(); if (options.onadd) { options.onadd(tab); } @@ -455,6 +451,9 @@ RED.tabs = (function() { } }) } + setTimeout(function() { + updateTabWidths(); + },10); collapsibleMenu = null; }, removeTab: removeTab, diff --git a/editor/js/ui/common/typedInput.js b/editor/js/ui/common/typedInput.js index f5227fd68..2243f0718 100644 --- a/editor/js/ui/common/typedInput.js +++ b/editor/js/ui/common/typedInput.js @@ -14,10 +14,38 @@ * limitations under the License. **/ (function($) { + var contextParse = function(v) { + var parts = RED.utils.parseContextKey(v); + return { + option: parts.store, + value: parts.key + } + } + var contextExport = function(v,opt) { + if (!opt) { + return v; + } + var store = ((typeof opt === "string")?opt:opt.value) + if (store !== RED.settings.context.default) { + return "#:("+store+")::"+v; + } else { + return v; + } + } var allOptions = { msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression}, - flow: {value:"flow",label:"flow.",validate:RED.utils.validatePropertyExpression}, - global: {value:"global",label:"global.",validate:RED.utils.validatePropertyExpression}, + flow: {value:"flow",label:"flow.",hasValue:true, + options:[], + validate:RED.utils.validatePropertyExpression, + parse: contextParse, + export: contextExport + }, + global: {value:"global",label:"global.",hasValue:true, + options:[], + validate:RED.utils.validatePropertyExpression, + parse: contextParse, + export: contextExport + }, str: {value:"str",label:"string",icon:"red/images/typedInput/az.png"}, num: {value:"num",label:"number",icon:"red/images/typedInput/09.png",validate:/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/}, bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.png",options:["true","false"]}, @@ -87,25 +115,40 @@ $.widget( "nodered.typedInput", { _create: function() { + try { if (!nlsd && RED && RED._) { for (var i in allOptions) { if (allOptions.hasOwnProperty(i)) { allOptions[i].label = RED._("typedInput.type."+i,{defaultValue:allOptions[i].label}); } } + var contextStores = RED.settings.context.stores; + var contextOptions = contextStores.map(function(store) { + return {value:store,label: store, icon:''} + }) + if (contextOptions.length < 2) { + allOptions.flow.options = []; + allOptions.global.options = []; + } else { + allOptions.flow.options = contextOptions; + allOptions.global.options = contextOptions; + } } nlsd = true; var that = this; this.disarmClick = false; + this.input = $(''); + this.input.insertAfter(this.element); + this.input.val(this.element.val()); this.element.addClass('red-ui-typedInput'); this.uiWidth = this.element.outerWidth(); - this.elementDiv = this.element.wrap("
").parent().addClass('red-ui-typedInput-input'); + this.elementDiv = this.input.wrap("
").parent().addClass('red-ui-typedInput-input'); this.uiSelect = this.elementDiv.wrap( "
" ).parent(); var attrStyle = this.element.attr('style'); var m; if ((m = /width\s*:\s*(calc\s*\(.*\)|\d+(%|px))/i.exec(attrStyle)) !== null) { - this.element.css('width','100%'); + this.input.css('width','100%'); this.uiSelect.width(m[1]); this.uiWidth = null; } else { @@ -114,17 +157,19 @@ ["Right","Left"].forEach(function(d) { var m = that.element.css("margin"+d); that.uiSelect.css("margin"+d,m); - that.element.css("margin"+d,0); + that.input.css("margin"+d,0); }); + this.uiSelect.addClass("red-ui-typedInput-container"); + this.element.attr('type','hidden'); + this.options.types = this.options.types||Object.keys(allOptions); this.selectTrigger = $('').prependTo(this.uiSelect); - if (this.options.types.length > 1) { - $('').appendTo(this.selectTrigger); - } - this.selectLabel = $('').appendTo(this.selectTrigger); + $('').toggle(this.options.types.length > 1).appendTo(this.selectTrigger); + + this.selectLabel = $('').appendTo(this.selectTrigger); this.types(this.options.types); @@ -138,14 +183,16 @@ this.typeField = $("",{type:'hidden'}).appendTo(this.uiSelect); } - this.element.on('focus', function() { + this.input.on('focus', function() { that.uiSelect.addClass('red-ui-typedInput-focus'); }); - this.element.on('blur', function() { + this.input.on('blur', function() { that.uiSelect.removeClass('red-ui-typedInput-focus'); }); - this.element.on('change', function() { + this.input.on('change', function() { that.validate(); + that.element.val(that.value()); + that.element.trigger('change',that.propertyType,that.value()); }) this.selectTrigger.click(function(event) { event.preventDefault(); @@ -161,8 +208,11 @@ }) // explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline' - this.optionSelectTrigger = $('').appendTo(this.uiSelect); + this.optionSelectTrigger = $('').appendTo(this.uiSelect); this.optionSelectLabel = $('').prependTo(this.optionSelectTrigger); + RED.popover.tooltip(this.optionSelectLabel,function() { + return that.optionValue; + }); this.optionSelectTrigger.click(function(event) { event.preventDefault(); that._showOptionSelectMenu(); @@ -177,17 +227,18 @@ that.uiSelect.addClass('red-ui-typedInput-focus'); }); - this.optionExpandButton = $('').appendTo(this.uiSelect); - - + this.optionExpandButton = $('').appendTo(this.uiSelect); this.type(this.options.default||this.typeList[0].value); + }catch(err) { + console.log(err.stack); + } }, _showTypeMenu: function() { if (this.typeList.length > 1) { this._showMenu(this.menu,this.selectTrigger); this.menu.find("[value='"+this.propertyType+"']").focus(); } else { - this.element.focus(); + this.input.focus(); } }, _showOptionSelectMenu: function() { @@ -196,8 +247,8 @@ minWidth:this.optionSelectLabel.width() }); - this._showMenu(this.optionMenu,this.optionSelectLabel); - var selectedOption = this.optionMenu.find("[value='"+this.value()+"']"); + this._showMenu(this.optionMenu,this.optionSelectTrigger); + var selectedOption = this.optionMenu.find("[value='"+this.optionValue+"']"); if (selectedOption.length === 0) { selectedOption = this.optionMenu.children(":first"); } @@ -209,7 +260,7 @@ $(document).off("mousedown.close-property-select"); menu.hide(); if (this.elementDiv.is(":visible")) { - this.element.focus(); + this.input.focus(); } else if (this.optionSelectTrigger.is(":visible")){ this.optionSelectTrigger.focus(); } else { @@ -228,10 +279,19 @@ op.text(opt.label); } if (opt.icon) { - $('',{src:opt.icon,style:"margin-right: 4px; height: 18px;"}).prependTo(op); + if (opt.icon.indexOf("<") === 0) { + $(opt.icon).prependTo(op); + } else if (opt.icon.indexOf("/") !== -1) { + $('',{src:opt.icon,style:"margin-right: 4px; height: 18px;"}).prependTo(op); + } else { + $('',{class:"red-ui-typedInput-icon "+opt.icon}).prependTo(op); + } } else { op.css({paddingLeft: "18px"}); } + if (!opt.icon && !opt.label) { + op.text(opt.value); + } op.click(function(event) { event.preventDefault(); @@ -310,7 +370,8 @@ if (this.uiWidth !== null) { this.uiSelect.width(this.uiWidth); } - if (this.typeMap[this.propertyType] && this.typeMap[this.propertyType].hasValue === false) { + var type = this.typeMap[this.propertyType]; + if (type && type.hasValue === false) { this.selectTrigger.addClass("red-ui-typedInput-full-width"); } else { this.selectTrigger.removeClass("red-ui-typedInput-full-width"); @@ -320,13 +381,68 @@ this.elementDiv.css('right',"22px"); } else { this.elementDiv.css('right','0'); + this.input.css({ + 'border-top-right-radius': '4px', + 'border-bottom-right-radius': '4px' + }); } + + // if (this.optionSelectTrigger) { + // this.optionSelectTrigger.css({'left':(labelWidth)+"px",'width':'calc( 100% - '+labelWidth+'px )'}); + // } + if (this.optionSelectTrigger) { - this.optionSelectTrigger.css({'left':(labelWidth)+"px",'width':'calc( 100% - '+labelWidth+'px )'}); + if (type && type.options && type.hasValue === true) { + this.optionSelectLabel.css({'left':'auto'}) + var lw = this._getLabelWidth(this.optionSelectLabel); + this.optionSelectTrigger.css({'width':(23+lw)+"px"}); + this.elementDiv.css('right',(23+lw)+"px"); + this.input.css({ + 'border-top-right-radius': 0, + 'border-bottom-right-radius': 0 + }); + } else { + this.optionSelectLabel.css({'left':'0'}) + this.optionSelectTrigger.css({'width':'calc( 100% - '+labelWidth+'px )'}); + if (!this.optionExpandButton.is(":visible")) { + this.elementDiv.css({'right':0}); + this.input.css({ + 'border-top-right-radius': '4px', + 'border-bottom-right-radius': '4px' + }); + } + } } } }, + _updateOptionSelectLabel: function(o) { + var opt = this.typeMap[this.propertyType]; + this.optionSelectLabel.empty(); + if (o.icon) { + if (o.icon.indexOf("<") === 0) { + $(o.icon).prependTo(this.optionSelectLabel); + } else if (o.icon.indexOf("/") !== -1) { + // url + $('',{src:o.icon,style:"height: 18px;"}).prependTo(this.optionSelectLabel); + } else { + // icon class + $('',{class:"red-ui-typedInput-icon "+o.icon}).prependTo(this.optionSelectLabel); + } + } else if (o.label) { + this.optionSelectLabel.text(o.label); + } else { + this.optionSelectLabel.text(o.value); + } + if (opt.hasValue) { + this.optionValue = o.value; + this._resize(); + this.input.trigger('change',this.propertyType,this.value()); + } + }, _destroy: function() { + if (this.optionMenu) { + this.optionMenu.remove(); + } this.menu.remove(); }, types: function(types) { @@ -344,13 +460,18 @@ return result; }); this.selectTrigger.toggleClass("disabled", this.typeList.length === 1); + this.selectTrigger.find(".fa-sort-desc").toggle(this.typeList.length > 1) if (this.menu) { this.menu.remove(); } this.menu = this._createMenu(this.typeList, function(v) { that.type(v) }); if (currentType && !this.typeMap.hasOwnProperty(currentType)) { this.type(this.typeList[0].value); + } else { + this.propertyType = null; + this.type(currentType); } + setTimeout(function() {that._resize();},0); }, width: function(desiredWidth) { this.uiWidth = desiredWidth; @@ -358,33 +479,33 @@ }, value: function(value) { if (!arguments.length) { - return this.element.val(); + var v = this.input.val(); + if (this.typeMap[this.propertyType].export) { + v = this.typeMap[this.propertyType].export(v,this.optionValue) + } + return v; } else { + var selectedOption; if (this.typeMap[this.propertyType].options) { - var validValue = false; - var label; for (var i=0;i",{class:"node-diff-node-entry-node"}); - var colour = def.color; + var colour = RED.utils.getNodeColor(node.type,def); var icon_url = RED.utils.getNodeIcon(def,node); if (node.type === 'tab') { colour = "#C0DEED"; diff --git a/editor/js/ui/editor.js b/editor/js/ui/editor.js index f6493f601..e0445be54 100644 --- a/editor/js/ui/editor.js +++ b/editor/js/ui/editor.js @@ -108,17 +108,6 @@ RED.editor = (function() { } } } - if (node.icon) { - var iconPath = RED.utils.separateIconPath(node.icon); - if (!iconPath.module) { - return isValid; - } - var iconSets = RED.nodes.getIconSets(); - var iconFileList = iconSets[iconPath.module]; - if (!iconFileList || iconFileList.indexOf(iconPath.file) === -1) { - isValid = false; - } - } return isValid; } @@ -170,27 +159,6 @@ RED.editor = (function() { } } } - validateIcon(node); - } - - function validateIcon(node) { - if (node._def.hasOwnProperty("defaults") && !node._def.defaults.hasOwnProperty("icon") && node.icon) { - var iconPath = RED.utils.separateIconPath(node.icon); - var iconSets = RED.nodes.getIconSets(); - var iconFileList = iconSets[iconPath.module]; - var iconModule = $("#node-settings-icon-module"); - var iconFile = $("#node-settings-icon-file"); - if (!iconFileList) { - iconModule.addClass("input-error"); - iconFile.removeClass("input-error"); - } else if (iconFileList.indexOf(iconPath.file) === -1) { - iconModule.removeClass("input-error"); - iconFile.addClass("input-error"); - } else { - iconModule.removeClass("input-error"); - iconFile.removeClass("input-error"); - } - } } function validateNodeEditorProperty(node,defaults,property,prefix) { @@ -528,6 +496,8 @@ RED.editor = (function() { label = node.type; if (node.type === '_expression') { label = RED._("expressionEditor.title"); + } else if (node.type === '_js') { + label = RED._("jsEditor.title"); } else if (node.type === '_json') { label = RED._("jsonEditor.title"); } else if (node.type === '_markdown') { @@ -711,6 +681,97 @@ RED.editor = (function() { } return result; } + function showIconPicker(container, node, iconPath, done) { + var containerPos = container.offset(); + var pickerBackground = $('
').css({ + position: "absolute",top:0,bottom:0,left:0,right:0,zIndex:20 + }).appendTo("body"); + + var top = containerPos.top - 30; + + if (top+280 > $( window ).height()) { + top = $( window ).height() - 280; + } + var picker = $('
').css({ + top: top+"px", + left: containerPos.left+"px", + }).appendTo("body"); + + var hide = function() { + pickerBackground.remove(); + picker.remove(); + RED.keyboard.remove("escape"); + } + RED.keyboard.add("*","escape",function(){hide()}); + pickerBackground.on("mousedown", hide); + + var searchDiv = $("
",{class:"red-ui-search-container"}).appendTo(picker); + searchInput = $('').attr("placeholder",RED._("editor.searchIcons")).appendTo(searchDiv).searchBox({ + delay: 50, + change: function() { + var searchTerm = $(this).val().trim(); + if (searchTerm === "") { + iconList.find(".red-ui-icon-list-module").show(); + iconList.find(".red-ui-icon-list-icon").show(); + } else { + iconList.find(".red-ui-icon-list-module").hide(); + iconList.find(".red-ui-icon-list-icon").each(function(i,n) { + if ($(n).data('icon').indexOf(searchTerm) === -1) { + $(n).hide(); + } else { + $(n).show(); + } + }); + } + } + }); + + var row = $('
').appendTo(picker); + var iconList = $('
').appendTo(picker); + var metaRow = $('
').appendTo(picker); + var summary = $('').appendTo(metaRow); + var resetButton = $('').appendTo(metaRow).click(function(e) { + e.preventDefault(); + hide(); + done(null); + }); + var iconSets = RED.nodes.getIconSets(); + Object.keys(iconSets).forEach(function(moduleName) { + var icons = iconSets[moduleName]; + if (icons.length > 0) { + // selectIconModule.append($("").val(moduleName).text(moduleName)); + var header = $('
').text(moduleName).appendTo(iconList); + $('').prependTo(header); + icons.forEach(function(icon) { + var iconDiv = $('
',{class:"red-ui-icon-list-icon"}).appendTo(iconList); + var nodeDiv = $('
',{class:"red-ui-search-result-node"}).appendTo(iconDiv); + var colour = RED.utils.getNodeColor(node.type, node._def); + var icon_url = "icons/"+moduleName+"/"+icon; + iconDiv.data('icon',icon_url) + nodeDiv.css('backgroundColor',colour); + var iconContainer = $('
',{class:"palette_icon_container"}).appendTo(nodeDiv); + $('
',{class:"palette_icon",style:"background-image: url("+icon_url+")"}).appendTo(iconContainer); + + if (iconPath.module === moduleName && iconPath.file === icon) { + iconDiv.addClass("selected"); + } + iconDiv.on("mouseover", function() { + summary.text(icon); + }) + iconDiv.on("mouseout", function() { + summary.html(" "); + }) + iconDiv.click(function() { + hide(); + done(moduleName+"/"+icon); + }) + }) + } + }); + picker.slideDown(100); + searchInput.focus(); + } + function buildLabelForm(container,node) { var dialogForm = $('
').appendTo(container); @@ -748,81 +809,36 @@ RED.editor = (function() { } if ((!node._def.defaults || !node._def.defaults.hasOwnProperty("icon"))) { - $('
').appendTo(dialogForm); - var iconDiv = $("#node-settings-icon"); - $('