Änderungen von Dokument DiagramEditSheet
Zuletzt geändert von Daniel Herrmann am 2026/02/04 20:25
Von Version 1.1
bearbeitet von Mike Schneider
am 2025/12/30 10:37
am 2025/12/30 10:37
Änderungskommentar:
Install extension [org.xwiki.contrib:application-diagram/1.3]
Auf Version
2.1
bearbeitet von Daniel Herrmann
am 2026/02/04 20:25
am 2026/02/04 20:25
Änderungskommentar:
Install extension [com.xwiki.diagram:application-diagram/1.22.11]
Zusammenfassung
Details
- Seiteneigenschaften
-
- Dokument-Autor
-
... ... @@ -1,1 +1,1 @@ 1 -XWiki. mschneide1 +XWiki.dherrman - Inhalt
-
... ... @@ -1,3 +1,34 @@ 1 +{{include reference="Diagram.ResourceSelector.WebHome" /}} 2 + 3 +{{velocity output="false"}} 4 +#macro (diagramLinkModal) 5 + <div class="modal" id="diagramLinkModal" tabindex="-1" role="dialog" 6 + aria-labelledby="diagramLinkModal-label" data-backdrop="static" data-keyboard="false"> 7 + <div class="modal-dialog modal-lg" role="document"> 8 + <div class="modal-content"> 9 + <div class="modal-header"> 10 + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> 11 + <span aria-hidden="true">×</span> 12 + </button> 13 + <div class="modal-title" id="diagramLinkModal-label"> 14 + Link 15 + </div> 16 + </div> 17 + <div class="modal-body xform"> 18 + #resourceSelector() 19 + </div> 20 + <div class="modal-footer"> 21 + <button type="button" class="btn btn-default" data-dismiss="modal"> 22 + $escapetool.xml($services.localization.render('cancel')) 23 + </button> 24 + <button type="button" class="btn btn-primary"></button> 25 + </div> 26 + </div> 27 + </div> 28 + </div> 29 +#end 30 +{{/velocity}} 31 + 1 1 {{velocity}} 2 2 {{html clean="false"}} 3 3 #if ($doc.getObject('Diagram.DiagramClass')) ... ... @@ -4,13 +4,28 @@ 4 4 ## Disable the keyboard shortcuts because they prevent the user from typing text inside the diagram (the diagram 5 5 ## editor doesn't use standard text fields for entering the labels for the various shapes the diagram is made of) 6 6 #set ($keyboardShortcutsEnabled = false) 7 - #set ($discard = $xwiki.ssx.use('Diagram.Diagram EditSheet'))38 + #set ($discard = $xwiki.ssx.use('Diagram.DiagramSheet')) 8 8 #set ($discard = $xwiki.jsx.use('Diagram.DiagramEditSheet')) 9 - <div class="diagram-editor loading"> 40 + ## Issue #219: Cannot modify diagram title in diagram editor 41 + <div class="row xform"> 42 + <div class="col-xs-12"> 43 + <dl> 44 + <dt> 45 + <label for="xwikidoctitleinput"> 46 + $escapetool.xml($services.localization.render('core.editors.content.titleField.label')) 47 + </label> 48 + </dt> 49 + <dd> 50 + <input id="xwikidoctitleinput" type="text" name="title" value="$escapetool.xml($tdoc.title)"> 51 + </dd> 52 + </dl> 53 + </div> 54 + </div> 55 + 56 + <div class="diagram-editor loading" data-diagram-config="$escapetool.xml($jsontool.serialize($diagramConfig))"> 10 10 <input class="diagram-content" type="hidden" name="content" value="$escapetool.xml($tdoc.content)" /> 11 - <input class="diagram-svg" type="hidden" name="Diagram.DiagramClass_0_svg" 12 - value="$!escapetool.xml($doc.getValue('svg'))" /> 13 13 </div> 59 + #diagramLinkModal() 14 14 #end 15 15 {{/html}} 16 16 {{/velocity}}
- XWiki.JavaScriptExtension[0]
-
- Pufferstrategie
-
... ... @@ -1,1 +1,0 @@ 1 -long - Code
-
... ... @@ -1,84 +1,0 @@ 1 -// mxGraph Client Configuration 2 -var mxBasePath = "$services.webjars.url('org.xwiki.contrib:mxgraph-client', '')"; 3 -var mxLanguage = '$xcontext.locale'; 4 - 5 -var mxGraphEditorBasePath = "$services.webjars.url('org.xwiki.contrib:mxgraph-editor', '')"; 6 - 7 -// Diagram Editor Configuration 8 -var diagramEditorBasePath = "$services.webjars.url('org.xwiki.contrib:draw.io', '')"; 9 -var RESOURCES_PATH = diagramEditorBasePath + 'resources'; 10 -// Comment out the following line when using the basic mxGraph Editor. 11 -var RESOURCE_BASE = RESOURCES_PATH + '/dia'; 12 -var STENCIL_PATH = diagramEditorBasePath + 'stencils'; 13 -var IMAGE_PATH = diagramEditorBasePath + 'images'; 14 -var STYLE_PATH = CSS_PATH = diagramEditorBasePath + 'styles'; 15 - 16 -var SHAPES_PATH = diagramEditorBasePath + 'shapes'; 17 -var GRAPH_IMAGE_PATH = diagramEditorBasePath + 'img'; 18 -var TEMPLATE_PATH = diagramEditorBasePath + 'templates'; 19 - 20 -var isLocalStorage = true; 21 - 22 -var urlParams = (function(params) { 23 - var pairs = window.location.search.substr(1).split('&'); 24 - pairs.forEach(function(pair) { 25 - var parts = pair.split('=', 2); 26 - if (parts.length === 2) { 27 - params[parts[0]] = decodeURIComponent(parts[1].replace(/\+/g, " ")); 28 - } 29 - }); 30 - return params; 31 -})({ 32 - // Don't show the splash screen. 33 - 'splash': '0', 34 - // Disable the tabbed UI. 35 - 'pages': '0', 36 - // Disable the GitHub integration. 37 - 'gh': '0', 38 - // Disable the Dropbox integration. 39 - 'db': '0', 40 - // Disable the Google Drive integration. 41 - 'gapi': '0', 42 - // Disable Google Analytics. 43 - 'analytics': '0', 44 - // Disable the One Drive integration. 45 - 'od': '0' 46 -}); 47 - 48 -// Disabling the integration with these external services is not enough because the draw.io code has hard-coded references. 49 -var DriveFile = DropboxFile = GitHubFile = OneDriveFile = false; 50 - 51 -require.config({ 52 - paths: { 53 - 'mxgraph-init': diagramEditorBasePath + 'js/draw.io.init.min', 54 - 'mxgraph-client': mxBasePath + 'mxClient.min', 55 - 'jscolor': mxGraphEditorBasePath + 'jscolor/jscolor.min', 56 - 'sanitizer': mxGraphEditorBasePath + 'sanitizer/sanitizer.min', 57 - 'mxgraph-editor': mxGraphEditorBasePath + 'mxGraphEditor.min', 58 - 'base64': diagramEditorBasePath + 'js/deflate/base64.min', 59 - 'pako': diagramEditorBasePath + 'js/deflate/pako.min', 60 - 'spin': diagramEditorBasePath + 'js/spin/spin.min', 61 - 'draw.io': diagramEditorBasePath + 'js/draw.io.min', 62 - }, 63 - shim: { 64 - 'mxgraph-client': { 65 - deps: ['mxgraph-init'] 66 - }, 67 - 'mxgraph-editor': { 68 - deps: ['mxgraph-client', 'jscolor', 'sanitizer'] 69 - }, 70 - 'draw.io': { 71 - deps: ['mxgraph-editor', 'base64', 'pako-global', 'spin-global'] 72 - } 73 - } 74 -}) 75 - 76 -define('pako-global', ['pako'], function(pako) { 77 - // draw.io expects a global variable. 78 - window.pako = pako; 79 -}); 80 - 81 -define('spin-global', ['spin'], function(spin) { 82 - // draw.io expects a global variable. 83 - window.Spinner = spin; 84 -}); - Name
-
... ... @@ -1,1 +1,0 @@ 1 -Configuration - Inhalt parsen
-
... ... @@ -1,1 +1,0 @@ 1 -Ja - Benutze diese Erweiterung
-
... ... @@ -1,1 +1,0 @@ 1 -onDemand
- XWiki.JavaScriptExtension[1]
-
- Code
-
... ... @@ -1,19 +1,29 @@ 1 1 /** 2 2 * Adds support for editing diagrams stored in XWiki pages. 3 3 */ 4 -define('diagramStore', ['jquery', 'draw.io', 'xwiki-events-bridge'], function($) { 4 +define('diagramEditTranslations', { 5 + prefix: 'diagram.editor.', 6 + keys: [ 7 + 'saveAsImageAttachmentError' 8 + ] 9 +}); 10 + 11 +define('diagram-store', ['jquery', 'xwiki-meta', 'xwiki-utils', 'diagram-utils', 'diagram-config', 12 + 'xwiki-l10n!diagramEditTranslations', 'draw.io', 13 + 'xwiki-events-bridge'], function($, xm, xutils, diagramUtils, diagramConfig, l10n) { 5 5 var files = []; 6 6 window._xfiles = files; 7 - var createFile = function(ui, input, title) { 8 - var file = new XWikiFile(ui, input, title); 16 + var createFile = function(ui, input, title, documentReference) { 17 + var file = new XWikiFile(ui, input, title, documentReference); 9 9 files.push(file); 10 10 return file; 11 11 }; 12 12 13 - var XWikiFile = function(ui, input, title) { 14 - DrawioFile.call(this, ui); 22 + var XWikiFile = function(ui, input, title, documentReference) { 15 15 this.input = input; 24 + DrawioFile.call(this, ui, input.val()); 16 16 this.title = title; 26 + this.documentReference = documentReference; 17 17 }; 18 18 19 19 mxUtils.extend(XWikiFile, DrawioFile); ... ... @@ -31,13 +31,40 @@ 31 31 setData: function(data) { 32 32 this.input.val(data); 33 33 }, 34 - updateFileData: function() { 35 - // We overwrite the base implementation because we don't want to support files that contain multiple diagrams. 36 - this.setData(mxUtils.getPrettyXml(this.ui.editor.getGraphXml())); 44 + isCompressed: function() { 45 + return false; 37 37 }, 47 + // TODO: When upgrading the drawio version, ensure that this method is copied from the drawio code and the 48 + // `resolveReferences` parameter of the getFileData is set to `true` to prevent incorrect formatting of links to 49 + // wiki pages in the diagram content. https://github.com/xwikisas/application-diagram/issues/295 50 + createData: function() { 51 + var actualPages = this.ui.pages; 52 + 53 + if (this.isRealtime()) { 54 + // Uses ownPages for getting file data below 55 + this.ui.pages = this.ownPages; 56 + 57 + // Updates view state in own current page 58 + if (this.ui.currentPage != null) { 59 + var ownPage = this.ui.getPageById( 60 + this.ui.currentPage.getId(), 61 + this.ownPages); 62 + 63 + if (ownPage != null) { 64 + ownPage.viewState = this.ui.editor.graph.getViewState(); 65 + ownPage.needsUpdate = true; 66 + } 67 + } 68 + } 69 + 70 + var result = this.ui.getFileData(null, null, null, null, null, null, null, null, this, 71 + !this.isCompressed(), true); 72 + this.ui.pages = actualPages; 73 + return result; 74 + }, 38 38 open: function() { 39 39 var graphXML = this.getData() || '<mxGraphModel/>'; 40 - this.ui. editor.setGraphXml(mxUtils.parseXml(graphXML).documentElement);77 + this.ui.setFileData(graphXML); 41 41 this.changeListener = mxUtils.bind(this, function(sender, eventObject) { 42 42 this.setModified(true); 43 43 }); ... ... @@ -55,6 +55,9 @@ 55 55 56 56 var updateFormFields = function(event) { 57 57 forEachOpenedFile(function(file) { 95 + // This is a workaround for https://github.com/jgraph/drawio/issues/490 96 + // Stop editing for getting the latest content from diagram 97 + file.ui.editor.graph.stopEditing(false); 58 58 file.updateFileData(); 59 59 }); 60 60 }; ... ... @@ -65,19 +65,143 @@ 65 65 }); 66 66 }; 67 67 68 - // We need to update the form fields before the form is validated (for Preview, Save and Save & Continue). 69 - $(document).on('xwiki:actions:beforePreview xwiki:actions:beforeSave', updateFormFields); 108 + var pipeDeferred = function(left, right) { 109 + left.done($.proxy(right, 'resolve')).fail($.proxy(right, 'reject')); 110 + }; 70 70 71 - $(document).on('xwiki:actions:beforeSave', function() { 72 - forEachOpenedFile(function(file) { 73 - var svgInput = file.input.next('.diagram-svg'); 74 - if (svgInput.length > 0) { 75 - var svgRoot = file.getUi().editor.graph.getSvg('#ffffff', true, false, false, null, true); 76 - svgInput.val(mxUtils.getXml(svgRoot)); 112 + var saveBlobAsImageAttachment = function(blob, fileName, documentReference) { 113 + var attachmentReference = new XWiki.AttachmentReference(fileName, documentReference); 114 + var uploadMethod = (diagramConfig.isTemporaryUploadSupported) ? xutils.temporaryUploadAttachment : xutils.uploadAttachment; 115 + var uploadAttachment = $.proxy(uploadMethod, null, blob, attachmentReference); 116 + return uploadAttachment(); 117 + }; 118 + 119 + var imageCache = {}; 120 + var saveFileAsPNGImageAttachment = function(file, index, originalPage) { 121 + var deferred = $.Deferred(); 122 + let page = file.ui.pages[index]; 123 + file.ui.selectPage(page, true, null); 124 + file.getUi().exportToCanvas(/* callback */ function (canvas) { 125 + if (canvas) { 126 + try { 127 + canvas.toBlob(function (blob) { 128 + pipeDeferred(saveBlobAsImageAttachment(blob, `${getXWikiAttachmentName(index)}.png`, file.documentReference), deferred); 129 + }); 130 + } catch(err) { 131 + deferred.reject(); 132 + } 133 + } else { 134 + deferred.reject(); 77 77 } 136 + }, /* width */ null, /* imageCache */ imageCache, /* background */ null, /* error */ function(e) { 137 + new XWiki.widgets.Notification( 138 + l10n['saveAsImageAttachmentError'], 'error'); 139 + deferred.reject(); 140 + }, /* limitHeight */ null, /* ignoreSelection */ true, /* scale */ diagramConfig.pdfImageExportZoom); 141 + file.ui.selectPage(originalPage, true, null); 142 + return deferred.promise(); 143 + }; 144 + 145 + var saveFileAsSVGImageAttachment = function(file, index, originalPage) { 146 + var deferred = $.Deferred() 147 + let page = file.ui.pages[index]; 148 + file.ui.selectPage(page, true, null); 149 + var svgRoot = file.ui.editor.graph.getSvg(/* background: */ '#ffffff', /* scale: */ null, /* border: */ null, 150 + /* nocrop: */ true, /* crisp: */ null, /* ignoreSelection: */ true); 151 + file.ui.selectPage(originalPage, true, null); 152 + // Embed the images because the PDF exporter might not be able to access them. 153 + file.ui.convertImages(svgRoot, function() { 154 + var svg = '<?xml version="1.0" encoding="UTF-8"?>\n' + 155 + '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' + 156 + mxUtils.getXml(svgRoot); 157 + var blob = new Blob([svg], {type: 'image/svg+xml'}); 158 + pipeDeferred(saveBlobAsImageAttachment(blob, `${getXWikiAttachmentName(index)}.svg`, file.documentReference), 159 + deferred); 160 + }, imageCache); 161 + return deferred.promise(); 162 + }; 163 + 164 + var saveFileAsImageAttachments = function(file, index, originalPage) { 165 + // This is a workaround for https://github.com/jgraph/drawio/issues/490 166 + // Stop editing for getting the latest content from diagram 167 + file.ui.editor.graph.stopEditing(false); 168 + // We upload the PNG image even if the SVG upload has failed. 169 + var pngUpload = $.proxy(saveFileAsPNGImageAttachment, null, file, index, originalPage); 170 + return saveFileAsSVGImageAttachment(file, index, originalPage).then(pngUpload, pngUpload); 171 + }; 172 + 173 + var getXWikiAttachmentName = function (index) { 174 + if (index == 0) 175 + { 176 + // If is the first page we keep the old naming strategy -> diagram.png / diagram.svg 177 + return "diagram"; 178 + } 179 + return `diagram${index+1}`; 180 + }; 181 + 182 + var deleteAllDiagrams = function() { 183 + let baseUrl = `${XWiki.contextPath}/rest/diagram/deleteDiagramAttachments`; 184 + let queryString = $.param({ 185 + "form_token": xm.form_token, 186 + "documentReference": encodeURIComponent(xm.documentReference.toString()) 78 78 }); 188 + return $.post(`${baseUrl}?${queryString}`); 189 + }; 190 + 191 + var saveFilesAsImageAttachments = function() { 192 + var uploadDeferredList = [] 193 + 194 + forEachOpenedFile(function (file) { 195 + var uploadDeferred = $.Deferred().resolve(); 196 + // Get the current page so we know where to return after saving. 197 + let originalPage = file.ui.currentPage; 198 + 199 + file.ui.pages.forEach(function (page, index) { 200 + // We do the next upload even if the previous uploads have failed. 201 + var nextUpload = $.proxy(saveFileAsImageAttachments, null, file, index,originalPage); 202 + uploadDeferred = uploadDeferred.then(nextUpload, nextUpload); 203 + uploadDeferredList.push(uploadDeferred.promise()); 204 + }); 205 + }); 206 + // Wait for all deferreds to complete. 207 + return $.when.apply($, uploadDeferredList).then( 208 + function () { 209 + // Resolve the overall promise if all succeeded. 210 + return { status: 'success' }; 211 + }, 212 + function () { 213 + // Reject the overall promise if any failed. 214 + return $.Deferred().reject({ status: 'fail' }); 215 + } 216 + ).promise(); 217 + }; 218 + 219 + var uploadInProgress = false; 220 + // Attach the diagram SVG to the diagram page in order to use it for viewing the diagram and for exporting the diagram 221 + // to PDF. For this we need to stop the default save until the upload action is completed and trigger it after. 222 + $(document).on('xwiki:actions:beforeSave', function(event, data) { 223 + if (!uploadInProgress) { 224 + uploadInProgress = true; 225 + event.stopPropagation(); 226 + var saveButton = $(event.target); 227 + saveButton.prop('disabled', true); 228 + deleteAllDiagrams() 229 + .then(saveFilesAsImageAttachments) 230 + .catch(function(e) { 231 + new XWiki.widgets.Notification( 232 + l10n['saveAsImageAttachmentError'], 'error'); 233 + }) 234 + .always(function() { 235 + saveButton.prop('disabled', false).click(); 236 + }); 237 + } else { 238 + uploadInProgress = false; 239 + } 79 79 }); 80 80 242 + // We need to update the form fields before the form is validated (for Preview, Save and Save & Continue). 243 + $(document).on('xwiki:actions:beforePreview xwiki:actions:beforeSave', updateFormFields); 244 + 81 81 var submitInProgress = false; 82 82 // Disable the leave confirmation when the form action buttons are used. 83 83 $(document).on('xwiki:actions:cancel xwiki:actions:preview xwiki:actions:save xwiki:document:saved', - Inhalt parsen
-
... ... @@ -1,1 +1,1 @@ 1 - Ja1 +Nein
- XWiki.JavaScriptExtension[2]
-
- Code
-
... ... @@ -1,20 +1,384 @@ 1 -define('diagramEditor', ['jquery', 'diagramStore'], function($, diagramStore) { 1 +/** 2 + * Overrides the link dialog in order to support creating links to wiki pages. 3 + */ 4 +define('diagram-link-editor', [ 5 + 'jquery', 6 + 'diagram-link-handler', 7 + 'draw.io', 8 + 'resourceSelector' 9 +], function($, diagramLinkHandler) { 10 + /** 11 + * Override in order to change the Element type check from a.constructor !== Element, which was failing due to a 12 + * collision with the PrototypeJS's own implementation of the standard Element. 13 + * See http://api.prototypejs.org/dom/Element/new/index.html. This code is taken from the minified version of 14 + * mxClient.js and should be removed after PrototypeJS is no longer loaded. 15 + */ 16 + mxUtils.isNode = function(a, b, c, d) { 17 + return null == a || a.nodeType !== Node.ELEMENT_NODE || null != b && 18 + a.nodeName.toLowerCase() != b.toLowerCase() ? !1 : null == c || a.getAttribute(c) == d; 19 + }; 20 + 21 +/** 22 + * TODO: When upgrading, make sure to check if this method has changed. 23 + * This method was copied directly from draw.io, and the only change made was the call to this.showDialog. 24 + * Both the width and height were modified to fit better within the XWiki UI. 25 + */ 26 +EditorUi.prototype.showBackgroundImageDialog = function (apply, img, color, showColor) { 27 + apply = (apply != null) ? apply : mxUtils.bind(this, function (image, failed, color, shadowVisible) { 28 + if (!failed) { 29 + var change = new ChangePageSetup(this, (showColor) ? color : null, image); 30 + change.ignoreColor = !showColor; 31 + if (shadowVisible != null && showColor) { 32 + change.shadowVisible = shadowVisible; 33 + } 34 + this.editor.graph.model.execute(change); 35 + } 36 + }); 37 + 38 + var dlg = new BackgroundImageDialog(this, apply, img, color, showColor); 39 + this.showDialog(dlg.container, 420, (showColor) ? 260 : 240, true, true); 40 + dlg.init(); 41 +}; 42 + 43 + 44 + EditorUi.prototype.showLinkDialog = function(value, selectLabel, callback, showNewWindowOption, linkTarget) { 45 + var resourceReference = diagramLinkHandler.getResourceReferenceFromCustomLink(value); 46 + // We append the modal to the body element in order to fix Issue #108: "Inserting a link in full screen mode is not 47 + // possible". 48 + $('#diagramLinkModal').appendTo('body').selectResource(resourceReference, { 49 + selectLabel: selectLabel 50 + }).done(function(resourceReference) { 51 + callback(diagramLinkHandler.getCustomLinkFromResourceReference(resourceReference), null, linkTarget); 52 + }); 53 + }; 54 + 55 + // For some cases, to consider wrapping or overflow, w might be altered and we need to keep the initial value as 56 + // it was from bounds.width of the base mxText node. 57 + var originalText = mxSvgCanvas2D.prototype.text; 58 + mxSvgCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir) { 59 + this.state.initialWidth = w; 60 + return originalText.apply(this, arguments); 61 + }; 62 + 63 + // Don't add warning when the viewer doesn't support SVG 1.1, since we create a fallback for foreignObjects. 64 + Graph.prototype.addForeignObjectWarning = function(canvas, root) { 65 + // Do nothing. 66 + } 67 + 68 + // Override the Graph.updateSvgLinks method to ensure that the links are preserved in the final SVG. 69 + // We need to directly modify the original method because drawio does not provide a parameter to override the default 70 + // behavior or a smaller method that only performs the replacement. 71 + Graph.prototype.updateSvgLinks = function (node, target, removeCustom) { 72 + var links = node.getElementsByTagName('a'); 73 + for (var i = 0; i < links.length; i++) { 74 + if (links[i].getAttribute('target') == null) { 75 + var href = links[i].getAttribute('href'); 76 + if (href == null) { 77 + href = links[i].getAttribute('xlink:href'); 78 + } 79 + 80 + if (href != null) { 81 + if (target != null && /^https?:\/\//.test(href)) { 82 + // If the href starts with http or https, set the target attribute. 83 + links[i].setAttribute('target', target); 84 + } else if (this.isCustomLink(href)) { 85 + // Handle custom links 86 + let absoluteLink = $('<a/>').attr('href', diagramLinkHandler.getURLFromCustomLink(href)).prop('href'); 87 + links[i].setAttribute('href', absoluteLink); 88 + } 89 + } 90 + } 91 + } 92 + }; 93 + 94 + // Overwrite Graph.getSvg in order to replace XWiki custom links with absolute URLs. 95 + // Also fix the text fallback for viewers with no support for foreignObjects. 96 + var originalGraphGetSVG = Graph.prototype.getSvg; 97 + Graph.prototype.getSvg = function(background, scale, border, nocrop, crisp, ignoreSelection, showText, imgExport, 98 + linkTarget, hasShadow, incExtFonts, keepTheme, exportType, cells) { 99 + imgExport = imgExport || this.createSvgImageExport(); 100 + var originalGetLinkForCellState = imgExport.getLinkForCellState; 101 + imgExport.getLinkForCellState = function() { 102 + var result = originalGetLinkForCellState.apply(this, arguments); 103 + if (diagramLinkHandler.isXWikiCustomLink(result)) { 104 + result = diagramLinkHandler.getURLFromCustomLink(result); 105 + // Use the absolute URL because this SVG is used for PDF export which needs to be portable. 106 + result = $('<a/>').attr('href', result).prop('href'); 107 + } 108 + return result; 109 + } 110 + var originalDrawState = imgExport.drawState; 111 + imgExport.drawState = function(state, canvas) { 112 + var originalCreateAlternateContent = canvas.createAlternateContent; 113 + canvas.createAlternateContent = function(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, 114 + rotation) { 115 + if (format === 'html') { 116 + // Keep only the text content. 117 + str = $('<div/>').html(str).text() || this.foAltText; 118 + } 119 + return originalCreateAlternateContent.call(this, fo, x, y, w, h, str, align, valign, wrap, format, overflow, 120 + clip, rotation); 121 + }; 122 + return originalDrawState.apply(this, arguments); 123 + }; 124 + try { 125 + return originalGraphGetSVG.call(this, background, scale, border, nocrop, crisp, ignoreSelection, showText, 126 + imgExport, linkTarget, hasShadow, incExtFonts, keepTheme, exportType, cells); 127 + } finally { 128 + imgExport.getLinkForCellState = originalGetLinkForCellState; 129 + imgExport.drawState = originalDrawState; 130 + } 131 + }; 132 +}); 133 + 134 +/** 135 + * Adds support for using XWiki image attachments in diagrams. 136 + */ 137 +define('diagram-image-editor', ['xwiki-utils', 'diagram-link-handler', 'draw.io'], function(xutils, diagramLinkHandler) { 138 + // Fix the base URL used when exporting the diagram as image. 139 + var originalCreateImageUrlConverter = EditorUi.prototype.createImageUrlConverter; 140 + EditorUi.prototype.createImageUrlConverter = function() { 141 + var converter = originalCreateImageUrlConverter.call(this); 142 + converter.convert = function(src) { 143 + // Use baseDomain instead of baseUrl to detect external URLs. 144 + if (src && (src.substr(0, 7) === 'http://' || src.substr(0, 8) === 'https://') && 145 + src.substr(0, converter.baseDomain.length) !== converter.baseDomain) { 146 + src = PROXY_URL + '?url=' + encodeURIComponent(src); 147 + } 148 + return src; 149 + }; 150 + return converter; 151 + }; 152 + 153 + // Override for uploading the image as attachment instead of encode it to Base64. 154 + var originalImportFiles = EditorUi.prototype.importFiles; 155 + EditorUi.prototype.importFiles = function(files, x, y, maxSize, fn, resultFn, filterFn, barrierFn, resizeDialog, 156 + maxBytes, resampleThreshold, ignoreEmbeddedXml, evt) { 157 + let importFilesArgs = arguments; 158 + if (fn) { 159 + let editorUi = this; 160 + importFilesArgs = Array.prototype.slice.call(arguments); 161 + // This is the call back function responsible to insert the image. 162 + importFilesArgs[4] = function(data, mimeType, x, y, w, h, filename) { 163 + if (data.substring(0, 5) == 'data:') { 164 + let fnArgs = Array.prototype.slice.call(arguments); 165 + let fileBase64Data = data.substring(data.indexOf(',') + 1); 166 + xutils.uploadAttachment(editorUi.base64ToBlob(fileBase64Data, mimeType), filename).done(function() { 167 + // Include the attachment reference in the URL using the fragment identifier in order to be able to use it 168 + // when saving the diagram (we want to save the attachment reference not the attachment URL). 169 + let resourceReference = { 170 + type: 'attach', 171 + reference: XWiki.Model.serialize(new XWiki.AttachmentReference(filename)) 172 + }; 173 + let customLink = diagramLinkHandler.getCustomLinkFromResourceReference(resourceReference); 174 + fnArgs[0] = xutils.getAttachmentURL(filename) + '#' + encodeURIComponent(customLink); 175 + fn.apply(this, fnArgs); 176 + }); 177 + } else { 178 + fn.apply(this, arguments); 179 + } 180 + }; 181 + } 182 + originalImportFiles.apply(this, importFilesArgs); 183 + }; 184 + 185 + // Add support for inserting images by specifying the XWiki attachment reference. 186 + var originalShowImageDialog = EditorUi.prototype.showImageDialog; 187 + EditorUi.prototype.showImageDialog = function(title, value, fn, ignoreExisting, convertDataUri, withCrop, 188 + initClipPath) { 189 + var showImageDialogArgs = Array.prototype.slice.call(arguments); 190 + var customLink = diagramLinkHandler.getCustomLinkFromURL(value); 191 + if (customLink) { 192 + // Edit the XWiki custom link instead of the actual URL. 193 + showImageDialogArgs[1] = customLink; 194 + } 195 + if (typeof fn === 'function') { 196 + showImageDialogArgs[2] = function(newValue, width, height) { 197 + var fnArgs = Array.prototype.slice.call(arguments); 198 + if (diagramLinkHandler.isXWikiCustomLink(newValue)) { 199 + // Save the actual URL, but keep the custom link as fragment identifier. 200 + fnArgs[0] = diagramLinkHandler.getURLFromCustomLink(newValue) + '#' + encodeURIComponent(newValue); 201 + } 202 + fn.apply(this, fnArgs); 203 + }; 204 + } 205 + originalShowImageDialog.apply(this, showImageDialogArgs); 206 + }; 207 + 208 + // This function is used by the image dialog to preload the images before inserting them. We have to overwrite it in 209 + // order to make it use the actual URL for XWiki image attachments. 210 + var originalLoadImage = EditorUi.prototype.loadImage; 211 + EditorUi.prototype.loadImage = function(uri, onload, onerror) { 212 + var loadImageArgs = Array.prototype.slice.call(arguments); 213 + if (diagramLinkHandler.isXWikiCustomLink(uri)) { 214 + loadImageArgs[0] = diagramLinkHandler.getURLFromCustomLink(uri); 215 + } 216 + originalLoadImage.apply(this, loadImageArgs); 217 + }; 218 +}); 219 + 220 +/** 221 + * Customizes the diagram export as URL and the diagram import from URL. 222 + */ 223 +define('diagram-url-io', ['diagram-utils', 'draw.io'], function(diagramUtils) { 224 + var urlParam = function(parameter, url) { 225 + var results = new RegExp('[\?&]' + parameter + '=([^&#]*)').exec(url); 226 + if (results === null) { 227 + return null; 228 + } 229 + return decodeURIComponent(results[1]); 230 + }; 231 + 232 + var getParameterValueFromURL = function(parameter, url) { 233 + if (typeof URLSearchParams === 'function') { 234 + return new URL(url, window.location.href).searchParams.get(parameter); 235 + } 236 + // IE will get here since it's not supporting URLSearchParams. 237 + return urlParam(parameter, url); 238 + }; 239 + 240 + // Custom diagram import from URL. 241 + var loadUrl = function(url) { 242 + var diagramXML = null; 243 + let exportedUrl = getParameterValueFromURL('url', url); 244 + if (exportedUrl) { 245 + diagramXML = diagramUtils.getDiagramXMLFromURL(exportedUrl); 246 + } 247 + return diagramXML; 248 + }; 249 + 250 + // Custom diagram export as URL (using the current host). 251 + var originalCreateLink = EditorUi.prototype.createLink; 252 + EditorUi.prototype.createLink = function(linkTarget, linkColor, allPages, lightbox, editLink, layers, url, ignoreFile, 253 + params, useOpenParameter) { 254 + let rawURL = originalCreateLink.apply(this, arguments); 255 + // Do not include '#' because it's automatically added by getURL function below. 256 + let documentFragmentIndex = rawURL.indexOf('#'); 257 + let documentFragment = rawURL.substring(documentFragmentIndex + 1); 258 + if (documentFragment.substring(0, 1) == 'R') { 259 + let queryString = ''; 260 + let queryStringIndex = rawURL.indexOf('?'); 261 + if (queryStringIndex > -1) { 262 + queryString = rawURL.substring (queryStringIndex + 1, documentFragmentIndex); 263 + } 264 + // Append source parameter to the query 265 + queryString += '&source=url'; 266 + return window.location.protocol + '//' + window.location.host + 267 + new XWiki.Document('DiagramViewSheet', 'Diagram').getURL('view', queryString, documentFragment); 268 + } 269 + return rawURL; 270 + } 271 + 272 + return { 273 + loadUrl: loadUrl 274 + }; 275 +}); 276 + 277 +/** 278 + * In case external services are disabled, stop the features that require it (online shape search, help section 279 + * external links) and show an info dialog. 280 + */ 281 +define('diagram-external-services', ['jquery', 'diagram-config', 'draw.io'], function($, diagramConfig) { 282 + var showDisabledServicesDialog = function(editorUi) { 283 + var errorMessage = $('<div></div') 284 + .html($jsontool.serialize($services.localization.render('diagram.editor.disabledExternalServices'))); 285 + 286 + errorMessage.addClass('externalServicesDialog'); 287 + var dlg = new CustomDialog(/*editorUi*/ editorUi, /*content*/ errorMessage[0], /*okFn*/ null, /*cancelFn*/ null, 288 + /*okButtonText*/ mxResources.get('ok'), /*helpLink*/ null, /*buttonsContent*/ null, 289 + /*hideCancel*/ true); 290 + editorUi.showDialog(dlg.container, 250, 75, true, true); 291 + }; 292 + 293 + var originalIsOfflineApp = EditorUi.prototype.isOfflineApp; 294 + EditorUi.prototype.isOfflineApp = function() { 295 + return diagramConfig.disableExternalServices || originalIsOfflineApp(); 296 + }; 297 +}); 298 + 299 +define('diagramMenuTranslations', { 300 + prefix: 'diagram.editor.menu.', 301 + keys: [ 302 + // File menu. 303 + 'print.label', 304 + 'print.title' 305 + ] 306 +}); 307 + 308 +/** 309 + * Integrates draw.io diagram editor in XWiki. 310 + */ 311 +define('diagram-editor', [ 312 + 'jquery', 313 + 'diagram-store', 314 + 'diagram-utils', 315 + 'diagram-url-io', 316 + 'diagram-config', 317 + 'xwiki-l10n!diagramMenuTranslations', 318 + 'diagram-graph-xml-filter', 319 + 'diagram-link-editor', 320 + 'diagram-image-editor', 321 + 'diagram-external-services' 322 +], function($, diagramStore, diagramUtils, diagramUrlIO, diagramConfig, l10n) { 323 + var diagramEditorKeyboardShortcutsPath = "$services.webjars.url('org.xwiki.contrib:draw.io', 'shortcuts.svg')"; 324 + // These variables are used to decide if an image should be uploaded at original resolution or 325 + // should be declined for being too big. 326 + // Default values: 327 + // EditorUi.prototype.maxImageSize = 520; 328 + // EditorUi.prototype.maxImageBytes = 1000000; 329 + 2 2 // 3 3 // Diagram Editor Constructor. 4 4 // 5 5 var createDiagramEditor = function(options) { 6 6 options = options || {}; 7 - var editorUI = new App(new Editor(false, options.themes), options.container); 8 - var file = diagramStore.createFile(editorUI, options.input, options.fileName); 9 - editorUI.loadFile(options.fileName, true, file); 335 + // This is needed since we do not use drafts and it would create the file to soon, leading to the file being opened 336 + // in a new window. 337 + EditorUi.enableDrafts = false; 338 + var editor = new Editor(/* chromeless: */ uiTheme === 'min', options.themes, /* model: */ null, /* graph: */ null, 339 + /* editable: */ true); 340 + var editorUI = new App(editor, options.container); 341 + // This is usefull for debugging the diagram editor from the JavaScript console. 342 + $(editorUI.container).data('diagramEditor', editorUI).trigger('diagramEditorCreated', editorUI); 343 + // Fix the editor UI before loading the diagram because layout changes can influence the way the shapes are drawn. 344 + fixEditorUI(editorUI); 345 + fixLoadUrl(editorUI); 346 + var file = diagramStore.createFile(editorUI, options.input, options.fileName, options.documentReference); 347 + // The first letter of the file name is used to determine the storage type. Let's use 'X' for XWiki storage. 348 + editorUI.loadFile('X' + options.fileName, true, file); 10 10 return editorUI; 11 11 }; 12 12 352 + var fixEditorUI = function(editorUI) { 353 + cleanMenu(editorUI); 354 + renameMenu(editorUI); 355 + fixKeyboardShortcutsAction(editorUI); 356 + fixEditorButtons($(editorUI.container)); 357 + removeThemeButton(); 358 + // These are not present on mobile. 359 + if (editorUI.menubar != null) { 360 + removeCompactModeToggle(editorUI); 361 + fixFullScreenToggle(editorUI); 362 + } 363 + }; 364 + 365 + var fixLoadUrl = function(editorUI) { 366 + // Custom diagram import from URL. 367 + var originalLoadUrl = editorUI.editor.loadUrl; 368 + editorUI.editor.loadUrl = function(url, success, error, forceBinary, retry, dataUriPrefix, noBinary, headers) { 369 + var diagramXML = diagramUrlIO.loadUrl(url); 370 + if (diagramXML != null) { 371 + return success(diagramXML); 372 + } 373 + return originalLoadUrl.apply(this, arguments); 374 + }; 375 + }; 376 + 13 13 // 14 - // Disable thetabbedUI (settingurlParams['pages']to'0'is notenough..)378 + // Change the service name in order to disable notifications. 15 15 // 16 - EditorUi.prototype. initPages= function() {17 - // Do nothing.380 + EditorUi.prototype.getServiceName = function() { 381 + return 'xwiki.com'; 18 18 }; 19 19 20 20 // Don't change the document title. ... ... @@ -24,24 +24,70 @@ 24 24 // Add support for disabling an entire sub-menu. 25 25 // 26 26 var originalAddSubmenu = Menus.prototype.addSubmenu; 27 - Menus.prototype.addSubmenu = function(name, menu, parent) { 391 + Menus.prototype.addSubmenu = function(name, menu, parent, label) { 28 28 var subMenu = this.get(name); 29 - if (subMenu && subMenu. visible !== false) {30 - originalAddSubmenu.apply(this, arguments); 393 + if (subMenu && subMenu.isEnabled() !== false) { 394 + return originalAddSubmenu.apply(this, arguments); 31 31 } 32 32 }; 33 33 398 + 399 + /* 400 + * Map with all the menu items that we want to have a title. 401 + */ 402 + const titleMap = new Map([ 403 + ['print', l10n['print.title']] 404 + ]); 405 + 406 + /* 407 + * Update the title of the menu items. 408 + */ 409 + var originalAddMenuItem = Menus.prototype.addMenuItem; 410 + Menus.prototype.addMenuItem = function(menu, key, parent, trigger, sprite, label) { 411 + let item = originalAddMenuItem.apply(this, arguments); 412 + if (item != null && titleMap.has(key)) { 413 + item.title = titleMap.get(key); 414 + } 415 + return item; 416 + }; 417 + 418 + // Remove the language picker because the diagram editor is configured to use the XWiki language. 419 + var originalCreateMenubar = Menus.prototype.createMenubar; 420 + Menus.prototype.createMenubar = function(container) { 421 + delete this.menus['language']; 422 + return originalCreateMenubar.apply(this, arguments); 423 + } 424 + 34 34 // 35 - // Hidetheeditor footer.426 + // Add support for not displaying 'browser' option from 'Import from' sub-menu 36 36 // 37 - var hideFooter = function(editorUI) { 38 - // We call this just in case the footer is visible. 39 - editorUI.hideFooter(); 40 - // Make sure the diagram editor doesn't leave space for the footer. 41 - editorUI.footerHeight = 0; 428 + var originalAddItem = mxPopupMenu.prototype.addItem; 429 + mxPopupMenu.prototype.addItem = function(title, image, funct, parent, iconCls, enabled, active) { 430 + if (title === (mxResources.get('browser') + '...') && parent && parent.innerText === 'Import from') { 431 + return null; 432 + } 433 + return originalAddItem.apply(this, arguments); 42 42 }; 43 43 44 44 // 437 + // Rename menu options to fit our needs. 438 + // 439 + var renameMenu = function(editorUI) { 440 + const menuItems = [ 441 + // File menu 442 + ['print', l10n['print.label']] 443 + ]; 444 + 445 + // Iterate over the array of tuples 446 + menuItems.forEach(function([menuKey, newLabel]) { 447 + var action = editorUI.actions.actions[menuKey]; 448 + if (action) { 449 + action.label = newLabel; 450 + } 451 + }); 452 + }; 453 + 454 + // 45 45 // Clean the editor menu by removing the features that are not needed. 46 46 // 47 47 var cleanMenu = function(editorUI) { ... ... @@ -48,11 +48,13 @@ 48 48 // Disable and hide some of the menu entries. 49 49 [ 50 50 // File menu 51 - 'new', 'open', 'save', 'saveAs', 'rename', 'makeCopy', 'close', 461 + 'synchronize', 'new', 'open', 'save', 'saveAs', 'rename', 'makeCopy', 'close', 52 52 // Extras menu 53 53 'autosave', 'showStartScreen', 'plugins', 'offline', 'chromeApp', 54 - // Help menu (Graph Editor) 55 - 'help' 464 + // Help menu 465 + 'downloadDesktop', 'useOffline', 466 + // ExportAs 467 + 'exportHtml', 'exportPdf' 56 56 ].forEach(function(actionName) { 57 57 var action = editorUI.actions.actions[actionName]; 58 58 if (action) { ... ... @@ -74,51 +74,26 @@ 74 74 }); 75 75 }; 76 76 77 - // Fix the base URL used when exporting the diagram as image. 78 - var oldCreateImageUrlConverter = EditorUi.prototype.createImageUrlConverter; 79 - EditorUi.prototype.createImageUrlConverter = function() { 80 - var converter = oldCreateImageUrlConverter.call(this); 81 - converter.convert = function(src) { 82 - // Use baseDomain instead of baseUrl to detect external URLs. 83 - if (src && (src.substr(0, 7) === 'http://' || src.substr(0, 8) === 'https://') && 84 - src.substr(0, converter.baseDomain.length) !== converter.baseDomain) { 85 - src = PROXY_URL + '?url=' + encodeURIComponent(src); 86 - } 87 - return src; 88 - }; 89 - return converter; 489 + // 490 + // Fix the side bar tool tip: the tool tip position is computed as if the editor takes the full screen. 491 + // 492 + var originalGetTooltipOffset = Sidebar.prototype.getTooltipOffset; 493 + Sidebar.prototype.getTooltipOffset = function(elt, bounds) { 494 + var fullScreenCoordinates = originalGetTooltipOffset.apply(this, arguments); 495 + // Adjust the tool tip coordinates with the editor offset. 496 + var offset = $(this.editorUi.container).offsetParent().offset(); 497 + return new mxPoint(fullScreenCoordinates.x + offset.left, fullScreenCoordinates.y + offset.top); 90 90 }; 91 91 92 92 // 93 - // Fixthe sidebartooltip: thetool tippositionis computedasifthe editortakes the full screen.501 + // Consider the basePath for the shapes added in the left panel. 94 94 // 95 - var oldShowTooltip = Sidebar.prototype.showTooltip; 96 - Sidebar.prototype.showTooltip = function(elt) { 97 - if (this.enableTooltips && this.showTooltips && this.currentElt != elt) { 98 - // The next usage of mxUtils.bind() is to bind the (private) show function to the side bar object. 99 - // We need to overwrite the show function. 100 - var oldBind = mxUtils.bind; 101 - mxUtils.bind = function(object, method) { 102 - // Restore the original function. 103 - mxUtils.bind = oldBind; 104 - return oldBind(object, function() { 105 - var result = method.apply(this, arguments); 106 - // Adjust the tool tip coordinates because they are computed as if the editor takes the full screen. 107 - // Thus we need to add the editor offset. 108 - var offset = $(this.container).parent().offsetParent().offset(); 109 - $(this.tooltip).css({ 110 - left: (offset.left + parseInt(this.tooltip.style.left)) + 'px', 111 - top: (offset.top + parseInt(this.tooltip.style.top)) + 'px' 112 - }); 113 - $(this.tooltipImage).css({ 114 - left: (offset.left + parseInt(this.tooltipImage.style.left)) + 'px', 115 - top: (offset.top + parseInt(this.tooltipImage.style.top)) + 'px' 116 - }); 117 - return result; 118 - }); 119 - }; 120 - } 121 - oldShowTooltip.apply(this, arguments); 503 + var oldCreateVertexTemplateEntry = Sidebar.prototype.createVertexTemplateEntry; 504 + Sidebar.prototype.createVertexTemplateEntry = function(style, width, height, value, title, showLabel, showTitle, 505 + allowCellsInserted, showTooltip, clickFn, thumbWidth, thumbHeight) { 506 + style = style.replace(/(image=)(img\/lib)/g, '$1' + diagramConfig.drawIOBasePath + '$2'); 507 + return oldCreateVertexTemplateEntry.call(this, style, width, height, value, title, showLabel, showTitle, 508 + allowCellsInserted, showTooltip, clickFn, thumbWidth, thumbHeight); 122 122 }; 123 123 124 124 // ... ... @@ -127,12 +127,12 @@ 127 127 var fixKeyboardShortcutsAction = function(editorUI) { 128 128 var keyboardShortcutsAction = editorUI.actions.get('keyboardShortcuts'); 129 129 if (keyboardShortcutsAction) { 130 - var ol dFunct = keyboardShortcutsAction.funct;517 + var originalFunct = keyboardShortcutsAction.funct; 131 131 keyboardShortcutsAction.funct = function() { 132 132 if (mxClient.IS_SVG) { 133 - window.open(diagramEditor BasePath+ 'shortcuts.svg');520 + window.open(diagramEditorKeyboardShortcutsPath); 134 134 } else { 135 - ol dFunct.apply(this, arguments);522 + originalFunct.apply(this, arguments); 136 136 } 137 137 }; 138 138 } ... ... @@ -143,7 +143,7 @@ 143 143 // 144 144 var removeCompactModeToggle = function(editorUI) { 145 145 if (typeof editorUI.toggleCompactMode === 'function') { 146 - editorUI.toggleCompactMode( true);533 + editorUI.toggleCompactMode(/* visible: */ false); 147 147 var buttons = $(editorUI.container).find('.geToolbarContainer > a.geButton'); 148 148 buttons.last().remove(); 149 149 buttons.css('right', function(index, value) { ... ... @@ -153,6 +153,11 @@ 153 153 }; 154 154 155 155 var fullScreen = new XWiki.widgets.FullScreen(); 543 + // We only call the init in versions that have the function defined so we don't get an error in the console. 544 + // The 'if' should be removed after upgrading the app to a parent >= 17.10.2. 545 + if (typeof fullScreen.initDom === 'function') { 546 + fullScreen.initDom(); 547 + } 156 156 var fixFullScreenToggle = function(editorUI) { 157 157 mxEvent.removeAllListeners(editorUI.fullscreenElement); 158 158 editorUI.container._x_fullScreenActivator = editorUI.fullscreenElement; ... ... @@ -162,7 +162,7 @@ 162 162 if (isFullScreen) { 163 163 // Exit full screen mode. 164 164 fullScreen.closeFullScreen(); 165 - editorUI. toggleCompactMode(true);557 + editorUI.refresh(); 166 166 } else { 167 167 // Enter full screen mode. 168 168 fullScreen.makeFullScreen(editorUI.container); ... ... @@ -169,54 +169,78 @@ 169 169 // The previous line hides the 'fullScreenActivator' and shows the 'Exit Full Screen' button. We want to use the 170 170 // 'fullScreenActivator' for exiting the full screen mode. The 'Exit Full Screen' button is hidden from CSS. 171 171 $(editorUI.fullscreenElement).show(); 172 - editorUI. toggleCompactMode();564 + editorUI.refresh(); 173 173 } 174 174 }); 175 175 }; 176 176 177 177 // 178 - // jQuery plugin 570 + // Add the type of the buttons manually to stop the default submit. The preventDefault used in editDiagram is not 571 + // working for these since in draw.io code is called stopPropagation. 179 179 // 573 + var fixEditorButtons = function(editor) { 574 + editor.find('button:not([type])').each(function() { 575 + $(this).attr('type', 'button'); 576 + }); 577 + }; 578 + 579 + var removeThemeButton = function() { 580 + $('.geAdaptiveAsset[title="Theme"]').remove(); 581 + }; 582 + 583 + // mxRuler adds the ruler to the document body instead of adding it to the editor container. 584 + var originalMxDualRuler = mxDualRuler; 585 + mxDualRuler = function(editorUI, unit) { 586 + originalMxDualRuler.apply(this, arguments); 587 + $([this.hRuler.container, this.vRuler.container]).appendTo(editorUI.container); 588 + }; 589 + mxDualRuler.prototype = Object.create(originalMxDualRuler.prototype); 590 + mxDualRuler.prototype.constructor = mxDualRuler; 591 + 180 180 var themes = {}; 593 + var getDiagramEditorConfig = function(container) { 594 + var input = $(container).children('input.diagram-content'); 595 + var documentReference = input.data('reference') || ''; 596 + if (typeof documentReference === 'string') { 597 + documentReference = XWiki.Model.resolve(documentReference, XWiki.EntityType.DOCUMENT, 598 + XWiki.currentDocument.documentReference); 599 + } 600 + var fileName = input.data('title') || $('#document-title').text(); 601 + if (!fileName) { 602 + fileName = documentReference.name == 'WebHome' ? documentReference.parent.name : documentReference.name; 603 + } 604 + return { 605 + container: container, 606 + themes: themes, 607 + fileName: fileName, 608 + input: input, 609 + documentReference: documentReference 610 + }; 611 + }; 612 + 613 + // 614 + // jQuery plugin 615 + // 181 181 $.fn.editDiagram = function(options) { 182 182 return this.on('click', 'button', function(event) { 183 183 // Make sure the buttons from the editor UI don't submit the edit form. 184 184 event.preventDefault(); 620 + }).on('keydown keyup keypress', '.geContentEditable', function(event) { 621 + // Make sure the keyboard events triggered from the nested editable sections are not propagated as they may 622 + // trigger shortcut keys (the nested editable sections need to behave like input fields). 623 + // See issue #15: If you install XWebIDE Application you can't use "W" letter in the diagram text. 624 + event.stopPropagation(); 185 185 }).each(function() { 186 - var editorUI = createDiagramEditor($.extend({ 187 - container: this, 188 - themes: themes, 189 - fileName: $('#document-title').text() || XWiki.currentPage, 190 - input: $(this).children('input.diagram-content') 191 - }, options)); 626 + // We need this CSS class on the body element in order to have proper styling for the UI elements (menus, dialogs, 627 + // tooltips) that are added directly under the body element. 628 + $(document.body).addClass('geEditor'); 629 + createDiagramEditor($.extend(getDiagramEditorConfig(this), options)); 192 192 $(this).removeClass('loading'); 193 - hideFooter(editorUI); 194 - cleanMenu(editorUI); 195 - fixKeyboardShortcutsAction(editorUI); 196 - removeCompactModeToggle(editorUI); 197 - fixFullScreenToggle(editorUI); 198 198 }); 199 199 }; 200 200 201 - // 202 - // Load the translation files. 203 - // 204 - var diagramEditorDeferred = $.Deferred(); 205 - mxResources.loadDefaultBundle = false; 206 - var bundle = mxResources.getDefaultBundle(RESOURCE_BASE, mxLanguage) || 207 - mxResources.getSpecialBundle(RESOURCE_BASE, mxLanguage); 208 - mxUtils.getAll([bundle, STYLE_PATH + '/default.xml'], function(response) { 209 - // Adds bundle text to resources. 210 - mxResources.parse(response[0].getText()); 211 - 212 - // Configures the default editor theme. 213 - themes[Graph.prototype.defaultThemeName] = response[1].getDocumentElement(); 214 - 215 - diagramEditorDeferred.resolve(); 216 - }, function() { 217 - // Failed to load resources. 218 - diagramEditorDeferred.reject(); 634 + return diagramUtils.loadTranslationAndTheme().done(function(theme) { 635 + // Configure the default editor theme. 636 + themes[Graph.prototype.defaultThemeName] = theme; 219 219 }); 220 - 221 - return diagramEditorDeferred.promise(); 222 222 });
- XWiki.JavaScriptExtension[3]
-
- Code
-
... ... @@ -1,5 +1,37 @@ 1 -require(['jquery', 'diagramEditor'], function($, diagramEditorPromise) { 2 - diagramEditorPromise.done(function() { 3 - $('.diagram-editor').editDiagram(); 1 +/*! 2 +## Make sure that the version loaded with RequireJS is not a cached one. 3 +#set ($version = $services.extension.installed.getInstalledExtension('com.xwiki.diagram:application-diagram', 4 + "wiki:$xcontext.database").version.value) 5 +#set ($params = $escapetool.url({ 6 + 'minify': $!services.debug.minify, 7 + 'appVersion': $version 8 +})) 9 +#[[*/ 10 +// Start JavaScript-only code. 11 +(function(params) { 12 + "use strict"; 13 + 14 +require.config({ 15 + paths: { 16 + 'diagram-setup': new XWiki.Document('DiagramSheet', 'Diagram').getURL('jsx', params) 17 + }, 18 + map: { 19 + 'diagram-utils': { 20 + 'mxgraph-common': 'mxgraph-editor' 21 + }, 22 + 'diagram-link-handler': { 23 + 'draw.io.common': 'draw.io' 24 + } 25 + } 26 +}); 27 + 28 +require(['diagram-setup'], function() { 29 + require(['jquery', 'diagram-editor'], function($, diagramEditorPromise) { 30 + diagramEditorPromise.done(function() { 31 + $('.diagram-editor').editDiagram(); 32 + }); 4 4 }); 5 5 }); 35 + 36 +// End JavaScript-only code. 37 +}).apply(']]#', $jsontool.serialize([$params]));
- XWiki.StyleSheetExtension[0]
-
- Pufferstrategie
-
... ... @@ -1,1 +1,0 @@ 1 -long - Code
-
... ... @@ -1,87 +1,0 @@ 1 -/* The diagram editor styles should be loaded after the skin and before our overwrites. */ 2 -@import url("$services.webjars.url('org.xwiki.contrib:mxgraph-editor', 'styles/grapheditor.css')"); 3 - 4 -.diagram-editor { 5 - height: 600px; 6 - min-height: 20px; 7 - position: relative; 8 -} 9 - 10 -.diagram-editor input[type="checkbox"], .diagram-editor input[type="radio"], 11 -.mxPopupMenu input[type="checkbox"], .mxPopupMenu input[type="radio"], 12 -.mxWindow input[type="checkbox"], .mxWindow input[type="radio"], 13 -.geDialog input[type="checkbox"], .geDialog input[type="radio"] { 14 - vertical-align: text-bottom; 15 -} 16 - 17 -.fullScreenWrapper .buttons > .buttonwrapper:first-child { 18 - /* Hide the "Exit Full Screen" button. We have a tool bar entry for this. */ 19 - display: none !important; 20 -} 21 - 22 -/** 23 - * Overwrite XWiki skin styles 24 - */ 25 -.diagram-editor *, 26 -.mxPopupMenu *, 27 -.mxWindow *, 28 -.geDialog, 29 -.geDialog * { 30 - box-sizing: content-box; 31 -} 32 - 33 -.mxPopupMenu, 34 -.mxWindow, 35 -.geDialog { 36 - /* We need the same font size as on draw.io because the dialog height is hard-coded. */ 37 - font-size: 10pt; 38 -} 39 - 40 -.diagram-editor button, .diagram-editor select, 41 -.mxPopupMenu button, .mxPopupMenu select, 42 -.mxWindow button, .mxWindow select, 43 -.geDialog button, .geDialog select { 44 - box-sizing: border-box; 45 -} 46 - 47 -.diagram-editor input[type="text"], 48 -.mxPopupMenu input[type="text"], 49 -.mxWindow input[type="text"], 50 -.geDialog input[type="text"] { 51 - font-size: inherit; 52 - height: auto; 53 - padding: 1px; 54 -} 55 - 56 -.diagram-editor img, 57 -.mxPopupMenu img, 58 -.mxWindow img, 59 -.geDialog img { 60 - vertical-align: baseline; 61 -} 62 - 63 -.diagram-editor hr, 64 -.mxPopupMenu hr, 65 -.mxWindow hr, 66 -.geDialog hr { 67 - margin: 0; 68 -} 69 - 70 -.mxPopupMenu table, 71 -.mxWindow table, 72 -.geDialog table { 73 - margin-bottom: 0; 74 - width: auto; 75 -} 76 - 77 -.diagram-editor table > tbody > tr > td, 78 -.mxPopupMenu table > tbody > tr > td, 79 -.mxWindow table > tbody > tr > td, 80 -.geDialog table > tbody > tr > td { 81 - border-top: 0 none; 82 -} 83 - 84 -.geDialog table > tbody > tr > td { 85 - padding: 0; 86 - vertical-align: baseline; 87 -} - Content Type
-
... ... @@ -1,1 +1,0 @@ 1 -CSS - Inhalt parsen
-
... ... @@ -1,1 +1,0 @@ 1 -Ja - Benutze diese Erweiterung
-
... ... @@ -1,1 +1,0 @@ 1 -onDemand