1/** 2 * Copyright (c) 2006-2020, JGraph Ltd 3 * Copyright (c) 2006-2020, draw.io AG 4 */ 5(function() 6{ 7 // Adds scrollbars for menus that exceed the page height 8 var mxPopupMenuShowMenu = mxPopupMenu.prototype.showMenu; 9 mxPopupMenu.prototype.showMenu = function() 10 { 11 mxPopupMenuShowMenu.apply(this, arguments); 12 13 this.div.style.overflowY = 'auto'; 14 this.div.style.overflowX = 'hidden'; 15 var h0 = Math.max(document.body.clientHeight, document.documentElement.clientHeight); 16 this.div.style.maxHeight = (h0 - 10) + 'px'; 17 }; 18 19 Menus.prototype.createHelpLink = function(href) 20 { 21 var link = document.createElement('span'); 22 link.setAttribute('title', mxResources.get('help')); 23 link.style.cssText = 'color:blue;text-decoration:underline;margin-left:8px;cursor:help;'; 24 25 var icon = document.createElement('img'); 26 mxUtils.setOpacity(icon, 50); 27 icon.style.height = '16px'; 28 icon.style.width = '16px'; 29 icon.setAttribute('border', '0'); 30 icon.setAttribute('valign', 'bottom'); 31 icon.setAttribute('src', Editor.helpImage); 32 link.appendChild(icon); 33 34 mxEvent.addGestureListeners(link, mxUtils.bind(this, function(evt) 35 { 36 this.editorUi.hideCurrentMenu(); 37 this.editorUi.openLink(href); 38 mxEvent.consume(evt); 39 })); 40 41 return link; 42 }; 43 44 Menus.prototype.addLinkToItem = function(item, href) 45 { 46 if (item != null) 47 { 48 item.firstChild.nextSibling.appendChild(this.createHelpLink(href)); 49 } 50 }; 51 52 var menusInit = Menus.prototype.init; 53 Menus.prototype.init = function() 54 { 55 menusInit.apply(this, arguments); 56 var editorUi = this.editorUi; 57 var graph = editorUi.editor.graph; 58 var isGraphEnabled = mxUtils.bind(graph, graph.isEnabled); 59 var googleEnabled = ((urlParams['embed'] != '1' && urlParams['gapi'] != '0') || 60 (urlParams['embed'] == '1' && urlParams['gapi'] == '1')) && mxClient.IS_SVG && 61 isLocalStorage && (document.documentMode == null || document.documentMode >= 10); 62 var dropboxEnabled = ((urlParams['embed'] != '1' && urlParams['db'] != '0') || (urlParams['embed'] == '1' && urlParams['db'] == '1')) && 63 mxClient.IS_SVG && (document.documentMode == null || document.documentMode > 9); 64 var oneDriveEnabled = (window.location.hostname == 'www.draw.io' || window.location.hostname == 'test.draw.io' || 65 window.location.hostname == 'drive.draw.io' || window.location.hostname == 'app.diagrams.net') && 66 (((urlParams['embed'] != '1' && urlParams['od'] != '0') || (urlParams['embed'] == '1' && urlParams['od'] == '1')) && 67 !mxClient.IS_IOS && (navigator.userAgent.indexOf('MSIE') < 0 || document.documentMode >= 10)); 68 var trelloEnabled = urlParams['tr'] == '1' && mxClient.IS_SVG && (document.documentMode == null || 69 document.documentMode > 9); 70 71 if (!mxClient.IS_SVG && !editorUi.isOffline()) 72 { 73 var img = new Image(); 74 img.src = IMAGE_PATH + '/help.png'; 75 } 76 77 if (urlParams['noFileMenu'] == '1') 78 { 79 this.defaultMenuItems = this.defaultMenuItems.filter(function(m) 80 { 81 return m != 'file'; 82 }); 83 } 84 85 editorUi.actions.addAction('new...', function() 86 { 87 var compact = editorUi.isOffline(); 88 89 if (!compact && urlParams['newTempDlg'] == '1' && editorUi.mode == App.MODE_GOOGLE) 90 { 91 function driveObjToTempDlg(item) 92 { 93 return {id: item.id, isExt: true, url: item.downloadUrl, title: item.title, imgUrl: item.thumbnailLink, 94 changedBy: item.lastModifyingUserName, lastModifiedOn: item.modifiedDate} 95 }; 96 97 var tempDlg = new TemplatesDialog(editorUi, function(templateXml, title, infoObj) 98 { 99 var templateLibs = infoObj.libs, templateClibs = infoObj.clibs; 100 101 editorUi.pickFolder(editorUi.mode, function(folderId) 102 { 103 editorUi.createFile(title, templateXml, (templateLibs != null && 104 templateLibs.length > 0) ? templateLibs : null, null, function() 105 { 106 editorUi.hideDialog(); 107 }, null, folderId, null, (templateClibs != null && 108 templateClibs.length > 0) ? templateClibs : null); 109 }, editorUi.stateArg == null || 110 editorUi.stateArg.folderId == null); 111 112 }, null, null, null, 'user', function(callback, error, username) 113 { 114 var oneWeek = new Date(); 115 oneWeek.setDate(oneWeek.getDate() - 7); 116 117 editorUi.drive.listFiles(null, oneWeek, username? true : false, function(resp) 118 { 119 var results = []; 120 121 for (var i = 0; i < resp.items.length; i++) 122 { 123 results.push(driveObjToTempDlg(resp.items[i])); 124 } 125 126 callback(results); 127 }, error) 128 }, function(str, callback, error, username) 129 { 130 editorUi.drive.listFiles(str, null, username? true : false, function(resp) 131 { 132 var results = []; 133 134 for (var i = 0; i < resp.items.length; i++) 135 { 136 results.push(driveObjToTempDlg(resp.items[i])); 137 } 138 139 callback(results); 140 }, error) 141 }, function(obj, callback, error) 142 { 143 editorUi.drive.getFile(obj.id, function(file) 144 { 145 callback(file.data); 146 }, error); 147 }, null, null, false, false); 148 149 editorUi.showDialog(tempDlg.container, window.innerWidth, window.innerHeight, true, false, null, false, true); 150 151 return; 152 }; 153 154 var dlg = new NewDialog(editorUi, compact, !(editorUi.mode == App.MODE_DEVICE && 'chooseFileSystemEntries' in window)); 155 156 editorUi.showDialog(dlg.container, (compact) ? 350 : 620, (compact) ? 70 : 460, true, true, function(cancel) 157 { 158 editorUi.sidebar.hideTooltip(); 159 160 if (cancel && editorUi.getCurrentFile() == null) 161 { 162 editorUi.showSplash(); 163 } 164 }); 165 166 dlg.init(); 167 }); 168 169 editorUi.actions.put('insertTemplate', new Action(mxResources.get('template') + '...', function() 170 { 171 if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) 172 { 173 var dlg = new NewDialog(editorUi, null, false, function(xml) 174 { 175 editorUi.hideDialog(); 176 177 if (xml != null) 178 { 179 var insertPoint = editorUi.editor.graph.getFreeInsertPoint(); 180 graph.setSelectionCells(editorUi.importXml(xml, 181 Math.max(insertPoint.x, 20), 182 Math.max(insertPoint.y, 20), 183 true, null, null, true)); 184 graph.scrollCellToVisible(graph.getSelectionCell()); 185 } 186 }, null, null, null, null, null, null, null, null, null, null, 187 false, mxResources.get('insert')); 188 189 editorUi.showDialog(dlg.container, 620, 460, true, true, function() 190 { 191 editorUi.sidebar.hideTooltip(); 192 }); 193 194 dlg.init(); 195 } 196 })).isEnabled = isGraphEnabled; 197 198 var pointAction = editorUi.actions.addAction('points', function() 199 { 200 editorUi.editor.graph.view.setUnit(mxConstants.POINTS); 201 }); 202 203 pointAction.setToggleAction(true); 204 pointAction.setSelectedCallback(function() { return editorUi.editor.graph.view.unit == mxConstants.POINTS; }); 205 206 var inchAction = editorUi.actions.addAction('inches', function() 207 { 208 editorUi.editor.graph.view.setUnit(mxConstants.INCHES); 209 }); 210 211 inchAction.setToggleAction(true); 212 inchAction.setSelectedCallback(function() { return editorUi.editor.graph.view.unit == mxConstants.INCHES; }); 213 214 var mmAction = editorUi.actions.addAction('millimeters', function() 215 { 216 editorUi.editor.graph.view.setUnit(mxConstants.MILLIMETERS); 217 }); 218 219 mmAction.setToggleAction(true); 220 mmAction.setSelectedCallback(function() { return editorUi.editor.graph.view.unit == mxConstants.MILLIMETERS; }); 221 222 var meterAction = editorUi.actions.addAction('meters', function() 223 { 224 editorUi.editor.graph.view.setUnit(mxConstants.METERS); 225 }); 226 227 meterAction.setToggleAction(true); 228 meterAction.setSelectedCallback(function() { return editorUi.editor.graph.view.unit == mxConstants.METERS; }); 229 230 this.put('units', new Menu(mxUtils.bind(this, function(menu, parent) 231 { 232 this.addMenuItems(menu, ['points', 'inches', 'millimeters', 'meters'], parent); 233 }))); 234 235 var rulerAction = editorUi.actions.addAction('ruler', function() 236 { 237 mxSettings.setRulerOn(!mxSettings.isRulerOn()); 238 mxSettings.save(); 239 240 if (editorUi.ruler != null) 241 { 242 editorUi.ruler.destroy(); 243 editorUi.ruler = null; 244 editorUi.refresh(); 245 } 246 else 247 { 248 editorUi.ruler = new mxDualRuler(editorUi, editorUi.editor.graph.view.unit); 249 editorUi.refresh(); 250 } 251 }); 252 rulerAction.setEnabled(editorUi.canvasSupported && document.documentMode != 9); 253 rulerAction.setToggleAction(true); 254 rulerAction.setSelectedCallback(function() { return editorUi.ruler != null; }); 255 256 var fullscreenAction = editorUi.actions.addAction('fullscreen', function() 257 { 258 if (urlParams['embedInline'] == '1') 259 { 260 editorUi.setInlineFullscreen(!Editor.inlineFullscreen); 261 } 262 else 263 { 264 if (document.fullscreenElement == null) 265 { 266 document.body.requestFullscreen(); 267 } 268 else 269 { 270 document.exitFullscreen(); 271 } 272 } 273 }); 274 fullscreenAction.visible = urlParams['embedInline'] == '1' || 275 (document.fullscreenEnabled && document.body.requestFullscreen != null); 276 fullscreenAction.setToggleAction(true); 277 fullscreenAction.setSelectedCallback(function() 278 { 279 return urlParams['embedInline'] == '1' ? 280 Editor.inlineFullscreen : 281 document.fullscreenElement != null; 282 }); 283 284 editorUi.actions.addAction('properties...', function() 285 { 286 var dlg = new FilePropertiesDialog(editorUi); 287 editorUi.showDialog(dlg.container, 320, 120, true, true); 288 dlg.init(); 289 }).isEnabled = isGraphEnabled; 290 291 if (window.mxFreehand) 292 { 293 editorUi.actions.put('insertFreehand', new Action(mxResources.get('freehand') + '...', function(evt) 294 { 295 if (graph.isEnabled()) 296 { 297 if (this.freehandWindow == null) 298 { 299 this.freehandWindow = new FreehandWindow(editorUi, document.body.offsetWidth - 420, 102, 176, 84); 300 } 301 302 if (graph.freehand.isDrawing()) 303 { 304 graph.freehand.stopDrawing(); 305 } 306 else 307 { 308 graph.freehand.startDrawing(); 309 } 310 311 this.freehandWindow.window.setVisible(graph.freehand.isDrawing()); 312 } 313 })).isEnabled = function() 314 { 315 return isGraphEnabled() && mxClient.IS_SVG; 316 }; 317 } 318 319 editorUi.actions.put('exportXml', new Action(mxResources.get('formatXml') + '...', function() 320 { 321 var div = document.createElement('div'); 322 div.style.whiteSpace = 'nowrap'; 323 var noPages = editorUi.pages == null || editorUi.pages.length <= 1; 324 325 var hd = document.createElement('h3'); 326 mxUtils.write(hd, mxResources.get('formatXml')); 327 hd.style.cssText = 'width:100%;text-align:center;margin-top:0px;margin-bottom:4px'; 328 div.appendChild(hd); 329 330 var selection = editorUi.addCheckbox(div, mxResources.get('selectionOnly'), 331 false, graph.isSelectionEmpty()); 332 var compressed = editorUi.addCheckbox(div, mxResources.get('compressed'), true); 333 var pages = editorUi.addCheckbox(div, mxResources.get('allPages'), !noPages, noPages); 334 pages.style.marginBottom = '16px'; 335 336 mxEvent.addListener(selection, 'change', function() 337 { 338 if (selection.checked) 339 { 340 pages.setAttribute('disabled', 'disabled'); 341 } 342 else 343 { 344 pages.removeAttribute('disabled'); 345 } 346 }); 347 348 var dlg = new CustomDialog(editorUi, div, mxUtils.bind(this, function() 349 { 350 editorUi.downloadFile('xml', !compressed.checked, null, 351 !selection.checked, noPages || !pages.checked); 352 }), null, mxResources.get('export')); 353 354 editorUi.showDialog(dlg.container, 300, 200, true, true); 355 })); 356 357 if (Editor.enableExportUrl) 358 { 359 editorUi.actions.put('exportUrl', new Action(mxResources.get('url') + '...', function() 360 { 361 editorUi.showPublishLinkDialog(mxResources.get('url'), true, null, null, 362 function(linkTarget, linkColor, allPages, lightbox, editLink, layers, width, height, tags) 363 { 364 var params = []; 365 366 if (tags) 367 { 368 params.push('tags=%7B%7D'); 369 } 370 371 var dlg = new EmbedDialog(editorUi, editorUi.createLink(linkTarget, linkColor, 372 allPages, lightbox, editLink, layers, null, true, params)); 373 editorUi.showDialog(dlg.container, 450, 240, true, true); 374 dlg.init(); 375 }); 376 })); 377 } 378 379 editorUi.actions.put('exportHtml', new Action(mxResources.get('formatHtmlEmbedded') + '...', function() 380 { 381 if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) 382 { 383 editorUi.getPublicUrl(editorUi.getCurrentFile(), function(url) 384 { 385 editorUi.spinner.stop(); 386 387 editorUi.showHtmlDialog(mxResources.get('export'), null, url, function(publicUrl, zoomEnabled, 388 initialZoom, linkTarget, linkColor, fit, allPages, layers, tags, lightbox, editLink) 389 { 390 editorUi.createHtml(publicUrl, zoomEnabled, initialZoom, linkTarget, linkColor, fit, allPages, 391 layers, tags, lightbox, editLink, mxUtils.bind(this, function(html, scriptTag) 392 { 393 var basename = editorUi.getBaseFilename(allPages); 394 var result = '<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=5,IE=9" ><![endif]-->\n' + 395 '<!DOCTYPE html>\n<html>\n<head>\n<title>' + mxUtils.htmlEntities(basename) + '</title>\n' + 396 '<meta charset="utf-8"/>\n</head>\n<body>' + html + '\n' + scriptTag + '\n</body>\n</html>'; 397 editorUi.saveData(basename + ((basename.substring(basename.lenth - 7) == 398 '.drawio') ? '' : '.drawio') + '.html', 'html', result, 'text/html'); 399 })); 400 }); 401 }); 402 } 403 })); 404 405 editorUi.actions.put('exportPdf', new Action(mxResources.get('formatPdf') + '...', function() 406 { 407 if (!EditorUi.isElectronApp && (editorUi.isOffline() || editorUi.printPdfExport)) 408 { 409 // Export PDF action for chrome OS (same as print with different dialog title) 410 editorUi.showDialog(new PrintDialog(editorUi, mxResources.get('formatPdf')).container, 360, 411 (editorUi.pages != null && editorUi.pages.length > 1 && (editorUi.editor.editable || 412 urlParams['hide-pages'] != '1')) ? 413 450 : 370, true, true); 414 } 415 else 416 { 417 var noPages = editorUi.pages == null || editorUi.pages.length <= 1; 418 var div = document.createElement('div'); 419 div.style.whiteSpace = 'nowrap'; 420 421 var hd = document.createElement('h3'); 422 mxUtils.write(hd, mxResources.get('formatPdf')); 423 hd.style.cssText = 'width:100%;text-align:center;margin-top:0px;margin-bottom:4px'; 424 div.appendChild(hd); 425 426 var cropEnableFn = function() 427 { 428 if (allPages != this && this.checked) 429 { 430 crop.removeAttribute('disabled'); 431 crop.checked = !graph.pageVisible; 432 } 433 else 434 { 435 crop.setAttribute('disabled', 'disabled'); 436 crop.checked = false; 437 } 438 }; 439 440 var dlgH = 200; 441 var pageCount = 1; 442 var currentPage = null; 443 444 if (editorUi.pdfPageExport && !noPages) 445 { 446 var allPages = editorUi.addRadiobox(div, 'pages', mxResources.get('allPages'), true); 447 var pagesRadio = editorUi.addRadiobox(div, 'pages', mxResources.get('pages') + ':', false, null, true); 448 449 var pagesFromInput = document.createElement('input'); 450 pagesFromInput.style.cssText = 'margin:0 8px 0 8px;' 451 pagesFromInput.setAttribute('value', '1'); 452 pagesFromInput.setAttribute('type', 'number'); 453 pagesFromInput.setAttribute('min', '1'); 454 pagesFromInput.style.width = '50px'; 455 div.appendChild(pagesFromInput); 456 457 var span = document.createElement('span'); 458 mxUtils.write(span, mxResources.get('to')); 459 div.appendChild(span); 460 461 var pagesToInput = pagesFromInput.cloneNode(true); 462 div.appendChild(pagesToInput); 463 464 mxEvent.addListener(pagesFromInput, 'focus', function() 465 { 466 pagesRadio.checked = true; 467 }); 468 469 mxEvent.addListener(pagesToInput, 'focus', function() 470 { 471 pagesRadio.checked = true; 472 }); 473 474 function validatePageRange() 475 { 476 pagesToInput.value = Math.max(1, Math.min(pageCount, Math.max(parseInt(pagesToInput.value), parseInt(pagesFromInput.value)))); 477 pagesFromInput.value = Math.max(1, Math.min(pageCount, Math.min(parseInt(pagesToInput.value), parseInt(pagesFromInput.value)))); 478 }; 479 480 mxEvent.addListener(pagesFromInput, 'change', validatePageRange); 481 mxEvent.addListener(pagesToInput, 'change', validatePageRange); 482 483 if (editorUi.pages != null) 484 { 485 pageCount = editorUi.pages.length; 486 487 if (editorUi.currentPage != null) 488 { 489 for (var i = 0; i < editorUi.pages.length; i++) 490 { 491 if (editorUi.currentPage == editorUi.pages[i]) 492 { 493 currentPage = i + 1; 494 pagesFromInput.value = currentPage; 495 pagesToInput.value = currentPage; 496 break; 497 } 498 } 499 } 500 } 501 502 pagesFromInput.setAttribute('max', pageCount); 503 pagesToInput.setAttribute('max', pageCount); 504 mxUtils.br(div); 505 506 var selection = editorUi.addRadiobox(div, 'pages', mxResources.get('selectionOnly'), false, graph.isSelectionEmpty()); 507 var crop = editorUi.addCheckbox(div, mxResources.get('crop'), false, true); 508 var grid = editorUi.addCheckbox(div, mxResources.get('grid'), false, false); 509 510 mxEvent.addListener(allPages, 'change', cropEnableFn); 511 mxEvent.addListener(pagesRadio, 'change', cropEnableFn); 512 mxEvent.addListener(selection, 'change', cropEnableFn); 513 dlgH += 64; 514 } 515 else 516 { 517 var selection = editorUi.addCheckbox(div, mxResources.get('selectionOnly'), 518 false, graph.isSelectionEmpty()); 519 var crop = editorUi.addCheckbox(div, mxResources.get('crop'), 520 !graph.pageVisible || !editorUi.pdfPageExport, 521 !editorUi.pdfPageExport); 522 var grid = editorUi.addCheckbox(div, mxResources.get('grid'), false, false); 523 524 // Crop is only enabled if selection only is selected 525 if (!editorUi.pdfPageExport) 526 { 527 mxEvent.addListener(selection, 'change', cropEnableFn); 528 } 529 } 530 531 var isDrawioWeb = !mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp && 532 editorUi.getServiceName() == 'draw.io'; 533 534 var transparentBkg = null, include = null; 535 536 if (EditorUi.isElectronApp || isDrawioWeb) 537 { 538 include = editorUi.addCheckbox(div, mxResources.get('includeCopyOfMyDiagram'), 539 Editor.defaultIncludeDiagram); 540 dlgH += 30; 541 } 542 543 if (isDrawioWeb) 544 { 545 transparentBkg = editorUi.addCheckbox(div, 546 mxResources.get('transparentBackground'), false); 547 548 dlgH += 30; 549 } 550 551 var dlg = new CustomDialog(editorUi, div, mxUtils.bind(this, function() 552 { 553 var pageRange = null; 554 555 if (!noPages) 556 { 557 var from = parseInt(pagesFromInput.value); 558 var to = parseInt(pagesToInput.value); 559 pageRange = (!allPages.checked && 560 (from != currentPage || to != currentPage)) ? 561 {from: Math.max(0, Math.min(pageCount - 1, from - 1)), 562 to: Math.max(0, Math.min(pageCount - 1, to - 1))} : null; 563 } 564 565 editorUi.downloadFile('pdf', null, null, !selection.checked, 566 noPages? true : !allPages.checked && pageRange == null, 567 !crop.checked, transparentBkg != null && transparentBkg.checked, null, 568 null, grid.checked, include != null && include.checked, pageRange); 569 }), null, mxResources.get('export')); 570 editorUi.showDialog(dlg.container, 300, dlgH, true, true); 571 } 572 })); 573 574 editorUi.actions.addAction('open...', function() 575 { 576 editorUi.pickFile(); 577 }); 578 579 editorUi.actions.addAction('close', function() 580 { 581 var currentFile = editorUi.getCurrentFile(); 582 583 function fn() 584 { 585 if (currentFile != null) 586 { 587 currentFile.removeDraft(); 588 } 589 590 editorUi.fileLoaded(null); 591 }; 592 593 if (currentFile != null && currentFile.isModified()) 594 { 595 editorUi.confirm(mxResources.get('allChangesLost'), null, fn, 596 mxResources.get('cancel'), mxResources.get('discardChanges')); 597 } 598 else 599 { 600 fn(); 601 } 602 }); 603 604 editorUi.actions.addAction('editShape...', mxUtils.bind(this, function() 605 { 606 var cells = graph.getSelectionCells(); 607 608 if (graph.getSelectionCount() == 1) 609 { 610 var cell = graph.getSelectionCell(); 611 var state = graph.view.getState(cell); 612 613 if (state != null && state.shape != null && state.shape.stencil != null) 614 { 615 var dlg = new EditShapeDialog(editorUi, cell, mxResources.get('editShape') + ':', 630, 400); 616 editorUi.showDialog(dlg.container, 640, 480, true, false); 617 dlg.init(); 618 } 619 } 620 })); 621 622 editorUi.actions.addAction('revisionHistory...', function() 623 { 624 if (!editorUi.isRevisionHistorySupported()) 625 { 626 editorUi.showError(mxResources.get('error'), mxResources.get('notAvailable'), mxResources.get('ok')); 627 } 628 else if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) 629 { 630 editorUi.getRevisions(mxUtils.bind(this, function(revs, restoreFn) 631 { 632 editorUi.spinner.stop(); 633 var dlg = new RevisionDialog(editorUi, revs, restoreFn); 634 editorUi.showDialog(dlg.container, 640, 480, true, true); 635 dlg.init(); 636 }), mxUtils.bind(this, function(err) 637 { 638 editorUi.handleError(err); 639 })); 640 } 641 }); 642 643 editorUi.actions.addAction('createRevision', function() 644 { 645 editorUi.actions.get('save').funct(); 646 }, null, null, Editor.ctrlKey + '+S'); 647 648 var action = editorUi.actions.addAction('synchronize', function() 649 { 650 editorUi.synchronizeCurrentFile(DrawioFile.SYNC == 'none'); 651 }, null, null, 'Alt+Shift+S'); 652 653 // Changes the label if synchronization is disabled 654 if (DrawioFile.SYNC == 'none') 655 { 656 action.label = mxResources.get('refresh'); 657 } 658 659 editorUi.actions.addAction('upload...', function() 660 { 661 var file = editorUi.getCurrentFile(); 662 663 if (file != null) 664 { 665 // Data is pulled from global variable after tab loads 666 // LATER: Change to use message passing to deal with potential cross-domain 667 window.drawdata = editorUi.getFileData(); 668 var filename = (file.getTitle() != null) ? file.getTitle() : editorUi.defaultFilename; 669 editorUi.openLink(window.location.protocol + '//' + window.location.host + '/?create=drawdata&' + 670 ((editorUi.mode == App.MODE_DROPBOX) ? 'mode=dropbox&' : '') + 671 'title=' + encodeURIComponent(filename), null, true); 672 } 673 }); 674 675 if (typeof(MathJax) !== 'undefined') 676 { 677 var action = editorUi.actions.addAction('mathematicalTypesetting', function() 678 { 679 var change = new ChangePageSetup(editorUi); 680 change.ignoreColor = true; 681 change.ignoreImage = true; 682 change.mathEnabled = !editorUi.isMathEnabled(); 683 684 graph.model.execute(change); 685 }); 686 687 action.setToggleAction(true); 688 action.setSelectedCallback(function() { return editorUi.isMathEnabled(); }); 689 action.isEnabled = isGraphEnabled; 690 } 691 692 if (isLocalStorage) 693 { 694 var action = editorUi.actions.addAction('showStartScreen', function() 695 { 696 mxSettings.setShowStartScreen(!mxSettings.getShowStartScreen()); 697 mxSettings.save(); 698 }); 699 700 action.setToggleAction(true); 701 action.setSelectedCallback(function() { return mxSettings.getShowStartScreen(); }); 702 } 703 704 var autosaveAction = editorUi.actions.addAction('autosave', function() 705 { 706 editorUi.editor.setAutosave(!editorUi.editor.autosave); 707 }); 708 709 autosaveAction.setToggleAction(true); 710 autosaveAction.setSelectedCallback(function() 711 { 712 return autosaveAction.isEnabled() && editorUi.editor.autosave; 713 }); 714 715 editorUi.actions.addAction('editGeometry...', function() 716 { 717 var cells = graph.getSelectionCells(); 718 var vertices = []; 719 720 for (var i = 0; i < cells.length; i++) 721 { 722 if (graph.getModel().isVertex(cells[i])) 723 { 724 vertices.push(cells[i]); 725 } 726 } 727 728 if (vertices.length > 0) 729 { 730 var dlg = new EditGeometryDialog(editorUi, vertices); 731 editorUi.showDialog(dlg.container, 200, 270, true, true); 732 dlg.init(); 733 } 734 }, null, null, Editor.ctrlKey + '+Shift+M'); 735 736 var currentStyle = null; 737 738 editorUi.actions.addAction('copyStyle', function() 739 { 740 if (graph.isEnabled() && !graph.isSelectionEmpty()) 741 { 742 currentStyle = graph.copyStyle(graph.getSelectionCell()) 743 } 744 }, null, null, Editor.ctrlKey + '+Shift+C'); 745 746 editorUi.actions.addAction('pasteStyle', function() 747 { 748 if (graph.isEnabled() && !graph.isSelectionEmpty() && currentStyle != null) 749 { 750 graph.pasteStyle(currentStyle, graph.getSelectionCells()) 751 } 752 }, null, null, Editor.ctrlKey + '+Shift+V'); 753 754 editorUi.actions.put('pageBackgroundImage', new Action(mxResources.get('backgroundImage') + '...', function() 755 { 756 if (!editorUi.isOffline()) 757 { 758 var apply = function(image) 759 { 760 editorUi.setBackgroundImage(image); 761 }; 762 763 var dlg = new BackgroundImageDialog(editorUi, apply); 764 editorUi.showDialog(dlg.container, 320, 170, true, true); 765 dlg.init(); 766 } 767 })); 768 769 editorUi.actions.put('exportSvg', new Action(mxResources.get('formatSvg') + '...', function() 770 { 771 editorUi.showExportDialog(mxResources.get('formatSvg'), true, mxResources.get('export'), 772 'https://www.diagrams.net/doc/faq/export-diagram', 773 mxUtils.bind(this, function(scale, transparentBackground, ignoreSelection, 774 addShadow, editable, embedImages, border, cropImage, currentPage, 775 linkTarget, grid, keepTheme, exportType, embedFonts) 776 { 777 var val = parseInt(scale); 778 779 if (!isNaN(val) && val > 0) 780 { 781 editorUi.exportSvg(val / 100, transparentBackground, ignoreSelection, 782 addShadow, editable, embedImages, border, !cropImage, false, 783 linkTarget, keepTheme, exportType, embedFonts); 784 } 785 }), true, null, 'svg', true); 786 })); 787 788 editorUi.actions.put('exportPng', new Action(mxResources.get('formatPng') + '...', function() 789 { 790 if (editorUi.isExportToCanvas()) 791 { 792 editorUi.showExportDialog(mxResources.get('image'), false, mxResources.get('export'), 793 'https://www.diagrams.net/doc/faq/export-diagram', 794 mxUtils.bind(this, function(scale, transparentBackground, ignoreSelection, addShadow, editable, 795 embedImages, border, cropImage, currentPage, dummy, grid, keepTheme, exportType) 796 { 797 var val = parseInt(scale); 798 799 if (!isNaN(val) && val > 0) 800 { 801 editorUi.exportImage(val / 100, transparentBackground, ignoreSelection, 802 addShadow, editable, border, !cropImage, false, null, grid, null, 803 keepTheme, exportType); 804 } 805 }), true, Editor.defaultIncludeDiagram, 'png', true); 806 } 807 else if (!editorUi.isOffline() && (!mxClient.IS_IOS || !navigator.standalone)) 808 { 809 editorUi.showRemoteExportDialog(mxResources.get('export'), null, mxUtils.bind(this, function(ignoreSelection, editable, transparent, scale, border) 810 { 811 editorUi.downloadFile((editable) ? 'xmlpng' : 'png', null, null, ignoreSelection, null, null, transparent, scale, border); 812 }), false, true); 813 } 814 })); 815 816 editorUi.actions.put('exportJpg', new Action(mxResources.get('formatJpg') + '...', function() 817 { 818 if (editorUi.isExportToCanvas()) 819 { 820 editorUi.showExportDialog(mxResources.get('image'), false, mxResources.get('export'), 821 'https://www.diagrams.net/doc/faq/export-diagram', 822 mxUtils.bind(this, function(scale, transparentBackground, ignoreSelection, addShadow, editable, 823 embedImages, border, cropImage, currentPage, dummy, grid, keepTheme, exportType) 824 { 825 var val = parseInt(scale); 826 827 if (!isNaN(val) && val > 0) 828 { 829 editorUi.exportImage(val / 100, false, ignoreSelection, 830 addShadow, false, border, !cropImage, false, 'jpeg', 831 grid, null, keepTheme, exportType); 832 } 833 }), true, false, 'jpeg', true); 834 } 835 else if (!editorUi.isOffline() && (!mxClient.IS_IOS || !navigator.standalone)) 836 { 837 editorUi.showRemoteExportDialog(mxResources.get('export'), null, mxUtils.bind(this, function(ignoreSelection, editable, tranaparent, scale, border) 838 { 839 editorUi.downloadFile('jpeg', null, null, ignoreSelection, null, null, null, scale, border); 840 }), true, true); 841 } 842 })); 843 844 action = editorUi.actions.addAction('copyAsImage', mxUtils.bind(this, function() 845 { 846 var cells = mxUtils.sortCells(graph.model.getTopmostCells(graph.getSelectionCells())); 847 var xml = mxUtils.getXml((cells.length == 0) ? editorUi.editor.getGraphXml() : graph.encodeCells(cells)); 848 editorUi.copyImage(cells, xml); 849 })); 850 851 // Disabled in Safari as operation is not allowed 852 action.visible = Editor.enableNativeCipboard && editorUi.isExportToCanvas() && !mxClient.IS_SF; 853 854 action = editorUi.actions.put('shadowVisible', new Action(mxResources.get('shadow'), function() 855 { 856 graph.setShadowVisible(!graph.shadowVisible); 857 })); 858 action.setToggleAction(true); 859 action.setSelectedCallback(function() { return graph.shadowVisible; }); 860 861 editorUi.actions.put('about', new Action(mxResources.get('about') + ' ' + EditorUi.VERSION + '...', function() 862 { 863 if (editorUi.isOffline() || mxClient.IS_CHROMEAPP || EditorUi.isElectronApp) 864 { 865 editorUi.alert(editorUi.editor.appName + ' ' + EditorUi.VERSION); 866 } 867 else 868 { 869 editorUi.openLink('https://www.diagrams.net/'); 870 } 871 })); 872 873 editorUi.actions.addAction('support...', function() 874 { 875 if (EditorUi.isElectronApp) 876 { 877 editorUi.openLink('https://github.com/jgraph/drawio-desktop/wiki/Getting-Support'); 878 } 879 else 880 { 881 editorUi.openLink('https://github.com/jgraph/drawio/wiki/Getting-Support'); 882 } 883 }); 884 885 editorUi.actions.addAction('exportOptionsDisabled...', function() 886 { 887 editorUi.handleError({message: mxResources.get('exportOptionsDisabledDetails')}, 888 mxResources.get('exportOptionsDisabled')); 889 }); 890 891 editorUi.actions.addAction('keyboardShortcuts...', function() 892 { 893 if (mxClient.IS_SVG && !mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp) 894 { 895 editorUi.openLink('shortcuts.svg'); 896 } 897 else 898 { 899 editorUi.openLink('https://viewer.diagrams.net/#Uhttps%3A%2F%2Fviewer.diagrams.net%2Fshortcuts.svg'); 900 } 901 }); 902 903 editorUi.actions.addAction('feedback...', function() 904 { 905 var dlg = new FeedbackDialog(editorUi); 906 editorUi.showDialog(dlg.container, 610, 360, true, false); 907 dlg.init(); 908 }); 909 910 editorUi.actions.addAction('quickStart...', function() 911 { 912 editorUi.openLink('https://www.youtube.com/watch?v=Z0D96ZikMkc'); 913 }); 914 915 editorUi.actions.addAction('forkme', function() 916 { 917 if (EditorUi.isElectronApp) 918 { 919 editorUi.openLink('https://github.com/jgraph/drawio-desktop'); 920 } 921 else 922 { 923 editorUi.openLink('https://github.com/jgraph/drawio'); 924 } 925 }).label = 'Fork me on GitHub...'; 926 927 editorUi.actions.addAction('downloadDesktop...', function() 928 { 929 editorUi.openLink('https://get.diagrams.net/'); 930 }); 931 932 action = editorUi.actions.addAction('tags', mxUtils.bind(this, function() 933 { 934 if (this.tagsWindow == null) 935 { 936 this.tagsWindow = new TagsWindow(editorUi, document.body.offsetWidth - 400, 60, 212, 200); 937 this.tagsWindow.window.addListener('show', mxUtils.bind(this, function() 938 { 939 editorUi.fireEvent(new mxEventObject('tags')); 940 this.tagsWindow.window.fit(); 941 })); 942 this.tagsWindow.window.addListener('hide', function() 943 { 944 editorUi.fireEvent(new mxEventObject('tags')); 945 }); 946 this.tagsWindow.window.setVisible(true); 947 editorUi.fireEvent(new mxEventObject('tags')); 948 } 949 else 950 { 951 this.tagsWindow.window.setVisible(!this.tagsWindow.window.isVisible()); 952 } 953 })); 954 action.setToggleAction(true); 955 action.setSelectedCallback(mxUtils.bind(this, function() { return this.tagsWindow != null && this.tagsWindow.window.isVisible(); })); 956 957 action = editorUi.actions.addAction('findReplace...', mxUtils.bind(this, function(arg1, evt) 958 { 959 var findReplace = graph.isEnabled() && (evt == null || !mxEvent.isShiftDown(evt)); 960 var evtName = (findReplace) ? 'findReplace' : 'find'; 961 var name = evtName + 'Window'; 962 963 if (this[name] == null) 964 { 965 var w = (findReplace) ? ((uiTheme == 'min') ? 330 : 300) : 240; 966 var h = (findReplace) ? ((uiTheme == 'min') ? 304 : 288) : 170; 967 this[name] = new FindWindow(editorUi, 968 document.body.offsetWidth - (w + 20), 969 100, w, h, findReplace); 970 this[name].window.addListener('show', function() 971 { 972 editorUi.fireEvent(new mxEventObject(evtName)); 973 }); 974 this[name].window.addListener('hide', function() 975 { 976 editorUi.fireEvent(new mxEventObject(evtName)); 977 }); 978 this[name].window.setVisible(true); 979 } 980 else 981 { 982 this[name].window.setVisible(!this[name].window.isVisible()); 983 } 984 }), null, null, Editor.ctrlKey + '+F'); 985 action.setToggleAction(true); 986 action.setSelectedCallback(mxUtils.bind(this, function() 987 { 988 var name = (graph.isEnabled()) ? 'findReplaceWindow' : 'findWindow'; 989 990 return this[name] != null && this[name].window.isVisible(); 991 })); 992 993 editorUi.actions.put('exportVsdx', new Action(mxResources.get('formatVsdx') + ' (beta)...', function() 994 { 995 var noPages = editorUi.pages == null || editorUi.pages.length <= 1; 996 997 if (noPages) 998 { 999 editorUi.exportVisio(); 1000 } 1001 else 1002 { 1003 var div = document.createElement('div'); 1004 div.style.whiteSpace = 'nowrap'; 1005 1006 var hd = document.createElement('h3'); 1007 mxUtils.write(hd, mxResources.get('formatVsdx')); 1008 hd.style.cssText = 'width:100%;text-align:center;margin-top:0px;margin-bottom:4px'; 1009 div.appendChild(hd); 1010 1011 var pages = editorUi.addCheckbox(div, mxResources.get('allPages'), !noPages, noPages); 1012 pages.style.marginBottom = '16px'; 1013 1014 var dlg = new CustomDialog(editorUi, div, mxUtils.bind(this, function() 1015 { 1016 editorUi.exportVisio(!pages.checked); 1017 }), null, mxResources.get('export')); 1018 1019 editorUi.showDialog(dlg.container, 300, 130, true, true); 1020 } 1021 })); 1022 1023 if (isLocalStorage && localStorage != null && urlParams['embed'] != '1') 1024 { 1025 editorUi.actions.addAction('configuration...', function() 1026 { 1027 // Add help, link button 1028 var value = localStorage.getItem(Editor.configurationKey); 1029 1030 var buttons = [[mxResources.get('reset'), function(evt, input) 1031 { 1032 editorUi.confirm(mxResources.get('areYouSure'), function() 1033 { 1034 try 1035 { 1036 localStorage.removeItem(Editor.configurationKey); 1037 1038 if (mxEvent.isShiftDown(evt)) 1039 { 1040 localStorage.removeItem('.drawio-config'); 1041 localStorage.removeItem('.mode'); 1042 } 1043 1044 editorUi.hideDialog(); 1045 editorUi.alert(mxResources.get('restartForChangeRequired')); 1046 } 1047 catch (e) 1048 { 1049 editorUi.handleError(e); 1050 } 1051 }); 1052 }]]; 1053 1054 if (!EditorUi.isElectronApp) 1055 { 1056 buttons.push([mxResources.get('share'), function(evt, input) 1057 { 1058 if (input.value.length > 0) 1059 { 1060 try 1061 { 1062 var obj = JSON.parse(input.value); 1063 var url = window.location.protocol + '//' + window.location.host + 1064 '/' + editorUi.getSearch() + '#_CONFIG_' + 1065 Graph.compress(JSON.stringify(obj)); 1066 var dlg = new EmbedDialog(editorUi, url); 1067 editorUi.showDialog(dlg.container, 450, 240, true); 1068 dlg.init(); 1069 } 1070 catch (e) 1071 { 1072 editorUi.handleError(e); 1073 } 1074 } 1075 else 1076 { 1077 editorUi.handleError({message: mxResources.get('invalidInput')}); 1078 } 1079 }]) 1080 } 1081 1082 var dlg = new TextareaDialog(editorUi, mxResources.get('configuration') + ':', 1083 (value != null) ? JSON.stringify(JSON.parse(value), null, 2) : '', function(newValue) 1084 { 1085 if (newValue != null) 1086 { 1087 try 1088 { 1089 if (newValue.length > 0) 1090 { 1091 var obj = JSON.parse(newValue); 1092 1093 localStorage.setItem(Editor.configurationKey, JSON.stringify(obj)); 1094 } 1095 else 1096 { 1097 localStorage.removeItem(Editor.configurationKey); 1098 } 1099 1100 editorUi.hideDialog(); 1101 editorUi.alert(mxResources.get('restartForChangeRequired')); 1102 } 1103 catch (e) 1104 { 1105 editorUi.handleError(e); 1106 } 1107 } 1108 }, null, null, null, null, null, true, null, null, 1109 'https://www.diagrams.net/doc/faq/configure-diagram-editor', 1110 buttons); 1111 1112 dlg.textarea.style.width = '600px'; 1113 dlg.textarea.style.height = '380px'; 1114 editorUi.showDialog(dlg.container, 620, 460, true, false); 1115 dlg.init(); 1116 }); 1117 } 1118 1119 // Adds language menu to options only if localStorage is available for 1120 // storing the choice. We do not want to use cookies for older browsers. 1121 // Note that the URL param lang=XX is available for setting the language 1122 // in older browsers. URL param has precedence over the saved setting. 1123 if (mxClient.IS_CHROMEAPP || isLocalStorage) 1124 { 1125 this.put('language', new Menu(mxUtils.bind(this, function(menu, parent) 1126 { 1127 var addLangItem = mxUtils.bind(this, function (id) 1128 { 1129 var lang = (id == '') ? mxResources.get('automatic') : mxLanguageMap[id]; 1130 var item = null; 1131 1132 if (lang != '') 1133 { 1134 item = menu.addItem(lang, null, mxUtils.bind(this, function() 1135 { 1136 mxSettings.setLanguage(id); 1137 mxSettings.save(); 1138 1139 // Shows dialog in new language 1140 mxClient.language = id; 1141 mxResources.loadDefaultBundle = false; 1142 mxResources.add(RESOURCE_BASE); 1143 1144 editorUi.alert(mxResources.get('restartForChangeRequired')); 1145 }), parent); 1146 1147 if (id == mxLanguage || (id == '' && mxLanguage == null)) 1148 { 1149 menu.addCheckmark(item, Editor.checkmarkImage); 1150 } 1151 } 1152 1153 return item; 1154 }); 1155 1156 var item = addLangItem(''); 1157 menu.addSeparator(parent); 1158 1159 // LATER: Sort menu by language name 1160 for(var langId in mxLanguageMap) 1161 { 1162 addLangItem(langId); 1163 } 1164 }))); 1165 1166 // Extends the menubar with the language menu 1167 var menusCreateMenuBar = Menus.prototype.createMenubar; 1168 Menus.prototype.createMenubar = function(container) 1169 { 1170 var menubar = menusCreateMenuBar.apply(this, arguments); 1171 1172 if (menubar != null && urlParams['noLangIcon'] != '1') 1173 { 1174 var langMenu = this.get('language'); 1175 1176 if (langMenu != null) 1177 { 1178 var elt = menubar.addMenu('', langMenu.funct); 1179 elt.setAttribute('title', mxResources.get('language')); 1180 elt.style.width = '16px'; 1181 elt.style.paddingTop = '2px'; 1182 elt.style.paddingLeft = '4px'; 1183 elt.style.zIndex = '1'; 1184 elt.style.position = 'absolute'; 1185 elt.style.display = 'block'; 1186 elt.style.cursor = 'pointer'; 1187 elt.style.right = '17px'; 1188 1189 if (uiTheme == 'atlas') 1190 { 1191 elt.style.top = '6px'; 1192 elt.style.right = '15px'; 1193 } 1194 else if (uiTheme == 'min') 1195 { 1196 elt.style.top = '2px'; 1197 } 1198 else 1199 { 1200 elt.style.top = '0px'; 1201 } 1202 1203 if (EditorUi.isElectronApp) 1204 { 1205 elt.style.right = '95px'; 1206 } 1207 1208 var icon = document.createElement('div'); 1209 icon.style.backgroundImage = 'url(' + Editor.globeImage + ')'; 1210 icon.style.backgroundPosition = 'center center'; 1211 icon.style.backgroundRepeat = 'no-repeat'; 1212 icon.style.backgroundSize = '19px 19px'; 1213 icon.style.position = 'absolute'; 1214 icon.style.height = '19px'; 1215 icon.style.width = '19px'; 1216 icon.style.marginTop = '2px'; 1217 icon.style.zIndex = '1'; 1218 elt.appendChild(icon); 1219 mxUtils.setOpacity(elt, 40); 1220 1221 if (uiTheme == 'atlas' || uiTheme == 'dark') 1222 { 1223 elt.style.opacity = '0.85'; 1224 elt.style.filter = 'invert(100%)'; 1225 } 1226 1227 document.body.appendChild(elt); 1228 } 1229 } 1230 1231 return menubar; 1232 }; 1233 } 1234 1235 editorUi.customLayoutConfig = [{'layout': 'mxHierarchicalLayout', 1236 'config': 1237 {'orientation': 'west', 1238 'intraCellSpacing': 30, 1239 'interRankCellSpacing': 100, 1240 'interHierarchySpacing': 60, 1241 'parallelEdgeSpacing': 10}}]; 1242 1243 // Adds action 1244 editorUi.actions.addAction('runLayout', function() 1245 { 1246 var dlg = new TextareaDialog(editorUi, 'Run Layouts:', 1247 JSON.stringify(editorUi.customLayoutConfig, null, 2), 1248 function(newValue) 1249 { 1250 if (newValue.length > 0) 1251 { 1252 try 1253 { 1254 var layoutList = JSON.parse(newValue); 1255 editorUi.executeLayoutList(layoutList) 1256 editorUi.customLayoutConfig = layoutList; 1257 } 1258 catch (e) 1259 { 1260 editorUi.handleError(e); 1261 1262 if (window.console != null) 1263 { 1264 console.error(e); 1265 } 1266 } 1267 } 1268 }, null, null, null, null, null, true, null, null, 1269 'https://www.diagrams.net/doc/faq/apply-layouts'); 1270 1271 dlg.textarea.style.width = '600px'; 1272 dlg.textarea.style.height = '380px'; 1273 editorUi.showDialog(dlg.container, 620, 460, true, true); 1274 dlg.init(); 1275 }); 1276 1277 var layoutMenu = this.get('layout'); 1278 var layoutMenuFunct = layoutMenu.funct; 1279 1280 layoutMenu.funct = function(menu, parent) 1281 { 1282 layoutMenuFunct.apply(this, arguments); 1283 1284 menu.addItem(mxResources.get('orgChart'), null, function() 1285 { 1286 var branchOptimizer = null, parentChildSpacingVal = 20, siblingSpacingVal = 20, notExecuted = true; 1287 1288 // Invoked when orgchart code was loaded 1289 var delayed = function() 1290 { 1291 editorUi.loadingOrgChart = false; 1292 editorUi.spinner.stop(); 1293 1294 if (typeof mxOrgChartLayout !== 'undefined' && branchOptimizer != null && notExecuted) 1295 { 1296 var graph = editorUi.editor.graph; 1297 var orgChartLayout = new mxOrgChartLayout(graph, branchOptimizer, parentChildSpacingVal, siblingSpacingVal); 1298 1299 var cell = graph.getDefaultParent(); 1300 1301 if (graph.model.getChildCount(graph.getSelectionCell()) > 1) 1302 { 1303 cell = graph.getSelectionCell(); 1304 } 1305 1306 orgChartLayout.execute(cell); 1307 notExecuted = false; 1308 } 1309 }; 1310 1311 // Invoked from dialog 1312 function doLayout() 1313 { 1314 if (typeof mxOrgChartLayout === 'undefined' && !editorUi.loadingOrgChart && !editorUi.isOffline(true)) 1315 { 1316 if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) 1317 { 1318 editorUi.loadingOrgChart = true; 1319 1320 if (urlParams['dev'] == '1') 1321 { 1322 mxscript('js/orgchart/bridge.min.js', function() 1323 { 1324 mxscript('js/orgchart/bridge.collections.min.js', function() 1325 { 1326 mxscript('js/orgchart/OrgChart.Layout.min.js', function() 1327 { 1328 mxscript('js/orgchart/mxOrgChartLayout.js', delayed); 1329 }); 1330 }); 1331 }); 1332 } 1333 else 1334 { 1335 mxscript('js/extensions.min.js', delayed); 1336 } 1337 } 1338 } 1339 else 1340 { 1341 delayed(); 1342 } 1343 }; 1344 1345 var div = document.createElement('div'); 1346 1347 var title = document.createElement('div'); 1348 title.style.marginTop = '6px'; 1349 title.style.display = 'inline-block'; 1350 title.style.width = '140px'; 1351 mxUtils.write(title, mxResources.get('orgChartType') + ': '); 1352 1353 div.appendChild(title); 1354 1355 var typeSelect = document.createElement('select'); 1356 typeSelect.style.width = '200px'; 1357 typeSelect.style.boxSizing = 'border-box'; 1358 1359 //Types are hardcoded here since the code is not loaded yet 1360 var typesArr = [mxResources.get('linear'), 1361 mxResources.get('hanger2'), 1362 mxResources.get('hanger4'), 1363 mxResources.get('fishbone1'), 1364 mxResources.get('fishbone2'), 1365 mxResources.get('1ColumnLeft'), 1366 mxResources.get('1ColumnRight'), 1367 mxResources.get('smart') 1368 ]; 1369 1370 for (var i = 0; i < typesArr.length; i++) 1371 { 1372 var option = document.createElement('option'); 1373 mxUtils.write(option, typesArr[i]); 1374 option.value = i; 1375 1376 if (i == 2) 1377 { 1378 option.setAttribute('selected', 'selected'); 1379 } 1380 1381 typeSelect.appendChild(option); 1382 } 1383 1384 mxEvent.addListener(typeSelect, 'change', function() 1385 { 1386 branchOptimizer = typeSelect.value; 1387 }); 1388 1389 div.appendChild(typeSelect); 1390 1391 title = document.createElement('div'); 1392 title.style.marginTop = '6px'; 1393 title.style.display = 'inline-block'; 1394 title.style.width = '140px'; 1395 mxUtils.write(title, mxResources.get('parentChildSpacing') + ': '); 1396 div.appendChild(title); 1397 1398 var parentChildSpacing = document.createElement('input'); 1399 parentChildSpacing.type = 'number'; 1400 parentChildSpacing.value = parentChildSpacingVal; 1401 parentChildSpacing.style.width = '200px'; 1402 parentChildSpacing.style.boxSizing = 'border-box'; 1403 div.appendChild(parentChildSpacing); 1404 1405 mxEvent.addListener(parentChildSpacing, 'change', function() 1406 { 1407 parentChildSpacingVal = parentChildSpacing.value; 1408 }); 1409 1410 title = document.createElement('div'); 1411 title.style.marginTop = '6px'; 1412 title.style.display = 'inline-block'; 1413 title.style.width = '140px'; 1414 mxUtils.write(title, mxResources.get('siblingSpacing') + ': '); 1415 div.appendChild(title); 1416 1417 var siblingSpacing = document.createElement('input'); 1418 siblingSpacing.type = 'number'; 1419 siblingSpacing.value = siblingSpacingVal; 1420 siblingSpacing.style.width = '200px'; 1421 siblingSpacing.style.boxSizing = 'border-box'; 1422 div.appendChild(siblingSpacing); 1423 1424 mxEvent.addListener(siblingSpacing, 'change', function() 1425 { 1426 siblingSpacingVal = siblingSpacing.value; 1427 }); 1428 1429 var dlg = new CustomDialog(editorUi, div, function() 1430 { 1431 if (branchOptimizer == null) 1432 { 1433 branchOptimizer = 2; 1434 } 1435 1436 doLayout(); 1437 }); 1438 1439 editorUi.showDialog(dlg.container, 355, 140, true, true); 1440 }, parent, null, isGraphEnabled()); 1441 1442 menu.addSeparator(parent); 1443 1444 menu.addItem(mxResources.get('parallels'), null, mxUtils.bind(this, function() 1445 { 1446 // Keeps parallel edges apart 1447 var layout = new mxParallelEdgeLayout(graph); 1448 layout.checkOverlap = true; 1449 layout.spacing = 20; 1450 1451 editorUi.executeLayout(function() 1452 { 1453 layout.execute(graph.getDefaultParent(), (!graph.isSelectionEmpty()) ? 1454 graph.getSelectionCells() : null); 1455 }, false); 1456 }), parent); 1457 1458 menu.addSeparator(parent); 1459 editorUi.menus.addMenuItem(menu, 'runLayout', parent, null, null, mxResources.get('apply') + '...'); 1460 }; 1461 1462 this.put('help', new Menu(mxUtils.bind(this, function(menu, parent) 1463 { 1464 if (!mxClient.IS_CHROMEAPP && editorUi.isOffline()) 1465 { 1466 this.addMenuItems(menu, ['about'], parent); 1467 } 1468 else 1469 { 1470 // No translation for menu item since help is english only 1471 var item = menu.addItem('Search:', null, null, parent, null, null, false); 1472 item.style.backgroundColor = Editor.isDarkMode() ? '#505759' : 'whiteSmoke'; 1473 item.style.cursor = 'default'; 1474 1475 var input = document.createElement('input'); 1476 input.setAttribute('type', 'text'); 1477 input.setAttribute('size', '25'); 1478 input.style.marginLeft = '8px'; 1479 1480 mxEvent.addListener(input, 'keydown', mxUtils.bind(this, function(e) 1481 { 1482 var term = mxUtils.trim(input.value); 1483 1484 if (e.keyCode == 13 && term.length > 0) 1485 { 1486 this.editorUi.openLink('https://www.diagrams.net/search?src=' + 1487 EditorUi.isElectronApp? 'DESKTOP' : encodeURIComponent(location.host) + 1488 '&search=' + encodeURIComponent(term)); 1489 input.value = ''; 1490 EditorUi.logEvent({category: 'SEARCH-HELP', action: 'search', label: term}); 1491 1492 window.setTimeout(mxUtils.bind(this, function() 1493 { 1494 this.editorUi.hideCurrentMenu(); 1495 }), 0); 1496 } 1497 else if (e.keyCode == 27) 1498 { 1499 input.value = ''; 1500 } 1501 })); 1502 1503 item.firstChild.nextSibling.appendChild(input); 1504 1505 mxEvent.addGestureListeners(input, function(evt) 1506 { 1507 if (document.activeElement != input) 1508 { 1509 input.focus(); 1510 } 1511 1512 mxEvent.consume(evt); 1513 }, function(evt) 1514 { 1515 mxEvent.consume(evt); 1516 }, function(evt) 1517 { 1518 mxEvent.consume(evt); 1519 }); 1520 1521 window.setTimeout(function() 1522 { 1523 input.focus(); 1524 }, 0); 1525 1526 if (EditorUi.isElectronApp) 1527 { 1528 editorUi.actions.addAction('website...', function() 1529 { 1530 editorUi.openLink('https://www.diagrams.net'); 1531 }); 1532 1533 editorUi.actions.addAction('check4Updates', function() 1534 { 1535 editorUi.checkForUpdates(); 1536 }); 1537 1538 console.log('electron help menu'); 1539 this.addMenuItems(menu, ['-', 'keyboardShortcuts', 'quickStart', 1540 'website', 'support', '-'], parent); 1541 1542 if (urlParams['disableUpdate'] != '1') 1543 { 1544 this.addMenuItems(menu, ['check4Updates', '-'], parent); 1545 } 1546 1547 this.addMenuItems(menu, ['forkme', '-', 'about'], parent); 1548 1549 } 1550 else 1551 { 1552 this.addMenuItems(menu, ['-', 'keyboardShortcuts', 'quickStart', 1553 'support', '-', 'forkme', 'downloadDesktop', '-', 'about'], parent); 1554 } 1555 } 1556 1557 if (urlParams['test'] == '1') 1558 { 1559 menu.addSeparator(parent); 1560 this.addSubmenu('testDevelop', menu, parent); 1561 } 1562 }))); 1563 1564 // Experimental 1565 mxResources.parse('diagramLanguage=Diagram Language'); 1566 editorUi.actions.addAction('diagramLanguage...', function() 1567 { 1568 var lang = prompt('Language Code', Graph.diagramLanguage || ''); 1569 1570 if (lang != null) 1571 { 1572 Graph.diagramLanguage = (lang.length > 0) ? lang : null; 1573 graph.refresh(); 1574 } 1575 }); 1576 1577 // Only visible in test mode 1578 if (urlParams['test'] == '1') 1579 { 1580 mxResources.parse('testDevelop=Develop'); 1581 mxResources.parse('showBoundingBox=Show bounding box'); 1582 mxResources.parse('createSidebarEntry=Create Sidebar Entry'); 1583 mxResources.parse('testCheckFile=Check File'); 1584 mxResources.parse('testDiff=Diff/Sync'); 1585 mxResources.parse('testInspect=Inspect'); 1586 mxResources.parse('testShowConsole=Show Console'); 1587 mxResources.parse('testXmlImageExport=XML Image Export'); 1588 mxResources.parse('testDownloadRtModel=Export RT model'); 1589 mxResources.parse('testImportRtModel=Import RT model'); 1590 1591 editorUi.actions.addAction('createSidebarEntry', mxUtils.bind(this, function() 1592 { 1593 if (!graph.isSelectionEmpty()) 1594 { 1595 var cells = graph.cloneCells(graph.getSelectionCells()); 1596 var bbox = graph.getBoundingBoxFromGeometry(cells); 1597 cells = graph.moveCells(cells, -bbox.x, -bbox.y); 1598 1599 editorUi.showTextDialog('Create Sidebar Entry', 'this.addDataEntry(\'tag1 tag2\', ' + 1600 bbox.width + ', ' + bbox.height + ', \'The Title\', \'' + 1601 Graph.compress(mxUtils.getXml(graph.encodeCells(cells))) + '\'),'); 1602 } 1603 })); 1604 1605 editorUi.actions.addAction('showBoundingBox', mxUtils.bind(this, function() 1606 { 1607 var b = graph.getGraphBounds(); 1608 var tr = graph.view.translate; 1609 var s = graph.view.scale; 1610 graph.insertVertex(graph.getDefaultParent(), null, '', 1611 b.x / s - tr.x, b.y / s - tr.y, b.width / s, b.height / s, 1612 'fillColor=none;strokeColor=red;'); 1613 })); 1614 1615 editorUi.actions.addAction('testCheckFile', mxUtils.bind(this, function() 1616 { 1617 var xml = (editorUi.pages != null && editorUi.getCurrentFile() != null) ? 1618 editorUi.getCurrentFile().getAnonymizedXmlForPages(editorUi.pages) : ''; 1619 1620 var dlg = new TextareaDialog(editorUi, 'Paste Data:', xml, 1621 function(newValue) 1622 { 1623 if (newValue.length > 0) 1624 { 1625 try 1626 { 1627 if (newValue.charAt(0) != '<') 1628 { 1629 newValue = Graph.decompress(newValue); 1630 mxLog.debug('See console for uncompressed XML'); 1631 console.log('xml', newValue); 1632 } 1633 1634 var doc = mxUtils.parseXml(newValue); 1635 var pages = editorUi.getPagesForNode(doc.documentElement, 'mxGraphModel'); 1636 1637 if (pages != null && pages.length > 0) 1638 { 1639 try 1640 { 1641 var checksum = editorUi.getHashValueForPages(pages); 1642 mxLog.debug('Checksum: ', checksum); 1643 } 1644 catch (e) 1645 { 1646 mxLog.debug('Error: ', e.message); 1647 } 1648 } 1649 else 1650 { 1651 mxLog.debug('No pages found for checksum'); 1652 } 1653 1654 // Checks for duplicates 1655 function checkModel(node) 1656 { 1657 var pageId = node.parentNode.id; 1658 var all = node.childNodes; 1659 var allIds = {}; 1660 var childs = {}; 1661 var root = null; 1662 var dups = {}; 1663 1664 for (var i = 0; i < all.length; i++) 1665 { 1666 var el = all[i]; 1667 1668 if (el.id != null && el.id.length > 0) 1669 { 1670 if (allIds[el.id] == null) 1671 { 1672 allIds[el.id] = el.id; 1673 var pid = el.getAttribute('parent'); 1674 1675 if (pid == null) 1676 { 1677 if (root != null) 1678 { 1679 mxLog.debug(pageId + ': Multiple roots: ' + el.id); 1680 } 1681 else 1682 { 1683 root = el.id; 1684 } 1685 } 1686 else 1687 { 1688 if (childs[pid] == null) 1689 { 1690 childs[pid] = []; 1691 } 1692 1693 childs[pid].push(el.id); 1694 } 1695 } 1696 else 1697 { 1698 dups[el.id] = el.id; 1699 } 1700 } 1701 } 1702 1703 if (Object.keys(dups).length > 0) 1704 { 1705 var log = pageId + ': ' + Object.keys(dups).length + ' Duplicates: ' + Object.keys(dups).join(', '); 1706 mxLog.debug(log + ' (see console)'); 1707 } 1708 else 1709 { 1710 mxLog.debug(pageId + ': Checked'); 1711 } 1712 1713 // Checks tree for cycles 1714 var visited = {}; 1715 1716 function visit(id) 1717 { 1718 if (visited[id] == null) 1719 { 1720 visited[id] = true; 1721 1722 if (childs[id] != null) 1723 { 1724 while (childs[id].length > 0) 1725 { 1726 var temp = childs[id].pop(); 1727 visit(temp); 1728 } 1729 1730 delete childs[id]; 1731 } 1732 } 1733 else 1734 { 1735 mxLog.debug(pageId + ': Visited: ' + id); 1736 } 1737 }; 1738 1739 if (root == null) 1740 { 1741 mxLog.debug(pageId + ': No root'); 1742 } 1743 else 1744 { 1745 visit(root); 1746 1747 if (Object.keys(visited).length != Object.keys(allIds).length) 1748 { 1749 mxLog.debug(pageId + ': Invalid tree: (see console)'); 1750 console.log(pageId + ': Invalid tree', childs); 1751 } 1752 } 1753 }; 1754 1755 var roots = doc.getElementsByTagName('root'); 1756 1757 for (var i = 0; i < roots.length; i++) 1758 { 1759 checkModel(roots[i]); 1760 } 1761 1762 mxLog.show(); 1763 } 1764 catch (e) 1765 { 1766 editorUi.handleError(e); 1767 1768 if (window.console != null) 1769 { 1770 console.error(e); 1771 } 1772 } 1773 } 1774 }); 1775 1776 dlg.textarea.style.width = '600px'; 1777 dlg.textarea.style.height = '380px'; 1778 editorUi.showDialog(dlg.container, 620, 460, true, true); 1779 dlg.init(); 1780 })); 1781 1782 var snapshot = null; 1783 1784 editorUi.actions.addAction('testDiff', mxUtils.bind(this, function() 1785 { 1786 if (editorUi.pages != null) 1787 { 1788 var buttons = [['Snapshot', function(evt, input) 1789 { 1790 snapshot = editorUi.getPagesForNode(mxUtils.parseXml( 1791 editorUi.getFileData(true)).documentElement); 1792 dlg.textarea.value = 'Snapshot updated ' + new Date().toLocaleString(); 1793 }], ['Diff', function(evt, input) 1794 { 1795 try 1796 { 1797 dlg.textarea.value = JSON.stringify(editorUi.diffPages( 1798 snapshot, editorUi.pages), null, 2); 1799 } 1800 catch (e) 1801 { 1802 editorUi.handleError(e); 1803 } 1804 }]]; 1805 1806 var dlg = new TextareaDialog(editorUi, 'Diff/Sync:', '', 1807 function(newValue) 1808 { 1809 var file = editorUi.getCurrentFile(); 1810 1811 if (newValue.length > 0 && file != null) 1812 { 1813 try 1814 { 1815 var patch = JSON.parse(newValue); 1816 file.patch([patch], null, true); 1817 editorUi.hideDialog(); 1818 } 1819 catch (e) 1820 { 1821 editorUi.handleError(e); 1822 } 1823 } 1824 }, null, 'Close', null, null, null, true, null, 'Patch', null, buttons); 1825 1826 dlg.textarea.style.width = '600px'; 1827 dlg.textarea.style.height = '380px'; 1828 1829 1830 if (snapshot == null) 1831 { 1832 snapshot = editorUi.getPagesForNode(mxUtils.parseXml( 1833 editorUi.getFileData(true)).documentElement); 1834 dlg.textarea.value = 'Snapshot created ' + new Date().toLocaleString(); 1835 } 1836 else 1837 { 1838 dlg.textarea.value = JSON.stringify(editorUi.diffPages( 1839 snapshot, editorUi.pages), null, 2); 1840 } 1841 1842 editorUi.showDialog(dlg.container, 620, 460, true, true); 1843 dlg.init(); 1844 } 1845 else 1846 { 1847 editorUi.alert('No pages'); 1848 } 1849 })); 1850 1851 editorUi.actions.addAction('testInspect', mxUtils.bind(this, function() 1852 { 1853 console.log(editorUi, graph.getModel()); 1854 })); 1855 1856 editorUi.actions.addAction('testXmlImageExport', mxUtils.bind(this, function() 1857 { 1858 var bg = '#ffffff'; 1859 var scale = 1; 1860 var b = 1; 1861 1862 var imgExport = new mxImageExport(); 1863 var bounds = graph.getGraphBounds(); 1864 var vs = graph.view.scale; 1865 1866 // New image export 1867 var xmlDoc = mxUtils.createXmlDocument(); 1868 var root = xmlDoc.createElement('output'); 1869 xmlDoc.appendChild(root); 1870 1871 // Renders graph. Offset will be multiplied with state's scale when painting state. 1872 var xmlCanvas = new mxXmlCanvas2D(root); 1873 xmlCanvas.translate(Math.floor((b / scale - bounds.x) / vs), Math.floor((b / scale - bounds.y) / vs)); 1874 xmlCanvas.scale(scale / vs); 1875 1876 var stateCounter = 0; 1877 1878 var canvasSave = xmlCanvas.save; 1879 xmlCanvas.save = function() 1880 { 1881 stateCounter++; 1882 canvasSave.apply(this, arguments); 1883 }; 1884 1885 var canvasRestore = xmlCanvas.restore; 1886 xmlCanvas.restore = function() 1887 { 1888 stateCounter--; 1889 canvasRestore.apply(this, arguments); 1890 }; 1891 1892 var exportDrawShape = imgExport.drawShape; 1893 imgExport.drawShape = function(state) 1894 { 1895 mxLog.debug('entering shape', state, stateCounter); 1896 exportDrawShape.apply(this, arguments); 1897 mxLog.debug('leaving shape', state, stateCounter); 1898 }; 1899 1900 imgExport.drawState(graph.getView().getState(graph.model.root), xmlCanvas); 1901 1902 // Puts request data together 1903 var w = Math.ceil(bounds.width * scale / vs + 2 * b); 1904 var h = Math.ceil(bounds.height * scale / vs + 2 * b); 1905 1906 mxLog.show(); 1907 mxLog.debug(mxUtils.getXml(root)); 1908 mxLog.debug('stateCounter', stateCounter); 1909 })); 1910 1911 editorUi.actions.addAction('testShowConsole', function() 1912 { 1913 if (!mxLog.isVisible()) 1914 { 1915 mxLog.show(); 1916 } 1917 else 1918 { 1919 mxLog.window.fit(); 1920 } 1921 1922 mxLog.window.div.style.zIndex = mxPopupMenu.prototype.zIndex - 2; 1923 }); 1924 1925 this.put('testDevelop', new Menu(mxUtils.bind(this, function(menu, parent) 1926 { 1927 this.addMenuItems(menu, ['createSidebarEntry', 'showBoundingBox', '-', 1928 'testCheckFile', 'testDiff', '-', 'testInspect', '-', 1929 'testXmlImageExport', '-', 'testShowConsole'], parent); 1930 }))); 1931 } 1932 1933 editorUi.actions.addAction('shapes...', function() 1934 { 1935 if (mxClient.IS_CHROMEAPP || !editorUi.isOffline()) 1936 { 1937 editorUi.showDialog(new MoreShapesDialog(editorUi, true).container, 640, (isLocalStorage) ? 1938 ((mxClient.IS_IOS) ? 480 : 460) : 440, true, true); 1939 } 1940 else 1941 { 1942 editorUi.showDialog(new MoreShapesDialog(editorUi, false).container, 360, (isLocalStorage) ? 1943 ((mxClient.IS_IOS) ? 300 : 280) : 260, true, true); 1944 } 1945 }); 1946 1947 editorUi.actions.put('createShape', new Action(mxResources.get('shape') + '...', function(evt) 1948 { 1949 if (graph.isEnabled()) 1950 { 1951 var cell = new mxCell('', new mxGeometry(0, 0, 120, 120), editorUi.defaultCustomShapeStyle); 1952 cell.vertex = true; 1953 1954 var dlg = new EditShapeDialog(editorUi, cell, mxResources.get('editShape') + ':', 630, 400); 1955 editorUi.showDialog(dlg.container, 640, 480, true, false); 1956 dlg.init(); 1957 } 1958 })).isEnabled = isGraphEnabled; 1959 1960 editorUi.actions.put('embedHtml', new Action(mxResources.get('html') + '...', function() 1961 { 1962 if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) 1963 { 1964 editorUi.getPublicUrl(editorUi.getCurrentFile(), function(url) 1965 { 1966 editorUi.spinner.stop(); 1967 1968 editorUi.showHtmlDialog(mxResources.get('create'), 'https://www.diagrams.net/doc/faq/embed-html-options', 1969 url, function(publicUrl, zoomEnabled, initialZoom, linkTarget, linkColor, fit, allPages, layers, tags, lightbox, editLink) 1970 { 1971 editorUi.createHtml(publicUrl, zoomEnabled, initialZoom, linkTarget, linkColor, fit, allPages, 1972 layers, tags, lightbox, editLink, mxUtils.bind(this, function(html, scriptTag) 1973 { 1974 var dlg = new EmbedDialog(editorUi, html + '\n' + scriptTag, null, null, function() 1975 { 1976 var wnd = window.open(); 1977 var doc = wnd.document; 1978 1979 if (doc != null) 1980 { 1981 if (document.compatMode === 'CSS1Compat') 1982 { 1983 doc.writeln('<!DOCTYPE html>'); 1984 } 1985 1986 doc.writeln('<html>'); 1987 doc.writeln('<head><title>' + encodeURIComponent(mxResources.get('preview')) + 1988 '</title><meta charset="utf-8"></head>'); 1989 doc.writeln('<body>'); 1990 doc.writeln(html); 1991 1992 var direct = mxClient.IS_IE || mxClient.IS_EDGE || document.documentMode != null; 1993 1994 if (direct) 1995 { 1996 doc.writeln(scriptTag); 1997 } 1998 1999 doc.writeln('</body>'); 2000 doc.writeln('</html>'); 2001 doc.close(); 2002 2003 // Adds script tag after closing page and delay to fix timing issues 2004 if (!direct) 2005 { 2006 var info = wnd.document.createElement('div'); 2007 info.marginLeft = '26px'; 2008 info.marginTop = '26px'; 2009 mxUtils.write(info, mxResources.get('updatingDocument')); 2010 2011 var img = wnd.document.createElement('img'); 2012 img.setAttribute('src', window.location.protocol + '//' + window.location.hostname + 2013 '/' + IMAGE_PATH + '/spin.gif'); 2014 img.style.marginLeft = '6px'; 2015 info.appendChild(img); 2016 2017 wnd.document.body.insertBefore(info, wnd.document.body.firstChild); 2018 2019 window.setTimeout(function() 2020 { 2021 var script = document.createElement('script'); 2022 script.type = 'text/javascript'; 2023 script.src = /<script.*?src="(.*?)"/.exec(scriptTag)[1]; 2024 doc.body.appendChild(script); 2025 2026 info.parentNode.removeChild(info); 2027 }, 20); 2028 } 2029 } 2030 else 2031 { 2032 editorUi.handleError({message: mxResources.get('errorUpdatingPreview')}); 2033 } 2034 }); 2035 editorUi.showDialog(dlg.container, 450, 240, true, true); 2036 dlg.init(); 2037 })); 2038 }); 2039 }); 2040 } 2041 })); 2042 2043 editorUi.actions.put('liveImage', new Action('Live image...', function() 2044 { 2045 var current = editorUi.getCurrentFile(); 2046 2047 if (current != null && editorUi.spinner.spin(document.body, mxResources.get('loading'))) 2048 { 2049 editorUi.getPublicUrl(editorUi.getCurrentFile(), function(url) 2050 { 2051 editorUi.spinner.stop(); 2052 2053 if (url != null) 2054 { 2055 var dlg = new EmbedDialog(editorUi, '<img src="' + ((current.constructor != DriveFile) ? 2056 url : 'https://drive.google.com/uc?id=' + current.getId()) + '"/>'); 2057 editorUi.showDialog(dlg.container, 450, 240, true, true); 2058 dlg.init(); 2059 } 2060 else 2061 { 2062 editorUi.handleError({message: mxResources.get('invalidPublicUrl')}); 2063 } 2064 }); 2065 } 2066 })); 2067 2068 editorUi.actions.put('embedImage', new Action(mxResources.get('image') + '...', function() 2069 { 2070 editorUi.showEmbedImageDialog(function(fit, shadow, retina, lightbox, editLink, layers) 2071 { 2072 if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) 2073 { 2074 editorUi.createEmbedImage(fit, shadow, retina, lightbox, editLink, layers, function(result) 2075 { 2076 editorUi.spinner.stop(); 2077 var dlg = new EmbedDialog(editorUi, result); 2078 editorUi.showDialog(dlg.container, 450, 240, true, true); 2079 dlg.init(); 2080 }, function(err) 2081 { 2082 editorUi.spinner.stop(); 2083 editorUi.handleError(err); 2084 }); 2085 } 2086 }, mxResources.get('image'), mxResources.get('retina'), editorUi.isExportToCanvas()); 2087 })); 2088 2089 editorUi.actions.put('embedSvg', new Action(mxResources.get('formatSvg') + '...', function() 2090 { 2091 editorUi.showEmbedImageDialog(function(fit, shadow, image, lightbox, editLink, layers) 2092 { 2093 if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) 2094 { 2095 editorUi.createEmbedSvg(fit, shadow, image, lightbox, editLink, layers, function(result) 2096 { 2097 editorUi.spinner.stop(); 2098 2099 var dlg = new EmbedDialog(editorUi, result); 2100 editorUi.showDialog(dlg.container, 450, 240, true, true); 2101 dlg.init(); 2102 }, function(err) 2103 { 2104 editorUi.spinner.stop(); 2105 editorUi.handleError(err); 2106 }); 2107 } 2108 }, mxResources.get('formatSvg'), mxResources.get('image'), 2109 true, 'https://www.diagrams.net/doc/faq/embed-svg.html'); 2110 })); 2111 2112 editorUi.actions.put('embedIframe', new Action(mxResources.get('iframe') + '...', function() 2113 { 2114 var bounds = graph.getGraphBounds(); 2115 2116 editorUi.showPublishLinkDialog(mxResources.get('iframe'), null, '100%', 2117 Math.ceil(bounds.height / graph.view.scale) + 2, 2118 function(linkTarget, linkColor, allPages, lightbox, editLink, layers, width, height, tags) 2119 { 2120 if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) 2121 { 2122 editorUi.getPublicUrl(editorUi.getCurrentFile(), function(url) 2123 { 2124 editorUi.spinner.stop(); 2125 var params = []; 2126 2127 if (tags) 2128 { 2129 params.push('tags=%7B%7D'); 2130 } 2131 2132 var dlg = new EmbedDialog(editorUi, '<iframe frameborder="0" style="width:' + width + 2133 ';height:' + height + ';" src="' + editorUi.createLink(linkTarget, linkColor, 2134 allPages, lightbox, editLink, layers, url, null, params) + '"></iframe>'); 2135 editorUi.showDialog(dlg.container, 450, 240, true, true); 2136 dlg.init(); 2137 }); 2138 } 2139 }, true); 2140 })); 2141 2142 editorUi.actions.put('embedNotion', new Action(mxResources.get('notion') + '...', function() 2143 { 2144 editorUi.showPublishLinkDialog(mxResources.get('notion'), null, null, null, 2145 function(linkTarget, linkColor, allPages, lightbox, editLink, layers, width, height, tags) 2146 { 2147 if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) 2148 { 2149 editorUi.getPublicUrl(editorUi.getCurrentFile(), function(url) 2150 { 2151 editorUi.spinner.stop(); 2152 var params = ['border=0']; 2153 2154 if (tags) 2155 { 2156 params.push('tags=%7B%7D'); 2157 } 2158 2159 var dlg = new EmbedDialog(editorUi, editorUi.createLink(linkTarget, linkColor, 2160 allPages, lightbox, editLink, layers, url, null, params, true)); 2161 editorUi.showDialog(dlg.container, 450, 240, true, true); 2162 dlg.init(); 2163 }); 2164 } 2165 }, true); 2166 })); 2167 2168 editorUi.actions.put('publishLink', new Action(mxResources.get('link') + '...', function() 2169 { 2170 editorUi.showPublishLinkDialog(null, null, null, null, 2171 function(linkTarget, linkColor, allPages, lightbox, editLink, layers, width, height, tags) 2172 { 2173 if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) 2174 { 2175 editorUi.getPublicUrl(editorUi.getCurrentFile(), function(url) 2176 { 2177 editorUi.spinner.stop(); 2178 2179 var params = []; 2180 2181 if (tags) 2182 { 2183 params.push('tags=%7B%7D'); 2184 } 2185 2186 var dlg = new EmbedDialog(editorUi, editorUi.createLink(linkTarget, linkColor, 2187 allPages, lightbox, editLink, layers, url, null, params)); 2188 editorUi.showDialog(dlg.container, 450, 240, true, true); 2189 dlg.init(); 2190 }); 2191 } 2192 }); 2193 })); 2194 2195 editorUi.actions.addAction('microsoftOffice...', function() 2196 { 2197 editorUi.openLink('https://office.draw.io'); 2198 }); 2199 2200 editorUi.actions.addAction('googleDocs...', function() 2201 { 2202 editorUi.openLink('http://docsaddon.draw.io'); 2203 }); 2204 2205 editorUi.actions.addAction('googleSlides...', function() 2206 { 2207 editorUi.openLink('https://slidesaddon.draw.io'); 2208 }); 2209 2210 editorUi.actions.addAction('googleSheets...', function() 2211 { 2212 editorUi.openLink('https://sheetsaddon.draw.io'); 2213 }); 2214 2215 editorUi.actions.addAction('googleSites...', function() 2216 { 2217 if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) 2218 { 2219 editorUi.getPublicUrl(editorUi.getCurrentFile(), function(url) 2220 { 2221 editorUi.spinner.stop(); 2222 var dlg = new GoogleSitesDialog(editorUi, url); 2223 editorUi.showDialog(dlg.container, 420, 256, true, true); 2224 dlg.init(); 2225 }); 2226 } 2227 }); 2228 2229 // Adds plugins menu item only if localStorage is available for storing the plugins 2230 if (isLocalStorage || mxClient.IS_CHROMEAPP) 2231 { 2232 var action = editorUi.actions.addAction('scratchpad', function() 2233 { 2234 editorUi.toggleScratchpad(); 2235 }); 2236 2237 action.setToggleAction(true); 2238 action.setSelectedCallback(function() 2239 { 2240 return editorUi.scratchpad != null; 2241 }); 2242 2243 if (urlParams['plugins'] != '0') 2244 { 2245 editorUi.actions.addAction('plugins...', function() 2246 { 2247 editorUi.showDialog(new PluginsDialog(editorUi).container, 360, 170, true, false); 2248 }); 2249 } 2250 } 2251 2252 var action = editorUi.actions.addAction('search', function() 2253 { 2254 var visible = editorUi.sidebar.isEntryVisible('search'); 2255 editorUi.sidebar.showPalette('search', !visible); 2256 2257 if (isLocalStorage) 2258 { 2259 mxSettings.settings.search = !visible; 2260 mxSettings.save(); 2261 } 2262 }); 2263 2264 action.label = mxResources.get('searchShapes'); 2265 action.setToggleAction(true); 2266 action.setSelectedCallback(function() { return editorUi.sidebar.isEntryVisible('search'); }); 2267 2268 if (urlParams['embed'] == '1') 2269 { 2270 editorUi.actions.get('save').funct = function(exit) 2271 { 2272 if (graph.isEditing()) 2273 { 2274 graph.stopEditing(); 2275 } 2276 2277 var data = (urlParams['pages'] != '0' || (editorUi.pages != null && editorUi.pages.length > 1)) ? 2278 editorUi.getFileData(true) : mxUtils.getXml(editorUi.editor.getGraphXml()); 2279 2280 if (urlParams['proto'] == 'json') 2281 { 2282 var msg = editorUi.createLoadMessage('save'); 2283 msg.xml = data; 2284 2285 if (exit) 2286 { 2287 msg.exit = true; 2288 } 2289 2290 data = JSON.stringify(msg); 2291 } 2292 2293 var parent = window.opener || window.parent; 2294 parent.postMessage(data, '*'); 2295 2296 if (urlParams['modified'] != '0' && urlParams['keepmodified'] != '1') 2297 { 2298 editorUi.editor.modified = false; 2299 editorUi.editor.setStatus(''); 2300 } 2301 2302 //Add support to saving files if embedded mode is running with files 2303 var file = editorUi.getCurrentFile(); 2304 2305 if (file != null && file.constructor != EmbedFile && (file.constructor != LocalFile || file.mode != null)) 2306 { 2307 editorUi.saveFile(); 2308 } 2309 }; 2310 2311 var saveAndExitAction = editorUi.actions.addAction('saveAndExit', function() 2312 { 2313 editorUi.actions.get('save').funct(true); 2314 }); 2315 2316 saveAndExitAction.label = urlParams['publishClose'] == '1' ? mxResources.get('publish') : mxResources.get('saveAndExit'); 2317 2318 editorUi.actions.addAction('exit', function() 2319 { 2320 if (urlParams['embedInline'] == '1') 2321 { 2322 editorUi.sendEmbeddedSvgExport(); 2323 } 2324 else 2325 { 2326 var fn = function() 2327 { 2328 editorUi.editor.modified = false; 2329 var msg = (urlParams['proto'] == 'json') ? JSON.stringify({event: 'exit', 2330 modified: editorUi.editor.modified}) : ''; 2331 var parent = window.opener || window.parent; 2332 parent.postMessage(msg, '*'); 2333 } 2334 2335 if (!editorUi.editor.modified) 2336 { 2337 fn(); 2338 } 2339 else 2340 { 2341 editorUi.confirm(mxResources.get('allChangesLost'), null, fn, 2342 mxResources.get('cancel'), mxResources.get('discardChanges')); 2343 } 2344 } 2345 }); 2346 } 2347 2348 this.put('exportAs', new Menu(mxUtils.bind(this, function(menu, parent) 2349 { 2350 if (editorUi.isExportToCanvas()) 2351 { 2352 this.addMenuItems(menu, ['exportPng'], parent); 2353 2354 if (editorUi.jpgSupported) 2355 { 2356 this.addMenuItems(menu, ['exportJpg'], parent); 2357 } 2358 } 2359 2360 // Disabled for standalone mode in iOS because new tab cannot be closed 2361 else if (!editorUi.isOffline() && (!mxClient.IS_IOS || !navigator.standalone)) 2362 { 2363 this.addMenuItems(menu, ['exportPng', 'exportJpg'], parent); 2364 } 2365 2366 this.addMenuItems(menu, ['exportSvg', '-'], parent); 2367 2368 // Redirects export to PDF to print in Chrome App 2369 if (editorUi.isOffline() || editorUi.printPdfExport) 2370 { 2371 this.addMenuItems(menu, ['exportPdf'], parent); 2372 } 2373 // Disabled for standalone mode in iOS because new tab cannot be closed 2374 else if (!editorUi.isOffline() && (!mxClient.IS_IOS || !navigator.standalone)) 2375 { 2376 this.addMenuItems(menu, ['exportPdf'], parent); 2377 } 2378 2379 if (!mxClient.IS_IE && (typeof(VsdxExport) !== 'undefined' || !editorUi.isOffline())) 2380 { 2381 this.addMenuItems(menu, ['exportVsdx'], parent); 2382 } 2383 2384 this.addMenuItems(menu, ['-', 'exportHtml', 'exportXml', 'exportUrl'], parent); 2385 2386 if (!editorUi.isOffline()) 2387 { 2388 menu.addSeparator(parent); 2389 this.addMenuItem(menu, 'export', parent).firstChild.nextSibling.innerHTML = mxResources.get('advanced') + '...'; 2390 } 2391 }))); 2392 2393 this.put('importFrom', new Menu(mxUtils.bind(this, function(menu, parent) 2394 { 2395 var doImportFile = mxUtils.bind(this, function(data, mime, filename) 2396 { 2397 // Gets insert location 2398 var view = graph.view; 2399 var bds = graph.getGraphBounds(); 2400 var x = graph.snap(Math.ceil(Math.max(0, bds.x / view.scale - view.translate.x) + 4 * graph.gridSize)); 2401 var y = graph.snap(Math.ceil(Math.max(0, (bds.y + bds.height) / view.scale - view.translate.y) + 4 * graph.gridSize)); 2402 2403 if (data.substring(0, 11) == 'data:image/') 2404 { 2405 editorUi.loadImage(data, mxUtils.bind(this, function(img) 2406 { 2407 var resizeImages = true; 2408 2409 var doInsert = mxUtils.bind(this, function() 2410 { 2411 editorUi.resizeImage(img, data, mxUtils.bind(this, function(data2, w2, h2) 2412 { 2413 var s = (resizeImages) ? Math.min(1, Math.min(editorUi.maxImageSize / w2, editorUi.maxImageSize / h2)) : 1; 2414 2415 editorUi.importFile(data, mime, x, y, Math.round(w2 * s), Math.round(h2 * s), filename, function(cells) 2416 { 2417 editorUi.spinner.stop(); 2418 graph.setSelectionCells(cells); 2419 graph.scrollCellToVisible(graph.getSelectionCell()); 2420 }); 2421 }), resizeImages); 2422 }); 2423 2424 if (data.length > editorUi.resampleThreshold) 2425 { 2426 editorUi.confirmImageResize(function(doResize) 2427 { 2428 resizeImages = doResize; 2429 doInsert(); 2430 }); 2431 } 2432 else 2433 { 2434 doInsert(); 2435 } 2436 }), mxUtils.bind(this, function() 2437 { 2438 editorUi.handleError({message: mxResources.get('cannotOpenFile')}); 2439 })); 2440 } 2441 else 2442 { 2443 editorUi.importFile(data, mime, x, y, 0, 0, filename, function(cells) 2444 { 2445 editorUi.spinner.stop(); 2446 graph.setSelectionCells(cells); 2447 graph.scrollCellToVisible(graph.getSelectionCell()); 2448 }); 2449 } 2450 }); 2451 2452 var getMimeType = mxUtils.bind(this, function(filename) 2453 { 2454 var mime = 'text/xml'; 2455 2456 if (/\.png$/i.test(filename)) 2457 { 2458 mime = 'image/png'; 2459 } 2460 else if (/\.jpe?g$/i.test(filename)) 2461 { 2462 mime = 'image/jpg'; 2463 } 2464 else if (/\.gif$/i.test(filename)) 2465 { 2466 mime = 'image/gif'; 2467 } 2468 else if (/\.pdf$/i.test(filename)) 2469 { 2470 mime = 'application/pdf'; 2471 } 2472 2473 return mime; 2474 }); 2475 2476 function pickFileFromService(service) 2477 { 2478 // Drive requires special arguments for libraries and bypassing realtime 2479 service.pickFile(function(id) 2480 { 2481 if (editorUi.spinner.spin(document.body, mxResources.get('loading'))) 2482 { 2483 // NOTE The third argument in getFile says denyConvert to match 2484 // the existing signature in the original DriveClient which has 2485 // as slightly different semantic, but works the same way. 2486 service.getFile(id, function(file) 2487 { 2488 var mime = (file.getData().substring(0, 11) == 'data:image/') ? getMimeType(file.getTitle()) : 'text/xml'; 2489 2490 // Imports SVG as images 2491 if (/\.svg$/i.test(file.getTitle()) && !editorUi.editor.isDataSvg(file.getData())) 2492 { 2493 file.setData(Editor.createSvgDataUri(file.getData())); 2494 mime = 'image/svg+xml'; 2495 } 2496 2497 doImportFile(file.getData(), mime, file.getTitle()); 2498 }, 2499 function(resp) 2500 { 2501 editorUi.handleError(resp, (resp != null) ? mxResources.get('errorLoadingFile') : null); 2502 }, service == editorUi.drive); 2503 } 2504 }, true); 2505 }; 2506 2507 if (typeof(google) != 'undefined' && typeof(google.picker) != 'undefined') 2508 { 2509 if (editorUi.drive != null) 2510 { 2511 // Requires special arguments for libraries and realtime 2512 menu.addItem(mxResources.get('googleDrive') + '...', null, function() 2513 { 2514 pickFileFromService(editorUi.drive); 2515 }, parent); 2516 } 2517 else if (googleEnabled && typeof window.DriveClient === 'function') 2518 { 2519 menu.addItem(mxResources.get('googleDrive') + ' (' + mxResources.get('loading') + '...)', null, function() 2520 { 2521 // do nothing 2522 }, parent, null, false); 2523 } 2524 } 2525 2526 if (editorUi.oneDrive != null) 2527 { 2528 menu.addItem(mxResources.get('oneDrive') + '...', null, function() 2529 { 2530 pickFileFromService(editorUi.oneDrive); 2531 }, parent); 2532 } 2533 else if (oneDriveEnabled && typeof window.OneDriveClient === 'function') 2534 { 2535 menu.addItem(mxResources.get('oneDrive') + ' (' + mxResources.get('loading') + '...)', null, function() 2536 { 2537 // do nothing 2538 }, parent, null, false); 2539 } 2540 2541 if (editorUi.dropbox != null) 2542 { 2543 menu.addItem(mxResources.get('dropbox') + '...', null, function() 2544 { 2545 pickFileFromService(editorUi.dropbox); 2546 }, parent); 2547 } 2548 else if (dropboxEnabled && typeof window.DropboxClient === 'function') 2549 { 2550 menu.addItem(mxResources.get('dropbox') + ' (' + mxResources.get('loading') + '...)', null, function() 2551 { 2552 // do nothing 2553 }, parent, null, false); 2554 } 2555 2556 menu.addSeparator(parent); 2557 2558 if (editorUi.gitHub != null) 2559 { 2560 menu.addItem(mxResources.get('github') + '...', null, function() 2561 { 2562 pickFileFromService(editorUi.gitHub); 2563 }, parent); 2564 } 2565 2566 if (editorUi.gitLab != null) 2567 { 2568 menu.addItem(mxResources.get('gitlab') + '...', null, function() 2569 { 2570 pickFileFromService(editorUi.gitLab); 2571 }, parent); 2572 } 2573 2574 if (editorUi.notion != null) 2575 { 2576 menu.addSeparator(parent); 2577 menu.addItem(mxResources.get('notion') + '...', null, function() 2578 { 2579 pickFileFromService(editorUi.notion); 2580 }, parent); 2581 } 2582 2583 if (editorUi.trello != null) 2584 { 2585 menu.addItem(mxResources.get('trello') + '...', null, function() 2586 { 2587 pickFileFromService(editorUi.trello); 2588 }, parent); 2589 } 2590 else if (trelloEnabled && typeof window.TrelloClient === 'function') 2591 { 2592 menu.addItem(mxResources.get('trello') + ' (' + mxResources.get('loading') + '...)', null, function() 2593 { 2594 // do nothing 2595 }, parent, null, false); 2596 } 2597 2598 menu.addSeparator(parent); 2599 2600 if (isLocalStorage && urlParams['browser'] != '0') 2601 { 2602 menu.addItem(mxResources.get('browser') + '...', null, function() 2603 { 2604 editorUi.importLocalFile(false); 2605 }, parent); 2606 } 2607 2608 if (urlParams['noDevice'] != '1') 2609 { 2610 menu.addItem(mxResources.get('device') + '...', null, function() 2611 { 2612 editorUi.importLocalFile(true); 2613 }, parent); 2614 } 2615 2616 if (!editorUi.isOffline()) 2617 { 2618 menu.addSeparator(parent); 2619 2620 menu.addItem(mxResources.get('url') + '...', null, function() 2621 { 2622 var dlg = new FilenameDialog(editorUi, '', mxResources.get('import'), function(fileUrl) 2623 { 2624 if (fileUrl != null && fileUrl.length > 0 && editorUi.spinner.spin(document.body, mxResources.get('loading'))) 2625 { 2626 var mime = (/(\.png)($|\?)/i.test(fileUrl)) ? 'image/png' : 'text/xml'; 2627 2628 // Uses proxy to avoid CORS issues 2629 editorUi.editor.loadUrl(PROXY_URL + '?url=' + encodeURIComponent(fileUrl), function(data) 2630 { 2631 doImportFile(data, mime, fileUrl); 2632 }, 2633 function () 2634 { 2635 editorUi.spinner.stop(); 2636 editorUi.handleError(null, mxResources.get('errorLoadingFile')); 2637 }, mime == 'image/png'); 2638 } 2639 }, mxResources.get('url')); 2640 editorUi.showDialog(dlg.container, 300, 80, true, true); 2641 dlg.init(); 2642 }, parent); 2643 } 2644 }))).isEnabled = isGraphEnabled; 2645 2646 this.put('theme', new Menu(mxUtils.bind(this, function(menu, parent) 2647 { 2648 var theme = (urlParams['sketch'] == '1') ? 'sketch' : mxSettings.getUi(); 2649 2650 var item = menu.addItem(mxResources.get('automatic'), null, function() 2651 { 2652 mxSettings.setUi(''); 2653 editorUi.alert(mxResources.get('restartForChangeRequired')); 2654 }, parent); 2655 2656 if (theme != 'kennedy' && theme != 'atlas' && 2657 theme != 'dark' && theme != 'min' && 2658 theme != 'sketch') 2659 { 2660 menu.addCheckmark(item, Editor.checkmarkImage); 2661 } 2662 2663 menu.addSeparator(parent); 2664 2665 item = menu.addItem(mxResources.get('default'), null, function() 2666 { 2667 mxSettings.setUi('kennedy'); 2668 editorUi.alert(mxResources.get('restartForChangeRequired')); 2669 }, parent); 2670 2671 if (theme == 'kennedy') 2672 { 2673 menu.addCheckmark(item, Editor.checkmarkImage); 2674 } 2675 2676 item = menu.addItem(mxResources.get('minimal'), null, function() 2677 { 2678 mxSettings.setUi('min'); 2679 editorUi.alert(mxResources.get('restartForChangeRequired')); 2680 }, parent); 2681 2682 if (theme == 'min') 2683 { 2684 menu.addCheckmark(item, Editor.checkmarkImage); 2685 } 2686 2687 item = menu.addItem(mxResources.get('atlas'), null, function() 2688 { 2689 mxSettings.setUi('atlas'); 2690 editorUi.alert(mxResources.get('restartForChangeRequired')); 2691 }, parent); 2692 2693 if (theme == 'atlas') 2694 { 2695 menu.addCheckmark(item, Editor.checkmarkImage); 2696 } 2697 2698 if (theme == 'dark' || (!mxClient.IS_IE && !mxClient.IS_IE11)) 2699 { 2700 item = menu.addItem(mxResources.get('dark'), null, function() 2701 { 2702 mxSettings.setUi('dark'); 2703 editorUi.alert(mxResources.get('restartForChangeRequired')); 2704 }, parent); 2705 2706 if (theme == 'dark') 2707 { 2708 menu.addCheckmark(item, Editor.checkmarkImage); 2709 } 2710 } 2711 2712 menu.addSeparator(parent); 2713 2714 item = menu.addItem(mxResources.get('sketch'), null, function() 2715 { 2716 mxSettings.setUi('sketch'); 2717 editorUi.alert(mxResources.get('restartForChangeRequired')); 2718 }, parent); 2719 2720 if (theme == 'sketch') 2721 { 2722 menu.addCheckmark(item, Editor.checkmarkImage); 2723 } 2724 }))); 2725 2726 var renameAction = this.editorUi.actions.addAction('rename...', mxUtils.bind(this, function() 2727 { 2728 var file = this.editorUi.getCurrentFile(); 2729 2730 if (file != null) 2731 { 2732 if (file.constructor == LocalFile && file.fileHandle != null) 2733 { 2734 editorUi.showSaveFilePicker(mxUtils.bind(editorUi, function(fileHandle, desc) 2735 { 2736 file.invalidFileHandle = null; 2737 file.fileHandle = fileHandle; 2738 file.title = desc.name; 2739 file.desc = desc; 2740 editorUi.save(desc.name); 2741 }), null, editorUi.createFileSystemOptions(file.getTitle())); 2742 } 2743 else 2744 { 2745 var filename = (file.getTitle() != null) ? file.getTitle() : this.editorUi.defaultFilename; 2746 2747 var dlg = new FilenameDialog(this.editorUi, filename, mxResources.get('rename'), mxUtils.bind(this, function(title) 2748 { 2749 if (title != null && title.length > 0 && file != null && title != file.getTitle() && 2750 this.editorUi.spinner.spin(document.body, mxResources.get('renaming'))) 2751 { 2752 // Delete old file, save new file in dropbox if autosize is enabled 2753 file.rename(title, mxUtils.bind(this, function(resp) 2754 { 2755 this.editorUi.spinner.stop(); 2756 }), 2757 mxUtils.bind(this, function(resp) 2758 { 2759 this.editorUi.handleError(resp, (resp != null) ? mxResources.get('errorRenamingFile') : null); 2760 })); 2761 } 2762 }), (file.constructor == DriveFile || file.constructor == StorageFile) ? 2763 mxResources.get('diagramName') : null, function(name) 2764 { 2765 if (name != null && name.length > 0) 2766 { 2767 return true; 2768 } 2769 2770 editorUi.showError(mxResources.get('error'), mxResources.get('invalidName'), mxResources.get('ok')); 2771 2772 return false; 2773 }, null, null, null, null, editorUi.editor.fileExtensions); 2774 this.editorUi.showDialog(dlg.container, 340, 96, true, true); 2775 dlg.init(); 2776 } 2777 } 2778 })); 2779 2780 renameAction.isEnabled = function() 2781 { 2782 return this.enabled && isGraphEnabled.apply(this, arguments); 2783 } 2784 2785 renameAction.visible = urlParams['embed'] != '1'; 2786 2787 editorUi.actions.addAction('makeCopy...', mxUtils.bind(this, function() 2788 { 2789 var file = editorUi.getCurrentFile(); 2790 2791 if (file != null) 2792 { 2793 var title = editorUi.getCopyFilename(file); 2794 2795 if (file.constructor == DriveFile) 2796 { 2797 var dlg = new CreateDialog(editorUi, title, mxUtils.bind(this, function(newTitle, mode) 2798 { 2799 if (mode == '_blank') 2800 { 2801 editorUi.editor.editAsNew(editorUi.getFileData(), newTitle); 2802 } 2803 else 2804 { 2805 // Mode is "download" if Create button is pressed, means use Google Drive 2806 if (mode == 'download') 2807 { 2808 mode = App.MODE_GOOGLE; 2809 } 2810 2811 if (newTitle != null && newTitle.length > 0) 2812 { 2813 if (mode == App.MODE_GOOGLE) 2814 { 2815 if (editorUi.spinner.spin(document.body, mxResources.get('saving'))) 2816 { 2817 // Saveas does not update the file descriptor in Google Drive 2818 file.saveAs(newTitle, mxUtils.bind(this, function(resp) 2819 { 2820 // Replaces file descriptor in-place and saves 2821 file.desc = resp; 2822 2823 // Makes sure the latest XML is in the file 2824 file.save(false, mxUtils.bind(this, function() 2825 { 2826 editorUi.spinner.stop(); 2827 file.setModified(false); 2828 file.addAllSavedStatus(); 2829 }), mxUtils.bind(this, function(resp) 2830 { 2831 editorUi.handleError(resp); 2832 })); 2833 }), mxUtils.bind(this, function(resp) 2834 { 2835 editorUi.handleError(resp); 2836 })); 2837 } 2838 } 2839 else 2840 { 2841 editorUi.createFile(newTitle, editorUi.getFileData(true), null, mode); 2842 } 2843 } 2844 } 2845 }), mxUtils.bind(this, function() 2846 { 2847 editorUi.hideDialog(); 2848 }), mxResources.get('makeCopy'), mxResources.get('create'), null, 2849 null, true, null, true, null, null, null, null, 2850 editorUi.editor.fileExtensions); 2851 editorUi.showDialog(dlg.container, 420, 380, true, true); 2852 dlg.init(); 2853 } 2854 else 2855 { 2856 // Creates a copy with no predefined storage 2857 editorUi.editor.editAsNew(this.editorUi.getFileData(true), title); 2858 } 2859 } 2860 })); 2861 2862 editorUi.actions.addAction('moveToFolder...', mxUtils.bind(this, function() 2863 { 2864 var file = editorUi.getCurrentFile(); 2865 2866 if (file.getMode() == App.MODE_GOOGLE || file.getMode() == App.MODE_ONEDRIVE) 2867 { 2868 var isInRoot = false; 2869 2870 if (file.getMode() == App.MODE_GOOGLE && file.desc.parents != null) 2871 { 2872 for (var i = 0; i < file.desc.parents.length; i++) 2873 { 2874 if (file.desc.parents[i].isRoot) 2875 { 2876 isInRoot = true; 2877 break; 2878 } 2879 } 2880 } 2881 2882 editorUi.pickFolder(file.getMode(), mxUtils.bind(this, function(folderId) 2883 { 2884 if (editorUi.spinner.spin(document.body, mxResources.get('moving'))) 2885 { 2886 file.move(folderId, mxUtils.bind(this, function(resp) 2887 { 2888 editorUi.spinner.stop(); 2889 }), mxUtils.bind(this, function(resp) 2890 { 2891 editorUi.handleError(resp); 2892 })); 2893 } 2894 }), null, true, isInRoot); 2895 } 2896 })); 2897 2898 this.put('publish', new Menu(mxUtils.bind(this, function(menu, parent) 2899 { 2900 this.addMenuItems(menu, ['publishLink'], parent); 2901 }))); 2902 2903 editorUi.actions.put('useOffline', new Action(mxResources.get('useOffline') + '...', function() 2904 { 2905 editorUi.openLink('https://app.draw.io/') 2906 })); 2907 2908 editorUi.actions.put('downloadDesktop', new Action(mxResources.get('downloadDesktop') + '...', function() 2909 { 2910 editorUi.openLink('https://get.draw.io/') 2911 })); 2912 2913 this.editorUi.actions.addAction('share...', mxUtils.bind(this, function() 2914 { 2915 try 2916 { 2917 var file = editorUi.getCurrentFile(); 2918 2919 if (file != null) 2920 { 2921 file.share(); 2922 } 2923 } 2924 catch (e) 2925 { 2926 editorUi.handleError(e); 2927 } 2928 })); 2929 2930 this.put('embed', new Menu(mxUtils.bind(this, function(menu, parent) 2931 { 2932 var file = editorUi.getCurrentFile(); 2933 2934 if (file != null && (file.getMode() == App.MODE_GOOGLE || 2935 file.getMode() == App.MODE_GITHUB) && /(\.png)$/i.test(file.getTitle())) 2936 { 2937 this.addMenuItems(menu, ['liveImage', '-'], parent); 2938 } 2939 2940 this.addMenuItems(menu, ['embedImage', 'embedSvg', '-', 'embedHtml'], parent); 2941 2942 if (!navigator.standalone && !editorUi.isOffline()) 2943 { 2944 this.addMenuItems(menu, ['embedIframe'], parent); 2945 } 2946 2947 if (urlParams['embed'] != '1' && !editorUi.isOffline()) 2948 { 2949 this.addMenuItems(menu, ['-', 'googleDocs', 'googleSlides', 'googleSheets', '-', 'microsoftOffice', '-', 'embedNotion'], parent); 2950 } 2951 }))); 2952 2953 editorUi.addInsertItem = function(menu, parent, title, method) 2954 { 2955 if (method != 'plantUml' || (EditorUi.enablePlantUml && !editorUi.isOffline())) 2956 { 2957 menu.addItem(title, null, mxUtils.bind(this, function() 2958 { 2959 if (method == 'fromText' || method == 'formatSql' || 2960 method == 'plantUml' || method == 'mermaid') 2961 { 2962 var dlg = new ParseDialog(editorUi, title, method); 2963 editorUi.showDialog(dlg.container, 620, 420, true, false); 2964 editorUi.dialog.container.style.overflow = 'auto'; 2965 dlg.init(); 2966 } 2967 else 2968 { 2969 var dlg = new CreateGraphDialog(editorUi, title, method); 2970 editorUi.showDialog(dlg.container, 620, 420, true, false); 2971 // Executed after dialog is added to dom 2972 dlg.init(); 2973 } 2974 }), parent, null, isGraphEnabled()); 2975 } 2976 }; 2977 2978 var insertVertex = function(value, w, h, style) 2979 { 2980 var cell = new mxCell(value, new mxGeometry(0, 0, w, h), style); 2981 cell.vertex = true; 2982 2983 var pt = graph.getCenterInsertPoint(graph.getBoundingBoxFromGeometry([cell], true)); 2984 cell.geometry.x = pt.x; 2985 cell.geometry.y = pt.y; 2986 2987 graph.getModel().beginUpdate(); 2988 try 2989 { 2990 cell = graph.addCell(cell); 2991 graph.fireEvent(new mxEventObject('cellsInserted', 'cells', [cell])); 2992 } 2993 finally 2994 { 2995 graph.getModel().endUpdate(); 2996 } 2997 2998 graph.scrollCellToVisible(cell); 2999 graph.setSelectionCell(cell); 3000 graph.container.focus(); 3001 3002 if (graph.editAfterInsert) 3003 { 3004 graph.startEditing(cell); 3005 } 3006 3007 // Async call is workaroun for touch events resetting hover icons 3008 window.setTimeout(function() 3009 { 3010 if (editorUi.hoverIcons != null) 3011 { 3012 editorUi.hoverIcons.update(graph.view.getState(cell)); 3013 } 3014 }, 0); 3015 3016 return cell; 3017 }; 3018 3019 editorUi.actions.put('insertText', new Action(mxResources.get('text'), function() 3020 { 3021 if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) 3022 { 3023 graph.startEditingAtCell(insertVertex('Text', 40, 20, 'text;html=1;resizable=0;autosize=1;' + 3024 'align=center;verticalAlign=middle;points=[];fillColor=none;strokeColor=none;rounded=0;')); 3025 } 3026 }), null, null, Editor.ctrlKey + '+Shift+X').isEnabled = isGraphEnabled; 3027 3028 editorUi.actions.put('insertRectangle', new Action(mxResources.get('rectangle'), function() 3029 { 3030 if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) 3031 { 3032 insertVertex('', 120, 60, 'whiteSpace=wrap;html=1;'); 3033 } 3034 }), null, null, Editor.ctrlKey + '+K').isEnabled = isGraphEnabled; 3035 3036 editorUi.actions.put('insertEllipse', new Action(mxResources.get('ellipse'), function() 3037 { 3038 if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) 3039 { 3040 insertVertex('', 80, 80, 'ellipse;whiteSpace=wrap;html=1;'); 3041 } 3042 }), null, null, Editor.ctrlKey + '+Shift+K').isEnabled = isGraphEnabled; 3043 3044 editorUi.actions.put('insertRhombus', new Action(mxResources.get('rhombus'), function() 3045 { 3046 if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) 3047 { 3048 insertVertex('', 80, 80, 'rhombus;whiteSpace=wrap;html=1;'); 3049 } 3050 })).isEnabled = isGraphEnabled; 3051 3052 editorUi.addInsertMenuItems = mxUtils.bind(this, function(menu, parent, methods) 3053 { 3054 for (var i = 0; i < methods.length; i++) 3055 { 3056 if (methods[i] == '-') 3057 { 3058 menu.addSeparator(parent); 3059 } 3060 else 3061 { 3062 editorUi.addInsertItem(menu, parent, mxResources.get(methods[i]) + '...', methods[i]); 3063 } 3064 } 3065 }); 3066 3067 this.put('insert', new Menu(mxUtils.bind(this, function(menu, parent) 3068 { 3069 this.addMenuItems(menu, ['insertRectangle', 'insertEllipse', 3070 'insertRhombus', '-', 'insertText', 'insertLink', '-', 3071 'createShape', 'insertFreehand', '-', 'insertImage'], parent); 3072 3073 if (editorUi.insertTemplateEnabled && !editorUi.isOffline()) 3074 { 3075 this.addMenuItems(menu, ['insertTemplate'], parent); 3076 } 3077 3078 menu.addSeparator(parent); 3079 this.addSubmenu('insertLayout', menu, parent, mxResources.get('layout')); 3080 this.addSubmenu('insertAdvanced', menu, parent, mxResources.get('advanced')); 3081 }))); 3082 3083 this.put('insertLayout', new Menu(mxUtils.bind(this, function(menu, parent) 3084 { 3085 editorUi.addInsertMenuItems(menu, parent, ['horizontalFlow', 'verticalFlow', '-', 'horizontalTree', 3086 'verticalTree', 'radialTree', '-', 'organic', 'circle']); 3087 }))); 3088 3089 this.put('insertAdvanced', new Menu(mxUtils.bind(this, function(menu, parent) 3090 { 3091 editorUi.addInsertMenuItems(menu, parent, ['fromText', 'plantUml', 'mermaid', '-', 'formatSql']); 3092 3093 menu.addItem(mxResources.get('csv') + '...', null, function() 3094 { 3095 editorUi.showImportCsvDialog(); 3096 }, parent, null, isGraphEnabled()); 3097 }))); 3098 3099 this.put('openRecent', new Menu(function(menu, parent) 3100 { 3101 var recent = editorUi.getRecent(); 3102 3103 if (recent != null) 3104 { 3105 for (var i = 0; i < recent.length; i++) 3106 { 3107 (function(entry) 3108 { 3109 var modeKey = entry.mode; 3110 3111 // Google and oneDrive use different keys 3112 if (modeKey == App.MODE_GOOGLE) 3113 { 3114 modeKey = 'googleDrive'; 3115 } 3116 else if (modeKey == App.MODE_ONEDRIVE) 3117 { 3118 modeKey = 'oneDrive'; 3119 } 3120 3121 menu.addItem(entry.title + ' (' + mxResources.get(modeKey) + ')', null, function() 3122 { 3123 editorUi.loadFile(entry.id); 3124 }, parent); 3125 })(recent[i]); 3126 } 3127 3128 menu.addSeparator(parent); 3129 } 3130 3131 menu.addItem(mxResources.get('reset'), null, function() 3132 { 3133 editorUi.resetRecent(); 3134 }, parent); 3135 })); 3136 3137 this.put('openFrom', new Menu(function(menu, parent) 3138 { 3139 if (editorUi.drive != null) 3140 { 3141 menu.addItem(mxResources.get('googleDrive') + '...', null, function() 3142 { 3143 editorUi.pickFile(App.MODE_GOOGLE); 3144 }, parent); 3145 } 3146 else if (googleEnabled && typeof window.DriveClient === 'function') 3147 { 3148 menu.addItem(mxResources.get('googleDrive') + ' (' + mxResources.get('loading') + '...)', null, function() 3149 { 3150 // do nothing 3151 }, parent, null, false); 3152 } 3153 3154 if (editorUi.oneDrive != null) 3155 { 3156 menu.addItem(mxResources.get('oneDrive') + '...', null, function() 3157 { 3158 editorUi.pickFile(App.MODE_ONEDRIVE); 3159 }, parent); 3160 } 3161 else if (oneDriveEnabled && typeof window.OneDriveClient === 'function') 3162 { 3163 menu.addItem(mxResources.get('oneDrive') + ' (' + mxResources.get('loading') + '...)', null, function() 3164 { 3165 // do nothing 3166 }, parent, null, false); 3167 } 3168 3169 if (editorUi.dropbox != null) 3170 { 3171 menu.addItem(mxResources.get('dropbox') + '...', null, function() 3172 { 3173 editorUi.pickFile(App.MODE_DROPBOX); 3174 }, parent); 3175 } 3176 else if (dropboxEnabled && typeof window.DropboxClient === 'function') 3177 { 3178 menu.addItem(mxResources.get('dropbox') + ' (' + mxResources.get('loading') + '...)', null, function() 3179 { 3180 // do nothing 3181 }, parent, null, false); 3182 } 3183 3184 menu.addSeparator(parent); 3185 3186 if (editorUi.gitHub != null) 3187 { 3188 menu.addItem(mxResources.get('github') + '...', null, function() 3189 { 3190 editorUi.pickFile(App.MODE_GITHUB); 3191 }, parent); 3192 } 3193 3194 if (editorUi.gitLab != null) 3195 { 3196 menu.addItem(mxResources.get('gitlab') + '...', null, function() 3197 { 3198 editorUi.pickFile(App.MODE_GITLAB); 3199 }, parent); 3200 } 3201 3202 if (editorUi.notion != null) 3203 { 3204 menu.addSeparator(parent); 3205 menu.addItem(mxResources.get('notion') + '...', null, function() 3206 { 3207 editorUi.pickFile(App.MODE_NOTION); 3208 }, parent); 3209 } 3210 3211 if (editorUi.trello != null) 3212 { 3213 menu.addItem(mxResources.get('trello') + '...', null, function() 3214 { 3215 editorUi.pickFile(App.MODE_TRELLO); 3216 }, parent); 3217 } 3218 else if (trelloEnabled && typeof window.TrelloClient === 'function') 3219 { 3220 menu.addItem(mxResources.get('trello') + ' (' + mxResources.get('loading') + '...)', null, function() 3221 { 3222 // do nothing 3223 }, parent, null, false); 3224 } 3225 3226 menu.addSeparator(parent); 3227 3228 if (isLocalStorage && urlParams['browser'] != '0') 3229 { 3230 menu.addItem(mxResources.get('browser') + '...', null, function() 3231 { 3232 editorUi.pickFile(App.MODE_BROWSER); 3233 }, parent); 3234 } 3235 3236 //if (!mxClient.IS_IOS) 3237 if (urlParams['noDevice'] != '1') 3238 { 3239 menu.addItem(mxResources.get('device') + '...', null, function() 3240 { 3241 editorUi.pickFile(App.MODE_DEVICE); 3242 }, parent); 3243 } 3244 3245 if (!editorUi.isOffline()) 3246 { 3247 menu.addSeparator(parent); 3248 3249 menu.addItem(mxResources.get('url') + '...', null, function() 3250 { 3251 var dlg = new FilenameDialog(editorUi, '', mxResources.get('open'), function(fileUrl) 3252 { 3253 if (fileUrl != null && fileUrl.length > 0) 3254 { 3255 if (editorUi.getCurrentFile() == null) 3256 { 3257 window.location.hash = '#U' + encodeURIComponent(fileUrl); 3258 } 3259 else 3260 { 3261 window.openWindow(((mxClient.IS_CHROMEAPP) ? 3262 'https://www.draw.io/' : 'https://' + location.host + '/') + 3263 window.location.search + '#U' + encodeURIComponent(fileUrl)); 3264 } 3265 } 3266 }, mxResources.get('url')); 3267 editorUi.showDialog(dlg.container, 300, 80, true, true); 3268 dlg.init(); 3269 }, parent); 3270 } 3271 })); 3272 3273 if (Editor.enableCustomLibraries) 3274 { 3275 this.put('newLibrary', new Menu(function(menu, parent) 3276 { 3277 if (typeof(google) != 'undefined' && typeof(google.picker) != 'undefined') 3278 { 3279 if (editorUi.drive != null) 3280 { 3281 menu.addItem(mxResources.get('googleDrive') + '...', null, function() 3282 { 3283 editorUi.showLibraryDialog(null, null, null, null, App.MODE_GOOGLE); 3284 }, parent); 3285 } 3286 else if (googleEnabled && typeof window.DriveClient === 'function') 3287 { 3288 menu.addItem(mxResources.get('googleDrive') + ' (' + mxResources.get('loading') + '...)', null, function() 3289 { 3290 // do nothing 3291 }, parent, null, false); 3292 } 3293 } 3294 3295 if (editorUi.oneDrive != null) 3296 { 3297 menu.addItem(mxResources.get('oneDrive') + '...', null, function() 3298 { 3299 editorUi.showLibraryDialog(null, null, null, null, App.MODE_ONEDRIVE); 3300 }, parent); 3301 } 3302 else if (oneDriveEnabled && typeof window.OneDriveClient === 'function') 3303 { 3304 menu.addItem(mxResources.get('oneDrive') + ' (' + mxResources.get('loading') + '...)', null, function() 3305 { 3306 // do nothing 3307 }, parent, null, false); 3308 } 3309 3310 if (editorUi.dropbox != null) 3311 { 3312 menu.addItem(mxResources.get('dropbox') + '...', null, function() 3313 { 3314 editorUi.showLibraryDialog(null, null, null, null, App.MODE_DROPBOX); 3315 }, parent); 3316 } 3317 else if (dropboxEnabled && typeof window.DropboxClient === 'function') 3318 { 3319 menu.addItem(mxResources.get('dropbox') + ' (' + mxResources.get('loading') + '...)', null, function() 3320 { 3321 // do nothing 3322 }, parent, null, false); 3323 } 3324 3325 menu.addSeparator(parent); 3326 3327 if (editorUi.gitHub != null) 3328 { 3329 menu.addItem(mxResources.get('github') + '...', null, function() 3330 { 3331 editorUi.showLibraryDialog(null, null, null, null, App.MODE_GITHUB); 3332 }, parent); 3333 } 3334 3335 if (editorUi.gitLab != null) 3336 { 3337 menu.addItem(mxResources.get('gitlab') + '...', null, function() 3338 { 3339 editorUi.showLibraryDialog(null, null, null, null, App.MODE_GITLAB); 3340 }, parent); 3341 } 3342 3343 if (editorUi.notion != null) 3344 { 3345 menu.addSeparator(parent); 3346 menu.addItem(mxResources.get('notion') + '...', null, function() 3347 { 3348 editorUi.showLibraryDialog(null, null, null, null, App.MODE_NOTION); 3349 }, parent); 3350 } 3351 3352 if (editorUi.trello != null) 3353 { 3354 menu.addItem(mxResources.get('trello') + '...', null, function() 3355 { 3356 editorUi.showLibraryDialog(null, null, null, null, App.MODE_TRELLO); 3357 }, parent); 3358 } 3359 else if (trelloEnabled && typeof window.TrelloClient === 'function') 3360 { 3361 menu.addItem(mxResources.get('trello') + ' (' + mxResources.get('loading') + '...)', null, function() 3362 { 3363 // do nothing 3364 }, parent, null, false); 3365 } 3366 3367 menu.addSeparator(parent); 3368 3369 if (isLocalStorage && urlParams['browser'] != '0') 3370 { 3371 menu.addItem(mxResources.get('browser') + '...', null, function() 3372 { 3373 editorUi.showLibraryDialog(null, null, null, null, App.MODE_BROWSER); 3374 }, parent); 3375 } 3376 3377 //if (!mxClient.IS_IOS) 3378 if (urlParams['noDevice'] != '1') 3379 { 3380 menu.addItem(mxResources.get('device') + '...', null, function() 3381 { 3382 editorUi.showLibraryDialog(null, null, null, null, App.MODE_DEVICE); 3383 }, parent); 3384 } 3385 })); 3386 3387 this.put('openLibraryFrom', new Menu(function(menu, parent) 3388 { 3389 if (typeof(google) != 'undefined' && typeof(google.picker) != 'undefined') 3390 { 3391 if (editorUi.drive != null) 3392 { 3393 menu.addItem(mxResources.get('googleDrive') + '...', null, function() 3394 { 3395 editorUi.pickLibrary(App.MODE_GOOGLE); 3396 }, parent); 3397 } 3398 else if (googleEnabled && typeof window.DriveClient === 'function') 3399 { 3400 menu.addItem(mxResources.get('googleDrive') + ' (' + mxResources.get('loading') + '...)', null, function() 3401 { 3402 // do nothing 3403 }, parent, null, false); 3404 } 3405 } 3406 3407 if (editorUi.oneDrive != null) 3408 { 3409 menu.addItem(mxResources.get('oneDrive') + '...', null, function() 3410 { 3411 editorUi.pickLibrary(App.MODE_ONEDRIVE); 3412 }, parent); 3413 } 3414 else if (oneDriveEnabled && typeof window.OneDriveClient === 'function') 3415 { 3416 menu.addItem(mxResources.get('oneDrive') + ' (' + mxResources.get('loading') + '...)', null, function() 3417 { 3418 // do nothing 3419 }, parent, null, false); 3420 } 3421 3422 if (editorUi.dropbox != null) 3423 { 3424 menu.addItem(mxResources.get('dropbox') + '...', null, function() 3425 { 3426 editorUi.pickLibrary(App.MODE_DROPBOX); 3427 }, parent); 3428 } 3429 else if (dropboxEnabled && typeof window.DropboxClient === 'function') 3430 { 3431 menu.addItem(mxResources.get('dropbox') + ' (' + mxResources.get('loading') + '...)', null, function() 3432 { 3433 // do nothing 3434 }, parent, null, false); 3435 } 3436 3437 menu.addSeparator(parent); 3438 3439 if (editorUi.gitHub != null) 3440 { 3441 menu.addItem(mxResources.get('github') + '...', null, function() 3442 { 3443 editorUi.pickLibrary(App.MODE_GITHUB); 3444 }, parent); 3445 } 3446 3447 if (editorUi.gitLab != null) 3448 { 3449 menu.addItem(mxResources.get('gitlab') + '...', null, function() 3450 { 3451 editorUi.pickLibrary(App.MODE_GITLAB); 3452 }, parent); 3453 } 3454 3455 if (editorUi.notion != null) 3456 { 3457 menu.addSeparator(parent); 3458 menu.addItem(mxResources.get('notion') + '...', null, function() 3459 { 3460 editorUi.pickLibrary(App.MODE_NOTION); 3461 }, parent); 3462 } 3463 3464 if (editorUi.trello != null) 3465 { 3466 menu.addItem(mxResources.get('trello') + '...', null, function() 3467 { 3468 editorUi.pickLibrary(App.MODE_TRELLO); 3469 }, parent); 3470 } 3471 else if (trelloEnabled && typeof window.TrelloClient === 'function') 3472 { 3473 menu.addItem(mxResources.get('trello') + ' (' + mxResources.get('loading') + '...)', null, function() 3474 { 3475 // do nothing 3476 }, parent, null, false); 3477 } 3478 3479 menu.addSeparator(parent); 3480 3481 if (isLocalStorage && urlParams['browser'] != '0') 3482 { 3483 menu.addItem(mxResources.get('browser') + '...', null, function() 3484 { 3485 editorUi.pickLibrary(App.MODE_BROWSER); 3486 }, parent); 3487 } 3488 3489 //if (!mxClient.IS_IOS) 3490 if (urlParams['noDevice'] != '1') 3491 { 3492 menu.addItem(mxResources.get('device') + '...', null, function() 3493 { 3494 editorUi.pickLibrary(App.MODE_DEVICE); 3495 }, parent); 3496 } 3497 3498 if (!editorUi.isOffline()) 3499 { 3500 menu.addSeparator(parent); 3501 3502 menu.addItem(mxResources.get('url') + '...', null, function() 3503 { 3504 var dlg = new FilenameDialog(editorUi, '', mxResources.get('open'), function(fileUrl) 3505 { 3506 if (fileUrl != null && fileUrl.length > 0 && editorUi.spinner.spin(document.body, mxResources.get('loading'))) 3507 { 3508 var realUrl = fileUrl; 3509 3510 if (!editorUi.editor.isCorsEnabledForUrl(fileUrl)) 3511 { 3512 realUrl = PROXY_URL + '?url=' + encodeURIComponent(fileUrl); 3513 } 3514 3515 // Uses proxy to avoid CORS issues 3516 mxUtils.get(realUrl, function(req) 3517 { 3518 if (req.getStatus() >= 200 && req.getStatus() <= 299) 3519 { 3520 editorUi.spinner.stop(); 3521 3522 try 3523 { 3524 editorUi.loadLibrary(new UrlLibrary(this, req.getText(), fileUrl)); 3525 } 3526 catch (e) 3527 { 3528 editorUi.handleError(e, mxResources.get('errorLoadingFile')); 3529 } 3530 } 3531 else 3532 { 3533 editorUi.spinner.stop(); 3534 editorUi.handleError(null, mxResources.get('errorLoadingFile')); 3535 } 3536 }, function() 3537 { 3538 editorUi.spinner.stop(); 3539 editorUi.handleError(null, mxResources.get('errorLoadingFile')); 3540 }); 3541 } 3542 }, mxResources.get('url')); 3543 editorUi.showDialog(dlg.container, 300, 80, true, true); 3544 dlg.init(); 3545 }, parent); 3546 } 3547 3548 if (urlParams['confLib'] == '1') 3549 { 3550 menu.addSeparator(parent); 3551 3552 menu.addItem(mxResources.get('confluenceCloud') + '...', null, function() 3553 { 3554 editorUi.showRemotelyStoredLibrary(mxResources.get('libraries')); 3555 }, parent); 3556 } 3557 })); 3558 } 3559 3560 // Overrides edit menu to add find, copyAsImage editGeometry 3561 this.put('edit', new Menu(mxUtils.bind(this, function(menu, parent) 3562 { 3563 this.addMenuItems(menu, ['undo', 'redo', '-', 'cut', 'copy', 'copyAsImage', 'paste', 3564 'delete', '-', 'duplicate', '-', 'findReplace', '-', 'editData', 'editTooltip', '-', 3565 'editStyle', 'editGeometry', '-', 'edit', '-', 'editLink', 'openLink', '-', 3566 'selectVertices', 'selectEdges', 'selectAll', 'selectNone', '-', 'lockUnlock']); 3567 }))); 3568 3569 var action = editorUi.actions.addAction('comments', mxUtils.bind(this, function() 3570 { 3571 if (this.commentsWindow == null) 3572 { 3573 // LATER: Check outline window for initial placement 3574 this.commentsWindow = new CommentsWindow(editorUi, document.body.offsetWidth - 380, 120, 300, 350); 3575 //TODO Are these events needed? 3576 this.commentsWindow.window.addListener('show', function() 3577 { 3578 editorUi.fireEvent(new mxEventObject('comments')); 3579 }); 3580 this.commentsWindow.window.addListener('hide', function() 3581 { 3582 editorUi.fireEvent(new mxEventObject('comments')); 3583 }); 3584 this.commentsWindow.window.setVisible(true); 3585 editorUi.fireEvent(new mxEventObject('comments')); 3586 } 3587 else 3588 { 3589 var isVisible = !this.commentsWindow.window.isVisible(); 3590 this.commentsWindow.window.setVisible(isVisible); 3591 3592 this.commentsWindow.refreshCommentsTime(); 3593 3594 if (isVisible && this.commentsWindow.hasError) 3595 { 3596 this.commentsWindow.refreshComments(); 3597 } 3598 } 3599 })); 3600 action.setToggleAction(true); 3601 action.setSelectedCallback(mxUtils.bind(this, function() { return this.commentsWindow != null && this.commentsWindow.window.isVisible(); })); 3602 3603 // Destroys comments window to force update or disable if not supported 3604 editorUi.editor.addListener('fileLoaded', mxUtils.bind(this, function() 3605 { 3606 if (this.commentsWindow != null) 3607 { 3608 this.commentsWindow.destroy(); 3609 this.commentsWindow = null; 3610 } 3611 })); 3612 3613 // Extends toolbar dropdown to add comments 3614 var viewPanelsMenu = this.get('viewPanels'); 3615 var viewPanelsFunct = viewPanelsMenu.funct; 3616 3617 viewPanelsMenu.funct = function(menu, parent) 3618 { 3619 viewPanelsFunct.apply(this, arguments); 3620 3621 editorUi.menus.addMenuItems(menu, ['tags'], parent); 3622 3623 if (editorUi.commentsSupported()) 3624 { 3625 editorUi.menus.addMenuItems(menu, ['comments'], parent); 3626 } 3627 }; 3628 3629 // Overrides view menu to add search and scratchpad 3630 this.put('view', new Menu(mxUtils.bind(this, function(menu, parent) 3631 { 3632 this.addMenuItems(menu, ((this.editorUi.format != null) ? ['formatPanel'] : []). 3633 concat(['outline', 'layers', 'tags']).concat((editorUi.commentsSupported()) ? 3634 ['comments', '-'] : ['-'])); 3635 3636 this.addMenuItems(menu, ['-', 'search'], parent); 3637 3638 if (isLocalStorage || mxClient.IS_CHROMEAPP) 3639 { 3640 var item = this.addMenuItem(menu, 'scratchpad', parent); 3641 3642 if (!editorUi.isOffline() || mxClient.IS_CHROMEAPP || EditorUi.isElectronApp) 3643 { 3644 this.addLinkToItem(item, 'https://www.diagrams.net/doc/faq/scratchpad'); 3645 } 3646 } 3647 3648 this.addMenuItems(menu, ['shapes', '-', 'pageView', 'pageScale']); 3649 this.addSubmenu('units', menu, parent); 3650 this.addMenuItems(menu, ['-', 'scrollbars', 'tooltips', 'ruler', '-', 3651 'grid', 'guides'], parent); 3652 3653 if (mxClient.IS_SVG && (document.documentMode == null || document.documentMode > 9)) 3654 { 3655 this.addMenuItem(menu, 'shadowVisible', parent); 3656 } 3657 3658 this.addMenuItems(menu, ['-', 'connectionArrows', 'connectionPoints', '-', 3659 'resetView', 'zoomIn', 'zoomOut'], parent); 3660 3661 if (urlParams['sketch'] != '1') 3662 { 3663 this.addMenuItems(menu, ['-', 'fullscreen'], parent); 3664 } 3665 }))); 3666 3667 this.put('extras', new Menu(mxUtils.bind(this, function(menu, parent) 3668 { 3669 if (urlParams['noLangIcon'] == '1') 3670 { 3671 this.addSubmenu('language', menu, parent); 3672 menu.addSeparator(parent); 3673 } 3674 3675 if (urlParams['embed'] != '1') 3676 { 3677 this.addSubmenu('theme', menu, parent); 3678 menu.addSeparator(parent); 3679 } 3680 3681 if (typeof(MathJax) !== 'undefined') 3682 { 3683 var item = this.addMenuItem(menu, 'mathematicalTypesetting', parent); 3684 3685 if (!editorUi.isOffline() || mxClient.IS_CHROMEAPP || EditorUi.isElectronApp) 3686 { 3687 this.addLinkToItem(item, 'https://www.diagrams.net/doc/faq/math-typesetting'); 3688 } 3689 } 3690 3691 this.addMenuItems(menu, ['copyConnect', 'collapseExpand', '-'], parent); 3692 3693 if (urlParams['embed'] != '1' && (isLocalStorage || mxClient.IS_CHROMEAPP)) 3694 { 3695 this.addMenuItems(menu, ['showStartScreen'], parent); 3696 } 3697 3698 if (urlParams['embed'] != '1') 3699 { 3700 this.addMenuItems(menu, ['autosave'], parent); 3701 } 3702 3703 menu.addSeparator(parent); 3704 3705 if (!editorUi.isOfflineApp() && isLocalStorage) 3706 { 3707 this.addMenuItem(menu, 'plugins', parent); 3708 } 3709 3710 this.addMenuItems(menu, ['-', 'editDiagram'], parent); 3711 3712 if (Graph.translateDiagram) 3713 { 3714 this.addMenuItems(menu, ['diagramLanguage']); 3715 } 3716 3717 this.addMenuItems(menu, ['-', 'configuration'], parent); 3718 3719 // Adds trailing separator in case new plugin entries are added 3720 menu.addSeparator(parent); 3721 3722 if (urlParams['newTempDlg'] == '1') 3723 { 3724 editorUi.actions.addAction('templates', function() 3725 { 3726 function driveObjToTempDlg(item) 3727 { 3728 return {id: item.id, isExt: true, url: item.downloadUrl, title: item.title, imgUrl: item.thumbnailLink, 3729 changedBy: item.lastModifyingUserName, lastModifiedOn: item.modifiedDate} 3730 }; 3731 3732 var tempDlg = new TemplatesDialog(editorUi, function(xml){console.log(arguments)}, null, 3733 null, null, 'user', function(callback, error, username) 3734 { 3735 var oneWeek = new Date(); 3736 oneWeek.setDate(oneWeek.getDate() - 7); 3737 3738 editorUi.drive.listFiles(null, oneWeek, username? true : false, function(resp) 3739 { 3740 var results = []; 3741 3742 for (var i = 0; i < resp.items.length; i++) 3743 { 3744 results.push(driveObjToTempDlg(resp.items[i])); 3745 } 3746 3747 callback(results); 3748 }, error) 3749 }, function(str, callback, error, username) 3750 { 3751 editorUi.drive.listFiles(str, null, username? true : false, function(resp) 3752 { 3753 var results = []; 3754 3755 for (var i = 0; i < resp.items.length; i++) 3756 { 3757 results.push(driveObjToTempDlg(resp.items[i])); 3758 } 3759 3760 callback(results); 3761 }, error) 3762 }, function(obj, callback, error) 3763 { 3764 editorUi.drive.getFile(obj.id, function(file) 3765 { 3766 callback(file.data); 3767 }, error); 3768 }, null, function(callback) 3769 { 3770 callback({'Test': []}, 1); 3771 }, true, false); 3772 3773 editorUi.showDialog(tempDlg.container, window.innerWidth, window.innerHeight, true, false, null, false, true); 3774 }); 3775 this.addMenuItem(menu, 'templates', parent); 3776 } 3777 }))); 3778 3779 this.put('file', new Menu(mxUtils.bind(this, function(menu, parent) 3780 { 3781 if (urlParams['embed'] == '1') 3782 { 3783 this.addSubmenu('importFrom', menu, parent); 3784 this.addSubmenu('exportAs', menu, parent); 3785 this.addSubmenu('embed', menu, parent); 3786 3787 if (urlParams['libraries'] == '1') 3788 { 3789 this.addMenuItems(menu, ['-'], parent); 3790 this.addSubmenu('newLibrary', menu, parent); 3791 this.addSubmenu('openLibraryFrom', menu, parent); 3792 } 3793 3794 if (editorUi.isRevisionHistorySupported()) 3795 { 3796 this.addMenuItems(menu, ['-', 'revisionHistory'], parent); 3797 } 3798 3799 this.addMenuItems(menu, ['-', 'pageSetup', 'print', '-', 'rename'], parent); 3800 3801 if (urlParams['embedInline'] != '1') 3802 { 3803 if (urlParams['noSaveBtn'] == '1') 3804 { 3805 if (urlParams['saveAndExit'] != '0') 3806 { 3807 this.addMenuItems(menu, ['saveAndExit'], parent); 3808 } 3809 } 3810 else 3811 { 3812 this.addMenuItems(menu, ['save'], parent); 3813 3814 if (urlParams['saveAndExit'] == '1') 3815 { 3816 this.addMenuItems(menu, ['saveAndExit'], parent); 3817 } 3818 } 3819 } 3820 3821 if (urlParams['noExitBtn'] != '1') 3822 { 3823 this.addMenuItems(menu, ['exit'], parent); 3824 } 3825 } 3826 else 3827 { 3828 var file = this.editorUi.getCurrentFile(); 3829 3830 if (file != null && file.constructor == DriveFile) 3831 { 3832 if (file.isRestricted()) 3833 { 3834 this.addMenuItems(menu, ['exportOptionsDisabled'], parent); 3835 } 3836 3837 this.addMenuItems(menu, ['save', '-', 'share'], parent); 3838 3839 var item = this.addMenuItem(menu, 'synchronize', parent); 3840 3841 if (!editorUi.isOffline() || mxClient.IS_CHROMEAPP || EditorUi.isElectronApp) 3842 { 3843 this.addLinkToItem(item, 'https://www.diagrams.net/doc/faq/synchronize'); 3844 } 3845 3846 menu.addSeparator(parent); 3847 } 3848 else 3849 { 3850 this.addMenuItems(menu, ['new'], parent); 3851 } 3852 3853 this.addSubmenu('openFrom', menu, parent); 3854 3855 if (isLocalStorage) 3856 { 3857 this.addSubmenu('openRecent', menu, parent); 3858 } 3859 3860 if (file != null && file.constructor == DriveFile) 3861 { 3862 this.addMenuItems(menu, ['new', '-', 'rename', 'makeCopy', 'moveToFolder'], parent); 3863 } 3864 else 3865 { 3866 if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp && 3867 file != null && (file.constructor != LocalFile || 3868 file.fileHandle != null)) 3869 { 3870 menu.addSeparator(parent); 3871 var item = this.addMenuItem(menu, 'synchronize', parent); 3872 3873 if (!editorUi.isOffline() || mxClient.IS_CHROMEAPP || EditorUi.isElectronApp) 3874 { 3875 this.addLinkToItem(item, 'https://www.diagrams.net/doc/faq/synchronize'); 3876 } 3877 } 3878 3879 this.addMenuItems(menu, ['-', 'save', 'saveAs', '-'], parent); 3880 3881 if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp && 3882 editorUi.getServiceName() == 'draw.io' && 3883 !editorUi.isOfflineApp() && file != null) 3884 { 3885 this.addMenuItems(menu, ['share', '-'], parent); 3886 } 3887 3888 this.addMenuItems(menu, ['rename'], parent); 3889 3890 if (editorUi.isOfflineApp()) 3891 { 3892 if (navigator.onLine && urlParams['stealth'] != '1' && urlParams['lockdown'] != '1') 3893 { 3894 this.addMenuItems(menu, ['upload'], parent); 3895 } 3896 } 3897 else 3898 { 3899 this.addMenuItems(menu, ['makeCopy'], parent); 3900 3901 if (file != null && file.constructor == OneDriveFile) 3902 { 3903 this.addMenuItems(menu, ['moveToFolder'], parent); 3904 } 3905 } 3906 } 3907 3908 menu.addSeparator(parent); 3909 this.addSubmenu('importFrom', menu, parent); 3910 this.addSubmenu('exportAs', menu, parent); 3911 menu.addSeparator(parent); 3912 this.addSubmenu('embed', menu, parent); 3913 this.addSubmenu('publish', menu, parent); 3914 menu.addSeparator(parent); 3915 this.addSubmenu('newLibrary', menu, parent); 3916 this.addSubmenu('openLibraryFrom', menu, parent); 3917 3918 if (editorUi.isRevisionHistorySupported()) 3919 { 3920 this.addMenuItems(menu, ['-', 'revisionHistory'], parent); 3921 } 3922 3923 if (file != null && editorUi.fileNode != null && urlParams['embedInline'] != '1') 3924 { 3925 var filename = (file.getTitle() != null) ? 3926 file.getTitle() : editorUi.defaultFilename; 3927 3928 if (!/(\.html)$/i.test(filename) && 3929 !/(\.svg)$/i.test(filename)) 3930 { 3931 this.addMenuItems(menu, ['-', 'properties']); 3932 } 3933 } 3934 3935 this.addMenuItems(menu, ['-', 'pageSetup'], parent); 3936 3937 // Cannot use print in standalone mode on iOS as we cannot open new windows 3938 if (!mxClient.IS_IOS || !navigator.standalone) 3939 { 3940 this.addMenuItems(menu, ['print'], parent); 3941 } 3942 3943 this.addMenuItems(menu, ['-', 'close']); 3944 } 3945 }))); 3946 3947 /** 3948 * External Fonts undoable change 3949 */ 3950 function ChangeExtFonts(ui, extFonts, customFonts) 3951 { 3952 this.ui = ui; 3953 this.extFonts = extFonts; 3954 this.previousExtFonts = extFonts; 3955 this.customFonts = customFonts; 3956 this.prevCustomFonts = customFonts; 3957 }; 3958 3959 /** 3960 * Implementation of the undoable External Fonts Change. 3961 */ 3962 ChangeExtFonts.prototype.execute = function() 3963 { 3964 var graph = this.ui.editor.graph; 3965 this.customFonts = this.prevCustomFonts; 3966 this.prevCustomFonts = this.ui.menus.customFonts; 3967 this.ui.fireEvent(new mxEventObject('customFontsChanged', 'customFonts', this.customFonts)); 3968 3969 this.extFonts = this.previousExtFonts; 3970 var tmp = graph.extFonts; 3971 3972 for (var i = 0; tmp != null && i < tmp.length; i++) 3973 { 3974 var fontElem = document.getElementById('extFont_' + tmp[i].name); 3975 3976 if (fontElem != null) 3977 { 3978 fontElem.parentNode.removeChild(fontElem); 3979 } 3980 } 3981 3982 graph.extFonts = []; 3983 3984 for (var i = 0; this.previousExtFonts != null && i < this.previousExtFonts.length; i++) 3985 { 3986 this.ui.editor.graph.addExtFont(this.previousExtFonts[i].name, this.previousExtFonts[i].url); 3987 } 3988 3989 this.previousExtFonts = tmp; 3990 }; 3991 3992 //Replace the default font family menu 3993 this.put('fontFamily', new Menu(mxUtils.bind(this, function(menu, parent) 3994 { 3995 var addItem = mxUtils.bind(this, function(fontName, fontUrl, deletable, fontLabel, tooltip) 3996 { 3997 var graph = editorUi.editor.graph; 3998 3999 var tr = this.styleChange(menu, fontLabel || fontName, 4000 (urlParams['ext-fonts'] != '1') ? 4001 [mxConstants.STYLE_FONTFAMILY, 'fontSource', 'FType'] : [mxConstants.STYLE_FONTFAMILY], 4002 (urlParams['ext-fonts'] != '1') ? 4003 [fontName, (fontUrl != null) ? encodeURIComponent(fontUrl) : null, null] : [fontName], 4004 null, parent, function() 4005 { 4006 if (urlParams['ext-fonts'] != '1') 4007 { 4008 graph.setFont(fontName, fontUrl); 4009 } 4010 else 4011 { 4012 document.execCommand('fontname', false, fontName); 4013 //Add the font to the file in case it was a previous font from the settings 4014 graph.addExtFont(fontName, fontUrl); 4015 } 4016 4017 editorUi.fireEvent(new mxEventObject('styleChanged', 4018 'keys', (urlParams['ext-fonts'] != '1') ? 4019 [mxConstants.STYLE_FONTFAMILY, 'fontSource', 'FType'] : [mxConstants.STYLE_FONTFAMILY], 4020 'values', (urlParams['ext-fonts'] != '1') ? 4021 [fontName, (fontUrl != null) ? encodeURIComponent(fontUrl) : null, null] : [fontName], 4022 'cells', [graph.cellEditor.getEditingCell()])); 4023 }, function() 4024 { 4025 graph.updateLabelElements(graph.getSelectionCells(), function(elt) 4026 { 4027 elt.removeAttribute('face'); 4028 elt.style.fontFamily = null; 4029 4030 if (elt.nodeName == 'PRE') 4031 { 4032 graph.replaceElement(elt, 'div'); 4033 } 4034 }); 4035 4036 //Add the font to the file in case it was a previous font from the settings 4037 if (urlParams['ext-fonts'] == '1') 4038 { 4039 graph.addExtFont(fontName, fontUrl); 4040 } 4041 }); 4042 4043 if (deletable) 4044 { 4045 var img = document.createElement('span'); 4046 img.className = 'geSprite geSprite-delete'; 4047 img.style.cursor = 'pointer'; 4048 img.style.display = 'inline-block'; 4049 tr.firstChild.nextSibling.nextSibling.appendChild(img); 4050 4051 mxEvent.addListener(img, (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup', mxUtils.bind(this, function(evt) 4052 { 4053 if (urlParams['ext-fonts'] != '1') 4054 { 4055 delete Graph.recentCustomFonts[fontName.toLowerCase()]; 4056 4057 for (var i = 0; i < this.customFonts.length; i++) 4058 { 4059 if (this.customFonts[i].name == fontName && 4060 this.customFonts[i].url == fontUrl) 4061 { 4062 this.customFonts.splice(i, 1); 4063 editorUi.fireEvent(new mxEventObject('customFontsChanged')); 4064 4065 break; 4066 } 4067 } 4068 } 4069 else 4070 { 4071 var extFonts = mxUtils.clone(this.editorUi.editor.graph.extFonts); 4072 4073 if (extFonts != null && extFonts.length > 0) 4074 { 4075 for (var i = 0; i < extFonts.length; i++) 4076 { 4077 if (extFonts[i].name == fontName) 4078 { 4079 extFonts.splice(i, 1); 4080 break; 4081 } 4082 } 4083 } 4084 4085 var customFonts = mxUtils.clone(this.customFonts); 4086 4087 for (var i = 0; i < customFonts.length; i++) 4088 { 4089 if (customFonts[i].name == fontName) 4090 { 4091 customFonts.splice(i, 1); 4092 break; 4093 } 4094 } 4095 4096 var change = new ChangeExtFonts(this.editorUi, extFonts, customFonts); 4097 this.editorUi.editor.graph.model.execute(change); 4098 } 4099 4100 this.editorUi.hideCurrentMenu(); 4101 mxEvent.consume(evt); 4102 })); 4103 } 4104 4105 Graph.addFont(fontName, fontUrl); 4106 tr.firstChild.nextSibling.style.fontFamily = fontName; 4107 4108 if (tooltip != null) 4109 { 4110 tr.setAttribute('title', tooltip); 4111 } 4112 }); 4113 4114 var reserved = {}; 4115 4116 for (var i = 0; i < this.defaultFonts.length; i++) 4117 { 4118 var value = this.defaultFonts[i]; 4119 4120 if (typeof value === 'string') 4121 { 4122 addItem(value); 4123 } 4124 else if (value.fontFamily != null && value.fontUrl != null) 4125 { 4126 reserved[encodeURIComponent(value.fontFamily) + '@' + 4127 encodeURIComponent(value.fontUrl)] = true; 4128 addItem(value.fontFamily, value.fontUrl); 4129 } 4130 } 4131 4132 menu.addSeparator(parent); 4133 4134 if (urlParams['ext-fonts'] != '1') 4135 { 4136 // Special entries in the font menu are composed of custom fonts 4137 // from the local storage and actual used fonts in the file 4138 var duplicates = {}; 4139 var fontNames = {}; 4140 var entries = []; 4141 4142 function addEntry(entry) 4143 { 4144 var key = encodeURIComponent(entry.name) + 4145 ((entry.url == null) ? '' : 4146 '@' + encodeURIComponent(entry.url)); 4147 4148 if (!reserved[key]) 4149 { 4150 var label = entry.name; 4151 var counter = 0; 4152 4153 while (fontNames[label.toLowerCase()] != null) 4154 { 4155 label = entry.name + ' (' + (++counter) + ')'; 4156 } 4157 4158 if (duplicates[key] == null) 4159 { 4160 entries.push({name: entry.name, url: entry.url, 4161 label: label, title: entry.url}); 4162 fontNames[label.toLowerCase()] = entry; 4163 duplicates[key] = entry; 4164 } 4165 } 4166 }; 4167 4168 // Adds custom user defined fonts from local storage 4169 for (var i = 0; i < this.customFonts.length; i++) 4170 { 4171 addEntry(this.customFonts[i]); 4172 } 4173 4174 // Adds fonts that were recently used in the editor 4175 for (var key in Graph.recentCustomFonts) 4176 { 4177 addEntry(Graph.recentCustomFonts[key]); 4178 } 4179 4180 // Sorts by label 4181 entries.sort(function(a, b) 4182 { 4183 if (a.label < b.label) 4184 { 4185 return -1; 4186 } 4187 else if (a.label > b.label) 4188 { 4189 return 1; 4190 } 4191 else 4192 { 4193 return 0; 4194 } 4195 }); 4196 4197 if (entries.length > 0) 4198 { 4199 for (var i = 0; i < entries.length; i++) 4200 { 4201 addItem(entries[i].name, entries[i].url, true, 4202 entries[i].label, entries[i].url); 4203 } 4204 4205 menu.addSeparator(parent); 4206 } 4207 4208 menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function() 4209 { 4210 Graph.recentCustomFonts = {}; 4211 this.customFonts = []; 4212 editorUi.fireEvent(new mxEventObject('customFontsChanged')); 4213 }), parent); 4214 4215 menu.addSeparator(parent); 4216 } 4217 else 4218 { 4219 //Load custom fonts already in the Graph 4220 var extFonts = this.editorUi.editor.graph.extFonts; 4221 4222 //Merge external fonts with custom fonts 4223 if (extFonts != null && extFonts.length > 0) 4224 { 4225 var custMap = {}, changed = false; 4226 4227 for (var i = 0; i < this.customFonts.length; i++) 4228 { 4229 custMap[this.customFonts[i].name] = true; 4230 } 4231 4232 for (var i = 0; i < extFonts.length; i++) 4233 { 4234 if (!custMap[extFonts[i].name]) 4235 { 4236 this.customFonts.push(extFonts[i]); 4237 changed = true; 4238 } 4239 } 4240 4241 if (changed) 4242 { 4243 this.editorUi.fireEvent(new mxEventObject('customFontsChanged', 'customFonts', this.customFonts)); 4244 } 4245 } 4246 4247 if (this.customFonts.length > 0) 4248 { 4249 for (var i = 0; i < this.customFonts.length; i++) 4250 { 4251 var name = this.customFonts[i].name, url = this.customFonts[i].url; 4252 addItem(name, url, true); 4253 4254 //Load external fonts without saving them to the file 4255 this.editorUi.editor.graph.addExtFont(name, url, true); 4256 } 4257 4258 menu.addSeparator(parent); 4259 4260 menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function() 4261 { 4262 var change = new ChangeExtFonts(this.editorUi, [], []); 4263 editorUi.editor.graph.model.execute(change); 4264 }), parent); 4265 4266 menu.addSeparator(parent); 4267 } 4268 } 4269 4270 menu.addItem(mxResources.get('custom') + '...', null, mxUtils.bind(this, function() 4271 { 4272 var graph = this.editorUi.editor.graph; 4273 var curFontName = graph.getStylesheet().getDefaultVertexStyle() 4274 [mxConstants.STYLE_FONTFAMILY]; 4275 var curType = 's'; 4276 var curUrl = null; 4277 4278 // Handles in-place editing custom fonts via font family lookup 4279 if (urlParams['ext-fonts'] != '1' && graph.isEditing()) 4280 { 4281 var node = graph.getSelectedEditingElement(); 4282 4283 if (node != null) 4284 { 4285 var css = mxUtils.getCurrentStyle(node); 4286 4287 if (css != null) 4288 { 4289 curFontName = Graph.stripQuotes(css.fontFamily); 4290 curUrl = Graph.getFontUrl(curFontName, null); 4291 4292 if (curUrl != null) 4293 { 4294 if (Graph.isGoogleFontUrl(curUrl)) 4295 { 4296 curUrl = null; 4297 curType = 'g'; 4298 } 4299 else 4300 { 4301 curType = 'w'; 4302 } 4303 } 4304 } 4305 } 4306 } 4307 else 4308 { 4309 var state = graph.getView().getState(graph.getSelectionCell()); 4310 4311 if (state != null) 4312 { 4313 curFontName = state.style[mxConstants.STYLE_FONTFAMILY] || curFontName; 4314 4315 if (urlParams['ext-fonts'] != '1') 4316 { 4317 var temp = state.style['fontSource']; 4318 4319 if (temp != null) 4320 { 4321 temp = decodeURIComponent(temp); 4322 4323 if (Graph.isGoogleFontUrl(temp)) 4324 { 4325 curType = 'g'; 4326 } 4327 else 4328 { 4329 curType = 'w'; 4330 curUrl = temp; 4331 } 4332 } 4333 } 4334 else 4335 { 4336 curType = state.style['FType'] || curType; 4337 4338 if (curType == 'w') 4339 { 4340 var extFonts = this.editorUi.editor.graph.extFonts; 4341 var webFont = null; 4342 4343 if (extFonts != null) 4344 { 4345 webFont = extFonts.find(function(ef) 4346 { 4347 return ef.name == curFontName; 4348 }); 4349 } 4350 4351 // TODO: Resource is not defined 4352 curUrl = webFont != null? webFont.url : mxResources.get('urlNotFound', null, 'URL not found'); 4353 } 4354 } 4355 } 4356 } 4357 4358 if (curUrl != null && curUrl.substring(0, PROXY_URL.length) == PROXY_URL) 4359 { 4360 curUrl = decodeURIComponent(curUrl.substr((PROXY_URL + '?url=').length)); 4361 } 4362 4363 // Saves the current selection state 4364 var selState = null; 4365 4366 if (document.activeElement == graph.cellEditor.textarea) 4367 { 4368 selState = graph.cellEditor.saveSelection(); 4369 } 4370 4371 var dlg = new FontDialog(this.editorUi, curFontName, curUrl, curType, mxUtils.bind(this, function(fontName, fontUrl, type) 4372 { 4373 // Restores the selection state 4374 if (selState != null) 4375 { 4376 graph.cellEditor.restoreSelection(selState); 4377 selState = null; 4378 } 4379 4380 if (fontName != null && fontName.length > 0) 4381 { 4382 if (urlParams['ext-fonts'] != '1' && graph.isEditing()) 4383 { 4384 graph.setFont(fontName, fontUrl); 4385 } 4386 else 4387 { 4388 graph.getModel().beginUpdate(); 4389 4390 try 4391 { 4392 graph.stopEditing(false); 4393 4394 if (urlParams['ext-fonts'] != '1') 4395 { 4396 graph.setCellStyles(mxConstants.STYLE_FONTFAMILY, fontName); 4397 graph.setCellStyles('fontSource', (fontUrl != null) ? 4398 encodeURIComponent(fontUrl) : null); 4399 graph.setCellStyles('FType', null); 4400 } 4401 else 4402 { 4403 graph.setCellStyles(mxConstants.STYLE_FONTFAMILY, fontName); 4404 4405 if (type != 's') 4406 { 4407 graph.setCellStyles('FType', type); 4408 4409 if (fontUrl.indexOf('http://') == 0) 4410 { 4411 fontUrl = PROXY_URL + '?url=' + encodeURIComponent(fontUrl); 4412 } 4413 4414 this.editorUi.editor.graph.addExtFont(fontName, fontUrl); 4415 } 4416 } 4417 4418 var addToCustom = true; 4419 4420 for (var i = 0; i < this.customFonts.length; i++) 4421 { 4422 if (this.customFonts[i].name == fontName) 4423 { 4424 addToCustom = false; 4425 break; 4426 } 4427 } 4428 4429 if (addToCustom) 4430 { 4431 this.customFonts.push({name: fontName, url: fontUrl}); 4432 this.editorUi.fireEvent(new mxEventObject('customFontsChanged', 'customFonts', this.customFonts)); 4433 } 4434 } 4435 finally 4436 { 4437 graph.getModel().endUpdate(); 4438 } 4439 } 4440 } 4441 })); 4442 this.editorUi.showDialog(dlg.container, 380, Editor.enableWebFonts ? 250 : 180, true, true); 4443 dlg.init(); 4444 }), parent, null, true); 4445 }))); 4446 }; 4447})();