diff --git a/Gruntfile.js b/Gruntfile.js index 3738e60b4..faf68f100 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -144,10 +144,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/tray.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/images/typedInput/env.png b/editor/images/typedInput/env.png new file mode 100644 index 000000000..0ea51da00 Binary files /dev/null and b/editor/images/typedInput/env.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/main.js b/editor/js/main.js index cf295e7e7..80e0a692f 100644 --- a/editor/js/main.js +++ b/editor/js/main.js @@ -153,13 +153,13 @@ loadFlows(function() { var project = RED.projects.getActiveProject(); var message = { - "change-branch":"Change to local branch '"+project.git.branches.local+"'", - "merge-abort":"Git merge aborted", - "loaded":"Project '"+msg.project+"' loaded", - "updated":"Project '"+msg.project+"' updated", - "pull":"Project '"+msg.project+"' reloaded", - "revert": "Project '"+msg.project+"' reloaded", - "merge-complete":"Git merge completed" + "change-branch": RED._("notification.project.change-branch", {project: project.git.branches.local}), + "merge-abort": RED._("notification.project.merge-abort"), + "loaded": RED._("notification.project.loaded", {project: msg.project}), + "updated": RED._("notification.project.updated", {project: msg.project}), + "pull": RED._("notification.project.pull", {project: msg.project}), + "revert": RED._("notification.project.revert", {project: msg.project}), + "merge-complete": RED._("notification.project.merge-complete") }[msg.action]; RED.notify("

"+message+"

"); RED.sidebar.info.refresh() @@ -183,7 +183,7 @@ if (!!RED.projects.getActiveProject()) { options.buttons = [ { - text: "Manage project dependencies", + text: RED._("notification.label.manage-project-dep"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.settings.show('deps'); @@ -194,7 +194,7 @@ } else { options.buttons = [ { - text: "Close", + text: RED._("common.label.close"), click: function() { persistentNotifications[notificationId].hideNotification(); } @@ -207,7 +207,7 @@ if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Setup credentials", + text: RED._("notification.label.setup-cred"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.showCredentialsPrompt(); @@ -218,7 +218,7 @@ } else { options.buttons = [ { - text: "Close", + text: RED._("common.label.close"), click: function() { persistentNotifications[notificationId].hideNotification(); } @@ -229,7 +229,7 @@ if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Setup project files", + text: RED._("notification.label.setup-project"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.showFilesPrompt(); @@ -241,7 +241,7 @@ if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Create default package file", + text: RED._("notification.label.create-default-package"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.createDefaultPackageFile(); @@ -253,13 +253,13 @@ if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "No thanks", + text: RED._("notification.label.no-thanks"), click: function() { persistentNotifications[notificationId].hideNotification(); } }, { - text: "Create default project files", + text: RED._("notification.label.create-default-project"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.createDefaultFileSet(); @@ -273,7 +273,7 @@ if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Show merge conflicts", + text: RED._("notification.label.show-merge-conflicts"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.sidebar.versionControl.showLocalChanges(); @@ -382,10 +382,10 @@ function loadEditor() { var menuOptions = []; if (RED.settings.theme("projects.enabled",false)) { - menuOptions.push({id:"menu-item-projects-menu",label:"Projects",options:[ - {id:"menu-item-projects-new",label:"New",disabled:false,onselect:"core:new-project"}, - {id:"menu-item-projects-open",label:"Open",disabled:false,onselect:"core:open-project"}, - {id:"menu-item-projects-settings",label:"Project Settings",disabled:false,onselect:"core:show-project-settings"} + menuOptions.push({id:"menu-item-projects-menu",label:RED._("menu.label.projects"),options:[ + {id:"menu-item-projects-new",label:RED._("menu.label.projects-new"),disabled:false,onselect:"core:new-project"}, + {id:"menu-item-projects-open",label:RED._("menu.label.projects-open"),disabled:false,onselect:"core:open-project"}, + {id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"} ]}); } @@ -410,8 +410,8 @@ {id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:show-import-dialog"}, {id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]} ]}); - menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),disabled:true,options:[ - {id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:"core:show-export-dialog"}, + menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),options:[ + {id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:show-export-dialog"}, {id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:"core:library-export"} ]}); menuOptions.push(null); 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 2bdd20052..e4350a464 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.__encoded__) { + 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; @@ -309,18 +320,6 @@ RED.clipboard = (function() { $('').appendTo("body"); - RED.events.on("view:selection-changed",function(selection) { - if (!selection.nodes) { - RED.menu.setDisabled("menu-item-export",true); - RED.menu.setDisabled("menu-item-export-clipboard",true); - RED.menu.setDisabled("menu-item-export-library",true); - } else { - RED.menu.setDisabled("menu-item-export",false); - RED.menu.setDisabled("menu-item-export-clipboard",false); - RED.menu.setDisabled("menu-item-export-library",false); - } - }); - RED.actions.add("core:show-export-dialog",exportNodes); RED.actions.add("core:show-import-dialog",importNodes); diff --git a/editor/js/ui/common/popover.js b/editor/js/ui/common/popover.js index 6a0bc9fdf..4e2a0b0ef 100644 --- a/editor/js/ui/common/popover.js +++ b/editor/js/ui/common/popover.js @@ -19,12 +19,14 @@ RED.popover = (function() { "default": { top: 10, leftRight: 17, - leftLeft: 25 + leftLeft: 25, + leftBottom: 8, }, "small": { top: 5, leftRight: 17, - leftLeft: 16 + leftLeft: 16, + leftBottom: 3, } } function createPopover(options) { @@ -46,29 +48,39 @@ 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') { div.css({top: targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top,left:targetPos.left+targetWidth+deltaSizes[size].leftRight}); } else if (direction === 'left') { div.css({top: targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top,left:targetPos.left-deltaSizes[size].leftLeft-divWidth}); + } else if (direction === 'bottom') { + div.css({top: targetPos.top+targetHeight+deltaSizes[size].top,left:targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftBottom}); } if (instant) { div.show(); @@ -143,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 f66b95421..700618495 100644 --- a/editor/js/ui/common/tabs.js +++ b/editor/js/ui/common/tabs.js @@ -19,8 +19,10 @@ RED.tabs = (function() { function createTabs(options) { var tabs = {}; + var pinnedTabsCount = 0; var currentTabWidth; var currentActiveTabWidth = 0; + var collapsibleMenu; var ul = options.element || $("#"+options.id); var wrapper = ul.wrap( "
" ).parent(); @@ -50,6 +52,55 @@ RED.tabs = (function() { scrollRight = $('
').appendTo(wrapper).find("a"); scrollRight.on('mousedown',function(evt) { scrollEventHandler(evt,'+=150') }).on('click',function(evt){ evt.preventDefault();}); } + + if (options.collapsible) { + // var dropDown = $('
',{class:"red-ui-tabs-select"}).appendTo(wrapper); + // ul.hide(); + wrapper.addClass("red-ui-tabs-collapsible"); + + var collapsedButtonsRow = $('').appendTo(wrapper); + + var selectButton = $('').appendTo(collapsedButtonsRow); + selectButton.addClass("red-ui-tab-link-button-menu") + selectButton.click(function(evt) { + evt.preventDefault(); + if (!collapsibleMenu) { + var pinnedOptions = []; + var options = []; + ul.children().each(function(i,el) { + var id = $(el).data('tabId'); + var opt = { + id:"red-ui-tabs-menu-option-"+id, + label: tabs[id].name, + onselect: function() { + activateTab(id); + } + }; + if (tabs[id].pinned) { + pinnedOptions.push(opt); + } else { + options.push(opt); + } + }); + options = pinnedOptions.concat(options); + collapsibleMenu = RED.menu.init({id:"debug-message-option-menu",options: options}); + collapsibleMenu.css({ + position: "absolute" + }) + 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" + }) + collapsibleMenu.toggle(); + }) + + } + function scrollEventHandler(evt,dir) { evt.preventDefault(); if ($(this).hasClass('disabled')) { @@ -118,6 +169,9 @@ RED.tabs = (function() { ul.children().removeClass("active"); 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 selected"); + $("#"+parentId+"-link-button").addClass("active selected"); if (options.scrollable) { var pos = link.parent().position().left; if (pos-21 < 0) { @@ -155,41 +209,70 @@ RED.tabs = (function() { var tabs = ul.find("li.red-ui-tab"); var width = wrapper.width(); var tabCount = tabs.size(); - var tabWidth = (width-12-(tabCount*6))/tabCount; - currentTabWidth = (100*tabWidth/width)+"%"; - currentActiveTabWidth = currentTabWidth+"%"; - if (options.scrollable) { - tabWidth = Math.max(tabWidth,140); - currentTabWidth = tabWidth+"px"; - currentActiveTabWidth = 0; - var listWidth = Math.max(wrapper.width(),12+(tabWidth+6)*tabCount); - ul.width(listWidth); - updateScroll(); - } else if (options.hasOwnProperty("minimumActiveTabWidth")) { - if (tabWidth < options.minimumActiveTabWidth) { - tabCount -= 1; - tabWidth = (width-12-options.minimumActiveTabWidth-(tabCount*6))/tabCount; - currentTabWidth = (100*tabWidth/width)+"%"; - currentActiveTabWidth = options.minimumActiveTabWidth+"px"; + var tabWidth; + + if (options.collapsible) { + tabWidth = width - collapsedButtonsRow.width()-10; + if (tabWidth < 198) { + var delta = 198 - tabWidth; + var b = collapsedButtonsRow.find("a:last").prev(); + while (b.is(":not(:visible)")) { + b = b.prev(); + } + if (!b.hasClass("red-ui-tab-link-button-pinned")) { + b.hide(); + } + tabWidth = width - collapsedButtonsRow.width()-10; } else { - currentActiveTabWidth = 0; + var space = width - 198 - collapsedButtonsRow.width(); + if (space > 40) { + collapsedButtonsRow.find("a:not(:visible):first").show(); + tabWidth = width - collapsedButtonsRow.width()-10; + } } - } - tabs.css({width:currentTabWidth}); - if (tabWidth < 50) { - ul.find(".red-ui-tab-close").hide(); - ul.find(".red-ui-tab-icon").hide(); - ul.find(".red-ui-tab-label").css({paddingLeft:Math.min(12,Math.max(0,tabWidth-38))+"px"}) + tabs.css({width:tabWidth}); + } else { - ul.find(".red-ui-tab-close").show(); - ul.find(".red-ui-tab-icon").show(); - ul.find(".red-ui-tab-label").css({paddingLeft:""}) - } - if (currentActiveTabWidth !== 0) { - ul.find("li.red-ui-tab.active").css({"width":options.minimumActiveTabWidth}); - ul.find("li.red-ui-tab.active .red-ui-tab-close").show(); - ul.find("li.red-ui-tab.active .red-ui-tab-icon").show(); - ul.find("li.red-ui-tab.active .red-ui-tab-label").css({paddingLeft:""}) + var tabWidth = (width-12-(tabCount*6))/tabCount; + currentTabWidth = (100*tabWidth/width)+"%"; + currentActiveTabWidth = currentTabWidth+"%"; + if (options.scrollable) { + tabWidth = Math.max(tabWidth,140); + currentTabWidth = tabWidth+"px"; + currentActiveTabWidth = 0; + var listWidth = Math.max(wrapper.width(),12+(tabWidth+6)*tabCount); + ul.width(listWidth); + updateScroll(); + } else if (options.hasOwnProperty("minimumActiveTabWidth")) { + if (tabWidth < options.minimumActiveTabWidth) { + tabCount -= 1; + tabWidth = (width-12-options.minimumActiveTabWidth-(tabCount*6))/tabCount; + currentTabWidth = (100*tabWidth/width)+"%"; + currentActiveTabWidth = options.minimumActiveTabWidth+"px"; + } else { + currentActiveTabWidth = 0; + } + } + if (options.collapsible) { + console.log(currentTabWidth); + } + + tabs.css({width:currentTabWidth}); + if (tabWidth < 50) { + ul.find(".red-ui-tab-close").hide(); + ul.find(".red-ui-tab-icon").hide(); + ul.find(".red-ui-tab-label").css({paddingLeft:Math.min(12,Math.max(0,tabWidth-38))+"px"}) + } else { + ul.find(".red-ui-tab-close").show(); + ul.find(".red-ui-tab-icon").show(); + ul.find(".red-ui-tab-label").css({paddingLeft:""}) + } + if (currentActiveTabWidth !== 0) { + ul.find("li.red-ui-tab.active").css({"width":options.minimumActiveTabWidth}); + ul.find("li.red-ui-tab.active .red-ui-tab-close").show(); + ul.find("li.red-ui-tab.active .red-ui-tab-icon").show(); + ul.find("li.red-ui-tab.active .red-ui-tab-label").css({paddingLeft:""}) + } } } @@ -210,11 +293,15 @@ RED.tabs = (function() { activateTab(tab.find("a")); } li.remove(); + if (tabs[id].pinned) { + pinnedTabsCount--; + } if (options.onremove) { options.onremove(tabs[id]); } delete tabs[id]; updateTabWidths(); + collapsibleMenu = null; } return { @@ -223,13 +310,48 @@ RED.tabs = (function() { var li = $("
  • ",{class:"red-ui-tab"}).appendTo(ul); li.attr('id',"red-ui-tab-"+(tab.id.replace(".","-"))); li.data("tabId",tab.id); + + if (options.maximumTabWidth) { + li.css("maxWidth",options.maximumTabWidth+"px"); + } var link = $("",{href:"#"+tab.id, class:"red-ui-tab-label"}).appendTo(li); if (tab.icon) { $('').appendTo(link); + } else if (tab.iconClass) { + $('',{class:"red-ui-tab-icon "+tab.iconClass}).appendTo(link); } var span = $('',{class:"bidiAware"}).text(tab.label).appendTo(link); span.attr('dir', RED.text.bidi.resolveBaseTextDir(tab.label)); + if (options.collapsible) { + li.addClass("red-ui-tab-pinned"); + var pinnedLink = $(''); + if (tab.pinned) { + if (pinnedTabsCount === 0) { + pinnedLink.prependTo(collapsedButtonsRow) + } else { + pinnedLink.insertAfter(collapsedButtonsRow.find("a.red-ui-tab-link-button-pinned:last")); + } + } else { + pinnedLink.insertBefore(collapsedButtonsRow.find("a:last")); + } + pinnedLink.attr('id',li.attr('id')+"-link-button"); + if (tab.iconClass) { + $('',{class:tab.iconClass}).appendTo(pinnedLink); + } else { + $('',{class:"fa fa-lemon-o"}).appendTo(pinnedLink); + } + pinnedLink.click(function(evt) { + evt.preventDefault(); + activateTab(tab.id); + }); + if (tab.pinned) { + pinnedLink.addClass("red-ui-tab-link-button-pinned"); + pinnedTabsCount++; + } + RED.popover.tooltip($(pinnedLink), tab.name); + + } link.on("click",onTabClick); link.on("dblclick",onTabDblClick); if (tab.closeable) { @@ -326,6 +448,7 @@ RED.tabs = (function() { } }) } + collapsibleMenu = null; }, removeTab: removeTab, activateTab: activateTab, diff --git a/editor/js/ui/common/typedInput.js b/editor/js/ui/common/typedInput.js index 3b5a0d62c..74bbeb9ac 100644 --- a/editor/js/ui/common/typedInput.js +++ b/editor/js/ui/common/typedInput.js @@ -14,10 +14,43 @@ * limitations under the License. **/ (function($) { + var contextParse = function(v) { + var parts = {}; + var m = /^#:\((\S+?)\)::(.*)$/.exec(v); + if (m) { + parts.option = m[1]; + parts.value = m[2]; + } else { + parts.value = v; + parts.option = RED.settings.context.default; + } + return parts; + } + 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"]}, @@ -76,31 +109,51 @@ } }) } + }, + env: { + value: "env", + label: "env variable", + icon: "red/images/typedInput/env.png" } }; var nlsd = false; $.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 { @@ -109,17 +162,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); @@ -133,14 +188,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(); @@ -156,8 +213,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(); @@ -172,17 +232,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() { @@ -191,8 +252,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"); } @@ -204,7 +265,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 { @@ -223,10 +284,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(); @@ -305,7 +375,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"); @@ -315,12 +386,64 @@ 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() { this.menu.remove(); }, @@ -339,13 +462,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; @@ -353,33 +481,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').appendTo(codeBody); var binaryContent = $('').appendTo(diffBinaryRow); - $('').text("Cannot show binary file contents").appendTo(binaryContent); + $('').text(RED._("diff.noBinaryFileShowed")).appendTo(binaryContent); } else { if (commitOptions.unmerged) { - conflictHeader = $(''+resolvedConflicts+' of '+unresolvedConflicts+' conflicts resolved').appendTo(content); + conflictHeader = $(''+RED._("diff.conflictHeader",{resolved:resolvedConflicts, unresolved:unresolvedConflicts})+'').appendTo(content); } hunks.forEach(function(hunk) { var diffRow = $('').appendTo(codeBody); @@ -1914,7 +1914,7 @@ RED.diff = (function() { diffRow.remove(); addedRows.find(".linetext").addClass('added'); conflictHeader.empty(); - $(''+resolvedConflicts+' of '+unresolvedConflicts+' conflicts resolved').appendTo(conflictHeader); + $(''+RED._("diff.conflictHeader",{resolved:resolvedConflicts, unresolved:unresolvedConflicts})+'').appendTo(conflictHeader); conflictResolutions[file.file] = conflictResolutions[file.file] || {}; conflictResolutions[file.file][hunk.localChangeStart] = { @@ -1946,7 +1946,7 @@ RED.diff = (function() { function showCommitDiff(options) { var commit = parseCommitDiff(options.commit); var trayOptions = { - title: "View Commit Changes", //TODO: nls + title: RED._("diff.viewCommitDiff"), width: Infinity, overlay: true, buttons: [ @@ -2008,7 +2008,7 @@ RED.diff = (function() { } var trayOptions = { - title: title||"Compare Changes", //TODO: nls + title: title|| RED._("diff.compareChanges"), width: Infinity, overlay: true, buttons: [ @@ -2041,7 +2041,7 @@ RED.diff = (function() { trayOptions.buttons.push( { id: "node-diff-view-resolve-diff", - text: "Save conflict resolution", + text: RED._("diff.saveConflict"), class: "primary disabled", click: function() { if (!$("#node-diff-view-resolve-diff").hasClass('disabled')) { diff --git a/editor/js/ui/editor.js b/editor/js/ui/editor.js index df50db725..0f6050b8a 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) { @@ -711,6 +679,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","Search icons").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 = node._def.color; + 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 +807,36 @@ RED.editor = (function() { } if ((!node._def.defaults || !node._def.defaults.hasOwnProperty("icon"))) { - $('
    ').appendTo(dialogForm); - var iconDiv = $("#node-settings-icon"); - $('