From fdd8321c967bf6cfa8849e3415227c1df7ae8cc7 Mon Sep 17 00:00:00 2001 From: "Thomas M. Edwards" Date: Sat, 20 Mar 2021 15:14:27 -0500 Subject: [PATCH 01/48] FIX + UPDATE: Fix `selected` keyword and added `once` keyword. * Fixed `selected` keyword for `<>` and `<>`. * Added `once` keyword to `<>`. --- src/macros/macrolib.js | 62 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/src/macros/macrolib.js b/src/macros/macrolib.js index a5df9ef..bfc0a66 100644 --- a/src/macros/macrolib.js +++ b/src/macros/macrolib.js @@ -1400,7 +1400,22 @@ return this.error('no options specified'); } - const autoselect = this.args.length > 1 && this.args[1] === 'autoselect'; + const config = { + autoselect : false, + once : false + }; + + // Process arguments. + for (let i = 1; i < this.args.length; ++i) { + const arg = this.args[i]; + + switch (arg) { + case 'once': config.once = true; break; + case 'autoselect': config.autoselect = true; break; + default: return this.error(`unknown argument: ${arg}`); + } + } + const options = []; const tagCount = { option : 0, optionsfrom : 0 }; let selectedIdx = -1; @@ -1417,13 +1432,37 @@ return this.error(`no arguments specified for <<${payload.name}>> (#${tagCount.option})`); } - options.push({ - label : String(payload.args[0]), - value : payload.args.length === 1 ? payload.args[0] : payload.args[1] - }); + const option = { label : String(payload.args[0]) }; + let isSelected = false; + + switch (payload.args.length) { + case 1: + option.value = payload.args[0]; + break; - if (payload.args.length > 2 && payload.args[2] === 'selected') { - if (autoselect) { + case 2: + if (payload.args[1] === 'selected') { + option.value = payload.args[0]; + isSelected = true; + } + else { + option.value = payload.args[1]; + } + break; + + default: + option.value = payload.args[1]; + + if (payload.args[2] === 'selected') { + isSelected = true; + } + break; + } + + options.push(option); + + if (isSelected) { + if (config.autoselect) { return this.error('cannot specify both the autoselect and selected keywords'); } else if (selectedIdx !== -1) { @@ -1483,7 +1522,7 @@ // No options were selected by the user, so we must select one. if (selectedIdx === -1) { // Attempt to automatically select an option by matching the variable's current value. - if (autoselect) { + if (config.autoselect) { // NOTE: This will usually fail for objects due to a variety of reasons. const sameValueZero = Util.sameValueZero; const curValue = State.getVar(varName); @@ -1505,9 +1544,14 @@ .attr('id', `${this.name}-${varId}`) .addClass(`macro-${this.name}`) .ariaClick({ namespace : '.macros' }, this.createShadowWrapper(function () { + const $this = $(this); cycleIdx = (cycleIdx + 1) % options.length; - $(this).empty().wikiWithOptions({ profile : 'core' }, options[cycleIdx].label); State.setVar(varName, options[cycleIdx].value); + $this.empty().wikiWithOptions({ profile : 'core' }, options[cycleIdx].label); + + if (config.once && (cycleIdx + 1) % options.length === selectedIdx) { + $this.off().contents().unwrap(); + } })) .appendTo(this.output); } From 11838cab535b166d40abb4235f495b012accf224 Mon Sep 17 00:00:00 2001 From: "Thomas M. Edwards" Date: Sat, 20 Mar 2021 15:17:41 -0500 Subject: [PATCH 02/48] UPDATE: Allow non-void widgets and new widget variables. * Allow non-void widgets via `block` keyword. * Add widget variables `_args` and `_contents`. * Deprecate widget variable `$args`. --- src/macros/macrolib.js | 75 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/src/macros/macrolib.js b/src/macros/macrolib.js index bfc0a66..1a83acc 100644 --- a/src/macros/macrolib.js +++ b/src/macros/macrolib.js @@ -3702,6 +3702,7 @@ } const widgetName = this.args[0]; + const isNonVoid = this.args.length > 1 && this.args[1] === 'block'; if (Macro.has(widgetName)) { if (!Macro.get(widgetName).isWidget) { @@ -3713,30 +3714,52 @@ } try { - Macro.add(widgetName, { + const widgetDef = { isWidget : true, - handler : (function (contents) { + handler : (function (widgetCode) { return function () { - let argsCache; + const shadowStore = {}; + // Cache the existing value of the `_args` variable, if necessary. + if (State.temporary.hasOwnProperty('args')) { + shadowStore._args = State.temporary.args; + } + + // Set up the widget `_args` variable and add a shadow. + State.temporary.args = [...this.args]; + State.temporary.args.raw = this.args.raw; + State.temporary.args.full = this.args.full; + this.addShadow('_args'); + + if (isNonVoid) { + // Cache the existing value of the `_contents` variable, if necessary. + if (State.temporary.hasOwnProperty('contents')) { + shadowStore._contents = State.temporary.contents; + } + + // Set up the widget `_contents` variable and add a shadow. + State.temporary.contents = this.payload[0].contents; + this.addShadow('_contents'); + } + + /* legacy */ // Cache the existing value of the `$args` variable, if necessary. if (State.variables.hasOwnProperty('args')) { - argsCache = State.variables.args; + shadowStore.$args = State.variables.args; } // Set up the widget `$args` variable and add a shadow. - State.variables.args = [...this.args]; - State.variables.args.raw = this.args.raw; - State.variables.args.full = this.args.full; + State.variables.args = State.temporary.args; this.addShadow('$args'); + /* /legacy */ try { // Set up the error trapping variables. const resFrag = document.createDocumentFragment(); const errList = []; - // Wikify the widget contents. - new Wikifier(resFrag, contents); + // Wikify the widget's code. + new Wikifier(resFrag, widgetCode); // Carry over the output, unless there were errors. Array.from(resFrag.querySelectorAll('.error')).forEach(errEl => { @@ -3747,24 +3770,50 @@ this.output.appendChild(resFrag); } else { - return this.error(`error${errList.length > 1 ? 's' : ''} within widget contents (${errList.join('; ')})`); + return this.error(`error${errList.length > 1 ? 's' : ''} within widget code (${errList.join('; ')})`); } } catch (ex) { return this.error(`cannot execute widget: ${ex.message}`); } finally { + // Revert the `_args` variable shadowing. + if (shadowStore.hasOwnProperty('_args')) { + State.temporary.args = shadowStore._args; + } + else { + delete State.temporary.args; + } + + if (isNonVoid) { + // Revert the `_contents` variable shadowing. + if (shadowStore.hasOwnProperty('_contents')) { + State.temporary.contents = shadowStore._contents; + } + else { + delete State.temporary.contents; + } + } + + /* legacy */ // Revert the `$args` variable shadowing. - if (typeof argsCache !== 'undefined') { - State.variables.args = argsCache; + if (shadowStore.hasOwnProperty('$args')) { + State.variables.args = shadowStore.$args; } else { delete State.variables.args; } + /* /legacy */ } }; })(this.payload[0].contents) - }); + }; + + if (isNonVoid) { + widgetDef.tags = []; + } + + Macro.add(widgetName, widgetDef); // Custom debug view setup. if (Config.debug) { From 4878a2e8677dbbb0287abe831f04352b4355d5f0 Mon Sep 17 00:00:00 2001 From: "Thomas M. Edwards" Date: Sat, 20 Mar 2021 17:17:47 -0500 Subject: [PATCH 03/48] UPDATE: Make `once` stop at last option rather than one complete loop. --- src/macros/macrolib.js | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/macros/macrolib.js b/src/macros/macrolib.js index 1a83acc..485044d 100644 --- a/src/macros/macrolib.js +++ b/src/macros/macrolib.js @@ -1538,22 +1538,30 @@ // Set up and append the appropriate element to the output buffer. if (this.name === 'cycle') { - let cycleIdx = selectedIdx; - jQuery(document.createElement('a')) - .wikiWithOptions({ profile : 'core' }, options[selectedIdx].label) - .attr('id', `${this.name}-${varId}`) - .addClass(`macro-${this.name}`) - .ariaClick({ namespace : '.macros' }, this.createShadowWrapper(function () { - const $this = $(this); - cycleIdx = (cycleIdx + 1) % options.length; - State.setVar(varName, options[cycleIdx].value); - $this.empty().wikiWithOptions({ profile : 'core' }, options[cycleIdx].label); + const lastIdx = options.length - 1; - if (config.once && (cycleIdx + 1) % options.length === selectedIdx) { - $this.off().contents().unwrap(); - } - })) - .appendTo(this.output); + if (config.once && selectedIdx === lastIdx) { + jQuery(this.output) + .wikiWithOptions({ profile : 'core' }, options[selectedIdx].label); + } + else { + let cycleIdx = selectedIdx; + jQuery(document.createElement('a')) + .wikiWithOptions({ profile : 'core' }, options[selectedIdx].label) + .attr('id', `${this.name}-${varId}`) + .addClass(`macro-${this.name}`) + .ariaClick({ namespace : '.macros' }, this.createShadowWrapper(function () { + const $this = $(this); + cycleIdx = (cycleIdx + 1) % options.length; + State.setVar(varName, options[cycleIdx].value); + $this.empty().wikiWithOptions({ profile : 'core' }, options[cycleIdx].label); + + if (config.once && cycleIdx === lastIdx) { + $this.off().contents().unwrap(); + } + })) + .appendTo(this.output); + } } else { // this.name === 'listbox' const $select = jQuery(document.createElement('select')); From 9a077142451c6bf4354f4d10e926b29c7442aac7 Mon Sep 17 00:00:00 2001 From: "Thomas M. Edwards" Date: Sat, 20 Mar 2021 20:17:22 -0500 Subject: [PATCH 04/48] UPDATE: `<>` docs. --- docs/core/macros.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/core/macros.md b/docs/core/macros.md index f7db219..ffac7fc 100644 --- a/docs/core/macros.md +++ b/docs/core/macros.md @@ -1093,7 +1093,7 @@ What pies do you enjoy? -### `<>`
`[<
`<
>` {#macros-macro-cycle} +### `<>`
`[<
`<
>` {#macros-macro-cycle} Creates a cycling link, used to modify the value of the variable with the given name. The cycling options are populated via `<