@lmjs/core 1.0.7 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bundle.js +264 -0
- package/build/plugins-registry.js +216 -0
- package/dist/lumenjs-core-with-plugins.js +59258 -0
- package/dist/lumenjs-core.js +50 -0
- package/dist/lumenjs-plugins.css +2822 -0
- package/package.json +59 -28
- package/src/_re.js +2709 -0
- package/src/dom-shim.js +640 -0
- package/src/index-bootstrap.js +53 -0
- package/src/lstnrs.js +1409 -0
- package/src/walk.js +837 -0
- package/src/workers/css.js +1 -0
- package/src/workers/cssRaw.js +1173 -0
- package/src/workers/esp.js +1 -0
- package/src/workers/espRaw.js +78 -0
- package/src/workers/up.js +1 -0
- package/src/workers/upRaw.js +491 -0
- package/src/workers/work.js +1 -0
- package/src/workers/workRaw.js +1097 -0
- package/src/ws.js +1162 -0
- package/vendor/astring.js +3 -0
- package/vendor/bootstrap2-less-stubs/mixins.less +16 -0
- package/vendor/bootstrap2-less-stubs/variables.less +13 -0
- package/vendor/md5.js +1 -0
- package/vendor/reconnecting-websocket.js +4143 -0
- package/vendor/webworker-helper.js +1 -0
- package/LICENSE +0 -201
- package/README.md +0 -3
- package/index.js +0 -14
package/src/dom-shim.js
ADDED
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
// dom-shim.js — a minimal jQuery-compatible layer.
|
|
2
|
+
//
|
|
3
|
+
// Scope, deliberately narrow: covers exactly the surface LumenJS's own core
|
|
4
|
+
// (_re.js, walk.js, ws.js, lstnrs.js) uses internally, plus the documented
|
|
5
|
+
// app-facing API (el.css/.data/.val/.attr, el[0], .trigger) from
|
|
6
|
+
// lumenjs-spec.md §6.3/§10. Scoped by grepping actual usage across the core
|
|
7
|
+
// files, not guessed.
|
|
8
|
+
//
|
|
9
|
+
// This is NOT a jQuery replacement for everything — optional plugins
|
|
10
|
+
// (Select2, Flickity, the colorpicker, etc.) stay on real jQuery when a page
|
|
11
|
+
// opts into them, since they're third-party code written against jQuery's
|
|
12
|
+
// actual $.fn plugin architecture. This shim only has to satisfy the core
|
|
13
|
+
// runtime, which is what makes it small.
|
|
14
|
+
(function (global) {
|
|
15
|
+
function toArray(x) {
|
|
16
|
+
if (x == null) return [];
|
|
17
|
+
if (typeof NodeList !== 'undefined' && x instanceof NodeList) return Array.prototype.slice.call(x);
|
|
18
|
+
if (typeof HTMLCollection !== 'undefined' && x instanceof HTMLCollection) return Array.prototype.slice.call(x);
|
|
19
|
+
if (Array.isArray(x)) return x;
|
|
20
|
+
if (x instanceof $) return Array.prototype.slice.call(x);
|
|
21
|
+
return [x];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function $(selector, context) {
|
|
25
|
+
if (!(this instanceof $)) return new $(selector, context);
|
|
26
|
+
|
|
27
|
+
var els = [];
|
|
28
|
+
var sel = undefined;
|
|
29
|
+
if (!selector) {
|
|
30
|
+
els = [];
|
|
31
|
+
} else if (typeof selector === 'string') {
|
|
32
|
+
var trimmed = selector.trim();
|
|
33
|
+
if (trimmed[0] === '<') {
|
|
34
|
+
var tpl = document.createElement('template');
|
|
35
|
+
tpl.innerHTML = trimmed;
|
|
36
|
+
els = toArray(tpl.content.childNodes).filter(function (n) { return n.nodeType === 1; });
|
|
37
|
+
} else {
|
|
38
|
+
sel = selector;
|
|
39
|
+
var root = context ? (context instanceof $ ? context[0] : context) : document;
|
|
40
|
+
els = root ? toArray(root.querySelectorAll(selector)) : [];
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
els = toArray(selector);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (var i = 0; i < els.length; i++) this[i] = els[i];
|
|
47
|
+
this.length = els.length;
|
|
48
|
+
this.selector = sel;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$.fn = $.prototype;
|
|
53
|
+
$.fn.jquery = 'lumenjs-shim';
|
|
54
|
+
|
|
55
|
+
$.fn.extend = function (methods) {
|
|
56
|
+
for (var k in methods) if (Object.prototype.hasOwnProperty.call(methods, k)) $.fn[k] = methods[k];
|
|
57
|
+
return $;
|
|
58
|
+
};
|
|
59
|
+
$.extend = function (target) {
|
|
60
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
61
|
+
var src = arguments[i];
|
|
62
|
+
for (var k in src) if (Object.prototype.hasOwnProperty.call(src, k)) target[k] = src[k];
|
|
63
|
+
}
|
|
64
|
+
return target;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// jQuery's own Event wrapper — used internally by the vendored WebWorker
|
|
68
|
+
// helper (`new $.Event(type)`, then it assigns custom properties onto
|
|
69
|
+
// the result). A plain native Event supports arbitrary property
|
|
70
|
+
// assignment already, so this just needs to exist as a constructor.
|
|
71
|
+
$.Event = function (type, props) {
|
|
72
|
+
var e = typeof type === 'string' ? new Event(type, { bubbles: true, cancelable: true }) : type;
|
|
73
|
+
if (props) for (var k in props) e[k] = props[k];
|
|
74
|
+
return e;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
$.proxy = function (fn, context) { return fn.bind(context); };
|
|
78
|
+
$.trim = function (s) { return (s == null ? '' : String(s)).trim(); };
|
|
79
|
+
$.inArray = function (val, arr) { return Array.prototype.indexOf.call(arr, val); };
|
|
80
|
+
$.each = function (arr, fn) {
|
|
81
|
+
if (Array.isArray(arr) || arr instanceof $) {
|
|
82
|
+
for (var i = 0; i < arr.length; i++) if (fn.call(arr[i], i, arr[i]) === false) break;
|
|
83
|
+
} else {
|
|
84
|
+
for (var k in arr) if (fn.call(arr[k], k, arr[k]) === false) break;
|
|
85
|
+
}
|
|
86
|
+
return arr;
|
|
87
|
+
};
|
|
88
|
+
// Minimal fetch-based $.ajax — covers ws.js's one call site (a plain
|
|
89
|
+
// GET/POST with success/error callbacks), not jQuery's full option set.
|
|
90
|
+
// Fires the global ajaxStart/ajaxStop events real jQuery provides for a
|
|
91
|
+
// loading-indicator pattern (`$(document).ajaxStart(...)`) — used at
|
|
92
|
+
// _re.js's top level.
|
|
93
|
+
var _ajaxActive = 0;
|
|
94
|
+
$.ajax = function (opts) {
|
|
95
|
+
opts = opts || {};
|
|
96
|
+
var method = (opts.method || opts.type || 'GET').toUpperCase();
|
|
97
|
+
var init = { method: method, headers: opts.headers || {} };
|
|
98
|
+
if (opts.data != null && method !== 'GET') {
|
|
99
|
+
init.body = typeof opts.data === 'string' ? opts.data : JSON.stringify(opts.data);
|
|
100
|
+
}
|
|
101
|
+
if (_ajaxActive === 0) $(document).trigger('ajaxStart');
|
|
102
|
+
_ajaxActive++;
|
|
103
|
+
$(document).trigger('ajaxSend');
|
|
104
|
+
return fetch(opts.url, init)
|
|
105
|
+
.then(function (res) {
|
|
106
|
+
var ct = res.headers.get('content-type') || '';
|
|
107
|
+
return ct.indexOf('json') > -1 ? res.json() : res.text();
|
|
108
|
+
})
|
|
109
|
+
.then(function (data) {
|
|
110
|
+
opts.success && opts.success(data);
|
|
111
|
+
$(document).trigger('ajaxSuccess');
|
|
112
|
+
return data;
|
|
113
|
+
})
|
|
114
|
+
.catch(function (err) {
|
|
115
|
+
opts.error && opts.error(err);
|
|
116
|
+
$(document).trigger('ajaxError');
|
|
117
|
+
throw err;
|
|
118
|
+
})
|
|
119
|
+
.finally(function () {
|
|
120
|
+
_ajaxActive--;
|
|
121
|
+
$(document).trigger('ajaxComplete');
|
|
122
|
+
if (_ajaxActive === 0) $(document).trigger('ajaxStop');
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
$.fn.each = function (fn) {
|
|
127
|
+
for (var i = 0; i < this.length; i++) if (fn.call(this[i], i, this[i]) === false) break;
|
|
128
|
+
return this;
|
|
129
|
+
};
|
|
130
|
+
$.fn.get = function (i) { return i === undefined ? toArray(this) : this[i]; };
|
|
131
|
+
// 2026-09-17, found alongside .find()/.is() (same real bug's radio-
|
|
132
|
+
// group branch in checkFormInputs()) — excludes elements matching a
|
|
133
|
+
// raw DOM node, an array of nodes, or a $()-wrapped set; jQuery also
|
|
134
|
+
// supports a selector string here, not needed by anything in this
|
|
135
|
+
// codebase's actual usage (grepped), so not implemented.
|
|
136
|
+
$.fn.not = function (exclude) {
|
|
137
|
+
var excludeArr = exclude instanceof $ ? toArray(exclude) : (Array.isArray(exclude) ? exclude : [exclude]);
|
|
138
|
+
var out = [];
|
|
139
|
+
this.each(function () { if (excludeArr.indexOf(this) === -1) out.push(this); });
|
|
140
|
+
return $(out);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
$.fn.attr = function (name, value) {
|
|
144
|
+
if (value === undefined) return this[0] ? this[0].getAttribute(name) : undefined;
|
|
145
|
+
return this.each(function () { this.setAttribute(name, value); });
|
|
146
|
+
};
|
|
147
|
+
// 2026-09-17, found in the same real-page click test as .find()/.is()/
|
|
148
|
+
// .not() above — real jQuery distinguishes .prop() (a direct DOM
|
|
149
|
+
// property, e.g. .checked/.disabled/.scrollHeight) from .attr() (the
|
|
150
|
+
// HTML attribute string); the two diverge for exactly the boolean
|
|
151
|
+
// properties this codebase's real usage needs (checkFormInputs()'s
|
|
152
|
+
// "disabled", the vendored search-widget's "disabled", scroll
|
|
153
|
+
// measurements) — grepped actual call sites, not guessed.
|
|
154
|
+
$.fn.prop = function (name, value) {
|
|
155
|
+
if (value === undefined) return this[0] ? this[0][name] : undefined;
|
|
156
|
+
return this.each(function () { this[name] = value; });
|
|
157
|
+
};
|
|
158
|
+
$.fn.removeAttr = function (name) {
|
|
159
|
+
return this.each(function () { this.removeAttribute(name); });
|
|
160
|
+
};
|
|
161
|
+
// Not a real jQuery method — LumenJS's own $.fn.extend addition
|
|
162
|
+
// (lstnrs.js), kept here since the core relies on it directly.
|
|
163
|
+
//
|
|
164
|
+
// 2026-09-16, real bug found and fixed: native `hasAttribute` can never
|
|
165
|
+
// see a `@`-prefixed name (`@click`, `@sort`, ...) — vendor/
|
|
166
|
+
// reconnecting-websocket.js monkey-patches HTMLElement.prototype.
|
|
167
|
+
// setAttribute/getAttribute/removeAttribute so any `@`-prefixed write
|
|
168
|
+
// is redirected to `el.events[name]` instead of the real attribute
|
|
169
|
+
// store (deliberate, real V2 framework behavior, not something this
|
|
170
|
+
// shim invented — see that file). Native `hasAttribute` was never part
|
|
171
|
+
// of that patch, so it always returned false for these, and lstnrs.js's
|
|
172
|
+
// processEv() bails immediately on `!el.hasAttr("@" + n)` — meaning
|
|
173
|
+
// every `@`-event handler's before/after-guard and payload lookup was
|
|
174
|
+
// unreachable. `getAttribute` *is* patch-aware, so route through that
|
|
175
|
+
// instead — correct for both real attributes (native getAttribute
|
|
176
|
+
// returns null when absent) and `@`-prefixed ones.
|
|
177
|
+
$.fn.hasAttr = function (name) {
|
|
178
|
+
return !!(this[0] && this[0].getAttribute(name) != null);
|
|
179
|
+
};
|
|
180
|
+
$.fn.data = function (key, value) {
|
|
181
|
+
if (value === undefined) {
|
|
182
|
+
if (!this[0]) return undefined;
|
|
183
|
+
if (key === undefined) return Object.assign({}, this[0].dataset);
|
|
184
|
+
var v = this[0].dataset[key];
|
|
185
|
+
try { return JSON.parse(v); } catch (e) { return v; }
|
|
186
|
+
}
|
|
187
|
+
return this.each(function () { this.dataset[key] = typeof value === 'string' ? value : JSON.stringify(value); });
|
|
188
|
+
};
|
|
189
|
+
$.fn.val = function (value) {
|
|
190
|
+
if (value === undefined) return this[0] ? this[0].value : undefined;
|
|
191
|
+
return this.each(function () { this.value = value; });
|
|
192
|
+
};
|
|
193
|
+
$.fn.html = function (value) {
|
|
194
|
+
if (value === undefined) return this[0] ? this[0].innerHTML : undefined;
|
|
195
|
+
// 2026-09-14, real bug found alongside .append()'s: _re.js calls
|
|
196
|
+
// `.html(_rel._RealDOM)` (an array of real Nodes, e.g. for a
|
|
197
|
+
// rendered layout/view) and `.html(_re._RealDOM)` for the view
|
|
198
|
+
// itself — a bare string assignment (`this.innerHTML = value`)
|
|
199
|
+
// coerces an array via toString(), producing garbage text like
|
|
200
|
+
// "[object HTMLDivElement]" instead of actually inserting the
|
|
201
|
+
// rendered content. Route anything non-string through the same
|
|
202
|
+
// append() normalization instead.
|
|
203
|
+
if (typeof value !== 'string') {
|
|
204
|
+
var nodes = _normalizeAppendable([value]);
|
|
205
|
+
return this.each(function () {
|
|
206
|
+
this.innerHTML = '';
|
|
207
|
+
var self = this;
|
|
208
|
+
nodes.forEach(function (n) { self.appendChild(n); });
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return this.each(function () { this.innerHTML = value; });
|
|
212
|
+
};
|
|
213
|
+
// 2026-09-17, found alongside .find()/.is()/.not()/.prop() above —
|
|
214
|
+
// checkFormInputs()'s "data-into" error-message branch calls
|
|
215
|
+
// into.text(...). Not hit by the specific real page that surfaced
|
|
216
|
+
// the others (this codebase's checkbox demo has no data-into), added
|
|
217
|
+
// proactively rather than waiting for a second real crash to find it.
|
|
218
|
+
$.fn.text = function (value) {
|
|
219
|
+
if (value === undefined) return this[0] ? this[0].textContent : '';
|
|
220
|
+
return this.each(function () { this.textContent = value; });
|
|
221
|
+
};
|
|
222
|
+
$.fn.css = function (name, value) {
|
|
223
|
+
if (typeof name === 'object') {
|
|
224
|
+
return this.each(function () {
|
|
225
|
+
var el = this;
|
|
226
|
+
for (var k in name) el.style[k] = name[k];
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (value === undefined) return this[0] ? getComputedStyle(this[0])[name] : undefined;
|
|
230
|
+
return this.each(function () { this.style[name] = value; });
|
|
231
|
+
};
|
|
232
|
+
$.fn.addClass = function (cls) {
|
|
233
|
+
var list = cls.split(/\s+/).filter(Boolean);
|
|
234
|
+
return this.each(function () { this.classList.add.apply(this.classList, list); });
|
|
235
|
+
};
|
|
236
|
+
$.fn.removeClass = function (cls) {
|
|
237
|
+
var list = cls.split(/\s+/).filter(Boolean);
|
|
238
|
+
return this.each(function () { this.classList.remove.apply(this.classList, list); });
|
|
239
|
+
};
|
|
240
|
+
$.fn.hasClass = function (cls) { return !!(this[0] && this[0].classList.contains(cls)); };
|
|
241
|
+
$.fn.toggleClass = function (cls, force) {
|
|
242
|
+
var list = cls.split(/\s+/).filter(Boolean);
|
|
243
|
+
return this.each(function () {
|
|
244
|
+
var el = this;
|
|
245
|
+
list.forEach(function (c) { el.classList.toggle(c, force); });
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
$.fn.remove = function () {
|
|
249
|
+
return this.each(function () { this.parentNode && this.parentNode.removeChild(this); });
|
|
250
|
+
};
|
|
251
|
+
// Flattens real jQuery's .append()/.prepend() calling convention — any
|
|
252
|
+
// number of arguments, each a string, a real Node, a $-wrapped
|
|
253
|
+
// element, or an array of any of those — into a flat list of real
|
|
254
|
+
// Nodes. Anything else (a plain object, a number, etc.) is silently
|
|
255
|
+
// dropped, matching real jQuery's tolerance for garbage input.
|
|
256
|
+
//
|
|
257
|
+
// 2026-09-14, real bug found testing a real project against a real dev
|
|
258
|
+
// server: this file's old `.append(content)` only ever looked at its
|
|
259
|
+
// FIRST argument and assumed it was a string, a Node, or $-wrapped —
|
|
260
|
+
// `_re.js`'s `el.append(..._re._RealDOM)` (spreading a Node array) hit
|
|
261
|
+
// this whenever _RealDOM had zero or more-than-one entries, and a
|
|
262
|
+
// pre-existing, vestigial `$("body").append({beajs:true, ...})` call
|
|
263
|
+
// in vendor/reconnecting-websocket.js (a plain object, not a Node —
|
|
264
|
+
// dead/legacy code, evidenced by the commented-out line right above
|
|
265
|
+
// it) crashed outright instead of being silently ignored the way real
|
|
266
|
+
// jQuery would have.
|
|
267
|
+
function _normalizeAppendable(args) {
|
|
268
|
+
var out = [];
|
|
269
|
+
function add(item) {
|
|
270
|
+
if (item == null) return;
|
|
271
|
+
if (Array.isArray(item)) { item.forEach(add); return; }
|
|
272
|
+
if (item instanceof $) { for (var i = 0; i < item.length; i++) add(item[i]); return; }
|
|
273
|
+
if (typeof item === 'string') {
|
|
274
|
+
var tpl = document.createElement('template');
|
|
275
|
+
tpl.innerHTML = item;
|
|
276
|
+
while (tpl.content.firstChild) out.push(tpl.content.removeChild(tpl.content.firstChild));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (item instanceof Node) { out.push(item); return; }
|
|
280
|
+
}
|
|
281
|
+
for (var i = 0; i < args.length; i++) add(args[i]);
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
$.fn.append = function () {
|
|
285
|
+
if (arguments.length === 1 && typeof arguments[0] === 'string') {
|
|
286
|
+
var html = arguments[0];
|
|
287
|
+
return this.each(function () { this.insertAdjacentHTML('beforeend', html); });
|
|
288
|
+
}
|
|
289
|
+
var nodes = _normalizeAppendable(arguments);
|
|
290
|
+
return this.each(function () {
|
|
291
|
+
var self = this;
|
|
292
|
+
nodes.forEach(function (n) { self.appendChild(n); });
|
|
293
|
+
});
|
|
294
|
+
};
|
|
295
|
+
$.fn.prepend = function () {
|
|
296
|
+
if (arguments.length === 1 && typeof arguments[0] === 'string') {
|
|
297
|
+
var html = arguments[0];
|
|
298
|
+
return this.each(function () { this.insertAdjacentHTML('afterbegin', html); });
|
|
299
|
+
}
|
|
300
|
+
var nodes = _normalizeAppendable(arguments);
|
|
301
|
+
return this.each(function () {
|
|
302
|
+
var self = this;
|
|
303
|
+
var ref = self.firstChild;
|
|
304
|
+
nodes.forEach(function (n) { self.insertBefore(n, ref); });
|
|
305
|
+
});
|
|
306
|
+
};
|
|
307
|
+
$.fn.after = function (content) {
|
|
308
|
+
return this.each(function () {
|
|
309
|
+
if (typeof content === 'string') this.insertAdjacentHTML('afterend', content);
|
|
310
|
+
else this.parentNode.insertBefore(content instanceof $ ? content[0] : content, this.nextSibling);
|
|
311
|
+
});
|
|
312
|
+
};
|
|
313
|
+
$.fn.appendTo = function (target) { $(target).append(this); return this; };
|
|
314
|
+
// Real layout metrics (jsdom has no rendering engine, so these will
|
|
315
|
+
// read 0 here — harmless for anything that isn't actually testing
|
|
316
|
+
// pixel measurements, which nothing in this codebase's core does).
|
|
317
|
+
$.fn.width = function () { return this[0] ? this[0].offsetWidth : 0; };
|
|
318
|
+
$.fn.height = function () { return this[0] ? this[0].offsetHeight : 0; };
|
|
319
|
+
$.fn.outerWidth = function (includeMargin) {
|
|
320
|
+
if (!this[0]) return 0;
|
|
321
|
+
var w = this[0].offsetWidth;
|
|
322
|
+
if (includeMargin) {
|
|
323
|
+
var s = getComputedStyle(this[0]);
|
|
324
|
+
w += parseFloat(s.marginLeft || 0) + parseFloat(s.marginRight || 0);
|
|
325
|
+
}
|
|
326
|
+
return w;
|
|
327
|
+
};
|
|
328
|
+
$.fn.outerHeight = function (includeMargin) {
|
|
329
|
+
if (!this[0]) return 0;
|
|
330
|
+
var h = this[0].offsetHeight;
|
|
331
|
+
if (includeMargin) {
|
|
332
|
+
var s = getComputedStyle(this[0]);
|
|
333
|
+
h += parseFloat(s.marginTop || 0) + parseFloat(s.marginBottom || 0);
|
|
334
|
+
}
|
|
335
|
+
return h;
|
|
336
|
+
};
|
|
337
|
+
$.fn.parent = function () { return $(this[0] ? this[0].parentElement : null); };
|
|
338
|
+
$.fn.next = function () { return $(this[0] ? this[0].nextElementSibling : null); };
|
|
339
|
+
$.fn.closest = function (sel) { return $(this[0] ? this[0].closest(sel) : null); };
|
|
340
|
+
// 2026-09-17, real bug found and fixed: never implemented at all —
|
|
341
|
+
// vendor/reconnecting-websocket.js's ported V1 form-validation code
|
|
342
|
+
// (checkFormInputs()) calls f.find(...) on the result of .closest(
|
|
343
|
+
// "form"), and .closest() legitimately returns an empty $() when
|
|
344
|
+
// there's no enclosing form (a completely normal case — a single
|
|
345
|
+
// checkbox/input outside a <form>). With .find() missing, that threw
|
|
346
|
+
// "f.find is not a function" the moment ANY input/textarea/select
|
|
347
|
+
// fired a "change" event anywhere on the page, form or no form —
|
|
348
|
+
// found clicking a plain checkbox on lumenjs.com's own real V2
|
|
349
|
+
// homepage. Real jQuery .find() semantics: search descendants of
|
|
350
|
+
// each matched element, union the results (safe/empty on an empty
|
|
351
|
+
// set, exactly what's needed here).
|
|
352
|
+
$.fn.find = function (sel) {
|
|
353
|
+
var out = [];
|
|
354
|
+
this.each(function () {
|
|
355
|
+
var found = this.querySelectorAll(sel);
|
|
356
|
+
for (var i = 0; i < found.length; i++) if (out.indexOf(found[i]) === -1) out.push(found[i]);
|
|
357
|
+
});
|
|
358
|
+
return $(out);
|
|
359
|
+
};
|
|
360
|
+
// 2026-09-17, found alongside .find() (same crash, next line of the
|
|
361
|
+
// same real bug): checkFormInputs() also calls el.is(":checkbox")/
|
|
362
|
+
// ":radio"/":checked" — real CSS pseudo-classes for the former two
|
|
363
|
+
// don't exist (native .matches(":checkbox") throws a SyntaxError,
|
|
364
|
+
// it's a jQuery-only pseudo-selector), so a short, explicit table for
|
|
365
|
+
// the handful actually used across the vendored code (grepped, not
|
|
366
|
+
// guessed) plus a native .matches() fallback for everything else
|
|
367
|
+
// (tag names, real attribute selectors like "[dp0]") — safely false
|
|
368
|
+
// rather than throwing if a selector native matches() can't parse.
|
|
369
|
+
var JQUERY_PSEUDOS = {
|
|
370
|
+
':checkbox': function (el) { return el.tagName === 'INPUT' && el.type === 'checkbox'; },
|
|
371
|
+
':radio': function (el) { return el.tagName === 'INPUT' && el.type === 'radio'; },
|
|
372
|
+
':checked': function (el) { return !!el.checked; },
|
|
373
|
+
};
|
|
374
|
+
$.fn.is = function (sel) {
|
|
375
|
+
if (!this[0]) return false;
|
|
376
|
+
if (JQUERY_PSEUDOS[sel]) return JQUERY_PSEUDOS[sel](this[0]);
|
|
377
|
+
try { return this[0].matches(sel); } catch (e) { return false; }
|
|
378
|
+
};
|
|
379
|
+
$.fn.ready = function (fn) {
|
|
380
|
+
if (document.readyState !== 'loading') fn($);
|
|
381
|
+
else document.addEventListener('DOMContentLoaded', function () { fn($); });
|
|
382
|
+
return this;
|
|
383
|
+
};
|
|
384
|
+
// jQuery's full family of global ajax events — all just named events on
|
|
385
|
+
// document under the hood, registered/fired the same way.
|
|
386
|
+
['ajaxStart', 'ajaxStop', 'ajaxSend', 'ajaxSuccess', 'ajaxError', 'ajaxComplete'].forEach(function (name) {
|
|
387
|
+
$.fn[name] = function (fn) { return this.on(name, fn); };
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// ---- Events ----
|
|
391
|
+
// A handful of event names need translating for delegation to actually
|
|
392
|
+
// work — left as-is, these would silently never fire, exactly the class
|
|
393
|
+
// of bug point 6 exists to stop happening. `touch` isn't a real DOM
|
|
394
|
+
// event name at all. `focus`/`blur` are real, but don't bubble, so
|
|
395
|
+
// delegating them from `document`/`body` (lstnrs.js's whole pattern)
|
|
396
|
+
// requires their bubbling equivalents instead — this is what real
|
|
397
|
+
// jQuery does internally for the same reason.
|
|
398
|
+
var EVENT_NAME_MAP = { touch: 'touchstart', focus: 'focusin', blur: 'focusout' };
|
|
399
|
+
|
|
400
|
+
// jQuery lets you `.on()`/`.trigger()` an arbitrary plain object, not
|
|
401
|
+
// just DOM nodes — real jQuery keeps its own event table for that case.
|
|
402
|
+
// Needed here: the vendored WebWorker helper wraps itself (`$(this)`)
|
|
403
|
+
// and uses `.on()`/`.trigger()` purely as a pub/sub bus, never touching
|
|
404
|
+
// the DOM. Bridge non-Node targets to a real EventTarget instead of
|
|
405
|
+
// assuming addEventListener always exists.
|
|
406
|
+
var _eventBuses = typeof WeakMap !== 'undefined' ? new WeakMap() : null;
|
|
407
|
+
function _isNode(x) {
|
|
408
|
+
return x === window || x === document || (typeof Node !== 'undefined' && x instanceof Node);
|
|
409
|
+
}
|
|
410
|
+
// 2026-09-14, real bug found testing a real project end-to-end: the
|
|
411
|
+
// same WebWorker helper ALSO does `$(this.getNativeWorker()).on(
|
|
412
|
+
// "message", ...)` to receive real messages posted FROM the actual
|
|
413
|
+
// worker thread. A native `Worker` isn't a DOM Node, so `_isNode` was
|
|
414
|
+
// false and this got routed through the fake WeakMap-bridged
|
|
415
|
+
// EventTarget below instead of the real Worker instance's own native
|
|
416
|
+
// event system — meaning real `message` events the browser itself
|
|
417
|
+
// dispatches on the Worker never reached it, and the worker's
|
|
418
|
+
// load/start handshake (which is entirely message-driven) silently
|
|
419
|
+
// stalled forever. Any object that's already a genuine EventTarget
|
|
420
|
+
// (Worker, WebSocket, XMLHttpRequest, ...) should be used directly,
|
|
421
|
+
// not bridged.
|
|
422
|
+
function _isRealEventTarget(x) {
|
|
423
|
+
return x != null && typeof x.addEventListener === 'function' && typeof x.dispatchEvent === 'function';
|
|
424
|
+
}
|
|
425
|
+
function _eventTargetFor(x) {
|
|
426
|
+
if (_isNode(x) || _isRealEventTarget(x)) return x;
|
|
427
|
+
if (!_eventBuses.has(x)) _eventBuses.set(x, new EventTarget());
|
|
428
|
+
return _eventBuses.get(x);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// 2026-09-16, real bug found and fixed: lstnrs.js delegates every
|
|
432
|
+
// @click/@sort/@drag/... binding via `$("body").on(evt, "[\\@name]",
|
|
433
|
+
// handler)` — 34 call sites, all this exact single-attribute-presence
|
|
434
|
+
// shape. Native `Element.prototype.closest()` (used below for every
|
|
435
|
+
// other selector) matches against the browser's real internal
|
|
436
|
+
// attribute storage, which can never see the `@`-prefixed redirect
|
|
437
|
+
// described in $.fn.hasAttr's comment above — so a delegated `@`-event
|
|
438
|
+
// listener could never fire at all, in any environment, real browser
|
|
439
|
+
// included (confirmed against an actual `lm build` output driven by
|
|
440
|
+
// real Chrome, not just jsdom). Detect that exact selector shape and
|
|
441
|
+
// walk up manually using `getAttribute` (patch-aware) instead of
|
|
442
|
+
// native closest(); every other selector (e.g. lstnrs.js's own
|
|
443
|
+
// "[up] [browse]") is untouched, still native.
|
|
444
|
+
var AT_ATTR_SELECTOR = /^\[\\@([\w-]+)\]$/;
|
|
445
|
+
$.fn.on = function (events, selector, handler) {
|
|
446
|
+
if (typeof selector === 'function') { handler = selector; selector = null; }
|
|
447
|
+
var evts = events.split(/\s+/).filter(Boolean);
|
|
448
|
+
var atMatch = selector && AT_ATTR_SELECTOR.exec(selector);
|
|
449
|
+
var atAttr = atMatch ? '@' + atMatch[1] : null;
|
|
450
|
+
return this.each(function () {
|
|
451
|
+
var node = this;
|
|
452
|
+
var bus = _eventTargetFor(node);
|
|
453
|
+
evts.forEach(function (evt) {
|
|
454
|
+
var real = EVENT_NAME_MAP[evt] || evt;
|
|
455
|
+
bus.addEventListener(real, function (e) {
|
|
456
|
+
if (atAttr) {
|
|
457
|
+
var el = e.target;
|
|
458
|
+
var match = null;
|
|
459
|
+
while (el && el.nodeType === 1) {
|
|
460
|
+
if (el.getAttribute(atAttr) != null) { match = el; break; }
|
|
461
|
+
el = el.parentElement;
|
|
462
|
+
}
|
|
463
|
+
if (match && node.contains(match)) handler.call(match, e);
|
|
464
|
+
} else if (selector && _isNode(node)) {
|
|
465
|
+
var match2 = e.target.closest(selector);
|
|
466
|
+
if (match2 && node.contains(match2)) handler.call(match2, e);
|
|
467
|
+
} else {
|
|
468
|
+
handler.call(node, e);
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
};
|
|
474
|
+
// No .live() — 2026-09-13: confirmed every call site in lstnrs.js was
|
|
475
|
+
// internal (never part of the app-facing API), and the file already
|
|
476
|
+
// uses `$("body").on(event, selector, handler)` everywhere else for the
|
|
477
|
+
// exact same pattern. Migrated all 11 sites to that instead of shimming
|
|
478
|
+
// a deprecated jQuery API just to keep two spellings of one thing.
|
|
479
|
+
$.fn.click = function (handler) { return handler ? this.on('click', handler) : this.trigger('click'); };
|
|
480
|
+
$.fn.keydown = function (handler) { return handler ? this.on('keydown', handler) : this.trigger('keydown'); };
|
|
481
|
+
$.fn.trigger = function (type, data) {
|
|
482
|
+
return this.each(function () {
|
|
483
|
+
var bus = _eventTargetFor(this);
|
|
484
|
+
var ev;
|
|
485
|
+
if (type instanceof Event) {
|
|
486
|
+
// 2026-09-14, real bug found testing a real project end-to-
|
|
487
|
+
// end: the vendored WebWorker helper (vendor/webworker-
|
|
488
|
+
// helper.js) builds `$.Event(name)` — a real Event — then
|
|
489
|
+
// assigns custom properties onto it (e.g. `.worker`) and
|
|
490
|
+
// triggers with THAT OBJECT, not a name string. The old
|
|
491
|
+
// code here did `new Event(type, ...)` regardless, so
|
|
492
|
+
// passing an Event object as `type` got silently coerced
|
|
493
|
+
// to garbage like "[object Event]" by the native Event
|
|
494
|
+
// constructor — no error, just an event nothing ever
|
|
495
|
+
// matched, which is why _csswrk (the scoped-CSS worker,
|
|
496
|
+
// gating renderView() itself) never got past its
|
|
497
|
+
// INITIALIZED state: its own start()/load() sequence is
|
|
498
|
+
// entirely driven by trigger()ing exactly this pattern.
|
|
499
|
+
// Dispatch the existing Event as-is so its custom
|
|
500
|
+
// properties survive.
|
|
501
|
+
ev = type;
|
|
502
|
+
} else if (type && typeof type === 'object' && typeof type.type === 'string') {
|
|
503
|
+
ev = new Event(type.type, { bubbles: _isNode(this) });
|
|
504
|
+
for (var k in type) {
|
|
505
|
+
if (k !== 'type' && Object.prototype.hasOwnProperty.call(type, k)) {
|
|
506
|
+
try { ev[k] = type[k]; } catch (e) { /* read-only Event own-property, e.g. `target` */ }
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
} else {
|
|
510
|
+
ev = new Event(type, { bubbles: _isNode(this) });
|
|
511
|
+
}
|
|
512
|
+
if (data !== undefined) ev.detail = data;
|
|
513
|
+
bus.dispatchEvent(ev);
|
|
514
|
+
});
|
|
515
|
+
};
|
|
516
|
+
// Real removal needs the original listener reference, which this shim
|
|
517
|
+
// doesn't track (LumenJS's own model is delegation-first — see
|
|
518
|
+
// lumenjs-spec.md §5.6 — so long-lived per-node listeners needing unbind
|
|
519
|
+
// are already the exception, not the pattern). Kept for API completeness.
|
|
520
|
+
$.fn.unbind = function () { return this; };
|
|
521
|
+
|
|
522
|
+
// jQuery's shorthand event methods (2026-09-14, real gap found testing
|
|
523
|
+
// a real project's vendor code end-to-end — `$(el).keyup(...)` threw
|
|
524
|
+
// "not a function", never caught before because nothing had exercised
|
|
525
|
+
// this vendor code path this far). `.eventname(handler)` binds,
|
|
526
|
+
// `.eventname()` triggers — matching real jQuery exactly.
|
|
527
|
+
["click", "dblclick", "keyup", "keydown", "keypress", "focus", "blur",
|
|
528
|
+
"change", "submit", "focusin", "focusout", "mouseenter", "mouseleave",
|
|
529
|
+
"mouseover", "mouseout", "mousedown", "mouseup", "mousemove",
|
|
530
|
+
"resize", "scroll", "load", "select"].forEach(function (name) {
|
|
531
|
+
$.fn[name] = function (handler) {
|
|
532
|
+
return handler ? this.on(name, handler) : this.trigger(name);
|
|
533
|
+
};
|
|
534
|
+
});
|
|
535
|
+
$.fn.hover = function (enter, leave) {
|
|
536
|
+
this.on('mouseenter', enter);
|
|
537
|
+
this.on('mouseleave', leave || enter);
|
|
538
|
+
return this;
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
// 2026-09-18, real gap found testing a real migrated V1 project end-to-
|
|
542
|
+
// end: `st()` (vendor/reconnecting-websocket.js's own real, shipped
|
|
543
|
+
// scroll-to helper — not project code, framework code) does
|
|
544
|
+
// `$("html,body").stop().animate({scrollTop: x}, {duration: 400})` —
|
|
545
|
+
// a very common pattern across real V1 pages (called at the top of
|
|
546
|
+
// most real view scripts for smooth-scroll navigation). Neither
|
|
547
|
+
// method existed in the shim at all, throwing "not a function" and
|
|
548
|
+
// aborting the rest of that script on every real page that called it.
|
|
549
|
+
// `scrollTop`/`scrollLeft` are DOM properties, not CSS ones — handled
|
|
550
|
+
// as a special case, matching real jQuery's own animate() behavior;
|
|
551
|
+
// any other property is treated as a numeric CSS property (adding
|
|
552
|
+
// "px" unless it's already a bare number-only string/value, matching
|
|
553
|
+
// jQuery's own unit-inference for a plain number).
|
|
554
|
+
var _animTimers = new WeakMap();
|
|
555
|
+
function _animGetScroll(el, prop) {
|
|
556
|
+
if (el === window) return prop === 'scrollLeft' ? window.pageXOffset : window.pageYOffset;
|
|
557
|
+
return el[prop];
|
|
558
|
+
}
|
|
559
|
+
function _animSetScroll(el, prop, val) {
|
|
560
|
+
if (el === window) {
|
|
561
|
+
if (prop === 'scrollLeft') window.scrollTo(val, window.pageYOffset);
|
|
562
|
+
else window.scrollTo(window.pageXOffset, val);
|
|
563
|
+
} else {
|
|
564
|
+
el[prop] = val;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
// Real jQuery's own cssNumber list — these CSS properties take a bare
|
|
568
|
+
// unitless number, not "Npx" (opacity: "0.5px" is simply invalid and
|
|
569
|
+
// silently ignored by the browser, which is exactly what broke the
|
|
570
|
+
// very first real-content test of this: an opacity fade never moved).
|
|
571
|
+
var _cssNumberProps = { opacity: 1, zIndex: 1, zoom: 1, fontWeight: 1, lineHeight: 1, columnCount: 1, flexGrow: 1, flexShrink: 1, order: 1, widows: 1, orphans: 1 };
|
|
572
|
+
function _animSetStyle(el, prop, val) {
|
|
573
|
+
el.style[prop] = _cssNumberProps[prop] ? val : (val + 'px');
|
|
574
|
+
}
|
|
575
|
+
$.fn.animate = function (props, options) {
|
|
576
|
+
var opts = typeof options === 'number' ? { duration: options } : (options || {});
|
|
577
|
+
var duration = opts.duration == null ? 400 : opts.duration;
|
|
578
|
+
this.each(function () {
|
|
579
|
+
var el = this;
|
|
580
|
+
var isWindowScroll = (el === document.documentElement || el === document.body);
|
|
581
|
+
var target = isWindowScroll ? window : el;
|
|
582
|
+
var start = {};
|
|
583
|
+
var isScrollProp = {};
|
|
584
|
+
for (var prop in props) {
|
|
585
|
+
if (!Object.prototype.hasOwnProperty.call(props, prop)) continue;
|
|
586
|
+
isScrollProp[prop] = (prop === 'scrollTop' || prop === 'scrollLeft');
|
|
587
|
+
if (isScrollProp[prop]) {
|
|
588
|
+
start[prop] = _animGetScroll(isWindowScroll ? window : el, prop);
|
|
589
|
+
} else {
|
|
590
|
+
start[prop] = parseFloat(getComputedStyle(el)[prop]) || 0;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
var existing = _animTimers.get(el);
|
|
594
|
+
if (existing) cancelAnimationFrame(existing);
|
|
595
|
+
if (duration === 0) {
|
|
596
|
+
for (var p in props) {
|
|
597
|
+
var v = parseFloat(props[p]);
|
|
598
|
+
if (isScrollProp[p]) _animSetScroll(isWindowScroll ? window : el, p, v);
|
|
599
|
+
else _animSetStyle(el, p, v);
|
|
600
|
+
}
|
|
601
|
+
if (opts.complete) opts.complete.call(el);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
var startTime = null;
|
|
605
|
+
function step(ts) {
|
|
606
|
+
if (startTime === null) startTime = ts;
|
|
607
|
+
var elapsed = ts - startTime;
|
|
608
|
+
var t = Math.min(1, elapsed / duration);
|
|
609
|
+
for (var p in props) {
|
|
610
|
+
if (!Object.prototype.hasOwnProperty.call(props, p)) continue;
|
|
611
|
+
var end = parseFloat(props[p]);
|
|
612
|
+
var val = start[p] + (end - start[p]) * t;
|
|
613
|
+
if (isScrollProp[p]) _animSetScroll(isWindowScroll ? window : el, p, val);
|
|
614
|
+
else _animSetStyle(el, p, val);
|
|
615
|
+
}
|
|
616
|
+
if (t < 1) {
|
|
617
|
+
_animTimers.set(el, requestAnimationFrame(step));
|
|
618
|
+
} else {
|
|
619
|
+
_animTimers.delete(el);
|
|
620
|
+
if (opts.complete) opts.complete.call(el);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
_animTimers.set(el, requestAnimationFrame(step));
|
|
624
|
+
});
|
|
625
|
+
return this;
|
|
626
|
+
};
|
|
627
|
+
$.fn.stop = function () {
|
|
628
|
+
this.each(function () {
|
|
629
|
+
var id = _animTimers.get(this);
|
|
630
|
+
if (id) {
|
|
631
|
+
cancelAnimationFrame(id);
|
|
632
|
+
_animTimers.delete(this);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
return this;
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
global.$ = $;
|
|
639
|
+
global.jQuery = $;
|
|
640
|
+
})(typeof window !== 'undefined' ? window : globalThis);
|