diff --git a/Gruntfile.js b/Gruntfile.js index 597337ae5..ca01292f8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -119,7 +119,7 @@ module.exports = function(grunt) { "editor/js/ui/palette.js", "editor/js/ui/tab-info.js", "editor/js/ui/tab-config.js", - "editor/js/ui/tab-palette.js", + "editor/js/ui/palette-editor.js", "editor/js/ui/editor.js", "editor/js/ui/tray.js", "editor/js/ui/clipboard.js", diff --git a/editor/js/main.js b/editor/js/main.js index 3e1b55761..06fd8cf9f 100644 --- a/editor/js/main.js +++ b/editor/js/main.js @@ -56,11 +56,9 @@ var RED = (function() { success: function(data) { $("body").append(data); $("body").i18n(); - - $(".palette-spinner").hide(); - $(".palette-scroll").show(); - $("#palette-search").show(); + $(".palette-scroll").removeClass("hide"); + $("#palette-search").removeClass("hide"); loadFlows(); } }); diff --git a/editor/js/nodes.js b/editor/js/nodes.js index 32085802c..8a6a50c03 100644 --- a/editor/js/nodes.js +++ b/editor/js/nodes.js @@ -32,12 +32,22 @@ RED.nodes = (function() { } var registry = (function() { + var moduleList = {}; var nodeList = []; var nodeSets = {}; var typeToId = {}; var nodeDefinitions = {}; var exports = { + getModule: function(module) { + return moduleList[module]; + }, + getNodeSetForType: function(nodeType) { + return exports.getNodeSet(typeToId[nodeType]); + }, + getModuleList: function() { + return moduleList; + }, getNodeList: function() { return nodeList; }, @@ -55,27 +65,38 @@ RED.nodes = (function() { typeToId[ns.types[j]] = ns.id; } nodeList.push(ns); + + moduleList[ns.module] = moduleList[ns.module] || { + name:ns.module, + version:ns.version, + local:ns.local, + sets:{} + }; + moduleList[ns.module].sets[ns.name] = ns; + RED.events.emit("registry:node-set-added",ns); }, removeNodeSet: function(id) { var ns = nodeSets[id]; for (var j=0;j').appendTo(this.element); + var li = $('
  • '); + var added = false; + if (this.options.sort) { + var items = this.items(); + var skip = false; + items.each(function(i,el) { + if (added) { return } + var itemData = el.data('data'); + if (that.options.sort(data,itemData) < 0) { + li.insertBefore(el.closest("li")); + added = true; + } + }); + } + if (!added) { + li.appendTo(this.element); + } var row = $('
    ').addClass("red-ui-editableList-item-content").appendTo(li); row.data('data',data); if (this.options.sortable === true) { diff --git a/editor/js/ui/palette-editor.js b/editor/js/ui/palette-editor.js new file mode 100644 index 000000000..310e07539 --- /dev/null +++ b/editor/js/ui/palette-editor.js @@ -0,0 +1,317 @@ +/** + * Copyright 2016 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ +RED.palette.editor = (function() { + + var nodeList; + var typesInUse = {}; + + var nodeEntries = {}; + + var eventTimers = {}; + + function changeNodeState(id,state,callback) { + $.ajax({ + url:"nodes/"+id, + type: "PUT", + data: JSON.stringify({ + enabled: state + }), + contentType: "application/json; charset=utf-8" + }).done(function(data,textStatus,xhr) { + callback(); + }).fail(function(xhr,textStatus,err) { + callback(xhr); + }) + } + function refreshNodeModule(module) { + if (!eventTimers.hasOwnProperty(module)) { + eventTimers[module] = setTimeout(function() { + delete eventTimers[module]; + _refreshNodeModule(module); + },100); + } + } + + + function getContrastingBorder(rgbColor){ + var parts = /^rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)[,)]/.exec(rgbColor); + if (parts) { + var r = parseInt(parts[1]); + var g = parseInt(parts[2]); + var b = parseInt(parts[3]); + var yiq = ((r*299)+(g*587)+(b*114))/1000; + if (yiq > 160) { + r = Math.floor(r*0.8); + g = Math.floor(g*0.8); + b = Math.floor(b*0.8); + return "rgb("+r+","+g+","+b+")"; + } + } + return rgbColor; + } + + + function _refreshNodeModule(module) { + if (!nodeEntries.hasOwnProperty(module)) { + nodeEntries[module] = {info:RED.nodes.registry.getModule(module)}; + nodeList.editableList('addItem', nodeEntries[module]); + //console.log(nodeList.editableList('items')); + + } else { + var moduleInfo = nodeEntries[module].info; + var nodeEntry = nodeEntries[module].elements; + if (nodeEntry) { + var activeTypeCount = 0; + var typeCount = 0; + nodeEntries[module].totalUseCount = 0; + nodeEntries[module].setUseCount = {}; + + for (var setName in moduleInfo.sets) { + if (moduleInfo.sets.hasOwnProperty(setName)) { + var inUseCount = 0; + var set = moduleInfo.sets[setName]; + var setElements = nodeEntry.sets[setName]; + + if (set.enabled) { + activeTypeCount += set.types.length; + } + typeCount += set.types.length; + for (var i=0;i 0) { + setElements.enableButton.html('in use'); + setElements.enableButton.addClass('disabled'); + } else { + setElements.enableButton.removeClass('disabled'); + if (set.enabled) { + setElements.enableButton.html('disable'); + } else { + setElements.enableButton.html('enable'); + } + } + setElements.setRow.toggleClass("palette-module-set-disabled",!set.enabled); + } + } + var nodeCount = (activeTypeCount === typeCount)?typeCount:activeTypeCount+" / "+typeCount; + nodeEntry.setCount.html(nodeCount+" node"+(typeCount>1?"s":"")); + + if (nodeEntries[module].totalUseCount > 0) { + nodeEntry.enableButton.html("in use"); + nodeEntry.enableButton.addClass('disabled'); + nodeEntry.removeButton.hide(); + } else { + nodeEntry.enableButton.removeClass('disabled'); + nodeEntry.removeButton.show(); + if (activeTypeCount === 0) { + nodeEntry.enableButton.html("enable all"); + } else { + nodeEntry.enableButton.html("disable all"); + } + nodeEntry.container.toggleClass("disabled",(activeTypeCount === 0)); + } + } + } + + } + function showPaletteEditor() { + $("#header-shade").show(); + $("#editor-shade").show(); + $("#sidebar-shade").show(); + $("#main-container").addClass("palette-expanded"); + } + function hidePaletteEditor() { + $("#main-container").removeClass("palette-expanded"); + $("#header-shade").hide(); + $("#editor-shade").hide(); + $("#sidebar-shade").hide(); + } + + function init() { + $("#editor-shade").click(function() { + if ($("#main-container").hasClass("palette-expanded")) { + hidePaletteEditor(); + } + }); + $("#palette-edit").on("click",function(e) { + if ($("#main-container").hasClass("palette-expanded")) { + hidePaletteEditor(); + } else { + showPaletteEditor(); + } + }); + $("#palette-editor-close").on("click", function(e) { + hidePaletteEditor(); + }) + + var divTabs = $('
    ',{style:"position:absolute;top:80px;left:0;right:0;bottom:0"}).appendTo("#palette-editor"); + + nodeList = $('
      ',{id:"palette-module-list", style:"position: absolute;top: 0;bottom: 0;left: 0;right: 0px;"}).appendTo(divTabs).editableList({ + addButton: false, + sort: function(A,B) { + return A.info.name.localeCompare(B.info.name); + }, + addItem: function(container,i,object) { + var entry = object.info; + + var headerRow = $('
      ',{class:"palette-module-header"}).appendTo(container); + + var titleRow = $('
      ',{class:"palette-module-meta"}).appendTo(headerRow); + var chevron = $('').appendTo(titleRow); + var title = $('',{class:"palette-module-name"}).html(entry.name).appendTo(titleRow); + + var metaRow = $('
      ',{class:"palette-module-meta"}).appendTo(headerRow); + var version = $('').appendTo(metaRow); + $('').html(entry.version).appendTo(version); + + + var buttonRow = $('
      ',{class:"palette-module-meta"}).appendTo(headerRow); + + var setButton = $(' ').appendTo(buttonRow); + var setCount = $('').appendTo(setButton); + + var buttonGroup = $('
      ',{class:"palette-module-button-group"}).appendTo(buttonRow); + var removeButton = $('').html('remove').appendTo(buttonGroup); + if (!entry.local) { + removeButton.hide(); + } + var enableButton = $('').html('disable all').appendTo(buttonGroup); + + var contentRow = $('
      ',{class:"palette-module-content"}).appendTo(container); + + object.elements = { + removeButton: removeButton, + enableButton: enableButton, + setCount: setCount, + container: container, + sets: {} + } + setButton.click(function() { + if (container.hasClass('expanded')) { + container.removeClass('expanded'); + contentRow.slideUp(); + } else { + container.addClass('expanded'); + contentRow.slideDown(); + } + }) + + var setList = Object.keys(entry.sets) + setList.sort(function(A,B) { + return A.toLowerCase().localeCompare(B.toLowerCase()); + }); + setList.forEach(function(setName) { + var set = entry.sets[setName]; + var setRow = $('
      ',{class:"palette-module-set"}).appendTo(contentRow); + var buttonGroup = $('
      ',{class:"palette-module-set-button-group"}).appendTo(setRow); + var typeSwatches = {}; + set.types.forEach(function(t) { + var typeDiv = $('
      ',{class:"palette-module-type"}).appendTo(setRow); + typeSwatches[t] = $('',{class:"palette-module-type-swatch"}).appendTo(typeDiv); + $('',{class:"palette-module-type-node"}).html(t).appendTo(typeDiv); + }) + + var enableButton = $('').appendTo(buttonGroup); + enableButton.click(function(evt) { + if (object.setUseCount[setName] === 0) { + var currentSet = RED.nodes.registry.getNodeSet(set.id); + changeNodeState(set.id,!currentSet.enabled,function(xhr){ + console.log(xhr) + }); + } + evt.preventDefault(); + }) + + object.elements.sets[set.name] = { + setRow: setRow, + enableButton: enableButton, + swatches: typeSwatches + }; + }); + enableButton.click(function(evt) { + if (object.totalUseCount === 0) { + changeNodeState(entry.name,(container.hasClass('disabled')),function(xhr){ + console.log(xhr) + }); + } + evt.preventDefault(); + }) + refreshNodeModule(entry.name); + } + }); + + RED.events.on('registry:node-set-enabled', function(ns) { + refreshNodeModule(ns.module); + }); + RED.events.on('registry:node-set-disabled', function(ns) { + refreshNodeModule(ns.module); + }); + RED.events.on('registry:node-type-added', function(nodeType) { + var ns = RED.nodes.registry.getNodeSetForType(nodeType); + refreshNodeModule(ns.module); + }); + RED.events.on('registry:node-type-removed', function(nodeType) { + var ns = RED.nodes.registry.getNodeSetForType(nodeType); + refreshNodeModule(ns.module); + }); + RED.events.on('registry:node-set-added', function(ns) { + refreshNodeModule(ns.module); + }); + RED.events.on('registry:node-set-removed', function(ns) { + refreshNodeModule(ns.module); + }); + RED.events.on('nodes:add', function(n) { + typesInUse[n.type] = (typesInUse[n.type]||0)+1; + if (typesInUse[n.type] === 1) { + var ns = RED.nodes.registry.getNodeSetForType(n.type); + refreshNodeModule(ns.module); + } + }) + RED.events.on('nodes:remove', function(n) { + if (typesInUse.hasOwnProperty(n.type)) { + typesInUse[n.type]--; + if (typesInUse[n.type] === 0) { + delete typesInUse[n.type]; + var ns = RED.nodes.registry.getNodeSetForType(n.type); + refreshNodeModule(ns.module); + } + } + }) + + + } + + return { + init: init, + } +})(); diff --git a/editor/js/ui/palette.js b/editor/js/ui/palette.js index f17889ded..86e9b8c45 100644 --- a/editor/js/ui/palette.js +++ b/editor/js/ui/palette.js @@ -393,6 +393,49 @@ RED.palette = (function() { } function init() { + + RED.events.on('registry:node-type-added', function(nodeType) { + var def = RED.nodes.getType(nodeType); + addNodeType(nodeType,def); + if (def.onpaletteadd && typeof def.onpaletteadd === "function") { + def.onpaletteadd.call(def); + } + }); + RED.events.on('registry:node-type-removed', function(nodeType) { + removeNodeType(nodeType); + }); + + RED.events.on('registry:node-set-enabled', function(nodeSet) { + for (var j=0;j
      -
      +
      + + +
      +
      • Manage palette
      +
      +
      + +
      +
      diff --git a/red/runtime/nodes/registry/loader.js b/red/runtime/nodes/registry/loader.js index 9e5897593..d51b427e9 100644 --- a/red/runtime/nodes/registry/loader.js +++ b/red/runtime/nodes/registry/loader.js @@ -188,7 +188,8 @@ function loadNodeConfig(fileInfo) { template: file.replace(/\.js$/,".html"), enabled: isEnabled, loaded:false, - version: version + version: version, + local: fileInfo.local }; if (fileInfo.hasOwnProperty("types")) { node.types = fileInfo.types; diff --git a/red/runtime/nodes/registry/localfilesystem.js b/red/runtime/nodes/registry/localfilesystem.js index 112ed72ee..9ca1307e3 100644 --- a/red/runtime/nodes/registry/localfilesystem.js +++ b/red/runtime/nodes/registry/localfilesystem.js @@ -141,7 +141,8 @@ function scanTreeForNodesModules(moduleName) { if (settings.userDir) { userDir = path.join(settings.userDir,"node_modules"); - results = results.concat(scanDirForNodesModules(userDir,moduleName)); + results = scanDirForNodesModules(userDir,moduleName); + results.forEach(function(r) { r.local = true; }); } if (dir) { @@ -240,12 +241,14 @@ function getNodeFiles(disableNodePathScan) { nodeList[moduleFile.package.name] = { name: moduleFile.package.name, version: moduleFile.package.version, + local: moduleFile.local||false, nodes: {} }; if (moduleFile.package['node-red'].version) { nodeList[moduleFile.package.name].redVersion = moduleFile.package['node-red'].version; } nodeModuleFiles.forEach(function(node) { + node.local = moduleFile.local||false; nodeList[moduleFile.package.name].nodes[node.name] = node; }); nodeFiles = nodeFiles.concat(nodeModuleFiles); diff --git a/red/runtime/nodes/registry/registry.js b/red/runtime/nodes/registry/registry.js index 74606853e..02a3b304d 100644 --- a/red/runtime/nodes/registry/registry.js +++ b/red/runtime/nodes/registry/registry.js @@ -56,7 +56,8 @@ function filterNodeInfo(n) { id: n.id||n.module+"/"+n.name, name: n.name, types: n.types, - enabled: n.enabled + enabled: n.enabled, + local: n.local||false }; if (n.hasOwnProperty("module")) { r.module = n.module; @@ -90,6 +91,7 @@ function saveNodeList() { moduleList[module] = { name: module, version: moduleConfigs[module].version, + local: moduleConfigs[module].local||false, nodes: {} }; } @@ -179,6 +181,7 @@ function addNodeSet(id,set,version) { if (version) { moduleConfigs[set.module].version = version; } + moduleConfigs[set.module].local = set.local; moduleConfigs[set.module].nodes[set.name] = set; nodeList.push(id); @@ -306,6 +309,7 @@ function getModuleInfo(module) { var m = { name: module, version: moduleConfigs[module].version, + local: moduleConfigs[module].local, nodes: [] }; for (var i = 0; i < nodes.length; ++i) {