Änderungen von Dokument DiagramEditSheet

Zuletzt geändert von Daniel Herrmann am 2026/02/04 20:25

Von Version 1.1 Icon
bearbeitet von Mike Schneider
am 2025/12/30 10:37
Änderungskommentar: Install extension [org.xwiki.contrib:application-diagram/1.3]
Auf Version Icon 2.1
bearbeitet von Daniel Herrmann
am 2026/02/04 20:25
Änderungskommentar: Install extension [com.xwiki.diagram:application-diagram/1.22.11]

Zusammenfassung

Details

Icon Seiteneigenschaften
Dokument-Autor
... ... @@ -1,1 +1,1 @@
1 -XWiki.mschneide
1 +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">&times;</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.DiagramEditSheet'))
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}}
Icon 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
Icon 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 -Ja
1 +Nein
Icon 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 the tabbed UI (setting urlParams['pages'] to '0' is not enough..)
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 - // Hide the editor 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 - // Fix the side bar tool tip: the tool tip position is computed as if the editor takes 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 oldFunct = keyboardShortcutsAction.funct;
517 + var originalFunct = keyboardShortcutsAction.funct;
131 131   keyboardShortcutsAction.funct = function() {
132 132   if (mxClient.IS_SVG) {
133 - window.open(diagramEditorBasePath + 'shortcuts.svg');
520 + window.open(diagramEditorKeyboardShortcutsPath);
134 134   } else {
135 - oldFunct.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  });
Icon 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]));
Icon 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