1/** 2 * FIXME should messages be sent to diagramsEditor.contentWindow instead of diagramsEditor? 3 */ 4class DiagramsEditor { 5 /** @type {HTMLIFrameElement} the editor iframe */ 6 diagramsEditor = null; 7 8 /** @type {CallableFunction} the method to call for saving the diagram */ 9 handleSave = null; 10 11 /** 12 * Initialize the editor for editing a media file 13 * 14 * @param {string} mediaid The media ID to edit, if 404 a new file will be created 15 */ 16 async editMediaFile(mediaid) { 17 this.#createEditor(); 18 19 this.handleSave = (svg) => this.#saveMediaFile(mediaid, svg); 20 21 const response = await fetch(DOKU_BASE + 'lib/exe/fetch.php?media=' + mediaid, { 22 method: 'GET', 23 cache: 'no-cache', 24 }); 25 26 let svg = ''; 27 if (response.ok) { 28 // if not 404, load the SVG data 29 svg = await response.text(); 30 } 31 32 this.#loadDocument(svg); 33 } 34 35 /** 36 * Initialize the editor for editing an embedded diagram 37 * 38 * @param {string} pageid The page ID to on which the diagram is embedded 39 * @param {int} index The index of the diagram on the page (0-based) 40 */ 41 async editEmbed(pageid, index) { 42 this.#createEditor(); 43 44 this.handleSave = (svg) => this.#saveEmbed(pageid, index, svg); 45 46 const response = await fetch(DOKU_BASE + 'lib/exe/fetch.php?media=' + mediaid, { 47 method: 'GET', 48 cache: 'no-cache', 49 }); 50 51 let svg = ''; 52 if (response.ok) { 53 // if not 404, load the SVG data 54 svg = await response.text(); 55 } else { 56 // a 404 for an embedded diagram should not happen 57 alert(LANG.plugins.diagrams.errorLoading); 58 this.#removeEditor(); 59 return; 60 } 61 62 this.#loadDocument(svg); 63 } 64 65 /** 66 * Initialize the editor for editing a diagram in memory 67 * 68 * @param {string} svg The SVG raw data to edit, empty for new file 69 * @param {CallableFunction} callback The callback to call when the editor is closed 70 */ 71 editMemory(svg, callback) { 72 this.#createEditor(); 73 this.handleSave = callback; 74 this.#loadDocument(svg); 75 } 76 77 #saveMediaFile(mediaid, svg) { 78 // FIXME save to media file 79 80 81 } 82 83 #saveEmbed(pageid, index, svg) { 84 // FIXME save to page 85 } 86 87 /** 88 * Create the editor iframe and attach the message listener 89 */ 90 #createEditor() { 91 this.diagramsEditor = document.createElement('iframe'); 92 this.diagramsEditor.id = 'diagrams-frame'; 93 this.diagramsEditor.style = { 94 border: 0, 95 position: 'fixed', 96 top: 0, 97 left: 0, 98 right: 0, 99 bottom: 0, 100 width: '100%', 101 height: '100%', 102 zIndex: 9999, 103 }; // FIXME assign these via CSS 104 this.diagramsEditor.src = JSINFO['plugins']['diagrams']['service_url']; 105 document.body.appendChild(this.diagramsEditor); 106 window.addEventListener('message', this.#handleMessage.bind(this)); // FIXME might need to be public 107 } 108 109 /** 110 * Remove the editor iframe and detach the message listener 111 */ 112 #removeEditor() { 113 this.diagramsEditor.remove(); 114 this.diagramsEditor = null; 115 window.removeDiagramsEditor(this.#handleMessage.bind(this)); 116 } 117 118 /** 119 * Load the given SVG document into the editor 120 * 121 * @param {string} svg 122 */ 123 #loadDocument(svg) { 124 this.diagramsEditor.postMessage(JSON.stringify({action: 'load', xml: svg}), '*'); 125 } 126 127 /** 128 * Handle messages from diagramming service 129 * 130 * @param {Event} event 131 */ 132 #handleMessage(event) { 133 // FIXME do we need this originalEvent here? or is that jQuery specific? 134 const msg = JSON.parse(event.originalEvent.data); 135 136 switch (msg.event) { 137 case 'init': 138 // FIXME do we need to wait for this? before we can call #loadDocument? 139 break; 140 case 'save': 141 // Save triggers an export to SVG action 142 this.diagramsEditor.postMessage( 143 JSON.stringify({ 144 action: 'export', 145 format: 'xmlsvg', 146 spin: LANG.plugins.diagrams.saving 147 }), 148 '*' 149 ); 150 break; 151 case 'export': 152 if (msg.format !== 'svg') { 153 alert(LANG.plugins.diagrams.errorUnsupportedFormat); 154 return; 155 } 156 this.handleSave( 157 // FIXME we used to prepend a doctype, but doctypes are no longer recommended for SVG 158 // FIXME we may need to add a XML header though? 159 decodeURIComponent(atob( 160 msg.data.split(',')[1]) 161 .split('') 162 .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)) 163 .join('') 164 ) 165 ); 166 break; 167 case 'exit': 168 this.#removeEditor(); 169 break; 170 } 171 } 172 173} 174