11'use strict' ;
22
3- // padToggle — emit parallel checkboxes in the User Settings (mySettings) and
4- // Pad Wide Settings (padSettings) panels, mirroring how native settings like
5- // stickychat / lineNumbers / disablechat work. The pad-wide value rides the
6- // existing padoptions COLLABROOM rail (the helper's value lives at
7- // pad.padOptions[pluginName] = {enabled: bool}), so broadcast, persistence,
8- // creator-only-write, and enforceSettings semantics all come for free —
9- // provided Etherpad core ships the ep_* passthrough patch (since 2.7.4).
3+ // padToggle (client side) — wires up the parallel User Settings / Pad Wide
4+ // Settings checkboxes that pad-toggle-server.js renders. Reads the helper's
5+ // clientVars block (capability flag + initial pad-wide value), persists the
6+ // per-user choice in padcookie, and forwards pad-wide changes through the
7+ // native pad.changePadOption() flow so they ride the existing padoptions
8+ // COLLABROOM broadcast.
109//
11- // On older cores ( no PluginCapabilities module), the pad-wide block is a
12- // no-op and the user-side cookie toggle still works. Plugins built on this
13- // helper continue to function everywhere; only the pad-wide column is gone .
10+ // This file deliberately has no top-level requires that touch server-only
11+ // modules — esbuild bundles it into the browser pad bundle, and any
12+ // node-only path would break the client build .
1413
1514const PLUGIN_NAME_RE = / ^ e p _ [ a - z 0 - 9 _ ] + $ / ;
1615
17- let padOptionsPluginPassthrough = false ;
18- try {
19- // Server-only. Wrapped because the Settings module pulls in node deps
20- // (fs, path) that don't exist in the esbuild-bundled client. The require
21- // also fails on Etherpad versions before the passthrough patch shipped.
22- // eslint-disable-next-line global-require
23- const caps = require ( 'ep_etherpad-lite/node/utils/PluginCapabilities' ) ;
24- padOptionsPluginPassthrough = caps && caps . padOptionsPluginPassthrough === true ;
25- } catch ( _e ) { /* older core or client bundle — leave as false */ }
26-
27- const padToggle = ( config ) => {
16+ const validateConfig = ( config ) => {
2817 if ( ! config || typeof config !== 'object' ) {
2918 throw new Error ( 'padToggle requires a config object' ) ;
3019 }
@@ -39,64 +28,19 @@ const padToggle = (config) => {
3928 if ( ! l10nId || typeof l10nId !== 'string' ) {
4029 throw new Error ( 'padToggle requires l10nId (string) — never hardcode a label' ) ;
4130 }
31+ return { pluginName, settingId, l10nId, defaultEnabled : ! ! defaultEnabled } ;
32+ } ;
4233
34+ const padToggleClient = ( rawConfig ) => {
35+ const { pluginName, settingId, defaultEnabled} = validateConfig ( rawConfig ) ;
4336 const userCheckboxId = `options-${ settingId } ` ;
4437 const padCheckboxId = `padsettings-options-${ settingId } ` ;
45- let cachedDefaultEnabled = ! ! defaultEnabled ;
46-
47- // ---------- Server hooks ----------
48-
49- const loadSettings = async ( hookName , args ) => {
50- const ps = ( args && args . settings && args . settings [ pluginName ] ) || { } ;
51- if ( typeof ps . defaultEnabled === 'boolean' ) cachedDefaultEnabled = ps . defaultEnabled ;
52- } ;
53-
54- const clientVars = async ( hookName , ctx ) => {
55- let initialPadEnabled = cachedDefaultEnabled ;
56- try {
57- const padSettings = ctx && ctx . pad && typeof ctx . pad . getPadSettings === 'function'
58- ? ctx . pad . getPadSettings ( ) : null ;
59- const stored = padSettings && padSettings [ pluginName ] ;
60- if ( stored && typeof stored . enabled === 'boolean' ) initialPadEnabled = stored . enabled ;
61- } catch ( _e ) { /* leave initialPadEnabled at instance default */ }
62-
63- const helperBlock = {
64- [ pluginName ] : {
65- padWideSupported : padOptionsPluginPassthrough ,
66- settingId,
67- l10nId,
68- defaultEnabled : cachedDefaultEnabled ,
69- initialPadEnabled,
70- } ,
71- } ;
72- return { ep_plugin_helpers : { padToggle : helperBlock } } ;
73- } ;
74-
75- const renderCheckbox = ( idPrefix ) =>
76- `<p>` +
77- `<input type="checkbox" id="${ idPrefix } options-${ settingId } ">` +
78- `<label for="${ idPrefix } options-${ settingId } " data-l10n-id="${ l10nId } "></label>` +
79- `</p>` ;
80-
81- const eejsBlock_mySettings = ( hookName , args , cb ) => {
82- args . content += renderCheckbox ( '' ) ;
83- return cb ( ) ;
84- } ;
85-
86- const eejsBlock_padSettings = ( hookName , args , cb ) => {
87- if ( ! padOptionsPluginPassthrough ) return cb ( ) ;
88- args . content += renderCheckbox ( 'padsettings-' ) ;
89- return cb ( ) ;
90- } ;
91-
92- // ---------- Client-side state (closed over by init/handleClientMessage) ----------
9338
9439 let onChangeCallback = ( ) => { } ;
9540 let lastEffective = null ;
9641
9742 const getPad = ( ) => {
9843 if ( typeof window === 'undefined' ) return null ;
99- // Try the AMD pad module first (preferred), then the global.
10044 try {
10145 // eslint-disable-next-line global-require
10246 const m = require ( 'ep_etherpad-lite/static/js/pad' ) ;
@@ -148,12 +92,12 @@ const padToggle = (config) => {
14892 const getEffective = ( ) => {
14993 if ( isEnforced ( ) ) {
15094 const padVal = readPadValue ( ) ;
151- return padVal != null ? padVal : cachedDefaultEnabled ;
95+ return padVal != null ? padVal : defaultEnabled ;
15296 }
15397 const userVal = readUserValue ( ) ;
15498 if ( userVal != null ) return userVal ;
15599 const padVal = readPadValue ( ) ;
156- return padVal != null ? padVal : cachedDefaultEnabled ;
100+ return padVal != null ? padVal : defaultEnabled ;
157101 } ;
158102
159103 const refreshUI = ( ) => {
@@ -181,9 +125,6 @@ const padToggle = (config) => {
181125 const $u = window . $ ( `#${ userCheckboxId } ` ) ;
182126 const $p = window . $ ( `#${ padCheckboxId } ` ) ;
183127
184- // User-side checkbox: cookie-backed, mirrors the effective value when not
185- // enforced. Disabled visually + functionally when the pad creator has
186- // turned on enforceSettings.
187128 if ( $u . length ) {
188129 $u . prop ( 'checked' , getEffective ( ) ) ;
189130 $u . prop ( 'disabled' , isEnforced ( ) ) ;
@@ -198,9 +139,6 @@ const padToggle = (config) => {
198139 } ) ;
199140 }
200141
201- // Pad-wide checkbox: only present when the rendering hook ran (i.e. the
202- // server has the passthrough patch). changePadOption broadcasts and the
203- // local applyPadSettings updates pad.padOptions[pluginName] in-place.
204142 if ( $p . length && pad && typeof pad . changePadOption === 'function' ) {
205143 const initial = readPadValue ( ) ;
206144 if ( initial != null ) $p . prop ( 'checked' , initial ) ;
@@ -210,9 +148,6 @@ const padToggle = (config) => {
210148 refreshUI ( ) ;
211149 } ) ;
212150 } else if ( ! isSupportedClient ( ) ) {
213- // Surface the degraded state once per pad load so the operator notices
214- // when their core lacks the passthrough patch but plugin authors expect
215- // pad-wide behavior.
216151 if ( typeof console !== 'undefined' && ! init . _warned ) {
217152 console . warn (
218153 `[ep_plugin_helpers.padToggle ${ pluginName } ] pad-wide settings ` +
@@ -231,24 +166,17 @@ const padToggle = (config) => {
231166 } ;
232167 } ;
233168
234- // Etherpad dispatches handleClientMessage_<type> for every incoming
235- // COLLABROOM message. For pad-wide changes, the outer type is
236- // CLIENT_MESSAGE and the inner payload. type is padoptions. Plugins
237- // re-export this hook so the helper can refresh local state when another
238- // user toggles the pad-wide value .
169+ // Plugin re-exports this so the helper sees pad-wide broadcasts and
170+ // refreshes local state when another user toggles the pad-wide checkbox.
171+ // Etherpad dispatches handleClientMessage_< type> for every COLLABROOM
172+ // message; for pad-wide changes the outer type is CLIENT_MESSAGE and the
173+ // inner payload.type is padoptions .
239174 const handleClientMessage_CLIENT_MESSAGE = ( hookName , ctx ) => {
240175 if ( ! ctx || ! ctx . payload ) return ;
241176 if ( ctx . payload . type === 'padoptions' ) refreshUI ( ) ;
242177 } ;
243178
244- return {
245- loadSettings,
246- clientVars,
247- eejsBlock_mySettings,
248- eejsBlock_padSettings,
249- init,
250- handleClientMessage_CLIENT_MESSAGE,
251- } ;
179+ return { init, handleClientMessage_CLIENT_MESSAGE} ;
252180} ;
253181
254- module . exports = { padToggle, createPadToggle : padToggle } ;
182+ module . exports = { padToggle : padToggleClient , createPadToggle : padToggleClient } ;
0 commit comments