/
githubmirror
/
drawio
Обзор
Документация
Войти
/
githubmirror
/
drawio
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
src/main/webapp/js/diagramly/sidebar/Sidebar.js
2 595 строк
115 KB
David Benson
31.1.8 release
07 авг 2026, 02:43
07 авг 2026, 02:43
a1f615b
Код
Авторство
О чём код?
/** * Copyright (c) 2020-2025, JGraph Holdings Ltd * Copyright (c) 2020-2025, draw.io AG */ (function() { /** * Download from following URL as TSV and convert using https://jgraph.github.io/drawio-tools/tools/convert.html: * https://docs.google.com/spreadsheets/d/1sAL1zn-UtmJtKPH4cLApGjRX-TRSJa5dYdfZ9NKYfRs * Maps package and stencil names to additional tags. */ Sidebar.prototype.tagIndex = '5V1dV+M6sv01rDvngax0oLvveYQEaGaAziE0PW8sxVYSDbblI9uk6V9/VVWS7ST+kB0zL3etbmIn3ltlfZRKUqkU/rpRLN6MmFJym5yM/8QL/Xnw7yLceXQ03fA3JaOTyfjCQCKZehvu66tErCMW6J9E1M4jlJcFTJWIPP1VI … [Строка слишком длинная. Вы можете скачать файл] /** * To update this, go to https://test.draw.io/?dev=1&test=1&drawdev=1&demo=1&createindex=1 and * copy the output of searchFileData from the browser console to the variable below. */ Sidebar.prototype.searchIndexData = '7Z1rU+M40Kh/zVbN+wEq98vHkHDbJUxeHGDrfFEpjkh0cKwc2R6G/fVHchKGKMwuRo7Usr21xQyBzUaPW62+qfuP9tkf3bM/Go0ofg2I+POP5kB+t8Rr8kdztPq54Hi9PMWcs5fodCD/QCP2Ev7RPFvGq0D8Sl389Q … [Строка слишком длинная. Вы можете скачать файл] /** * Overrides gear image URL. */ Sidebar.prototype.gearImage = GRAPH_IMAGE_PATH + '/clipart/Gear_128x128.png'; /** * Aliases for IDs in the libs parameter. */ Sidebar.prototype.libAliases = {'aws2': 'aws3', 'gcp' : 'gcp2'}; /** * */ Sidebar.prototype.defaultEntries = 'general;uml;er;bpmn;flowchart;basic;arrows2'; /** * Library definitions */ Sidebar.prototype.signs = ['Animals', 'Food', 'Healthcare', 'Nature', 'People', 'Safety', 'Science', 'Sports', 'Tech', 'Transportation', 'Travel']; Sidebar.prototype.ibm = ['Analytics', 'Applications', 'Blockchain', 'Data', 'DevOps', 'Infrastructure', 'Management', 'Miscellaneous', 'Security', 'Social', 'Users', 'VPC', 'Boxes', 'Connectors']; Sidebar.prototype.ibm_cloud = ['Groups', 'Actors', 'AI', 'Compute', 'Containers', 'Data', 'DevOps', 'Network', 'Observability', 'Security', 'Storage', 'Connectors']; Sidebar.prototype.allied_telesis = ['Buildings', 'Computer and Terminals', 'Media Converters', 'Security', 'Storage', 'Switch', 'Wireless']; Sidebar.prototype.gcp = ['Cards', 'Big Data', 'Compute', 'Developer Tools', 'Extras', 'Identity and Security', 'Machine Learning', 'Management Tools', 'Networking', 'Storage Databases']; Sidebar.prototype.gcp2 = ['Paths', 'Zones', 'Service Cards', 'Compute', 'API Management', 'Security', 'Data Analytics', 'Data Transfer', 'Cloud AI', 'Internet of Things', 'Databases', 'Storage', 'Management Tools', 'Networking', 'Developer Tools', 'Expanded Product Cards', 'User Device Cards', 'Product Cards', 'General Icons', 'Icons AI and Machine Learning', 'Icons Compute', 'Icons Serverless', 'Icons Data Analytics', 'Icons Operations', 'Icons Networking', 'Icons CI CD', 'Icons Integration Services', 'Icons API Management', 'Icons Internet of Things', 'Icons Databases', 'Icons Storage', 'Icons Security', 'Icons Migration', 'Icons Hybrid and Multi Cloud', 'Icons Open Source Icons']; Sidebar.prototype.gcp3 = ['Categories', 'Core Products']; Sidebar.prototype.gcpicons = ['AI and Machine Learning', 'API Management', 'Compute', 'Data Analytics', 'Databases', 'Developer Tools', 'Expanded Product Card Icons', 'Generic', 'Hybrid and Multi Cloud', 'Security', 'Internet of Things', 'Management Tools', 'Migration', 'Networking', 'Open Source Icons', 'Storage']; Sidebar.prototype.rack = ['General', 'APC', 'Cisco', 'Dell', 'F5', 'HP', 'HPE Aruba Gateways Controllers', 'HPE Aruba Security', 'HPE Aruba Switches', 'IBM', 'Oracle']; Sidebar.prototype.pids = ['Agitators', 'Apparatus Elements', 'Centrifuges', 'Compressors', 'Compressors ISO', 'Crushers Grinding', 'Driers', 'Engines', 'Feeders', 'Filters', 'Fittings', 'Flow Sensors', 'Heat Exchangers', 'Instruments', 'Misc', 'Mixers', 'Piping', 'Pumps', 'Pumps DIN', 'Pumps ISO', 'Separators', 'Shaping Machines', 'Valves', 'Vessels']; Sidebar.prototype.cisco = ['Buildings', 'Computers and Peripherals', 'Controllers and Modules', 'Directors', 'Hubs and Gateways', 'Misc', 'Modems and Phones', 'People', 'Routers', 'Security', 'Servers', 'Storage', 'Switches', 'Wireless']; Sidebar.prototype.cisco19 = ['LAN Switching', 'Routing WAN', 'Network Management', 'Data Center', 'Wireless LAN', 'Collaboration', 'Security Clouds Connectors', 'Endpoint Client Device Icons', 'DNA SD Access', 'SD WAN Viptela', 'ETA Stealthwatch', 'SAFE']; Sidebar.prototype.cisco_safe = ['Architecture', 'Business Icons', 'Capability', 'Design', 'IoT Things Icons', 'People Places Things Icons', 'Security Icons', 'Technology Icons', 'Threat']; Sidebar.prototype.sap = ['Annotations', 'Area Shapes', 'BTP SaaS', 'Colored Connectors', 'Foundations', 'Integration Suite', 'App Dev Automation', 'Data Analytics', 'AI', 'Generic Icons', 'Component Groups', 'Components', 'Default Connectors', 'Default Shapes', 'Numbers', 'Other', 'Products', 'Build Workzone', 'Semantic Accent', 'Texts']; Sidebar.prototype.sysml = ['Model Elements', 'Blocks', 'Ports and Flows', 'Constraint Blocks', 'Activities', 'Interactions', 'State Machines', 'Use Cases', 'Allocations', 'Requirements', 'Profiles', 'Stereotypes']; Sidebar.prototype.eip = ['Message Construction', 'Message Routing', 'Message Transformation', 'Messaging Channels', 'Messaging Endpoints', 'Messaging Systems', 'System Management']; Sidebar.prototype.gmdl = ['Bottom Navigation', 'Bottom Sheets', 'Buttons', 'Cards', 'Chips', 'Dialogs', 'Dividers', 'Grid Lists', 'Icons', 'Lists', 'Menus', 'Misc', 'Pickers', 'Selection Controls', 'Sliders', 'Steppers', 'Tabs', 'Text Fields']; Sidebar.prototype.aws2 = ['Analytics', 'Application Services', 'Compute', 'Database', 'Developer Tools', 'Enterprise Applications', 'Game Development', 'General', 'Internet of Things', 'Management Tools', 'Mobile Services', 'Networking', 'On-Demand Workforce', 'SDKs', 'Security and Identity', 'Storage and Content Delivery', 'Groups']; Sidebar.prototype.aws3 = ['Analytics', 'Application Services', 'Artificial Intelligence', 'Business Productivity', 'Compute', 'Contact Center', 'Database', 'Desktop and App Streaming', 'Developer Tools', 'Game Development', 'General', 'Groups', 'Internet of Things', 'Management Tools', 'Messaging', 'Migration', 'Mobile Services', 'Networking and Content Delivery', 'On Demand Workforce', 'SDKs', 'Security Identity and Compliance', 'Storage']; Sidebar.prototype.aws4b = ['Arrows', 'General Resources', 'Illustrations', 'Groups Light', 'Groups Dark', 'Analytics', 'Application Integration', 'AR VR', 'Cost Management', 'Business Productivity', 'Compute', 'Customer Engagement', 'Database', 'Desktop App Streaming', 'Developer Tools', 'Game Development', 'Internet of Things', 'IoT Things', 'IoT Resources', 'Machine Learning', 'Management Tools', 'Media Services', 'Migration', 'Mobile Services', 'Network Content Delivery', 'Security Identity Compliance', 'Storage']; Sidebar.prototype.aws4 = ['Arrows', 'General Resources', 'Illustrations', 'Groups', 'Analytics', 'Application Integration', 'AR VR', 'Artificial Intelligence', 'Cloud Financial Management', 'Blockchain', 'Business Applications', 'Compute', 'Contact Center', 'Containers', 'Customer Enablement', 'Customer Engagement', 'Database', 'Developer Tools', 'End User Computing', 'Front End Web Mobile', 'Games', 'Internet of Things', 'IoT Things', 'IoT Resources', 'Management Governance', 'Media Services', 'Migration Modernization', 'Network Content Delivery', 'Quantum Technologies', 'Robotics', 'Satellite', 'Serverless', 'Security Identity Compliance', 'Storage']; Sidebar.prototype.office = ['Clouds', 'Communications', 'Concepts', 'Databases', 'Devices', 'Security', 'Servers', 'Services', 'Sites', 'Users']; Sidebar.prototype.openstack = ['Blue', 'Grey', 'Green', 'Red']; Sidebar.prototype.veeam = ['Data Center', 'Misc', 'Software', 'Storage', 'UsersStatus', 'VASComponents', 'Backup Replication', 'Products', 'VMs and Tape', '2D', '3D']; Sidebar.prototype.veeam2 = ['Auxiliary', 'Data Center', 'Features', 'General', 'Products and Components', 'Software', 'States', 'Storage', '3D']; Sidebar.prototype.archimate3 = ['Application', 'Business', 'Generic', 'Implementation and Migration', 'Motivation', 'Relationships', 'Strategy', 'Technology']; Sidebar.prototype.archimate4 = ['Common', 'Relationships and Junctions', 'Motivation', 'Strategy', 'Business', 'Application', 'Technology', 'Implementation and Migration']; Sidebar.prototype.electrical = ['LogicGates', 'Resistors', 'Capacitors', 'Inductors', 'SwitchesRelays', 'Diodes', 'Sources', 'Transistors', 'Misc', 'Audio', 'PlcLadder', 'Abstract', 'Optical', 'VacuumTubes', 'Waveforms', 'Instruments', 'RotMech', 'Transmission']; /** * Description of custom libraries, see https://www.drawio.com/doc/faq/configure-diagram-editor */ Sidebar.prototype.customEntries = null; /** * Specifies if custom libraries should appear after built-in libraries. * Default is false. */ Sidebar.prototype.appendCustomLibraries = false; /** * Array of strings for the built-in libraries to be enabled in the more shapes dialog. Null means all, * empty array means none, possible keys are listed for the libs parameter at * * https://www.drawio.com/doc/faq/supported-url-parameters */ Sidebar.prototype.enabledLibraries = null; /** * Maximum number of custom libraries to preload into the search index. */ Sidebar.prototype.maxPreloadCount = 20; /** * */ Sidebar.prototype.configuration = [{id: 'general', libs: ['general', 'misc', 'advanced']}, {id: 'uml'}, {id: 'uml25'}, {id: 'search'}, {id: 'er'}, {id: 'alibaba_cloud'}, {id: 'azure2', prefix: 'azure2', libs: ['AI Machine Learning', 'Analytics', 'App Services', 'Azure Ecosystem', 'Azure Stack', 'Azure VMware Solution', 'Blockchain', 'Compute', 'Containers', 'CXP', 'Databases', 'DevOps', 'General', 'Hybrid and Multicloud', 'Identity', 'Integration', 'Intune', 'IoT', 'Management Governance', 'Menu', 'Migrate', 'Mixed Reality', 'Mobile', 'Monitor', 'Networking', 'Other', 'Power Platform', 'Preview', 'Security', 'Storage', 'Web']}, {id: 'dynamics365', prefix: 'dynamics365', libs: ['App', 'Mixed Reality', 'Product Family', 'Sub App']}, {id: 'ios', prefix: 'ios', libs: [''/*prefix is library*/, '7icons', '7ui']}, {id: 'android', prefix: 'android', libs: [''/*prefix is library*/]}, {id: 'aws3d'}, {id: 'flowchart'}, {id: 'basic'}, {id: 'infographic'}, {id: 'arrows'}, {id: 'arrows2'}, {id: 'lean_mapping'}, {id: 'citrix'}, {id: 'azure'}, {id: 'network'}, {id: 'network2'}, {id: 'vvd'}, {id: 'sitemap'}, {id:'c4'}, {id: 'dfd'}, {id: 'threatModeling'}, {id : 'kubernetes'}, {id : 'cisco19', prefix: 'cisco19', libs: Sidebar.prototype.cisco19}, {id: 'citrix2', prefix: 'citrix2', libs: ['Authentication', 'Cloud Services', 'Components', 'Locations', 'NetScaler', 'Networking', 'Resources', 'Users and Devices']}, {id: 'mscae', prefix: 'mscae', libs: ['Companies', 'EnterpriseFlat', 'IntuneFlat', 'OMSFlat', 'System CenterFlat', 'AI and ML Service', 'Analytics Service', 'Compute Service', 'Compute Service VM', 'Container Service', 'Databases Service', 'DevOps Service', 'General Service', 'Identity Service', 'Integration Service', 'Internet of Things Service', 'Intune Service', 'Management and Governance Service', 'Management and Governance Service Media', 'Migrate Service', 'Mixed Reality Service', 'Mobile Service', 'Networking Service', 'Other Category Service', 'Security Service', 'Storage Service', 'Web Service']}, {id: 'active_directory'}, {id: 'bpmn2', prefix: 'bpmn2', libs: ['General', 'Tasks', 'Choreographies', 'Events', 'Gateways']}, {id: 'clipart', prefix: null, libs: ['computer', 'finance', 'clipart', 'networking', 'people', 'telco']}, {id: 'ibm', prefix: 'ibm', libs: Sidebar.prototype.ibm}, {id: 'ibm_cloud', prefix: 'ibm_cloud', libs: Sidebar.prototype.ibm_cloud}, {id: 'allied_telesis', prefix: 'allied_telesis', libs: Sidebar.prototype.allied_telesis}, {id: 'cumulus', libs: ['cumulus']}, {id: 'eip', prefix: 'eip', libs: Sidebar.prototype.eip}, {id: 'mockups', prefix: 'mockup', libs: ['Buttons', 'Containers', 'Forms', 'Graphics', 'Markup', 'Misc', 'Navigation', 'Text']}, {id: 'pid2', prefix: 'pid2', libs: ['Agitators', 'Apparatus Elements', 'Centrifuges', 'Compressors', 'Compressors ISO', 'Crushers Grinding', 'Driers', 'Engines', 'Feeders', 'Filters', 'Fittings', 'Flow Sensors', 'Heat Exchangers', 'Instruments', 'Misc', 'Mixers', 'Piping', 'Pumps', 'Pumps DIN', 'Pumps ISO', 'Separators', 'Shaping Machines', 'Valves', 'Vessels']}, {id: 'salesforce', prefix: 'salesforce', libs: ['Components', 'Product', 'Platform', 'Industry']}, {id: 'signs', prefix: 'signs', libs: Sidebar.prototype.signs}, {id: 'gcp', prefix: 'gcp', libs: Sidebar.prototype.gcp}, {id: 'gcp2', prefix: 'gcp2', libs: Sidebar.prototype.gcp2}, {id: 'gcp3', prefix: 'gcp3', libs: Sidebar.prototype.gcp3}, {id: 'gcpicons', prefix: 'gcpicons', libs: Sidebar.prototype.gcpicons}, {id: 'rack', prefix: 'rack', libs: Sidebar.prototype.rack}, {id: 'electrical', prefix: 'electrical', libs: Sidebar.prototype.electrical}, {id: 'aws2', prefix: 'aws2', libs: Sidebar.prototype.aws2}, {id: 'aws3', prefix: 'aws3', libs: Sidebar.prototype.aws3}, {id: 'aws4b', prefix: 'aws4b', libs: Sidebar.prototype.aws4b}, {id: 'sap', prefix: 'sap', libs: Sidebar.prototype.sap}, {id: 'aws4', prefix: 'aws4', libs: Sidebar.prototype.aws4}, {id: 'pid', prefix: 'pid', libs: Sidebar.prototype.pids}, {id: 'cisco', prefix: 'cisco', libs: Sidebar.prototype.cisco}, {id: 'cisco_safe', prefix: 'cisco_safe', libs: Sidebar.prototype.cisco_safe}, {id: 'office', prefix: 'office', libs: Sidebar.prototype.office}, {id: 'openstack', prefix: 'openstack', libs : Sidebar.prototype.openstack}, {id: 'veeam', prefix: 'veeam', libs: Sidebar.prototype.veeam}, {id: 'veeam2', prefix: 'veeam2', libs: Sidebar.prototype.veeam2}, {id: 'cabinets', libs: ['cabinets']}, {id: 'floorplan', libs: ['floorplan']}, {id: 'bootstrap', libs: ['bootstrap']}, {id: 'atlassian', libs: ['atlassian']}, {id: 'fluid_power', libs: ['fluid_power']}, {id: 'gmdl', prefix: 'gmdl', libs: Sidebar.prototype.gmdl}, {id: 'archimate4', prefix: 'archimate4', libs: Sidebar.prototype.archimate4}, {id: 'archimate3', prefix: 'archimate3', libs: Sidebar.prototype.archimate3}, {id: 'archimate', libs: ['archimate']}, {id: 'webicons', libs: ['webicons', 'weblogos']}, {id: 'sysml', prefix: 'sysml', libs: Sidebar.prototype.sysml}]; /** * Removes disabled libraries from search results. */ var sidebarAddEntry = Sidebar.prototype.addEntry; Sidebar.prototype.addEntry = function(tags, fn) { if (this.currentSearchEntryLibrary != null && this.enabledLibraries != null && mxUtils.indexOf(this.enabledLibraries, this.currentSearchEntryLibrary.id) < 0) { return fn; } else { return sidebarAddEntry.apply(this, arguments); } }; /** * Refreshes scratchpad. */ var sidebarRefresh = Sidebar.prototype.refresh; Sidebar.prototype.refresh = function() { var ui = this.editorUi; sidebarRefresh.apply(this, arguments); if (ui.scratchpad != null) { ui.loadLibrary(new StorageLibrary(ui, ui.scratchpad.getData(), '.scratchpad')); } }; /** * Toggle palette. */ Sidebar.prototype.togglePalettes = function(prefix, ids) { this.showPalettes(prefix, ids); }; /** * Toggle palette. */ Sidebar.prototype.togglePalette = function(id) { this.showPalette(id); }; /** * Shows or hides palettes. */ Sidebar.prototype.showPalettes = function(prefix, ids, visible) { var result = []; for (var i = 0; i < ids.length; i++) { result = result.concat(this.showPalette(prefix + ids[i], visible)); } return result; }; /** * Shows or hides a palette. */ Sidebar.prototype.showPalette = function(id, visible) { var elts = this.palettes[id]; if (elts != null) { var vis = (visible != null) ? ((visible) ? '' : 'none') : (elts[0].style.display == 'none') ? '' : 'none'; for (var i = 0; i < elts.length; i++) { elts[i].style.display = vis; } } return elts; }; /** * */ Sidebar.prototype.getConfigurationById = function(id) { for (var i = 0; i < this.configuration.length; i++) { if (this.configuration[i].id == id) { return this.configuration[i]; } } if (this.customEntries != null) { for (var i = 0; i < this.customEntries.length; i++) { var section = this.customEntries[i] || {}; for (var j = 0; section.entries != null && j < section.entries.length; j++) { var entry = section.entries[j]; if (entry.id == id) { return {id: entry.id, libs: entry.libs}; } } } } return null; }; /** * */ Sidebar.prototype.isEntryVisible = function(key) { if (key == '.scratchpad') { return this.editorUi.scratchpad != null; } else { var config = this.getConfigurationById(key); if (config != null) { var id = (config.libs != null) ? ((config.prefix || '') + config.libs[0]) : key; var elts = this.palettes[id]; if (elts != null) { return elts[0].style.display != 'none'; } } if (this.customEntries != null) { for (var i = 0; i < this.customEntries.length; i++) { var section = this.customEntries[i] || {}; for (var j = 0; section.entries != null && j < section.entries.length; j++) { var entry = section.entries[j]; if (entry.id == key) { if (entry.libs != null && entry.libs.length > 0) { var elts = this.palettes[entry.id + '.0']; if (elts != null) { return elts[0].style.display != 'none'; } } break; } } } } return false; } }; /** * */ Sidebar.prototype.showEntries = function(entries, remember, force) { var all = []; if (remember) { mxSettings.setLibraries(entries); mxSettings.save(); } if (entries != null && (force || entries.length > 0)) { all.push(entries); } else { var done = false; if (urlParams['libs'] != null && urlParams['libs'].length > 0) { all.push(decodeURIComponent(urlParams['libs'])); done = this.editorUi.getServiceName() == 'draw.io'; } // Libs parameter overrides configuration for online app so that // links can be created to show just the specifies libraries if (!done) { if (mxSettings != null && mxSettings.settings != null) { all.push(mxSettings.getLibraries()); } else { all.push(this.defaultEntries); } } } // Merges array of semicolon separated strings into a single array var temp = all.join(';').split(';'); // Resolves aliases and creates lookup var visible = {}; for (var i = 0; i < temp.length; i++) { visible[this.libAliases[temp[i]] || temp[i]] = true; } for (var i = 0; i < this.configuration.length; i++) { // Search has separate switch in Extras menu if (this.configuration[i].id != 'search') { this.showPalettes(this.configuration[i].prefix || '', this.configuration[i].libs || [this.configuration[i].id], visible[this.configuration[i].id] == true); } } if (this.customEntries != null) { for (var i = 0; i < this.customEntries.length; i++) { var section = this.customEntries[i] || {}; for (var j = 0; section.entries != null && j < section.entries.length; j++) { var entry = section.entries[j]; if (entry.libs != null && entry.libs.length > 0) { var libs = []; for (var k = 0; k < entry.libs.length; k++) { libs.push(entry.id + '.' + k); } // Missing entries must hide the palette or showPalette toggles it this.showPalettes('', libs, visible[entry.id] == true); } } } } }; /** * Overrides the sidebar init. */ Sidebar.prototype.init = function() { this.updateEntries(); // Uses search.xml index file instead (faster load times) this.addStencilsToIndex = false; // Contains additional tags for shapes this.shapetags = {}; // Adds tags from compressed text file for improved searches if (this.tagIndex != null) { this.addTagIndex(Graph.decompress(this.tagIndex)); this.tagIndex = null; } this.initPalettes(); // Records default palette order after all palettes are positioned this.defaultPaletteOrder = this.getCurrentPaletteOrder(); this.applyPaletteOrder(); }; /** * Defines all entries for the sidebar. This is used in the MoreShapes dialog. Create screenshots using the savesidebar URL parameter and * http://www.alderg.com/merge.html for creating a vertical stack of PNG images if multiple sidebars are part of an entry. */ Sidebar.prototype.updateEntries = function() { var stdEntries = [{title: mxResources.get('general'), id: 'general', image: IMAGE_PATH + '/sidebar-general.png'}, {title: mxResources.get('basic'), id: 'basic', image: IMAGE_PATH + '/sidebar-basic.png'}, {title: mxResources.get('arrows'), id: 'arrows2', image: IMAGE_PATH + '/sidebar-arrows2.png'}, {title: mxResources.get('clipart'), id: 'clipart', image: IMAGE_PATH + '/sidebar-clipart.png'}, {title: mxResources.get('flowchart'), id: 'flowchart', image: IMAGE_PATH + '/sidebar-flowchart.png'}]; if (Editor.currentTheme == 'simple' || Editor.currentTheme == 'sketch' || Editor.currentTheme == 'min') { stdEntries = [{title: mxResources.get('searchShapes'), id: 'search'}, {title: mxResources.get('scratchpad'), id: '.scratchpad'}]. concat(stdEntries); } // Get current year as yyyy var year = new Date().getFullYear(); this.entries = [{title: mxResources.get('standard'), entries: stdEntries}, {title: mxResources.get('software'), entries: [{title: 'Active Directory', id: 'active_directory', image: IMAGE_PATH + '/sidebar-active_directory.png'}, {title: mxResources.get('android'), id: 'android', image: IMAGE_PATH + '/sidebar-android.png'}, {title: 'Atlassian', id: 'atlassian', image: IMAGE_PATH + '/sidebar-atlassian.png'}, {title: mxResources.get('bootstrap'), id: 'bootstrap', image: IMAGE_PATH + '/sidebar-bootstrap.png'}, {title: 'C4', id: 'c4', image: IMAGE_PATH + '/sidebar-c4.png'}, {title: 'Data Flow Diagram', id: 'dfd', image: IMAGE_PATH + '/sidebar-dfd.png'}, {title: mxResources.get('entityRelation'), id: 'er', image: IMAGE_PATH + '/sidebar-er.png'}, {title: mxResources.get('ios'), id: 'ios', image: IMAGE_PATH + '/sidebar-ios.png'}, {title: mxResources.get('mockups'), id: 'mockups', image: IMAGE_PATH + '/sidebar-mockups.png'}, {title: 'Sitemap', id: 'sitemap', image: IMAGE_PATH + '/sidebar-sitemap.png'}, {title: 'Salesforce', id: 'salesforce', image: IMAGE_PATH + '/sidebar-salesforce.png'}, {title: mxResources.get('uml') + ' 2.5', id: 'uml25', image: IMAGE_PATH + '/sidebar-uml25.png'}, {title: mxResources.get('uml'), id: 'uml', image: IMAGE_PATH + '/sidebar-uml.png'}]}, {title: mxResources.get('networking'), entries: [{title: 'Alibaba Cloud', id: 'alibaba_cloud', image: IMAGE_PATH + '/sidebar-alibaba_cloud.png'}, {title: 'Allied Telesis', id: 'allied_telesis', image: IMAGE_PATH + '/sidebar-allied_telesis.png'}, {title: 'AWS17', id: 'aws3', image: IMAGE_PATH + '/sidebar-aws3.png'}, {title: 'AWS18', id: 'aws4b', image: IMAGE_PATH + '/sidebar-aws4b.png'}, {title: 'AWS ' + year, id: 'aws4', image: IMAGE_PATH + '/sidebar-aws4.png'}, // TODO: Add isometric containers {title: mxResources.get('aws3d'), id: 'aws3d', image: IMAGE_PATH + '/sidebar-aws3d.png'}, {title: mxResources.get('azure'), id: 'azure2', image: IMAGE_PATH + '/sidebar-azure.png'}, {title: 'Cloud & Enterprise', id: 'mscae', image: IMAGE_PATH + '/sidebar-mscae.png'}, {title: mxResources.get('cisco'), id: 'cisco', image: IMAGE_PATH + '/sidebar-cisco.png'}, {title: 'Cisco19', id: 'cisco19', image: IMAGE_PATH + '/sidebar-cisco19.png'}, {title: 'Cisco Safe', id: 'cisco_safe', image: IMAGE_PATH + '/sidebar-cisco_safe.png'}, {title: 'Cumulus', id: 'cumulus', image: IMAGE_PATH + '/sidebar-cumulus.png'}, {title: 'Citrix', id: 'citrix2', image: IMAGE_PATH + '/sidebar-citrix2.png'}, {title: 'Citrix (legacy)', id: 'citrix', image: IMAGE_PATH + '/sidebar-citrix.png'}, {title: 'Dynamics365', id: 'dynamics365', image: IMAGE_PATH + '/sidebar-dynamics365.png'}, {title: 'Google Cloud Platform', id: 'gcp2', image: IMAGE_PATH + '/sidebar-gcp2.png'}, {title: 'Google Cloud Platform 2026', id: 'gcp3', image: IMAGE_PATH + '/sidebar-gcp3.png'}, {title: 'GCP Icons', id: 'gcpicons', image: IMAGE_PATH + '/sidebar-gcpicons.png'}, {title: 'IBM', id: 'ibm', image: IMAGE_PATH + '/sidebar-ibm.png'}, {title: 'IBM Cloud', id: 'ibm_cloud', image: IMAGE_PATH + '/sidebar-ibmcloud.png'}, {title: 'Kubernetes', id: 'kubernetes', image: IMAGE_PATH + '/sidebar-kubernetes.png'}, {title: 'Network 2025', id: 'network2', image: IMAGE_PATH + '/sidebar-network2.png'}, {title: 'Network 2018', id: 'network', image: IMAGE_PATH + '/sidebar-network.png'}, {title: 'Office', id: 'office', image: IMAGE_PATH + '/sidebar-office.png'}, {title: 'OpenStack', id: 'openstack', image: IMAGE_PATH + '/sidebar-openstack.png'}, {title: mxResources.get('rack'), id: 'rack', image: IMAGE_PATH + '/sidebar-rack.png'}, {title: 'SAP', id: 'sap', image: IMAGE_PATH + '/sidebar-sap.png'}, {title: 'Veeam', id: 'veeam2', image: IMAGE_PATH + '/sidebar-veeam.png'}, {title: 'VMware', id: 'vvd', image: IMAGE_PATH + '/sidebar-vvd.png'}]}, {title: mxResources.get('business'), entries: [{title: 'ArchiMate 4', id: 'archimate4', image: IMAGE_PATH + '/sidebar-archimate4.png'}, {title: 'ArchiMate 3.2', id: 'archimate3', image: IMAGE_PATH + '/sidebar-archimate3.png'}, {title: mxResources.get('archiMate21'), id: 'archimate', image: IMAGE_PATH + '/sidebar-archimate.png'}, {title: mxResources.get('bpmn') + ' 2.0', id: 'bpmn2', image: IMAGE_PATH + '/sidebar-bpmn.png'}, {title: mxResources.get('sysml'), id: 'sysml', image: IMAGE_PATH + '/sidebar-sysml.png'}, {title: 'Value Stream Mapping', id: 'lean_mapping', image: IMAGE_PATH + '/sidebar-leanmapping.png'}]}, {title: mxResources.get('other'), entries: [{title: mxResources.get('cabinets'), id: 'cabinets', image: IMAGE_PATH + '/sidebar-cabinets.png'}, {title: 'Infographic', id: 'infographic', image: IMAGE_PATH + '/sidebar-infographic.png'}, {title: mxResources.get('eip'), id: 'eip', image: IMAGE_PATH + '/sidebar-eip.png'}, {title: mxResources.get('electrical'), id: 'electrical', image: IMAGE_PATH + '/sidebar-electrical.png'}, {title: mxResources.get('floorplans'), id: 'floorplan', image: IMAGE_PATH + '/sidebar-floorplans.png'}, {title: 'Fluid Power (ISO 1219)', id: 'fluid_power', image: IMAGE_PATH + '/sidebar-fluid_power.png'}, {title: mxResources.get('gmdl'), id: 'gmdl', image: IMAGE_PATH + '/sidebar-gmdl.png'}, {title: mxResources.get('procEng'), id: 'pid', image: IMAGE_PATH + '/sidebar-pid.png'}, {title: 'Threat Modeling', id: 'threatModeling', image: IMAGE_PATH + '/sidebar-threatmodeling.png'}, {title: 'Web Icons', id: 'webicons', image: IMAGE_PATH + '/sidebar-webIcons.png'}, {title: mxResources.get('signs'), id: 'signs', image: IMAGE_PATH + '/sidebar-signs.png'}]}]; }; /** * Overridden to add image export via servlet */ if (urlParams['savesidebar'] == '1') { Sidebar.prototype.addFoldingHandler = function(title, content, funct) { var initialized = false; title.style.backgroundImage = (content.style.display == 'none') ? 'url(\'' + Editor.arrowRightImage + '\')' : 'url(\'' + Editor.arrowDownImage + '\')'; var btn = document.createElement('button'); btn.style.margin = '0 4px'; mxUtils.write(btn, 'Save'); // Palettes that start expanded never enter the expand branch // below, so the button must be added at install time if (content.style.display != 'none') { title.appendChild(btn); } mxEvent.addListener(title, 'click', mxUtils.bind(this, function(evt) { if (mxEvent.getSource(evt).nodeName == 'BUTTON') { var svgs = content.getElementsByTagName('svg'); var w = 6 * 68 + 2 * 12; var h = Math.ceil(svgs.length / 6) * 68 + 24 + 12; var root = Graph.createSvgNode(0, 0, w, h, mxUtils.getLightDarkColor('#ffffff')); // Adds CSS variables used for background colors in thumbs root.ownerDocument.documentElement.style.setProperty('--ge-panel-color', '#ffffff'); root.ownerDocument.documentElement.style.setProperty('--ge-dark-panel-color', Editor.darkColor); // Adds title var canvas = new mxSvgCanvas2D(root); canvas.setFontFamily(mxConstants.DEFAULT_FONTFAMILY); canvas.setFontStyle(mxConstants.FONT_BOLD); canvas.setFontColor('rgb(80, 80, 80)'); canvas.setFontSize(14); // Extracts title text from the first span that contains // text, as the first span in the title element is the // invisible collapse/expand hit-area overlay added in // Sidebar.createTitle var spans = title.getElementsByTagName('span'); for (var i = 0; i < spans.length; i++) { var text = mxUtils.getTextContent(spans[i]); if (text != '') { canvas.text(6, 0, 0, 0, text); break; } } for (var i = 0; i < svgs.length; i++) { var svg = svgs[i]; var clone = svg.cloneNode(true); clone.removeAttribute('style'); clone.setAttribute('width', this.thumbWidth); clone.setAttribute('height', this.thumbHeight); clone.setAttribute('x', 12 + mxUtils.mod(i, 6) * 68); clone.setAttribute('y', 24 + Math.floor(i / 6) * 68); root.appendChild(clone); } this.editorUi.editor.convertImages(root, mxUtils.bind(this, function(body) { var canvas = document.createElement('canvas'); canvas.width = w; canvas.height = h; var img = document.createElement('img'); img.onload = mxUtils.bind(this, function() { var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0) this.editorUi.saveCanvas(canvas, null, 'png'); }); var xml = Graph.xmlDeclaration + '\n' + Graph.svgDoctype + '\n' + mxUtils.getXml(root); img.src = Editor.createSvgDataUri(xml); })); return; } if (content.style.display == 'none') { if (!initialized) { initialized = true; if (funct != null) { if (btn.parentNode != null) { btn.parentNode.removeChild(btn); } // Wait cursor does not show up on Mac title.style.cursor = 'wait'; var prev = title.innerHTML; title.innerHTML = mxResources.get('loading') + '...'; window.setTimeout(function() { funct(content); title.style.cursor = ''; title.innerHTML = prev; title.appendChild(btn); }, 0); } else { title.appendChild(btn); } } else { title.appendChild(btn); } title.style.backgroundImage = 'url(\'' + Editor.arrowDownImage + '\')'; content.style.display = 'block'; } else { title.style.backgroundImage = 'url(\'' + Editor.arrowRightImage + '\')'; content.style.display = 'none'; if (btn.parentNode != null) { btn.parentNode.removeChild(btn); } } mxEvent.consume(evt); })); }; }; // Overrides addPalette to persist expanded/collapsed library state var sidebarAddPalette = Sidebar.prototype.addPalette; Sidebar.prototype.addPalette = function(id, title, expanded, onInit, eager) { expanded = this.editorUi.getLibraryExpanded(id, expanded); var result = sidebarAddPalette.call(this, id, title, expanded, onInit, eager); // Tags title and content divs with palette id if (id != null && this.palettes[id] != null) { this.palettes[id][0]._paletteId = id; var contentDiv = this.palettes[id][1].firstChild; if (contentDiv != null) { contentDiv._paletteId = id; } // Installs drag reorder on non-search palettes if (id != 'search') { this.installPaletteDragReorder( this.palettes[id][0], this.palettes[id][1], id); } this.installPaletteContextMenu(this.palettes[id][0], id); // Debounced re-apply of saved order for late-loaded palettes if (this._applyOrderTimer != null) { window.clearTimeout(this._applyOrderTimer); } this._applyOrderTimer = window.setTimeout(mxUtils.bind(this, function() { this._applyOrderTimer = null; this.applyPaletteOrder(); }), 0); } return result; }; // Overrides setContentVisible to persist state on toggle var sidebarSetContentVisible = Sidebar.prototype.setContentVisible; Sidebar.prototype.setContentVisible = function(content, visible) { sidebarSetContentVisible.apply(this, arguments); if (content._paletteId != null) { this.editorUi.setLibraryExpanded(content._paletteId, visible); } }; /** * Installs drag-to-reorder on a palette title element. A click still * expands/collapses; the drag only activates after significant movement. * The preview moves the actual title and content in-place in the DOM. */ Sidebar.prototype.installPaletteDragReorder = function(title, outer, id) { var sidebar = this; var dragThreshold = 20; // Use pointer events when available (required because addFoldingHandler // calls mxEvent.preventDefault which suppresses mousedown via pointerdown) var downEvt = (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown'; var moveEvt = (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove'; var upEvt = (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup'; mxEvent.addListener(title, downEvt, mxUtils.bind(this, function(evt) { if (evt.button != 0) { return; } var startY = evt.clientY; var dragging = false; var moved = false; var dragPreview = null; var moveHandler = mxUtils.bind(this, function(e) { var dy = e.clientY - startY; if (!dragging) { if (Math.abs(dy) > dragThreshold) { dragging = true; sidebar._paletteDragging = true; sidebar.container.classList.add('gePaletteDragging'); // Create floating preview clone of the title var titleRect = title.getBoundingClientRect(); dragPreview = title.cloneNode(true); dragPreview.style.position = 'fixed'; dragPreview.style.zIndex = '10001'; dragPreview.style.width = titleRect.width + 'px'; dragPreview.style.left = titleRect.left + 'px'; dragPreview.style.top = (e.clientY - titleRect.height / 2) + 'px'; dragPreview.style.pointerEvents = 'none'; dragPreview.style.opacity = '0.8'; dragPreview.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)'; document.body.appendChild(dragPreview); // Show placeholder at source position and hide content title.classList.add('gePaletteDragPlaceholder'); outer.style.display = 'none'; } return; } // Update preview position if (dragPreview != null) { var titleRect = title.getBoundingClientRect(); dragPreview.style.top = (e.clientY - dragPreview.offsetHeight / 2) + 'px'; } // Move the actual title+outer in the DOM to the target position var wrapper = sidebar.getEntryContainer(); var children = wrapper.childNodes; var targetBefore = null; for (var i = 0; i < children.length; i++) { var child = children[i]; if (child._paletteId == null || child.className == null || child.className.indexOf('geTitle') < 0 || child._paletteId == 'search' || child.style.display == 'none' || child._paletteId == id) { continue; } var rect = child.getBoundingClientRect(); var nextSib = child.nextSibling; var pairBottom = (nextSib != null && nextSib.firstChild != null && nextSib.firstChild.style.display != 'none') ? nextSib.getBoundingClientRect().bottom : rect.bottom; if (e.clientY < (rect.top + pairBottom) / 2) { targetBefore = child; break; } } // Move title+outer before the target (or to end) if (targetBefore != null) { if (outer.nextSibling != targetBefore) { wrapper.insertBefore(title, targetBefore); wrapper.insertBefore(outer, targetBefore); moved = true; } } else if (wrapper.lastChild != outer) { wrapper.appendChild(title); wrapper.appendChild(outer); moved = true; } // Auto-scroll near edges var wrapperRect = wrapper.getBoundingClientRect(); if (e.clientY - wrapperRect.top < 30) { wrapper.scrollTop -= 10; } else if (wrapperRect.bottom - e.clientY < 30) { wrapper.scrollTop += 10; } e.preventDefault(); }); var upHandler = mxUtils.bind(this, function(e) { if (dragging && moved) { sidebar.savePaletteOrder(); } if (dragging) { sidebar.container.classList.remove('gePaletteDragging'); title.classList.remove('gePaletteDragPlaceholder'); outer.style.display = ''; if (dragPreview != null && dragPreview.parentNode != null) { dragPreview.parentNode.removeChild(dragPreview); dragPreview = null; } // Suppress the click that follows mouseup window.setTimeout(function() { sidebar._paletteDragging = false; }, 100); } document.removeEventListener(moveEvt, moveHandler); document.removeEventListener(upEvt, upHandler); }); document.addEventListener(moveEvt, moveHandler); document.addEventListener(upEvt, upHandler); })); }; /** * Installs a right-click context menu on every palette title with * Collapse/Expand for the clicked section, Collapse All / Expand All * for the whole sidebar, and (for reorderable palettes) Reset to * clear the saved drag order. */ Sidebar.prototype.installPaletteContextMenu = function(title, id) { var sidebar = this; mxEvent.addListener(title, 'contextmenu', mxUtils.bind(this, function(evt) { mxEvent.consume(evt); var menuDiv = document.createElement('div'); menuDiv.className = 'mxPopupMenu geMenubarMenu'; menuDiv.style.position = 'absolute'; menuDiv.style.zIndex = '10001'; menuDiv.style.left = evt.clientX + 'px'; menuDiv.style.top = evt.clientY + 'px'; var table = document.createElement('table'); table.className = 'mxPopupMenu'; var tbody = document.createElement('tbody'); table.appendChild(tbody); menuDiv.appendChild(table); var hide = function() { if (menuDiv.parentNode != null) { menuDiv.parentNode.removeChild(menuDiv); } document.removeEventListener('mousedown', hideMenu); }; var hideMenu = function(e) { if (!menuDiv.contains(e.target)) { hide(); } }; var addItem = function(label, fn) { var tr = document.createElement('tr'); tr.className = 'mxPopupMenuItem'; var td = document.createElement('td'); td.className = 'mxPopupMenuItem'; td.style.padding = '6px 10px'; td.style.cursor = 'pointer'; mxUtils.write(td, label); tr.appendChild(td); tbody.appendChild(tr); mxEvent.addListener(tr, 'mouseup', function() { hide(); fn(); }); mxEvent.addListener(tr, 'mouseenter', function() { tr.className = 'mxPopupMenuItem mxPopupMenuItemHover'; }); mxEvent.addListener(tr, 'mouseleave', function() { tr.className = 'mxPopupMenuItem'; }); }; var addSeparator = function() { var tr = document.createElement('tr'); var td = document.createElement('td'); td.style.padding = '0'; var hr = document.createElement('hr'); hr.style.cssText = 'border:none;border-top:1px solid ' + 'light-dark(#e0e0e0,#444);margin:4px 0'; td.appendChild(hr); tr.appendChild(td); tbody.appendChild(tr); }; var elts = sidebar.palettes[id]; var contentDiv = (elts != null) ? elts[1].firstChild : null; var expanded = contentDiv != null && contentDiv.style.display != 'none'; addItem(mxResources.get(expanded ? 'collapse' : 'expand'), function() { title.click(); }); addSeparator(); addItem(mxResources.get('collapseAll'), function() { sidebar.setAllPalettesExpanded(false); }); addItem(mxResources.get('expandAll'), function() { sidebar.setAllPalettesExpanded(true); }); if (id != 'search' && mxSettings.getLibraryOrder() != null) { addSeparator(); addItem(mxResources.get('reset'), function() { sidebar.resetPaletteOrder(); }); } document.body.appendChild(menuDiv); mxUtils.fit(menuDiv); document.addEventListener('mousedown', hideMenu); })); }; /** * Toggles every palette to the given expanded state by clicking each * title that doesn't already match. Going through the normal click * path keeps the arrow icon, lazy init (onInit), and persisted state * (via setContentVisible override) all in sync. */ Sidebar.prototype.setAllPalettesExpanded = function(expanded) { for (var pid in this.palettes) { var elts = this.palettes[pid]; if (elts == null) { continue; } var titleEl = elts[0]; var contentDiv = elts[1].firstChild; if (titleEl == null || contentDiv == null) { continue; } var currentlyExpanded = contentDiv.style.display != 'none'; if (currentlyExpanded != expanded) { titleEl.click(); } } }; /** * Moves a palette before another palette in the DOM and saves the order. */ Sidebar.prototype.movePalette = function(paletteId, beforePaletteId) { var elts = this.palettes[paletteId]; if (elts == null) { return; } var titleEl = elts[0]; var outerEl = elts[1]; var wrapper = this.getEntryContainer(); if (beforePaletteId != null) { var beforeElts = this.palettes[beforePaletteId]; if (beforeElts != null) { wrapper.insertBefore(titleEl, beforeElts[0]); wrapper.insertBefore(outerEl, beforeElts[0]); } } else { wrapper.appendChild(titleEl); wrapper.appendChild(outerEl); } this.savePaletteOrder(); }; /** * Returns the current palette order from the DOM. */ Sidebar.prototype.getCurrentPaletteOrder = function() { var order = []; var wrapper = this.getEntryContainer(); var children = wrapper.childNodes; for (var i = 0; i < children.length; i++) { if (children[i]._paletteId != null && children[i].className != null && children[i].className.indexOf('geTitle') >= 0) { order.push(children[i]._paletteId); } } return order; }; /** * Saves the current palette order to settings. */ Sidebar.prototype.savePaletteOrder = function() { var order = this.getCurrentPaletteOrder(); mxSettings.setLibraryOrder(order); mxSettings.save(); }; /** * Applies saved palette order from settings after init. */ Sidebar.prototype.applyPaletteOrder = function() { var order = mxSettings.getLibraryOrder(); if (order == null || order.length == 0) { return; } var wrapper = this.getEntryContainer(); for (var i = 0; i < order.length; i++) { var elts = this.palettes[order[i]]; if (elts != null && elts[0].parentNode == wrapper) { wrapper.appendChild(elts[0]); wrapper.appendChild(elts[1]); } } }; /** * Resets palette order to the default. */ Sidebar.prototype.resetPaletteOrder = function() { mxSettings.setLibraryOrder(null); mxSettings.save(); if (this.defaultPaletteOrder != null) { var wrapper = this.getEntryContainer(); for (var i = 0; i < this.defaultPaletteOrder.length; i++) { var elts = this.palettes[this.defaultPaletteOrder[i]]; if (elts != null && elts[0].parentNode == wrapper) { wrapper.appendChild(elts[0]); wrapper.appendChild(elts[1]); } } } }; /** * Overridden to use shapetags to improve search results. */ Sidebar.prototype.extractShapeStyle = function(style) { if (style != null && style.substring(0, 6) == 'shape=') { var semi = style.indexOf(';'); if (semi < 0) { semi = style.length; } return style.substring(6, semi); } return null; }; /** * Overridden to use shapetags to improve search results. */ var sidebarGetTagsForStencil = Sidebar.prototype.getTagsForStencil; Sidebar.prototype.getTagsForStencil = function(pkg, stc, moreTags) { var tags = sidebarGetTagsForStencil.apply(this, arguments); // Adds tags from tags file if (this.shapetags != null) { pkg = pkg.toLowerCase(); stc = stc.toLowerCase(); if (this.shapetags[pkg] != null) { tags.push(this.shapetags[pkg]); } stc = pkg + '.' + stc; if (this.shapetags[stc] != null) { tags.push(this.shapetags[stc]); } } return tags; }; /** * Overrides the sidebar init. */ Sidebar.prototype.addTagIndex = function(text) { var lines = String(text).split('\n'); for (var i = 0; i < lines.length; i++) { if (lines[i] != null) { var tags = lines[i].split('\t'); if (tags.length > 1) { var key = tags[0].toLowerCase().replace(' ', '_'); var value = mxUtils.trim(tags.slice(1, tags.length).join(' ').toLowerCase()); if (value.length > 0) { this.shapetags[key] = value; } } } } }; /** * */ Sidebar.prototype.getUniqueLibraries = function(libs) { var lookup = {}; var result = []; for (var i = 0; i < libs.length; i++) { var key = JSON.stringify(libs[i]); if (lookup[key] == null) { lookup[key] = true; result.push(libs[i]); } } return result; }; /** * */ Sidebar.prototype.openLibraries = function(libs) { libs = this.getUniqueLibraries(libs); var elts = null; for (var i = 0; i < libs.length; i++) { var config = this.getConfigurationById(libs[i].id); if (config != null) { var temp = this.openLibrary(config, libs[i].lib || libs[i].id); if (temp != null && elts == null) { elts = temp; } } } if (elts != null) { window.setTimeout(function() { elts[0].scrollIntoView({behavior: 'smooth'}); }, 0); } }; /** * Opens the given library. */ Sidebar.prototype.openLibrary = function(config, lib) { this.showPalettes(config.prefix || '', config.libs || [config.id], true); var elts = this.showPalette(lib, true); if (elts != null && elts.length > 1 && elts[1].firstChild != null && (elts[1].firstChild.firstChild == null || elts[1].firstChild.style.display == 'none')) { elts[0].click(); } return elts; }; /** * Adds shape search UI. */ Sidebar.prototype.showPopupMenuForEntry = function(elt, libs, evt) { if (libs != null && libs.length > 0) { var offset = mxUtils.getOffset(elt); this.editorUi.showPopupMenu(mxUtils.bind(this, function(menu, parent) { menu.addItem(mxResources.get('openLibrary'), null, mxUtils.bind(this, function() { this.openLibraries(libs); })); }), offset.x, offset.y + elt.offsetHeight, evt); mxEvent.consume(evt); } }; /** * Overrides the sidebar init. */ Sidebar.prototype.addSearchIndex = function(shapes) { if (shapes != null) { for (var i = 0; i < shapes.length; i++) { if (shapes[i].style != null) { var style = shapes[i].style; var shapeStyle = this.extractShapeStyle(style); if (shapeStyle != null) { var lastDot = shapeStyle.lastIndexOf('.'); if (lastDot > 0) { var pkg = shapeStyle.substring(0, lastDot); var stc = shapeStyle.substring(lastDot + 1, shapeStyle.length); var tags = this.getTagsForStencil(pkg, stc, shapes[i].tags); // TODO: Use shapetags for programmatic stencils if (tags != null) { // Converts stencil name to lowercase var semi = style.indexOf(';'); style = 'shape=' + pkg + '.' + stc.toLowerCase() + ';' + ((semi < 0) ? '' : style.substring(semi + 1)); this.setCurrentSearchEntryLibrary(shapes[i].id, shapes[i].lib); this.createVertexTemplateEntry(style, parseInt(shapes[i].w), parseInt(shapes[i].h), '', stc.replace(/_/g, ' '), null, null, this.filterTags(tags.join(' '))); if (this.currentSearchEntryLibrary != null) { this.addLibForStyle(this.getKeyStyle(style), this.currentSearchEntryLibrary); } this.setCurrentSearchEntryLibrary(); } } } } } } }; /** * Overrides the sidebar init. */ Sidebar.prototype.initPalettes = function() { var imgDir = GRAPH_IMAGE_PATH; var dir = STENCIL_PATH; var signs = this.signs; var rack = this.rack; var pids = this.pids; var cisco = this.cisco; var sysml = this.sysml; if (urlParams['createindex'] == '1') { this.createdSearchIndex = []; } this.addSearchPalette(true); if (!this.appendCustomLibraries) { this.addCustomEntries(); } this.addGeneralPalette(this.customEntries == null); this.addMiscPalette(false); this.addAdvancedPalette(false); this.addBasicPalette(); this.addStencilPalette('arrows', mxResources.get('arrows'), dir + '/arrows.xml', ';html=1;' + mxConstants.STYLE_VERTICAL_LABEL_POSITION + '=bottom;' + mxConstants.STYLE_VERTICAL_ALIGN + '=top;' + mxConstants.STYLE_STROKEWIDTH + '=2;strokeColor=#000000;', null, null, null, null, null, 'arrows'); this.addArrows2Palette(); this.setCurrentSearchEntryLibrary('clipart', 'computer'); this.addImagePalette('computer', 'Clipart / Computer', imgDir + '/lib/clip_art/computers/', '_128x128.png', ['Antivirus', 'Data_Filtering', 'Database', 'Database_Add', 'Database_Minus', 'Database_Move_Stack', 'Database_Remove', 'Fujitsu_Tablet', 'Harddrive', 'IBM_Tablet', 'iMac', 'iPad', 'Laptop', 'MacBook', 'Mainframe', 'Monitor', 'Monitor_Tower', 'Monitor_Tower_Behind', 'Netbook', 'Network', 'Network_2', 'Printer', 'Printer_Commercial', 'Secure_System', 'Server', 'Server_Rack', 'Server_Rack_Empty', 'Server_Rack_Partial', 'Server_Tower', 'Software', 'Stylus', 'Touch', 'USB_Hub', 'Virtual_Application', 'Virtual_Machine', 'Virus', 'Workstation' ], [ 'Antivirus', 'Data Filtering', 'Database', 'Database Add', 'Database Minus', 'Database Move Stack', 'Database Remove', 'Fujitsu Tablet', 'Harddrive', 'IBMTablet', 'iMac', 'iPad', 'Laptop', 'MacBook', 'Mainframe', 'Monitor', 'Monitor Tower', 'Monitor Tower Behind', 'Netbook', 'Network', 'Network 2', 'Printer', 'Printer Commercial', 'Secure System', 'Server', 'Server Rack', 'Server Rack Empty', 'Server Rack Partial', 'Server Tower', 'Software', 'Stylus', 'Touch', 'USB Hub', 'Virtual Application', 'Virtual Machine', 'Virus', 'Workstation']); this.setCurrentSearchEntryLibrary('clipart', 'finance'); this.addImagePalette('finance', 'Clipart / Finance', imgDir + '/lib/clip_art/finance/', '_128x128.png', [ 'Arrow_Down', 'Arrow_Up', 'Coins', 'Credit_Card', 'Dollar', 'Graph', 'Pie_Chart', 'Piggy_Bank', 'Safe', 'Shopping_Cart', 'Stock_Down', 'Stock_Up'], ['Arrow_Down', 'Arrow Up', 'Coins', 'Credit Card', 'Dollar', 'Graph', 'Pie Chart', 'Piggy Bank', 'Safe', 'Shopping Basket', 'Stock Down', 'Stock Up']); this.setCurrentSearchEntryLibrary('clipart', 'clipart'); this.addImagePalette('clipart', 'Clipart / Various', imgDir + '/lib/clip_art/general/', '_128x128.png', [ 'Battery_0', 'Battery_100', 'Battery_50', 'Battery_75', 'Battery_allstates', 'Bluetooth', 'Earth_globe', 'Empty_Folder', 'Full_Folder', 'Gear', 'Keys', 'Lock', 'Mouse_Pointer', 'Plug', 'Ships_Wheel', 'Star', 'Tire' ], [ 'Battery 0%', 'Battery 100%', 'Battery 50%', 'Battery 75%', 'Battery', 'Bluetooth', 'Globe', 'Empty Folder', 'Full Folder', 'Gear', 'Keys', 'Lock', 'Mousepointer', 'Plug', 'Ships Wheel', 'Star', 'Tire']); this.setCurrentSearchEntryLibrary('clipart', 'networking'); this.addImagePalette('networking', 'Clipart / Networking', imgDir + '/lib/clip_art/networking/', '_128x128.png', ['Bridge', 'Certificate', 'Certificate_Off', 'Cloud', 'Cloud_Computer', 'Cloud_Computer_Private', 'Cloud_Rack', 'Cloud_Rack_Private', 'Cloud_Server', 'Cloud_Server_Private', 'Cloud_Storage', 'Concentrator', 'Email', 'Firewall_02', 'Firewall', 'Firewall-page1', 'Ip_Camera', 'Modem', 'power_distribution_unit', 'Print_Server', 'Print_Server_Wireless', 'Repeater', 'Router', 'Router_Icon', 'Switch', 'UPS', 'Wireless_Router', 'Wireless_Router_N'], ['Bridge', 'Certificate', 'Certificate Off', 'Cloud', 'Cloud Computer', 'Cloud Computer Private', 'Cloud Rack', 'Cloud Rack Private', 'Cloud Server', 'Cloud Server Private', 'Cloud Storage', 'Concentrator', 'Email', 'Firewall 1', 'Firewall 2', 'Firewall', 'Camera', 'Modem', 'Power Distribution Unit', 'Print Server', 'Print Server Wireless', 'Repeater', 'Router', 'Router Icon', 'Switch', 'UPS', 'Wireless Router', 'Wireless Router N'], {'Wireless_Router': 'wireless router switch wap wifi access point wlan', 'Wireless_Router_N': 'wireless router switch wap wifi access point wlan', 'Router': 'router switch', 'Router_Icon': 'router switch'}); this.setCurrentSearchEntryLibrary('clipart', 'people'); this.addImagePalette('people', 'Clipart / People', imgDir + '/lib/clip_art/people/', '_128x128.png', ['Suit_Man', 'Suit_Man_Black', 'Suit_Man_Blue', 'Suit_Man_Green', 'Suit_Man_Green_Black', 'Suit_Woman', 'Suit_Woman_Black', 'Suit_Woman_Blue', 'Suit_Woman_Green', 'Suit_Woman_Green_Black', 'Construction_Worker_Man', 'Construction_Worker_Man_Black', 'Construction_Worker_Woman', 'Construction_Worker_Woman_Black', 'Doctor_Man', 'Doctor_Man_Black', 'Doctor_Woman', 'Doctor_Woman_Black', 'Farmer_Man', 'Farmer_Man_Black', 'Farmer_Woman', 'Farmer_Woman_Black', 'Nurse_Man', 'Nurse_Man_Black', 'Nurse_Woman', 'Nurse_Woman_Black', 'Military_Officer', 'Military_Officer_Black', 'Military_Officer_Woman', 'Military_Officer_Woman_Black', 'Pilot_Man', 'Pilot_Man_Black', 'Pilot_Woman', 'Pilot_Woman_Black', 'Scientist_Man', 'Scientist_Man_Black', 'Scientist_Woman', 'Scientist_Woman_Black', 'Security_Man', 'Security_Man_Black', 'Security_Woman', 'Security_Woman_Black', 'Tech_Man', 'Tech_Man_Black', 'Telesales_Man', 'Telesales_Man_Black', 'Telesales_Woman', 'Telesales_Woman_Black', 'Waiter', 'Waiter_Black', 'Waiter_Woman', 'Waiter_Woman_Black', 'Worker_Black', 'Worker_Man', 'Worker_Woman', 'Worker_Woman_Black']); this.setCurrentSearchEntryLibrary('clipart', 'telco'); this.addImagePalette('telco', 'Clipart / Telecommunication', imgDir + '/lib/clip_art/telecommunication/', '_128x128.png', [ 'BlackBerry', 'Cellphone', 'HTC_smartphone', 'iPhone', 'Palm_Treo', 'Signal_tower_off', 'Signal_tower_on' ], ['BlackBerry', 'Cellphone', 'HTC smartphone', 'iPhone', 'Palm Treo', 'Signaltower off', 'Signaltower on']); this.setCurrentSearchEntryLibrary(); this.addFlowchartPalette(); this.addActiveDirectoryPalette(); this.addAndroidPalette(); this.addAtlassianPalette(); this.addBootstrapPalette(); this.addDFDPalette(); this.addErPalette(); this.addIos7Palette(); this.addIosPalette(); this.addKubernetesPalette(); this.addMockupPalette(); this.addSitemapPalette(); this.addSalesforcePalette(); this.addUml25Palette(); this.addUmlPalette(false); this.addAlibabaCloudPalette(); this.addAlliedTelesisPalette(); this.addAWS3Palette(); this.addAWS4bPalette(); this.addAWS4Palette(); this.addAWS3DPalette(); this.addAzurePalette(); this.addAzure2Palette(); this.addMSCAEPalette(); this.addC4Palette(); this.addCiscoPalette(cisco, dir); this.addCisco19Palette(); this.addCiscoSafePalette(); this.addCumulusPalette(); this.addCitrix2Palette(); this.addCitrixPalette(); this.addDynamics365Palette(); this.addGCP3Palette(); this.addGCP2Palette(); this.addGCPIconsPalette(); this.addIBMPalette(); this.addIBMCloudPalette(); this.addNetwork2Palette(); this.addNetworkPalette(); this.addSAPPalette(); this.addOfficePalette(); this.addOpenStackPalette(); this.addRackPalette(rack, dir); this.addVeeamPalette(); this.addVeeam2Palette(); this.addVVDPalette(); this.addArchimate4Palette(); this.addArchimate3Palette(); this.addArchiMatePalette(); this.addBpmn2Palette(); this.addSysMLPalette(sysml, dir); this.addLeanMappingPalette(); this.addCabinetsPalette(); this.addInfographicPalette(); this.addEipPalette(); this.addElectricalPalette(); this.addFloorplanPalette(); this.addFluidPowerPalette(); this.addGMDLPalette(); this.addPidPalette(pids, dir); this.addThreatModelingPalette(); this.addWebIconsPalette(); this.addWebLogosPalette(); this.addSignsPalette(signs, dir); if (this.appendCustomLibraries) { this.addCustomEntries(); } // LATER: Check if conflicts with restore libs after loading file this.showEntries(); if (this.createdSearchIndex != null) { console.log('searchFileData', Graph.compress(JSON.stringify(this.createdSearchIndex))); } }; /** * Adds custom entries to the sidebar. */ Sidebar.prototype.addCustomEntries = function() { if (this.customEntries != null) { var preloadCount = 0; for (var i = 0; i < this.customEntries.length; i++) { var section = this.customEntries[i] || {}; for (var j = 0; section.entries != null && j < section.entries.length; j++) { var entry = section.entries[j] || {}; for (var k = 0; entry.libs != null && k < entry.libs.length; k++) { (mxUtils.bind(this, function(lib) { var data = null; var error = null; var content = null; var title = null; var showError = mxUtils.bind(this, function(err, c) { var div = document.createElement('span'); div.style.paddingBottom = '6px'; div.style.paddingTop = '6px'; div.style.fontSize = '11px'; mxUtils.write(div, err); c.innerHTML = '<img align="top" src="' + mxGraph.prototype.warningImage.src + '"/> '; c.appendChild(div); }); var barrier = mxUtils.bind(this, function() { if (content != null && title != null) { if (error != null) { content.style.display = 'block'; title.innerText = ''; mxUtils.write(title, this.editorUi.getResource(lib.title)); showError(error, content); } else if (data != null) { this.editorUi.addLibraryEntries(data, content); content.style.display = 'block'; title.innerText = ''; mxUtils.write(title, this.editorUi.getResource(lib.title)); } else { content.style.display = 'none'; title.innerText = ''; mxUtils.write(title, mxResources.get('loading') + '...'); } } }); if (lib.data == null && lib.url != null && (!lib.preload && preloadCount >= this.maxPreloadCount)) { this.addPalette(entry.id + '.' + k, this.editorUi.getResource(lib.title), false, mxUtils.bind(this, function(content, title) { var dataLoaded = mxUtils.bind(this, function(images, defaultTags) { this.setCurrentSearchEntryLibrary(entry.id, entry.id + '.' + k); this.addEntries(images, defaultTags); this.setCurrentSearchEntryLibrary(); this.editorUi.addLibraryEntries(images, content); }); content.style.display = 'none'; title.innerText = ''; mxUtils.write(title, mxResources.get('loading') + '...'); var url = lib.url; if (!this.editorUi.editor.isCorsEnabledForUrl(url)) { url = PROXY_URL + '?url=' + encodeURIComponent(url); } this.editorUi.editor.loadUrl(url, mxUtils.bind(this, function(data) { content.style.display = 'block'; title.innerText = ''; mxUtils.write(title, this.editorUi.getResource(lib.title)); try { var doc = mxUtils.parseXml(data); if (doc.documentElement.nodeName == 'mxlibrary') { var images = JSON.parse(mxUtils.getTextContent(doc.documentElement)); dataLoaded(images, doc.documentElement.getAttribute('tags')); } else { showError(mxResources.get('notALibraryFile'), content); } } catch (e) { showError(mxResources.get('error') + ': ' + e.message, content); } })); })); } else { this.addPalette(entry.id + '.' + k, this.editorUi.getResource(lib.title), false, mxUtils.bind(this, function(c, t) { content = c; title = t; barrier(); })); if (lib.data != null) { this.setCurrentSearchEntryLibrary(entry.id, entry.id + '.' + k); this.addEntries(lib.data); this.setCurrentSearchEntryLibrary(); data = lib.data; barrier(); } else if (lib.url != null) { preloadCount++; var url = lib.url; if (!this.editorUi.editor.isCorsEnabledForUrl(url)) { url = PROXY_URL + '?url=' + encodeURIComponent(url); } this.editorUi.editor.loadUrl(url, mxUtils.bind(this, function(temp) { try { var doc = mxUtils.parseXml(temp); if (doc.documentElement.nodeName == 'mxlibrary') { data = JSON.parse(mxUtils.getTextContent(doc.documentElement)); this.addEntries(data, doc.documentElement.getAttribute('tags')); barrier(); } else { error = mxResources.get('notALibraryFile'); barrier(); } } catch (e) { error = mxResources.get('error') + ': ' + e.message; barrier(); } }), mxUtils.bind(this, function(e) { error = (e != null && e.message != null) ? e.message : e; barrier(); })); } else { error = mxResources.get('invalidInput'); barrier(); } } }))(entry.libs[k]); } } } } }; /** * Overridden to manually create search index for stencil files which are not pre-loaded * and no entries are created programmatically. */ if (urlParams['createindex'] == '1') { var sidebarAddStencilPalette = Sidebar.prototype.addStencilPalette; Sidebar.prototype.addStencilPalette = function(id, title, stencilFile, style, ignore, onInit, scale, tags, customFns, groupId) { sidebarAddStencilPalette.apply(this, arguments); scale = (scale != null) ? scale : 1; // Used for creating index mxStencilRegistry.loadStencilSet(stencilFile, mxUtils.bind(this, function(packageName, stencilName, displayName, w, h) { if (this.createdSearchIndex != null && ignore == null || mxUtils.indexOf(ignore, stencilName) < 0) { var entry = {style: 'shape=' + packageName + stencilName + style, w: Math.round(w * scale), h: Math.round(h * scale)}; var tmpTags = (tags != null) ? tags[stencilName] : null; if (tmpTags != null) { entry.tags = tmpTags; } if (groupId != null) { entry.id = groupId; entry.lib = id; } this.createdSearchIndex.push(entry); } }), true); }; } /** * Extracs icons from the search result. Entries with a data property * (server-side inlining, see Editor.inlineExtIcons) produce * self-contained cells; others reference the remote image URL. */ Sidebar.prototype.extractIconsFromResponse = function(res, results) { for (var i = 0; i < res.images.length; i++) { (mxUtils.bind(this, function(img) { results.push(mxUtils.bind(this, function() { return this.createVertexTemplate('shape=image;html=1;verticalAlign=top;' + 'verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;imageAspect=0;' + 'aspect=fixed;image=' + ((img.data != null) ? img.data : img.url), img.width, img.height, ''); })); }))(res.images[i]); } }; /** * Returns true if the grouped icon service (ICON_SERVICE_PATH) is * available. Falls back to the flat ICONSEARCH_PATH protocol otherwise. */ Sidebar.prototype.isIconServiceEnabled = function() { return typeof ICON_SERVICE_PATH !== 'undefined' && ICON_SERVICE_PATH != null; }; /** * Returns the request URL for an icon search page in the active * protocol. */ Sidebar.prototype.getIconSearchUrl = function(searchTerms, count, page) { return (this.isIconServiceEnabled() ? ICON_SERVICE_PATH + '/search' : ICONSEARCH_PATH) + '?q=' + encodeURIComponent(searchTerms) + '&p=' + page + '&c=' + count + ((this.isIconServiceEnabled() && Editor.inlineExtIcons) ? '&inline=1' : ''); }; /** * Maximum number of chips above the search results (matching built-in * libraries and icon service sets combined). */ Sidebar.prototype.maxSearchResultChips = 4; /** * Search tokens that never select a built-in library chip on their own * (they would prefix-match unrelated titles, eg. "and" - Android, * "off" - Office). */ Sidebar.prototype.libraryChipStopwords = ['and', 'the', 'for', 'with', 'off', 'from']; /** * More Shapes entries that are never offered as chips - superseded * versions where only the latest library of a family should be * suggested (all remain available via More Shapes and search). */ Sidebar.prototype.libraryChipExcluded = ['uml', 'aws3', 'aws4b', 'cisco', 'network', 'citrix', 'gcp2', 'archimate']; /** * Search ranking weights for superseded libraries - on equal search * scores, shapes from the latest library of a family rank above the * ones from its predecessors (eg. "aws" lists AWS 2026 shapes before * AWS18 and AWS17), independent of the order in which the palettes * were added to the search index. Only breaks ties, so a demoted * shape that matches the search terms better still ranks first. The * default UML library is not demoted as it is enabled by default. */ Sidebar.prototype.librarySearchWeights = {aws3: -2, aws4b: -1, azure: -1, cisco: -1, citrix: -1, gcp2: -1, network: -1, veeam: -1, archimate: -1}; /** * Stores the icon sets returned for the given search terms and shows * them as chips if the results header has already been rendered (the * sets can arrive before or after the header depending on whether the * icon request blocked the search callback). */ Sidebar.prototype.setIconSearchSets = function(terms, sets) { this.lastIconSearchSets = {terms: terms, sets: (sets != null) ? sets : []}; if (this.iconSearchSetsContainer != null && this.iconSearchSetsTerms == terms) { this.populateIconSearchSets(this.iconSearchSetsContainer, this.lastIconSearchSets.sets); } }; /** * Returns the More Shapes entries (built-in libraries) that match the * given search terms and are not currently shown in the sidebar. */ Sidebar.prototype.getMatchingLibraryChips = function(searchTerms) { var chips = []; if (this.entries != null && searchTerms != null) { var tokens = []; var temp = searchTerms.toLowerCase().split(/[^a-z0-9]+/); for (var i = 0; i < temp.length; i++) { if (temp[i].length > 1 && mxUtils.indexOf( this.libraryChipStopwords, temp[i]) < 0) { tokens.push(temp[i]); } } if (tokens.length > 0) { for (var i = 0; i < this.entries.length; i++) { var section = this.entries[i]; for (var j = 0; section.entries != null && j < section.entries.length; j++) { var entry = section.entries[j]; if (chips.length < this.maxSearchResultChips && entry.id != 'search' && entry.id != '.scratchpad' && mxUtils.indexOf(this.libraryChipExcluded, entry.id) < 0 && this.getConfigurationById(entry.id) != null && !this.isEntryVisible(entry.id) && this.isLibraryChipMatch(entry, tokens)) { chips.push(entry); } } } } } return chips; }; /** * Returns true if a search token matches a word of the entry title or * ID - exact for short tokens, prefix in either direction otherwise * (eg. "uml" - "UML 2.5"/uml25, "networks" - "Network 2025"). */ Sidebar.prototype.isLibraryChipMatch = function(entry, tokens) { var words = (entry.title + ' ' + entry.id).toLowerCase().split(/[^a-z0-9]+/); for (var i = 0; i < tokens.length; i++) { for (var j = 0; j < words.length; j++) { var t = tokens[i]; var w = words[j]; if (w.length > 1 && (w == t || (t.length > 2 && w.substring(0, t.length) == t) || (w.length > 2 && t.substring(0, w.length) == w))) { return true; } } } return false; }; /** * Shows the given More Shapes entry in the sidebar, persists the change * like the More Shapes dialog and scrolls to its first palette. */ Sidebar.prototype.enableLibraryEntry = function(id) { var libs = ['search']; for (var i = 0; i < this.configuration.length; i++) { var entryId = this.configuration[i].id; if (entryId != 'search' && this.isEntryVisible(entryId)) { libs.push(entryId); } } if (this.customEntries != null) { for (var i = 0; i < this.customEntries.length; i++) { var section = this.customEntries[i] || {}; for (var j = 0; section.entries != null && j < section.entries.length; j++) { if (this.isEntryVisible(section.entries[j].id)) { libs.push(section.entries[j].id); } } } } if (mxUtils.indexOf(libs, id) < 0) { libs.push(id); } this.showEntries(libs.join(';'), isLocalStorage); // Scrolls to the first palette of the entry var config = this.getConfigurationById(id); var lib = (config != null && config.libs != null && config.libs.length > 0) ? ((config.prefix != null ? config.prefix : '') + config.libs[0]) : id; this.openLibraries([{id: id, lib: lib}]); }; /** * Creates a chip for the search results header. */ Sidebar.prototype.createSearchResultChip = function(label, tooltip) { var dark = Editor.isDarkMode(); var chip = document.createElement('div'); chip.setAttribute('title', tooltip); chip.style.cssText = 'display:inline-block;max-width:96%;overflow:hidden;' + 'text-overflow:ellipsis;white-space:nowrap;cursor:pointer;' + 'font-size:11px;padding:2px 8px;margin:0 4px 4px 0;' + 'border-radius:10px;border:1px solid ' + (dark ? '#505759' : '#d0d0d0') + ';background:' + (dark ? 'rgba(255,255,255,0.06)' : '#f5f5f5') + ';'; mxUtils.write(chip, '+ ' + label); return chip; }; /** * Returns true if the given icon set's generated library is already in * the sidebar (mirrors the ID normalization and duplicate check in * App.loadLibraries, including pending loads). */ Sidebar.prototype.isIconSetInstalled = function(set) { try { var id = encodeURIComponent(decodeURIComponent('U' + set.libraryUrl)); return (this.editorUi.loadedLibraries != null && this.editorUi.loadedLibraries[id] != null) || this.palettes[id] != null; } catch (e) { // ignore } return false; }; /** * Renders the chip row above the search results: matching built-in * libraries first (shown in the sidebar on click), then the matching * icon sets (installed as custom libraries via the existing plumbing, * persisted in settings, removable via the palette). Sets whose library * is already installed are skipped. The total is capped at * maxSearchResultChips so the chips do not overwhelm the actual results. */ Sidebar.prototype.populateIconSearchSets = function(container, sets) { container.innerHTML = ''; var entries = (this.iconSearchLibraryChips != null) ? this.iconSearchLibraryChips : []; sets = (sets != null && this.editorUi.loadLibraries != null) ? sets : []; var temp = []; for (var i = 0; i < sets.length; i++) { if (!this.isIconSetInstalled(sets[i])) { temp.push(sets[i]); } } sets = temp; var max = this.maxSearchResultChips; if (entries.length == 0 && sets.length == 0) { container.style.display = 'none'; return; } container.style.display = 'block'; for (var i = 0; i < entries.length && i < max; i++) { (mxUtils.bind(this, function(entry) { var chip = this.createSearchResultChip(entry.title, mxResources.get('openLibrary') + ': ' + entry.title); // Re-rendered after a click when the icon sets arrive if (this.isEntryVisible(entry.id)) { chip.style.opacity = '0.5'; chip.style.pointerEvents = 'none'; } mxEvent.addListener(chip, 'click', mxUtils.bind(this, function(evt) { this.enableLibraryEntry(entry.id); chip.style.opacity = '0.5'; chip.style.pointerEvents = 'none'; mxEvent.consume(evt); })); container.appendChild(chip); }))(entries[i]); } for (var i = 0; i < sets.length && entries.length + i < max; i++) { (mxUtils.bind(this, function(set) { var chip = this.createSearchResultChip(set.name + ' (' + set.iconCount + ')', mxResources.get('addToLibrary') + ': ' + set.name + ' (' + set.iconCount + ')'); mxEvent.addListener(chip, 'click', mxUtils.bind(this, function(evt) { this.editorUi.loadLibraries(['U' + set.libraryUrl]); chip.style.opacity = '0.5'; chip.style.pointerEvents = 'none'; mxEvent.consume(evt); })); container.appendChild(chip); }))(sets[i]); } }; /** * Adds the chip row above the first page of search results. Built-in * library matches are rendered immediately, matching icon sets are * added when the service response arrives. */ Sidebar.prototype.insertSearchResultsHeader = function(div, searchTerm, page) { if (page == 1) { var chips = this.getMatchingLibraryChips(searchTerm); if (chips.length > 0 || (this.isIconServiceEnabled() && !this.editorUi.isOffline())) { var container = document.createElement('div'); container.style.display = 'none'; container.style.width = '100%'; div.appendChild(container); this.iconSearchSetsContainer = container; this.iconSearchSetsTerms = searchTerm; this.iconSearchLibraryChips = chips; if (this.lastIconSearchSets != null && this.lastIconSearchSets.terms == searchTerm) { this.populateIconSearchSets(container, this.lastIconSearchSets.sets); } else if (chips.length > 0) { this.populateIconSearchSets(container, null); } } } }; /** * Fetches the icon sets for the given terms without adding icon results * (used when the local shape results are plentiful so no icons are * appended, but matching sets should still be offered). */ Sidebar.prototype.fetchIconSearchSets = function(searchTerms) { mxUtils.get(this.getIconSearchUrl(searchTerms, 8, 0), mxUtils.bind(this, function(req) { try { if (req.getStatus() >= 200 && req.getStatus() <= 299 && req.getText() != null && req.getText().length > 0) { var res = JSON.parse(req.getText()); if (res != null && res.sets != null) { this.setIconSearchSets(searchTerms, res.sets); } } } catch (e) { // ignore - chips are optional } }), function() { // ignore - chips are optional }); }; /** * Queries the icon provider for an image-only search and returns the * results via the given callback. Used by the "Search Images" omnibox * option. Pagination is via the 0-based page (p=page); the provider * uses a fixed page size and ignores the requested count, so "more * results" is signalled by a non-empty page (paging stops when a page * comes back empty) rather than by a full page (== count). */ Sidebar.prototype.searchIcons = function(searchTerms, count, page, success) { var results = []; mxUtils.get(this.getIconSearchUrl(searchTerms, count, page), mxUtils.bind(this, function(req) { try { // Ignores response if nothing or error returned if (req.getStatus() >= 200 && req.getStatus() <= 299 && req.getText() != null && req.getText().length > 0) { var res = JSON.parse(req.getText()); if (res == null || res.images == null) { success(results, page * count, false, searchTerms); this.editorUi.handleError(res); } else { if (res.sets != null) { this.setIconSearchSets(searchTerms, res.sets); } this.extractIconsFromResponse(res, results); success(results, page * count + results.length, results.length > 0, searchTerms); } } else { success(results, page * count, false, searchTerms); } } catch (e) { success(results, page * count, false, searchTerms); this.editorUi.handleError(e); } }), mxUtils.bind(this, function() { success(results, page * count, false, searchTerms); })); }; /** * Image search is available when an icon provider is configured and the * editor is online (mirrors the icon results appended to shape search). */ Sidebar.prototype.isImageSearchSupported = function() { return (this.isIconServiceEnabled() || ICONSEARCH_PATH != null) && !this.editorUi.isOffline(); }; /** * Returns true if the search index was loaded. */ Sidebar.prototype.isSearchIndexLoaded = function() { return this.searchIndexData == null; } /** * Lazy-loading for search index. */ Sidebar.prototype.updateSearchIndex = function() { if (this.searchIndexData != null) { this.addSearchIndex(JSON.parse(Graph.decompress(this.searchIndexData))); this.searchIndexData = null; } }; /** * Adds server icon results to local search results */ var sidebarSearchEntries = Sidebar.prototype.searchEntries; Sidebar.prototype.searchEntries = function(searchTerms, count, page, success, error, searchClosedLibraries, imagesOnly) { var succ = success; this.updateSearchIndex(); // Image-only search bypasses the local shape index and queries the // icon provider directly (the "Search Images" omnibox option). if (imagesOnly) { if (this.isImageSearchSupported()) { this.searchIcons(searchTerms, count, page, success); } else { success([], 0, false, searchTerms); } return; } if ((this.isIconServiceEnabled() || ICONSEARCH_PATH != null) && searchClosedLibraries) { success = mxUtils.bind(this, function(results, len, more, terms) { if (!this.editorUi.isOffline() && results.length <= count / 4) { var pg = page - Math.ceil((len - count / 4) / count); mxUtils.get(this.getIconSearchUrl(searchTerms, count, pg), mxUtils.bind(this, function(req) { try { // Ignore response if nothing or error returned if (req.getStatus() >= 200 && req.getStatus() <= 299 && req.getText() != null && req.getText().length > 0) { try { var res = JSON.parse(req.getText()); if (res == null || res.images == null) { succ(results, len, false, terms); this.editorUi.handleError(res); } else { if (res.sets != null) { this.setIconSearchSets(searchTerms, res.sets); } this.extractIconsFromResponse(res, results); succ(results, (page - 1) * count + results.length, res.images.length == count, terms); } } catch (e) { succ(results, len, false, terms); this.editorUi.handleError(e); } } else { succ(results, len, false, terms); } } catch (e) { succ(results, len, false, terms); this.editorUi.handleError(e); } }, function() { succ(results, len, false, terms); })); } else { // No icon results are appended, but matching sets are // still offered as chips on the first page. if (page == 0 && this.isIconServiceEnabled() && !this.editorUi.isOffline()) { this.fetchIconSearchSets(searchTerms); } succ(results, len, more || !this.editorUi.isOffline(), terms); } }); } sidebarSearchEntries.apply(this, arguments); }; // Fixes sidebar tooltips (previews) var sidebarGetTooltipOffset = Sidebar.prototype.getTooltipOffset; Sidebar.prototype.getTooltipOffset = function(elt, bounds) { if (Editor.currentTheme == 'simple' || Editor.currentTheme == 'sketch' || Editor.currentTheme == 'min') { if (mxUtils.isAncestorNode(this.editorUi.sketchPickerMenuElt, elt)) { var off = mxUtils.getOffset(elt); if (Editor.currentTheme == 'simple') { off.x += (elt.offsetWidth - bounds.width) / 2 - 14; off.y = elt.parentNode.offsetHeight - 2; } else { off.x = elt.parentNode.offsetLeft + elt.parentNode.offsetWidth + 2; off.y += (elt.offsetHeight - bounds.height) / 2; } return new mxPoint(Math.max(0, off.x), Math.max(0, off.y)); } else if (this.editorUi.sidebarWindow != null) { var off = mxUtils.getOffset(this.editorUi.sidebarWindow.window.div); off.x += this.editorUi.sidebarWindow.window.div.offsetWidth + 2; off.y += elt.offsetTop + (elt.offsetHeight - bounds.height) / 2; if (elt.offsetParent != null) { off.y -= elt.offsetParent.scrollTop; } return new mxPoint(Math.max(0, off.x), Math.max(0, off.y)); } } return sidebarGetTooltipOffset.apply(this, arguments); }; /** * Adds a click handler for inserting the cell as target for dangling edge. */ var sidebarItemClicked = Sidebar.prototype.itemClicked; Sidebar.prototype.itemClicked = function(cells, ds, evt) { var graph = this.editorUi.editor.graph; var handled = false; if (cells != null && graph.getSelectionCount() == 1 && graph.getModel().isVertex(cells[0])) { var target = graph.cloneCell(cells[0]); // Inserts cell as target of selected edge if not connected if (graph.getModel().isEdge(graph.getSelectionCell()) && graph.getModel().getTerminal(graph.getSelectionCell(), false) == null && graph.getModel().isVertex(target)) { graph.getModel().beginUpdate(); try { var edgeState = graph.view.getState(graph.getSelectionCell()); if (edgeState != null) { var tr = graph.view.translate; var s = graph.view.scale; var pt = edgeState.absolutePoints[edgeState.absolutePoints.length - 1]; target.geometry.x = pt.x / s - tr.x - target.geometry.width / 2; target.geometry.y = pt.y / s - tr.y - target.geometry.height / 2; } graph.addCell(target); graph.getModel().setTerminal(graph.getSelectionCell(), target, false); if (evt == null || !mxEvent.isShiftDown(evt)) { graph.fireEvent(new mxEventObject('cellsInserted', 'cells', [target])); } } catch (e) { this.editorUi.handleError(e); } finally { graph.getModel().endUpdate(); } graph.scrollCellToVisible(target); graph.setSelectionCell(target); handled = true; } } if (!handled) { sidebarItemClicked.apply(this, arguments); } }; })();