1/** 2 * Plugin for embed mode in Confluence Connect post version 1.4.8 3 */ 4Draw.loadPlugin(function(ui) 5{ 6 // Handle data governess by modifying external services URLs 7 var allowedRegions = { 8 eu: 1, 9 us: 1 10 }; 11 12 if (allowedRegions[urlParams['dataGov']]) 13 { 14 var region = urlParams['dataGov']; 15 var urls = { 16 'EXPORT_URL': 'export', 17 'PLANT_URL': 'plant', 18 'VSD_CONVERT_URL': 'vsd', 19 'EMF_CONVERT_URL': 'emf', 20 'OPEN_URL': 'import' 21 }; 22 23 for (var key in urls) 24 { 25 var val = window[key]; 26 27 if (val) 28 { 29 window[key] = '/region-' + urls[key] + '-' + region; 30 } 31 } 32 } 33 34 // Extracts macro data from JSON protocol 35 var macroData = {}; 36 37 mxEvent.addListener(window, 'message', mxUtils.bind(this, function(evt) 38 { 39 var data = evt.data; 40 41 try 42 { 43 data = JSON.parse(data); 44 45 if (data.action == 'load') 46 { 47 if (data.macroData != null) 48 { 49 macroData = data.macroData; 50 51 if (ui.format != null) 52 { 53 ui.format.refresh(); 54 } 55 } 56 57 ui.initComments(macroData.contentId || macroData.custContentId); 58 macroData.diagramDisplayName = data.title; 59 60 //Fetch notifications 61 ui.fetchAndShowNotification('conf'); 62 } 63 } 64 catch (e) 65 { 66 data = null; 67 } 68 })); 69 70 var renameAction = ui.actions.get("rename"); 71 72 renameAction.visible = true; 73 74 renameAction.isEnabled = function() 75 { 76 return macroData.diagramDisplayName != null; 77 } 78 79 function descriptorChangedListener() 80 { 81 var curFile = ui.getCurrentFile(); 82 var fileTitle = curFile.getTitle(); 83 84 //Update file name in the UI 85 var tmp = document.createElement('span'); 86 mxUtils.write(tmp, mxUtils.htmlEntities(fileTitle)); 87 88 if (ui.embedFilenameSpan != null) 89 { 90 ui.embedFilenameSpan.parentNode.removeChild(ui.embedFilenameSpan); 91 } 92 93 ui.buttonContainer.appendChild(tmp); 94 ui.embedFilenameSpan = tmp; 95 macroData.diagramDisplayName = fileTitle; 96 97 var vSettings = curFile.desc.viewerSettings; 98 99 if (vSettings != null) 100 { 101 macroData.tbstyle = vSettings.tbstyle; 102 macroData.links = vSettings.links; 103 macroData.simple = vSettings.simple; 104 macroData.lbox = vSettings.lbox; 105 macroData.zoom = vSettings.zoom; 106 macroData.pCenter = vSettings.pCenter; 107 macroData.aspect = vSettings.aspect; 108 macroData.hiResPreview = vSettings.hiResPreview; 109 110 if (ui.format != null) 111 { 112 ui.format.refresh(); 113 } 114 } 115 }; 116 117 var xdm_e = decodeURIComponent(urlParams['site']); 118 var license = urlParams['atlas-lic']; 119 120 ui.remoteInvoke('checkConfLicense', [license, xdm_e], null, function(licenseValid) 121 { 122 if (!licenseValid) 123 { 124 ui.menus.get('file').funct = function(menu, parent) 125 { 126 menu.addItem(mxResources.get('licenseRequired'), null, function() 127 { 128 // do nothing 129 }, parent, null, false); 130 } 131 132 ui.menus.get('insertAdvanced').funct = function(menu, parent) 133 { 134 menu.addItem(mxResources.get('licenseRequired'), null, function() 135 { 136 // do nothing 137 }, parent, null, false); 138 } 139 140 if (typeof(MathJax) !== 'undefined') 141 { 142 ui.actions.get('mathematicalTypesetting').funct = function() 143 { 144 ui.alert(mxResources.get('licenseRequired')); 145 }; 146 } 147 148 EditorUi.prototype.insertPage = function(page, index) 149 { 150 this.alert(mxResources.get('licenseRequired')); 151 }; 152 153 Sidebar.prototype.searchEntries = function(searchTerms, count, page, success, error) 154 { 155 success(); 156 }; 157 158 Sidebar.prototype.insertSearchHint = function(div, searchTerm, count, page, results, len, more, terms) 159 { 160 var link = document.createElement('div'); 161 link.className = 'geTitle'; 162 link.style.cssText = 'background-color:#ffd350;border-radius:6px;color:black;' + 163 'border:1px solid black !important;text-align:center;white-space:normal;' + 164 'padding:6px 0px 6px 0px !important;margin:4px 4px 8px 2px;font-size:12px;'; 165 mxUtils.write(link, mxResources.get('licenseRequired')); 166 div.appendChild(link); 167 }; 168 169 DrawioFileSync.prototype.fileChangedNotify = function() 170 { 171 //Disable RT syncing 172 }; 173 174 ui.importFiles = function() 175 { 176 //Disable DnD and file import 177 ui.alert(mxResources.get('licenseRequired')); 178 } 179 180 //Disable comments 181 ui.getComments = function(success, error) 182 { 183 error({message: mxResources.get('licenseRequired')}); 184 } 185 186 ui.addComment = function(comment, success, error) 187 { 188 error(); 189 } 190 } 191 }, 192 function(){}); 193 194 renameAction.funct = function() 195 { 196 var dlg = new FilenameDialog(ui, macroData.diagramDisplayName || "", 197 mxResources.get('rename'), function(newName) 198 { 199 if (newName != null && newName.length > 0) 200 { 201 //TODO This is not needed with RT since title is added to desc 202 macroData.diagramDisplayName = newName; 203 var parent = window.opener || window.parent; 204 parent.postMessage(JSON.stringify({event: 'rename', name: newName}), '*'); 205 206 //Update and sync new name 207 ui.getCurrentFile().rename(newName); 208 } 209 }, mxResources.get('rename'), function(name) 210 { 211 var err = ""; 212 if (name == null || name.length == 0) 213 { 214 err = 'Filename too short'; 215 } 216 else if (/[&\*+=\\;/{}|\":<>\?~]/g.test(name)) 217 { 218 err = 'Invalid characters \\ / | : { } < > & + ? = ; * " ~'; 219 } 220 else 221 { 222 return true; 223 } 224 225 ui.showError(mxResources.get('error'), err, mxResources.get('ok')); 226 return false; 227 }); 228 ui.showDialog(dlg.container, 300, 80, true, true); 229 dlg.init(); 230 } 231 232 // Returns modified macro data to client 233 var uiCreateLoadMessage = ui.createLoadMessage; 234 235 ui.createLoadMessage = function(eventName) 236 { 237 var msg = uiCreateLoadMessage.apply(this, arguments); 238 239 if (eventName == 'export') 240 { 241 msg.macroData = macroData; 242 243 var desc = ui.getCurrentFile().getDescriptor(); 244 245 //Until app.min.js is propagated, this code is necessary 246 if (desc != null) 247 { 248 if (desc.key == null) 249 { 250 desc.key = Editor.guid(32); 251 desc.channel = Editor.guid(32); 252 desc.etagP = Editor.guid(32); 253 desc.title = macroData.diagramDisplayName; 254 } 255 else if (desc.title) 256 { 257 macroData.diagramDisplayName = desc.title; 258 } 259 260 msg.desc = desc; 261 } 262 else 263 { 264 msg.desc = {}; 265 } 266 } 267 268 return msg; 269 }; 270 271 // Adds new section for confluence cloud 272 var diagramFormatPanelInit = DiagramFormatPanel.prototype.init; 273 274 DiagramFormatPanel.prototype.init = function() 275 { 276 this.container.appendChild(this.addViewerOptions(this.createPanel())); 277 278 diagramFormatPanelInit.apply(this, arguments); 279 }; 280 281 // Adds viewer config to style options and refreshes 282 DiagramFormatPanel.prototype.addViewerOptions = function(div) 283 { 284 var ui = this.editorUi; 285 var editor = ui.editor; 286 var graph = editor.graph; 287 288 div.appendChild(this.createTitle(mxResources.get('viewerSettings'))); 289 290 // Viewer simple 291 div.appendChild(this.createOption(mxResources.get('simpleViewer'), function() 292 { 293 return macroData.simple == '1'; 294 }, function(checked) 295 { 296 macroData.simple = (checked) ? '1' : '0'; 297 })); 298 299 // Viewer lightbox 300 div.appendChild(this.createOption(mxResources.get('lightbox'), function() 301 { 302 return macroData.lbox != '0'; 303 }, function(checked) 304 { 305 macroData.lbox = (checked) ? '1' : '0'; 306 })); 307 308 // Viewer centering 309 div.appendChild(this.createOption(mxResources.get('center'), function() 310 { 311 return macroData.pCenter == '1'; 312 }, function(checked) 313 { 314 macroData.pCenter = (checked) ? '1' : '0'; 315 })); 316 317 // High Resolution Preview 318 div.appendChild(this.createOption(mxResources.get('hiResPreview', null, 'High Res Preview'), function() 319 { 320 return (macroData.hiResPreview == null && Editor.config != null && Editor.config.hiResPreview) || macroData.hiResPreview == '1'; 321 }, function(checked) 322 { 323 macroData.hiResPreview = (checked) ? '1' : '0'; 324 ui.remoteInvoke('setHiResPreview', [checked], null, function(){}, function(){}); //Notify plugin of the change, ignoring both success and error callbacks 325 })); 326 327 // Toolbar 328 var stylePanel = this.createPanel(); 329 stylePanel.style.position = 'relative'; 330 stylePanel.style.borderWidth = '0px'; 331 stylePanel.style.marginLeft = '0px'; 332 stylePanel.style.paddingTop = '8px'; 333 stylePanel.style.paddingBottom = '4px'; 334 stylePanel.style.fontWeight = 'normal'; 335 stylePanel.className = 'geToolbarContainer'; 336 337 mxUtils.write(stylePanel, mxResources.get('toolbar')); 338 339 // Adds toolbar options 340 var tbSelect = document.createElement('select'); 341 tbSelect.style.position = 'absolute'; 342 tbSelect.style.right = '20px'; 343 tbSelect.style.width = '97px'; 344 tbSelect.style.marginTop = '-2px'; 345 346 var opts = [{value: 'top', title: mxResources.get('top')}, 347 {value: 'inline', title: mxResources.get('embed')}, 348 {value: 'hidden', title: mxResources.get('hidden')}] 349 var validTb = false; 350 351 for (var i = 0; i < opts.length; i++) 352 { 353 validTb = validTb || macroData.tbstyle == opts[i].value; 354 var tbOption = document.createElement('option'); 355 tbOption.setAttribute('value', opts[i].value); 356 mxUtils.write(tbOption, opts[i].title); 357 tbSelect.appendChild(tbOption); 358 } 359 360 tbSelect.value = (validTb) ? macroData.tbstyle : 'top'; 361 stylePanel.appendChild(tbSelect); 362 div.appendChild(stylePanel); 363 364 mxEvent.addListener(tbSelect, 'change', function(evt) 365 { 366 macroData.tbstyle = tbSelect.value; 367 mxEvent.consume(evt); 368 }); 369 370 // Links 371 stylePanel = stylePanel.cloneNode(false); 372 stylePanel.style.paddingTop = '4px'; 373 mxUtils.write(stylePanel, mxResources.get('links')); 374 375 // Adds links options 376 var linksSelect = document.createElement('select'); 377 linksSelect.style.position = 'absolute'; 378 linksSelect.style.right = '20px'; 379 linksSelect.style.width = '97px'; 380 linksSelect.style.marginTop = '-2px'; 381 382 var opts = [{value: 'auto', title: mxResources.get('automatic')}, 383 {value: 'blank', title: mxResources.get('openInNewWindow')}, 384 {value: 'self', title: mxResources.get('openInThisWindow')}] 385 var validLinks = false; 386 387 for (var i = 0; i < opts.length; i++) 388 { 389 validLinks = validLinks || macroData.links == opts[i].value; 390 var linkOption = document.createElement('option'); 391 linkOption.setAttribute('value', opts[i].value); 392 mxUtils.write(linkOption, opts[i].title); 393 linksSelect.appendChild(linkOption); 394 } 395 396 linksSelect.value = (validLinks) ? macroData.links : 'auto'; 397 stylePanel.appendChild(linksSelect); 398 div.appendChild(stylePanel); 399 400 mxEvent.addListener(linksSelect, 'change', function(evt) 401 { 402 macroData.links = linksSelect.value; 403 mxEvent.consume(evt); 404 }); 405 406 // Zoom 407 var zoomOpt = this.createRelativeOption(mxResources.get('zoom'), null, null, function(input) 408 { 409 var value = (input.value == '') ? 100 : parseInt(input.value); 410 value = Math.max(0, (isNaN(value)) ? 100 : value); 411 input.value = value + ' %'; 412 macroData.zoom = value / 100; 413 }, function(input) 414 { 415 input.value = (parseFloat(macroData.zoom || 1) * 100) + '%'; 416 }); 417 418 zoomOpt.style.fontWeight = 'normal'; 419 zoomOpt.style.paddingBottom = '6px'; 420 zoomOpt.style.paddingTop = '6px'; 421 zoomOpt.style.border = 'none'; 422 423 div.appendChild(zoomOpt); 424 425 //Page and layers settings 426 div.appendChild(this.createTitle(mxResources.get('pageLayers', null, 'Page and Layers'))); 427 428 var hasAspect = false; 429 var pageId = null, layerIds = null; 430 431 var customizeBtn = mxUtils.button(mxResources.get('customize', null, 'Customize'), function() 432 { 433 434 var dlg = new AspectDialog(ui, pageId, layerIds, function(info) 435 { 436 pageId = info.pageId; 437 layerIds = info.layerIds; 438 macroData.aspect = pageId + ' ' + layerIds.join(' '); 439 ui.remoteInvoke('setAspect', [macroData.aspect], null, function(){}, function(){}); //Notify plugin of the change, ignoring both success and error callbacks 440 }); 441 442 ui.showDialog(dlg.container, 700, 465, true, true); 443 dlg.init(); 444 }); 445 446 customizeBtn.className = 'geColorBtn'; 447 customizeBtn.style.marginLeft = '10px'; 448 customizeBtn.style.padding = '2px'; 449 customizeBtn.setAttribute('disabled', 'disabled'); 450 451 if (macroData.aspect != null) 452 { 453 var aspectArray = macroData.aspect.split(' '); 454 455 if (aspectArray.length > 0) 456 { 457 pageId = aspectArray[0]; 458 layerIds = aspectArray.slice(1); 459 hasAspect = true; 460 customizeBtn.removeAttribute('disabled'); 461 } 462 } 463 464 var firstPageRadio = ui.addRadiobox(div, 'pageLayers', mxResources.get('firstPage', null, 'First Page (All Layers)'), !hasAspect); 465 firstPageRadio.style.marginTop = '4px'; 466 467 mxEvent.addListener(firstPageRadio, 'change', function() 468 { 469 if (this.checked) 470 { 471 macroData.aspect = null; 472 ui.remoteInvoke('setAspect', [macroData.aspect], null, function(){}, function(){}); //Notify plugin of the change, ignoring both success and error callbacks 473 customizeBtn.setAttribute('disabled', 'disabled'); 474 } 475 }); 476 477 var currentStateRadio = ui.addRadiobox(div, 'pageLayers', mxResources.get('curEditorState', null, 'Current Editor State'), false); 478 currentStateRadio.style.marginTop = '8px'; 479 480 mxEvent.addListener(currentStateRadio, 'change', function() 481 { 482 if (this.checked) 483 { 484 var curPage = ui.updatePageRoot(ui.currentPage); 485 var layerIds = [], layers = curPage.root.children; 486 487 for (var i = 0; i < layers.length; i++) 488 { 489 if (layers[i].visible != false) 490 { 491 layerIds.push(layers[i].id); 492 } 493 } 494 495 macroData.aspect = curPage.getId() + ' ' + layerIds.join(' '); 496 ui.remoteInvoke('setAspect', [macroData.aspect], null, function(){}, function(){}); //Notify plugin of the change, ignoring both success and error callbacks 497 customizeBtn.setAttribute('disabled', 'disabled'); 498 } 499 }); 500 501 var customStateRadio = ui.addRadiobox(div, 'pageLayers', mxResources.get('custom', null, 'Custom'), hasAspect, false, true); 502 customStateRadio.style.marginTop = '8px'; 503 504 mxEvent.addListener(customStateRadio, 'change', function() 505 { 506 if (this.checked) 507 { 508 customizeBtn.removeAttribute('disabled'); 509 } 510 }); 511 512 div.appendChild(customizeBtn); 513 514 return div; 515 }; 516 517 if (ui.format != null) 518 { 519 ui.format.refresh(); 520 } 521 522 //Adding Link to Confluence Page Anchor 523 524 var origLinkDialog = LinkDialog; 525 526 LinkDialog = function(editorUi, initialValue, btnLabel, fn, showPages) 527 { 528 function modFn(link, selDoc) 529 { 530 if (anchorRadio.checked) 531 { 532 fn('data:confluence/anchor,' + anchorSelect.value); 533 } 534 else 535 { 536 fn(link, selDoc); 537 } 538 }; 539 540 origLinkDialog.call(this, editorUi, initialValue, btnLabel, modFn, showPages); 541 542 var baseUrl = ''; 543 544 ui.remoteInvoke('getBaseUrl', null, null, function(url) 545 { 546 baseUrl = url; 547 }, 548 function() 549 { 550 //Extremely rare, we can safely ignore since the editor won't work 551 }); 552 553 var inner = this.container.querySelector('.geTitle'), urlInput = inner.querySelector('input[type="text"]'), urlCheck = urlInput.previousSibling; 554 555 var lbl = document.createElement('div'); 556 mxUtils.write(lbl, mxResources.get('confAnchor') + ':'); 557 inner.appendChild(lbl); 558 559 function addOption(select, name, value, isDisabled, isSelected) 560 { 561 var opt = document.createElement('option'); 562 563 if (isDisabled) 564 { 565 opt.setAttribute('disabled', 'disabled'); 566 } 567 568 if (isSelected) 569 { 570 opt.setAttribute('selected', 'selected'); 571 } 572 573 if (value) 574 { 575 opt.setAttribute('value', value); 576 } 577 578 mxUtils.write(opt, name); 579 select.appendChild(opt); 580 } 581 582 var anchorRadio = document.createElement('input'); 583 anchorRadio.style.cssText = 'margin-right:8px;margin-bottom:8px;'; 584 anchorRadio.setAttribute('value', 'url'); 585 anchorRadio.setAttribute('type', 'radio'); 586 587 var anchorSelect = document.createElement('select'); 588 anchorSelect.style.marginTop = '6px'; 589 anchorSelect.style.width = '680px'; 590 591 var anchorBusyIcn = document.createElement('img'); 592 anchorBusyIcn.src = '/images/spin.gif'; 593 anchorBusyIcn.style.position = 'absolute'; 594 595 var selAnchor = null; 596 597 if (initialValue != null && initialValue.substring(0, 23) == 'data:confluence/anchor,') 598 { 599 urlInput.value = ''; 600 selAnchor = initialValue.substring(23); 601 anchorRadio.setAttribute('checked', 'checked'); 602 anchorRadio.defaultChecked = true; 603 } 604 605 ui.remoteInvoke('getCurPageAnchors', null, null, function(headings) 606 { 607 addOption(anchorSelect, headings.length == 0? mxResources.get('noAnchorsFound') : mxResources.get('confAnchor'), null, true, selAnchor == null); 608 609 if (headings.length == 0) 610 { 611 anchorSelect.setAttribute('disabled', 'disabled'); 612 anchorRadio.setAttribute('disabled', 'disabled'); 613 } 614 else 615 { 616 for(var i = 0; i < headings.length; i++) 617 { 618 addOption(anchorSelect, headings[i], headings[i], false, selAnchor == headings[i]); 619 } 620 } 621 622 anchorBusyIcn.style.display = 'none'; 623 }, function() 624 { 625 anchorSelect.style.border = '1px solid red'; 626 anchorSelect.setAttribute('disabled', 'disabled'); 627 anchorRadio.setAttribute('disabled', 'disabled'); 628 anchorBusyIcn.style.display = 'none'; 629 }); 630 631 mxEvent.addListener(anchorSelect, 'focus', function() 632 { 633 anchorRadio.setAttribute('checked', 'checked'); 634 anchorRadio.checked = true; 635 }); 636 637 inner.appendChild(anchorRadio); 638 inner.appendChild(anchorSelect); 639 inner.appendChild(anchorBusyIcn); 640 641 //Attachments select 642 lbl = document.createElement('div'); 643 mxUtils.write(lbl, mxResources.get('attachments') + ':'); 644 inner.appendChild(lbl); 645 646 var attSelect = document.createElement('select'); 647 attSelect.style.margin = '6px 0 5px 0'; 648 attSelect.style.width = '705px'; 649 650 var attBusyIcn = document.createElement('img'); 651 attBusyIcn.src = '/images/spin.gif'; 652 attBusyIcn.style.position = 'absolute'; 653 654 var attMap = {}; 655 656 ui.remoteInvoke('getCurPageAttachments', null, null, function(atts) 657 { 658 addOption(attSelect, atts.length == 0? mxResources.get('noAttachments') : mxResources.get('attachments'), null, true, true); 659 660 if (atts.length == 0) 661 { 662 attSelect.setAttribute('disabled', 'disabled'); 663 } 664 else 665 { 666 atts = atts.filter(function(a) 667 { 668 //Exclude draft and temp files 669 return a.metadata.mediaType != 'application/vnd.jgraph.mxfile.cached' && !/^\~.+\.tmp$/.test(a.title); 670 }); 671 672 for(var i = 0; i < atts.length; i++) 673 { 674 attMap[atts[i].id] = atts[i]; 675 addOption(attSelect, atts[i].title, atts[i].id, false, false); 676 } 677 } 678 679 attBusyIcn.style.display = 'none'; 680 }, function() 681 { 682 attSelect.style.border = '1px solid red'; 683 attSelect.setAttribute('disabled', 'disabled'); 684 attBusyIcn.style.display = 'none'; 685 }); 686 687 function setUrlValue(content) 688 { 689 //Attachment webui link doesn't work, so build it 690 if (content.type == 'attachment') 691 { 692 var pageId = content._expandable.container.match(/\d+/)[0]; 693 urlInput.value = baseUrl + '/pages/viewpageattachments.action?pageId=' 694 + pageId 695 + '&preview=/' + pageId + '/' + content.id.replace('att', '') + '/' 696 + encodeURIComponent(content.title); 697 } 698 else 699 { 700 urlInput.value = baseUrl + content._links.webui; 701 } 702 703 urlCheck.checked = true; 704 }; 705 706 mxEvent.addListener(attSelect, 'change', function() 707 { 708 var att = attMap[attSelect.value]; 709 710 if (att.metadata.mediaType == 'application/vnd.jgraph.mxfile') 711 { 712 attBusyIcn.style.display = ''; 713 var pageId = att._expandable.container; 714 pageId = pageId.substr(pageId.lastIndexOf('/') + 1); 715 716 ui.remoteInvoke('getPageDrawioDiagrams', [pageId], null, function(drawioCCs) 717 { 718 var attCC = drawioCCs.filter(function(c) 719 { 720 return c.info.name == att.title; 721 })[0]; 722 723 if (attCC) 724 { 725 setUrlValue(attCC.obj); 726 } 727 else 728 { 729 setUrlValue(att); 730 } 731 732 attBusyIcn.style.display = 'none'; 733 }, function() 734 { 735 attSelect.style.border = '1px solid red'; 736 attBusyIcn.style.display = 'none'; 737 }); 738 } 739 else 740 { 741 setUrlValue(att); 742 } 743 }); 744 745 inner.appendChild(attSelect); 746 inner.appendChild(attBusyIcn); 747 748 //Search 749 lbl = document.createElement('div'); 750 mxUtils.write(lbl, mxResources.get('search') + ':'); 751 inner.appendChild(lbl); 752 753 var searchInput = document.createElement('input'); 754 searchInput.placeholder = mxResources.get('search'); 755 searchInput.style.margin = '6px 5px 5px 0'; 756 searchInput.style.width = '490px'; 757 758 var spaceSelect = document.createElement('select'); 759 spaceSelect.style.marginTop = '6px'; 760 spaceSelect.style.width = '202px'; 761 762 var spaceBusyIcn = document.createElement('img'); 763 spaceBusyIcn.src = '/images/spin.gif'; 764 spaceBusyIcn.style.position = 'absolute'; 765 766 var searchResult = document.createElement('div'); 767 searchResult.style.cssText = 'border: 1px solid black;width: 705px;height:200px;overflow-y:auto; overflow-x:hidden'; 768 769 addOption(spaceSelect, mxResources.get('allSpaces'), '*', false, true); 770 771 var typesMap = { 772 'page': mxResources.get('page'), 773 'attachment': mxResources.get('attachment', null, 'Attachment'), 774 'blogpost': mxResources.get('blog'), 775 'ac:com.mxgraph.confluence.plugins.diagramly:drawio-diagram': mxResources.get('drawDiag') 776 }; 777 778 ui.remoteInvoke('getAvailableSpaces', null, null, function(spaces) 779 { 780 for(var i = 0; i < spaces.length; i++) 781 { 782 addOption(spaceSelect, spaces[i].title, spaces[i].space.key, false, false); 783 } 784 785 spaceBusyIcn.style.display = 'none'; 786 }, function() 787 { 788 //We'll use all spaces and ignore error 789 spaceBusyIcn.style.display = 'none'; 790 }); 791 792 var searchTimeout = null, searchResultsMap = {}; 793 794 function resultRowClick() 795 { 796 var cId = this.getAttribute('data-url'); 797 setUrlValue(searchResultsMap[cId]); 798 }; 799 800 function doSearch() 801 { 802 clearTimeout(searchTimeout); 803 804 if(searchInput.value != '') 805 { 806 searchResult.innerHTML = '<img src="/images/spin.gif">'; 807 searchResultsMap = {}; 808 809 ui.remoteInvoke('contentSearch', [searchInput.value, spaceSelect.value == '*'? null : [spaceSelect.value]], null, function(results) 810 { 811 searchResult.innerHTML = ''; 812 813 results = results.filter(function(r) 814 { 815 //Exclude draft files and diagram files (since it is returned as custom contents) 816 return r.metadata.mediaType != 'application/vnd.jgraph.mxfile.cached' && r.metadata.mediaType != 'application/vnd.jgraph.mxfile'; 817 }); 818 819 if (results.length == 0) 820 { 821 searchResult.innerHTML = mxResources.get('noSearchResults'); 822 } 823 else 824 { 825 var table = document.createElement('table'); 826 table.className = 'geStripedTable'; 827 table.innerHTML = '<tr><th style="width:335px;">' + mxResources.get('title') + '</th><th style="width:105px;">' + mxResources.get('type') 828 + '</th><th style="width:130px;">' + mxResources.get('space') + '</th><th>' + mxResources.get('lastModified') + '</th></tr>'; 829 830 for(var i = 0; i < results.length; i++) 831 { 832 var res = results[i]; 833 searchResultsMap[res.id] = res; 834 var spaceName = res.space? res.space.name : ''; 835 836 var tr = document.createElement('tr'); 837 tr.setAttribute('data-url', res.id); 838 var type = typesMap[res.type]; 839 tr.innerHTML = '<td>' + mxUtils.htmlEntities(res.title) + '</td><td>' + (type? type : mxResources.get('other')) 840 + '</td><td>' + mxUtils.htmlEntities(spaceName) + '</td><td>' + mxUtils.htmlEntities(res.version.friendlyWhen) + '</td></tr>'; 841 842 mxEvent.addListener(tr, 'click', resultRowClick); 843 table.appendChild(tr); 844 } 845 846 searchResult.appendChild(table); 847 } 848 }, function() 849 { 850 searchResult.innerHTML = mxResources.get('confAErrOccured'); 851 }); 852 } 853 }; 854 855 mxEvent.addListener(searchInput, 'keypress', function(e) 856 { 857 if(e.which == 13) 858 { 859 doSearch(); 860 } 861 }); 862 863 mxEvent.addListener(searchInput, 'input', function(e) 864 { 865 clearTimeout(searchTimeout); 866 searchTimeout = setTimeout(doSearch, 1000); 867 }); 868 869 inner.appendChild(searchInput); 870 inner.appendChild(spaceSelect); 871 inner.appendChild(spaceBusyIcn); 872 inner.appendChild(searchResult); 873 874 var origInit = this.init; 875 876 this.init = function() 877 { 878 origInit.apply(this, arguments); 879 880 if (anchorRadio.checked) 881 { 882 anchorSelect.focus(); 883 } 884 }; 885 }; 886 887 mxUtils.extend(LinkDialog, origLinkDialog); 888 889 ui.showLinkDialog = function(value, btnLabel, fn) 890 { 891 var dlg = new LinkDialog(this, value, btnLabel, fn, true); 892 this.showDialog(dlg.container, 700, 470, true, true); 893 dlg.init(); 894 }; 895 896 //Viewer also had to change this in viewer (Graph.prototype.customLinkClicked) 897 var origHandleCustomLink = ui.handleCustomLink; 898 899 //This code is similar to AC.gotoAnchor but we don't have access to AC here 900 ui.handleCustomLink = function(href) 901 { 902 if (href.substring(0, 19) == 'data:confluence/id,') 903 { 904 var id = href.substring(19); 905 906 var newWin = window.open(); 907 908 if (id) 909 { 910 ui.remoteInvoke('getContentInfo', [id], null, function(info) 911 { 912 ui.remoteInvoke('getBaseUrl', null, null, function(url) 913 { 914 newWin.location = url + info._links.webui; 915 }, 916 function(){}); 917 }, function() 918 { 919 newWin.document.writeln(mxResources.get('objectNotFound')); 920 }); 921 } 922 else 923 { 924 throw new Error('Empty ID'); 925 } 926 } 927 else if (href.substring(0, 23) == 'data:confluence/anchor,') 928 { 929 var anchor = href.substring(23); 930 931 var newWin = window.open(); 932 933 if (anchor) 934 { 935 ui.remoteInvoke('getPageInfo', [true], null, function(info) 936 { 937 var url = info.url; 938 939 if (url != null) 940 { 941 //remove any hash 942 var hash = url.indexOf('#'); 943 944 if (hash > -1) 945 { 946 url = url.substring(0, hash); 947 } 948 949 //We assume the new editor for simplicity 950 newWin.location = url + '#' + encodeURIComponent(anchor.replace(/\s/g, '-')); 951 } 952 }, function() 953 { 954 throw new Error('Unexpected Error'); 955 }); 956 } 957 else 958 { 959 throw new Error('Empty Anchor'); 960 } 961 } 962 else 963 { 964 origHandleCustomLink.apply(ui, arguments); 965 } 966 }; 967 968 var origGetLinkTitle = ui.getLinkTitle; 969 970 ui.getLinkTitle = function(href) 971 { 972 if (href.substring(0, 19) == 'data:confluence/id,') 973 { 974 return mxResources.get('link'); //We only have the id which is not helpful 975 } 976 else if (href.substring(0, 23) == 'data:confluence/anchor,') 977 { 978 return mxResources.get('anchor') + ': ' + href.substring(23); 979 } 980 else 981 { 982 return origGetLinkTitle.apply(ui, arguments); 983 } 984 }; 985 986 //======================== Revisions ======================== 987 988 ui.isRevisionHistoryEnabled = function() 989 { 990 return macroData.pageId != null; 991 }; 992 993 ui.isRevisionHistorySupported = function() 994 { 995 return true; 996 }; 997 998 /** 999 * Get revisions of current file 1000 */ 1001 ui.getRevisions = function(success, error) 1002 { 1003 function getXml(success, error) 1004 { 1005 ui.remoteInvoke('getFileContent', [this.downloadUrl], null, success, error); 1006 }; 1007 1008 function restoreFn(xml) 1009 { 1010 if (ui.spinner.spin(document.body, mxResources.get('restoring'))) 1011 { 1012 ui.replaceFileData(xml); 1013 ui.spinner.stop(); 1014 ui.hideDialog(); 1015 } 1016 }; 1017 1018 ui.remoteInvoke('getDiagramRevisions', [macroData.diagramName, macroData.pageId], null, function(revisions) 1019 { 1020 //convert to editor format and add getXml function 1021 var revs = []; 1022 1023 for (var i = 0; i < revisions.length; i++) 1024 { 1025 var rev = revisions[i]; 1026 rev.getXml = mxUtils.bind(rev, getXml); 1027 revs.push(rev); 1028 } 1029 1030 success(revs, restoreFn); 1031 }, error); 1032 }; 1033 1034 //============= Support Action =============== 1035 ui.actions.addAction('support...', function() 1036 { 1037 ui.remoteInvoke('getPageInfo', [true], null, function(info) 1038 { 1039 var url = info.url; 1040 1041 if (url != null) 1042 { 1043 var wikiPos = url.indexOf('/wiki/'); 1044 1045 if (wikiPos > -1) 1046 { 1047 url = url.substring(0, wikiPos); 1048 } 1049 1050 ui.openLink(url + '/wiki/plugins/servlet/ac/com.mxgraph.confluence.plugins.diagramly/support'); 1051 } 1052 else 1053 { 1054 ui.openLink('https://about.draw.io/support/'); 1055 } 1056 }, function() 1057 { 1058 ui.openLink('https://about.draw.io/support/'); 1059 }); 1060 }); 1061 1062 //=============Custom Libraries in More Shapes =================== 1063 function addImage(container, data, w, h, img) 1064 { 1065 var ew = 100; 1066 var eh = 100; 1067 1068 var iw = w; 1069 var ih = h; 1070 1071 if (w > ui.maxImageSize || h > ui.maxImageSize) 1072 { 1073 var s = Math.min(1, Math.min(ui.maxImageSize / Math.max(1, w)), ui.maxImageSize / Math.max(1, h)); 1074 w *= s; 1075 h *= s; 1076 } 1077 1078 if (iw > ih) 1079 { 1080 ih = Math.round(ih * ew / iw); 1081 iw = ew; 1082 } 1083 else 1084 { 1085 iw = Math.round(iw * eh / ih); 1086 ih = eh; 1087 } 1088 1089 var wrapper = document.createElement('div'); 1090 wrapper.setAttribute('draggable', 'true'); 1091 wrapper.style.display = 'inline-block'; 1092 wrapper.style.cursor = 'move'; 1093 1094 if (data != null) 1095 { 1096 var elt = document.createElement('img'); 1097 elt.setAttribute('src', data); 1098 elt.style.width = iw + 'px'; 1099 elt.style.height = ih + 'px'; 1100 elt.style.margin = '10px'; 1101 1102 elt.style.paddingBottom = Math.floor((eh - ih) / 2) + 'px'; 1103 elt.style.paddingLeft = Math.floor((ew - iw) / 2) + 'px'; 1104 1105 wrapper.appendChild(elt); 1106 } 1107 else if (img != null) 1108 { 1109 var cells = ui.stringToCells(Graph.decompress(img.xml)); 1110 1111 if (cells.length > 0) 1112 { 1113 ui.sidebar.createThumb(cells, ew, eh, wrapper, null, true, false); 1114 1115 // Needs inline block on SVG for delete icon to appear on same line 1116 wrapper.firstChild.style.display = 'inline-block'; 1117 wrapper.firstChild.style.cursor = ''; 1118 } 1119 } 1120 1121 container.appendChild(wrapper); 1122 }; 1123 1124 var customLibraries = []; 1125 1126 ui.actions.addAction('shapes...', mxUtils.bind(this, function() 1127 { 1128 ui.remoteInvoke('getCustomLibraries', null, null, function(libs) 1129 { 1130 customLibraries = libs; 1131 1132 for(var i = 0; i < libs.length; i++) 1133 { 1134 libs[i].imageCallback = mxUtils.bind(libs[i], function(preview) 1135 { 1136 preview.innerHTML = '<img src="/images/spin.gif">'; 1137 1138 ui.remoteInvoke('getFileContent', [this.downloadUrl], null, function(libContent) 1139 { 1140 try 1141 { 1142 preview.innerHTML = ''; 1143 doc = mxUtils.parseXml(libContent); 1144 var images = JSON.parse(mxUtils.getTextContent(doc.documentElement)); 1145 1146 for(var i = 0; i < images.length; i++) 1147 { 1148 addImage(preview, images[i].data, images[i].w, images[i].h, images[i]); 1149 } 1150 } 1151 catch(e) 1152 { 1153 preview.innerHTML = mxResources.get('confAErrOccured'); 1154 console.log(e); 1155 } 1156 }, function(err) 1157 { 1158 preview.innerHTML = mxResources.get('errorLoadingFile'); 1159 console.log(err); 1160 }); 1161 }); 1162 } 1163 1164 var customLibsEntry = libs.length > 0? [{title : mxResources.get('customLib'), entries : libs}] : []; 1165 ui.showDialog(new MoreShapesDialog(ui, true, ui.sidebar.entries.concat(customLibsEntry)).container, 640, (isLocalStorage) ? 1166 ((mxClient.IS_IOS) ? 650 : 630) : 650, true, true); 1167 }, function(err) 1168 { 1169 console.log(err); 1170 ui.showDialog(new MoreShapesDialog(ui, true, ui.sidebar.entries).container, 640, (isLocalStorage) ? 1171 ((mxClient.IS_IOS) ? 650 : 630) : 650, true, true); 1172 }); 1173 })); 1174 1175 var showEntriesOld = Sidebar.prototype.showEntries; 1176 1177 Sidebar.prototype.showEntries = function(stc, remember, force) 1178 { 1179 showEntriesOld.apply(this, arguments); 1180 1181 if(stc == null) 1182 return; 1183 1184 var libIds = stc.split(';'); 1185 1186 for(var i = 0; i < customLibraries.length; i++) 1187 { 1188 lib = customLibraries[i]; 1189 1190 if(mxUtils.indexOf(libIds, lib.id) != -1) 1191 { 1192 ui.remoteInvoke('getFileContent', [lib.downloadUrl], null, mxUtils.bind(lib, function(libContent) 1193 { 1194 try 1195 { 1196 ui.loadLibrary(new RemoteLibrary(ui, libContent, this)); 1197 } 1198 catch (e) 1199 { 1200 //Ignore 1201 } 1202 }), function() 1203 { 1204 //Ignore 1205 }); 1206 } 1207 else 1208 { 1209 ui.closeLibrary(new RemoteLibrary(ui, '', lib)); 1210 } 1211 }; 1212 }; 1213 1214 var isEntryVisibleOld = Sidebar.prototype.isEntryVisible; 1215 1216 Sidebar.prototype.isEntryVisible = function(key) 1217 { 1218 var visible = isEntryVisibleOld.apply(this, arguments); 1219 var cVisible = false; 1220 1221 var customLibSelection = mxSettings.getCustomLibraries(); 1222 1223 for(var i = 0; i < customLibSelection.length; i++) 1224 { 1225 try 1226 { 1227 var hash = customLibSelection[i]; 1228 1229 if (hash.charAt(0) == 'R') 1230 { 1231 if(JSON.parse(decodeURIComponent(hash.substr(1)))[0] == key) 1232 { 1233 cVisible = true; 1234 } 1235 } 1236 } 1237 catch(e){} //ignore 1238 } 1239 1240 return visible || cVisible; 1241 }; 1242 1243 //Show custom templates in templates dialog 1244 //Overriding the action here is too late as the ui button is created using the action function 1245 //The solution is to override the NewDialog itself but it's tricky and hacky 1246 var origNewDialog = NewDialog; 1247 1248 NewDialog = function(editorUi, compact, showName, callback, createOnly, cancelCallback, 1249 leftHighlight, rightHighlight, rightHighlightBorder, itemPadding, templateFile, 1250 recentDocsCallback, searchDocsCallback, openExtDocCallback, showImport, createButtonLabel, customTempCallback, withoutType) 1251 { 1252 if (!showName && recentDocsCallback == null && 1253 searchDocsCallback == null && openExtDocCallback == null && customTempCallback == null) 1254 { 1255 openExtDocCallback = function(url, info, name) 1256 { 1257 ui.remoteInvoke('getFileContent', [url], null, callback, function() 1258 { 1259 ui.showError(mxResources.get('error'), mxResources.get('cantReadChckPerms'), mxResources.get('ok')); 1260 }); 1261 }; 1262 1263 customTempCallback = function(customTempCallback) 1264 { 1265 ui.remoteInvoke('getCustomTemplates', null, null, customTempCallback, function() 1266 { 1267 customTempCallback({}, 0); //ignore error by sending empty templates 1268 }); 1269 }; 1270 } 1271 1272 origNewDialog.call(this, editorUi, compact, showName, callback, createOnly, cancelCallback, 1273 leftHighlight, rightHighlight, rightHighlightBorder, itemPadding, templateFile, 1274 recentDocsCallback, searchDocsCallback, openExtDocCallback, showImport, createButtonLabel, customTempCallback, withoutType); 1275 }; 1276 1277 mxUtils.extend(NewDialog, origNewDialog); 1278 1279 for (var key in origNewDialog) 1280 { 1281 NewDialog[key] = origNewDialog[key]; 1282 } 1283 //=============Embed File with real-time collab support (based on remote invocation) 1284 //Until app.min.js is propagated, this code is necessary 1285 if (typeof EmbedFile === 'undefined') 1286 { 1287 var origInstallMessageHandler = ui.installMessageHandler; 1288 1289 ui.installMessageHandler = function() 1290 { 1291 var parent = window.opener || window.parent; 1292 parent.postMessage(JSON.stringify({event: 'disableRT'}), '*'); 1293 1294 origInstallMessageHandler.apply(this, arguments); 1295 } 1296 1297 return; 1298 } 1299 1300 /** 1301 * Workaround for changing etag after save is higher autosave delay to allow 1302 * for preflight etag update and decrease possible conflicts on file save. 1303 */ 1304 EmbedFile.prototype.autosaveDelay = 500; 1305 1306 /** 1307 * Delay for last save in ms. 1308 */ 1309 EmbedFile.prototype.saveDelay = 0; 1310 1311 /** 1312 * 1313 */ 1314 EmbedFile.prototype.isConflict = function(err) 1315 { 1316 return err != null && err.status == 409; 1317 }; 1318 1319 /** 1320 * Returns the current user. 1321 */ 1322 EmbedFile.prototype.getCurrentUser = function() 1323 { 1324 return ui.getCurrentUser(); 1325 }; 1326 1327 /** 1328 * Returns true if an autosave is required at the time of execution. 1329 */ 1330 EmbedFile.prototype.isAutosave = function() 1331 { 1332 return this.desc.id != null; 1333 }; 1334 1335 /** 1336 * Specifies if the autosave checkbox should be shown in the document 1337 * properties dialog. Default is false. 1338 */ 1339 EmbedFile.prototype.isAutosaveOptional = function() 1340 { 1341 return this.desc.id == null; 1342 }; 1343 1344 /** 1345 * 1346 */ 1347 EmbedFile.prototype.isRenamable = function() 1348 { 1349 return this.isEditable() && DrawioFile.prototype.isEditable.apply(this, arguments); 1350 }; 1351 1352 /** 1353 * 1354 */ 1355 EmbedFile.prototype.save = function(revision, success, error, unloading, overwrite) 1356 { 1357 this.saveStarted = true; 1358 1359 DrawioFile.prototype.save.apply(this, [revision, mxUtils.bind(this, function() 1360 { 1361 this.saveFile(null, revision, success, error, unloading, overwrite); 1362 this.saveStarted = false; 1363 }), error, unloading, overwrite]); 1364 }; 1365 1366 /** 1367 * 1368 */ 1369 EmbedFile.prototype.setModified = function(value) 1370 { 1371 DrawioFile.prototype.setModified.apply(this, arguments); 1372 1373 //Set editor modified also to prevent accidental closure or exiting without saving 1374 ui.editor.modified = value; 1375 }; 1376 1377 /** 1378 * 1379 */ 1380 EmbedFile.prototype.saveFile = function(title, revision, success, error, unloading, overwrite) 1381 { 1382 try 1383 { 1384 if (!this.isEditable()) 1385 { 1386 if (success != null) 1387 { 1388 success(); 1389 } 1390 } 1391 else if (!this.savingFile) 1392 { 1393 // Sets shadow modified state during save 1394 this.savingFileTime = new Date(); 1395 this.setShadowModified(false); 1396 this.savingFile = true; 1397 1398 this.createSecret(mxUtils.bind(this, function(secret, token) 1399 { 1400 var doSave = mxUtils.bind(this, function(realOverwrite, realRevision) 1401 { 1402 try 1403 { 1404 var lastDesc = this.desc; 1405 var savedData = this.getData(); 1406 this.desc.secret = secret; 1407 this.desc.key = this.desc.key? this.desc.key : Editor.guid(32); 1408 this.desc.channel = this.desc.channel? this.desc.channel : Editor.guid(32); 1409 this.desc.etagP = this.desc.etagP? this.desc.etagP : Editor.guid(32); 1410 this.desc.title = this.desc.title? this.desc.title : macroData.diagramDisplayName; 1411 1412 ui.remoteInvoke('saveDraftWithFileDesc', [savedData, this.desc], null, mxUtils.bind(this, function(resp) 1413 { 1414 try 1415 { 1416 this.savingFile = false; 1417 1418 // Handles special case where resp is false eg 1419 // if the old file was converted to realtime 1420 if (resp != false) 1421 { 1422 // Checks for changes during save 1423 this.setModified(this.getShadowModified()); 1424 1425 if (revision) 1426 { 1427 this.lastAutosaveRevision = new Date().getTime(); 1428 } 1429 1430 // Adaptive autosave delay 1431 this.autosaveDelay = Math.min(8000, 1432 Math.max(this.saveDelay + 500, 1433 EmbedFile.prototype.autosaveDelay)); 1434 this.desc = resp; 1435 1436 // Shows possible errors but keeps the modified flag as the 1437 // file was saved but the cache entry could not be written 1438 if (token != null) 1439 { 1440 this.fileSaved(savedData, lastDesc, mxUtils.bind(this, function() 1441 { 1442 this.contentChanged(); 1443 1444 if (success != null) 1445 { 1446 success(resp); 1447 } 1448 }), error, token); 1449 } 1450 else 1451 { 1452 success(resp); 1453 } 1454 } 1455 else if (error != null) 1456 { 1457 error(resp); 1458 } 1459 } 1460 catch (e) 1461 { 1462 this.savingFile = false; 1463 1464 if (error != null) 1465 { 1466 error(e); 1467 } 1468 else 1469 { 1470 throw e; 1471 } 1472 } 1473 }), 1474 mxUtils.bind(this, function(err, desc) 1475 { 1476 //TODO EMBED desc is null here 1477 try 1478 { 1479 this.savingFile = false; 1480 1481 if (this.isConflict(err)) 1482 { 1483 this.inConflictState = true; 1484 1485 if (this.sync != null) 1486 { 1487 //Reduce number of retials since we already retry in AC.saveDraftWithFileDesc 1488 this.sync.maxCatchupRetries = 2; 1489 this.savingFile = true; 1490 1491 this.sync.fileConflict(desc, mxUtils.bind(this, function() 1492 { 1493 // Adds random cool-off 1494 window.setTimeout(mxUtils.bind(this, function() 1495 { 1496 this.updateFileData(); 1497 this.setShadowModified(false); 1498 doSave(realOverwrite, true); 1499 }), 100 + Math.random() * 500 + (err.isLocked? 500 : 0)); 1500 }), mxUtils.bind(this, function() 1501 { 1502 this.savingFile = false; 1503 1504 if (error != null) 1505 { 1506 error(); 1507 } 1508 })); 1509 } 1510 else if (error != null) 1511 { 1512 error(); 1513 } 1514 } 1515 else if (error != null) 1516 { 1517 error(err); 1518 } 1519 } 1520 catch (e) 1521 { 1522 this.savingFile = false; 1523 1524 if (error != null) 1525 { 1526 error(e); 1527 } 1528 else 1529 { 1530 throw e; 1531 } 1532 } 1533 })); 1534 } 1535 catch (e) 1536 { 1537 this.savingFile = false; 1538 1539 if (error != null) 1540 { 1541 error(e); 1542 } 1543 else 1544 { 1545 throw e; 1546 } 1547 } 1548 }); 1549 1550 doSave(overwrite, revision); 1551 })); 1552 } 1553 } 1554 catch (e) 1555 { 1556 if (error != null) 1557 { 1558 error(e); 1559 } 1560 else 1561 { 1562 throw e; 1563 } 1564 } 1565 }; 1566 1567 /** 1568 * 1569 */ 1570 EmbedFile.prototype.copyFile = function(success, error) 1571 { 1572 //Download a copy of the file since it is difficult to add a copy to current confluence page 1573 this.updateFileData(); 1574 ui.doSaveLocalFile(this.data, this.getTitle(), 'text/xml'); 1575 error(); //Since the problem is not fixed //TODO Confirm this is OK?? 1576 }; 1577 1578 /** 1579 * 1580 */ 1581 EmbedFile.prototype.rename = function(title, success, error) 1582 { 1583 var etag = this.getCurrentEtag(); 1584 this.desc.title = title; 1585 1586 ui.remoteInvoke('setFileDescriptor', [this.desc], null, mxUtils.bind(this, function(desc) 1587 { 1588 this.desc = desc; 1589 this.descriptorChanged(); 1590 1591 if (this.sync != null) 1592 { 1593 this.sync.descriptorChanged(etag); 1594 } 1595 1596 if (success != null) 1597 { 1598 success(desc); 1599 } 1600 }), error); 1601 }; 1602 1603 /** 1604 * 1605 */ 1606 EmbedFile.prototype.getTitle = function() 1607 { 1608 return this.desc.title || macroData.diagramDisplayName; 1609 }; 1610 1611 /** 1612 * 1613 */ 1614 EmbedFile.prototype.getHash = function() 1615 { 1616 return 'E' + this.getId(); 1617 }; 1618 1619 /** 1620 * 1621 */ 1622 EmbedFile.prototype.getId = function() 1623 { 1624 return this.desc.id; 1625 }; 1626 1627 /** 1628 * 1629 */ 1630 EmbedFile.prototype.isSyncSupported = function() 1631 { 1632 return this.desc.id != null; 1633 }; 1634 1635 /** 1636 * 1637 */ 1638 EmbedFile.prototype.isRevisionHistorySupported = function() 1639 { 1640 return true; 1641 }; 1642 1643 /** 1644 * 1645 */ 1646 EmbedFile.prototype.getLatestVersion = function(success, error) 1647 { 1648 ui.remoteInvoke('getDraftFileContent', null, null, mxUtils.bind(this, function(data, desc) 1649 { 1650 success(new EmbedFile(ui, data, desc)); 1651 }), error); 1652 }; 1653 1654 /** 1655 * Gets the channel ID from the given descriptor. 1656 */ 1657 EmbedFile.prototype.getChannelId = function() 1658 { 1659 var chan = this.desc.channel; 1660 1661 if (chan != null) 1662 { 1663 chan = 'E-' + this.getId() + '.' + chan; 1664 } 1665 1666 return chan; 1667 }; 1668 1669 /** 1670 * Gets the channel key from the given descriptor. 1671 */ 1672 EmbedFile.prototype.getChannelKey = function() 1673 { 1674 return this.desc.key; 1675 }; 1676 1677 /** 1678 * 1679 */ 1680 EmbedFile.prototype.getLastModifiedDate = function() 1681 { 1682 return new Date(this.desc.modifiedDate); 1683 }; 1684 1685 /** 1686 * 1687 */ 1688 EmbedFile.prototype.getDescriptor = function() 1689 { 1690 return this.desc; 1691 }; 1692 1693 /** 1694 * Updates the descriptor of this file with the one from the given file. 1695 */ 1696 EmbedFile.prototype.setDescriptor = function(desc) 1697 { 1698 this.desc = desc; 1699 }; 1700 1701 /** 1702 * Returns the secret from the given descriptor. 1703 */ 1704 EmbedFile.prototype.getDescriptorSecret = function(desc) 1705 { 1706 return desc.secret; 1707 }; 1708 1709 /** 1710 * Updates the revision ID on the given descriptor. 1711 */ 1712 EmbedFile.prototype.setDescriptorRevisionId = function(desc, id) 1713 { 1714 desc.headRevisionId = id; 1715 }; 1716 1717 /** 1718 * Returns the revision ID from the given descriptor. 1719 */ 1720 EmbedFile.prototype.getDescriptorRevisionId = function(desc) 1721 { 1722 return desc.headRevisionId; 1723 }; 1724 1725 /** 1726 * 1727 */ 1728 EmbedFile.prototype.getDescriptorEtag = function(desc) 1729 { 1730 return desc.etag; 1731 }; 1732 1733 /** 1734 * 1735 */ 1736 EmbedFile.prototype.setDescriptorEtag = function(desc, etag) 1737 { 1738 desc.etag = etag; 1739 }; 1740 1741 /** 1742 * 1743 */ 1744 EmbedFile.prototype.patchDescriptor = function(desc, patch) 1745 { 1746 DrawioFile.prototype.patchDescriptor.apply(this, arguments); 1747 1748 desc.headRevisionId = patch.headRevisionId; 1749 desc.modifiedDate = patch.modifiedDate; 1750 }; 1751 1752 /** 1753 * 1754 */ 1755 EmbedFile.prototype.loadDescriptor = function(success, error) 1756 { 1757 ui.remoteInvoke('getFileDescriptor', null, null, success, error); 1758 }; 1759 1760 var allowAutoSave = true; 1761 1762 EmbedFile.prototype.isAutosaveNow = function(success, error) 1763 { 1764 return allowAutoSave; 1765 }; 1766 1767 //Ensure saving of draft before publishing 1768 var origSaveAction = ui.actions.get('save').funct; 1769 1770 ui.actions.get('save').funct = function(exit) 1771 { 1772 var actArgs = arguments; 1773 var curFile = ui.getCurrentFile(); 1774 var desc = curFile.getDescriptor(); 1775 var isNewFile = desc == null || desc.key == null; 1776 1777 if (exit) 1778 { 1779 //Prevent stpping the spinner early by creating our own spinner 1780 var spinner = new Spinner({ 1781 lines: 12, // The number of lines to draw 1782 length: 24, // The length of each line 1783 width: 8, // The line thickness 1784 radius: 12, // The radius of the inner circle 1785 rotate: 0, // The rotation offset 1786 color: '#000', // #rgb or #rrggbb 1787 speed: 1.5, // Rounds per second 1788 trail: 60, // Afterglow percentage 1789 shadow: false, // Whether to render a shadow 1790 hwaccel: false, // Whether to use hardware acceleration 1791 zIndex: 2e9 // The z-index (defaults to 2000000000) 1792 }); 1793 1794 if (!isNewFile) 1795 { 1796 spinner.spin(document.body); 1797 } 1798 1799 allowAutoSave = false; 1800 1801 if (desc != null) 1802 { 1803 desc.viewerSettings = { 1804 tbstyle: macroData.tbstyle, 1805 links: macroData.links, 1806 simple: macroData.simple, 1807 lbox: macroData.lbox, 1808 zoom: macroData.zoom, 1809 pCenter: macroData.pCenter, 1810 aspect: macroData.aspect, 1811 hiResPreview: macroData.hiResPreview 1812 }; 1813 } 1814 1815 var etag = curFile.getCurrentEtag(); 1816 } 1817 1818 function doActions() 1819 { 1820 origSaveAction.apply(ui, actArgs); 1821 1822 if (exit && curFile.sync != null) 1823 { 1824 curFile.sync.descriptorChanged(etag); 1825 } 1826 }; 1827 1828 function doSave() 1829 { 1830 if (curFile.saveStarted || curFile.savingFile) 1831 { 1832 setTimeout(doSave, 100); 1833 return; 1834 } 1835 1836 if (curFile.isModified()) 1837 { 1838 //Save file (draft) first 1839 ui.saveFile(null, doActions); 1840 } 1841 else if (exit) //Save descriptor only to update the viewer settings 1842 { 1843 ui.remoteInvoke('setFileDescriptor', [desc], null, doActions, doActions); 1844 } 1845 else 1846 { 1847 doActions(); 1848 } 1849 }; 1850 1851 if (isNewFile) 1852 { 1853 //New files are saved directly and descriptor is added during publishing after creating the custom content 1854 doActions(); 1855 } 1856 else 1857 { 1858 doSave(); 1859 } 1860 }; 1861 1862 var p2pCollab = null; 1863 //Add file opening here (or it should be for all in EditorUi?) 1864 var origInstallMessageHandler = ui.installMessageHandler; 1865 1866 ui.installMessageHandler = function(callback) 1867 { 1868 origInstallMessageHandler.call(this, function() 1869 { 1870 callback.apply(this, arguments); 1871 1872 var file = ui.getCurrentFile(); 1873 1874 file.loadDescriptor(function(desc) 1875 { 1876 file.desc = desc; 1877 ui.fileLoaded(file, true); 1878 1879 if (file.desc) 1880 { 1881 var descChangedNeeded = false; 1882 1883 if (file.desc.title && file.desc.title != macroData.diagramDisplayName) 1884 { 1885 macroData.diagramDisplayName = file.desc.title; 1886 descChangedNeeded = true; 1887 } 1888 1889 if (file.desc.viewerSettings != null) 1890 { 1891 descChangedNeeded = true; 1892 } 1893 1894 if (descChangedNeeded) 1895 { 1896 descriptorChangedListener(); 1897 } 1898 1899 //RT Cursors 1900 if (urlParams['rtCursors'] == '1' && p2pCollab != null) 1901 { 1902 p2pCollab.joinFile(file.getChannelId()); 1903 file.p2pCollab = p2pCollab; 1904 } 1905 } 1906 }); 1907 1908 file.addListener('descriptorChanged', descriptorChangedListener); 1909 }); 1910 } 1911 1912 ui.editor.setModified = function() 1913 { 1914 //Cancel set modified of the editor and use the file's one 1915 }; 1916 1917 //P2P RT 1918 if (urlParams['rtCursors'] == '1') 1919 { 1920 p2pCollab = new P2PCollab(ui); 1921 } 1922}); 1923