viewer2.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Draw.io Viewer</title>
  6. <style type="text/css">
  7. html, body {
  8. height:100%;
  9. overflow:hidden;
  10. }
  11. body {
  12. background-color:#ffffff;
  13. background-image:url(/images/drawlogo-gray.svg);
  14. background-repeat:no-repeat;
  15. background-position:center top;
  16. background-size: 64px;
  17. font-family:Arial,sans-serif;
  18. width:100%;
  19. margin:0;
  20. }
  21. </style>
  22. </head>
  23. <body>
  24. <script type="text/javascript">
  25. // Parses URL parameters
  26. function getUrlParam(param)
  27. {
  28. var result = (new RegExp(param + '=([^&]*)')).exec(window.location.search);
  29. if (result != null && result.length > 0)
  30. {
  31. return decodeURIComponent(result[1].replace(/\+/g, '%20'))
  32. }
  33. return null;
  34. };
  35. // Sets global environment variables
  36. RESOURCE_BASE = '/resources/dia';
  37. STENCIL_PATH = '/stencils';
  38. SHAPES_PATH = '/shapes';
  39. // Overrides browser language with Confluence user language
  40. var lang = getUrlParam('loc');
  41. var lightbox = getUrlParam('lightbox') == '1';
  42. if (lightbox)
  43. {
  44. document.body.style.backgroundImage = 'url(/images/drawlogo-text-bottom.svg)';
  45. document.body.style.backgroundPosition = 'center 30%';
  46. document.body.style.backgroundSize = '128px';
  47. }
  48. // Language is in the Connect URL
  49. if (lang != null)
  50. {
  51. var dash = lang.indexOf('-');
  52. if (dash >= 0)
  53. {
  54. mxLanguage = lang.substring(0, dash);
  55. }
  56. }
  57. </script>
  58. <script type="text/javascript" src="/js/viewer.min.js"></script>
  59. <script type="text/javascript">
  60. (function()
  61. {
  62. // Enables dynamic loading of shapes and stencils (same domain)
  63. mxStencilRegistry.dynamicLoading = true;
  64. // Loads the Atlassian API
  65. var script = document.createElement('script');
  66. var baseUrl = getUrlParam('xdm_e') + getUrlParam('cp');
  67. // Loads the attachment and renders the diagram
  68. var diagramWidth = parseFloat(getUrlParam('width'));
  69. var diagramHeight = parseFloat(getUrlParam('height'));
  70. var diagramName = getUrlParam('diagramName');
  71. //ceoId and owningPageId are IDs of the page that potentially hold the attachment
  72. //they will differ when page history is shown, ceoId will be historical version ID,
  73. //owningPageId will be the ID of the current version that holds the attachment
  74. //ceoId is used as fallback in case owningPageId is not set(should be very rare)
  75. var ceoId = getUrlParam('ceoId');
  76. var owningPageId = getUrlParam('owningPageId');
  77. var revision = null;
  78. var tbStyle = getUrlParam('tbstyle') || 'top';
  79. var links = getUrlParam('links') || 'auto';
  80. var enableLightbox = getUrlParam('lbox') != '0';
  81. var simpleViewer = false; // unused
  82. var tbHeight = (tbStyle == 'top' && !simpleViewer) ? GraphViewer.prototype.toolbarHeight : 0;
  83. var zoom = parseFloat(getUrlParam('zoom') || 1);
  84. var border = (simpleViewer) ? 0 : 8;
  85. if (!lightbox)
  86. {
  87. document.body.style.backgroundImage = 'url(/images/aui-wait.gif)';
  88. document.body.style.backgroundPosition = 'left top';
  89. document.body.style.backgroundSize = 'auto auto';
  90. }
  91. function main()
  92. {
  93. // Sets initial placeholder size to allow for scrollbars in fit to page width
  94. AP.resize('100%', (lightbox) ? '100%' : (diagramHeight * zoom + tbHeight + 2 * border));
  95. function showError(msg)
  96. {
  97. document.body.style.backgroundImage = 'none';
  98. document.body.style.padding = '4px';
  99. mxUtils.write(document.body, msg);
  100. AP.resize('100%', 24);
  101. };
  102. AP.require(['request', 'dialog', 'messages', 'navigator'], function(request, dialog, messages, navigator)
  103. {
  104. // Uses pageId from current page as page in macro may be outdated after export
  105. navigator.getLocation(function (data)
  106. {
  107. var candidateId = (owningPageId != null && owningPageId.length > 0) ? owningPageId : ceoId;
  108. if (data != null && data.target != null && data.context != null)
  109. {
  110. candidateId = data.context.contentId;
  111. }
  112. // Loads the given XML into the viewer
  113. function showDiagram(id, backupId, name, revision, page, links, retryParams)
  114. {
  115. id = id.toString();
  116. retryParams = retryParams || {}; //so we can use it without NPE check
  117. if (id != null && id.length > 0 && name != null && name.length > 0)
  118. {
  119. // Option currently not available in the UI
  120. if (simpleViewer)
  121. {
  122. document.body.style.backgroundImage = 'none';
  123. var img = document.createElement('img');
  124. img.style.cssText = 'max-width:100%;';
  125. img.setAttribute('src', baseUrl + '/download/attachments/' + id + '/'
  126. + encodeURIComponent(name) + ".png?api=v2"
  127. + (revision != null ? "&version=" + revision : ""));
  128. if (zoom != 1)
  129. {
  130. img.style.width = Math.round(diagramWidth * zoom) + 'px';
  131. }
  132. document.body.appendChild(img);
  133. }
  134. else
  135. {
  136. var serverName = document.referrer;
  137. var timeout = 25000;
  138. var index1 = serverName.indexOf('//');
  139. if (index1 > 0)
  140. {
  141. var index2 = serverName.indexOf('/', index1 + 2);
  142. if (index2 > index1)
  143. {
  144. serverName = serverName.substring(index1 + 2, index2);
  145. }
  146. }
  147. var acceptResponse = true;
  148. var timeoutThread = window.setTimeout(function()
  149. {
  150. acceptResponse = false;
  151. if (lightbox)
  152. {
  153. var message = messages.error('The connection has timed out', 'The server at ' +
  154. serverName + ' is taking too long to respond.');
  155. messages.onClose(message, function()
  156. {
  157. dialog.close();
  158. });
  159. }
  160. else
  161. {
  162. showError('The connection has timed out: The server at ' +
  163. serverName + ' is taking too long to respond.');
  164. }
  165. }, timeout);
  166. request(
  167. {
  168. url: '/download/attachments/' + id + '/' + encodeURIComponent(name) +
  169. ((revision != null && revision.length > 0) ? '?version=' + revision : ''),
  170. success: function(xml)
  171. {
  172. window.clearTimeout(timeoutThread);
  173. if (acceptResponse)
  174. {
  175. document.body.style.backgroundImage = 'none';
  176. if (lightbox)
  177. {
  178. var config = {highlight: '#3572b0', nav: true, lightbox: false};
  179. if (links != 'auto')
  180. {
  181. config.target = links;
  182. }
  183. var viewer = new GraphViewer(null, null, config);
  184. viewer.currentPage = parseInt(page || 0);
  185. viewer.lightboxChrome = false;
  186. viewer.xml = xml;
  187. // Enables layers via flag to avoid toolbar
  188. viewer.layersEnabled = true;
  189. var ui = viewer.showLocalLightbox();
  190. // Destroy lightbox with ui instance
  191. var destroy = ui.destroy;
  192. ui.destroy = function()
  193. {
  194. dialog.close();
  195. destroy.apply(this, arguments);
  196. };
  197. }
  198. else
  199. {
  200. // LATER: Fix horizontal alignment ignored with 100% width of iframe
  201. // LATER: Fix page scrolling on touch device if trigger event on diagram
  202. // LATER: Hide toolbar after second container click for iOS
  203. // LATER: Disable responsive resize while lightbox shows
  204. var container = document.createElement('div');
  205. container.style.cssText = 'position:absolute;box-sizing:border-box;' +
  206. 'max-width:100%;margin-bottom:' + tbHeight +'px;border:1px solid transparent;';
  207. document.body.appendChild(container);
  208. var doc = mxUtils.parseXml(xml);
  209. var config = {highlight: '#3572b0', 'toolbar-position': tbStyle,
  210. nav: true, border: 2, zoom: zoom};
  211. if (tbStyle == 'top')
  212. {
  213. config.title = name;
  214. }
  215. if (links != 'auto')
  216. {
  217. config.target = links;
  218. }
  219. if (!enableLightbox)
  220. {
  221. config.lightbox = false;
  222. }
  223. if (tbStyle != 'hidden')
  224. {
  225. config.toolbar = 'pages zoom layers';
  226. config.border = border;
  227. if (enableLightbox)
  228. {
  229. config.toolbar += ' lightbox';
  230. }
  231. }
  232. else
  233. {
  234. // Workaround for invalid width if no toolbar is present
  235. var updateContainerWidth = GraphViewer.prototype.updateContainerWidth;
  236. GraphViewer.prototype.updateContainerWidth = function(container, width)
  237. {
  238. width += 3;
  239. updateContainerWidth.apply(this, arguments);
  240. };
  241. }
  242. var viewer = new GraphViewer(container, doc.documentElement, config);
  243. // Handles resize of iframe after zoom
  244. var graphDoResizeContainer = viewer.graph.doResizeContainer;
  245. function updateHeight(height)
  246. {
  247. AP.resize('100%', (tbHeight == 0) ? Math.ceil(height) : container.offsetHeight + tbHeight);
  248. };
  249. viewer.graph.doResizeContainer = function(width, height)
  250. {
  251. graphDoResizeContainer.apply(this, arguments);
  252. updateHeight(height);
  253. };
  254. // Updates the size of the iframe in responsive cases
  255. viewer.updateContainerHeight = function(container, height)
  256. {
  257. updateHeight(height);
  258. };
  259. updateHeight();
  260. viewer.showLightbox = function()
  261. {
  262. dialog.create(
  263. {
  264. header: name,
  265. key: 'lightbox',
  266. size: 'fullscreen',
  267. customData: {id: id, name: name, revision: revision, page: viewer.currentPage, links: links},
  268. chrome: true
  269. });
  270. };
  271. }
  272. if (retryParams.saveIt)
  273. {
  274. //Since attachment wasn't found in this page, it is better to save it to this page
  275. //First load AC dynamically. Since AC is not needed in the viewer except for this case
  276. var head = document.getElementsByTagName('head')[0];
  277. var script = document.createElement('script');
  278. script.setAttribute('data-options', 'resize:false;margin:false');
  279. // Main
  280. script.onload = function()
  281. {
  282. //save diagram
  283. AC.saveDiagram(retryParams.pageId, name, btoa(unescape(encodeURIComponent(xml))),
  284. function()
  285. {
  286. //nothing!
  287. },
  288. function()
  289. {
  290. //nothing!
  291. }, false, 'text/plain', 'Diagram imported by Draw.io');
  292. //TODO save preview png
  293. //This requires an editor to do the png export, may be a canvas can be used with supported browsers
  294. };
  295. script.src = 'ac.js';
  296. head.appendChild(script);
  297. }
  298. }
  299. },
  300. error: function (err)
  301. {
  302. window.clearTimeout(timeoutThread);
  303. if (err.status == 404)
  304. {
  305. //Copied pages are reset to revision 1, in addition, copy&paste pages saves diagrams imported from another page
  306. //So, try revision 1 first
  307. if (revision > 1)
  308. {
  309. showDiagram(id, backupId, name, null, page, links, {revision: revision});
  310. }
  311. else if (backupId != null)
  312. {
  313. //Since attachment wasn't found in this page, it is better to save it to this page
  314. showDiagram(backupId, null, name, revision || retryParams.revision, page, links, {saveIt: true, pageId: id});
  315. }
  316. }
  317. else if (acceptResponse)
  318. {
  319. document.body.style.backgroundImage = 'none';
  320. showError('Error: ' + err.status);
  321. }
  322. }
  323. });
  324. }
  325. }
  326. else
  327. {
  328. showError('Error: Invalid descriptor');
  329. }
  330. };
  331. if (lightbox)
  332. {
  333. // Gets the paramters from the customData object in lightbox mode
  334. // LATER: Add XML to custom data (does not seem to work)
  335. showDiagram(dialog.customData.id, dialog.customData.id, dialog.customData.name, dialog.customData.revision, dialog.customData.page, dialog.customData.links);
  336. }
  337. else
  338. {
  339. var myPageId = (owningPageId != null && owningPageId.length > 0) ? owningPageId : ceoId;
  340. showDiagram(candidateId, (owningPageId != null && owningPageId.length > 0) ? owningPageId : ceoId, diagramName, revision, null, links);
  341. }
  342. });
  343. });
  344. };
  345. mxResources.loadDefaultBundle = false;
  346. var bundle = mxResources.getDefaultBundle(RESOURCE_BASE, mxLanguage) ||
  347. mxResources.getSpecialBundle(RESOURCE_BASE, mxLanguage);
  348. // Prefetches asynchronous requests so that below code runs synchronous
  349. // Loading the correct bundle (one file) via the fallback system in mxResources. The stylesheet
  350. // is compiled into JS in the build process and is only needed for local development.
  351. var bundleLoaded = false;
  352. var scriptLoaded = false;
  353. var validSize = document.documentElement.offsetWidth > 0;
  354. function mainBarrier()
  355. {
  356. if (validSize && bundleLoaded && scriptLoaded)
  357. {
  358. main();
  359. }
  360. };
  361. // Disables delayed rendering since the container is created on the fly
  362. GraphViewer.prototype.checkVisibleState = false;
  363. // Workaround for collapsed panel is to delay main until size is not 0
  364. if (!validSize)
  365. {
  366. var listener = function()
  367. {
  368. if (document.documentElement.offsetWidth > 0)
  369. {
  370. window.removeEventListener('resize', listener);
  371. validSize = true;
  372. mainBarrier();
  373. }
  374. };
  375. window.addEventListener('resize', listener);
  376. }
  377. mxUtils.getAll([bundle], function(xhr)
  378. {
  379. // Adds bundle text to resources
  380. mxResources.parse(xhr[0].getText());
  381. bundleLoaded = true;
  382. mainBarrier();
  383. });
  384. script.onload = function()
  385. {
  386. // Workaround for Google Chrome triggering
  387. // no resize event if height is set to 0
  388. if (mxClient.IS_GC && !validSize)
  389. {
  390. AP.resize('100%', 1);
  391. }
  392. scriptLoaded = true;
  393. mainBarrier();
  394. };
  395. script.src = baseUrl + '/atlassian-connect/all.js';
  396. script.setAttribute('data-options', 'sizeToParent:true;');
  397. document.getElementsByTagName('head')[0].appendChild(script);
  398. })();
  399. </script>
  400. </body>
  401. </html>