bitwrench 1.2.16 → 2.0.7

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.
Files changed (130) hide show
  1. package/README.md +160 -158
  2. package/bin/bitwrench.js +3 -0
  3. package/dist/bitwrench-code-edit.cjs.js +639 -0
  4. package/dist/bitwrench-code-edit.es5.js +875 -0
  5. package/dist/bitwrench-code-edit.es5.min.js +15 -0
  6. package/dist/bitwrench-code-edit.esm.js +628 -0
  7. package/dist/bitwrench-code-edit.esm.min.js +15 -0
  8. package/dist/bitwrench-code-edit.umd.js +645 -0
  9. package/dist/bitwrench-code-edit.umd.min.js +15 -0
  10. package/dist/bitwrench.cjs.js +6983 -0
  11. package/dist/bitwrench.cjs.min.js +62 -0
  12. package/dist/bitwrench.css +5100 -0
  13. package/dist/bitwrench.es5.js +8446 -0
  14. package/dist/bitwrench.es5.min.js +31 -0
  15. package/dist/bitwrench.esm.js +6981 -0
  16. package/dist/bitwrench.esm.min.js +62 -0
  17. package/dist/bitwrench.umd.js +6989 -0
  18. package/dist/bitwrench.umd.min.js +62 -0
  19. package/dist/builds.json +127 -0
  20. package/dist/sri.json +18 -0
  21. package/package.json +86 -24
  22. package/readme.html +288 -0
  23. package/src/bitwrench-code-edit.js +627 -0
  24. package/src/bitwrench-color-utils.js +311 -0
  25. package/src/bitwrench-component-base.js +736 -0
  26. package/src/bitwrench-components-inline.js +374 -0
  27. package/src/bitwrench-components-v2.js +1879 -0
  28. package/src/bitwrench-components.js +610 -0
  29. package/src/bitwrench-styles.js +3240 -0
  30. package/src/bitwrench.js +3367 -0
  31. package/src/cli/convert.js +205 -0
  32. package/src/cli/index.js +122 -0
  33. package/src/cli/inject.js +55 -0
  34. package/src/cli/layout-default.js +142 -0
  35. package/src/generate-css.js +381 -0
  36. package/src/vendor/quikdown.js +654 -0
  37. package/src/version.js +16 -0
  38. package/.eslintrc.json +0 -27
  39. package/.github/workflows/codeql-analysis.yml +0 -72
  40. package/.travis.yml +0 -34
  41. package/bitwrench.css +0 -92
  42. package/bitwrench.js +0 -3348
  43. package/bitwrench.js_sri.txt +0 -1
  44. package/bitwrench.min.js +0 -1
  45. package/bitwrench.min.js_sri.txt +0 -1
  46. package/bitwrench_ESM.js +0 -3207
  47. package/bitwrench_ESM.js_sri.txt +0 -1
  48. package/bitwrench_ESM.min.js +0 -1
  49. package/bitwrench_ESM.min.js_sri.txt +0 -1
  50. package/dev/bitwrench-todo.md +0 -215
  51. package/dev/css-arrows.md +0 -23
  52. package/dev/docStringDev.js +0 -124
  53. package/dev/docStringParseDev.js +0 -171
  54. package/dev/example11-load-mjs-page.html +0 -17
  55. package/dev/figures.html +0 -37
  56. package/dev/html_gen.js +0 -349
  57. package/dev/htmld.md +0 -250
  58. package/dev/htmldev.html +0 -45
  59. package/dev/index-old.html +0 -87
  60. package/dev/misc-notes.md +0 -21
  61. package/dev/norm.css +0 -30
  62. package/dev/notes.md +0 -2
  63. package/dev/pageData.mjs +0 -69
  64. package/dev/sizes.html +0 -49
  65. package/dev/universal-js-module.js +0 -37
  66. package/examples/example1.html +0 -78
  67. package/examples/example10.html +0 -84
  68. package/examples/example11.html +0 -17
  69. package/examples/example12.html +0 -18
  70. package/examples/example2.html +0 -44
  71. package/examples/example3.html +0 -50
  72. package/examples/example4.html +0 -22
  73. package/examples/example5.html +0 -82
  74. package/examples/example6.html +0 -128
  75. package/examples/example7.html +0 -91
  76. package/examples/example8.html +0 -27
  77. package/examples/example9.html +0 -102
  78. package/examples/examplePageData12.mjs +0 -73
  79. package/examples/pageData.mjs +0 -69
  80. package/examples/pico.min.css +0 -5
  81. package/icon/bitwrench-dark-tall.png +0 -0
  82. package/icon/bitwrench-dark.png +0 -0
  83. package/icon/bitwrench-icon-lt-grey.png +0 -0
  84. package/icon/bitwrench-icon.vsd +0 -0
  85. package/icon/bitwrench-logo-dark.png +0 -0
  86. package/icon/bitwrench-logo-full.png +0 -0
  87. package/icon/bitwrench-logo-green.png +0 -0
  88. package/icon/bitwrench-logo-grey.png +0 -0
  89. package/icon/bitwrench-logo-white.png +0 -0
  90. package/icon/bitwrench-logos-colors.png +0 -0
  91. package/icon/bitwrench-thick-logo.png +0 -0
  92. package/icon/bitwrench-thick-teal/android-chrome-192x192.png +0 -0
  93. package/icon/bitwrench-thick-teal/android-chrome-512x512.png +0 -0
  94. package/icon/bitwrench-thick-teal/apple-touch-icon.png +0 -0
  95. package/icon/bitwrench-thick-teal/browserconfig.xml +0 -9
  96. package/icon/bitwrench-thick-teal/favicon-16x16.png +0 -0
  97. package/icon/bitwrench-thick-teal/favicon-32x32.png +0 -0
  98. package/icon/bitwrench-thick-teal/favicon.ico +0 -0
  99. package/icon/bitwrench-thick-teal/mstile-144x144.png +0 -0
  100. package/icon/bitwrench-thick-teal/mstile-150x150.png +0 -0
  101. package/icon/bitwrench-thick-teal/mstile-310x150.png +0 -0
  102. package/icon/bitwrench-thick-teal/mstile-310x310.png +0 -0
  103. package/icon/bitwrench-thick-teal/mstile-70x70.png +0 -0
  104. package/icon/bitwrench-thick-teal/site.webmanifest +0 -19
  105. package/icon/bitwrench-thick-teal.ico +0 -0
  106. package/icon/bitwrench-thick-teal.svg +0 -44
  107. package/icon/bitwrench-thick-teal.zip +0 -0
  108. package/icon/favicon-test.html +0 -20
  109. package/icon/logos-test.PNG +0 -0
  110. package/images/bitwrench-512x512.png +0 -0
  111. package/images/bitwrench-logo-med.png +0 -0
  112. package/images/bitwrench-thick-logo.png +0 -0
  113. package/images/bitwrench-thick-logo.svg +0 -64
  114. package/images/bitwrench-thick-teal.ico +0 -0
  115. package/images/favicon.ico +0 -0
  116. package/index.html +0 -282
  117. package/instr_tmp/bitwrench.js +0 -1350
  118. package/karma.conf.js +0 -140
  119. package/makefile +0 -21
  120. package/quick-docs.html +0 -206
  121. package/test/bitwrench_test.js +0 -1255
  122. package/test/karma-test.js +0 -1081
  123. package/tools/bw_deprecatedNames.js +0 -19
  124. package/tools/bwconsole.js +0 -20
  125. package/tools/createSimpleHTMLPage.js +0 -41
  126. package/tools/emitreadme.sh +0 -4
  127. package/tools/export-bw-default-css.js +0 -41
  128. package/tools/umd2ModuleHack.js +0 -32
  129. package/tools/update-bw-package.js +0 -36
  130. package/tools/updatereadme.js +0 -34
