/** * Copyright JS Foundation and other contributors, http://js.foundation * * 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.search = (function() { var disabled = false; var dialog = null; var searchInput; var searchResults; var selected = -1; var visible = false; var index = {}; var keys = []; var results = []; function indexProperty(node,label,property) { if (typeof property === 'string' || typeof property === 'number') { property = (""+property).toLowerCase(); index[property] = index[property] || {}; index[property][node.id] = {node:node,label:label}; } else if (Array.isArray(property)) { property.forEach(function(prop) { indexProperty(node,label,prop); }) } else if (typeof property === 'object') { for (var prop in property) { if (property.hasOwnProperty(prop)) { indexProperty(node,label,property[prop]) } } } } function indexNode(n) { var l = RED.utils.getNodeLabel(n); if (l) { l = (""+l).toLowerCase(); index[l] = index[l] || {}; index[l][n.id] = {node:n,label:l} } l = l||n.label||n.name||n.id||""; var properties = ['id','type','name','label','info']; if (n._def && n._def.defaults) { properties = properties.concat(Object.keys(n._def.defaults)); } for (var i=0;i<properties.length;i++) { if (n.hasOwnProperty(properties[i])) { indexProperty(n, l, n[properties[i]]); } } } function indexWorkspace() { index = {}; RED.nodes.eachWorkspace(indexNode); RED.nodes.eachSubflow(indexNode); RED.nodes.eachConfig(indexNode); RED.nodes.eachNode(indexNode); keys = Object.keys(index); keys.sort(); keys.forEach(function(key) { index[key] = Object.keys(index[key]).map(function(id) { return index[key][id]; }) }) } function search(val) { searchResults.editableList('empty'); selected = -1; results = []; if (val.length > 0) { val = val.toLowerCase(); var i; var j; var list = []; var nodes = {}; for (i=0;i<keys.length;i++) { var key = keys[i]; var kpos = keys[i].indexOf(val); if (kpos > -1) { for (j=0;j<index[key].length;j++) { var node = index[key][j]; nodes[node.node.id] = nodes[node.node.id] = node; nodes[node.node.id].index = Math.min(nodes[node.node.id].index||Infinity,kpos); } } } list = Object.keys(nodes); list.sort(function(A,B) { return nodes[A].index - nodes[B].index; }); for (i=0;i<list.length;i++) { results.push(nodes[list[i]]); } if (results.length > 0) { for (i=0;i<Math.min(results.length,25);i++) { searchResults.editableList('addItem',results[i]) } } else { searchResults.editableList('addItem',{}); } } } function ensureSelectedIsVisible() { var selectedEntry = searchResults.find("li.selected"); if (selectedEntry.length === 1) { var scrollWindow = searchResults.parent(); var scrollHeight = scrollWindow.height(); var scrollOffset = scrollWindow.scrollTop(); var y = selectedEntry.position().top; var h = selectedEntry.height(); if (y+h > scrollHeight) { scrollWindow.animate({scrollTop: '-='+(scrollHeight-(y+h)-10)},50); } else if (y<0) { scrollWindow.animate({scrollTop: '+='+(y-10)},50); } } } function createDialog() { dialog = $("<div>",{id:"red-ui-search",class:"red-ui-search"}).appendTo("#main-container"); var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog); searchInput = $('<input type="text" data-i18n="[placeholder]menu.label.searchInput">').appendTo(searchDiv).searchBox({ delay: 200, change: function() { search($(this).val()); } }); searchInput.on('keydown',function(evt) { var children; if (results.length > 0) { if (evt.keyCode === 40) { // Down children = searchResults.children(); if (selected < children.length-1) { if (selected > -1) { $(children[selected]).removeClass('selected'); } selected++; } $(children[selected]).addClass('selected'); ensureSelectedIsVisible(); evt.preventDefault(); } else if (evt.keyCode === 38) { // Up children = searchResults.children(); if (selected > 0) { if (selected < children.length) { $(children[selected]).removeClass('selected'); } selected--; } $(children[selected]).addClass('selected'); ensureSelectedIsVisible(); evt.preventDefault(); } else if (evt.keyCode === 13) { // Enter if (results.length > 0) { reveal(results[Math.max(0,selected)].node); } } } }); searchInput.i18n(); var searchResultsDiv = $("<div>",{class:"red-ui-search-results-container"}).appendTo(dialog); searchResults = $('<ol>',{id:"search-result-list", style:"position: absolute;top: 5px;bottom: 5px;left: 5px;right: 5px;"}).appendTo(searchResultsDiv).editableList({ addButton: false, addItem: function(container,i,object) { var node = object.node; if (node === undefined) { $('<div>',{class:"red-ui-search-empty"}).text(RED._('search.empty')).appendTo(container); } else { var def = node._def; var div = $('<a>',{href:'#',class:"red-ui-search-result"}).appendTo(container); var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div); var colour = RED.utils.getNodeColor(node.type,def); var icon_url = RED.utils.getNodeIcon(def,node); if (node.type === 'tab') { colour = "#C0DEED"; } nodeDiv.css('backgroundColor',colour); var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv); $('<div/>',{class:"palette_icon",style:"background-image: url("+icon_url+")"}).appendTo(iconContainer); var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div); if (node.z) { var workspace = RED.nodes.workspace(node.z); if (!workspace) { workspace = RED.nodes.subflow(node.z); workspace = "subflow:"+workspace.name; } else { workspace = "flow:"+workspace.label; } $('<div>',{class:"red-ui-search-result-node-flow"}).text(workspace).appendTo(contentDiv); } $('<div>',{class:"red-ui-search-result-node-label"}).text(object.label || node.id).appendTo(contentDiv); $('<div>',{class:"red-ui-search-result-node-type"}).text(node.type).appendTo(contentDiv); $('<div>',{class:"red-ui-search-result-node-id"}).text(node.id).appendTo(contentDiv); div.click(function(evt) { evt.preventDefault(); reveal(node); }); } }, scrollOnAdd: false }); } function reveal(node) { hide(); RED.view.reveal(node.id); } function show() { if (disabled) { return; } if (!visible) { RED.keyboard.add("*","escape",function(){hide()}); $("#header-shade").show(); $("#editor-shade").show(); $("#palette-shade").show(); $("#sidebar-shade").show(); $("#sidebar-separator").hide(); indexWorkspace(); if (dialog === null) { createDialog(); } dialog.slideDown(300); RED.events.emit("search:open"); visible = true; } searchInput.focus(); } function hide() { if (visible) { RED.keyboard.remove("escape"); visible = false; $("#header-shade").hide(); $("#editor-shade").hide(); $("#palette-shade").hide(); $("#sidebar-shade").hide(); $("#sidebar-separator").show(); if (dialog !== null) { dialog.slideUp(200,function() { searchInput.searchBox('value',''); }); } RED.events.emit("search:close"); } } function init() { RED.actions.add("core:search",show); RED.events.on("editor:open",function() { disabled = true; }); RED.events.on("editor:close",function() { disabled = false; }); RED.events.on("type-search:open",function() { disabled = true; }); RED.events.on("type-search:close",function() { disabled = false; }); $("#header-shade").on('mousedown',hide); $("#editor-shade").on('mousedown',hide); $("#palette-shade").on('mousedown',hide); $("#sidebar-shade").on('mousedown',hide); } return { init: init, show: show, hide: hide }; })();