regex_field 0.1.0
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/app/assets/javascripts/regex_field/regex_colorizer.js +528 -0
- data/app/assets/javascripts/regex_field.js +17 -0
- data/lib/regex_field/engine.rb +5 -0
- data/lib/regex_field/form_builder.rb +10 -0
- data/lib/regex_field/version.rb +3 -0
- data/lib/regex_field.rb +4 -0
- metadata +70 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4afbdd2c2de56a734faad1e4a8f61fd2542ce79d
|
4
|
+
data.tar.gz: '00857d6d06aead0ed3d67b3f6a3d40fadb76ef0f'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1be03c30b1f6a7d9f3b8d0b91e384a6fbb4c80d37ea8ef08b11a1d63d0590392cabed2f740c4c134df4784223f961cfe0c00b5d59c0e364783e64db106f35670
|
7
|
+
data.tar.gz: 3b9b591677faebf122f21cb7a3870e895f292f5b03ff3a890a87bd9ff16b86f1afbd3a14363a67d3b993c4e82c115b8d7e1dd33cb3bcecee7c31b150f2772b89
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
* Copyright (c) 2017, Applidium (https://applidium.com)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
@@ -0,0 +1,528 @@
|
|
1
|
+
/*! Regex Colorizer v0.3.1
|
2
|
+
* (c) 2010-2012 Steven Levithan <http://stevenlevithan.com/regex/colorizer/>
|
3
|
+
* MIT license
|
4
|
+
*/
|
5
|
+
|
6
|
+
/* v0.1 of this script was extracted from RegexPal v0.1.4 and named 'JavaScript Regex Syntax
|
7
|
+
* Highlighter'. The name changed to Regex Colorizer in v0.2. Currently supports JavaScript (with
|
8
|
+
* web reality) regex syntax only.
|
9
|
+
*/
|
10
|
+
|
11
|
+
var RegexColorizer = (function () {
|
12
|
+
"use strict";
|
13
|
+
|
14
|
+
/*--------------------------------------
|
15
|
+
* Private variables
|
16
|
+
*------------------------------------*/
|
17
|
+
|
18
|
+
var self = {},
|
19
|
+
regexToken = /\[\^?]?(?:[^\\\]]+|\\[\S\s]?)*]?|\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9][0-9]*|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|c[A-Za-z]|[\S\s]?)|\((?:\?[:=!]?)?|(?:[?*+]|\{[0-9]+(?:,[0-9]*)?\})\??|[^.?*+^${[()|\\]+|./g,
|
20
|
+
charClassToken = /[^\\-]+|-|\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|c[A-Za-z]|[\S\s]?)/g,
|
21
|
+
charClassParts = /^(\[\^?)(]?(?:[^\\\]]+|\\[\S\s]?)*)(]?)$/,
|
22
|
+
quantifier = /^(?:[?*+]|\{[0-9]+(?:,[0-9]*)?\})\??$/,
|
23
|
+
type = {
|
24
|
+
NONE: 0,
|
25
|
+
RANGE_HYPHEN: 1,
|
26
|
+
SHORT_CLASS: 2,
|
27
|
+
ALTERNATOR: 3
|
28
|
+
},
|
29
|
+
error = {
|
30
|
+
UNCLOSED_CLASS: "Unclosed character class",
|
31
|
+
INCOMPLETE_TOKEN: "Incomplete regex token",
|
32
|
+
INVALID_RANGE: "Reversed or invalid range",
|
33
|
+
INVALID_GROUP_TYPE: "Invalid or unsupported group type",
|
34
|
+
UNBALANCED_LEFT_PAREN: "Unclosed grouping",
|
35
|
+
UNBALANCED_RIGHT_PAREN: "No matching opening parenthesis",
|
36
|
+
INTERVAL_OVERFLOW: "Interval quantifier cannot use value over 65,535",
|
37
|
+
INTERVAL_REVERSED: "Interval quantifier range is reversed",
|
38
|
+
UNQUANTIFIABLE: "Quantifiers must be preceded by a token that can be repeated",
|
39
|
+
IMPROPER_EMPTY_ALTERNATIVE: "Empty alternative effectively truncates the regex here"
|
40
|
+
};
|
41
|
+
|
42
|
+
/*--------------------------------------
|
43
|
+
* Private helper functions
|
44
|
+
*------------------------------------*/
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Returns HTML for error highlighting.
|
48
|
+
* @private
|
49
|
+
* @param {String} str Pattern to apply error highlighting to.
|
50
|
+
* @param {String} [desc] Error description.
|
51
|
+
* @returns {String} HTML for error highlighting.
|
52
|
+
*/
|
53
|
+
function errorize(str, desc) {
|
54
|
+
return '<b class="err"' + (desc ? ' title="' + desc + '"' : '') + '>' + str + '</b>';
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Returns HTML for group highlighting.
|
59
|
+
* @private
|
60
|
+
* @param {String} str Pattern to apply group highlighting to.
|
61
|
+
* @param {Number} depth Group nesting depth.
|
62
|
+
* @returns {String} HTML for group highlighting.
|
63
|
+
*/
|
64
|
+
function groupize(str, depth) {
|
65
|
+
return '<b class="g' + depth + '">' + str + '</b>';
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Expands &, <, and > characters in the provided string to HTML entities &, <, and >.
|
70
|
+
* @private
|
71
|
+
* @param {String} str String with characters to expand.
|
72
|
+
* @returns {String} String with characters expanded.
|
73
|
+
*/
|
74
|
+
function expandHtmlEntities(str) {
|
75
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
76
|
+
}
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Returns a set of elements within the page body that have the given class name.
|
80
|
+
* @private
|
81
|
+
* @param {String} cls Class name.
|
82
|
+
* @returns {NodeList|HTMLCollection|Array} Set of elements.
|
83
|
+
*/
|
84
|
+
function elsByClass(cls) {
|
85
|
+
if (document.getElementsByClassName) {
|
86
|
+
return document.body.getElementsByClassName(cls);
|
87
|
+
}
|
88
|
+
var els = document.body.getElementsByTagName("*"),
|
89
|
+
regex = new RegExp("(?:^|\\s)" + cls + "(?:\\s|$)"),
|
90
|
+
result = [],
|
91
|
+
len = els.length,
|
92
|
+
i;
|
93
|
+
for (i = 0; i < len; i++) {
|
94
|
+
if (regex.test(els[i].className)) {
|
95
|
+
result.push(els[i]);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
return result;
|
99
|
+
}
|
100
|
+
|
101
|
+
/**
|
102
|
+
* Returns the character code for the provided regex token. Supports tokens used within character
|
103
|
+
* classes only, since that's all it's currently needed for.
|
104
|
+
* @private
|
105
|
+
* @param {String} token Regex token.
|
106
|
+
* @returns {Number} Character code of the provided token, or NaN.
|
107
|
+
*/
|
108
|
+
function getTokenCharCode(token) {
|
109
|
+
// Escape sequence
|
110
|
+
if (token.length > 1 && token.charAt(0) === "\\") {
|
111
|
+
var t = token.slice(1);
|
112
|
+
// Control character
|
113
|
+
if (/^c[A-Za-z]$/.test(t)) {
|
114
|
+
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(t.charAt(1).toUpperCase()) + 1;
|
115
|
+
}
|
116
|
+
// Two or four digit hexadecimal character code
|
117
|
+
if (/^(?:x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4})$/.test(t)) {
|
118
|
+
return parseInt(t.slice(1), 16);
|
119
|
+
}
|
120
|
+
// One to three digit octal character code up to 377 (0xFF)
|
121
|
+
if (/^(?:[0-3][0-7]{0,2}|[4-7][0-7]?)$/.test(t)) {
|
122
|
+
return parseInt(t, 8);
|
123
|
+
}
|
124
|
+
// Shorthand class or incomplete token
|
125
|
+
if (t.length === 1 && "cuxDdSsWw".indexOf(t) > -1) {
|
126
|
+
return NaN;
|
127
|
+
}
|
128
|
+
// Metacharacter representing a single character index, or escaped literal character
|
129
|
+
if (t.length === 1) {
|
130
|
+
switch (t) {
|
131
|
+
case "b": return 8; // Backspace
|
132
|
+
case "f": return 12; // Form feed
|
133
|
+
case "n": return 10; // Line feed
|
134
|
+
case "r": return 13; // Carriage return
|
135
|
+
case "t": return 9; // Horizontal tab
|
136
|
+
case "v": return 11; // Vertical tab
|
137
|
+
default : return t.charCodeAt(0); // Escaped literal character
|
138
|
+
}
|
139
|
+
}
|
140
|
+
}
|
141
|
+
// Unescaped literal token(s)
|
142
|
+
if (token !== "\\") {
|
143
|
+
return token.charCodeAt(0);
|
144
|
+
}
|
145
|
+
return NaN;
|
146
|
+
}
|
147
|
+
|
148
|
+
/**
|
149
|
+
* Applies regex syntax highlighting to the provided character class. Character classes have their
|
150
|
+
* own syntax rules which are different (sometimes quite subtly) from surrounding regex syntax.
|
151
|
+
* Hence, they're treated as a single token and parsed separately.
|
152
|
+
* @private
|
153
|
+
* @param {String} value Character class pattern to be colorized.
|
154
|
+
* @returns {String} HTML for displaying the character class with syntax highlighting.
|
155
|
+
*/
|
156
|
+
function parseCharClass(value) {
|
157
|
+
var output = "",
|
158
|
+
parts = charClassParts.exec(value),
|
159
|
+
lastToken = {
|
160
|
+
rangeable: false,
|
161
|
+
type: type.NONE
|
162
|
+
},
|
163
|
+
match, m;
|
164
|
+
parts = {
|
165
|
+
opening: parts[1],
|
166
|
+
content: parts[2],
|
167
|
+
closing: parts[3]
|
168
|
+
};
|
169
|
+
|
170
|
+
output += parts.closing ? parts.opening : errorize(parts.opening, error.UNCLOSED_CLASS);
|
171
|
+
|
172
|
+
// The charClassToken regex does most of the tokenization grunt work
|
173
|
+
while (match = charClassToken.exec(parts.content)) {
|
174
|
+
m = match[0];
|
175
|
+
// Escape
|
176
|
+
if (m.charAt(0) === "\\") {
|
177
|
+
/* Inside character classes, browsers differ on how they handle the following:
|
178
|
+
* - Any representation of character index zero (\0, \00, \000, \x00, \u0000).
|
179
|
+
* - "\c", when not followed by A-Z or a-z.
|
180
|
+
* - "\x", when not followed by two hex characters.
|
181
|
+
* - "\u", when not followed by four hex characters.
|
182
|
+
* However, although representations of character index zero within character
|
183
|
+
* classes don't work on their own in Firefox, they don't throw an error, they work
|
184
|
+
* when used with ranges, and it's highly unlikely that the user will actually have
|
185
|
+
* such a character in their test data, so such tokens are highlighted normally.
|
186
|
+
* The remaining metasequences are flagged as errors.
|
187
|
+
*/
|
188
|
+
if (/^\\[cux]$/.test(m)) {
|
189
|
+
output += errorize(m, error.INCOMPLETE_TOKEN);
|
190
|
+
lastToken = {rangeable: lastToken.type !== type.RANGE_HYPHEN};
|
191
|
+
// Shorthand class (matches more than one character index)
|
192
|
+
} else if (/^\\[dsw]$/i.test(m)) {
|
193
|
+
output += "<b>" + m + "</b>";
|
194
|
+
/* Traditional regex behavior is that a shorthand class should be unrangeable.
|
195
|
+
* Hence, [-\dz], [\d-z], and [z-\d] should all be equivalent. However, at
|
196
|
+
* least some browsers handle this inconsistently. E.g., Firefox 2 throws an
|
197
|
+
* invalid range error for [z-\d] and [\d--].
|
198
|
+
*/
|
199
|
+
lastToken = {
|
200
|
+
rangeable: lastToken.type !== type.RANGE_HYPHEN,
|
201
|
+
type: type.SHORT_CLASS
|
202
|
+
};
|
203
|
+
// Unescaped "\" at the end of the regex
|
204
|
+
} else if (m === "\\") {
|
205
|
+
output += errorize(m, error.INCOMPLETE_TOKEN);
|
206
|
+
// Don't need to set lastToken since this is the end of the line
|
207
|
+
// Metasequence representing a single character index, or escaped literal character
|
208
|
+
} else {
|
209
|
+
output += "<b>" + expandHtmlEntities(m) + "</b>";
|
210
|
+
lastToken = {
|
211
|
+
rangeable: lastToken.type !== type.RANGE_HYPHEN,
|
212
|
+
charCode: getTokenCharCode(m)
|
213
|
+
};
|
214
|
+
}
|
215
|
+
// Hyphen (might indicate a range)
|
216
|
+
} else if (m === "-") {
|
217
|
+
if (lastToken.rangeable) {
|
218
|
+
// Save the regex's lastIndex so we can reset it after checking the next token
|
219
|
+
var lastIndex = charClassToken.lastIndex,
|
220
|
+
nextToken = charClassToken.exec(parts.content);
|
221
|
+
|
222
|
+
if (nextToken) {
|
223
|
+
var nextTokenCharCode = getTokenCharCode(nextToken[0]);
|
224
|
+
// Hypen for a reverse range (e.g., z-a) or shorthand class (e.g., \d-x or x-\S)
|
225
|
+
if (
|
226
|
+
(!isNaN(nextTokenCharCode) && lastToken.charCode > nextTokenCharCode) ||
|
227
|
+
lastToken.type === type.SHORT_CLASS ||
|
228
|
+
/^\\[dsw]$/i.test(nextToken[0])
|
229
|
+
) {
|
230
|
+
output += errorize("-", error.INVALID_RANGE);
|
231
|
+
// Hyphen creating a valid range
|
232
|
+
} else {
|
233
|
+
output += "<u>-</u>";
|
234
|
+
}
|
235
|
+
lastToken = {
|
236
|
+
rangeable: false,
|
237
|
+
type: type.RANGE_HYPHEN
|
238
|
+
};
|
239
|
+
} else {
|
240
|
+
// Hyphen at the end of a properly closed character class (literal character)
|
241
|
+
if (parts.closing) {
|
242
|
+
output += "-"; // Since this is a literal, it's technically "rangeable", but that doesn't matter
|
243
|
+
// Hyphen at the end of an unclosed character class (i.e., the end of the regex)
|
244
|
+
} else {
|
245
|
+
output += "<u>-</u>";
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
// Reset the regex's lastIndex so the next while loop iteration will continue appropriately
|
250
|
+
charClassToken.lastIndex = lastIndex;
|
251
|
+
// Hyphen at the beginning of a character class or after a non-rangeable token
|
252
|
+
} else {
|
253
|
+
output += "-";
|
254
|
+
lastToken = {rangeable: lastToken.type !== type.RANGE_HYPHEN};
|
255
|
+
}
|
256
|
+
// Literal character sequence
|
257
|
+
} else {
|
258
|
+
output += expandHtmlEntities(m);
|
259
|
+
lastToken = {
|
260
|
+
rangeable: (m.length > 1 || lastToken.type !== type.RANGE_HYPHEN),
|
261
|
+
charCode: m.charCodeAt(m.length - 1)
|
262
|
+
};
|
263
|
+
}
|
264
|
+
} // End charClassToken loop
|
265
|
+
|
266
|
+
return output + parts.closing;
|
267
|
+
}
|
268
|
+
|
269
|
+
/*--------------------------------------
|
270
|
+
* Public methods
|
271
|
+
*------------------------------------*/
|
272
|
+
|
273
|
+
/**
|
274
|
+
* Applies regex syntax highlighting to the provided regex pattern string.
|
275
|
+
* @memberOf RegexColorizer
|
276
|
+
* @param {String} pattern Regex pattern to be colorized.
|
277
|
+
* @returns {String} HTML for displaying the regex with syntax highlighting.
|
278
|
+
* @example
|
279
|
+
*
|
280
|
+
* RegexColorizer.colorizeText('^regexp? pattern$');
|
281
|
+
*/
|
282
|
+
self.colorizeText = function (pattern) {
|
283
|
+
var output = "",
|
284
|
+
capturingGroupCount = 0,
|
285
|
+
groupStyleDepth = 0,
|
286
|
+
openGroups = [],
|
287
|
+
lastToken = {
|
288
|
+
quantifiable: false,
|
289
|
+
type: type.NONE
|
290
|
+
},
|
291
|
+
match, m, char0, char1;
|
292
|
+
|
293
|
+
while (match = regexToken.exec(pattern)) {
|
294
|
+
m = match[0];
|
295
|
+
char0 = m.charAt(0);
|
296
|
+
char1 = m.charAt(1);
|
297
|
+
// Character class
|
298
|
+
if (char0 === "[") {
|
299
|
+
output += "<i>" + parseCharClass(m) + "</i>";
|
300
|
+
lastToken = {quantifiable: true};
|
301
|
+
// Group opening
|
302
|
+
} else if (char0 === "(") {
|
303
|
+
// If this is an invalid group type, mark the error and don't count it towards
|
304
|
+
// group depth or total count
|
305
|
+
if (m.length === 2) { // m is "(?"
|
306
|
+
output += errorize(m, error.INVALID_GROUP_TYPE);
|
307
|
+
} else {
|
308
|
+
if (m.length === 1) {
|
309
|
+
capturingGroupCount++;
|
310
|
+
}
|
311
|
+
groupStyleDepth = groupStyleDepth === 5 ? 1 : groupStyleDepth + 1;
|
312
|
+
/* Record the group opening's position and character sequence so we can later
|
313
|
+
* mark it as invalid if it turns out to be unclosed in the remainder of the
|
314
|
+
* regex. The value of index is the position plus the length of the opening <b>
|
315
|
+
* element with group-depth class.
|
316
|
+
*/
|
317
|
+
openGroups.push({
|
318
|
+
index: output.length + '<b class="gN">'.length,
|
319
|
+
opening: m
|
320
|
+
});
|
321
|
+
// Add markup to the group-opening character sequence
|
322
|
+
output += groupize(m, groupStyleDepth);
|
323
|
+
}
|
324
|
+
lastToken = {quantifiable: false};
|
325
|
+
// Group closing
|
326
|
+
} else if (char0 === ")") {
|
327
|
+
// If this is an invalid group closing
|
328
|
+
if (!openGroups.length) {
|
329
|
+
output += errorize(")", error.UNBALANCED_RIGHT_PAREN);
|
330
|
+
lastToken = {quantifiable: false};
|
331
|
+
} else {
|
332
|
+
output += groupize(")", groupStyleDepth);
|
333
|
+
/* Although at least in some browsers it is possible to quantify lookaheads,
|
334
|
+
* this adds no value, doesn't work as you'd expect in JavaScript, and is an
|
335
|
+
* error with some regex flavors such as PCRE (also ES5?), so flag them as
|
336
|
+
* unquantifiable.
|
337
|
+
*/
|
338
|
+
lastToken = {
|
339
|
+
quantifiable: !/^[=!]/.test(openGroups[openGroups.length - 1].opening.charAt(2)),
|
340
|
+
style: "g" + groupStyleDepth
|
341
|
+
};
|
342
|
+
groupStyleDepth = groupStyleDepth === 1 ? 5 : groupStyleDepth - 1;
|
343
|
+
// Drop the last opening paren from depth tracking
|
344
|
+
openGroups.pop();
|
345
|
+
}
|
346
|
+
// Escape or backreference
|
347
|
+
} else if (char0 === "\\") {
|
348
|
+
// Backreference or octal character code without a leading zero
|
349
|
+
if (/^[1-9]/.test(char1)) {
|
350
|
+
/* What does "\10" mean?
|
351
|
+
* - Backref 10, if 10 or more capturing groups opened before this point.
|
352
|
+
* - Backref 1 followed by "0", if 1-9 capturing groups opened before this point.
|
353
|
+
* - Otherwise, it's octal character index 10 (since 10 is in octal range 0-377).
|
354
|
+
* In the case of \8 or \9 when as many capturing groups weren't opened before
|
355
|
+
* this point, they're highlighted as special tokens. However, they should
|
356
|
+
* probably be marked as errors since the handling is browser-specific. E.g.,
|
357
|
+
* in Firefox 2 they seem to be equivalent to "(?!)", while in IE 7 they match
|
358
|
+
* the literal characters "8" and "9", which is correct handling. I don't mark
|
359
|
+
* them as errors because it would seem inconsistent to users who don't
|
360
|
+
* understand the highlighting rules for octals, etc. In fact, octals are not
|
361
|
+
* included in ECMA-262v3, but all the big browsers support them.
|
362
|
+
*/
|
363
|
+
var nonBackrefDigits = "",
|
364
|
+
num = +m.slice(1);
|
365
|
+
while (num > capturingGroupCount) {
|
366
|
+
nonBackrefDigits = /[0-9]$/.exec(num)[0] + nonBackrefDigits;
|
367
|
+
num = Math.floor(num / 10); // Drop the last digit
|
368
|
+
}
|
369
|
+
if (num > 0) {
|
370
|
+
output += "<b>\\" + num + "</b>" + nonBackrefDigits;
|
371
|
+
} else {
|
372
|
+
var parts = /^\\([0-3][0-7]{0,2}|[4-7][0-7]?|[89])([0-9]*)/.exec(m);
|
373
|
+
output += "<b>\\" + parts[1] + "</b>" + parts[2];
|
374
|
+
}
|
375
|
+
lastToken = {quantifiable: true};
|
376
|
+
// Metasequence
|
377
|
+
} else if (/^[0bBcdDfnrsStuvwWx]/.test(char1)) {
|
378
|
+
/* Browsers differ on how they handle:
|
379
|
+
* - "\c", when not followed by A-Z or a-z.
|
380
|
+
* - "\x", when not followed by two hex characters.
|
381
|
+
* - "\u", when not followed by four hex characters.
|
382
|
+
* Hence, such metasequences are flagged as errors.
|
383
|
+
*/
|
384
|
+
if (/^\\[cux]$/.test(m)) {
|
385
|
+
output += errorize(m, error.INCOMPLETE_TOKEN);
|
386
|
+
lastToken = {quantifiable: false};
|
387
|
+
// Unquantifiable metasequence
|
388
|
+
} else if ("bB".indexOf(char1) > -1) {
|
389
|
+
output += "<b>" + m + "</b>";
|
390
|
+
lastToken = {quantifiable: false};
|
391
|
+
// Quantifiable metasequence
|
392
|
+
} else {
|
393
|
+
output += "<b>" + m + "</b>";
|
394
|
+
lastToken = {quantifiable: true};
|
395
|
+
}
|
396
|
+
// Unescaped "\" at the end of the regex
|
397
|
+
} else if (m === "\\") {
|
398
|
+
output += errorize(m, error.INCOMPLETE_TOKEN);
|
399
|
+
// Don't need to set lastToken since this is the end of the line
|
400
|
+
// Escaped literal character
|
401
|
+
} else {
|
402
|
+
output += expandHtmlEntities(m);
|
403
|
+
lastToken = {quantifiable: true};
|
404
|
+
}
|
405
|
+
// Quantifier
|
406
|
+
} else if (quantifier.test(m)) {
|
407
|
+
if (lastToken.quantifiable) {
|
408
|
+
var interval = /^\{([0-9]+)(?:,([0-9]*))?/.exec(m);
|
409
|
+
// Interval quantifier out of range for Firefox
|
410
|
+
if (interval && (+interval[1] > 65535 || (interval[2] && +interval[2] > 65535))) {
|
411
|
+
output += errorize(m, error.INTERVAL_OVERFLOW);
|
412
|
+
// Interval quantifier in reverse numeric order
|
413
|
+
} else if (interval && interval[2] && (+interval[1] > +interval[2])) {
|
414
|
+
output += errorize(m, error.INTERVAL_REVERSED);
|
415
|
+
} else {
|
416
|
+
// Quantifiers for groups are shown in the style of the (preceeding) group's depth
|
417
|
+
output += (lastToken.style ? '<b class="' + lastToken.style + '">' : '<b>') + m + '</b>';
|
418
|
+
}
|
419
|
+
} else {
|
420
|
+
output += errorize(m, error.UNQUANTIFIABLE);
|
421
|
+
}
|
422
|
+
lastToken = {quantifiable: false};
|
423
|
+
// Vertical bar (alternator)
|
424
|
+
} else if (m === "|") {
|
425
|
+
/* If there is a vertical bar at the very start of the regex, flag it as an error
|
426
|
+
* since it effectively truncates the regex at that point. If two top-level
|
427
|
+
* vertical bars are next to each other, flag it as an error for similar reasons.
|
428
|
+
*/
|
429
|
+
if (lastToken.type === type.NONE || (lastToken.type === type.ALTERNATOR && !openGroups.length)) {
|
430
|
+
output += errorize(m, error.IMPROPER_EMPTY_ALTERNATIVE);
|
431
|
+
} else {
|
432
|
+
// Alternators within groups are shown in the style of the containing group's depth
|
433
|
+
output += openGroups.length ? groupize("|", groupStyleDepth) : "<b>|</b>";
|
434
|
+
}
|
435
|
+
lastToken = {
|
436
|
+
quantifiable: false,
|
437
|
+
type: type.ALTERNATOR
|
438
|
+
};
|
439
|
+
// ^ or $ anchor
|
440
|
+
} else if (m === "^" || m === "$") {
|
441
|
+
output += "<b>" + m + "</b>";
|
442
|
+
lastToken = {quantifiable: false};
|
443
|
+
// Dot (.)
|
444
|
+
} else if (m === ".") {
|
445
|
+
output += "<b>.</b>";
|
446
|
+
lastToken = {quantifiable: true};
|
447
|
+
// Literal character sequence
|
448
|
+
} else {
|
449
|
+
output += expandHtmlEntities(m);
|
450
|
+
lastToken = {quantifiable: true};
|
451
|
+
}
|
452
|
+
} // End regexToken loop
|
453
|
+
|
454
|
+
// Mark the opening character sequence for each unclosed grouping as invalid
|
455
|
+
var numCharsAdded = 0, errorIndex, i;
|
456
|
+
for (i = 0; i < openGroups.length; i++) {
|
457
|
+
errorIndex = openGroups[i].index + numCharsAdded;
|
458
|
+
output = (
|
459
|
+
output.slice(0, errorIndex) +
|
460
|
+
errorize(openGroups[i].opening, error.UNBALANCED_LEFT_PAREN) +
|
461
|
+
output.slice(errorIndex + openGroups[i].opening.length)
|
462
|
+
);
|
463
|
+
numCharsAdded += errorize("", error.UNBALANCED_LEFT_PAREN).length;
|
464
|
+
}
|
465
|
+
|
466
|
+
return output;
|
467
|
+
};
|
468
|
+
|
469
|
+
/**
|
470
|
+
* Applies regex syntax highlighting to all elements on the page with the specified class.
|
471
|
+
* @memberOf RegexColorizer
|
472
|
+
* @param {String} [cls='regex'] Class name used by elements to be colorized.
|
473
|
+
* @example
|
474
|
+
*
|
475
|
+
* // Basic use
|
476
|
+
* RegexColorizer.colorizeAll();
|
477
|
+
*
|
478
|
+
* // With class name
|
479
|
+
* RegexColorizer.colorizeAll('my-class');
|
480
|
+
*/
|
481
|
+
self.colorizeAll = function (cls) {
|
482
|
+
cls = cls || "regex";
|
483
|
+
var els = elsByClass(cls),
|
484
|
+
len = els.length,
|
485
|
+
el, i;
|
486
|
+
for (i = 0; i < len; i++) {
|
487
|
+
el = els[i];
|
488
|
+
el.innerHTML = self.colorizeText(el.textContent || el.innerText);
|
489
|
+
}
|
490
|
+
};
|
491
|
+
|
492
|
+
/**
|
493
|
+
* Adds a stylesheet with the default regex highlighting styles to the page. If you provide your
|
494
|
+
* own stylesheet, you don't need to run this.
|
495
|
+
* @memberOf RegexColorizer
|
496
|
+
* @example
|
497
|
+
*
|
498
|
+
* RegexColorizer.addStyleSheet();
|
499
|
+
*/
|
500
|
+
self.addStyleSheet = function () {
|
501
|
+
var ss = document.createElement("style"),
|
502
|
+
rules =
|
503
|
+
".regex {font-family: Monospace;} " +
|
504
|
+
".regex b {background: #aad1f7;} " + // metasequence
|
505
|
+
".regex i {background: #e3e3e3;} " + // char class
|
506
|
+
".regex i b {background: #9fb6dc;} " + // char class: metasequence
|
507
|
+
".regex i u {background: #c3c3c3;} " + // char class: range-hyphen
|
508
|
+
".regex b.g1 {background: #b4fa50; color: #000;} " + // group: depth 1
|
509
|
+
".regex b.g2 {background: #8cd400; color: #000;} " + // group: depth 2
|
510
|
+
".regex b.g3 {background: #26b809; color: #fff;} " + // group: depth 3
|
511
|
+
".regex b.g4 {background: #30ea60; color: #000;} " + // group: depth 4
|
512
|
+
".regex b.g5 {background: #0c8d15; color: #fff;} " + // group: depth 5
|
513
|
+
".regex b.err {background: #e30000; color: #fff;} " + // error
|
514
|
+
".regex b, .regex i, .regex u {font-weight: normal; font-style: normal; text-decoration: none;}";
|
515
|
+
ss.id = "regex-colorizer-ss";
|
516
|
+
// Need to add to the DOM before setting cssText for IE < 9
|
517
|
+
document.getElementsByTagName("head")[0].appendChild(ss);
|
518
|
+
// Can't use innerHTML or innerText for stylesheets in IE < 9
|
519
|
+
if (ss.styleSheet) {
|
520
|
+
ss.styleSheet.cssText = rules;
|
521
|
+
} else {
|
522
|
+
ss.innerHTML = rules;
|
523
|
+
}
|
524
|
+
};
|
525
|
+
|
526
|
+
return self;
|
527
|
+
|
528
|
+
}());
|
@@ -0,0 +1,17 @@
|
|
1
|
+
//= require regex_field/regex_colorizer
|
2
|
+
|
3
|
+
$(document).ready(function () {
|
4
|
+
RegexColorizer.addStyleSheet();
|
5
|
+
RegexColorizer.colorizeAll();
|
6
|
+
|
7
|
+
$('.regex').on('focus', function() {
|
8
|
+
this.innerHTML = this.textContent || this.innerText;
|
9
|
+
});
|
10
|
+
|
11
|
+
$('.regex').on('blur', function() {
|
12
|
+
RegexColorizer.colorizeAll();
|
13
|
+
|
14
|
+
target = $(this).data('target');
|
15
|
+
$(this).parent().find("input[id$='" + target + "']").val(this.textContent || this.innerText);
|
16
|
+
});
|
17
|
+
});
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module RegexField
|
2
|
+
module FormBuilder
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def regex_field(name, options = {})
|
6
|
+
content_tag(:pre, object.send(name), class: 'regex', contenteditable: true, data: {target: name}) +
|
7
|
+
hidden_field(name, options)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/lib/regex_field.rb
ADDED
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: regex_field
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hugo Hache
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.2.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '5.2'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 4.2.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '5.2'
|
33
|
+
description: Add a regex field on Rails form helper
|
34
|
+
email: hugo.hache@fabernovel.com
|
35
|
+
executables: []
|
36
|
+
extensions: []
|
37
|
+
extra_rdoc_files: []
|
38
|
+
files:
|
39
|
+
- LICENSE
|
40
|
+
- app/assets/javascripts/regex_field.js
|
41
|
+
- app/assets/javascripts/regex_field/regex_colorizer.js
|
42
|
+
- lib/regex_field.rb
|
43
|
+
- lib/regex_field/engine.rb
|
44
|
+
- lib/regex_field/form_builder.rb
|
45
|
+
- lib/regex_field/version.rb
|
46
|
+
homepage: https://github.com/applidium/regex_field
|
47
|
+
licenses:
|
48
|
+
- MIT
|
49
|
+
metadata: {}
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
requirements: []
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 2.6.13
|
67
|
+
signing_key:
|
68
|
+
specification_version: 4
|
69
|
+
summary: Regex field
|
70
|
+
test_files: []
|