Fossil SCM
Experimentally added '?' help buttons in wikiedit. Experimentally emit all fossil.XYZ APIs, rather than selected ones, to test whether that reduces overall transmission together with caching. DOM init-time timing workarounds to get confirmer buttons to pin their sizes properly.
Commit
9edbb7eab1b0fe489d79cbdfbf8d9394203f7e2c553dca2bbcd43dceef73d564
Parent
1f4143ba285d490…
11 files changed
+59
-5
+6
+8
+2
-2
+3
+34
-19
+107
+4
+25
-10
+8
-1
+37
-16
+59
-5
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -1100,15 +1100,16 @@ | ||
| 1100 | 1100 | .font-size-200 { |
| 1101 | 1101 | font-size: 200%; |
| 1102 | 1102 | } |
| 1103 | 1103 | |
| 1104 | 1104 | /** |
| 1105 | - .input-with-label is intended to be a wrapper element which | |
| 1106 | - contain both a LABEL tag and an INPUT or SELECT control. | |
| 1107 | - The wrapper is "necessary", as opposed to placing the INPUT | |
| 1108 | - in the LABEL, so that we can include multiple INPUT | |
| 1109 | - elements (e.g. a set of radio buttons). | |
| 1105 | + .input-with-label is intended to be a wrapper element which contain | |
| 1106 | + both a LABEL tag and an INPUT or SELECT control. The wrapper is | |
| 1107 | + "necessary", as opposed to placing the INPUT in the LABEL, so that | |
| 1108 | + we can include multiple INPUT elements (e.g. a set of radio | |
| 1109 | + buttons). Note that these elements must sometimes be BLOCK elements | |
| 1110 | + (e.g. DIV) so that certain nesting constructs are legal. | |
| 1110 | 1111 | */ |
| 1111 | 1112 | .input-with-label { |
| 1112 | 1113 | border: 1px inset #808080; |
| 1113 | 1114 | border-radius: 0.25em; |
| 1114 | 1115 | padding: 0.25em 0.4em; |
| @@ -1293,5 +1294,58 @@ | ||
| 1293 | 1294 | |
| 1294 | 1295 | blockquote.file-content { |
| 1295 | 1296 | /* file content block in the /file page */ |
| 1296 | 1297 | margin: 0 1em; |
| 1297 | 1298 | } |
| 1299 | + | |
| 1300 | + | |
| 1301 | +/** | |
| 1302 | + Circular "help" buttons intended to be placed to the right of | |
| 1303 | + another element and hold text text for it. These get initialized | |
| 1304 | + automatically at page startup via fossil.popupwidget.js. All child | |
| 1305 | + content gets moved out of the DOM and shown in a popup when they | |
| 1306 | + are clicked. They may be SPAN elements if their children are all | |
| 1307 | + inline elements, otherwise they must be DIVs (block elements) | |
| 1308 | + so that nesting of block elements is legal. | |
| 1309 | +*/ | |
| 1310 | +.help-buttonlet { | |
| 1311 | + display: inline-block; | |
| 1312 | + min-width: 1rem; | |
| 1313 | + max-width: 1rem; | |
| 1314 | + min-height: 1rem; | |
| 1315 | + max-height: 1rem; | |
| 1316 | + font-size: 0.9em; | |
| 1317 | + border-radius: 0.5rem; | |
| 1318 | + background-color: rgba(54, 54, 255,0.4); | |
| 1319 | + color: rgb(255, 255, 255); | |
| 1320 | + cursor: pointer; | |
| 1321 | + font-family: monspace; | |
| 1322 | + text-align: center; | |
| 1323 | + margin: 0 0 0 0.35em; | |
| 1324 | + border-width: 1px; | |
| 1325 | + border-style: outset; | |
| 1326 | + font-weight: 700; | |
| 1327 | + overflow: hidden; | |
| 1328 | +} | |
| 1329 | + | |
| 1330 | +.help-buttonlet::before { | |
| 1331 | + content: "?"; | |
| 1332 | +} | |
| 1333 | + | |
| 1334 | +/** | |
| 1335 | + We really want to hide all help text via CSS but CSS cannot select | |
| 1336 | + TEXT nodes. Thus we move them out of the way programmatically | |
| 1337 | + during initialization. | |
| 1338 | +*/ | |
| 1339 | +.help-buttonlet > *{} | |
| 1340 | + | |
| 1341 | +/** | |
| 1342 | + CSS class for PopupWidget which wraps .help-buttonlet content. | |
| 1343 | + They also have class fossil-tooltip. We need an overly-exact | |
| 1344 | + selector here to be certain that this class's style overrides | |
| 1345 | + that of fossil-tooltip. | |
| 1346 | +*/ | |
| 1347 | +.fossil-tooltip.help-buttonlet-content { | |
| 1348 | + cursor: default; | |
| 1349 | + text-align: left; | |
| 1350 | + border-style: outset; | |
| 1351 | +} | |
| 1298 | 1352 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1100,15 +1100,16 @@ | |
| 1100 | .font-size-200 { |
| 1101 | font-size: 200%; |
| 1102 | } |
| 1103 | |
| 1104 | /** |
| 1105 | .input-with-label is intended to be a wrapper element which |
| 1106 | contain both a LABEL tag and an INPUT or SELECT control. |
| 1107 | The wrapper is "necessary", as opposed to placing the INPUT |
| 1108 | in the LABEL, so that we can include multiple INPUT |
| 1109 | elements (e.g. a set of radio buttons). |
| 1110 | */ |
| 1111 | .input-with-label { |
| 1112 | border: 1px inset #808080; |
| 1113 | border-radius: 0.25em; |
| 1114 | padding: 0.25em 0.4em; |
| @@ -1293,5 +1294,58 @@ | |
| 1293 | |
| 1294 | blockquote.file-content { |
| 1295 | /* file content block in the /file page */ |
| 1296 | margin: 0 1em; |
| 1297 | } |
| 1298 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1100,15 +1100,16 @@ | |
| 1100 | .font-size-200 { |
| 1101 | font-size: 200%; |
| 1102 | } |
| 1103 | |
| 1104 | /** |
| 1105 | .input-with-label is intended to be a wrapper element which contain |
| 1106 | both a LABEL tag and an INPUT or SELECT control. The wrapper is |
| 1107 | "necessary", as opposed to placing the INPUT in the LABEL, so that |
| 1108 | we can include multiple INPUT elements (e.g. a set of radio |
| 1109 | buttons). Note that these elements must sometimes be BLOCK elements |
| 1110 | (e.g. DIV) so that certain nesting constructs are legal. |
| 1111 | */ |
| 1112 | .input-with-label { |
| 1113 | border: 1px inset #808080; |
| 1114 | border-radius: 0.25em; |
| 1115 | padding: 0.25em 0.4em; |
| @@ -1293,5 +1294,58 @@ | |
| 1294 | |
| 1295 | blockquote.file-content { |
| 1296 | /* file content block in the /file page */ |
| 1297 | margin: 0 1em; |
| 1298 | } |
| 1299 | |
| 1300 | |
| 1301 | /** |
| 1302 | Circular "help" buttons intended to be placed to the right of |
| 1303 | another element and hold text text for it. These get initialized |
| 1304 | automatically at page startup via fossil.popupwidget.js. All child |
| 1305 | content gets moved out of the DOM and shown in a popup when they |
| 1306 | are clicked. They may be SPAN elements if their children are all |
| 1307 | inline elements, otherwise they must be DIVs (block elements) |
| 1308 | so that nesting of block elements is legal. |
| 1309 | */ |
| 1310 | .help-buttonlet { |
| 1311 | display: inline-block; |
| 1312 | min-width: 1rem; |
| 1313 | max-width: 1rem; |
| 1314 | min-height: 1rem; |
| 1315 | max-height: 1rem; |
| 1316 | font-size: 0.9em; |
| 1317 | border-radius: 0.5rem; |
| 1318 | background-color: rgba(54, 54, 255,0.4); |
| 1319 | color: rgb(255, 255, 255); |
| 1320 | cursor: pointer; |
| 1321 | font-family: monspace; |
| 1322 | text-align: center; |
| 1323 | margin: 0 0 0 0.35em; |
| 1324 | border-width: 1px; |
| 1325 | border-style: outset; |
| 1326 | font-weight: 700; |
| 1327 | overflow: hidden; |
| 1328 | } |
| 1329 | |
| 1330 | .help-buttonlet::before { |
| 1331 | content: "?"; |
| 1332 | } |
| 1333 | |
| 1334 | /** |
| 1335 | We really want to hide all help text via CSS but CSS cannot select |
| 1336 | TEXT nodes. Thus we move them out of the way programmatically |
| 1337 | during initialization. |
| 1338 | */ |
| 1339 | .help-buttonlet > *{} |
| 1340 | |
| 1341 | /** |
| 1342 | CSS class for PopupWidget which wraps .help-buttonlet content. |
| 1343 | They also have class fossil-tooltip. We need an overly-exact |
| 1344 | selector here to be certain that this class's style overrides |
| 1345 | that of fossil-tooltip. |
| 1346 | */ |
| 1347 | .fossil-tooltip.help-buttonlet-content { |
| 1348 | cursor: default; |
| 1349 | text-align: left; |
| 1350 | border-style: outset; |
| 1351 | } |
| 1352 |
+6
| --- src/fileedit.c | ||
| +++ src/fileedit.c | ||
| @@ -1980,12 +1980,18 @@ | ||
| 1980 | 1980 | CX("</ul>"); |
| 1981 | 1981 | } |
| 1982 | 1982 | CX("</div>"/*#fileedit-tab-help*/); |
| 1983 | 1983 | |
| 1984 | 1984 | builtin_request_js("sbsdiff.js"); |
| 1985 | +#if 0 | |
| 1985 | 1986 | style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer", |
| 1986 | 1987 | "storage", 0); |
| 1988 | +#else | |
| 1989 | + style_emit_all_fossil_js_apis(); | |
| 1990 | + builtin_fulfill_js_requests(); | |
| 1991 | + builtin_request_js("fossil.page.fileedit.js"); | |
| 1992 | +#endif | |
| 1987 | 1993 | builtin_fulfill_js_requests(); |
| 1988 | 1994 | /* |
| 1989 | 1995 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1990 | 1996 | ** used for dynamically toggling certain UI components on and off. |
| 1991 | 1997 | ** Must come after window.fossil has been intialized and before |
| 1992 | 1998 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -1980,12 +1980,18 @@ | |
| 1980 | CX("</ul>"); |
| 1981 | } |
| 1982 | CX("</div>"/*#fileedit-tab-help*/); |
| 1983 | |
| 1984 | builtin_request_js("sbsdiff.js"); |
| 1985 | style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer", |
| 1986 | "storage", 0); |
| 1987 | builtin_fulfill_js_requests(); |
| 1988 | /* |
| 1989 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1990 | ** used for dynamically toggling certain UI components on and off. |
| 1991 | ** Must come after window.fossil has been intialized and before |
| 1992 |
| --- src/fileedit.c | |
| +++ src/fileedit.c | |
| @@ -1980,12 +1980,18 @@ | |
| 1980 | CX("</ul>"); |
| 1981 | } |
| 1982 | CX("</div>"/*#fileedit-tab-help*/); |
| 1983 | |
| 1984 | builtin_request_js("sbsdiff.js"); |
| 1985 | #if 0 |
| 1986 | style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer", |
| 1987 | "storage", 0); |
| 1988 | #else |
| 1989 | style_emit_all_fossil_js_apis(); |
| 1990 | builtin_fulfill_js_requests(); |
| 1991 | builtin_request_js("fossil.page.fileedit.js"); |
| 1992 | #endif |
| 1993 | builtin_fulfill_js_requests(); |
| 1994 | /* |
| 1995 | ** Set up a JS-side mapping of the AJAX_RENDER_xyz values. This is |
| 1996 | ** used for dynamically toggling certain UI components on and off. |
| 1997 | ** Must come after window.fossil has been intialized and before |
| 1998 |
| --- src/fossil.bootstrap.js | ||
| +++ src/fossil.bootstrap.js | ||
| @@ -328,10 +328,18 @@ | ||
| 328 | 328 | */ |
| 329 | 329 | F.onPageLoad = function(callback){ |
| 330 | 330 | window.addEventListener('load', callback, false); |
| 331 | 331 | return this; |
| 332 | 332 | }; |
| 333 | + /** | |
| 334 | + Convenience wrapper which adds a DOMContentLoadedevent listener | |
| 335 | + to the window object. Returns this. | |
| 336 | + */ | |
| 337 | + F.onDOMContentLoaded = function(callback){ | |
| 338 | + window.addEventListener('DOMContentLoaded', callback, false); | |
| 339 | + return this; | |
| 340 | + }; | |
| 333 | 341 | |
| 334 | 342 | /** |
| 335 | 343 | Assuming name is a repo-style filename, this function returns |
| 336 | 344 | a shortened form of that name: |
| 337 | 345 | |
| 338 | 346 |
| --- src/fossil.bootstrap.js | |
| +++ src/fossil.bootstrap.js | |
| @@ -328,10 +328,18 @@ | |
| 328 | */ |
| 329 | F.onPageLoad = function(callback){ |
| 330 | window.addEventListener('load', callback, false); |
| 331 | return this; |
| 332 | }; |
| 333 | |
| 334 | /** |
| 335 | Assuming name is a repo-style filename, this function returns |
| 336 | a shortened form of that name: |
| 337 | |
| 338 |
| --- src/fossil.bootstrap.js | |
| +++ src/fossil.bootstrap.js | |
| @@ -328,10 +328,18 @@ | |
| 328 | */ |
| 329 | F.onPageLoad = function(callback){ |
| 330 | window.addEventListener('load', callback, false); |
| 331 | return this; |
| 332 | }; |
| 333 | /** |
| 334 | Convenience wrapper which adds a DOMContentLoadedevent listener |
| 335 | to the window object. Returns this. |
| 336 | */ |
| 337 | F.onDOMContentLoaded = function(callback){ |
| 338 | window.addEventListener('DOMContentLoaded', callback, false); |
| 339 | return this; |
| 340 | }; |
| 341 | |
| 342 | /** |
| 343 | Assuming name is a repo-style filename, this function returns |
| 344 | a shortened form of that name: |
| 345 | |
| 346 |
+2
-2
| --- src/fossil.numbered-lines.js | ||
| +++ src/fossil.numbered-lines.js | ||
| @@ -6,19 +6,19 @@ | ||
| 6 | 6 | |
| 7 | 7 | Requires: fossil.bootstrap, fossil.dom, fossil.popupwidget, |
| 8 | 8 | fossil.copybutton |
| 9 | 9 | */ |
| 10 | 10 | var tbl = arg || document.querySelectorAll('table.numbered-lines'); |
| 11 | - if(!tbl) return /* no matching elements */; | |
| 12 | - else if(!arg){ | |
| 11 | + if(tbl && !arg){ | |
| 13 | 12 | if(tbl.length>1){ /* multiple query results: recurse */ |
| 14 | 13 | tbl.forEach( (t)=>callee(t) ); |
| 15 | 14 | return; |
| 16 | 15 | }else{/* single query result */ |
| 17 | 16 | tbl = tbl[0]; |
| 18 | 17 | } |
| 19 | 18 | } |
| 19 | + if(!tbl) return /* no matching elements */; | |
| 20 | 20 | const F = window.fossil, D = F.dom; |
| 21 | 21 | const tdLn = tbl.querySelector('td.line-numbers'); |
| 22 | 22 | const lineState = { |
| 23 | 23 | urlArgs: (window.location.search||'?') |
| 24 | 24 | .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */ |
| 25 | 25 |
| --- src/fossil.numbered-lines.js | |
| +++ src/fossil.numbered-lines.js | |
| @@ -6,19 +6,19 @@ | |
| 6 | |
| 7 | Requires: fossil.bootstrap, fossil.dom, fossil.popupwidget, |
| 8 | fossil.copybutton |
| 9 | */ |
| 10 | var tbl = arg || document.querySelectorAll('table.numbered-lines'); |
| 11 | if(!tbl) return /* no matching elements */; |
| 12 | else if(!arg){ |
| 13 | if(tbl.length>1){ /* multiple query results: recurse */ |
| 14 | tbl.forEach( (t)=>callee(t) ); |
| 15 | return; |
| 16 | }else{/* single query result */ |
| 17 | tbl = tbl[0]; |
| 18 | } |
| 19 | } |
| 20 | const F = window.fossil, D = F.dom; |
| 21 | const tdLn = tbl.querySelector('td.line-numbers'); |
| 22 | const lineState = { |
| 23 | urlArgs: (window.location.search||'?') |
| 24 | .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */ |
| 25 |
| --- src/fossil.numbered-lines.js | |
| +++ src/fossil.numbered-lines.js | |
| @@ -6,19 +6,19 @@ | |
| 6 | |
| 7 | Requires: fossil.bootstrap, fossil.dom, fossil.popupwidget, |
| 8 | fossil.copybutton |
| 9 | */ |
| 10 | var tbl = arg || document.querySelectorAll('table.numbered-lines'); |
| 11 | if(tbl && !arg){ |
| 12 | if(tbl.length>1){ /* multiple query results: recurse */ |
| 13 | tbl.forEach( (t)=>callee(t) ); |
| 14 | return; |
| 15 | }else{/* single query result */ |
| 16 | tbl = tbl[0]; |
| 17 | } |
| 18 | } |
| 19 | if(!tbl) return /* no matching elements */; |
| 20 | const F = window.fossil, D = F.dom; |
| 21 | const tdLn = tbl.querySelector('td.line-numbers'); |
| 22 | const lineState = { |
| 23 | urlArgs: (window.location.search||'?') |
| 24 | .replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */ |
| 25 |
| --- src/fossil.page.fileedit.js | ||
| +++ src/fossil.page.fileedit.js | ||
| @@ -492,10 +492,11 @@ | ||
| 492 | 492 | "Warning: persistent storage is not available, "+ |
| 493 | 493 | "so uncomitted edits will not survive a page reload." |
| 494 | 494 | )); |
| 495 | 495 | } |
| 496 | 496 | domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint); |
| 497 | + P.tabs.switchToTab(1/*DOM visibility workaround*/); | |
| 497 | 498 | F.confirmer(btnClear, { |
| 498 | 499 | /* must come after insertion into the DOM for the pinSize option to work. */ |
| 499 | 500 | pinSize: true, |
| 500 | 501 | confirmText: "DISCARD all local edits?", |
| 501 | 502 | onconfirm: function(e){ |
| @@ -509,10 +510,11 @@ | ||
| 509 | 510 | }, |
| 510 | 511 | ticks: F.config.confirmerButtonTicks |
| 511 | 512 | }); |
| 512 | 513 | D.addClass(this.e.btnClear,'hidden' /* must not be set until after confirmer is set up!*/); |
| 513 | 514 | $stash._fireStashEvent(/*read the page-load-time stash*/); |
| 515 | + P.tabs.switchToTab(0/*DOM visibility workaround*/); | |
| 514 | 516 | delete this.init; |
| 515 | 517 | }, |
| 516 | 518 | /** |
| 517 | 519 | Regenerates the edit selection list. |
| 518 | 520 | */ |
| @@ -737,10 +739,11 @@ | ||
| 737 | 739 | "click",(e)=>P.diff(false), false |
| 738 | 740 | ); |
| 739 | 741 | P.e.btnCommit.addEventListener( |
| 740 | 742 | "click",(e)=>P.commit(), false |
| 741 | 743 | ); |
| 744 | + P.tabs.switchToTab(1/*DOM visibility workaround*/); | |
| 742 | 745 | F.confirmer(P.e.btnReload, { |
| 743 | 746 | pinSize: true, |
| 744 | 747 | confirmText: "Really reload, losing edits?", |
| 745 | 748 | onconfirm: (e)=>P.unstashContent().loadFile(), |
| 746 | 749 | ticks: F.config.confirmerButtonTicks |
| 747 | 750 |
| --- src/fossil.page.fileedit.js | |
| +++ src/fossil.page.fileedit.js | |
| @@ -492,10 +492,11 @@ | |
| 492 | "Warning: persistent storage is not available, "+ |
| 493 | "so uncomitted edits will not survive a page reload." |
| 494 | )); |
| 495 | } |
| 496 | domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint); |
| 497 | F.confirmer(btnClear, { |
| 498 | /* must come after insertion into the DOM for the pinSize option to work. */ |
| 499 | pinSize: true, |
| 500 | confirmText: "DISCARD all local edits?", |
| 501 | onconfirm: function(e){ |
| @@ -509,10 +510,11 @@ | |
| 509 | }, |
| 510 | ticks: F.config.confirmerButtonTicks |
| 511 | }); |
| 512 | D.addClass(this.e.btnClear,'hidden' /* must not be set until after confirmer is set up!*/); |
| 513 | $stash._fireStashEvent(/*read the page-load-time stash*/); |
| 514 | delete this.init; |
| 515 | }, |
| 516 | /** |
| 517 | Regenerates the edit selection list. |
| 518 | */ |
| @@ -737,10 +739,11 @@ | |
| 737 | "click",(e)=>P.diff(false), false |
| 738 | ); |
| 739 | P.e.btnCommit.addEventListener( |
| 740 | "click",(e)=>P.commit(), false |
| 741 | ); |
| 742 | F.confirmer(P.e.btnReload, { |
| 743 | pinSize: true, |
| 744 | confirmText: "Really reload, losing edits?", |
| 745 | onconfirm: (e)=>P.unstashContent().loadFile(), |
| 746 | ticks: F.config.confirmerButtonTicks |
| 747 |
| --- src/fossil.page.fileedit.js | |
| +++ src/fossil.page.fileedit.js | |
| @@ -492,10 +492,11 @@ | |
| 492 | "Warning: persistent storage is not available, "+ |
| 493 | "so uncomitted edits will not survive a page reload." |
| 494 | )); |
| 495 | } |
| 496 | domInsertPoint.parentNode.insertBefore(wrapper, domInsertPoint); |
| 497 | P.tabs.switchToTab(1/*DOM visibility workaround*/); |
| 498 | F.confirmer(btnClear, { |
| 499 | /* must come after insertion into the DOM for the pinSize option to work. */ |
| 500 | pinSize: true, |
| 501 | confirmText: "DISCARD all local edits?", |
| 502 | onconfirm: function(e){ |
| @@ -509,10 +510,11 @@ | |
| 510 | }, |
| 511 | ticks: F.config.confirmerButtonTicks |
| 512 | }); |
| 513 | D.addClass(this.e.btnClear,'hidden' /* must not be set until after confirmer is set up!*/); |
| 514 | $stash._fireStashEvent(/*read the page-load-time stash*/); |
| 515 | P.tabs.switchToTab(0/*DOM visibility workaround*/); |
| 516 | delete this.init; |
| 517 | }, |
| 518 | /** |
| 519 | Regenerates the edit selection list. |
| 520 | */ |
| @@ -737,10 +739,11 @@ | |
| 739 | "click",(e)=>P.diff(false), false |
| 740 | ); |
| 741 | P.e.btnCommit.addEventListener( |
| 742 | "click",(e)=>P.commit(), false |
| 743 | ); |
| 744 | P.tabs.switchToTab(1/*DOM visibility workaround*/); |
| 745 | F.confirmer(P.e.btnReload, { |
| 746 | pinSize: true, |
| 747 | confirmText: "Really reload, losing edits?", |
| 748 | onconfirm: (e)=>P.unstashContent().loadFile(), |
| 749 | ticks: F.config.confirmerButtonTicks |
| 750 |
+34
-19
| --- src/fossil.page.wikiedit.js | ||
| +++ src/fossil.page.wikiedit.js | ||
| @@ -2,11 +2,11 @@ | ||
| 2 | 2 | "use strict"; |
| 3 | 3 | /** |
| 4 | 4 | Client-side implementation of the /wikiedit app. Requires that |
| 5 | 5 | the fossil JS bootstrapping is complete and that several fossil |
| 6 | 6 | JS APIs have been installed: fossil.fetch, fossil.dom, |
| 7 | - fossil.tabs, fossil.storage, fossil.confirmer. | |
| 7 | + fossil.tabs, fossil.storage, fossil.confirmer, fossil.popupwidget. | |
| 8 | 8 | |
| 9 | 9 | Custom events which can be listened for via |
| 10 | 10 | fossil.page.addEventListener(): |
| 11 | 11 | |
| 12 | 12 | - Event 'wiki-page-loaded': passes on information when it |
| @@ -557,11 +557,11 @@ | ||
| 557 | 557 | D.attr(sel, 'size', 12); |
| 558 | 558 | D.option(D.disable(D.clearElement(sel)), "Loading..."); |
| 559 | 559 | |
| 560 | 560 | /** Set up filter checkboxes for the various types |
| 561 | 561 | of wiki pages... */ |
| 562 | - const fsFilter = D.fieldset("Page types"), | |
| 562 | + const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"), | |
| 563 | 563 | fsFilterBody = D.div(), |
| 564 | 564 | filters = ['normal', 'branch/...', 'tag/...', 'checkin/...'] |
| 565 | 565 | ; |
| 566 | 566 | D.append(fsFilter, fsFilterBody); |
| 567 | 567 | D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch'); |
| @@ -597,13 +597,18 @@ | ||
| 597 | 597 | 'deleted'), |
| 598 | 598 | 'for', cbId), |
| 599 | 599 | cb = D.attr(D.input('checkbox'), 'id', cbId); |
| 600 | 600 | cb.checked = false; |
| 601 | 601 | D.addClass(parentElem,'hide-deleted'); |
| 602 | - D.attr(lbl, 'title', | |
| 603 | - 'Fossil considers empty pages to be "deleted" in some contexts.'); | |
| 604 | - D.append(fsFilterBody, D.append(D.span(), cb, lbl)); | |
| 602 | + D.attr(lbl); | |
| 603 | + const deletedTip = F.helpButtonlets.create( | |
| 604 | + D.span(), | |
| 605 | + 'Fossil considers empty pages to be "deleted" in some contexts.' | |
| 606 | + ); | |
| 607 | + D.append(fsFilterBody, D.append( | |
| 608 | + D.span(), cb, lbl, deletedTip | |
| 609 | + )); | |
| 605 | 610 | cb.addEventListener( |
| 606 | 611 | 'change', |
| 607 | 612 | function(ev){ |
| 608 | 613 | if(ev.target.checked) D.removeClass(parentElem,'hide-deleted'); |
| 609 | 614 | else D.addClass(parentElem,'hide-deleted'); |
| @@ -682,22 +687,26 @@ | ||
| 682 | 687 | init: function(domInsertPoint/*insert widget BEFORE this element*/){ |
| 683 | 688 | const wrapper = D.addClass( |
| 684 | 689 | D.attr(D.div(),'id','wikiedit-stash-selector'), |
| 685 | 690 | 'input-with-label' |
| 686 | 691 | ); |
| 687 | - const sel = this.e.select = D.select(); | |
| 688 | - const btnClear = this.e.btnClear = D.button("Discard Edits"); | |
| 692 | + const sel = this.e.select = D.select(), | |
| 693 | + btnClear = this.e.btnClear = D.button("Discard Edits"), | |
| 694 | + btnHelp = D.append( | |
| 695 | + D.addClass(D.div(), "help-buttonlet"), | |
| 696 | + 'Locally-edited wiki pages. Timestamps are the last local edit time. ', | |
| 697 | + 'Only the ',P.config.defaultMaxStashSize,' most recent pages ', | |
| 698 | + 'are retained. Saving or reloading a file removes it from this list. ', | |
| 699 | + D.append(D.code(),'localStorage'),' uses browser-local persistent storage. ', | |
| 700 | + D.append(D.code(),'sessionStorage'),' uses storage local to this browser tab.' | |
| 701 | + ); | |
| 689 | 702 | D.append(wrapper, "Local edits (", |
| 690 | 703 | D.append(D.code(), |
| 691 | 704 | F.storage.storageImplName()), |
| 692 | 705 | "):", |
| 693 | - sel, btnClear); | |
| 694 | - D.attr(wrapper, "title", [ | |
| 695 | - 'Locally-edited wiki pages. Timestamps are the last local edit time.', | |
| 696 | - 'Only the',P.config.defaultMaxStashSize,'most recent pages', | |
| 697 | - 'are retained. Saving or reloading a file removes it from this list.' | |
| 698 | - ].join(' ')); | |
| 706 | + btnHelp, sel, btnClear); | |
| 707 | + F.helpButtonlets.setup(btnHelp); | |
| 699 | 708 | D.option(D.disable(sel), "(empty)"); |
| 700 | 709 | P.addEventListener('wiki-stash-updated',(e)=>this.updateList(e.detail)); |
| 701 | 710 | P.addEventListener('wiki-page-loaded',(e)=>this.updateList($stash, e.detail)); |
| 702 | 711 | sel.addEventListener('change',function(e){ |
| 703 | 712 | const opt = this.selectedOptions[0]; |
| @@ -835,13 +844,11 @@ | ||
| 835 | 844 | P.base.originalHref = P.base.tag.href; |
| 836 | 845 | P.e = { /* various DOM elements we work with... */ |
| 837 | 846 | taEditor: E('#wikiedit-content-editor'), |
| 838 | 847 | btnReload: E("#wikiedit-tab-content button.wikiedit-content-reload"), |
| 839 | 848 | btnSave: E("button.wikiedit-save"), |
| 840 | - btnSaveClose: D.attr(E("button.wikiedit-save-close"), | |
| 841 | - 'title', | |
| 842 | - 'Save changes and return to the wiki reader.'), | |
| 849 | + btnSaveClose: E("button.wikiedit-save-close"), | |
| 843 | 850 | selectMimetype: E('select[name=mimetype]'), |
| 844 | 851 | selectFontSizeWrap: E('#select-font-size'), |
| 845 | 852 | // selectDiffWS: E('select[name=diff_ws]'), |
| 846 | 853 | cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'), |
| 847 | 854 | previewTarget: E('#wikiedit-tab-preview-wrapper'), |
| @@ -870,12 +877,12 @@ | ||
| 870 | 877 | 'before-switch-to', function(ev){ |
| 871 | 878 | const theTab = ev.detail, btnSlot = theTab.querySelector('.save-button-slot'); |
| 872 | 879 | if(btnSlot){ |
| 873 | 880 | /* Several places make sense for a save button, so we'll |
| 874 | 881 | move that button around to those tabs where it makes sense. */ |
| 875 | - btnSlot.parentNode.insertBefore( P.e.btnSave, btnSlot ); | |
| 876 | - btnSlot.parentNode.insertBefore( P.e.btnSaveClose, btnSlot ); | |
| 882 | + btnSlot.parentNode.insertBefore( P.e.btnSave.parentNode, btnSlot ); | |
| 883 | + btnSlot.parentNode.insertBefore( P.e.btnSaveClose.parentNode, btnSlot ); | |
| 877 | 884 | P.updateSaveButton(); |
| 878 | 885 | } |
| 879 | 886 | if(theTab===P.e.tabs.preview){ |
| 880 | 887 | P.baseHrefForWiki(); |
| 881 | 888 | if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview(); |
| @@ -958,10 +965,11 @@ | ||
| 958 | 965 | F.message("Discarded new page ["+w.name+"]."); |
| 959 | 966 | } |
| 960 | 967 | }; |
| 961 | 968 | |
| 962 | 969 | if(P.config.useConfirmerButtons.reload){ |
| 970 | + P.tabs.switchToTab(1/*DOM visibility workaround*/); | |
| 963 | 971 | F.confirmer(P.e.btnReload, { |
| 964 | 972 | pinSize: true, |
| 965 | 973 | confirmText: "Really reload, losing edits?", |
| 966 | 974 | onconfirm: doReload, |
| 967 | 975 | ticks: F.config.confirmerButtonTicks |
| @@ -968,10 +976,11 @@ | ||
| 968 | 976 | }); |
| 969 | 977 | }else{ |
| 970 | 978 | P.e.btnReload.addEventListener('click', doReload, false); |
| 971 | 979 | } |
| 972 | 980 | if(P.config.useConfirmerButtons.save){ |
| 981 | + P.tabs.switchToTab(1/*DOM visibility workaround*/); | |
| 973 | 982 | F.confirmer(P.e.btnSave, { |
| 974 | 983 | pinSize: true, |
| 975 | 984 | confirmText: "Really save changes?", |
| 976 | 985 | onconfirm: ()=>doSave(), |
| 977 | 986 | ticks: F.config.confirmerButtonTicks |
| @@ -1069,13 +1078,19 @@ | ||
| 1069 | 1078 | } |
| 1070 | 1079 | P.updatePageTitle().updateSaveButton(/* b/c save() routes through here */); |
| 1071 | 1080 | }, |
| 1072 | 1081 | false |
| 1073 | 1082 | ); |
| 1074 | - /* These init()s need to come after P's event handlers are registered */ | |
| 1083 | + /* These init()s need to come after P's event handlers are registered. | |
| 1084 | + The tab-switching is a workaround for the pinSize option of the confirmer widgets: | |
| 1085 | + it does not work if the confirmer button being initialized is in a hidden | |
| 1086 | + part of the DOM :/. */ | |
| 1087 | + P.tabs.switchToTab(0); | |
| 1075 | 1088 | WikiList.init( P.e.tabs.pageList.firstElementChild ); |
| 1089 | + P.tabs.switchToTab(1); | |
| 1076 | 1090 | P.stashWidget.init(P.e.tabs.content.lastElementChild); |
| 1091 | + P.tabs.switchToTab(0); | |
| 1077 | 1092 | //P.$wikiList = WikiList/*only for testing/debugging*/; |
| 1078 | 1093 | }/*F.onPageLoad()*/); |
| 1079 | 1094 | |
| 1080 | 1095 | /** |
| 1081 | 1096 | Returns true if fossil.page.winfo is set, indicating that a page |
| 1082 | 1097 |
| --- src/fossil.page.wikiedit.js | |
| +++ src/fossil.page.wikiedit.js | |
| @@ -2,11 +2,11 @@ | |
| 2 | "use strict"; |
| 3 | /** |
| 4 | Client-side implementation of the /wikiedit app. Requires that |
| 5 | the fossil JS bootstrapping is complete and that several fossil |
| 6 | JS APIs have been installed: fossil.fetch, fossil.dom, |
| 7 | fossil.tabs, fossil.storage, fossil.confirmer. |
| 8 | |
| 9 | Custom events which can be listened for via |
| 10 | fossil.page.addEventListener(): |
| 11 | |
| 12 | - Event 'wiki-page-loaded': passes on information when it |
| @@ -557,11 +557,11 @@ | |
| 557 | D.attr(sel, 'size', 12); |
| 558 | D.option(D.disable(D.clearElement(sel)), "Loading..."); |
| 559 | |
| 560 | /** Set up filter checkboxes for the various types |
| 561 | of wiki pages... */ |
| 562 | const fsFilter = D.fieldset("Page types"), |
| 563 | fsFilterBody = D.div(), |
| 564 | filters = ['normal', 'branch/...', 'tag/...', 'checkin/...'] |
| 565 | ; |
| 566 | D.append(fsFilter, fsFilterBody); |
| 567 | D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch'); |
| @@ -597,13 +597,18 @@ | |
| 597 | 'deleted'), |
| 598 | 'for', cbId), |
| 599 | cb = D.attr(D.input('checkbox'), 'id', cbId); |
| 600 | cb.checked = false; |
| 601 | D.addClass(parentElem,'hide-deleted'); |
| 602 | D.attr(lbl, 'title', |
| 603 | 'Fossil considers empty pages to be "deleted" in some contexts.'); |
| 604 | D.append(fsFilterBody, D.append(D.span(), cb, lbl)); |
| 605 | cb.addEventListener( |
| 606 | 'change', |
| 607 | function(ev){ |
| 608 | if(ev.target.checked) D.removeClass(parentElem,'hide-deleted'); |
| 609 | else D.addClass(parentElem,'hide-deleted'); |
| @@ -682,22 +687,26 @@ | |
| 682 | init: function(domInsertPoint/*insert widget BEFORE this element*/){ |
| 683 | const wrapper = D.addClass( |
| 684 | D.attr(D.div(),'id','wikiedit-stash-selector'), |
| 685 | 'input-with-label' |
| 686 | ); |
| 687 | const sel = this.e.select = D.select(); |
| 688 | const btnClear = this.e.btnClear = D.button("Discard Edits"); |
| 689 | D.append(wrapper, "Local edits (", |
| 690 | D.append(D.code(), |
| 691 | F.storage.storageImplName()), |
| 692 | "):", |
| 693 | sel, btnClear); |
| 694 | D.attr(wrapper, "title", [ |
| 695 | 'Locally-edited wiki pages. Timestamps are the last local edit time.', |
| 696 | 'Only the',P.config.defaultMaxStashSize,'most recent pages', |
| 697 | 'are retained. Saving or reloading a file removes it from this list.' |
| 698 | ].join(' ')); |
| 699 | D.option(D.disable(sel), "(empty)"); |
| 700 | P.addEventListener('wiki-stash-updated',(e)=>this.updateList(e.detail)); |
| 701 | P.addEventListener('wiki-page-loaded',(e)=>this.updateList($stash, e.detail)); |
| 702 | sel.addEventListener('change',function(e){ |
| 703 | const opt = this.selectedOptions[0]; |
| @@ -835,13 +844,11 @@ | |
| 835 | P.base.originalHref = P.base.tag.href; |
| 836 | P.e = { /* various DOM elements we work with... */ |
| 837 | taEditor: E('#wikiedit-content-editor'), |
| 838 | btnReload: E("#wikiedit-tab-content button.wikiedit-content-reload"), |
| 839 | btnSave: E("button.wikiedit-save"), |
| 840 | btnSaveClose: D.attr(E("button.wikiedit-save-close"), |
| 841 | 'title', |
| 842 | 'Save changes and return to the wiki reader.'), |
| 843 | selectMimetype: E('select[name=mimetype]'), |
| 844 | selectFontSizeWrap: E('#select-font-size'), |
| 845 | // selectDiffWS: E('select[name=diff_ws]'), |
| 846 | cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'), |
| 847 | previewTarget: E('#wikiedit-tab-preview-wrapper'), |
| @@ -870,12 +877,12 @@ | |
| 870 | 'before-switch-to', function(ev){ |
| 871 | const theTab = ev.detail, btnSlot = theTab.querySelector('.save-button-slot'); |
| 872 | if(btnSlot){ |
| 873 | /* Several places make sense for a save button, so we'll |
| 874 | move that button around to those tabs where it makes sense. */ |
| 875 | btnSlot.parentNode.insertBefore( P.e.btnSave, btnSlot ); |
| 876 | btnSlot.parentNode.insertBefore( P.e.btnSaveClose, btnSlot ); |
| 877 | P.updateSaveButton(); |
| 878 | } |
| 879 | if(theTab===P.e.tabs.preview){ |
| 880 | P.baseHrefForWiki(); |
| 881 | if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview(); |
| @@ -958,10 +965,11 @@ | |
| 958 | F.message("Discarded new page ["+w.name+"]."); |
| 959 | } |
| 960 | }; |
| 961 | |
| 962 | if(P.config.useConfirmerButtons.reload){ |
| 963 | F.confirmer(P.e.btnReload, { |
| 964 | pinSize: true, |
| 965 | confirmText: "Really reload, losing edits?", |
| 966 | onconfirm: doReload, |
| 967 | ticks: F.config.confirmerButtonTicks |
| @@ -968,10 +976,11 @@ | |
| 968 | }); |
| 969 | }else{ |
| 970 | P.e.btnReload.addEventListener('click', doReload, false); |
| 971 | } |
| 972 | if(P.config.useConfirmerButtons.save){ |
| 973 | F.confirmer(P.e.btnSave, { |
| 974 | pinSize: true, |
| 975 | confirmText: "Really save changes?", |
| 976 | onconfirm: ()=>doSave(), |
| 977 | ticks: F.config.confirmerButtonTicks |
| @@ -1069,13 +1078,19 @@ | |
| 1069 | } |
| 1070 | P.updatePageTitle().updateSaveButton(/* b/c save() routes through here */); |
| 1071 | }, |
| 1072 | false |
| 1073 | ); |
| 1074 | /* These init()s need to come after P's event handlers are registered */ |
| 1075 | WikiList.init( P.e.tabs.pageList.firstElementChild ); |
| 1076 | P.stashWidget.init(P.e.tabs.content.lastElementChild); |
| 1077 | //P.$wikiList = WikiList/*only for testing/debugging*/; |
| 1078 | }/*F.onPageLoad()*/); |
| 1079 | |
| 1080 | /** |
| 1081 | Returns true if fossil.page.winfo is set, indicating that a page |
| 1082 |
| --- src/fossil.page.wikiedit.js | |
| +++ src/fossil.page.wikiedit.js | |
| @@ -2,11 +2,11 @@ | |
| 2 | "use strict"; |
| 3 | /** |
| 4 | Client-side implementation of the /wikiedit app. Requires that |
| 5 | the fossil JS bootstrapping is complete and that several fossil |
| 6 | JS APIs have been installed: fossil.fetch, fossil.dom, |
| 7 | fossil.tabs, fossil.storage, fossil.confirmer, fossil.popupwidget. |
| 8 | |
| 9 | Custom events which can be listened for via |
| 10 | fossil.page.addEventListener(): |
| 11 | |
| 12 | - Event 'wiki-page-loaded': passes on information when it |
| @@ -557,11 +557,11 @@ | |
| 557 | D.attr(sel, 'size', 12); |
| 558 | D.option(D.disable(D.clearElement(sel)), "Loading..."); |
| 559 | |
| 560 | /** Set up filter checkboxes for the various types |
| 561 | of wiki pages... */ |
| 562 | const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"), |
| 563 | fsFilterBody = D.div(), |
| 564 | filters = ['normal', 'branch/...', 'tag/...', 'checkin/...'] |
| 565 | ; |
| 566 | D.append(fsFilter, fsFilterBody); |
| 567 | D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch'); |
| @@ -597,13 +597,18 @@ | |
| 597 | 'deleted'), |
| 598 | 'for', cbId), |
| 599 | cb = D.attr(D.input('checkbox'), 'id', cbId); |
| 600 | cb.checked = false; |
| 601 | D.addClass(parentElem,'hide-deleted'); |
| 602 | D.attr(lbl); |
| 603 | const deletedTip = F.helpButtonlets.create( |
| 604 | D.span(), |
| 605 | 'Fossil considers empty pages to be "deleted" in some contexts.' |
| 606 | ); |
| 607 | D.append(fsFilterBody, D.append( |
| 608 | D.span(), cb, lbl, deletedTip |
| 609 | )); |
| 610 | cb.addEventListener( |
| 611 | 'change', |
| 612 | function(ev){ |
| 613 | if(ev.target.checked) D.removeClass(parentElem,'hide-deleted'); |
| 614 | else D.addClass(parentElem,'hide-deleted'); |
| @@ -682,22 +687,26 @@ | |
| 687 | init: function(domInsertPoint/*insert widget BEFORE this element*/){ |
| 688 | const wrapper = D.addClass( |
| 689 | D.attr(D.div(),'id','wikiedit-stash-selector'), |
| 690 | 'input-with-label' |
| 691 | ); |
| 692 | const sel = this.e.select = D.select(), |
| 693 | btnClear = this.e.btnClear = D.button("Discard Edits"), |
| 694 | btnHelp = D.append( |
| 695 | D.addClass(D.div(), "help-buttonlet"), |
| 696 | 'Locally-edited wiki pages. Timestamps are the last local edit time. ', |
| 697 | 'Only the ',P.config.defaultMaxStashSize,' most recent pages ', |
| 698 | 'are retained. Saving or reloading a file removes it from this list. ', |
| 699 | D.append(D.code(),'localStorage'),' uses browser-local persistent storage. ', |
| 700 | D.append(D.code(),'sessionStorage'),' uses storage local to this browser tab.' |
| 701 | ); |
| 702 | D.append(wrapper, "Local edits (", |
| 703 | D.append(D.code(), |
| 704 | F.storage.storageImplName()), |
| 705 | "):", |
| 706 | btnHelp, sel, btnClear); |
| 707 | F.helpButtonlets.setup(btnHelp); |
| 708 | D.option(D.disable(sel), "(empty)"); |
| 709 | P.addEventListener('wiki-stash-updated',(e)=>this.updateList(e.detail)); |
| 710 | P.addEventListener('wiki-page-loaded',(e)=>this.updateList($stash, e.detail)); |
| 711 | sel.addEventListener('change',function(e){ |
| 712 | const opt = this.selectedOptions[0]; |
| @@ -835,13 +844,11 @@ | |
| 844 | P.base.originalHref = P.base.tag.href; |
| 845 | P.e = { /* various DOM elements we work with... */ |
| 846 | taEditor: E('#wikiedit-content-editor'), |
| 847 | btnReload: E("#wikiedit-tab-content button.wikiedit-content-reload"), |
| 848 | btnSave: E("button.wikiedit-save"), |
| 849 | btnSaveClose: E("button.wikiedit-save-close"), |
| 850 | selectMimetype: E('select[name=mimetype]'), |
| 851 | selectFontSizeWrap: E('#select-font-size'), |
| 852 | // selectDiffWS: E('select[name=diff_ws]'), |
| 853 | cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'), |
| 854 | previewTarget: E('#wikiedit-tab-preview-wrapper'), |
| @@ -870,12 +877,12 @@ | |
| 877 | 'before-switch-to', function(ev){ |
| 878 | const theTab = ev.detail, btnSlot = theTab.querySelector('.save-button-slot'); |
| 879 | if(btnSlot){ |
| 880 | /* Several places make sense for a save button, so we'll |
| 881 | move that button around to those tabs where it makes sense. */ |
| 882 | btnSlot.parentNode.insertBefore( P.e.btnSave.parentNode, btnSlot ); |
| 883 | btnSlot.parentNode.insertBefore( P.e.btnSaveClose.parentNode, btnSlot ); |
| 884 | P.updateSaveButton(); |
| 885 | } |
| 886 | if(theTab===P.e.tabs.preview){ |
| 887 | P.baseHrefForWiki(); |
| 888 | if(P.previewNeedsUpdate && P.e.cbAutoPreview.checked) P.preview(); |
| @@ -958,10 +965,11 @@ | |
| 965 | F.message("Discarded new page ["+w.name+"]."); |
| 966 | } |
| 967 | }; |
| 968 | |
| 969 | if(P.config.useConfirmerButtons.reload){ |
| 970 | P.tabs.switchToTab(1/*DOM visibility workaround*/); |
| 971 | F.confirmer(P.e.btnReload, { |
| 972 | pinSize: true, |
| 973 | confirmText: "Really reload, losing edits?", |
| 974 | onconfirm: doReload, |
| 975 | ticks: F.config.confirmerButtonTicks |
| @@ -968,10 +976,11 @@ | |
| 976 | }); |
| 977 | }else{ |
| 978 | P.e.btnReload.addEventListener('click', doReload, false); |
| 979 | } |
| 980 | if(P.config.useConfirmerButtons.save){ |
| 981 | P.tabs.switchToTab(1/*DOM visibility workaround*/); |
| 982 | F.confirmer(P.e.btnSave, { |
| 983 | pinSize: true, |
| 984 | confirmText: "Really save changes?", |
| 985 | onconfirm: ()=>doSave(), |
| 986 | ticks: F.config.confirmerButtonTicks |
| @@ -1069,13 +1078,19 @@ | |
| 1078 | } |
| 1079 | P.updatePageTitle().updateSaveButton(/* b/c save() routes through here */); |
| 1080 | }, |
| 1081 | false |
| 1082 | ); |
| 1083 | /* These init()s need to come after P's event handlers are registered. |
| 1084 | The tab-switching is a workaround for the pinSize option of the confirmer widgets: |
| 1085 | it does not work if the confirmer button being initialized is in a hidden |
| 1086 | part of the DOM :/. */ |
| 1087 | P.tabs.switchToTab(0); |
| 1088 | WikiList.init( P.e.tabs.pageList.firstElementChild ); |
| 1089 | P.tabs.switchToTab(1); |
| 1090 | P.stashWidget.init(P.e.tabs.content.lastElementChild); |
| 1091 | P.tabs.switchToTab(0); |
| 1092 | //P.$wikiList = WikiList/*only for testing/debugging*/; |
| 1093 | }/*F.onPageLoad()*/); |
| 1094 | |
| 1095 | /** |
| 1096 | Returns true if fossil.page.winfo is set, indicating that a page |
| 1097 |
+107
| --- src/fossil.popupwidget.js | ||
| +++ src/fossil.popupwidget.js | ||
| @@ -264,6 +264,113 @@ | ||
| 264 | 264 | error: function(/*...*/){ |
| 265 | 265 | return toastImpl('error',2,arguments); |
| 266 | 266 | } |
| 267 | 267 | }/*F.toast*/; |
| 268 | 268 | |
| 269 | + | |
| 270 | + F.helpButtonlets = { | |
| 271 | + /** | |
| 272 | + Initializes one or more "help buttonlets". It may be passed any of: | |
| 273 | + | |
| 274 | + - A string: CSS selector (multiple matches are legal) | |
| 275 | + | |
| 276 | + - A single DOM element. | |
| 277 | + | |
| 278 | + - A forEach-compatible container of DOM elements. | |
| 279 | + | |
| 280 | + - No arguments, which is equivalent to passing the string | |
| 281 | + ".help-buttonlet:not(.processed)". | |
| 282 | + | |
| 283 | + Passing the same element(s) more than once is a no-op: during | |
| 284 | + initialization, each elements get the class'processed' added to | |
| 285 | + it, and any elements with that class are skipped. | |
| 286 | + | |
| 287 | + All child nodes of a help buttonlet are removed from the button | |
| 288 | + during initialization and stashed away for use in a PopupWidget | |
| 289 | + when the botton is clicked. | |
| 290 | + | |
| 291 | + */ | |
| 292 | + setup: function f(){ | |
| 293 | + if(!f.clickHandler){ | |
| 294 | + f.clickHandler = function fch(ev){ | |
| 295 | + if(!fch.popup){ | |
| 296 | + fch.popup = new F.PopupWidget({ | |
| 297 | + cssClass: ['fossil-tooltip', 'help-buttonlet-content'], | |
| 298 | + refresh: function(){ | |
| 299 | + } | |
| 300 | + }); | |
| 301 | + const hide = ()=>fch.popup.hide(); | |
| 302 | + fch.popup.e.addEventListener('click', hide, false); | |
| 303 | + document.body.addEventListener('click', hide, true); | |
| 304 | + document.body.addEventListener('keydown', function(ev){ | |
| 305 | + if(fch.popup.isShown() && 27===ev.which){ | |
| 306 | + fch.popup.hide(); | |
| 307 | + } | |
| 308 | + }, true); | |
| 309 | + } | |
| 310 | + D.append(D.clearElement(fch.popup.e), ev.target.$helpContent); | |
| 311 | + const rect1 = ev.target.getClientRects()[0]; | |
| 312 | + var x = rect1.left, y = rect1.top; | |
| 313 | + if(x<0) x = 0; | |
| 314 | + if(y<0) y = 0; | |
| 315 | + /* shift help to the left 1/2 the width of fch.popup.e. However, | |
| 316 | + fch.popup.e.getClientRects() is empty until the popup is shown, | |
| 317 | + so we have to show it, calculate that size, then move it. */ | |
| 318 | + fch.popup.show(x, y); | |
| 319 | + const rect2 = fch.popup.e.getClientRects()[0]; | |
| 320 | + x -= rect2.width/2; | |
| 321 | + if(x<0) x = 0; | |
| 322 | + fch.popup.show(x, y); | |
| 323 | + }; | |
| 324 | + } | |
| 325 | + var elems; | |
| 326 | + if(!arguments.length){ | |
| 327 | + arguments[0] = '.help-buttonlet:not(.processed)'; | |
| 328 | + arguments.length = 1; | |
| 329 | + } | |
| 330 | + if(arguments.length){ | |
| 331 | + if('string'===typeof arguments[0]){ | |
| 332 | + elems = document.querySelectorAll(arguments[0]); | |
| 333 | + }else if(arguments[0] instanceof HTMLElement){ | |
| 334 | + elems = [arguments[0]]; | |
| 335 | + }else{/* assume DOM element list or array */ | |
| 336 | + elems = arguments[0]; | |
| 337 | + } | |
| 338 | + } | |
| 339 | + if(!elems) return; | |
| 340 | + elems.forEach(function(e){ | |
| 341 | + if(e.classList.contains('processed')) return; | |
| 342 | + e.classList.add('processed'); | |
| 343 | + e.$helpContent = []; | |
| 344 | + /* We have to move all child nodes out of the way because we | |
| 345 | + cannot hide TEXT nodes via CSS (which cannot select TEXT | |
| 346 | + nodes). We have to do it in two steps to avoid invaliding | |
| 347 | + the list during traversal. */ | |
| 348 | + e.childNodes.forEach((ch)=>e.$helpContent.push(ch)); | |
| 349 | + e.$helpContent.forEach((ch)=>ch.remove()); | |
| 350 | + e.addEventListener('click', f.clickHandler, false); | |
| 351 | + }); | |
| 352 | + }, | |
| 353 | + | |
| 354 | + /** | |
| 355 | + Sets up the given element as a "help buttonlet", adding the CSS | |
| 356 | + class help-buttonlet to it. Any (optional) arguments after the | |
| 357 | + first are appended to the element using fossil.dom.append(), so | |
| 358 | + that they become the content for the buttonlet's popup help. | |
| 359 | + | |
| 360 | + The element is then passed to this.setup() before it | |
| 361 | + is returned from this function. | |
| 362 | + */ | |
| 363 | + create: function(elem/*...body*/){ | |
| 364 | + D.addClass(elem, 'help-buttonlet'); | |
| 365 | + if(arguments.length>1){ | |
| 366 | + const args = Array.prototype.slice.call(arguments,1); | |
| 367 | + D.append(elem, args); | |
| 368 | + } | |
| 369 | + this.setup(elem); | |
| 370 | + return elem; | |
| 371 | + } | |
| 372 | + }/*helpButtonlets*/; | |
| 373 | + | |
| 374 | + F.onDOMContentLoaded( ()=>F.helpButtonlets.setup() ); | |
| 375 | + | |
| 269 | 376 | })(window.fossil); |
| 270 | 377 |
| --- src/fossil.popupwidget.js | |
| +++ src/fossil.popupwidget.js | |
| @@ -264,6 +264,113 @@ | |
| 264 | error: function(/*...*/){ |
| 265 | return toastImpl('error',2,arguments); |
| 266 | } |
| 267 | }/*F.toast*/; |
| 268 | |
| 269 | })(window.fossil); |
| 270 |
| --- src/fossil.popupwidget.js | |
| +++ src/fossil.popupwidget.js | |
| @@ -264,6 +264,113 @@ | |
| 264 | error: function(/*...*/){ |
| 265 | return toastImpl('error',2,arguments); |
| 266 | } |
| 267 | }/*F.toast*/; |
| 268 | |
| 269 | |
| 270 | F.helpButtonlets = { |
| 271 | /** |
| 272 | Initializes one or more "help buttonlets". It may be passed any of: |
| 273 | |
| 274 | - A string: CSS selector (multiple matches are legal) |
| 275 | |
| 276 | - A single DOM element. |
| 277 | |
| 278 | - A forEach-compatible container of DOM elements. |
| 279 | |
| 280 | - No arguments, which is equivalent to passing the string |
| 281 | ".help-buttonlet:not(.processed)". |
| 282 | |
| 283 | Passing the same element(s) more than once is a no-op: during |
| 284 | initialization, each elements get the class'processed' added to |
| 285 | it, and any elements with that class are skipped. |
| 286 | |
| 287 | All child nodes of a help buttonlet are removed from the button |
| 288 | during initialization and stashed away for use in a PopupWidget |
| 289 | when the botton is clicked. |
| 290 | |
| 291 | */ |
| 292 | setup: function f(){ |
| 293 | if(!f.clickHandler){ |
| 294 | f.clickHandler = function fch(ev){ |
| 295 | if(!fch.popup){ |
| 296 | fch.popup = new F.PopupWidget({ |
| 297 | cssClass: ['fossil-tooltip', 'help-buttonlet-content'], |
| 298 | refresh: function(){ |
| 299 | } |
| 300 | }); |
| 301 | const hide = ()=>fch.popup.hide(); |
| 302 | fch.popup.e.addEventListener('click', hide, false); |
| 303 | document.body.addEventListener('click', hide, true); |
| 304 | document.body.addEventListener('keydown', function(ev){ |
| 305 | if(fch.popup.isShown() && 27===ev.which){ |
| 306 | fch.popup.hide(); |
| 307 | } |
| 308 | }, true); |
| 309 | } |
| 310 | D.append(D.clearElement(fch.popup.e), ev.target.$helpContent); |
| 311 | const rect1 = ev.target.getClientRects()[0]; |
| 312 | var x = rect1.left, y = rect1.top; |
| 313 | if(x<0) x = 0; |
| 314 | if(y<0) y = 0; |
| 315 | /* shift help to the left 1/2 the width of fch.popup.e. However, |
| 316 | fch.popup.e.getClientRects() is empty until the popup is shown, |
| 317 | so we have to show it, calculate that size, then move it. */ |
| 318 | fch.popup.show(x, y); |
| 319 | const rect2 = fch.popup.e.getClientRects()[0]; |
| 320 | x -= rect2.width/2; |
| 321 | if(x<0) x = 0; |
| 322 | fch.popup.show(x, y); |
| 323 | }; |
| 324 | } |
| 325 | var elems; |
| 326 | if(!arguments.length){ |
| 327 | arguments[0] = '.help-buttonlet:not(.processed)'; |
| 328 | arguments.length = 1; |
| 329 | } |
| 330 | if(arguments.length){ |
| 331 | if('string'===typeof arguments[0]){ |
| 332 | elems = document.querySelectorAll(arguments[0]); |
| 333 | }else if(arguments[0] instanceof HTMLElement){ |
| 334 | elems = [arguments[0]]; |
| 335 | }else{/* assume DOM element list or array */ |
| 336 | elems = arguments[0]; |
| 337 | } |
| 338 | } |
| 339 | if(!elems) return; |
| 340 | elems.forEach(function(e){ |
| 341 | if(e.classList.contains('processed')) return; |
| 342 | e.classList.add('processed'); |
| 343 | e.$helpContent = []; |
| 344 | /* We have to move all child nodes out of the way because we |
| 345 | cannot hide TEXT nodes via CSS (which cannot select TEXT |
| 346 | nodes). We have to do it in two steps to avoid invaliding |
| 347 | the list during traversal. */ |
| 348 | e.childNodes.forEach((ch)=>e.$helpContent.push(ch)); |
| 349 | e.$helpContent.forEach((ch)=>ch.remove()); |
| 350 | e.addEventListener('click', f.clickHandler, false); |
| 351 | }); |
| 352 | }, |
| 353 | |
| 354 | /** |
| 355 | Sets up the given element as a "help buttonlet", adding the CSS |
| 356 | class help-buttonlet to it. Any (optional) arguments after the |
| 357 | first are appended to the element using fossil.dom.append(), so |
| 358 | that they become the content for the buttonlet's popup help. |
| 359 | |
| 360 | The element is then passed to this.setup() before it |
| 361 | is returned from this function. |
| 362 | */ |
| 363 | create: function(elem/*...body*/){ |
| 364 | D.addClass(elem, 'help-buttonlet'); |
| 365 | if(arguments.length>1){ |
| 366 | const args = Array.prototype.slice.call(arguments,1); |
| 367 | D.append(elem, args); |
| 368 | } |
| 369 | this.setup(elem); |
| 370 | return elem; |
| 371 | } |
| 372 | }/*helpButtonlets*/; |
| 373 | |
| 374 | F.onDOMContentLoaded( ()=>F.helpButtonlets.setup() ); |
| 375 | |
| 376 | })(window.fossil); |
| 377 |
+4
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -2118,12 +2118,16 @@ | ||
| 2118 | 2118 | cgi_printf("%z", htmlize(z, nZ)); |
| 2119 | 2119 | CX("</code></pre></td></tr></tbody></table>\n"); |
| 2120 | 2120 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2121 | 2121 | builtin_request_js("scroll.js"); |
| 2122 | 2122 | } |
| 2123 | +#if 0 | |
| 2123 | 2124 | style_emit_fossil_js_apis(0, "dom", "copybutton", "popupwidget", |
| 2124 | 2125 | "numbered-lines", 0); |
| 2126 | +#else | |
| 2127 | + style_emit_all_fossil_js_apis(); | |
| 2128 | +#endif | |
| 2125 | 2129 | } |
| 2126 | 2130 | |
| 2127 | 2131 | /* |
| 2128 | 2132 | ** COMMAND: test-line-numbers |
| 2129 | 2133 | ** |
| 2130 | 2134 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2118,12 +2118,16 @@ | |
| 2118 | cgi_printf("%z", htmlize(z, nZ)); |
| 2119 | CX("</code></pre></td></tr></tbody></table>\n"); |
| 2120 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2121 | builtin_request_js("scroll.js"); |
| 2122 | } |
| 2123 | style_emit_fossil_js_apis(0, "dom", "copybutton", "popupwidget", |
| 2124 | "numbered-lines", 0); |
| 2125 | } |
| 2126 | |
| 2127 | /* |
| 2128 | ** COMMAND: test-line-numbers |
| 2129 | ** |
| 2130 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2118,12 +2118,16 @@ | |
| 2118 | cgi_printf("%z", htmlize(z, nZ)); |
| 2119 | CX("</code></pre></td></tr></tbody></table>\n"); |
| 2120 | if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ |
| 2121 | builtin_request_js("scroll.js"); |
| 2122 | } |
| 2123 | #if 0 |
| 2124 | style_emit_fossil_js_apis(0, "dom", "copybutton", "popupwidget", |
| 2125 | "numbered-lines", 0); |
| 2126 | #else |
| 2127 | style_emit_all_fossil_js_apis(); |
| 2128 | #endif |
| 2129 | } |
| 2130 | |
| 2131 | /* |
| 2132 | ** COMMAND: test-line-numbers |
| 2133 | ** |
| 2134 |
+25
-10
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -1230,16 +1230,16 @@ | ||
| 1230 | 1230 | ** element. If isChecked is true, the checkbox gets the "checked" |
| 1231 | 1231 | ** attribute set, else it is not. |
| 1232 | 1232 | ** |
| 1233 | 1233 | ** Resulting structure: |
| 1234 | 1234 | ** |
| 1235 | -** <span class='input-with-label' title={{zTip}} id={{zWrapperId}}> | |
| 1235 | +** <div class='input-with-label' title={{zTip}} id={{zWrapperId}}> | |
| 1236 | 1236 | ** <input type='checkbox' name={{zFieldName}} value={{zValue}} |
| 1237 | 1237 | ** id='A RANDOM VALUE' |
| 1238 | 1238 | ** {{isChecked ? " checked : ""}}/> |
| 1239 | 1239 | ** <label for='ID OF THE INPUT FIELD'>{{zLabel}}</label> |
| 1240 | -** </span> | |
| 1240 | +** </div> | |
| 1241 | 1241 | ** |
| 1242 | 1242 | ** zLabel, and zValue are required. zFieldName, zWrapperId, and zTip |
| 1243 | 1243 | ** are may be NULL or empty. |
| 1244 | 1244 | ** |
| 1245 | 1245 | ** Be sure that the input-with-label CSS class is defined sensibly, in |
| @@ -1249,11 +1249,11 @@ | ||
| 1249 | 1249 | void style_labeled_checkbox(const char * zWrapperId, |
| 1250 | 1250 | const char *zFieldName, const char * zLabel, |
| 1251 | 1251 | const char * zValue, int isChecked, |
| 1252 | 1252 | const char * zTip){ |
| 1253 | 1253 | char * zLabelID = style_next_input_id(); |
| 1254 | - CX("<span class='input-with-label'"); | |
| 1254 | + CX("<div class='input-with-label'"); | |
| 1255 | 1255 | if(zTip && *zTip){ |
| 1256 | 1256 | CX(" title='%h'", zTip); |
| 1257 | 1257 | } |
| 1258 | 1258 | if(zWrapperId && *zWrapperId){ |
| 1259 | 1259 | CX(" id='%s'",zWrapperId); |
| @@ -1262,11 +1262,11 @@ | ||
| 1262 | 1262 | if(zFieldName && *zFieldName){ |
| 1263 | 1263 | CX("name='%s' ",zFieldName); |
| 1264 | 1264 | } |
| 1265 | 1265 | CX("value='%T'%s/>", |
| 1266 | 1266 | zValue ? zValue : "", isChecked ? " checked" : ""); |
| 1267 | - CX("<label for='%s'>%h</label></span>", zLabelID, zLabel); | |
| 1267 | + CX("<label for='%s'>%h</label></div>", zLabelID, zLabel); | |
| 1268 | 1268 | fossil_free(zLabelID); |
| 1269 | 1269 | } |
| 1270 | 1270 | |
| 1271 | 1271 | /* |
| 1272 | 1272 | ** Outputs a SELECT list from a compile-time list of integers. |
| @@ -1296,14 +1296,14 @@ | ||
| 1296 | 1296 | ** |
| 1297 | 1297 | ** zTooltip is an optional value for the SELECT's title attribute. |
| 1298 | 1298 | ** |
| 1299 | 1299 | ** The structure of the emitted HTML is: |
| 1300 | 1300 | ** |
| 1301 | -** <span class='input-with-label' title={{zToolTip}} id={{zWrapperId}}> | |
| 1301 | +** <div class='input-with-label' title={{zToolTip}} id={{zWrapperId}}> | |
| 1302 | 1302 | ** <label for='SELECT ELEMENT ID'>{{zLabel}}</label> |
| 1303 | 1303 | ** <select id='RANDOM ID' name={{zFieldName}}>...</select> |
| 1304 | -** </span> | |
| 1304 | +** </div> | |
| 1305 | 1305 | ** |
| 1306 | 1306 | ** Example: |
| 1307 | 1307 | ** |
| 1308 | 1308 | ** style_select_list_int("my-grapes", "my_grapes", "Grapes", |
| 1309 | 1309 | ** "Select the number of grapes", |
| @@ -1318,11 +1318,11 @@ | ||
| 1318 | 1318 | ... ){ |
| 1319 | 1319 | char * zLabelID = style_next_input_id(); |
| 1320 | 1320 | va_list vargs; |
| 1321 | 1321 | |
| 1322 | 1322 | va_start(vargs,selectedVal); |
| 1323 | - CX("<span class='input-with-label'"); | |
| 1323 | + CX("<div class='input-with-label'"); | |
| 1324 | 1324 | if(zToolTip && *zToolTip){ |
| 1325 | 1325 | CX(" title='%h'",zToolTip); |
| 1326 | 1326 | } |
| 1327 | 1327 | if(zWrapperId && *zWrapperId){ |
| 1328 | 1328 | CX(" id='%s'",zWrapperId); |
| @@ -1347,11 +1347,11 @@ | ||
| 1347 | 1347 | CX("%d",v); |
| 1348 | 1348 | } |
| 1349 | 1349 | CX("</option>\n"); |
| 1350 | 1350 | } |
| 1351 | 1351 | CX("</select>\n"); |
| 1352 | - CX("</span>\n"); | |
| 1352 | + CX("</div>\n"); | |
| 1353 | 1353 | va_end(vargs); |
| 1354 | 1354 | fossil_free(zLabelID); |
| 1355 | 1355 | } |
| 1356 | 1356 | |
| 1357 | 1357 | /* |
| @@ -1382,11 +1382,11 @@ | ||
| 1382 | 1382 | |
| 1383 | 1383 | va_start(vargs,zSelectedVal); |
| 1384 | 1384 | if(!zSelectedVal){ |
| 1385 | 1385 | zSelectedVal = __FILE__/*some string we'll never match*/; |
| 1386 | 1386 | } |
| 1387 | - CX("<span class='input-with-label'"); | |
| 1387 | + CX("<div class='input-with-label'"); | |
| 1388 | 1388 | if(zToolTip && *zToolTip){ |
| 1389 | 1389 | CX(" title='%h'",zToolTip); |
| 1390 | 1390 | } |
| 1391 | 1391 | if(zWrapperId && *zWrapperId){ |
| 1392 | 1392 | CX(" id='%s'",zWrapperId); |
| @@ -1411,11 +1411,11 @@ | ||
| 1411 | 1411 | CX("%h",zVal); |
| 1412 | 1412 | } |
| 1413 | 1413 | CX("</option>\n"); |
| 1414 | 1414 | } |
| 1415 | 1415 | CX("</select>\n"); |
| 1416 | - CX("</span>\n"); | |
| 1416 | + CX("</div>\n"); | |
| 1417 | 1417 | va_end(vargs); |
| 1418 | 1418 | fossil_free(zLabelID); |
| 1419 | 1419 | } |
| 1420 | 1420 | |
| 1421 | 1421 | |
| @@ -1560,5 +1560,20 @@ | ||
| 1560 | 1560 | builtin_request_js(zName); |
| 1561 | 1561 | fossil_free(zName); |
| 1562 | 1562 | } |
| 1563 | 1563 | va_end(vargs); |
| 1564 | 1564 | } |
| 1565 | + | |
| 1566 | +/* | |
| 1567 | +** Emits, via builtin_request_js(), all JS fossil.XYZ APIs which are | |
| 1568 | +** not strictly specific to a single page. The idea is that we can get | |
| 1569 | +** better bundle caching and reduced HTTP requests by including all | |
| 1570 | +** JS, rather than creating separate bundles on a per-page basis. | |
| 1571 | +*/ | |
| 1572 | +void style_emit_all_fossil_js_apis(void){ | |
| 1573 | + style_emit_fossil_js_apis(0, | |
| 1574 | + "dom", "fetch", | |
| 1575 | + "storage", "tabs", | |
| 1576 | + "confirmer", "popupwidget", | |
| 1577 | + "copybutton", "numbered-lines", | |
| 1578 | + 0); | |
| 1579 | +} | |
| 1565 | 1580 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1230,16 +1230,16 @@ | |
| 1230 | ** element. If isChecked is true, the checkbox gets the "checked" |
| 1231 | ** attribute set, else it is not. |
| 1232 | ** |
| 1233 | ** Resulting structure: |
| 1234 | ** |
| 1235 | ** <span class='input-with-label' title={{zTip}} id={{zWrapperId}}> |
| 1236 | ** <input type='checkbox' name={{zFieldName}} value={{zValue}} |
| 1237 | ** id='A RANDOM VALUE' |
| 1238 | ** {{isChecked ? " checked : ""}}/> |
| 1239 | ** <label for='ID OF THE INPUT FIELD'>{{zLabel}}</label> |
| 1240 | ** </span> |
| 1241 | ** |
| 1242 | ** zLabel, and zValue are required. zFieldName, zWrapperId, and zTip |
| 1243 | ** are may be NULL or empty. |
| 1244 | ** |
| 1245 | ** Be sure that the input-with-label CSS class is defined sensibly, in |
| @@ -1249,11 +1249,11 @@ | |
| 1249 | void style_labeled_checkbox(const char * zWrapperId, |
| 1250 | const char *zFieldName, const char * zLabel, |
| 1251 | const char * zValue, int isChecked, |
| 1252 | const char * zTip){ |
| 1253 | char * zLabelID = style_next_input_id(); |
| 1254 | CX("<span class='input-with-label'"); |
| 1255 | if(zTip && *zTip){ |
| 1256 | CX(" title='%h'", zTip); |
| 1257 | } |
| 1258 | if(zWrapperId && *zWrapperId){ |
| 1259 | CX(" id='%s'",zWrapperId); |
| @@ -1262,11 +1262,11 @@ | |
| 1262 | if(zFieldName && *zFieldName){ |
| 1263 | CX("name='%s' ",zFieldName); |
| 1264 | } |
| 1265 | CX("value='%T'%s/>", |
| 1266 | zValue ? zValue : "", isChecked ? " checked" : ""); |
| 1267 | CX("<label for='%s'>%h</label></span>", zLabelID, zLabel); |
| 1268 | fossil_free(zLabelID); |
| 1269 | } |
| 1270 | |
| 1271 | /* |
| 1272 | ** Outputs a SELECT list from a compile-time list of integers. |
| @@ -1296,14 +1296,14 @@ | |
| 1296 | ** |
| 1297 | ** zTooltip is an optional value for the SELECT's title attribute. |
| 1298 | ** |
| 1299 | ** The structure of the emitted HTML is: |
| 1300 | ** |
| 1301 | ** <span class='input-with-label' title={{zToolTip}} id={{zWrapperId}}> |
| 1302 | ** <label for='SELECT ELEMENT ID'>{{zLabel}}</label> |
| 1303 | ** <select id='RANDOM ID' name={{zFieldName}}>...</select> |
| 1304 | ** </span> |
| 1305 | ** |
| 1306 | ** Example: |
| 1307 | ** |
| 1308 | ** style_select_list_int("my-grapes", "my_grapes", "Grapes", |
| 1309 | ** "Select the number of grapes", |
| @@ -1318,11 +1318,11 @@ | |
| 1318 | ... ){ |
| 1319 | char * zLabelID = style_next_input_id(); |
| 1320 | va_list vargs; |
| 1321 | |
| 1322 | va_start(vargs,selectedVal); |
| 1323 | CX("<span class='input-with-label'"); |
| 1324 | if(zToolTip && *zToolTip){ |
| 1325 | CX(" title='%h'",zToolTip); |
| 1326 | } |
| 1327 | if(zWrapperId && *zWrapperId){ |
| 1328 | CX(" id='%s'",zWrapperId); |
| @@ -1347,11 +1347,11 @@ | |
| 1347 | CX("%d",v); |
| 1348 | } |
| 1349 | CX("</option>\n"); |
| 1350 | } |
| 1351 | CX("</select>\n"); |
| 1352 | CX("</span>\n"); |
| 1353 | va_end(vargs); |
| 1354 | fossil_free(zLabelID); |
| 1355 | } |
| 1356 | |
| 1357 | /* |
| @@ -1382,11 +1382,11 @@ | |
| 1382 | |
| 1383 | va_start(vargs,zSelectedVal); |
| 1384 | if(!zSelectedVal){ |
| 1385 | zSelectedVal = __FILE__/*some string we'll never match*/; |
| 1386 | } |
| 1387 | CX("<span class='input-with-label'"); |
| 1388 | if(zToolTip && *zToolTip){ |
| 1389 | CX(" title='%h'",zToolTip); |
| 1390 | } |
| 1391 | if(zWrapperId && *zWrapperId){ |
| 1392 | CX(" id='%s'",zWrapperId); |
| @@ -1411,11 +1411,11 @@ | |
| 1411 | CX("%h",zVal); |
| 1412 | } |
| 1413 | CX("</option>\n"); |
| 1414 | } |
| 1415 | CX("</select>\n"); |
| 1416 | CX("</span>\n"); |
| 1417 | va_end(vargs); |
| 1418 | fossil_free(zLabelID); |
| 1419 | } |
| 1420 | |
| 1421 | |
| @@ -1560,5 +1560,20 @@ | |
| 1560 | builtin_request_js(zName); |
| 1561 | fossil_free(zName); |
| 1562 | } |
| 1563 | va_end(vargs); |
| 1564 | } |
| 1565 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1230,16 +1230,16 @@ | |
| 1230 | ** element. If isChecked is true, the checkbox gets the "checked" |
| 1231 | ** attribute set, else it is not. |
| 1232 | ** |
| 1233 | ** Resulting structure: |
| 1234 | ** |
| 1235 | ** <div class='input-with-label' title={{zTip}} id={{zWrapperId}}> |
| 1236 | ** <input type='checkbox' name={{zFieldName}} value={{zValue}} |
| 1237 | ** id='A RANDOM VALUE' |
| 1238 | ** {{isChecked ? " checked : ""}}/> |
| 1239 | ** <label for='ID OF THE INPUT FIELD'>{{zLabel}}</label> |
| 1240 | ** </div> |
| 1241 | ** |
| 1242 | ** zLabel, and zValue are required. zFieldName, zWrapperId, and zTip |
| 1243 | ** are may be NULL or empty. |
| 1244 | ** |
| 1245 | ** Be sure that the input-with-label CSS class is defined sensibly, in |
| @@ -1249,11 +1249,11 @@ | |
| 1249 | void style_labeled_checkbox(const char * zWrapperId, |
| 1250 | const char *zFieldName, const char * zLabel, |
| 1251 | const char * zValue, int isChecked, |
| 1252 | const char * zTip){ |
| 1253 | char * zLabelID = style_next_input_id(); |
| 1254 | CX("<div class='input-with-label'"); |
| 1255 | if(zTip && *zTip){ |
| 1256 | CX(" title='%h'", zTip); |
| 1257 | } |
| 1258 | if(zWrapperId && *zWrapperId){ |
| 1259 | CX(" id='%s'",zWrapperId); |
| @@ -1262,11 +1262,11 @@ | |
| 1262 | if(zFieldName && *zFieldName){ |
| 1263 | CX("name='%s' ",zFieldName); |
| 1264 | } |
| 1265 | CX("value='%T'%s/>", |
| 1266 | zValue ? zValue : "", isChecked ? " checked" : ""); |
| 1267 | CX("<label for='%s'>%h</label></div>", zLabelID, zLabel); |
| 1268 | fossil_free(zLabelID); |
| 1269 | } |
| 1270 | |
| 1271 | /* |
| 1272 | ** Outputs a SELECT list from a compile-time list of integers. |
| @@ -1296,14 +1296,14 @@ | |
| 1296 | ** |
| 1297 | ** zTooltip is an optional value for the SELECT's title attribute. |
| 1298 | ** |
| 1299 | ** The structure of the emitted HTML is: |
| 1300 | ** |
| 1301 | ** <div class='input-with-label' title={{zToolTip}} id={{zWrapperId}}> |
| 1302 | ** <label for='SELECT ELEMENT ID'>{{zLabel}}</label> |
| 1303 | ** <select id='RANDOM ID' name={{zFieldName}}>...</select> |
| 1304 | ** </div> |
| 1305 | ** |
| 1306 | ** Example: |
| 1307 | ** |
| 1308 | ** style_select_list_int("my-grapes", "my_grapes", "Grapes", |
| 1309 | ** "Select the number of grapes", |
| @@ -1318,11 +1318,11 @@ | |
| 1318 | ... ){ |
| 1319 | char * zLabelID = style_next_input_id(); |
| 1320 | va_list vargs; |
| 1321 | |
| 1322 | va_start(vargs,selectedVal); |
| 1323 | CX("<div class='input-with-label'"); |
| 1324 | if(zToolTip && *zToolTip){ |
| 1325 | CX(" title='%h'",zToolTip); |
| 1326 | } |
| 1327 | if(zWrapperId && *zWrapperId){ |
| 1328 | CX(" id='%s'",zWrapperId); |
| @@ -1347,11 +1347,11 @@ | |
| 1347 | CX("%d",v); |
| 1348 | } |
| 1349 | CX("</option>\n"); |
| 1350 | } |
| 1351 | CX("</select>\n"); |
| 1352 | CX("</div>\n"); |
| 1353 | va_end(vargs); |
| 1354 | fossil_free(zLabelID); |
| 1355 | } |
| 1356 | |
| 1357 | /* |
| @@ -1382,11 +1382,11 @@ | |
| 1382 | |
| 1383 | va_start(vargs,zSelectedVal); |
| 1384 | if(!zSelectedVal){ |
| 1385 | zSelectedVal = __FILE__/*some string we'll never match*/; |
| 1386 | } |
| 1387 | CX("<div class='input-with-label'"); |
| 1388 | if(zToolTip && *zToolTip){ |
| 1389 | CX(" title='%h'",zToolTip); |
| 1390 | } |
| 1391 | if(zWrapperId && *zWrapperId){ |
| 1392 | CX(" id='%s'",zWrapperId); |
| @@ -1411,11 +1411,11 @@ | |
| 1411 | CX("%h",zVal); |
| 1412 | } |
| 1413 | CX("</option>\n"); |
| 1414 | } |
| 1415 | CX("</select>\n"); |
| 1416 | CX("</div>\n"); |
| 1417 | va_end(vargs); |
| 1418 | fossil_free(zLabelID); |
| 1419 | } |
| 1420 | |
| 1421 | |
| @@ -1560,5 +1560,20 @@ | |
| 1560 | builtin_request_js(zName); |
| 1561 | fossil_free(zName); |
| 1562 | } |
| 1563 | va_end(vargs); |
| 1564 | } |
| 1565 | |
| 1566 | /* |
| 1567 | ** Emits, via builtin_request_js(), all JS fossil.XYZ APIs which are |
| 1568 | ** not strictly specific to a single page. The idea is that we can get |
| 1569 | ** better bundle caching and reduced HTTP requests by including all |
| 1570 | ** JS, rather than creating separate bundles on a per-page basis. |
| 1571 | */ |
| 1572 | void style_emit_all_fossil_js_apis(void){ |
| 1573 | style_emit_fossil_js_apis(0, |
| 1574 | "dom", "fetch", |
| 1575 | "storage", "tabs", |
| 1576 | "confirmer", "popupwidget", |
| 1577 | "copybutton", "numbered-lines", |
| 1578 | 0); |
| 1579 | } |
| 1580 |
+8
-1
| --- src/style.wikiedit.css | ||
| +++ src/style.wikiedit.css | ||
| @@ -184,11 +184,18 @@ | ||
| 184 | 184 | flex-direction: row; |
| 185 | 185 | flex-wrap: wrap; |
| 186 | 186 | align-items: baseline; |
| 187 | 187 | } |
| 188 | 188 | body.wikiedit #wikiedit-stash-selector select { |
| 189 | - margin: 0 1em; | |
| 189 | + margin: 0 1em 0 0.5em; | |
| 190 | 190 | height: initial; |
| 191 | 191 | font-family: monospace; |
| 192 | 192 | flex: 10 1 auto; |
| 193 | 193 | } |
| 194 | 194 | |
| 195 | + | |
| 196 | +body.wikiedit fieldset.page-types-list > div > span { | |
| 197 | + display: flex; | |
| 198 | + flex-direction: row; | |
| 199 | + flex-wrap: nowrap; | |
| 200 | + align-items: center; | |
| 201 | +} | |
| 195 | 202 |
| --- src/style.wikiedit.css | |
| +++ src/style.wikiedit.css | |
| @@ -184,11 +184,18 @@ | |
| 184 | flex-direction: row; |
| 185 | flex-wrap: wrap; |
| 186 | align-items: baseline; |
| 187 | } |
| 188 | body.wikiedit #wikiedit-stash-selector select { |
| 189 | margin: 0 1em; |
| 190 | height: initial; |
| 191 | font-family: monospace; |
| 192 | flex: 10 1 auto; |
| 193 | } |
| 194 | |
| 195 |
| --- src/style.wikiedit.css | |
| +++ src/style.wikiedit.css | |
| @@ -184,11 +184,18 @@ | |
| 184 | flex-direction: row; |
| 185 | flex-wrap: wrap; |
| 186 | align-items: baseline; |
| 187 | } |
| 188 | body.wikiedit #wikiedit-stash-selector select { |
| 189 | margin: 0 1em 0 0.5em; |
| 190 | height: initial; |
| 191 | font-family: monospace; |
| 192 | flex: 10 1 auto; |
| 193 | } |
| 194 | |
| 195 | |
| 196 | body.wikiedit fieldset.page-types-list > div > span { |
| 197 | display: flex; |
| 198 | flex-direction: row; |
| 199 | flex-wrap: nowrap; |
| 200 | align-items: center; |
| 201 | } |
| 202 |
+37
-16
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -1162,33 +1162,45 @@ | ||
| 1162 | 1162 | "data-tab-parent='wikiedit-tabs' " |
| 1163 | 1163 | "data-tab-label='Editor' " |
| 1164 | 1164 | "class='hidden'" |
| 1165 | 1165 | ">"); |
| 1166 | 1166 | CX("<div class='flex-container flex-row child-gap-small'>"); |
| 1167 | - CX("<span class='input-with-label'>" | |
| 1167 | + CX("<div class='input-with-label'>" | |
| 1168 | 1168 | "<label>Mime type</label>"); |
| 1169 | 1169 | mimetype_option_menu(0); |
| 1170 | - CX("</span>"); | |
| 1170 | + CX("</div>"); | |
| 1171 | 1171 | style_select_list_int("select-font-size", |
| 1172 | 1172 | "editor_font_size", "Editor font size", |
| 1173 | 1173 | NULL/*tooltip*/, |
| 1174 | 1174 | 100, |
| 1175 | 1175 | "100%", 100, "125%", 125, |
| 1176 | 1176 | "150%", 150, "175%", 175, |
| 1177 | 1177 | "200%", 200, NULL); |
| 1178 | - CX("<button class='wikiedit-save'>" | |
| 1178 | + CX("<div class='input-with-label'>" | |
| 1179 | + "<button class='wikiedit-save'>" | |
| 1179 | 1180 | "Save</button>" |
| 1180 | - /*will get moved around dynamically*/); | |
| 1181 | - CX("<button class='wikiedit-save-close'>" | |
| 1182 | - "Save & Close</button>"/*will get moved around dynamically*/); | |
| 1181 | + "</div>" /*will get moved around dynamically*/); | |
| 1182 | + CX("<div class='input-with-label'>" | |
| 1183 | + "<button class='wikiedit-save-close'>" | |
| 1184 | + "Save & Close</button>" | |
| 1185 | + "<div class='help-buttonlet'>" | |
| 1186 | + "Saves edits to this page and returns to the wiki page viewer." | |
| 1187 | + "</div>" | |
| 1188 | + "</div>" /*will get moved around dynamically*/); | |
| 1183 | 1189 | CX("<span class='save-button-slot'></span>"); |
| 1184 | - CX("<button class='wikiedit-content-reload' " | |
| 1185 | - "title='Reload the file from the server, discarding " | |
| 1190 | + | |
| 1191 | + CX("<div class='input-with-label'>" | |
| 1192 | + "<button class='wikiedit-content-reload' " | |
| 1193 | + ">Discard & Reload</button>" | |
| 1194 | + "<div class='help-buttonlet'>" | |
| 1195 | + "Reload the file from the server, discarding " | |
| 1186 | 1196 | "any local edits. To help avoid accidental loss of " |
| 1187 | 1197 | "edits, it requires confirmation (a second click) within " |
| 1188 | - "a few seconds or it will not reload.'" | |
| 1189 | - ">Discard & Reload</button>"); | |
| 1198 | + "a few seconds or it will not reload." | |
| 1199 | + "</div>" | |
| 1200 | + "</div>"); | |
| 1201 | + | |
| 1190 | 1202 | CX("</div>"); |
| 1191 | 1203 | CX("<div class='flex-container flex-column stretch'>"); |
| 1192 | 1204 | CX("<textarea name='content' id='wikiedit-content-editor' " |
| 1193 | 1205 | "class='wikiedit' rows='25'>"); |
| 1194 | 1206 | CX("</textarea>"); |
| @@ -1213,16 +1225,19 @@ | ||
| 1213 | 1225 | /* ^^^ fossil.page[methodName](content, callback) */ |
| 1214 | 1226 | "data-f-preview-to='#wikiedit-tab-preview-wrapper' " |
| 1215 | 1227 | /* ^^^ dest elem ID */ |
| 1216 | 1228 | ">Refresh</button>"); |
| 1217 | 1229 | /* Toggle auto-update of preview when the Preview tab is selected. */ |
| 1218 | - style_labeled_checkbox("cb-preview-autoupdate", | |
| 1219 | - NULL, | |
| 1220 | - "Auto-refresh?", | |
| 1221 | - "1", 1, | |
| 1222 | - "If on, the preview will automatically " | |
| 1223 | - "refresh when this tab is selected."); | |
| 1230 | + CX("<div class='input-with-label'>" | |
| 1231 | + "<input type='checkbox' value='1' " | |
| 1232 | + "id='cb-preview-autorefresh' checked=''>" | |
| 1233 | + "<label for='cb-preview-autorefresh'>Auto-refresh?</label>" | |
| 1234 | + "<div class='help-buttonlet'>" | |
| 1235 | + "If on, the preview will automatically " | |
| 1236 | + "refresh when this tab is selected." | |
| 1237 | + "</div>" | |
| 1238 | + "</div>"); | |
| 1224 | 1239 | CX("<span class='save-button-slot'></span>"); |
| 1225 | 1240 | CX("</div>"/*.wikiedit-options*/); |
| 1226 | 1241 | CX("<div id='wikiedit-tab-preview-wrapper'></div>"); |
| 1227 | 1242 | CX("</div>"/*#wikiedit-tab-preview*/); |
| 1228 | 1243 | } |
| @@ -1271,12 +1286,18 @@ | ||
| 1271 | 1286 | well_formed_wiki_name_rules(); |
| 1272 | 1287 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1273 | 1288 | } |
| 1274 | 1289 | |
| 1275 | 1290 | builtin_request_js("sbsdiff.js"); |
| 1291 | +#if 0 | |
| 1276 | 1292 | style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer", |
| 1277 | 1293 | "storage", "page.wikiedit", 0); |
| 1294 | +#else | |
| 1295 | + style_emit_all_fossil_js_apis(); | |
| 1296 | + builtin_fulfill_js_requests(); | |
| 1297 | + builtin_request_js("fossil.page.wikiedit.js"); | |
| 1298 | +#endif | |
| 1278 | 1299 | builtin_fulfill_js_requests(); |
| 1279 | 1300 | /* Dynamically populate the editor... */ |
| 1280 | 1301 | style_emit_script_tag(0,0); |
| 1281 | 1302 | { |
| 1282 | 1303 | /* Render the current page list to save us an XHR request |
| 1283 | 1304 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -1162,33 +1162,45 @@ | |
| 1162 | "data-tab-parent='wikiedit-tabs' " |
| 1163 | "data-tab-label='Editor' " |
| 1164 | "class='hidden'" |
| 1165 | ">"); |
| 1166 | CX("<div class='flex-container flex-row child-gap-small'>"); |
| 1167 | CX("<span class='input-with-label'>" |
| 1168 | "<label>Mime type</label>"); |
| 1169 | mimetype_option_menu(0); |
| 1170 | CX("</span>"); |
| 1171 | style_select_list_int("select-font-size", |
| 1172 | "editor_font_size", "Editor font size", |
| 1173 | NULL/*tooltip*/, |
| 1174 | 100, |
| 1175 | "100%", 100, "125%", 125, |
| 1176 | "150%", 150, "175%", 175, |
| 1177 | "200%", 200, NULL); |
| 1178 | CX("<button class='wikiedit-save'>" |
| 1179 | "Save</button>" |
| 1180 | /*will get moved around dynamically*/); |
| 1181 | CX("<button class='wikiedit-save-close'>" |
| 1182 | "Save & Close</button>"/*will get moved around dynamically*/); |
| 1183 | CX("<span class='save-button-slot'></span>"); |
| 1184 | CX("<button class='wikiedit-content-reload' " |
| 1185 | "title='Reload the file from the server, discarding " |
| 1186 | "any local edits. To help avoid accidental loss of " |
| 1187 | "edits, it requires confirmation (a second click) within " |
| 1188 | "a few seconds or it will not reload.'" |
| 1189 | ">Discard & Reload</button>"); |
| 1190 | CX("</div>"); |
| 1191 | CX("<div class='flex-container flex-column stretch'>"); |
| 1192 | CX("<textarea name='content' id='wikiedit-content-editor' " |
| 1193 | "class='wikiedit' rows='25'>"); |
| 1194 | CX("</textarea>"); |
| @@ -1213,16 +1225,19 @@ | |
| 1213 | /* ^^^ fossil.page[methodName](content, callback) */ |
| 1214 | "data-f-preview-to='#wikiedit-tab-preview-wrapper' " |
| 1215 | /* ^^^ dest elem ID */ |
| 1216 | ">Refresh</button>"); |
| 1217 | /* Toggle auto-update of preview when the Preview tab is selected. */ |
| 1218 | style_labeled_checkbox("cb-preview-autoupdate", |
| 1219 | NULL, |
| 1220 | "Auto-refresh?", |
| 1221 | "1", 1, |
| 1222 | "If on, the preview will automatically " |
| 1223 | "refresh when this tab is selected."); |
| 1224 | CX("<span class='save-button-slot'></span>"); |
| 1225 | CX("</div>"/*.wikiedit-options*/); |
| 1226 | CX("<div id='wikiedit-tab-preview-wrapper'></div>"); |
| 1227 | CX("</div>"/*#wikiedit-tab-preview*/); |
| 1228 | } |
| @@ -1271,12 +1286,18 @@ | |
| 1271 | well_formed_wiki_name_rules(); |
| 1272 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1273 | } |
| 1274 | |
| 1275 | builtin_request_js("sbsdiff.js"); |
| 1276 | style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer", |
| 1277 | "storage", "page.wikiedit", 0); |
| 1278 | builtin_fulfill_js_requests(); |
| 1279 | /* Dynamically populate the editor... */ |
| 1280 | style_emit_script_tag(0,0); |
| 1281 | { |
| 1282 | /* Render the current page list to save us an XHR request |
| 1283 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -1162,33 +1162,45 @@ | |
| 1162 | "data-tab-parent='wikiedit-tabs' " |
| 1163 | "data-tab-label='Editor' " |
| 1164 | "class='hidden'" |
| 1165 | ">"); |
| 1166 | CX("<div class='flex-container flex-row child-gap-small'>"); |
| 1167 | CX("<div class='input-with-label'>" |
| 1168 | "<label>Mime type</label>"); |
| 1169 | mimetype_option_menu(0); |
| 1170 | CX("</div>"); |
| 1171 | style_select_list_int("select-font-size", |
| 1172 | "editor_font_size", "Editor font size", |
| 1173 | NULL/*tooltip*/, |
| 1174 | 100, |
| 1175 | "100%", 100, "125%", 125, |
| 1176 | "150%", 150, "175%", 175, |
| 1177 | "200%", 200, NULL); |
| 1178 | CX("<div class='input-with-label'>" |
| 1179 | "<button class='wikiedit-save'>" |
| 1180 | "Save</button>" |
| 1181 | "</div>" /*will get moved around dynamically*/); |
| 1182 | CX("<div class='input-with-label'>" |
| 1183 | "<button class='wikiedit-save-close'>" |
| 1184 | "Save & Close</button>" |
| 1185 | "<div class='help-buttonlet'>" |
| 1186 | "Saves edits to this page and returns to the wiki page viewer." |
| 1187 | "</div>" |
| 1188 | "</div>" /*will get moved around dynamically*/); |
| 1189 | CX("<span class='save-button-slot'></span>"); |
| 1190 | |
| 1191 | CX("<div class='input-with-label'>" |
| 1192 | "<button class='wikiedit-content-reload' " |
| 1193 | ">Discard & Reload</button>" |
| 1194 | "<div class='help-buttonlet'>" |
| 1195 | "Reload the file from the server, discarding " |
| 1196 | "any local edits. To help avoid accidental loss of " |
| 1197 | "edits, it requires confirmation (a second click) within " |
| 1198 | "a few seconds or it will not reload." |
| 1199 | "</div>" |
| 1200 | "</div>"); |
| 1201 | |
| 1202 | CX("</div>"); |
| 1203 | CX("<div class='flex-container flex-column stretch'>"); |
| 1204 | CX("<textarea name='content' id='wikiedit-content-editor' " |
| 1205 | "class='wikiedit' rows='25'>"); |
| 1206 | CX("</textarea>"); |
| @@ -1213,16 +1225,19 @@ | |
| 1225 | /* ^^^ fossil.page[methodName](content, callback) */ |
| 1226 | "data-f-preview-to='#wikiedit-tab-preview-wrapper' " |
| 1227 | /* ^^^ dest elem ID */ |
| 1228 | ">Refresh</button>"); |
| 1229 | /* Toggle auto-update of preview when the Preview tab is selected. */ |
| 1230 | CX("<div class='input-with-label'>" |
| 1231 | "<input type='checkbox' value='1' " |
| 1232 | "id='cb-preview-autorefresh' checked=''>" |
| 1233 | "<label for='cb-preview-autorefresh'>Auto-refresh?</label>" |
| 1234 | "<div class='help-buttonlet'>" |
| 1235 | "If on, the preview will automatically " |
| 1236 | "refresh when this tab is selected." |
| 1237 | "</div>" |
| 1238 | "</div>"); |
| 1239 | CX("<span class='save-button-slot'></span>"); |
| 1240 | CX("</div>"/*.wikiedit-options*/); |
| 1241 | CX("<div id='wikiedit-tab-preview-wrapper'></div>"); |
| 1242 | CX("</div>"/*#wikiedit-tab-preview*/); |
| 1243 | } |
| @@ -1271,12 +1286,18 @@ | |
| 1286 | well_formed_wiki_name_rules(); |
| 1287 | CX("</div>"/*#wikiedit-tab-save*/); |
| 1288 | } |
| 1289 | |
| 1290 | builtin_request_js("sbsdiff.js"); |
| 1291 | #if 0 |
| 1292 | style_emit_fossil_js_apis(0, "fetch", "dom", "tabs", "confirmer", |
| 1293 | "storage", "page.wikiedit", 0); |
| 1294 | #else |
| 1295 | style_emit_all_fossil_js_apis(); |
| 1296 | builtin_fulfill_js_requests(); |
| 1297 | builtin_request_js("fossil.page.wikiedit.js"); |
| 1298 | #endif |
| 1299 | builtin_fulfill_js_requests(); |
| 1300 | /* Dynamically populate the editor... */ |
| 1301 | style_emit_script_tag(0,0); |
| 1302 | { |
| 1303 | /* Render the current page list to save us an XHR request |
| 1304 |