Fossil SCM
/fileedit now accommodates relative links in wiki-rendered previews by dynamically adjusting/restoring the base.href when the preview tab is selected/deselected, and the backend can now report to the client, via response header, which rendering mode was selected, allowing the UI to adjust from 'Guess' to the current format.
Commit
2bec2c758f5a1a1373bede86bb1ff8b1ea3424f2151a768e9cfa9cd9ccb9060b
Parent
2863abf5c52c22d…
2 files changed
+30
-15
+82
-13
+30
-15
| --- src/fileedit.c | ||
| +++ src/fileedit.c | ||
| @@ -935,21 +935,24 @@ | ||
| 935 | 935 | return rc; |
| 936 | 936 | } |
| 937 | 937 | |
| 938 | 938 | /* |
| 939 | 939 | ** Performs the PREVIEW mode for /filepage. |
| 940 | +** | |
| 941 | +** If *renderMode==FE_RENDER_GUESS then *renderMode gets set to the | |
| 942 | +** mode which is guessed at for the rendering. | |
| 940 | 943 | */ |
| 941 | 944 | static void fileedit_render_preview(Blob * pContent, |
| 942 | 945 | const char *zFilename, |
| 943 | - int flags, int renderMode, | |
| 946 | + int flags, int * renderMode, | |
| 944 | 947 | int nIframeHeightEm){ |
| 945 | 948 | const char * zMime; |
| 946 | 949 | zMime = mimetype_from_name(zFilename); |
| 947 | - if(FE_RENDER_GUESS==renderMode){ | |
| 948 | - renderMode = fileedit_render_mode_for_mimetype(zMime); | |
| 950 | + if(FE_RENDER_GUESS==*renderMode){ | |
| 951 | + *renderMode = fileedit_render_mode_for_mimetype(zMime); | |
| 949 | 952 | } |
| 950 | - switch(renderMode){ | |
| 953 | + switch(*renderMode){ | |
| 951 | 954 | case FE_RENDER_HTML_IFRAME:{ |
| 952 | 955 | char * z64 = encode64(blob_str(pContent), blob_size(pContent)); |
| 953 | 956 | CX("<iframe width='100%%' frameborder='0' " |
| 954 | 957 | "marginwidth='0' style='height:%dem' " |
| 955 | 958 | "marginheight='0' sandbox='allow-same-origin' " |
| @@ -1239,21 +1242,41 @@ | ||
| 1239 | 1242 | const char * zContent = P("content"); |
| 1240 | 1243 | int renderMode = atoi(PD("render_mode","0")); |
| 1241 | 1244 | int ln = atoi(PD("ln","0")); |
| 1242 | 1245 | int iframeHeight = atoi(PD("iframe_height","40")); |
| 1243 | 1246 | Blob content = empty_blob; |
| 1244 | - | |
| 1247 | + const char * zRenderMode = 0; | |
| 1245 | 1248 | fileedit_get_fnci_args( &zFilename, 0 ); |
| 1246 | 1249 | if(!fileedit_ajax_boostrap() |
| 1247 | 1250 | || !fileedit_ajax_check_filename(zFilename)){ |
| 1248 | 1251 | return; |
| 1249 | 1252 | } |
| 1250 | 1253 | cgi_set_content_type("text/html"); |
| 1251 | 1254 | blob_init(&content, zContent, -1); |
| 1252 | 1255 | fileedit_render_preview(&content, zFilename, |
| 1253 | 1256 | ln ? FE_PREVIEW_LINE_NUMBERS : 0, |
| 1254 | - renderMode, iframeHeight); | |
| 1257 | + &renderMode, iframeHeight); | |
| 1258 | + /* | |
| 1259 | + ** Now tell the caller if we did indeed use FE_RENDER_WIKI, so that | |
| 1260 | + ** they can re-set the <base href> to an appropriate value (which | |
| 1261 | + ** requires knowing the content's current checkin version, which we | |
| 1262 | + ** don't have here). | |
| 1263 | + */ | |
| 1264 | + switch(renderMode){ | |
| 1265 | + /* The strings used here MUST correspond to those used in the JS-side | |
| 1266 | + ** fossil.page.previewModes map. | |
| 1267 | + */ | |
| 1268 | + case FE_RENDER_WIKI: zRenderMode = "wiki"; break; | |
| 1269 | + case FE_RENDER_HTML_INLINE: zRenderMode = "htmlInline"; break; | |
| 1270 | + case FE_RENDER_HTML_IFRAME: zRenderMode = "htmlIframe"; break; | |
| 1271 | + case FE_RENDER_PLAIN_TEXT: zRenderMode = "text"; break; | |
| 1272 | + case FE_RENDER_GUESS: | |
| 1273 | + assert(!"cannot happen"); | |
| 1274 | + } | |
| 1275 | + if(zRenderMode!=0){ | |
| 1276 | + cgi_printf_header("X-fileedit-render-mode: %s\r\n", zRenderMode); | |
| 1277 | + } | |
| 1255 | 1278 | } |
| 1256 | 1279 | |
| 1257 | 1280 | /* |
| 1258 | 1281 | ** AJAX route /fileedit?ajax=diff |
| 1259 | 1282 | ** |
| @@ -1786,13 +1809,11 @@ | ||
| 1786 | 1809 | { |
| 1787 | 1810 | CX("<div id='fileedit-tab-preview' " |
| 1788 | 1811 | "data-tab-parent='fileedit-tabs' " |
| 1789 | 1812 | "data-tab-label='Preview'" |
| 1790 | 1813 | ">"); |
| 1791 | - | |
| 1792 | - CX("<div class='fileedit-options flex-container flex-column'>"); | |
| 1793 | - CX("<div class='flex-container flex-row'>"); | |
| 1814 | + CX("<div class='fileedit-options flex-container flex-row'>"); | |
| 1794 | 1815 | CX("<button id='btn-preview-refresh' " |
| 1795 | 1816 | "data-f-preview-from='fileedit-content-editor' " |
| 1796 | 1817 | /* ^^^ text source elem ID*/ |
| 1797 | 1818 | "data-f-preview-via='_postPreview' " |
| 1798 | 1819 | /* ^^^ fossil.page[methodName](content, callback) */ |
| @@ -1853,16 +1874,10 @@ | ||
| 1853 | 1874 | "preview_ln", |
| 1854 | 1875 | "Add line numbers to plain-text previews?", |
| 1855 | 1876 | "1", P("preview_ln")!=0, |
| 1856 | 1877 | "If on, plain-text files (only) will get " |
| 1857 | 1878 | "line numbers added to the preview."); |
| 1858 | - CX("</div>"/*.flex-container.flex-row (buttons/options)*/); | |
| 1859 | - CX("<div class='fileedit-hint'>" | |
| 1860 | - "Note that hyperlinks in previewed HTML are relative to " | |
| 1861 | - "<em>this</em> page, and therefore not correct. Clicking " | |
| 1862 | - "them will leave this page, losing any edits." | |
| 1863 | - "</div>"); | |
| 1864 | 1879 | CX("</div>"/*.fileedit-options*/); |
| 1865 | 1880 | CX("<div id='fileedit-tab-preview-wrapper'></div>"); |
| 1866 | 1881 | CX("</div>"/*#fileedit-tab-preview*/); |
| 1867 | 1882 | } |
| 1868 | 1883 | |
| 1869 | 1884 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -935,21 +935,24 @@ | |
| 935 | return rc; |
| 936 | } |
| 937 | |
| 938 | /* |
| 939 | ** Performs the PREVIEW mode for /filepage. |
| 940 | */ |
| 941 | static void fileedit_render_preview(Blob * pContent, |
| 942 | const char *zFilename, |
| 943 | int flags, int renderMode, |
| 944 | int nIframeHeightEm){ |
| 945 | const char * zMime; |
| 946 | zMime = mimetype_from_name(zFilename); |
| 947 | if(FE_RENDER_GUESS==renderMode){ |
| 948 | renderMode = fileedit_render_mode_for_mimetype(zMime); |
| 949 | } |
| 950 | switch(renderMode){ |
| 951 | case FE_RENDER_HTML_IFRAME:{ |
| 952 | char * z64 = encode64(blob_str(pContent), blob_size(pContent)); |
| 953 | CX("<iframe width='100%%' frameborder='0' " |
| 954 | "marginwidth='0' style='height:%dem' " |
| 955 | "marginheight='0' sandbox='allow-same-origin' " |
| @@ -1239,21 +1242,41 @@ | |
| 1239 | const char * zContent = P("content"); |
| 1240 | int renderMode = atoi(PD("render_mode","0")); |
| 1241 | int ln = atoi(PD("ln","0")); |
| 1242 | int iframeHeight = atoi(PD("iframe_height","40")); |
| 1243 | Blob content = empty_blob; |
| 1244 | |
| 1245 | fileedit_get_fnci_args( &zFilename, 0 ); |
| 1246 | if(!fileedit_ajax_boostrap() |
| 1247 | || !fileedit_ajax_check_filename(zFilename)){ |
| 1248 | return; |
| 1249 | } |
| 1250 | cgi_set_content_type("text/html"); |
| 1251 | blob_init(&content, zContent, -1); |
| 1252 | fileedit_render_preview(&content, zFilename, |
| 1253 | ln ? FE_PREVIEW_LINE_NUMBERS : 0, |
| 1254 | renderMode, iframeHeight); |
| 1255 | } |
| 1256 | |
| 1257 | /* |
| 1258 | ** AJAX route /fileedit?ajax=diff |
| 1259 | ** |
| @@ -1786,13 +1809,11 @@ | |
| 1786 | { |
| 1787 | CX("<div id='fileedit-tab-preview' " |
| 1788 | "data-tab-parent='fileedit-tabs' " |
| 1789 | "data-tab-label='Preview'" |
| 1790 | ">"); |
| 1791 | |
| 1792 | CX("<div class='fileedit-options flex-container flex-column'>"); |
| 1793 | CX("<div class='flex-container flex-row'>"); |
| 1794 | CX("<button id='btn-preview-refresh' " |
| 1795 | "data-f-preview-from='fileedit-content-editor' " |
| 1796 | /* ^^^ text source elem ID*/ |
| 1797 | "data-f-preview-via='_postPreview' " |
| 1798 | /* ^^^ fossil.page[methodName](content, callback) */ |
| @@ -1853,16 +1874,10 @@ | |
| 1853 | "preview_ln", |
| 1854 | "Add line numbers to plain-text previews?", |
| 1855 | "1", P("preview_ln")!=0, |
| 1856 | "If on, plain-text files (only) will get " |
| 1857 | "line numbers added to the preview."); |
| 1858 | CX("</div>"/*.flex-container.flex-row (buttons/options)*/); |
| 1859 | CX("<div class='fileedit-hint'>" |
| 1860 | "Note that hyperlinks in previewed HTML are relative to " |
| 1861 | "<em>this</em> page, and therefore not correct. Clicking " |
| 1862 | "them will leave this page, losing any edits." |
| 1863 | "</div>"); |
| 1864 | CX("</div>"/*.fileedit-options*/); |
| 1865 | CX("<div id='fileedit-tab-preview-wrapper'></div>"); |
| 1866 | CX("</div>"/*#fileedit-tab-preview*/); |
| 1867 | } |
| 1868 | |
| 1869 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -935,21 +935,24 @@ | |
| 935 | return rc; |
| 936 | } |
| 937 | |
| 938 | /* |
| 939 | ** Performs the PREVIEW mode for /filepage. |
| 940 | ** |
| 941 | ** If *renderMode==FE_RENDER_GUESS then *renderMode gets set to the |
| 942 | ** mode which is guessed at for the rendering. |
| 943 | */ |
| 944 | static void fileedit_render_preview(Blob * pContent, |
| 945 | const char *zFilename, |
| 946 | int flags, int * renderMode, |
| 947 | int nIframeHeightEm){ |
| 948 | const char * zMime; |
| 949 | zMime = mimetype_from_name(zFilename); |
| 950 | if(FE_RENDER_GUESS==*renderMode){ |
| 951 | *renderMode = fileedit_render_mode_for_mimetype(zMime); |
| 952 | } |
| 953 | switch(*renderMode){ |
| 954 | case FE_RENDER_HTML_IFRAME:{ |
| 955 | char * z64 = encode64(blob_str(pContent), blob_size(pContent)); |
| 956 | CX("<iframe width='100%%' frameborder='0' " |
| 957 | "marginwidth='0' style='height:%dem' " |
| 958 | "marginheight='0' sandbox='allow-same-origin' " |
| @@ -1239,21 +1242,41 @@ | |
| 1242 | const char * zContent = P("content"); |
| 1243 | int renderMode = atoi(PD("render_mode","0")); |
| 1244 | int ln = atoi(PD("ln","0")); |
| 1245 | int iframeHeight = atoi(PD("iframe_height","40")); |
| 1246 | Blob content = empty_blob; |
| 1247 | const char * zRenderMode = 0; |
| 1248 | fileedit_get_fnci_args( &zFilename, 0 ); |
| 1249 | if(!fileedit_ajax_boostrap() |
| 1250 | || !fileedit_ajax_check_filename(zFilename)){ |
| 1251 | return; |
| 1252 | } |
| 1253 | cgi_set_content_type("text/html"); |
| 1254 | blob_init(&content, zContent, -1); |
| 1255 | fileedit_render_preview(&content, zFilename, |
| 1256 | ln ? FE_PREVIEW_LINE_NUMBERS : 0, |
| 1257 | &renderMode, iframeHeight); |
| 1258 | /* |
| 1259 | ** Now tell the caller if we did indeed use FE_RENDER_WIKI, so that |
| 1260 | ** they can re-set the <base href> to an appropriate value (which |
| 1261 | ** requires knowing the content's current checkin version, which we |
| 1262 | ** don't have here). |
| 1263 | */ |
| 1264 | switch(renderMode){ |
| 1265 | /* The strings used here MUST correspond to those used in the JS-side |
| 1266 | ** fossil.page.previewModes map. |
| 1267 | */ |
| 1268 | case FE_RENDER_WIKI: zRenderMode = "wiki"; break; |
| 1269 | case FE_RENDER_HTML_INLINE: zRenderMode = "htmlInline"; break; |
| 1270 | case FE_RENDER_HTML_IFRAME: zRenderMode = "htmlIframe"; break; |
| 1271 | case FE_RENDER_PLAIN_TEXT: zRenderMode = "text"; break; |
| 1272 | case FE_RENDER_GUESS: |
| 1273 | assert(!"cannot happen"); |
| 1274 | } |
| 1275 | if(zRenderMode!=0){ |
| 1276 | cgi_printf_header("X-fileedit-render-mode: %s\r\n", zRenderMode); |
| 1277 | } |
| 1278 | } |
| 1279 | |
| 1280 | /* |
| 1281 | ** AJAX route /fileedit?ajax=diff |
| 1282 | ** |
| @@ -1786,13 +1809,11 @@ | |
| 1809 | { |
| 1810 | CX("<div id='fileedit-tab-preview' " |
| 1811 | "data-tab-parent='fileedit-tabs' " |
| 1812 | "data-tab-label='Preview'" |
| 1813 | ">"); |
| 1814 | CX("<div class='fileedit-options flex-container flex-row'>"); |
| 1815 | CX("<button id='btn-preview-refresh' " |
| 1816 | "data-f-preview-from='fileedit-content-editor' " |
| 1817 | /* ^^^ text source elem ID*/ |
| 1818 | "data-f-preview-via='_postPreview' " |
| 1819 | /* ^^^ fossil.page[methodName](content, callback) */ |
| @@ -1853,16 +1874,10 @@ | |
| 1874 | "preview_ln", |
| 1875 | "Add line numbers to plain-text previews?", |
| 1876 | "1", P("preview_ln")!=0, |
| 1877 | "If on, plain-text files (only) will get " |
| 1878 | "line numbers added to the preview."); |
| 1879 | CX("</div>"/*.fileedit-options*/); |
| 1880 | CX("<div id='fileedit-tab-preview-wrapper'></div>"); |
| 1881 | CX("</div>"/*#fileedit-tab-preview*/); |
| 1882 | } |
| 1883 | |
| 1884 |
+82
-13
| --- src/fossil.page.fileedit.js | ||
| +++ src/fossil.page.fileedit.js | ||
| @@ -150,23 +150,43 @@ | ||
| 150 | 150 | btnReload.addEventListener( |
| 151 | 151 | 'click', (e)=>this.loadLeaves(), false |
| 152 | 152 | ); |
| 153 | 153 | delete this.init; |
| 154 | 154 | } |
| 155 | + }/*P.fileSelector*/; | |
| 156 | + | |
| 157 | + /** | |
| 158 | + Internal workaround to select the current preview mode | |
| 159 | + and fire a change event if the value actually changes | |
| 160 | + or if forceEvent is truthy. | |
| 161 | + */ | |
| 162 | + P.selectPreviewMode = function(modeValue, forceEvent){ | |
| 163 | + const s = this.e.selectPreviewMode; | |
| 164 | + if(!modeValue) modeValue = s.value; | |
| 165 | + else if(s.value != modeValue){ | |
| 166 | + s.value = modeValue; | |
| 167 | + forceEvent = true; | |
| 168 | + } | |
| 169 | + if(forceEvent){ | |
| 170 | + // Force UI update | |
| 171 | + s.dispatchEvent(new Event('change',{target:s})); | |
| 172 | + } | |
| 155 | 173 | }; |
| 156 | 174 | |
| 157 | 175 | window.addEventListener("load", function() { |
| 176 | + P.base = {tag: E('base')}; | |
| 177 | + P.base.originalHref = P.base.tag.href; | |
| 158 | 178 | P.tabs = new fossil.TabManager('#fileedit-tabs'); |
| 159 | 179 | P.e = { |
| 160 | 180 | taEditor: E('#fileedit-content-editor'), |
| 161 | 181 | taCommentSmall: E('#fileedit-comment'), |
| 162 | 182 | taCommentBig: E('#fileedit-comment-big'), |
| 163 | 183 | ajaxContentTarget: E('#ajax-target'), |
| 164 | 184 | btnCommit: E("#fileedit-btn-commit"), |
| 165 | 185 | btnReload: E("#fileedit-tab-content > .fileedit-options > " |
| 166 | 186 | +"button.fileedit-content-reload"), |
| 167 | - selectPreviewModeWrap: E('#select-preview-mode'), | |
| 187 | + selectPreviewMode: E('#select-preview-mode select'), | |
| 168 | 188 | selectHtmlEmsWrap: E('#select-preview-html-ems'), |
| 169 | 189 | selectEolWrap: E('#select-preview-html-ems'), |
| 170 | 190 | cbLineNumbersWrap: E('#cb-line-numbers'), |
| 171 | 191 | cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'), |
| 172 | 192 | tabs:{ |
| @@ -199,13 +219,21 @@ | ||
| 199 | 219 | ); |
| 200 | 220 | |
| 201 | 221 | P.tabs.addEventListener( |
| 202 | 222 | /* Set up auto-refresh of the preview tab... */ |
| 203 | 223 | 'before-switch-to', function(ev){ |
| 204 | - if(ev.detail===P.e.tabs.preview | |
| 205 | - && P.e.cbAutoPreview.checked){ | |
| 206 | - P.preview(); | |
| 224 | + if(ev.detail===P.e.tabs.preview){ | |
| 225 | + P.baseHrefForFile(); | |
| 226 | + if(P.e.cbAutoPreview.checked) P.preview(); | |
| 227 | + } | |
| 228 | + } | |
| 229 | + ); | |
| 230 | + P.tabs.addEventListener( | |
| 231 | + /* Set up auto-refresh of the preview tab... */ | |
| 232 | + 'before-switch-from', function(ev){ | |
| 233 | + if(ev.detail===P.e.tabs.preview){ | |
| 234 | + P.baseHrefRestore(); | |
| 207 | 235 | } |
| 208 | 236 | } |
| 209 | 237 | ); |
| 210 | 238 | |
| 211 | 239 | F.connectPagePreviewers( |
| @@ -236,17 +264,16 @@ | ||
| 236 | 264 | /** |
| 237 | 265 | Cosmetic: jump through some hoops to enable/disable |
| 238 | 266 | certain preview options depending on the current |
| 239 | 267 | preview mode... |
| 240 | 268 | */ |
| 241 | - const selectPreviewMode = | |
| 242 | - P.e.selectPreviewModeWrap.querySelector('select'); | |
| 243 | - selectPreviewMode.addEventListener( | |
| 269 | + P.e.selectPreviewMode.addEventListener( | |
| 244 | 270 | "change", function(e){ |
| 245 | 271 | const mode = e.target.value, |
| 246 | 272 | name = P.previewModes[mode], |
| 247 | 273 | hide = [], unhide = []; |
| 274 | + P.previewModes.current = name; | |
| 248 | 275 | if('guess'===name){ |
| 249 | 276 | unhide.push(P.e.cbLineNumbersWrap, |
| 250 | 277 | P.e.selectHtmlEmsWrap); |
| 251 | 278 | }else{ |
| 252 | 279 | if('text'===name) unhide.push(P.e.cbLineNumbersWrap); |
| @@ -256,14 +283,11 @@ | ||
| 256 | 283 | } |
| 257 | 284 | hide.forEach((e)=>e.classList.add('hidden')); |
| 258 | 285 | unhide.forEach((e)=>e.classList.remove('hidden')); |
| 259 | 286 | }, false |
| 260 | 287 | ); |
| 261 | - selectPreviewMode.dispatchEvent( | |
| 262 | - // Force UI update | |
| 263 | - new Event('change',{target:selectPreviewMode}) | |
| 264 | - ); | |
| 288 | + P.selectPreviewMode(false, true); | |
| 265 | 289 | const selectFontSize = E('select[name=editor_font_size]'); |
| 266 | 290 | if(selectFontSize){ |
| 267 | 291 | selectFontSize.addEventListener( |
| 268 | 292 | "change",function(e){ |
| 269 | 293 | const ed = P.e.taEditor; |
| @@ -294,10 +318,51 @@ | ||
| 294 | 318 | }else{ |
| 295 | 319 | this.e.taEditor.value = arguments[0]; |
| 296 | 320 | return this; |
| 297 | 321 | } |
| 298 | 322 | }; |
| 323 | + | |
| 324 | + /** | |
| 325 | + If either of... | |
| 326 | + | |
| 327 | + - P.previewModes.current==='wiki' | |
| 328 | + | |
| 329 | + - P.previewModes.current==='guess' AND the currently-loaded | |
| 330 | + file has an extension of (md|wiki) | |
| 331 | + | |
| 332 | + ... then this function updates the document's base.href to a | |
| 333 | + repo-relative /doc/{{this.finfo.checkin}}/{{directory part of | |
| 334 | + this.finfo.filename}}/ | |
| 335 | + | |
| 336 | + If neither of those conditions applies, this is a no-op. | |
| 337 | + */ | |
| 338 | + P.baseHrefForFile = function f(){ | |
| 339 | + const fn = this.finfo ? this.finfo.filename : undefined; | |
| 340 | + if(!fn) return this; | |
| 341 | + if(!f.rxWiki){ | |
| 342 | + f.rxWiki = /\.(wiki|md)$/i; | |
| 343 | + } | |
| 344 | + if('wiki'===P.previewModes.current | |
| 345 | + || ('guess'===P.previewModes.current | |
| 346 | + && f.rxWiki.test(fn))){ | |
| 347 | + const a = fn.split('/'); | |
| 348 | + a.pop(); | |
| 349 | + this.base.tag.href = F.repoUrl( | |
| 350 | + 'doc/'+F.hashDigits(this.finfo.checkin) | |
| 351 | + +'/'+(a.length ? a.join('/')+'/' : '') | |
| 352 | + ); | |
| 353 | + } | |
| 354 | + return this; | |
| 355 | + }; | |
| 356 | + | |
| 357 | + /** | |
| 358 | + Sets the document's base.href value to its page-load-time | |
| 359 | + setting. | |
| 360 | + */ | |
| 361 | + P.baseHrefRestore = function(){ | |
| 362 | + P.base.tag.href = P.base.originalHref; | |
| 363 | + }; | |
| 299 | 364 | |
| 300 | 365 | /** |
| 301 | 366 | Toggles between single- and multi-line comment |
| 302 | 367 | mode. |
| 303 | 368 | */ |
| @@ -433,21 +498,25 @@ | ||
| 433 | 498 | if(!content){ |
| 434 | 499 | callback(content); |
| 435 | 500 | return this; |
| 436 | 501 | } |
| 437 | 502 | const fd = new FormData(); |
| 438 | - fd.append('render_mode',E('select[name=preview_render_mode]').value); | |
| 503 | + fd.append('render_mode',this.e.selectPreviewMode.value); | |
| 439 | 504 | fd.append('filename',this.finfo.filename); |
| 440 | 505 | fd.append('ln',E('[name=preview_ln]').checked ? 1 : 0); |
| 441 | 506 | fd.append('iframe_height', E('[name=preview_html_ems]').value); |
| 442 | 507 | fd.append('content',content || ''); |
| 443 | 508 | F.message( |
| 444 | 509 | "Fetching preview..." |
| 445 | 510 | ).fetch('fileedit',{ |
| 446 | 511 | urlParams: {ajax: 'preview'}, |
| 447 | 512 | payload: fd, |
| 448 | - onload: (r)=>{ | |
| 513 | + responseHeaders: 'x-fileedit-render-mode', | |
| 514 | + onload: (r,header)=>{ | |
| 515 | + P.selectPreviewMode(P.previewModes[header]); | |
| 516 | + if('wiki'===header) P.baseHrefForFile(); | |
| 517 | + else P.baseHrefRestore(); | |
| 449 | 518 | callback(r); |
| 450 | 519 | F.message('Updated preview.'); |
| 451 | 520 | }, |
| 452 | 521 | onerror: (e)=>{ |
| 453 | 522 | fossil.fetch.onerror(e); |
| 454 | 523 |
| --- src/fossil.page.fileedit.js | |
| +++ src/fossil.page.fileedit.js | |
| @@ -150,23 +150,43 @@ | |
| 150 | btnReload.addEventListener( |
| 151 | 'click', (e)=>this.loadLeaves(), false |
| 152 | ); |
| 153 | delete this.init; |
| 154 | } |
| 155 | }; |
| 156 | |
| 157 | window.addEventListener("load", function() { |
| 158 | P.tabs = new fossil.TabManager('#fileedit-tabs'); |
| 159 | P.e = { |
| 160 | taEditor: E('#fileedit-content-editor'), |
| 161 | taCommentSmall: E('#fileedit-comment'), |
| 162 | taCommentBig: E('#fileedit-comment-big'), |
| 163 | ajaxContentTarget: E('#ajax-target'), |
| 164 | btnCommit: E("#fileedit-btn-commit"), |
| 165 | btnReload: E("#fileedit-tab-content > .fileedit-options > " |
| 166 | +"button.fileedit-content-reload"), |
| 167 | selectPreviewModeWrap: E('#select-preview-mode'), |
| 168 | selectHtmlEmsWrap: E('#select-preview-html-ems'), |
| 169 | selectEolWrap: E('#select-preview-html-ems'), |
| 170 | cbLineNumbersWrap: E('#cb-line-numbers'), |
| 171 | cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'), |
| 172 | tabs:{ |
| @@ -199,13 +219,21 @@ | |
| 199 | ); |
| 200 | |
| 201 | P.tabs.addEventListener( |
| 202 | /* Set up auto-refresh of the preview tab... */ |
| 203 | 'before-switch-to', function(ev){ |
| 204 | if(ev.detail===P.e.tabs.preview |
| 205 | && P.e.cbAutoPreview.checked){ |
| 206 | P.preview(); |
| 207 | } |
| 208 | } |
| 209 | ); |
| 210 | |
| 211 | F.connectPagePreviewers( |
| @@ -236,17 +264,16 @@ | |
| 236 | /** |
| 237 | Cosmetic: jump through some hoops to enable/disable |
| 238 | certain preview options depending on the current |
| 239 | preview mode... |
| 240 | */ |
| 241 | const selectPreviewMode = |
| 242 | P.e.selectPreviewModeWrap.querySelector('select'); |
| 243 | selectPreviewMode.addEventListener( |
| 244 | "change", function(e){ |
| 245 | const mode = e.target.value, |
| 246 | name = P.previewModes[mode], |
| 247 | hide = [], unhide = []; |
| 248 | if('guess'===name){ |
| 249 | unhide.push(P.e.cbLineNumbersWrap, |
| 250 | P.e.selectHtmlEmsWrap); |
| 251 | }else{ |
| 252 | if('text'===name) unhide.push(P.e.cbLineNumbersWrap); |
| @@ -256,14 +283,11 @@ | |
| 256 | } |
| 257 | hide.forEach((e)=>e.classList.add('hidden')); |
| 258 | unhide.forEach((e)=>e.classList.remove('hidden')); |
| 259 | }, false |
| 260 | ); |
| 261 | selectPreviewMode.dispatchEvent( |
| 262 | // Force UI update |
| 263 | new Event('change',{target:selectPreviewMode}) |
| 264 | ); |
| 265 | const selectFontSize = E('select[name=editor_font_size]'); |
| 266 | if(selectFontSize){ |
| 267 | selectFontSize.addEventListener( |
| 268 | "change",function(e){ |
| 269 | const ed = P.e.taEditor; |
| @@ -294,10 +318,51 @@ | |
| 294 | }else{ |
| 295 | this.e.taEditor.value = arguments[0]; |
| 296 | return this; |
| 297 | } |
| 298 | }; |
| 299 | |
| 300 | /** |
| 301 | Toggles between single- and multi-line comment |
| 302 | mode. |
| 303 | */ |
| @@ -433,21 +498,25 @@ | |
| 433 | if(!content){ |
| 434 | callback(content); |
| 435 | return this; |
| 436 | } |
| 437 | const fd = new FormData(); |
| 438 | fd.append('render_mode',E('select[name=preview_render_mode]').value); |
| 439 | fd.append('filename',this.finfo.filename); |
| 440 | fd.append('ln',E('[name=preview_ln]').checked ? 1 : 0); |
| 441 | fd.append('iframe_height', E('[name=preview_html_ems]').value); |
| 442 | fd.append('content',content || ''); |
| 443 | F.message( |
| 444 | "Fetching preview..." |
| 445 | ).fetch('fileedit',{ |
| 446 | urlParams: {ajax: 'preview'}, |
| 447 | payload: fd, |
| 448 | onload: (r)=>{ |
| 449 | callback(r); |
| 450 | F.message('Updated preview.'); |
| 451 | }, |
| 452 | onerror: (e)=>{ |
| 453 | fossil.fetch.onerror(e); |
| 454 |
| --- src/fossil.page.fileedit.js | |
| +++ src/fossil.page.fileedit.js | |
| @@ -150,23 +150,43 @@ | |
| 150 | btnReload.addEventListener( |
| 151 | 'click', (e)=>this.loadLeaves(), false |
| 152 | ); |
| 153 | delete this.init; |
| 154 | } |
| 155 | }/*P.fileSelector*/; |
| 156 | |
| 157 | /** |
| 158 | Internal workaround to select the current preview mode |
| 159 | and fire a change event if the value actually changes |
| 160 | or if forceEvent is truthy. |
| 161 | */ |
| 162 | P.selectPreviewMode = function(modeValue, forceEvent){ |
| 163 | const s = this.e.selectPreviewMode; |
| 164 | if(!modeValue) modeValue = s.value; |
| 165 | else if(s.value != modeValue){ |
| 166 | s.value = modeValue; |
| 167 | forceEvent = true; |
| 168 | } |
| 169 | if(forceEvent){ |
| 170 | // Force UI update |
| 171 | s.dispatchEvent(new Event('change',{target:s})); |
| 172 | } |
| 173 | }; |
| 174 | |
| 175 | window.addEventListener("load", function() { |
| 176 | P.base = {tag: E('base')}; |
| 177 | P.base.originalHref = P.base.tag.href; |
| 178 | P.tabs = new fossil.TabManager('#fileedit-tabs'); |
| 179 | P.e = { |
| 180 | taEditor: E('#fileedit-content-editor'), |
| 181 | taCommentSmall: E('#fileedit-comment'), |
| 182 | taCommentBig: E('#fileedit-comment-big'), |
| 183 | ajaxContentTarget: E('#ajax-target'), |
| 184 | btnCommit: E("#fileedit-btn-commit"), |
| 185 | btnReload: E("#fileedit-tab-content > .fileedit-options > " |
| 186 | +"button.fileedit-content-reload"), |
| 187 | selectPreviewMode: E('#select-preview-mode select'), |
| 188 | selectHtmlEmsWrap: E('#select-preview-html-ems'), |
| 189 | selectEolWrap: E('#select-preview-html-ems'), |
| 190 | cbLineNumbersWrap: E('#cb-line-numbers'), |
| 191 | cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'), |
| 192 | tabs:{ |
| @@ -199,13 +219,21 @@ | |
| 219 | ); |
| 220 | |
| 221 | P.tabs.addEventListener( |
| 222 | /* Set up auto-refresh of the preview tab... */ |
| 223 | 'before-switch-to', function(ev){ |
| 224 | if(ev.detail===P.e.tabs.preview){ |
| 225 | P.baseHrefForFile(); |
| 226 | if(P.e.cbAutoPreview.checked) P.preview(); |
| 227 | } |
| 228 | } |
| 229 | ); |
| 230 | P.tabs.addEventListener( |
| 231 | /* Set up auto-refresh of the preview tab... */ |
| 232 | 'before-switch-from', function(ev){ |
| 233 | if(ev.detail===P.e.tabs.preview){ |
| 234 | P.baseHrefRestore(); |
| 235 | } |
| 236 | } |
| 237 | ); |
| 238 | |
| 239 | F.connectPagePreviewers( |
| @@ -236,17 +264,16 @@ | |
| 264 | /** |
| 265 | Cosmetic: jump through some hoops to enable/disable |
| 266 | certain preview options depending on the current |
| 267 | preview mode... |
| 268 | */ |
| 269 | P.e.selectPreviewMode.addEventListener( |
| 270 | "change", function(e){ |
| 271 | const mode = e.target.value, |
| 272 | name = P.previewModes[mode], |
| 273 | hide = [], unhide = []; |
| 274 | P.previewModes.current = name; |
| 275 | if('guess'===name){ |
| 276 | unhide.push(P.e.cbLineNumbersWrap, |
| 277 | P.e.selectHtmlEmsWrap); |
| 278 | }else{ |
| 279 | if('text'===name) unhide.push(P.e.cbLineNumbersWrap); |
| @@ -256,14 +283,11 @@ | |
| 283 | } |
| 284 | hide.forEach((e)=>e.classList.add('hidden')); |
| 285 | unhide.forEach((e)=>e.classList.remove('hidden')); |
| 286 | }, false |
| 287 | ); |
| 288 | P.selectPreviewMode(false, true); |
| 289 | const selectFontSize = E('select[name=editor_font_size]'); |
| 290 | if(selectFontSize){ |
| 291 | selectFontSize.addEventListener( |
| 292 | "change",function(e){ |
| 293 | const ed = P.e.taEditor; |
| @@ -294,10 +318,51 @@ | |
| 318 | }else{ |
| 319 | this.e.taEditor.value = arguments[0]; |
| 320 | return this; |
| 321 | } |
| 322 | }; |
| 323 | |
| 324 | /** |
| 325 | If either of... |
| 326 | |
| 327 | - P.previewModes.current==='wiki' |
| 328 | |
| 329 | - P.previewModes.current==='guess' AND the currently-loaded |
| 330 | file has an extension of (md|wiki) |
| 331 | |
| 332 | ... then this function updates the document's base.href to a |
| 333 | repo-relative /doc/{{this.finfo.checkin}}/{{directory part of |
| 334 | this.finfo.filename}}/ |
| 335 | |
| 336 | If neither of those conditions applies, this is a no-op. |
| 337 | */ |
| 338 | P.baseHrefForFile = function f(){ |
| 339 | const fn = this.finfo ? this.finfo.filename : undefined; |
| 340 | if(!fn) return this; |
| 341 | if(!f.rxWiki){ |
| 342 | f.rxWiki = /\.(wiki|md)$/i; |
| 343 | } |
| 344 | if('wiki'===P.previewModes.current |
| 345 | || ('guess'===P.previewModes.current |
| 346 | && f.rxWiki.test(fn))){ |
| 347 | const a = fn.split('/'); |
| 348 | a.pop(); |
| 349 | this.base.tag.href = F.repoUrl( |
| 350 | 'doc/'+F.hashDigits(this.finfo.checkin) |
| 351 | +'/'+(a.length ? a.join('/')+'/' : '') |
| 352 | ); |
| 353 | } |
| 354 | return this; |
| 355 | }; |
| 356 | |
| 357 | /** |
| 358 | Sets the document's base.href value to its page-load-time |
| 359 | setting. |
| 360 | */ |
| 361 | P.baseHrefRestore = function(){ |
| 362 | P.base.tag.href = P.base.originalHref; |
| 363 | }; |
| 364 | |
| 365 | /** |
| 366 | Toggles between single- and multi-line comment |
| 367 | mode. |
| 368 | */ |
| @@ -433,21 +498,25 @@ | |
| 498 | if(!content){ |
| 499 | callback(content); |
| 500 | return this; |
| 501 | } |
| 502 | const fd = new FormData(); |
| 503 | fd.append('render_mode',this.e.selectPreviewMode.value); |
| 504 | fd.append('filename',this.finfo.filename); |
| 505 | fd.append('ln',E('[name=preview_ln]').checked ? 1 : 0); |
| 506 | fd.append('iframe_height', E('[name=preview_html_ems]').value); |
| 507 | fd.append('content',content || ''); |
| 508 | F.message( |
| 509 | "Fetching preview..." |
| 510 | ).fetch('fileedit',{ |
| 511 | urlParams: {ajax: 'preview'}, |
| 512 | payload: fd, |
| 513 | responseHeaders: 'x-fileedit-render-mode', |
| 514 | onload: (r,header)=>{ |
| 515 | P.selectPreviewMode(P.previewModes[header]); |
| 516 | if('wiki'===header) P.baseHrefForFile(); |
| 517 | else P.baseHrefRestore(); |
| 518 | callback(r); |
| 519 | F.message('Updated preview.'); |
| 520 | }, |
| 521 | onerror: (e)=>{ |
| 522 | fossil.fetch.onerror(e); |
| 523 |