zpt
Version:
Zenon Page Templates - JS (ZPT-JS)
1,474 lines (1,284 loc) • 131 kB
JavaScript
/*!
* SyntaxHighlighter
* https://github.com/syntaxhighlighter/syntaxhighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 4.0.1 (Wed, 28 Mar 2018 11:12:42 GMT)
*
* @copyright
* Copyright (C) 2004-2016 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _core = __webpack_require__(1);
Object.keys(_core).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _core[key];
}
});
});
var _domready = __webpack_require__(26);
var _domready2 = _interopRequireDefault(_domready);
var _core2 = _interopRequireDefault(_core);
var _dasherize = __webpack_require__(27);
var dasherize = _interopRequireWildcard(_dasherize);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// configured through the `--compat` parameter.
if (false) {
require('./compatibility_layer_v3');
}
(0, _domready2.default)(function () {
return _core2.default.highlight(dasherize.object(window.syntaxhighlighterConfig || {}));
});
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var optsParser = __webpack_require__(2),
match = __webpack_require__(5),
Renderer = __webpack_require__(9).default,
utils = __webpack_require__(10),
transformers = __webpack_require__(11),
dom = __webpack_require__(17),
config = __webpack_require__(18),
defaults = __webpack_require__(19),
HtmlScript = __webpack_require__(20);
var sh = {
Match: match.Match,
Highlighter: __webpack_require__(22),
config: __webpack_require__(18),
regexLib: __webpack_require__(3).commonRegExp,
/** Internal 'global' variables. */
vars: {
discoveredBrushes: null,
highlighters: {}
},
/** This object is populated by user included external brush files. */
brushes: {},
/**
* Finds all elements on the page which should be processes by SyntaxHighlighter.
*
* @param {Object} globalParams Optional parameters which override element's
* parameters. Only used if element is specified.
*
* @param {Object} element Optional element to highlight. If none is
* provided, all elements in the current document
* are returned which qualify.
*
* @return {Array} Returns list of <code>{ target: DOMElement, params: Object }</code> objects.
*/
findElements: function findElements(globalParams, element) {
var elements = element ? [element] : utils.toArray(document.getElementsByTagName(sh.config.tagName)),
conf = sh.config,
result = [];
// support for <SCRIPT TYPE="syntaxhighlighter" /> feature
elements = elements.concat(dom.getSyntaxHighlighterScriptTags());
if (elements.length === 0) return result;
for (var i = 0, l = elements.length; i < l; i++) {
var item = {
target: elements[i],
// local params take precedence over globals
params: optsParser.defaults(optsParser.parse(elements[i].className), globalParams)
};
if (item.params['brush'] == null) continue;
result.push(item);
}
return result;
},
/**
* Shorthand to highlight all elements on the page that are marked as
* SyntaxHighlighter source code.
*
* @param {Object} globalParams Optional parameters which override element's
* parameters. Only used if element is specified.
*
* @param {Object} element Optional element to highlight. If none is
* provided, all elements in the current document
* are highlighted.
*/
highlight: function highlight(globalParams, element) {
var elements = sh.findElements(globalParams, element),
propertyName = 'innerHTML',
brush = null,
renderer,
conf = sh.config;
if (elements.length === 0) return;
for (var i = 0, l = elements.length; i < l; i++) {
var element = elements[i],
target = element.target,
params = element.params,
brushName = params.brush,
brush,
matches,
code;
if (brushName == null) continue;
brush = findBrush(brushName);
if (!brush) continue;
// local params take precedence over defaults
params = optsParser.defaults(params || {}, defaults);
params = optsParser.defaults(params, config);
// Instantiate a brush
if (params['html-script'] == true || defaults['html-script'] == true) {
brush = new HtmlScript(findBrush('xml'), brush);
brushName = 'htmlscript';
} else {
brush = new brush();
}
code = target[propertyName];
// remove CDATA from <SCRIPT/> tags if it's present
if (conf.useScriptTags) code = stripCData(code);
// Inject title if the attribute is present
if ((target.title || '') != '') params.title = target.title;
params['brush'] = brushName;
code = transformers(code, params);
matches = match.applyRegexList(code, brush.regexList, params);
renderer = new Renderer(code, matches, params);
element = dom.create('div');
element.innerHTML = renderer.getHtml();
// id = utils.guid();
// element.id = highlighters.id(id);
// highlighters.set(id, element);
if (params.quickCode) dom.attachEvent(dom.findElement(element, '.code'), 'dblclick', dom.quickCodeHandler);
// carry over ID
if ((target.id || '') != '') element.id = target.id;
target.parentNode.replaceChild(element, target);
}
}
}; // end of sh
/**
* Displays an alert.
* @param {String} str String to display.
*/
function alert(str) {
window.alert('SyntaxHighlighter\n\n' + str);
};
/**
* Finds a brush by its alias.
*
* @param {String} alias Brush alias.
* @param {Boolean} showAlert Suppresses the alert if false.
* @return {Brush} Returns bursh constructor if found, null otherwise.
*/
function findBrush(alias, showAlert) {
var brushes = sh.vars.discoveredBrushes,
result = null;
if (brushes == null) {
brushes = {};
// Find all brushes
for (var brushName in sh.brushes) {
var brush = sh.brushes[brushName],
aliases = brush.aliases;
if (aliases == null) {
continue;
}
brush.className = brush.className || brush.aliases[0];
brush.brushName = brush.className || brushName.toLowerCase();
for (var i = 0, l = aliases.length; i < l; i++) {
brushes[aliases[i]] = brushName;
}
}
sh.vars.discoveredBrushes = brushes;
}
result = sh.brushes[brushes[alias]];
if (result == null && showAlert) alert(sh.config.strings.noBrush + alias);
return result;
};
/**
* Strips <![CDATA[]]> from <SCRIPT /> content because it should be used
* there in most cases for XHTML compliance.
* @param {String} original Input code.
* @return {String} Returns code without leading <![CDATA[]]> tags.
*/
function stripCData(original) {
var left = '<![CDATA[',
right = ']]>',
// for some reason IE inserts some leading blanks here
copy = utils.trim(original),
changed = false,
leftLength = left.length,
rightLength = right.length;
if (copy.indexOf(left) == 0) {
copy = copy.substring(leftLength);
changed = true;
}
var copyLength = copy.length;
if (copy.indexOf(right) == copyLength - rightLength) {
copy = copy.substring(0, copyLength - rightLength);
changed = true;
}
return changed ? copy : original;
};
var brushCounter = 0;
exports.default = sh;
var registerBrush = exports.registerBrush = function registerBrush(brush) {
return sh.brushes['brush' + brushCounter++] = brush.default || brush;
};
var clearRegisteredBrushes = exports.clearRegisteredBrushes = function clearRegisteredBrushes() {
sh.brushes = {};
brushCounter = 0;
};
/* an EJS hook for `gulp build --brushes` command
* */
registerBrush(__webpack_require__(23));
registerBrush(__webpack_require__(24));
registerBrush(__webpack_require__(25));
/*
*/
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
var XRegExp = __webpack_require__(3).XRegExp;
var BOOLEANS = { 'true': true, 'false': false };
function camelize(key) {
return key.replace(/-(\w+)/g, function (match, word) {
return word.charAt(0).toUpperCase() + word.substr(1);
});
}
function process(value) {
var result = BOOLEANS[value];
return result == null ? value : result;
}
module.exports = {
defaults: function defaults(target, source) {
for (var key in source || {}) {
if (!target.hasOwnProperty(key)) target[key] = target[camelize(key)] = source[key];
}return target;
},
parse: function parse(str) {
var match,
key,
result = {},
arrayRegex = XRegExp("^\\[(?<values>(.*?))\\]$"),
pos = 0,
regex = XRegExp("(?<name>[\\w-]+)" + "\\s*:\\s*" + "(?<value>" + "[\\w%#-]+|" + // word
"\\[.*?\\]|" + // [] array
'".*?"|' + // "" string
"'.*?'" + // '' string
")\\s*;?", "g");
while ((match = XRegExp.exec(str, regex, pos)) != null) {
var value = match.value.replace(/^['"]|['"]$/g, '') // strip quotes from end of strings
;
// try to parse array value
if (value != null && arrayRegex.test(value)) {
var m = XRegExp.exec(value, arrayRegex);
value = m.values.length > 0 ? m.values.split(/\s*,\s*/) : [];
}
value = process(value);
result[match.name] = result[camelize(match.name)] = value;
pos = match.index + match[0].length;
}
return result;
}
};
/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.commonRegExp = exports.XRegExp = undefined;
var _xregexp = __webpack_require__(4);
var _xregexp2 = _interopRequireDefault(_xregexp);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.XRegExp = _xregexp2.default;
var commonRegExp = exports.commonRegExp = {
multiLineCComments: (0, _xregexp2.default)('/\\*.*?\\*/', 'gs'),
singleLineCComments: /\/\/.*$/gm,
singleLinePerlComments: /#.*$/gm,
doubleQuotedString: /"([^\\"\n]|\\.)*"/g,
singleQuotedString: /'([^\\'\n]|\\.)*'/g,
multiLineDoubleQuotedString: (0, _xregexp2.default)('"([^\\\\"]|\\\\.)*"', 'gs'),
multiLineSingleQuotedString: (0, _xregexp2.default)("'([^\\\\']|\\\\.)*'", 'gs'),
xmlComments: (0, _xregexp2.default)('(<|<)!--.*?--(>|>)', 'gs'),
url: /\w+:\/\/[\w-.\/?%&=:@;#]*/g,
phpScriptTags: { left: /(<|<)\?(?:=|php)?/g, right: /\?(>|>)/g, 'eof': true },
aspScriptTags: { left: /(<|<)%=?/g, right: /%(>|>)/g },
scriptScriptTags: { left: /(<|<)\s*script.*?(>|>)/gi, right: /(<|<)\/\s*script\s*(>|>)/gi }
};
/***/ }),
/* 4 */
/***/ (function(module, exports) {
/*!
* XRegExp 3.1.0-dev
* <xregexp.com>
* Steven Levithan (c) 2007-2015 MIT License
*/
/**
* XRegExp provides augmented, extensible regular expressions. You get additional regex syntax and
* flags, beyond what browsers support natively. XRegExp is also a regex utility belt with tools to
* make your client-side grepping simpler and more powerful, while freeing you from related
* cross-browser inconsistencies.
*/
'use strict';
/* ==============================
* Private variables
* ============================== */
// Property name used for extended regex instance data
var REGEX_DATA = 'xregexp';
// Optional features that can be installed and uninstalled
var features = {
astral: false,
natives: false
};
// Native methods to use and restore ('native' is an ES3 reserved keyword)
var nativ = {
exec: RegExp.prototype.exec,
test: RegExp.prototype.test,
match: String.prototype.match,
replace: String.prototype.replace,
split: String.prototype.split
};
// Storage for fixed/extended native methods
var fixed = {};
// Storage for regexes cached by `XRegExp.cache`
var regexCache = {};
// Storage for pattern details cached by the `XRegExp` constructor
var patternCache = {};
// Storage for regex syntax tokens added internally or by `XRegExp.addToken`
var tokens = [];
// Token scopes
var defaultScope = 'default';
var classScope = 'class';
// Regexes that match native regex syntax, including octals
var nativeTokens = {
// Any native multicharacter token in default scope, or any single character
'default': /\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u(?:[\dA-Fa-f]{4}|{[\dA-Fa-f]+})|c[A-Za-z]|[\s\S])|\(\?[:=!]|[?*+]\?|{\d+(?:,\d*)?}\??|[\s\S]/,
// Any native multicharacter token in character class scope, or any single character
'class': /\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u(?:[\dA-Fa-f]{4}|{[\dA-Fa-f]+})|c[A-Za-z]|[\s\S])|[\s\S]/
};
// Any backreference or dollar-prefixed character in replacement strings
var replacementToken = /\$(?:{([\w$]+)}|(\d\d?|[\s\S]))/g;
// Check for correct `exec` handling of nonparticipating capturing groups
var correctExecNpcg = nativ.exec.call(/()??/, '')[1] === undefined;
// Check for ES6 `u` flag support
var hasNativeU = function () {
var isSupported = true;
try {
new RegExp('', 'u');
} catch (exception) {
isSupported = false;
}
return isSupported;
}();
// Check for ES6 `y` flag support
var hasNativeY = function () {
var isSupported = true;
try {
new RegExp('', 'y');
} catch (exception) {
isSupported = false;
}
return isSupported;
}();
// Check for ES6 `flags` prop support
var hasFlagsProp = /a/.flags !== undefined;
// Tracker for known flags, including addon flags
var registeredFlags = {
g: true,
i: true,
m: true,
u: hasNativeU,
y: hasNativeY
};
// Shortcut to `Object.prototype.toString`
var toString = {}.toString;
/* ==============================
* Private functions
* ============================== */
/**
* Attaches extended data and `XRegExp.prototype` properties to a regex object.
*
* @private
* @param {RegExp} regex Regex to augment.
* @param {Array} captureNames Array with capture names, or `null`.
* @param {String} xSource XRegExp pattern used to generate `regex`, or `null` if N/A.
* @param {String} xFlags XRegExp flags used to generate `regex`, or `null` if N/A.
* @param {Boolean} [isInternalOnly=false] Whether the regex will be used only for internal
* operations, and never exposed to users. For internal-only regexes, we can improve perf by
* skipping some operations like attaching `XRegExp.prototype` properties.
* @returns {RegExp} Augmented regex.
*/
function augment(regex, captureNames, xSource, xFlags, isInternalOnly) {
var p;
regex[REGEX_DATA] = {
captureNames: captureNames
};
if (isInternalOnly) {
return regex;
}
// Can't auto-inherit these since the XRegExp constructor returns a nonprimitive value
if (regex.__proto__) {
regex.__proto__ = XRegExp.prototype;
} else {
for (p in XRegExp.prototype) {
// An `XRegExp.prototype.hasOwnProperty(p)` check wouldn't be worth it here, since
// this is performance sensitive, and enumerable `Object.prototype` or
// `RegExp.prototype` extensions exist on `regex.prototype` anyway
regex[p] = XRegExp.prototype[p];
}
}
regex[REGEX_DATA].source = xSource;
// Emulate the ES6 `flags` prop by ensuring flags are in alphabetical order
regex[REGEX_DATA].flags = xFlags ? xFlags.split('').sort().join('') : xFlags;
return regex;
}
/**
* Removes any duplicate characters from the provided string.
*
* @private
* @param {String} str String to remove duplicate characters from.
* @returns {String} String with any duplicate characters removed.
*/
function clipDuplicates(str) {
return nativ.replace.call(str, /([\s\S])(?=[\s\S]*\1)/g, '');
}
/**
* Copies a regex object while preserving extended data and augmenting with `XRegExp.prototype`
* properties. The copy has a fresh `lastIndex` property (set to zero). Allows adding and removing
* flags g and y while copying the regex.
*
* @private
* @param {RegExp} regex Regex to copy.
* @param {Object} [options] Options object with optional properties:
* <li>`addG` {Boolean} Add flag g while copying the regex.
* <li>`addY` {Boolean} Add flag y while copying the regex.
* <li>`removeG` {Boolean} Remove flag g while copying the regex.
* <li>`removeY` {Boolean} Remove flag y while copying the regex.
* <li>`isInternalOnly` {Boolean} Whether the copied regex will be used only for internal
* operations, and never exposed to users. For internal-only regexes, we can improve perf by
* skipping some operations like attaching `XRegExp.prototype` properties.
* @returns {RegExp} Copy of the provided regex, possibly with modified flags.
*/
function copyRegex(regex, options) {
if (!XRegExp.isRegExp(regex)) {
throw new TypeError('Type RegExp expected');
}
var xData = regex[REGEX_DATA] || {},
flags = getNativeFlags(regex),
flagsToAdd = '',
flagsToRemove = '',
xregexpSource = null,
xregexpFlags = null;
options = options || {};
if (options.removeG) {
flagsToRemove += 'g';
}
if (options.removeY) {
flagsToRemove += 'y';
}
if (flagsToRemove) {
flags = nativ.replace.call(flags, new RegExp('[' + flagsToRemove + ']+', 'g'), '');
}
if (options.addG) {
flagsToAdd += 'g';
}
if (options.addY) {
flagsToAdd += 'y';
}
if (flagsToAdd) {
flags = clipDuplicates(flags + flagsToAdd);
}
if (!options.isInternalOnly) {
if (xData.source !== undefined) {
xregexpSource = xData.source;
}
// null or undefined; don't want to add to `flags` if the previous value was null, since
// that indicates we're not tracking original precompilation flags
if (xData.flags != null) {
// Flags are only added for non-internal regexes by `XRegExp.globalize`. Flags are
// never removed for non-internal regexes, so don't need to handle it
xregexpFlags = flagsToAdd ? clipDuplicates(xData.flags + flagsToAdd) : xData.flags;
}
}
// Augment with `XRegExp.prototype` properties, but use the native `RegExp` constructor to
// avoid searching for special tokens. That would be wrong for regexes constructed by
// `RegExp`, and unnecessary for regexes constructed by `XRegExp` because the regex has
// already undergone the translation to native regex syntax
regex = augment(new RegExp(regex.source, flags), hasNamedCapture(regex) ? xData.captureNames.slice(0) : null, xregexpSource, xregexpFlags, options.isInternalOnly);
return regex;
}
/**
* Converts hexadecimal to decimal.
*
* @private
* @param {String} hex
* @returns {Number}
*/
function dec(hex) {
return parseInt(hex, 16);
}
/**
* Returns native `RegExp` flags used by a regex object.
*
* @private
* @param {RegExp} regex Regex to check.
* @returns {String} Native flags in use.
*/
function getNativeFlags(regex) {
return hasFlagsProp ? regex.flags :
// Explicitly using `RegExp.prototype.toString` (rather than e.g. `String` or
// concatenation with an empty string) allows this to continue working predictably when
// `XRegExp.proptotype.toString` is overriden
nativ.exec.call(/\/([a-z]*)$/i, RegExp.prototype.toString.call(regex))[1];
}
/**
* Determines whether a regex has extended instance data used to track capture names.
*
* @private
* @param {RegExp} regex Regex to check.
* @returns {Boolean} Whether the regex uses named capture.
*/
function hasNamedCapture(regex) {
return !!(regex[REGEX_DATA] && regex[REGEX_DATA].captureNames);
}
/**
* Converts decimal to hexadecimal.
*
* @private
* @param {Number|String} dec
* @returns {String}
*/
function hex(dec) {
return parseInt(dec, 10).toString(16);
}
/**
* Returns the first index at which a given value can be found in an array.
*
* @private
* @param {Array} array Array to search.
* @param {*} value Value to locate in the array.
* @returns {Number} Zero-based index at which the item is found, or -1.
*/
function indexOf(array, value) {
var len = array.length,
i;
for (i = 0; i < len; ++i) {
if (array[i] === value) {
return i;
}
}
return -1;
}
/**
* Determines whether a value is of the specified type, by resolving its internal [[Class]].
*
* @private
* @param {*} value Object to check.
* @param {String} type Type to check for, in TitleCase.
* @returns {Boolean} Whether the object matches the type.
*/
function isType(value, type) {
return toString.call(value) === '[object ' + type + ']';
}
/**
* Checks whether the next nonignorable token after the specified position is a quantifier.
*
* @private
* @param {String} pattern Pattern to search within.
* @param {Number} pos Index in `pattern` to search at.
* @param {String} flags Flags used by the pattern.
* @returns {Boolean} Whether the next token is a quantifier.
*/
function isQuantifierNext(pattern, pos, flags) {
return nativ.test.call(flags.indexOf('x') > -1 ?
// Ignore any leading whitespace, line comments, and inline comments
/^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/ :
// Ignore any leading inline comments
/^(?:\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})/, pattern.slice(pos));
}
/**
* Pads the provided string with as many leading zeros as needed to get to length 4. Used to produce
* fixed-length hexadecimal values.
*
* @private
* @param {String} str
* @returns {String}
*/
function pad4(str) {
while (str.length < 4) {
str = '0' + str;
}
return str;
}
/**
* Checks for flag-related errors, and strips/applies flags in a leading mode modifier. Offloads
* the flag preparation logic from the `XRegExp` constructor.
*
* @private
* @param {String} pattern Regex pattern, possibly with a leading mode modifier.
* @param {String} flags Any combination of flags.
* @returns {Object} Object with properties `pattern` and `flags`.
*/
function prepareFlags(pattern, flags) {
var i;
// Recent browsers throw on duplicate flags, so copy this behavior for nonnative flags
if (clipDuplicates(flags) !== flags) {
throw new SyntaxError('Invalid duplicate regex flag ' + flags);
}
// Strip and apply a leading mode modifier with any combination of flags except g or y
pattern = nativ.replace.call(pattern, /^\(\?([\w$]+)\)/, function ($0, $1) {
if (nativ.test.call(/[gy]/, $1)) {
throw new SyntaxError('Cannot use flag g or y in mode modifier ' + $0);
}
// Allow duplicate flags within the mode modifier
flags = clipDuplicates(flags + $1);
return '';
});
// Throw on unknown native or nonnative flags
for (i = 0; i < flags.length; ++i) {
if (!registeredFlags[flags.charAt(i)]) {
throw new SyntaxError('Unknown regex flag ' + flags.charAt(i));
}
}
return {
pattern: pattern,
flags: flags
};
}
/**
* Prepares an options object from the given value.
*
* @private
* @param {String|Object} value Value to convert to an options object.
* @returns {Object} Options object.
*/
function prepareOptions(value) {
var options = {};
if (isType(value, 'String')) {
XRegExp.forEach(value, /[^\s,]+/, function (match) {
options[match] = true;
});
return options;
}
return value;
}
/**
* Registers a flag so it doesn't throw an 'unknown flag' error.
*
* @private
* @param {String} flag Single-character flag to register.
*/
function registerFlag(flag) {
if (!/^[\w$]$/.test(flag)) {
throw new Error('Flag must be a single character A-Za-z0-9_$');
}
registeredFlags[flag] = true;
}
/**
* Runs built-in and custom regex syntax tokens in reverse insertion order at the specified
* position, until a match is found.
*
* @private
* @param {String} pattern Original pattern from which an XRegExp object is being built.
* @param {String} flags Flags being used to construct the regex.
* @param {Number} pos Position to search for tokens within `pattern`.
* @param {Number} scope Regex scope to apply: 'default' or 'class'.
* @param {Object} context Context object to use for token handler functions.
* @returns {Object} Object with properties `matchLength`, `output`, and `reparse`; or `null`.
*/
function runTokens(pattern, flags, pos, scope, context) {
var i = tokens.length,
leadChar = pattern.charAt(pos),
result = null,
match,
t;
// Run in reverse insertion order
while (i--) {
t = tokens[i];
if (t.leadChar && t.leadChar !== leadChar || t.scope !== scope && t.scope !== 'all' || t.flag && flags.indexOf(t.flag) === -1) {
continue;
}
match = XRegExp.exec(pattern, t.regex, pos, 'sticky');
if (match) {
result = {
matchLength: match[0].length,
output: t.handler.call(context, match, scope, flags),
reparse: t.reparse
};
// Finished with token tests
break;
}
}
return result;
}
/**
* Enables or disables implicit astral mode opt-in. When enabled, flag A is automatically added to
* all new regexes created by XRegExp. This causes an error to be thrown when creating regexes if
* the Unicode Base addon is not available, since flag A is registered by that addon.
*
* @private
* @param {Boolean} on `true` to enable; `false` to disable.
*/
function setAstral(on) {
features.astral = on;
}
/**
* Enables or disables native method overrides.
*
* @private
* @param {Boolean} on `true` to enable; `false` to disable.
*/
function setNatives(on) {
RegExp.prototype.exec = (on ? fixed : nativ).exec;
RegExp.prototype.test = (on ? fixed : nativ).test;
String.prototype.match = (on ? fixed : nativ).match;
String.prototype.replace = (on ? fixed : nativ).replace;
String.prototype.split = (on ? fixed : nativ).split;
features.natives = on;
}
/**
* Returns the object, or throws an error if it is `null` or `undefined`. This is used to follow
* the ES5 abstract operation `ToObject`.
*
* @private
* @param {*} value Object to check and return.
* @returns {*} The provided object.
*/
function toObject(value) {
// null or undefined
if (value == null) {
throw new TypeError('Cannot convert null or undefined to object');
}
return value;
}
/* ==============================
* Constructor
* ============================== */
/**
* Creates an extended regular expression object for matching text with a pattern. Differs from a
* native regular expression in that additional syntax and flags are supported. The returned object
* is in fact a native `RegExp` and works with all native methods.
*
* @class XRegExp
* @constructor
* @param {String|RegExp} pattern Regex pattern string, or an existing regex object to copy.
* @param {String} [flags] Any combination of flags.
* Native flags:
* <li>`g` - global
* <li>`i` - ignore case
* <li>`m` - multiline anchors
* <li>`u` - unicode (ES6)
* <li>`y` - sticky (Firefox 3+, ES6)
* Additional XRegExp flags:
* <li>`n` - explicit capture
* <li>`s` - dot matches all (aka singleline)
* <li>`x` - free-spacing and line comments (aka extended)
* <li>`A` - astral (requires the Unicode Base addon)
* Flags cannot be provided when constructing one `RegExp` from another.
* @returns {RegExp} Extended regular expression object.
* @example
*
* // With named capture and flag x
* XRegExp('(?<year> [0-9]{4} ) -? # year \n\
* (?<month> [0-9]{2} ) -? # month \n\
* (?<day> [0-9]{2} ) # day ', 'x');
*
* // Providing a regex object copies it. Native regexes are recompiled using native (not XRegExp)
* // syntax. Copies maintain extended data, are augmented with `XRegExp.prototype` properties, and
* // have fresh `lastIndex` properties (set to zero).
* XRegExp(/regex/);
*/
function XRegExp(pattern, flags) {
var context = {
hasNamedCapture: false,
captureNames: []
},
scope = defaultScope,
output = '',
pos = 0,
result,
token,
generated,
appliedPattern,
appliedFlags;
if (XRegExp.isRegExp(pattern)) {
if (flags !== undefined) {
throw new TypeError('Cannot supply flags when copying a RegExp');
}
return copyRegex(pattern);
}
// Copy the argument behavior of `RegExp`
pattern = pattern === undefined ? '' : String(pattern);
flags = flags === undefined ? '' : String(flags);
if (XRegExp.isInstalled('astral') && flags.indexOf('A') === -1) {
// This causes an error to be thrown if the Unicode Base addon is not available
flags += 'A';
}
if (!patternCache[pattern]) {
patternCache[pattern] = {};
}
if (!patternCache[pattern][flags]) {
// Check for flag-related errors, and strip/apply flags in a leading mode modifier
result = prepareFlags(pattern, flags);
appliedPattern = result.pattern;
appliedFlags = result.flags;
// Use XRegExp's tokens to translate the pattern to a native regex pattern.
// `appliedPattern.length` may change on each iteration if tokens use `reparse`
while (pos < appliedPattern.length) {
do {
// Check for custom tokens at the current position
result = runTokens(appliedPattern, appliedFlags, pos, scope, context);
// If the matched token used the `reparse` option, splice its output into the
// pattern before running tokens again at the same position
if (result && result.reparse) {
appliedPattern = appliedPattern.slice(0, pos) + result.output + appliedPattern.slice(pos + result.matchLength);
}
} while (result && result.reparse);
if (result) {
output += result.output;
pos += result.matchLength || 1;
} else {
// Get the native token at the current position
token = XRegExp.exec(appliedPattern, nativeTokens[scope], pos, 'sticky')[0];
output += token;
pos += token.length;
if (token === '[' && scope === defaultScope) {
scope = classScope;
} else if (token === ']' && scope === classScope) {
scope = defaultScope;
}
}
}
patternCache[pattern][flags] = {
// Cleanup token cruft: repeated `(?:)(?:)` and leading/trailing `(?:)`
pattern: nativ.replace.call(output, /\(\?:\)(?:[*+?]|\{\d+(?:,\d*)?})?\??(?=\(\?:\))|^\(\?:\)(?:[*+?]|\{\d+(?:,\d*)?})?\??|\(\?:\)(?:[*+?]|\{\d+(?:,\d*)?})?\??$/g, ''),
// Strip all but native flags
flags: nativ.replace.call(appliedFlags, /[^gimuy]+/g, ''),
// `context.captureNames` has an item for each capturing group, even if unnamed
captures: context.hasNamedCapture ? context.captureNames : null
};
}
generated = patternCache[pattern][flags];
return augment(new RegExp(generated.pattern, generated.flags), generated.captures, pattern, flags);
};
// Add `RegExp.prototype` to the prototype chain
XRegExp.prototype = new RegExp();
/* ==============================
* Public properties
* ============================== */
/**
* The XRegExp version number as a string containing three dot-separated parts. For example,
* '2.0.0-beta-3'.
*
* @static
* @memberOf XRegExp
* @type String
*/
XRegExp.version = '3.1.0-dev';
/* ==============================
* Public methods
* ============================== */
/**
* Extends XRegExp syntax and allows custom flags. This is used internally and can be used to
* create XRegExp addons. If more than one token can match the same string, the last added wins.
*
* @memberOf XRegExp
* @param {RegExp} regex Regex object that matches the new token.
* @param {Function} handler Function that returns a new pattern string (using native regex syntax)
* to replace the matched token within all future XRegExp regexes. Has access to persistent
* properties of the regex being built, through `this`. Invoked with three arguments:
* <li>The match array, with named backreference properties.
* <li>The regex scope where the match was found: 'default' or 'class'.
* <li>The flags used by the regex, including any flags in a leading mode modifier.
* The handler function becomes part of the XRegExp construction process, so be careful not to
* construct XRegExps within the function or you will trigger infinite recursion.
* @param {Object} [options] Options object with optional properties:
* <li>`scope` {String} Scope where the token applies: 'default', 'class', or 'all'.
* <li>`flag` {String} Single-character flag that triggers the token. This also registers the
* flag, which prevents XRegExp from throwing an 'unknown flag' error when the flag is used.
* <li>`optionalFlags` {String} Any custom flags checked for within the token `handler` that are
* not required to trigger the token. This registers the flags, to prevent XRegExp from
* throwing an 'unknown flag' error when any of the flags are used.
* <li>`reparse` {Boolean} Whether the `handler` function's output should not be treated as
* final, and instead be reparseable by other tokens (including the current token). Allows
* token chaining or deferring.
* <li>`leadChar` {String} Single character that occurs at the beginning of any successful match
* of the token (not always applicable). This doesn't change the behavior of the token unless
* you provide an erroneous value. However, providing it can increase the token's performance
* since the token can be skipped at any positions where this character doesn't appear.
* @example
*
* // Basic usage: Add \a for the ALERT control code
* XRegExp.addToken(
* /\\a/,
* function() {return '\\x07';},
* {scope: 'all'}
* );
* XRegExp('\\a[\\a-\\n]+').test('\x07\n\x07'); // -> true
*
* // Add the U (ungreedy) flag from PCRE and RE2, which reverses greedy and lazy quantifiers.
* // Since `scope` is not specified, it uses 'default' (i.e., transformations apply outside of
* // character classes only)
* XRegExp.addToken(
* /([?*+]|{\d+(?:,\d*)?})(\??)/,
* function(match) {return match[1] + (match[2] ? '' : '?');},
* {flag: 'U'}
* );
* XRegExp('a+', 'U').exec('aaa')[0]; // -> 'a'
* XRegExp('a+?', 'U').exec('aaa')[0]; // -> 'aaa'
*/
XRegExp.addToken = function (regex, handler, options) {
options = options || {};
var optionalFlags = options.optionalFlags,
i;
if (options.flag) {
registerFlag(options.flag);
}
if (optionalFlags) {
optionalFlags = nativ.split.call(optionalFlags, '');
for (i = 0; i < optionalFlags.length; ++i) {
registerFlag(optionalFlags[i]);
}
}
// Add to the private list of syntax tokens
tokens.push({
regex: copyRegex(regex, {
addG: true,
addY: hasNativeY,
isInternalOnly: true
}),
handler: handler,
scope: options.scope || defaultScope,
flag: options.flag,
reparse: options.reparse,
leadChar: options.leadChar
});
// Reset the pattern cache used by the `XRegExp` constructor, since the same pattern and
// flags might now produce different results
XRegExp.cache.flush('patterns');
};
/**
* Caches and returns the result of calling `XRegExp(pattern, flags)`. On any subsequent call with
* the same pattern and flag combination, the cached copy of the regex is returned.
*
* @memberOf XRegExp
* @param {String} pattern Regex pattern string.
* @param {String} [flags] Any combination of XRegExp flags.
* @returns {RegExp} Cached XRegExp object.
* @example
*
* while (match = XRegExp.cache('.', 'gs').exec(str)) {
* // The regex is compiled once only
* }
*/
XRegExp.cache = function (pattern, flags) {
if (!regexCache[pattern]) {
regexCache[pattern] = {};
}
return regexCache[pattern][flags] || (regexCache[pattern][flags] = XRegExp(pattern, flags));
};
// Intentionally undocumented
XRegExp.cache.flush = function (cacheName) {
if (cacheName === 'patterns') {
// Flush the pattern cache used by the `XRegExp` constructor
patternCache = {};
} else {
// Flush the regex cache populated by `XRegExp.cache`
regexCache = {};
}
};
/**
* Escapes any regular expression metacharacters, for use when matching literal strings. The result
* can safely be used at any point within a regex that uses any flags.
*
* @memberOf XRegExp
* @param {String} str String to escape.
* @returns {String} String with regex metacharacters escaped.
* @example
*
* XRegExp.escape('Escaped? <.>');
* // -> 'Escaped\?\ <\.>'
*/
XRegExp.escape = function (str) {
return nativ.replace.call(toObject(str), /[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
};
/**
* Executes a regex search in a specified string. Returns a match array or `null`. If the provided
* regex uses named capture, named backreference properties are included on the match array.
* Optional `pos` and `sticky` arguments specify the search start position, and whether the match
* must start at the specified position only. The `lastIndex` property of the provided regex is not
* used, but is updated for compatibility. Also fixes browser bugs compared to the native
* `RegExp.prototype.exec` and can be used reliably cross-browser.
*
* @memberOf XRegExp
* @param {String} str String to search.
* @param {RegExp} regex Regex to search with.
* @param {Number} [pos=0] Zero-based index at which to start the search.
* @param {Boolean|String} [sticky=false] Whether the match must start at the specified position
* only. The string `'sticky'` is accepted as an alternative to `true`.
* @returns {Array} Match array with named backreference properties, or `null`.
* @example
*
* // Basic use, with named backreference
* var match = XRegExp.exec('U+2620', XRegExp('U\\+(?<hex>[0-9A-F]{4})'));
* match.hex; // -> '2620'
*
* // With pos and sticky, in a loop
* var pos = 2, result = [], match;
* while (match = XRegExp.exec('<1><2><3><4>5<6>', /<(\d)>/, pos, 'sticky')) {
* result.push(match[1]);
* pos = match.index + match[0].length;
* }
* // result -> ['2', '3', '4']
*/
XRegExp.exec = function (str, regex, pos, sticky) {
var cacheKey = 'g',
addY = false,
match,
r2;
addY = hasNativeY && !!(sticky || regex.sticky && sticky !== false);
if (addY) {
cacheKey += 'y';
}
regex[REGEX_DATA] = regex[REGEX_DATA] || {};
// Shares cached copies with `XRegExp.match`/`replace`
r2 = regex[REGEX_DATA][cacheKey] || (regex[REGEX_DATA][cacheKey] = copyRegex(regex, {
addG: true,
addY: addY,
removeY: sticky === false,
isInternalOnly: true
}));
r2.lastIndex = pos = pos || 0;
// Fixed `exec` required for `lastIndex` fix, named backreferences, etc.
match = fixed.exec.call(r2, str);
if (sticky && match && match.index !== pos) {
match = null;
}
if (regex.global) {
regex.lastIndex = match ? r2.lastIndex : 0;
}
return match;
};
/**
* Executes a provided function once per regex match. Searches always start at the beginning of the
* string and continue until the end, regardless of the state of the regex's `global` property and
* initial `lastIndex`.
*
* @memberOf XRegExp
* @param {String} str String to search.
* @param {RegExp} regex Regex to search with.
* @param {Function} callback Function to execute for each match. Invoked with four arguments:
* <li>The match array, with named backreference properties.
* <li>The zero-based match index.
* <li>The string being traversed.
* <li>The regex object being used to traverse the string.
* @example
*
* // Extracts every other digit from a string
* var evens = [];
* XRegExp.forEach('1a2345', /\d/, function(match, i) {
* if (i % 2) evens.push(+match[0]);
* });
* // evens -> [2, 4]
*/
XRegExp.forEach = function (str, regex, callback) {
var pos = 0,
i = -1,
match;
while (match = XRegExp.exec(str, regex, pos)) {
// Because `regex` is provided to `callback`, the function could use the deprecated/
// nonstandard `RegExp.prototype.compile` to mutate the regex. However, since
// `XRegExp.exec` doesn't use `lastIndex` to set the search position, this can't lead
// to an infinite loop, at least. Actually, because of the way `XRegExp.exec` caches
// globalized versions of regexes, mutating the regex will not have any effect on the
// iteration or matched strings, which is a nice side effect that brings extra safety
callback(match, ++i, str, regex);
pos = match.index + (match[0].length || 1);
}
};
/**
* Copies a regex object and adds flag `g`. The copy maintains extended data, is augmented with
* `XRegExp.prototype` properties, and has a fresh `lastIndex` property (set to zero). Native
* regexes are not recompiled using XRegExp syntax.
*
* @memberOf XRegExp
* @param {RegExp} regex Regex to globalize.
* @returns {RegExp} Copy of the provided regex with flag `g` added.
* @example
*
* var globalCopy = XRegExp.globalize(/regex/);
* globalCopy.global; // -> true
*/
XRegExp.globalize = function (regex) {
return copyRegex(regex, { addG: true });
};
/**
* Installs optional features according to the specified options. Can be undone using
* {@link #XRegExp.uninstall}.
*
* @memberOf XRegExp
* @param {Object|String} options Options object or string.
* @example
*
* // With an options object
* XRegExp.install({
* // Enables support for astral code points in Unicode addons (implicitly sets flag A)
* astral: true,
*
* // Overrides native regex methods with fixed/extended versions that support named
* // backreferences and fix numerous cross-browser bugs
* natives: true
* });
*
* // With an options string
* XRegExp.install('astral natives');
*/
XRegExp.install = function (options) {
options = prepareOptions(options);
if (!features.astral && options.astral) {
setAstral(true);
}
if (!features.natives && options.natives) {
setNatives(true);
}
};
/**
* Checks whether an individual optional feature is installed.
*
* @memberOf XRegExp
* @param {String} feature Name of the feature to check. One of:
* <li>`natives`
* <li>`astral`
* @returns {Boolean} Whether the feature is installed.
* @example
*
* XRegExp.isInstalled('natives');
*/
XRegExp.isInstalled = function (feature) {
return !!features[feature];
};
/**
* Returns `true` if an object is a regex; `false` if it isn't. This works correctly for regexes
* created in another frame, when `instanceof` and `constructor` checks would fail.
*
* @memberOf XRegExp
* @param {*} value Object to check.
* @returns {Boolean} Whether the object is a `RegExp` object.
* @example
*
* XRegExp.isRegExp('string'); // -> false
* XRegExp.isRegExp(/regex/i); // -> true
* XRegExp.isRegExp(RegExp('^', 'm')); // -> true
* XRegExp.isRegExp(XRegExp('(?s).')); // -> true
*/
XRegExp.isRegExp = function (value) {
return toString.call(value) === '[object RegExp]';
//return isType(value, 'RegExp');
};
/**
* Returns the first matched string, or in global mode, an array containing all matched strings.
* This is essentially a more convenient re-implementation of `String.prototype.match` that gives
* the result types you actually want (string instead of `exec`-style array in match-first mode,
* and an empty array instead of `null` when no matches are found in match-all mode). It also lets
* you override flag g and ignore `lastIndex`, and fixes browser bugs.
*
* @memberOf XRegExp
* @param {String} str String to search.
* @param {RegExp} regex Regex to search with.
* @param {String} [scope='one'] Use 'one' to return the first match as a string. Use 'all' to
* return an array of all matched strings. If not explicitly specified and `regex` uses flag g,
* `scope` is 'all'.
* @returns {String|Array} In match-first mode: First match as a string, or `null`. In match-all
* mode: Array of all matched strings, or an empty array.
* @example
*
* // Match first
* XRegExp.match('abc', /\w/); // -> 'a'
* XRegExp.match('abc', /\w/g, 'one'); // -> 'a'
* XRegExp.match('abc', /x/g, 'one'); // -> null
*
* // Match all
* XRegExp.match('abc', /\w/g); // -> ['a', 'b', 'c']
* XRegExp.match('abc', /\w/, 'all'); // -> ['a', 'b', 'c']
* XRegExp.match('abc', /x/, 'all'); // -> []
*/
XRegExp.match = function (str, regex, scope) {
var global = regex.global && scope !== 'one' || scope === 'all',
cacheKey = (global ? 'g' : '') + (regex.sticky ? 'y' : '') || 'noGY',
result,
r2;
regex[REGEX_DATA] = regex[REGEX_DATA] || {};
// Shares cached copies with `XRegExp.exec`/`replace`
r2 = regex[REGEX_DATA][cacheKey] || (regex[REGEX_DATA][cacheKey] = copyRegex(regex, {
addG: !!global,
addY: !!regex.sticky,
removeG: scope === 'one',
isInternalOnly: true
}));
result = nativ.match.call(toObject(str), r2);
if (regex.global) {
regex.lastIndex = scope === 'one' && result ?
// Can't use `r2.lastIndex` since `r2` is nonglobal in this case
result.index + result[0].length : 0;
}
return global ? result || [] : result && result[0];
};
/**
* Retrieves the matches from searching a string using a chain of regexes that successively search
* within previous matches. The provided `chain` array can contain regexes and or objects with
* `regex` and `backref` properties. When a backreference is specified, the named or numbered
* backreference is passed forward to the next regex or returned.
*
* @memberO