@@ -0,0 +1,639 @@
1
+ /*! bitwrench v2.0.7 | BSD-2-Clause | http://deftio.com/bitwrench */
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ /**
7
+ * bitwrench-code-edit.js - syntax-highlighted contenteditable code editor addon
8
+ *
9
+ * Provides bw.highlight() for tokenizing JS/CSS/HTML into TACO spans,
10
+ * and bw.codeEditor() for a live editable code block with syntax coloring.
11
+ *
12
+ * Can be loaded standalone (browser script tag after bitwrench.umd.js),
13
+ * or imported as an ES module / CJS module.
14
+ *
15
+ * @module bitwrench-code-edit
16
+ * @license BSD-2-Clause
17
+ */
18
+
19
+ // -- CSS (injected once) ----------------------------------------------
20
+ var _cssInjected = false;
21
+ var CSS_TEXT =
22
+ '.bw-ce{background:#1e293b;border-radius:6px;border:1px solid rgba(255,255,255,0.08);overflow:auto}' +
23
+ '.bw-ce pre{margin:0;padding:0}' +
24
+ '.bw-ce code{font-family:"SF Mono",Monaco,"Cascadia Code",Consolas,monospace;font-size:0.875rem;line-height:1.6;color:#e2e8f0;outline:none;white-space:pre-wrap;display:block;padding:0.75rem 1rem}' +
25
+ '.bw-ce code:empty::before{content:"\\200b"}' +
26
+ '.bw-ce .bw-ce-keyword{color:#c792ea}' +
27
+ '.bw-ce .bw-ce-string{color:#c3e88d}' +
28
+ '.bw-ce .bw-ce-comment{color:#546e7a;font-style:italic}' +
29
+ '.bw-ce .bw-ce-number{color:#f78c6c}' +
30
+ '.bw-ce .bw-ce-operator{color:#89ddff}' +
31
+ '.bw-ce .bw-ce-punctuation{color:#89ddff}' +
32
+ '.bw-ce .bw-ce-property{color:#82aaff}' +
33
+ '.bw-ce .bw-ce-function{color:#82aaff}' +
34
+ '.bw-ce .bw-ce-tag{color:#f07178}' +
35
+ '.bw-ce .bw-ce-attr-name{color:#ffcb6b}' +
36
+ '.bw-ce .bw-ce-attr-value{color:#c3e88d}' +
37
+ '.bw-ce .bw-ce-selector{color:#c792ea}' +
38
+ '.bw-ce .bw-ce-css-prop{color:#82aaff}' +
39
+ '.bw-ce .bw-ce-css-value{color:#f78c6c}' +
40
+ '.bw-ce .bw-ce-at-rule{color:#c792ea;font-style:italic}' +
41
+ '.bw-ce .bw-ce-color{color:#f78c6c}' +
42
+ '.bw-ce .bw-ce-template-interp{color:#89ddff}' +
43
+ // Light theme
44
+ '.bw-ce-light.bw-ce{background:#fafafa;border-color:#d8d8d8}' +
45
+ '.bw-ce-light.bw-ce code{color:#1a1a1a}' +
46
+ '.bw-ce-light.bw-ce .bw-ce-keyword{color:#7c3aed}' +
47
+ '.bw-ce-light.bw-ce .bw-ce-string{color:#16a34a}' +
48
+ '.bw-ce-light.bw-ce .bw-ce-comment{color:#9ca3af;font-style:italic}' +
49
+ '.bw-ce-light.bw-ce .bw-ce-number{color:#ea580c}' +
50
+ '.bw-ce-light.bw-ce .bw-ce-operator{color:#0891b2}' +
51
+ '.bw-ce-light.bw-ce .bw-ce-punctuation{color:#6b7280}' +
52
+ '.bw-ce-light.bw-ce .bw-ce-property{color:#2563eb}' +
53
+ '.bw-ce-light.bw-ce .bw-ce-function{color:#2563eb}' +
54
+ '.bw-ce-light.bw-ce .bw-ce-tag{color:#dc2626}' +
55
+ '.bw-ce-light.bw-ce .bw-ce-attr-name{color:#d97706}' +
56
+ '.bw-ce-light.bw-ce .bw-ce-attr-value{color:#16a34a}' +
57
+ '.bw-ce-light.bw-ce .bw-ce-selector{color:#7c3aed}' +
58
+ '.bw-ce-light.bw-ce .bw-ce-css-prop{color:#2563eb}' +
59
+ '.bw-ce-light.bw-ce .bw-ce-css-value{color:#ea580c}' +
60
+ '.bw-ce-light.bw-ce .bw-ce-at-rule{color:#7c3aed}' +
61
+ '.bw-ce-light.bw-ce .bw-ce-color{color:#ea580c}' +
62
+ '.bw-ce-light.bw-ce .bw-ce-template-interp{color:#0891b2}';
63
+
64
+ function ensureCSS(bw) {
65
+ if (_cssInjected) return;
66
+ _cssInjected = true;
67
+ if (bw && bw.injectCSS) {
68
+ bw.injectCSS(CSS_TEXT, { id: 'bw-code-edit-styles' });
69
+ }
70
+ }
71
+
72
+ // -- JS keywords ------------------------------------------------------
73
+ var JS_KEYWORDS = {};
74
+ 'var,const,let,function,return,if,else,for,while,do,switch,case,break,continue,new,typeof,instanceof,this,class,extends,import,export,default,from,true,false,null,undefined,try,catch,throw,finally,async,await,yield,of,in,delete,void,with,super,static,get,set,debugger'.split(',').forEach(function(k) { JS_KEYWORDS[k] = true; });
75
+
76
+ // -- JS Tokenizer -----------------------------------------------------
77
+ function tokenizeJS(code) {
78
+ var tokens = [];
79
+ var i = 0, len = code.length;
80
+ var buf = '';
81
+
82
+ function flush(type) {
83
+ if (buf.length) { tokens.push({ type: type, text: buf }); buf = ''; }
84
+ }
85
+
86
+ while (i < len) {
87
+ var ch = code[i];
88
+ var next = code[i + 1];
89
+
90
+ // Line comment
91
+ if (ch === '/' && next === '/') {
92
+ flush('plain');
93
+ var end = code.indexOf('\n', i);
94
+ if (end === -1) end = len;
95
+ tokens.push({ type: 'comment', text: code.substring(i, end) });
96
+ i = end;
97
+ continue;
98
+ }
99
+
100
+ // Block comment
101
+ if (ch === '/' && next === '*') {
102
+ flush('plain');
103
+ var end2 = code.indexOf('*/', i + 2);
104
+ if (end2 === -1) end2 = len - 2;
105
+ tokens.push({ type: 'comment', text: code.substring(i, end2 + 2) });
106
+ i = end2 + 2;
107
+ continue;
108
+ }
109
+
110
+ // Regex (simple heuristic: / after operator or start-of-line)
111
+ if (ch === '/' && next !== '/' && next !== '*') {
112
+ var prevToken = tokens.length ? tokens[tokens.length - 1] : null;
113
+ var prevBuf = buf.trim();
114
+ var isRegex = false;
115
+ if (!prevBuf.length) {
116
+ if (!prevToken) isRegex = true;
117
+ else if (prevToken.type === 'operator' || prevToken.type === 'punctuation' || prevToken.type === 'keyword') isRegex = true;
118
+ }
119
+ if (isRegex) {
120
+ flush('plain');
121
+ var rBuf = '/';
122
+ var ri = i + 1;
123
+ var escaped = false;
124
+ var inCharClass = false;
125
+ while (ri < len) {
126
+ var rc = code[ri];
127
+ if (escaped) { rBuf += rc; escaped = false; ri++; continue; }
128
+ if (rc === '\\') { escaped = true; rBuf += rc; ri++; continue; }
129
+ if (rc === '[') { inCharClass = true; rBuf += rc; ri++; continue; }
130
+ if (rc === ']') { inCharClass = false; rBuf += rc; ri++; continue; }
131
+ if (rc === '/' && !inCharClass) { rBuf += rc; ri++; break; }
132
+ if (rc === '\n') break;
133
+ rBuf += rc; ri++;
134
+ }
135
+ // Flags
136
+ while (ri < len && /[gimsuvy]/.test(code[ri])) { rBuf += code[ri]; ri++; }
137
+ tokens.push({ type: 'string', text: rBuf });
138
+ i = ri;
139
+ continue;
140
+ }
141
+ }
142
+
143
+ // Strings
144
+ if (ch === '"' || ch === "'" || ch === '`') {
145
+ flush('plain');
146
+ var quote = ch;
147
+ var sBuf = ch;
148
+ var si = i + 1;
149
+ if (quote === '`') {
150
+ // Template literal
151
+ while (si < len) {
152
+ var sc = code[si];
153
+ if (sc === '\\') { sBuf += sc + (code[si + 1] || ''); si += 2; continue; }
154
+ if (sc === '$' && code[si + 1] === '{') {
155
+ // Flush string part so far
156
+ if (sBuf.length) tokens.push({ type: 'string', text: sBuf }); sBuf = '';
157
+ // Find matching brace (simple: count braces)
158
+ tokens.push({ type: 'template-interp', text: '${' });
159
+ si += 2;
160
+ var depth = 1;
161
+ var interpBuf = '';
162
+ while (si < len && depth > 0) {
163
+ if (code[si] === '{') depth++;
164
+ else if (code[si] === '}') { depth--; if (depth === 0) break; }
165
+ interpBuf += code[si]; si++;
166
+ }
167
+ // Tokenize the interpolation content recursively
168
+ var interpTokens = tokenizeJS(interpBuf);
169
+ tokens = tokens.concat(interpTokens);
170
+ tokens.push({ type: 'template-interp', text: '}' });
171
+ si++; // skip closing }
172
+ continue;
173
+ }
174
+ if (sc === '`') { sBuf += sc; si++; break; }
175
+ sBuf += sc; si++;
176
+ }
177
+ if (sBuf.length) tokens.push({ type: 'string', text: sBuf });
178
+ } else {
179
+ while (si < len) {
180
+ var sc2 = code[si];
181
+ if (sc2 === '\\') { sBuf += sc2 + (code[si + 1] || ''); si += 2; continue; }
182
+ if (sc2 === quote) { sBuf += sc2; si++; break; }
183
+ if (sc2 === '\n') break;
184
+ sBuf += sc2; si++;
185
+ }
186
+ tokens.push({ type: 'string', text: sBuf });
187
+ }
188
+ i = si;
189
+ continue;
190
+ }
191
+
192
+ // Numbers
193
+ if (/[0-9]/.test(ch) || (ch === '.' && next && /[0-9]/.test(next))) {
194
+ flush('plain');
195
+ var nBuf = '';
196
+ if (ch === '0' && next && /[xXbBoO]/.test(next)) {
197
+ nBuf = ch + next; i += 2;
198
+ while (i < len && /[0-9a-fA-F_]/.test(code[i])) { nBuf += code[i]; i++; }
199
+ } else {
200
+ while (i < len && /[0-9._eE]/.test(code[i])) {
201
+ if ((code[i] === 'e' || code[i] === 'E') && code[i + 1] && /[+\-0-9]/.test(code[i + 1])) {
202
+ nBuf += code[i] + code[i + 1]; i += 2; continue;
203
+ }
204
+ nBuf += code[i]; i++;
205
+ }
206
+ }
207
+ tokens.push({ type: 'number', text: nBuf });
208
+ continue;
209
+ }
210
+
211
+ // Identifiers / keywords
212
+ if (/[a-zA-Z_$]/.test(ch)) {
213
+ flush('plain');
214
+ var wBuf = '';
215
+ while (i < len && /[a-zA-Z0-9_$]/.test(code[i])) { wBuf += code[i]; i++; }
216
+ // Look-ahead: is it a function call?
217
+ var la = i;
218
+ while (la < len && (code[la] === ' ' || code[la] === '\t')) la++;
219
+ // Look-back: is it a property (after dot)?
220
+ var prevTok = tokens.length ? tokens[tokens.length - 1] : null;
221
+ var isDot = prevTok && prevTok.type === 'punctuation' && prevTok.text === '.';
222
+
223
+ if (JS_KEYWORDS[wBuf]) {
224
+ tokens.push({ type: 'keyword', text: wBuf });
225
+ } else if (isDot) {
226
+ if (la < len && code[la] === '(') {
227
+ tokens.push({ type: 'function', text: wBuf });
228
+ } else {
229
+ tokens.push({ type: 'property', text: wBuf });
230
+ }
231
+ } else if (la < len && code[la] === '(') {
232
+ tokens.push({ type: 'function', text: wBuf });
233
+ } else {
234
+ tokens.push({ type: 'plain', text: wBuf });
235
+ }
236
+ continue;
237
+ }
238
+
239
+ // Operators
240
+ if ('=+-*/%!<>&|^~?:'.indexOf(ch) !== -1) {
241
+ flush('plain');
242
+ // Consume multi-char operators
243
+ var opBuf = ch;
244
+ i++;
245
+ if (i < len && '=+-*/%!<>&|^~?:'.indexOf(code[i]) !== -1) {
246
+ opBuf += code[i]; i++;
247
+ if (i < len && '=>&|'.indexOf(code[i]) !== -1) { opBuf += code[i]; i++; }
248
+ }
249
+ tokens.push({ type: 'operator', text: opBuf });
250
+ continue;
251
+ }
252
+
253
+ // Punctuation
254
+ if ('(){}[];,.'.indexOf(ch) !== -1) {
255
+ flush('plain');
256
+ tokens.push({ type: 'punctuation', text: ch });
257
+ i++;
258
+ continue;
259
+ }
260
+
261
+ // Plain (whitespace + anything else)
262
+ buf += ch;
263
+ i++;
264
+ }
265
+ flush('plain');
266
+ return tokens;
267
+ }
268
+
269
+ // -- CSS Tokenizer ----------------------------------------------------
270
+ function tokenizeCSS(code) {
271
+ var tokens = [];
272
+ var i = 0, len = code.length;
273
+ var state = 'selector'; // selector | prop | value
274
+ var buf = '';
275
+
276
+ function flush(type) {
277
+ if (buf.length) { tokens.push({ type: type || 'plain', text: buf }); buf = ''; }
278
+ }
279
+
280
+ while (i < len) {
281
+ var ch = code[i];
282
+ var next = code[i + 1];
283
+
284
+ // Block comment
285
+ if (ch === '/' && next === '*') {
286
+ flush(state === 'selector' ? 'selector' : state === 'prop' ? 'css-prop' : 'css-value');
287
+ var end = code.indexOf('*/', i + 2);
288
+ if (end === -1) end = len - 2;
289
+ tokens.push({ type: 'comment', text: code.substring(i, end + 2) });
290
+ i = end + 2;
291
+ continue;
292
+ }
293
+
294
+ // Strings in values
295
+ if ((ch === '"' || ch === "'") && (state === 'value' || state === 'selector')) {
296
+ flush(state === 'selector' ? 'selector' : 'css-value');
297
+ var quote = ch;
298
+ var sBuf = ch;
299
+ i++;
300
+ while (i < len) {
301
+ if (code[i] === '\\') { sBuf += code[i] + (code[i + 1] || ''); i += 2; continue; }
302
+ if (code[i] === quote) { sBuf += code[i]; i++; break; }
303
+ sBuf += code[i]; i++;
304
+ }
305
+ tokens.push({ type: 'string', text: sBuf });
306
+ continue;
307
+ }
308
+
309
+ // At-rules
310
+ if (ch === '@' && state === 'selector') {
311
+ flush('selector');
312
+ var aBuf = '@';
313
+ i++;
314
+ while (i < len && /[a-zA-Z\-]/.test(code[i])) { aBuf += code[i]; i++; }
315
+ tokens.push({ type: 'at-rule', text: aBuf });
316
+ continue;
317
+ }
318
+
319
+ // Hex colors in values
320
+ if (ch === '#' && state === 'value') {
321
+ flush('css-value');
322
+ var hBuf = '#';
323
+ i++;
324
+ while (i < len && /[0-9a-fA-F]/.test(code[i])) { hBuf += code[i]; i++; }
325
+ tokens.push({ type: 'color', text: hBuf });
326
+ continue;
327
+ }
328
+
329
+ // Numbers in values
330
+ if (state === 'value' && /[0-9]/.test(ch)) {
331
+ flush('css-value');
332
+ var nBuf2 = '';
333
+ while (i < len && /[0-9.]/.test(code[i])) { nBuf2 += code[i]; i++; }
334
+ // Unit
335
+ var uBuf = '';
336
+ while (i < len && /[a-zA-Z%]/.test(code[i])) { uBuf += code[i]; i++; }
337
+ tokens.push({ type: 'number', text: nBuf2 + uBuf });
338
+ continue;
339
+ }
340
+
341
+ // Punctuation and state transitions
342
+ if (ch === '{') {
343
+ flush(state === 'selector' ? 'selector' : 'plain');
344
+ tokens.push({ type: 'punctuation', text: ch });
345
+ state = 'prop';
346
+ i++; continue;
347
+ }
348
+ if (ch === '}') {
349
+ flush(state === 'prop' ? 'css-prop' : state === 'value' ? 'css-value' : 'plain');
350
+ tokens.push({ type: 'punctuation', text: ch });
351
+ state = 'selector';
352
+ i++; continue;
353
+ }
354
+ if (ch === ':' && state === 'prop') {
355
+ flush('css-prop');
356
+ tokens.push({ type: 'punctuation', text: ch });
357
+ state = 'value';
358
+ i++; continue;
359
+ }
360
+ if (ch === ';') {
361
+ flush(state === 'value' ? 'css-value' : 'plain');
362
+ tokens.push({ type: 'punctuation', text: ch });
363
+ state = 'prop';
364
+ i++; continue;
365
+ }
366
+ if (ch === ',') {
367
+ flush(state === 'selector' ? 'selector' : state === 'value' ? 'css-value' : 'plain');
368
+ tokens.push({ type: 'punctuation', text: ch });
369
+ i++; continue;
370
+ }
371
+
372
+ // Accumulate into buffer
373
+ buf += ch;
374
+ i++;
375
+ }
376
+ flush(state === 'selector' ? 'selector' : state === 'prop' ? 'css-prop' : 'css-value');
377
+ return tokens;
378
+ }
379
+
380
+ // -- HTML Tokenizer ---------------------------------------------------
381
+ function tokenizeHTML(code) {
382
+ var tokens = [];
383
+ var i = 0, len = code.length;
384
+ var buf = '';
385
+
386
+ function flush(type) {
387
+ if (buf.length) { tokens.push({ type: type, text: buf }); buf = ''; }
388
+ }
389
+
390
+ while (i < len) {
391
+ var ch = code[i];
392
+
393
+ // Comment
394
+ if (ch === '<' && code.substring(i, i + 4) === '<!--') {
395
+ flush('plain');
396
+ var end = code.indexOf('-->', i + 4);
397
+ if (end === -1) end = len - 3;
398
+ tokens.push({ type: 'comment', text: code.substring(i, end + 3) });
399
+ i = end + 3;
400
+ continue;
401
+ }
402
+
403
+ // Tag
404
+ if (ch === '<') {
405
+ flush('plain');
406
+ // Consume < and optional /
407
+ var tBuf = '<';
408
+ i++;
409
+ if (i < len && code[i] === '/') { tBuf += '/'; i++; }
410
+ // Tag name
411
+ while (i < len && /[a-zA-Z0-9\-]/.test(code[i])) { tBuf += code[i]; i++; }
412
+ tokens.push({ type: 'tag', text: tBuf });
413
+
414
+ // Attributes
415
+ while (i < len && code[i] !== '>' && !(code[i] === '/' && code[i + 1] === '>')) {
416
+ // Whitespace
417
+ if (/\s/.test(code[i])) {
418
+ var wBuf = '';
419
+ while (i < len && /\s/.test(code[i])) { wBuf += code[i]; i++; }
420
+ tokens.push({ type: 'plain', text: wBuf });
421
+ continue;
422
+ }
423
+ // Attribute name
424
+ if (/[a-zA-Z_\-@:]/.test(code[i])) {
425
+ var aBuf = '';
426
+ while (i < len && /[a-zA-Z0-9_\-@:]/.test(code[i])) { aBuf += code[i]; i++; }
427
+ tokens.push({ type: 'attr-name', text: aBuf });
428
+ // = sign
429
+ if (i < len && code[i] === '=') {
430
+ tokens.push({ type: 'punctuation', text: '=' });
431
+ i++;
432
+ // Attribute value
433
+ if (i < len && (code[i] === '"' || code[i] === "'")) {
434
+ var q = code[i];
435
+ var vBuf = q;
436
+ i++;
437
+ while (i < len && code[i] !== q) { vBuf += code[i]; i++; }
438
+ if (i < len) { vBuf += code[i]; i++; }
439
+ tokens.push({ type: 'attr-value', text: vBuf });
440
+ } else {
441
+ // Unquoted value
442
+ var uBuf2 = '';
443
+ while (i < len && !/[\s>]/.test(code[i])) { uBuf2 += code[i]; i++; }
444
+ tokens.push({ type: 'attr-value', text: uBuf2 });
445
+ }
446
+ }
447
+ continue;
448
+ }
449
+ // Anything else in tag
450
+ buf += code[i]; i++;
451
+ flush('plain');
452
+ }
453
+
454
+ // Close of tag
455
+ var closeBuf = '';
456
+ if (i < len && code[i] === '/') { closeBuf += '/'; i++; }
457
+ if (i < len && code[i] === '>') { closeBuf += '>'; i++; }
458
+ if (closeBuf) tokens.push({ type: 'tag', text: closeBuf });
459
+ continue;
460
+ }
461
+
462
+ // Entity
463
+ if (ch === '&') {
464
+ flush('plain');
465
+ var eBuf = '&';
466
+ i++;
467
+ while (i < len && code[i] !== ';' && /[a-zA-Z0-9#]/.test(code[i])) { eBuf += code[i]; i++; }
468
+ if (i < len && code[i] === ';') { eBuf += ';'; i++; }
469
+ tokens.push({ type: 'string', text: eBuf });
470
+ continue;
471
+ }
472
+
473
+ // Plain text
474
+ buf += ch;
475
+ i++;
476
+ }
477
+ flush('plain');
478
+ return tokens;
479
+ }
480
+
481
+ // -- Token to TACO conversion -----------------------------------------
482
+ var TOKENIZERS = { js: tokenizeJS, javascript: tokenizeJS, css: tokenizeCSS, html: tokenizeHTML };
483
+
484
+ function tokensToTACO(tokArr) {
485
+ var result = [];
486
+ for (var i = 0; i < tokArr.length; i++) {
487
+ var tok = tokArr[i];
488
+ if (tok.type === 'plain') {
489
+ result.push(tok.text);
490
+ } else {
491
+ result.push({ t: 'span', a: { class: 'bw-ce-' + tok.type }, c: tok.text });
492
+ }
493
+ }
494
+ return result;
495
+ }
496
+
497
+ // -- Public: highlight ------------------------------------------------
498
+ function highlight(code, lang) {
499
+ var tokenizer = TOKENIZERS[lang] || tokenizeJS;
500
+ var tokens = tokenizer(code);
501
+ return tokensToTACO(tokens);
502
+ }
503
+
504
+ // -- Caret save/restore -----------------------------------------------
505
+ function getCaretOffset(el) {
506
+ var sel = window.getSelection();
507
+ if (!sel.rangeCount) return 0;
508
+ var range = sel.getRangeAt(0).cloneRange();
509
+ range.selectNodeContents(el);
510
+ range.setEnd(sel.getRangeAt(0).startContainer, sel.getRangeAt(0).startOffset);
511
+ return range.toString().length;
512
+ }
513
+
514
+ function setCaretOffset(el, offset) {
515
+ var sel = window.getSelection();
516
+ var range = document.createRange();
517
+ var walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null, false);
518
+ var pos = 0;
519
+ var node;
520
+ while ((node = walker.nextNode())) {
521
+ var nodeLen = node.textContent.length;
522
+ if (pos + nodeLen >= offset) {
523
+ range.setStart(node, offset - pos);
524
+ range.collapse(true);
525
+ sel.removeAllRanges();
526
+ sel.addRange(range);
527
+ return;
528
+ }
529
+ pos += nodeLen;
530
+ }
531
+ // If offset exceeds content, place at end
532
+ range.selectNodeContents(el);
533
+ range.collapse(false);
534
+ sel.removeAllRanges();
535
+ sel.addRange(range);
536
+ }
537
+
538
+ // -- Public: codeEditor -----------------------------------------------
539
+ function codeEditor(opts) {
540
+ opts = opts || {};
541
+ var code = opts.code || '';
542
+ var lang = opts.lang || 'js';
543
+ var height = opts.height || '180px';
544
+ var readOnly = !!opts.readOnly;
545
+ var className = 'bw-ce' + (opts.className ? ' ' + opts.className : '');
546
+
547
+ var highlighted = highlight(code, lang);
548
+
549
+ var codeAttrs = {
550
+ spellcheck: 'false',
551
+ class: 'bw-ce-code'
552
+ };
553
+ if (!readOnly) {
554
+ codeAttrs.contenteditable = 'true';
555
+ }
556
+
557
+ return {
558
+ t: 'div',
559
+ a: { class: className, style: 'max-height:' + height + ';overflow:auto' },
560
+ c: [
561
+ { t: 'pre', c: { t: 'code', a: codeAttrs, c: highlighted } }
562
+ ],
563
+ o: {
564
+ mounted: function(el) {
565
+ var codeEl = el.querySelector('.bw-ce-code');
566
+ if (!codeEl) return;
567
+
568
+ var currentCode = code;
569
+ var debounceTimer = null;
570
+
571
+ // Resolve bw from global or import context
572
+ var bw = (typeof window !== 'undefined' && window.bw) || {};
573
+
574
+ function getValue() { return codeEl.textContent || ''; }
575
+
576
+ function setValue(newCode) {
577
+ currentCode = newCode;
578
+ var tacos = highlight(newCode, lang);
579
+ if (bw.html) codeEl.innerHTML = bw.html({ t: 'span', c: tacos });
580
+ }
581
+
582
+ // Expose API on the element
583
+ el._bwCodeEdit = { getValue: getValue, setValue: setValue };
584
+
585
+ if (readOnly) return;
586
+
587
+ function rehighlight() {
588
+ var newCode = getValue();
589
+ if (newCode === currentCode) return;
590
+ currentCode = newCode;
591
+ var offset = getCaretOffset(codeEl);
592
+ var tacos = highlight(newCode, lang);
593
+ if (bw.html) codeEl.innerHTML = bw.html({ t: 'span', c: tacos });
594
+ setCaretOffset(codeEl, offset);
595
+ if (opts.onChange) opts.onChange(newCode);
596
+ }
597
+
598
+ codeEl.addEventListener('input', function() {
599
+ clearTimeout(debounceTimer);
600
+ debounceTimer = setTimeout(rehighlight, 50);
601
+ });
602
+
603
+ // Tab key: insert 2 spaces
604
+ codeEl.addEventListener('keydown', function(e) {
605
+ if (e.key === 'Tab') {
606
+ e.preventDefault();
607
+ document.execCommand('insertText', false, ' ');
608
+ }
609
+ });
610
+ }
611
+ }
612
+ };
613
+ }
614
+
615
+ // -- Auto-attach to bw when loaded as script tag ----------------------
616
+ function install(bw) {
617
+ if (!bw) return;
618
+ bw.highlight = highlight;
619
+ bw.codeEditor = function(opts) {
620
+ ensureCSS(bw);
621
+ return codeEditor(opts);
622
+ };
623
+ }
624
+
625
+ // Auto-install if bw is on window (script tag usage)
626
+ if (typeof window !== 'undefined' && window.bw) {
627
+ install(window.bw);
628
+ }
629
+ var bitwrenchCodeEdit = { highlight, codeEditor, install, CSS_TEXT };
630
+
631
+ exports.CSS_TEXT = CSS_TEXT;
632
+ exports.codeEditor = codeEditor;
633
+ exports.default = bitwrenchCodeEdit;
634
+ exports.highlight = highlight;
635
+ exports.install = install;
636
+ exports.tokenizeCSS = tokenizeCSS;
637
+ exports.tokenizeHTML = tokenizeHTML;
638
+ exports.tokenizeJS = tokenizeJS;
639
+ //# sourceMappingURL=bitwrench-code-edit.cjs.js.map