@valten/regulex-visualizer 1.0.8 → 1.0.9

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.
@@ -1,357 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5
- <title>Regulex Vue:JavaScript Regular Expression Visualizer</title>
6
- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
7
- <link rel="stylesheet" href="./style.css">
8
- </head>
9
- <body>
10
- <div id="app">
11
- <h1>Regulex <em>JavaScript Regular Expression Visualizer (Vue版本)</em></h1>
12
-
13
- <div class="input-container">
14
- <div class="regex-input code">
15
- <table>
16
- <tr>
17
- <td style="width:1em">/</td>
18
- <td style="width:auto">
19
- <input
20
- v-model="regexInput"
21
- class="input"
22
- @keyup="onKeyup"
23
- @keydown="onEnter"
24
- @paste="onPaste"
25
- placeholder="输入正则表达式"
26
- />
27
- </td>
28
- <td style="width:1em">/</td>
29
- <td style="width:3em">{{ flags }}</td>
30
- </tr>
31
- </table>
32
- </div>
33
-
34
- <button class="btn" @click="visualize">Visualize</button>
35
- <button class="btn" @click="exportImage">Export Image</button>
36
- <button class="btn" @click="embedCode">Embed On My Site!</button>
37
-
38
- <label class="flag-label">
39
- <input type="checkbox" v-model="flagI" @change="onFlagChange"/>IgnoreCase
40
- </label>
41
- <label class="flag-label">
42
- <input type="checkbox" v-model="flagM" @change="onFlagChange"/>Multiline
43
- </label>
44
- <label class="flag-label">
45
- <input type="checkbox" v-model="flagG" @change="onFlagChange"/>GlobalMatch
46
- </label>
47
- </div>
48
-
49
- <p v-if="errorMessage" class="error-box code">{{ errorMessage }}</p>
50
-
51
- <div ref="graphContainer" class="graph-container code"></div>
52
- </div>
53
-
54
- <script src="../assets/regulex.js"></script>
55
- <script>
56
- const { createApp, ref, reactive, computed, onMounted, nextTick } = Vue;
57
-
58
- // 获取 regulex 依赖的函数
59
- function getRegulexDependencies() {
60
- const regulex = require('regulex');
61
- return {
62
- parse: regulex.parse,
63
- visualize: regulex.visualize,
64
- Raphael: regulex.Raphael,
65
- Kit: regulex.Kit
66
- };
67
- }
68
-
69
- createApp({
70
- setup() {
71
- const regexInput = ref('^(a|b)*?$');
72
- const flagI = ref(false);
73
- const flagM = ref(false);
74
- const flagG = ref(false);
75
- const errorMessage = ref('');
76
- const graphContainer = ref(null);
77
- const paper = ref(null);
78
- const regulexDeps = ref(null);
79
- const params = reactive({
80
- embed: false,
81
- re: '',
82
- highlight: true,
83
- flags: '',
84
- debug: false,
85
- cmd: ''
86
- });
87
-
88
- // 计算属性
89
- const flags = computed(() => {
90
- let result = '';
91
- if (flagI.value) result += 'i';
92
- if (flagM.value) result += 'm';
93
- if (flagG.value) result += 'g';
94
- return result;
95
- });
96
-
97
- // 工具函数
98
- function trim(s) {
99
- return s.replace(/^\s+/, '').replace(/\s+$/, '');
100
- }
101
-
102
- function getParams() {
103
- const hash = location.hash;
104
- if (!hash || hash.length < 2) {
105
- return { embed: false, re: "", highlight: true, flags: '' };
106
- }
107
-
108
- const paramsStr = hash.slice(2);
109
- const paramsObj = paramsStr.split("&").reduce(function (p, a) {
110
- const parts = a.split("=");
111
- p[parts[0]] = parts[1];
112
- return p;
113
- }, {});
114
-
115
- return {
116
- embed: paramsObj.embed === 'true',
117
- flags: paramsObj.flags || '',
118
- re: paramsObj.re ? trim(decodeURIComponent(paramsObj.re)) : '',
119
- debug: paramsObj.debug === 'true',
120
- cmd: paramsObj.cmd || ''
121
- };
122
- }
123
-
124
- function serializeHash() {
125
- const re = trim(regexInput.value);
126
- const flagsStr = flags.value;
127
- return "#!" +
128
- (params.debug ? "debug=true&" : "") +
129
- (params.cmd ? "cmd=" + params.cmd + "&" : "") +
130
- (params.embed ? "embed=true&" : "") +
131
- "flags=" + flagsStr + "&re=" + encodeURIComponent(re);
132
- }
133
-
134
- function changeHash() {
135
- location.hash = serializeHash();
136
- }
137
-
138
- function hideError() {
139
- errorMessage.value = '';
140
- }
141
-
142
- function showError(re, err) {
143
- let msg = ["Error:" + err.message, ""];
144
- if (typeof err.lastIndex === 'number') {
145
- msg.push(re);
146
- msg.push('-'.repeat(err.lastIndex) + "^");
147
- }
148
- errorMessage.value = msg.join("\n");
149
- }
150
-
151
- // 核心功能函数 - 修改为支持多种依赖获取方式
152
- function visualize(skipError = false) {
153
- if (!regulexDeps.value) {
154
- console.error('Regulex dependencies not available');
155
- return false;
156
- }
157
-
158
- if (!graphContainer.value) {
159
- console.error('Graph container not available');
160
- return false;
161
- }
162
-
163
- const re = trim(regexInput.value);
164
- changeHash();
165
- hideError();
166
- try {
167
- // 只在第一次创建 paper,后续重复使用
168
- if (!paper.value) {
169
- paper.value = regulexDeps.value.Raphael(graphContainer.value, 10, 10);
170
- }
171
-
172
- // 解析正则表达式
173
- const ast = regulexDeps.value.parse(re);
174
-
175
- // 可视化 - regulex 的 visualize 函数内部会调用 paper.clear()
176
- regulexDeps.value.visualize(ast, flags.value, paper.value);
177
- return true;
178
- } catch (e) {
179
- if (!skipError) {
180
- showError(re, e);
181
- }
182
- return false;
183
- }
184
- }
185
-
186
- function onFlagChange() {
187
- visualize();
188
- changeHash();
189
- }
190
-
191
- function onKeyup(e) {
192
- if (e.keyCode === 13) return; // Enter
193
- clearTimeout(window.onKeyupTid);
194
- window.onKeyupTid = setTimeout(() => {
195
- visualize(true);
196
- }, 100);
197
- }
198
-
199
- function onEnter(e) {
200
- if (e.keyCode === 13) {
201
- e.preventDefault();
202
- e.stopPropagation();
203
- visualize();
204
- }
205
- }
206
-
207
- function onPaste(e) {
208
- setTimeout(() => {
209
- const content = trim(e.clipboardData.getData('text'));
210
- if (content[0] === '/' && /\/[img]*$/.test(content)) {
211
- const endIndex = content.lastIndexOf('/');
212
- const flagsStr = content.slice(endIndex + 1);
213
- const regexStr = content.slice(1, endIndex);
214
-
215
- // 设置标志
216
- flagI.value = flagsStr.includes('i');
217
- flagM.value = flagsStr.includes('m');
218
- flagG.value = flagsStr.includes('g');
219
-
220
- // 设置正则表达式
221
- regexInput.value = regexStr;
222
- }
223
- visualize();
224
- }, 50);
225
- }
226
-
227
- function exportImage() {
228
- const newParams = { ...params };
229
- newParams.cmd = 'export';
230
- const hash = serializeHash();
231
- window.open(location.href.split('#!')[0] + hash, "_blank");
232
- }
233
-
234
- function embedCode() {
235
- if (!visualize()) return false;
236
-
237
- const src = location.href;
238
- const i = src.indexOf('#');
239
- const baseSrc = i > 0 ? src.slice(0, i) : src;
240
- changeHash();
241
- const re = trim(regexInput.value);
242
- const html = `<iframe frameborder="0" width="400" height="300" src="${baseSrc}#!embed=true&flags=${flags.value}&re=${encodeURIComponent(re)}"></iframe>`;
243
- window.prompt("复制HTML代码:", html);
244
- }
245
-
246
- function dragGraph(container) {
247
- if (!container) return;
248
-
249
- container.addEventListener('mousedown', startMove);
250
-
251
- function startMove(e) {
252
- clearSelect();
253
- let x = e.clientX, y = e.clientY;
254
- container.addEventListener('mousemove', onMove);
255
-
256
- document.addEventListener('mouseup', unbind, true);
257
- window.addEventListener('mouseup', unbind, true);
258
-
259
- function unbind(e) {
260
- container.removeEventListener('mousemove', onMove);
261
- document.removeEventListener('mouseup', unbind, true);
262
- window.removeEventListener('mouseup', unbind, true);
263
- }
264
-
265
- function onMove(e) {
266
- let dx = x - e.clientX, dy = y - e.clientY;
267
- if (dx > 0 && container.scrollWidth - container.scrollLeft - container.clientWidth < 2
268
- || dx < 0 && container.scrollLeft < 1) {
269
- document.documentElement.scrollLeft += dx;
270
- document.body.scrollLeft += dx;
271
- } else {
272
- container.scrollLeft += dx;
273
- }
274
- if (dy > 0 && container.scrollHeight - container.scrollTop - container.clientHeight < 2
275
- || dy < 0 && container.scrollTop < 1) {
276
- document.documentElement.scrollTop += dy;
277
- document.body.scrollTop += dy;
278
- } else {
279
- container.scrollTop += dy;
280
- }
281
- x = e.clientX;
282
- y = e.clientY;
283
- }
284
- }
285
- }
286
-
287
- function clearSelect() {
288
- if (window.getSelection) {
289
- if (window.getSelection().empty) {
290
- window.getSelection().empty();
291
- } else if (window.getSelection().removeAllRanges) {
292
- window.getSelection().removeAllRanges();
293
- }
294
- } else if (document.selection) {
295
- document.selection.empty();
296
- }
297
- }
298
-
299
- // 初始化
300
- onMounted(() => {
301
- // 获取 regulex 依赖
302
- regulexDeps.value = getRegulexDependencies();
303
-
304
- if (!regulexDeps.value) {
305
- console.error('Failed to load Regulex dependencies');
306
- errorMessage.value = 'Error: Failed to load Regulex dependencies';
307
- return;
308
- }
309
-
310
- // 获取URL参数
311
- const urlParams = getParams();
312
- Object.assign(params, urlParams);
313
-
314
- // 设置初始值
315
- if (params.flags) {
316
- flagI.value = params.flags.includes('i');
317
- flagM.value = params.flags.includes('m');
318
- flagG.value = params.flags.includes('g');
319
- }
320
-
321
- if (params.re) {
322
- regexInput.value = params.re;
323
- }
324
-
325
- // 使用 nextTick 确保 DOM 渲染完成后再初始化
326
- nextTick(() => {
327
- // 初始化可视化
328
- visualize();
329
-
330
- // 初始化拖拽功能
331
- if (graphContainer.value) {
332
- dragGraph(graphContainer.value);
333
- }
334
- });
335
- });
336
-
337
- return {
338
- regexInput,
339
- flagI,
340
- flagM,
341
- flagG,
342
- flags,
343
- errorMessage,
344
- graphContainer,
345
- visualize,
346
- onFlagChange,
347
- onKeyup,
348
- onEnter,
349
- onPaste,
350
- exportImage,
351
- embedCode
352
- };
353
- }
354
- }).mount('#app');
355
- </script>
356
- </body>
357
- </html>
@@ -1,208 +0,0 @@
1
- body, html * {
2
- margin: 0;
3
- padding: 0;
4
- font-family: sans-serif;
5
- }
6
-
7
- body {
8
- background: #303030;
9
- }
10
-
11
- .code, .code * {
12
- font-family: "DejaVu Sans Mono", monospace;
13
- }
14
-
15
- h1 {
16
- font-size: 3em;
17
- color: #40C0FF;
18
- margin: 14px 0;
19
- padding: 40px 16px 20px;
20
- border-bottom: 2px dashed grey;
21
- min-height: 60px;
22
- }
23
-
24
- h1 em {
25
- color: #BBE0E0;
26
- font-size: small;
27
- padding-left: 2em;
28
- font-style: normal;
29
- font-weight: normal;
30
- display: inline-block;
31
- }
32
-
33
- .input-container, .error-box {
34
- margin: 8px 16px;
35
- }
36
-
37
- .regex-input {
38
- background: #EEE;
39
- font-size: 1.2em;
40
- line-height: 1.4em;
41
- color: #333;
42
- border: 1px solid black;
43
- padding: 4px;
44
- font-weight: bold;
45
- word-break: break-all;
46
- word-wrap: break-word;
47
- }
48
-
49
- .regex-input table {
50
- border: none;
51
- padding: 0;
52
- margin: 0;
53
- width: 100%;
54
- }
55
-
56
- .regex-input .input {
57
- color: #3030C0;
58
- padding: 0 2px;
59
- border: none;
60
- background: #EEE;
61
- font-size: 1.2em;
62
- line-height: 1.4em;
63
- height: 1.4em;
64
- font-weight: bold;
65
- word-break: break-all;
66
- word-wrap: break-word;
67
- margin: 0;
68
- width: auto;
69
- }
70
-
71
- .regex-input .input {
72
- display: table;
73
- width: 100%;
74
- }
75
-
76
- .flag-label {
77
- color: white;
78
- cursor: pointer;
79
- display: inline-block;
80
- }
81
-
82
- .flag-label input {
83
- margin-right: 4px;
84
- }
85
-
86
- .error-box {
87
- background: #EEE;
88
- font-size: 1.2em;
89
- line-height: 1.4em;
90
- border: 1px solid black;
91
- padding: 4px;
92
- color: darkred;
93
- white-space: pre;
94
- word-wrap: normal;
95
- word-break: keep-all;
96
- overflow: auto;
97
- }
98
-
99
- .btn {
100
- font-size: 16px;
101
- line-height: 16px;
102
- display: inline-block;
103
- margin: 10px 10px 10px 0;
104
- padding: 12px 12px 10px;
105
- color: #EEF;
106
- border: none;
107
- cursor: pointer;
108
- background: #40B0EF linear-gradient(to bottom, #40B0EF, #3060A0);
109
- border-radius: 12px;
110
- text-decoration: none;
111
- }
112
-
113
- .btn:hover {
114
- color: #FEF;
115
- background: #3CB0FD linear-gradient(to bottom, #3CB0FD, #3498DF);
116
- text-decoration: none;
117
- }
118
-
119
- .graph-container {
120
- padding: 4px;
121
- margin: 0px 16px;
122
- border: 1px solid black;
123
- background: #EEE;
124
- overflow: auto;
125
- cursor: move;
126
- -webkit-touch-callout: none;
127
- -webkit-user-select: none;
128
- -khtml-user-select: none;
129
- -moz-user-select: none;
130
- -ms-user-select: none;
131
- user-select: none;
132
- }
133
-
134
- .graph-container svg {
135
- display: block;
136
- margin: 0 auto;
137
- }
138
-
139
- footer div.normal {
140
- text-align: center;
141
- border-top: 1px solid grey;
142
- margin: 1em auto;
143
- padding: 10px;
144
- font-size: 1.2em;
145
- }
146
-
147
- footer, footer a {
148
- color: #EEF;
149
- }
150
-
151
- footer div.embed {
152
- display: none;
153
- }
154
-
155
- body.embed {
156
- background: #EEE;
157
- }
158
-
159
- body.embed, body.embed .graph-container {
160
- margin: 0;
161
- padding: 0;
162
- border: none;
163
- }
164
-
165
- body.embed h1,
166
- body.embed .input-container,
167
- body.embed #github {
168
- display: none !important;
169
- }
170
-
171
- body.embed footer div.normal {
172
- display: none;
173
- }
174
-
175
- body.embed footer div.embed {
176
- display: block;
177
- }
178
-
179
- body.embed footer div.embed, body.embed div.footer a {
180
- color: white;
181
- font-size: 14px;
182
- }
183
-
184
- body.embed footer {
185
- display: block;
186
- width: auto;
187
- margin: 0;
188
- padding: 2px 10px;
189
- background: rgb(75, 0, 130);
190
- opacity: 0.85;
191
- position: fixed;
192
- right: 0;
193
- bottom: 0;
194
- }
195
-
196
- body.embed {
197
- height: 100%;
198
- }
199
-
200
- body.embed .graph-container {
201
- height: 100vh;
202
- }
203
-
204
- .export-canvas {
205
- display: block;
206
- margin: 0 auto;
207
- image-rendering: pixelated;
208
- }
File without changes
File without changes