--- /dev/null
+/**
+ Customizing MARC record data fields based on Alexander O'Neill's work
+ 2009 Guoying Liu
+ */
+
+function rdetailShowCustomizedMARCs( ) {
+ loadMARCRecord();
+}
+
+var MARCRequest;
+var marctags;
+var firstResultRow;
+
+function loadMARCRecord( ) {
+ MARCRequest = null;
+
+ // Code for all new browsers
+ if (window.XMLHttpRequest)
+ MARCRequest = new XMLHttpRequest();
+ // Code for IE 5 and 6
+ else if ( window.ActiveXObject )
+ MARCRequest = new ActiveXObject( "Microsoft.XMLHTTP" );
+
+ if ( MARCRequest != null ) {
+ MARCRequest.onreadystatechange = marcrec_State_Change;
+ MARCRequest.open( "GET", "/opac/extras/supercat/retrieve/marcxml/record/" + record.doc_id(), true );
+ MARCRequest.send(null);
+ }
+}
+
+function marcrec_State_Change() {
+ var hasFirstISSN = false;
+
+ // 4 means "loaded"
+ if ( MARCRequest.readyState == 4 ) {
+ // 200 means "OK"
+ if ( MARCRequest.status == 200 ) {
+ marctags = document.getElementsByTagName("MARC");
+
+ for ( i = 0; i < marctags.length; i++ ) {
+ dataField = marctags.item(i).getAttribute('dataField');
+ controlField = marctags.item(i).getAttribute('controlField');
+ marcItems = getElementsByAttribute( MARCRequest.responseXML, 'controlfield', 'tag', controlField );
+
+ if ( marcItems.length > 0 ) {
+ // This is a control field, which has no subfields, rather than a data field.
+ itemString = getText( marcItems[0] );
+ currentField = document.createElement( 'span' );
+
+ setText( currentField, itemString );
+ marctags[i].parentNode.appendChild( currentField );
+ continue;
+ }
+ marcItems = getElementsByAttribute( MARCRequest.responseXML, 'datafield', 'tag', dataField );
+
+ buildSearchString = ( marctags[i].getAttribute( 'searchfield' ) != null ? true : false );
+
+ for ( j = 0; j < marcItems.length; j++ ) {
+ nextItem = getElementsByAttribute( marcItems[j], 'subfield', 'code', ( marctags[i].getAttribute('subfield') != null ? marctags[i].getAttribute('subfield') : '*' ) );
+
+ for ( k = 0; k < nextItem.length; k++ ) {
+ itemString = nextItem[k].firstChild.nodeValue;
+ currentSubField = document.createElement( (buildSearchString ? 'a' : 'span') );
+ setText( currentSubField, itemString );
+ marctags[i].parentNode.appendChild( currentSubField );
+
+ //show SFX Button if dataField "022" has value;
+ if (dataField == "022"){
+ if(hasFirstISSN == false){
+ rdetailShowSFXButton( itemString );
+ hasFirstISSN = true;
+ }
+ }
+
+ if ( buildSearchString ) {
+ href = '../xml/rresult.xml?rt=' + marctags[i].getAttribute('searchfield') + '&tp=' + marctags[i].getAttribute('searchfield') + '&t=';
+
+ for ( l = 0; l <= k; l++ ) {
+ href += nextItem[l].firstChild.nodeValue + '%20'; // it's ok to have a space at the end.
+ }
+ href += '&l=1&d=0&f=&av=';
+ currentSubField.setAttribute('href', href);
+ currentSubField.setAttribute('title', 'Perform a search on this subject' );
+ }
+
+ separatorItem = document.createElement('span');
+
+ if ( k < nextItem.length - 1) {
+ setText( separatorItem, ( marctags[i].getAttribute('separator') != null ? ' ' + marctags[i].getAttribute('separator') + ' ' : ' ' ) );
+ marctags[i].parentNode.appendChild( separatorItem );
+ } else {
+ if ( marctags[i].getAttribute( 'newline' ) != null ) {
+ if ( marctags[i].getAttribute( 'newline' ) == 'no' ){
+ setText( separatorItem, ' ' );
+ marctags[i].parentNode.appendChild( separatorItem );
+ } else {
+ marctags[i].parentNode.appendChild( document.createElement('br') );
+ }
+ } else {
+ marctags[i].parentNode.appendChild( document.createElement('br') );
+ }
+ }
+ }//for k
+
+ } // for j
+ if ( getText(marctags[i].parentNode).replace(/^\s+|\s+$/g, '') == '' ) {
+ hideMe(marctags[i].parentNode.parentNode);
+ }
+ }
+ }
+ }
+}
+
+function getElementsByAttribute(oElm, strTagName, strAttributeName, strAttributeValue) {
+ var arrElements = oElm.getElementsByTagName(strTagName );
+ var arrReturnElements = new Array();
+ var oAttributeValue = new XRegExp( "(^|\\s)" + strAttributeValue + "(\\s|$)", "i" );
+ var oCurrent;
+ var oAttribute;
+
+ for ( var i=0; i < arrElements.length; i++ ) {
+ oCurrent = arrElements[i];
+ oAttribute = oCurrent.getAttribute( strAttributeName );
+ if( oAttribute != null && oAttribute.length > 0)
+ if( oAttributeValue && oAttributeValue.test( oAttribute ) )
+ arrReturnElements.push(oCurrent);
+ }
+ return arrReturnElements;
+}
+
+function getText( control ) {
+ if ( control == null )
+ return '';
+
+ if (document.all)
+ return control.innerText;
+ else
+ return trimStr(control.textContent);
+}
+
+function setText(control, value) {
+ if (document.all)
+ control.innerText = value;
+ else
+ control.textContent = value;
+}
+
+function trimStr( str ) {
+ if (str == null)
+ return '';
+ return str.replace(/^\s+|\s+$/g, '');
+}
--- /dev/null
+/*\r
+ XRegExp 0.6.1\r
+ (c) 2007-2008 Steven Levithan\r
+ <http://stevenlevithan.com/regex/xregexp/>\r
+ MIT License\r
+*/\r
+\r
+/** provides an augmented, cross-browser implementation of regular expressions\r
+ including support for additional modifiers and syntax. several convenience\r
+ methods and a recursive-construct parser are also included.\r
+*/\r
+\r
+// prevent running twice, which would break references to native globals\r
+if (!window.XRegExp) {\r
+// anonymous function to avoid global variables\r
+(function () {\r
+// copy various native globals for reference. can't use the name ``native``\r
+// because it's a reserved JavaScript keyword.\r
+var real = {\r
+ exec: RegExp.prototype.exec,\r
+ match: String.prototype.match,\r
+ replace: String.prototype.replace,\r
+ split: String.prototype.split\r
+ },\r
+ /* regex syntax parsing with support for all the necessary cross-\r
+ browser and context issues (escapings, character classes, etc.) */\r
+ lib = {\r
+ part: /(?:[^\\([#\s.]+|\\(?!k<[\w$]+>|[pP]{[^}]+})[\S\s]?|\((?=\?(?!#|<[\w$]+>)))+|(\()(?:\?(?:(#)[^)]*\)|<([$\w]+)>))?|\\(?:k<([\w$]+)>|[pP]{([^}]+)})|(\[\^?)|([\S\s])/g,\r
+ replaceVar: /(?:[^$]+|\$(?![1-9$&`']|{[$\w]+}))+|\$(?:([1-9]\d*|[$&`'])|{([$\w]+)})/g,\r
+ extended: /^(?:\s+|#.*)+/,\r
+ quantifier: /^(?:[?*+]|{\d+(?:,\d*)?})/,\r
+ classLeft: /&&\[\^?/g,\r
+ classRight: /]/g\r
+ },\r
+ indexOf = function (array, item, from) {\r
+ for (var i = from || 0; i < array.length; i++)\r
+ if (array[i] === item) return i;\r
+ return -1;\r
+ },\r
+ brokenExecUndef = /()??/.exec("")[1] !== undefined,\r
+ plugins = {};\r
+\r
+/*** XRegExp\r
+ accepts a pattern and flags, returns a new, extended RegExp object.\r
+ differs from a native regex in that additional flags and syntax are\r
+ supported and browser inconsistencies are ameliorated.\r
+*/\r
+XRegExp = function (pattern, flags) {\r
+ if (pattern instanceof RegExp) {\r
+ if (flags !== undefined)\r
+ throw TypeError("can't supply flags when constructing one RegExp from another");\r
+ return pattern.addFlags(); // new copy\r
+ }\r
+\r
+ var flags = flags || "",\r
+ singleline = flags.indexOf("s") > -1,\r
+ extended = flags.indexOf("x") > -1,\r
+ hasNamedCapture = false,\r
+ captureNames = [],\r
+ output = [],\r
+ part = lib.part,\r
+ match, cc, len, index, regex;\r
+\r
+ part.lastIndex = 0; // in case the last XRegExp compilation threw an error (unbalanced character class)\r
+\r
+ while (match = real.exec.call(part, pattern)) {\r
+ // comment pattern. this check must come before the capturing group check,\r
+ // because both match[1] and match[2] will be non-empty.\r
+ if (match[2]) {\r
+ // keep tokens separated unless the following token is a quantifier\r
+ if (!lib.quantifier.test(pattern.slice(part.lastIndex)))\r
+ output.push("(?:)");\r
+ // capturing group\r
+ } else if (match[1]) {\r
+ captureNames.push(match[3] || null);\r
+ if (match[3])\r
+ hasNamedCapture = true;\r
+ output.push("(");\r
+ // named backreference\r
+ } else if (match[4]) {\r
+ index = indexOf(captureNames, match[4]);\r
+ // keep backreferences separate from subsequent literal numbers\r
+ // preserve backreferences to named groups that are undefined at this point as literal strings\r
+ output.push(index > -1 ?\r
+ "\\" + (index + 1) + (isNaN(pattern.charAt(part.lastIndex)) ? "" : "(?:)") :\r
+ match[0]\r
+ );\r
+ // unicode element (requires plugin)\r
+ } else if (match[5]) {\r
+ output.push(plugins.unicode ?\r
+ plugins.unicode.get(match[5], match[0].charAt(1) === "P") :\r
+ match[0]\r
+ );\r
+ // character class opening delimiter ("[" or "[^")\r
+ // (non-native unicode elements are not supported within character classes)\r
+ } else if (match[6]) {\r
+ if (pattern.charAt(part.lastIndex) === "]") {\r
+ // for cross-browser compatibility with ECMA-262 v3 behavior,\r
+ // convert [] to (?!) and [^] to [\S\s].\r
+ output.push(match[6] === "[" ? "(?!)" : "[\\S\\s]");\r
+ part.lastIndex++;\r
+ } else {\r
+ // parse the character class with support for inner escapes and\r
+ // ES4's infinitely nesting intersection syntax ([&&[^&&[]]]).\r
+ cc = XRegExp.matchRecursive("&&" + pattern.slice(match.index), lib.classLeft, lib.classRight, "", {escapeChar: "\\"})[0];\r
+ output.push(match[6] + cc + "]");\r
+ part.lastIndex += cc.length + 1;\r
+ }\r
+ // dot ("."), pound sign ("#"), or whitespace character\r
+ } else if (match[7]) {\r
+ if (singleline && match[7] === ".") {\r
+ output.push("[\\S\\s]");\r
+ } else if (extended && lib.extended.test(match[7])) {\r
+ len = real.exec.call(lib.extended, pattern.slice(part.lastIndex - 1))[0].length;\r
+ // keep tokens separated unless the following token is a quantifier\r
+ if (!lib.quantifier.test(pattern.slice(part.lastIndex - 1 + len)))\r
+ output.push("(?:)");\r
+ part.lastIndex += len - 1;\r
+ } else {\r
+ output.push(match[7]);\r
+ }\r
+ } else {\r
+ output.push(match[0]);\r
+ }\r
+ }\r
+\r
+ regex = RegExp(output.join(""), real.replace.call(flags, /[sx]+/g, ""));\r
+ regex._x = {\r
+ source: pattern,\r
+ captureNames: hasNamedCapture ? captureNames : null\r
+ };\r
+ return regex;\r
+};\r
+\r
+// barebones plugin support for now (intentionally undocumented)\r
+XRegExp.addPlugin = function (name, o) {\r
+ plugins[name] = o;\r
+};\r
+\r
+/*** RegExp.prototype.exec\r
+ adds named capture support, with values returned as ``result.name``.\r
+ also fixes two cross-browser issues, following the ECMA-262 v3 spec:\r
+ - captured values for non-participating capturing groups should be returned\r
+ as ``undefined``, rather than the empty string.\r
+ - the regex's ``lastIndex`` should not be incremented after zero-length\r
+ matches.\r
+*/\r
+RegExp.prototype.exec = function (str) {\r
+ var match = real.exec.call(this, str),\r
+ name, i, r2;\r
+ if (match) {\r
+ // fix browsers whose exec methods don't consistently return\r
+ // undefined for non-participating capturing groups\r
+ if (brokenExecUndef && match.length > 1) {\r
+ // r2 doesn't need /g or /y, but they shouldn't hurt\r
+ r2 = new RegExp("^" + this.source + "$(?!\\s)", this.getNativeFlags());\r
+ real.replace.call(match[0], r2, function () {\r
+ for (i = 1; i < arguments.length - 2; i++) {\r
+ if (arguments[i] === undefined) match[i] = undefined;\r
+ }\r
+ });\r
+ }\r
+ // attach named capture properties\r
+ if (this._x && this._x.captureNames) {\r
+ for (i = 1; i < match.length; i++) {\r
+ name = this._x.captureNames[i - 1];\r
+ if (name) match[name] = match[i];\r
+ }\r
+ }\r
+ // fix browsers that increment lastIndex after zero-length matches\r
+ if (this.global && this.lastIndex > (match.index + match[0].length))\r
+ this.lastIndex--;\r
+ }\r
+ return match;\r
+};\r
+\r
+/*** String.prototype.match\r
+ run the altered ``exec`` when called with a non-global regex.\r
+*/\r
+String.prototype.match = function (regex) {\r
+ if (!(regex instanceof RegExp))\r
+ regex = new XRegExp(regex);\r
+ if (regex.global)\r
+ return real.match.call(this, regex);\r
+ return regex.exec(this); // run the altered exec\r
+};\r
+\r
+/*** String.prototype.replace\r
+ add named capture support to replacement strings using the syntax\r
+ ``${name}``, and to replacement functions as ``arguments[0].name``.\r
+*/\r
+String.prototype.replace = function (search, replacement) {\r
+ var captureNames = (search._x || {}).captureNames;\r
+\r
+ // if search is not a regex which uses named capture, use the native replace method\r
+ if (!(search instanceof RegExp && captureNames))\r
+ return real.replace.apply(this, arguments);\r
+\r
+ if (typeof replacement === "function") {\r
+ return real.replace.call(this, search, function () {\r
+ // change the arguments[0] string primitive to a String object which can store properties\r
+ arguments[0] = new String(arguments[0]);\r
+ // store named backreferences on arguments[0] before calling replacement\r
+ for (var i = 0; i < captureNames.length; i++) {\r
+ if (captureNames[i])\r
+ arguments[0][captureNames[i]] = arguments[i + 1];\r
+ }\r
+ return replacement.apply(window, arguments);\r
+ });\r
+ } else {\r
+ return real.replace.call(this, search, function () {\r
+ var args = arguments;\r
+ return real.replace.call(replacement, lib.replaceVar, function ($0, $1, $2) {\r
+ // numbered backreference or special variable\r
+ if ($1) {\r
+ switch ($1) {\r
+ case "$": return "$";\r
+ case "&": return args[0];\r
+ case "`": return args[args.length - 1].slice(0, args[args.length - 2]);\r
+ case "'": return args[args.length - 1].slice(args[args.length - 2] + args[0].length);\r
+ // numbered backreference\r
+ default:\r
+ /* what does "$10" mean?\r
+ - backreference 10, if 10 or more capturing groups exist\r
+ - backreference 1 followed by "0", if 1-9 capturing groups exist\r
+ - otherwise, it's the string "$10"\r
+ */\r
+ var literalNumbers = "";\r
+ $1 = +$1; // type-convert\r
+ while ($1 > captureNames.length) {\r
+ literalNumbers = real.split.call($1, "").pop() + literalNumbers;\r
+ $1 = Math.floor($1 / 10); // drop the last digit\r
+ }\r
+ return ($1 ? args[$1] : "$") + literalNumbers;\r
+ }\r
+ // named backreference\r
+ } else if ($2) {\r
+ /* what does "${name}" mean?\r
+ - backreference to named capture "name", if it exists\r
+ - otherwise, it's the string "${name}"\r
+ */\r
+ var index = indexOf(captureNames, $2);\r
+ return index > -1 ? args[index + 1] : $0;\r
+ } else {\r
+ return $0;\r
+ }\r
+ });\r
+ });\r
+ }\r
+};\r
+\r
+/*** String.prototype.split\r
+ a consistent cross-browser, ECMA-262 v3 compliant split method\r
+*/\r
+String.prototype.split = function (s /* separator */, limit) {\r
+ // if separator is not a regex, use the native split method\r
+ if (!(s instanceof RegExp))\r
+ return real.split.apply(this, arguments);\r
+\r
+ var output = [],\r
+ origLastIndex = s.lastIndex,\r
+ lastLastIndex = 0,\r
+ i = 0, match, lastLength;\r
+\r
+ /* behavior for limit: if it's...\r
+ - undefined: no limit\r
+ - NaN or zero: return an empty array\r
+ - a positive number: use limit after dropping any decimal\r
+ - a negative number: no limit\r
+ - other: type-convert, then use the above rules\r
+ */\r
+ if (limit === undefined || +limit < 0) {\r
+ limit = false;\r
+ } else {\r
+ limit = Math.floor(+limit);\r
+ if (!limit)\r
+ return [];\r
+ }\r
+\r
+ if (s.global)\r
+ s.lastIndex = 0;\r
+ else\r
+ s = s.addFlags("g");\r
+\r
+ while ((!limit || i++ <= limit) && (match = s.exec(this))) { // run the altered exec!\r
+ if (s.lastIndex > lastLastIndex) {\r
+ output = output.concat(this.slice(lastLastIndex, match.index));\r
+ if (1 < match.length && match.index < this.length)\r
+ output = output.concat(match.slice(1));\r
+ lastLength = match[0].length; // only needed if s.lastIndex === this.length\r
+ lastLastIndex = s.lastIndex;\r
+ }\r
+ if (!match[0].length)\r
+ s.lastIndex++; // avoid an infinite loop\r
+ }\r
+\r
+ // since this uses test(), output must be generated before restoring lastIndex\r
+ output = lastLastIndex === this.length ?\r
+ (s.test("") && !lastLength ? output : output.concat("")) :\r
+ (limit ? output : output.concat(this.slice(lastLastIndex)));\r
+ s.lastIndex = origLastIndex; // only needed if s.global, else we're working with a copy of the regex\r
+ return output;\r
+};\r
+})(); // end anonymous function\r
+} // end if(!window.XRegExp)\r
+\r
+// intentionally undocumented\r
+RegExp.prototype.getNativeFlags = function () {\r
+ return (this.global ? "g" : "") +\r
+ (this.ignoreCase ? "i" : "") +\r
+ (this.multiline ? "m" : "") +\r
+ (this.extended ? "x" : "") +\r
+ (this.sticky ? "y" : "");\r
+};\r
+\r
+/*** RegExp.prototype.addFlags\r
+ accepts flags; returns a new XRegExp object generated by recompiling\r
+ the regex with the additional flags (may include non-native flags).\r
+ the original regex object is not altered.\r
+*/\r
+RegExp.prototype.addFlags = function (flags) {\r
+ var regex = new XRegExp(this.source, (flags || "") + this.getNativeFlags());\r
+ if (this._x) {\r
+ regex._x = {\r
+ source: this._x.source,\r
+ captureNames: this._x.captureNames ? this._x.captureNames.slice(0) : null\r
+ };\r
+ }\r
+ return regex;\r
+};\r
+\r
+/*** RegExp.prototype.call\r
+ accepts a context object and string; returns the result of calling\r
+ ``exec`` with the provided string. the context is ignored but is\r
+ accepted for congruity with ``Function.prototype.call``.\r
+*/\r
+RegExp.prototype.call = function (context, str) {\r
+ return this.exec(str);\r
+};\r
+\r
+/*** RegExp.prototype.apply\r
+ accepts a context object and arguments array; returns the result of\r
+ calling ``exec`` with the first value in the arguments array. the context\r
+ is ignored but is accepted for congruity with ``Function.prototype.apply``.\r
+*/\r
+RegExp.prototype.apply = function (context, args) {\r
+ return this.exec(args[0]);\r
+};\r
+\r
+/*** XRegExp.cache\r
+ accepts a pattern and flags; returns an XRegExp object. if the pattern\r
+ and flag combination has previously been cached, the cached copy is\r
+ returned, otherwise the new object is cached.\r
+*/\r
+XRegExp.cache = function (pattern, flags) {\r
+ var key = "/" + pattern + "/" + (flags || "");\r
+ return XRegExp.cache[key] || (XRegExp.cache[key] = new XRegExp(pattern, flags));\r
+};\r
+\r
+/*** XRegExp.escape\r
+ accepts a string; returns the string with regex metacharacters escaped.\r
+ the returned string can safely be used within a regex to match a literal\r
+ string. escaped characters are [, ], {, }, (, ), -, *, +, ?, ., \, ^, $,\r
+ |, #, [comma], and whitespace.\r
+*/\r
+XRegExp.escape = function (str) {\r
+ return str.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, "\\$&");\r
+};\r
+\r
+/*** XRegExp.matchRecursive\r
+ accepts a string to search, left and right delimiters as regex pattern\r
+ strings, optional regex flags (may include non-native s, x, and y flags),\r
+ and an options object which allows setting an escape character and changing\r
+ the return format from an array of matches to a two-dimensional array of\r
+ string parts with extended position data. returns an array of matches\r
+ (optionally with extended data), allowing nested instances of left and right\r
+ delimiters. use the g flag to return all matches, otherwise only the first\r
+ is returned. if delimiters are unbalanced within the subject data, an error\r
+ is thrown.\r
+\r
+ this function admittedly pushes the boundaries of what can be accomplished\r
+ sensibly without a "real" parser. however, by doing so it provides flexible\r
+ and powerful recursive parsing capabilities with minimal code weight.\r
+\r
+ warning: the ``escapeChar`` option is considered experimental and might be\r
+ changed or removed in future versions of XRegExp.\r
+\r
+ unsupported features:\r
+ - backreferences within delimiter patterns when using ``escapeChar``.\r
+ - although providing delimiters as regex objects adds the minor feature of\r
+ independent delimiter flags, it introduces other limitations and is only\r
+ intended to be done by the ``XRegExp`` constructor (which can't call\r
+ itself while building a regex).\r
+*/\r
+XRegExp.matchRecursive = function (str, left, right, flags, options) {\r
+ var options = options || {},\r
+ escapeChar = options.escapeChar,\r
+ vN = options.valueNames,\r
+ flags = flags || "",\r
+ global = flags.indexOf("g") > -1,\r
+ ignoreCase = flags.indexOf("i") > -1,\r
+ multiline = flags.indexOf("m") > -1,\r
+ sticky = flags.indexOf("y") > -1,\r
+ /* sticky mode has its own handling in this function, which means you\r
+ can use flag "y" even in browsers which don't support it natively */\r
+ flags = flags.replace(/y/g, ""),\r
+ left = left instanceof RegExp ? (left.global ? left : left.addFlags("g")) : new XRegExp(left, "g" + flags),\r
+ right = right instanceof RegExp ? (right.global ? right : right.addFlags("g")) : new XRegExp(right, "g" + flags),\r
+ output = [],\r
+ openTokens = 0,\r
+ delimStart = 0,\r
+ delimEnd = 0,\r
+ lastOuterEnd = 0,\r
+ outerStart, innerStart, leftMatch, rightMatch, escaped, esc;\r
+\r
+ if (escapeChar) {\r
+ if (escapeChar.length > 1) throw SyntaxError("can't supply more than one escape character");\r
+ if (multiline) throw TypeError("can't supply escape character when using the multiline flag");\r
+ escaped = XRegExp.escape(escapeChar);\r
+ /* Escape pattern modifiers:\r
+ /g - not needed here\r
+ /i - included\r
+ /m - **unsupported**, throws error\r
+ /s - handled by XRegExp when delimiters are provided as strings\r
+ /x - handled by XRegExp when delimiters are provided as strings\r
+ /y - not needed here; supported by other handling in this function\r
+ */\r
+ esc = new RegExp(\r
+ "^(?:" + escaped + "[\\S\\s]|(?:(?!" + left.source + "|" + right.source + ")[^" + escaped + "])+)+",\r
+ ignoreCase ? "i" : ""\r
+ );\r
+ }\r
+\r
+ while (true) {\r
+ /* advance the starting search position to the end of the last delimiter match.\r
+ a couple special cases are also covered:\r
+ - if using an escape character, advance to the next delimiter's starting position,\r
+ skipping any escaped characters\r
+ - first time through, reset lastIndex in case delimiters were provided as regexes\r
+ */\r
+ left.lastIndex = right.lastIndex = delimEnd +\r
+ (escapeChar ? (esc.exec(str.slice(delimEnd)) || [""])[0].length : 0);\r
+\r
+ leftMatch = left.exec(str);\r
+ rightMatch = right.exec(str);\r
+\r
+ // only keep the result which matched earlier in the string\r
+ if (leftMatch && rightMatch) {\r
+ if (leftMatch.index <= rightMatch.index)\r
+ rightMatch = null;\r
+ else leftMatch = null;\r
+ }\r
+\r
+ /* paths*:\r
+ leftMatch | rightMatch | openTokens | result\r
+ 1 | 0 | 1 | ...\r
+ 1 | 0 | 0 | ...\r
+ 0 | 1 | 1 | ...\r
+ 0 | 1 | 0 | throw\r
+ 0 | 0 | 1 | throw\r
+ 0 | 0 | 0 | break\r
+ * - does not include the sticky mode special case\r
+ - the loop ends after the first completed match if not in global mode\r
+ */\r
+\r
+ if (leftMatch || rightMatch) {\r
+ delimStart = (leftMatch || rightMatch).index;\r
+ delimEnd = (leftMatch ? left : right).lastIndex;\r
+ } else if (!openTokens) {\r
+ break;\r
+ }\r
+\r
+ if (sticky && !openTokens && delimStart > lastOuterEnd)\r
+ break;\r
+\r
+ if (leftMatch) {\r
+ if (!openTokens++) {\r
+ outerStart = delimStart;\r
+ innerStart = delimEnd;\r
+ }\r
+ } else if (rightMatch && openTokens) {\r
+ if (!--openTokens) {\r
+ if (vN) {\r
+ if (vN[0] && outerStart > lastOuterEnd)\r
+ output.push([vN[0], str.slice(lastOuterEnd, outerStart), lastOuterEnd, outerStart]);\r
+ if (vN[1]) output.push([vN[1], str.slice(outerStart, innerStart), outerStart, innerStart]);\r
+ if (vN[2]) output.push([vN[2], str.slice(innerStart, delimStart), innerStart, delimStart]);\r
+ if (vN[3]) output.push([vN[3], str.slice(delimStart, delimEnd), delimStart, delimEnd]);\r
+ } else {\r
+ output.push(str.slice(innerStart, delimStart));\r
+ }\r
+ lastOuterEnd = delimEnd;\r
+ if (!global)\r
+ break;\r
+ }\r
+ } else {\r
+ // reset lastIndex in case delimiters were provided as regexes\r
+ left.lastIndex = right.lastIndex = 0;\r
+ throw Error("subject data contains unbalanced delimiters");\r
+ }\r
+\r
+ // if the delimiter matched an empty string, advance delimEnd to avoid an infinite loop\r
+ if (delimStart === delimEnd)\r
+ delimEnd++;\r
+ }\r
+\r
+ if (global && !sticky && vN && vN[0] && str.length > lastOuterEnd)\r
+ output.push([vN[0], str.slice(lastOuterEnd), lastOuterEnd, str.length]);\r
+\r
+ // reset lastIndex in case delimiters were provided as regexes\r
+ left.lastIndex = right.lastIndex = 0;\r
+\r
+ return output;\r
+};\r
+\r