PageSourceSearch

https://alexandria.ch/discovery/lib/citeproc.js?version=22f63e4b92

js alexandria.ch collected 2026-10-02 08:31:24 UTC 970,499 bytes, 25,088 lines download raw bytes

vendor: 5,150 bytes, lines 1-141
1(function webpackUniversalModuleDefinition(root, factory) {
2	if(typeof exports === 'object' && typeof module === 'object')
3		module.exports = factory();
4	else if(typeof define === 'function' && define.amd)
5		define([], factory);
6	else if(typeof exports === 'object')
7		exports["citeproc"] = factory();
8	else
9		root["citeproc"] = factory();
10})(typeof self !== 'undefined' ? self : this, function() {
11return /******/ (function(modules) { // webpackBootstrap
12/******/ 	// The module cache
13/******/ 	var installedModules = {};
14/******/
15/******/ 	// The require function
16/******/ 	function __webpack_require__(moduleId) {
17/******/
18/******/ 		// Check if module is in cache
19/******/ 		if(installedModules[moduleId]) {
20/******/ 			return installedModules[moduleId].exports;
21/******/ 		}
22/******/ 		// Create a new module (and put it into the cache)
23/******/ 		var module = installedModules[moduleId] = {
24/******/ 			i: moduleId,
25/******/ 			l: false,
26/******/ 			exports: {}
27/******/ 		};
28/******/
29/******/ 		// Execute the module function
30/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31/******/
32/******/ 		// Flag the module as loaded
33/******/ 		module.l = true;
34/******/
35/******/ 		// Return the exports of the module
36/******/ 		return module.exports;
37/******/ 	}
38/******/
39/******/
40/******/ 	// expose the modules object (__webpack_modules__)
41/******/ 	__webpack_require__.m = modules;
42/******/
43/******/ 	// expose the module cache
44/******/ 	__webpack_require__.c = installedModules;
45/******/
46/******/ 	// define getter function for harmony exports
47/******/ 	__webpack_require__.d = function(exports, name, getter) {
48/******/ 		if(!__webpack_require__.o(exports, name)) {
49/******/ 			Object.defineProperty(exports, name, {
50/******/ 				configurable: false,
51/******/ 				enumerable: true,
52/******/ 				get: getter
53/******/ 			});
54/******/ 		}
55/******/ 	};
56/******/
57/******/ 	// getDefaultExport function for compatibility with non-harmony modules
58/******/ 	__webpack_require__.n = function(module) {
59/******/ 		var getter = module && module.__esModule ?
60/******/ 			function getDefault() { return module['default']; } :
61/******/ 			function getModuleExports() { return module; };
62/******/ 		__webpack_require__.d(getter, 'a', getter);
63/******/ 		return getter;
64/******/ 	};
65/******/
66/******/ 	// Object.prototype.hasOwnProperty.call
67/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
68/******/
69/******/ 	// __webpack_public_path__
70/******/ 	__webpack_require__.p = "";
71/******/
72/******/ 	// Load entry module and return exports
73/******/ 	return __webpack_require__(__webpack_require__.s = 0);
74/******/ })
75/************************************************************************/
76/******/ ([
77/* 0 */
78/***/ (function(module, exports, __webpack_require__) {
79
80module.exports = __webpack_require__(1);
81
82
83/***/ }),
84/* 1 */
85/***/ (function(module, exports, __webpack_require__) {
86
87"use strict";
88/*
89Copyright (c) 2009-2019 Frank Bennett
90
91	This program is free software: you can redistribute it and/or
92	modify it under EITHER
93
94      * the terms of the Common Public Attribution License (CPAL) as
95	    published by the Open Source Initiative, either version 1 of
96	    the CPAL, or (at your option) any later version; OR
97
98      * the terms of the GNU Affero General Public License (AGPL)
99        as published by the Free Software Foundation, either version
100        3 of the AGPL, or (at your option) any later version.
101
102	This program is distributed in the hope that it will be useful,
103	but WITHOUT ANY WARRANTY; without even the implied warranty of
104	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
105	Affero General Public License for more details.
106
107	You should have received copies of the Common Public Attribution
108    License and of the GNU Affero General Public License along with
109    this program.  If not, see <https://opensource.org/licenses/> or
110    <http://www.gnu.org/licenses/> respectively.
111*/
112/*global CSL: true */
113
114/**
115 * A Javascript implementation of the CSL citation formatting language.
116 *
117 * <p>A configured instance of the process is built in two stages,
118 * using {@link CSL.Core.Build} and {@link CSL.Core.Configure}.
119 * The former sets up hash-accessible locale data and imports the CSL format file
120 * to be applied to the citations,
121 * transforming it into a one-dimensional token list, and
122 * registering functions and parameters on each token as appropriate.
123 * The latter sets jump-point information
124 * on tokens that constitute potential branch
125 * points, in a single back-to-front scan of the token list.
126 * This
127 * yields a token list that can be executed front-to-back by
128 * body methods available on the
129 * {@link CSL.Engine} class.</p>
130 *
131 * <p>This top-level {@link CSL} object itself carries
132 * constants that are needed during processing.</p>
133 * @namespace A CSL citation formatter.
134 */
135
136// IE6 does not implement Array.indexOf().
137// IE7 neither, according to rumour.
138
139
140// Potential skip words:
141// under; along; out; between; among; outside; inside; amid; amidst; against; toward; towards.
142// See https://forums.zotero.org/discussion/30484/?Focus=159613#Comment_159613
143
144
145
146
147var CSL = {
148
149    PROCESSOR_VERSION: "1.4.61",
150
151    error: function(str) { // default error function
152        if ("undefined" === typeof Error) {
153            throw new Error("citeproc-js error: " + str);
154        } else {
155            throw "citeproc-js error: " + str;
156        }
157    },
158    debug: function(str) { // default debug function
159        if ("undefined" === typeof console) {
160            dump("CSL: " + str + "\n");
161        } else {
162            console.log("citeproc-js warning: " + str);
163        }
164    },
165
166    toLocaleUpperCase(str) {
167        var arr = this.tmp.lang_array;
168        try {
169            str = str.toLocaleUpperCase(arr);
170        } catch (e) {
171            str = str.toUpperCase();
172        }
173        return str;
174    },
175
176    toLocaleLowerCase(str) {
177        var arr = this.tmp.lang_array;
178        try {
179            str = str.toLocaleLowerCase(arr);
180        } catch (e) {
181            str = str.toLowerCase();
182        }
183        return str;
184    },
185
186    LOCATOR_LABELS_REGEXP: new RegExp("^((vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|rn|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\\.)\\s+(.*)"),
187
188    STATUTE_SUBDIV_PLAIN_REGEX: /(?:(?:^| )(?:vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|rn|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\. *)/,
189    STATUTE_SUBDIV_PLAIN_REGEX_FRONT: /(?:^\s*[.,;]*\s*(?:vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|rn|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\. *)/,
190 
191    STATUTE_SUBDIV_STRINGS: {
192        "vrs.": "verse",
193		"sv.": "sub-verbo",
194        "subpara.": "subparagraph",
195        "op.": "opus",
196        "subch.": "subchapter",
197        "add.": "addendum",
198        "amend.": "amendment",
199        "annot.": "annotation",
200        "app.": "appendix",
201        "art.": "article",
202        "bibliog.": "bibliography",
203        "bk.": "book",
204        "ch.": "chapter",
205        "cl.": "clause",
206        "col.": "column",
207        "cmt.": "comment",
208        "dec.": "decision",
209        "dept.": "department",
210        "ex.": "example",
vendor: 15,442 bytes, lines 211-677
211        "fig.": "figure",
212        "fld.": "field",
213        "fol.": "folio",
214        "n.": "note",
215        "hypo.": "hypothetical",
216        "illus.": "illustration",
217        "intro.": "introduction",
218        "l.": "line",
219        "no.": "issue",
220        "p.": "page",
221        "pp.": "page",
222        "para.": "paragraph",
223        "pt.": "part",
224        "pmbl.": "preamble",
225        "princ.": "principle",
226        "pub.": "publication",
227        "r.": "rule",
228        "rn.": "randnummer",
229        "sched.": "schedule",
230        "sec.": "section",
231        "ser.": "series,",
232        "subdiv.": "subdivision",
233        "subsec.": "subsection",
234        "supp.": "supplement",
235        "tbl.": "table",
236        "tit.": "title",
237        "vol.": "volume"
238    },
239    STATUTE_SUBDIV_STRINGS_REVERSE: {
240        "verse": "vrs.",
241		"sub-verbo": "sv.",
242        "sub verbo": "sv.",
243        "subparagraph": "subpara.",
244        "opus": "op.",
245        "subchapter": "subch.",
246        "addendum": "add.",
247        "amendment": "amend.",
248        "annotation": "annot.",
249        "appendix": "app.",
250        "article": "art.",
251        "bibliography": "bibliog.",
252        "book": "bk.",
253        "chapter": "ch.",
254        "clause": "cl.",
255        "column": "col.",
256        "comment": "cmt.",
257        "decision": "dec.",
258        "department": "dept.",
259        "example": "ex.",
260        "figure": "fig.",
261        "field": "fld.",
262        "folio": "fol.",
263        "note": "n.",
264        "hypothetical": "hypo.",
265        "illustration": "illus.",
266        "introduction": "intro.",
267        "line": "l.",
268        "issue": "no.",
269        "page": "p.",
270        "paragraph": "para.",
271        "part": "pt.",
272        "preamble": "pmbl.",
273        "principle": "princ.",
274        "publication": "pub.",
275        "rule": "r.",
276        "randnummer": "rn.",
277        "schedule": "sched.",
278        "section": "sec.",
279        "series,": "ser.",
280        "subdivision": "subdiv.",
281        "subsection": "subsec.",
282        "supplement": "supp.",
283        "table": "tbl.",
284        "title": "tit.",
285        "volume": "vol."
286    },
287
288    LOCATOR_LABELS_MAP: {
289        "vrs": "verse",
290		"sv": "sub-verbo",
291        "subpara": "subparagraph",
292        "op": "opus",
293        "subch": "subchapter",
294        "add": "addendum",
295        "amend": "amendment",
296        "annot": "annotation",
297        "app": "appendix",
298        "art": "article",
299        "bibliog": "bibliography",
300        "bk": "book",
301        "ch": "chapter",
302        "cl": "clause",
303        "col": "column",
304        "cmt": "comment",
305        "dec": "decision",
306        "dept": "department",
307        "ex": "example",
308        "fig": "figure",
309        "fld": "field",
310        "fol": "folio",
311        "n": "note",
312        "hypo": "hypothetical",
313        "illus": "illustration",
314        "intro": "introduction",
315        "l": "line",
316        "no": "issue",
317        "p": "page",
318        "pp": "page",
319        "para": "paragraph",
320        "pt": "part",
321        "pmbl": "preamble",
322        "princ": "principle",
323        "pub": "publication",
324        "r": "rule",
325        "rn": "randnummer",
326        "sched": "schedule",
327        "sec": "section",
328        "ser": "series,",
329        "subdiv": "subdivision",
330        "subsec": "subsection",
331        "supp": "supplement",
332        "tbl": "table",
333        "tit": "title",
334        "vol": "volume"
335    },
336    MODULE_MACROS: {
337        "juris-pretitle": true,
338        "juris-title": true,
339        "juris-pretitle-short": true,
340        "juris-title-short": true,
341        "juris-main": true,
342        "juris-main-short": true,
343        "juris-tail": true,
344        "juris-tail-short": true,
345        "juris-locator": true
346    },
347    MODULE_TYPES: {
348        "legal_case": true,
349        "legislation": true,
350        "bill": true,
351        "hearing": true,
352        "gazette": true,
353        "report": true,
354        "regulation": true,
355        "standard": true,
356        "patent": true,
357        "locator": true
358    },
359    checkNestedBrace: function(state) {
360        if (state.opt.xclass === "note") {
361            this.depth = 0;
362            this.update = function(str) {
363                
364                // Receives affix string, returns with flipped parens.
365                
366                var str = str ? str : "";
367                var lst = str.split(/([\(\)])/);
368                for (var i=1,ilen=lst.length;i<ilen;i += 2) {
369                    if (lst[i] === "(") {
370                        if (1 === (this.depth % 2)) {
371                            lst[i] = "[";
372                        }
373                        this.depth += 1;
374                    } else if (lst[i] === ")") {
375                        if (0 === (this.depth % 2)) {
376                            lst[i] = "]";
377                        }
378                        this.depth -= 1;
379                    }
380                }
381                var ret = lst.join("");
382                return ret;
383            };
384        } else {
385            this.update = function(str) {
386                return str;
387            };
388        }
389    },
390
391    MULTI_FIELDS: ["event", "publisher", "publisher-place", "event-place", "title", "container-title", "collection-title", "authority","genre","title-short","medium","country","jurisdiction","archive","archive-place"],
392
393    LangPrefsMap: {
394        "title":"titles",
395        "title-short":"titles",
396        "event":"titles",
397        "genre":"titles",
398        "medium":"titles",
399        "container-title":"journals",
400        "collection-title":"titles",
401        "archive":"journals",
402        "publisher":"publishers",
403        "authority":"publishers",
404        "publisher-place": "places",
405        "event-place": "places",
406        "archive-place": "places",
407        "jurisdiction": "places",
408        "number": "places",
409        "edition":"places",
410        "issue":"places",
411        "volume":"places"
412    },
413
414    AbbreviationSegments: function () {
415        this["container-title"] = {};
416        this["collection-title"] = {};
417        this["institution-entire"] = {};
418        this["institution-part"] = {};
419        this.nickname = {};
420        this.number = {};
421        this.title = {};
422        this.place = {};
423        this.hereinafter = {};
424        this.classic = {};
425        this["container-phrase"] = {};
426        this["title-phrase"] = {};
427    },
428
429    getAbbrevsDomain: function (state, country, lang) {
430		var domain = null;
431        if (state.opt.availableAbbrevDomains && country && country !== "default") {
432	        var globalDomainPreference = state.locale[state.opt.lang].opts["jurisdiction-preference"];
433		    var itemDomainPreference = null;
434		    if (state.locale[lang]) {
435			    itemDomainPreference = state.locale[lang].opts["jurisdiction-preference"];
436		    }
437		    if (itemDomainPreference) {
438			    for (var j=itemDomainPreference.length-1; j > -1; j--) {
439				    if (state.opt.availableAbbrevDomains[country].indexOf(itemDomainPreference[j]) > -1) {
440					    domain = itemDomainPreference[j];
441					    break;
442				    }
443			    }
444		    }
445		    if (!domain && globalDomainPreference) {
446			    for (var j=globalDomainPreference.length-1; j > -1; j--) {
447				    if (state.opt.availableAbbrevDomains[country].indexOf(globalDomainPreference[j]) > -1) {
448					    domain = globalDomainPreference[j];
449					    break;
450				    }
451			    }
452		    }
453        }
454        return domain;
455    },
456    
457    FIELD_CATEGORY_REMAP: {
458        "title": "title",
459        "container-title": "container-title",
460        "collection-title": "collection-title",
461        "country": "place",
462        "number": "number",
463        "place": "place",
464        "archive": "container-title",
465        "title-short": "title",
466        "genre": "title",
467        "event": "title",
468        "medium": "title",
469		"archive-place": "place",
470		"publisher-place": "place",
471		"event-place": "place",
472		"jurisdiction": "place",
473		"language-name": "place",
474		"language-name-original": "place",
475        "call-number": "number",
476        "chapter-number": "number",
477        "collection-number": "number",
478        "edition": "number",
479        "page": "number",
480        "issue": "number",
481        "locator": "number",
482        "locator-extra": "number",
483        "number-of-pages": "number",
484        "number-of-volumes": "number",
485        "volume": "number",
486        "citation-number": "number",
487        "publisher": "institution-part"
488    },
489    
490    parseLocator: function(item) {
491        if (this.opt.development_extensions.locator_date_and_revision) {
492            // Break out locator elements if necessary
493            if (item.locator) {
494                item.locator = "" + item.locator;
495                var idx = item.locator.indexOf("|");
496                if (idx > -1) {
497                    var raw_locator = item.locator;
498                    item.locator = raw_locator.slice(0, idx);
499                    raw_locator = raw_locator.slice(idx + 1);
500                    var m = raw_locator.match(/^([0-9]{4}-[0-9]{2}-[0-9]{2}).*/);
501                    if (m) {
502                        item["locator-date"] = this.fun.dateparser.parseDateToObject(m[1]);
503                        raw_locator = raw_locator.slice(m[1].length);
504                    }
505                    item["locator-extra"] = raw_locator.replace(/^\s+/, "").replace(/\s+$/, "");
506                }
507            }
508        }
509        if (item.locator) {
510            item.locator = ("" + item.locator).replace(/\s+$/, '');
511        }
512        return item;
513    },
514
515    normalizeLocaleStr: function(str) {
516        if (!str) {
517            return;
518        }
519        var lst = str.split('-');
520        lst[0] = lst[0].toLowerCase();
521        if (lst[1]) {
522            lst[1] = lst[1].toUpperCase();
523        }
524        return lst.join("-");
525    },
526
527    parseNoteFieldHacks: function(Item, validFieldsForType, allowDateOverride) {
528        if ("string" !== typeof Item.note) {
529            return;
530        }
531        var elems = [];
532        var lines = Item.note.split('\n');
533        // Normalize entries
534        for (var i=0, ilen=lines.length; i<ilen; i++) {
535            var line = lines[i];
536            var elems = [];
537            var m = line.match(CSL.NOTE_FIELDS_REGEXP);
538            if (m) {
539                var splt = line.split(CSL.NOTE_FIELDS_REGEXP);
540                for (var j=0,jlen=(splt.length-1);j<jlen;j++) {
541                    elems.push(splt[j]);
542                    elems.push(m[j]);
543                }
544                elems.push(splt[splt.length-1]);
545                for (var j=1,jlen=elems.length;j<jlen;j += 2) {
546                    // Abort conversions if preceded by unparseable text
547                    if (elems[j-1].trim() && (i>0 || j>1) && !elems[j-1].match(CSL.NOTE_FIELD_REGEXP)) {
548                        break;
549                    } else {
550                        elems[j] = '\n' + elems[j].slice(2,-1).trim() + '\n';
551                    }
552                }
553                lines[i] = elems.join('');
554            }
555        }
556        // Resplit
557        lines = lines.join('\n').split('\n');
558        var offset = 0;
559        var names = {};
560        for (var i=0,ilen=lines.length;i<ilen;i++) {
561            var line = lines[i];
562            var mm = line.match(CSL.NOTE_FIELD_REGEXP);
563            if (!line.trim()) {
564                continue;
565            } else if (!mm) {
566                if (i === 0) {
567                    continue;
568                } else {
569                    offset = i;
570                    break;
571                }
572            }
573            var key = mm[1];
574            var val = mm[2].replace(/^\s+/, "").replace(/\s+$/, "");
575            if (key === "type") {
576                Item.type = val;
577                lines[i] = "";
578            } else if (CSL.DATE_VARIABLES.indexOf(key.replace(/^alt-/, "")) > -1) {
579                if (!Item[key] || allowDateOverride) {
580                    Item[key] = CSL.DateParser.parseDateToArray(val);
581                    if (!validFieldsForType || (validFieldsForType[key] && this.isDateString(val))) {
582                        lines[i] = "";
583                    }
584                }
585            } else if (!Item[key]) {
586                if (CSL.NAME_VARIABLES.indexOf(key.replace(/^alt-/, "")) > -1) {
587                    if (!names[key]) {
588                        names[key] = [];
589                    }
590                    var lst = val.split(/\s*\|\|\s*/);
591                    if (lst.length === 1) {
592                        names[key].push({literal:lst[0]});
593                    } else if (lst.length === 2) {
594                        var name = {family:lst[0],given:lst[1]};
595                        CSL.parseParticles(name);
596                        names[key].push(name);
597                    }
598                } else {
599                    Item[key] = val;
600                }
601                if (!validFieldsForType || validFieldsForType[key]) {
602                    lines[i] = "";
603                }
604            }
605        }
606        for (var key in names) {
607            Item[key] = names[key];
608        }
609        // Final cleanup for validCslFields only: eliminate blank lines, add blank line to text
610        if (validFieldsForType) {
611            if (lines[offset].trim()) {
612                lines[offset] = '\n' + lines[offset];
613            }
614            for (var i=offset-1;i>-1;i--) {
615                if (!lines[i].trim()) {
616                    lines = lines.slice(0, i).concat(lines.slice(i + 1));
617                }
618            }
619        }
620        Item.note = lines.join("\n").trim();
621    },
622
623    checkPrefixSpaceAppend: function (state, prefix) {
624        if (!prefix) {
625            prefix = "";
626        }
627        var sp = "";
628        // We need the raw string, without decorations
629        // of any kind. Markup scheme is known, though, so
630        // markup can be safely stripped at string level.
631        //
632        // U+201d = right double quotation mark
633        // U+2019 = right single quotation mark
634        // U+00bb = right double angle bracket (guillemet)
635        // U+202f = non-breaking thin space
636        // U+00a0 = non-breaking space
637        var test_prefix = prefix.replace(/<[^>]+>/g, "").replace(/["'\u201d\u2019\u00bb\u202f\u00a0 ]+$/g,"");
638        var test_char = test_prefix.slice(-1);
639        if (test_prefix.match(CSL.ENDSWITH_ROMANESQUE_REGEXP)) {
640            sp = " ";
641        } else if (CSL.TERMINAL_PUNCTUATION.slice(0,-1).indexOf(test_char) > -1) {
642            sp = " ";
643        } else if (test_char.match(/[\)\],0-9]/)) {
644            sp = " ";
645        }
646        // Protect against double spaces, which would trigger an extra,
647        // explicit, non-breaking space.
648        var prefix = (prefix + sp).replace(/\s+/g, " ");
649        return prefix;
650    },
651
652    checkIgnorePredecessor: function(state, prefix) {
653        var ignorePredecessor = false;
654        var test_prefix = prefix.replace(/<[^>]+>/g, "").replace(/["'\u201d\u2019\u00bb\u202f\u00a0 ]+$/g,"");
655        var test_char = test_prefix.slice(-1);
656        if (CSL.TERMINAL_PUNCTUATION.slice(0,-1).indexOf(test_char) > -1 && prefix.trim().indexOf(" ") > -1) {
657            state.tmp.term_predecessor = false;
658            return true;
659        }
660        return false;
661    },
662
663    checkSuffixSpacePrepend: function(state, suffix) {
664        if (!suffix) {
665            return "";
666        }
667        if (suffix.match(CSL.STARTSWITH_ROMANESQUE_REGEXP) || ['[','('].indexOf(suffix.slice(0,1)) > -1) {
668            suffix = " " + suffix;
669        }
670        return suffix;
671    },
672    
673    GENDERS: ["masculine", "feminine"],
674    
675    ERROR_NO_RENDERED_FORM: 1,
676
677    PREVIEW: "Just for laughs.",
678    ASSUME_ALL_ITEMS_REGISTERED: 2,
679
680    START: 0,
681    END: 1,
682    SINGLETON: 2,
683
684    SEEN: 6,
685    SUCCESSOR: 3,
686    SUCCESSOR_OF_SUCCESSOR: 4,
687    SUPPRESS: 5,
688
689    SINGULAR: 0,
690    PLURAL: 1,
691
692    LITERAL: true,
693
694    BEFORE: 1,
695    AFTER: 2,
696
697    DESCENDING: 1,
698    ASCENDING: 2,
699
700    PRIMARY: 1,
701    SECONDARY: 2,
702    
703    POSITION_FIRST: 0,
704    POSITION_SUBSEQUENT: 1,
705    POSITION_IBID: 2,
706    POSITION_IBID_WITH_LOCATOR: 3,
707    POSITION_CONTAINER_SUBSEQUENT: 4,
708
709    POSITION_MAP: {
710        "0": 0,
711        "4": 1,
712        "1": 2,
713        "2": 3,
714        "3": 4
715    },
716    
717    POSITION_TEST_VARS: ["position", "first-reference-note-number", "near-note"],
718
719    AREAS: ["citation", "citation_sort", "bibliography", "bibliography_sort", "intext"],
720
721    CITE_FIELDS: ["first-reference-note-number", "first-container-reference-note-number", "locator", "locator-extra"],
722
723    SWAPPING_PUNCTUATION: [".", "!", "?", ":", ","],
724    TERMINAL_PUNCTUATION: [":", ".", ";", "!", "?", " "],
725
726    // update modes
727    NONE: 0,
728    NUMERIC: 1,
729    POSITION: 2,
730    TRIGRAPH: 3,
731
732    DATE_PARTS: ["year", "month", "day"],
733    DATE_PARTS_ALL: ["year", "month", "day", "season"],
734    DATE_PARTS_INTERNAL: ["year", "month", "day", "year_end", "month_end", "day_end"],
735
736    NAME_PARTS: ["non-dropping-particle", "family", "given", "dropping-particle", "suffix", "literal"],
737
738    DISAMBIGUATE_OPTIONS: [
739        "disambiguate-add-names",
740        "disambiguate-add-givenname",
741        "disambiguate-add-year-suffix"
742    ],
743
744    GIVENNAME_DISAMBIGUATION_RULES: [
745        "all-names",
746        "all-names-with-initials",
747        "primary-name",
748        "primary-name-with-initials",
749        "by-cite"
750    ],
751
752    NAME_ATTRIBUTES: [
753        "and",
754        "delimiter-precedes-last",
755        "delimiter-precedes-et-al",
756        "initialize-with",
757        "initialize",
758        "name-as-sort-order",
759        "sort-separator",
760        "et-al-min",
761        "et-al-use-first",
762        "et-al-subsequent-min",
763        "et-al-subsequent-use-first",
764        "form",
765        "prefix",
766        "suffix",
767        "delimiter"
768    ],
769
770    LOOSE: 0,
771    STRICT: 1,
772    TOLERANT: 2,
773
774    PREFIX_PUNCTUATION: /[.;:]\s*$/,
775    SUFFIX_PUNCTUATION: /^\s*[.;:,\(\)]/,
776
777    NUMBER_REGEXP: /(?:^\d+|\d+$)/,
778    //
779    // \u0400-\u042f are cyrillic and extended cyrillic capitals
780    // this is not fully smart yet.  can't do what this was trying to do
781    // with regexps, actually; we want to identify strings with a leading
782    // capital letter, and any subsequent capital letters.  Have to compare
783    // locale caps version with existing version, character by character.
784    // hard stuff, but if it breaks, that's what to do.
785    // \u0600-\u06ff is Arabic/Persian
786    // \u200c-\u200e and \u202a-\u202e are special spaces and left-right 
787    // control characters
788
789
790
791    NAME_INITIAL_REGEXP: /^([A-Z\u0e01-\u0e5b\u00c0-\u017f\u0400-\u042f\u0590-\u05d4\u05d6-\u05ff\u0600-\u06ff\u0370\u0372\u0376\u0386\u0388-\u03ab\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03fd-\u03ff])([a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0400-\u052f\u0600-\u06ff\u0370-\u03ff\u1f00-\u1fff]*|)(\.)*/,
792    ROMANESQUE_REGEXP: /[-0-9a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/,
793    ROMANESQUE_NOT_REGEXP: /[^a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/g,
794    STARTSWITH_ROMANESQUE_REGEXP: /^[&a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/,
795    ENDSWITH_ROMANESQUE_REGEXP: /[.;:&a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]$/,
796    ALL_ROMANESQUE_REGEXP: /^[a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]+$/,
797
798    VIETNAMESE_SPECIALS: /[\u00c0-\u00c3\u00c8-\u00ca\u00cc\u00cd\u00d2-\u00d5\u00d9\u00da\u00dd\u00e0-\u00e3\u00e8-\u00ea\u00ec\u00ed\u00f2-\u00f5\u00f9\u00fa\u00fd\u0101\u0103\u0110\u0111\u0128\u0129\u
vendor: 18,874 bytes, lines 798-1264
7980168\u0169\u01a0\u01a1\u01af\u01b0\u1ea0-\u1ef9]/,
799
800    VIETNAMESE_NAMES: /^(?:(?:[.AaBbCcDdEeGgHhIiKkLlMmNnOoPpQqRrSsTtUuVvXxYy \u00c0-\u00c3\u00c8-\u00ca\u00cc\u00cd\u00d2-\u00d5\u00d9\u00da\u00dd\u00e0-\u00e3\u00e8-\u00ea\u00ec\u00ed\u00f2-\u00f5\u00f9\u00fa\u00fd\u0101\u0103\u0110\u0111\u0128\u0129\u0168\u0169\u01a0\u01a1\u01af\u01b0\u1ea0-\u1ef9]{2,6})(\s+|$))+$/,
801
802    NOTE_FIELDS_REGEXP: /\{:(?:[\-_a-z]+|[A-Z]+):[^\}]+\}/g,
803    NOTE_FIELD_REGEXP: /^([\-_a-z]+|[A-Z]+):\s*([^\}]+)$/,
804
805	PARTICLE_GIVEN_REGEXP: /^([^ ]+(?:\u02bb |\u2019 | |\' ) *)(.+)$/,
806	PARTICLE_FAMILY_REGEXP: /^([^ ]+(?:\-|\u02bb|\u2019| |\') *)(.+)$/,
807
808    DISPLAY_CLASSES: ["block", "left-margin", "right-inline", "indent"],
809
810    NAME_VARIABLES: [
811        "author",
812        "chair",
813        "collection-editor",
814        "compiler",
815        "composer",
816        "container-author",
817        "contributor",
818        "curator",
819        "director",
820        "editor",
821        "editor-translator", 
822        "editorial-director",
823        "executive-producer",
824        "guest",
825        "host",
826        "illustrator",
827        "interviewer",
828        "narrator", 
829        "organizer",
830        "original-author",
831        "performer",
832        "producer",
833        "recipient",
834        "reviewed-author",
835        "script-writer",
836        "series-creator",
837        "translator",
838        "commenter"
839    ],
840    CREATORS: [
841        "author",
842        "chair",
843        "collection-editor",
844        "compiler",
845        "composer",
846        "container-author",
847        "contributor",
848        "curator",
849        "director",
850        "editor",
851        "editor-translator", 
852        "editorial-director",
853        "executive-producer",
854        "guest",
855        "host",
856        "illustrator",
857        "interviewer",
858        "narrator", 
859        "organizer",
860        "original-author",
861        "performer",
862        "producer",
863        "recipient",
864        "reviewed-author",
865        "script-writer",
866        "series-creator",
867        "translator",
868        "commenter"
869    ],
870    NUMERIC_VARIABLES: [
871        "call-number",
872        "chapter-number",
873        "collection-number",
874        "division",
875        "edition",
876        "page",
877        "issue",
878        "locator",
879        "locator-extra",
880        "number",
881        "number-of-pages",
882        "number-of-volumes",
883        "part-number",
884        "printing-number",
885        "section",
886        "supplement-number",
887        "version",
888        "volume",
889        "supplement", // maybe deprecated this? supplement-number should serve this purpose in standard CSL.
890        "citation-number"
891    ],
892    //var x = new Array();
893    //x = x.concat(["title","container-title","issued","page"]);
894    //x = x.concat(["locator","collection-number","original-date"]);
895    //x = x.concat(["reporting-date","decision-date","filing-date"]);
896    //x = x.concat(["revision-date"]);
897    //NUMERIC_VARIABLES = x.slice();
898    DATE_VARIABLES: [
899        "locator-date", 
900        "issued", 
901        "event-date", 
902        "accessed", 
903        "original-date",
904        "publication-date",
905        "available-date",
906        "submitted",
907        "alt-issued",
908        "alt-event"
909    ],
910    VARIABLES_WITH_SHORT_FORM: [
911        "title",
912        "container-title"
913    ],
914    TITLE_FIELD_SPLITS: function(seg) {
915        var keys = ["title", "short", "main", "sub", "subjoin"];
916        var ret = {};
917        for (var i=0,ilen=keys.length;i<ilen;i++) {
918            ret[keys[i]] = seg + "title" + (keys[i] === "title" ? "" : "-" + keys[i]);
919        }
920        return ret;
921    },
922    
923    demoteNoiseWords: function (state, fld, drop_or_demote) {
924        var SKIP_WORDS = state.locale[state.opt.lang].opts["leading-noise-words"];
925        if (fld && drop_or_demote) {
926            fld = fld.split(/\s+/);
927            fld.reverse();
928            var toEnd = [];
929            for (var j  = fld.length - 1; j > -1; j += -1) {
930                if (SKIP_WORDS.indexOf(fld[j].toLowerCase()) > -1) {
931                    toEnd.push(fld.pop());
932                } else {
933                    break;
934                }
935            }
936            fld.reverse();
937            var start = fld.join(" ");
938            var end = toEnd.join(" ");
939            if ("drop" === drop_or_demote || !end) {
940                fld = start;
941            } else if ("demote" === drop_or_demote) {
942                fld = [start, end].join(", ");
943            }
944        }
945        return fld;
946    },
947
948    extractTitleAndSubtitle: function (Item, narrowSpaceLocale) {
949        var narrowSpace = narrowSpaceLocale ? "\u202f" : "";
950        // XXX In this function, split on split-char, but prefer exact match
951        // XXX of subtitle to a split-char in title if found.
952        var segments = [""];
953        if (this.opt.development_extensions.split_container_title) {
954            segments.push("container-");
955        }
956        for (var i=0,ilen=segments.length;i<ilen;i++) {
957            var seg = segments[i];
958            var title = CSL.TITLE_FIELD_SPLITS(seg);
959            var langs = [false];
960            if (Item.multi) {
961                for (var lang in Item.multi._keys[title.short]) {
962                    langs.push(lang);
963                }
964            }
965            for (var j=0,jlen=langs.length;j<jlen;j++) {
966                var lang = langs[j];
967                var vals = {};
968                if (lang) {
969                    if (Item.multi._keys[title.title]) {
970                        vals[title.title] = Item.multi._keys[title.title][lang];
971                    }
972                    if (Item.multi._keys[title["short"]]) {
973                        vals[title["short"]] = Item.multi._keys[title["short"]][lang];
974                    }
975                } else {
976                    vals[title.title] = Item[title.title];
977                    vals[title["short"]] = Item[title["short"]];
978                }
979                vals[title.main] = vals[title.title];
980                vals[title.sub] = false;
981                var shortTitle = vals[title["short"]];
982                if (vals[title.title]) {
983                    // Rules
984                    // TITLE_SPLIT eliminates split-points of period-space preceded by a capital letter.
985                    // If short title exists and matches exactly to a split-point, use that split-point only.
986                    // Otherwise if there is just one split-point, use that as main/sub split.
987                    // Otherwise use all split-points ... which is handled in titleCaseSentenceOrNormal, not here.
988                    if (shortTitle && shortTitle.toLowerCase() === vals[title.title].toLowerCase()) {
989                        vals[title.main] = vals[title.title];
990                        vals[title.subjoin] = "";
991                        vals[title.sub] = "";
992                    } else if (shortTitle) {
993                        // check for valid match to shortTitle
994                        var tail = vals[title.title].slice(shortTitle.replace(/[\?\!]+$/, "").length);
995                        var top = vals[title.title].replace(tail.replace(/^[\?\!]+/, ""), "").trim();
996                        var m = CSL.TITLE_SPLIT_REGEXP.matchfirst.exec(tail);
997                        if (m && top.toLowerCase() === shortTitle.toLowerCase()) {
998                            vals[title.main] = top;
999                            vals[title.subjoin] = m[1].replace(/[\?\!]+(\s*)$/, "$1");
1000                            vals[title.sub] = tail.replace(CSL.TITLE_SPLIT_REGEXP.matchfirst, "");
1001                            if (this.opt.development_extensions.force_short_title_casing_alignment) {
1002                                vals[title["short"]] = vals[title.main];
1003                            }
1004                        } else {
1005                            var splitTitle = CSL.TITLE_SPLIT(vals[title.title]);
1006                            if (splitTitle.length == 3) {
1007                                vals[title.main] = splitTitle[0];
1008                                vals[title.subjoin] = splitTitle[1];
1009                                vals[title.sub] = splitTitle[2];
1010                            } else {
1011                                vals[title.main] = vals[title.title];
1012                                vals[title.subjoin] = "";
1013                                vals[title.sub] = "";
1014                            }
1015                        }
1016                    } else {
1017                        var splitTitle = CSL.TITLE_SPLIT(vals[title.title]);
1018                        if (splitTitle.length == 3) {
1019                            vals[title.main] = splitTitle[0];
1020                            vals[title.subjoin] = splitTitle[1];
1021                            vals[title.sub] = splitTitle[2];
1022                            if (this.opt.development_extensions.implicit_short_title && Item.type !== "legal_case") {
1023                                if (!Item[title.short] && !vals[title.main].match(/^[\-\.[0-9]+$/)) {
1024                                    var punct = vals[title.subjoin].trim();
1025                                    if (["?", "!"].indexOf(punct) === -1) {
1026                                        punct = "";
1027                                    }
1028                                    vals[title.short] = vals[title.main] + punct;
1029                                }
1030                            }
1031                        } else {
1032                            vals[title.main] = vals[title.title];
1033                            vals[title.subjoin] = "";
1034                            vals[title.sub] = "";
1035                        }
1036                    }
1037                    if (vals[title.subjoin]) {
1038                        if (vals[title.subjoin].match(/([\?\!])/)) {
1039                            var m = vals[title.subjoin].match(/(\s*)$/)
1040                            vals[title.main] = vals[title.main] + narrowSpace +vals[title.subjoin].trim();
1041                            vals[title.subjoin] = m[1];
1042                        }
1043                    }
1044                }
1045                if (vals[title.subjoin]) {
1046                    if (vals[title.subjoin].indexOf(":") > -1) {
1047                        vals[title.subjoin] = narrowSpace + ": ";
1048                    }
1049                    if (vals[title.subjoin].indexOf("-") > -1 || vals[title.subjoin].indexOf("—") > -1) {
1050                        vals[title.subjoin] = "—";
1051                    }
1052                }
1053                if (lang) {
1054                    for (var key in vals) {
1055                        if (!Item.multi._keys[key]) {
1056                            Item.multi._keys[key] = {};
1057                        }
1058                        Item.multi._keys[key][lang] = vals[key];
1059                    }
1060                } else {
1061                    for (var key in vals) {
1062                        Item[key] = vals[key];
1063                    }
1064                }
1065            }
1066        }
1067    },
1068
1069    titlecaseSentenceOrNormal: function(state, Item, seg, lang, sentenceCase) {
1070        // Hold on here.
1071        // What is seg here?
1072        // It's ... either "" or "container-". Which is ugly, but works.
1073        // But this ALWAYS returns the full title, never short.
1074        // So sentence-casing cannot be applied to short.
1075        // Goes unnoticed because forced sentence-casing almost never appears in styles.
1076        var title = CSL.TITLE_FIELD_SPLITS(seg);
1077        var vals = {};
1078        if (lang && Item.multi) {
1079            if (Item.multi._keys[title.title]) {
1080                vals[title.title] = Item.multi._keys[title.title][lang];
1081            }
1082            if (Item.multi._keys[title.main]) {
1083                vals[title.main] = Item.multi._keys[title.main][lang];
1084            }
1085            if (Item.multi._keys[title.sub]) {
1086                vals[title.sub] = Item.multi._keys[title.sub][lang];
1087            }
1088            if (Item.multi._keys[title.subjoin]) {
1089                vals[title.subjoin] = Item.multi._keys[title.subjoin][lang];
1090            }
1091        } else {
1092            vals[title.title] = Item[title.title];
1093            vals[title.main] = Item[title.main];
1094            vals[title.sub] = Item[title.sub];
1095            vals[title.subjoin] = Item[title.subjoin];
1096        }
1097        if (vals[title.main] && vals[title.sub]) {
1098            var mainTitle = vals[title.main];
1099            var subJoin = vals[title.subjoin];
1100            var subTitle = vals[title.sub];
1101            if (sentenceCase) {
1102                mainTitle = CSL.Output.Formatters.sentence(state, mainTitle);
1103                subTitle = CSL.Output.Formatters.sentence(state, subTitle);
1104            } else if (state.opt.development_extensions.uppercase_subtitles) {
1105                subTitle = CSL.Output.Formatters["capitalize-first"](state, subTitle);
1106            }
1107            return [mainTitle, subJoin, subTitle].join("");
1108        } else if (vals[title.title]) {
1109            if (sentenceCase) {
1110                return CSL.Output.Formatters.sentence(state, vals[title.title]);
1111            } else if (state.opt.development_extensions.uppercase_subtitles) {
1112                // Split and apply everywhere.
1113                var splits = CSL.TITLE_SPLIT(vals[title.title]);
1114                for (var i=0,ilen=splits.length; i<ilen; i += 2) {
1115                    splits[i] = CSL.Output.Formatters["capitalize-first"](state, splits[i]);
1116                }
1117                for (var i=1, ilen=splits.length-1; i < ilen; i += 2) {
1118                    var m = splits[i].match(/([:\?\!] )/);
1119                    if (m) {
1120                        var narrowSpace = state.opt["default-locale"][0].slice(0, 2).toLowerCase() === "fr" ? "\u202f" : "";
1121                        splits[i] = narrowSpace + m[1];
1122                    }
1123                    if (splits[i].indexOf("-") > -1 || splits[i].indexOf("—") > -1) {
1124                        splits[i] = "—";
1125                    }
1126                }
1127                vals[title.title] = splits.join("");
1128                return vals[title.title];
1129            } else {
1130                return vals[title.title];
1131            }
1132        } else {
1133            return "";
1134        }
1135    },
1136
1137    getSafeEscape: function(state) {
1138        if (["bibliography", "citation"].indexOf(state.tmp.area) > -1) {
1139            // Callback to apply thin space hack
1140            // Callback to force LTR/RTL on parens and braces
1141            // XXX Is this really necessary?
1142            var callbacks = [];
1143            if (state.opt.development_extensions.thin_non_breaking_space_html_hack && state.opt.mode === "html") {
1144                callbacks.push(function (txt) {
1145                    return txt.replace(/\u202f/g, '<span style="white-space:nowrap">&thinsp;</span>');
1146                });
1147            }
1148            if (callbacks.length) {
1149                return function (txt) {
1150                    for (var i = 0, ilen = callbacks.length; i < ilen; i += 1) {
1151                        txt = callbacks[i](txt);
1152                    }
1153                    return CSL.Output.Formats[state.opt.mode].text_escape(txt);
1154                };
1155            } else {
1156                return CSL.Output.Formats[state.opt.mode].text_escape;
1157            }
1158        } else {
1159            return function (txt) { return txt; };
1160        }
1161    },
1162
1163    SKIP_WORDS: ["about","above","across","afore","after","against","al", "along","alongside","amid","amidst","among","amongst","anenst","apropos","apud","around","as","aside","astride","at","athwart","atop","barring","before","behind","below","beneath","beside","besides","between","beyond","but","by","circa","despite","down","during","et", "except","for","forenenst","from","given","in","inside","into","lest","like","modulo","near","next","notwithstanding","of","off","on","onto","out","over","per","plus","pro","qua","sans","since","than","through"," thru","throughout","thruout","till","to","toward","towards","under","underneath","until","unto","up","upon","versus","vs.","v.","vs","v","via","vis-à-vis","with","within","without","according to","ahead of","apart from","as for","as of","as per","as regards","aside from","back to","because of","close to","due to","except for","far from","inside of","instead of","near to","next to","on to","out from","out of","outside of","prior to","pursuant to","rather than","regardless of","such as","that of","up to","where as","or", "yet", "so", "for", "and", "nor", "a", "an", "the", "de", "d'", "von", "van", "c", "ca"],
1164
1165    FORMAT_KEY_SEQUENCE: [
1166        "@strip-periods",
1167        "@font-style",
1168        "@font-variant",
1169        "@font-weight",
1170        "@text-decoration",
1171        "@vertical-align",
1172        "@quotes"
1173    ],
1174
1175    INSTITUTION_KEYS: [
1176        "font-style",
1177        "font-variant",
1178        "font-weight",
1179        "text-decoration",
1180        "text-case"
1181    ],
1182
1183    SUFFIX_CHARS: "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z",
1184    ROMAN_NUMERALS: [
1185        [ "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" ],
1186        [ "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" ],
1187        [ "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" ],
1188        [ "", "m", "mm", "mmm", "mmmm", "mmmmm"]
1189    ],
1190
1191    LANGS: {
1192        "af-ZA":"Afrikaans",
1193        "ar":"Arabic",
1194        "bg-BG":"Bulgarian",
1195        "ca-AD":"Catalan",
1196        "cs-CZ":"Czech",
1197        "da-DK":"Danish",
1198        "de-AT":"Austrian",
1199        "de-CH":"German (CH)",
1200        "de-DE":"German (DE)",
1201        "el-GR":"Greek",
1202        "en-GB":"English (GB)",
1203        "en-US":"English (US)",
1204        "es-ES":"Spanish",
1205        "et-EE":"Estonian",
1206        "eu":"European",
1207        "fa-IR":"Persian",
1208        "fi-FI":"Finnish",
1209        "fr-CA":"French (CA)",
1210        "fr-FR":"French (FR)",
1211        "he-IL":"Hebrew",
1212        "hr-HR":"Croatian",
1213        "hu-HU":"Hungarian",
1214        "is-IS":"Icelandic",
1215        "it-IT":"Italian",
1216        "ja-JP":"Japanese",
1217        "km-KH":"Khmer",
1218        "ko-KR":"Korean",
1219        "lt-LT":"Lithuanian",
1220        "lv-LV":"Latvian",
1221        "mn-MN":"Mongolian",
1222        "nb-NO":"Norwegian (Bokmål)",
1223        "nl-NL":"Dutch",
1224        "nn-NO":"Norwegian (Nynorsk)",
1225        "pl-PL":"Polish",
1226        "pt-BR":"Portuguese (BR)",
1227        "pt-PT":"Portuguese (PT)",
1228        "ro-RO":"Romanian",
1229        "ru-RU":"Russian",
1230        "sk-SK":"Slovak",
1231        "sl-SI":"Slovenian",
1232        "sr-RS":"Serbian",
1233        "sv-SE":"Swedish",
1234        "th-TH":"Thai",
1235        "tr-TR":"Turkish",
1236        "uk-UA":"Ukrainian",
1237        "vi-VN":"Vietnamese",
1238        "zh-CN":"Chinese (CN)",
1239        "zh-TW":"Chinese (TW)"
1240    },
1241
1242    LANG_BASES: {
1243        af: "af_ZA",
1244        ar: "ar",
1245        bg: "bg_BG",
1246        ca: "ca_AD",
1247        cs: "cs_CZ",
1248        da: "da_DK",
1249        de: "de_DE",
1250        el: "el_GR",
1251        en: "en_US",
1252        es: "es_ES",
1253        et: "et_EE",
1254        eu: "eu",
1255        fa: "fa_IR",
1256        fi: "fi_FI",
1257        fr: "fr_FR",
1258        he: "he_IL",
1259        hr: "hr-HR",
1260        hu: "hu_HU",
1261        is: "is_IS",
1262        it: "it_IT",
1263        ja: "ja_JP",
1264        km: "km_KH",
1265        ko: "ko_KR",
1266        lt: "lt_LT",
1267        lv: "lv-LV",
1268        mn: "mn_MN",
1269        nb: "nb_NO",
1270        nl: "nl_NL",
1271        nn: "nn-NO",
1272        pl: "pl_PL",
1273        pt: "pt_PT",
1274        ro: "ro_RO",
1275        ru: "ru_RU",
1276        sk: "sk_SK",
1277        sl: "sl_SI",
1278        sr: "sr_RS",
1279        sv: "sv_SE",
1280        th: "th_TH",
1281        tr: "tr_TR",
1282        uk: "uk_UA",
1283        vi: "vi_VN",
1284        zh: "zh_CN"
1285    },
1286
1287    SUPERSCRIPTS: {
1288        "\u00AA": "\u0061",
1289        "\u00B2": "\u0032",
1290        "\u00B3": "\u0033",
1291        "\u00B9": "\u0031",
1292        "\u00BA": "\u006F",
1293        "\u02B0": "\u0068",
1294        "\u02B1": "\u0266",
1295        "\u02B2": "\u006A",
1296        "\u02B3": "\u0072",
1297        "\u02B4": "\u0279",
1298        "\u02B5": "\u027B",
1299        "\u02B6": "\u0281",
1300        "\u02B7": "\u0077",
1301        "\u02B8": "\u0079",
1302        "\u02E0": "\u0263",
1303        "\u02E1": "\u006C",
1304        "\u02E2": "\u0073",
1305        "\u02E3": "\u0078",
1306        "\u02E4": "\u0295",
1307        "\u1D2C": "\u0041",
1308        "\u1D2D": "\u00C6",
1309        "\u1D2E": "\u0042",
1310        "\u1D30": "\u0044",
1311        "\u1D31": "\u0045",
1312        "\u1D32": "\u018E",
1313        "\u1D33": "\u0047",
1314        "\u1D34": "\u0048",
1315        "\u1D35": "\u0049",
1316        "\u1D36": "\u004A",
1317        "\u1D37": "\u004B",
1318        "\u1D38": "\u004C",
1319        "\u1D39": "\u004D",
1320        "\u1D3A": "\u004E",
1321        "\u1D3C": "\u004F",
1322        "\u1D3D": "\u0222",
1323        "\u1D3E": "\u0050",
1324        "\u1D3F": "\u0052",
1325        "\u1D40": "\u0054",
1326        "\u1D41": "\u0055",
1327        "\u1D42": "\u0057",
1328        "\u1D43": "\u0061",
1329        "\u1D44": "\u0250",
1330        "\u1D45": "\u0251",
1331        "\u1D46": "\u1D02",
1332        "\u1D47": "\u0062",
1333        "\u1D48": "\u0064",
1334        "\u1D49": "\u0065",
1335        "\u1D4A": "\u0259",
1336        "\u1D4B": "\u025B",
1337        "\u1D4C": "\u025C",
1338        "\u1D4D": "\u0067",
1339        "\u1D4F": "\u006B",
1340        "\u1D50": "\u006D",
1341        "\u1D51": "\u014B",
1342        "\u1D52": "\u006F",
1343        "\u1D53": "\u0254",
1344        "\u1D54": "\u1D16",
1345        "\u1D55": "\u1D17",
1346        "\u1D56": "\u0070",
1347        "\u1D57": "\u0074",
1348        "\u1D58": "\u0075",
1349        "\u1D59": "\u1D1D",
1350        "\u1D5A": "\u026F",
1351        "\u1D5B": "\u0076",
1352        "\u1D5C": "\u1D25",
1353        "\u1D5D": "\u03B2",
1354        "\u1D5E": "\u03B3",
1355        "\u1D5F": "\u03B4",
1356        "\u1D60": "\u03C6",
1357        "\u1D61": "\u03C7",
1358        "\u2070": "\u0030",
1359        "\u2071": "\u0069",
1360        "\u2074": "\u0034",
1361        "\u2075": "\u0035",
1362        "\u2076": "\u0036",
1363        "\u2077": "\u0037",
1364        "\u2078": "\u0038",
1365        "\u2079": "\u0039",
1366        "\u207A": "\u002B",
1367        "\u207B": "\u2212",
1368        "\u207C": "\u003D",
1369        "\u207D": "\u0028",
1370        "\u207E": "\u0029",
1371        "\u207F": "\u006E",
1372        "\u2120": "\u0053\u004D",
1373        "\u2122": "\u0054\u004D",
1374        "\u3192": "\u4E00",
1375        "\u3193": "\u4E8C",
1376        "\u3194": "\u4E09",
1377        "\u3195": "\u56DB",
1378        "\u3196": "\u4E0A",
1379        "\u3197": "\u4E2D",
1380        "\u3198": "\u4E0B",
1381        "\u3199": "\u7532",
1382        "\u319A": "\u4E59",
1383        "\u319B": "\u4E19",
1384        "\u319C": "\u4E01",
1385        "\u319D": "\u5929",
1386        "\u319E": "\u5730",
1387        "\u319F": "\u4EBA",
1388        "\u02C0": "\u0294",
1389        "\u02C1": "\u0295",
1390        "\u06E5": "\u0648",
1391        "\u06E6": "\u064A"
1392    },
1393    SUPERSCRIPTS_REGEXP: new RegExp("[\u00AA\u00B2\u00B3\u00B9\u00BA\u02B0\u02B1\u02B2\u02B3\u02B4\u02B5\u02B6\u02B7\u02B8\u02E0\u02E1\u02E2\u02E3\u02E4\u1D2C\u1D2D\u1D2E\u1D30\u1D31\u1D32\u1D33\u1D34\u1D35\u1D36\u1D37\u1D38\u1D39\u1D3A\u1D3C\u1D3D\u1D3E\u1D3F\u1D40\u1D41\u1D42\u1D43\u1D44\u1D45\u1D46\u1D47\u1D48\u1D49\u1D4A\u1D4B\u1D4C\u1D4D\u1D4F\u1D50\u1D51\u1D52\u1D53\u1D54\u1D55\u1D56\u1D57\u1D58\u1D59\u1D5A\u1D5B\u1D5C\u1D5D\u1D5E\u1D5F\u1D60\u1D61\u2070\u2071\u2074\u2075\u2076\u2077\u2078\u2079\u207A\u207B\u207C\u207D\u207E\u207F\u2120\u2122\u3192\u3193\u3194\u3195\u3196\u3197\u3198\u3199\u319A\u319B\u319C\u319D\u319E\u319F\u02C0\u02C1\u06E5\u06E6]", "g"),
1394
1395    // I think we need to have separate args for prefix and term,
1396    // since they have different effects between comma-safe and comma-safe-numbers-only.
1397    // Either that, or -- oh, we could just bang the two together for the test where
1398    // necessary.
1399    
1400    UPDATE_GROUP_CONTEXT_CONDITION: function (state, str, valueTerm, token, value) {
vendor: 75,357 bytes, lines 1401-3623
1401        if (!state.opt.use_context_condition) return;
1402        var flags = state.tmp.group_context.tip;
1403        if (flags.condition) {
1404            if (!flags.condition.termtxt) {
1405                flags.condition.termtxt = str;
1406                flags.condition.valueTerm = valueTerm;
1407            }
1408            if (!flags.value_seen && flags.condition.test === "comma-safe-numbers-only") {
1409                if (value) {
1410                    flags.value_seen = true;
1411                    if (!value.match(/^[0-9]/)) {
1412                        state.tmp.just_did_number = false;
1413                    }
1414                }
1415            }
1416        } else {
1417            // If not inside a conditional group, raise numeric flag
1418            // if and only if the current term string ends in a number.
1419            if (token && token.decorations.filter(o => o[0] === "@vertical-align").length > 0) {
1420                state.tmp.just_did_number = false;
1421            } else if (token && token.strings.suffix) {
1422                state.tmp.just_did_number = false;
1423            } else if (str) {
1424                if (str.match(/[0-9]$/)) {
1425                    state.tmp.just_did_number = true;
1426                } else {
1427                    state.tmp.just_did_number = false;
1428                }
1429            }
1430        }
1431    },
1432
1433    EVALUATE_GROUP_CONDITION: function(state, flags) {
1434        if (!state.opt.use_context_condition) return;
1435        var testres;
1436        var numbersOnly = flags.condition.test === "comma-safe-numbers-only";
1437        if (flags.condition.test === "empty-label") {
1438            testres = !flags.condition.termtxt;
1439        } else if (flags.condition.test === "empty-label-no-decor") {
1440            testres = !flags.condition.termtxt || flags.condition.termtxt.indexOf("%s") > -1;
1441        } else if (["comma-safe", "comma-safe-numbers-only"].indexOf(flags.condition.test) > -1) {
1442            var locale_term = flags.condition.termtxt;
1443            var termStartAlpha = false;
1444            if (flags.condition.termtxt) {
1445                termStartAlpha = flags.condition.termtxt.slice(0,1).match(CSL.ALL_ROMANESQUE_REGEXP);
1446            }
1447            var num = state.tmp.just_did_number;
1448            if (num) {
1449                if (flags.condition.valueTerm) {
1450                    testres = numbersOnly ? false : true;
1451                } else if (!locale_term) {
1452                    testres = true;
1453                } else if (termStartAlpha) {
1454                    testres = numbersOnly ? false : true;
1455                } else if (["always", "after-number"].indexOf(state.opt.require_comma_on_symbol) > -1) {
1456                    testres = true;
1457                } else {
1458                    testres = false;
1459                }
1460            } else {
1461                if (flags.condition.valueTerm) {
1462                    testres = false;
1463                } else if (!locale_term) {
1464                    testres = false;
1465                } else if (termStartAlpha) {
1466                    testres = numbersOnly ? false : true;
1467                } else if (state.opt.require_comma_on_symbol === "always") {
1468                    testres = true;
1469                } else {
1470                    testres = false;
1471                }
1472            }
1473        }
1474        if (testres) {
1475            var force_suppress = false;
1476        } else {
1477            var force_suppress = true;
1478        }
1479        if (flags.condition.not) {
1480            force_suppress = !force_suppress;
1481        }
1482        return force_suppress;
1483    },
1484    
1485    SYS_OPTIONS: [
1486        "prioritize_disambiguate_condition",
1487        "csl_reverse_lookup_support",
1488        "main_title_from_short_title",
1489        "uppercase_subtitles",
1490        "force_short_title_casing_alignment",
1491        "implicit_short_title",
1492        "split_container_title"
1493    ],
1494
1495    TITLE_SPLIT_REGEXP: (function() {
1496        var splits = [
1497            "\\.\\s+",
1498            "\\!\\s+",
1499            "\\?\\s+",
1500            "\\s*::*\\s+",
1501            "\\s*—\\s*",
1502            "\\s+\\-\\s+",
1503            "\\s*\\-\\-\\-*\\s*"
1504        ]
1505        return {
1506            match: new RegExp("(" + splits.join("|") + ")", "g"),
1507            matchfirst: new RegExp("^(" + splits.join("|") + ")"),
1508            split: new RegExp("(?:" + splits.join("|") + ")")
1509        }
1510    })(),
1511
1512    TITLE_SPLIT: function(str) {
1513        if (!str) {
1514            return str;
1515        }
1516        var m = str.match(CSL.TITLE_SPLIT_REGEXP.match);
1517        var lst = str.split(CSL.TITLE_SPLIT_REGEXP.split);
1518        for (var i=lst.length-2; i>-1; i--) {
1519            lst[i] = lst[i].trim();
1520            if (lst[i] && lst[i].slice(-1).toLowerCase() !== lst[i].slice(-1)) {
1521                // recombine
1522                lst[i] = lst[i] + m[i] + lst[i+1];
1523                lst = lst.slice(0, i+1).concat(lst.slice(i+2))
1524            } else {
1525                // merge
1526                lst = lst.slice(0, i+1).concat([m[i]]).concat(lst.slice(i+1))
1527            }
1528        }
1529        return lst;
1530    },
1531    
1532    GET_COURT_CLASS: function(state, Item, sortKey){
1533        // Get authority as a string
1534        var cls = "";
1535        var authority = null;
1536        var country = Item.jurisdiction ? Item.jurisdiction.split(":")[0] : null;
1537        // inStyle versus in module
1538        var classType = "court_condition_classes";
1539        if (sortKey) {
1540            classType = "court_key_classes";
1541        }
1542        if (country && Item.authority) {
1543            if ("string" === typeof Item.authority) {
1544                authority = Item.authority;
1545            } else {
1546                if (Item.authority[0] && Item.authority[0].literal) {
1547                    authority = Item.authority[0].literal;
1548                }
1549            }
1550        }
1551        if (authority) {
1552            if (this.lang && state.locale[this.lang].opts[classType] && state.locale[this.lang].opts[classType][country] && state.locale[this.lang].opts[classType][country][authority]) {
1553                cls = state.locale[this.lang].opts[classType][country][authority];
1554            } else if (state.locale[state.opt["default-locale"][0]].opts[classType] && state.locale[state.opt["default-locale"][0]].opts[classType][country] && state.locale[state.opt["default-locale"][0]].opts[classType][country][authority]) {
1555                cls = state.locale[state.opt["default-locale"][0]].opts[classType][country][authority]
1556            }
1557        }
1558        return cls;
1559    },
1560
1561    SET_COURT_CLASSES: function(state, lang, myxml, dataObj) {
1562        var nodes = myxml.getNodesByName(dataObj, 'court-class');
1563        for (var pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) {
1564            var courtclass = nodes[pos];
1565            var attributes = myxml.attributes(courtclass);
1566            var cls = attributes["@name"];
1567            var country = attributes["@country"];
1568            var courts = attributes["@courts"];
1569            
1570            // Okay, this is a hack.
1571            // If state.registry IS NOT yet defined, this is an in-style declaration.
1572            // If state.registry IS defined, this is an in-module declaration.
1573            var classType = "court_key_classes";
1574            if (state.registry) {
1575                classType = "court_condition_classes";
1576            }
1577            
1578            if (cls && country && courts) {
1579                courts = courts.trim().split(/\s+/);
1580                if (!state.locale[lang].opts[classType]) {
1581                    state.locale[lang].opts[classType] = {};
1582                }
1583                if (!state.locale[lang].opts[classType][country]) {
1584                    state.locale[lang].opts[classType][country] = {};
1585                }
1586                for (var i=0,ilen=courts.length;i<ilen;i++) {
1587                    state.locale[lang].opts[classType][country][courts[i]] = cls;
1588                }
1589            }
1590        }
1591    },
1592
1593    INIT_JURISDICTION_MACROS: function (state, Item, item, macroName) {
1594        if (Item["best-jurisdiction"]) {
1595            return true;
1596        }
1597        if (!state.sys.retrieveStyleModule || !CSL.MODULE_MACROS[macroName] || !Item.jurisdiction) {
1598            return false;
1599        }
1600        var jurisdictionList = state.getJurisdictionList(Item.jurisdiction);
1601        // Set up a list of jurisdictions here, we will reuse it
1602        if (!state.opt.jurisdictions_seen[jurisdictionList[0]]) {
1603            var res = state.retrieveAllStyleModules(jurisdictionList);
1604            // Okay. We have code for each of the novel modules in the
1605            // hierarchy. Load them all into the processor.
1606            for (var jurisdiction in res) {
1607                var fallback = state.loadStyleModule(jurisdiction, res[jurisdiction]);
1608                if (fallback) {
1609                    if (!res[fallback]) {
1610                        Object.assign(res, state.retrieveAllStyleModules([fallback]));
1611                        state.loadStyleModule(fallback, res[fallback], true);
1612                    }
1613                }
1614            }
1615        }
1616        var jurisdictionList = state.getJurisdictionList(Item.jurisdiction);
1617        if (state.opt.parallel.enable) {
1618            if (!state.parallel) {
1619                state.parallel = new CSL.Parallel(state);
1620            }
1621        }
1622        // Identify the best jurisdiction for the item and return true, otherwise return false
1623        for (var i=0,ilen=jurisdictionList.length;i<ilen;i++) {
1624            var jurisdiction = jurisdictionList[i];
1625            if (item) {
1626                if (state.juris[jurisdiction] && !item["best-jurisdiction"] && state.juris[jurisdiction].types.locator) {
1627                    Item["best-jurisdiction"] = jurisdiction;
1628                }
1629            }
1630            if(state.juris[jurisdiction] && state.juris[jurisdiction].types[Item.type]) {
1631                Item["best-jurisdiction"] = jurisdiction;
1632                return true;
1633            }
1634        }
1635        return false;
1636    }
1637};
1638
1639/**
1640 * Functions for parsing an XML object converted to JSON.
1641 */
1642
1643/*
1644  Style and locale JSON should be formatted as follows. Note that
1645  an empty literal should be set as an explicit empty strings within
1646  children:[]
1647  
1648  {
1649    name:"term",
1650    children:[
1651      ""
1652    ],
1653    attrs:{
1654      name:"author"
1655    }
1656  }
1657
1658  The following script will generate correctly formatted JSON
1659  from a CSL style or locale file:
1660*/
1661
1662CSL.XmlJSON = function (dataObj) {
1663    this.dataObj = dataObj;
1664    this.institution = {
1665        name:"institution",
1666        attrs:{
1667            "institution-parts":"long",
1668            "delimiter":", "
1669        },
1670        children:[
1671            {
1672                name:"institution-part",
1673                attrs:{
1674                    name:"long"
1675                },
1676                children:[]
1677            }
1678        ]
1679    };
1680};
1681
1682/**
1683 * No need for cleaning with native JSON.
1684 */
1685CSL.XmlJSON.prototype.clean = function (json) {
1686    return json;
1687};
1688
1689
1690/**
1691 * Methods to call on a node.
1692 */
1693CSL.XmlJSON.prototype.getStyleId = function (myjson, styleName) {
1694    var tagName = 'id';
1695    if (styleName) {
1696        tagName = 'title';
1697    }
1698    var ret = "";
1699    var children = myjson.children;
1700    for (var i=0,ilen=children.length;i<ilen;i++) {
1701        if (children[i].name === 'info') {
1702            var grandkids = children[i].children;
1703            for (var j=0,jlen=grandkids.length;j<jlen;j++) {
1704                if (grandkids[j].name === tagName) {
1705                    ret = grandkids[j].children[0];
1706                }
1707            }
1708        }
1709    }
1710    return ret;
1711};
1712
1713CSL.XmlJSON.prototype.children = function (myjson) {
1714    //print("children()");
1715    if (myjson && myjson.children.length) {
1716        return myjson.children.slice();
1717    } else {
1718        return false;
1719    }
1720};
1721
1722CSL.XmlJSON.prototype.nodename = function (myjson) {
1723    //print("nodename()");
1724    return myjson ? myjson.name : null;
1725};
1726
1727CSL.XmlJSON.prototype.attributes = function (myjson) {
1728    //print("attributes()");
1729    var ret = {};
1730    for (var attrname in myjson.attrs) {
1731        ret["@"+attrname] = myjson.attrs[attrname];
1732    }
1733    return ret;
1734};
1735
1736
1737CSL.XmlJSON.prototype.content = function (myjson) {
1738    //print("content()");
1739    // xmldom.js and xmle4x.js have "undefined" as default
1740    var ret = "";
1741    // This only catches content at first level, but that is good enough
1742    // for us.
1743    if (!myjson || !myjson.children) {
1744        return ret;
1745    }
1746    for (var i=0, ilen=myjson.children.length; i < ilen; i += 1) {
1747        if ("string" === typeof myjson.children[i]) {
1748            ret += myjson.children[i];
1749        }
1750    }
1751    return ret;
1752};
1753
1754
1755CSL.XmlJSON.prototype.namespace = {}
1756
1757CSL.XmlJSON.prototype.numberofnodes = function (myjson) {
1758    //print("numberofnodes()");
1759    if (myjson && "number" == typeof myjson.length) {
1760        return myjson.length;
1761    } else {
1762        return 0;
1763    }
1764};
1765
1766// getAttributeName() removed. Looks like it was not being used.
1767
1768CSL.XmlJSON.prototype.getAttributeValue = function (myjson,name,namespace) {
1769    //print("getAttributeValue()");
1770    var ret = "";
1771    if (namespace) {
1772        name = namespace+":"+name;
1773    }
1774    if (myjson) {
1775        if (myjson.attrs) {
1776            if (myjson.attrs[name]) {
1777                ret = myjson.attrs[name];
1778            } else {
1779                ret = "";
1780            }
1781        }
1782    }
1783    return ret;
1784}
1785
1786CSL.XmlJSON.prototype.getNodeValue = function (myjson,name) {
1787    //print("getNodeValue()");
1788    var ret = "";
1789    if (name){
1790        for (var i=0, ilen=myjson.children.length; i < ilen; i += 1) {
1791            if (myjson.children[i].name === name) {
1792                // This will always be Object() unless empty
1793                if (myjson.children[i].children.length) {
1794                    ret = myjson.children[i];
1795                } else {
1796                    ret = "";
1797                }
1798            }
1799        }
1800    } else if (myjson) {
1801        ret = myjson;
1802    }
1803    // Just being careful here, following the former DOM code. The JSON object we receive 
1804    // for this should be fully normalized.
1805    if (ret && ret.children && ret.children.length == 1 && "string" === typeof ret.children[0]) {
1806        ret = ret.children[0];
1807    }
1808    return ret;
1809}
1810
1811CSL.XmlJSON.prototype.setAttributeOnNodeIdentifiedByNameAttribute = function (myjson,nodename,partname,attrname,val) {
1812    //print("setAttributeOnNodeIdentifiedByNameAttribute()");
1813    var pos, len, xml, nodes, node;
1814    if (attrname.slice(0,1) === '@'){
1815        attrname = attrname.slice(1);
1816    }
1817    // In the one place this is used in citeproc-js code, it doesn't need to recurse.
1818    for (var i=0,ilen=myjson.children.length; i<ilen; i += 1) {
1819        if (myjson.children[i].name === nodename && myjson.children[i].attrs.name === partname) {
1820            myjson.children[i].attrs[attrname] = val;
1821        }
1822    }
1823}
1824
1825CSL.XmlJSON.prototype.deleteNodeByNameAttribute = function (myjson,val) {
1826    //print("deleteNodeByNameAttribute()");
1827    var i, ilen;
1828    for (i = 0, ilen = myjson.children.length; i < ilen; i += 1) {
1829        if (!myjson.children[i] || "string" === typeof myjson.children[i]) {
1830            continue;
1831        }
1832        if (myjson.children[i].attrs.name == val) {
1833            myjson.children = myjson.children.slice(0,i).concat(myjson.children.slice(i+1));
1834        }
1835    }
1836}
1837
1838CSL.XmlJSON.prototype.deleteAttribute = function (myjson,attrname) {
1839    //print("deleteAttribute()");
1840    var i, ilen;
1841    if ("undefined" !== typeof myjson.attrs[attrname]) {
1842        myjson.attrs.pop(attrname);
1843    }
1844}
1845
1846CSL.XmlJSON.prototype.setAttribute = function (myjson,attr,val) {
1847    //print("setAttribute()");
1848    myjson.attrs[attr] = val;
1849    return false;
1850}
1851
1852CSL.XmlJSON.prototype.nodeCopy = function (myjson,clone) {
1853    //print("nodeCopy()");
1854    if (!clone) {
1855        var clone = {};
1856    }
1857    if ("object" === typeof clone && "undefined" === typeof clone.length) {
1858        // myjson is an object
1859        for (var key in myjson) {
1860            if ("string" === typeof myjson[key]) {
1861                clone[key] = myjson[key];
1862            } else if ("object" === typeof myjson[key]) {
1863                if ("undefined" === typeof myjson[key].length) {
1864                    clone[key] = this.nodeCopy(myjson[key],{});
1865                } else {
1866                    clone[key] = this.nodeCopy(myjson[key],[]);
1867                }
1868            }
1869        }
1870    } else {
1871        // myjson is an array
1872        for (var i=0,ilen=myjson.length;i<ilen; i += 1) {
1873            if ("string" === typeof myjson[i]) {
1874                clone[i] = myjson[i];
1875            } else {
1876                // If it's at the first level of an array, it's an object.
1877                clone[i] = this.nodeCopy(myjson[i],{});
1878            }
1879        }
1880    }
1881    return clone;
1882}
1883
1884CSL.XmlJSON.prototype.getNodesByName = function (myjson,name,nameattrval,ret) {
1885    //print("getNodesByName()");
1886    var nodes, node, pos, len;
1887    if (!ret) {
1888        var ret = [];
1889    }
1890    if (!myjson || !myjson.children) {
1891        return ret;
1892    }
1893    if (name === myjson.name) {
1894        if (nameattrval) {
1895            if (nameattrval === myjson.attrs.name) {
1896                ret.push(myjson);
1897            }
1898        } else {
1899            ret.push(myjson);
1900        }
1901    }
1902    for (var i=0,ilen=myjson.children.length;i<ilen;i+=1){
1903        if ("object" !== typeof myjson.children[i]) {
1904            continue;
1905        }
1906        this.getNodesByName(myjson.children[i],name,nameattrval,ret);
1907    }
1908    return ret;
1909}
1910
1911CSL.XmlJSON.prototype.nodeNameIs = function (myjson,name) {
1912    //print("nodeNameIs()");
1913    if (typeof myjson === "undefined") {
1914        return false;
1915    }
1916    if (name == myjson.name) {
1917        return true;
1918    }
1919    return false;
1920}
1921
1922CSL.XmlJSON.prototype.makeXml = function (myjson) {
1923    //print("makeXml()");
1924    if ("string" === typeof myjson) {
1925        if (myjson.slice(0, 1) === "<") {
1926            myjson = this.jsonStringWalker.walkToObject(myjson);
1927        } else {
1928            myjson = JSON.parse(myjson);
1929        }
1930    }
1931    return myjson;
1932};
1933
1934CSL.XmlJSON.prototype.insertChildNodeAfter = function (parent,node,pos,datejson) {
1935    //print("insertChildNodeAfter()");
1936    // Function is misnamed: this replaces the node
1937    for (var i=0,ilen=parent.children.length;i<ilen;i+=1) {
1938        if (node === parent.children[i]) {
1939            parent.children = parent.children.slice(0,i).concat([datejson]).concat(parent.children.slice(i+1));
1940            break;
1941        }
1942    }
1943    return parent;
1944};
1945
1946
1947CSL.XmlJSON.prototype.insertPublisherAndPlace = function(myjson) {
1948    if (myjson.name === "group") {
1949        var useme = true;
1950        var mustHaves = ["publisher","publisher-place"];
1951        for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) {
1952            var haveVarname = mustHaves.indexOf(myjson.children[i].attrs.variable);
1953            var isText = myjson.children[i].name === "text";
1954            if (isText && haveVarname > -1 && !myjson.children[i].attrs.prefix && !myjson.children[i].attrs.suffix) {
1955                mustHaves = mustHaves.slice(0,haveVarname).concat(mustHaves.slice(haveVarname+1));
1956            } else {
1957                useme = false;
1958                break;
1959            }
1960        }
1961        if (useme && !mustHaves.length) {
1962            myjson.attrs["has-publisher-and-publisher-place"] = true;
1963       }
1964    }
1965    for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) {
1966        if ("object" === typeof myjson.children[i]) {
1967            this.insertPublisherAndPlace(myjson.children[i]);
1968        }
1969    }    
1970}
1971/*
1972CSL.XmlJSON.prototype.insertPublisherAndPlace = function(myxml) {
1973    var group = myxml.getElementsByTagName("group");
1974    for (var i = 0, ilen = group.length; i < ilen; i += 1) {
1975        var node = group.item(i);
1976        var skippers = [];
1977        for (var j = 0, jlen = node.childNodes.length; j < jlen; j += 1) {
1978            if (node.childNodes.item(j).nodeType !== 1) {
1979                skippers.push(j);
1980            }
1981        }
1982        if (node.childNodes.length - skippers.length === 2) {
1983            var twovars = [];
1984            for (var j = 0, jlen = 2; j < jlen; j += 1) {
1985                if (skippers.indexOf(j) > -1) {
1986                    continue;
1987                }
1988                var child = node.childNodes.item(j);                    
1989                var subskippers = [];
1990                for (var k = 0, klen = child.childNodes.length; k < klen; k += 1) {
1991                    if (child.childNodes.item(k).nodeType !== 1) {
1992                        subskippers.push(k);
1993                    }
1994                }
1995                if (child.childNodes.length - subskippers.length === 0) {
1996                    twovars.push(child.getAttribute('variable'));
1997                    if (child.getAttribute('suffix')
1998                        || child.getAttribute('prefix')) {
1999                        twovars = [];
2000                        break;
2001                    }
2002                }
2003            }
2004            if (twovars.indexOf("publisher") > -1 && twovars.indexOf("publisher-place") > -1) {
2005                node.setAttribute('has-publisher-and-publisher-place', true);
2006            }
2007        }
2008    }
2009};
2010*/
2011
2012CSL.XmlJSON.prototype.isChildOfSubstitute = function(parents) {
2013    if (parents.length > 0) {
2014        var myparents = parents.slice();
2015        var parent = myparents.pop();
2016        if (parent === "substitute") {
2017            return true;
2018        } else {
2019            return this.isChildOfSubstitute(myparents);
2020        }
2021    }
2022    return false;
2023};
2024
2025CSL.XmlJSON.prototype.addMissingNameNodes = function(myjson,parents) {
2026    if (!parents) {
2027        parents = [];
2028    }
2029    if (myjson.name === "names") {
2030        // Trawl through children to decide whether a name node is needed here
2031        if (!this.isChildOfSubstitute(parents)) {
2032            var addName = true;
2033            for (var i=0,ilen=myjson.children.length;i<ilen;i++) {
2034                if (myjson.children[i].name === "name") {
2035                    addName = false;
2036                    break;
2037                }
2038            }
2039            if (addName) {
2040                myjson.children = [{name:"name",attrs:{},children:[]}].concat(myjson.children);
2041            }
2042        }
2043    }
2044    parents.push(myjson.name);
2045    for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) {
2046        if ("object" === typeof myjson.children[i]) {
2047            this.addMissingNameNodes(myjson.children[i],parents);
2048        }
2049    }
2050    parents.pop();
2051}
2052
2053
2054CSL.XmlJSON.prototype.addInstitutionNodes = function(myjson) {
2055    //print("addInstitutionNodes()");
2056    var names, thenames, institution, theinstitution, name, thename, xml, pos, len;
2057    // The idea here is to map relevant attributes from name and nampart=family
2058    // to the "long" institution-part node, when and only when forcing insert
2059    // of the default node.
2060    if (myjson.name === "names") {
2061        // do stuff
2062        var attributes = {};
2063        var insertPos = -1;
2064        for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) {
2065            if (myjson.children[i].name == "name") {
2066                for (var key in myjson.children[i].attrs) {
2067                    attributes[key] = myjson.children[i].attrs[key];
2068                }
2069                attributes.delimiter = myjson.children[i].attrs.delimiter;
2070                attributes.and = myjson.children[i].attrs.and;
2071                insertPos = i;
2072                for (var k=0,klen=myjson.children[i].children.length;k<klen;k+=1) {
2073                    if (myjson.children[i].children[k].attrs.name !== 'family') {
2074                        continue;
2075                    }
2076                    for (var key in myjson.children[i].children[k].attrs) {
2077                        attributes[key] = myjson.children[i].children[k].attrs[key];
2078                    }
2079                }
2080            }
2081            if (myjson.children[i].name == "institution") {
2082                insertPos = -1;
2083                break;
2084            }
2085        }
2086        if (insertPos > -1) {
2087            var institution = this.nodeCopy(this.institution);
2088            for (var i=0,ilen = CSL.INSTITUTION_KEYS.length;i<ilen;i+=1) {
2089                var attrname = CSL.INSTITUTION_KEYS[i];
2090                if ("undefined" !== typeof attributes[attrname]) {
2091                    institution.children[0].attrs[attrname] = attributes[attrname];
2092                }
2093                if (attributes.delimiter) {
2094                    institution.attrs.delimiter = attributes.delimiter;
2095                }
2096                if (attributes.and) {
2097                    institution.attrs.and = attributes.and;
2098                }
2099            }
2100            myjson.children = myjson.children.slice(0,insertPos+1).concat([institution]).concat(myjson.children.slice(insertPos+1));
2101        }
2102    }
2103    for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) {
2104        if ("string" === typeof myjson.children[i]) {
2105            continue;
2106        }
2107        // Recurse
2108        this.addInstitutionNodes(myjson.children[i]);
2109    }
2110}
2111CSL.XmlJSON.prototype.flagDateMacros = function(myjson) {
2112    // print("flagDateMacros()");
2113    for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) {
2114        if (myjson.children[i].name === "macro") {
2115            if (this.inspectDateMacros(myjson.children[i])) {
2116                myjson.children[i].attrs["macro-has-date"] = "true";
2117            }
2118        }
2119    }
2120}
2121CSL.XmlJSON.prototype.inspectDateMacros = function(myjson) {
2122    //print("inspectDateMacros()");
2123    if (!myjson || !myjson.children) {
2124        return false;
2125    }
2126    if (myjson.name === "date") {
2127        return true;
2128    } else {
2129        for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) {
2130            if (this.inspectDateMacros(myjson.children[i])) {
2131                return true;
2132            }
2133        }
2134    }
2135    return false;
2136}
2137
2138/*
2139 * Clean serialized XML
2140 */
2141CSL.stripXmlProcessingInstruction = function (xml) {
2142    if (!xml) {
2143        return xml;
2144    }
2145    xml = xml.replace(/^<\?[^?]+\?>/, "");
2146    xml = xml.replace(/<!--[^>]+-->/g, "");
2147    xml = xml.replace(/^\s+/g, "");
2148    xml = xml.replace(/\s+$/g, "");
2149    return xml;
2150};
2151
2152
2153/*
2154 * String parser for XML inputs
2155 */
2156CSL.parseXml = function(str) {
2157
2158    var _pos = 0;
2159    var _obj = {children:[]};
2160    var _stack = [_obj.children];
2161
2162    function _listifyString(str) {
2163        str = str.split(/(?:\r\n|\n|\r)/).join(" ").replace(/>[	 ]+</g, "><").replace(/<\!--.*?-->/g, "");
2164        var lst = str.split("><");
2165        var stylePos = null;
2166        for (var i=0,ilen=lst.length;i<ilen;i++) {
2167            if (i > 0) {
2168                lst[i] = "<" + lst[i];
2169            }
2170            if (i < (lst.length-1)) {
2171                lst[i] = lst[i] + ">";
2172            }
2173            if ("number" != typeof stylePos) {
2174                if (lst[i].slice(0, 7) === "<style " || lst[i].slice(0, 8) == "<locale ") {
2175                    stylePos = i;
2176                }
2177            }
2178        }
2179        lst = lst.slice(stylePos);
2180        // Combine open/close elements for empty terms,
2181        // so that they will be passed through correctly
2182        // as empty strings.
2183        for (var i=lst.length-2;i>-1;i--) {
2184            if (lst[i].slice(1).indexOf("<") === -1) {
2185                var stub = lst[i].slice(0, 5);
2186                if (lst[i].slice(-2) !== "/>") {
2187                    if (stub === "<term") {
2188                        if (lst[i+1].slice(0, 6) === "</term") {
2189                            lst[i] = lst[i] + lst[i+1];
2190                            lst = lst.slice(0, i+1).concat(lst.slice(i+2));
2191                        }
2192                    } else if (["<sing", "<mult"].indexOf(stub) > -1) {
2193                        if (lst[i].slice(-2) !== "/>" && lst[i+1].slice(0, 1) === "<") {
2194                            lst[i] = lst[i] + lst[i+1];
2195                            lst = lst.slice(0, i+1).concat(lst.slice(i+2));
2196                        }
2197                    }
2198                }
2199            }
2200        }
2201        return lst;
2202    }
2203
2204    function _decodeHtmlEntities(str) {
2205        return str
2206            .split("&amp;").join("&")
2207            .split("&quot;").join("\"")
2208            .split("&gt;").join(">").split("&lt;").join("<")
2209            .replace(/&#([0-9]{1,6});/gi, function(match, numStr) {
2210                var num = parseInt(numStr, 10); // read num as normal number
2211                return String.fromCharCode(num);
2212            })
2213            .replace(/&#x([a-f0-9]{1,6});/gi, function(match, numStr){
2214                var num = parseInt(numStr, 16); // read num as hex
2215                return String.fromCharCode(num);
2216            });
2217    }
2218
2219    function _getAttributes(elem) {
2220        var m = elem.match(/([^\'\"=	 ]+)=(?:\"[^\"]*\"|\'[^\']*\')/g);
2221        if (m) {
2222            for (var i=0,ilen=m.length;i<ilen;i++) {
2223                m[i] = m[i].replace(/=.*/, "");
2224            }
2225        }
2226        return m;
2227    }
2228
2229    function _getAttribute(elem, attr) {
2230        var rex = RegExp('^.*[	 ]+' + attr + '=(\"(?:[^\"]*)\"|\'(?:[^\']*)\').*$');
2231        var m = elem.match(rex);
2232        return m ? m[1].slice(1, -1) : null;
2233    }
2234
2235    function _getTagName(elem) {
2236        var rex = RegExp("^<([^	 />]+)");
2237        var m = elem.match(rex);
2238        return m ? m[1] : null;
2239    }
2240    
2241
2242    function _castObjectFromOpeningTag(elem) {
2243        var obj = {};
2244        obj.name = _getTagName(elem);
2245        obj.attrs = {};
2246        var attributes = _getAttributes(elem);
2247        if (attributes) {
2248            for (var i=0,ilen=attributes.length;i<ilen;i++) {
2249                var attr = {
2250                    name: attributes[i],
2251                    value: _getAttribute(elem, attributes[i])
2252                }
2253                obj.attrs[attr.name] = _decodeHtmlEntities(attr.value);
2254            }
2255        }
2256        obj.children = [];
2257        return obj;
2258    }
2259
2260    function _extractTextFromCompositeElement(elem) {
2261        var m = elem.match(/^.*>([^<]*)<.*$/);
2262        return _decodeHtmlEntities(m[1]);
2263    }
2264
2265    function _appendToChildren(obj) {
2266        _stack.slice(-1)[0].push(obj);
2267    }
2268
2269    function _extendStackWithNewChildren(obj) {
2270        _stack.push(obj.children);
2271    }
2272
2273    function processElement(elem) {
2274        var obj;
2275        if (elem.slice(1).indexOf('<') > -1) {
2276            // withtext
2277            var tag = elem.slice(0, elem.indexOf('>')+1);
2278            obj = _castObjectFromOpeningTag(tag);
2279            obj.children = [_extractTextFromCompositeElement(elem)];
2280            _appendToChildren(obj);
2281        } else if (elem.slice(-2) === '/>') {
2282            // singleton
2283            obj = _castObjectFromOpeningTag(elem);
2284            // Empty term as singleton
2285            if (_getTagName(elem) === 'term') {
2286                obj.children.push('');
2287            }
2288            _appendToChildren(obj);
2289        } else if (elem.slice(0, 2) === '</') {
2290            // close
2291            _stack.pop();
2292        } else {
2293            // open
2294            obj = _castObjectFromOpeningTag(elem);
2295            _appendToChildren(obj)
2296            _extendStackWithNewChildren(obj);
2297        }
2298    }
2299
2300    var lst = _listifyString(str);
2301
2302    for (var i=0,ilen=lst.length;i<ilen;i++) {
2303        var elem = lst[i];
2304        processElement(elem);
2305    }
2306    return _obj.children[0];
2307}
2308
2309/**
2310 * Functions for parsing an XML object using E4X.
2311 */
2312
2313CSL.XmlDOM = function (dataObj) {
2314    this.dataObj = dataObj;
2315    if ("undefined" == typeof DOMParser) {
2316        DOMParser = function() {};
2317        DOMParser.prototype.parseFromString = function(str, contentType) {
2318            if ("undefined" != typeof ActiveXObject) {
2319                var xmldata = new ActiveXObject('MSXML.DomDocument');
2320                xmldata.async = false;
2321                xmldata.loadXML(str);
2322                return xmldata;
2323            } else if ("undefined" != typeof XMLHttpRequest) {
2324                var xmldata = new XMLHttpRequest;
2325                if (!contentType) {
2326                    contentType = 'text/xml';
2327                }
2328                xmldata.open('GET', 'data:' + contentType + ';charset=utf-8,' + encodeURIComponent(str), false);
2329                if(xmldata.overrideMimeType) {
2330                    xmldata.overrideMimeType(contentType);
2331                }
2332                xmldata.send(null);
2333                return xmldata.responseXML;
2334            } else if ("undefined" != typeof marknote) {
2335                var parser = new marknote.Parser();
2336                return parser.parse(str);
2337            }
2338        };
2339        this.hasAttributes = function (node) {
2340            var ret;
2341            if (node.attributes && node.attributes.length) {
2342                ret = true;
2343            } else {
2344                ret = false;
2345            }
2346            return ret;
2347        };
2348    } else {
2349        /*
2350        this.hasAttributes = function (node) {
2351            return node["hasAttributes"]();
2352        };
2353        */
2354        this.hasAttributes = function (node) {
2355            var ret;
2356            if (node.attributes && node.attributes.length) {
2357                ret = true;
2358            } else {
2359                ret = false;
2360            }
2361            return ret;
2362        };
2363    }
2364    this.importNode = function (doc, srcElement) {
2365        var ret;
2366        if ("undefined" == typeof doc.importNode) {
2367            ret = this._importNode(doc, srcElement, true);
2368        } else {
2369            ret = doc.importNode(srcElement, true);
2370        }
2371        return ret;
2372    };
2373    // In case importNode is not available.
2374    // Thx + hat tip to Anthony T. Holdener III
2375    // http://www.alistapart.com/articles/crossbrowserscripting
2376    // cases 3, 4, 8 = text, cdata, comment
2377    this._importNode = function(doc, node, allChildren) {
2378        switch (node.nodeType) {
2379            // element node
2380            case 1:
2381                var newNode = doc.createElement(node.nodeName);
2382                if (node.attributes && node.attributes.length > 0)
2383                    for (var i = 0, il = node.attributes.length; i < il;)
2384                        newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i++].nodeName));
2385                    if (allChildren && node.childNodes && node.childNodes.length > 0)
2386                        for (var i = 0, il = node.childNodes.length; i < il;)
2387                            newNode.appendChild(this._importNode(doc, node.childNodes[i++], allChildren));
2388                return newNode;
2389                break;
2390            case 3:
2391            case 4:
2392            case 8:
2393                // Drop comments on the floor as well.
2394                //return doc.createTextNode(node.nodeValue);
2395                //break;
2396        }
2397    };
2398    this.parser = new DOMParser();
2399
2400    // This seems horribly tormented, but there might be a reason for it.
2401    // Perhaps this was the only way I found to get namespacing to work ... ?
2402    var str = "<docco><institution institution-parts=\"long\" delimiter=\", \" substitute-use-first=\"1\" use-last=\"1\"><institution-part name=\"long\"/></institution></docco>";
2403    var inst_doc = this.parser.parseFromString(str, "text/xml");
2404    var inst_node = inst_doc.getElementsByTagName("institution");
2405    this.institution = inst_node.item(0);
2406    var inst_part_node = inst_doc.getElementsByTagName("institution-part");
2407    this.institutionpart = inst_part_node.item(0);
2408    this.ns = "http://purl.org/net/xbiblio/csl";
2409};
2410
2411/**
2412 * No need for cleaning with the DOM, I think.  This will probably just be a noop.
2413 * But first, let's get XML mode switching up and running.
2414 */
2415CSL.XmlDOM.prototype.clean = function (xml) {
2416    xml = xml.replace(/<\?[^?]+\?>/g, "");
2417    xml = xml.replace(/<![^>]+>/g, "");
2418    xml = xml.replace(/^\s+/, "");
2419    xml = xml.replace(/\s+$/, "");
2420    xml = xml.replace(/^\n*/, "");
2421    return xml;
2422};
2423
2424
2425/**
2426 * Methods to call on a node.
2427 */
2428CSL.XmlDOM.prototype.getStyleId = function (myxml, styleName) {
2429    var text = "";
2430    var tagName = "id";
2431    if (styleName) {
2432        tagName = "title";
2433    }
2434    var node = myxml.getElementsByTagName(tagName);
2435    if (node && node.length) {
2436        node = node.item(0);
2437    }
2438    if (node) {
2439        // W3C conformant browsers
2440        text = node.textContent;
2441    }
2442    if (!text) {
2443        // Opera, IE 6 & 7
2444        text = node.innerText;
2445    }
2446    if (!text) {
2447        // Safari
2448        text = node.innerHTML;
2449    }
2450    return text;
2451};
2452
2453CSL.XmlDOM.prototype.children = function (myxml) {
2454    var children, pos, len, ret;
2455    if (myxml) {
2456        ret = [];
2457        children = myxml.childNodes;
2458        for (pos = 0, len = children.length; pos < len; pos += 1) {
2459            if (children[pos].nodeName != "#text") {
2460                ret.push(children[pos]);
2461            }
2462        }
2463        return ret;
2464    } else {
2465        return [];
2466    }
2467};
2468
2469CSL.XmlDOM.prototype.nodename = function (myxml) {
2470    var ret = myxml.nodeName;
2471    return ret;
2472};
2473
2474CSL.XmlDOM.prototype.attributes = function (myxml) {
2475    var ret, attrs, attr, key, xml, pos, len;
2476    ret = new Object();
2477    if (myxml && this.hasAttributes(myxml)) {
2478        attrs = myxml.attributes;
2479        for (pos = 0, len=attrs.length; pos < len; pos += 1) {
2480            attr = attrs[pos];
2481            ret["@" + attr.name] = attr.value;
2482        }
2483    }
2484    return ret;
2485};
2486
2487
2488CSL.XmlDOM.prototype.content = function (myxml) {
2489    var ret;
2490    if ("undefined" != typeof myxml.textContent) {
2491        ret = myxml.textContent;
2492    } else if ("undefined" != typeof myxml.innerText) {
2493        ret = myxml.innerText;
2494    } else {
2495        ret = myxml.txt;
2496    }
2497    return ret;
2498};
2499
2500
2501CSL.XmlDOM.prototype.namespace = {
2502    "xml":"http://www.w3.org/XML/1998/namespace"
2503}
2504
2505CSL.XmlDOM.prototype.numberofnodes = function (myxml) {
2506    if (myxml) {
2507        return myxml.length;
2508    } else {
2509        return 0;
2510    }
2511};
2512
2513CSL.XmlDOM.prototype.getAttributeName = function (attr) {
2514    var ret = attr.name;
2515    return ret;
2516}
2517
2518CSL.XmlDOM.prototype.getAttributeValue = function (myxml,name,namespace) {
2519    var ret = "";
2520    if (namespace) {
2521        name = namespace+":"+name;
2522    }
2523    if (myxml && this.hasAttributes(myxml) && myxml.getAttribute(name)) {
2524        ret = myxml.getAttribute(name);
2525    }
2526    return ret;
2527}
2528
2529//
2530// Can't this be, you know ... simplified?
2531//
2532CSL.XmlDOM.prototype.getNodeValue = function (myxml,name) {
2533    var ret = null;
2534    if (name){
2535        var vals = myxml.getElementsByTagName(name);
2536        if (vals.length > 0) {
2537            if ("undefined" != typeof vals[0].textContent) {
2538                ret = vals[0].textContent;
2539            } else if ("undefined" != typeof vals[0].innerText) {
2540                ret = vals[0].innerText;
2541            } else {
2542                ret = vals[0].text;
2543            }
2544        }
2545    }
2546    if (ret === null && myxml && myxml.childNodes && (myxml.childNodes.length == 0 || (myxml.childNodes.length == 1 && myxml.firstChild.nodeName == "#text"))) {
2547        if ("undefined" != typeof myxml.textContent) {
2548            ret = myxml.textContent;
2549        } else if ("undefined" != typeof myxml.innerText) {
2550            ret = myxml.innerText;
2551        } else {
2552            ret = myxml.text;
2553        }
2554    }
2555    if (ret === null) {
2556        ret = myxml;
2557    }
2558    return ret;
2559}
2560
2561CSL.XmlDOM.prototype.setAttributeOnNodeIdentifiedByNameAttribute = function (myxml,nodename,partname,attrname,val) {
2562    var pos, len, xml, nodes, node;
2563    if (attrname.slice(0,1) === '@'){
2564        attrname = attrname.slice(1);
2565    }
2566    nodes = myxml.getElementsByTagName(nodename);
2567    for (pos = 0, len = nodes.length; pos < len; pos += 1) {
2568        node = nodes[pos];
2569        if (node.getAttribute("name") != partname) {
2570            continue;
2571        }
2572        node.setAttribute(attrname, val);
2573    }
2574}
2575
2576CSL.XmlDOM.prototype.deleteNodeByNameAttribute = function (myxml,val) {
2577    var pos, len, node, nodes;
2578    nodes = myxml.childNodes;
2579    for (pos = 0, len = nodes.length; pos < len; pos += 1) {
2580        node = nodes[pos];
2581        if (!node || node.nodeType == node.TEXT_NODE) {
2582            continue;
2583        }
2584        if (this.hasAttributes(node) && node.getAttribute("name") == val) {
2585            myxml.removeChild(nodes[pos]);
2586        }
2587    }
2588}
2589
2590CSL.XmlDOM.prototype.deleteAttribute = function (myxml,attr) {
2591    myxml.removeAttribute(attr);
2592}
2593
2594CSL.XmlDOM.prototype.setAttribute = function (myxml,attr,val) {
2595    if (!myxml.ownerDocument) {
2596        myxml = myxml.firstChild;
2597    }
2598    // "unknown" to satisfy IE8, which crashes when setAttribute
2599    // is checked directly as a property, and report its type as
2600    // "unknown".
2601    // Many thanks to Phil Lord for tracing the cause of the fault.
2602    if (["function", "unknown"].indexOf(typeof myxml.setAttribute) > -1) {
2603        myxml.setAttribute(attr, val);
2604    }
2605    return false;
2606}
2607
2608CSL.XmlDOM.prototype.nodeCopy = function (myxml) {
2609    var cloned_node = myxml.cloneNode(true);
2610    return cloned_node;
2611}
2612
2613CSL.XmlDOM.prototype.getNodesByName = function (myxml,name,nameattrval) {
2614    var ret, nodes, node, pos, len;
2615    ret = [];
2616    nodes = myxml.getElementsByTagName(name);
2617    for (pos = 0, len = nodes.length; pos < len; pos += 1) {
2618        node = nodes.item(pos);
2619        if (nameattrval && !(this.hasAttributes(node) && node.getAttribute("name") == nameattrval)) {
2620//        if (nameattrval && !(this.attributes && node.attributes.name && node.attributes.name.value == nameattrval)) {
2621            continue;
2622        }
2623        ret.push(node);
2624    }
2625    return ret;
2626}
2627
2628CSL.XmlDOM.prototype.nodeNameIs = function (myxml,name) {
2629    if (name == myxml.nodeName) {
2630        return true;
2631    }
2632    return false;
2633}
2634
2635CSL.XmlDOM.prototype.makeXml = function (myxml) {
2636    var ret, topnode;
2637    if (!myxml) {
2638        myxml = "<docco><bogus/></docco>";
2639    }
2640    myxml = myxml.replace(/\s*<\?[^>]*\?>\s*\n*/g, "");
2641    var nodetree = this.parser.parseFromString(myxml, "application/xml");
2642    return nodetree.firstChild;
2643};
2644
2645CSL.XmlDOM.prototype.insertChildNodeAfter = function (parent,node,pos,datexml) {
2646    var myxml, xml;
2647    myxml = this.importNode(node.ownerDocument, datexml);
2648    parent.replaceChild(myxml, node);
2649     return parent;
2650};
2651
2652CSL.XmlDOM.prototype.insertPublisherAndPlace = function(myxml) {
2653    var group = myxml.getElementsByTagName("group");
2654    for (var i = 0, ilen = group.length; i < ilen; i += 1) {
2655        var node = group.item(i);
2656        var skippers = [];
2657        for (var j = 0, jlen = node.childNodes.length; j < jlen; j += 1) {
2658            if (node.childNodes.item(j).nodeType !== 1) {
2659                skippers.push(j);
2660            }
2661        }
2662        if (node.childNodes.length - skippers.length === 2) {
2663            var twovars = [];
2664            for (var j = 0, jlen = 2; j < jlen; j += 1) {
2665                if (skippers.indexOf(j) > -1) {
2666                    continue;
2667                }
2668                var child = node.childNodes.item(j);                    
2669                var subskippers = [];
2670                for (var k = 0, klen = child.childNodes.length; k < klen; k += 1) {
2671                    if (child.childNodes.item(k).nodeType !== 1) {
2672                        subskippers.push(k);
2673                    }
2674                }
2675                if (child.childNodes.length - subskippers.length === 0) {
2676                    twovars.push(child.getAttribute('variable'));
2677                    if (child.getAttribute('suffix')
2678                        || child.getAttribute('prefix')) {
2679                        twovars = [];
2680                        break;
2681                    }
2682                }
2683            }
2684            if (twovars.indexOf("publisher") > -1 && twovars.indexOf("publisher-place") > -1) {
2685                node.setAttribute('has-publisher-and-publisher-place', true);
2686            }
2687        }
2688    }
2689};
2690
2691CSL.XmlDOM.prototype.isChildOfSubstitute = function(node) {
2692    if (node.parentNode) {
2693        if (node.parentNode.tagName.toLowerCase() === "substitute") {
2694            return true;
2695        } else {
2696            return this.isChildOfSubstitute(node.parentNode);
2697        }
2698    }
2699    return false;
2700};
2701
2702CSL.XmlDOM.prototype.addMissingNameNodes = function(myxml) {
2703    var nameslist = myxml.getElementsByTagName("names");
2704    for (var i = 0, ilen = nameslist.length; i < ilen; i += 1) {
2705        var names = nameslist.item(i);
2706        var namelist = names.getElementsByTagName("name");
2707        if ((!namelist || namelist.length === 0)
2708            && !this.isChildOfSubstitute(names)) {
2709            
2710            var doc = names.ownerDocument;
2711            var name = doc.createElement("name");
2712            names.appendChild(name);
2713        }
2714    }
2715};
2716
2717
2718CSL.XmlDOM.prototype.addInstitutionNodes = function(myxml) {
2719    var names, thenames, institution, theinstitution, theinstitutionpart, name, thename, xml, pos, len;
2720    names = myxml.getElementsByTagName("names");
2721    for (pos = 0, len = names.length; pos < len; pos += 1) {
2722        thenames = names.item(pos);
2723        name = thenames.getElementsByTagName("name");
2724        if (name.length == 0) {
2725            continue;
2726        }
2727        institution = thenames.getElementsByTagName("institution");
2728        if (institution.length == 0) {
2729            theinstitution = this.importNode(myxml.ownerDocument, this.institution);
2730            theinstitutionpart = theinstitution.getElementsByTagName("institution-part").item(0);
2731            thename = name.item(0);
2732            thenames.insertBefore(theinstitution, thename.nextSibling);
2733            for (var j = 0, jlen = CSL.INSTITUTION_KEYS.length; j < jlen; j += 1) {
2734                var attrname = CSL.INSTITUTION_KEYS[j];
2735                var attrval = thename.getAttribute(attrname);
2736                if (attrval) {
2737                    theinstitutionpart.setAttribute(attrname, attrval);
2738                }
2739            }
2740            var nameparts = thename.getElementsByTagName("name-part");
2741            for (var j = 0, jlen = nameparts.length; j < jlen; j += 1) {
2742                if ('family' === nameparts[j].getAttribute('name')) {
2743                    for (var k = 0, klen = CSL.INSTITUTION_KEYS.length; k < klen; k += 1) {
2744                        var attrname = CSL.INSTITUTION_KEYS[k];
2745                        var attrval = nameparts[j].getAttribute(attrname);
2746                        if (attrval) {
2747                            theinstitutionpart.setAttribute(attrname, attrval);
2748                        }
2749                    }
2750                }
2751            }
2752        }
2753    }
2754};
2755
2756
2757CSL.XmlDOM.prototype.flagDateMacros = function(myxml) {
2758    var pos, len, thenode, thedate;
2759    var nodes = myxml.getElementsByTagName("macro");
2760    for (pos = 0, len = nodes.length; pos < len; pos += 1) {
2761        thenode = nodes.item(pos);
2762        thedate = thenode.getElementsByTagName("date");
2763        if (thedate.length) {
2764            thenode.setAttribute('macro-has-date', 'true');
2765        }
2766    }
2767};
2768
2769
2770/*global CSL: true */
2771
2772CSL.setupXml = function(xmlObject) {
2773    var dataObj = {};
2774    var parser = null;
2775    if ("undefined" !== typeof xmlObject) {
2776        if ("string" === typeof xmlObject) {
2777            xmlObject = xmlObject.replace("^\uFEFF", "")
2778                .replace(/^\s+/, "");
2779            if (xmlObject.slice(0, 1) === "<") {
2780                // Assume serialized XML
2781                dataObj = CSL.parseXml(xmlObject);
2782            } else {
2783                // Assume serialized JSON
2784                dataObj = JSON.parse(xmlObject);
2785            }
2786            parser = new CSL.XmlJSON(dataObj);
2787        } else if ("undefined" !== typeof xmlObject.getAttribute) {
2788            // Assume DOM instance
2789            parser = new CSL.XmlDOM(xmlObject);
2790        } else if ("undefined" !== typeof xmlObject.toXMLString) {
2791            // Assume E4X object
2792            parser = new CSL.XmlE4X(xmlObject);
2793        } else {
2794            // Assume JS object
2795            parser = new CSL.XmlJSON(xmlObject);
2796        }
2797    } else {
2798        CSL.error("unable to parse XML input");
2799    }
2800    if (!parser) {
2801        CSL.error("citeproc-js error: unable to parse CSL style or locale object");
2802    }
2803    return parser;
2804};
2805
2806/*global CSL: true */
2807
2808CSL.getSortCompare = function (default_locale) {
2809    if (CSL.stringCompare) {
2810        return CSL.stringCompare;
2811    }
2812    var me = this;
2813    var strcmp;
2814    var strcmp_opts = {
2815        sensitivity:"base",
2816        ignorePunctuation:true,
2817        numeric:true
2818    };
2819    // In order, attempt the following:
2820    //   (1) Set locale collation from processor language
2821    //   (2) Use localeCompare()
2822    if (!default_locale) {
2823        default_locale = "en-US";
2824    }
2825    strcmp = function (a, b) {
2826        return CSL.toLocaleLowerCase.call(me, a).localeCompare(CSL.toLocaleLowerCase.call(me, b),default_locale,strcmp_opts);
2827    };
2828    var stripPunct = function (str) {
2829        return str.replace(/^[\[\]\'\"]*/g, "");
2830    };
2831    var getBracketPreSort = function () {
2832        if (!strcmp("[x","x")) {
2833            return false;
2834        } else {
2835            return function (a, b) {
2836                return strcmp(stripPunct(a), stripPunct(b));
2837            };
2838        }
2839    };
2840    var bracketPreSort = getBracketPreSort();
2841    var sortCompare = function (a, b) {
2842        if (bracketPreSort) {
2843            return bracketPreSort(a, b);
2844        } else {
2845            return strcmp(a, b);
2846        }
2847    };
2848    return sortCompare;
2849};
2850
2851/*global CSL: true */
2852
2853CSL.ambigConfigDiff = function(a, b) {
2854    var pos, len, ppos, llen;
2855    // return of true means the ambig configs differ
2856    if (a.names.length !== b.names.length) {
2857        //print("   (1)");
2858        return 1;
2859    } else {
2860        for (pos = 0, len = a.names.length; pos < len; pos += 1) {
2861            if (a.names[pos] !== b.names[pos]) {
2862        //print("   (2) "+a.names[pos]+" "+b.names[pos]);
2863                return 1;
2864            } else {
2865                for (ppos = 0, llen = a.givens[pos]; ppos < llen; ppos += 1) {
2866                    if (a.givens[pos][ppos] !== b.givens[pos][ppos]) {
2867        //print("   (3): "+a.givens[pos][ppos]+" "+b.givens[pos][ppos]+" "+pos+"/"+ppos+" "+b.givens[pos]);
2868                        return 1;
2869                    }
2870                }
2871            }
2872        }
2873    }
2874    if (a.disambiguate != b.disambiguate) {
2875        //print("   (4) "+a.disambiguate+" "+b.disambiguate);
2876        return 1;
2877    }
2878    if (a.year_suffix !== b.year_suffix) {
2879        //print("   (5) "+a.year_suffix+" "+b.year_suffix);
2880        return 1;
2881    }
2882    return 0;
2883};
2884
2885CSL.cloneAmbigConfig = function (config, oldconfig) {
2886    var i, ilen, j, jlen, param;
2887    var ret = {};
2888    ret.names = [];
2889    ret.givens = [];
2890    ret.year_suffix = false;
2891    ret.disambiguate = false;
2892    for (i = 0, ilen = config.names.length; i < ilen; i += 1) {
2893        param = config.names[i];
2894        // Fixes update bug affecting plugins, without impacting
2895        // efficiency with update of large numbers of year-suffixed
2896        // items.
2897        ret.names[i] = param;
2898    }
2899    for (i  = 0, ilen = config.givens.length; i < ilen; i += 1) {
2900        param = [];
2901        for (j = 0, jlen = config.givens[i].length; j < jlen; j += 1) {
2902            // condition at line 312 of disambiguate.js protects against negative
2903            // values of j
2904            param.push(config.givens[i][j]);
2905        }
2906        ret.givens.push(param);
2907    }
2908    // XXXX Is this necessary at all?
2909    if (oldconfig) {
2910        ret.year_suffix = oldconfig.year_suffix;
2911        ret.disambiguate = oldconfig.disambiguate;
2912    } else {
2913        ret.year_suffix = config.year_suffix;
2914        ret.disambiguate = config.disambiguate;
2915    }
2916    return ret;
2917};
2918
2919/**
2920 * Return current base configuration for disambiguation
2921 */
2922CSL.getAmbigConfig = function () {
2923    var config, ret;
2924    config = this.tmp.disambig_request;
2925    if (!config) {
2926        config = this.tmp.disambig_settings;
2927    }
2928    var ret = CSL.cloneAmbigConfig(config);
2929    return ret;
2930};
2931
2932/**
2933 * Return max values for disambiguation
2934 */
2935CSL.getMaxVals = function () {
2936    return this.tmp.names_max.mystack.slice();
2937};
2938
2939/**
2940 * Return min value for disambiguation
2941 */
2942CSL.getMinVal = function () {
2943    return this.tmp["et-al-min"];
2944};
2945
2946/*global CSL: true */
2947
2948/* For node execution pretty-printing (see below) */
2949
2950/*
2951var INDENT = "";
2952*/
2953
2954CSL.tokenExec = function (token, Item, item) {
2955    // Called on state object
2956    var next, maybenext, exec, debug;
2957    debug = false;
2958    next = token.next;
2959    maybenext = false;
2960
2961    /* Pretty-print node executions */
2962
2963    /*
2964    if (["if", "else-if", "else"].indexOf(token.name) === -1) {
2965        if (token.tokentype == 1) {
2966            INDENT = INDENT.slice(0, -2);
2967        }
2968    }
2969    this.sys.print(INDENT + "---> Token: " + token.name + " (" + token.tokentype + ") in " + this.tmp.area + ", " + this.output.current.mystack.length);
2970    if (["if", "else-if", "else"].indexOf(token.name) === -1) {
2971        if (token.tokentype == 0) {
2972            INDENT += "  ";
2973        }
2974    }
2975    */
2976
2977    var record = function (result) {
2978        if (result) {
2979            this.tmp.jump.replace("succeed");
2980            return token.succeed;
2981        } else {
2982            this.tmp.jump.replace("fail");
2983            return token.fail;
2984        }
2985    };
2986    if (token.test) {
2987        next = record.call(this,token.test(Item, item));
2988    }
2989    for (var i=0,ilen=token.execs.length;i<ilen;i++) {
2990        exec = token.execs[i];
2991        maybenext = exec.call(token, this, Item, item);
2992        if (maybenext) {
2993            next = maybenext;
2994        }
2995    }
2996    //SNIP-START
2997    if (debug) {
2998        CSL.debug(token.name + " (" + token.tokentype + ") ---> done");
2999    }
3000    //SNIP-END
3001    return next;
3002};
3003
3004/**
3005 * Macro expander.
3006 * <p>Called on the state object.</p>
3007 */
3008CSL.expandMacro = function (macro_key_token, target) {
3009    var mkey, macro_nodes, end_of_macro, func;
3010
3011    mkey = macro_key_token.postponed_macro;
3012
3013    var sort_direction = macro_key_token.strings.sort_direction;
3014    
3015    // Decorations and affixes are in wrapper applied in cs:text
3016    macro_key_token = new CSL.Token("group", CSL.START);
3017    
3018    var hasDate = false;
3019    var macroid = false;
3020    macro_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, 'macro', mkey);
3021    if (macro_nodes.length) {
3022        macroid = this.cslXml.getAttributeValue(macro_nodes[0],'cslid');
3023        hasDate = this.cslXml.getAttributeValue(macro_nodes[0], "macro-has-date");
3024    }
3025    if (hasDate) {
3026        mkey = mkey + "@" + this.build.current_default_locale;
3027        func = function (state) {
3028            if (state.tmp.extension) {
3029                state.tmp["doing-macro-with-date"] = true;
3030            }
3031        };
3032        macro_key_token.execs.push(func);
3033    }
3034
3035    if (this.build.macro_stack.indexOf(mkey) > -1) {
3036        CSL.error("CSL processor error: call to macro \"" + mkey + "\" would cause an infinite loop");
3037    } else {
3038        this.build.macro_stack.push(mkey);
3039    }
3040
3041    macro_key_token.cslid = macroid;
3042
3043    if (CSL.MODULE_MACROS[mkey]) {
3044        macro_key_token.juris = mkey;
3045        this.opt.update_mode = CSL.POSITION;
3046    }
3047    // Macro group is treated as a real node in the style
3048    CSL.Node.group.build.call(macro_key_token, this, target, true);
3049
3050    // Node does not exist in the CSL
3051    if (!this.cslXml.getNodeValue(macro_nodes)) {
3052        CSL.error("CSL style error: undefined macro \"" + mkey + "\"");
3053    }
3054
3055    // Let's macro
3056    var mytarget = CSL.getMacroTarget.call(this, mkey);
3057    if (mytarget) {
3058        CSL.buildMacro.call(this, mytarget, macro_nodes);
3059        CSL.configureMacro.call(this, mytarget);
3060    }
3061    if (!this.build.extension) {
3062        var func = (function(macro_name) {
3063            return function (state, Item, item) {
3064                var next = 0;
3065                while (next < state.macros[macro_name].length) {
3066                    next = CSL.tokenExec.call(state, state.macros[macro_name][next], Item, item);
3067                }
3068            };
3069        }(mkey));
3070        var text_node = new CSL.Token("text", CSL.SINGLETON);
3071        text_node.execs.push(func);
3072        target.push(text_node);
3073    }
3074
3075    // Decorations and affixes are in wrapper applied in cs:text
3076    end_of_macro = new CSL.Token("group", CSL.END);
3077    end_of_macro.strings.sort_direction = sort_direction;
3078    
3079    if (hasDate) {
3080        func = function (state) {
3081            if (state.tmp.extension) {
3082                state.tmp["doing-macro-with-date"] = false;
3083            }
3084        };
3085        end_of_macro.execs.push(func);
3086    }
3087    if (macro_key_token.juris) {
3088        end_of_macro.juris = mkey;
3089     }
3090    // Macro group is treated as a real node in the style
3091    CSL.Node.group.build.call(end_of_macro, this, target, true);
3092
3093    this.build.macro_stack.pop();
3094};
3095
3096CSL.getMacroTarget = function (mkey) {
3097    var mytarget = false;
3098    if (this.build.extension) {
3099        mytarget = this[this.build.root + this.build.extension].tokens;
3100    } else if (!this.macros[mkey]) {
3101        mytarget = [];
3102        this.macros[mkey] = mytarget;
3103    }
3104    return mytarget;
3105};
3106
3107CSL.buildMacro = function (mytarget, macro_nodes) {
3108    var builder = CSL.makeBuilder(this, mytarget);
3109    var mynode;
3110    if ("undefined" === typeof macro_nodes.length) {
3111        mynode = macro_nodes;
3112    } else {
3113        mynode = macro_nodes[0];
3114    }
3115    builder(mynode);
3116};
3117
3118CSL.configureMacro = function (mytarget) {
3119    if (!this.build.extension) {
3120        this.configureTokenList(mytarget);
3121    }
3122};
3123
3124
3125/**
3126 * Convert XML node to token.
3127 * <p>This is called on an XML node.  After extracting the name and attribute
3128 * information from the node, it performs three operations.  Attribute information
3129 * relating to output formatting is stored on the node as an array of tuples,
3130 * which fixes the sequence of execution of output functions to be invoked
3131 * in the next phase of processing.  Other attribute information is reduced
3132 * to functions, and is pushed into an array on the token in no particular
3133 * order, for later execution.  The element name is used as a key to
3134 * invoke the relevant <code>build</code> method of the target element.
3135 * Element methods are defined in {@link CSL.Node}.</p>
3136 * @param {Object} state  The state object returned by {@link CSL.Engine}.
3137 * @param {Int} tokentype  A CSL namespace constant (<code>CSL.START</code>,
3138 * <code>CSL.END</code> or <code>CSL.SINGLETON</code>.
3139 */
3140CSL.XmlToToken = function (state, tokentype, explicitTarget, var_stack) {
3141    var name, txt, attrfuncs, attributes, decorations, token, key, target;
3142    name = state.cslXml.nodename(this);
3143    //CSL.debug(tokentype + " : " + name);
3144    if (state.build.skip && state.build.skip !== name) {
3145        return;
3146    }
3147    if (!name) {
3148        txt = state.cslXml.content(this);
3149        if (txt) {
3150            state.build.text = txt;
3151        }
3152        return;
3153    }
3154    if (!CSL.Node[state.cslXml.nodename(this)]) {
3155        CSL.error("Undefined node name \"" + name + "\".");
3156    }
3157    attrfuncs = [];
3158    attributes = state.cslXml.attributes(this);
3159    decorations = CSL.setDecorations.call(this, state, attributes);
3160    token = new CSL.Token(name, tokentype);
3161    if (tokentype !== CSL.END || name === "if" || name === "else-if" || name === "layout") {
3162        //
3163        // xml: more xml stuff
3164        //
3165        for (var key in attributes) {
3166            if (attributes.hasOwnProperty(key)) {
3167                if (tokentype === CSL.END && key !== "@language" && key !== "@locale") {
3168                    continue;
3169                }
3170                if (attributes.hasOwnProperty(key)) {
3171                    if (CSL.Attributes[key]) {
3172                        try {
3173                            CSL.Attributes[key].call(token, state, "" + attributes[key]);
3174                        } catch (e) {
3175                            CSL.error(key + " attribute: " + e);
3176                        }
3177                    } else {
3178                        CSL.debug("warning: undefined attribute \""+key+"\" in style");
3179                    }
3180                }
3181            }
3182        }
3183        token.decorations = decorations;
3184        if (CSL.DATE_VARIABLES.indexOf(attributes['@variable']) > -1) {
3185            var_stack.push(token.variables);
3186        }
3187    } else if (tokentype === CSL.END && attributes['@variable']) {
3188        token.hasVariable = true;
3189        if (CSL.DATE_VARIABLES.indexOf(attributes['@variable']) > -1) {
3190            token.variables = var_stack.pop();
3191        }
3192    }
3193    //
3194    // !!!!!: eliminate diversion of tokens to separate
3195    // token list (formerly used for reading in macros
3196    // and terms).
3197    //
3198    if (explicitTarget) {
3199        target = explicitTarget;
3200    } else {
3201        target = state[state.build.area].tokens;
3202    }
3203    // True flags real nodes in the style
3204    CSL.Node[name].build.call(token, state, target, true);
3205};
3206
3207
3208
3209/*global CSL: true */
3210
3211
3212CSL.DateParser = function () {
3213
3214    /*
3215     * Fixed values
3216     */
3217
3218    // jse imperial years
3219    var epochPairs = [
3220        ["\u660E\u6CBB", 1867],
3221        ["\u5927\u6B63", 1911],
3222        ["\u662D\u548C", 1925],
3223        ["\u5E73\u6210", 1988]
3224    ];
3225
3226    // years by jse imperial epoch
3227    var epochYearByName = {};
3228    for (var i=0,ilen=epochPairs.length; i<ilen; i++) {
3229        var key = epochPairs[i][0];
3230        var val = epochPairs[i][1];
3231        epochYearByName[key] = val;
3232    }
3233    
3234    var epochMatchStrings = [];
3235    var epochMap = {};
3236    for (var i=0,ilen=epochPairs.length; i<ilen; i++) {
3237        var pair = epochPairs[i];
3238        var val = pair[0];
3239        epochMatchStrings.push(val);
3240        epochMap[pair[0]] = pair[1];
3241    }
3242    var epochMatchString = epochMatchStrings.join("|");
3243
3244    // regular expression to trap year name and year
3245    // (splitter and matcher, to cope with ancient JS implementations)
3246    var epochSplitter = new RegExp("(?:" + epochMatchString + ")(?:[0-9]+)");
3247    var epochMatcher = new RegExp("(?:" + epochMatchString + ")(?:[0-9]+)", "g");
3248
3249    // regular expression for month or day kanji label
3250    var kanjiMonthDay = /(\u6708|\u5E74)/g;
3251
3252    // regular expression for year kanji label
3253    var kanjiYear = /\u65E5/g;
3254
3255    // regular expression for double-width Japanese range marker
3256    var kanjiRange = /\u301c/g;
3257
3258    // parsing regexps for normalized strings
3259    //   raw materials
3260    var yearLast = "(?:[?0-9]{1,2}%%NUMD%%){0,2}[?0-9]{4}(?![0-9])";
3261    var yearFirst = "[?0-9]{4}(?:%%NUMD%%[?0-9]{1,2}){0,2}(?![0-9])";
3262    var numberVal = "[?0-9]{1,3}";
3263    var rangeSeparator = "[%%DATED%%]";
3264    var fuzzyChar = "[?~]";
3265    var chars = "[^\-\/\~\?0-9]+";
3266    var rexString = "(" + yearFirst + "|" + yearLast + "|" + numberVal + "|" + rangeSeparator + "|" + fuzzyChar + "|" + chars + ")";
3267    //   composed regexps
3268    var rexDash = new RegExp(rexString.replace(/%%NUMD%%/g, "-").replace(/%%DATED%%/g, "-"));
3269    var rexDashSlash = new RegExp(rexString.replace(/%%NUMD%%/g, "-").replace(/%%DATED%%/g, "\/"));
3270    var rexSlashDash = new RegExp(rexString.replace(/%%NUMD%%/g, "\/").replace(/%%DATED%%/g, "-"));
3271
3272    /*
3273     * Mutable values
3274     */
3275
3276    // months
3277    var monthString = "january february march april may june july august september october november december spring summer fall winter spring summer";
3278    this.monthStrings = monthString.split(" ");
3279
3280    /*
3281     * Configuration functions
3282     */
3283
3284    this.setOrderDayMonth = function() {
3285        // preferred ordering for numeric dates
3286        this.monthGuess = 1;
3287        this.dayGuess = 0;
3288    };
3289
3290    this.setOrderMonthDay = function() {
3291        // preferred ordering for numeric dates
3292        this.monthGuess = 0;
3293        this.dayGuess = 1;
3294    };
3295
3296    this.resetDateParserMonths = function() {
3297        // Function to reset months to default.
3298        this.monthSets = [];
3299        for (var i=0,ilen=this.monthStrings.length; i<ilen; i++) {
3300            this.monthSets.push([this.monthStrings[i]]);
3301        }
3302        this.monthAbbrevs = [];
3303        for (var i=0,ilen=this.monthSets.length; i<ilen; i++) {
3304            this.monthAbbrevs.push([]);
3305            for (var j=0,jlen=this.monthSets[i].length; j<jlen; j++) {
3306                this.monthAbbrevs[i].push(this.monthSets[i][0].slice(0, 3));
3307            }
3308        }
3309        this.monthRexes = [];
3310        for (var i=0,ilen=this.monthAbbrevs.length; i<ilen; i++) {
3311            this.monthRexes.push(new RegExp("(?:" + this.monthAbbrevs[i].join("|") + ")"));
3312        }
3313    };
3314
3315    this.addDateParserMonths = function(lst) {
3316        // Extend list of months with an additional set of month abbreviations,
3317        // extending strings as required to resolve ambiguities.
3318
3319        // Normalize string to list
3320        if ("string" === typeof lst) {
3321            lst = lst.split(/\s+/);
3322        }
3323
3324        // Check that there are twelve (or sixteen) to add
3325        if (lst.length !== 12 && lst.length !== 16) {
3326            CSL.debug("month [+season] list of "+lst.length+", expected 12 or 16. Ignoring.");
3327            return;
3328        }
3329
3330        // Extend as necessary to resolve ambiguities
3331        // For each new month string ...
3332        for (var i=0,ilen=lst.length; i<ilen; i++) {
3333            var abbrevLength = null;
3334            var skip = false;
3335            var insert = 3;
3336            var extendedSets = {};
3337            for (var j=0,jlen=this.monthAbbrevs.length; j<jlen; j++) {
3338                extendedSets[j] = {};
3339                if (j === i) {
3340                    // Mark for skipping if same as an existing abbreviation of same month
3341                    for (var k=0,klen=this.monthAbbrevs[i].length; k<klen; k++) {
3342                        if (this.monthAbbrevs[i][k] === lst[i].slice(0, this.monthAbbrevs[i][k].length)) {
3343                            skip = true;
3344                            break;
3345                        }
3346                    }
3347                } else {
3348                    // Mark for extending if same as existing abbreviation of any expression of another month
3349                    for (var k=0,klen=this.monthAbbrevs[j].length; k<klen; k++) {
3350                        abbrevLength = this.monthAbbrevs[j][k].length;
3351                        if (this.monthAbbrevs[j][k] === lst[i].slice(0, abbrevLength)) {
3352                            while (this.monthSets[j][k].slice(0, abbrevLength) === lst[i].slice(0, abbrevLength)) {
3353                                // Abort when full length is hit, otherwise extend
3354                                if (abbrevLength > lst[i].length || abbrevLength > this.monthSets[j][k].length) {
3355                                    CSL.debug("unable to disambiguate month string in date parser: "+lst[i]);
3356                                    break;
3357                                } else {
3358                                    // Mark both new entry and existing abbrev for extension
3359                                    abbrevLength += 1;
3360                                }
3361                            }
3362                            insert = abbrevLength;
3363                            extendedSets[j][k] = abbrevLength;
3364                        }
3365                    }
3366                }
3367                for (var jKey in extendedSets) {
3368                    for (var kKey in extendedSets[jKey]) {
3369                        abbrevLength = extendedSets[jKey][kKey];
3370                        jKey = parseInt(jKey, 10);
3371                        kKey = parseInt(kKey, 10);
3372                        this.monthAbbrevs[jKey][kKey] = this.monthSets[jKey][kKey].slice(0, abbrevLength);
3373                    }
3374                }
3375            }
3376            // Insert here
3377            if (!skip) {
3378                this.monthSets[i].push(lst[i]);
3379                this.monthAbbrevs[i].push(lst[i].slice(0, insert));
3380            }
3381        }
3382
3383        // Compose
3384        this.monthRexes = [];
3385        this.monthRexStrs = [];
3386        for (var i=0,ilen=this.monthAbbrevs.length; i<ilen; i++) {
3387            this.monthRexes.push(new RegExp("^(?:" + this.monthAbbrevs[i].join("|") + ")"));
3388            this.monthRexStrs.push("^(?:" + this.monthAbbrevs[i].join("|") + ")");
3389        }
3390        if (this.monthAbbrevs.length === 18) {
3391            for (var i=12,ilen=14; i<ilen; i++) {
3392                this.monthRexes[i+4] = new RegExp("^(?:" + this.monthAbbrevs[i].join("|") + ")");
3393                this.monthRexStrs[i+4] = "^(?:" + this.monthAbbrevs[i].join("|") + ")";
3394            }
3395        }
3396    };
3397
3398    /*
3399     * Conversion functions
3400     */
3401
3402    this.convertDateObjectToArray = function (thedate) {
3403        // Converts object in place and returns object
3404        thedate["date-parts"] = [];
3405        thedate["date-parts"].push([]);
3406        var slicelen = 0;
3407        var part;
3408        for (var i=0,ilen=3; i<ilen; i++) {
3409            part = ["year", "month", "day"][i];
3410            if (!thedate[part]) {
3411                break;
3412            }
3413            slicelen += 1;
3414            thedate["date-parts"][0].push(thedate[part]);
3415            delete thedate[part];
3416        }
3417        thedate["date-parts"].push([]);
3418        for (var i=0, ilen=slicelen; i<ilen; i++) {
3419            part = ["year_end", "month_end", "day_end"][i];
3420            if (!thedate[part]) {
3421                break;
3422            }
3423            thedate["date-parts"][1].push(thedate[part]);
3424            delete thedate[part];
3425        }
3426        if (thedate["date-parts"][0].length !== thedate["date-parts"][1].length) {
3427            thedate["date-parts"].pop();
3428        }
3429        return thedate;
3430    };
3431
3432    // XXXX String output is currently unable to represent ranges
3433    this.convertDateObjectToString = function(thedate) {
3434        // Returns string
3435        var ret = [];
3436        for (var i = 0, ilen = 3; i < ilen; i += 1) {
3437            if (thedate[CSL.DATE_PARTS_ALL[i]]) {
3438                ret.push(thedate[CSL.DATE_PARTS_ALL[i]]);
3439            } else {
3440                break;
3441            }
3442        }
3443        return ret.join("-");
3444    };
3445
3446    /*
3447     * Utility function
3448     */
3449
3450    this._parseNumericDate = function (ret, delim, suff, txt) {
3451        if (!suff) {
3452            suff = "";
3453        }
3454        var lst = txt.split(delim);
3455        
3456        for (var i=0, ilen=lst.length; i<ilen; i++) {
3457            if (lst[i].length === 4) {
3458                ret[("year" + suff)] = lst[i].replace(/^0*/, "");
3459                if (!i) {
3460                    lst = lst.slice(1);
3461                } else {
3462                    lst = lst.slice(0, i);
3463                }
3464                break;
3465            }
3466        }
3467        for (var i=0,ilen=lst.length; i<ilen; i++) {
3468            lst[i] = parseInt(lst[i], 10);
3469        }
3470        if (lst.length === 1 || (lst.length === 2 && !lst[1])) {
3471            var month = lst[0];
3472            if (month) {
3473                ret[("month" + suff)] = "" + lst[0];
3474            }
3475        } else if (lst.length === 2) {
3476            if (lst[this.monthGuess] > 12) {
3477                var month = lst[this.dayGuess];
3478                var day = lst[this.monthGuess];
3479                if (month) {
3480                    ret[("month" + suff)] = "" + month;
3481                    if (day) {
3482                        ret[("day" + suff)] = "" + day;
3483                    }
3484                }
3485            } else {
3486                var month = lst[this.monthGuess];
3487                var day = lst[this.dayGuess];
3488                if (month) {
3489                    ret[("month" + suff)] = "" + month;
3490                    if (day) {
3491                        ret[("day" + suff)] = "" + day;
3492                    }
3493                }
3494            }
3495        }
3496    };
3497
3498    /*
3499     * Parsing functions
3500     */
3501
3502    this.parseDateToObject = function (txt) {
3503        //
3504        // Normalize the format and the year if it's a Japanese date
3505        //
3506        var orig = txt;
3507        var slashPos = -1;
3508        var dashPos = -1;
3509        var yearIsNegative = false;
3510        var lst;
3511        if (txt) {
3512            txt = txt.replace(/^(.*[0-9])T[0-9].*/, "$1");
3513            // If string leads with a minus sign, strip and memo it.
3514            if (txt.slice(0, 1) === "-") {
3515                yearIsNegative = true;
3516                txt = txt.slice(1);
3517            }
3518            
3519            // If string is a number of 1 to 3 characters only, treat as year.
3520            if (txt.match(/^[0-9]{1,3}$/)) {
3521                while (txt.length < 4) {
3522                    txt = "0" + txt;
3523                }
3524            }
3525            
3526            // Normalize to string
3527            txt = "" + txt;
3528            // Remove things that look like times
3529            txt = txt.replace(/\s*[0-9]{2}:[0-9]{2}(?::[0-9]+)/,"");
3530            var m = txt.match(kanjiMonthDay);
3531            if (m) {
3532                txt = txt.replace(/\s+/g, "");
3533                txt = txt.replace(kanjiYear, "");
3534                txt = txt.replace(kanjiMonthDay, "-");
3535                txt = txt.replace(kanjiRange, "/");
3536                txt = txt.replace(/\-\//g, "/");
3537                txt = txt.replace(/-$/g,"");
3538
3539                // Tortuous workaround for IE6
3540                var slst = txt.split(epochSplitter);
3541                lst = [];
3542                var mm = txt.match(epochMatcher);
3543                if (mm) {
3544                    var mmx = [];
3545                    for (var i=0,ilen=mm.length; i<ilen; i++) {
3546                        mmx = mmx.concat(mm[i].match(/([^0-9]+)([0-9]+)/).slice(1));
3547                    }
3548                    for (var i=0,ilen=slst.length; i<ilen; i++) {
3549                        lst.push(slst[i]);
3550                        if (i !== (ilen - 1)) {
3551                            // pos is undeclared, and multiplying by 2 here is insane.
3552                            var mmpos = (i * 2);
3553                            lst.push(mmx[mmpos]);
3554                            lst.push(mmx[mmpos + 1]);
3555                        }
3556                    }
3557                } else {
3558                    lst = slst;
3559                }
3560                // workaround duly applied, this now works
3561                for (var i=1,ilen=lst.length; i<ilen; i+=3) {
3562                    lst[i + 1] = epochMap[lst[i]] + parseInt(lst[i + 1], 10);
3563                    lst[i] = "";
3564                }
3565                txt = lst.join("");
3566                txt = txt.replace(/\s*-\s*$/, "").replace(/\s*-\s*\//, "/");
3567                //
3568                // normalize date and identify delimiters
3569                //
3570                txt = txt.replace(/\.\s*$/, "");
3571
3572                // not sure what this is meant to do
3573                txt = txt.replace(/\.(?! )/, "");
3574
3575                slashPos = txt.indexOf("/");
3576                dashPos = txt.indexOf("-");
3577            }
3578        }
3579        // drop punctuation from a.d., b.c.
3580        txt = txt.replace(/([A-Za-z])\./g, "$1");
3581
3582        var number = "";
3583        var note = "";
3584        var thedate = {};
3585        var rangeDelim;
3586        var dateDelim;
3587        if (txt.slice(0, 1) === "\"" && txt.slice(-1) === "\"") {
3588            thedate.literal = txt.slice(1, -1);
3589            return thedate;
3590        }
3591        if (slashPos > -1 && dashPos > -1) {
3592            var slashCount = txt.split("/");
3593            if (slashCount.length > 3) {
3594                rangeDelim = "-";
3595                txt = txt.replace(/\_/g, "-");
3596                dateDelim = "/";
3597                lst = txt.split(rexSlashDash);
3598            } else {
3599                rangeDelim = "/";
3600                txt = txt.replace(/\_/g, "/");
3601                dateDelim = "-";
3602                lst = txt.split(rexDashSlash);
3603            }
3604        } else {
3605            txt = txt.replace(/\//g, "-");
3606            txt = txt.replace(/\_/g, "-");
3607            rangeDelim = "-";
3608            dateDelim = "-";
3609            lst = txt.split(rexDash);
3610        }
3611        var ret = [];
3612        for (var i=0,ilen=lst.length; i<ilen; i++) {
3613            var m = lst[i].match(/^\s*([\-\/]|[^\-\/\~\?0-9]+|[\-~?0-9]+)\s*$/);
3614            if (m) {
3615                ret.push(m[1]);
3616            }
3617        }
3618        //
3619        // Phase 2
3620        //
3621        var delimPos = ret.indexOf(rangeDelim);
3622        var delims = [];
3623        var isRange = false;
3624        if (delimPos > -1) {
3625            delims.push([0, delimPos]);
3626            delims.push([(delimPos + 1), ret.length]);
3627            isRange = true;
3628        } else {
3629            delims.push([0, ret.length]);
3630        }
3631        //
3632        // For each side of a range divide ...
3633        //
3634        var suff = "";
3635        
3636        for (var i=0,ilen=delims.length; i<ilen; i++) {
3637            var delim = delims[i];
3638            //
3639            // Process each element ...
3640            //
3641            var date = ret.slice(delim[0], delim[1]);
3642            outer: 
3643            for (var j=0,jlen=date.length; j<jlen; j++) {
3644                var element = date[j];
3645                //
3646                // If it's a numeric date, process it.
3647                //
3648                if (element.indexOf(dateDelim) > -1) {
3649                    this._parseNumericDate(thedate, dateDelim, suff, element);
3650                    continue;
3651                }
3652                //
3653                // If it's an obvious year, record it.
3654                //
3655                if (element.match(/[0-9]{4}/)) {
3656                    thedate[("year" + suff)] = element.replace(/^0*/, "");
3657                    continue;
3658                }
3659                //
3660                // If it's a fuzzy marker, record it.
3661                //
3662                if (element === "~" || element === "?" || element === "c" || element.match(/^cir/)) {
3663                    thedate.circa = true;
3664                }
3665                //
3666                // If it's a month, record it.
3667                //
3668                for (var k=0,klen=this.monthRexes.length; k<klen; k++) {
3669                    if (element.toLocaleLowerCase().match(this.monthRexes[k])) {
3670                        thedate[("month" + suff)] = "" + (parseInt(k, 10) + 1);
3671                        continue outer;
3672                    }
3673                }
3674                //
3675                // If it's a number, make a note of it
3676                //
3677                if (element.match(/^[0-9]+$/)) {
3678                    number = element;
3679                }
3680                //
3681                // If it's a BC or AD marker, make a year of
3682                // any note.  Separate, reverse the sign of the year
3683                // if it's BC.
3684                //
3685                if (element.toLocaleLowerCase().match(/^bc/) && number) {
3686                    thedate[("year" + suff)] = "" + (number * -1);
3687                    number = "";
3688                    continue;
3689                }
3690                if (element.toLocaleLowerCase().match(/^ad/) && number) {
3691                    thedate[("year" + suff)] = "" + number;
3692                    number = "";
3693                    continue;
3694                }
3695                //
3696                // If it's cruft, make a note of it
3697                //
3698                if (element.toLocaleLowerCase().match(/(?:mic|tri|hil|eas)/) && !thedate[("season" + suff)]) {
3699                    note = element;
3700                    continue;
3701                }
3702            }
3703            //
3704            // If at the end of the string there's still a note
3705            // hanging around, make a day of it.
3706            //
3707            if (number) {
3708                thedate[("day" + suff)] = number;
3709                number = "";
3710            }
3711            //
3712            // If at the end of the string there's cruft lying
3713            // around, and the season field is empty, put the
3714            // cruft there.
3715            //
3716            if (note && !thedate[("season" + suff)]) {
3717                thedate[("season" + suff)] = note.trim();
3718                note = "";
3719            }
3720            suff = "_end";
3721        }
3722        //
3723        // update any missing elements on each side of the divide
3724        // from the other
3725        //
3726        if (isRange) {
3727            for (var j=0,jlen=CSL.DATE_PARTS_ALL.length; j<jlen; j++) {
3728                var item = CSL.DATE_PARTS_ALL[j];
3729                if (thedate[item] && !thedate[(item + "_end")]) {
3730                    thedate[(item + "_end")] = thedate[item];
3731                } else if (!thedate[item] && thedate[(item + "_end")]) {
3732                    thedate[item] = thedate[(item + "_end")];
3733                }
3734            }
3735        }
3736        //
3737        // If there's no year, or if there only a year and a day, it's a failure; pass through the literal
3738        //
3739        if (!thedate.year || (thedate.year && thedate.day && !thedate.month)) {
3740            thedate = { "literal": orig };
3741        }
3742        var parts = ["year", "month", "day", "year_end", "month_end", "day_end"];
3743        for (var i=0,ilen=parts.length; i<ilen; i++) {
3744            var part = parts[i];
3745            if ("string" === typeof thedate[part] && thedate[part].match(/^[0-9]+$/)) {
3746                thedate[part] = parseInt(thedate[part], 10);
3747            }
3748            
3749        }
vendor: 103,346 bytes, lines 3750-6572
3750        if (yearIsNegative && Object.keys(thedate).indexOf("year") > -1) {
3751            thedate.year = (thedate.year * -1);
3752        }
3753        return thedate;
3754    };
3755
3756    this.parseDateToArray = function(txt) {
3757        return this.convertDateObjectToArray(this.parseDateToObject(txt));            
3758    };
3759
3760    this.parseDateToString = function(txt) {
3761        return this.convertDateObjectToString(this.parseDateToObject(txt));
3762    };
3763    
3764    this.parse = function(txt) {
3765        return this.parseDateToObject(txt);
3766    };
3767    
3768    /*
3769
3770     * Setup
3771     */
3772
3773    this.setOrderMonthDay();
3774    this.resetDateParserMonths();
3775};
3776CSL.DateParser = new CSL.DateParser();
3777
3778/*global CSL: true */
3779
3780CSL.Engine = function (sys, style, lang, forceLang) {
3781    var attrs, langspec;
3782    this.processor_version = CSL.PROCESSOR_VERSION;
3783    this.csl_version = "1.0";
3784    this.sys = sys;
3785    
3786    if (typeof Object.assign != 'function') {
3787        // Must be writable: true, enumerable: false, configurable: true
3788        Object.defineProperty(Object, "assign", {
3789            value: function assign(target) { // .length of function is 2
3790                'use strict';
3791                if (target == null) { // TypeError if undefined or null
3792                    throw new TypeError('Cannot convert undefined or null to object');
3793                }
3794
3795                var to = Object(target);
3796
3797                for (var index = 1; index < arguments.length; index++) {
3798                    var nextSource = arguments[index];
3799
3800                    if (nextSource != null) { // Skip over if undefined or null
3801                        for (var nextKey in nextSource) {
3802                            // Avoid bugs when hasOwnProperty is shadowed
3803                            if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
3804                                to[nextKey] = nextSource[nextKey];
3805                            }
3806                        }
3807                    }
3808                }
3809                return to;
3810            },
3811            writable: true,
3812            configurable: true
3813        });
3814    }
3815
3816    // XXX This may be excess code. Given the normalization performed on
3817    // XXX the output queue before variableWrapper() is run, a single
3818    // XXX space should be the most cruft that we ever see before a variable.
3819    if (sys.variableWrapper) {
3820        CSL.VARIABLE_WRAPPER_PREPUNCT_REX = new RegExp('^([' + [" "].concat(CSL.SWAPPING_PUNCTUATION).join("") + ']*)(.*)');
3821    }
3822    // XXXX This should be restored -- temporarily suspended for testing of JSON style support.
3823    if (CSL.retrieveStyleModule) {
3824        this.sys.retrieveStyleModule = CSL.retrieveStyleModule;
3825    }
3826    if (CSL.getAbbreviation) {
3827        this.sys.getAbbreviation = CSL.getAbbreviation;
3828    }
3829    if (this.sys.stringCompare) {
3830        CSL.stringCompare = this.sys.stringCompare;
3831    }
3832    this.sys.AbbreviationSegments = CSL.AbbreviationSegments;
3833
3834    this.transform = new CSL.Transform(this);
3835    // true or false
3836    this.setParseNames = function (val) {
3837        this.opt['parse-names'] = val;
3838    };
3839    
3840    this.opt = new CSL.Engine.Opt();
3841    this.tmp = new CSL.Engine.Tmp();
3842    this.build = new CSL.Engine.Build();
3843    this.fun = new CSL.Engine.Fun(this);
3844
3845    this.configure = new CSL.Engine.Configure();
3846    // Build citation before citation_sort in order to pick up
3847    // state.opt.update_mode, needed it determine whether
3848    // a grouped sort should be performed.
3849    this.citation_sort = new CSL.Engine.CitationSort();
3850    this.bibliography_sort = new CSL.Engine.BibliographySort();
3851    this.citation = new CSL.Engine.Citation(this);
3852    this.bibliography = new CSL.Engine.Bibliography();
3853    this.intext = new CSL.Engine.InText();
3854
3855    this.output = new CSL.Output.Queue(this);
3856
3857    //this.render = new CSL.Render(this);
3858    //
3859    // This latter queue is used for formatting date chunks
3860    // before they are folded back into the main queue.
3861    //
3862    this.dateput = new CSL.Output.Queue(this);
3863
3864    this.cslXml = CSL.setupXml(style);
3865
3866    for (var i in CSL.SYS_OPTIONS) {
3867        var option = CSL.SYS_OPTIONS[i];
3868        if ("boolean" === typeof this.sys[option]) {
3869            this.opt.development_extensions[option] = this.sys[option];
3870        }
3871        
3872    }
3873    if (this.opt.development_extensions.uppercase_subtitles || this.opt.development_extensions.implicit_short_title) {
3874        this.opt.development_extensions.main_title_from_short_title = true;
3875    }
3876    if (this.opt.development_extensions.csl_reverse_lookup_support) {
3877        this.build.cslNodeId = 0;
3878        this.setCslNodeIds = function(myxml, nodename) {
3879            var children = this.cslXml.children(myxml);
3880            this.cslXml.setAttribute(myxml, 'cslid', this.build.cslNodeId);
3881            this.opt.nodenames.push(nodename);
3882            this.build.cslNodeId += 1;
3883            for (var i = 0, ilen = this.cslXml.numberofnodes(children); i < ilen; i += 1) {
3884                nodename = this.cslXml.nodename(children[i]);
3885                if (nodename) {
3886                    this.setCslNodeIds(children[i], nodename);
3887                }
3888            }
3889        };
3890        this.setCslNodeIds(this.cslXml.dataObj, "style");
3891    }
3892    // Preprocessing ops for the XML input
3893    this.cslXml.addMissingNameNodes(this.cslXml.dataObj);
3894    this.cslXml.addInstitutionNodes(this.cslXml.dataObj);
3895    this.cslXml.insertPublisherAndPlace(this.cslXml.dataObj);
3896    this.cslXml.flagDateMacros(this.cslXml.dataObj);
3897    attrs = this.cslXml.attributes(this.cslXml.dataObj);
3898    if ("undefined" === typeof attrs["@sort-separator"]) {
3899        this.cslXml.setAttribute(this.cslXml.dataObj, "sort-separator", ", ");
3900    }
3901    // This setting does the right thing and seems not to be side-effects
3902    this.opt["initialize-with-hyphen"] = true;
3903
3904    // Locale resolution
3905    //
3906    // (1) Get three locale strings 
3907    //     -- default-locale (stripped)
3908    //     -- processor-locale
3909    //     -- en_US
3910    
3911    this.setStyleAttributes();
3912
3913    this.opt.xclass = this.cslXml.getAttributeValue(this.cslXml.dataObj, "class");
3914    this.opt["class"] = this.opt.xclass;
3915    this.opt.styleID = this.cslXml.getStyleId(this.cslXml.dataObj);
3916    this.opt.styleName = this.cslXml.getStyleId(this.cslXml.dataObj, true);
3917
3918    if (this.opt.version.slice(0,4) === "1.1m") {
3919        this.opt.development_extensions.consolidate_legal_items = true;
3920        this.opt.development_extensions.consolidate_container_items = true;
3921        this.opt.development_extensions.main_title_from_short_title = true;
3922        this.opt.development_extensions.expect_and_symbol_form = true;
3923        this.opt.development_extensions.require_explicit_legal_case_title_short = true;
3924        this.opt.development_extensions.force_jurisdiction = true;
3925        this.opt.development_extensions.force_title_abbrev_fallback = true;
3926    }
3927    // We seem to have two language specs flying around:
3928    //   this.opt["default-locale"], and this.opt.lang
3929    // Keeping them aligned for safety's sake, pending
3930    // eventual cleanup.
3931    if (lang) {
3932        lang = lang.replace("_", "-");
3933        lang = CSL.normalizeLocaleStr(lang);
3934    }
3935    if (this.opt["default-locale"][0]) {
3936        this.opt["default-locale"][0] = this.opt["default-locale"][0].replace("_", "-");
3937        this.opt["default-locale"][0] = CSL.normalizeLocaleStr(this.opt["default-locale"][0]);
3938    }
3939    if (lang && forceLang) {
3940        this.opt["default-locale"] = [lang];
3941    }
3942    if (lang && !forceLang && this.opt["default-locale"][0]) {
3943        lang = this.opt["default-locale"][0];
3944    }
3945    if (this.opt["default-locale"].length === 0) {
3946        if (!lang) {
3947            lang = "en-US";
3948        }
3949        this.opt["default-locale"].push("en-US");
3950    }
3951    if (!lang) {
3952        lang = this.opt["default-locale"][0];
3953    }
3954    langspec = CSL.localeResolve(lang);
3955    this.opt.lang = langspec.best;
3956    this.opt["default-locale"][0] = langspec.best;
3957    this.locale = {};
3958    if (!this.opt["default-locale-sort"]) {
3959        this.opt["default-locale-sort"] = this.opt["default-locale"][0];
3960    }
3961    // Test processor against JS engine locale mess to find a field separator that works
3962    if ('dale|'.localeCompare('daleb', this.opt["default-locale-sort"]) > -1) {
3963        this.opt.sort_sep = "@";
3964    } else {
3965        this.opt.sort_sep = "|";
3966    }
3967    this.localeConfigure(langspec);
3968
3969    // Build skip-word regexp
3970    function makeRegExp(lst) {
3971        var lst = lst.slice();
3972        var ret = new RegExp( "(?:(?:[?!:]*\\s+|-|^)(?:" + lst.join("|") + ")(?=[!?:]*\\s+|-|$))", "g");
3973        return ret;
3974    }
3975    this.locale[this.opt.lang].opts["skip-words-regexp"] = makeRegExp(this.locale[this.opt.lang].opts["skip-words"]);
3976
3977    this.output.adjust = new CSL.Output.Queue.adjust(this.getOpt('punctuation-in-quote'));
3978
3979    this.registry = new CSL.Registry(this);
3980
3981    // XXX For modular jurisdiction support, parameterize buildTokenLists().
3982    // XXX Feed as arguments:
3983    // XXX * actual node to be walked (cslXml)
3984    // XXX * actual target array
3985
3986    this.macros = {};
3987
3988    this.build.area = "citation";
3989    var area_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, this.build.area);
3990    this.buildTokenLists(area_nodes, this[this.build.area].tokens);
3991
3992    this.build.area = "bibliography";
3993    var area_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, this.build.area);
3994    this.buildTokenLists(area_nodes, this[this.build.area].tokens);
3995
3996    this.build.area = "intext";
3997    var area_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, this.build.area);
3998    this.buildTokenLists(area_nodes, this[this.build.area].tokens);
3999
4000    if (this.opt.parallel.enable) {
4001        this.parallel = new CSL.Parallel(this);
4002    }
4003
4004    this.juris = {};
4005
4006    this.configureTokenLists();
4007
4008    this.disambiguate = new CSL.Disambiguation(this);
4009
4010    this.splice_delimiter = false;
4011
4012    //
4013    // date parser
4014    //
4015    this.fun.dateparser = CSL.DateParser;
4016    //
4017    // flip-flopper for inline markup
4018    //
4019    this.fun.flipflopper = new CSL.Util.FlipFlopper(this);
4020    //
4021    // utility functions for quotes
4022    //
4023    this.setCloseQuotesArray();
4024    //
4025    // configure ordinal numbers generator
4026    //
4027    this.fun.ordinalizer.init(this);
4028    //
4029    // configure long ordinal numbers generator
4030    //
4031    this.fun.long_ordinalizer.init(this);
4032    //
4033    // set up page mangler
4034    //
4035    this.fun.page_mangler = CSL.Util.PageRangeMangler.getFunction(this, "page");
4036    this.fun.year_mangler = CSL.Util.PageRangeMangler.getFunction(this, "year");
4037
4038    this.setOutputFormat("html");
4039};
4040
4041CSL.Engine.prototype.setCloseQuotesArray = function () {
4042    var ret;
4043    ret = [];
4044    ret.push(this.getTerm("close-quote"));
4045    ret.push(this.getTerm("close-inner-quote"));
4046    ret.push('"');
4047    ret.push("'");
4048    this.opt.close_quotes_array = ret;
4049};
4050
4051// Walker for preparsed XML input
4052CSL.makeBuilder = function (me, target) {
4053    var var_stack = [];
4054    var node_stack = [];
4055    function runStart (node) {
4056        node_stack.push(node);
4057        CSL.XmlToToken.call(node, me, CSL.START, target, var_stack);
4058    }
4059    function runEnd () {
4060        var node = node_stack.pop();
4061        CSL.XmlToToken.call(node, me, CSL.END, target, var_stack);
4062    }
4063    function runSingle (node) {
4064        CSL.XmlToToken.call(node, me, CSL.SINGLETON, target, var_stack);
4065    }
4066    function buildStyle (nodes, parent, node_stack) {
4067        if (!node_stack) {
4068            node_stack = [];
4069        }
4070        if (!nodes) {
4071            nodes = [];
4072        }
4073        if ("undefined" === typeof nodes.length) {
4074            nodes = [nodes];
4075        }
4076        for (var i=0; i<nodes.length; i++) {
4077            var node = nodes[i];
4078            if (me.cslXml.nodename(node) === null) {
4079                continue;
4080            }
4081            if (parent && me.cslXml.nodename(node) === "date") {
4082                CSL.Util.fixDateNode.call(me, parent, i, node);
4083                node = me.cslXml.children(parent)[i];
4084            }
4085            if (me.cslXml.numberofnodes(me.cslXml.children(node))) {
4086                runStart(node);
4087                buildStyle(me.cslXml.children(node), node, node_stack);
4088                runEnd();
4089            } else {
4090                runSingle(node);
4091            }
4092        }
4093    }
4094    return buildStyle;
4095};
4096
4097
4098CSL.Engine.prototype.buildTokenLists = function (area_nodes, target) {
4099    if (!this.cslXml.getNodeValue(area_nodes)) {
4100        return;
4101    }
4102    var builder = CSL.makeBuilder(this, target);
4103    var mynode;
4104    if ("undefined" === typeof area_nodes.length) {
4105        mynode = area_nodes;
4106    } else {
4107        mynode = area_nodes[0];
4108    }
4109    builder(mynode);
4110};
4111
4112
4113CSL.Engine.prototype.setStyleAttributes = function () {
4114    var dummy, attributes, attrname;
4115    // Protect against DOM engines that deliver a top-level document
4116    // (needed for createElement) that does not contain our top-level node.
4117    // 
4118    // The string coercion on this.cslXml.tagName addresses a bizarre
4119    // condition on the top-level node in jsdom running under node.js, in which:
4120    //   (1) typeof this.cslXml.tagName === "undefined"; and
4121    //   (2) !this.cslXml.tagName === false
4122    // Coerced, it becomes an empty string.
4123    var dummy = {};
4124    dummy.name = this.cslXml.nodename(this.cslXml.dataObj);
4125    attributes = this.cslXml.attributes(this.cslXml.dataObj);
4126    for (attrname in attributes) {
4127        if (attributes.hasOwnProperty(attrname)) {
4128            // attr = attributes[key];
4129            CSL.Attributes[attrname].call(dummy, this, attributes[attrname]);
4130        }
4131    }
4132};
4133
4134CSL.Engine.prototype.getTerm = function (term, form, plural, gender, mode, forceDefaultLocale) {
4135    if (term && term.match(/[A-Z]/) && term === term.toUpperCase()) {
4136        CSL.debug("Warning: term key is in uppercase form: "+term);
4137        term = term.toLowerCase();
4138    }
4139    var lang;
4140    if (forceDefaultLocale) {
4141        lang = this.opt["default-locale"][0];
4142    } else {
4143        lang = this.opt.lang;
4144    }
4145    var ret = CSL.Engine.getField(CSL.LOOSE, this.locale[lang].terms, term, form, plural, gender);
4146    // XXXXX Temporary, until locale term is deployed in CSL.
4147    if (!ret && term === "range-delimiter") {
4148        ret = "\u2013";
4149    }
4150    // XXXXX Not so good if mode is neither strict nor tolerant ...
4151    if (typeof ret === "undefined") {
4152        if (mode === CSL.STRICT) {
4153            CSL.error("Error in getTerm: term \"" + term + "\" does not exist.");
4154        } else if (mode === CSL.TOLERANT) {
4155            ret = "";
4156        }
4157    }
4158    if (ret) {
4159        this.tmp.cite_renders_content = true;
4160    }
4161    return ret;
4162};
4163
4164CSL.Engine.prototype.getDate = function (form, forceDefaultLocale) {
4165    var lang;
4166    if (forceDefaultLocale) {
4167        lang = this.opt["default-locale"];
4168    } else {
4169        lang = this.opt.lang;
4170    }
4171    if (this.locale[lang].dates[form]) {
4172        return this.locale[lang].dates[form];
4173    } else {
4174        return false;
4175    }
4176};
4177
4178CSL.Engine.prototype.getOpt = function (arg) {
4179    if ("undefined" !== typeof this.locale[this.opt.lang].opts[arg]) {
4180        return this.locale[this.opt.lang].opts[arg];
4181    } else {
4182        return false;
4183    }
4184};
4185
4186
4187
4188CSL.Engine.prototype.getVariable = function (Item, varname, form, plural) {
4189    return CSL.Engine.getField(CSL.LOOSE, Item, varname, form, plural);
4190};
4191
4192CSL.Engine.prototype.getDateNum = function (ItemField, partname) {
4193    if ("undefined" === typeof ItemField) {
4194        return 0;
4195    } else {
4196        return ItemField[partname];
4197    }
4198};
4199
4200CSL.Engine.getField = function (mode, hash, term, form, plural, gender) {
4201    var ret, forms, f, pos, len, hashterm;
4202    ret = "";
4203    if ("undefined" === typeof hash[term]) {
4204        if (mode === CSL.STRICT) {
4205            CSL.error("Error in getField: term \"" + term + "\" does not exist.");
4206        } else {
4207            return undefined;
4208        }
4209    }
4210    if (gender && hash[term][gender]) {
4211        hashterm = hash[term][gender];
4212    } else {
4213        hashterm = hash[term];
4214    }
4215    forms = [];
4216    if (form === "symbol") {
4217        forms = ["symbol", "short"];
4218    } else if (form === "verb-short") {
4219        forms = ["verb-short", "verb"];
4220    } else if (form !== "long") {
4221        forms = [form];
4222    }
4223    forms = forms.concat(["long"]);
4224    len = forms.length;
4225    for (pos = 0; pos < len; pos += 1) {
4226        f = forms[pos];
4227        if ("string" === typeof hashterm || "number" === typeof hashterm) {
4228            ret = hashterm;
4229        } else if ("undefined" !== typeof hashterm[f]) {
4230            if ("string" === typeof hashterm[f] || "number" === typeof hashterm[f]) {
4231                ret = hashterm[f];
4232            } else {
4233                if ("number" === typeof plural) {
4234                    ret = hashterm[f][plural];
4235                } else {
4236                    ret = hashterm[f][0];
4237                }
4238            }
4239            break;
4240        }
4241    }
4242    return ret;
4243};
4244
4245CSL.Engine.prototype.configureTokenLists = function () {
4246    var area, pos, len;
4247    //for each (var area in ["citation", "citation_sort", "bibliography","bibliography_sort"]) {
4248    len = CSL.AREAS.length;
4249    for (pos = 0; pos < len; pos += 1) {
4250        //var ret = [];
4251        area = CSL.AREAS[pos];
4252        var tokens = this[area].tokens;
4253        this.configureTokenList(tokens);
4254    }
4255    this.version = CSL.version;
4256    return this.state;
4257};
4258
4259CSL.Engine.prototype.configureTokenList = function (tokens) {
4260    var dateparts_master, token, dateparts, part, ppos, pppos, llen, lllen;
4261    dateparts_master = ["year", "month", "day"];
4262    llen = tokens.length - 1;
4263    for (ppos = llen; ppos > -1; ppos += -1) {
4264        token = tokens[ppos];
4265        //token.pos = ppos;
4266        //ret.push(token);
4267        if ("date" === token.name && CSL.END === token.tokentype) {
4268            dateparts = [];
4269        }
4270        if ("date-part" === token.name && token.strings.name) {
4271            lllen = dateparts_master.length;
4272            for (pppos = 0; pppos < lllen; pppos += 1) {
4273                part = dateparts_master[pppos];
4274                if (part === token.strings.name) {
4275                    dateparts.push(token.strings.name);
4276                }
4277            }
4278        }
4279        if ("date" === token.name && CSL.START === token.tokentype) {
4280            dateparts.reverse();
4281            token.dateparts = dateparts;
4282        }
4283        token.next = (ppos + 1);
4284        if (token.name && CSL.Node[token.name].configure) {
4285            CSL.Node[token.name].configure.call(token, this, ppos);
4286        }
4287    }
4288};
4289
4290CSL.Engine.prototype.refetchItems = function (ids) {
4291    var ret = [];
4292    for (var i = 0, ilen = ids.length; i < ilen; i += 1) {
4293        ret.push(this.refetchItem("" + ids[i]));
4294    }
4295    return ret;
4296};
4297
4298CSL.ITERATION = 0;
4299
4300// Wrapper for sys.retrieveItem supplied by calling application.
4301// Adds experimental fields embedded in the note field for
4302// style development trial and testing purposes.
4303CSL.Engine.prototype.retrieveItem = function (id) {
4304    var Item, m, i;
4305
4306    if (!this.tmp.loadedItemIDs[id]) {
4307        this.tmp.loadedItemIDs[id] = true;
4308    } else {
4309        return this.registry.refhash[id];
4310    }
4311
4312    if (this.opt.development_extensions.normalize_lang_keys_to_lowercase &&
4313        "boolean" === typeof this.opt.development_extensions.normalize_lang_keys_to_lowercase) {
4314        // This is a hack. Should properly be configured by a processor method after build.
4315        for (var i=0,ilen=this.opt["default-locale"].length; i<ilen; i+=1) {
4316            this.opt["default-locale"][i] = this.opt["default-locale"][i].toLowerCase();
4317        }
4318        for (var i=0,ilen=this.opt["locale-translit"].length; i<ilen; i+=1) {
4319            this.opt["locale-translit"][i] = this.opt["locale-translit"][i].toLowerCase();
4320        }
4321        for (var i=0,ilen=this.opt["locale-translat"].length; i<ilen; i+=1) {
4322            this.opt["locale-translat"][i] = this.opt["locale-translat"][i].toLowerCase();
4323        }
4324        this.opt.development_extensions.normalize_lang_keys_to_lowercase = 100;
4325    }
4326
4327    //Zotero.debug("XXX === ITERATION " + CSL.ITERATION + " "+ id +" ===");
4328    CSL.ITERATION += 1;
4329
4330    Item = JSON.parse(JSON.stringify(this.sys.retrieveItem("" + id)));
4331
4332    // Optionally normalize keys to lowercase()
4333    if (this.opt.development_extensions.normalize_lang_keys_to_lowercase) {
4334        if (Item.multi) {
4335            if (Item.multi._keys) {
4336                for (var field in Item.multi._keys) {
4337                    for (var key in Item.multi._keys[field]) {
4338                        if (key !== key.toLowerCase()) {
4339                            Item.multi._keys[field][key.toLowerCase()] = Item.multi._keys[field][key];
4340                            delete Item.multi._keys[field][key];
4341                        }
4342                    }
4343                }
4344            }
4345            if (Item.multi.main) {
4346                for (var field in Item.multi.main) {
4347                    Item.multi.main[field] = Item.multi.main[field].toLowerCase();
4348                }
4349            }
4350        }
4351        for (var i=0, ilen=CSL.NAME_VARIABLES.length; i>ilen; i+=1) {
4352            var ctype = CSL.NAME_VARIABLES[i];
4353            if (Item[ctype] && Item[ctype].multi) {
4354                for (var j=0, jlen=Item[ctype].length; j<jlen; j+=1) {
4355                    var creator = Item[ctype][j];
4356                    if (creator.multi) {
4357                        if (creator.multi._key) {
4358                            for (var key in creator.multi._key) {
4359                                if (key !== key.toLowerCase()) {
4360                                    creator.multi._key[key.toLowerCase()] = creator.multi._key[key];
4361                                    delete creator.multi._key[key];
4362                                }
4363                            }
4364                        }
4365                        if (creator.multi.main) {
4366                            creator.multi.main = creator.multi.main.toLowerCase();
4367                        }
4368                    }
4369                }
4370            }
4371        }
4372    }
4373
4374    // Normalize language field into "language" and "language-original"
4375    if (Item.language && Item.language.match(/[><]/)) {
4376        // Attempt to split field in two
4377        var m = Item.language.match(/(.*?)([<>])(.*)/);
4378        if (m[2] === "<") {
4379            Item["language-name"] = m[1];
4380            Item["language-name-original"] = m[3];
4381        } else {
4382            Item["language-name"] = m[3];
4383            Item["language-name-original"] = m[1];
4384        }
4385        if (this.opt.multi_layout) {
4386            if (Item["language-name-original"]) {
4387                Item.language = Item["language-name-original"];
4388            }
4389        } else {
4390            if (Item["language-name"]) {
4391                Item.language = Item["language-name"];
4392            }
4393        }
4394    }
4395
4396    if (Item.page) {
4397        Item["page-first"] = Item.page;
4398        var num = "" + Item.page;
4399        var m = num.split(/\s*(?:&|, |-|\u2013)\s*/);
4400        if (m[0].slice(-1) !== "\\") {
4401            Item["page-first"] = m[0];
4402        }
4403    }
4404    // Optional development extensions
4405    if (this.opt.development_extensions.field_hack && Item.note) {
4406        // false is for validFieldsForType (all conforming entries scrubbed when false)
4407        CSL.parseNoteFieldHacks(Item, false, this.opt.development_extensions.allow_field_hack_date_override);
4408    }
4409    // not including locator-date
4410    for (var key in Item) {
4411        if (CSL.DATE_VARIABLES.indexOf(key.replace(/^alt-/, "")) > -1) {
4412            var dateobj = Item[key];
4413            if (dateobj) {
4414                // raw date parsing is harmless, but can be disabled if desired
4415                if (this.opt.development_extensions.raw_date_parsing) {
4416                    if (dateobj.raw && (!dateobj["date-parts"] || dateobj["date-parts"].length === 0)) {
4417                        dateobj = this.fun.dateparser.parseDateToObject(dateobj.raw);
4418                    }
4419                }
4420                Item[key] = this.dateParseArray(dateobj);
4421            }
4422        }
4423    }
4424    if (this.opt.development_extensions.consolidate_legal_items) {
4425        if (Item.type && ["bill","gazette","legislation","regulation","treaty"].indexOf(Item.type) > -1) {
4426            var varname;
4427            var elements = ["type", "title", "jurisdiction", "genre", "volume", "container-title"];
4428            var legislation_id = [];
4429            for (var i = 0, ilen = elements.length; i < ilen; i += 1) {
4430                varname = elements[i];
4431				if (Item[varname]) {
4432					legislation_id.push(Item[varname]);
4433				}
4434			}
4435            elements = ["original-date", "issued"];
4436			for (var i = 0, ilen=elements.length; i < ilen; i += 1) {
4437                varname = elements[i];
4438				if (Item[varname] && Item[varname].year) {
4439					var value = Item[varname].year;
4440					legislation_id.push(value);
4441					break;
4442				}
4443			}
4444			Item.legislation_id = legislation_id.join("::");
4445        }
4446    }
4447    if (this.bibliography.opt.track_container_items) {
4448        if (this.bibliography.opt.track_container_items.indexOf(Item.type) > -1) {
4449            var varname;
4450            var elements = ["type", "container-title", "publisher", "edition"];
4451            var container_id = [];
4452            for (var i = 0, ilen = elements.length; i < ilen; i += 1) {
4453                varname = elements[i];
4454				if (Item[varname]) {
4455					container_id.push(Item[varname]);
4456				}
4457			}
4458			Item.container_id = container_id.join("::");
4459        }
4460    }
4461    // For authority to name shape in legal styles
4462    if (this.opt.development_extensions.force_jurisdiction) {
4463        if ("string" === typeof Item.authority) {
4464            Item.authority = [
4465                {
4466                    literal: Item.authority,
4467                    multi: {
4468                        _key: {}
4469                    }
4470                }
4471            ];
4472            if (Item.multi && Item.multi._keys && Item.multi._keys.authority) {
4473                Item.authority[0].multi._key = {};
4474                for (var key in Item.multi._keys.authority) {
4475                    Item.authority[0].multi._key[key] = {
4476                        literal: Item.multi._keys.authority[key]
4477                    };
4478                }
4479            }
4480        }
4481    }
4482    // Add getAbbreviation() call for title-short and container-title-short
4483    if (!Item["title-short"]) {
4484        Item["title-short"] = Item.shortTitle;
4485    }
4486    // Add support for main_title_from_short_title
4487    if (this.opt.development_extensions.main_title_from_short_title) {
4488        var narrowSpaceLocale = this.opt["default-locale"][0].slice(0, 2).toLowerCase() === "fr";
4489        CSL.extractTitleAndSubtitle.call(this, Item, narrowSpaceLocale);
4490    }
4491    var isLegalType = ["bill","legal_case","legislation","gazette","regulation"].indexOf(Item.type) > -1;
4492    if (this.opt.development_extensions.force_jurisdiction && isLegalType) {
4493        if (!Item.jurisdiction) {
4494            Item.jurisdiction = "us";
4495        }
4496    }
4497    var normalizedKey;
4498    if (!isLegalType && Item.title && this.sys.getAbbreviation) {
4499        var noHints = false;
4500        if (!Item.jurisdiction) {
4501            noHints = true;
4502        }
4503        if (this.sys.normalizeAbbrevsKey) {
4504             normalizedKey = this.sys.normalizeAbbrevsKey("title", Item.title);
4505        } else {
4506            normalizedKey = Item.title;
4507        }
4508        var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "title", normalizedKey, Item.language);
4509        if (this.transform.abbrevs[jurisdiction].title) {
4510            if (this.transform.abbrevs[jurisdiction].title[normalizedKey]) {
4511                Item["title-short"] = this.transform.abbrevs[jurisdiction].title[normalizedKey];
4512            }
4513        }
4514    }
4515    if (!Item["container-title-short"]) {
4516        Item["container-title-short"] = Item.journalAbbreviation;
4517    }
4518    if (Item["container-title"] && this.sys.getAbbreviation) {
4519        if (this.sys.normalizeAbbrevsKey) {
4520            normalizedKey = this.sys.normalizeAbbrevsKey(Item["container-title"]);
4521        } else {
4522            normalizedKey = Item["container-title"];
4523        }
4524        var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "container-title", normalizedKey, Item.language);
4525        if (this.transform.abbrevs[jurisdiction]["container-title"]) {
4526            if (this.transform.abbrevs[jurisdiction]["container-title"][normalizedKey]) {
4527                Item["container-title-short"] = this.transform.abbrevs[jurisdiction]["container-title"][normalizedKey];
4528            }
4529        }
4530    }
4531    if (Item.jurisdiction) {
4532        Item.country = Item.jurisdiction.split(":")[0];
4533    }
4534    if (this.registry.refhash[id]) {
4535        if (JSON.stringify(this.registry.refhash[id]) != JSON.stringify(Item)) {
4536            for (var key in this.registry.refhash[id]) {
4537                delete this.registry.refhash[id][key];
4538            }
4539            this.tmp.taintedItemIDs[Item.id] = true;
4540            Object.assign(this.registry.refhash[id], Item);
4541        }
4542    } else {
4543        this.registry.refhash[id] = Item;
4544    }
4545    return this.registry.refhash[id];
4546};
4547
4548CSL.Engine.prototype.refetchItem = function (id) {
4549    return this.registry.refhash[id];
4550};
4551
4552// Executed during style build
4553CSL.Engine.prototype.setOpt = function (token, name, value) {
4554    if (token.name === "style" || token.name === "cslstyle") {
4555        this.opt.inheritedAttributes[name] = value;
4556        this.citation.opt.inheritedAttributes[name] = value;
4557        this.bibliography.opt.inheritedAttributes[name] = value;
4558    } else if (["citation", "bibliography"].indexOf(token.name) > -1) {
4559        this[token.name].opt.inheritedAttributes[name] = value;
4560    } else {
4561        token.strings[name] = value;
4562    }
4563};
4564
4565// Executed at runtime, since macros can occur in the context of citation or bibliography
4566CSL.Engine.prototype.inheritOpt = function (token, attrname, parentname, defaultValue) {
4567    if ("undefined" !== typeof token.strings[attrname]) {
4568        return token.strings[attrname];
4569    } else {
4570        var parentValue = this[this.tmp.root].opt.inheritedAttributes[parentname ? parentname : attrname];
4571        if ("undefined" !== typeof parentValue) {
4572            return parentValue;
4573        } else {
4574            return defaultValue;
4575        }
4576    }
4577};
4578
4579CSL.Engine.prototype.remapSectionVariable = function (inputList) {
4580    // We have items with a value in the section field (on Item) that must
4581    // be mapped to the locator field (on item). We simply prepend it as
4582    // a string here, and handle all parsing of the resulting string
4583    // in processNumber(). Plurals and numeric are set in processNumber().
4584    
4585    // Because the target is in the citation item (lowercase), the
4586    // remapping cannot take place when the Item data is received.
4587    // Citation provides a list of Item/item pairs, hence the iteration
4588    // used here.
4589    for (var i = 0, ilen = inputList.length; i < ilen; i += 1) {
4590        var Item = inputList[i][0];
4591        var item = inputList[i][1];
4592
4593        if (["bill","gazette","legislation","regulation","treaty"].indexOf(Item.type) > -1) {
4594             // If a locator value exists, then
4595            //   (a) Leave be an overriding label at the start of the locator field, defaulting to label value
4596            if (item.locator) {
4597                item.locator = item.locator.trim();
4598                var m = item.locator.match(CSL.STATUTE_SUBDIV_PLAIN_REGEX_FRONT);
4599                if (!m) {
4600                    if (item.label) {
4601                        item.locator = CSL.STATUTE_SUBDIV_STRINGS_REVERSE[item.label] + " " + item.locator;
4602                    } else {
4603                        item.locator = "p. " + item.locator;
4604                    }
4605                }
4606            }
4607            // If a section value exists, then
4608            //   (a) Apply an overriding label at the start of the section field, defaulting to sec.
4609            var sectionMasterLabel = null;
4610            if (Item.section) {
4611                Item.section = Item.section.trim();
4612                var m = Item.section.match(CSL.STATUTE_SUBDIV_PLAIN_REGEX_FRONT);
4613                if (!m) {
4614                    Item.section = "sec. " + Item.section;
4615                    sectionMasterLabel = "sec.";
4616                } else {
4617                    sectionMasterLabel = m[0].trim();
4618                }
4619            }
4620            // If section is nil, then
4621            //   (a) Do nothing
4622            if (Item.section) {
4623            // If section exists and locator is nil
4624            //   (a) Set section string in locator field
4625                if (!item.locator) {
4626                    item.locator = Item.section;
4627                } else {
4628            // If both section and locator exist, then
4629            //   (a) If locator starts with p., remove p., merge with space or no-space, and set in locator field
4630            //   (b) If locator starts with non-p., prepend section value to locator with space, and set in locator field
4631                    var m = item.locator.match(/^([^ ]*)\s*(.*)/);
4632                    var space = " ";
4633                    if (m) {
4634                        if (m[1] === "p." && sectionMasterLabel !== "p.") {
4635                            item.locator = m[2];
4636                        }
4637                        if (["[", "(", ".", ",", ";", ":", "?"].indexOf(item.locator.slice(0, 1)) > -1) {
4638                            space = "";
4639                        }
4640                    } else {
4641                       space = ""; 
4642                    }
4643                    item.locator = Item.section + space + item.locator;
4644                }
4645                //Item.section = "";
4646            }
4647            item.label = "";
4648            // And that's it. Pre-parse complete.
4649        }
4650    }
4651};
4652
4653
4654CSL.Engine.prototype.setNumberLabels = function (Item) {
4655     if (Item.number
4656        && ["bill", "gazette", "legislation","regulation","treaty"].indexOf(Item.type) > -1
4657        && this.opt.development_extensions.consolidate_legal_items
4658        && !this.tmp.shadow_numbers["number"]) {
4659
4660        this.tmp.shadow_numbers["number"] = {};
4661        this.tmp.shadow_numbers["number"].values = [];
4662        this.tmp.shadow_numbers["number"].plural = 0;
4663        this.tmp.shadow_numbers["number"].numeric = false;
4664        this.tmp.shadow_numbers["number"].label = false;
4665        
4666        // Labels embedded in number variable
4667        var value = "" + Item.number;
4668        value = value.split("\\").join("");
4669        // Get first word, parse out labels only if it parses
4670        var firstword = value.split(/\s+/)[0];
4671        var firstlabel = CSL.STATUTE_SUBDIV_STRINGS[firstword];
4672        if (firstlabel) {
4673            // Get list and match
4674            var splt = value.split(CSL.STATUTE_SUBDIV_PLAIN_REGEX);
4675            if (splt.length > 1) {
4676                // Convert matches to localized form
4677                var lst = [];
4678                for (var j=1, jlen=splt.length; j < jlen; j += 1) {
4679                    lst.push(splt[j].replace(/\s*$/, "").replace(/^\s*/, ""));
4680                }
4681                // Preemptively save to shadow_numbers
4682                value = lst.join(" ");
4683            } else {
4684                value = splt[0];
4685            }
4686            this.tmp.shadow_numbers["number"].label = firstlabel;
4687            this.tmp.shadow_numbers["number"].values.push(["Blob", value, false]);
4688            this.tmp.shadow_numbers["number"].numeric = false;
4689        } else {
4690            this.tmp.shadow_numbers["number"].values.push(["Blob", value, false]);
4691            this.tmp.shadow_numbers["number"].numeric = true;
4692        }
4693    }
4694};
4695
4696/*global CSL: true */
4697
4698CSL.substituteOne = function (template) {
4699    return function (state, list) {
4700        if (!list) {
4701            return "";
4702        } else {
4703            // ("string" === typeof list)
4704            return template.replace("%%STRING%%", list);
4705        }
4706    };
4707};
4708
4709
4710/**
4711 * Two-tiered substitutions gadget.
4712 * <p>This is used for
4713 * options like (now defunct) "font-family", where the option value
4714 * cannot be determined until the attribute is processed.
4715 * Need for this function might be reviewed at some point ...</p>
4716 * @param {String} template A template containing
4717 * <code>%%STRING%%</code> and <code>%%PARAM%%</code>
4718 * placeholders.  See {@link CSL.Output.Formats.html} for
4719 * examples.
4720 */
4721CSL.substituteTwo = function (template) {
4722    return function (param) {
4723        var template2 = template.replace("%%PARAM%%", param);
4724        return function (state, list) {
4725            if (!list) {
4726                return "";
4727            } else {
4728                //("string" === typeof list){
4729                return template2.replace("%%STRING%%", list);
4730            }
4731        };
4732    };
4733};
4734
4735/**
4736 * Generate string functions for designated output mode.
4737 * <p>Only "html" (the default) is supported at present.</p>
4738 * @param {String} mode Either "html" or "rtf", eventually.
4739 */
4740CSL.Mode = function (mode) {
4741    var decorations, params, param, func, val, args;
4742    decorations = {};
4743    params = CSL.Output.Formats[mode];
4744    for (param in params) {
4745        if (true) {
4746
4747            if ("@" !== param.slice(0, 1)) {
4748                decorations[param] = params[param];
4749                continue;
4750            }
4751            func = false;
4752            val = params[param];
4753            args = param.split('/');
4754
4755            if (typeof val === "string" && val.indexOf("%%STRING%%") > -1)  {
4756                if (val.indexOf("%%PARAM%%") > -1) {
4757                    func = CSL.substituteTwo(val);
4758                } else {
4759                    func = CSL.substituteOne(val);
4760                }
4761            } else if (typeof val === "boolean" && !val) {
4762                func = CSL.Output.Formatters.passthrough;
4763            } else if (typeof val === "function") {
4764                func = val;
4765            } else {
4766                CSL.error("Bad " + mode + " config entry for " + param + ": " + val);
4767            }
4768
4769            if (args.length === 1) {
4770                decorations[args[0]] = func;
4771            } else if (args.length === 2) {
4772                if (!decorations[args[0]]) {
4773                    decorations[args[0]] = {};
4774                }
4775                decorations[args[0]][args[1]] = func;
4776            }
4777        }
4778    }
4779    return decorations;
4780};
4781
4782
4783/**
4784 * Generate a separate list of formatting attributes.
4785 * <p>This generates a list of tuples containing attribute
4786 * information relevant to output formatting, in the order
4787 * fixed in the constant {@link CSL.FORMAT_KEY_SEQUENCE}.
4788 * This function is called during {@link CSL.Core.Build}.
4789 * Formatting hints are distilled to functions
4790 * later, in the second compilation pass ({@link CSL.Core.Configure}).</p>
4791 * @param {Object} state The state object returned by
4792 * {@link CSL.Engine}.
4793 * @param {Object} attributes The hash object containing
4794 * the attributes and values extracted from an XML node.
4795 */
4796CSL.setDecorations = function (state, attributes) {
4797    var ret, key, pos;
4798    // This applies a fixed processing sequence
4799    ret = [];
4800    for (pos in CSL.FORMAT_KEY_SEQUENCE) {
4801        if (true) {
4802            var key = CSL.FORMAT_KEY_SEQUENCE[pos];
4803            if (attributes[key]) {
4804                ret.push([key, attributes[key]]);
4805                delete attributes[key];
4806            }
4807        }
4808    }
4809    return ret;
4810};
4811
4812CSL.Doppeler = function(rexStr, stringMangler) {
4813    var matchRex = new RegExp("(" + rexStr + ")", "g");
4814    var splitRex = new RegExp(rexStr, "g");
4815    this.split = function (str) {
4816        // Normalize markup
4817        if (stringMangler) {
4818            str = stringMangler(str);
4819        }
4820        var match = str.match(matchRex);
4821        if (!match) {
4822            return {
4823                tags: [],
4824                strings: [str]
4825            };
4826        }
4827        var split = str.split(splitRex);
4828        for (var i=match.length-1; i> -1; i--) {
4829            if (typeof match[i] === "number") {
4830                match[i] = "";
4831            }
4832            var tag = match[i];
4833            if (tag === "\'" && split[i+1].length > 0) {
4834                // Fixes https://forums.zotero.org/discussion/comment/294317
4835                split[i+1] = match[i] + split[i+1];
4836                match[i] = "";
4837            }
4838        }
4839        return {
4840            tags: match,
4841            strings: split,
4842            origStrings: split.slice()
4843        };
4844    };
4845    this.join = function (obj) {
4846        var lst = obj.strings.slice(-1);
4847        for (var i=obj.tags.length-1; i>-1; i--) {
4848            lst.push(obj.tags[i]);
4849            lst.push(obj.strings[i]);
4850        }
4851        lst.reverse();
4852        return lst.join("");
4853    };
4854};
4855
4856CSL.Engine.prototype.normalDecorIsOrphan = function (blob, params) {
4857    //print("params: "+JSON.stringify(params));
4858    if (params[1] === "normal") {
4859        var use_param = false;
4860        var all_the_decor;
4861        if (this.tmp.area === "citation") {
4862            all_the_decor = [this.citation.opt.layout_decorations].concat(blob.alldecor);
4863        } else {
4864            all_the_decor = blob.alldecor;
4865        }
4866        for (var k = all_the_decor.length - 1; k > -1; k += -1) {
4867            //print("  all decor: "+JSON.stringify(all_the_decor[k]));
4868            for (var n = all_the_decor[k].length - 1; n > -1; n += -1) {
4869                //print("  superior param"+n+": "+all_the_decor[k][n][0]);
4870                if (all_the_decor[k][n][0] === params[0]) {
4871                    //print("  HIT!");
4872                    if (all_the_decor[k][n][1] !== "normal") {
4873                        use_param = true;
4874                    }
4875                }
4876            }
4877        }
4878        if (!use_param) {
4879            return true;
4880        }
4881    }
4882    return false;
4883};
4884
4885/*global CSL: true */
4886
4887
4888CSL.Engine.prototype.getCitationLabel = function (Item) {
4889    var label = "";
4890    var params = this.getTrigraphParams();
4891    var config = params[0];
4892    var myname = this.getTerm("reference", "short", 0);
4893    if ("undefined" === typeof myname) {
4894        myname = "reference";
4895    }
4896    myname = myname.replace(".", "");
4897    myname = myname.slice(0, 1).toUpperCase() + myname.slice(1);
4898    for (var i = 0, ilen = CSL.NAME_VARIABLES.length; i < ilen; i += 1) {
4899        var n = CSL.NAME_VARIABLES[i];
4900        if (Item[n]) {
4901            var names = Item[n];
4902            if (names.length > params.length) {
4903                config = params[params.length - 1];
4904            } else {
4905                config = params[names.length - 1];
4906            }
4907            for (var j = 0, jlen = names.length; j < jlen; j += 1) {
4908                if (j === config.authors.length) {
4909                    break;
4910                }
4911                var res = this.nameOutput.getName(names[j], "locale-translit", true);
4912                var name = res.name;
4913                if (name && name.family) {
4914                    myname = name.family;
4915                    myname = myname.replace(/^([ \'\u2019a-z]+\s+)/, "");
4916
4917                } else if (name && name.literal) {
4918                    myname = name.literal;
4919                }
4920                var m = myname.toLowerCase().match(/^(a\s+|the\s+|an\s+)/);
4921                if (m) {
4922                    myname = myname.slice(m[1].length);
4923                }
4924                myname = myname.replace(CSL.ROMANESQUE_NOT_REGEXP, "");
4925                if (!myname) {
4926                    break;
4927                }
4928                myname = myname.slice(0, config.authors[j]);
4929                if (myname.length > 1) {
4930                    myname = myname.slice(0, 1).toUpperCase() + myname.slice(1).toLowerCase();
4931                } else if (myname.length === 1) {
4932                    myname = myname.toUpperCase();
4933                }
4934                label += myname;
4935            }
4936            break;
4937        }
4938    }
4939    if (!label) {
4940        // Try for something using title
4941        if (Item.title) {
4942            var skipWords = this.locale[this.opt.lang].opts["skip-words"];
4943            var lst = Item.title.split(/\s+/);
4944            for (var i = lst.length - 1; i > -1; i--) {
4945                if (skipWords.indexOf(lst[i]) > -1) {
4946                    lst = lst.slice(0, i).concat(lst.slice(i + 1));
4947                }
4948            }
4949            var str = lst.join('');
4950            str = str.slice(0, params[0].authors[0]);
4951            if (str.length > 1) {
4952                str = str.slice(0, 1).toUpperCase() + str.slice(1).toLowerCase();
4953            } else if (str.length === 1) {
4954                str = str.toUpperCase();
4955            }
4956            label = str;
4957        }
4958    }
4959    var year = "0000";
4960    if (Item.issued) {
4961        if (Item.issued.year) {
4962            year = "" + Item.issued.year;
4963        }
4964    }
4965    year = year.slice((config.year * -1));
4966    label = label + year;
4967    return label;
4968};
4969
4970CSL.Engine.prototype.getTrigraphParams = function () {
4971    var params = [];
4972    var ilst = this.opt.trigraph.split(":");
4973    if (!this.opt.trigraph || this.opt.trigraph.slice(0,1) !== "A") {
4974        CSL.error("Bad trigraph definition: "+this.opt.trigraph);
4975    }
4976    for (var i = 0, ilen = ilst.length; i < ilen; i += 1) {
4977        var str = ilst[i];
4978        var config = {authors:[], year:0};
4979        for (var j = 0, jlen = str.length; j < jlen; j += 1) {
4980            switch (str.slice(j,j+1)) {
4981            case "A":
4982                config.authors.push(1);
4983                break;
4984            case "a":
4985                config.authors[config.authors.length - 1] += 1;
4986                break;
4987            case "0":
4988                config.year += 1;
4989                break;
4990            default:
4991                CSL.error("Invalid character in trigraph definition: "+this.opt.trigraph);
4992            }
4993        }
4994        params.push(config);
4995    }
4996    return params;
4997};
4998
4999/*global CSL: true */
5000
5001CSL.Engine.prototype.setOutputFormat = function (mode) {
5002    this.opt.mode = mode;
5003    this.fun.decorate = CSL.Mode(mode);
5004    if (!this.output[mode]) {
5005        this.output[mode] = {};
5006        this.output[mode].tmp = {};
5007    }
5008};
5009
5010CSL.Engine.prototype.getSortFunc = function () {
5011    return function (a,b) {
5012        a = a.split("-");
5013        b = b.split("-");
5014        if (a.length < b.length) {
5015            return 1;
5016        } else if (a.length > b.length) {
5017            return -1;
5018        } else {
5019            a = a.slice(-1)[0];
5020            b = b.slice(-1)[0];
5021            if (a.length < b.length) {
5022                return 1;
5023            } else if (a.length > b.length) {
5024                return -1;
5025            } else {
5026                return 0;
5027            }
5028        }
5029    };
5030};
5031
5032CSL.Engine.prototype.setLangTagsForCslSort = function (tags) {
5033    var i, ilen;
5034    if (tags) {
5035        this.opt['locale-sort'] = [];
5036        for (i = 0, ilen = tags.length; i < ilen; i += 1) {
5037            this.opt['locale-sort'].push(tags[i]);
5038        }
5039    }
5040    this.opt['locale-sort'].sort(this.getSortFunc());
5041};
5042    
5043CSL.Engine.prototype.setLangTagsForCslTransliteration = function (tags) {
5044    var i, ilen;
5045    this.opt['locale-translit'] = [];
5046    if (tags) {
5047        for (i = 0, ilen = tags.length; i < ilen; i += 1) {
5048            this.opt['locale-translit'].push(tags[i]);
5049        }
5050    }
5051    this.opt['locale-translit'].sort(this.getSortFunc());
5052};
5053    
5054CSL.Engine.prototype.setLangTagsForCslTranslation = function (tags) {
5055    var i, ilen;
5056    this.opt['locale-translat'] = [];
5057    if (tags) {
5058        for (i = 0, ilen = tags.length; i < ilen; i += 1) {
5059            this.opt['locale-translat'].push(tags[i]);
5060        }
5061    }
5062    this.opt['locale-translat'].sort(this.getSortFunc());
5063};
5064
5065CSL.Engine.prototype.setLangPrefsForCites = function (obj, conv) {
5066    var opt = this.opt['cite-lang-prefs'];
5067    if (!conv) {
5068        conv = function (key) {
5069            return key.toLowerCase();
5070        };
5071    }
5072    var segments = ['Persons', 'Institutions', 'Titles', 'Journals', 'Publishers', 'Places'];
5073    // Set values in place
5074    for (var i = 0, ilen = segments.length; i < ilen; i += 1) {
5075        var clientSegment = conv(segments[i]);
5076        var citeprocSegment = segments[i].toLowerCase();
5077        if (!obj[clientSegment]) {
5078            continue;
5079        }
5080        //
5081        // Normalize the sequence of secondary and tertiary
5082        // in the provided obj segment list.
5083        //
5084        var supplements = [];
5085        while (obj[clientSegment].length > 1) {
5086            supplements.push(obj[clientSegment].pop());
5087        }
5088        var sortval = {orig:1,translit:2,translat:3};
5089        if (supplements.length === 2 && sortval[supplements[0]] < sortval[supplements[1]]) {
5090            supplements.reverse();
5091        }
5092        while (supplements.length) {
5093            obj[clientSegment].push(supplements.pop());
5094        }
5095        //
5096        // normalization done.
5097        //
5098        var lst = opt[citeprocSegment];
5099        while (lst.length) {
5100            lst.pop();
5101        }
5102        for (var j = 0, jlen = obj[clientSegment].length; j < jlen; j += 1) {
5103            lst.push(obj[clientSegment][j]);
5104        }
5105    }
5106};
5107
5108CSL.Engine.prototype.setLangPrefsForCiteAffixes = function (affixList) {
5109    if (affixList && affixList.length === 48) {
5110        var affixes = this.opt.citeAffixes;
5111        var count = 0;
5112        var settings = ["persons", "institutions", "titles", "journals", "publishers", "places"];
5113        var forms = ["translit", "orig", "translit", "translat"];
5114        var value;
5115        for (var i = 0, ilen = settings.length; i < ilen; i += 1) {
5116            for (var j = 0, jlen = forms.length; j < jlen; j += 1) {
5117                value = "";
5118                if ((count % 8) === 4) {
5119                    if (!affixes[settings[i]]["locale-"+forms[j]].prefix
5120                        && !affixes[settings[i]]["locale-"+forms[j]].suffix) {
5121
5122                        value = affixList[count] ? affixList[count] : "";
5123                        affixes[settings[i]]["locale-" + forms[j]].prefix = value;
5124                        value = affixList[count] ? affixList[count + 1] : "";
5125                        affixes[settings[i]]["locale-" + forms[j]].suffix = value;
5126                    }
5127                } else {
5128                    value = affixList[count] ? affixList[count] : "";
5129                    affixes[settings[i]]["locale-" + forms[j]].prefix = value;
5130                    value = affixList[count] ? affixList[count + 1] : "";
5131                    affixes[settings[i]]["locale-" + forms[j]].suffix = value;
5132                }
5133                count += 2;
5134            }
5135        }
5136        this.opt.citeAffixes = affixes;
5137    }
5138};
5139
5140CSL.Engine.prototype.setAutoVietnameseNamesOption = function (arg) {
5141    if (arg) {
5142        this.opt["auto-vietnamese-names"] = true;
5143    } else {
5144        this.opt["auto-vietnamese-names"] = false;
5145    }
5146};
5147
5148CSL.Engine.prototype.setAbbreviations = function (arg) {
5149    if (this.sys.setAbbreviations) {
5150        this.sys.setAbbreviations(arg);
5151    }
5152};
5153
5154CSL.Engine.prototype.setSuppressTrailingPunctuation = function (arg) {
5155    this.citation.opt.suppressTrailingPunctuation = !!arg;
5156};
5157
5158/*global CSL: true */
5159
5160CSL.Output = {};
5161/**
5162 * Output queue object.
5163 * @class
5164 */
5165CSL.Output.Queue = function (state) {
5166    this.levelname = ["top"];
5167    this.state = state;
5168    this.queue = [];
5169    this.empty = new CSL.Token("empty");
5170    var tokenstore = {};
5171    tokenstore.empty = this.empty;
5172    this.formats = new CSL.Stack(tokenstore);
5173    this.current = new CSL.Stack(this.queue);
5174};
5175
5176// XXX This works, but causes a mismatch in api_cite
5177// Could insert a placeholder
5178// Better to have a function that spits out an independent blob.
5179// Is that possible though?
5180// Okay. Use queue.append() with fake_queue instead.
5181CSL.Output.Queue.prototype.pop = function () {
5182    // For some reason, state.output.current.value() here can be an array, 
5183    // not a blob ... ?
5184    var drip = this.current.value();
5185    if (drip.length) {
5186        return drip.pop();
5187    } else {
5188        return drip.blobs.pop();
5189    }
5190};
5191
5192CSL.Output.Queue.prototype.getToken = function (name) {
5193    var ret = this.formats.value()[name];
5194    return ret;
5195};
5196
5197CSL.Output.Queue.prototype.mergeTokenStrings = function (base, modifier) {
5198    var base_token, modifier_token, ret, key;
5199    base_token = this.formats.value()[base];
5200    modifier_token = this.formats.value()[modifier];
5201    ret = base_token;
5202    if (modifier_token) {
5203        if (!base_token) {
5204            base_token = new CSL.Token(base, CSL.SINGLETON);
5205            base_token.decorations = [];
5206        }
5207        ret = new CSL.Token(base, CSL.SINGLETON);
5208        var key = "";
5209        for (var key in base_token.strings) {
5210            if (base_token.strings.hasOwnProperty(key)) {
5211                ret.strings[key] = base_token.strings[key];
5212            }
5213        }
5214        for (var key in modifier_token.strings) {
5215            if (modifier_token.strings.hasOwnProperty(key)) {
5216                ret.strings[key] = modifier_token.strings[key];
5217            }
5218        }
5219        ret.decorations = base_token.decorations.concat(modifier_token.decorations);
5220    }
5221    return ret;
5222};
5223
5224// Store a new output format token based on another
5225CSL.Output.Queue.prototype.addToken = function (name, modifier, token) {
5226    var newtok, attr;
5227    newtok = new CSL.Token("output");
5228    if ("string" === typeof token) {
5229        token = this.formats.value()[token];
5230    }
5231    if (token && token.strings) {
5232        for (attr in token.strings) {
5233            if (token.strings.hasOwnProperty(attr)) {
5234                newtok.strings[attr] = token.strings[attr];
5235            }
5236        }
5237        newtok.decorations = token.decorations;
5238
5239    }
5240    if ("string" === typeof modifier) {
5241        newtok.strings.delimiter = modifier;
5242    }
5243    this.formats.value()[name] = newtok;
5244};
5245
5246//
5247// newFormat adds a new bundle of formatting tokens to
5248// the queue's internal stack of such bundles
5249CSL.Output.Queue.prototype.pushFormats = function (tokenstore) {
5250    if (!tokenstore) {
5251        tokenstore = {};
5252    }
5253    tokenstore.empty = this.empty;
5254    this.formats.push(tokenstore);
5255};
5256
5257
5258CSL.Output.Queue.prototype.popFormats = function () {
5259    this.formats.pop();
5260};
5261
5262CSL.Output.Queue.prototype.startTag = function (name, token) {
5263    var tokenstore = {};
5264    if (this.state.tmp["doing-macro-with-date"] && this.state.tmp.extension) {
5265        token = this.empty;
5266        name = "empty";
5267    }
5268    tokenstore[name] = token;
5269    this.pushFormats(tokenstore);
5270    this.openLevel(name);
5271};
5272
5273CSL.Output.Queue.prototype.endTag = function (name) {
5274    this.closeLevel(name);
5275    this.popFormats();
5276};
5277
5278//
5279// newlevel adds a new blob object to the end of the current
5280// list, and adjusts the current pointer so that subsequent
5281// appends are made to blob list of the new object.
5282
5283CSL.Output.Queue.prototype.openLevel = function (token) {
5284    var blob, curr;
5285    if ("object" === typeof token) {
5286        // delimiter, prefix, suffix, decorations from token
5287        blob = new CSL.Blob(undefined, token);
5288    } else if ("undefined" === typeof token) {
5289        blob = new CSL.Blob(undefined, this.formats.value().empty, "empty");
5290    } else {
5291        if (!this.formats.value() || !this.formats.value()[token]) {
5292            CSL.error("CSL processor error: call to nonexistent format token \"" + token + "\"");
5293        }
5294        // delimiter, prefix, suffix, decorations from token
5295        blob = new CSL.Blob(undefined, this.formats.value()[token], token);
5296    }
5297    curr = this.current.value();
5298    if (!this.state.tmp.just_looking && this.checkNestedBrace) {
5299        blob.strings.prefix = this.checkNestedBrace.update(blob.strings.prefix);
5300    }
5301    curr.push(blob);
5302    this.current.push(blob);
5303};
5304
5305/**
5306 * "merge" used to be real complicated, now it's real simple.
5307 */
5308CSL.Output.Queue.prototype.closeLevel = function (name) {
5309    // CLEANUP: Okay, so this.current.value() holds the blob at the
5310    // end of the current list.  This is wrong.  It should
5311    // be the parent, so that we have  the choice of reading
5312    // the affixes and decorations, or appending to its
5313    // content.  The code that manipulates blobs will be
5314    // much simpler that way.
5315    if (name && name !== this.current.value().levelname) {
5316        CSL.error("Level mismatch error:  wanted " + name + " but found " + this.current.value().levelname);
5317    }
5318    var blob = this.current.pop();
5319    if (!this.state.tmp.just_looking && this.checkNestedBrace) {
5320        blob.strings.suffix = this.checkNestedBrace.update(blob.strings.suffix);
5321    }
5322};
5323
5324//
5325// append does the same thing as newlevel, except
5326// that the blob it pushes has text content,
5327// and the current pointer is not moved after the push.
5328
5329CSL.Output.Queue.prototype.append = function (str, tokname, notSerious, ignorePredecessor, noStripPeriods) {
5330    var token, blob, curr;
5331    var useblob = true;
5332    if (notSerious) {
5333        ignorePredecessor = true;
5334    }
5335    // XXXXX Nasty workaround, but still an improvement
5336    // over the reverse calls to the cs:date node build
5337    // function that we had before.
5338    if (this.state.tmp["doing-macro-with-date"] && !notSerious) {
5339        if (tokname !== "macro-with-date") {
5340            return false;
5341        }
5342        if (tokname === "macro-with-date") {
5343            tokname = "empty";
5344        }
5345    }
5346    if ("undefined" === typeof str) {
5347        return false;
5348    }
5349    if ("number" === typeof str) {
5350        str = "" + str;
5351    }
5352    if (!notSerious 
5353        && this.state.tmp.element_trace 
5354        && this.state.tmp.element_trace.value() === "suppress-me") {
5355        
5356        return false;
5357    }
5358    blob = false;
5359    if (!tokname) {
5360        token = this.formats.value().empty;
5361    } else if (tokname === "literal") {
5362        token = true;
5363        useblob = false;
5364    } else if ("string" === typeof tokname) {
5365        token = this.formats.value()[tokname];
5366    } else {
5367        token = tokname;
5368    }
5369    if (!token) {
5370        CSL.error("CSL processor error: unknown format token name: " + tokname);
5371    }
5372    // Unset delimiters must be left undefined until they reach the queue
5373    // in order to discriminate unset from explicitly empty delimiters
5374    // when inheriting a default value from a superior node. [??? really ???]
5375    if (token.strings && "undefined" === typeof token.strings.delimiter) {
5376        token.strings.delimiter = "";
5377    }
5378    if ("string" === typeof str && str.length) {
5379
5380        // Source (;?!»«): http://en.wikipedia.org/wiki/Space_(punctuation)#Breaking_and_non-breaking_spaces
5381        // Source (:): http://forums.zotero.org/discussion/4933/localized-quotes/#Comment_88384
5382        str = str.replace(/ ([:;?!\u00bb])/g, "\u202f$1").replace(/\u00ab /g, "\u00ab\u202f");
5383
5384        this.last_char_rendered = str.slice(-1);
5385        // This, and not the str argument below on flipflop, is the
5386        // source of the flipflopper string source.
5387        str = str.replace(/\s+'/g, " \'");
5388        if (!notSerious) {
5389            // this condition for sort_LeadingApostropheOnNameParticle
5390            str = str.replace(/^'/g, " \'");
5391        }
5392
5393        // signal whether we end with terminal punctuation?
5394        if (!ignorePredecessor) {
5395            this.state.tmp.term_predecessor = true;
5396            this.state.tmp.in_cite_predecessor = true;
5397        } else if (notSerious) {
5398            this.state.tmp.term_predecessor_name = true;
5399        }
5400    }
5401    blob = new CSL.Blob(str, token);
5402    curr = this.current.value();
5403    if ("undefined" === typeof curr && this.current.mystack.length === 0) {
5404        // XXXX An operation like this is missing somewhere, this should NOT be necessary.
5405        // Addresses error triggered in multi-layouts.
5406        this.current.mystack.push([]);
5407        curr = this.current.value();
5408    }
5409    if ("string" === typeof blob.blobs) {
5410        if (!ignorePredecessor) {
5411            this.state.tmp.term_predecessor = true;
5412            this.state.tmp.in_cite_predecessor = true;
5413        } else if (notSerious) {
5414            this.state.tmp.term_predecessor_name = true;
5415        }
5416    }
5417    //
5418    // Caution: The parallel detection machinery will blow up if tracking
5419    // variables are not properly initialized elsewhere.
5420    //
5421    if ("string" === typeof str) {
5422        if ("string" === typeof blob.blobs) {
5423            if (blob.blobs.slice(0, 1) !== " ") {
5424                var blobPrefix = "";
5425                var blobBlobs = blob.blobs;
5426                while (CSL.TERMINAL_PUNCTUATION.indexOf(blobBlobs.slice(0, 1)) > -1) {
5427                    blobPrefix = blobPrefix + blobBlobs.slice(0, 1);
5428                    blobBlobs = blobBlobs.slice(1);
5429                }
5430                if (blobBlobs && blobPrefix) {
5431                    blob.strings.prefix = blob.strings.prefix + blobPrefix;
5432                    blob.blobs = blobBlobs;
5433                }
5434            }
5435        }
5436        if (blob.strings["text-case"]) {
5437            //
5438            // This one is _particularly_ hard to follow.  It's not obvious,
5439            // but the blob already contains the input string at this
5440            // point, as blob.blobs -- it's a terminal node, as it were.
5441            // The str variable also contains the input string, but
5442            // that copy is not used for onward processing.  We have to
5443            // apply our changes to the blob copy.
5444            //
5445            blob.blobs = CSL.Output.Formatters[blob.strings["text-case"]](this.state, str);
5446        }
5447        if (this.state.tmp.strip_periods && !noStripPeriods) {
5448            blob.blobs = blob.blobs.replace(/\.([^a-z]|$)/g, "$1");
5449        }
5450        for (var i = blob.decorations.length - 1; i > -1; i += -1) {
5451            if (blob.decorations[i][0] === "@quotes" && blob.decorations[i][1] !== "false") {
5452                blob.punctuation_in_quote = this.state.getOpt("punctuation-in-quote");
5453            }
5454            if (!blob.blobs.match(CSL.ROMANESQUE_REGEXP)) {
5455                if (blob.decorations[i][0] === "@font-style") {
5456                    blob.decorations = blob.decorations.slice(0, i).concat(blob.decorations.slice(i + 1));
5457                }
5458            }
5459        }
5460        //
5461        // XXX: Beware superfluous code in your code.  str in this
5462        // case is not the source of the final rendered string.
5463        // See note above.
5464        //
5465        curr.push(blob);
5466        this.state.fun.flipflopper.processTags(blob);
5467    } else if (useblob) {
5468        curr.push(blob);
5469    } else {
5470        curr.push(str);
5471    }
5472    return true;
5473};
5474
5475CSL.Output.Queue.prototype.string = function (state, myblobs, blob) {
5476    var i, ilen, j, jlen, b;
5477    //if (blob && blob.strings.delimiter) {
5478    //    print("DELIMITER: "+blob.strings.delimiter+" on "+[x.blobs[0].num for each (x in myblobs)]);
5479    //}
5480    //var blobs, ret, blob_delimiter, i, params, blobjr, last_str, last_char, b, use_suffix, qres, addtoret, span_split, j, res, blobs_start, blobs_end, key, pos, len, ppos, llen, ttype, ltype, terminal, leading, delimiters, use_prefix, txt_esc;
5481    var txt_esc = CSL.getSafeEscape(this.state);
5482    var blobs = myblobs.slice();
5483    var ret = [];
5484    
5485    if (blobs.length === 0) {
5486        return ret;
5487    }
5488
5489    var blob_delimiter = "";
5490    if (blob) {
5491        blob_delimiter = blob.strings.delimiter;
5492    } else {
5493        //print("=== Setting false to start ===");
5494        state.tmp.count_offset_characters = false;
5495        state.tmp.offset_characters = 0;
5496    }
5497
5498    if (blob && blob.new_locale) {
5499        blob.old_locale = state.opt.lang;
5500        state.opt.lang = blob.new_locale;
5501    }
5502
5503    var blobjr, use_suffix, use_prefix, params;
5504    for (var i = 0, ilen = blobs.length; i < ilen; i += 1) {
5505        blobjr = blobs[i];
5506
5507        if (blobjr.strings.first_blob) {
5508            // Being the Item.id of the the entry being rendered.
5509            //print("  -- turning on counting");
5510            state.tmp.count_offset_characters = blobjr.strings.first_blob;
5511        }
5512
5513        if ("string" === typeof blobjr.blobs) {
5514            if ("number" === typeof blobjr.num) {
5515                ret.push(blobjr);
5516            } else if (blobjr.blobs) {
5517                if (blobjr.particle) {
5518                    blobjr.blobs = blobjr.particle + blobjr.blobs;
5519                    blobjr.particle = "";
5520                }
5521                // (skips empty strings)
5522                //b = txt_esc(blobjr.blobs);
5523                b = txt_esc(blobjr.blobs);
5524                var blen = b.length;
5525
5526                if (!state.tmp.suppress_decorations) {
5527                    for (j = 0, jlen = blobjr.decorations.length; j < jlen; j += 1) {
5528                        params = blobjr.decorations[j];
5529                        if (params[0] === "@showid") {
5530                            continue;
5531                        }
5532                        if (state.normalDecorIsOrphan(blobjr, params)) {
5533                            continue;
5534                        }
5535                        b = state.fun.decorate[params[0]][params[1]].call(blobjr, state, b, params[2]);
5536                    }
5537                }
5538                //
5539                // because we will rip out portions of the output
5540                // queue before rendering, group wrappers need
5541                // to produce no output if they are found to be
5542                // empty.
5543                if (b && b.length) {
5544                    b = txt_esc(blobjr.strings.prefix) + b + txt_esc(blobjr.strings.suffix);
5545                    if (state.opt.development_extensions.csl_reverse_lookup_support && !state.tmp.suppress_decorations) {
5546                        for (j = 0, jlen = blobjr.decorations.length; j < jlen; j += 1) {
5547                            params = blobjr.decorations[j];
5548
5549                            if (params[0] === "@showid") {
5550                                b = state.fun.decorate[params[0]][params[1]].call(blobjr, state, b, params[2]);
5551                            }
5552                        }
5553                    }
5554                    ret.push(b);
5555                    if (state.tmp.count_offset_characters) {
5556                        state.tmp.offset_characters += (blen + blobjr.strings.suffix.length + blobjr.strings.prefix.length);
5557                    }
5558                }
5559            }
5560        } else if (blobjr.blobs.length) {
5561            var addtoret = state.output.string(state, blobjr.blobs, blobjr);
5562            if (blob) {
5563                // Patch up world-class weird bug in the ill-constructed code of mine.
5564                if ("string" !== addtoret && addtoret.length > 1 && blobjr.strings.delimiter) {
5565                    var numberSeen = false;
5566                    for (var j=0,jlen=addtoret.length;j<jlen;j++) {
5567                        if ("string" !== typeof addtoret[j]) {
5568                            numberSeen = true;
5569                        } else if (numberSeen) {
5570                            addtoret[j] = (blobjr.strings.delimiter + addtoret[j]);
5571                        }
5572                    }
5573                }
5574            }
5575            ret = ret.concat(addtoret);
5576        }
5577        if (blobjr.strings.first_blob && state.registry.registry[blobjr.strings.first_blob]) {
5578            // The Item.id of the entry being rendered.
5579            state.registry.registry[blobjr.strings.first_blob].offset = state.tmp.offset_characters;
5580            state.tmp.count_offset_characters = false;
5581        }
5582    }
5583
5584    // Provide delimiters on adjacent numeric blobs
5585    for (i=0,ilen=ret.length - 1;i<ilen;i+=1) {
5586        if ("number" === typeof ret[i].num && "number" === typeof ret[i+1].num && !ret[i+1].UGLY_DELIMITER_SUPPRESS_HACK) {
5587            // XXX watch this
5588            ret[i].strings.suffix = ret[i].strings.suffix + (blob_delimiter ? blob_delimiter : "");
5589            ret[i+1].successor_prefix = "";
5590            ret[i+1].UGLY_DELIMITER_SUPPRESS_HACK = true;
5591        }
5592    }
5593
5594    var span_split = 0;
5595    for (var i = 0, ilen = ret.length; i < ilen; i += 1) {
5596        if ("string" === typeof ret[i]) {
5597            span_split = (parseInt(i, 10) + 1);
5598            if (i < ret.length - 1  && "object" === typeof ret[i + 1]) {
5599                if (blob_delimiter && !ret[i + 1].UGLY_DELIMITER_SUPPRESS_HACK) {
5600                    ret[i] += txt_esc(blob_delimiter);
5601                }
5602                // One bite of the apple
5603                ret[i + 1].UGLY_DELIMITER_SUPPRESS_HACK = true;
5604            }
5605            //span_split = ret.length;
5606            //print("XXX ret: "+ret+" -- "+blob_delimiter);
5607        }
5608    }
5609/*
5610    if (blob && (blob.decorations.length || blob.strings.suffix || blob.strings.prefix)) {
5611        span_split = ret.length;
5612    }
5613*/
5614    if (blob && (blob.decorations.length || blob.strings.suffix)) {
5615        span_split = ret.length;
5616    } else if (blob && blob.strings.prefix) {
5617        for (var i=0,ilen=ret.length;i<ilen;i++) {
5618            if ("undefined" !== typeof ret[i].num) {
5619                span_split = i;
5620                if (i === 0) {
5621                    ret[i].strings.prefix = blob.strings.prefix + ret[i].strings.prefix;
5622                }
5623                break;
5624            }
5625        }
5626    }
5627
5628    var blobs_start = state.output.renderBlobs(ret.slice(0, span_split), blob_delimiter, false, blob);
5629    if (blobs_start && blob && (blob.decorations.length || blob.strings.suffix || blob.strings.prefix)) {
5630        if (!state.tmp.suppress_decorations) {
5631            for (var i = 0, ilen = blob.decorations.length; i < ilen; i += 1) {
5632                params = blob.decorations[i];
5633                if (["@cite","@bibliography", "@display", "@showid"].indexOf(params[0]) > -1) {
5634                    continue;
5635                }
5636                if (state.normalDecorIsOrphan(blobjr, params)) {
5637                    continue;
5638                }
5639                if (!params[0]) continue;
5640                if ("string" === typeof blobs_start) {
5641                    blobs_start = state.fun.decorate[params[0]][params[1]].call(blob, state, blobs_start, params[2]);
5642                }
5643            }
5644        }
5645        //
5646        // XXXX: cut-and-paste warning.  same as a code block above.
5647        //
5648        b = blobs_start;
5649        use_suffix = blob.strings.suffix;
5650        if (b && b.length) {
5651            use_prefix = blob.strings.prefix;
5652            b = txt_esc(use_prefix) + b + txt_esc(use_suffix);
5653            if (state.tmp.count_offset_characters) {
5654                state.tmp.offset_characters += (use_prefix.length + use_suffix.length);
5655            }
5656        }
5657        blobs_start = b;
5658        if (!state.tmp.suppress_decorations) {
5659            for (var i = 0, ilen = blob.decorations.length; i < ilen; i += 1) {
5660                params = blob.decorations[i];
5661                if (["@cite","@bibliography", "@display", "@showid"].indexOf(params[0]) === -1) {
5662                    continue;
5663                }
5664                if ("string" === typeof blobs_start) {
5665                    blobs_start = state.fun.decorate[params[0]][params[1]].call(blob, state, blobs_start, params[2]);
5666                }
5667            }
5668        }
5669    }
5670
5671    var blobs_end = ret.slice(span_split, ret.length);
5672    if (!blobs_end.length && blobs_start) {
5673        ret = [blobs_start];
5674    } else if (blobs_end.length && !blobs_start) {
5675        ret = blobs_end;
5676    } else if (blobs_start && blobs_end.length) {
5677        ret = [blobs_start].concat(blobs_end);
5678    }
5679    //
5680    // Blobs is now definitely a string with
5681    // trailing blobs.  Return it.
5682    if ("undefined" === typeof blob) {
5683        this.queue = [];
5684        this.current.mystack = [];
5685        this.current.mystack.push(this.queue);
5686        if (state.tmp.suppress_decorations) {
5687            ret = state.output.renderBlobs(ret, undefined, false);
5688        }
5689    } else if ("boolean" === typeof blob) {
5690        ret = state.output.renderBlobs(ret, undefined, true);
5691    }
5692
5693    if (blob && blob.new_locale) {
5694        state.opt.lang = blob.old_locale;
5695    }
5696    //if (!blob && !state.tmp.just_looking) {
5697    //  print("QUEUE ("+ state.tmp.just_looking +"): "+JSON.stringify(state.output.queue, ["num", "strings", "decorations", "blobs", "prefix", "suffix", "delimiter"], 2));
5698    //}
5699    return ret;
5700};
5701
5702CSL.Output.Queue.prototype.clearlevel = function () {
5703    var blob, pos, len;
5704    blob = this.current.value();
5705    len = blob.blobs.length;
5706    for (pos = 0; pos < len; pos += 1) {
5707        blob.blobs.pop();
5708    }
5709};
5710
5711CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite, parent) {
5712    var state, ret, ret_last_char, use_delim, blob, pos, len, ppos, llen, str, params, txt_esc;
5713    txt_esc = CSL.getSafeEscape(this.state);
5714    if (!delim) {
5715        delim = "";
5716    }
5717    state = this.state;
5718    ret = "";
5719    ret_last_char = [];
5720    use_delim = "";
5721    len = blobs.length;
5722    if (this.state.tmp.area === "citation" && !this.state.tmp.just_looking && len === 1 && typeof blobs[0] === "object" && parent) {
5723        blobs[0].strings.prefix = parent.strings.prefix + blobs[0].strings.prefix;
5724        blobs[0].strings.suffix = blobs[0].strings.suffix + parent.strings.suffix;
5725        blobs[0].decorations = blobs[0].decorations.concat(parent.decorations);
5726        blobs[0].params = parent.params;
5727        return blobs[0];
5728    }
5729    var start = true;
5730    for (pos = 0; pos < len; pos += 1) {
5731        if (blobs[pos].checkNext) {
5732            blobs[pos].checkNext(blobs[pos + 1],start);
5733            start = false;
5734        } else if (blobs[pos+1] && blobs[pos+1].splice_prefix) {
5735            start = false;
5736            //blobs[pos+1].checkNext(blobs[pos + 1],start);
5737        } else {
5738            start = true;
5739        }
5740    }
5741    
5742    // print("LEN="+len+" "+JSON.stringify(blobs, null, 2));
5743    // Fix last non-range join
5744    var doit = true;
5745    for (pos = blobs.length - 1; pos > 0; pos += -1) {
5746        if (blobs[pos].checkLast) {
5747            if (doit && blobs[pos].checkLast(blobs[pos - 1])) {
5748                doit = false;
5749            }
5750        } else {
5751            doit = true;
5752        }
5753    }
5754    len = blobs.length;
5755    for (pos = 0; pos < len; pos += 1) {
5756        blob = blobs[pos];
5757        if (ret) {
5758            use_delim = delim;
5759        }
5760        if ("string" === typeof blob) {
5761            ret += txt_esc(use_delim);
5762            // XXX Blob should be run through flipflop and flattened here.
5763            // (I think it must be a fragment of text around a numeric
5764            // variable)
5765            ret += blob;
5766            if (state.tmp.count_offset_characters) {
5767                //state.tmp.offset_characters += (use_delim.length + blob.length);
5768                state.tmp.offset_characters += (use_delim.length);
5769            }
5770        } else if (in_cite) {
5771            // pass
5772            // Okay, so this does it -- but we're now not able to return a string!
5773            if (ret) {
5774                ret = [ret, blob];
5775            } else {
5776                ret = [blob];
5777            }
5778        } else if (blob.status !== CSL.SUPPRESS) {
5779            if (blob.particle) {
5780                str = blob.particle + blob.num;
5781            } else {
5782                str = blob.formatter.format(blob.num, blob.gender);
5783            }
5784            // Workaround to get a more or less accurate value.
5785            var strlen = str.replace(/<[^>]*>/g, "").length;
5786            // notSerious
5787            this.append(str, "empty", true);
5788            var str_blob = this.pop();
5789            var count_offset_characters = state.tmp.count_offset_characters;
5790            str = this.string(state, [str_blob], false);
5791            state.tmp.count_offset_characters = count_offset_characters;
5792            if (blob.strings["text-case"]) {
5793                str = CSL.Output.Formatters[blob.strings["text-case"]](this.state, str);
5794            }
5795            if (str && this.state.tmp.strip_periods) {
5796                str = str.replace(/\.([^a-z]|$)/g, "$1");
5797            }
5798            if (!state.tmp.suppress_decorations) {
5799                llen = blob.decorations.length;
5800                for (ppos = 0; ppos < llen; ppos += 1) {
5801                    params = blob.decorations[ppos];
5802                    if (state.normalDecorIsOrphan(blob, params)) {
5803                        continue;
5804                    }
5805                    str = state.fun.decorate[params[0]][params[1]].call(blob, state, str, params[2]);
5806                }
5807            }
5808            str = txt_esc(blob.strings.prefix) + str + txt_esc(blob.strings.suffix);
5809            var addme = "";
5810            if (blob.status === CSL.END) {
5811                //print("  CSL.END");
5812                addme = txt_esc(blob.range_prefix);
5813            } else if (blob.status === CSL.SUCCESSOR) {
5814                //print("  CSL.SUCCESSOR");
5815                addme = txt_esc(blob.successor_prefix);
5816            } else if (blob.status === CSL.START) {
5817                //print("  CSL.START");
5818                if (pos > 0 && !blob.suppress_splice_prefix) {
5819                    addme = txt_esc(blob.splice_prefix);
5820                } else {
5821                    addme = "";
5822                }
5823            } else if (blob.status === CSL.SEEN) {
5824                //print("  CSL.SEEN");
5825
5826                // THIS IS NOT THE PROPER FUNCTION OF CSL.SEEN, IS IT?
5827
5828                addme = txt_esc(blob.splice_prefix);
5829            }
5830            ret += addme;
5831            ret += str;
5832            if (state.tmp.count_offset_characters) {
5833                state.tmp.offset_characters += (addme.length + blob.strings.prefix.length + strlen + blob.strings.suffix.length);
5834            }
5835        }
5836    }
5837    return ret;
5838};
5839
5840CSL.Output.Queue.purgeEmptyBlobs = function (parent) {
5841    //print("START1");
5842    if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) {
5843        return;
5844    }
5845    // back-to-front, bottom-first
5846    for (var i=parent.blobs.length-1;i>-1;i--) {
5847        CSL.Output.Queue.purgeEmptyBlobs(parent.blobs[i]);
5848        var child = parent.blobs[i];
5849        if (!child || !child.blobs || !child.blobs.length) {
5850            var buf = [];
5851            while ((parent.blobs.length-1) > i) {
5852                buf.push(parent.blobs.pop());
5853            }
5854            parent.blobs.pop();
5855            while (buf.length) {
5856                parent.blobs.push(buf.pop());
5857            }
5858        }
5859    }
5860    //print("   end");
5861};
5862
5863// Adjustments to be made:
5864//
5865// * Never migrate beyond a @quotes node
5866// * Never migrate into a num node.
5867
5868CSL.Output.Queue.adjust = function (punctInQuote) {
5869
5870    var NO_SWAP_IN = {
5871        ";": true,
5872        ":": true
5873    };
5874
5875    var NO_SWAP_OUT = {
5876        ".": true,
5877        "!": true,
5878        "?": true
5879    };
5880
5881    var LtoR_MAP = {
5882        "!": {
5883            ".": "!",
5884            "?": "!?",
5885            ":": "!",
5886            ",": "!,",
5887            ";": "!;"
5888        },
5889        "?": {
5890            "!": "?!",
5891            ".": "?",
5892            ":": "?",
5893            ",": "?,",
5894            ";": "?;"
5895        },
5896        ".": {
5897            "!": ".!",
5898            "?": ".?",
5899            ":": ".:",
5900            ",": ".,",
5901            ";": ".;"
5902        },
5903        ":": {
5904            "!": "!",
5905            "?": "?",
5906            ".": ":",
5907            ",": ":,",
5908            ";": ":;"
5909        },
5910        ",": {
5911            "!": ",!",
5912            "?": ",?",
5913            ":": ",:",
5914            ".": ",.",
5915            ";": ",;"
5916        },
5917        ";": {
5918            "!": "!",
5919            "?": "?",
5920            ":": ";",
5921            ",": ";,",
5922            ".": ";"
5923        }
5924    };
5925
5926    var SWAP_IN = {};
5927    var SWAP_OUT = {};
5928    var PUNCT = {};
5929    var PUNCT_OR_SPACE = {};
5930    for (var key in LtoR_MAP) {
5931        PUNCT[key] = true;
5932        PUNCT_OR_SPACE[key] = true;
5933        if (!NO_SWAP_IN[key]) {
5934            SWAP_IN[key] = true;
5935        }
5936        if (!NO_SWAP_OUT[key]) {
5937            SWAP_OUT[key] = true;
5938        }
5939    }
5940    PUNCT_OR_SPACE[" "] = true;
5941    PUNCT_OR_SPACE[" "] = true;
5942
5943    var RtoL_MAP = {};
5944    for (var key in LtoR_MAP) {
5945        for (var subkey in LtoR_MAP[key]) {
5946            if (!RtoL_MAP[subkey]) {
5947                RtoL_MAP[subkey] = {};
5948            }
5949            RtoL_MAP[subkey][key] = LtoR_MAP[key][subkey];
5950        }
5951    }
5952
5953    function blobIsNumber(blob) {
5954        return ("number" === typeof blob.num || (blob.blobs && blob.blobs.length === 1 && "number" === typeof blob.blobs[0].num));
5955    }
5956
5957    function blobEndsInNumber(blob) {
5958        if ("number" === typeof blob.num) {
5959            return true;
5960        }
5961        if (!blob.blobs || "object" !==  typeof blob.blobs) {
5962            return false;
5963        }
5964        if (blobEndsInNumber(blob.blobs[blob.blobs.length-1])) {
5965            return true;
5966        }
5967    }
5968    
5969    function blobHasDecorations(blob,includeQuotes) {
5970        var ret = false;
5971        var decorlist = ['@font-style','@font-variant','@font-weight','@text-decoration','@vertical-align'];
5972        if (includeQuotes) {
5973            decorlist.push('@quotes');
5974        }
5975        if (blob.decorations) {
5976            for (var i=0,ilen=blob.decorations.length;i<ilen;i++) {
5977                if (decorlist.indexOf(blob.decorations[i][0]) > -1) {
5978                    ret = true;
5979                    break;
5980                }
5981            }
5982        }
5983        return ret;
5984    }
5985    
5986    function blobHasDescendantQuotes(blob) {
5987        if (blob.decorations) {
5988            for (var i=0,ilen=blob.decorations.length;i<ilen;i++) {
5989                if (blob.decorations[i][0] === '@quotes' && blob.decorations[i][1] !== "false") {
5990                    return true;
5991                }
5992            }
5993        }
5994        if ("object" !== typeof blob.blobs) {
5995            return false;
5996        }
5997        return blobHasDescendantQuotes(blob.blobs[blob.blobs.length-1]);
5998        //if (blobHasDescendantQuotes(blob.blobs[blob.blobs.length-1])) {
5999        //    return true
6000        //};
6001        //return false;
6002    }
6003    
6004    function blobHasDescendantMergingPunctuation(parentChar,blob) {
6005        var childChar = blob.strings.suffix.slice(-1);
6006        if (!childChar && "string" === typeof blob.blobs) {
6007            childChar = blob.blobs.slice(-1);
6008        }
6009        var mergedChars = RtoL_MAP[parentChar][childChar];
6010        if (mergedChars && mergedChars.length === 1) {
6011            return true;
6012        }
6013        if ("object" !== typeof blob.blobs) {
6014            return false;
6015        }
6016        if (blobHasDescendantMergingPunctuation(parentChar,blob.blobs[blob.blobs.length-1])) {
6017            return true;
6018        }
6019        return false;
6020    }
6021    
6022    function matchLastChar(blob, chr) {
6023        if (!PUNCT[chr]) {
6024            return false;
6025        }
6026        if ("string" === typeof blob.blobs) {
6027
6028            if (blob.blobs.slice(-1) === chr) {
6029                return true;
6030            } else {
6031                return false;
6032            }
6033        } else {
6034            var child = blob.blobs[blob.blobs.length-1];
6035            if (child) {
6036                var childChar = child.strings.suffix.slice(-1);
6037                if (!childChar) {
6038                    return matchLastChar(child,chr);
6039                } else if (child.strings.suffix.slice(-1) == chr) {
6040                    return true;
6041                } else {
6042                    return false;
6043                }
6044            } else {
6045                return false;
6046            }
6047        }
6048    }
6049    
6050    function mergeChars (First, first, Second, second, merge_right) {
6051        var FirstStrings = "blobs" === first ? First : First.strings;
6052        var SecondStrings = "blobs" === second ? Second: Second.strings;
6053        var firstChar = FirstStrings[first].slice(-1);
6054        var secondChar = SecondStrings[second].slice(0,1);
6055        function cullRight () {
6056            SecondStrings[second] = SecondStrings[second].slice(1);
6057        }
6058        function cullLeft () {
6059            FirstStrings[first] = FirstStrings[first].slice(0,-1);
6060        }
6061        function addRight (chr) {
6062            SecondStrings[second] = chr + SecondStrings[second];
6063        }
6064        function addLeft (chr) {
6065            FirstStrings[first] += chr;
6066        }
6067        var cull = merge_right ? cullLeft : cullRight;
6068        function matchOnRight () {
6069            return RtoL_MAP[secondChar];
6070        }
6071        function matchOnLeft () {
6072            return LtoR_MAP[firstChar];
6073        }
6074        var match = merge_right ? matchOnLeft : matchOnRight;
6075        function mergeToRight () {
6076            var chr = LtoR_MAP[firstChar][secondChar];
6077            if ("string" === typeof chr) {
6078                cullLeft();
6079                cullRight();
6080                addRight(chr);
6081            } else {
6082                addRight(firstChar);
6083                cullLeft();
6084            }
6085        }
6086        function mergeToLeft () {
6087            var chr = RtoL_MAP[secondChar][firstChar];
6088            if ("string" === typeof chr) {
6089                cullLeft();
6090                cullRight();
6091                addLeft(chr);
6092            } else {
6093                addLeft(secondChar);
6094                cullRight();
6095            }
6096        }
6097        var merge = merge_right ? mergeToRight: mergeToLeft;
6098
6099        var isDuplicate = firstChar === secondChar;
6100        if (isDuplicate) {
6101            cull();
6102        } else {
6103            if (match()) {
6104                merge();
6105            }
6106        }
6107    }
6108
6109    function upward (parent) {
6110        //print("START2");
6111        // Terminus if no blobs
6112        if (parent.blobs && "string" == typeof parent.blobs) {
6113            if (PUNCT[parent.strings.suffix.slice(0,1)]
6114                && parent.strings.suffix.slice(0,1) === parent.blobs.slice(-1)) {
6115
6116                parent.strings.suffix = parent.strings.suffix.slice(1);
6117            }
6118            return;
6119        } else if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) {
6120            return;
6121        }
6122
6123        // back-to-front, bottom-first
6124        var parentDecorations = blobHasDecorations(parent,true);
6125        for (var i=parent.blobs.length-1;i>-1;i--) {
6126            this.upward(parent.blobs[i]);
6127            var parentStrings = parent.strings;
6128            var childStrings = parent.blobs[i].strings;
6129            if (i === 0) {
6130                // Remove leading space on first-position child node prefix if there is a trailing space on the node prefix above 
6131                if (" " === parentStrings.prefix.slice(-1) && " " === childStrings.prefix.slice(0, 1)) {
6132                    childStrings.prefix = childStrings.prefix.slice(1);
6133                }
6134                // Migrate leading punctuation or space on a first-position prefix upward
6135                var childChar = childStrings.prefix.slice(0, 1);
6136                if (!parentDecorations && PUNCT_OR_SPACE[childChar] && !parentStrings.prefix) {
6137                    parentStrings.prefix += childChar;
6138                    childStrings.prefix = childStrings.prefix.slice(1);
6139                }
6140            }
6141            if (i === (parent.blobs.length - 1)) {
6142                // Migrate trailing space ONLY on a last-position suffix upward, controlling for duplicates
6143                var childChar = childStrings.suffix.slice(-1);
6144                // ZZZ Loosened to fix initialized names wrapped in a span and followed by a period
6145                if (!parentDecorations && [" "].indexOf(childChar) > -1) {
6146                    if (parentStrings.suffix.slice(0,1) !== childChar) {
6147                        parentStrings.suffix = childChar + parentStrings.suffix;
6148                    }
6149                    childStrings.suffix = childStrings.suffix.slice(0, -1);
6150                }
6151            }
6152            if (parentStrings.delimiter && i > 0) {
6153                // Remove leading space on mid-position child node prefix if there is a trailing space on delimiter above
6154                if (PUNCT_OR_SPACE[parentStrings.delimiter.slice(-1)]
6155                    && parentStrings.delimiter.slice(-1) === childStrings.prefix.slice(0, 1)) {
6156
6157                    childStrings.prefix = childStrings.prefix.slice(1);
6158                }
6159            }
6160            // Siblings are handled in adjustNearsideSuffixes()
6161        }
6162        //print("   end");
6163    }
6164
6165    function leftward (parent) {
6166        // Terminus if no blobs
6167        if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) {
6168            return;
6169        }
6170
6171        for (var i=parent.blobs.length-1;i>-1;i--) {
6172            this.leftward(parent.blobs[i]);
6173            // This is a delicate one.
6174            //
6175            // Migrate if:
6176            // * there is no umbrella delimiter [ok]
6177            // * neither the child nor its sibling is a number [ok]
6178            // * decorations exist neither on the child nor on the sibling [ok]
6179            // * sibling prefix char is a swapping char [ok]
6180            //
6181            // Suppress without migration if:
6182            // * sibling prefix char matches child suffix char or
6183            // * child suffix is empty and sibling prefix char match last field char
6184            if ((i < parent.blobs.length -1) && !parent.strings.delimiter) {
6185                // If there is a trailing swappable character on a sibling prefix with no intervening delimiter, copy it to suffix,
6186                // controlling for duplicates
6187                var child = parent.blobs[i];
6188                var childChar = child.strings.suffix.slice(-1);
6189                var sibling = parent.blobs[i+1];
6190                var siblingChar = sibling.strings.prefix.slice(0, 1);
6191                var hasDecorations = blobHasDecorations(child) || blobHasDecorations(sibling);
6192                var hasNumber = "number" === typeof childChar || "number" === typeof siblingChar;
6193
6194                if (!hasDecorations && !hasNumber && PUNCT[siblingChar] && !hasNumber) {
6195                    var suffixAndPrefixMatch = siblingChar === child.strings.suffix.slice(-1);
6196                    var suffixAndFieldMatch = (!child.strings.suffix && "string" === typeof child.blobs && child.blobs.slice(-1) === siblingChar);
6197                    if (!suffixAndPrefixMatch && !suffixAndFieldMatch) {
6198                        mergeChars(child, 'suffix', sibling, 'prefix');
6199                        //child.strings.suffix += siblingChar;
6200                    } else {
6201                        sibling.strings.prefix = sibling.strings.prefix.slice(1);
6202                    }
6203                }
6204            }
6205        }
6206    }
6207
6208    function downward (parent) {
6209        //print("START3");
6210        // Terminus if no blobs
6211        if (parent.blobs && "string" == typeof parent.blobs) {
6212            if (PUNCT[parent.strings.suffix.slice(0,1)]
6213                && parent.strings.suffix.slice(0,1) === parent.blobs.slice(-1)) {
6214
6215                parent.strings.suffix = parent.strings.suffix.slice(1);
6216            }
6217            return;
6218        } else if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) {
6219            return;
6220        }
6221        //if (top) {
6222        //    print("JSON "+JSON.stringify(parent, ["strings", "decorations", "blobs", "prefix", "suffix", "delimiter"], 2));
6223        //}
6224
6225        var parentStrings = parent.strings;
6226        // Check for numeric child
6227        var someChildrenAreNumbers = false;
6228        for (var i=0,ilen=parent.blobs.length;i<ilen;i++) {
6229            if (blobIsNumber(parent.blobs[i])) {
6230                someChildrenAreNumbers = true;
6231                break;
6232            }
6233        }
6234        if (true) {
6235            // If there is a leading swappable character on delimiter, copy it to suffixes IFF none of the targets are numbers
6236            if (parentStrings.delimiter && PUNCT[parentStrings.delimiter.slice(0, 1)]) {
6237                var delimChar = parentStrings.delimiter.slice(0, 1);
6238                for (var i=parent.blobs.length-2;i>-1;i--) {
6239                    var childStrings = parent.blobs[i].strings;
6240                    if (childStrings.suffix.slice(-1) !== delimChar) {
6241                        childStrings.suffix += delimChar;
6242                    }
6243                }
6244                parentStrings.delimiter = parentStrings.delimiter.slice(1);
6245            }
6246        }
6247        // back-to-front, top-first
6248        for (var i=parent.blobs.length-1;i>-1;i--) {
6249            var child = parent.blobs[i];
6250            var childStrings = parent.blobs[i].strings;
6251            var childDecorations = blobHasDecorations(child, true);
6252            var childIsNumber = blobIsNumber(child);
6253
6254            if (i === (parent.blobs.length - 1)) {
6255
6256                //if (blobHasDescendantQuotes(child)) {
6257                //    print("JSON "+JSON.stringify(parent, ["strings", "decorations", "blobs", "prefix", "suffix", "delimiter"]));
6258                //}
6259
6260                if (true) {
6261                    // If we have decorations, drill down to see if there are quotes below.
6262                    // If so, we allow migration anyway.
6263                    // Original discussion is here:
6264                    // https://forums.zotero.org/discussion/37091/citeproc-bug-punctuation-in-quotes/
6265                    var parentChar = parentStrings.suffix.slice(0, 1);
6266
6267                    // Hmm.
6268                    // Consider writing out the matching child from blobHasDescendant functions.
6269                    // It should save some cycles, and produce the same result.
6270
6271                    var allowMigration = false;
6272                    if (PUNCT[parentChar]) {
6273                        allowMigration = blobHasDescendantMergingPunctuation(parentChar,child);
6274                        if (!allowMigration && punctInQuote) {
6275                            allowMigration = blobHasDescendantQuotes(child);
6276                        }
6277                    }
6278                    if (allowMigration) {
6279                        if (PUNCT[parentChar]) {
6280                            if (!blobEndsInNumber(child)) {
6281                                if ("string" === typeof child.blobs) {
6282                                    mergeChars(child, 'blobs', parent, 'suffix');
6283                                } else {
6284                                    mergeChars(child, 'suffix', parent, 'suffix');
6285                                }
6286                                if (parentStrings.suffix.slice(0,1) === ".") {
6287                                    childStrings.suffix += parentStrings.suffix.slice(0,1);
6288                                    parentStrings.suffix = parentStrings.suffix.slice(1);
6289                                }
6290                            }
6291                        }
6292                    }
6293                    if (childStrings.suffix.slice(-1) === " " && parentStrings.suffix.slice(0,1) === " ") {
6294                        parentStrings.suffix = parentStrings.suffix.slice(1);
6295                    }
6296                    // More duplicates control
6297                    if (PUNCT_OR_SPACE[childStrings.suffix.slice(0,1)]) {
6298                        if ("string" === typeof child.blobs && child.blobs.slice(-1) === childStrings.suffix.slice(0,1)) {
6299                            // Remove parent punctuation of it duplicates the last character of a field
6300                            childStrings.suffix = childStrings.suffix.slice(1);
6301                        }
6302                        if (childStrings.suffix.slice(-1) === parentStrings.suffix.slice(0, 1)) {
6303                            // Remove duplicate punctuation on child suffix
6304                            parentStrings.suffix = parentStrings.suffix.slice(0, -1);
6305                        }
6306                    }
6307                }
6308                // Squash dupes
6309                if (matchLastChar(parent,parent.strings.suffix.slice(0,1))) {
6310                    parent.strings.suffix = parent.strings.suffix.slice(1);
6311                }
6312            } else if (parentStrings.delimiter) {
6313                // Remove trailing space on mid-position child node suffix if there is a leading space on delimiter above
6314                if (PUNCT_OR_SPACE[parentStrings.delimiter.slice(0,1)]
6315                    && parentStrings.delimiter.slice(0, 1) === childStrings.suffix.slice(-1)) {
6316
6317                    parent.blobs[i].strings.suffix = parent.blobs[i].strings.suffix.slice(0, -1);
6318                    
6319                }
6320            } else {
6321                // Otherwise it's a sibling. We don't care about moving spaces here, just suppress a duplicate
6322                var siblingStrings = parent.blobs[i+1].strings;
6323                if (!blobIsNumber(child) 
6324                    && !childDecorations
6325                    && PUNCT_OR_SPACE[childStrings.suffix.slice(-1)]
6326                    && childStrings.suffix.slice(-1) === siblingStrings.prefix.slice(0, 1)) {
6327
6328                    siblingStrings.prefix = siblingStrings.prefix.slice(1);
6329                }
6330            }
6331            // If field content ends with swappable punctuation, suppress swappable punctuation in style suffix.
6332            if (!childIsNumber && !childDecorations && PUNCT[childStrings.suffix.slice(0,1)]
6333                && "string" === typeof child.blobs) {
6334                
6335                mergeChars(child, 'blobs', child, 'suffix');
6336            }
6337            this.downward(parent.blobs[i]);
6338        }
6339/*
6340        if (top) {
6341
6342            var seen = [];
6343            print(JSON.stringify(parent, function(key, val) {
6344                if (!val || key === 'alldecor') return;
6345                if (typeof val == "object") {
6346                    if (seen.indexOf(val) >= 0)
6347                        return
6348                    seen.push(val)
6349                }
6350                return val
6351            },2));
6352        }
6353*/
6354
6355        //print("  end");
6356    }
6357    // Abstract out a couple of utility functions, used in fix() below.
6358    function swapToTheLeft (child) {
6359        var childChar = child.strings.suffix.slice(0,1);
6360        if ("string" === typeof child.blobs) {
6361            while (SWAP_IN[childChar]) {
6362                mergeChars(child, 'blobs', child, 'suffix');
6363                childChar = child.strings.suffix.slice(0,1);
6364            }                                
6365        } else {
6366            while (SWAP_IN[childChar]) {
6367                mergeChars(child.blobs[child.blobs.length-1], 'suffix', child, 'suffix');
6368                childChar = child.strings.suffix.slice(0,1);
6369            }
6370        }
6371    }
6372    function swapToTheRight (child) {
6373        if ("string" === typeof child.blobs) {
6374            var childChar = child.blobs.slice(-1);
6375            while (SWAP_OUT[childChar]) {
6376                mergeChars(child, 'blobs', child, 'suffix', true);
6377                childChar = child.blobs.slice(-1);
6378            }
6379        } else {
6380            var childChar = child.blobs[child.blobs.length-1].strings.suffix.slice(-1);
6381            while (SWAP_OUT[childChar]) {
6382                mergeChars(child.blobs[child.blobs.length-1], 'suffix', child, 'suffix', true);
6383                childChar = child.blobs[child.blobs.length-1].strings.suffix.slice(-1);
6384            }
6385        }
6386    }
6387
6388    function fix (parent) {
6389        // Terminus if no blobs
6390        if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) {
6391            return;
6392        }
6393        
6394        //print("START4");
6395        // Do the swap, front-to-back, bottom-first
6396        var lastChar;
6397
6398        // XXX Two things to fix with this:
6399        // XXX (1) Stalls after one character
6400        // XXX (2) Moves colon and semicolon, both of which SHOULD stall
6401
6402        for (var i=0,ilen=parent.blobs.length;i<ilen;i++) {
6403            var child = parent.blobs[i];
6404            var quoteSwap = false;
6405            for (var j=0,jlen=child.decorations.length;j<jlen;j++) {
6406                var decoration = child.decorations[j];
6407                if (decoration[0] === "@quotes" && decoration[1] !== "false") {
6408                    quoteSwap = true;
6409                }
6410            }
6411            if (quoteSwap) {
6412                if (punctInQuote) {
6413                    swapToTheLeft(child);
6414                } else {
6415                    swapToTheRight(child);
6416                }
6417            }
6418            lastChar = this.fix(parent.blobs[i]);
6419            if (child.blobs && "string" === typeof child.blobs) {
6420                lastChar = child.blobs.slice(-1);
6421            }
6422        }
6423        return lastChar;
6424    }
6425    this.upward = upward;
6426    this.leftward = leftward;
6427    this.downward = downward;
6428    this.fix = fix;
6429};
6430
6431/*global CSL: true */
6432
6433CSL.Engine.Opt = function () {
6434    this.parallel = {
6435        enable: false,
6436    },
6437    this.has_disambiguate = false;
6438    this.mode = "html";
6439    this.dates = {};
6440    this.jurisdictions_seen = {};
6441    this.suppressedJurisdictions = {};
6442    this.inheritedAttributes = {};
6443    this["locale-sort"] = [];
6444    this["locale-translit"] = [];
6445    this["locale-translat"] = [];
6446    this.citeAffixes = {
6447        persons:{
6448            "locale-orig":{
6449                prefix:"",
6450                suffix:""
6451            },
6452            "locale-translit":{
6453                prefix:"",
6454                suffix:""
6455            },
6456            "locale-translat":{
6457                prefix:"",
6458                suffix:""
6459            }
6460        },
6461        institutions:{
6462            "locale-orig":{
6463                prefix:"",
6464                suffix:""
6465            },
6466            "locale-translit":{
6467                prefix:"",
6468                suffix:""
6469            },
6470            "locale-translat":{
6471                prefix:"",
6472                suffix:""
6473            }
6474        },
6475        titles:{
6476            "locale-orig":{
6477                prefix:"",
6478                suffix:""
6479            },
6480            "locale-translit":{
6481                prefix:"",
6482                suffix:""
6483            },
6484            "locale-translat":{
6485                prefix:"",
6486                suffix:""
6487            }
6488        },
6489        journals:{
6490            "locale-orig":{
6491                prefix:"",
6492                suffix:""
6493            },
6494            "locale-translit":{
6495                prefix:"",
6496                suffix:""
6497            },
6498            "locale-translat":{
6499                prefix:"",
6500                suffix:""
6501            }
6502        },
6503        publishers:{
6504            "locale-orig":{
6505                prefix:"",
6506                suffix:""
6507            },
6508            "locale-translit":{
6509                prefix:"",
6510                suffix:""
6511            },
6512            "locale-translat":{
6513                prefix:"",
6514                suffix:""
6515            }
6516        },
6517        places:{
6518            "locale-orig":{
6519                prefix:"",
6520                suffix:""
6521            },
6522            "locale-translit":{
6523                prefix:"",
6524                suffix:""
6525            },
6526            "locale-translat":{
6527                prefix:"",
6528                suffix:""
6529            }
6530        }
6531    };
6532    this["default-locale"] = [];
6533    this.update_mode = CSL.NONE;
6534    this.bib_mode = CSL.NONE;
6535    this.sort_citations = false;
6536    /*
6537     * Default values.
6538     * The various et-al values are set globally,
6539     * and the appropriate value is set by the names start
6540     * tag at runtime, depending on whether the Item is a
6541     * first or a subsequent reference.
6542     */
6543    this["et-al-min"] = 0;
6544    this["et-al-use-first"] = 1;
6545    this["et-al-use-last"] = false;
6546    this["et-al-subsequent-min"] = false;
6547    this["et-al-subsequent-use-first"] = false;
6548
6549    this["demote-non-dropping-particle"] = "display-and-sort";
6550    // default of true, because none of our consuming
6551    // applications so far store the various prefixes and 
6552    // suffixes we support in separate fields.
6553    this["parse-names"] = true;
6554    // this["auto-vietnamese-names"] = true;
6555
6556    this.citation_number_slug = false;
6557    this.trigraph = "Aaaa00:AaAa00:AaAA00:AAAA00";
6558
6559    this.nodenames = [];
6560
6561    this.gender = {};
6562    this['cite-lang-prefs'] = {
6563        persons:['orig'],
6564        institutions:['orig'],
6565        titles:['orig'],
6566        journals:['orig'],
6567        publishers:['orig'],
6568        places:['orig'],
6569        number:['orig']
6570    };
6571
6572    this.has_layout_locale = false;
6573    this.disable_duplicate_year_suppression = [];
6574    this.use_context_condition = false;
6575
6576    this.jurisdiction_fallbacks = {};
6577
6578    this.development_extensions = {};
6579    this.development_extensions.field_hack = true;
6580    this.development_extensions.allow_field_hack_date_override = true;
6581    this.development_extensions.locator_date_and_revision = true;
6582    this.development_extensions.locator_label_parse = true;
6583    this.development_extensions.raw_date_parsing = true;
6584    this.development_extensions.clean_up_csl_flaws = true;
6585    this.development_extensions.consolidate_legal_items = false;
6586    this.development_extensions.csl_reverse_lookup_support = false;
6587    this.development_extensions.wrap_url_and_doi = false;
6588    this.development_extensions.thin_non_breaking_space_html_hack = false;
6589    this.development_extensions.apply_citation_wrapper = false;
6590    this.development_extensions.main_title_from_short_title = false;
6591    this.development_extensions.uppercase_subtitles = false;
6592    this.development_extensions.normalize_lang_keys_to_lowercase = false;
6593    this.development_extensions.strict_text_case_locales = false;
6594    this.development_extensions.expect_and_symbol_form = false;
6595    this.development_extensions.require_explicit_legal_case_title_short = false;
6596    this.development_extensions.spoof_institutional_affiliations = false;
6597    this.development_extensions.force_jurisdiction = false;
6598    this.development_extensions.parse_names = true;
6599    this.development_extensions.hanging_indent_legacy_number = false;
6600    this.development_extensions.throw_on_empty = false;
6601    this.development_extensions.strict_inputs = true;
6602    this.development_extensions.prioritize_disambiguate_condition = false;
6603    this.development_extensions.force_short_title_casing_alignment = true;
6604    this.development_extensions.implicit_short_title = false;
6605    this.development_extensions.force_title_abbrev_fallback = false;
6606    this.development_extensions.split_container_title = false;
6607    this.development_extensions.legacy_institution_name_ordering = false;
6608    this.development_extensions.etal_min_etal_usefirst_hack = false;
6609};
6610
6611CSL.Engine.Tmp = function () {
6612    //
6613    // scratch variable to display the total
6614    // number of names in all rendered variables
6615    // in a cite.  initialized to zero by the
6616    // citation element, incremented by each
6617    // name variable actually rendered
6618    this.names_max = new CSL.Stack();
6619    this.names_base = new CSL.Stack();
6620    this.givens_base = new CSL.Stack();
6621    //
6622    // this holds the field values collected by the @value
6623    // and @variable attributes, for processing by the
6624    // element functions.
6625    this.value = [];
6626    /**
6627     * Object to hold the decorations declared by a name-part
6628     * element.
6629     */
6630    this.namepart_decorations = {};
6631    /**
6632     * String variable to hold the type of a name-part
6633     * element.
6634     */
6635    this.namepart_type = false;
vendor: 463,898 bytes, lines 6636-18015
6636    //
6637    // scratch variable to flag whether we are processing
6638    // a citation or a bibiliography.  this diverts token and
6639    // configuration to the appropriateo objects inside
6640    // state.  the default is "citation".
6641    this.area = "citation";
6642    this.root = "citation";
6643    this.extension = "";
6644    //
6645    // controls the implicit conditional wrappers applied
6646    // to top-level elements inside a names substitute span.
6647    // false by default, names start tag pushes a new true level,
6648    // names end tag pops it.  Output value check in @variable
6649    // function of attributes.js sets level to false.  closing names
6650    // tag maps a false value to superior level.
6651    this.can_substitute = new CSL.Stack(0, CSL.LITERAL);
6652    //
6653    // notes whether the formatted elements of a date span
6654    // rendered anything.  controls whether literal fallback
6655    // is used.
6656    this.element_rendered_ok = false;
6657    //
6658    // element_trace keeps a record of rendered elements.
6659    // used to implement author-only.
6660    //
6661    this.element_trace = new CSL.Stack("style");
6662    //
6663    // counter for total namesets
6664    this.nameset_counter = 0;
6665    //
6666    /////  this.fun.check_for_output = CSL.check_for_output;
6667    //
6668    // stack flag used for term handling.  Set to true
6669    // if at least one variable has tried to render, and
6670    // no variables had content.
6671    this.group_context = new CSL.Stack({
6672        term_intended: false,
6673        variable_attempt: false,
6674        variable_success: false,
6675        output_tip: undefined,
6676        label_form:  undefined,
6677        parallel_first: undefined,
6678        parallel_last: undefined,
6679        parallel_delimiter_override: undefined,
6680        condition: false,
6681        force_suppress: false,
6682        done_vars: []
6683    });
6684    //
6685    // boolean flag used to control first-letter capitalization
6686    // of terms.  Set to true if any item preceding the term
6687    // being handled has rendered successfully, otherwise
6688    // false.
6689    this.term_predecessor = false;
6690    //
6691    // boolean flag to control use of layout delimiter
6692    // immediately before numbers. This hack is needed for
6693    // some numeric styles.
6694    this.in_cite_predecessor = false;
6695    //
6696    // stack flag used to control jumps in the closing
6697    // token of a conditional.
6698    this.jump = new CSL.Stack(0, CSL.LITERAL);
6699    //
6700    // holds string parameters for group formatting, between
6701    // the start of a group and the closing token.
6702    this.decorations = new CSL.Stack();
6703    //
6704    // token store stack.
6705    this.tokenstore_stack = new CSL.Stack();
6706
6707    // for collapsing
6708    this.last_suffix_used = "";
6709    this.last_names_used = [];
6710    this.last_years_used = [];
6711    this.years_used = [];
6712    this.names_used = [];
6713
6714    this.taintedItemIDs = {};
6715    this.taintedCitationIDs = {};
6716    //
6717    // scratch stack containing initialize-with strings or null values
6718    this.initialize_with = new CSL.Stack();
6719    //
6720    // this is used to set a requested set of
6721    // disambiguation parameters in the output.
6722    // for the array elements, the base array
6723    // (either zero for each nameset, or full-up
6724    // if givens are already used) is set
6725    // during names processing, if no value
6726    // is set in the processor before a rendering
6727    // run.  to simplify things for the calling
6728    // function, these are just bog-standard arrays,
6729    // and can be safely overwritten.
6730    this.disambig_request = false;
6731    //
6732    // scratch variable to toggle an attempt to set a
6733    // name in sort order rather than display
6734    // order.
6735    this["name-as-sort-order"] = false;
6736    //
6737    // suppress decorations (used for generating
6738    // sort keys and disambiguation keys)
6739    this.suppress_decorations = false;
6740    //
6741    // empty settings array, used to report settings used
6742    // if disambig_request is not set at runtime
6743    this.disambig_settings = new CSL.AmbigConfig();
6744    //
6745    // sort key array
6746    this.bib_sort_keys = [];
6747    //
6748    // holds the prefix between the start of a group
6749    // and the closing token.
6750    this.prefix = new CSL.Stack("", CSL.LITERAL);
6751    //
6752    // holds the suffix between the start of a group
6753    // and the closing token.
6754    this.suffix = new CSL.Stack("", CSL.LITERAL);
6755    //
6756    // holds the group delimiter between the start of a group
6757    // and the closing token.
6758    this.delimiter = new CSL.Stack("", CSL.LITERAL);
6759    //
6760    // Used for conditional locale switching.
6761    this.cite_locales = [];
6762    this.cite_affixes = {
6763        citation: false, 
6764        bibliography: false,
6765        citation_sort: false, 
6766        bibliography_sort: false
6767    };
6768    this.strip_periods = 0;
6769    this.shadow_numbers = {};
6770    this.authority_stop_last = 0;
6771    this.loadedItemIDs = {};
6772    //
6773    // Push/pop array for set/unset of opt.lang setting, used
6774    // in if locale="XX" to force terms to language of item.
6775    // @locale tests track their nesting level in a counter,
6776    // and push the current value of state.opt.lang to one array,
6777    // and the counter value to another. On the way back up,
6778    // closing node decrements the counter, compares its value
6779    // with the trailing value on the array, and pops both
6780    // arrays, resetting state.opt.lang to the previous value.
6781    // A hack to solve a surprisingly difficult problem caused
6782    // by the use of an execution stack for the nested structure.
6783    this.condition_counter = 0; //incremented/decremented on ALL conditions
6784    this.condition_lang_val_arr = [];
6785    this.condition_lang_counter_arr = [];
6786};
6787
6788
6789CSL.Engine.Fun = function (state) {
6790    //
6791    // matcher
6792    this.match = new CSL.Util.Match();
6793    //
6794    // utility to get standard suffixes for disambiguation
6795    this.suffixator = new CSL.Util.Suffixator(CSL.SUFFIX_CHARS);
6796    //
6797    // utility to romanize a numeric value
6798    this.romanizer = new CSL.Util.Romanizer();
6799    //
6800    // utility to make an ordinal form of a number
6801    this.ordinalizer = new CSL.Util.Ordinalizer(state);
6802    //
6803    // utility to make the long ordinal form of a number, if possible
6804    this.long_ordinalizer = new CSL.Util.LongOrdinalizer();
6805};
6806
6807
6808CSL.Engine.Build = function () {
6809    // Alternate et-al term
6810    // Holds the localization key of the alternative term
6811    // to be used for et-al in a names environment.  Reduced
6812    // to a term object when the element tag is processed during
6813    // Build.
6814    this["alternate-term"] = false;
6815    //
6816    // flags that we are in the bibliography area.
6817    // used by sort.
6818    this.in_bibliography = false;
6819    //
6820    // scratch variable to alter behaviour when processing
6821    // locale files
6822    this.in_style = false;
6823    //
6824    // used to ignore info
6825    this.skip = false;
6826    //
6827    // the macro ATTRIBUTE stores a macro name on this
6828    // scratch variable anywhere outside the layout area
6829    // during build.  The macro name is picked up when
6830    // the token is encountered inside the layout area,
6831    // either through a direct call, or as part of a nested
6832    // macro expansion, and the macro content is exploded
6833    // into the token list.
6834    this.postponed_macro = false;
6835    //
6836    // used especially for controlling macro expansion
6837    // during Build.
6838    this.layout_flag = false;
6839    //
6840    // (was buffer_name)
6841    // scratch variable to hold the name of a macro
6842    // or a term until its children have been collected.
6843    this.name = false;
6844    this.names_variables = [[]];
6845    this.name_label = [{}];
6846    //
6847    // scratch variable to hold the value of a form
6848    // attribute until other attributes needed for
6849    // processing have been collected.
6850    this.form = false;
6851    this.term = false;
6852    //
6853    // the macros themselves are discarded after Build
6854    this.macro = {};
6855    //
6856    // the macro build stack.  used to raise an error
6857    // when macros would attempt to call themselves.
6858    this.macro_stack = [];
6859    //
6860    // stores the content of an XML text node during processing
6861    this.text = false;
6862    //
6863    // this is a scratch variable for holding an attribute
6864    // value during processing
6865    this.lang = false;
6866    //
6867    // should be able to run uninitialized; may attract some
6868    // cruft this way.
6869    this.area = "citation";
6870    this.root = "citation";
6871    this.extension = "";
6872    //
6873    // controls the application of implicit conditional wrappers
6874    // to top-level elements inside a names substitute span.
6875    // zero by default, build of names tag pushes a
6876    // new level with value 1.  group start tag increments by 1,
6877    // group end tag decrements by 1.  conditional wrappers are
6878    // only applied if value is exactly 1.
6879    this.substitute_level = new CSL.Stack(0, CSL.LITERAL);
6880    this.names_level = 0;
6881    this.render_nesting_level = 0;
6882    this.render_seen = false;
6883    this.bibliography_key_pos = 0;
6884};
6885
6886
6887CSL.Engine.Configure = function () {
6888    //
6889    // the fail and succeed arrays are used for stack
6890    // processing during configure.
6891    this.tests = [];
6892    this.fail = [];
6893    this.succeed = [];
6894};
6895
6896
6897CSL.Engine.Citation = function (state) {
6898     // Citation options area.
6899     // Holds a mixture of persistent and ephemeral
6900     // options and scratch data used during processing of
6901     // a citation.</p>
6902    this.opt = {
6903        inheritedAttributes: {}
6904    };
6905
6906    this.tokens = [];
6907    // Placeholder function
6908    this.srt = new CSL.Registry.Comparifier(state, "citation_sort");
6909    //
6910    // configuration array to hold the collapse
6911    // options, if any.
6912    this.opt.collapse = [];
6913    //
6914    // disambiguate options
6915    this.opt["disambiguate-add-names"] = false;
6916    this.opt["disambiguate-add-givenname"] = false;
6917    this.opt["disambiguate-add-year-suffix"] = false;
6918    this.opt["givenname-disambiguation-rule"] = "by-cite";
6919    this.opt["near-note-distance"] = 5;
6920
6921    this.opt.topdecor = [];
6922    this.opt.layout_decorations = [];
6923    this.opt.layout_prefix = "";
6924    this.opt.layout_suffix = "";
6925    this.opt.layout_delimiter = "";
6926    //
6927    // sorting
6928    this.opt.sort_locales = [];
6929    this.opt.max_number_of_names = 0;
6930    this.root = "citation";
6931};
6932
6933
6934CSL.Engine.Bibliography = function () {
6935    this.opt = {
6936        inheritedAttributes: {}
6937    };
6938    this.tokens = [];
6939
6940    this.opt.collapse = [];
6941
6942    this.opt.topdecor = [];
6943    this.opt.layout_decorations = [];
6944    this.opt.layout_prefix = "";
6945    this.opt.layout_suffix = "";
6946    this.opt.layout_delimiter = "";
6947    this.opt["line-spacing"] = 1;
6948    this.opt["entry-spacing"] = 1;
6949    //
6950    // sorting
6951    this.opt.sort_locales = [];
6952    this.opt.max_number_of_names = 0;
6953    this.root = "bibliography";
6954};
6955
6956
6957CSL.Engine.BibliographySort = function () {
6958    this.tokens = [];
6959    this.opt = {};
6960    this.opt.sort_directions = [];
6961    this.opt.topdecor = [];
6962    // Holds the final citation-number sort direction, for use
6963    // in applying numbers in cs:citation and cs:bibliography.
6964    // Value is exclusively controlled by cs:key in bibliography_sort
6965    this.opt.citation_number_sort_direction = CSL.ASCENDING;
6966    this.opt.citation_number_secondary = false;
6967    this.tmp = {};
6968    this.keys = [];
6969    this.root = "bibliography";
6970};
6971
6972
6973CSL.Engine.CitationSort = function () {
6974    this.tokens = [];
6975    this.opt = {};
6976    this.opt.sort_directions = [];
6977    this.keys = [];
6978    this.opt.topdecor = [];
6979    this.root = "citation";
6980};
6981
6982CSL.Engine.InText = function () {
6983     // InText options area.
6984     // Holds a mixture of persistent and ephemeral
6985     // options and scratch data used during processing of
6986     // a citation.</p>
6987    this.opt = {
6988        inheritedAttributes: {}
6989    };
6990
6991    this.tokens = [];
6992    // Placeholder function
6993    //this.srt = new CSL.Registry.Comparifier(state, "citation_sort");
6994    //
6995    // configuration array to hold the collapse
6996    // options, if any.
6997    this.opt.collapse = [];
6998    //
6999    // disambiguate options
7000    this.opt["disambiguate-add-names"] = false;
7001    this.opt["disambiguate-add-givenname"] = false;
7002    this.opt["disambiguate-add-year-suffix"] = false;
7003    this.opt["givenname-disambiguation-rule"] = "by-cite";
7004    this.opt["near-note-distance"] = 5;
7005
7006    this.opt.topdecor = [];
7007    this.opt.layout_decorations = [];
7008    this.opt.layout_prefix = "";
7009    this.opt.layout_suffix = "";
7010    this.opt.layout_delimiter = "";
7011    //
7012    // sorting
7013    this.opt.sort_locales = [];
7014    this.opt.max_number_of_names = 0;
7015    this.root = "intext";
7016};
7017
7018/*global CSL: true */
7019
7020CSL.Engine.prototype.previewCitationCluster = function (citation, citationsPre, citationsPost, newMode) {
7021    // Generate output for a hypothetical citation at the current position,
7022    // Leave the registry in the same state in which it was found.
7023    //print("################### previewCitationCluster() #################");
7024    var oldMode = this.opt.mode;
7025    this.setOutputFormat(newMode);
7026    // Avoids generating unwanted ibids, if the citationID already exists in document
7027	if (citation.citationID) {
7028		delete citation.citationID;
7029	}
7030    var ret = this.processCitationCluster(citation, citationsPre, citationsPost, CSL.PREVIEW);
7031
7032    this.setOutputFormat(oldMode);
7033    return ret[1];
7034};
7035
7036CSL.Engine.prototype.appendCitationCluster = function (citation) {
7037    var citationsPre = [];
7038    var len = this.registry.citationreg.citationByIndex.length;
7039    for (var pos = 0; pos < len; pos += 1) {
7040        var c = this.registry.citationreg.citationByIndex[pos];
7041        citationsPre.push(["" + c.citationID, c.properties.noteIndex]);
7042    }
7043    // Drop the data segment to return a list of pos/string pairs.
7044    return this.processCitationCluster(citation, citationsPre, [])[1];
7045};
7046
7047
7048CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, citationsPost, flag) {
7049    var c, preCitation, postCitation, i, ilen, j, jlen, k, klen, n, nlen, key, Item, item, noteCitations, textCitations, m, citationsInNote;
7050    this.debug = false;
7051    this.tmp.loadedItemIDs = {};
7052
7053    // Revert citation dereference from 2ffc4664ae
7054    //citation = JSON.parse(JSON.stringify(citation));
7055    
7056    //print("################### processCitationCluster() #################");
7057    this.tmp.citation_errors = [];
7058    this.registry.return_data = {"bibchange": false};
7059
7060    // make sure this citation has a unique ID, and register it in citationById.
7061    this.setCitationId(citation);
7062
7063    var oldCitationList;
7064    var oldItemList;
7065    var oldAmbigs;
7066    if (flag === CSL.PREVIEW) {
7067        //SNIP-START
7068        if (this.debug) {
7069            CSL.debug("****** start state save *********");
7070        }
7071        //SNIP-END
7072        //
7073        // Simplify.
7074
7075        // Take a slice of existing citations.
7076        oldCitationList = this.registry.citationreg.citationByIndex.slice();
7077
7078        // Take a slice of current items, for later use with update.
7079        oldItemList = this.registry.reflist.slice();
7080
7081        // Make a list of preview citation ref objects. Omit the current
7082        // citation, because it will not exist in registry if: (a) this is
7083        // a new citation; or (b) the calling application is assigning
7084        // new citationIDs for every transaction.
7085        var newCitationList = citationsPre.concat(citationsPost);
7086
7087        // Make a full list of desired ids, for use in preview update,
7088        // and a hash list of same while we're at it.
7089        // First step through known citations, then step through
7090        // the items in the citation for preview.
7091        var newItemIds = {};
7092        var newItemIdsList = [];
7093        for (var i = 0, ilen = newCitationList.length; i < ilen; i += 1) {
7094            c = this.registry.citationreg.citationById[newCitationList[i][0]];
7095            for (j = 0, jlen = c.citationItems.length; j < jlen; j += 1) {
7096                newItemIds[c.citationItems[j].id] = true;
7097                newItemIdsList.push("" + c.citationItems[j].id);
7098            }
7099        }
7100        for (j = 0, jlen = citation.citationItems.length; j < jlen; j += 1) {
7101            newItemIds[citation.citationItems[j].id] = true;
7102            newItemIdsList.push("" + citation.citationItems[j].id);
7103        }
7104
7105        // Clone and save off disambigs of items that will be lost.
7106        oldAmbigs = {};
7107        for (var i = 0, ilen = oldItemList.length; i < ilen; i += 1) {
7108            if (!newItemIds[oldItemList[i].id]) {
7109                var oldAkey = this.registry.registry[oldItemList[i].id].ambig;
7110                var ids = this.registry.ambigcites[oldAkey];
7111                if (ids) {
7112                    for (j = 0, jlen = ids.length; j < jlen; j += 1) {
7113                        oldAmbigs[ids[j]] = CSL.cloneAmbigConfig(this.registry.registry[ids[j]].disambig);
7114                    }
7115                }
7116            }
7117        }
7118
7119        // Update items.  This will produce the base name data and sort things.
7120        // Possibly unnecessary?
7121        //this.updateItems(this.registry.mylist.concat(tmpItems));
7122
7123        //SNIP-START
7124        if (this.debug) {
7125            CSL.debug("****** end state save *********");
7126        }
7127        //SNIP-END
7128    }
7129
7130    this.tmp.taintedCitationIDs = {};
7131    var sortedItems = [];
7132
7133    // Styles that use note backreferencing with a by-cite
7134    // givenname disambiguation rule include the note number
7135    // in the cite for disambiguation purposes. Correct resolution
7136    // of disambiguate="true" conditions on first-reference cites 
7137    // in certain editing scenarios (e.g. where a cite is moved across
7138    // notes) requires that disambiguation be rerun on cites
7139    // affected by the edit.
7140    var rerunAkeys = {};
7141
7142    // retrieve item data and compose items for use in rendering
7143    // attach pointer to item data to shared copy for good measure
7144    for (var i = 0, ilen = citation.citationItems.length; i < ilen; i += 1) {
7145        // Protect against caller-side overwrites to locator strings etc
7146        item = {};
7147        for (var key in citation.citationItems[i]) {
7148            item[key] = citation.citationItems[i][key];
7149        }
7150        Item = this.retrieveItem("" + item.id);
7151        if (Item.id) {
7152            this.transform.loadAbbreviation("default", "hereinafter", Item.id, Item.language);
7153        }
7154        item = CSL.parseLocator.call(this, item);
7155        if (this.opt.development_extensions.consolidate_legal_items) {
7156            this.remapSectionVariable([[Item,item]]);
7157        }
7158        if (this.opt.development_extensions.locator_label_parse) {
7159            if (item.locator && ["bill","gazette","legislation","regulation","treaty"].indexOf(Item.type) === -1 && (!item.label || item.label === 'page')) {
7160                var m = CSL.LOCATOR_LABELS_REGEXP.exec(item.locator);
7161                if (m) {
7162                    var tryLabel = CSL.LOCATOR_LABELS_MAP[m[2]];
7163                    if (this.getTerm(tryLabel)) {
7164                        item.label = tryLabel;
7165                        item.locator = m[3];
7166                    }
7167                }
7168            }
7169        }
7170        var newitem = [Item, item];
7171        sortedItems.push(newitem);
7172        citation.citationItems[i].item = Item;
7173    }
7174
7175    // ZZZ sort stuff moved from here.
7176
7177    // attach the sorted list to the citation item
7178    citation.sortedItems = sortedItems;
7179
7180    // build reconstituted citations list in current document order
7181    var citationByIndex = [];
7182    var citationById = {};
7183    var lastNotePos;
7184    for (i=0, ilen=citationsPre.length; i<ilen; i += 1) {
7185        preCitation = citationsPre[i];
7186        if (this.opt.development_extensions.strict_inputs) {
7187            if (citationById[preCitation[0]]) {
7188                CSL.error("Previously referenced citationID " + preCitation[0] + " encountered in citationsPre");
7189            }
7190            if (preCitation[1]) {
7191                if (lastNotePos > preCitation[1]) {
7192                    CSL.debug("Note index sequence is not sane at citationsPre[" + i + "]");
7193                }
7194                lastNotePos = preCitation[1];
7195            }
7196        }
7197        this.registry.citationreg.citationById[preCitation[0]].properties.noteIndex = preCitation[1];
7198        citationByIndex.push(this.registry.citationreg.citationById[preCitation[0]]);
7199        citationById[preCitation[0]] = this.registry.citationreg.citationById[preCitation[0]];
7200    }
7201    if (!citation.properties) {
7202        citation.properties = {
7203            noteIndex: 0
7204        };
7205    }
7206    if (this.opt.development_extensions.strict_inputs) {
7207        if (citationById[citation.citationID]) {
7208            CSL.error("Citation with previously referenced citationID " + citation.citationID);
7209        }
7210        if (citation.properties.noteIndex) {
7211            if (lastNotePos > citation.properties.noteIndex) {
7212                CSL.debug("Note index sequence is not sane for citation " + citation.citationID);
7213            }
7214            lastNotePos = citation.properties.noteIndex;
7215        }
7216    }
7217    citationByIndex.push(citation);
7218    citationById[citation.citationID] = citation;
7219    for (i=0, ilen=citationsPost.length; i<ilen; i += 1) {
7220        postCitation = citationsPost[i];
7221        if (this.opt.development_extensions.strict_inputs) {
7222            if (citationById[postCitation[0]]) {
7223                CSL.error("Previously referenced citationID " + postCitation[0] + " encountered in citationsPost");
7224            }
7225            if (postCitation[1]) {
7226                if (lastNotePos > postCitation[1]) {
7227                    CSL.debug("Note index sequence is not sane at postCitation[" + i + "]");
7228                }
7229                lastNotePos = postCitation[1];
7230            }
7231        }
7232        this.registry.citationreg.citationById[postCitation[0]].properties.noteIndex = postCitation[1];
7233        citationByIndex.push(this.registry.citationreg.citationById[postCitation[0]]);
7234        citationById[postCitation[0]] = this.registry.citationreg.citationById[postCitation[0]];
7235    }
7236    this.registry.citationreg.citationByIndex = citationByIndex;
7237    this.registry.citationreg.citationById = citationById;
7238
7239    //
7240    // The processor provides three facilities to support
7241    // updates following position reevaluation.
7242    //
7243    // (1) The updateItems() function reports tainted ItemIDs
7244    // to state.tmp.taintedItemIDs.
7245    //
7246    // (2) The processor memos the type of style referencing as
7247    // CSL.NONE, CSL.NUMERIC or CSL.POSITION in state.opt.update_mode.
7248    //
7249    // XXXX: NO LONGER
7250    // (3) For citations containing cites with backreference note numbers,
7251    // a string image of the rendered citation is held in
7252    // citation.properties.backref_citation, and a list of
7253    // ItemIDs to be used to update the backreference note numbers
7254    // is memoed at citation.properties.backref_index.  When such
7255    // citations change position, they can be updated with a
7256    // series of simple find and replace operations, without
7257    // need for rerendering.
7258    //
7259
7260    //
7261    // Position evaluation!
7262    //
7263    // set positions in reconstituted list, noting taints
7264    this.registry.citationreg.citationsByItemId = {};
7265    if (this.opt.update_mode === CSL.POSITION) {
7266        textCitations = [];
7267        noteCitations = [];
7268        citationsInNote = {};
7269    }
7270    var update_items = [];
7271    for (var i = 0, ilen = citationByIndex.length; i < ilen; i += 1) {
7272        citationByIndex[i].properties.index = i;
7273        for (j = 0, jlen = citationByIndex[i].sortedItems.length; j < jlen; j += 1) {
7274            item = citationByIndex[i].sortedItems[j];
7275            if (!this.registry.citationreg.citationsByItemId[item[1].id]) {
7276                this.registry.citationreg.citationsByItemId[item[1].id] = [];
7277                update_items.push("" + item[1].id);
7278            }
7279            if (this.registry.citationreg.citationsByItemId[item[1].id].indexOf(citationByIndex[i]) === -1) {
7280                this.registry.citationreg.citationsByItemId[item[1].id].push(citationByIndex[i]);
7281            }
7282        }
7283        if (this.opt.update_mode === CSL.POSITION) {
7284            if (citationByIndex[i].properties.noteIndex) {
7285                noteCitations.push(citationByIndex[i]);
7286            } else {
7287                citationByIndex[i].properties.noteIndex = 0;
7288                textCitations.push(citationByIndex[i]);
7289            }
7290        }
7291    }
7292    //
7293    // update bibliography items here
7294    //
7295    if (flag !== CSL.ASSUME_ALL_ITEMS_REGISTERED) {
7296        //SNIP-START
7297        if (this.debug) {
7298            CSL.debug("****** start update items *********");
7299        }
7300        //SNIP-END
7301        // true signals implicit updateItems (will not rerun sys.retrieveItem())
7302        this.updateItems(update_items, null, null, true);
7303        //SNIP-START
7304        if (this.debug) {
7305            CSL.debug("****** endo update items *********");
7306        }
7307        //SNIP-END
7308    }
7309
7310    if (!this.opt.citation_number_sort && sortedItems && sortedItems.length > 1 && this.citation_sort.tokens.length > 0) {
7311        for (var i = 0, ilen = sortedItems.length; i < ilen; i += 1) {
7312            sortedItems[i][1].sortkeys = CSL.getSortKeys.call(this, sortedItems[i][0], "citation_sort");
7313        }
7314
7315        /* 
7316         * Grouped sort stuff (start)
7317         */
7318
7319        if (this.opt.grouped_sort &&  !citation.properties.unsorted) {
7320            // Insert authorstring as key.
7321            for (var i = 0, ilen = sortedItems.length; i < ilen; i += 1) {
7322                var sortkeys = sortedItems[i][1].sortkeys;
7323                this.tmp.authorstring_request = true;
7324                // Run getAmbiguousCite() with the current disambig
7325                // parameters, and pick up authorstring from the registry.
7326                var mydisambig = this.registry.registry[sortedItems[i][0].id].disambig;
7327                
7328                this.tmp.authorstring_request = true;
7329                CSL.getAmbiguousCite.call(this, sortedItems[i][0], mydisambig);
7330                var authorstring = this.registry.authorstrings[sortedItems[i][0].id];
7331                this.tmp.authorstring_request = false;
7332
7333                sortedItems[i][1].sortkeys = [authorstring].concat(sortkeys);
7334            }
7335
7336            sortedItems.sort(this.citation.srt.compareCompositeKeys);
7337            // Replace authorstring key in items with same (authorstring) with the 
7338            // keystring of first normal key. This forces grouped sorts,
7339            // as discussed here:
7340            // https://github.com/citation-style-language/schema/issues/40
7341            var lastauthor = false;
7342            var thiskey = false;
7343            var thisauthor = false;
7344            for (var i = 0, ilen = sortedItems.length; i < ilen; i += 1) {
7345                if (sortedItems[i][1].sortkeys[0] !== lastauthor) {
7346                    thisauthor = sortedItems[i][1].sortkeys[0];
7347                    thiskey =  sortedItems[i][1].sortkeys[1];
7348                }
7349                sortedItems[i][1].sortkeys[0] = "" + thiskey + i;
7350                lastauthor = thisauthor;
7351            }
7352        }
7353        /*
7354         * Grouped sort stuff (end)
7355         */
7356
7357        if (!citation.properties.unsorted) {
7358            sortedItems.sort(this.citation.srt.compareCompositeKeys);
7359        }
7360    }
7361
7362    // evaluate parallels
7363
7364    if (this.opt.parallel.enable) {
7365        this.parallel.StartCitation(citation.sortedItems);
7366    }
7367
7368    var citations;
7369    if (this.opt.update_mode === CSL.POSITION) {
7370        for (var i = 0; i < 2; i += 1) {
7371            var first_ref = {};
7372            var last_ref = {};
7373            var first_container_ref = {};
7374            citations = [textCitations, noteCitations][i];
7375            for (j = 0, jlen = citations.length; j < jlen; j += 1) {
7376                var onecitation = citations[j];
7377                if (!citations[j].properties.noteIndex) {
7378                    citations[j].properties.noteIndex = 0;
7379                }
7380                citations[j].properties.noteIndex = parseInt(citations[j].properties.noteIndex, 10);
7381                if (j > 0 && onecitation.properties.noteIndex && citations[j - 1].properties.noteIndex > onecitation.properties.noteIndex) {
7382                    citationsInNote = {};
7383                    first_ref = {};
7384                    last_ref = {};
7385                    first_container_ref = {};
7386                }
7387                for (k = 0, klen = onecitation.sortedItems.length; k < klen; k += 1) {
7388                    if (onecitation.sortedItems[k][1].parallel && onecitation.sortedItems[k][1].parallel !== "first") {
7389                        continue;
7390                    }
7391                    if (!citationsInNote[onecitation.properties.noteIndex]) {
7392                        citationsInNote[onecitation.properties.noteIndex] = 1;
7393                    } else {
7394                        citationsInNote[onecitation.properties.noteIndex] += 1;
7395                    }
7396                }
7397                // Set the following:
7398                //
7399                // (1) position as required (as per current Zotero)
7400                // (2) first-reference-note-number as required (on onecitation item)
7401                // (3) near-note as required (on onecitation item, according to
7402                //     state.opt["near-note-distance"] parameter)
7403                // (4) state.registry.citationreg.citationsByItemId.
7404                //
7405                // Any state changes caused by unsetting or resetting should
7406                // trigger a single entry for the citations in
7407                // state.tmp.taintedCitationIDs (can block on presence of
7408                // state.registry.citationreg.citationsByItemId).
7409                //
7410                for (k = 0, klen = citations[j].sortedItems.length; k < klen; k += 1) {
7411                    item = citations[j].sortedItems[k];
7412                    // Okay ...
7413                    // We set up three IDs for use in position evaluation.
7414                    // item_id is the real Item.id
7415                    // first_id is the legislation_id or Item.id (so statutes backref to first in set, chapters to specific chapter)
7416                    // last_id is the legislation_id or container_id (so statute AND chapter distance is from any ref in set)
7417                    // (replaces myid)
7418                    var item_id = item[0].id;
7419                    var first_id = item[0].legislation_id ? item[0].legislation_id : item[0].id;
7420                    var last_id = item[0].legislation_id ? item[0].legislation_id : item[0].container_id ? item[0].container_id : item[0].id;
7421                    var myxloc = item[1]["locator-extra"];
7422                    var mylocator = item[1].locator;
7423                    var mylabel = item[1].label;
7424                    var incitationid;
7425                    var incitationxloc;
7426                    if (k > 0) {
7427                        // incitationid is only reached in the else branch
7428                        // following "undefined" === typeof first_ref[myid]
7429                        // below
7430                        if (onecitation.sortedItems[k - 1][0].legislation_id) {
7431                            incitationid = onecitation.sortedItems[k - 1][0].legislation_id;
7432                        } else {
7433                            incitationid = onecitation.sortedItems[k - 1][1].id;
7434                            incitationxloc = onecitation.sortedItems[k - 1][1]["locator-extra"];
7435                            //if (onecitation.sortedItems[k-1][1].parallel === "last") {
7436                                for (var l=k-2; l>-1; l--) {
7437                                    if (onecitation.sortedItems[l][1].parallel === "first") {
7438                                        incitationid = onecitation.sortedItems[l][1].id;
7439                                        incitationxloc = onecitation.sortedItems[l][1]["locator-extra"];
7440                                    }
7441                                }
7442                            //}
7443                        }
7444                    }
7445                    // Don't touch item data of other cites when previewing
7446                    if (flag === CSL.PREVIEW) {
7447                        if (onecitation.citationID != citation.citationID) {
7448                            if ("undefined" === typeof first_ref[item[1].id]) {
7449                                first_ref[first_id] = onecitation.properties.noteIndex;
7450                                last_ref[last_id] = onecitation.properties.noteIndex;
7451                            } else {
7452                                last_ref[last_id] = onecitation.properties.noteIndex;
7453                            }
7454                            continue;
7455                        }
7456                    }
7457                    var oldvalue = {};
7458                    oldvalue.position = item[1].position;
7459                    oldvalue["first-reference-note-number"] = item[1]["first-reference-note-number"];
7460                    oldvalue["first-container-reference-note-number"] = item[1]["first-container-reference-note-number"];
7461                    oldvalue["near-note"] = item[1]["near-note"];
7462                    item[1]["first-reference-note-number"] = 0;
7463                    item[1]["first-container-reference-note-number"] = 0;
7464                    item[1]["near-note"] = false;
7465                    if (this.registry.citationreg.citationsByItemId[item_id]) {
7466                        if (this.opt.xclass === 'note' && this.opt.has_disambiguate) {
7467                            var oldCount = this.registry.registry[item[0].id]["citation-count"];
7468                            var newCount = this.registry.citationreg.citationsByItemId[item_id].length;
7469                            this.registry.registry[item[0].id]["citation-count"] = this.registry.citationreg.citationsByItemId[item_id].length;
7470                            if ("number" === typeof oldCount) {
7471                                var oldCountCheck = (oldCount < 2);
7472                                var newCountCheck = (newCount < 2);
7473                                if (oldCountCheck !== newCountCheck) {
7474                                    for (var l=0,llen=this.registry.citationreg.citationsByItemId[item_id].length;l<llen;l++) {
7475                                        rerunAkeys[this.registry.registry[item[0].id].ambig] = true;
7476                                        this.tmp.taintedCitationIDs[this.registry.citationreg.citationsByItemId[item_id][l].citationID] = true;
7477                                    }
7478                                }
7479                            } else {
7480                                for (var l=0,llen=this.registry.citationreg.citationsByItemId[item_id].length;l<llen;l++) {
7481                                    rerunAkeys[this.registry.registry[item[0].id].ambig] = true;
7482                                    this.tmp.taintedCitationIDs[this.registry.citationreg.citationsByItemId[item_id][l].citationID] = true;
7483                                }
7484                            }
7485                        }
7486                    }
7487                    var oldlastid;
7488                    var oldlastxloc;
7489                    
7490                    // Okay, chill.
7491                    // The first test needs to be for presence of last_ref[last_id]. Everything
7492                    // after in subsequent evaluation depends on that.
7493
7494                    // HOWEVER, despite starting with this test, we need to catch every member
7495                    // of the set, and set its first-container-reference-note-number to point at the
7496                    // first.
7497
7498                    // ALSO, despite starting with this test, we need to set first-reference-note-number
7499                    // on every item.
7500                    
7501                    // So ... we run an independent test on first_ref[first_id]], and let this ride.
7502                    
7503                    if ("undefined" === typeof last_ref[last_id] && onecitation.properties.mode !== "author-only") {
7504                        first_ref[first_id] = onecitation.properties.noteIndex;
7505                        last_ref[last_id] = onecitation.properties.noteIndex;
7506                        first_container_ref[last_id] = onecitation.properties.noteIndex;
7507                        item[1].position = CSL.POSITION_FIRST;
7508                    } else {
7509                        //
7510                        // backward-looking position evaluation happens here.
7511                        //
7512                        //
7513                        //
7514                        var ibidme = false;
7515                        var suprame = false;
7516                        var prevCitation = null;
7517                        if (j > 0) {
7518                            var prevCitation = citations[j-1];
7519                        }
7520                        var thisCitation = citations[j];
7521                        // XXX Ugly, but This is used in the second else-if branch condition below.
7522                        if (j > 0) {
7523                            var old_last_id_offset = 1;
7524                            if (prevCitation.properties.mode === "author-only" && j > 1) {
7525                                old_last_id_offset = 2;
7526                            }
7527                            var adjusted_offset = (j - old_last_id_offset);
7528                            if (citations[adjusted_offset].sortedItems.length) {
7529                                oldlastid =  citations[adjusted_offset].sortedItems.slice(-1)[0][1].id;
7530                                oldlastxloc =  citations[j - old_last_id_offset].sortedItems.slice(-1)[0][1]["locator-extra"];
7531                            }
7532                            if (prevCitation.sortedItems.length) {
7533                                if (prevCitation.sortedItems[0].slice(-1)[0].legislation_id) {
7534                                    oldlastid = prevCitation.sortedItems[0].slice(-1)[0].legislation_id;
7535                                }
7536                            }
7537                        }
7538                        if (j > 0 && k === 0 && prevCitation.properties.noteIndex !== thisCitation.properties.noteIndex) {
7539                            // Case 1: source in previous onecitation
7540                            // (1) Threshold conditions
7541                            //     (a) there must be a previous onecitation with one item
7542                            //     (b) this item must be the first in this onecitation
7543                            //     (c) the previous onecitation must contain a reference
7544                            //         to the same item ...
7545                            //     (d) the note numbers must be the same or consecutive.
7546                            // (this has some jiggery-pokery in it for parallels)
7547                            var useme = false;
7548                            // XXX Can oldid be equated with oldlastid, I wonder ...
7549                            var oldid = prevCitation.sortedItems[0][0].id;
7550                            if (prevCitation.sortedItems[0][0].legislation_id) {
7551                                oldid = prevCitation.sortedItems[0][0].legislation_id;
7552                            }
7553                            if ((oldid  == first_id && prevCitation.properties.noteIndex >= (thisCitation.properties.noteIndex - 1))) {
7554                                var prevxloc = prevCitation.sortedItems[0][1]["locator-extra"];
7555                                var thisxloc = thisCitation.sortedItems[0][1]["locator-extra"];
7556                                if ((citationsInNote[prevCitation.properties.noteIndex] === 1 || prevCitation.properties.noteIndex === 0) && prevxloc === thisxloc) {
7557                                    useme = true;
7558                                }
7559                            }
7560                            if (useme) {
7561                                ibidme = true;
7562                            } else {
7563                                suprame = true;
7564                            }
7565                        } else if (k > 0 && incitationid == first_id && incitationxloc == myxloc) {
7566                            // Case 2: immediately preceding source in this onecitation
7567                            // (1) Threshold conditions
7568                            //     (a) there must be an imediately preceding reference to  the
7569                            //         same item in this onecitation; and
7570                            ibidme = true;
7571                        } else if (k === 0 && j > 0 && prevCitation.properties.noteIndex == thisCitation.properties.noteIndex
7572                                   && prevCitation.sortedItems.length 
7573                                   && oldlastid == first_id && oldlastxloc == myxloc) {
7574                            // ... in case there are separate citations in the same note ...
7575                            // Case 2 [take 2]: immediately preceding source in this onecitation
7576                            // (1) Threshold conditions
7577                            //     (a) there must be an imediately preceding reference to  the
7578                            //         same item in this onecitation; and
7579                            ibidme = true;
7580                        } else {
7581                            // everything else is definitely subsequent
7582                            suprame = true;
7583                        }
7584                        // conditions
7585                        var prev, prev_locator, prev_label, curr_locator, curr_label;
7586                        if (ibidme) {
7587                            if (k > 0) {
7588                                prev = onecitation.sortedItems[(k - 1)][1];
7589                            } else {
7590                                prev = citations[(j - 1)].sortedItems[0][1];
7591                            }
7592                            if (prev.locator) {
7593                                if (prev.label) {
7594                                    prev_label = prev.label;
7595                                } else {
7596                                    prev_label = "";
7597                                }
7598                                prev_locator = "" + prev.locator + prev_label;
7599                            } else {
7600                                prev_locator = prev.locator;
7601                            }
7602                            if (mylocator) {
7603                                if (mylabel) {
7604                                    curr_label = mylabel;
7605                                } else {
7606                                    curr_label = "";
7607                                }
7608                                curr_locator = "" + mylocator + curr_label;
7609                            } else {
7610                                curr_locator = mylocator;
7611                            }
7612                        }
7613                        // triage
7614                        if (ibidme && prev_locator && !curr_locator) {
7615                            ibidme = false;
7616                            suprame = true;
7617
7618                        }
7619                        if (ibidme) {
7620                            if (!prev_locator && curr_locator) {
7621                                //     (a) if the previous onecitation had no locator
7622                                //         and this onecitation has one, use ibid+pages
7623                                item[1].position = CSL.POSITION_IBID_WITH_LOCATOR;
7624                            } else if (!prev_locator && !curr_locator) {
7625                                //     (b) if the previous onecitation had no locator
7626                                //         and this onecitation also has none, use ibid
7627                                item[1].position = CSL.POSITION_IBID;
7628                                //print("setting ibid in cmd_cite()");
7629                            } else if (prev_locator && curr_locator === prev_locator) {
7630                                //     (c) if the previous onecitation had a locator
7631                                //         (page number, etc.) and this onecitation has
7632                                //         a locator that is identical, use ibid
7633
7634                                item[1].position = CSL.POSITION_IBID;
7635                                //print("setting ibid in cmd_cite() [2]");
7636                            } else if (prev_locator && curr_locator && curr_locator !== prev_locator) {
7637                                //     (d) if the previous onecitation had a locator,
7638                                //         and this onecitation has one that differs,
7639                                //         use ibid+pages
7640                                item[1].position = CSL.POSITION_IBID_WITH_LOCATOR;
7641                            } else {
7642                                //     (e) if the previous onecitation had a locator
7643                                //         and this onecitation has none, use subsequent
7644                                //
7645                                //     ... and everything else would be subsequent also
7646                                ibidme = false; // just to be clear
7647                                suprame = true;
7648                            }
7649                        }
7650                        if (suprame) {
7651                            item[1].position = CSL.POSITION_CONTAINER_SUBSEQUENT;
7652                            if ("undefined" === typeof first_ref[first_id]) {
7653                                first_ref[first_id] = onecitation.properties.noteIndex;
7654                            } else {
7655                                item[1].position = CSL.POSITION_SUBSEQUENT;
7656                            }
7657                        }
7658                        if (suprame || ibidme) {
7659                            if (onecitation.properties.mode === "author-only") {
7660                                item[1].position = CSL.POSITION_FIRST;
7661                            }
7662                            if (first_container_ref[last_id] != onecitation.properties.noteIndex) {
7663                                item[1]['first-container-reference-note-number'] = first_container_ref[last_id];
7664                                if (this.registry.registry[item[0].id]) {
7665                                    this.registry.registry[item[0].id]['first-container-reference-note-number'] = first_container_ref[last_id];
7666                                }
7667                            }
7668                            if (first_ref[first_id] != onecitation.properties.noteIndex) {
7669                                item[1]["first-reference-note-number"] = first_ref[first_id];
7670                                if (this.registry.registry[item[0].id]) {
7671                                    // This is either the earliest recorded number, or the number of the current citation, whichever is smaller.
7672                                    /*
7673                                    var oldFirst = this.registry.citationreg.citationsByItemId[item_id][0].properties.noteIndex;
7674                                    var newFirst = onecitation.properties.noteIndex;
7675                                    this.registry.registry[item[0].id]['first-reference-note-number'] = newFirst < oldFirst ? newFirst: oldFirst;
7676                                     */
7677                                    // Try this instead?
7678                                    this.registry.registry[item[0].id]['first-reference-note-number'] = first_ref[first_id];
7679                                }
7680                            }
7681                        }
7682                    }
7683                    if (onecitation.properties.noteIndex) {
7684                        var note_distance = parseInt(onecitation.properties.noteIndex, 10) - parseInt(last_ref[last_id], 10);
7685                        if (item[1].position !== CSL.POSITION_FIRST 
7686                            && note_distance <= this.citation.opt["near-note-distance"]) {
7687                            item[1]["near-note"] = true;
7688                        }
7689                        last_ref[last_id] = onecitation.properties.noteIndex;
7690                    } else if (item[1].position !== CSL.POSITION_FIRST) {
7691                        item[1]["near-note"] = true;
7692                    }
7693                    if (onecitation.citationID != citation.citationID) {
7694                        for (n = 0, nlen = CSL.POSITION_TEST_VARS.length; n < nlen; n += 1) {
7695                            var param = CSL.POSITION_TEST_VARS[n];
7696                            if (item[1][param] !== oldvalue[param]) {
7697                                if (this.registry.registry[item[0].id]) {
7698                                    if (param === 'first-reference-note-number') {
7699                                        rerunAkeys[this.registry.registry[item[0].id].ambig] = true;
7700                                        this.tmp.taintedItemIDs[item[0].id] = true;
7701                                    }
7702                                }
7703                                this.tmp.taintedCitationIDs[onecitation.citationID] = true;
7704                            }
7705                        }
7706                    }
7707                    if (this.sys.variableWrapper) {
7708                        item[1].index = onecitation.properties.index;
7709                        item[1].noteIndex = onecitation.properties.noteIndex;
7710                    }
7711                }
7712            }
7713        }
7714    }
7715    if (this.opt.citation_number_sort && sortedItems && sortedItems.length > 1 && this.citation_sort.tokens.length > 0) {
7716        if (!citation.properties.unsorted) {
7717            for (var i = 0, ilen = sortedItems.length; i < ilen; i += 1) {
7718                sortedItems[i][1].sortkeys = CSL.getSortKeys.call(this, sortedItems[i][0], "citation_sort");
7719            }
7720            sortedItems.sort(this.citation.srt.compareCompositeKeys);
7721        }
7722    }
7723    for (var key in this.tmp.taintedItemIDs) {
7724        if (this.tmp.taintedItemIDs.hasOwnProperty(key)) {
7725            citations = this.registry.citationreg.citationsByItemId[key];
7726            // Current citation may be tainted but will not exist
7727            // during previewing.
7728            if (citations) {
7729                for (var i = 0, ilen = citations.length; i < ilen; i += 1) {
7730                    this.tmp.taintedCitationIDs[citations[i].citationID] = true;
7731                }
7732            }
7733        }
7734    }
7735
7736    var ret = [];
7737    if (flag === CSL.PREVIEW) {
7738        // If previewing, return only a rendered string
7739        //SNIP-START
7740        if (this.debug) {
7741            CSL.debug("****** start run processor *********");
7742        }
7743        //SNIP-END
7744        try {
7745            ret = this.process_CitationCluster.call(this, citation.sortedItems, citation);
7746        } catch (e) {
7747            CSL.error("Error running CSL processor for preview: "+e);
7748        }
7749            
7750        //SNIP-START
7751        if (this.debug) {
7752            CSL.debug("****** end run processor *********");
7753            CSL.debug("****** start state restore *********");
7754        }
7755        //SNIP-END
7756        // Wind out anything related to new items added for the preview.
7757        // This means (1) names, (2) disambig state for affected items,
7758        // (3) keys registered in the ambigs pool arrays, and (4) registry
7759        // items.
7760        //
7761
7762        // restore sliced citations
7763        this.registry.citationreg.citationByIndex = oldCitationList;
7764        this.registry.citationreg.citationById = {};
7765        for (var i = 0, ilen = oldCitationList.length; i < ilen; i += 1) {
7766            this.registry.citationreg.citationById[oldCitationList[i].citationID] = oldCitationList[i];
7767        }
7768
7769        //SNIP-START
7770        if (this.debug) {
7771            CSL.debug("****** start final update *********");
7772        }
7773        //SNIP-END
7774        var oldItemIds = [];
7775        for (var i = 0, ilen = oldItemList.length; i < ilen; i += 1) {
7776            oldItemIds.push("" + oldItemList[i].id);
7777        }
7778        this.updateItems(oldItemIds, null, null, true);
7779        //SNIP-START
7780        if (this.debug) {
7781            CSL.debug("****** end final update *********");
7782        }
7783        //SNIP-END
7784        // Roll back disambig states
7785        for (var key in oldAmbigs) {
7786            if (oldAmbigs.hasOwnProperty(key)) {
7787                this.registry.registry[key].disambig = oldAmbigs[key];
7788            }
7789        }
7790        //SNIP-START
7791        if (this.debug) {
7792            CSL.debug("****** end state restore *********");
7793        }
7794        //SNIP-END
7795    } else {
7796        // Rerun cites that have moved across citations or had a change
7797        // in their number of subsequent references, so that disambiguate
7798        // and subsequent-reference-count conditions are applied
7799        // correctly in output.
7800        for (var rerunAkey in rerunAkeys) {
7801            this.disambiguate.run(rerunAkey, citation);
7802        }
7803        // Run taints only if not previewing
7804        //
7805        // Push taints to the return object
7806        //
7807        var obj;
7808        for (var key in this.tmp.taintedCitationIDs) {
7809            if (key == citation.citationID) {
7810                continue;
7811            }
7812            var mycitation = this.registry.citationreg.citationById[key];
7813            if (!mycitation.properties.unsorted) {
7814                for (var i = 0, ilen = mycitation.sortedItems.length; i < ilen; i += 1) {
7815                    mycitation.sortedItems[i][1].sortkeys = CSL.getSortKeys.call(this, mycitation.sortedItems[i][0], "citation_sort");
7816                }
7817                mycitation.sortedItems.sort(this.citation.srt.compareCompositeKeys);
7818            }
7819            // For error reporting
7820            this.tmp.citation_pos = mycitation.properties.index;
7821            this.tmp.citation_note_index = mycitation.properties.noteIndex;
7822            this.tmp.citation_id = "" + mycitation.citationID;
7823            obj = [];
7824            obj.push(mycitation.properties.index);
7825            obj.push(this.process_CitationCluster.call(this, mycitation.sortedItems, mycitation));
7826            obj.push(mycitation.citationID);
7827            ret.push(obj);
7828        }
7829        this.tmp.taintedItemIDs = {};
7830        this.tmp.taintedCitationIDs = {};
7831
7832        // For error reporting again
7833        this.tmp.citation_pos = citation.properties.index;
7834        this.tmp.citation_note_index = citation.properties.noteIndex;
7835        this.tmp.citation_id = "" + citation.citationID;
7836
7837        obj = [];
7838        obj.push(citationsPre.length);
7839        obj.push(this.process_CitationCluster.call(this, sortedItems, citation));
7840        obj.push(citation.citationID);
7841        ret.push(obj);
7842        //
7843        // note for posterity: Rhino and Spidermonkey produce different
7844        // sort results for items with matching keys.  That discrepancy
7845        // turned up a subtle bug in the parallel detection code, trapped
7846        // at line 266, above, and in line 94 of util_parallel.js.
7847        //
7848        ret.sort(function (a, b) {
7849            if (a[0] > b[0]) {
7850                return 1;
7851            } else if (a[0] < b[0]) {
7852                return -1;
7853            } else {
7854                return 0;
7855            }
7856        });
7857        //
7858        // In normal rendering, return is a list of two-part arrays, with the first element
7859        // a citation index number, and the second the text to be inserted.
7860        //
7861    }
7862    this.registry.return_data.citation_errors = this.tmp.citation_errors.slice();
7863    return [this.registry.return_data, ret];
7864};
7865
7866CSL.Engine.prototype.process_CitationCluster = function (sortedItems, citation) {
7867    var str = "";
7868    if (citation && citation.properties && citation.properties.mode === "composite") {
7869        citation.properties.mode = "author-only";
7870        var firstChunk = CSL.getCitationCluster.call(this, sortedItems, citation);
7871        citation.properties.mode = "suppress-author";
7872        var secondChunk = "";
7873        if (citation.properties.infix) {
7874            this.output.append(citation.properties.infix);
7875            secondChunk = this.output.string(this, this.output.queue);
7876            // Had no idea this could return a single-element array! Go figure.
7877            if ("object" === typeof secondChunk) {
7878                secondChunk = secondChunk.join("");
7879            }
7880        }
7881        var thirdChunk = CSL.getCitationCluster.call(this, sortedItems, citation);
7882        citation.properties.mode = "composite";
7883        if (firstChunk && secondChunk && CSL.SWAPPING_PUNCTUATION.concat(["\u2019", "\'"]).indexOf(secondChunk[0]) > -1) {
7884            firstChunk += secondChunk;
7885            secondChunk = false;
7886        }
7887        str = [firstChunk, secondChunk, thirdChunk].filter(function(obj) {
7888            return obj;
7889        }).join(" ");
7890    } else {
7891        str = CSL.getCitationCluster.call(this, sortedItems, citation);
7892    }
7893    return str;
7894};
7895
7896CSL.Engine.prototype.makeCitationCluster = function (rawList) {
7897    var inputList, newitem, str, pos, len, item, Item;
7898    inputList = [];
7899    len = rawList.length;
7900    for (pos = 0; pos < len; pos += 1) {
7901        item = {};
7902        for (var key in rawList[pos]) {
7903            item[key] = rawList[pos][key];
7904        }
7905        Item = this.retrieveItem("" + item.id);
7906        // Code block is copied from processCitationCluster() above
7907        if (this.opt.development_extensions.locator_label_parse) {
7908            if (item.locator && ["bill","gazette","legislation","regulation","treaty"].indexOf(Item.type) === -1 && (!item.label || item.label === 'page')) {
7909                var m = CSL.LOCATOR_LABELS_REGEXP.exec(item.locator);
7910                if (m) {
7911                    var tryLabel = CSL.LOCATOR_LABELS_MAP[m[2]];
7912                    if (this.getTerm(tryLabel)) {
7913                        item.label = tryLabel;
7914                        item.locator = m[3];
7915                    }
7916                }
7917            }
7918        }
7919        if (item.locator) {
7920            item.locator = ("" + item.locator).replace(/\s+$/, '');
7921        }
7922        newitem = [Item, item];
7923        inputList.push(newitem);
7924    }
7925    if (this.opt.development_extensions.consolidate_legal_items) {
7926        this.remapSectionVariable(inputList);
7927    }
7928    if (inputList && inputList.length > 1 && this.citation_sort.tokens.length > 0) {
7929        len = inputList.length;
7930        for (pos = 0; pos < len; pos += 1) {
7931            inputList[pos][1].sortkeys = CSL.getSortKeys.call(this, inputList[pos][0], "citation_sort");
7932        }
7933        inputList.sort(this.citation.srt.compareCompositeKeys);
7934    }
7935    this.tmp.citation_errors = [];
7936    var str = CSL.getCitationCluster.call(this, inputList);
7937    return str;
7938};
7939
7940
7941/**
7942 * Get the undisambiguated version of a cite, without decorations
7943 * <p>This is used internally by the Registry.</p>
7944 *
7945 * [object] CSL Item
7946 * [object] disambiguation parameters
7947 * [boolean] If true, include first-reference-note-number value in cite
7948 */
7949CSL.getAmbiguousCite = function (Item, disambig, visualForm, item) {
7950    var ret;
7951    var flags = this.tmp.group_context.tip;
7952    var oldTermSiblingLayer = {
7953        term_intended: flags.term_intended,
7954        variable_attempt: flags.variable_attempt,
7955        variable_success: flags.variable_success,
7956        output_tip: flags.output_tip,
7957        label_form: flags.label_form,
7958        non_parallel: flags.non_parallel,
7959        parallel_last: flags.parallel_last,
7960        parallel_first: flags.parallel_first,
7961        parallel_last_override: flags.parallel_last_override,
7962        parallel_delimiter_override: flags.parallel_delimiter_override,
7963        parallel_delimiter_override_on_suppress: flags.parallel_delimiter_override_on_suppress,
7964        condition: flags.condition,
7965        force_suppress: flags.force_suppress,
7966        done_vars: flags.done_vars.slice()
7967    };
7968    if (disambig) {
7969        this.tmp.disambig_request = disambig;
7970    } else {
7971        this.tmp.disambig_request = false;
7972    }
7973    var itemSupp = {
7974        position: CSL.POSITION_SUBSEQUENT,
7975        "near-note": true
7976    };
7977
7978    if (item) {
7979        itemSupp.locator = item.locator;
7980        itemSupp.label = item.label;
7981    }
7982
7983    if (this.registry.registry[Item.id] 
7984        && this.registry.citationreg.citationsByItemId
7985        && this.registry.citationreg.citationsByItemId[Item.id]
7986        && this.registry.citationreg.citationsByItemId[Item.id].length 
7987        && visualForm) {
7988        if (this.citation.opt["givenname-disambiguation-rule"] === "by-cite") {
7989            itemSupp['first-reference-note-number'] = this.registry.registry[Item.id]['first-reference-note-number'];
7990        }
7991    }
7992    this.tmp.area = "citation";
7993    this.tmp.root = "citation";
7994    var origSuppressDecorations = this.tmp.suppress_decorations;
7995    this.tmp.suppress_decorations = true;
7996    this.tmp.just_looking = true;
7997
7998    CSL.getCite.call(this, Item, itemSupp, null, false);
7999    // !!!
8000    for (var i=0,ilen=this.output.queue.length;i<ilen;i+=1) {
8001        CSL.Output.Queue.purgeEmptyBlobs(this.output.queue[i]);
8002    }
8003    if (this.opt.development_extensions.clean_up_csl_flaws) {
8004        for (var j=0,jlen=this.output.queue.length;j<jlen;j+=1) {
8005            this.output.adjust.upward(this.output.queue[j]);
8006            this.output.adjust.leftward(this.output.queue[j]);
8007            this.output.adjust.downward(this.output.queue[j]);
8008            this.output.adjust.fix(this.output.queue[j]);
8009        }
8010    }
8011    var ret = this.output.string(this, this.output.queue);
8012    this.tmp.just_looking = false;
8013    this.tmp.suppress_decorations = origSuppressDecorations;
8014    // Cache the result.
8015    this.tmp.group_context.replace(oldTermSiblingLayer);
8016    return ret;
8017};
8018
8019/**
8020 * Return delimiter for use in join
8021 * <p>Splice evaluation is done during cite
8022 * rendering, and this method returns the
8023 * result.  Evaluation requires three items
8024 * of information from the preceding cite, if
8025 * one is present: the names used; the years
8026 * used; and the suffix appended to the
8027 * citation.  These details are copied into
8028 * the state object before processing begins,
8029 * and are cleared by the processor on
8030 * completion of the run.</p>
8031 */
8032
8033CSL.getSpliceDelimiter = function (last_locator, last_collapsed, pos) {
8034    //print(pos +  " after-collapse-delimiter="+this.citation.opt["after-collapse-delimiter"] + "\n  cite_group_delimiter=" + this.tmp.use_cite_group_delimiter + "\n  last_collapsed=" +last_collapsed + "\n  have_collapsed=" +this.tmp.have_collapsed + "\n  last_locator=" + last_locator)
8035    if (undefined !== this.citation.opt["after-collapse-delimiter"]) {
8036        if (last_locator) {
8037            this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
8038        } else if (last_collapsed && !this.tmp.have_collapsed) {
8039            this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
8040        } else if (!last_collapsed && !this.tmp.have_collapsed && this.citation.opt.collapse !== "year-suffix") {
8041            this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
8042        } else {
8043            this.tmp.splice_delimiter = this.citation.opt.layout_delimiter;
8044        }
8045    } else if (this.tmp.use_cite_group_delimiter) {
8046        this.tmp.splice_delimiter = this.citation.opt.cite_group_delimiter;
8047    } else {
8048        if (this.tmp.have_collapsed && this.opt.xclass === "in-text" && this.opt.update_mode !== CSL.NUMERIC) {
8049            this.tmp.splice_delimiter = ", ";
8050        } else if (this.tmp.cite_locales[pos - 1]) {
8051            //
8052            // Must have a value to take effect.  Use zero width space to force empty delimiter.
8053            var alt_affixes = this.tmp.cite_affixes[this.tmp.area][this.tmp.cite_locales[pos - 1]];
8054            if (alt_affixes && alt_affixes.delimiter) {
8055                this.tmp.splice_delimiter = alt_affixes.delimiter;
8056            }
8057        } else if (!this.tmp.splice_delimiter) {
8058            // This happens when no delimiter is set on cs:layout under cs:citation
8059            this.tmp.splice_delimiter = "";
8060        }
8061    }
8062
8063/*
8064    if (last_locator && "string" === typeof this.citation.opt["after-collapse-delimiter"]) {
8065        this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
8066    } else if (last_collapsed && !this.tmp.have_collapsed && "string" === typeof this.citation.opt["after-collapse-delimiter"]) {
8067        this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
8068    } else if (!last_collapsed && !this.tmp.have_collapsed && "string" === typeof this.citation.opt["after-collapse-delimiter"] && !this.citation.opt.collapse === "year-suffix") {
8069        this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
8070    } else if (this.tmp.use_cite_group_delimiter) {
8071        this.tmp.splice_delimiter = this.citation.opt.cite_group_delimiter;
8072    } else if (this.tmp.have_collapsed && this.opt.xclass === "in-text" && this.opt.update_mode !== CSL.NUMERIC) {
8073        this.tmp.splice_delimiter = ", ";
8074    } else if (this.tmp.cite_locales[pos - 1]) {
8075        //
8076        // Must have a value to take effect.  Use zero width space to force empty delimiter.
8077        var alt_affixes = this.tmp.cite_affixes[this.tmp.area][this.tmp.cite_locales[pos - 1]];
8078        if (alt_affixes && alt_affixes.delimiter) {
8079            this.tmp.splice_delimiter = alt_affixes.delimiter;
8080        }
8081    } else if (!this.tmp.splice_delimiter) {
8082        // This happens when no delimiter is set on cs:layout under cs:citation
8083        this.tmp.splice_delimiter = "";
8084    }
8085*/
8086    // Paranoia
8087    //if (!this.tmp.splice_delimiter) {
8088    //    this.tmp.splice_delimiter = "";
8089    //}
8090    return this.tmp.splice_delimiter;
8091};
8092
8093/*
8094 * Compose individual cites into a single string, with
8095 * flexible inter-cite splicing.
8096 */
8097CSL.getCitationCluster = function (inputList, citation) {
8098    var result, objects, myparams, len, pos, item, last_collapsed, params, empties, composite, compie, myblobs, Item, llen, ppos, obj, preceding_item, txt_esc, error_object, citationID, authorOnly, suppressAuthor;
8099    var citation_prefix = "";
8100    this.output.checkNestedBrace = new CSL.checkNestedBrace(this);
8101    if (citation) {
8102        citationID = citation.citationID;
8103        authorOnly = citation.properties.mode === "author-only" ? !!citation.properties.mode : false;
8104        if (this.opt.xclass !== "note") {
8105            suppressAuthor = citation.properties.mode === "suppress-author" ? !!citation.properties.mode : false;
8106        }
8107        if (citation.properties.prefix) {
8108            citation_prefix = CSL.checkPrefixSpaceAppend(this, citation.properties.prefix);
8109        }
8110    }
8111    inputList = inputList ? inputList : [];
8112    this.tmp.last_primary_names_string = false;
8113    txt_esc = CSL.getSafeEscape(this);
8114    this.tmp.area = "citation";
8115    this.tmp.root = "citation";
8116    result = "";
8117    objects = [];
8118    this.tmp.last_suffix_used = "";
8119    this.tmp.last_names_used = [];
8120    this.tmp.last_years_used = [];
8121    this.tmp.backref_index = [];
8122    this.tmp.cite_locales = [];
8123    if (!this.tmp.just_looking) {
8124        this.tmp.abbrev_trimmer = {
8125            QUASHES: {}
8126        };
8127    }
8128
8129    var use_layout_prefix = this.output.checkNestedBrace.update(this.citation.opt.layout_prefix + citation_prefix);
8130    //var use_layout_prefix = this.citation.opt.layout_prefix;
8131
8132    var suppressTrailingPunctuation = false;
8133    if (this.citation.opt.suppressTrailingPunctuation) {
8134        suppressTrailingPunctuation = true;
8135    }
8136    if (citationID) {
8137        //this.registry.citationreg.citationById[citationID].properties.backref_index = false;
8138        //this.registry.citationreg.citationById[citationID].properties.backref_citation = false;
8139        if (this.registry.citationreg.citationById[citationID].properties["suppress-trailing-punctuation"]) {
8140            suppressTrailingPunctuation = true;
8141        }
8142    }
8143
8144    // Adjust locator positions if that looks like a sensible thing to do.
8145    if (this.opt.xclass === "note") {
8146        var parasets = [];
8147        var lastTitle = false;
8148        var lastPosition = false;
8149        var lastID = false;
8150        var lst = [];
8151        for (var i=0, ilen = inputList.length; i < ilen; i += 1) {
8152            var type = inputList[i][0].type;
8153            var title = inputList[i][0].title;
8154            var position = inputList[i][1].position;
8155            var id = inputList[i][0].id;
8156            if (title && type === "legal_case" && id !== lastID && position) {
8157                // Start a fresh sublist if the item title does not match the last one
8158                if (title !== lastTitle || parasets.length === 0) {
8159                    lst = [];
8160                    parasets.push(lst);
8161                }
8162                lst.push(inputList[i][1]);
8163            }
8164            lastTitle = title;
8165            lastPosition = position;
8166            lastID = id;
8167        }
8168        // We now have a list of sublists, each w/matching titles
8169        for (i=0, ilen=parasets.length; i < ilen; i += 1) {
8170            lst = parasets[i];
8171            if (lst.length < 2) {
8172                continue;
8173            }
8174            // Get the locator in last position, but only if it's the only one in the set.
8175            var locatorInLastPosition = lst.slice(-1)[0].locator;
8176            if (locatorInLastPosition) {
8177                for (var j=0, jlen=lst.length - 1; j < jlen; j += 1) {
8178                    if (lst[j].locator) {
8179                        locatorInLastPosition = false;
8180                    }
8181                }
8182            }
8183            // move the locator here, if it's called for.
8184            if (locatorInLastPosition) {
8185                lst[0].locator = locatorInLastPosition;
8186                delete lst.slice(-1)[0].locator;
8187                lst[0].label = lst.slice(-1)[0].label;
8188                if (lst.slice(-1)[0].label) {
8189                    delete lst.slice(-1)[0].label;
8190                }
8191            }
8192       }
8193    }
8194    myparams = [];
8195    len = inputList.length;
8196    if (inputList[0] && inputList[0][1]) {
8197        if (authorOnly) {
8198            delete inputList[0][1]["suppress-author"];
8199            inputList[0][1]["author-only"] = true;
8200        } else if (suppressAuthor) {
8201            delete inputList[0][1]["author-only"];
8202            inputList[0][1]["suppress-author"] = true;
8203        }
8204    }
8205    if (this.opt.parallel.enable) {
8206        this.parallel.StartCitation(inputList);
8207    }
8208    for (pos = 0; pos < len; pos += 1) {
8209
8210        // Also for parallels only
8211        this.tmp.cite_index = pos;
8212
8213        Item = inputList[pos][0];
8214        item = inputList[pos][1];
8215        item = CSL.parseLocator.call(this, item);
8216        last_collapsed = this.tmp.have_collapsed;
8217        var last_locator = false;
8218        if (pos > 0 && inputList[pos-1][1]) {
8219            last_locator = !!inputList[pos-1][1].locator;
8220        }
8221        params = {};
8222        
8223        // Reset shadow_numbers here, suppress reset in getCite()
8224        this.tmp.shadow_numbers = {};
8225        if (!this.tmp.just_looking && this.opt.hasPlaceholderTerm) {
8226            var output = this.output;
8227            this.output = new CSL.Output.Queue(this);
8228            this.output.adjust = new CSL.Output.Queue.adjust();
8229            CSL.getAmbiguousCite.call(this, Item, null, false, item);
8230            this.output = output;
8231        }
8232
8233        this.tmp.in_cite_predecessor = false;
8234        // true is to block reset of shadow numbers
8235        
8236        
8237        if (pos > 0) {
8238            CSL.getCite.call(this, Item, item, "" + inputList[(pos - 1)][0].id, true);
8239        } else {
8240            this.tmp.term_predecessor = false;
8241            CSL.getCite.call(this, Item, item, null, true);
8242        }
8243
8244        // Make a note of any errors
8245        if (!this.tmp.cite_renders_content) {
8246            error_object = {
8247                citationID: "" + this.tmp.citation_id,
8248                index: this.tmp.citation_pos,
8249                noteIndex: this.tmp.citation_note_index,
8250                itemID: "" + Item.id,
8251                citationItems_pos: pos,
8252                error_code: CSL.ERROR_NO_RENDERED_FORM
8253            };
8254            this.tmp.citation_errors.push(error_object);
8255        }
8256        params.splice_delimiter = CSL.getSpliceDelimiter.call(this, last_locator, last_collapsed, pos);
8257        // XXX This appears to be superfluous.
8258        if (item && item["author-only"]) {
8259            this.tmp.suppress_decorations = true;
8260        }
8261
8262        if (pos > 0) {
8263            preceding_item = inputList[pos - 1][1];
8264
8265            // XXX OR if preceding suffix is empty, and the current prefix begins with a full stop.
8266
8267            var precedingEndsInPeriodOrComma = preceding_item.suffix && [";", ".", ","].indexOf(preceding_item.suffix.slice(-1)) > -1;
8268            var currentStartsWithPeriodOrComma = !preceding_item.suffix && item.prefix && [";", ".", ","].indexOf(item.prefix.slice(0, 1)) > -1;
8269            if (precedingEndsInPeriodOrComma || currentStartsWithPeriodOrComma) {
8270                var spaceidx = params.splice_delimiter.indexOf(" ");
8271                if (spaceidx > -1 && !currentStartsWithPeriodOrComma) {
8272                    params.splice_delimiter = params.splice_delimiter.slice(spaceidx);
8273                } else {
8274                    params.splice_delimiter = "";
8275                }
8276            }
8277        }
8278        params.suppress_decorations = this.tmp.suppress_decorations;
8279        params.have_collapsed = this.tmp.have_collapsed;
8280        //
8281        // XXXXX: capture parameters to an array, which
8282        // will be of the same length as this.output.queue,
8283        // corresponding to each element.
8284        //
8285        myparams.push(params);
8286        if (item["author-only"]) {
8287            break;
8288        }
8289    }
8290    //
8291    // output.queue is a simple array.  do a slice
8292    // of it to get each cite item, setting params from
8293    // the array that was built in the preceding loop.
8294    //
8295    empties = 0;
8296    myblobs = this.output.queue.slice();
8297
8298    var citation_suffix = "";
8299    if (citation) {
8300        citation_suffix = CSL.checkSuffixSpacePrepend(this, citation.properties.suffix);
8301    }
8302    var suffix = this.citation.opt.layout_suffix;
8303    var last_locale = this.tmp.cite_locales[this.tmp.cite_locales.length - 1];
8304    //
8305    // Must have a value to take effect.  Use zero width space to force empty suffix.
8306    if (last_locale && this.tmp.cite_affixes[this.tmp.area][last_locale] && this.tmp.cite_affixes[this.tmp.area][last_locale].suffix) {
8307        suffix = this.tmp.cite_affixes[this.tmp.area][last_locale].suffix;
8308    }
8309    if (CSL.TERMINAL_PUNCTUATION.slice(0, -1).indexOf(suffix.slice(0, 1)) > -1) {
8310        suffix = suffix.slice(0, 1);
8311    }
8312    //print("=== FROM CITE ===");
8313    suffix = this.output.checkNestedBrace.update(citation_suffix + suffix);
8314
8315
8316    for (var i=0,ilen=this.output.queue.length;i<ilen;i+=1) {
8317        CSL.Output.Queue.purgeEmptyBlobs(this.output.queue[i]);
8318    }
8319    if (!this.tmp.suppress_decorations && this.output.queue.length) {
8320        if (!(this.opt.development_extensions.apply_citation_wrapper
8321              && this.sys.wrapCitationEntry
8322               && !this.tmp.just_looking
8323              && this.tmp.area === "citation")) { 
8324
8325            if (!suppressTrailingPunctuation) {
8326                this.output.queue[this.output.queue.length - 1].strings.suffix = suffix;
8327            }
8328            this.output.queue[0].strings.prefix = use_layout_prefix;
8329        }
8330    }
8331    if (this.opt.development_extensions.clean_up_csl_flaws) {
8332        for (var j=0,jlen=this.output.queue.length;j<jlen;j+=1) {
8333            //print("OUTPUT[5]: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2))
8334            this.output.adjust.upward(this.output.queue[j]);
8335            //print("OUTPUT[4]: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2))
8336            this.output.adjust.leftward(this.output.queue[j]);
8337            //print("OUTPUT[3]: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2))
8338            this.output.adjust.downward(this.output.queue[j]);
8339            //print("OUTPUT[2]: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2))
8340            this.tmp.last_chr = this.output.adjust.fix(this.output.queue[j]);
8341            //print("OUTPUT[1]: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations','num'],2))
8342        }
8343    }
8344    //print("this.tmp.last_chr="+this.tmp.last_chr);
8345    for (pos = 0, len = myblobs.length; pos < len; pos += 1) {
8346        var buffer = [];
8347        this.output.queue = [myblobs[pos]];
8348        this.tmp.suppress_decorations = myparams[pos].suppress_decorations;
8349        this.tmp.splice_delimiter = myparams[pos].splice_delimiter;
8350        //
8351        // oh, one last second thought on delimiters ...
8352        //
8353
8354        if (myblobs[pos].parallel_delimiter) {
8355            this.tmp.splice_delimiter = myblobs[pos].parallel_delimiter;
8356        }
8357        this.tmp.have_collapsed = myparams[pos].have_collapsed;
8358
8359        composite = this.output.string(this, this.output.queue);
8360
8361        this.tmp.suppress_decorations = false;
8362        // meaningless assignment
8363        // this.tmp.handle_ranges = false;
8364        if ("string" === typeof composite) {
8365            this.tmp.suppress_decorations = false;
8366            if (!composite) {
8367                if (this.opt.development_extensions.throw_on_empty) {
8368                    CSL.error("Citation would render no content");
8369                } else {
8370                    composite = "[NO_PRINTED_FORM]";
8371                }
8372            }
8373            return composite;
8374        }
8375        if ("object" === typeof composite && composite.length === 0 && !item["suppress-author"]) {
8376            if (pos === 0) {
8377                var errStr = "[CSL STYLE ERROR: reference with no printed form.]";
8378                var preStr = pos === 0 ? txt_esc(this.citation.opt.layout_prefix) : "";
8379                var sufStr = pos === (myblobs.length - 1) ? txt_esc(this.citation.opt.layout_suffix) : "";
8380                composite.push(preStr + errStr + sufStr);
8381            } else if (pos === myblobs.length - 1) {
8382                var tmpobj = objects[objects.length - 1];
8383                if (typeof tmpobj === "string") {
8384                    objects[objects.length -1] += (txt_esc(this.citation.opt.layout_suffix));
8385                } else if (typeof tmpobj === "object") {
8386                    tmpobj.strings.suffix += (txt_esc(this.citation.opt.layout_suffix));
8387                }
8388            }
8389        }
8390        if (buffer.length && "string" === typeof composite[0]) {
8391            composite.reverse();
8392            var tmpstr = composite.pop();
8393            if (tmpstr && tmpstr.slice(0, 1) === ",") {
8394                buffer.push(tmpstr);
8395            } else if ("string" == typeof buffer.slice(-1)[0] && buffer.slice(-1)[0].slice(-1) === ",") {
8396                buffer.push(" " + tmpstr);
8397            } else if (tmpstr) {
8398                buffer.push(txt_esc(this.tmp.splice_delimiter) + tmpstr);
8399            }
8400        } else {
8401            composite.reverse();
8402            compie = composite.pop();
8403            if ("undefined" !== typeof compie) {
8404                if (buffer.length && "string" === typeof buffer[buffer.length - 1]) {
8405                    buffer[buffer.length - 1] += compie.successor_prefix;
8406                }
8407                buffer.push(compie);
8408            }
8409        }
8410        // Seems odd, but this was unnecessary and broken.
8411        //composite.reverse();
8412        llen = composite.length;
8413        for (ppos = 0; ppos < llen; ppos += 1) {
8414            obj = composite[ppos];
8415            if ("string" === typeof obj) {
8416                buffer.push(txt_esc(this.tmp.splice_delimiter) + obj);
8417                continue;
8418            }
8419            compie = composite.pop();
8420            if ("undefined" !== typeof compie) {
8421                buffer.push(compie);
8422            }
8423        }
8424        if (buffer.length === 0 && !inputList[pos][1]["suppress-author"]) {
8425            empties += 1;
8426        }
8427        if (buffer.length > 1 && typeof buffer[0] !== "string") {
8428            buffer = [this.output.renderBlobs(buffer)];
8429        }
8430        if (buffer.length) {
8431            if ("string" === typeof buffer[0]) {
8432                if (pos > 0) {
8433                    buffer[0] = txt_esc(this.tmp.splice_delimiter) + buffer[0];
8434                }
8435            } else {
8436                if (pos > 0) {
8437                    buffer[0].splice_prefix = this.tmp.splice_delimiter;
8438                } else {
8439                    buffer[0].splice_prefix = "";
8440                }
8441            }
8442        }
8443        objects = objects.concat(buffer);
8444    }
8445    // print("OBJECTS="+objects);
8446    result += this.output.renderBlobs(objects);
8447
8448    if (result) {
8449        //if (CSL.TERMINAL_PUNCTUATION.indexOf(this.tmp.last_chr) > -1 
8450        //    && this.tmp.last_chr === use_layout_suffix.slice(0, 1)) {
8451        //    use_layout_suffix = use_layout_suffix.slice(1);
8452        //}
8453        if (!this.tmp.suppress_decorations) {
8454            len = this.citation.opt.layout_decorations.length;
8455            for (pos = 0; pos < len; pos += 1) {
8456                params = this.citation.opt.layout_decorations[pos];
8457                // The "normal" formats in some output modes expect
8458                // a superior nested decoration environment, and
8459                // so should produce no output here.
8460                if (params[1] === "normal") {
8461                    continue;
8462                }
8463                if (!item || !item["author-only"]) {
8464                    result = this.fun.decorate[params[0]][params[1]](this, result);
8465                }
8466            }
8467        }
8468    }
8469    this.tmp.suppress_decorations = false;
8470    if (!result) {
8471        if (this.opt.development_extensions.throw_on_empty) {
8472            CSL.error("Citation would render no content");
8473        } else {
8474            result = "[NO_PRINTED_FORM]"
8475        }
8476    }
8477    return result;
8478};
8479
8480/*
8481 * Render a single cite item.
8482 *
8483 * This is called on the state object, with a single
8484 * Item as input.  It iterates exactly once over the style
8485 * citation tokens, and leaves the result of rendering in
8486 * the top-level list in the relevant *.opt.output
8487 * stack, as a list item consisting of a single string.
8488 *
8489 * (This is dual-purposed for generating individual
8490 * entries in a bibliography.)
8491 */
8492CSL.getCite = function (Item, item, prevItemID, blockShadowNumberReset) {
8493    var next, error_object;
8494    var areaOrig = this.tmp.area;
8495    if (item && item["author-only"] && this.intext && this.intext.tokens.length > 0) {
8496            this.tmp.area = "intext";
8497    }
8498    this.tmp.cite_renders_content = false;
8499    this.tmp.probably_rendered_something = false;
8500    this.tmp.prevItemID = prevItemID;
8501
8502    CSL.citeStart.call(this, Item, item, blockShadowNumberReset);
8503    next = 0;
8504    this.tmp.name_node = {};
8505    this.nameOutput = new CSL.NameOutput(this, Item, item);
8506
8507    // rerun?
8508    while (next < this[this.tmp.area].tokens.length) {
8509        next = CSL.tokenExec.call(this, this[this.tmp.area].tokens[next], Item, item);
8510    }
8511
8512    CSL.citeEnd.call(this, Item, item);
8513    // Odd place for this, but it seems to fit here
8514    if (!this.tmp.cite_renders_content && !this.tmp.just_looking) {
8515        if (this.tmp.area === "bibliography") {
8516            error_object = {
8517                index: this.tmp.bibliography_pos,
8518                itemID: "" + Item.id,
8519                error_code: CSL.ERROR_NO_RENDERED_FORM
8520            };
8521            this.tmp.bibliography_errors.push(error_object);
8522        }
8523    }
8524    this.tmp.area = areaOrig;
8525    return "" + Item.id;
8526};
8527
8528
8529CSL.citeStart = function (Item, item, blockShadowNumberReset) {
8530    this.tmp.lang_array = [];
8531    if (Item.language) {
8532        // Guard against garbage locales in user input
8533        var m = Item.language.match(/^([a-zA-Z]+).*/);
8534        if (m) {
8535            this.tmp.lang_array.push(m[1].toLowerCase());
8536        }
8537    }
8538    this.tmp.lang_array.push(this.opt.lang);
8539    if (!blockShadowNumberReset) {
8540        this.tmp.shadow_numbers = {};
8541    }
8542    
8543    this.tmp.disambiguate_count = 0;
8544    this.tmp.disambiguate_maxMax = 0;
8545    this.tmp.same_author_as_previous_cite = false;
8546    if (!this.tmp.suppress_decorations) {
8547        this.tmp.subsequent_author_substitute_ok = true;
8548    } else {
8549        this.tmp.subsequent_author_substitute_ok = false;
8550    }
8551    this.tmp.lastchr = "";
8552    if (this.tmp.area === "citation" && this.citation.opt.collapse && this.citation.opt.collapse.length) {
8553        //this.tmp.have_collapsed = "year";
8554        this.tmp.have_collapsed = true;
8555    } else {
8556        this.tmp.have_collapsed = false;
8557    }
8558    this.tmp.render_seen = false;
8559    if (this.tmp.disambig_request  && ! this.tmp.disambig_override) {
8560        this.tmp.disambig_settings = this.tmp.disambig_request;
8561    } else if (this.registry.registry[Item.id] && ! this.tmp.disambig_override) {
8562        this.tmp.disambig_request = this.registry.registry[Item.id].disambig;
8563        this.tmp.disambig_settings = this.registry.registry[Item.id].disambig;
8564    } else {
8565        this.tmp.disambig_settings = new CSL.AmbigConfig();
8566    }
8567    if (this.tmp.area !== 'citation') {
8568        if (!this.registry.registry[Item.id]) {
8569            this.tmp.disambig_restore = new CSL.AmbigConfig();
8570        } else {
8571            this.tmp.disambig_restore = CSL.cloneAmbigConfig(this.registry.registry[Item.id].disambig);
8572            if (this.tmp.area === 'bibliography' && this.tmp.disambig_settings && this.tmp.disambig_override) {
8573                if (this.opt["disambiguate-add-names"]) {
8574                    this.tmp.disambig_settings.names = this.registry.registry[Item.id].disambig.names.slice();
8575                    if (this.tmp.disambig_request) {
8576                        this.tmp.disambig_request.names = this.registry.registry[Item.id].disambig.names.slice();
8577                    }
8578                }
8579                if (this.opt["disambiguate-add-givenname"]) {
8580                    // This is weird and delicate and not fully understood
8581                    this.tmp.disambig_request = this.tmp.disambig_settings;
8582                    this.tmp.disambig_settings.givens = this.registry.registry[Item.id].disambig.givens.slice();
8583                    this.tmp.disambig_request.givens = this.registry.registry[Item.id].disambig.givens.slice();
8584                    for (var i=0,ilen=this.tmp.disambig_settings.givens.length;i<ilen;i+=1) {
8585                        this.tmp.disambig_settings.givens[i] = this.registry.registry[Item.id].disambig.givens[i].slice();
8586                    }
8587                    for (var i=0,ilen=this.tmp.disambig_request.givens.length;i<ilen;i+=1) {
8588                        this.tmp.disambig_request.givens[i] = this.registry.registry[Item.id].disambig.givens[i].slice();
8589                    }
8590                }
8591            }
8592        }
8593    }
8594
8595    this.tmp.names_used = [];
8596    this.tmp.nameset_counter = 0;
8597    this.tmp.years_used = [];
8598    this.tmp.names_max.clear();
8599    if (!this.tmp.just_looking) {
8600        if (!item || item.parallel === "first" || !item.parallel) {
8601            this.tmp.abbrev_trimmer = {
8602                QUASHES: {}
8603            };
8604        }
8605    }
8606
8607    this.tmp.splice_delimiter = this[this.tmp.area].opt.layout_delimiter;
8608    //this.tmp.splice_delimiter = this[this.tmp.area].opt.delimiter;
8609
8610    this.bibliography_sort.keys = [];
8611    this.citation_sort.keys = [];
8612
8613    this.tmp.has_done_year_suffix = false;
8614    this.tmp.last_cite_locale = false;
8615    // SAVE PARAMETERS HERE, IF APPROPRIATE
8616    // (promiscuous addition of global parameters => death by a thousand cuts)
8617    if (!this.tmp.just_looking && item && !item.position && this.registry.registry[Item.id]) {
8618        this.tmp.disambig_restore = CSL.cloneAmbigConfig(this.registry.registry[Item.id].disambig);
8619    }
8620    // XXX This only applied to the "number" variable itself? Huh?
8621    //this.setNumberLabels(Item);
8622    this.tmp.first_name_string = false;
8623    this.tmp.authority_stop_last = 0;
8624};
8625
8626CSL.citeEnd = function (Item, item) {
8627    // RESTORE PARAMETERS IF APPROPRIATE
8628    if (this.tmp.disambig_restore && this.registry.registry[Item.id]) {
8629        this.registry.registry[Item.id].disambig.names = this.tmp.disambig_restore.names.slice();
8630        this.registry.registry[Item.id].disambig.givens = this.tmp.disambig_restore.givens.slice();
8631        for (var i=0,ilen=this.registry.registry[Item.id].disambig.givens.length;i<ilen;i+=1) {
8632            this.registry.registry[Item.id].disambig.givens[i] = this.tmp.disambig_restore.givens[i].slice();
8633        }
8634    }
8635    this.tmp.disambig_restore = false;
8636
8637    if (item && item.suffix) {
8638        //this.tmp.last_suffix_used = this.tmp.suffix.value();
8639        this.tmp.last_suffix_used = item.suffix;
8640    } else {
8641        this.tmp.last_suffix_used = "";
8642    }
8643    this.tmp.last_years_used = this.tmp.years_used.slice();
8644    this.tmp.last_names_used = this.tmp.names_used.slice();
8645    this.tmp.cut_var = false;
8646
8647    // This is a hack, in a way; I have lost track of where
8648    // the disambig (name rendering) settings used for rendering work their way
8649    // into the registry.  This resets defaults to the subsequent form,
8650    // when first cites are rendered.
8651    //if (this.tmp.disambig_restore && this.registry.registry[Item.id]) {
8652    //    this.registry.registry[Item.id].disambig = this.tmp.disambig_restore;
8653    //}
8654    //this.tmp.disambig_restore = false;
8655    this.tmp.disambig_request = false;
8656
8657    this.tmp.cite_locales.push(this.tmp.last_cite_locale);
8658
8659    if (this.tmp.issued_date && this.tmp.renders_collection_number) {
8660        var buf = [];
8661        for (var i = this.tmp.issued_date.list.length - 1; i > this.tmp.issued_date.pos; i += -1) {
8662            buf.push(this.tmp.issued_date.list.pop());
8663        }
8664        // Throw away the unwanted blob
8665        this.tmp.issued_date.list.pop();
8666        // Put the other stuff back
8667        for (i = buf.length - 1; i > -1; i += -1) {
8668            this.tmp.issued_date.list.push(buf.pop());
8669        }
8670    }
8671    this.tmp.issued_date = false;
8672    this.tmp.renders_collection_number = false;
8673
8674};
8675
8676/*global CSL: true */
8677
8678CSL.Engine.prototype.makeBibliography = function (bibsection) {
8679    var debug, ret, params, maxoffset, item, len, pos, tok, tokk, tokkk, entry_ids, entry_strings;
8680    debug = false;
8681    if (!bibsection && (this.bibliography.opt.exclude_types || this.bibliography.opt.exclude_with_fields)) {
8682        bibsection = {
8683            exclude: []
8684        };
8685        if (this.bibliography.opt.exclude_types) {
8686            for (var i in this.bibliography.opt.exclude_types) {
8687                var val = this.bibliography.opt.exclude_types[i];
8688                bibsection.exclude.push({
8689                    field: "type",
8690                    value: val
8691                });
8692            }
8693        }
8694        if (this.bibliography.opt.exclude_with_fields) {
8695            for (var i in this.bibliography.opt.exclude_with_fields) {
8696                var field = this.bibliography.opt.exclude_with_fields[i];
8697                bibsection.exclude.push({
8698                    field: field, value: true
8699                });
8700            }
8701        }
8702    }
8703    // API change: added in version 1.0.51
8704    if (!this.bibliography.tokens.length) {
8705        return false;
8706    }
8707    if ("string" === typeof bibsection) {
8708        this.opt.citation_number_slug = bibsection;
8709        bibsection = false;
8710    }
8711    //SNIP-START
8712    if (debug) {
8713        len = this.bibliography.tokens.length;
8714        for (pos = 0; pos < len; pos += 1) {
8715            tok = this.bibliography.tokens[pos];
8716            CSL.debug("bibtok: " + tok.name);
8717        }
8718        CSL.debug("---");
8719        len = this.citation.tokens.length;
8720        for (pos = 0; pos < len; pos += 1) {
8721            tokk = this.citation.tokens[pos];
8722            CSL.debug("cittok: " + tok.name);
8723        }
8724        CSL.debug("---");
8725        len = this.bibliography_sort.tokens.length;
8726        for (pos = 0; pos < len; pos += 1) {
8727            tokkk = this.bibliography_sort.tokens[pos];
8728            CSL.debug("bibsorttok: " + tok.name);
8729        }
8730    }
8731    //SNIP-END
8732
8733    // For paged returns
8734    ret = CSL.getBibliographyEntries.call(this, bibsection);
8735    entry_ids = ret[0];
8736    entry_strings = ret[1];
8737
8738    // For paged returns
8739    var done = ret[2];
8740
8741    params = {
8742        "maxoffset": 0,
8743        "entryspacing": this.bibliography.opt["entry-spacing"],
8744        "linespacing": this.bibliography.opt["line-spacing"],
8745        "second-field-align": false,
8746        "entry_ids": entry_ids,
8747        "bibliography_errors": this.tmp.bibliography_errors.slice(),
8748        "done": done
8749    };
8750    if (this.bibliography.opt["second-field-align"]) {
8751        params["second-field-align"] = this.bibliography.opt["second-field-align"];
8752    }
8753    maxoffset = 0;
8754    len = this.registry.reflist.length;
8755    for (pos = 0; pos < len; pos += 1) {
8756        item = this.registry.reflist[pos];
8757        if (item.offset > params.maxoffset) {
8758            params.maxoffset = item.offset;
8759        }
8760    }
8761    if (this.bibliography.opt.hangingindent) {
8762        params.hangingindent = this.bibliography.opt.hangingindent;
8763    }
8764    params.bibstart = this.fun.decorate.bibstart;
8765    params.bibend = this.fun.decorate.bibend;
8766
8767    this.opt.citation_number_slug = false;
8768    return [params, entry_strings];
8769};
8770
8771/*
8772 * Compose individual cites into a single string.
8773 */
8774CSL.getBibliographyEntries = function (bibsection) {
8775    var ret, input, include, anymatch, allmatch, bib_entry, res, item, spec, lllen, pppos, topblobs, entry_item_ids, debug, i, ilen, siblings, skips, sortedItems, eyetem, entry_item_data, j, jlen;
8776    ret = [];
8777    entry_item_data = [];
8778    this.tmp.area = "bibliography";
8779    this.tmp.root = "bibliography";
8780    this.tmp.last_rendered_name = false;
8781    this.tmp.bibliography_errors = [];
8782    this.tmp.bibliography_pos = 0;
8783    
8784    // For paged returns: disable generated entries and
8785    // do not fetch full items as a batch (input variable
8786    // consists of ids only in this case)
8787    if (bibsection && bibsection.page_start && bibsection.page_length) {
8788        input = this.registry.getSortedIds();        
8789    } else {
8790        input = this.refetchItems(this.registry.getSortedIds());
8791    }
8792    
8793    this.tmp.disambig_override = true;
8794    function eval_string(a, b) {
8795        if (a === b) {
8796            return true;
8797        }
8798        return false;
8799    }
8800    function eval_list(a, lst) {
8801        lllen = lst.length;
8802        for (pppos = 0; pppos < lllen; pppos += 1) {
8803            if (eval_string(a, lst[pppos])) {
8804                return true;
8805            }
8806        }
8807        return false;
8808    }
8809    function eval_spec(a, b) {
8810        if ("boolean" === typeof a || !a) {
8811            if (a) {
8812                return !!b;
8813            } else {
8814                return !b;
8815            }
8816        } else {
8817            if ("string" === typeof b) {
8818                return eval_string(a, b);
8819            } else if (!b) {
8820                return false;
8821            } else {
8822                return eval_list(a, b);
8823            }
8824        }
8825    }
8826
8827    skips = {};
8828
8829    // For paged returns
8830    var page_item_count;
8831    if (bibsection && bibsection.page_start && bibsection.page_length) {
8832        page_item_count = 0;
8833        if (bibsection.page_start !== true) {
8834            for (i = 0, ilen = input.length; i < ilen; i += 1) {
8835                skips[input[i]] = true;
8836                if (bibsection.page_start == input[i]) {
8837                    break;
8838                }
8839            }
8840        }
8841    }
8842
8843    var processed_item_ids = [];
8844
8845    var consolidatedIDs = {};
8846    this.tmp.container_item_count = {};
8847    input = input.filter(o => {
8848        var ret = o;
8849        if (o.legislation_id) {
8850            if (consolidatedIDs[o.legislation_id]) {
8851                ret = false;
8852            } else {
8853                consolidatedIDs[o.legislation_id] = true;
8854            }
8855        } else if (o.container_id) {
8856            if (!this.tmp.container_item_count[o.container_id]) {
8857                this.tmp.container_item_count[o.container_id] = 0;
8858            }
8859            this.tmp.container_item_count[o.container_id]++;
8860            if (this.bibliography.opt.consolidate_containers.indexOf(o.type) > -1) {
8861                if (consolidatedIDs[o.container_id]) {
8862                    ret = false;
8863                } else {
8864                    consolidatedIDs[o.container_id] = true;
8865                }
8866            }
8867        }
8868        return ret;
8869    });
8870
8871    this.tmp.container_item_pos = {};
8872
8873    for (i = 0, ilen = input.length; i < ilen; i += 1) {
8874        
8875        // For paged returns
8876        if (bibsection && bibsection.page_start && bibsection.page_length) {
8877            if (skips[input[i]]) {
8878                continue;
8879            }
8880            item = this.refetchItem(input[i]);
8881            if (page_item_count === bibsection.page_length) {
8882                break;
8883            }
8884        } else {
8885            item = input[i];
8886            if (skips[item.id]) {
8887                continue;
8888            }
8889        }
8890        if (bibsection) {
8891            include = true;
8892            if (bibsection.include) {
8893                //
8894                // Opt-in: these are OR-ed.
8895                //
8896                include = false;
8897                for (j = 0, jlen = bibsection.include.length; j < jlen; j += 1) {
8898                    spec = bibsection.include[j];
8899                    if (eval_spec(spec.value, item[spec.field])) {
8900                        include = true;
8901                        break;
8902                    }
8903                }
8904            } else if (bibsection.exclude) {
8905                //
8906                // Opt-out: these are also OR-ed.
8907                //
8908                anymatch = false;
8909                for (j = 0, jlen = bibsection.exclude.length; j < jlen; j += 1) {
8910                    spec = bibsection.exclude[j];
8911                    if (eval_spec(spec.value, item[spec.field])) {
8912                        anymatch = true;
8913                        break;
8914                    }
8915                }
8916                if (anymatch) {
8917                    include = false;
8918                }
8919            } else if (bibsection.select) {
8920                //
8921                // Multiple condition opt-in: these are AND-ed.
8922                //
8923                include = false;
8924                allmatch = true;
8925                for (j = 0, jlen = bibsection.select.length; j < jlen; j += 1) {
8926                    spec = bibsection.select[j];
8927                    if (!eval_spec(spec.value, item[spec.field])) {
8928                        allmatch = false;
8929                    }
8930                }
8931                if (allmatch) {
8932                    include = true;
8933                }
8934            }
8935            if (bibsection.quash) {
8936                //
8937                // Stop criteria: These are AND-ed.
8938                //
8939                allmatch = true;
8940                for (j = 0, jlen = bibsection.quash.length; j < jlen; j += 1) {
8941                    spec = bibsection.quash[j];
8942                    if (!eval_spec(spec.value, item[spec.field])) {
8943                        allmatch = false;
8944                    }
8945                }
8946                if (allmatch) {
8947                    include = false;
8948                }
8949            }
8950            if (!include) {
8951                continue;
8952            }
8953        }
8954        //SNIP-START
8955        if (debug) {
8956            CSL.debug("BIB: " + item.id);
8957        }
8958
8959        if (item.container_id) {
8960            if (!this.tmp.container_item_pos[item.container_id]) {
8961                this.tmp.container_item_pos[item.container_id] = 0;
8962            }
8963            this.tmp.container_item_pos[item.container_id]++;
8964        }
8965        
8966        //SNIP-END
8967        bib_entry = new CSL.Token("group", CSL.START);
8968        bib_entry.decorations = [["@bibliography", "entry"]].concat(this.bibliography.opt.layout_decorations);
8969        this.output.startTag("bib_entry", bib_entry);
8970        if (item.system_id && this.sys.embedBibliographyEntry) {
8971            this.output.current.value().item_id = item.system_id;
8972        } else {
8973            this.output.current.value().system_id = item.id;
8974        }
8975
8976        // 2019-06-25 Hacked to conform to new parallels evaluation method
8977        // 2020-04-25 Revised to work with latest, and final, parallel-first/parallel-last attributes
8978        entry_item_ids = [];
8979        if (this.registry.registry[item.id].master
8980            && !(bibsection && bibsection.page_start && bibsection.page_length)) {
8981            // Fetch item content
8982            sortedItems = [[item, {id: item.id}]];
8983            siblings = this.registry.registry[item.id].siblings;
8984            for (var j=0,jlen=siblings.length; j<jlen; j++) {
8985               sortedItems.push([this.refetchItem(siblings[j]), {id: siblings[j]}]);
8986            }
8987            // Adjust parameters
8988            this.parallel.StartCitation(sortedItems);
8989            if (this.registry.registry[item.id].parallel_delimiter_override) {
8990                this.output.queue[0].strings.delimiter = this.registry.registry[item.id].parallel_delimiter_override;
8991            } else {
8992                this.output.queue[0].strings.delimiter = ", ";
8993            }
8994            this.tmp.term_predecessor = false;
8995            this.tmp.cite_index = 0;
8996            // Run cites
8997            for (j = 0, jlen = sortedItems.length; j < jlen; j += 1) {
8998                if (j < (sortedItems.length - 1)) {
8999                    this.tmp.parallel_and_not_last = true;
9000                } else {
9001                    delete this.tmp.parallel_and_not_last;
9002                }
9003                entry_item_ids.push("" + CSL.getCite.call(this, sortedItems[j][0], sortedItems[j][1]));
9004                this.tmp.cite_index++;
9005                skips[sortedItems[j][0].id] = true;
9006            }
9007        } else if (!this.registry.registry[item.id].siblings) {
9008            this.tmp.term_predecessor = false;
9009            this.tmp.cite_index = 0;
9010            entry_item_ids.push("" + CSL.getCite.call(this, item));
9011            if (bibsection && bibsection.page_start && bibsection.page_length) {
9012                page_item_count += 1;
9013            }
9014            //skips[item.id] = true;
9015        }
9016        // For RDF support
9017        entry_item_data.push("");
9018
9019        this.tmp.bibliography_pos += 1;
9020
9021        processed_item_ids.push(entry_item_ids);
9022        //
9023        // XXX: loop to render parallels goes here
9024        // XXX: just have to mark them somehow ...
9025        //
9026        this.output.endTag("bib_entry");
9027        //
9028        // place layout prefix on first blob of each cite, and suffix
9029        // on the last non-empty blob of each cite.  there be dragons
9030        // here.
9031        //
9032        if (this.output.queue[0].blobs.length && this.output.queue[0].blobs[0].blobs.length) {
9033            // The output queue stuff needs cleaning up.  the result of
9034            // output.current.value() is sometimes a blob, sometimes its list
9035            // of blobs.  this inconsistency is a source of confusion, and
9036            // should be cleaned up across the code base in the first
9037            // instance, before making any other changes to output code.
9038            if (!this.output.queue[0].blobs[0].blobs[0].strings) {
9039                topblobs = this.output.queue[0].blobs;
9040            } else {
9041                topblobs = this.output.queue[0].blobs[0].blobs;
9042            }
9043            topblobs[0].strings.prefix = this.bibliography.opt.layout_prefix + topblobs[0].strings.prefix;
9044        }
9045        for (j=0,jlen=this.output.queue.length;j<jlen;j+=1) {
9046            CSL.Output.Queue.purgeEmptyBlobs(this.output.queue[j]);
9047            //print("XXX: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2))
9048        }
9049        for (j=0,jlen=this.output.queue.length;j<jlen;j+=1) {
9050            this.output.adjust.upward(this.output.queue[j]);
9051            this.output.adjust.leftward(this.output.queue[j]);
9052            this.output.adjust.downward(this.output.queue[j],true);
9053            this.output.adjust.fix(this.output.queue[j]);
9054            //print("OUTPUT: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2))
9055        }
9056
9057        //print("DUMP "+JSON.stringify(this.output.queue, ["strings", "decorations", "prefix", "suffix", "delimiter", "blobs"], 2));
9058
9059        // XXX Need to account for numeric blobs in input.
9060        // XXX No idea how this could have worked previously.
9061
9062        //print("BLOBS "+this.output.queue[0].blobs[0].blobs);
9063
9064        //print("JSON "+JSON.stringify(this.output.queue[0].blobs, null, 2));
9065
9066        res = this.output.string(this, this.output.queue)[0];
9067        
9068        if (!res && this.opt.update_mode === CSL.NUMERIC) {
9069            var err = (ret.length + 1) + ". [CSL STYLE ERROR: reference with no printed form.]";
9070            res = CSL.Output.Formats[this.opt.mode]["@bibliography/entry"](this, err);
9071        }
9072        if (res) {
9073            ret.push(res);
9074        }
9075    }
9076
9077    var done = false;
9078    if (bibsection && bibsection.page_start && bibsection.page_length) {
9079        var last_expected_id = input.slice(-1)[0];
9080        var last_seen_id = processed_item_ids.slice(-1)[0];
9081        if (!last_expected_id || !last_seen_id || last_expected_id == last_seen_id) {
9082            done = true;
9083        }
9084    }
9085    this.tmp.disambig_override = false;
9086
9087    // XXX done
9088    return [processed_item_ids, ret, done];
9089};
9090
9091/*global CSL: true */
9092
9093
9094CSL.Engine.prototype.setCitationId = function (citation, force) {
9095    var ret, id, direction;
9096    ret = false;
9097    if (!citation.citationID || force) {
9098        id = Math.floor(Math.random() * 100000000000000);
9099        while (true) {
9100            direction = 0;
9101            if (!this.registry.citationreg.citationById[id]) {
9102                // In case the ID is used as an HTML identifier in the
9103                // calling application.
9104                //   https://github.com/Juris-M/citeproc-js/issues/22
9105                citation.citationID = "a" + id.toString(32);
9106                break;
9107            } else if (!direction && id < 50000000000000) {
9108                direction = 1;
9109            } else {
9110                direction = -1;
9111            }
9112            if (direction === 1) {
9113                id += 1;
9114            } else {
9115                id += -1;
9116            }
9117        }
9118        ret = "" + id;
9119    }
9120    this.registry.citationreg.citationById[citation.citationID] = citation;
9121    return ret;
9122};
9123
9124CSL.Engine.prototype.rebuildProcessorState = function (citations, mode, uncitedItemIDs) {
9125    // Rebuilds the processor from scratch, based on a list of citation
9126    // objects. In a dynamic application, once the internal state of processor
9127    // is established, citations should edited with individual invocations
9128    // of processCitationCluster().
9129
9130    // citations is a list of citation objects in document order.
9131    // mode is one of "html", "text" or "rtf".
9132    // uncitedItemIDs is a list of itemIDs or a JS object with itemIDs as keys.
9133    // Returns a list of [citationID,noteIndex,string] triples in document order.
9134    // Set citation.properties.noteIndex to 0 for in-text citations.
9135    // It is not necessary to run updateItems() before this function.
9136    if (!citations) {
9137        citations = [];
9138    }
9139    if (!mode) {
9140        mode = 'html';
9141    }
9142    var doneIDs = {};
9143    var itemIDs = [];
9144    for (var i=0,ilen=citations.length;i<ilen;i+=1) {
9145        for (var j=0,jlen=citations[i].citationItems.length;j<jlen;j+=1) {
9146            var itemID = "" + citations[i].citationItems[j].id;
9147            if (!doneIDs[itemID]) {
9148                itemIDs.push(itemID);
9149            }
9150            doneIDs[itemID] = true;
9151        }
9152    }
9153    this.updateItems(itemIDs);
9154    var pre = [];
9155    var post = [];
9156    var ret = [];
9157    var oldMode = this.opt.mode;
9158    this.setOutputFormat(mode);
9159    for (var i=0,ilen=citations.length;i<ilen;i+=1) {
9160        // res contains a result report and a list of [index,string] pairs
9161        // index begins at 0
9162        var res = this.processCitationCluster(citations[i],pre,post,CSL.ASSUME_ALL_ITEMS_REGISTERED);
9163        pre.push([citations[i].citationID,citations[i].properties.noteIndex]);
9164        for (var j=0,jlen=res[1].length;j<jlen;j+=1) {
9165            var index = res[1][j][0];
9166            ret[index] = [
9167                pre[index][0],
9168                pre[index][1],
9169                res[1][j][1]
9170            ];
9171        }
9172    }
9173    this.updateUncitedItems(uncitedItemIDs);
9174    this.setOutputFormat(oldMode);
9175    return ret;
9176};
9177
9178
9179CSL.Engine.prototype.restoreProcessorState = function (citations) {
9180    var i, ilen, j, jlen, item, Item, newitem, citationList, itemList, sortedItems;
9181    
9182    // This function is deprecated.
9183    // Use rebuildProcessorState() instead.
9184
9185    // Quickly restore state from citation details retained by
9186    // calling application.
9187    //
9188    // if citations are provided, position details and sortkeys 
9189    // on the citation objects are are assumed to be correct.  Item
9190    // data is retrieved, and sortedItems arrays are created and
9191    // sorted as required by the current style.
9192    //
9193    // If citations is an empty list or nil, reset processor to
9194    // empty state.
9195    citationList = [];
9196    itemList = [];
9197    if (!citations) {
9198        citations = [];
9199    }
9200    // Adjust citationIDs to avoid duplicates, save off index numbers
9201    var indexNumbers = [];
9202    var citationIds = {};
9203    for (i = 0, ilen = citations.length; i < ilen; i += 1) {
9204        if (citationIds[citations[i].citationID]) {
9205            this.setCitationId(citations[i], true);
9206        }
9207        citationIds[citations[i].citationID] = true;
9208        indexNumbers.push(citations[i].properties.index);
9209    }
9210    // Slice citations and sort by their declared index positions, if any,
9211    // then reassign index and noteIndex numbers.
9212    var oldCitations = citations.slice();
9213    oldCitations.sort(
9214        function (a,b) {
9215            if (a.properties.index < b.properties.index) {
9216                return -1;
9217            } else if (a.properties.index > b.properties.index) {
9218                return 1;
9219            } else {
9220                return 0;
9221            }
9222        }
9223    );
9224    for (i = 0, ilen = oldCitations.length; i < ilen; i += 1) {
9225        oldCitations[i].properties.index = i;
9226    }
9227    for (i = 0, ilen = oldCitations.length; i < ilen; i += 1) {
9228        sortedItems = [];
9229        for (j = 0, jlen = oldCitations[i].citationItems.length; j < jlen; j += 1) {
9230            item = oldCitations[i].citationItems[j];
9231            if ("undefined" === typeof item.sortkeys) {
9232                item.sortkeys = [];
9233            }
9234            Item = this.retrieveItem("" + item.id);
9235            newitem = [Item, item];
9236            sortedItems.push(newitem);
9237            oldCitations[i].citationItems[j].item = Item;
9238            itemList.push("" + item.id);
9239        }
9240        if (!oldCitations[i].properties.unsorted) {
9241            sortedItems.sort(this.citation.srt.compareCompositeKeys);
9242        }
9243        oldCitations[i].sortedItems = sortedItems;
9244        // Save citation data in registry
9245        this.registry.citationreg.citationById[oldCitations[i].citationID] = oldCitations[i];
9246    }
9247    // Register Items
9248    this.updateItems(itemList);
9249
9250    // Construct citationList from original copy
9251    for (i = 0, ilen = citations.length; i < ilen; i += 1) {
9252        citationList.push(["" + citations[i].citationID, citations[i].properties.noteIndex]);
9253    }
9254
9255    var ret = [];
9256    if (citations && citations.length) {
9257        // Rendering one citation restores remainder of processor state.
9258        // If citations is empty, rest to empty state.
9259        ret = this.processCitationCluster(citations[0], [], citationList.slice(1));
9260    } else {
9261        this.registry = new CSL.Registry(this);
9262        this.tmp = new CSL.Engine.Tmp();
9263        this.disambiguate = new CSL.Disambiguation(this);
9264    }
9265    return ret;
9266};
9267
9268
9269CSL.Engine.prototype.updateItems = function (idList, nosort, rerun_ambigs, implicitUpdate) {
9270    var debug = false;
9271    var oldArea = this.tmp.area;
9272    var oldRoot = this.tmp.root;
9273    var oldExtension = this.tmp.extension;
9274    if (this.bibliography_sort.tokens.length === 0) {
9275        nosort = true;
9276    }
9277    this.tmp.area = "citation";
9278    this.tmp.root = "citation";
9279    this.tmp.extension = "";
9280    if (!implicitUpdate) {
9281        this.tmp.loadedItemIDs = {};
9282    }
9283    //CSL.debug = print
9284    //SNIP-START
9285    if (debug) {
9286        CSL.debug("--> init <--");
9287    }
9288    //SNIP-END
9289    this.registry.init(idList);
9290
9291	if (rerun_ambigs) {
9292		for (var ambig in this.registry.ambigcites) {
9293			this.registry.ambigsTouched[ambig] = true;
9294		}
9295	}
9296
9297    this.registry.dodeletes(this.registry.myhash);
9298    
9299    this.registry.doinserts(this.registry.mylist);
9300    
9301    this.registry.dorefreshes();
9302
9303    // *** affects reflist
9304    this.registry.rebuildlist(nosort);
9305    
9306    this.registry.setsortkeys();
9307
9308    // taints always
9309    this.registry.setdisambigs();
9310
9311    // *** affects reflist
9312    this.registry.sorttokens(nosort);
9313
9314    // *** affects reflist
9315    // taints if numbered style
9316    this.registry.renumber();
9317    
9318    // taints always
9319    //this.registry.yearsuffix();
9320
9321    this.tmp.extension = oldExtension;
9322    this.tmp.area = oldArea;
9323    this.tmp.root = oldRoot;
9324
9325    return this.registry.getSortedIds();
9326};
9327
9328CSL.Engine.prototype.updateUncitedItems = function (idList, nosort) {
9329    var idHash;
9330    var oldArea = this.tmp.area;
9331    var oldRoot = this.tmp.root;
9332    var oldExtension = this.tmp.extension;
9333    if (this.bibliography_sort.tokens.length === 0) {
9334        nosort = true;
9335    }
9336    this.tmp.area = "citation";
9337    this.tmp.root = "citation";
9338    this.tmp.extension = "";
9339    this.tmp.loadedItemIDs = {};
9340    // This should be a utility function
9341    if (!idList) {
9342        idList = [];
9343    }
9344    if ("object" == typeof idList) {
9345        if ("undefined" == typeof idList.length) {
9346            idHash = idList;
9347            idList = [];
9348            for (var key in idHash) {
9349                idList.push(key);
9350            }
9351        } else if ("number" == typeof idList.length) {
9352            idHash = {};
9353            for (var i=0,ilen=idList.length;i<ilen;i+=1) {
9354                idHash[idList[i]] = true;
9355            }
9356        }
9357    }
9358
9359    // prepare extended list of items
9360    this.registry.init(idList, true);
9361
9362    // Use purge instead of delete.
9363    // this.registry.dodeletes(this.registry.myhash);
9364    this.registry.dopurge(idHash);
9365
9366    // everything else is the same as updateItems()
9367    this.registry.doinserts(this.registry.mylist);
9368
9369    this.registry.dorefreshes();
9370
9371    this.registry.rebuildlist(nosort);
9372
9373    this.registry.setsortkeys();
9374
9375    this.registry.setdisambigs();
9376
9377    this.registry.sorttokens(nosort);
9378
9379    this.registry.renumber();
9380
9381    this.tmp.extension = oldExtension;
9382    this.tmp.area = oldArea;
9383    this.tmp.root = oldRoot;
9384
9385    return this.registry.getSortedIds();
9386};
9387
9388/*global CSL: true */
9389
9390CSL.localeResolve = function (langstr, defaultLocale) {
9391    var ret, langlst;
9392    if (!defaultLocale) {
9393        defaultLocale = "en-US";
9394    }
9395    if (!langstr) {
9396        langstr = defaultLocale;
9397    }
9398    ret = {};
9399    //if ("undefined" === typeof langstr) {
9400    //    langstr = "en_US";
9401    //}
9402    langlst = langstr.split(/[\-_]/);
9403    ret.base = CSL.LANG_BASES[langlst[0]];
9404    if ("undefined" === typeof ret.base) {
9405        //CSL.debug("Warning: unknown locale "+langstr+", setting fallback to "+defaultLocale);
9406        return {base:defaultLocale, best:langstr, bare:langlst[0]};
9407    }
9408    if (langlst.length === 1) {
9409        ret.generic = true;
9410    }
9411    if (langlst.length === 1 || langlst[1] === "x") {
9412        ret.best = ret.base.replace("_", "-");
9413    } else {
9414        ret.best = langlst.slice(0, 2).join("-");
9415    }
9416    ret.base = ret.base.replace("_", "-");
9417    ret.bare = langlst[0];
9418    return ret;
9419};
9420
9421// Use call to invoke this.
9422CSL.Engine.prototype.localeConfigure = function (langspec, beShy) {
9423    var localexml;
9424    if (beShy && this.locale[langspec.best]) {
9425        return;
9426    }
9427    if (langspec.best === "en-US") {
9428        localexml = CSL.setupXml(this.sys.retrieveLocale("en-US"));
9429        this.localeSet(localexml, "en-US", langspec.best);
9430    } else if (langspec.best !== "en-US") {
9431        if (langspec.base !== langspec.best) {
9432            localexml = CSL.setupXml(this.sys.retrieveLocale(langspec.base));
9433            this.localeSet(localexml, langspec.base, langspec.best);
9434        }
9435        localexml = CSL.setupXml(this.sys.retrieveLocale(langspec.best));
9436        this.localeSet(localexml, langspec.best, langspec.best);        
9437    }
9438    this.localeSet(this.cslXml, "", langspec.best);
9439    this.localeSet(this.cslXml, langspec.bare, langspec.best);
9440    if (langspec.base !== langspec.best) {
9441        this.localeSet(this.cslXml, langspec.base, langspec.best);
9442    }
9443    this.localeSet(this.cslXml, langspec.best, langspec.best);
9444    if ("undefined" === typeof this.locale[langspec.best].terms["page-range-delimiter"]) {
9445        if (["fr", "pt"].indexOf(langspec.best.slice(0, 2).toLowerCase()) > -1) {
9446            this.locale[langspec.best].terms["page-range-delimiter"] = "-";
9447        } else {
9448            this.locale[langspec.best].terms["page-range-delimiter"] = "\u2013";
9449        }
9450    }
9451    if ("undefined" === typeof this.locale[langspec.best].terms["year-range-delimiter"]) {
9452        this.locale[langspec.best].terms["year-range-delimiter"] = "\u2013";
9453    }
9454    if ("undefined" === typeof this.locale[langspec.best].terms["citation-range-delimiter"]) {
9455        this.locale[langspec.best].terms["citation-range-delimiter"] = "\u2013";
9456    }
9457    if (this.opt.development_extensions.normalize_lang_keys_to_lowercase) {
9458        var localeLists = ["default-locale","locale-sort","locale-translit","locale-translat"];
9459        for (var i=0,ilen=localeLists.length;i<ilen;i+=1) {
9460            for (var j=0,jlen=this.opt[localeLists[i]].length;j<jlen;j+=1) {
9461                this.opt[localeLists[i]][j] = this.opt[localeLists[i]][j].toLowerCase();
9462            }
9463        }
9464        this.opt.lang = this.opt.lang.toLowerCase();
9465    }
9466};
9467    
9468//
9469// XXXXX: Got it.  The locales objects need to be reorganized,
9470// with a top-level local specifier, and terms, opts, dates
9471// below.
9472//
9473CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) {
9474    var blob, locale, nodes, attributes, pos, term, form, termname, styleopts, date, attrname, len, genderform, target, i, ilen;
9475    lang_in = lang_in.replace("_", "-");
9476    lang_out = lang_out.replace("_", "-");
9477
9478    if (this.opt.development_extensions.normalize_lang_keys_to_lowercase) {
9479        lang_in = lang_in.toLowerCase();
9480        lang_out = lang_out.toLowerCase();
9481    }
9482
9483    if (!this.locale[lang_out]) {
9484        this.locale[lang_out] = {};
9485        this.locale[lang_out].terms = {};
9486        this.locale[lang_out].opts = {};
9487        // Set default skip words. Can be overridden in locale by attribute on style-options node.
9488        this.locale[lang_out].opts["skip-words"] = CSL.SKIP_WORDS;
9489        // Initialise leading noise word to false. Actual assignment is below. Empty by default, can be overridden in locale by attribute on style-options node.
9490        if (!this.locale[lang_out].opts["leading-noise-words"]) {
9491            this.locale[lang_out].opts["leading-noise-words"] = [];
9492        }
9493        this.locale[lang_out].dates = {};
9494        // For ordinals
9495        this.locale[lang_out].ord = {'1.0.1':false,keys:{}};
9496        this.locale[lang_out]["noun-genders"] = {};
9497    }
9498
9499    //
9500    // Xml: Test if node is "locale" (nb: ns declarations need to be invoked
9501    // on every access to the xml object; bundle this with the functions
9502    //
9503    locale = myxml.makeXml();
9504    if (myxml.nodeNameIs(myxml.dataObj, 'locale')) {
9505        locale = myxml.dataObj;
9506    } else {
9507        //
9508        // Xml: get a list of all "locale" nodes
9509        //
9510        nodes = myxml.getNodesByName(myxml.dataObj, "locale");
9511        var foundLocale = false;
9512        for (pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) {
9513            blob = nodes[pos];
9514            //
9515            // Xml: get locale xml:lang
9516            //
9517            // Iterate over all locales, but for non-matching nodes,
9518            // we set jurisdiction_preference only (processing of the
9519            // chosen one will process the attribute there,
9520            // separately.
9521            if (!foundLocale && myxml.getAttributeValue(blob, 'lang', 'xml') === lang_in) {
9522                locale = blob;
9523                foundLocale = true;
9524            } else {
9525                var lang = myxml.getAttributeValue(blob, 'lang', 'xml');
9526                var style_options = myxml.getNodesByName(blob, 'style-options');
9527                if (lang && style_options && style_options.length) {
9528                    var jurispref = myxml.getAttributeValue(style_options[0], 'jurisdiction-preference');
9529                    if (jurispref) {
9530                        if (!this.locale[lang]) {
9531                            this.locale[lang] = {
9532                                opts: {}
9533                            };
9534                        }
9535                        this.locale[lang].opts["jurisdiction-preference"] = jurispref.split(/\s+/);
9536                    }
9537                }
9538            }
9539        }
9540    }
9541    //
9542    // Xml: get a list of any cs:type nodes within locale
9543    //
9544    nodes = myxml.getNodesByName(locale, 'type');
9545    for (i = 0, ilen = myxml.numberofnodes(nodes); i < ilen; i += 1) {
9546        var typenode = nodes[i];
9547        var type = myxml.getAttributeValue(typenode, 'name');
9548        var gender = myxml.getAttributeValue(typenode, 'gender');
9549        this.opt.gender[type] = gender;
9550    }
9551    //
9552    // Xml: get a list of term nodes within locale
9553    //
9554
9555    // If we are setting CSL 1.0.1 ordinals inside a style, wipe the
9556    // slate clean and start over.
9557    var hasCslOrdinals101 = myxml.getNodesByName(locale, 'term', 'ordinal').length;
9558    if (hasCslOrdinals101) {
9559        for (var key in this.locale[lang_out].ord.keys) {
9560            delete this.locale[lang_out].terms[key];
9561        }
9562        this.locale[lang_out].ord = {"1.0.1":false,keys:{}};
9563    }
9564
9565    nodes = myxml.getNodesByName(locale, 'term');
9566    // Collect ordinals info as for 1.0.1, but save only if 1.0.1 toggle triggers
9567    var ordinals101 = {"last-digit":{},"last-two-digits":{},"whole-number":{}};
9568    var ordinals101_toggle = false;
9569    var genderized_terms = {};
9570    for (pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) {
9571        term = nodes[pos];
9572        //
9573        // Xml: get string value of attribute
9574        //
9575        termname = myxml.getAttributeValue(term, 'name');
9576        if (termname === "sub verbo") {
9577            termname = "sub-verbo";
9578        }
9579        if (termname.slice(0,7) === "ordinal") {
9580            if (termname === "ordinal") {
9581                ordinals101_toggle = true;
9582            } else {
9583                var match = myxml.getAttributeValue(term, 'match');
9584                var termstub = termname.slice(8);
9585                var genderform = myxml.getAttributeValue(term, 'gender-form');
9586                if (!genderform) {
9587                    genderform = "neuter";
9588                }
9589                if (!match) {
9590                    match = "last-two-digits";
9591                    if (termstub.slice(0,1) === "0") {
9592                        match = "last-digit";
9593                    }
9594                }
9595                if (termstub.slice(0,1) === "0") {
9596                    termstub = termstub.slice(1);
9597                }
9598                if (!ordinals101[match][termstub]) {
9599                    ordinals101[match][termstub] = {};
9600                }
9601                ordinals101[match][termstub][genderform] = termname;
9602            }
9603            this.locale[lang_out].ord.keys[termname] = true;
9604        }
9605        if ("undefined" === typeof this.locale[lang_out].terms[termname]) {
9606            this.locale[lang_out].terms[termname] = {};
9607        }
9608        form = "long";
9609        genderform = false;
9610        //
9611        // Xml: get string value of form attribute, if any
9612        //
9613        if (myxml.getAttributeValue(term, 'form')) {
9614            form = myxml.getAttributeValue(term, 'form');
9615        }
9616        //
9617        // Xml: get string value of gender attribute, if any
9618        // 
9619        if (myxml.getAttributeValue(term, 'gender-form')) {
9620            genderform = myxml.getAttributeValue(term, 'gender-form');
9621        }
9622        //
9623        // Xml: set global gender assignment for variable associated
9624        // with term name
9625        // 
9626        if (myxml.getAttributeValue(term, 'gender')) {
9627            this.locale[lang_out]["noun-genders"][termname] = myxml.getAttributeValue(term, 'gender');
9628        }
9629        // Work on main segment or gender-specific sub-segment as appropriate
9630        if (genderform) {
9631            this.locale[lang_out].terms[termname][genderform] = {};
9632            this.locale[lang_out].terms[termname][genderform][form] = [];
9633            target = this.locale[lang_out].terms[termname][genderform];
9634            genderized_terms[termname] = true;
9635        } else {
9636            this.locale[lang_out].terms[termname][form] = [];
9637            target = this.locale[lang_out].terms[termname];
9638        }
9639        //
9640        // Xml: test of existence of node
9641        //
9642        if (myxml.numberofnodes(myxml.getNodesByName(term, 'multiple'))) {
9643            //
9644            // Xml: get string value of attribute, plus
9645            // Xml: get string value of node content
9646            //
9647            target[form][0] = myxml.getNodeValue(term, 'single');
9648            if (target[form][0].indexOf("%s") > -1) {
9649                this.opt.hasPlaceholderTerm = true;
9650            }
9651            //
9652            // Xml: get string value of attribute, plus
9653            // Xml: get string value of node content
9654            //
9655            target[form][1] = myxml.getNodeValue(term, 'multiple');
9656            if (target[form][1].indexOf("%s") > -1) {
9657                this.opt.hasPlaceholderTerm = true;
9658            }
9659        } else {
9660            //
9661            // Xml: get string value of attribute, plus
9662            // Xml: get string value of node content
9663            //
9664            target[form] = myxml.getNodeValue(term);
9665            if (target[form].indexOf("%s") > -1) {
9666                this.opt.hasPlaceholderTerm = true;
9667            }
9668        }
9669    }
9670    if (!this.locale[lang_out].terms.supplement) {
9671        this.locale[lang_out].terms.supplement = {};
9672    }
9673    if (!this.locale[lang_out].terms.supplement["long"]) {
9674        this.locale[lang_out].terms.supplement["long"] = ["supplement", "supplements"];
9675    }
9676    // If locale had a CSL 1.0.1-style ordinal definition, install the logic object
9677    // and iterate over gendered terms, filling in default values for use by getTerm.
9678    if (ordinals101_toggle) {
9679        for (var ikey in genderized_terms) {
9680            var gender_segments = {};
9681            var form_segments = 0;
9682            for (var jkey in this.locale[lang_out].terms[ikey]) {
9683                if (["masculine","feminine"].indexOf(jkey) > -1) {
9684                    gender_segments[jkey] = this.locale[lang_out].terms[ikey][jkey];
9685                } else {
9686                    form_segments += 1;
9687                }
9688            }
9689            if (!form_segments) {
9690                if (gender_segments.feminine) {
9691                    // Link each feminine form segment to default
9692                    // (no need to filter, these will not have gender segments mixed in)
9693                    for (var jkey in gender_segments.feminine) {
9694                        this.locale[lang_out].terms[ikey][jkey] = gender_segments.feminine[jkey];
9695                    }
9696                } else if (gender_segments.masculine) {
9697                    // Otherwise link each masculine form segment to default 
9698                    for (var jkey in gender_segments.masculine) {
9699                        this.locale[lang_out].terms[ikey][jkey] = gender_segments.masculine[jkey];
9700                    }
9701                }
9702            }
9703        }
9704        this.locale[lang_out].ord['1.0.1'] = ordinals101;
9705    }
9706
9707    // Iterate over main segments, and fill in any holes in gender-specific data
9708    // sub-segments
9709    for (termname in this.locale[lang_out].terms) {
9710        for (i = 0, ilen = 2; i < ilen; i += 1) {
9711            genderform = CSL.GENDERS[i];
9712            if (this.locale[lang_out].terms[termname][genderform]) {
9713                for (form in this.locale[lang_out].terms[termname]) {
9714                    if (!this.locale[lang_out].terms[termname][genderform][form]) {
9715                        this.locale[lang_out].terms[termname][genderform][form] = this.locale[lang_out].terms[termname][form];
9716                    }
9717                }
9718            }
9719        }
9720    }
9721    //
9722    // Xml: get list of nodes by node type
9723    //
9724    nodes = myxml.getNodesByName(locale, 'style-options');
9725    for (pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) {
9726        if (true) {
9727            styleopts = nodes[pos];
9728            //
9729            // Xml: get list of attributes on a node
9730            //
9731            attributes = myxml.attributes(styleopts);
9732            for (attrname in attributes) {
9733                if (attributes.hasOwnProperty(attrname)) {
9734                    if (attrname === "@punctuation-in-quote" || attrname === "@limit-day-ordinals-to-day-1") {
9735                        if (attributes[attrname] === "true") {
9736                            // trim off leading @
9737                            this.locale[lang_out].opts[attrname.slice(1)] = true;
9738                        } else {
9739                            // trim off leading @
9740                            this.locale[lang_out].opts[attrname.slice(1)] = false;
9741                        }
9742                    } else if (attrname === "@jurisdiction-preference") {
9743                        var jurisdiction_preference = attributes[attrname].split(/\s+/);
9744                        this.locale[lang_out].opts[attrname.slice(1)] = jurisdiction_preference;
9745                    } else if (attrname === "@skip-words") {
9746                        var skip_words = attributes[attrname].split(/\s*,\s*/);
9747                        this.locale[lang_out].opts[attrname.slice(1)] = skip_words;
9748                    } else if (attrname === "@leading-noise-words") {
9749                        var val = attributes[attrname].split(/\s*,\s*/);
9750                        this.locale[lang_out].opts["leading-noise-words"] = val;
9751                    } else if (attrname === "@name-as-sort-order") {
9752                        // Fallback is okay here.
9753                        this.locale[lang_out].opts["name-as-sort-order"] = {};
9754                        var lst = attributes[attrname].split(/\s+/);
9755                        for (var i=0,ilen=lst.length;i<ilen;i+=1) {
9756                            this.locale[lang_out].opts["name-as-sort-order"][lst[i]] = true;
9757                        }
9758                    } else if (attrname === "@name-as-reverse-order") {
9759                        // Fallback is okay here.
9760                        this.locale[lang_out].opts["name-as-reverse-order"] = {};
9761                        var lst = attributes[attrname].split(/\s+/);
9762                        for (var i=0,ilen=lst.length;i<ilen;i+=1) {
9763                            this.locale[lang_out].opts["name-as-reverse-order"][lst[i]] = true;
9764                        }
9765                    } else if (attrname === "@name-never-short") {
9766                        // Here too.
9767                        this.locale[lang_out].opts["name-never-short"] = {};
9768                        var lst = attributes[attrname].split(/\s+/);
9769                        for (var i=0,ilen=lst.length;i<ilen;i+=1) {
9770                            this.locale[lang_out].opts["name-never-short"][lst[i]] = true;
9771                        }
9772                    }
9773                }
9774            }
9775        }
9776    }
9777    //
9778    // Xml: get list of nodes by type
9779    //
9780    nodes = myxml.getNodesByName(locale, 'date');
9781    for (pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) {
9782        if (true) {
9783            var date = nodes[pos];
9784            //
9785            // Xml: get string value of attribute
9786            //
9787            this.locale[lang_out].dates[myxml.getAttributeValue(date, "form")] = date;
9788        }
9789    }
9790    //
9791    // Xml: get list of nodes by node type
9792    //
9793    CSL.SET_COURT_CLASSES(this, lang_out, myxml, locale);
9794};
9795
9796
9797CSL.getLocaleNames = function (myxml, preferredLocale) {
9798    var stylexml = CSL.setupXml(myxml);
9799
9800    function extendLocaleList(localeList, locale) {
9801        var forms = ["base", "best"];
9802        if (locale) {
9803            var normalizedLocale = CSL.localeResolve(locale);
9804            for (var i=0,ilen=forms.length;i<ilen;i++) {
9805                if (normalizedLocale[forms[i]] && localeList.indexOf(normalizedLocale[forms[i]]) === -1) {
9806                    localeList.push(normalizedLocale[forms[i]]);
9807                }
9808            }
9809        }
9810    }
9811    
9812    var localeIDs = ["en-US"];
9813    
9814    function sniffLocaleOnOneNodeName(nodeName) {
9815        var nodes = stylexml.getNodesByName(stylexml.dataObj, nodeName);
9816        for (var i=0,ilen=nodes.length;i<ilen;i++) {
9817            var nodeLocales = stylexml.getAttributeValue(nodes[i], "locale");
9818            if (nodeLocales) {
9819                nodeLocales = nodeLocales.split(/ +/);
9820                for (var j=0,jlen=nodeLocales.length;j<jlen;j++) {
9821                    this.extendLocaleList(localeIDs, nodeLocales[j]);
9822                }
9823            }
9824        }
9825    }
9826
9827    extendLocaleList(localeIDs, preferredLocale);
9828
9829    var styleNode = stylexml.getNodesByName(stylexml.dataObj, "style")[0];
9830    var defaultLocale = stylexml.getAttributeValue(styleNode, "default-locale");
9831    extendLocaleList(localeIDs, defaultLocale);
9832
9833    var nodeNames = ["layout", "if", "else-if", "condition"];
9834    for (var i=0,ilen=nodeNames.length;i<ilen;i++) {
9835        sniffLocaleOnOneNodeName(stylexml, localeIDs, nodeNames[i]);
9836    }
9837    return localeIDs;
9838};
9839
9840/*global CSL: true */
9841
9842CSL.Node = {};
9843
9844CSL.Node.bibliography = {
9845    build: function (state, target) {
9846        if (this.tokentype === CSL.START) {
9847
9848            state.build.area = "bibliography";
9849            state.build.root = "bibliography";
9850            state.build.extension = "";
9851
9852            var func = function(state) {
9853                state.tmp.area = "bibliography";
9854                state.tmp.root = "bibliography";
9855                state.tmp.extension = "";
9856            };
9857            this.execs.push(func);
9858
9859        }
9860        target.push(this);
9861    }
9862};
9863
9864
9865/*global CSL: true */
9866
9867CSL.Node.choose = {
9868    build: function (state, target) {
9869        var func;
9870        if (this.tokentype === CSL.START) {
9871            //open condition
9872            func = function (state) {
9873                state.tmp.jump.push(undefined, CSL.LITERAL);
9874            };
9875        }
9876        if (this.tokentype === CSL.END) {
9877            //close condition
9878            func = function (state) {
9879                state.tmp.jump.pop();
9880            };
9881        }
9882        this.execs.push(func);
9883        target.push(this);
9884    },
9885
9886    configure: function (state, pos) {
9887        if (this.tokentype === CSL.END) {
9888            state.configure.fail.push((pos));
9889            state.configure.succeed.push((pos));
9890        } else {
9891            state.configure.fail.pop();
9892            state.configure.succeed.pop();
9893        }
9894    }
9895};
9896
9897/*global CSL: true */
9898
9899CSL.Node.citation = {
9900    build: function (state, target) {
9901        if (this.tokentype === CSL.START) {
9902
9903            state.build.area = "citation";
9904            state.build.root = "citation";
9905            state.build.extension = "";
9906
9907
9908            var func = function(state) {
9909                state.tmp.area = "citation";
9910                state.tmp.root = "citation";
9911                state.tmp.extension = "";
9912            };
9913            this.execs.push(func);
9914
9915/*
9916            state.build.root = "citation";
9917
9918            OK state.fixOpt(this, "names-delimiter", "delimiter");
9919            OK state.fixOpt(this, "name-delimiter", "delimiter");
9920            OK state.fixOpt(this, "name-form", "form");
9921            OK state.fixOpt(this, "and", "and");
9922            OK state.fixOpt(this, "delimiter-precedes-last", "delimiter-precedes-last");
9923            OK state.fixOpt(this, "delimiter-precedes-et-al", "delimiter-precedes-et-al");
9924            OK state.fixOpt(this, "initialize-with", "initialize-with");
9925            OK state.fixOpt(this, "initialize", "initialize");
9926            OK state.fixOpt(this, "name-as-sort-order", "name-as-sort-order");
9927            OK state.fixOpt(this, "sort-separator", "sort-separator");
9928
9929            OK state.fixOpt(this, "et-al-min", "et-al-min");
9930            OK state.fixOpt(this, "et-al-use-first", "et-al-use-first");
9931            OK state.fixOpt(this, "et-al-use-last", "et-al-use-last");
9932            state.fixOpt(this, "et-al-subsequent-min", "et-al-subsequent-min");
9933            state.fixOpt(this, "et-al-subsequent-use-first", "et-al-subsequent-use-first");
9934*/
9935        }
9936        if (this.tokentype === CSL.END) {
9937
9938            // Open an extra key at first position for use in
9939            // grouped sorts.
9940            // print("in cs:citation END");
9941            state.opt.grouped_sort = state.opt.xclass === "in-text" 
9942                && (state.citation.opt.collapse 
9943                    && state.citation.opt.collapse.length)
9944                || (state.citation.opt.cite_group_delimiter
9945                    && state.citation.opt.cite_group_delimiter.length)
9946                && state.opt.update_mode !== CSL.POSITION
9947                && state.opt.update_mode !== CSL.NUMERIC;
9948            
9949            if (state.opt.grouped_sort 
9950                && state.citation_sort.opt.sort_directions.length) {
9951                
9952                var firstkey = state.citation_sort.opt.sort_directions[0].slice();
9953                //print("extending sort keys "+state.citation_sort.opt.sort_directions+" with "+firstkey);
9954                state.citation_sort.opt.sort_directions = [firstkey].concat(state.citation_sort.opt.sort_directions);
9955                // print("new key directions in effect: "+state.citation_sort.opt.sort_directions);
9956            }
9957            // print("creating new comparifier");
9958            state.citation.srt = new CSL.Registry.Comparifier(state, "citation_sort");
9959        }
9960        target.push(this);
9961    }
9962};
9963
9964
9965/*global CSL: true */
9966
9967CSL.Node["#comment"] = {
9968       // This is a comment in the CSL file.
9969       build: function () {
9970        // Save some space in the log files -- no need to mention this, really.
9971        // CSL.debug("CSL processor warning: comment node reached");
9972       }
9973};
9974
9975/*global CSL: true */
9976
9977CSL.Node.date = {
9978    build: function (state, target) {
9979        var func, date_obj, len, pos, part, dpx, parts, mypos, start, end;
9980        if (this.tokentype === CSL.START || this.tokentype === CSL.SINGLETON) {
9981            // used to collect rendered date part names in node_datepart,
9982            // for passing through to node_key, for use in dates embedded
9983            // in macros
9984            state.dateput.string(state, state.dateput.queue);
9985            state.tmp.date_token = CSL.Util.cloneToken(this);
9986            state.tmp.date_token.strings.prefix = "";
9987            state.tmp.date_token.strings.suffix = "";
9988            state.dateput.openLevel(this);
9989            state.build.date_parts = [];
9990            state.build.date_variables = this.variables;
9991            if (!state.build.extension) {
9992                CSL.Util.substituteStart.call(this, state, target);
9993            }
9994            if (state.build.extension) {
9995                func = CSL.dateMacroAsSortKey;
9996            } else {
9997                func = function (state, Item, item) {
9998                    var dp;
9999                    state.tmp.element_rendered_ok = false;
10000                    state.tmp.donesies = [];
10001                    state.tmp.dateparts = [];
10002                    dp = [];
10003                    //if (this.variables.length && Item[this.variables[0]]){
10004                    if (this.variables.length
10005                        && !(state.tmp.just_looking
10006                             && this.variables[0] === "accessed")) {
10007                        
10008                        date_obj = Item[this.variables[0]];
10009                        if ("undefined" === typeof date_obj) {
10010                            date_obj = {"date-parts": [[0]] };
10011                            if (state.opt.development_extensions.locator_date_and_revision) {
10012                                if (item && this.variables[0] === "locator-date" && item["locator-date"]) {
10013                                    date_obj = item["locator-date"];
10014                                }
10015                            }
10016                        }
10017                        state.tmp.date_object = date_obj;
10018                        //
10019                        // Call a function here to analyze the
10020                        // data and set the name of the date-part that
10021                        // should collapse for this range, if any.
10022                        //
10023                        // (1) build a filtered list, in y-m-d order,
10024                        // consisting only of items that are (a) in the
10025                        // date-parts and (b) in the *_end data.
10026                        // (note to self: remember that season is a
10027                        // fallback var when month and day are empty)
10028                        
10029                        //if ("undefined" === typeof this.dateparts) {
10030                        //    this.dateparts = ["year", "month", "day"];
10031                        //}
10032                        len = this.dateparts.length;
10033                        for (pos = 0; pos < len; pos += 1) {
10034                            part = this.dateparts[pos];
10035                            if ("undefined" !== typeof state.tmp.date_object[(part +  "_end")]) {
10036                                dp.push(part);
10037                            } else if (part === "month" && "undefined" !== typeof state.tmp.date_object.season_end) {
10038                                dp.push(part);
10039                            }
10040                        }
10041                        dpx = [];
10042                        parts = ["year", "month", "day"];
10043                        len = parts.length;
10044                        for (pos = 0; pos < len; pos += 1) {
10045                            if (dp.indexOf(parts[pos]) > -1) {
10046                                dpx.push(parts[pos]);
10047                            }
10048                        }
10049                        dp = dpx.slice();
10050                        //
10051                        // (2) Reverse the list and step through in
10052                        // reverse order, popping each item if the
10053                        // primary and *_end data match.
10054                        mypos = 2;
10055                        len = dp.length;
10056                        for (pos = 0; pos < len; pos += 1) {
10057                            part = dp[pos];
10058                            start = state.tmp.date_object[part];
10059                            end = state.tmp.date_object[(part + "_end")];
10060                            if (start !== end) {
10061                                mypos = pos;
10062                                break;
10063                            }
10064                        }
10065                        
10066                        //
10067                        // (3) When finished, the first item in the
10068                        // list, if any, is the date-part where
10069                        // the collapse should occur.
10070
10071                        // XXXXX: was that it?
10072                        state.tmp.date_collapse_at = dp.slice(mypos);
10073                        //
10074                        // The collapse itself will be done by appending
10075                        // string output for the date, less suffix,
10076                        // placing a delimiter on output, then then
10077                        // doing the *_end of the range, dropping only
10078                        // the prefix.  That should give us concise expressions
10079                        // of ranges.
10080                        //
10081                        // Numeric dates should not collapse, though,
10082                        // and should probably use a slash delimiter.
10083                        // Scope for configurability will remain (all over
10084                        // the place), but this will do to get this feature
10085                        // started.
10086                        //
10087                    } else {
10088                        state.tmp.date_object = false;
10089                    }
10090                };
10091            }
10092            this.execs.push(func);
10093
10094            // newoutput
10095            func = function (state, Item) {
10096                if (!Item[this.variables[0]]) {
10097                    return;
10098                }
10099                state.output.startTag("date", this);
10100                if (this.variables[0] === "issued"
10101                    && (Item.type === "legal_case" || Item.type === "legislation")
10102                    && state.opt.disable_duplicate_year_suppression.indexOf(Item.country) === -1
10103                    && !state.tmp.extension
10104                    && "" + Item["collection-number"] === "" + state.tmp.date_object.year
10105                    && this.dateparts.length === 1
10106                    && this.dateparts[0] === "year") {
10107
10108                    // Set up to (maybe) suppress the year if we're not sorting, and
10109                    // it's the same as the collection-number, and we would render
10110                    // only the year, with not month or day, and this is a legal_case item.
10111                    // We save a pointer to the blob parent and its position here. The
10112                    // blob will be popped from output if at the end of processing for
10113                    // this cite we find that we have rendered the collection-number
10114                    // variable also.
10115                    for (var key in state.tmp.date_object) {
10116                        if (state.tmp.date_object.hasOwnProperty(key)) {
10117                            if (key.slice(0, 4) === "year") {
10118                                state.tmp.issued_date = {};
10119                                var lst = state.output.current.mystack.slice(-2)[0].blobs;
10120                                state.tmp.issued_date.list = lst;
10121                                state.tmp.issued_date.pos = lst.length - 1;
10122                            }
10123                        }
10124                    }
10125                }
10126            };
10127            this.execs.push(func);
10128        }
10129
10130        if (!state.build.extension && (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON)) {
10131            // mergeoutput
10132            func = function (state, Item) {
10133                if (!Item[this.variables[0]]) {
10134                    return;
10135                }
10136                state.output.endTag();
10137            };
10138            this.execs.push(func);
10139        }
10140        target.push(this);
10141
10142        if (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON) {
10143            if (!state.build.extension) {
10144                CSL.Util.substituteEnd.call(this, state, target);
10145            }
10146        }
10147    }
10148};
10149
10150/*global CSL: true */
10151
10152CSL.Node["date-part"] = {
10153    build: function (state, target) {
10154        var func, pos, len, first_date, value, value_end, real, have_collapsed, invoked, precondition, known_year, bc, ad, bc_end, ad_end, ready, curr, dcurr, number, num, formatter, item, blob;
10155        if (!this.strings.form) {
10156            this.strings.form = "long";
10157        }
10158        // used in node_date, to send a list of rendering date parts
10159        // to node_key, for dates embedded in macros.
10160        state.build.date_parts.push(this.strings.name);
10161        //
10162        // Set delimiter here, if poss.
10163        //
10164
10165        var date_variable = state.build.date_variables[0];
10166
10167        function formatAndStrip(myform, gender, val) {
10168            if (!val) {
10169                return val;
10170            }
10171            val = "" + CSL.Util.Dates[this.strings.name][myform](state, val, gender, this.default_locale);
10172            if ("month" === this.strings.name) {
10173                if (state.tmp.strip_periods) {
10174                    val = val.replace(/\./g, "");
10175                } else {
10176                    for (var i = 0, ilen = this.decorations.length; i < ilen; i += 1) {
10177                        if ("@strip-periods" === this.decorations[i][0] && "true" === this.decorations[i][1]) {
10178                            val = val.replace(/\./g, "");
10179                            break;
10180                        }
10181                    }
10182                }
10183            }
10184            return val;
10185        }
10186
10187        func = function (state, Item) {
10188
10189            if (!state.tmp.date_object) {
10190                return;
10191            } else {
10192                state.tmp.probably_rendered_something = true;
10193            }
10194
10195            var last_string_output = "";
10196
10197            first_date = true;
10198            value = "";
10199            value_end = "";
10200            state.tmp.donesies.push(this.strings.name);
10201
10202            // Render literal only when year is included in date output
10203            if (state.tmp.date_object.literal && "year" === this.strings.name) {
10204                last_string_output = state.tmp.date_object.literal;
10205                state.output.append(state.tmp.date_object.literal, this);
10206            }
10207
10208            if (state.tmp.date_object) {
10209                value = state.tmp.date_object[this.strings.name];
10210                value_end = state.tmp.date_object[(this.strings.name + "_end")];
10211            }
10212            if ("year" === this.strings.name && value === 0 && !state.tmp.suppress_decorations) {
10213                value = false;
10214            }
10215            real = !state.tmp.suppress_decorations;
10216            have_collapsed = state.tmp.have_collapsed;
10217            invoked = state[state.tmp.area].opt.collapse === "year-suffix" || state[state.tmp.area].opt.collapse === "year-suffix-ranged";
10218            precondition = state.opt["disambiguate-add-year-suffix"];
10219            if (real && precondition && invoked) {
10220                state.tmp.years_used.push(value);
10221                known_year = state.tmp.last_years_used.length >= state.tmp.years_used.length;
10222                if (known_year && have_collapsed) {
10223                    if (state.tmp.last_years_used[(state.tmp.years_used.length - 1)] === value) {
10224                        value = false;
10225                    }
10226                }
10227            }
10228            if ("undefined" !== typeof value) {
10229                bc = false;
10230                ad = false;
10231                bc_end = false;
10232                ad_end = false;
10233                if ("year" === this.strings.name) {
10234                    if (parseInt(value, 10) < 500 && parseInt(value, 10) > 0) {
10235                        ad = state.getTerm("ad");
10236                    }
10237                    if (parseInt(value, 10) < 0) {
10238                        bc = state.getTerm("bc");
10239                        value = (parseInt(value, 10) * -1);
10240                    }
10241                    if (value_end) {
10242                        if (parseInt(value_end, 10) < 500 && parseInt(value_end, 10) > 0) {
10243                            ad_end = state.getTerm("ad");
10244                        }
10245                        if (parseInt(value_end, 10) < 0) {
10246                            bc_end = state.getTerm("bc");
10247                            value_end = (parseInt(value_end, 10) * -1);
10248                        }
10249                    }
10250                }
10251
10252                // For gendered locales
10253                var monthnameid = ""+state.tmp.date_object.month;
10254                while (monthnameid.length < 2) {
10255                    monthnameid = "0"+monthnameid;
10256                }
10257                monthnameid = "month-"+monthnameid;
10258                var gender = state.locale[state.opt.lang]["noun-genders"][monthnameid];
10259                if (this.strings.form) {
10260                    var myform = this.strings.form;
10261                    var myform_end = this.strings.form;
10262                    if (this.strings.name === "day") {
10263                        if (myform === "ordinal" && state.locale[state.opt.lang].opts["limit-day-ordinals-to-day-1"]) {
10264                            if (value != 1) {
10265                                myform = "numeric";
10266                            }
10267                            if (value_end != 1) {
10268                                myform_end = "numeric";
10269                            }
10270                        }
10271                    }
10272                    value = formatAndStrip.call(this, myform, gender, value);
10273                    value_end = formatAndStrip.call(this, myform_end, gender, value_end);
10274                }
10275                state.output.openLevel("empty");
10276                if (state.tmp.date_collapse_at.length) {
10277                    //state.output.startTag(this.strings.name,this);
10278                    ready = true;
10279                    len = state.tmp.date_collapse_at.length;
10280                    for (pos = 0; pos < len; pos += 1) {
10281                        item = state.tmp.date_collapse_at[pos];
10282                        if (state.tmp.donesies.indexOf(item) === -1) {
10283                            ready = false;
10284                            break;
10285                        }
10286                    }
10287                    if (ready) {
10288                        if ("" + value_end !== "0") {
10289                            if (state.dateput.queue.length === 0) {
10290                                first_date = true;
10291                            }
10292
10293                            // OK! So if the actual data has no month, day or season,
10294                            // and we reach this block, then we can combine the dates
10295                            // to a string, run minimial-two, and output the trailing
10296                            // year right here. No impact on other functionality.
10297
10298                            if (state.opt["year-range-format"]
10299                                && state.opt["year-range-format"] !== "expanded"
10300                                && !state.tmp.date_object.day
10301                                && !state.tmp.date_object.month
10302                                && !state.tmp.date_object.season
10303                                && this.strings.name === "year"
10304                                && value && value_end) {
10305
10306                                // second argument adjusts collapse as required for years
10307                                // See OSCOLA section 1.3.2
10308                                value_end = state.fun.year_mangler(value + "-" + value_end, true);
10309                                var range_delimiter = state.getTerm("year-range-delimiter");
10310                                value_end = value_end.slice(value_end.indexOf(range_delimiter) + 1);
10311                            }
10312                            last_string_output = value_end;
10313                            state.dateput.append(value_end, this);
10314                            if (first_date) {
10315                                blob = state.dateput.current.value().blobs[0];
10316                                if (blob) {
10317                                    blob.strings.prefix = "";
10318                                }
10319
10320                            }
10321                        }
10322                        last_string_output = value;
10323                        state.output.append(value, this);
10324                        curr = state.output.current.value();
10325                        blob = curr.blobs[(curr.blobs.length - 1)];
10326                        if (blob) {
10327                            blob.strings.suffix = "";
10328                        }
10329
10330                        if (this.strings["range-delimiter"]) {
10331                            state.output.append(this.strings["range-delimiter"]);
10332                        } else {
10333                            state.output.append(state.getTerm("year-range-delimiter"), "empty");
10334                        }
10335                        state.dateput.closeLevel();
10336                        dcurr = state.dateput.current.value();
10337                        curr.blobs = curr.blobs.concat(dcurr);
10338                        // This may leave the stack pointer on a lower level.
10339                        // It's not a problem because the stack will be clobbered
10340                        // when the queue is initialized by the next cs:date node.
10341                        state.dateput.string(state, state.dateput.queue);
10342                        state.dateput.openLevel(state.tmp.date_token);
10343                        state.tmp.date_collapse_at = [];
10344                    } else {
10345                        last_string_output = value;
10346                        state.output.append(value, this);
10347                        // print("collapse_at: "+state.tmp.date_collapse_at);
10348                        if (state.tmp.date_collapse_at.indexOf(this.strings.name) > -1) {
10349                            //
10350                            // Use ghost dateput queue
10351                            //
10352                            if ("" + value_end !== "0") {
10353                                //
10354                                // XXXXX: It's a workaround.  It's ugly.
10355                                // There's another one above.
10356                                //
10357                                if (state.dateput.queue.length === 0) {
10358                                    first_date = true;
10359                                }
10360                                state.dateput.openLevel("empty");
10361                                last_string_output = value_end;
10362                                state.dateput.append(value_end, this);
10363                                if (first_date) {
10364                                    blob = state.dateput.current.value().blobs[0];
10365                                    if (blob) {
10366                                        blob.strings.prefix = "";
10367                                    }
10368                                }
10369                                if (bc) {
10370                                    last_string_output = bc;
10371                                    state.dateput.append(bc);
10372                                }
10373                                if (ad) {
10374                                    last_string_output = ad;
10375                                    state.dateput.append(ad);
10376                                }
10377                                state.dateput.closeLevel();
10378                            }
10379                        }
10380                    }
10381                } else {
10382                    last_string_output = value;
10383                    state.output.append(value, this);
10384                }
10385
10386                if (bc) {
10387                    last_string_output = bc;
10388                    state.output.append(bc);
10389                }
10390                if (ad) {
10391                    last_string_output = ad;
10392                    state.output.append(ad);
10393                }
10394                state.output.closeLevel();
10395                //state.output.endTag();
10396            } else if ("month" === this.strings.name) {
10397                // XXX The simpler solution here will be to
10398                // directly install season and season_end on
10399                // month, with a value of 13, 14, 15, 16, or
10400                // (to allow correct ranging with Down Under
10401                // dates) 17 or 18.  That will allow ranging
10402                // to take place in the normal way.  With this
10403                // "approach", it doesn't.
10404                //
10405                // No value for this target variable
10406                //
10407                if (state.tmp.date_object.season) {
10408                    value = "" + state.tmp.date_object.season;
10409                    if (value && value.match(/^[1-4]$/)) {
10410                        // XXXXXXXXXXXXXXXXXXX was replace([false, false, true]);
10411                        //state.tmp.group_context.replace([false, false, true]);
10412                        state.tmp.group_context.tip.variable_success = true;
10413                        last_string_output = "winter";
10414                        state.output.append(state.getTerm(("season-0" + value)), this);
10415                    } else if (value) {
10416                        last_string_output = value;
10417                        state.output.append(value, this);
10418                    }
10419                }
10420            }
10421            state.tmp.value = [];
10422            if (Item[date_variable] && (value || state.tmp.have_collapsed) && !state.opt.has_year_suffix && "year" === this.strings.name && !state.tmp.just_looking) {
10423                if (state.registry.registry[Item.id] && state.registry.registry[Item.id].disambig.year_suffix !== false && !state.tmp.has_done_year_suffix) {
10424                    state.tmp.has_done_year_suffix = true;
10425                    last_string_output = "x";
10426                    num = parseInt(state.registry.registry[Item.id].disambig.year_suffix, 10);
10427                    // first argument is for number particle [a-zA-Z], never present on dates
10428                    number = new CSL.NumericBlob(state, false, num, this, Item.id);
10429                    this.successor_prefix = state[state.build.area].opt.layout_delimiter;
10430                    this.splice_prefix = state[state.build.area].opt.layout_delimiter;
10431                    formatter = new CSL.Util.Suffixator(CSL.SUFFIX_CHARS);
10432                    number.setFormatter(formatter);
10433                    if (state[state.tmp.area].opt.collapse === "year-suffix-ranged") {
10434                        number.range_prefix = state.getTerm("citation-range-delimiter");
10435                    }
10436                    if (state[state.tmp.area].opt.cite_group_delimiter) {
10437                        number.successor_prefix = state[state.tmp.area].opt.cite_group_delimiter;
10438                    } else if (state[state.tmp.area].opt["year-suffix-delimiter"]) {
10439                        number.successor_prefix = state[state.tmp.area].opt["year-suffix-delimiter"];
10440                    } else {
10441                        number.successor_prefix = state[state.tmp.area].opt.layout_delimiter;
10442                    }
10443                    number.UGLY_DELIMITER_SUPPRESS_HACK = true;
10444                    state.output.append(number, "literal");
10445                }
10446            }
10447            if (last_string_output && !state.tmp.group_context.tip.condition) {
10448                state.tmp.just_did_number = last_string_output.match(/[0-9]$/);
10449                if (state.output.current.tip.strings.suffix) {
10450                    state.tmp.just_did_number = false;
10451                }
10452            }
10453        };
10454        this.execs.push(func);
10455        target.push(this);
10456    }
10457};
10458
10459/*global CSL: true */
10460
10461CSL.Node["else-if"] = {
10462    //
10463    // these function are the same as those in if, might just clone
10464    build: function (state, target) {
10465        CSL.Conditions.TopNode.call(this, state, target);
10466        target.push(this);
10467    },
10468    configure: function (state, pos) {
10469        CSL.Conditions.Configure.call(this, state, pos);
10470    }
10471};
10472
10473/*global CSL: true */
10474
10475CSL.Node["else"] = {
10476    build: function (state, target) {
10477        target.push(this);
10478    },
10479    configure: function (state, pos) {
10480        if (this.tokentype === CSL.START) {
10481            state.configure.fail[(state.configure.fail.length - 1)] = pos;
10482        }
10483    }
10484};
10485
10486
10487/*global CSL: true */
10488
10489CSL.Node["et-al"] = {
10490    build: function (state, target) {
10491        if (state.build.area === "citation" || state.build.area === "bibliography") {
10492            var func = function (state) {
10493                state.tmp.etal_node = this;
10494                if ("string" === typeof this.strings.term) {
10495                    state.tmp.etal_term = this.strings.term;
10496                }
10497            };
10498            this.execs.push(func);
10499        }
10500        target.push(this);
10501    }
10502};
10503
10504/*global CSL: true */
10505
10506CSL.Node.group = {
10507    build: function (state, target, realGroup) {
10508        var func, execs;
10509        this.realGroup = realGroup;
10510        if (this.tokentype === CSL.START) {
10511            CSL.Util.substituteStart.call(this, state, target);
10512            if (state.build.substitute_level.value()) {
10513                state.build.substitute_level.replace((state.build.substitute_level.value() + 1));
10514            }
10515            if (!this.juris) {
10516                target.push(this);
10517            }
10518
10519            // newoutput
10520            func = function (state) {
10521                state.output.startTag("group", this);
10522                
10523                if (this.strings.label_form_override) {
10524                    if (!state.tmp.group_context.tip.label_form) {
10525                        state.tmp.group_context.tip.label_form = this.strings.label_form_override;
10526                    }
10527                }
10528                
10529                if (this.strings.label_capitalize_if_first_override) {
10530                    if (!state.tmp.group_context.tip.label_capitalize_if_first) {
10531                        state.tmp.group_context.tip.label_capitalize_if_first = this.strings.label_capitalize_if_first_override;
10532                    }
10533                }
10534                
10535                if (this.realGroup) {
10536                    
10537                    if (state.tmp.group_context.tip.condition) {
10538                        CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, this.strings.prefix, null, this);
10539                    }
10540                    
10541                    var condition = false;
10542                    var force_suppress = false;
10543
10544                    // XXX Can we do something better for length here?
10545                    if (state.tmp.group_context.mystack.length) {
10546                        state.output.current.value().parent = state.tmp.group_context.tip.output_tip;
10547                    }
10548                    
10549                    // fieldcontextflag
10550                    var label_form = state.tmp.group_context.tip.label_form;
10551                    if (!label_form) {
10552                        label_form = this.strings.label_form_override;
10553                    }
10554                    
10555                    var label_capitalize_if_first = state.tmp.group_context.tip.label_capitalize_if_first;
10556                    if (!label_capitalize_if_first) {
10557                        label_capitalize_if_first = this.strings.label_capitalize_if_first;
10558                    }
10559                    if (state.tmp.group_context.tip.condition) {
10560                        condition = state.tmp.group_context.tip.condition;
10561                        force_suppress = state.tmp.group_context.tip.force_suppress;
10562                        //force_suppress: false;
10563                    } else if (this.strings.reject) {
10564                        condition = {
10565                            test: this.strings.reject,
10566                            not: true
10567                        };
10568                    } else if (this.strings.require) {
10569                        condition = {
10570                            test: this.strings.require,
10571                            not: false
10572                        };
10573                    }
10574                    var context = {
10575                        old_term_predecessor: state.tmp.term_predecessor,
10576                        term_intended: false,
10577                        variable_attempt: false,
10578                        variable_success: false,
10579                        variable_success_parent: state.tmp.group_context.tip.variable_success,
10580                        output_tip: state.output.current.tip,
10581                        label_form: label_form,
10582                        label_static: state.tmp.group_context.tip.label_static,
10583                        label_capitalize_if_first: label_capitalize_if_first,
10584                        parallel_delimiter_override: this.strings.set_parallel_delimiter_override,
10585                        parallel_delimiter_override_on_suppress: this.strings.set_parallel_delimiter_override_on_suppress,
10586                        condition: condition,
10587                        force_suppress: force_suppress,
10588                        done_vars: state.tmp.group_context.tip.done_vars.slice()
10589                    };
10590                    if(this.non_parallel) {
10591                        var non_parallel = state.tmp.group_context.tip.non_parallel;
10592                        if (!non_parallel) {
10593                            non_parallel = {};
10594                        }
10595                        Object.assign(non_parallel, this.non_parallel);
10596                        context.non_parallel = non_parallel;
10597                    }
10598                    if(this.parallel_first) {
10599                        var parallel_first = state.tmp.group_context.tip.parallel_first;
10600                        if (!parallel_first) {
10601                            parallel_first = {};
10602                        }
10603                        Object.assign(parallel_first, this.parallel_first);
10604                        context.parallel_first = parallel_first;
10605                    }
10606                    if(this.parallel_last) {
10607                        var parallel_last = state.tmp.group_context.tip.parallel_last;
10608                        if (!parallel_last) {
10609                            parallel_last = {};
10610                        }
10611                        Object.assign(parallel_last, this.parallel_last);
10612                        context.parallel_last = parallel_last;
10613                    }
10614                    if (state.tmp.abbrev_trimmer && state.tmp.abbrev_trimmer.LAST_TO_FIRST && context.parallel_last) {
10615                        if (!context.parallel_first) {
10616                            context.parallel_first = {};
10617                        }
10618                        for (var varname in state.tmp.abbrev_trimmer.LAST_TO_FIRST) {
10619                            if (context.parallel_last[varname]) {
10620                                context.parallel_first[varname] = true;
10621                                delete context.parallel_last[varname];
10622                            }
10623                        }
10624                    }
10625                    
10626                    /*
10627                    if(this.parallel_last_override) {
10628                        var parallel_last_override = state.tmp.group_context.tip.parallel_last_override;
10629                        if (!parallel_last_override) {
10630                            parallel_last_override = {};
10631                        }
10632                        Object.assign(parallel_last_override, this.parallel_last_override);
10633                        context.parallel_last_override = parallel_last_override;
10634                    }
10635                     */
10636                    state.tmp.group_context.push(context);
10637
10638                    if (state.tmp.abbrev_trimmer && this.parallel_last_to_first) {
10639                        if (!state.tmp.abbrev_trimmer.LAST_TO_FIRST) {
10640                            state.tmp.abbrev_trimmer.LAST_TO_FIRST = {};
10641                        }
10642                        for (var varname in this.parallel_last_to_first) {
10643                            state.tmp.abbrev_trimmer.LAST_TO_FIRST[varname] = true;
10644                        }
10645                    }
10646                    
10647                    //if (!state.tmp.just_looking) {
10648                    //    print("       flags="+JSON.stringify(state.tmp.group_context.tip, params))
10649                    //}
10650                }
10651            };
10652            //
10653            // Paranoia.  Assure that this init function is the first executed.
10654            execs = [];
10655            execs.push(func);
10656            this.execs = execs.concat(this.execs);
10657
10658            // "Special handling" for nodes that contain only
10659            // publisher and place, with no affixes. For such
10660            // nodes only, parallel publisher/place pairs
10661            // will be parsed out and properly joined, piggybacking on
10662            // join parameters set on cs:citation or cs:bibliography.
10663            if (this.strings["has-publisher-and-publisher-place"]) {
10664                // Pass variable string values to the closing
10665                // tag via a global, iff they conform to expectations.
10666                state.build["publisher-special"] = true;
10667                if (this.strings["subgroup-delimiter"]) {
10668                    // Set the handling function only if name-delimiter
10669                    // is set on the parent cs:citation or cs:bibliography
10670                    // node.
10671                    func = function (state, Item) {
10672                        if (Item.publisher && Item["publisher-place"]) {
10673                            var publisher_lst = Item.publisher.split(/;\s*/);
10674                            var publisher_place_lst = Item["publisher-place"].split(/;\s*/);
10675                            if (publisher_lst.length > 1
10676                                && publisher_lst.length === publisher_place_lst.length) {
10677                                state.publisherOutput = new CSL.PublisherOutput(state, this);
10678                                state.publisherOutput["publisher-list"] = publisher_lst;
10679                                state.publisherOutput["publisher-place-list"] = publisher_place_lst;
10680                            }
10681                        }
10682                    };
10683                    this.execs.push(func);
10684                }
10685            }
10686
10687            if (this.juris) {
10688                // "Special handling" for jurisdiction macros
10689                // We try to instantiate these as standalone token lists.
10690                // If available, the token list is executed,
10691                // the result is written directly into output,
10692                // and control returns here.
10693
10694                // So we'll have something like this:
10695                // * expandMacro() in util_node.js flags juris- macros
10696                //   on build. [DONE]
10697                // * Those are picked up here, and
10698                //   - A runtime function attempts to fetch and instantiate
10699                //     the macros in separate token lists under a segment
10700                //     opened for the jurisdiction. We assume that the
10701                //     jurisdiction has a full set of macros. That will need
10702                //     to be enforced by validation. [DONE HERE, function is TODO]
10703                //   - Success or failure is marked in a runtime flag object
10704                //     (in citeproc.opt). [DONE]
10705                //   - After the instantiation function comes a test, for
10706                //     juris- macros only, which either runs diverted code,
10707                //     or proceeds as per normal through the token list. [TODO]
10708                // I think that's all there is to it.
10709                
10710                // Code for fetching an instantiating?
10711
10712                var choose_start = new CSL.Token("choose", CSL.START);
10713                CSL.Node.choose.build.call(choose_start, state, target);
10714                
10715                var if_start = new CSL.Token("if", CSL.START);
10716
10717                func = (function (macroName) {
10718                    return function (Item, item) {
10719                        return CSL.INIT_JURISDICTION_MACROS(state, Item, item, macroName);
10720                    }
10721                }(this.juris));
10722                
10723                if_start.tests ? {} : if_start.tests = [];
10724                if_start.tests.push(func);
10725                if_start.test = state.fun.match.any(if_start, state, if_start.tests);
10726                target.push(if_start);
10727                var text_node = new CSL.Token("text", CSL.SINGLETON);
10728                func = function (state, Item, item) {
10729                    // This will run the juris- token list.
10730                    var itemItem = Item;
10731                    if (item && item["best-jurisdiction"] && this.juris === "juris-locator") {
10732                        itemItem = item;
10733                    }
10734                    var next = 0;
10735                    if (state.juris[itemItem["best-jurisdiction"]][this.juris]) {
10736                        while (next < state.juris[itemItem["best-jurisdiction"]][this.juris].length) {
10737                            next = CSL.tokenExec.call(state, state.juris[itemItem["best-jurisdiction"]][this.juris][next], Item, item);
10738                        }
10739                    }
10740                };
10741                text_node.juris = this.juris;
10742                text_node.execs.push(func);
10743                target.push(text_node);
10744
10745                var if_end = new CSL.Token("if", CSL.END);
10746                CSL.Node["if"].build.call(if_end, state, target);
10747                var else_start = new CSL.Token("else", CSL.START);
10748                CSL.Node["else"].build.call(else_start, state, target);
10749            }
10750        }
10751
10752        if (this.tokentype === CSL.END) {
10753            
10754            // Unbundle and print publisher lists
10755            // Same constraints on creating the necessary function here
10756            // as above. The full content of the group formatting token
10757            // is apparently not available on the closing tag here,
10758            // hence the global flag on state.build.
10759            if (state.build["publisher-special"]) {
10760                state.build["publisher-special"] = false;
10761                func = function (state) {
10762                    if (state.publisherOutput) {
10763                        state.publisherOutput.render();
10764                        state.publisherOutput = false;
10765                    }
10766                };
10767                this.execs.push(func);
10768            }
10769            
10770            // quashnonfields
10771            func = function (state, Item, item) {
10772                if (!state.tmp.group_context.tip.condition) {
10773                    if (state.output.current.tip.strings.suffix) {
10774                        state.tmp.just_did_number = false;
10775                    }
10776                }
10777                state.output.endTag();
10778                if (this.realGroup) {
10779                    var flags = state.tmp.group_context.pop();
10780                    if (flags.parallel_delimiter_override) {
10781                        state.tmp.group_context.tip.parallel_delimiter_override = flags.parallel_delimiter_override;
10782                        if (!state.tmp.just_looking && state.registry.registry[Item.id].master) {
10783                            state.registry.registry[Item.id].parallel_delimiter_override = flags.parallel_delimiter_override;
10784                        }
10785                    }
10786                    if (flags.parallel_delimiter_override_on_suppress) {
10787                        state.tmp.group_context.tip.parallel_delimiter_override_on_suppress = flags.parallel_delimiter_override_on_suppress;
10788                    }
10789                    if (state.tmp.area === "bibliography_sort") {
10790                        var citationNumberIdx = flags.done_vars.indexOf("citation-number");
10791                        if (this.strings.sort_direction && citationNumberIdx > -1 && state.tmp.group_context.length() == 1) {
10792                            if (this.strings.sort_direction === CSL.DESCENDING) {
10793                                state.bibliography_sort.opt.citation_number_sort_direction = CSL.DESCENDING;
10794                            } else {
10795                                state.bibliography_sort.opt.citation_number_sort_direction = CSL.ASCENDING;
10796                            }
10797                            flags.done_vars = flags.done_vars.slice(0, citationNumberIdx).concat(flags.done_vars.slice(citationNumberIdx + 1))
10798                        }
10799                    }
10800                    //var params = ["condition", "variable_success", "force_suppress","term_intended", "variable_attempt"]
10801                    //if (!state.tmp.just_looking) {
10802                    //    print("POP parent="+JSON.stringify(state.tmp.group_context.tip, params))
10803                    //    print("    flags="+JSON.stringify(flags, params));
10804                    //}
10805                    if (flags.condition) {
10806                        flags.force_suppress = CSL.EVALUATE_GROUP_CONDITION(state, flags);
10807                    }
10808                    if (state.tmp.group_context.tip.condition) {
10809                        state.tmp.group_context.tip.force_suppress = flags.force_suppress;
10810                    }
10811                            
10812                    if (!flags.force_suppress && (flags.variable_success || (flags.term_intended && !flags.variable_attempt))) {
10813                        if (!this.isJurisLocatorLabel) {
10814                            state.tmp.group_context.tip.variable_success = true;
10815                        }
10816                        var blobs = state.output.current.value().blobs;
10817                        var pos = state.output.current.value().blobs.length - 1;
10818
10819                        if (!state.tmp.just_looking && (flags.non_parallel || flags.parallel_last || flags.parallel_first || flags.parallel_delimiter_override || flags.parallel_delimiter_override_on_suppress)) {
10820                            // flags.parallel_last
10821                            // flags.parallel_first
10822
10823                            // Returns true ONLY if all variables listed on this group are repeats.
10824                            var hasRepeat = state.parallel.checkRepeats(flags);
10825                            if (hasRepeat) {
10826                                if (blobs) {
10827                                    blobs.pop();
10828                                }
10829                            }
10830                            if (state.tmp.cite_index > 0 && (hasRepeat || (!flags.parallel_first && !flags.parallel_last && !flags.non_parallel))) {
10831                                //state.sys.print(`${state.tmp.cite_index} ${JSON.stringify(state.tmp.suppress_repeats, null, 2)}`)
10832                                var info = state.tmp.suppress_repeats[state.tmp.cite_index-1];
10833                                if (hasRepeat && flags.parallel_delimiter_override_on_suppress && (info.SIBLING || info.ORPHAN)) {
10834                                    state.output.queue.slice(-1)[0].parallel_delimiter = flags.parallel_delimiter_override_on_suppress;
10835                                } else if (flags.parallel_delimiter_override && info.SIBLING) {
10836                                    state.output.queue.slice(-1)[0].parallel_delimiter = flags.parallel_delimiter_override;
10837                                }
10838                            }
10839                        }
10840                    } else {
10841                        state.tmp.term_predecessor = flags.old_term_predecessor;
10842                        state.tmp.group_context.tip.variable_attempt = flags.variable_attempt;
10843                        if (flags.force_suppress && !state.tmp.group_context.tip.condition) {
10844                            state.tmp.group_context.tip.variable_attempt = true;
10845                            state.tmp.group_context.tip.variable_success = flags.variable_success_parent;
10846                        }
10847                        if (flags.force_suppress) {
10848                            // 2019-04-15
10849                            // This is removing variables done within the group we're leaveing from global
10850                            // done_vars? How does that make sense?
10851                            // Ah. This is a FAILURE. So removing from done_vars allows it to re-render
10852                            // later in the cite if desired.
10853                            // Currently no tests fail from removing the condition, but leaving it in.
10854                            for (var i=0,ilen=flags.done_vars.length;i<ilen;i++) {
10855                                var doneVar = flags.done_vars[i];
10856                                for (var j=0,jlen=state.tmp.done_vars.length; j<jlen; j++) {
10857                                    if (state.tmp.done_vars[j] === doneVar) {
10858                                        state.tmp.done_vars = state.tmp.done_vars.slice(0, j).concat(state.tmp.done_vars.slice(j+1));
10859                                    }
10860                                }
10861                            }
10862                        }
10863                        if (state.output.current.value().blobs) {
10864                            state.output.current.value().blobs.pop();
10865                        }
10866                    }
10867                }
10868            };
10869            this.execs.push(func);
10870            
10871            if (this.juris) {
10872                var else_end = new CSL.Token("else", CSL.END);
10873                CSL.Node["else"].build.call(else_end, state, target);
10874                var choose_end = new CSL.Token("choose", CSL.END);
10875                CSL.Node.choose.build.call(choose_end, state, target);
10876            }
10877        }
10878
10879        if (this.tokentype === CSL.END) {
10880            if (!this.juris) {
10881                target.push(this);
10882            }
10883            if (state.build.substitute_level.value()) {
10884                state.build.substitute_level.replace((state.build.substitute_level.value() - 1));
10885            }
10886            CSL.Util.substituteEnd.call(this, state, target);
10887        }
10888    }
10889};
10890
10891/*global CSL: true */
10892
10893CSL.Node["if"] = {
10894    build: function (state, target) {
10895        CSL.Conditions.TopNode.call(this, state, target);
10896        target.push(this);
10897    },
10898    configure: function (state, pos) {
10899        CSL.Conditions.Configure.call(this, state, pos);
10900    }
10901};
10902
10903
10904CSL.Node["conditions"] = {
10905    build: function (state) {
10906        if (this.tokentype === CSL.START) {
10907            state.tmp.conditions.addMatch(this.match);
10908        }
10909        if (this.tokentype === CSL.END) {
10910            state.tmp.conditions.matchCombine();
10911        }
10912    }
10913};
10914
10915CSL.Node["condition"] = {
10916    build: function (state) {
10917        if (this.tokentype === CSL.SINGLETON) {
10918            var test = state.fun.match[this.match](this, state, this.tests);
10919            state.tmp.conditions.addTest(test);
10920        }
10921    }
10922};
10923
10924CSL.Conditions = {};
10925
10926CSL.Conditions.TopNode = function (state) {
10927    var func;
10928    if (this.tokentype === CSL.START || this.tokentype === CSL.SINGLETON) {
10929        if (this.locale) {
10930            state.opt.lang = this.locale;
10931        }
10932        if (!this.tests || !this.tests.length) {
10933            // Set up the condition compiler with our current context
10934            state.tmp.conditions = new CSL.Conditions.Engine(state, this);
10935        } else {
10936            // The usual.
10937            this.test = state.fun.match[this.match](this, state, this.tests);
10938        }
10939        if (state.build.substitute_level.value() === 0) {
10940            func = function(state) {
10941                state.tmp.condition_counter++;
10942            }
10943            this.execs.push(func);
10944        }
10945    }
10946    if (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON) {
10947        if (state.build.substitute_level.value() === 0) {
10948            func = function (state) {
10949                state.tmp.condition_counter--;
10950                if (state.tmp.condition_lang_counter_arr.length > 0) {
10951                    var counter = state.tmp.condition_lang_counter_arr.slice(-1)[0];
10952                    if (counter === state.tmp.condition_counter) {
10953                        state.opt.lang = state.tmp.condition_lang_val_arr.pop();
10954                        state.tmp.condition_lang_counter_arr.pop();
10955                    }
10956                }
10957                if (this.locale_default) {
10958                    // Open output tag with locale marker
10959                    state.output.current.value().old_locale = this.locale_default;
10960                    state.output.closeLevel("empty");
10961                    state.opt.lang = this.locale_default;
10962                }
10963            };
10964            this.execs.push(func);
10965        }
10966        // closingjump
10967        func = function (state) {
10968            var next = this[state.tmp.jump.value()];
10969            return next;
10970        };
10971        this.execs.push(func);
10972        if (this.locale_default) {
10973            state.opt.lang = this.locale_default;
10974        }
10975    }
10976};
10977
10978CSL.Conditions.Configure = function (state, pos) {
10979    if (this.tokentype === CSL.START) {
10980        // jump index on failure
10981        this.fail = state.configure.fail.slice(-1)[0];
10982        this.succeed = this.next;
10983        state.configure.fail[(state.configure.fail.length - 1)] = pos;
10984    } else if (this.tokentype === CSL.SINGLETON) {
10985        // jump index on failure
10986        this.fail = this.next;
10987        this.succeed = state.configure.succeed.slice(-1)[0];
10988        state.configure.fail[(state.configure.fail.length - 1)] = pos;
10989    } else {
10990        // jump index on success
10991        this.succeed = state.configure.succeed.slice(-1)[0];
10992        this.fail = this.next;
10993    }
10994};
10995
10996CSL.Conditions.Engine = function (state, token) {
10997    this.token = token;
10998    this.state = state;
10999};
11000
11001CSL.Conditions.Engine.prototype.addTest = function (test) {
11002    this.token.tests ? {} : this.token.tests = [];
11003    this.token.tests.push(test);
11004};
11005
11006CSL.Conditions.Engine.prototype.addMatch = function (match) {
11007    this.token.match = match;
11008};
11009
11010CSL.Conditions.Engine.prototype.matchCombine = function () {
11011    this.token.test = this.state.fun.match[this.token.match](this.token, this.state, this.token.tests);
11012};
11013
11014/*global CSL: true */
11015
11016CSL.Node.info = {
11017    build: function (state) {
11018        if (this.tokentype === CSL.START) {
11019            state.build.skip = "info";
11020        } else {
11021            state.build.skip = false;
11022        }
11023    }
11024};
11025
11026
11027/*global CSL: true */
11028
11029CSL.Node.institution = {
11030    build: function (state, target) {
11031        if ([CSL.SINGLETON, CSL.START].indexOf(this.tokentype) > -1) {
11032
11033            var func = function (state) {
11034                if ("string" === typeof this.strings.delimiter) {
11035                    state.tmp.institution_delimiter = this.strings.delimiter;
11036                } else {
11037                    state.tmp.institution_delimiter = state.tmp.name_delimiter;
11038                }
11039
11040                // This is the same code for the same result as in node_name.js, 
11041                // but when cs:institution comes on stream, it may produce
11042                // different results.
11043                if ("text" === state.inheritOpt(this, "and")) {
11044                    this.and_term = state.getTerm("and", "long", 0);
11045                } else if ("symbol" === state.inheritOpt(this, "and")) {
11046                    if (state.opt.development_extensions.expect_and_symbol_form) {
11047                        this.and_term = state.getTerm("and", "symbol", 0);
11048                    } else {
11049                        this.and_term = "&";
11050                    }
11051                } else if ("none" === state.inheritOpt(this, "and")) {
11052                    this.and_term = state.tmp.institution_delimiter;
11053                }
11054                if ("undefined" === typeof this.and_term && state.tmp.and_term) {
11055                    // this.and_term = state.getTerm("and", "long", 0);
11056                    this.and_term = state.tmp.and_term;
11057                }
11058                if (CSL.STARTSWITH_ROMANESQUE_REGEXP.test(this.and_term)) {
11059                    this.and_prefix_single = " ";
11060                    this.and_prefix_multiple = ", ";
11061                    if ("string" === typeof state.tmp.institution_delimiter) {
11062                        this.and_prefix_multiple = state.tmp.institution_delimiter;
11063                    }
11064                    this.and_suffix = " ";
11065                } else {
11066                    this.and_prefix_single = "";
11067                    this.and_prefix_multiple = "";
11068                    this.and_suffix = "";
11069                }
11070                if (state.inheritOpt(this, "delimiter-precedes-last") === "always") {
11071                    this.and_prefix_single = state.tmp.institution_delimiter;
11072                } else if (state.inheritOpt(this, "delimiter-precedes-last") === "never") {
11073                    // Slightly fragile: could test for charset here to make
11074                    // this more certain.
11075                    if (this.and_prefix_multiple) {
11076                        this.and_prefix_multiple = " ";
11077                    }
11078                }
11079                
11080                this.and = {};
11081                if ("undefined" !== typeof this.and_term) {
11082                    state.output.append(this.and_term, "empty", true);
11083                    this.and.single = state.output.pop();
11084                    this.and.single.strings.prefix = this.and_prefix_single;
11085                    this.and.single.strings.suffix = this.and_suffix;
11086                    state.output.append(this.and_term, "empty", true);
11087                    this.and.multiple = state.output.pop();
11088                    this.and.multiple.strings.prefix = this.and_prefix_multiple;
11089                    this.and.multiple.strings.suffix = this.and_suffix;
11090                } else if ("undefined" !== this.strings.delimiter) {
11091                    this.and.single = new CSL.Blob(state.tmp.institution_delimiter);
11092                    this.and.single.strings.prefix = "";
11093                    this.and.single.strings.suffix = "";
11094                    this.and.multiple = new CSL.Blob(state.tmp.institution_delimiter);
11095                    this.and.multiple.strings.prefix = "";
11096                    this.and.multiple.strings.suffix = "";
11097                }
11098                state.nameOutput.institution = this;
11099            };
11100            this.execs.push(func);
11101        }
11102        target.push(this);
11103    },
11104    configure: function (state) {
11105        if ([CSL.SINGLETON, CSL.START].indexOf(this.tokentype) > -1) {
11106            state.build.has_institution = true;
11107        }
11108    }
11109};
11110
11111/*global CSL: true */
11112
11113CSL.Node["institution-part"] = {
11114    build: function (state, target) {
11115        var func;
11116        if ("long" === this.strings.name) {
11117            if (this.strings["if-short"]) {
11118                func = function (state) {
11119                    state.nameOutput.institutionpart["long-with-short"] = this;
11120                };
11121            } else {
11122                func = function (state) {
11123                    state.nameOutput.institutionpart["long"] = this;
11124                };
11125            }
11126        } else if ("short" === this.strings.name) {
11127            func = function (state) {
11128                state.nameOutput.institutionpart["short"] = this;
11129            };
11130        }
11131        this.execs.push(func);
11132        target.push(this);
11133    }
11134};
11135
11136/*global CSL: true */
11137
11138CSL.Node.key = {
11139    build: function (state, target) {
11140        
11141        target = state[state.build.root + "_sort"].tokens;
11142
11143        var func;
11144        var debug = false;
11145        var start_key = new CSL.Token("key", CSL.START);
11146
11147        state.tmp.root = state.build.root;
11148
11149        // The params object for build and runtime (tmp) really shouldn't have been separated.
11150        // Oh, well.
11151        start_key.strings["et-al-min"] = state.inheritOpt(this, "et-al-min");
11152        start_key.strings["et-al-use-first"] = state.inheritOpt(this, "et-al-use-first");
11153        start_key.strings["et-al-use-last"] = state.inheritOpt(this, "et-al-use-last");
11154
11155
11156        // initialize done vars
11157        func = function (state) {
11158            state.tmp.done_vars = [];
11159        };
11160        start_key.execs.push(func);
11161
11162        // initialize output queue
11163        func = function (state) {
11164            state.output.openLevel("empty");
11165        };
11166        start_key.execs.push(func);
11167
11168        // sort direction
11169        var sort_direction = [];
11170        if (this.strings.sort_direction === CSL.DESCENDING) {
11171            //print("sort: descending on "+state.tmp.area);
11172            sort_direction.push(1);
11173            sort_direction.push(-1);
11174        } else {
11175            //print("sort: ascending");
11176            sort_direction.push(-1);
11177            sort_direction.push(1);
11178        }
11179        state[state.build.area].opt.sort_directions.push(sort_direction);
11180
11181        if (CSL.DATE_VARIABLES.indexOf(this.variables[0]) > -1) {
11182            state.build.date_key = true;
11183        }
11184
11185        // et al init
11186        func = function (state) {
11187            state.tmp.sort_key_flag = true;
11188            //print("== key node function ==");
11189            if (state.inheritOpt(this, "et-al-min")) {
11190                state.tmp["et-al-min"] = state.inheritOpt(this, "et-al-min");
11191            }
11192            if (state.inheritOpt(this, "et-al-use-first")) {
11193                state.tmp["et-al-use-first"] = state.inheritOpt(this, "et-al-use-first");
11194            }
11195            if ("boolean" === typeof state.inheritOpt(this, "et-al-use-last")) {
11196                state.tmp["et-al-use-last"] = state.inheritOpt(this, "et-al-use-last");
11197                //print("  set tmp et-al-use-last: "+this.strings["et-al-use-last"])
11198            }
11199        };
11200        start_key.execs.push(func);
11201        target.push(start_key);
11202        
11203        //
11204        // ops to initialize the key's output structures
11205        if (this.variables.length) {
11206            var variable = this.variables[0];
11207            if (CSL.NAME_VARIABLES.indexOf(variable) > -1) {
11208                //
11209                // Start tag
11210                var names_start_token = new CSL.Token("names", CSL.START);
11211                names_start_token.tokentype = CSL.START;
11212                names_start_token.variables = this.variables;
11213                CSL.Node.names.build.call(names_start_token, state, target);
11214                //
11215                // Name tag
11216                var name_token = new CSL.Token("name", CSL.SINGLETON);
11217                name_token.tokentype = CSL.SINGLETON;
11218                name_token.strings["name-as-sort-order"] = "all";
11219                name_token.strings["sort-separator"] = " ";
11220                name_token.strings["et-al-use-last"] = state.inheritOpt(this, "et-al-use-last");
11221                name_token.strings["et-al-min"] = state.inheritOpt(this, "et-al-min");
11222                name_token.strings["et-al-use-first"] = state.inheritOpt(this, "et-al-use-first");
11223                CSL.Node.name.build.call(name_token, state, target);
11224                //
11225                // Institution tag
11226                var institution_token = new CSL.Token("institution", CSL.SINGLETON);
11227                institution_token.tokentype = CSL.SINGLETON;
11228                CSL.Node.institution.build.call(institution_token, state, target);
11229                //
11230                // End tag
11231                var names_end_token = new CSL.Token("names", CSL.END);
11232                names_end_token.tokentype = CSL.END;
11233                CSL.Node.names.build.call(names_end_token, state, target);
11234            } else {
11235                var single_text = new CSL.Token("text", CSL.SINGLETON);
11236                single_text.strings.sort_direction = this.strings.sort_direction;
11237                single_text.dateparts = this.dateparts;
11238                if (CSL.NUMERIC_VARIABLES.indexOf(variable) > -1) {
11239                    // citation-number is virtualized. As a sort key it has no effect on registry
11240                    // sort order per se, but if set to DESCENDING, it reverses the sequence of numbers representing
11241                    // bib entries.
11242                    if (variable === "citation-number") {
11243                        func = function (state, Item) {
11244                            if (state.tmp.area === "bibliography_sort") {
11245                                if (this.strings.sort_direction === CSL.DESCENDING) {
11246                                    state.bibliography_sort.opt.citation_number_sort_direction = CSL.DESCENDING;
11247                                } else {
11248                                    state.bibliography_sort.opt.citation_number_sort_direction = CSL.ASCENDING;
11249                                }
11250                            }
11251                            if (state.tmp.area === "citation_sort" && state.bibliography_sort.tmp.citation_number_map) {
11252                                var num = state.bibliography_sort.tmp.citation_number_map[state.registry.registry[Item.id].seq];
11253                            } else {
11254                                var num = state.registry.registry[Item.id].seq;
11255                            }
11256                            if (num) {
11257                                // Code currently in util_number.js
11258                                num = CSL.Util.padding("" + num);
11259                            }
11260                            state.output.append(num, this);
11261                        };
11262                    } else {
11263                        func = function (state, Item) {
11264                            var num = false;
11265                            num = Item[variable];
11266                            // XXX What if this is NaN?
11267                            if (num) {
11268                                // Code currently in util_number.js
11269                                num = CSL.Util.padding(num);
11270                            }
11271                            state.output.append(num, this);
11272                        };
11273                    }
11274                } else if (variable === "citation-label") {
11275                    func = function (state, Item) {
11276                        var trigraph = state.getCitationLabel(Item);
11277                        state.output.append(trigraph, this);
11278                    };
11279                } else if (CSL.DATE_VARIABLES.indexOf(variable) > -1) {
11280                    func = CSL.dateAsSortKey;
11281                    single_text.variables = this.variables;
11282                } else if ("title" === variable) {
11283                    var abbrevfam = "title";
11284                    var abbrfall = false;
11285                    var altvar = false;
11286                    var transfall = true;
11287                    func = state.transform.getOutputFunction(this.variables, abbrevfam, abbrfall, altvar, transfall);
11288                } else if ("court-class" === variable) {
11289                    func = function(state, Item, item) {
11290                        CSL.INIT_JURISDICTION_MACROS(state, Item, item, "juris-main")
11291                        // true is for sortKey mode
11292                        var cls = CSL.GET_COURT_CLASS(state, Item, true);
11293                        state.output.append(cls, "empty");
11294                    }
11295                } else {
11296                    func = function (state, Item) {
11297                        var varval = Item[variable];
11298                        state.output.append(varval, "empty");
11299                    };
11300                }
11301                single_text.execs.push(func);
11302                target.push(single_text);
11303            }
11304        } else { // macro
11305            //
11306            // if it's not a variable, it's a macro
11307            var token = new CSL.Token("text", CSL.SINGLETON);
11308            token.strings.sort_direction = this.strings.sort_direction;
11309            token.postponed_macro = this.postponed_macro;
11310            CSL.expandMacro.call(state, token, target);
11311        }
11312        //
11313        // ops to output the key string result to an array go
11314        // on the closing "key" tag before it is pushed.
11315        // Do not close the level.
11316        var end_key = new CSL.Token("key", CSL.END);
11317
11318        // Eliminated at revision 1.0.159.
11319        // Was causing non-fatal error "wanted empty but found group".
11320        // Possible contributor to weird "PAGES" bug?
11321        //func = function (state, Item) {
11322        //state.output.closeLevel("empty");
11323        //};
11324        //end_key.execs.push(func);
11325        
11326        // store key for use
11327        func = function (state) {
11328            var keystring = state.output.string(state, state.output.queue);
11329            if (state.sys.normalizeUnicode) {
11330                keystring = state.sys.normalizeUnicode(keystring);
11331            }
11332            keystring = keystring ? (keystring.split(" ").join(state.opt.sort_sep) + state.opt.sort_sep) : "";
11333            //SNIP-START
11334            if (debug) {
11335                CSL.debug("keystring: " + keystring + " " + typeof keystring);
11336            }
11337            //print("keystring: (" + keystring + ") " + typeof keystring + " " + state.tmp.area);
11338            //SNIP-END
11339            //state.sys.print("keystring: (" + keystring + ") " + typeof keystring + " " + state.tmp.area);
11340            if ("" === keystring) {
11341                keystring = undefined;
11342            }
11343            if ("string" !== typeof keystring) {
11344                keystring = undefined;
11345                //state.tmp.empty_date = false;
11346            }
11347            state[state[state.tmp.area].root + "_sort"].keys.push(keystring);
11348            state.tmp.value = [];
11349        };
11350        end_key.execs.push(func);
11351
11352        // Set year-suffix key on anything that looks like a date
11353        if (state.build.date_key) {
11354            if (state.build.area === "citation" && state.build.extension === "_sort") {
11355                // ascending sort always
11356                state[state.build.area].opt.sort_directions.push([-1,1]);
11357                func = function (state, Item) {
11358                    // year-suffix Key
11359                    var year_suffix = state.registry.registry[Item.id].disambig.year_suffix;
11360                    if (!year_suffix) {
11361                        year_suffix = 0;
11362                    }
11363                    var key = CSL.Util.padding("" + year_suffix);
11364                    state[state.tmp.area].keys.push(key);
11365                };
11366                end_key.execs.push(func);
11367            }
11368            state.build.date_key = false;
11369        }
11370
11371        // reset key params
11372        func = function (state) {
11373            // state.tmp.name_quash = new Object();
11374
11375            // XXX This should work, should be necessary, but doesn't and isn't.
11376            //state.output.closeLevel("empty");
11377
11378            state.tmp["et-al-min"] = undefined;
11379            state.tmp["et-al-use-first"] = undefined;
11380            state.tmp["et-al-use-last"] = undefined;
11381            state.tmp.sort_key_flag = false;
11382        };
11383        end_key.execs.push(func);
11384        target.push(end_key);
11385    }
11386};
11387
11388/*global CSL: true */
11389
11390CSL.Node.label = {
11391    build: function (state, target) {
11392        
11393        if (this.strings.term) {
11394            // Non-names labels
11395            var func = function (state, Item, item) {
11396                // Must accomplish this without touching strings
11397                // shared with the calling application: "sub verbo"
11398                // and "sub-verbo" must both pass, as they stand.
11399                //if (item && item.label === "sub verbo") {
11400                //    item.label = "sub-verbo";
11401                //}
11402                // This is abstracted away, because the same
11403                // logic must be run in cs:names.
11404                var termtxt = CSL.evaluateLabel(this, state, Item, item);
11405                if (item && this.strings.term === "locator") {
11406
11407                    item.section_form_override = this.strings.form;
11408
11409                }
11410                if (termtxt) {
11411                    state.tmp.group_context.tip.term_intended = true;
11412                }
11413                CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, termtxt, null, this);
11414                if (termtxt.indexOf("%s") === -1) {
11415                    // ^ Suppress output here if we have an embedded term
11416                    if (this.strings.capitalize_if_first) {
11417                        if (!state.tmp.term_predecessor && !(state.opt["class"] === "in-text" && state.tmp.area === "citation")) {
11418                            termtxt = CSL.Output.Formatters["capitalize-first"](state, termtxt);
11419                        }
11420                    }
11421                    state.output.append(termtxt, this);
11422                }
11423            };
11424            this.execs.push(func);
11425        } else {
11426            if (!this.strings.form) {
11427                this.strings.form = "long";
11428            }
11429            // Names labels
11430            // Picked up in names END
11431            var namevars = state.build.names_variables[state.build.names_variables.length-1];
11432            var namelabels = state.build.name_label[state.build.name_label.length-1];
11433            for (var i = 0, ilen = namevars.length; i < ilen; i += 1) {
11434                if (!namelabels[namevars[i]]) {
11435                    namelabels[namevars[i]] = {};
11436                }
11437            }
11438            if (!state.build.name_flag) {
11439                for (var i = 0, ilen = namevars.length; i < ilen; i += 1) {
11440                    namelabels[namevars[i]].before = this;
11441                }
11442            } else {
11443                for (var i = 0, ilen = namevars.length; i < ilen; i += 1) {
11444                    namelabels[namevars[i]].after = this;
11445                }
11446            }
11447        }
11448        target.push(this);
11449    }
11450};
11451
11452/*global CSL: true */
11453
11454CSL.Node.layout = {
11455    build: function (state, target) {
11456        var func, prefix_token, suffix_token, tok;
11457
11458        function setSuffix() {
11459            if (state.build.area === "bibliography") {
11460                suffix_token = new CSL.Token("text", CSL.SINGLETON);
11461                func = function(state) {
11462                    // Suppress suffix on all but the last item in bibliography parallels
11463                    if (!state.tmp.parallel_and_not_last) {
11464                        var suffix;
11465                        if (state.tmp.cite_affixes[state.tmp.area][state.tmp.last_cite_locale]) {
11466                            suffix = state.tmp.cite_affixes[state.tmp.area][state.tmp.last_cite_locale].suffix;
11467                        } else {
11468                            suffix = state.bibliography.opt.layout_suffix;
11469                        }
11470
11471                        // If @display is used, layout suffix is placed on the last
11472                        // immediate child of the layout, which we assume will be a
11473                        // @display group node.
11474                        var topblob = state.output.current.value();
11475                        if (state.opt.using_display) {
11476                            topblob.blobs[topblob.blobs.length-1].strings.suffix = suffix;
11477                        } else {
11478                            topblob.strings.suffix = suffix;
11479                        }
11480                    }
11481                    if (state.bibliography.opt["second-field-align"]) {
11482                        // closes bib_other
11483                        state.output.endTag("bib_other");
11484                    }
11485                };
11486                suffix_token.execs.push(func);
11487                target.push(suffix_token);
11488            }
11489        }
11490
11491        if (this.tokentype === CSL.START) {
11492
11493            if (this.locale_raw) {
11494                state.build.current_default_locale = this.locale_raw;
11495            } else {
11496                state.build.current_default_locale = state.opt["default-locale"];
11497            }
11498
11499            func = function (state, Item, item) {
11500                if (state.opt.development_extensions.apply_citation_wrapper
11501                    && state.sys.wrapCitationEntry
11502                    && !state.tmp.just_looking
11503                    && Item.system_id 
11504                    && state.tmp.area === "citation") { 
11505
11506                    var cite_entry = new CSL.Token("group", CSL.START);
11507                    cite_entry.decorations = [["@cite", "entry"]];
11508                    state.output.startTag("cite_entry", cite_entry);
11509                    state.output.current.value().item_id = Item.system_id;
11510                    if (item) {
11511                        state.output.current.value().locator_txt = item.locator_txt;
11512                        state.output.current.value().suffix_txt = item.suffix_txt;
11513                    }
11514                }
11515            };
11516            this.execs.push(func);
11517        }
11518
11519        // XXX Works, but using state.tmp looks wrong here? We're in the build layer ...
11520        if (this.tokentype === CSL.START && !state.tmp.cite_affixes[state.build.area]) {
11521            //
11522            // done_vars is used to prevent the repeated
11523            // rendering of variables
11524            //
11525            // initalize done vars
11526            func = function (state, Item, item) {
11527
11528                state.tmp.done_vars = [];
11529                if (item && item["author-only"]) {
11530                    state.tmp.done_vars.push("locator");
11531                }
11532                if (state.opt.suppressedJurisdictions[Item["country"]]
11533                    && Item["country"]
11534                    && ["treaty", "patent"].indexOf(Item.type) === -1) {
11535                    
11536                    state.tmp.done_vars.push("country");
11537                }
11538                if (!state.tmp.just_looking && state.registry.registry[Item.id] && state.registry.registry[Item.id].parallel) {
11539                    state.tmp.done_vars.push("first-reference-note-number");
11540                }
11541                // trimmer is not available in getAmbiguousCite
11542                if (!state.tmp.just_looking && state.tmp.abbrev_trimmer && Item.jurisdiction) {
11543                    for (var field in state.tmp.abbrev_trimmer.QUASHES[Item.jurisdiction]) {
11544                        state.tmp.done_vars.push(field);
11545                    }
11546                }
11547
11548                //CSL.debug(" === init rendered_name === ");
11549                state.tmp.rendered_name = false;
11550            };
11551            this.execs.push(func);
11552            // set opt delimiter
11553            func = function (state) {
11554                // just in case
11555                state.tmp.sort_key_flag = false;
11556            };
11557            this.execs.push(func);
11558            
11559            // reset nameset counter [all nodes]
11560            func = function (state) {
11561                state.tmp.nameset_counter = 0;
11562            };
11563            this.execs.push(func);
11564
11565            func = function (state, Item) {
11566                var tok = new CSL.Token();
11567                state.output.openLevel(tok);
11568            };
11569            this.execs.push(func);
11570            target.push(this);
11571
11572            if (state.build.area === "citation") {
11573                prefix_token = new CSL.Token("text", CSL.SINGLETON);
11574                func = function (state, Item, item) {
11575                    if (item && item.prefix) {
11576                        var prefix = CSL.checkPrefixSpaceAppend(state, item.prefix);
11577                        if (!state.tmp.just_looking) {
11578                            prefix = state.output.checkNestedBrace.update(prefix);
11579                        }
11580                        var ignorePredecessor = CSL.checkIgnorePredecessor(state, prefix);
11581                        state.output.append(prefix, this, false, ignorePredecessor);
11582                    }
11583                };
11584                prefix_token.execs.push(func);
11585                target.push(prefix_token);
11586            }
11587        }
11588
11589        // Cast token to be used in one of the configurations below.
11590        var my_tok;
11591        if (this.locale_raw) {
11592            my_tok = new CSL.Token("dummy", CSL.START);
11593            my_tok.locale = this.locale_raw;
11594            my_tok.strings.delimiter = this.strings.delimiter;
11595            my_tok.strings.suffix = this.strings.suffix;
11596            if (!state.tmp.cite_affixes[state.build.area]) {
11597                state.tmp.cite_affixes[state.build.area] = {};
11598            }
11599        }
11600
11601        if (this.tokentype === CSL.START) {
11602            state.build.layout_flag = true;
11603                            
11604            // Only run the following once, to set up the final layout node ...
11605            if (!this.locale_raw) {
11606                //
11607                // save out decorations for flipflop processing [final node only]
11608                //
11609                state[state.tmp.area].opt.topdecor = [this.decorations];
11610                state[(state.tmp.area + "_sort")].opt.topdecor = [this.decorations];
11611
11612                state[state.build.area].opt.layout_prefix = this.strings.prefix;
11613                state[state.build.area].opt.layout_suffix = this.strings.suffix;
11614                state[state.build.area].opt.layout_delimiter = this.strings.delimiter;
11615
11616                state[state.build.area].opt.layout_decorations = this.decorations;
11617                
11618                // Only do this if we're running conditionals
11619                if (state.tmp.cite_affixes[state.build.area]) {
11620                    // if build_layout_locale_flag is true,
11621                    // write cs:else START to the token list.
11622                    tok = new CSL.Token("else", CSL.START);
11623                    CSL.Node["else"].build.call(tok, state, target);
11624                }
11625
11626            } // !this.locale_raw
11627
11628            // Conditionals
11629            if (this.locale_raw) {
11630                if (!state.build.layout_locale_flag) {
11631                    // if layout_locale_flag is untrue,
11632                    // write cs:choose START and cs:if START
11633                    // to the token list.
11634                    var choose_tok = new CSL.Token("choose", CSL.START);
11635                    CSL.Node.choose.build.call(choose_tok, state, target);
11636                    my_tok.name = "if";
11637                    CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw);
11638                    CSL.Node["if"].build.call(my_tok, state, target);
11639                } else {
11640                    // if build_layout_locale_flag is true,
11641                    // write cs:else-if START to the token list.
11642                    my_tok.name = "else-if";
11643                    CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw);
11644                    CSL.Node["else-if"].build.call(my_tok, state, target);
11645                }
11646                // cite_affixes for this node
11647                state.tmp.cite_affixes[state.build.area][my_tok.locale] = {};
11648                state.tmp.cite_affixes[state.build.area][my_tok.locale].delimiter = this.strings.delimiter;
11649                state.tmp.cite_affixes[state.build.area][my_tok.locale].suffix = this.strings.suffix;
11650            }
11651        }
11652        if (this.tokentype === CSL.END) {
11653            if (this.locale_raw) {
11654                setSuffix();
11655                if (!state.build.layout_locale_flag) {
11656                    // If layout_locale_flag is untrue, write cs:if END
11657                    // to the token list.
11658                    my_tok.name = "if";
11659                    my_tok.tokentype = CSL.END;
11660                    CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw);
11661                    CSL.Node["if"].build.call(my_tok, state, target);
11662                    state.build.layout_locale_flag = true;
11663                } else {
11664                    // If layout_locale_flag is true, write cs:else-if END
11665                    // to the token list.
11666                    my_tok.name = "else-if";
11667                    my_tok.tokentype = CSL.END;
11668                    CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw);
11669                    CSL.Node["else-if"].build.call(my_tok, state, target);
11670                }
11671            }
11672            if (!this.locale_raw) {
11673                setSuffix();
11674                // Only add this if we're running conditionals
11675                if (state.tmp.cite_affixes[state.build.area]) {
11676                    // If layout_locale_flag is true, write cs:else END
11677                    // and cs:choose END to the token list.
11678                    if (state.build.layout_locale_flag) {
11679                        tok = new CSL.Token("else", CSL.END);
11680                        CSL.Node["else"].build.call(tok, state, target);
11681                        tok = new CSL.Token("choose", CSL.END);
11682                        CSL.Node.choose.build.call(tok, state, target);
11683                    }
11684                }
11685                state.build_layout_locale_flag = true;
11686                if (state.build.area === "citation") {
11687                    suffix_token = new CSL.Token("text", CSL.SINGLETON);
11688                    func = function (state, Item, item) {
11689                        var sp;
11690                        if (item && item.suffix) {
11691                            var suffix = CSL.checkSuffixSpacePrepend(state, item.suffix);
11692                            if (!state.tmp.just_looking) {
11693                                suffix = state.output.checkNestedBrace.update(suffix);
11694                            }
11695                            state.output.append((suffix), this);
11696                        }
11697                    };
11698                    suffix_token.execs.push(func);
11699                    target.push(suffix_token);
11700                }
11701
11702                // Closes wrapper token
11703                func = function (state) {
11704                    state.output.closeLevel();
11705                };
11706                this.execs.push(func);
11707                func = function (state, Item) {
11708                    if (state.opt.development_extensions.apply_citation_wrapper
11709                        && state.sys.wrapCitationEntry
11710                        && !state.tmp.just_looking
11711                        && Item.system_id 
11712                        && state.tmp.area === "citation") { 
11713                        
11714                        state.output.endTag(); // closes citation link wrapper
11715                    }
11716                };
11717                this.execs.push(func);
11718                target.push(this);
11719                state.build.layout_flag = false;
11720                state.build.layout_locale_flag = false;
11721            } // !this.layout_raw
11722        }
11723    }
11724};
11725
11726/*global CSL: true */
11727
11728CSL.Node.macro = {
11729    build: function () {}
11730};
11731
11732/*global CSL: true */
11733
11734CSL.Node.alternative = {
11735    build: function (state, target) {
11736        if (this.tokentype === CSL.START) {
11737
11738            var choose_tok = new CSL.Token("choose", CSL.START);
11739            CSL.Node["choose"].build.call(choose_tok, state, target);
11740
11741            var if_tok = new CSL.Token("if", CSL.START);
11742            CSL.Attributes["@alternative-node-internal"].call(if_tok, state);
11743            CSL.Node["if"].build.call(if_tok, state, target);
11744
11745            var func = function(state, Item) {
11746
11747                state.tmp.oldItem = Item;
11748                state.tmp.oldLang = state.opt.lang;
11749                state.tmp.abort_alternative = true;
11750
11751                if (Item["language-name"] && Item["language-name-original"]) {
11752
11753                    var newItem = JSON.parse(JSON.stringify(Item));
11754
11755                    newItem.language = newItem["language-name"];
11756                    var langspec = CSL.localeResolve(newItem.language, state.opt["default-locale"][0]);
11757
11758                    if (state.opt.multi_layout) {
11759                        for (var i in state.opt.multi_layout) {
11760                            var locale_list = state.opt.multi_layout[i];
11761                            var gotlang = false;
11762                            for (var j in locale_list) {
11763                                var tryspec = locale_list[j];
11764                                if (langspec.best === tryspec.best || langspec.base === tryspec.base || langspec.bare === tryspec.bare) {
11765                                    gotlang = locale_list[0].best;
11766                                    break;
11767                                }
11768                            }
11769                            if (!gotlang) {
11770                                gotlang = state.opt["default-locale"][0];
11771                            }
11772                            state.opt.lang = gotlang;
11773                        }
11774                    }
11775
11776                    for (var key in newItem) {
11777                        if (["id", "type", "language", "multi"].indexOf(key) === -1 && key.slice(0, 4) !== "alt-") {
11778                            if (newItem.multi && newItem.multi._keys[key]) {
11779                                var deleteme = true;
11780                                for (var lang in newItem.multi._keys[key]) {
11781                                    if (langspec.bare === lang.replace(/^([a-zA-Z]+).*/, "$1")) {
11782                                        deleteme = false;
11783                                        break;
11784                                    }
11785                                }
11786                                if (deleteme) {
11787                                    delete newItem[key];
11788                                }
11789                            } else {
11790                                delete newItem[key];
11791                            }
11792                        }
11793                    }
11794                    for (var key in newItem) {
11795                        if (key.slice(0, 4) === "alt-") {
11796                            newItem[key.slice(4)] = newItem[key];
11797                            state.tmp.abort_alternative = false;
11798                        } else {
11799                            if (newItem.multi && newItem.multi._keys) {
11800                                if (!newItem["alt-" + key] && newItem.multi._keys[key]) {
11801                                    if (newItem.multi._keys[key][langspec.best]) {
11802                                        newItem[key] = newItem.multi._keys[key][langspec.best];
11803                                        state.tmp.abort_alternative = false;
11804                                    } else if (newItem.multi._keys[key][langspec.base]) {
11805                                        newItem[key] = newItem.multi._keys[key][langspec.base];
11806                                        state.tmp.abort_alternative = false;
11807                                    } else if (newItem.multi._keys[key][langspec.bare]) {
11808                                        newItem[key] = newItem.multi._keys[key][langspec.bare];
11809                                        state.tmp.abort_alternative = false;
11810                                    }
11811                                }
11812                            }
11813                        }
11814                    }
11815                }
11816
11817                state.output.openLevel(this);
11818                state.registry.refhash[Item.id] = newItem;
11819                state.nameOutput = new CSL.NameOutput(state, newItem);
11820            };
11821            this.execs.push(func);
11822            target.push(this);
11823
11824            var choose_tok = new CSL.Token("choose", CSL.START);
11825            CSL.Node["choose"].build.call(choose_tok, state, target);
11826
11827            var if_tok = new CSL.Token("if", CSL.START);
11828            CSL.Attributes["@alternative-node-internal"].call(if_tok, state);
11829            var func = function(state) {
11830                state.tmp.abort_alternative = true;
11831            }
11832            if_tok.execs.push(func);
11833            CSL.Node["if"].build.call(if_tok, state, target);
11834
11835        } else if (this.tokentype === CSL.END) {
11836
11837            var if_tok = new CSL.Token("if", CSL.END);
11838            CSL.Node["if"].build.call(if_tok, state, target);
11839
11840            var choose_tok = new CSL.Token("choose", CSL.END);
11841            CSL.Node["choose"].build.call(choose_tok, state, target);
11842
11843            var func = function(state, Item) {
11844                state.output.closeLevel();
11845                state.registry.refhash[Item.id] = state.tmp.oldItem;
11846                state.opt.lang = state.tmp.oldLang;
11847                state.nameOutput = new CSL.NameOutput(state, state.tmp.oldItem);
11848                state.tmp.abort_alternative = false;
11849            };
11850            this.execs.push(func);
11851            target.push(this);
11852
11853            var if_tok = new CSL.Token("if", CSL.END);
11854            CSL.Node["if"].build.call(if_tok, state, target);
11855
11856            var choose_tok = new CSL.Token("choose", CSL.END);
11857            CSL.Node["choose"].build.call(choose_tok, state, target);
11858
11859        }
11860    }
11861};
11862
11863CSL.Node["alternative-text"] = {
11864    build: function (state, target) {
11865        if (this.tokentype === CSL.SINGLETON) {
11866            // do stuff
11867            var func = function(state, Item) {
11868                var Item = state.refetchItem(Item.id);
11869                CSL.getCite.call(state, Item);
11870            };
11871            this.execs.push(func);
11872        }
11873        target.push(this);
11874    }
11875};
11876
11877
11878
11879/*global CSL: true */
11880
11881CSL.NameOutput = function(state, Item, item) {
11882    this.debug = false;
11883    this.state = state;
11884    //SNIP-START
11885    if (this.debug) {
11886        this.state.sys.print("(1)");
11887    }
11888    //SNIP-END
11889    this.Item = Item;
11890    this.item = item;
11891    this.nameset_base = 0;
11892    this.etal_spec = {};
11893    this._first_creator_variable = false;
11894    this._please_chop = false;
11895};
11896
11897CSL.NameOutput.prototype.init = function (names) {
11898    this.requireMatch = names.requireMatch;
11899    if (this.state.tmp.term_predecessor) {
11900        this.state.tmp.subsequent_author_substitute_ok = false;
11901    }
11902    if (this.nameset_offset) {
11903        this.nameset_base = this.nameset_base + this.nameset_offset;
11904    }
11905    this.nameset_offset = 0;
11906    this.names = names;
11907    this.variables = names.variables;
11908
11909    this.state.tmp.value = [];
11910    this.state.tmp.rendered_name = [];
11911    this.state.tmp.label_blob = false;
11912    this.state.tmp.etal_node = false;
11913    this.state.tmp.etal_term = false;
11914    for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
11915        if (this.Item[this.variables[i]] && this.Item[this.variables[i]].length) {
11916            this.state.tmp.value = this.state.tmp.value.concat(this.Item[this.variables[i]]);
11917        }
11918    }
11919    this["et-al"] = undefined;
11920    // REMOVE THIS
11921    this["with"] = undefined;
11922
11923    this.name = undefined;
11924    // long, long-with-short, short
11925    this.institutionpart = {};
11926    // family, given
11927    //this.namepart = {};
11928    // before, after
11929    //this.label = {};
11930
11931    this.state.tmp.group_context.tip.variable_attempt = true;
11932
11933    this.labelVariable = this.variables[0];
11934
11935    if (!this.state.tmp.value.length) {
11936        return;
11937    }
11938
11939    // Abort and proceed to the next substitution if a match is required,
11940    // two variables are called, and they do not match.
11941    var checkCommonTerm = this.checkCommonAuthor(this.requireMatch);
11942    if (checkCommonTerm) {
11943        this.state.tmp.can_substitute.pop();
11944        this.state.tmp.can_substitute.push(true);
11945        //this.state.tmp.group_context.mystack[this.state.tmp.group_context.mystack.length-1].variable_success = false;
11946        for (var i in this.variables) {
11947            var idx = this.state.tmp.done_vars.indexOf(this.variables[i]);
11948            if (idx > -1) {
11949                this.state.tmp.done_vars = this.state.tmp.done_vars.slice(0, idx).concat(this.state.tmp.done_vars.slice(i+1));
11950            }
11951        }
11952        this.state.tmp.common_term_match_fail = true;
11953        this.variables = [];
11954    }
11955};
11956
11957
11958CSL.NameOutput.prototype.reinit = function (names, labelVariable) {
11959    this.requireMatch = names.requireMatch;
11960    this.labelVariable = labelVariable;
11961
11962    if (this.state.tmp.can_substitute.value()) {
11963        this.nameset_offset = 0;
11964        // What-all should be carried across from the subsidiary
11965        // names node, and on what conditions? For each attribute,
11966        // and decoration, is it an override, or is it additive?
11967        this.variables = names.variables;
11968        
11969        // Not sure why this is necessary. Guards against a memory leak perhaps?
11970        var oldval = this.state.tmp.value.slice();
11971        this.state.tmp.value = [];
11972
11973        for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
11974            if (this.Item[this.variables[i]] && this.Item[this.variables[i]].length) {
11975                this.state.tmp.value = this.state.tmp.value.concat(this.Item[this.variables[i]]);
11976            }
11977        }
11978        if (this.state.tmp.value.length) {
11979            this.state.tmp.can_substitute.replace(false, CSL.LITERAL);
11980        }
11981
11982        this.state.tmp.value = oldval;
11983
11984    }
11985    // Abort and proceed to the next substitution if a match is required,
11986    // two variables are called, and they do not match.
11987    var checkCommonTerm = this.checkCommonAuthor(this.requireMatch);
11988    if (checkCommonTerm) {
11989        this.state.tmp.can_substitute.pop();
11990        this.state.tmp.can_substitute.push(true);
11991        for (var i in this.variables) {
11992            var idx = this.state.tmp.done_vars.indexOf(this.variables[i]);
11993            if (idx > -1) {
11994                this.state.tmp.done_vars = this.state.tmp.done_vars.slice(0, idx).concat(this.state.tmp.done_vars.slice(i+1));
11995            }
11996        }
11997        this.variables = [];
11998    }
11999};
12000
12001CSL.NameOutput.prototype.outputNames = function () {
12002    var i, ilen;
12003    var variables = this.variables;
12004    if (this.institution.and) {
12005        if (!this.institution.and.single.blobs || !this.institution.and.single.blobs.length) {
12006            this.institution.and.single.blobs = this.name.and.single.blobs;
12007        }
12008        if (!this.institution.and.multiple.blobs || !this.institution.and.multiple.blobs.length) {
12009            this.institution.and.multiple.blobs = this.name.and.multiple.blobs;
12010        }
12011    }
12012
12013    this.variable_offset = {};
12014    if (this.family) {
12015        this.family_decor = CSL.Util.cloneToken(this.family);
12016        this.family_decor.strings.prefix = "";
12017        this.family_decor.strings.suffix = "";
12018        // Sets text-case value (text-case="title" is suppressed for items
12019        // non-English with non-English value in Item.language)
12020        for (i = 0, ilen = this.family.execs.length; i < ilen; i += 1) {
12021            this.family.execs[i].call(this.family_decor, this.state, this.Item);
12022        }
12023    } else {
12024        this.family_decor = false;
12025    }
12026
12027    if (this.given) {
12028        this.given_decor = CSL.Util.cloneToken(this.given);
12029        this.given_decor.strings.prefix = "";
12030        this.given_decor.strings.suffix = "";
12031        // Sets text-case value (text-case="title" is suppressed for items
12032        // non-English with non-English value in Item.language)
12033        for (i = 0, ilen = this.given.execs.length; i < ilen; i += 1) {
12034            this.given.execs[i].call(this.given_decor, this.state, this.Item);
12035        }
12036    } else {
12037        this.given_decor = false;
12038    }
12039
12040    //SNIP-START
12041    if (this.debug) {
12042        this.state.sys.print("(2)");
12043    }
12044    //SNIP-END
12045    // util_names_etalconfig.js
12046    this.getEtAlConfig();
12047    //SNIP-START
12048    if (this.debug) {
12049        this.state.sys.print("(3)");
12050    }
12051    //SNIP-END
12052    // util_names_divide.js
12053    this.divideAndTransliterateNames();
12054    //SNIP-START
12055    if (this.debug) {
12056        this.state.sys.print("(4)");
12057    }
12058    //SNIP-END
12059    // util_names_truncate.js
12060
12061    this.truncatePersonalNameLists();
12062    //SNIP-START
12063    if (this.debug) {
12064        this.state.sys.print("(5)");
12065    }
12066    //SNIP-END
12067
12068    //SNIP-START
12069    if (this.debug) {
12070        this.state.sys.print("(6)");
12071    }
12072    //SNIP-END
12073    // util_names_disambig.js
12074    this.disambigNames();
12075
12076    // util_names_constraints.js
12077    this.constrainNames();
12078    //SNIP-START
12079    if (this.debug) {
12080        this.state.sys.print("(7)");
12081    }
12082    //SNIP-END
12083    // form="count"
12084    if (this.name.strings.form === "count") {
12085        if (this.state.tmp.extension || this.names_count != 0) {
12086            this.state.output.append(this.names_count, "empty");
12087            this.state.tmp.group_context.tip.variable_success = true;
12088        }
12089        return;
12090    }
12091
12092    //SNIP-START
12093    if (this.debug) {
12094        this.state.sys.print("(8)");
12095    }
12096    //SNIP-END
12097    this.setEtAlParameters();
12098    //SNIP-START
12099    if (this.debug) {
12100        this.state.sys.print("(9)");
12101    }
12102    //SNIP-END
12103    this.setCommonTerm(this.requireMatch);
12104    //SNIP-START
12105    if (this.debug) {
12106        this.state.sys.print("(10)");
12107    }
12108    //SNIP-END
12109    this.renderAllNames();
12110    //SNIP-START
12111    if (this.debug) {
12112        this.state.sys.print("(11)");
12113    }
12114    //SNIP-END
12115    var blob_list = [];
12116    for (i = 0, ilen = variables.length; i < ilen; i += 1) {
12117        var v = variables[i];
12118        var institution_sets = [];
12119        var institutions = false;
12120        var varblob = null;
12121        if (!this.state.opt.development_extensions.spoof_institutional_affiliations) {
12122            varblob = this._join([this.freeters[v]], "");
12123        } else {
12124            //SNIP-START
12125            if (this.debug) {
12126                this.state.sys.print("(11a)");
12127            }
12128            //SNIP-END
12129            for (var j = 0, jlen = this.institutions[v].length; j < jlen; j += 1) {
12130                institution_sets.push(this.joinPersonsAndInstitutions([this.persons[v][j], this.institutions[v][j]]));
12131            }
12132            //SNIP-START
12133            if (this.debug) {
12134                this.state.sys.print("(11b)");
12135            }
12136            //SNIP-END
12137            if (this.institutions[v].length) {
12138                var pos = this.nameset_base + this.variable_offset[v];
12139                if (this.freeters[v].length) {
12140                    pos += 1;
12141                }
12142                institutions = this.joinInstitutionSets(institution_sets, pos);
12143            }
12144            //SNIP-START
12145            if (this.debug) {
12146                this.state.sys.print("(11c)");
12147            }
12148            //SNIP-END
12149            var varblob = this.joinFreetersAndInstitutionSets([this.freeters[v], institutions]);
12150            //SNIP-START
12151            if (this.debug) {
12152                this.state.sys.print("(11d)");
12153            }
12154            //SNIP-END
12155        }
12156        if (varblob) {
12157            // Apply labels, if any
12158            if (!this.state.tmp.extension) {
12159                varblob = this._applyLabels(varblob, v);
12160            }
12161            blob_list.push(varblob);
12162        }
12163        //SNIP-START
12164        if (this.debug) {
12165            this.state.sys.print("(11e)");
12166        }
12167        //SNIP-END
12168        if (this.common_term) {
12169            break;
12170        }
12171    }
12172    //SNIP-START
12173    if (this.debug) {
12174        this.state.sys.print("(12)");
12175    }
12176    //SNIP-END
12177    this.state.output.openLevel("empty");
12178    this.state.output.current.value().strings.delimiter = this.state.inheritOpt(this.names, "delimiter", "names-delimiter");
12179    //SNIP-START
12180    if (this.debug) {
12181        this.state.sys.print("(13)");
12182    }
12183    //SNIP-END
12184    for (i = 0, ilen = blob_list.length; i < ilen; i += 1) {
12185        // notSerious
12186        this.state.output.append(blob_list[i], "literal", true);
12187    }
12188    if (!this.state.tmp.just_looking && blob_list.length > 0) {
12189        this.state.tmp.probably_rendered_something = true;
12190    }
12191    //SNIP-START
12192    if (this.debug) {
12193        this.state.sys.print("(14)");
12194    }
12195    //SNIP-END
12196    this.state.output.closeLevel("empty");
12197    //SNIP-START
12198    if (this.debug) {
12199        this.state.sys.print("(15)");
12200    }
12201    //SNIP-END
12202    var blob = this.state.output.pop();
12203    this.state.tmp.name_node.top = blob;
12204    //SNIP-START
12205    if (this.debug) {
12206        this.state.sys.print("(16)");
12207    }
12208    //SNIP-END
12209
12210    // Append will drop the names on the floor here if suppress-me is
12211    // set on element_trace.
12212    // Need to rescue the value for collapse comparison.
12213    var namesToken = CSL.Util.cloneToken(this.names);
12214    if (this.state.tmp.group_context.tip.condition) {
12215        CSL.UPDATE_GROUP_CONTEXT_CONDITION(this.state, this.names.strings.prefix, null, this.names);
12216    }
12217    this.state.output.append(blob, namesToken);
12218    if (this.state.tmp.term_predecessor_name) {
12219        this.state.tmp.term_predecessor = true;
12220    }
12221    //SNIP-START
12222    if (this.debug) {
12223        this.state.sys.print("(17)");
12224    }
12225    //SNIP-END
12226    // Also used in CSL.Util.substituteEnd (which could do with
12227    // some cleanup at this writing).
12228    //SNIP-START
12229    if (this.debug) {
12230        this.state.sys.print("(18)");
12231    }
12232    //SNIP-END
12233    if (variables[0] !== "authority") {
12234        // Just grab the string values in the name
12235        var name_node_string = [];
12236        var nameobjs = this.Item[variables[0]];
12237        if (nameobjs) {
12238            for (var i = 0, ilen = nameobjs.length; i < ilen; i += 1) {
12239                var substring = CSL.Util.Names.getRawName(nameobjs[i]);
12240                if (substring) {
12241                    name_node_string.push(substring);
12242                }
12243            }
12244        }
12245        name_node_string = name_node_string.join(", ");
12246        if (name_node_string) {
12247            this.state.tmp.name_node.string = name_node_string;
12248        }
12249    }
12250    // for classic support
12251    // This may be more convoluted than it needs to be. Or maybe not.
12252    //
12253    // Check for classic abbreviation
12254    //
12255    // If found, then (1) suppress title rendering, (2) replace the node
12256    // with the abbreviation output [and (3) do not run this._collapseAuthor() ?]
12257    if (this.state.tmp.name_node.string && !this.state.tmp.first_name_string) {
12258        this.state.tmp.first_name_string = this.state.tmp.name_node.string;
12259    }
12260    if ("classic" === this.Item.type) {
12261        if (this.state.tmp.first_name_string) {
12262            var author_title = [];
12263            author_title.push(this.state.tmp.first_name_string);
12264            if (this.Item.title) {
12265                author_title.push(this.Item.title);
12266            }
12267            author_title = author_title.join(", ");
12268            if (author_title && this.state.sys.getAbbreviation) {
12269                if (this.state.sys.normalizeAbbrevsKey) {
12270                    author_title = this.state.sys.normalizeAbbrevsKey("classic", author_title);
12271                }
12272                this.state.transform.loadAbbreviation("default", "classic", author_title, this.Item.language);
12273                if (this.state.transform.abbrevs["default"].classic[author_title]) {
12274                    this.state.tmp.done_vars.push("title");
12275                    this.state.output.append(this.state.transform.abbrevs["default"].classic[author_title], "empty", true);
12276                    blob = this.state.output.pop();
12277				    this.state.tmp.name_node.top.blobs.pop();
12278                    this.state.tmp.name_node.top.blobs.push(blob);
12279                }
12280            }
12281        }
12282    }
12283
12284    // Let's try something clever here.
12285    this._collapseAuthor();
12286
12287    // For name_SubstituteOnNamesSpanNamesSpanFail
12288    this.variables = [];
12289    
12290    // Reset stop-last after rendering
12291    this.state.tmp.authority_stop_last = 0;
12292
12293    //SNIP-START
12294    if (this.debug) {
12295        this.state.sys.print("(19)");
12296    }
12297    //SNIP-END
12298};
12299
12300CSL.NameOutput.prototype._applyLabels = function (blob, v) {
12301    var txt;
12302    if (!this.label || !this.label[this.labelVariable]) {
12303        return blob;
12304    }
12305    var plural = 0;
12306    var num = this.freeters_count[v] + this.institutions_count[v];
12307    if (num > 1) {
12308        plural = 1;
12309    } else {
12310        for (var i = 0, ilen = this.persons[v].length; i < ilen; i += 1) {
12311            num += this.persons_count[v][i];
12312        }
12313        if (num > 1) {
12314            plural = 1;
12315        }
12316    }
12317    // Some code duplication here, should be factored out.
12318    if (this.label[this.labelVariable].before) {
12319        if ("number" === typeof this.label[this.labelVariable].before.strings.plural) {
12320            plural = this.label[this.labelVariable].before.strings.plural;
12321        }
12322        txt = this._buildLabel(v, plural, "before", this.labelVariable);
12323        this.state.output.openLevel("empty");
12324        this.state.output.append(txt, this.label[this.labelVariable].before, true);
12325        this.state.output.append(blob, "literal", true);
12326        this.state.output.closeLevel("empty");
12327        blob = this.state.output.pop();
12328    } else if (this.label[this.labelVariable].after) {
12329        if ("number" === typeof this.label[this.labelVariable].after.strings.plural) {
12330            plural = this.label[this.labelVariable].after.strings.plural;
12331        }
12332        txt = this._buildLabel(v, plural, "after", this.labelVariable);
12333        this.state.output.openLevel("empty");
12334        this.state.output.append(blob, "literal", true);
12335        this.state.output.append(txt, this.label[this.labelVariable].after, true);
12336        this.state.tmp.label_blob = this.state.output.pop();
12337        this.state.output.append(this.state.tmp.label_blob,"literal",true);
12338        this.state.output.closeLevel("empty");
12339        blob = this.state.output.pop();
12340    }
12341    return blob;
12342};
12343
12344CSL.NameOutput.prototype._buildLabel = function (term, plural, position, v) {
12345    if (this.common_term) {
12346        term = this.common_term;
12347    }
12348
12349    var ret = false;
12350    var node = this.label[v][position];
12351    if (node) {
12352        ret = CSL.castLabel(this.state, node, term, plural, CSL.TOLERANT);
12353    }
12354    return ret;
12355};
12356
12357
12358CSL.NameOutput.prototype._collapseAuthor = function () {
12359    var myqueue, mystr, oldchars;
12360    // collapse can be undefined, an array of length zero, and probably
12361    // other things ... ugh.
12362    if (this.state.tmp.name_node.top.blobs.length === 0) {
12363        return;
12364    }
12365    if (this.nameset_base === 0 && this.Item[this.variables[0]] && !this._first_creator_variable) {
12366        this._first_creator_variable = this.variables[0];
12367    }
12368    if ((this.state[this.state.tmp.area].opt.collapse
12369            && this.state[this.state.tmp.area].opt.collapse.length)
12370        || (this.state[this.state.tmp.area].opt.cite_group_delimiter 
12371            && this.state[this.state.tmp.area].opt.cite_group_delimiter.length)) {
12372
12373        if (this.state.tmp.authorstring_request) {
12374            // Avoid running this on every call to getAmbiguousCite()?
12375            mystr = "";
12376            myqueue = this.state.tmp.name_node.top.blobs.slice(-1)[0].blobs;
12377            oldchars = this.state.tmp.offset_characters;
12378            if (myqueue) {
12379                mystr = this.state.output.string(this.state, myqueue, false);
12380            }
12381            // Avoid side-effects on character counting: we're only interested
12382            // in the final rendering.
12383            this.state.tmp.offset_characters = oldchars;
12384            this.state.registry.authorstrings[this.Item.id] = mystr;
12385        } else if (!this.state.tmp.just_looking
12386                   && !this.state.tmp.suppress_decorations && ((this.state[this.state.tmp.area].opt.collapse && this.state[this.state.tmp.area].opt.collapse.length) || this.state[this.state.tmp.area].opt.cite_group_delimiter && this.state[this.state.tmp.area].opt.cite_group_delimiter)) {
12387            // XX1 this.state.sys.print("RENDER: "+this.Item.id);
12388            mystr = "";
12389            myqueue = this.state.tmp.name_node.top.blobs.slice(-1)[0].blobs;
12390            oldchars = this.state.tmp.offset_characters;
12391            if (myqueue) {
12392                mystr = this.state.output.string(this.state, myqueue, false);
12393            }
12394            if (mystr === this.state.tmp.last_primary_names_string) {
12395                if (this.item["suppress-author"] || (this.state[this.state.tmp.area].opt.collapse && this.state[this.state.tmp.area].opt.collapse.length)) {
12396                    // XX1 this.state.sys.print("    CUT!");
12397                    this.state.tmp.name_node.top.blobs.pop();
12398                    this.state.tmp.name_node.children = [];
12399                    // If popped, avoid side-effects on character counting: we're only interested
12400                    // in things that actually render.
12401                    this.state.tmp.offset_characters = oldchars;
12402                }
12403                // Needed
12404                if (this.state[this.state.tmp.area].opt.cite_group_delimiter && this.state[this.state.tmp.area].opt.cite_group_delimiter) {
12405                    this.state.tmp.use_cite_group_delimiter = true;
12406                }
12407            } else {
12408                // XX1 this.state.sys.print("remembering: "+mystr);
12409                this.state.tmp.last_primary_names_string = mystr;
12410                // XXXXX A little more precision would be nice.
12411                // This will clobber variable="author editor" as well as variable="author".
12412
12413                if (this.variables.indexOf(this._first_creator_variable) > -1 && this.item && this.item["suppress-author"] && this.Item.type !== "legal_case") {
12414                    this.state.tmp.name_node.top.blobs.pop();
12415                    this.state.tmp.name_node.children = [];
12416                    // If popped, avoid side-effects on character counting: we're only interested
12417                    // in things that actually render.
12418                    this.state.tmp.offset_characters = oldchars;
12419
12420                    // A wild guess, but will usually be correct
12421                    this.state.tmp.term_predecessor = false;
12422                }
12423                // Arcane and probably unnecessarily complicated?
12424                this.state.tmp.have_collapsed = false;
12425                // Needed
12426                if (this.state[this.state.tmp.area].opt.cite_group_delimiter && this.state[this.state.tmp.area].opt.cite_group_delimiter) {
12427                    this.state.tmp.use_cite_group_delimiter = false;
12428                }
12429            }
12430        }
12431    }
12432};
12433
12434/*
12435CSL.NameOutput.prototype.suppressNames = function() {
12436    suppress_condition = suppress_min && display_names.length >= suppress_min;
12437    if (suppress_condition) {
12438        continue;
12439    }
12440}
12441*/
12442
12443/*global CSL: true */
12444
12445CSL.NameOutput.prototype.isPerson = function (value) {
12446    if (value.literal
12447        || (!value.given && value.family && value.isInstitution)) {
12448        
12449        return false;
12450    } else {
12451        return true;
12452    }
12453};
12454
12455/*global CSL: true */
12456
12457CSL.NameOutput.prototype.truncatePersonalNameLists = function () {
12458    var v, i, ilen, j, jlen, chopvar, values;
12459    // XXX Before truncation, make a note of the original number
12460    // of names, for use in et-al evaluation.
12461    this.freeters_count = {};
12462    this.persons_count = {};
12463    this.institutions_count = {};
12464    // By key is okay here, as we don't care about sequence.
12465    for (v in this.freeters) {
12466        if (this.freeters.hasOwnProperty(v)) {
12467            this.freeters_count[v] = this.freeters[v].length;
12468            this.freeters[v] = this._truncateNameList(this.freeters, v);
12469        }
12470    }
12471
12472    for (v in this.persons) {
12473        if (this.persons.hasOwnProperty(v)) {
12474            this.institutions_count[v] = this.institutions[v].length;
12475            this._truncateNameList(this.institutions, v);
12476            this.persons[v] = this.persons[v].slice(0, this.institutions[v].length);
12477            this.persons_count[v] = [];
12478            for (j = 0, jlen = this.persons[v].length; j < jlen; j += 1) {
12479                this.persons_count[v][j] = this.persons[v][j].length;
12480                this.persons[v][j] = this._truncateNameList(this.persons, v, j);
12481            }
12482        }
12483    }
12484    if (this.state.opt.development_extensions.etal_min_etal_usefirst_hack
12485        && this.etal_min === 1 && this.etal_use_first === 1 
12486        && !(this.state.tmp.extension
12487             || this.state.tmp.just_looking)) {
12488        chopvar = v;
12489    } else {
12490        chopvar = false;
12491    }
12492    if (chopvar || this._please_chop) {
12493        for (i = 0, ilen = this.variables.length; i < ilen; i += 1) {
12494            v = this.variables[i];
12495            if (this.freeters[v].length) {
12496                if (this._please_chop === v) {
12497                    this.freeters[v] = this.freeters[v].slice(1);
12498                    this.freeters_count[v] += -1;
12499                    this._please_chop = false;
12500                } else if (chopvar && !this._please_chop) {
12501                    this.freeters[v] = this.freeters[v].slice(0, 1);
12502                    this.freeters_count[v] = 1;
12503                    this.institutions[v] = [];
12504                    this.persons[v] = [];
12505                    this._please_chop = chopvar;
12506                }
12507            }
12508            for (var j=0,jlen = this.persons[v].length;j<jlen;j++) {
12509                if (this.persons[v][j].length) {
12510                    if (this._please_chop === v) {
12511                        this.persons[v][j] = this.persons[v][j].slice(1);
12512                        this.persons_count[v][j] += -1;
12513                        this._please_chop = false;
12514                        break;
12515                    } else if (chopvar && !this._please_chop) {
12516                        this.freeters[v] = this.persons[v][j].slice(0, 1);
12517                        this.freeters_count[v] = 1;
12518                        this.institutions[v] = [];
12519                        this.persons[v] = [];
12520                        values = [];
12521                        this._please_chop = chopvar;
12522                        break;
12523                    }
12524                }
12525            }
12526            if (this.institutions[v].length) {
12527                if (this._please_chop === v) {
12528                    this.institutions[v] = this.institutions[v].slice(1);
12529                    this.institutions_count[v] += -1;
12530                    this._please_chop = false;
12531                } else if (chopvar && !this._please_chop) {
12532                    this.institutions[v] = this.institutions[v].slice(0, 1);
12533                    this.institutions_count[v] = 1;
12534                    values = [];
12535                    this._please_chop = chopvar;
12536                }
12537            }
12538        }
12539    }
12540
12541    // Transliteration and abbreviation mapping
12542
12543    // Hmm. This could produce three lists for each nameset:
12544    //   - primary (transformed in place)
12545    //   - secondary
12546    //   - tertiary
12547    // with items that produce no result in the secondary and tertiary
12548    // transforms set to false. Maybe.
12549
12550    // Actually that would be insane, so forget it.
12551    // What we need is to add suitable parameters to getName(), and merge
12552    // the single-name-level operations below into that function. Then the
12553    // operation can be applied in util_names_render.js, and the logic
12554    // becomes very similar to what we already have running in util_transform.js.
12555
12556/*
12557    for (v in this.freeters) {
12558        this._transformNameset(this.freeters[v]);
12559    }
12560    for (v in this.persons) {
12561        for (i = 0, ilen = this.persons[v].length; i < ilen; i += 1) {
12562            this._transformNameset(this.persons[v][i]);
12563        }
12564        this._transformNameset(this.institutions[v]);
12565    }
12566*/
12567
12568    // Could also be factored out to a separate function for clarity.
12569    // ???? XXX Does this belong?
12570    for (i = 0, ilen = this.variables.length; i < ilen; i += 1) {
12571        if (this.institutions[v].length) {
12572            this.nameset_offset += 1;
12573        }
12574        for (var j=0,jlen=this.persons[v].length;j<jlen;j++) {
12575            if (this.persons[v][j].length) {
12576                this.nameset_offset += 1;
12577            }
12578            // this.institutions[v][i] = this._splitInstitution(this.institutions[v][i], v, i);
12579        }
12580    }
12581};
12582
12583CSL.NameOutput.prototype._truncateNameList = function (container, variable, index) {
12584    var lst;
12585    if ("undefined" === typeof index) {
12586        lst = container[variable];
12587    } else {
12588        lst = container[variable][index];
12589    }
12590    if (this.state[this.state[this.state.tmp.area].root].opt.max_number_of_names 
12591        && lst.length > 50 
12592        && lst.length > (this.state[this.state[this.state.tmp.area].root].opt.max_number_of_names + 2)) {
12593
12594        // Preserve the last name in the list, in case we're rendering with a PI ellipsis (et-al-use-last)
12595        var limit = this.state[this.state[this.state.tmp.area].root].opt.max_number_of_names;
12596        lst = lst.slice(0, limit+1).concat(lst.slice(-1));
12597    }
12598    return lst;
12599};
12600
12601
12602/*global CSL: true */
12603
12604CSL.NameOutput.prototype.divideAndTransliterateNames = function () {
12605    var i, ilen, j, jlen;
12606    var Item = this.Item;
12607    var variables = this.variables;
12608    this.varnames = variables.slice();
12609    this.freeters = {};
12610    this.persons = {};
12611    this.institutions = {};
12612    for (i = 0, ilen = variables.length; i < ilen; i += 1) {
12613        var v = variables[i];
12614        this.variable_offset[v] = this.nameset_offset;
12615        var values = this._normalizeVariableValue(Item, v);
12616        if (this.name.strings["suppress-min"] && values.length >= this.name.strings["suppress-min"]) {
12617            values = [];
12618        }
12619        if (this.name.strings["suppress-max"] && values.length <= this.name.strings["suppress-max"]) {
12620            values = [];
12621        }
12622        this._getFreeters(v, values);
12623        this._getPersonsAndInstitutions(v, values);
12624        if (this.state.opt.development_extensions.spoof_institutional_affiliations) {
12625            if (this.name.strings["suppress-min"] === 0) {
12626                this.freeters[v] = [];
12627                for (j = 0, jlen = this.persons[v].length; j < jlen; j += 1) {
12628                    this.persons[v][j] = [];
12629                }
12630            } else if (this.institution.strings["suppress-min"] === 0) {
12631                this.institutions[v] = [];
12632                this.freeters[v] = this.freeters[v].concat(this.persons[v]);
12633                for (j = 0, jlen = this.persons[v].length; j < jlen; j += 1) {
12634                    for (var k = 0, klen = this.persons[v][j].length; k < klen; k += 1) {
12635                        this.freeters[v].push(this.persons[v][j][k]);
12636                    }
12637                }
12638                this.persons[v] = [];
12639            }
12640        }
12641    }
12642};
12643
12644CSL.NameOutput.prototype._normalizeVariableValue = function (Item, variable) {
12645    var names;
12646    if ("string" === typeof Item[variable] || "number" === typeof Item[variable]) {
12647        CSL.debug("name variable \"" + variable + "\" is string or number, not array. Attempting to fix.");
12648        names = [{literal: Item[variable] + ""}];
12649    } else if (!Item[variable]) {
12650        names = [];
12651    } else if ("number" !== typeof Item[variable].length) {
12652        CSL.debug("name variable \"" + variable + "\" is object, not array. Attempting to fix.");
12653        Item[variable] = [Item[variable]];
12654        names = Item[variable].slice();
12655    } else {
12656        names = Item[variable].slice();
12657    }
12658    return names;
12659};
12660
12661CSL.NameOutput.prototype._getFreeters = function (v, values) {
12662    this.freeters[v] = [];
12663    if (this.state.opt.development_extensions.spoof_institutional_affiliations) {
12664        for (var i=values.length-1;i>-1;i--) {
12665            if (this.isPerson(values[i])) {
12666                var value = this._checkNickname(values.pop());
12667                if (value) {
12668                    this.freeters[v].push(value);
12669                }
12670            } else {
12671                break;
12672            }
12673        }
12674    } else {
12675        for (var i=values.length-1;i>-1;i--) {
12676            var value = values.pop();
12677            if (this.isPerson(value)) {
12678                var value = this._checkNickname(value);
12679            }
12680            this.freeters[v].push(value);
12681        }
12682    }
12683    this.freeters[v].reverse();
12684    if (this.freeters[v].length) {
12685        this.nameset_offset += 1;
12686    }
12687};
12688
12689CSL.NameOutput.prototype._getPersonsAndInstitutions = function (v, values) {
12690    this.persons[v] = [];
12691    this.institutions[v] = [];
12692    if (!this.state.opt.development_extensions.spoof_institutional_affiliations) {
12693        return;
12694    }
12695    var persons = [];
12696    var has_affiliates = false;
12697    var first = true;
12698    for (var i = values.length - 1; i > -1; i += -1) {
12699        if (this.isPerson(values[i])) {
12700            var value = this._checkNickname(values[i]);
12701            if (value) {
12702                persons.push(value);
12703            }
12704        } else {
12705            has_affiliates = true;
12706            this.institutions[v].push(values[i]);
12707            if (!first) {
12708                persons.reverse();
12709                this.persons[v].push(persons);
12710                persons = [];
12711            }
12712            first = false;
12713        }
12714    }
12715    if (has_affiliates) {
12716        persons.reverse();
12717        this.persons[v].push(persons);
12718        this.persons[v].reverse();
12719        this.institutions[v].reverse();
12720    }
12721};
12722
12723CSL.NameOutput.prototype._clearValues = function (values) {
12724    for (var i = values.length - 1; i > -1; i += -1) {
12725        values.pop();
12726    }
12727};
12728
12729CSL.NameOutput.prototype._checkNickname = function (name) {
12730    if (["interview", "personal_communication"].indexOf(this.Item.type) > -1) {
12731        var author = "";
12732        author = CSL.Util.Names.getRawName(name);
12733        if (author && this.state.sys.getAbbreviation && !(this.item && this.item["suppress-author"])) {
12734            var normalizedKey = author;
12735            if (this.state.sys.normalizeAbbrevsKey) {
12736                // The first argument does not have to be the exact variable name.
12737                normalizedKey = this.state.sys.normalizeAbbrevsKey("author", author);
12738            }
12739            this.state.transform.loadAbbreviation("default", "nickname", normalizedKey, this.Item.language);
12740            // XXX Why does this have to happen here?
12741            var myLocalName = this.state.transform.abbrevs["default"].nickname[normalizedKey];
12742            if (myLocalName) {
12743                if (myLocalName === "!here>>>") {
12744                    name = false;
12745                } else {
12746                    name = {family:myLocalName,given:''};
12747                }
12748            }
12749        }
12750    }
12751    return name;
12752};
12753
12754/*global CSL: true */
12755
12756CSL.NameOutput.prototype._purgeEmptyBlobs = function (blobs) {
12757    for (var i = blobs.length - 1; i > -1; i += -1) {
12758        if (!blobs[i] || blobs[i].length === 0 || !blobs[i].blobs.length) {
12759            blobs = blobs.slice(0, i).concat(blobs.slice(i + 1));
12760        }
12761    }
12762    return blobs;
12763};
12764
12765CSL.NameOutput.prototype.joinPersons = function (blobs, pos, j, tokenname) {
12766    var ret;
12767    blobs = this._purgeEmptyBlobs(blobs);
12768    if (!tokenname) {
12769        tokenname = "name";
12770    }
12771    if ("undefined" === typeof j) {
12772        if (this.etal_spec[pos].freeters === 1) {
12773           ret = this._joinEtAl(blobs);
12774        } else if (this.etal_spec[pos].freeters === 2) {
12775            ret = this._joinEllipsis(blobs);
12776        } else if (!this.state.tmp.sort_key_flag) {
12777            ret = this._joinAnd(blobs);
12778        } else {
12779            ret = this._join(blobs, this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", "));
12780        }
12781    } else {
12782        if (this.etal_spec[pos].persons[j] === 1) {
12783            ret = this._joinEtAl(blobs);
12784        } else if (this.etal_spec[pos].persons[j] === 2) {
12785            ret = this._joinEllipsis(blobs);
12786        } else if (!this.state.tmp.sort_key_flag) {
12787            ret = this._joinAnd(blobs);
12788        } else {
12789            ret = this._join(blobs, this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", "));
12790        }
12791    }
12792    return ret;
12793};
12794
12795
12796CSL.NameOutput.prototype.joinInstitutionSets = function (blobs, pos) {
12797    var ret;
12798    blobs = this._purgeEmptyBlobs(blobs);
12799    if (this.etal_spec[pos].institutions === 1) {
12800        ret = this._joinEtAl(blobs, "institution");
12801    } else if (this.etal_spec[pos].institutions === 2) {
12802        ret = this._joinEllipsis(blobs, "institution");
12803    } else {
12804        ret = this._joinAnd(blobs);
12805    }
12806    return ret;
12807};
12808
12809
12810CSL.NameOutput.prototype.joinPersonsAndInstitutions = function (blobs) {
12811    //
12812    blobs = this._purgeEmptyBlobs(blobs);
12813    var ret = this._join(blobs, this.state.tmp.name_delimiter);
12814    ret.isInstitution = true;
12815    return ret;
12816};
12817
12818// LEGACY
12819// This should go away eventually
12820CSL.NameOutput.prototype.joinFreetersAndInstitutionSets = function (blobs) {
12821    // Nothing, one or two, never more
12822    blobs = this._purgeEmptyBlobs(blobs);
12823    var ret = this._join(blobs, "[never here]", this["with"].single, this["with"].multiple);
12824    //var ret = this._join(blobs, "");
12825    return ret;
12826};
12827
12828CSL.NameOutput.prototype._getAfterInvertedName = function(blobs, delimiter, finalJoin) {
12829    if (finalJoin && blobs.length > 1) {
12830        if (this.state.inheritOpt(this.name, "delimiter-precedes-last") === "after-inverted-name") {
12831            var prevBlob = blobs[blobs.length - 2];
12832            if (prevBlob.blobs.length > 0 && prevBlob.blobs[0].isInverted) {
12833                finalJoin.strings.prefix = delimiter;
12834            }
12835        }
12836    }
12837    return finalJoin;
12838}
12839
12840CSL.NameOutput.prototype._getAndJoin = function (blobs, delimiter) {
12841    var finalJoin = false;
12842    if (blobs.length > 1) {
12843        var singleOrMultiple = "single";
12844        if (blobs.length > 2) {
12845            singleOrMultiple = "multiple";
12846        }
12847        if (blobs[blobs.length - 1].isInstitution) {
12848            finalJoin = this.institution.and[singleOrMultiple];
12849        } else {
12850            finalJoin = this.name.and[singleOrMultiple];
12851        }
12852        // finalJoin = new CSL.Blob(finalJoin);
12853        finalJoin = JSON.parse(JSON.stringify(finalJoin));
12854        finalJoin = this._getAfterInvertedName(blobs, delimiter, finalJoin);
12855    }
12856    return finalJoin;
12857};
12858
12859CSL.NameOutput.prototype._joinEtAl = function (blobs) {
12860    var delimiter = this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", ");
12861    var blob = this._join(blobs, delimiter);
12862    
12863    // notSerious
12864    this.state.output.openLevel(this._getToken("name"));
12865    // Delimiter is applied from separately saved source in this case,
12866    // for discriminate application of single and multiple joins.
12867    this.state.output.current.value().strings.delimiter = "";
12868    this.state.output.append(blob, "literal", true);
12869    if (blobs.length > 1) {
12870        this.state.output.append(this["et-al"].multiple, "literal", true);
12871    } else if (blobs.length === 1) {
12872        this.state.output.append(this["et-al"].single, "literal", true);
12873    }
12874    this.state.output.closeLevel();
12875    return this.state.output.pop();
12876};
12877
12878
12879CSL.NameOutput.prototype._joinEllipsis = function (blobs) {
12880    var delimiter = this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", ");
12881    var finalJoin = false;
12882    if (blobs.length > 1) {
12883        var singleOrMultiple = "single";
12884        if (blobs.length > 2) {
12885            singleOrMultiple = "multiple";
12886        }
12887        finalJoin = JSON.parse(JSON.stringify(this.name.ellipsis[singleOrMultiple]));
12888        finalJoin = this._getAfterInvertedName(blobs, delimiter , finalJoin);
12889        
12890    }
12891    return this._join(blobs, delimiter, finalJoin);
12892};
12893
12894CSL.NameOutput.prototype._joinAnd = function (blobs) {
12895    var delimiter = this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", ");
12896    var finalJoin = this._getAndJoin(blobs, delimiter);
12897    return this._join(blobs, delimiter, finalJoin);
12898};
12899
12900
12901CSL.NameOutput.prototype._join = function (blobs, delimiter, finalJoin) {
12902    var i, ilen;
12903    if (!blobs) {
12904        return false;
12905    }
12906    blobs = this._purgeEmptyBlobs(blobs);
12907    if (!blobs.length) {
12908        return false;
12909    }
12910    if (blobs.length > 1) {
12911        if (blobs.length === 2) {
12912            if (!finalJoin) {
12913                blobs[0].strings.suffix += delimiter;
12914            } else {
12915                blobs = [blobs[0], finalJoin, blobs[1]];
12916            }
12917        } else {
12918            var offset;
12919            if (finalJoin) {
12920                offset = 1;
12921            } else {
12922                offset = 0;
12923            }
12924            var blob = blobs.pop();
12925            for (var i=0,ilen=blobs.length - offset;i<ilen;i++) {
12926                blobs[i].strings.suffix += delimiter;
12927            }
12928            blobs.push(finalJoin);
12929            blobs.push(blob);
12930        }
12931    }
12932
12933    //this.state.output.openLevel(this._getToken(tokenname));
12934    this.state.output.openLevel();
12935
12936    //this.state.output.openLevel(this._getToken("empty"));
12937    // Delimiter is applied from separately saved source in this case,
12938    // for discriminate application of single and multiple joins.
12939    for (i = 0, ilen = blobs.length; i < ilen; i += 1) {
12940        this.state.output.append(blobs[i], false, true);
12941    }
12942    this.state.output.closeLevel();
12943    return this.state.output.pop();
12944};
12945
12946
12947CSL.NameOutput.prototype._getToken = function (tokenname) {
12948    var token = this[tokenname];
12949    if (tokenname === "institution") {
12950        var newtoken = new CSL.Token();
12951        // Which, hmm, is the same thing as "empty"
12952        // Oh, well.
12953        //newtoken.strings.prefix = token.prefix;
12954        //newtoken.strings.suffix = token.suffix;
12955        return newtoken;
12956    }
12957    return token;
12958};
12959
12960/*global CSL: true */
12961
12962CSL.NameOutput.prototype.checkCommonAuthor = function(requireMatch) {
12963    if (!requireMatch) {
12964        return false;
12965    }
12966    var common_term = false;
12967    if (this.variables.length === 2) {
12968        var variables = this.variables;
12969        var varnames = variables.slice();
12970        varnames.sort();
12971        common_term = varnames.join("");
12972    }
12973    if (!common_term) {
12974        return false;
12975    }
12976    var has_term = false;
12977    if (this.state.locale[this.state.opt.lang].terms[common_term]) {
12978        has_term = true;
12979    }
12980    if (!has_term) {
12981        this.state.tmp.done_vars.push(this.variables[0]);
12982        this.state.tmp.done_vars.push(this.variables[1]);
12983        return false;
12984    }
12985    var firstSet = this.Item[this.variables[0]];
12986    var secondSet = this.Item[this.variables[1]];
12987    var perfectMatch = this._compareNamesets(firstSet, secondSet);
12988    if (perfectMatch === true) {
12989        this.state.tmp.done_vars.push(this.variables[0]);
12990        this.state.tmp.done_vars.push(this.variables[1]);
12991    }
12992    // This may be counter-intuitive.
12993    // This check controls whether we will fail on the this attempt at rendering
12994    // and proceed with substitution. If the names match exactly (true), then
12995    // we do *not* want to abort and continue with substitution.
12996    return !perfectMatch;
12997};
12998
12999CSL.NameOutput.prototype.setCommonTerm = function () {
13000    var variables = this.variables;
13001    var varnames = variables.slice();
13002    varnames.sort();
13003    this.common_term = varnames.join("");
13004    // When no varnames are on offer
13005    if (!this.common_term) {
13006        return;
13007    }
13008    var has_term = false;
13009    if (this.label && this.label[this.variables[0]]) {
13010        if (this.label[this.variables[0]].before) {
13011            has_term = this.state.getTerm(this.common_term, this.label[this.variables[0]].before.strings.form, 0);
13012        } else if (this.label[this.variables[0]].after) {
13013            has_term = this.state.getTerm(this.common_term, this.label[this.variables[0]].after.strings.form, 0);
13014        }
13015     }
13016
13017    // When there is no common term
13018    if (!this.state.locale[this.state.opt.lang].terms[this.common_term]
13019        || !has_term
13020        || this.variables.length < 2) {
13021        this.common_term = false;
13022        return;
13023    }
13024    var freeters_offset = 0;
13025    for (var i = 0, ilen = this.variables.length - 1; i < ilen; i += 1) {
13026        var v = this.variables[i];
13027        var vv = this.variables[i + 1];
13028        if (this.freeters[v].length || this.freeters[vv].length) {
13029            if (this.etal_spec[v].freeters !== this.etal_spec[vv].freeters
13030                || !this._compareNamesets(this.freeters[v], this.freeters[vv])) {
13031                this.common_term = false;
13032                return;
13033            }
13034            freeters_offset += 1;
13035        }
13036        if (this.persons[v].length !== this.persons[vv].length) {
13037            this.common_term = false;
13038            return;
13039        }
13040        for (var j = 0, jlen = this.persons[v].length; j < jlen; j += 1) {
13041            if (this.etal_spec[v].persons[j] !== this.etal_spec[vv].persons[j]
13042                || !this._compareNamesets(this.persons[v][j], this.persons[vv][j])) {
13043                this.common_term = false;
13044                return;
13045            }
13046        }
13047    }
13048};
13049
13050CSL.NameOutput.prototype._compareNamesets = function (base_nameset, nameset) {
13051    if (!base_nameset || !nameset || base_nameset.length !== nameset.length) {
13052        return false;
13053    }
13054    for (var i = 0, ilen = nameset.length; i < ilen; i += 1) {
13055        for (var j = 0, jlen = CSL.NAME_PARTS.length; j < jlen; j += 1) {
13056            var part = CSL.NAME_PARTS[j];
13057            if (!base_nameset[i] || base_nameset[i][part] != nameset[i][part]) {
13058                return false;
13059            }
13060        }
13061    }
13062    return true;
13063};
13064
13065/*global CSL: true */
13066
13067CSL.NameOutput.prototype.constrainNames = function () {
13068    // figure out how many names to include, in light of the disambig params
13069    //
13070    this.names_count = 0;
13071    //var pos = 0;
13072    var pos;
13073    for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
13074        var v = this.variables[i];
13075        pos = this.nameset_base + i;
13076        // Constrain independent authors here
13077        if (this.freeters[v].length) {
13078            this.state.tmp.names_max.push(this.freeters[v].length, "literal");
13079            this._imposeNameConstraints(this.freeters, this.freeters_count, v, pos);
13080            this.names_count += this.freeters[v].length;
13081        }
13082
13083        // Constrain institutions here
13084        if (this.institutions[v].length) {
13085            this.state.tmp.names_max.push(this.institutions[v].length, "literal");
13086            this._imposeNameConstraints(this.institutions, this.institutions_count, v, pos);
13087            this.persons[v] = this.persons[v].slice(0, this.institutions[v].length);
13088            this.names_count += this.institutions[v].length;
13089        }
13090
13091        for (var j = 0, jlen = this.persons[v].length; j < jlen; j += 1) {
13092            // Constrain affiliated authors here
13093            if (this.persons[v][j].length) {
13094                this.state.tmp.names_max.push(this.persons[v][j].length, "literal");
13095                this._imposeNameConstraints(this.persons[v], this.persons_count[v], j, pos);
13096                this.names_count += this.persons[v][j].length;
13097            }
13098        }
13099    }
13100};
13101
13102CSL.NameOutput.prototype._imposeNameConstraints = function (lst, count, key, pos) {
13103    // display_names starts as the original length of this list of names.
13104    var display_names = lst[key];
13105    var discretionary_names_length = this.state.tmp["et-al-min"];
13106    
13107    // Mappings, to allow existing disambiguation machinery to
13108    // remain untouched.
13109    if (this.state.tmp.suppress_decorations) {
13110        if (this.state.tmp.disambig_request && this.state.tmp.disambig_request.names[pos]) {
13111            // Oh. Trouble.
13112            // state.tmp.nameset_counter is the number of the nameset
13113            // in the disambiguation try-sequence. Ouch.
13114            discretionary_names_length = this.state.tmp.disambig_request.names[pos];
13115        } else if (count[key] >= this.etal_min) {
13116            discretionary_names_length = this.etal_use_first;
13117        }
13118    } else {
13119        if (this.state.tmp.disambig_request 
13120            && this.state.tmp.disambig_request.names[pos] > this.etal_use_first) {
13121
13122            if (count[key] < this.etal_min) {
13123                discretionary_names_length = count[key];
13124            } else {
13125                discretionary_names_length = this.state.tmp.disambig_request.names[pos];
13126            }
13127        } else if (count[key] >= this.etal_min) {
13128            //discretionary_names_length = this.state.tmp["et-al-use-first"];
13129            discretionary_names_length = this.etal_use_first;
13130        }
13131        // XXXX: This is a workaround. Under some conditions.
13132        // Where namesets disambiguate on one of the two names
13133        // dropped here, it is possible for more than one
13134        // in-text citation to be close (and indistinguishable)
13135        // matches to a single bibliography entry.
13136        //
13137        // 
13138        if (this.etal_use_last && discretionary_names_length > (this.etal_min - 2)) {
13139            discretionary_names_length = this.etal_min - 2;
13140        }
13141    }
13142    var sane = this.etal_min >= this.etal_use_first;
13143    var overlength = count[key] > discretionary_names_length;
13144    // This var is used to control contextual join, and
13145    // lies about the number of names when forceEtAl is true,
13146    // unless normalized.
13147    if (discretionary_names_length > count[key]) {
13148
13149        // Use actual truncated list length, to avoid overrun.
13150        discretionary_names_length = display_names.length;
13151    }
13152    // forceEtAl is relevant when the author list is
13153    // truncated to eliminate clutter.
13154    if (sane && overlength) {
13155        if (this.etal_use_last) {
13156            lst[key] = display_names.slice(0, discretionary_names_length).concat(display_names.slice(-1));
13157        } else {
13158            lst[key] = display_names.slice(0, discretionary_names_length);
13159        }
13160    }
13161    this.state.tmp.disambig_settings.names[pos] = lst[key].length;
13162    this.state.disambiguate.padBase(this.state.tmp.disambig_settings);
13163    
13164
13165    // ???
13166    //if (!this.state.tmp.disambig_request) {
13167    //    this.state.tmp.disambig_settings.givens[pos] = [];
13168    //}
13169};
13170
13171// Disambiguate names (the number of names is controlled externally, by successive
13172// runs of the processor).
13173
13174/*global CSL: true */
13175
13176CSL.NameOutput.prototype.disambigNames = function () {
13177    var pos;
13178    for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
13179        var v = this.variables[i];
13180        pos = this.nameset_base + i;
13181        if (this.freeters[v].length) {
13182            this._runDisambigNames(this.freeters[v], pos);
13183        }
13184        // Is this even necessary???
13185        if (this.institutions[v].length) {
13186            if ("undefined" === typeof this.state.tmp.disambig_settings.givens[pos]) {
13187                this.state.tmp.disambig_settings.givens[pos] = [];
13188            }
13189            for (var j=0,jlen=this.institutions[v].length;j<jlen;j+=1) {
13190                if ("undefined" === typeof this.state.tmp.disambig_settings.givens[pos][j]) {
13191                    this.state.tmp.disambig_settings.givens[pos].push(2);
13192                }
13193            }
13194        }
13195        for (var j = 0, jlen = this.persons[v].length; j < jlen; j += 1) {
13196            if (this.persons[v][j].length) {
13197                this._runDisambigNames(this.persons[v][j], pos);
13198            }
13199        }
13200    }
13201};
13202
13203CSL.NameOutput.prototype._runDisambigNames = function (lst, pos) {
13204    var chk, myform, myinitials, param, i, ilen, paramx;
13205    //if (this.state.tmp.root === "bibliography") {
13206    //    return;
13207    //}
13208    for (i = 0, ilen = lst.length; i < ilen; i += 1) {
13209        //
13210        // register the name in the global names disambiguation
13211        // registry
13212
13213        if (!lst[i].given && !lst[i].family) {
13214            continue;
13215        }
13216
13217        myinitials = this.state.inheritOpt(this.name, "initialize-with");
13218        this.state.registry.namereg.addname("" + this.Item.id, lst[i], i);
13219        chk = this.state.tmp.disambig_settings.givens[pos];
13220        if ("undefined" === typeof chk) {
13221            // Holes can appear in the list, probably due to institutional
13222            // names that this doesn't touch. Maybe. This fills them up.
13223            for (var j = 0, jlen = pos + 1; j < jlen; j += 1) {
13224                if (!this.state.tmp.disambig_settings.givens[j]) {
13225                    this.state.tmp.disambig_settings.givens[j] = [];
13226                }
13227            }
13228        }
13229        chk = this.state.tmp.disambig_settings.givens[pos][i];
13230        //if ("undefined" !== typeof chk && this.state.tmp.root === 'citation') {
13231            //this.state.tmp.disambig_settings.givens[pos] = [];
13232            //chk = undefined;
13233        //}
13234        if ("undefined" === typeof chk) {
13235            myform = this.state.inheritOpt(this.name, "form", "name-form", "long");
13236            param = this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, 0, myform, myinitials);
13237            this.state.tmp.disambig_settings.givens[pos].push(param);
13238        }
13239        //
13240        // set the display mode default for givennames if required
13241        myform = this.state.inheritOpt(this.name, "form", "name-form", "long");
13242        paramx = this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, 0, myform, myinitials);
13243        // this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, 0, myform, myinitials);
13244        if (this.state.tmp.disambig_request) {
13245            //
13246            // fix a request for initials that makes no sense.
13247            // can't do this in disambig, because the availability
13248            // of initials is not a global parameter.
13249            var val = this.state.tmp.disambig_settings.givens[pos][i];
13250            // This is limited to by-cite disambiguation.
13251            // 2012-09-13: added lst[i].given check to condition
13252            if (val === 1 && 
13253                this.state.citation.opt["givenname-disambiguation-rule"] === "by-cite" && 
13254                ("undefined" === typeof this.state.inheritOpt(this.name, "initialize-with")
13255                 || "undefined" === typeof lst[i].given)) {
13256                val = 2;
13257            }
13258            param = val;
13259            // 2012-09-13: lst[i].given check protects against personal names
13260            // that have no first name element. These were causing an infinite loop,
13261            // this prevents that.
13262            if (this.state.opt["disambiguate-add-givenname"] && lst[i].given) {
13263                param = this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, param, this.state.inheritOpt(this.name, "form", "name-form", "long"), this.state.inheritOpt(this.name, "initialize-with"));
13264            }
13265        } else {
13266            //
13267            // it clicks.  here is where we will put the
13268            // call to the names register, to get the floor value
13269            // for an individual name.
13270            //
13271            param = paramx;
13272        }
13273        // Need to save off the settings based on subsequent
13274        // form, when first cites are rendered.
13275        if (!this.state.tmp.just_looking && this.item && this.item.position === CSL.POSITION_FIRST) {
13276            if (paramx > param) {
13277                param = paramx;
13278            }
13279        }
13280        if (!this.state.tmp.sort_key_flag) {
13281            this.state.tmp.disambig_settings.givens[pos][i] = param;
13282            if ("string" === typeof myinitials
13283                && ("undefined" === typeof this.name.strings["initialize"]
13284                    || true === this.name.strings["initialize"])) {
13285
13286                this.state.tmp.disambig_settings.use_initials = true;
13287            }
13288        }
13289    }
13290    //this.state.registry.registry[this.Item.id].disambig.givens = this.state.tmp.disambig_settings.givens.slice();
13291};
13292
13293/*global CSL: true */
13294
13295CSL.NameOutput.prototype.getEtAlConfig = function () {
13296    var item = this.item;
13297    this["et-al"] = {};
13298
13299    this.state.output.append(this.etal_term, this.etal_style, true);
13300    this["et-al"].single = this.state.output.pop();
13301    this["et-al"].single.strings.suffix = this.etal_suffix;
13302    this["et-al"].single.strings.prefix = this.etal_prefix_single;
13303    
13304    this.state.output.append(this.etal_term, this.etal_style, true);
13305    this["et-al"].multiple = this.state.output.pop();
13306    this["et-al"].multiple.strings.suffix = this.etal_suffix;
13307    this["et-al"].multiple.strings.prefix = this.etal_prefix_multiple;
13308
13309    // Et-al style parameters (may be sidestepped by disambiguation
13310    // in util_names_constraints.js)
13311    if ("undefined" === typeof item) {
13312        item = {};
13313    }
13314    //print("== getEtAlConfig() == "+this.state.tmp.area);
13315
13316    if (item.position) {
13317        if (this.state.inheritOpt(this.name, "et-al-subsequent-min")) {
13318            // XX
13319            this.etal_min = this.state.inheritOpt(this.name, "et-al-subsequent-min");
13320        } else {
13321            // XX
13322            this.etal_min = this.state.inheritOpt(this.name, "et-al-min");
13323        }
13324        if (this.state.inheritOpt(this.name, "et-al-subsequent-use-first")) {
13325            // XX
13326            this.etal_use_first = this.state.inheritOpt(this.name, "et-al-subsequent-use-first");
13327        } else {
13328            // XX
13329            this.etal_use_first = this.state.inheritOpt(this.name, "et-al-use-first");
13330        }
13331    } else {
13332        if (this.state.tmp["et-al-min"]) {
13333            this.etal_min = this.state.tmp["et-al-min"];
13334        } else {
13335            // XX
13336            this.etal_min = this.state.inheritOpt(this.name, "et-al-min");
13337        }
13338        if (this.state.tmp["et-al-use-first"]) {
13339            this.etal_use_first = this.state.tmp["et-al-use-first"];
13340        } else {
13341            // XX
13342            this.etal_use_first = this.state.inheritOpt(this.name, "et-al-use-first");
13343        }
13344        if ("boolean" === typeof this.state.tmp["et-al-use-last"]) {
13345            //print("  etal_use_last from tmp: "+this.state.tmp["et-al-use-last"]);
13346            this.etal_use_last = this.state.tmp["et-al-use-last"];
13347        } else {
13348            //print("  etal_use_last from name: "+this.name.strings["et-al-use-last"]);
13349            // XX
13350            this.etal_use_last = this.state.inheritOpt(this.name, "et-al-use-last");
13351        }
13352        //print("  etal_use_last: "+this.etal_use_last);
13353    }
13354    // Provided for use as the starting level for disambiguation.
13355    if (!this.state.tmp["et-al-min"]) {
13356        this.state.tmp["et-al-min"] = this.etal_min;
13357    }
13358};
13359
13360/*global CSL: true */
13361
13362CSL.NameOutput.prototype.setEtAlParameters = function () {
13363    var i, ilen, j, jlen;
13364    for (i = 0, ilen = this.variables.length; i < ilen; i += 1) {
13365        var v = this.variables[i];
13366        if ("undefined" === typeof this.etal_spec[v]) {
13367            this.etal_spec[v] = {freeters:0,institutions:0,persons:[]};
13368        }
13369        this.etal_spec[this.nameset_base + i] = this.etal_spec[v];
13370        if (this.freeters[v].length) {
13371            this._setEtAlParameter("freeters", v);
13372        }
13373        for (j = 0, jlen = this.persons[v].length; j < jlen; j += 1) {
13374            if ("undefined" === typeof this.etal_spec[v][j]) {
13375                this.etal_spec[v].persons[j] = 0;
13376            }
13377            this._setEtAlParameter("persons", v, j);
13378        }
13379        if (this.institutions[v].length) {
13380            this._setEtAlParameter("institutions", v);
13381        }
13382    }
13383};
13384
13385CSL.NameOutput.prototype._setEtAlParameter = function (type, v, j) {
13386    var lst, count;
13387    if (type === "persons") {
13388        lst = this.persons[v][j];
13389        count = this.persons_count[v][j];
13390    } else {
13391        lst = this[type][v];
13392        count = this[type + "_count"][v];
13393    }
13394    if (lst.length < count && !this.state.tmp.sort_key_flag) {
13395        if (this.etal_use_last) {
13396            if (type === "persons") {
13397                this.etal_spec[v].persons[j] = 2;
13398            } else {
13399                this.etal_spec[v][type] = 2;
13400            }
13401        } else {
13402            if (type === "persons") {
13403                this.etal_spec[v].persons[j] = 1;
13404            } else {
13405                this.etal_spec[v][type] = 1;
13406            }
13407        }
13408    } else {
13409        if (type === "persons") {
13410            this.etal_spec[v].persons[j] = 0;
13411        } else {
13412            this.etal_spec[v][type] = 0;
13413        }
13414    }
13415};
13416
13417/*global CSL: true */
13418
13419CSL.NameOutput.prototype.renderAllNames = function () {
13420    // Note that et-al/ellipsis parameters are set on the basis
13421    // of rendering order through the whole cite.
13422    var pos;
13423    for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
13424        var v = this.variables[i];
13425
13426        if (this.freeters[v].length || this.institutions[v].length) {
13427            if (!this.state.tmp.group_context.tip.condition) {
13428                this.state.tmp.just_did_number = false;
13429            }
13430        }
13431        
13432        pos = this.nameset_base + i;
13433        if (this.freeters[v].length) {
13434            this.freeters[v] = this._renderNames(v, this.freeters[v], pos);
13435        }
13436        for (var j = 0, jlen = this.institutions[v].length; j < jlen; j += 1) {
13437            this.persons[v][j] = this._renderNames(v, this.persons[v][j], pos, j);
13438        }
13439    }
13440    this.renderInstitutionNames();
13441};
13442
13443CSL.NameOutput.prototype.renderInstitutionNames = function () {
13444    // Institutions are split to string list as
13445    // this.institutions[v]["long"] and this.institutions[v]["short"]
13446    for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
13447        var v = this.variables[i];
13448        for (var j = 0, jlen = this.institutions[v].length; j < jlen; j += 1) {
13449            var institution;
13450
13451            var name = this.institutions[v][j];
13452
13453            
13454
13455            // XXX Start here for institutions
13456            // Figure out the three segments: primary, secondary, tertiary
13457            var j, jlen, localesets;
13458            if (this.state.tmp.extension) {
13459                localesets = ["sort"];
13460            } else if (name.isInstitution || name.literal) {
13461                // Will never hit this in this function, but preserving
13462                // in case we factor this out.
13463                localesets = this.state.opt['cite-lang-prefs'].institutions;
13464            } else {
13465                localesets = this.state.opt['cite-lang-prefs'].persons;
13466            }
13467
13468            var slot = {primary:'locale-orig',secondary:false,tertiary:false};
13469	        if (localesets) {
13470		        var slotnames = ["primary", "secondary", "tertiary"];
13471		        for (var k = 0, klen = slotnames.length; k < klen; k += 1) {
13472			        if (localesets.length - 1 <  k) {
13473				        break;
13474			        }
13475                    if (localesets[k]) {
13476			            slot[slotnames[k]] = 'locale-' + localesets[k];
13477                    }
13478		        }
13479	        } else {
13480		        slot.primary = 'locale-translat';
13481	        }
13482	        if (this.state.tmp.area !== "bibliography"
13483		        && !(this.state.tmp.area === "citation"
13484			         && this.state.opt.xclass === "note"
13485			         && this.item && !this.item.position)) {
13486                
13487		        slot.secondary = false;
13488		        slot.tertiary = false;
13489	        }
13490            // Get normalized name object for a start.
13491            // true invokes fallback
13492            this.setRenderedName(name);
13493
13494            // XXXX FROM HERE (instututions)
13495            var institution = this._renderInstitutionName(v, name, slot, j);
13496
13497            //this.institutions[v][j] = this._join(institution, "");
13498            this.institutions[v][j] = institution;
13499        }
13500    }
13501};
13502
13503CSL.NameOutput.prototype._renderInstitutionName = function (v, name, slot, j) {
13504    var secondary, tertiary, long_style, short_style, institution, institution_short, institution_long;
13505    var res = this.getName(name, slot.primary, true);
13506    var primary = res.name;
13507    var usedOrig = res.usedOrig;
13508    if (primary) {
13509        //print("primary, v, j = "+primary+", "+v+", "+j);
13510        primary = this.fixupInstitution(primary, v, j);
13511    }
13512	secondary = false;
13513	if (slot.secondary) {
13514        res = this.getName(name, slot.secondary, false, usedOrig);
13515        var secondary = res.name;
13516        usedOrig = res.usedOrig;
13517        if (secondary) {
13518			secondary = this.fixupInstitution(secondary, v, j);
13519        }
13520	}
13521    //Zotero.debug("XXX [2] secondary: "+secondary["long"].literal+", slot.secondary: "+slot.secondary);
13522	tertiary = false;
13523	if (slot.tertiary) {
13524        res = this.getName(name, slot.tertiary, false, usedOrig);
13525        tertiary = res.name;
13526        if (tertiary) {
13527			tertiary = this.fixupInstitution(tertiary, v, j);
13528        }
13529	}
13530    var n = {
13531        l: {
13532            pri: false,
13533            sec: false,
13534            ter: false
13535        },
13536        s: {
13537            pri: false,
13538            sec: false,
13539            ter: false
13540        }
13541    };
13542    if (primary) {
13543        n.l.pri = primary["long"];
13544        n.s.pri = primary["short"].length ? primary["short"] : primary["long"];
13545    }
13546    if (secondary) {
13547        n.l.sec = secondary["long"];
13548        n.s.sec = secondary["short"].length ? secondary["short"] : secondary["long"];
13549    }
13550    if (tertiary) {
13551        n.l.ter = tertiary["long"];
13552        n.s.ter = tertiary["short"].length ? tertiary["short"] : tertiary["long"];
13553    }
13554    switch (this.institution.strings["institution-parts"]) {
13555    case "short":
13556        // No multilingual for pure short form institution names.
13557        if (primary["short"].length) {
13558            short_style = this._getShortStyle();
13559            institution = [this._composeOneInstitutionPart([n.s.pri, n.s.sec, n.s.ter], slot, short_style, v)];
13560        } else {
13561            // Fail over to long.
13562            long_style = this._getLongStyle(primary, v, j);
13563            institution = [this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v)];
13564        }
13565        break;
13566    case "short-long":
13567        long_style = this._getLongStyle(primary, v, j);
13568        short_style = this._getShortStyle();
13569        institution_short = this._renderOneInstitutionPart(primary["short"], short_style);
13570        // true is to include multilingual supplement
13571        institution_long = this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v);
13572        institution = [institution_short, institution_long];
13573        break;
13574    case "long-short":
13575        long_style = this._getLongStyle(primary, v, j);
13576        short_style = this._getShortStyle();
13577        institution_short = this._renderOneInstitutionPart(primary["short"], short_style);
13578        // true is to include multilingual supplement
13579        institution_long = this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v);
13580        institution = [institution_long, institution_short];
13581        break;
13582    default:
13583        long_style = this._getLongStyle(primary, v, j);
13584        // true is to include multilingual supplement
13585        institution = [this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v)];
13586        break;
13587    }
13588    var blob = this._join(institution, " ");
13589    if (blob) {
13590        blob.isInstitution = true;
13591    }
13592    this.state.tmp.name_node.children.push(blob);
13593    return blob;
13594};
13595
13596CSL.NameOutput.prototype._composeOneInstitutionPart = function (names, slot, style) {
13597    var primary = false, secondary = false, tertiary = false, primary_tok, secondary_tok, tertiary_tok;
13598    if (names[0]) {
13599        primary_tok = CSL.Util.cloneToken(style);
13600        if (this.state.opt.citeAffixes[slot.primary]){
13601            if ("<i>" === this.state.opt.citeAffixes.institutions[slot.primary].prefix) {
13602                var hasItalic = false;
13603                for (var i = 0, ilen = primary_tok.decorations.length; i < ilen; i += 1) {
13604                    if (style.decorations[i][0] === "@font-style"
13605                        && primary_tok.decorations[i][1] === "italic") {
13606                        hasItalic = true;
13607                    }
13608                }
13609                if (!hasItalic) {
13610                    primary_tok.decorations.push(["@font-style", "italic"]);
13611                }
13612            }
13613        }
13614        primary = this._renderOneInstitutionPart(names[0], primary_tok);
13615     }
13616    if (names[1]) {
13617        secondary = this._renderOneInstitutionPart(names[1], style);
13618    }
13619    if (names[2]) {
13620        tertiary = this._renderOneInstitutionPart(names[2], style);
13621    }
13622    // Compose
13623    var institutionblob;
13624    if (secondary || tertiary) {
13625        this.state.output.openLevel("empty");
13626
13627        this.state.output.append(primary);
13628
13629        secondary_tok = CSL.Util.cloneToken(style);
13630        if (slot.secondary) {
13631            secondary_tok.strings.prefix = this.state.opt.citeAffixes.institutions[slot.secondary].prefix;
13632            secondary_tok.strings.suffix = this.state.opt.citeAffixes.institutions[slot.secondary].suffix;
13633            // Add a space if empty
13634            if (!secondary_tok.strings.prefix) {
13635                secondary_tok.strings.prefix = " ";
13636            }
13637        }
13638        var secondary_outer = new CSL.Token();
13639        secondary_outer.decorations.push(["@font-style", "normal"]);
13640        secondary_outer.decorations.push(["@font-weight", "normal"]);
13641        this.state.output.openLevel(secondary_outer);
13642        this.state.output.append(secondary, secondary_tok);
13643        this.state.output.closeLevel();
13644
13645        tertiary_tok = CSL.Util.cloneToken(style);
13646        if (slot.tertiary) {
13647            tertiary_tok.strings.prefix = this.state.opt.citeAffixes.institutions[slot.tertiary].prefix;
13648            tertiary_tok.strings.suffix = this.state.opt.citeAffixes.institutions[slot.tertiary].suffix;
13649            // Add a space if empty
13650            if (!tertiary_tok.strings.prefix) {
13651                tertiary_tok.strings.prefix = " ";
13652            }
13653        }
13654        var tertiary_outer = new CSL.Token();
13655        tertiary_outer.decorations.push(["@font-style", "normal"]);
13656        tertiary_outer.decorations.push(["@font-weight", "normal"]);
13657        this.state.output.openLevel(tertiary_outer);
13658        this.state.output.append(tertiary, tertiary_tok);
13659        this.state.output.closeLevel();
13660
13661        this.state.output.closeLevel();
13662
13663        institutionblob = this.state.output.pop();
13664    } else {
13665        institutionblob = primary;
13666    }
13667    return institutionblob;
13668};
13669
13670CSL.NameOutput.prototype._renderOneInstitutionPart = function (blobs, style) {
13671    for (var i = 0, ilen = blobs.length; i < ilen; i += 1) {
13672        if (blobs[i]) {
13673            var str = blobs[i];
13674            // XXXXX Cut-and-paste code in multiple locations. This code block should be
13675            // collected in a function.
13676            // Tag: strip-periods-block
13677            if (this.state.tmp.strip_periods) {
13678                str = str.replace(/\./g, "");
13679            } else {
13680                for (var j = 0, jlen = style.decorations.length; j < jlen; j += 1) {
13681                    if ("@strip-periods" === style.decorations[j][0] && "true" === style.decorations[j][1]) {
13682                        str = str.replace(/\./g, "");
13683                        break;
13684                    }
13685                }
13686            }
13687            //this.state.output.append(blobs[i], style, true);
13688            this.state.tmp.group_context.tip.variable_success = true;
13689            this.state.tmp.can_substitute.replace(false, CSL.LITERAL);
13690            if (str === "!here>>>") {
13691                blobs[i] = false;
13692            } else {
13693                this.state.output.append(str, style, true);
13694                blobs[i] = this.state.output.pop();
13695            }
13696        }
13697    }
13698    if ("undefined" === typeof this.institution.strings["part-separator"]) {
13699        this.institution.strings["part-separator"] = this.state.tmp.name_delimiter;
13700    }
13701    return this._join(blobs, this.institution.strings["part-separator"]);
13702};
13703
13704CSL.NameOutput.prototype._renderNames = function (v, values, pos, j) {
13705    //
13706    var ret = false;
13707    if (values.length) {
13708        var names = [];
13709        for (var i = 0, ilen = values.length; i < ilen; i += 1) {
13710            var name = values[i];
13711            
13712            // XXX We'll start here with attempts.
13713            // Figure out the three segments: primary, secondary, tertiary
13714            var ret, localesets;
13715            
13716            if (this.state.tmp.extension) {
13717                localesets = ["sort"];
13718            } else if (name.isInstitution || name.literal) {
13719                // Will never hit this in this function, but preserving
13720                // in case we factor this out.
13721                localesets = this.state.opt['cite-lang-prefs'].institutions;
13722            } else {
13723                localesets = this.state.opt['cite-lang-prefs'].persons;
13724            }
13725            var slot = {primary:'locale-orig',secondary:false,tertiary:false};
13726	        if (localesets) {
13727		        var slotnames = ["primary", "secondary", "tertiary"];
13728		        for (var k = 0, klen = slotnames.length; k < klen; k += 1) {
13729			        if (localesets.length - 1 <  k) {
13730				        break;
13731			        }
13732			        slot[slotnames[k]] = 'locale-' + localesets[k];
13733		        }
13734	        } else {
13735		        slot.primary = 'locale-translat';
13736	        }
13737	        if (this.state.tmp.sort_key_flag || (this.state.tmp.area !== "bibliography"
13738		        && !(this.state.tmp.area === "citation"
13739			         && this.state.opt.xclass === "note"
13740			         && this.item && !this.item.position))) {
13741                
13742		        slot.secondary = false;
13743		        slot.tertiary = false;
13744	        }
13745
13746            // primary
13747            // true is for fallback
13748            this.setRenderedName(name);
13749
13750            if (!name.literal && !name.isInstitution) {
13751                var nameBlob = this._renderPersonalName(v, name, slot, pos, i, j);
13752                var nameToken = CSL.Util.cloneToken(this.name);
13753                this.state.output.append(nameBlob, nameToken, true);
13754                names.push(this.state.output.pop());
13755            } else {
13756                names.push(this._renderInstitutionName(v, name, slot, j));
13757            }
13758        }
13759        //ret = this._join(names, "");
13760        ret = this.joinPersons(names, pos, j);
13761    }
13762    return ret;
13763};
13764
13765
13766CSL.NameOutput.prototype._renderPersonalName = function (v, name, slot, pos, i, j) {
13767    // XXXX FROM HERE (persons)
13768
13769    var res = this.getName(name, slot.primary, true);
13770    var primary = this._renderOnePersonalName(res.name, pos, i, j);
13771	var secondary = false;
13772	if (slot.secondary) {
13773        res = this.getName(name, slot.secondary, false, res.usedOrig);
13774        if (res.name) {
13775			secondary = this._renderOnePersonalName(res.name, pos, i, j);
13776        }
13777	}
13778	var tertiary = false;
13779	if (slot.tertiary) {
13780        res = this.getName(name, slot.tertiary, false, res.usedOrig);
13781        if (res.name) {
13782			tertiary = this._renderOnePersonalName(res.name, pos, i, j);
13783        }
13784	}
13785    // Now compose them to a unit
13786    var personblob;
13787    if (secondary || tertiary) {
13788
13789        this.state.output.openLevel("empty");
13790
13791        this.state.output.append(primary);
13792
13793        var secondary_tok = new CSL.Token();
13794        if (slot.secondary) {
13795            secondary_tok.strings.prefix = this.state.opt.citeAffixes.persons[slot.secondary].prefix;
13796            secondary_tok.strings.suffix = this.state.opt.citeAffixes.persons[slot.secondary].suffix;
13797            // Add a space if empty
13798            if (!secondary_tok.strings.prefix) {
13799                secondary_tok.strings.prefix = " ";
13800            }
13801        }
13802        this.state.output.append(secondary, secondary_tok);
13803
13804        var tertiary_tok = new CSL.Token();
13805        if (slot.tertiary) {
13806            tertiary_tok.strings.prefix = this.state.opt.citeAffixes.persons[slot.tertiary].prefix;
13807            tertiary_tok.strings.suffix = this.state.opt.citeAffixes.persons[slot.tertiary].suffix;
13808            // Add a space if empty
13809            if (!tertiary_tok.strings.prefix) {
13810                tertiary_tok.strings.prefix = " ";
13811            }
13812        }
13813        this.state.output.append(tertiary, tertiary_tok);
13814
13815        this.state.output.closeLevel();
13816
13817        personblob = this.state.output.pop();
13818    } else {
13819        personblob = primary;
13820    }
13821    return personblob;
13822};
13823
13824CSL.NameOutput.prototype._isRomanesque = function (name) {
13825    // 0 = entirely non-romanesque
13826    // 1 = mixed content
13827    // 2 = pure romanesque
13828    var ret = 2;
13829    if (!name.family.replace(/\"/g, '').match(CSL.ROMANESQUE_REGEXP)) {
13830        ret = 0;
13831    }
13832    if (!ret && name.given && name.given.match(CSL.STARTSWITH_ROMANESQUE_REGEXP)) {
13833        ret = 1;
13834    }
13835    var top_locale;
13836    if (ret == 2) {
13837        if (name.multi && name.multi.main) {
13838            top_locale = name.multi.main.slice(0, 2);
13839        } else if (this.Item.language) {
13840            top_locale = this.Item.language.slice(0, 2);
13841        }
13842        if (["ja", "zh"].indexOf(top_locale) > -1) {
13843            ret = 1;
13844        }
13845    }
13846    //print("name: "+name.given+", multi: "+name.multi+", ret: "+ret);
13847    return ret;
13848};
13849
13850CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) {
13851    var name = value;
13852    var dropping_particle = this._droppingParticle(name, pos, j);
13853    var family = this._familyName(name);
13854    var non_dropping_particle = this._nonDroppingParticle(name);
13855    var givenInfo = this._givenName(name, pos, i);
13856    var given = givenInfo.blob;
13857    var suffix = this._nameSuffix(name);
13858    if (given === false) {
13859        dropping_particle = false;
13860        suffix = false;
13861    }
13862    var sort_sep = this.state.inheritOpt(this.name, "sort-separator");
13863    if (!sort_sep) {
13864        sort_sep = "";
13865    }
13866    var suffix_sep;
13867    if (name["comma-suffix"]) {
13868        suffix_sep = ", ";
13869    } else {
13870        suffix_sep = " ";
13871    }
13872    var romanesque = this._isRomanesque(name);
13873    function hasJoiningPunctuation(blob) {
13874        if (!blob) {
13875            return false;
13876        } else if ("string" === typeof blob.blobs) {
13877            if (["\u2019", "\'", "-", " "].indexOf(blob.blobs.slice(-1)) > -1) {
13878                return true;
13879            } else {
13880                return false;
13881            }
13882        } else {
13883            return hasJoiningPunctuation(blob.blobs[blob.blobs.length-1]);
13884        }
13885    }
13886    
13887    var has_hyphenated_non_dropping_particle = hasJoiningPunctuation(non_dropping_particle);
13888
13889    var nbspace;
13890    if (["fr", "ru", "cs"].indexOf(this.state.opt["default-locale"][0].slice(0, 2)) > -1) {
13891        nbspace = "\u00a0";
13892    } else {
13893        nbspace = " ";
13894    }
13895
13896    var blob, merged, first, second;
13897    if (romanesque === 0) {
13898        // XXX handle affixes for given and family
13899        blob = this._join([non_dropping_particle, family, given], "");
13900    } else if (romanesque === 1 || name["static-ordering"]) { // entry likes sort order
13901        merged = this._join([non_dropping_particle, family], nbspace);
13902        blob = this._join([merged, given], " ");
13903    } else if (name["reverse-ordering"]) { // entry likes reverse order
13904        merged = this._join([non_dropping_particle, family], nbspace);
13905        blob = this._join([given, merged], " ");
13906    } else if (this.state.tmp.sort_key_flag) {
13907        // ok with no affixes here
13908        if (this.state.opt["demote-non-dropping-particle"] === "never") {
13909            merged = this._join([non_dropping_particle, family], nbspace);
13910            merged = this._join([merged, dropping_particle], " ");
13911            merged = this._join([merged, given], this.state.opt.sort_sep);
13912            blob = this._join([merged, suffix], " ");
13913        } else {
13914            second = this._join([given, dropping_particle, non_dropping_particle], " ");
13915            merged = this._join([family, second], this.state.opt.sort_sep);
13916            blob = this._join([merged, suffix], " ");
13917        }
13918    } else if (this.state.inheritOpt(this.name, "name-as-sort-order") === "all" || (this.state.inheritOpt(this.name, "name-as-sort-order") === "first" && i === 0 && (j === 0 || "undefined" === typeof j))) {
13919        //
13920        // Discretionary sort ordering and inversions
13921        //
13922        if (["Lord", "Lady"].indexOf(name.given) > -1) {
13923            sort_sep = ", ";
13924        }
13925
13926        // XXX Needs a more robust solution than this
13927        // XXX See https://forums.zotero.org/discussion/30974/any-idea-why-an-a-author-comes-last-in-the-bibliography/#Item_30
13928
13929        //if (["always", "display-and-sort"].indexOf(this.state.opt["demote-non-dropping-particle"]) > -1 && !has_hyphenated_non_dropping_particle) {
13930        if (["always", "display-and-sort"].indexOf(this.state.opt["demote-non-dropping-particle"]) > -1) {
13931            // Drop non-dropping particle
13932            //second = this._join([given, dropping_particle, non_dropping_particle], " ");
13933            second = this._join([given, dropping_particle], (name["comma-dropping-particle"] + " "));
13934        
13935            // This would be a problem with al-Ghazali. Avoided by has_hyphenated_non_dropping_particle check above.
13936            second = this._join([second, non_dropping_particle], " ");
13937            if (second && this.given) {
13938                second.strings.prefix = this.given.strings.prefix;
13939                second.strings.suffix = this.given.strings.suffix;
13940            }
13941            if (family && this.family) {
13942                family.strings.prefix = this.family.strings.prefix;
13943                family.strings.suffix = this.family.strings.suffix;
13944            }
13945            merged = this._join([family, second], sort_sep);
13946            blob = this._join([merged, suffix], sort_sep);
13947        } else {
13948            // Don't drop particle.
13949            // Don't do this
13950            //if (this.state.tmp.area === "bibliography" && !this.state.tmp.term_predecessor && non_dropping_particle) {
13951            //    if (!has_hyphenated_non_dropping_particle) {
13952            //        non_dropping_particle.blobs = CSL.Output.Formatters["capitalize-first"](this.state, non_dropping_particle.blobs)
13953            //    }
13954            //}
13955            if (has_hyphenated_non_dropping_particle) {
13956                first = this._join([non_dropping_particle, family], "");
13957            } else {
13958                first = this._join([non_dropping_particle, family], nbspace);
13959            }
13960            if (first && this.family) {
13961                first.strings.prefix = this.family.strings.prefix;
13962                first.strings.suffix = this.family.strings.suffix;
13963            }
13964
13965            second = this._join([given, dropping_particle], (name["comma-dropping-particle"] + " "));
13966            //second = this._join([given, dropping_particle], " ");
13967            if (second && this.given) {
13968                second.strings.prefix = this.given.strings.prefix;
13969                second.strings.suffix = this.given.strings.suffix;
13970            }
13971
13972            merged = this._join([first, second], sort_sep);
13973            blob = this._join([merged, suffix], sort_sep);
13974        }
13975        blob.isInverted = true;
13976    } else { // plain vanilla
13977        if (name["dropping-particle"] && name.family && !name["non-dropping-particle"]) {
13978            var dp = name["dropping-particle"];
13979            var apostrophes = ["'","\u02bc","\u2019","-"];
13980            if (apostrophes.indexOf(dp.slice(-1)) > -1 && dp.slice(0, -1) !== "de") {
13981                family = this._join([dropping_particle, family], "");
13982                dropping_particle = false;
13983            }
13984        }
13985
13986        if (has_hyphenated_non_dropping_particle) {
13987            second = this._join([non_dropping_particle, family], "");
13988            second = this._join([dropping_particle, second], nbspace);
13989        } else {
13990            second = this._join([dropping_particle, non_dropping_particle, family], nbspace);
13991        }
13992        second = this._join([second, suffix], suffix_sep);
13993        if (second && this.family) {
13994            second.strings.prefix = this.family.strings.prefix;
13995            second.strings.suffix = this.family.strings.suffix;
13996        }
13997        if (given && this.given) {
13998            given.strings.prefix = this.given.strings.prefix;
13999            given.strings.suffix = this.given.strings.suffix;
14000        }
14001        if (second.strings.prefix) {
14002            name["comma-dropping-particle"] = "";
14003        }
14004
14005        var space;
14006        if (this.state.inheritOpt(this.name, "initialize-with")
14007            && this.state.inheritOpt(this.name, "initialize-with").match(/[\u00a0\ufeff]/)
14008            && givenInfo.initializationLevel === 1) {
14009            
14010            space = nbspace;
14011        } else {
14012            space = " ";
14013        }
14014        blob = this._join([given, second], (name["comma-dropping-particle"] + space));
14015    }
14016    // XXX Just generally assume for the present that personal names render something
14017    this.state.tmp.group_context.tip.variable_success = true;
14018    this.state.tmp.can_substitute.replace(false, CSL.LITERAL);
14019    this.state.tmp.term_predecessor = true;
14020    // notSerious
14021    //this.state.output.append(blob, "literal", true);
14022    //var ret = this.state.output.pop();
14023    this.state.tmp.name_node.children.push(blob);
14024    return blob;
14025};
14026
14027/*
14028        // Do not include given name, dropping particle or suffix in strict short form of name
14029
14030        // initialize if appropriate
14031*/
14032
14033// Input names should be touched by _normalizeNameInput()
14034// exactly once: this is not idempotent.
14035CSL.NameOutput.prototype._normalizeNameInput = function (value) {
14036    var name = {
14037        literal:value.literal,
14038        family:value.family,
14039        isInstitution:value.isInstitution,
14040        given:value.given,
14041        suffix:value.suffix,
14042        "comma-suffix":value["comma-suffix"],
14043        "non-dropping-particle":value["non-dropping-particle"],
14044        "dropping-particle":value["dropping-particle"],
14045        "static-ordering":value["static-ordering"],
14046        "static-particles":value["static-particles"],
14047        "reverse-ordering":value["reverse-ordering"],
14048        "full-form-always": value["full-form-always"],
14049        "parse-names":value["parse-names"],
14050        "comma-dropping-particle": "",
14051        block_initialize:value.block_initialize,
14052        multi:value.multi
14053    };
14054    this._parseName(name);
14055    return name;
14056};
14057
14058// _transformNameset() replaced with enhanced transform.name().
14059
14060CSL.NameOutput.prototype._stripPeriods = function (tokname, str) {
14061    var decor_tok = this[tokname + "_decor"];
14062    if (str) {
14063        if (this.state.tmp.strip_periods) {
14064            str = str.replace(/\./g, "");
14065        } else  if (decor_tok) {
14066            for (var i = 0, ilen = decor_tok.decorations.length; i < ilen; i += 1) {
14067                if ("@strip-periods" === decor_tok.decorations[i][0] && "true" === decor_tok.decorations[i][1]) {
14068                    str = str.replace(/\./g, "");
14069                    break;
14070                }
14071            }
14072        }
14073    }
14074    return str;
14075};
14076
14077CSL.NameOutput.prototype._nonDroppingParticle = function (name) {
14078    var ndp = name["non-dropping-particle"];
14079    if (ndp && this.state.tmp.sort_key_flag) {
14080        ndp = ndp.replace(/[\'\u2019]/, "");
14081    }
14082    var str = this._stripPeriods("family", ndp);
14083    if (this.state.output.append(str, this.family_decor, true)) {
14084        return this.state.output.pop();
14085    }
14086    return false;
14087};
14088
14089CSL.NameOutput.prototype._droppingParticle = function (name, pos, j) {
14090    var dp = name["dropping-particle"];
14091    if (dp && this.state.tmp.sort_key_flag) {
14092        dp = dp.replace(/[\'\u2019]/, "");
14093    }
14094    var str = this._stripPeriods("given", dp);
14095    if (name["dropping-particle"] && name["dropping-particle"].match(/^et.?al[^a-z]$/)) {
14096        if (this.state.inheritOpt(this.name, "et-al-use-last")) {
14097            if ("undefined" === typeof j) { 
14098                this.etal_spec[pos].freeters = 2;
14099            } else {
14100                this.etal_spec[pos].persons = 2;
14101            }
14102        } else {
14103            if ("undefined" === typeof j) { 
14104                this.etal_spec[pos].freeters = 1;
14105            } else {
14106                this.etal_spec[pos].persons = 1;
14107            }
14108        }
14109        name["comma-dropping-particle"] = "";
14110    } else if (this.state.output.append(str, this.given_decor, true)) {
14111        return this.state.output.pop();
14112    }
14113    return false;
14114};
14115
14116CSL.NameOutput.prototype._familyName = function (name) {
14117    var str = this._stripPeriods("family", name.family);
14118    if (this.state.output.append(str, this.family_decor, true)) {
14119        return this.state.output.pop();
14120    }
14121    return false;
14122};
14123
14124CSL.NameOutput.prototype._givenName = function (name, pos, i) {
14125    var ret;
14126    // citation
14127    //   use disambig as-is
14128    // biblography
14129    //   use disambig only if it boosts over the default
14130    //   SO WHAT IS THE DEFAULT?
14131    //   A: If "form" is short, it's 0.
14132    //      If "form" is long, initialize-with exists (and initialize is not false) it's 1
14133    //      If "form" is long, and initialize_with does not exist, it's 2.
14134    var formIsShort = this.state.inheritOpt(this.name, "form", "name-form", "long") !== "long";
14135    var initializeIsTurnedOn = this.state.inheritOpt(this.name, "initialize") !== false;
14136    var hasInitializeWith = "string" === typeof this.state.inheritOpt(this.name, "initialize-with") && !name.block_initialize;
14137    var defaultLevel;
14138    var useLevel;
14139    if (name["full-form-always"]) {
14140        useLevel = 2;
14141    } else {
14142        if (formIsShort) {
14143            defaultLevel = 0;
14144        } else if (hasInitializeWith) {
14145            defaultLevel = 1;
14146        } else {
14147            defaultLevel = 2;
14148        }
14149        var requestedLevel = this.state.tmp.disambig_settings.givens[pos][i];
14150        if (requestedLevel > defaultLevel) {
14151            useLevel = requestedLevel;
14152        } else {
14153            useLevel = defaultLevel;
14154        }
14155    }
14156    var gdropt = this.state.citation.opt["givenname-disambiguation-rule"];
14157   if (gdropt && gdropt.slice(-14) === "-with-initials") {
14158        hasInitializeWith = true;
14159    }
14160    if (name.family && useLevel === 1) {
14161        if (hasInitializeWith) {
14162            var initialize_with = this.state.inheritOpt(this.name, "initialize-with", false, "");
14163            name.given = CSL.Util.Names.initializeWith(this.state, name.given, initialize_with, !initializeIsTurnedOn);
14164        } else {
14165            name.given = CSL.Util.Names.unInitialize(this.state, name.given);
14166        }
14167    } else if (useLevel === 0) {
14168        return {
14169            blob: false
14170        }
14171    } else if (useLevel === 2) {
14172        name.given = CSL.Util.Names.unInitialize(this.state, name.given);
14173    }
14174
14175    var str = this._stripPeriods("given", name.given);
14176    var rendered = this.state.output.append(str, this.given_decor, true);
14177    if (rendered) {
14178        ret = this.state.output.pop();
14179	    return {
14180            blob: ret,
14181            initializationLevel: useLevel
14182        };
14183    }
14184    return {
14185        blob: false
14186    };
14187};
14188
14189CSL.NameOutput.prototype._nameSuffix = function (name) {
14190
14191    var str = name.suffix, ret;
14192
14193    if (str && "string" === typeof this.state.inheritOpt(this.name, "initialize-with")) {
14194        str = CSL.Util.Names.initializeWith(this.state, str, this.state.inheritOpt(this.name, "initialize-with"), true);
14195    }
14196
14197    str = this._stripPeriods("family", str);
14198    var toSuffix = '';
14199    if (str && str.slice(-1) === '.') {
14200	str = str.slice(0, -1);
14201	toSuffix = '.';
14202    }
14203    var rendered = this.state.output.append(str, "empty", true);
14204    if (rendered) {
14205        ret = this.state.output.pop();
14206	ret.strings.suffix = toSuffix + ret.strings.suffix;
14207	return ret;
14208    }
14209    return false;
14210};
14211
14212CSL.NameOutput.prototype._getLongStyle = function (name) {
14213    var long_style;
14214    if (name["short"].length) {
14215        if (this.institutionpart["long-with-short"]) {
14216            long_style = this.institutionpart["long-with-short"];
14217        } else {
14218            long_style = this.institutionpart["long"];
14219        }
14220    } else {
14221        long_style = this.institutionpart["long"];
14222    }
14223    if (!long_style) {
14224        long_style = new CSL.Token();
14225    }
14226    return long_style;
14227};
14228
14229CSL.NameOutput.prototype._getShortStyle = function () {
14230    var short_style;
14231    if (this.institutionpart["short"]) {
14232        short_style = this.institutionpart["short"];
14233    } else {
14234        short_style = new CSL.Token();
14235    }
14236    return short_style;
14237};
14238
14239CSL.NameOutput.prototype._parseName = function (name) {
14240    if (!name["parse-names"] && "undefined" !== typeof name["parse-names"]) {
14241        return name;
14242    }
14243    if (name.family && !name.given && name.isInstitution) {
14244        name.literal = name.family;
14245        name.family = undefined;
14246        name.isInstitution = undefined;
14247    }
14248    var noparse;
14249    if (name.family 
14250        && (name.family.slice(0, 1) === '"' && name.family.slice(-1) === '"')
14251        || (!name["parse-names"] && "undefined" !== typeof name["parse-names"])) {
14252
14253        name.family = name.family.slice(1, -1);
14254        noparse = true;
14255        name["parse-names"] = 0;
14256    } else {
14257        noparse = false;
14258    }
14259    if (this.state.opt.development_extensions.parse_names) {
14260        if (!name["non-dropping-particle"] && name.family && !noparse && name.given) {
14261            if (!name["static-particles"]) {
14262                CSL.parseParticles(name, true);
14263            }
14264        }
14265    }
14266};
14267
14268/*
14269 * Return a single name object
14270  */
14271
14272// The interface is a mess, but this should serve.
14273
14274CSL.NameOutput.prototype.getName = function (name, slotLocaleset, fallback, stopOrig) {
14275
14276    // Needs to tell us whether we used orig or not.
14277    
14278    if (stopOrig && slotLocaleset === 'locale-orig') {
14279        return {name:false,usedOrig:stopOrig};
14280    }
14281
14282    // Normalize to string
14283    if (!name.family) {
14284        name.family = "";
14285    }
14286    if (!name.given) {
14287        name.given = "";
14288    }
14289
14290    // Recognized params are:
14291    //  block-initialize
14292    //  transliterated
14293    //  static-ordering
14294    //  full-form-always
14295    // All default to false, except for static-ordering, which is initialized
14296    // with a sniff.
14297    var name_params = {};
14298    // Determines the default static-order setting based on the characters
14299    // used in the headline field. Will be overridden by locale-based
14300    // parameters evaluated against explicit lang tags set on the (sub)field.
14301    name_params["static-ordering"] = this.getStaticOrder(name);
14302
14303    var foundTag = true;
14304    var langTag;
14305    if (slotLocaleset !== 'locale-orig') {
14306        foundTag = false;
14307        if (name.multi) {
14308            var langTags = this.state.opt[slotLocaleset];
14309            for (var i = 0, ilen = langTags.length; i < ilen; i += 1) {
14310                langTag = langTags[i];
14311                if (name.multi._key[langTag]) {
14312                    foundTag = true;
14313                    var isInstitution = name.isInstitution;
14314                    name = name.multi._key[langTag];
14315                    name.isInstitution = isInstitution;
14316                    // Set name formatting params
14317                    name_params = this.getNameParams(langTag);
14318                    name_params.transliterated = true;
14319                    break;
14320                }
14321            }
14322        }
14323    }
14324
14325    if (!foundTag) {
14326        langTag = false;
14327        if (name.multi && name.multi.main) {
14328            langTag = name.multi.main;
14329        } else if (this.Item.language) {
14330            langTag = this.Item.language;
14331        }
14332        if (langTag) {
14333            name_params = this.getNameParams(langTag);
14334        }
14335    }
14336
14337    if (!fallback && !foundTag) {
14338        return {name:false,usedOrig:stopOrig};
14339    }
14340    
14341    // Normalize to string (again)
14342    if (!name.family) {
14343        name.family = "";
14344    }
14345    if (!name.given) {
14346        name.given = "";
14347    }
14348    if (name.literal) {
14349        delete name.family;
14350        delete name.given;
14351    }
14352    // var clone the item before writing into it
14353    name = {
14354        family:name.family,
14355        given:name.given,
14356        "non-dropping-particle":name["non-dropping-particle"],
14357        "dropping-particle":name["dropping-particle"],
14358        suffix:name.suffix,
14359        "static-ordering":name_params["static-ordering"],
14360        "static-particles":name["static-particles"],
14361        "reverse-ordering":name_params["reverse-ordering"],
14362        "full-form-always": name_params["full-form-always"],
14363        "parse-names":name["parse-names"],
14364        "comma-suffix":name["comma-suffix"],
14365        "comma-dropping-particle":name["comma-dropping-particle"],
14366        transliterated: name_params.transliterated,
14367        block_initialize: name_params["block-initialize"],
14368        literal:name.literal,
14369        isInstitution:name.isInstitution,
14370        multi:name.multi
14371    };
14372    
14373    if (!name.literal && (!name.given && name.family && name.isInstitution)) {
14374        name.literal = name.family;
14375    }
14376    if (name.literal) {
14377        delete name.family;
14378        delete name.given;
14379    }
14380    name = this._normalizeNameInput(name);
14381    var usedOrig;
14382    if (stopOrig) {
14383        usedOrig = stopOrig;
14384    } else {
14385        usedOrig = !foundTag;
14386    }
14387    return {name:name,usedOrig:usedOrig};
14388};
14389
14390CSL.NameOutput.prototype.getNameParams = function (langTag) {
14391    var ret = {};
14392    var langspec = CSL.localeResolve(this.Item.language, this.state.opt["default-locale"][0]);
14393    var try_locale = this.state.locale[langspec.best] ? langspec.best : this.state.opt["default-locale"][0];
14394    var name_as_sort_order = this.state.locale[try_locale].opts["name-as-sort-order"];
14395    var name_as_reverse_order = this.state.locale[try_locale].opts["name-as-reverse-order"];
14396    var name_never_short = this.state.locale[try_locale].opts["name-never-short"];
14397    var field_lang_bare = langTag.split("-")[0];
14398    if (name_as_sort_order && name_as_sort_order[field_lang_bare]) {
14399        ret["static-ordering"] = true;
14400        ret["reverse-ordering"] = false;
14401    }
14402    if (name_as_reverse_order && name_as_reverse_order[field_lang_bare]) {
14403        ret["reverse-ordering"] = true;
14404        ret["static-ordering"] = false;
14405    }
14406    if (name_never_short && name_never_short[field_lang_bare]) {
14407        ret["full-form-always"] = true;
14408    }
14409    
14410    if (ret["static-ordering"]) {
14411        ret["block-initialize"] = true;
14412    }
14413    return ret;
14414};
14415
14416CSL.NameOutput.prototype.setRenderedName = function (name) {
14417    if (this.state.tmp.area === "bibliography") {
14418        var strname = "";
14419        for (var j=0,jlen=CSL.NAME_PARTS.length;j<jlen;j+=1) {
14420            if (name[CSL.NAME_PARTS[j]]) {
14421                strname += name[CSL.NAME_PARTS[j]];
14422            }
14423        }
14424        this.state.tmp.rendered_name.push(strname);
14425    }
14426};
14427
14428CSL.NameOutput.prototype.fixupInstitution = function (name, varname, listpos) {
14429    if (!name.literal && name.family) {
14430        name.literal = name.family;
14431        delete name.family;
14432    }
14433    var longNameStr = name.literal;
14434    var shortNameStr = longNameStr;
14435    var ret = {
14436        "long": longNameStr.split(/\s*\|\s*/),
14437        "short": shortNameStr.split(/\s*\|\s*/),
14438    };
14439    if (this.state.sys.getAbbreviation) {
14440        // Normalize longNameStr and shortNameStr
14441        if (this.institution.strings.form === "short") {
14442            let jurisdiction = this.Item.jurisdiction;
14443            jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-entire", longNameStr, this.Item.language);
14444            if (this.state.transform.abbrevs[jurisdiction]["institution-entire"][longNameStr]) {
14445                longNameStr = this.state.transform.abbrevs[jurisdiction]["institution-entire"][longNameStr];
14446            } else {
14447                jurisdiction = this.Item.jurisdiction;
14448                jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", longNameStr, this.Item.language);
14449                if (this.state.transform.abbrevs[jurisdiction]["institution-part"][longNameStr]) {
14450                    longNameStr = this.state.transform.abbrevs[jurisdiction]["institution-part"][longNameStr];
14451                }
14452            }
14453            longNameStr = this._quashChecks(jurisdiction, longNameStr);
14454        }
14455        if (["short", "short-long", "long-short"].indexOf(this.institution.strings["institution-parts"]) > -1) {
14456            let jurisdiction = this.Item.jurisdiction;
14457            jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", shortNameStr, this.Item.language);
14458            if (this.state.transform.abbrevs[jurisdiction]["institution-part"][shortNameStr]) {
14459                shortNameStr = this.state.transform.abbrevs[jurisdiction]["institution-part"][shortNameStr];
14460            }
14461            shortNameStr = this._quashChecks(jurisdiction, shortNameStr);
14462            if (["short-long", "long-short"].indexOf(this.institution.strings["institution-parts"]) > -1) {
14463                if (shortNameStr === longNameStr) {
14464                    shortNameStr = "";
14465                }
14466            }
14467        }
14468        // Split abbreviated strings
14469        // For pure long, split and we're done.
14470        ret["long"] = longNameStr.split(/\s*\|\s*/);
14471        // For short, split and then try abbrev with institution-part on each element
14472        ret["short"] = shortNameStr.split(/\s*\|\s*/);
14473        if (["short", "short-long", "long-short"].indexOf(this.institution.strings["institution-parts"]) > -1) {
14474            for (var j=ret["short"].length-1; j>-1; j--) {
14475                let jurisdiction = this.Item.jurisdiction;
14476                var abbrevKey = ret["short"][j];
14477                jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", abbrevKey, this.Item.language);
14478                if (this.state.transform.abbrevs[jurisdiction]["institution-part"][abbrevKey]) {
14479                    ret["short"][j] = this.state.transform.abbrevs[jurisdiction]["institution-part"][abbrevKey];
14480                }
14481                if (ret["short"][j].indexOf("|") > -1) {
14482                    let retShort = ret["short"];
14483                    let splitShort = retShort[j].split(/\s*\|\s*/);
14484                    ret["short"] = retShort.slice(0, j).concat(splitShort).concat(retShort.slice(j+1));
14485                }
14486            }
14487        }
14488        if (this.state.opt.development_extensions.legacy_institution_name_ordering) {
14489            ret["short"].reverse();
14490        }
14491        ret["short"] = this._trimInstitution(ret["short"]);
14492        if (this.institution.strings["reverse-order"]) {
14493            ret["short"].reverse();
14494        }
14495        // trimmer is not available in getAmbiguousCite
14496        if (!this.state.tmp.just_looking) {
14497            if (this.Item.jurisdiction) {
14498                let jurisdiction = this.Item.jurisdiction;
14499                var trimmer = this.state.tmp.abbrev_trimmer;
14500                if (trimmer && trimmer[jurisdiction] && trimmer[jurisdiction][varname]) {
14501                    for (var i=0,ilen=ret["short"].length;i<ilen;i++) {
14502                        var frag = ret["short"][i];
14503                        ret["short"][i] = frag.replace(trimmer[jurisdiction][varname], "").trim();
14504                    }
14505                }
14506            }
14507        }
14508    }
14509    if (this.state.opt.development_extensions.legacy_institution_name_ordering) {
14510        ret["long"].reverse();
14511    }
14512    ret["long"] = this._trimInstitution(ret["long"]);
14513    if (this.institution.strings["reverse-order"]) {
14514        ret["long"].reverse();
14515    }
14516
14517    return ret;
14518};
14519
14520
14521CSL.NameOutput.prototype.getStaticOrder = function (name, refresh) {
14522    var static_ordering_val = false;
14523    if (!refresh && name["static-ordering"]) {
14524        static_ordering_val = true;
14525    } else if (this._isRomanesque(name) === 0) {
14526        static_ordering_val = true;
14527    } else if ((!name.multi || !name.multi.main) && this.Item.language && ['vi', 'hu'].indexOf(this.Item.language) > -1) {
14528        static_ordering_val = true;
14529    } else if (name.multi && name.multi.main && ['vi', 'hu'].indexOf(name.multi.main.slice(0,2)) > -1) {
14530        static_ordering_val = true;
14531    } else {
14532        if (this.state.opt['auto-vietnamese-names']
14533            && (CSL.VIETNAMESE_NAMES.exec(name.family + " " + name.given)
14534                && CSL.VIETNAMESE_SPECIALS.exec(name.family + name.given))) {
14535            
14536            static_ordering_val = true;
14537        }
14538    }
14539    return static_ordering_val;
14540};
14541
14542CSL.NameOutput.prototype._quashChecks = function (jurisdiction, str) {
14543    var str = this.state.transform.quashCheck(jurisdiction, str);
14544    // If the abbreviation has date cut-offs, find the most recent
14545    // abbreviation within scope.
14546    var lst = str.split(/>>[0-9]{4}>>/);
14547    var m = str.match(/>>([0-9]{4})>>/);
14548    str = lst.pop();
14549    var date = this.Item["original-date"] ? this.Item["original-date"] : this.Item["issued"];
14550    if (date) {
14551        date = parseInt(date.year, 10);
14552        date = isNaN(date) ? false : date;
14553    }
14554    if (date) {
14555        if (lst.length > 0) {
14556            for (var k=m.length-1; k>0; k--) {
14557                if (date >= parseInt(m[k], 10)) {
14558                    break;
14559                }
14560                str = lst.pop();
14561            }
14562        }
14563        str = str.replace(/\s*\|\s*/g, "|");
14564    }
14565    return str;
14566}
14567
14568CSL.NameOutput.prototype._trimInstitution = function (subunits) {
14569	// Oh! Good catch in the tests. This happens before abbrevs.
14570    // Won't work that way. Need to do abbrev substitute first,
14571    // and apply this separately to long and to short.
14572    var use_first = false;
14573    var stop_last = false;
14574    var use_last = false;
14575    var stop_first = false;
14576    var s = subunits.slice();
14577    if (this.institution) {
14578        // If use_first, apply stop_last, then apply use_first;
14579        // If use_last, apply stop_first, then apply use_last;
14580        if ("undefined" !== typeof this.institution.strings["use-first"]) {
14581            // this.state.sys.print("use-first OK");
14582            use_first = this.institution.strings["use-first"];
14583        }
14584        if ("undefined" !== typeof this.institution.strings["use-last"]) {
14585            // this.state.sys.print("use-last OK");
14586            use_last = this.institution.strings["use-last"];
14587        }
14588        if ("undefined" !== typeof this.institution.strings["stop-first"]) {
14589            // this.state.sys.print("stop-first OK");
14590            stop_first = this.institution.strings["stop-first"];
14591        }
14592        if ("undefined" !== typeof this.institution.strings["stop-last"]) {
14593            stop_last = this.institution.strings["stop-last"];
14594        }
14595
14596        if (use_first) {
14597            if (stop_last) {
14598                s = s.slice(0, stop_last * -1);
14599            }
14600            s = s.slice(0, use_first);
14601        }
14602        
14603        if (use_last) {
14604            var ss = subunits.slice();
14605            if (use_first) {
14606                stop_first = use_first;
14607            } else {
14608                s = [];
14609            }
14610            if (stop_first) {
14611                ss = ss.slice(stop_first);
14612            }
14613            ss = ss.slice(use_last * -1);
14614            s = s.concat(ss);
14615        }
14616        subunits = s;
14617    }
14618    return subunits;
14619};
14620
14621/*global CSL: true */
14622
14623CSL.PublisherOutput = function (state, group_tok) {
14624    this.state = state;
14625    this.group_tok = group_tok;
14626    this.varlist = [];
14627};
14628
14629CSL.PublisherOutput.prototype.render = function () {
14630    this.clearVars();
14631    this.composeAndBlob();
14632    this.composeElements();
14633    this.composePublishers();
14634    this.joinPublishers();
14635};
14636
14637
14638// XXX Figure out how to adapt this to the House of Lords / House of Commons
14639// joint committee case
14640
14641// name_delimiter
14642// delimiter_precedes_last
14643// and
14644
14645CSL.PublisherOutput.prototype.composeAndBlob = function () {
14646    this.and_blob = {};
14647    var and_term = false;
14648    if (this.group_tok.strings.and === "text") {
14649        and_term = this.state.getTerm("and");
14650    } else if (this.group_tok.strings.and === "symbol") {
14651        and_term = "&";
14652    }
14653    var tok = new CSL.Token();
14654    tok.strings.suffix = " ";
14655    tok.strings.prefix = " ";
14656    this.state.output.append(and_term, tok, true);
14657    var no_delim = this.state.output.pop();
14658
14659    tok.strings.prefix = this.group_tok.strings["subgroup-delimiter"];
14660    this.state.output.append(and_term, tok, true);
14661    var with_delim = this.state.output.pop();
14662    
14663    this.and_blob.single = false;
14664    this.and_blob.multiple = false;
14665    if (and_term) {
14666        if (this.group_tok.strings["subgroup-delimiter-precedes-last"] === "always") {
14667            this.and_blob.single = with_delim;
14668        } else if (this.group_tok.strings["subgroup-delimiter-precedes-last"] === "never") {
14669            this.and_blob.single = no_delim;
14670            this.and_blob.multiple = no_delim;
14671        } else {
14672            this.and_blob.single = no_delim;
14673            this.and_blob.multiple = with_delim;
14674        }
14675    }
14676};
14677
14678
14679CSL.PublisherOutput.prototype.composeElements = function () {
14680    for (var i = 0, ilen = 2; i < ilen; i += 1) {
14681        var varname = ["publisher", "publisher-place"][i];
14682        for (var j = 0, jlen = this["publisher-list"].length; j < jlen; j += 1) {
14683            var str = this[varname + "-list"][j];
14684            var tok = this[varname + "-token"];
14685            // notSerious
14686            this.state.output.append(str, tok, true);
14687            this[varname + "-list"][j] = this.state.output.pop();
14688        }
14689    }
14690};
14691
14692
14693CSL.PublisherOutput.prototype.composePublishers = function () {
14694    var blobs;
14695    for (var i = 0, ilen = this["publisher-list"].length; i < ilen; i += 1) {
14696        blobs = [this[this.varlist[0] + "-list"][i], this[this.varlist[1] + "-list"][i]];
14697        this["publisher-list"][i] = this._join(blobs, this.group_tok.strings.delimiter);
14698    }
14699};
14700
14701
14702CSL.PublisherOutput.prototype.joinPublishers = function () {
14703    var blobs = this["publisher-list"];
14704    var publishers = this._join(blobs, this.group_tok.strings["subgroup-delimiter"], this.and_blob.single, this.and_blob.multiple, this.group_tok);
14705    this.state.output.append(publishers, "literal");
14706};
14707
14708
14709// blobs, delimiter, single, multiple, tokenname
14710// Tokenname is a key at top level of this object.
14711CSL.PublisherOutput.prototype._join = CSL.NameOutput.prototype._join;
14712CSL.PublisherOutput.prototype._getToken = CSL.NameOutput.prototype._getToken;
14713
14714
14715CSL.PublisherOutput.prototype.clearVars = function () {
14716    this.state.tmp["publisher-list"] = false;
14717    this.state.tmp["publisher-place-list"] = false;
14718    this.state.tmp["publisher-group-token"] = false;
14719    this.state.tmp["publisher-token"] = false;
14720    this.state.tmp["publisher-place-token"] = false;
14721};
14722
14723/*global CSL: true */
14724
14725CSL.evaluateLabel = function (node, state, Item, item) {
14726    var myterm;
14727    if ("locator" === node.strings.term) {
14728        if (item && item.label) {
14729            if (item.label === "sub verbo") {
14730                myterm = "sub-verbo";
14731            } else {
14732                myterm = item.label;
14733            }
14734        }
14735        if (!myterm) {
14736            myterm = "page";
14737        }
14738    } else {
14739        myterm = node.strings.term;
14740    }
14741    
14742    // Plurals detection.
14743    var plural = node.strings.plural;
14744    if ("number" !== typeof plural) {
14745        // (node, ItemObject, variable, type)
14746        var theItem = (item && node.strings.term === "locator") ? item : Item;
14747        if (theItem[node.strings.term]) {
14748            state.processNumber(false, theItem, node.strings.term, Item.type);
14749            plural = state.tmp.shadow_numbers[node.strings.term].plural;
14750            if (!state.tmp.shadow_numbers[node.strings.term].labelForm
14751                && !state.tmp.shadow_numbers[node.strings.term].labelDecorations) {
14752                if (node.strings.form) {
14753                    state.tmp.shadow_numbers[node.strings.term].labelForm = node.strings.form;
14754                } else if (state.tmp.group_context.tip.label_form) {
14755                    state.tmp.shadow_numbers[node.strings.term].labelForm = state.tmp.group_context.tip.label_form;
14756                }
14757                state.tmp.shadow_numbers[node.strings.term].labelCapitalizeIfFirst = node.strings.capitalize_if_first;
14758                state.tmp.shadow_numbers[node.strings.term].labelDecorations = node.decorations.slice();
14759            }
14760            
14761            if (["locator", "number", "page"].indexOf(node.strings.term) > -1 && state.tmp.shadow_numbers[node.strings.term].label) {
14762                myterm = state.tmp.shadow_numbers[node.strings.term].label;
14763            }
14764            if (node.decorations && state.opt.development_extensions.csl_reverse_lookup_support) {
14765                node.decorations.reverse();
14766                node.decorations.push(["@showid","true", node.cslid]);
14767                node.decorations.reverse();
14768            }
14769        }
14770    }
14771    return CSL.castLabel(state, node, myterm, plural, CSL.TOLERANT);
14772};
14773
14774CSL.castLabel = function (state, node, term, plural, mode) {
14775    var label_form = node.strings.form;
14776    var label_capitalize_if_first = node.strings.capitalize_if_first;
14777    if (state.tmp.group_context.tip.label_form) {
14778        if (label_form === "static") {
14779            state.tmp.group_context.tip.label_static = true;
14780        } else {
14781            label_form = state.tmp.group_context.tip.label_form;
14782        }
14783    }
14784
14785    if (state.tmp.group_context.tip.label_capitalize_if_first) {
14786        label_capitalize_if_first = state.tmp.group_context.tip.label_capitalize_if_first;
14787    }
14788    var ret = state.getTerm(term, label_form, plural, false, mode, node.default_locale);
14789    if (label_capitalize_if_first) {
14790        ret = CSL.Output.Formatters["capitalize-first"](state, ret);
14791    }
14792    // XXXXX Cut-and-paste code in multiple locations. This code block should be
14793    // collected in a function.
14794    // Tag: strip-periods-block
14795    if (state.tmp.strip_periods) {
14796        ret = ret.replace(/\./g, "");
14797    } else {
14798        for (var i = 0, ilen = node.decorations.length; i < ilen; i += 1) {
14799            if ("@strip-periods" === node.decorations[i][0] && "true" === node.decorations[i][1]) {
14800                ret = ret.replace(/\./g, "");
14801                break;
14802            }
14803        }
14804    }
14805    return ret;
14806};
14807
14808/*global CSL: true */
14809
14810CSL.Node.name = {
14811    build: function (state, target) {
14812        var func;
14813        if ([CSL.SINGLETON, CSL.START].indexOf(this.tokentype) > -1) {
14814            var oldTmpRoot;
14815            if ("undefined" === typeof state.tmp.root) {
14816                oldTmpRoot = undefined;
14817                state.tmp.root = "citation";
14818            } else {
14819                oldTmpRoot = state.tmp.root;
14820            }
14821            // Many CSL styles set et-al-[min|use-first]
14822            // and et-al-subsequent-[min|use-first] to the same
14823            // value.
14824            // Set state.opt.update_mode = CSL.POSITION if
14825            // et-al-subsequent-min or et-al-subsequent-use-first
14826            // are set AND their value differs from their plain
14827            // counterparts.
14828            if (state.inheritOpt(this, "et-al-subsequent-min")
14829                && (state.inheritOpt(this, "et-al-subsequent-min") !== state.inheritOpt(this, "et-al-min"))) {
14830                
14831                state.opt.update_mode = CSL.POSITION;
14832            }
14833            if (state.inheritOpt(this, "et-al-subsequent-use-first")
14834                && (state.inheritOpt(this, "et-al-subsequent-use-first") !== state.inheritOpt(this, "et-al-use-first"))) {
14835                
14836                state.opt.update_mode = CSL.POSITION;
14837            }
14838
14839            state.tmp.root = oldTmpRoot;
14840
14841            func = function (state) {
14842                // Et-al (onward processing in node_etal.js and node_names.js)
14843                // XXXXX Why is this necessary? This is available on this.name, right?
14844                state.tmp.etal_term = "et-al";
14845
14846                // Use default delimiter as fallback, in a way that allows explicit
14847                // empty strings.
14848                state.tmp.name_delimiter = state.inheritOpt(this, "delimiter", "name-delimiter", ", ");
14849                state.tmp["delimiter-precedes-et-al"] = state.inheritOpt(this, "delimiter-precedes-et-al");
14850                
14851                // And
14852                if ("text" === state.inheritOpt(this, "and")) {
14853                    this.and_term = state.getTerm("and", "long", 0);
14854                } else if ("symbol" === state.inheritOpt(this, "and")) {
14855                    if (state.opt.development_extensions.expect_and_symbol_form) {
14856                        this.and_term = state.getTerm("and", "symbol", 0);
14857                    } else {
14858                        this.and_term = "&";
14859                    }
14860                }
14861                state.tmp.and_term = this.and_term;
14862                if (CSL.STARTSWITH_ROMANESQUE_REGEXP.test(this.and_term)) {
14863                    this.and_prefix_single = " ";
14864                    this.and_prefix_multiple = ", ";
14865                    // Workaround to allow explicit empty string
14866                    // on cs:name delimiter.
14867                    if ("string" === typeof state.tmp.name_delimiter) {
14868                        this.and_prefix_multiple = state.tmp.name_delimiter;
14869                    }
14870                    this.and_suffix = " ";
14871
14872                    // Really can't inspect these values in the build phase. Sorry.
14873                    //state.build.name_delimiter = this.strings.delimiter;
14874
14875                } else {
14876                    this.and_prefix_single = "";
14877                    this.and_prefix_multiple = "";
14878                    this.and_suffix = "";
14879                }
14880                if (state.inheritOpt(this, "delimiter-precedes-last") === "always") {
14881                    this.and_prefix_single = state.tmp.name_delimiter;
14882                } else if (state.inheritOpt(this, "delimiter-precedes-last") === "never") {
14883                    // Slightly fragile: could test for charset here to make
14884                    // this more certain.
14885                    if (this.and_prefix_multiple) {
14886                        this.and_prefix_multiple = " ";
14887                    }
14888                } else if (state.inheritOpt(this, "delimiter-precedes-last") === "after-inverted-name") {
14889                    if (this.and_prefix_single) {
14890                        this.and_prefix_single = state.tmp.name_delimiter;
14891                    }
14892                    if (this.and_prefix_multiple) {
14893                        this.and_prefix_multiple = " ";
14894                    }
14895                }
14896
14897                this.and = {};
14898                if (state.inheritOpt(this, "and")) {
14899                    state.output.append(this.and_term, "empty", true);
14900                    this.and.single = state.output.pop();
14901                    this.and.single.strings.prefix = this.and_prefix_single;
14902                    this.and.single.strings.suffix = this.and_suffix;
14903                    state.output.append(this.and_term, "empty", true);
14904                    this.and.multiple = state.output.pop();
14905                    this.and.multiple.strings.prefix = this.and_prefix_multiple;
14906                    this.and.multiple.strings.suffix = this.and_suffix;
14907                } else if (state.tmp.name_delimiter) {
14908                    // This is a little weird, but it works.
14909                    this.and.single = new CSL.Blob(state.tmp.name_delimiter);
14910                    this.and.single.strings.prefix = "";
14911                    this.and.single.strings.suffix = "";
14912                    this.and.multiple = new CSL.Blob(state.tmp.name_delimiter);
14913                    this.and.multiple.strings.prefix = "";
14914                    this.and.multiple.strings.suffix = "";
14915                }
14916
14917                this.ellipsis = {};
14918                if (state.inheritOpt(this, "et-al-use-last")) {
14919                    // We use the dedicated Unicode ellipsis character because
14920                    // it is recommended by some editors, and can be more easily
14921                    // identified for find and replace operations.
14922                    // Source: http://en.wikipedia.org/wiki/Ellipsis#Computer_representations
14923                    //
14924                    
14925                    // Eventually, this should be localized as a term in CSL, with some
14926                    // mechanism for triggering appropriate punctuation handling around
14927                    // the ellipsis placeholder (Polish is a particularly tough case for that).
14928                    this.ellipsis_term = "\u2026";
14929                    // Similar treatment to "and", above, will be needed
14930                    // here when this becomes a locale term.
14931                    this.ellipsis_prefix_single = " ";
14932                    this.ellipsis_prefix_multiple =  state.inheritOpt(this, "delimiter", "name-delimiter", ", ");
14933                    this.ellipsis_suffix = " ";
14934                    this.ellipsis.single = new CSL.Blob(this.ellipsis_term);
14935                    this.ellipsis.single.strings.prefix = this.ellipsis_prefix_single;
14936                    this.ellipsis.single.strings.suffix = this.ellipsis_suffix;
14937                    this.ellipsis.multiple = new CSL.Blob(this.ellipsis_term);
14938                    this.ellipsis.multiple.strings.prefix = this.ellipsis_prefix_multiple;
14939                    this.ellipsis.multiple.strings.suffix = this.ellipsis_suffix;
14940                }
14941
14942                // et-al parameters are annoyingly incomprehensible
14943                // again.
14944                //
14945                // Explanation probably just adds a further layer of
14946                // irritation, but what's INTENDED here is that
14947                // the state.tmp et-al variables are set from the
14948                // cs:key element when composing sort keys, and a
14949                // macro containing a name can be called from cs:key.
14950                // So when cs:key sets et-al attributes, they are
14951                // set on state.tmp, and when the key is finished
14952                // processing, the state.tmp variables are reset to
14953                // undefined. IN THEORY the state.tmp et-al variables
14954                // will not be used in other contexts. I hope.
14955                //
14956                // Anyway, the current tests now seem to pass.
14957                if ("undefined" === typeof state.tmp["et-al-min"]) {
14958                    state.tmp["et-al-min"] = state.inheritOpt(this, "et-al-min");
14959                }
14960                if ("undefined" === typeof state.tmp["et-al-use-first"]) {
14961                    state.tmp["et-al-use-first"] = state.inheritOpt(this, "et-al-use-first");
14962                }
14963                if ("undefined" === typeof state.tmp["et-al-use-last"]) {
14964                    //print("  setting et-al-use-last from name: "+this.strings["et-al-use-last"]);
14965                    state.tmp["et-al-use-last"] = state.inheritOpt(this, "et-al-use-last");
14966                }
14967
14968                state.nameOutput.name = this;
14969            };
14970            
14971            state.build.name_flag = true;
14972
14973            this.execs.push(func);
14974        }
14975        target.push(this);
14976    }
14977};
14978
14979
14980
14981/*global CSL: true */
14982
14983CSL.Node["name-part"] = {
14984    build: function (state) {
14985        state.build[this.strings.name] = this;
14986    }
14987};
14988
14989/*global CSL: true */
14990
14991CSL.Node.names = {
14992    build: function (state, target) {
14993        var func;
14994        // CSL.debug = print;
14995
14996        if (this.tokentype === CSL.START || this.tokentype === CSL.SINGLETON) {
14997            CSL.Util.substituteStart.call(this, state, target);
14998            state.build.substitute_level.push(1);
14999        }
15000        
15001        if (this.tokentype === CSL.SINGLETON) {
15002            state.build.names_variables[state.build.names_variables.length-1].concat(this.variables);
15003            for (var i in this.variables) {
15004                var variable = this.variables[i];
15005                var name_labels = state.build.name_label[state.build.name_label.length-1];
15006                if (Object.keys(name_labels).length) {
15007                    name_labels[variable] = name_labels[Object.keys(name_labels)[0]];
15008                }
15009            }
15010            func = function (state) {
15011                state.nameOutput.reinit(this, this.variables_real[0]);
15012            };
15013            this.execs.push(func);
15014        }
15015
15016        if (this.tokentype === CSL.START) {
15017
15018            state.build.names_flag = true;
15019            state.build.name_flag = false;
15020            state.build.names_level += 1;
15021            state.build.names_variables.push(this.variables);
15022            state.build.name_label.push({});
15023            // init can substitute
15024            // init names
15025            func = function (state) {
15026                state.tmp.can_substitute.push(true);
15027                state.tmp.name_node = {};
15028                state.tmp.name_node.children = [];
15029                state.nameOutput.init(this);
15030            };
15031            this.execs.push(func);
15032
15033        }
15034        
15035        if (this.tokentype === CSL.END) {
15036
15037            // Set/reset name blobs if they exist, for processing
15038            // by namesOutput()
15039            for (var i = 0, ilen = 3; i < ilen; i += 1) {
15040                var key = ["family", "given", "et-al"][i];
15041                this[key] = state.build[key];
15042                if (state.build.names_level === 1) {
15043                    state.build[key] = undefined;
15044                }
15045            }
15046            // Labels, if any
15047            this.label = state.build.name_label[state.build.name_label.length-1];
15048            state.build.names_level += -1;
15049            state.build.names_variables.pop();
15050            state.build.name_label.pop();
15051
15052            // The with term. This isn't the right place
15053            // for this, but it's all hard-wired at the
15054            // moment.
15055
15056            // "and" and "ellipsis" are set in node_name.js
15057            func = function (state) {
15058                // Et-al (strings only)
15059                // Blob production has to happen inside nameOutput()
15060                // since proper escaping requires access to the output
15061                // queue.
15062                if (state.tmp.etal_node) {
15063                    this.etal_style = state.tmp.etal_node;
15064                } else {
15065                    this.etal_style = "empty";
15066                }
15067
15068                this.etal_term = state.getTerm(state.tmp.etal_term, "long", 0);
15069                this.etal_prefix_single = " ";
15070                // Should be name delimiter, not hard-wired.
15071                this.etal_prefix_multiple = state.tmp.name_delimiter;
15072                if (state.tmp["delimiter-precedes-et-al"] === "always") {
15073                    this.etal_prefix_single = state.tmp.name_delimiter;
15074                } else if (state.tmp["delimiter-precedes-et-al"] === "never") {
15075                    this.etal_prefix_multiple = " ";
15076                } else if (state.tmp["delimiter-precedes-et-al"] === "after-inverted-name") {
15077                    this.etal_prefix_single = state.tmp.name_delimiter;
15078                    this.etal_prefix_multiple = " ";
15079                }
15080                this.etal_suffix = "";
15081                if (!CSL.STARTSWITH_ROMANESQUE_REGEXP.test(this.etal_term)) {
15082                    // Not sure what the correct treatment is here, but we should not suppress
15083                    // a comma-space.
15084                    // https://forums.zotero.org/discussion/76679/delimiter-precedes-et-al-always-dose-not-work-in-locale-zh-cn
15085                    if (this.etal_prefix_single === " ") {
15086                        this.etal_prefix_single = "";
15087                    }
15088                    if (this.etal_prefix_multiple === " ") {
15089                        this.etal_prefix_multiple = "";
15090                    }
15091                    if (this.etal_suffix === " ") {
15092                        this.etal_suffix = "";
15093                    }
15094                }
15095                // et-al affixes are further adjusted in nameOutput(),
15096                // after the term (possibly changed in cs:et-al) is known.
15097
15098
15099                for (var i = 0, ilen = 3; i < ilen; i += 1) {
15100                    var key = ["family", "given"][i];
15101                    state.nameOutput[key] = this[key];
15102                }
15103                state.nameOutput["with"] = this["with"];
15104
15105                // REMOVE THIS
15106                var mywith = "with";
15107                var with_default_prefix = "";
15108                var with_suffix = "";
15109                if (CSL.STARTSWITH_ROMANESQUE_REGEXP.test(mywith)) {
15110                    with_default_prefix = " ";
15111                    with_suffix = " ";
15112                }
15113                var thewith = {};
15114                thewith.single = new CSL.Blob(mywith);
15115                thewith.single.strings.suffix = with_suffix;
15116                thewith.multiple = new CSL.Blob(mywith);
15117                thewith.multiple.strings.suffix = with_suffix;
15118                if (state.inheritOpt(state.nameOutput.name, "delimiter-precedes-last") === "always") {
15119                    thewith.single.strings.prefix = state.inheritOpt(this, "delimiter", "names-delimiter");
15120                    thewith.multiple.strings.prefix = state.inheritOpt(this, "delimiter", "names-delimiter");
15121                } else if (state.inheritOpt(state.nameOutput.name, "delimiter-precedes-last") === "contextual") {
15122                    thewith.single.strings.prefix = with_default_prefix;
15123                    thewith.multiple.strings.prefix = state.inheritOpt(this, "delimiter", "names-delimiter");
15124                } else if (state.inheritOpt(state.nameOutput.name, "delimiter-precedes-last") === "after-inverted-name") {
15125                    thewith.single.strings.prefix = state.inheritOpt(this, "delimiter", "names-delimiter");
15126                    thewith.multiple.strings.prefix = with_default_prefix;
15127                } else {
15128                    thewith.single.strings.prefix = with_default_prefix;
15129                    thewith.multiple.strings.prefix = with_default_prefix;
15130                }
15131                state.nameOutput["with"] = thewith;
15132
15133
15134                // XXX label style should be set per variable, since they may differ
15135                // XXX with full-form nested names constructs
15136                state.nameOutput.label = this.label;
15137
15138                state.nameOutput.etal_style = this.etal_style;
15139                state.nameOutput.etal_term = this.etal_term;
15140                state.nameOutput.etal_prefix_single = this.etal_prefix_single;
15141                state.nameOutput.etal_prefix_multiple = this.etal_prefix_multiple;
15142                state.nameOutput.etal_suffix = this.etal_suffix;
15143                state.nameOutput.outputNames();
15144                state.tmp["et-al-use-first"] = undefined;
15145                state.tmp["et-al-min"] = undefined;
15146                state.tmp["et-al-use-last"] = undefined;
15147            };
15148            this.execs.push(func);
15149
15150            // unsets
15151            func = function (state) {
15152                if (!state.tmp.can_substitute.pop()) {
15153                    state.tmp.can_substitute.replace(false, CSL.LITERAL);
15154                }
15155                
15156                // For posterity ...
15157                //
15158                // This was enough to fix the issue reported here:
15159                //
15160                //   http://forums.zotero.org/discussion/25223/citeproc-bug-substitute-doesnt-work-correctly-for-title-macro/
15161                //
15162                // The remainder of the changes applied in the same patch
15163                // relate to a label assignments, which were found to be
15164                // buggy while working on the issue. The test covering
15165                // both problems is here:
15166                //
15167                //   https://bitbucket.org/bdarcus/citeproc-test/src/ab136a6aa8f2/processor-tests/humans/substitute_SuppressOrdinaryVariable.txt
15168                if (state.tmp.can_substitute.mystack.length === 1) {
15169                    state.tmp.can_block_substitute = false;
15170                }
15171            };
15172            this.execs.push(func);
15173
15174            state.build.name_flag = false;
15175        }
15176        target.push(this);
15177
15178        if (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON) {
15179            state.build.substitute_level.pop();
15180            CSL.Util.substituteEnd.call(this, state, target);
15181        }
15182    }
15183};
15184
15185/*global CSL: true */
15186
15187CSL.Node.number = {
15188    build: function (state, target) {
15189        var func;
15190        CSL.Util.substituteStart.call(this, state, target);
15191        //
15192        // This should push a rangeable object to the queue.
15193        //
15194        if (this.strings.form === "roman") {
15195            this.formatter = state.fun.romanizer;
15196        } else if (this.strings.form === "ordinal") {
15197            this.formatter = state.fun.ordinalizer;
15198        } else if (this.strings.form === "long-ordinal") {
15199            this.formatter = state.fun.long_ordinalizer;
15200        }
15201        if ("undefined" === typeof this.successor_prefix) {
15202            this.successor_prefix = state[state.build.area].opt.layout_delimiter;
15203        }
15204        if ("undefined" === typeof this.splice_prefix) {
15205            this.splice_prefix = state[state.build.area].opt.layout_delimiter;
15206        }
15207        // is this needed?
15208        //if ("undefined" === typeof this.splice_prefix){
15209        //    this.splice_prefix = state[state.tmp.area].opt.layout_delimiter;
15210        //}
15211        //
15212        // Whether we actually stick a number object on
15213        // the output queue depends on whether the field
15214        // contains a pure number.
15215        //
15216        // push number or text
15217        func = function (state, Item, item) {
15218            // NOTE: this works because this is the ONLY function in this node.
15219            // If further functions are added, they need to start with the same
15220            // abort condition.
15221            if (this.variables.length === 0) {
15222                return;
15223            }
15224            var varname;
15225            varname = this.variables[0];
15226            if ("undefined" === typeof item) {
15227                var item = {};
15228            }
15229            if (["locator", "locator-extra"].indexOf(varname) > -1) {
15230                if (state.tmp.just_looking) {
15231                    return;
15232                }
15233                if (!item[varname]) {
15234                    return;
15235                }
15236            } else {
15237                if (!Item[varname]) {
15238                    return;
15239                }
15240            }
15241
15242            if (varname === 'collection-number' && Item.type === 'legal_case') {
15243                state.tmp.renders_collection_number = true;
15244            }
15245            
15246            // For bill or legislation items that have a label-form
15247            // attribute set on the cs:number node rendering the locator,
15248            // the form and pluralism of locator terms are controlled
15249            // separately from those of the initial label. Form is
15250            // straightforward: the label uses the value set on
15251            // the cs:label node that renders it, and the embedded
15252            // labels use the value of label-form set on the cs:number
15253            // node. Both default to "long".
15254            //
15255            // Pluralism is more complicated. For embedded labels,
15256            // pluralism is evaluated using a simple heuristic that
15257            // can be found below (it just looks for comma, ampersand etc).
15258            // The item.label rendered independently via cs:label
15259            // defaults to singular. It is always singular if embedded
15260            // labels exist that (when expanded to their valid CSL
15261            // value) do not match the value of item.label. Otherwise,
15262            // if one or more matching embedded labels exist, the
15263            // cs:label is set to plural.
15264            //
15265            // The code that does all this is divided between this module,
15266            // util_static_locator.js, and util_label.js. It's not easy
15267            // to follow, but seems to do the job. Let's home for good
15268            // luck out there in the wild.
15269            
15270            var node = this;
15271
15272            if (state.tmp.group_context.tip.force_suppress) {
15273                return false;
15274            }
15275
15276            if (["locator", "locator-extra"].indexOf(varname) > -1) {
15277                // amazing that we reach this. should abort sooner if no content?
15278                state.processNumber.call(state, node, item, varname, Item.type);
15279            } else {
15280                if (!state.tmp.group_context.tip.condition && Item[varname]) {
15281                    state.tmp.just_did_number = ("" + Item[varname]).match(/[0-9]$/);
15282                }
15283                // UPDATE_GROUP_CONTEXT_CONDITION is run by processNumber
15284                state.processNumber.call(state, node, Item, varname, Item.type);
15285            }
15286
15287            if (this.substring) {
15288                var val = Item[varname].slice(this.substring);
15289                state.output.append(val, node);
15290            } else {
15291                CSL.Util.outputNumericField(state, varname, Item.id);
15292            }
15293
15294            if (["locator", "locator-extra"].indexOf(this.variables_real[0]) > -1
15295               && !state.tmp.just_looking) {
15296                state.tmp.done_vars.push(this.variables_real[0]);
15297                state.tmp.group_context.tip.done_vars.push(this.variables_real[0]);
15298            }
15299        };
15300        this.execs.push(func);
15301        target.push(this);
15302        
15303        CSL.Util.substituteEnd.call(this, state, target);
15304    }
15305};
15306
15307/*global CSL: true */
15308
15309/*
15310 * Yikes, these functions were running out of scope for yonks.
15311 * now that they are set in the correct token list,
15312 * they might be useful for things.
15313 * FB 2013.11.09
15314*/
15315
15316CSL.Node.sort = {
15317    build: function (state, target) {
15318        target = state[state.build.root + "_sort"].tokens;
15319        if (this.tokentype === CSL.START) {
15320            if (state.build.area === "citation") {
15321                state.opt.sort_citations = true;
15322            }
15323            state.build.area = state.build.root + "_sort";
15324            state.build.extension = "_sort";
15325            
15326            var func = function (state, Item) {
15327                //state.tmp.area = state.tmp.root + "_sort";
15328                //state.tmp.extension = "_sort";
15329                if (state.opt.has_layout_locale) {
15330                    var langspec = CSL.localeResolve(Item.language, state.opt["default-locale"][0]);
15331                    var sort_locales = state[state.tmp.area.slice(0,-5)].opt.sort_locales;
15332                    var langForItem;
15333                    for (var i=0,ilen=sort_locales.length;i<ilen;i+=1) {
15334                        langForItem = sort_locales[i][langspec.bare];
15335                        if (!langForItem) {
15336                            langForItem = sort_locales[i][langspec.best];
15337                        }
15338                        if (langForItem) {
15339                            break;
15340                        }
15341                    }
15342                    if (!langForItem) {
15343                        langForItem = state.opt["default-locale"][0];
15344                    }
15345                    state.tmp.lang_sort_hold = state.opt.lang;
15346                    state.opt.lang = langForItem;
15347                }
15348            };
15349            this.execs.push(func);
15350            
15351        }
15352        if (this.tokentype === CSL.END) {
15353            state.build.area = state.build.root;
15354            state.build.extension = "";
15355            var func = function (state) {
15356                if (state.opt.has_layout_locale) {
15357                    state.opt.lang = state.tmp.lang_sort_hold;
15358                    delete state.tmp.lang_sort_hold;
15359                }
15360                //state.tmp.area = state.tmp.root;
15361                //state.tmp.extension = "";
15362            };
15363            this.execs.push(func);
15364            /*
15365            var func = function (state, Item) {
15366                state.tmp.area = state.tmp.root;
15367                state.tmp.extension = "";
15368            }
15369            this.execs.push(func);
15370            */
15371        }
15372        target.push(this);
15373    }
15374};
15375
15376
15377
15378/*global CSL: true */
15379
15380CSL.Node.substitute = {
15381    build: function (state, target) {
15382        var func;
15383        if (this.tokentype === CSL.START) {
15384            /* */
15385            // set conditional
15386            var choose_start = new CSL.Token("choose", CSL.START);
15387            CSL.Node.choose.build.call(choose_start, state, target);
15388            var if_singleton = new CSL.Token("if", CSL.SINGLETON);
15389            func = function() {
15390                if (state.tmp.value.length && !state.tmp.common_term_match_fail) {
15391                    return true;
15392                }
15393                return false;
15394            }
15395            if_singleton.tests = [func];
15396            if_singleton.test = state.fun.match.any(if_singleton, state, if_singleton.tests);
15397            target.push(if_singleton);
15398
15399            func = function (state) {
15400                state.tmp.can_block_substitute = true;
15401                if (state.tmp.value.length && !state.tmp.common_term_match_fail) {
15402                    state.tmp.can_substitute.replace(false, CSL.LITERAL);
15403                }
15404                state.tmp.common_term_match_fail = false;
15405            };
15406            this.execs.push(func);
15407            target.push(this);
15408            /* */
15409        }
15410        if (this.tokentype === CSL.END) {
15411            //var if_end = new CSL.Token("if", CSL.END);
15412            //CSL.Node["if"].build.call(if_end, state, target);
15413            /* */
15414            target.push(this);
15415            var choose_end = new CSL.Token("choose", CSL.END);
15416            CSL.Node.choose.build.call(choose_end, state, target);
15417            /* */
15418        }
15419    }
15420};
15421
15422
15423
15424/*global CSL: true */
15425
15426CSL.Node.text = {
15427    build: function (state, target) {
15428        var func, form, plural, id, num, number, formatter, firstoutput, specialdelimiter, label, suffix, term;
15429        if (this.postponed_macro) {
15430            var group_start = CSL.Util.cloneToken(this);
15431            group_start.name = "group";
15432            group_start.tokentype = CSL.START;
15433            CSL.Node.group.build.call(group_start, state, target);
15434
15435            CSL.expandMacro.call(state, this, target);
15436
15437            var group_end = CSL.Util.cloneToken(this);
15438            group_end.name = "group";
15439            group_end.tokentype = CSL.END;
15440            if (this.postponed_macro === 'juris-locator-label') {
15441                group_end.isJurisLocatorLabel = true;
15442            }
15443            CSL.Node.group.build.call(group_end, state, target);
15444
15445        } else {
15446            CSL.Util.substituteStart.call(this, state, target);
15447            // ...
15448            //
15449            // Do non-macro stuff
15450            
15451            // Guess again. this.variables is ephemeral, adjusted by an initial
15452            // function set on the node via @variable attribute setup.
15453            //variable = this.variables[0];
15454            
15455            if (!this.variables_real) {
15456                this.variables_real = [];
15457            }
15458            if (!this.variables) {
15459                this.variables = [];
15460            }
15461
15462            form = "long";
15463            plural = 0;
15464            if (this.strings.form) {
15465                form = this.strings.form;
15466            }
15467            if (this.strings.plural) {
15468                plural = this.strings.plural;
15469            }
15470            if ("citation-number" === this.variables_real[0] || "year-suffix" === this.variables_real[0] || "citation-label" === this.variables_real[0]) {
15471                //
15472                // citation-number and year-suffix are super special,
15473                // because they are rangeables, and require a completely
15474                // different set of formatting parameters on the output
15475                // queue.
15476                if (this.variables_real[0] === "citation-number") {
15477
15478                    if (state.build.root === "citation") {
15479                        state.opt.update_mode = CSL.NUMERIC;
15480                    }
15481                    if (state.build.root === "bibliography") {
15482                        state.opt.bib_mode = CSL.NUMERIC;
15483                    }
15484                    //this.strings.is_rangeable = true;
15485                    if ("citation-number" === state[state.tmp.area].opt.collapse) {
15486                        this.range_prefix = state.getTerm("citation-range-delimiter");
15487                    }
15488                    this.successor_prefix = state[state.build.area].opt.layout_delimiter;
15489                    this.splice_prefix = state[state.build.area].opt.layout_delimiter;
15490                    func = function (state, Item, item) {
15491
15492                        id = "" + Item.id;
15493                        if (!state.tmp.just_looking) {
15494                            if (state.tmp.area.slice(-5) === "_sort" && this.variables[0] === "citation-number") {
15495                                if (state.tmp.area === "bibliography_sort") {
15496                                    state.tmp.group_context.tip.done_vars.push("citation-number");
15497                                }
15498                                if (state.tmp.area === "citation_sort" && state.bibliography_sort.tmp.citation_number_map) {
15499                                    var num = state.bibliography_sort.tmp.citation_number_map[state.registry.registry[Item.id].seq];
15500                                } else {
15501                                    var num = state.registry.registry[Item.id].seq;
15502                                }
15503                                if (num) {
15504                                    // Code currently in util_number.js
15505                                    num = CSL.Util.padding("" + num);
15506                                }
15507                                state.output.append(num, this);
15508                                return;
15509                            }
15510                            if (item && item["author-only"]) {
15511                                state.tmp.element_trace.replace("suppress-me");
15512                            }
15513                            if (state.tmp.area !== "bibliography_sort" && state.bibliography_sort.tmp.citation_number_map && state.bibliography_sort.opt.citation_number_sort_direction === CSL.DESCENDING) {
15514                                num = state.bibliography_sort.tmp.citation_number_map[state.registry.registry[id].seq];
15515                            } else {
15516                                num = state.registry.registry[id].seq;
15517                            }
15518                            if (state.opt.citation_number_slug) {
15519                                state.output.append(state.opt.citation_number_slug, this);
15520                            } else {
15521                                number = new CSL.NumericBlob(state, false, num, this, Item.id);
15522                                if (state.tmp.in_cite_predecessor) {
15523                                    number.suppress_splice_prefix = true;
15524                                }
15525                                state.output.append(number, "literal");
15526                            }
15527                        }
15528                    };
15529                    this.execs.push(func);
15530                } else if (this.variables_real[0] === "year-suffix") {
15531
15532                    state.opt.has_year_suffix = true;
15533
15534                    if (state[state.tmp.area].opt.collapse === "year-suffix-ranged") {
15535                        //this.range_prefix = "-";
15536                        this.range_prefix = state.getTerm("citation-range-delimiter");
15537                    }
15538                    this.successor_prefix = state[state.build.area].opt.layout_delimiter;
15539                    if (state[state.tmp.area].opt["year-suffix-delimiter"]) {
15540                        this.successor_prefix = state[state.build.area].opt["year-suffix-delimiter"];
15541                    }
15542                    func = function (state, Item) {
15543                        if (state.registry.registry[Item.id] && state.registry.registry[Item.id].disambig.year_suffix !== false && !state.tmp.just_looking) {
15544                            //state.output.append(state.registry.registry[Item.id].disambig[2],this);
15545                            num = parseInt(state.registry.registry[Item.id].disambig.year_suffix, 10);
15546
15547                            //if (state[state.tmp.area].opt.collapse === "year-suffix-ranged") {
15548                            //    //this.range_prefix = "-";
15549                            //    this.range_prefix = state.getTerm("citation-range-delimiter");
15550                            //}
15551                            //this.successor_prefix = state[state.tmp.area].opt.layout_delimiter;
15552                            if (state[state.tmp.area].opt.cite_group_delimiter) {
15553                                this.successor_prefix = state[state.tmp.area].opt.cite_group_delimiter;
15554                            }
15555                            number = new CSL.NumericBlob(state, false, num, this, Item.id);
15556                            formatter = new CSL.Util.Suffixator(CSL.SUFFIX_CHARS);
15557                            number.setFormatter(formatter);
15558                            state.output.append(number, "literal");
15559                            firstoutput = false;
15560                            // XXX Can we do something better for length here?
15561                            for (var i=0,ilen=state.tmp.group_context.mystack.length; i<ilen; i++) {
15562                                var flags = state.tmp.group_context.mystack[i];
15563                                if (!flags.variable_success && (flags.variable_attempt || (!flags.variable_attempt && !flags.term_intended))) {
15564                                    firstoutput = true;
15565                                    break;
15566                                }
15567                            }
15568                            specialdelimiter = state[state.tmp.area].opt["year-suffix-delimiter"];
15569                            if (firstoutput && specialdelimiter && !state.tmp.sort_key_flag) {
15570                                state.tmp.splice_delimiter = state[state.tmp.area].opt["year-suffix-delimiter"];
15571                            }
15572                        }
15573                    };
15574                    this.execs.push(func);
15575                } else if (this.variables_real[0] === "citation-label") {
15576                    if (state.build.root === "bibliography") {
15577                        state.opt.bib_mode = CSL.TRIGRAPH;
15578                    }
15579                    state.opt.has_year_suffix = true;
15580                    func = function (state, Item) {
15581                        label = Item["citation-label"];
15582                        if (!label) {
15583                            label = state.getCitationLabel(Item);
15584                        }
15585                        if (!state.tmp.just_looking) {
15586                            suffix = "";
15587                            if (state.registry.registry[Item.id] && state.registry.registry[Item.id].disambig.year_suffix !== false) {
15588                                num = parseInt(state.registry.registry[Item.id].disambig.year_suffix, 10);
15589                                suffix = state.fun.suffixator.format(num);
15590                            }
15591                            label += suffix;
15592                        }
15593                        state.output.append(label, this);
15594                    };
15595                    this.execs.push(func);
15596                }
15597            } else {
15598                if (this.strings.term) {
15599                    
15600                    // printterm
15601                    func = function (state, Item) {
15602                        var gender = state.opt.gender[Item.type];
15603                        var term = this.strings.term;
15604                        term = state.getTerm(term, form, plural, gender, CSL.TOLERANT, this.default_locale);
15605                        var myterm;
15606                        // if the term is not an empty string, say
15607                        // that we rendered a term
15608                        if (term !== "") {
15609                            state.tmp.group_context.tip.term_intended = true;
15610                        }
15611                        CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, term, null, this);
15612                        
15613                        // capitalize the first letter of a term, if it is the
15614                        // first thing rendered in a citation (or if it is
15615                        // being rendered immediately after terminal punctuation,
15616                        // I guess, actually).
15617                        if (!state.tmp.term_predecessor && !(state.opt["class"] === "in-text" && state.tmp.area === "citation")) {
15618                            myterm = CSL.Output.Formatters["capitalize-first"](state, term);
15619                            //CSL.debug("Capitalize");
15620                        } else {
15621                            myterm = term;
15622                        }
15623                        
15624                        // XXXXX Cut-and-paste code in multiple locations. This code block should be
15625                        // collected in a function.
15626                        // Tag: strip-periods-block
15627                        if (state.tmp.strip_periods) {
15628                            myterm = myterm.replace(/\./g, "");
15629                        } else {
15630                            for (var i = 0, ilen = this.decorations.length; i < ilen; i += 1) {
15631                                if ("@strip-periods" === this.decorations[i][0] && "true" === this.decorations[i][1]) {
15632                                    myterm = myterm.replace(/\./g, "");
15633                                    break;
15634                                }
15635                            }
15636                        }
15637                        state.output.append(myterm, this);
15638                        if (state.tmp.can_block_substitute) {
15639                            // Black magic here. This causes the cs:substitution condition to pass,
15640                            // blocking further rendering within its scope. 
15641                            state.tmp.can_substitute.replace(false, CSL.LITERAL);
15642                        }
15643                    };
15644                    this.execs.push(func);
15645                    state.build.term = false;
15646                    state.build.form = false;
15647                    state.build.plural = false;
15648                } else if (this.variables_real.length) {
15649                    func = function (state, Item) {
15650
15651                        // If some text variable is rendered, we're not collapsing.
15652                        if (this.variables_real[0] !== "locator") {
15653                            state.tmp.have_collapsed = false;
15654                        }
15655
15656                        if (!state.tmp.group_context.tip.condition && Item[this.variables[0]]) {
15657                            state.tmp.just_did_number = false;
15658                        }
15659                        var val = Item[this.variables[0]];
15660                        if (val && !state.tmp.group_context.tip.condition) {
15661                            if (("" + val).slice(-1).match(/[0-9]/)) {
15662                                state.tmp.just_did_number = true;
15663                            } else {
15664                                state.tmp.just_did_number = false;
15665                            }
15666                        }
15667                    };
15668                    this.execs.push(func);
15669
15670                    // plain string fields
15671
15672                    // Deal with multi-fields and ordinary fields separately.
15673                    if (CSL.MULTI_FIELDS.indexOf(this.variables_real[0]) > -1
15674                        || this.variables_real[0].indexOf("-main") > -1
15675                        || this.variables_real[0].indexOf("-sub") > -1
15676                        || ["language-name", "language-name-original"].indexOf(this.variables_real[0]) > -1
15677                       ) {
15678
15679                        // multi-fields
15680                        // Initialize transform factory according to whether
15681                        // abbreviation is desired.
15682                        var abbrevfam = this.variables[0];
15683                        var abbrfall = false;
15684                        var altvar = false;
15685                        var transfall = false;
15686                        if (form === "short") {
15687                            if (this.variables_real[0].slice(-6) !== "-short") {
15688                                altvar = this.variables_real[0] + "-short";
15689                            }
15690                        } else {
15691                            abbrevfam = false;
15692                        }
15693                        if (state.build.extension) {
15694                            // multi-fields for sorting get a sort transform,
15695                            // (abbreviated if the short form was selected)
15696                            transfall = true;
15697                        } else {
15698                            transfall = true;
15699                            abbrfall = true;
15700						}
15701
15702                        func = state.transform.getOutputFunction(this.variables, abbrevfam, abbrfall, altvar, transfall);
15703                    } else {
15704                        // ordinary fields
15705                        if (CSL.CITE_FIELDS.indexOf(this.variables_real[0]) > -1) {
15706                            // per-cite fields are read from item, rather than Item
15707                            func = function (state, Item, item) {
15708                                if (item && item[this.variables[0]]) {
15709                                    // Code copied to page variable as well; both
15710                                    // become cs:number in MLZ extended schema
15711                                    
15712                                    // If locator, use cs:number. Otherwise, render
15713                                    // normally.
15714
15715                                    // XXX The code below is pretty-much copied from
15716                                    // XXX node_number.js. Should be a common function.
15717                                    // XXX BEGIN
15718                                    state.processNumber(this, item, this.variables[0], Item.type);
15719                                    CSL.Util.outputNumericField(state, this.variables[0], Item.id);
15720                                    // XXX END
15721
15722                                    if (["locator", "locator-extra"].indexOf(this.variables_real[0]) > -1
15723                                       && !state.tmp.just_looking) { 
15724                                        state.tmp.done_vars.push(this.variables_real[0]);
15725                                    }
15726                                }
15727                            };
15728                        } else  if (["page", "page-first", "chapter-number", "collection-number", "edition", "issue", "number", "number-of-pages", "number-of-volumes", "volume"].indexOf(this.variables_real[0]) > -1) {
15729                            // page gets mangled with the correct collapsing
15730                            // algorithm
15731                            func = function(state, Item) {
15732                                state.processNumber(this, Item, this.variables[0], Item.type);
15733                                CSL.Util.outputNumericField(state, this.variables[0], Item.id);
15734                            };
15735                        } else if (["URL", "DOI"].indexOf(this.variables_real[0]) > -1) {
15736                            func = function (state, Item) {
15737                                var value;
15738                                if (this.variables[0]) {
15739                                    value = state.getVariable(Item, this.variables[0], form);
15740                                    if (value) {
15741                                        if (this.variables[0] === "URL" && form === "short") {
15742                                            value = value.replace(/(.*\.[^\/]+)\/.*/, "$1");
15743                                            if (value.match(/\/\/www\./)) {
15744                                                value = value.replace(/https?:\/\//, "");
15745                                            }
15746                                        }
15747                                        // true is for non-suppression of periods
15748                                        if (state.opt.development_extensions.wrap_url_and_doi) {
15749                                            if (!this.decorations.length || this.decorations[0][0] !== "@" + this.variables[0]) {
15750                                                // Special-casing to fix https://github.com/Juris-M/citeproc-js/issues/57
15751                                                // clone current token, to avoid collateral damage
15752                                                var clonetoken = CSL.Util.cloneToken(this);
15753                                                // cast a group blob
15754                                                var groupblob = new CSL.Blob(null, null, "url-wrapper");
15755                                                // set the DOI decoration on the blob
15756                                                groupblob.decorations.push(["@DOI", "true"]);
15757                                                if (this.variables_real[0] === "DOI") {
15758                                                    // strip a proper DOI prefix
15759                                                    var prefix;
15760                                                    if (this.strings.prefix && this.strings.prefix.match(/^.*https:\/\/doi\.org\/$/)) {
15761                                                        value = value.replace(/^https?:\/\/doi\.org\//, "");
15762                                                        if (value.match(/^https?:\/\//)) {
15763                                                            // Do not tamper with another protocol + domain if already set in field value
15764                                                            prefix = "";
15765                                                        } else {
15766                                                            // Otherwise https + domain
15767                                                            prefix = "https://doi.org/";
15768                                                        }
15769                                                        // set any string prefix on the clone
15770                                                        clonetoken.strings.prefix = this.strings.prefix.slice(0, clonetoken.strings.prefix.length-16);
15771                                                    }
15772                                                    // cast a text blob
15773                                                    // set the prefix as the content of the blob
15774                                                    var prefixblob = new CSL.Blob(prefix);
15775                                                    // cast another text blob
15776                                                    // set the value as the content of the second blob
15777                                                    var valueblob = new CSL.Blob(value);
15778                                                    // append new text token and clone to group token
15779                                                    groupblob.push(prefixblob);
15780                                                    groupblob.push(valueblob);
15781                                                    // append group token to output
15782                                                    state.output.append(groupblob, clonetoken, false, false, true);
15783                                                } else {
15784                                                    var valueblob = new CSL.Blob(value);
15785                                                    // append new text token and clone to group token
15786                                                    groupblob.push(valueblob);
15787                                                    // append group token to output
15788                                                    //this.decorations = [["@" + this.variables[0], "true"]].concat(this.decorations);
15789                                                    state.output.append(groupblob, clonetoken, false, false, true);
15790                                                }
15791                                            } else {
15792                                                state.output.append(value, this, false, false, true);
15793                                            }
15794                                        } else {
15795                                            // This is totally unnecessary, isn't it?
15796                                            if (this.decorations.length) {
15797                                                for (var i=this.decorations.length-1; i>-1; i--) {
15798                                                    if (this.decorations[i][0] === "@" + this.variables[0]) {
15799                                                        this.decorations = this.decorations.slice(0, i).concat(this.decorations.slice(i+1));
15800                                                    }
15801                                                }
15802                                            }
15803                                            state.output.append(value, this, false, false, true);
15804                                        }
15805                                    }
15806                                }
15807                            };
15808                        } else if (this.variables_real[0] === "section") {
15809                            // Sections for statutes are special. This is an uncommon
15810                            // variable, so we save the cost of the runtime check
15811                            // unless it's being used.
15812                            func = function (state, Item) {
15813                                var value;
15814                                value = state.getVariable(Item, this.variables[0], form);
15815                                if (value) {
15816                                    state.output.append(value, this);
15817                                }
15818                            };
15819                        } else if (this.variables_real[0] === "hereinafter") {
15820                            func = function (state, Item) {
15821                                var value = state.transform.abbrevs["default"]["hereinafter"][Item.id];
15822                                if (value) {
15823                                    state.output.append(value, this);
15824                                    state.tmp.group_context.tip.variable_success = true;
15825                                }
15826                            };
15827                        } else {
15828                            // anything left over just gets output in the normal way.
15829                            func = function (state, Item) {
15830                                var value;
15831                                if (this.variables[0]) {
15832                                    value = state.getVariable(Item, this.variables[0], form);
15833                                    if (value) {
15834                                        value = "" + value;
15835                                        value = value.split("\\").join("");
15836                                        state.output.append(value, this);
15837                                    }
15838                                }
15839                            };
15840                        }
15841                    }
15842                    this.execs.push(func);
15843                } else if (this.strings.value) {
15844                    // for the text value attribute.
15845                    func = function (state) {
15846                        state.tmp.group_context.tip.term_intended = true;
15847                        // true flags that this is a literal-value term
15848                        CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, this.strings.value, true, this);
15849                        state.output.append(this.strings.value, this);
15850                        if (state.tmp.can_block_substitute) {
15851                            // Black magic here. This causes the cs:substitution condition to pass,
15852                            // blocking further rendering within its scope. 
15853                            state.tmp.can_substitute.replace(false, CSL.LITERAL);
15854                        }
15855                    };
15856                    this.execs.push(func);
15857                    // otherwise no output
15858                }
15859            }
15860            target.push(this);
15861            CSL.Util.substituteEnd.call(this, state, target);
15862        }
15863    }
15864};
15865
15866
15867
15868/*global CSL: true */
15869
15870CSL.Node.intext = {
15871    build: function (state, target) {
15872        if (this.tokentype === CSL.START) {
15873
15874            state.build.area = "intext";
15875            state.build.root = "intext";
15876            state.build.extension = "";
15877
15878            var func = function(state, Item) {
15879                state.tmp.area = "intext";
15880                state.tmp.root = "intext";
15881                state.tmp.extension = "";
15882            }
15883            this.execs.push(func);
15884        }
15885        if (this.tokentype === CSL.END) {
15886
15887            // Do whatever cs:citation does with sorting.
15888            state.intext_sort = {
15889                opt: {
15890                    sort_directions: state.citation_sort.opt.sort_directions
15891                }
15892            }
15893            state.intext.srt = state.citation.srt;
15894        }
15895        target.push(this);
15896    }
15897};
15898
15899
15900/*global CSL: true */
15901
15902CSL.Attributes = {};
15903
15904CSL.Attributes["@disambiguate"] = function (state, arg) {
15905    if (!this.tests) {this.tests = []; };
15906    if (arg === "true") {
15907        state.opt.has_disambiguate = true;
15908        var func = function (Item) {
15909            if (state.tmp.area === "bibliography") {
15910                if (state.tmp.disambiguate_count < state.registry.registry[Item.id].disambig.disambiguate) {
15911                    state.tmp.disambiguate_count += 1;
15912                    return true;
15913                }
15914            } else {
15915                state.tmp.disambiguate_maxMax += 1;
15916                if (state.tmp.disambig_settings.disambiguate
15917                    && state.tmp.disambiguate_count < state.tmp.disambig_settings.disambiguate) {
15918                    state.tmp.disambiguate_count += 1;
15919                    return true;
15920                }
15921            }
15922            return false;
15923        };
15924        this.tests.push(func);
15925    } else if (arg === "check-ambiguity-and-backreference") {
15926        var func = function (Item) {
15927            if (state.registry.registry[Item.id].disambig.disambiguate && state.registry.registry[Item.id]["citation-count"] > 1) {
15928                return true;
15929            }
15930            return false;
15931        };
15932        this.tests.push(func);
15933    }
15934};
15935
15936CSL.Attributes["@is-numeric"] = function (state, arg) {
15937    if (!this.tests) {this.tests = []; };
15938    var variables = arg.split(/\s+/);
15939    var maketest = function(variable) {
15940        return function (Item, item) {
15941            var myitem = Item;
15942            if (item && ["locator","locator-extra"].indexOf(variable) > -1) {
15943                myitem = item;
15944            }
15945            if (!myitem[variable]) {
15946                return false;
15947            }
15948            if (CSL.NUMERIC_VARIABLES.indexOf(variable) > -1) {
15949                if (!state.tmp.shadow_numbers[variable]) {
15950                    state.processNumber(false, myitem, variable, Item.type);
15951                }
15952                if (state.tmp.shadow_numbers[variable].numeric) {
15953                    return true;
15954                }
15955            } else if (["title","version"].indexOf(variable) > -1) {
15956                if (myitem[variable].slice(-1) === "" + parseInt(myitem[variable].slice(-1), 10)) {
15957                    return true;
15958                }
15959            }
15960            return false;
15961        };
15962    };
15963    for (var i=0; i<variables.length; i+=1) {
15964        this.tests.push(maketest(variables[i]));
15965    }
15966};
15967
15968
15969CSL.Attributes["@is-uncertain-date"] = function (state, arg) {
15970    if (!this.tests) {this.tests = []; };
15971    var variables = arg.split(/\s+/);
15972    // Strip off any boolean prefix.
15973    var maketest = function (myvariable) {
15974        return function(Item) {
15975            if (Item[myvariable] && Item[myvariable].circa) {
15976                return true;
15977            } else {
15978                return false;
15979            }
15980        };
15981    };
15982    for (var i=0,ilen=variables.length;i<ilen;i+=1) {
15983        this.tests.push(maketest(variables[i]));
15984    }
15985};
15986
15987
15988CSL.Attributes["@locator"] = function (state, arg) {
15989    if (!this.tests) {this.tests = []; };
15990    var trylabels = arg.replace("sub verbo", "sub-verbo");
15991    trylabels = trylabels.split(/\s+/);
15992    // Strip off any boolean prefix.
15993    var maketest = function (trylabel) {
15994        return function(Item, item) {
15995            var label;
15996            state.processNumber(false, item, "locator");
15997            label = state.tmp.shadow_numbers.locator.label;
15998            if (label && trylabel === label) {
15999                return true;
16000            } else {
16001                return false;
16002            }
16003        };
16004    };
16005    for (var i=0,ilen=trylabels.length;i<ilen;i+=1) {
16006        this.tests.push(maketest(trylabels[i]));
16007    }
16008};
16009
16010
16011CSL.Attributes["@position"] = function (state, arg) {
16012    if (!this.tests) {this.tests = []; };
16013    var tryposition;
16014    state.opt.update_mode = CSL.POSITION;
16015    var trypositions = arg.split(/\s+/);
16016    var testSubsequentNear = function (Item, item) {
16017        if (item && CSL.POSITION_MAP[item.position] >= CSL.POSITION_MAP[CSL.POSITION_SUBSEQUENT] && item["near-note"]) {
16018            return true;
16019        }
16020        return false;
16021    };
16022    var testSubsequentNotNear = function (Item, item) {
16023        if (item && CSL.POSITION_MAP[item.position] == CSL.POSITION_MAP[CSL.POSITION_SUBSEQUENT] && !item["near-note"]) {
16024            return true;
16025        }
16026        return false;
16027    };
16028    var maketest = function(tryposition) {
16029        return function (Item, item) {
16030            if (state.tmp.area === "bibliography") {
16031                return false;
16032            }
16033            if (item && "undefined" === typeof item.position) {
16034                item.position = 0;
16035            }
16036            if (item && typeof item.position === "number") {
16037                if (item.position === 0 && tryposition === 0) {
16038                    return true;
16039                } else if (tryposition > 0 && CSL.POSITION_MAP[item.position] >= CSL.POSITION_MAP[tryposition]) {
16040                    return true;
16041                }
16042            } else if (tryposition === 0) {
16043                return true;
16044            }
16045            return false;
16046        };
16047    };
16048    for (var i=0,ilen=trypositions.length;i<ilen;i+=1) {
16049        var tryposition = trypositions[i];
16050        if (tryposition === "first") {
16051            tryposition = CSL.POSITION_FIRST;
16052        } else if (tryposition === "container-subsequent") {
16053            tryposition = CSL.POSITION_CONTAINER_SUBSEQUENT;
16054        } else if (tryposition === "subsequent") {
16055            tryposition = CSL.POSITION_SUBSEQUENT;
16056        } else if (tryposition === "ibid") {
16057            tryposition = CSL.POSITION_IBID;
16058        } else if (tryposition === "ibid-with-locator") {
16059            tryposition = CSL.POSITION_IBID_WITH_LOCATOR;
16060        }
16061        if ("near-note" === tryposition) {
16062            this.tests.push(testSubsequentNear);
16063        } else if ("far-note" === tryposition) {
16064            this.tests.push(testSubsequentNotNear);
16065        } else {
16066            this.tests.push(maketest(tryposition));
16067        }
16068    }
16069};
16070
16071CSL.Attributes["@type"] = function (state, arg) {
16072    if (!this.tests) {this.tests = []; };
16073    // XXX This is ALWAYS composed as an "any" match
16074    var types = arg.split(/\s+/);
16075    // Strip off any boolean prefix.
16076    var maketest = function (mytype) {
16077        return function(Item) {
16078            var ret = (Item.type === mytype);
16079            if (ret) {
16080                return true;
16081            } else {
16082                return false;
16083            }
16084        };
16085    };
16086    var tests = [];
16087    for (var i=0,ilen=types.length;i<ilen;i+=1) {
16088        tests.push(maketest(types[i]));
16089    }
16090    this.tests.push(state.fun.match.any(this, state, tests));
16091};
16092
16093CSL.Attributes["@variable"] = function (state, arg) {
16094    if (!this.tests) {this.tests = []; };
16095    var func;
16096    this.variables = arg.split(/\s+/);
16097    this.variables_real = this.variables.slice();
16098
16099    // First the non-conditional code.
16100    if ("label" === this.name && this.variables[0]) {
16101        this.strings.term = this.variables[0];
16102    } else if (["names", "date", "text", "number"].indexOf(this.name) > -1) {
16103        //
16104        // An oddity of variable handling is that this.variables
16105        // is actually ephemeral; the full list of variables is
16106        // held in the variables_real var, and pushed into this.variables
16107        // conditionally in order to suppress repeat renderings of
16108        // the same item variable.  [STILL FUNCTIONAL? 2010.01.15]
16109        //
16110        // set variable names
16111        func = function (state, Item, item) {
16112            // Clear this.variables in place
16113            for (var i = this.variables.length - 1; i > -1; i += -1) {
16114                this.variables.pop();
16115            }
16116            for (var i=0,ilen=this.variables_real.length;i<ilen;i++) {
16117                // set variable name if not quashed, and if not the title of a legal case w/suppress-author
16118                if (state.tmp.done_vars.indexOf(this.variables_real[i]) === -1 
16119// This looks nuts. Why suppress a case name if not required by context?
16120//                    && !(item && Item.type === "legal_case" && item["suppress-author"] && this.variables_real[i] === "title")
16121                   ) {
16122                    this.variables.push(this.variables_real[i]);
16123                }
16124                if (state.tmp.can_block_substitute) {
16125                    state.tmp.done_vars.push(this.variables_real[i]);
16126                }
16127            }
16128        };
16129        this.execs.push(func);
16130
16131        // check for output
16132        func = function (state, Item, item) {
16133            var output = false;
16134            for (var i=0,ilen=this.variables.length;i<ilen;i++) {
16135                var variable = this.variables[i];
16136                if (["authority", "committee"].indexOf(variable) > -1
16137                    && "string" === typeof Item[variable]
16138                    && "names" === this.name) {
16139
16140                    // Great! So for each of these, we split.
16141                    // And we only recombine everything if the length
16142                    // of all the splits matches.
16143                    
16144                    // Preflight
16145                    var isValid = true;
16146                    var rawNames = Item[variable].split(/\s*;\s*/);
16147                    var rawMultiNames = {};
16148                    if (Item.multi && Item.multi._keys[variable]) {
16149                        for (var langTag in Item.multi._keys[variable]) {
16150                            rawMultiNames[langTag] = Item.multi._keys[variable][langTag].split(/\s*;\s*/);
16151                            if (rawMultiNames[langTag].length !== rawNames.length) {
16152                                isValid = false;
16153                                break;
16154                            }
16155                        }
16156                    }
16157                    if (!isValid) {
16158                        rawNames = [Item[variable]];
16159                        rawMultiNames = Item.multi._keys[variable];
16160                    }
16161                    for (var j = 0, jlen = rawNames.length; j < jlen; j++) {
16162                        var creatorParent = {
16163                            literal:rawNames[j],
16164                            multi:{
16165                                _key:{}
16166                            }
16167                        };
16168                        for (var langTag in rawMultiNames) {
16169                            var creatorChild = {
16170                                literal:rawMultiNames[langTag][j]
16171                            };
16172                            creatorParent.multi._key[langTag] = creatorChild;
16173                        }
16174                        rawNames[j] = creatorParent;
16175                    }
16176                    Item[variable] = rawNames;
16177                }
16178                if (this.strings.form === "short" && !Item[variable]) {
16179                    if (variable === "title") {
16180                        variable = "title-short";
16181                    } else if (variable === "container-title") {
16182                        variable = "container-title-short";
16183                    }
16184                }
16185                if (variable === "year-suffix") {
16186                    // year-suffix always signals that it produces output,
16187                    // even when it doesn't. This permits it to be used with
16188                    // the "no date" term inside a group used exclusively
16189                    // to control formatting.
16190                    output = true;
16191                    break;
16192                } else if (CSL.DATE_VARIABLES.indexOf(variable) > -1) {
16193                    if (state.opt.development_extensions.locator_date_and_revision && "locator-date" === variable) {
16194                        // If locator-date is set, it's valid.
16195                        output = true;
16196                        break;
16197                    }
16198                    if (Item[variable]) {
16199                        for (var key in Item[variable]) {
16200                            if (this.dateparts.indexOf(key) === -1 && "literal" !== key) {
16201                                continue;
16202                            }
16203                            if (Item[variable][key]) {
16204                                output = true;
16205                                break;
16206                            }
16207                        }
16208                        if (output) {
16209                            break;
16210                        }
16211                    }
16212                } else if ("locator" === variable) {
16213                    if (item && item.locator) {
16214                        output = true;
16215                    }
16216                    break;
16217                } else if ("locator-extra" === variable) {
16218                    if (item && item["locator-extra"]) {
16219                        output = true;
16220                    }
16221                    break;
16222                } else if (["citation-number","citation-label"].indexOf(variable) > -1) {
16223                    output = true;
16224                    break;
16225                } else if ("first-reference-note-number" === variable) {
16226                    if (item && item["first-reference-note-number"]) {
16227                        output = true;
16228                    }
16229                    break;
16230                } else if ("first-container-reference-note-number" === variable) {
16231                    if (item && item["first-container-reference-note-number"]) {
16232                        output = true;
16233                    }
16234                    break;
16235                } else if ("hereinafter" === variable) {
16236                    if (state.transform.abbrevs["default"].hereinafter[Item.id]
16237                        && state.sys.getAbbreviation
16238                        && Item.id) {
16239						
16240                        output = true;
16241                    }
16242                    break;
16243                } else if ("object" === typeof Item[variable]) {
16244                    break;
16245                } else if ("string" === typeof Item[variable] && Item[variable]) {
16246                    output = true;
16247                    break;
16248                } else if ("number" === typeof Item[variable]) {
16249                    output = true;
16250                    break;
16251                }
16252                if (output) {
16253                    break;
16254                }
16255            }
16256            //print("-- VAR: "+variable);
16257            //flag = state.tmp.group_context.tip;
16258            if (output) {
16259                for (var i=0,ilen=this.variables_real.length;i<ilen;i++) {
16260                    var variable = this.variables_real[i];
16261                    if (variable !== "citation-number" || state.tmp.area !== "bibliography") {
16262                        state.tmp.cite_renders_content = true;
16263                    }
16264                    //print("  setting [2] to true based on: " + arg);
16265                    state.tmp.group_context.tip.variable_success = true;
16266                    // For util_substitute.js, subsequent-author-substitute
16267                    if (state.tmp.can_substitute.value() 
16268                        && state.tmp.area === "bibliography"
16269                        && "string" === typeof Item[variable]) {
16270
16271                        state.tmp.name_node.top = state.output.current.value();
16272                        state.tmp.rendered_name.push(Item[variable]);
16273                    }
16274                }
16275                state.tmp.can_substitute.replace(false,  CSL.LITERAL);
16276            } else {
16277                //print("  setting [1] to true based on: " + arg);
16278                state.tmp.group_context.tip.variable_attempt = true;
16279            }
16280            //state.tmp.group_context.replace(flag);
16281        };
16282        this.execs.push(func);
16283    } else if (["if",  "else-if", "condition"].indexOf(this.name) > -1) {
16284        // Strip off any boolean prefix.
16285        // Now the conditionals.
16286        var maketest = function (variable) {
16287            return function(Item,item){
16288                var myitem = Item;
16289                if (item && ["locator", "locator-extra", "first-reference-note-number", "first-container-reference-note-number", "locator-date"].indexOf(variable) > -1) {
16290                    myitem = item;
16291                }
16292                // We don't run loadAbbreviation() here; it is run by the application-supplied
16293                // retrieveItem() if hereinafter functionality is to be used, so this key will
16294                // always exist in memory, possibly with a nil value.
16295                if (variable === "hereinafter" && state.sys.getAbbreviation && myitem.id) {
16296                    if (state.transform.abbrevs["default"].hereinafter[myitem.id]) {
16297                        return true;
16298                    }
16299                } else if (myitem[variable]) {
16300                    if ("number" === typeof myitem[variable] || "string" === typeof myitem[variable]) {
16301                        return true;
16302                    } else if ("object" === typeof myitem[variable]) {
16303                        //
16304                        // this will turn true only for hash objects
16305                        // that have at least one attribute, or for a
16306                        // non-zero-length list
16307                        //
16308                        for (var key in myitem[variable]) {
16309                            if (myitem[variable][key]) {
16310                                return true;
16311                            }
16312                        }
16313                    }
16314                }
16315                return false;
16316            };
16317        };
16318        for (var i=0,ilen=this.variables.length;i<ilen;i+=1) {
16319            this.tests.push(maketest(this.variables[i]));
16320        }
16321    }
16322};
16323
16324
16325CSL.Attributes["@page"] = function (state, arg) {
16326    if (!this.tests) {this.tests = []; };
16327    var trylabels = arg.replace("sub verbo", "sub-verbo");
16328    trylabels = trylabels.split(/\s+/);
16329    // Strip off any boolean prefix.
16330    var maketest = function (trylabel) {
16331        return function(Item) {
16332            var label;
16333            state.processNumber(false, Item, "page", Item.type);
16334            if (!state.tmp.shadow_numbers.page.label) {
16335                label = "page";
16336            } else if (state.tmp.shadow_numbers.page.label === "sub verbo") {
16337                label = "sub-verbo";
16338            } else {
16339                label = state.tmp.shadow_numbers.page.label;
16340            }
16341            if (state.tmp.shadow_numbers.page.values.length > 0) {
16342                if (state.tmp.shadow_numbers.page.values[0].gotosleepability) {
16343                    state.tmp.shadow_numbers.page.values[0].labelVisibility = false;
16344                }
16345            }
16346            if (trylabel === label) {
16347                return true;
16348            } else {
16349                return false;
16350            }
16351        };
16352    };
16353    for (var i=0,ilen=trylabels.length;i<ilen;i+=1) {
16354        this.tests.push(maketest(trylabels[i]));
16355    }
16356};
16357
16358
16359// a near duplicate of code above
16360CSL.Attributes["@number"] = function (state, arg) {
16361    if (!this.tests) {this.tests = []; };
16362    var trylabels = arg.split(/\s+/);
16363    var maketest = function(trylabel) {
16364        return function (Item) {
16365            var label;
16366            state.processNumber(false, Item, "number", Item.type);
16367            if (!state.tmp.shadow_numbers.number.label) {
16368                label = "number";
16369            } else {
16370                label = state.tmp.shadow_numbers.number.label;
16371            }
16372            if (trylabel === label) {
16373                return true;
16374            } else {
16375                return false;
16376            }
16377        };
16378    };
16379    for (var i=0,ilen=trylabels.length;i<ilen;i+=1) {
16380        this.tests.push(maketest(trylabels[i]));
16381    }
16382};
16383
16384CSL.Attributes["@jurisdiction"] = function (state, arg) {
16385    if (!this.tests) {this.tests = []; };
16386    var tryjurisdictions = arg.split(/\s+/);
16387    
16388    // This forces a match=any method, similar to @type
16389    var maketests = function (tryjurisdictions) {
16390        return function(Item) {
16391            if (!Item.jurisdiction) {
16392                return false;
16393            }
16394            var jurisdiction = Item.jurisdiction;
16395            for (var i=0,ilen=tryjurisdictions.length;i<ilen;i++) {
16396                if (jurisdiction === tryjurisdictions[i]) {
16397                    return true;
16398                }
16399            }
16400            return false;
16401        };
16402    };
16403    this.tests.push(maketests(tryjurisdictions));
16404};
16405
16406CSL.Attributes["@country"] = function (state, arg) {
16407    if (!this.tests) {this.tests = []; };
16408    var trycountries = arg.split(/\s+/);
16409    
16410    // This forces a match=any method, similar to @type
16411    var maketests = function (trycountries) {
16412        return function(Item) {
16413            if (!Item.country) {
16414                return false;
16415            }
16416            var country = Item.country;
16417            for (var i=0,ilen=trycountries.length;i<ilen;i++) {
16418                if (country === trycountries[i]) {
16419                    return true;
16420                }
16421            }
16422            return false;
16423        };
16424    };
16425    this.tests.push(maketests(trycountries));
16426};
16427
16428CSL.Attributes["@context"] = function (state, arg) {
16429    if (!this.tests) {this.tests = []; };
16430    var func = function () {
16431        if (["bibliography", "citation"].indexOf(arg) > -1) {
16432		    var area = state.tmp.area.slice(0, arg.length);
16433		    if (area === arg) {
16434			    return true;
16435		    }
16436		    return false;
16437        } else if ("alternative" === arg) {
16438            return !!state.tmp.abort_alternative;
16439        }
16440    };
16441    this.tests.push(func);
16442};
16443
16444CSL.Attributes["@has-year-only"] = function (state, arg) {
16445    if (!this.tests) {this.tests = []; };
16446    var trydates = arg.split(/\s+/);
16447    var maketest = function (trydate) {
16448        return function(Item) {
16449            var date = Item[trydate];
16450            if (!date || date.month || date.season) {
16451                return false;
16452            } else {
16453                return true;
16454            }
16455        };
16456    };
16457    for (var i=0,ilen=trydates.length;i<ilen;i+=1) {
16458        this.tests.push(maketest(trydates[i]));
16459    }
16460};
16461
16462CSL.Attributes["@has-to-month-or-season"] = function (state, arg) {
16463    if (!this.tests) {this.tests = []; };
16464    var trydates = arg.split(/\s+/);
16465    var maketest = function (trydate) {
16466        return function(Item) {
16467            var date = Item[trydate];
16468            if (!date || (!date.month && !date.season) || date.day) {
16469                return false;
16470            } else {
16471                return true;
16472            }
16473        };
16474    };
16475    for (var i=0,ilen=trydates.length;i<ilen;i+=1) {
16476        this.tests.push(maketest(trydates[i]));
16477    }
16478};
16479
16480CSL.Attributes["@has-day"] = function (state, arg) {
16481    if (!this.tests) {this.tests = []; };
16482    var trydates = arg.split(/\s+/);
16483    var maketest = function (trydate) {
16484        return function(Item) {
16485            var date = Item[trydate];
16486            if (!date || !date.day) {
16487                return false;
16488            } else {
16489                return true;
16490            }
16491        };
16492    };
16493    for (var i=0,ilen=trydates.length;i<ilen;i+=1) {
16494        this.tests.push(maketest(trydates[i]));
16495    }
16496};
16497
16498CSL.Attributes["@is-plural"] = function (state, arg) {
16499    if (!this.tests) {this.tests = []; };
16500    var func = function (Item) {
16501        var nameList = Item[arg];
16502        if (nameList && nameList.length) {
16503            var persons = 0;
16504            var institutions = 0;
16505            var last_is_person = false;
16506            for (var i = 0, ilen = nameList.length; i < ilen; i += 1) {
16507                if (state.opt.development_extensions.spoof_institutional_affiliations
16508                    && (nameList[i].literal || (nameList[i].isInstitution && nameList[i].family && !nameList[i].given))) {
16509                    institutions += 1;
16510                    last_is_person = false;
16511                } else {
16512                    persons += 1;
16513                    last_is_person = true;
16514                }
16515            }
16516            if (persons > 1) {
16517                return true;
16518            } else if (institutions > 1) {
16519                return true;
16520            } else if (institutions && last_is_person) {
16521                return true;
16522            }
16523        }
16524        return false;
16525    };
16526    this.tests.push(func);
16527};
16528
16529CSL.Attributes["@is-multiple"] = function (state, arg) {
16530    if (!this.tests) {this.tests = []; };
16531    var func = function (Item) {
16532        var val = ("" + Item[arg]);
16533        var lst = val.split(/(?:,\s|\s(?:tot\sen\smet|līdz|oraz|and|bis|έως|και|och|až|do|en|et|in|ir|ja|og|sa|to|un|und|és|și|i|u|y|à|e|a|и|-|–)\s|—|\&)/);
16534        if (lst.length > 1) {
16535            return true;
16536        }
16537        return false;
16538    };
16539    this.tests.push(func);
16540};
16541
16542
16543
16544
16545CSL.Attributes["@locale"] = function (state, arg) {
16546    if (!this.tests) {this.tests = []; };
16547    var ret, langspec, lang, lst, i, ilen;
16548    // Style default
16549    var locale_default = state.opt["default-locale"][0];
16550
16551    if (this.name === "layout") {
16552        // For layout
16553        this.locale_raw = arg;
16554        if (this.tokentype === CSL.START) {
16555            if (!state.opt.multi_layout) {
16556                state.opt.multi_layout = [];
16557            }
16558            var locale_data = [];
16559            // Register the primary locale in the set, and others that "map" to it, 
16560            // so that they can be used when generating sort keys. See node_sort.js.
16561            // Not idempotent. Only do this once.
16562            var locales = arg.split(/\s+/);
16563            var sort_locale = {};
16564            var localeMaster = CSL.localeResolve(locales[0], locale_default);
16565            locale_data.push(localeMaster);
16566            if (localeMaster.generic) {
16567                sort_locale[localeMaster.generic] = localeMaster.best;
16568            } else {
16569                sort_locale[localeMaster.best] = localeMaster.best;
16570            }
16571            for (var i=1,ilen=locales.length;i<ilen;i+=1) {
16572                var localeServant = CSL.localeResolve(locales[i], locale_default);
16573                locale_data.push(localeServant);
16574                if (localeServant.generic) {
16575                    sort_locale[localeServant.generic] = localeMaster.best;
16576                } else {
16577                    sort_locale[localeServant.best] = localeMaster.best;
16578                }
16579
16580            }
16581            state[state.build.area].opt.sort_locales.push(sort_locale);
16582            state.opt.multi_layout.push(locale_data);
16583        }
16584        state.opt.has_layout_locale = true;
16585    } else {
16586        // For if and if-else
16587
16588        // Split argument
16589        lst = arg.split(/\s+/);
16590
16591        // Expand each list element
16592        var locale_bares = [];
16593        for (i = 0, ilen = lst.length; i < ilen; i += 1) {
16594            // Parse out language string
16595            lang = lst[i];
16596        
16597            // Analyze the locale
16598            langspec = CSL.localeResolve(lang, locale_default);
16599            if (lst[i].length === 2) {
16600                // For fallback
16601                locale_bares.push(langspec.bare);
16602            }
16603            // Load the locale terms etc.
16604            // (second argument causes immediate return if locale already exists)
16605            state.localeConfigure(langspec, true);
16606            
16607            // Replace string with locale spec object
16608            lst[i] = langspec;
16609        }
16610        // Locales to test
16611        var locale_list = lst.slice();
16612
16613        // check for variable value
16614        // Closure probably not necessary here.
16615        var maketest = function (locale_list, locale_default,locale_bares) {
16616            return function (Item) {
16617                var res;
16618                ret = [];
16619                res = false;
16620                var langspec = false;
16621
16622                var lang;
16623                if (!Item.language) {
16624                    lang = locale_default;
16625                } else {
16626                    lang = Item.language;
16627                }
16628                langspec = CSL.localeResolve(lang, locale_default);
16629                for (i = 0, ilen = locale_list.length; i < ilen; i += 1) {
16630                    if (langspec.best === locale_list[i].best) {
16631                        state.tmp.condition_lang_counter_arr.push(state.tmp.condition_counter);
16632                        state.tmp.condition_lang_val_arr.push(state.opt.lang);
16633                        state.opt.lang = locale_list[0].best;
16634                        res = true;
16635                        break;
16636                    }
16637                }
16638                if (!res && locale_bares.indexOf(langspec.bare) > -1) {
16639                    state.tmp.condition_lang_counter_arr.push(state.tmp.condition_counter);
16640                    state.tmp.condition_lang_val_arr.push(state.opt.lang);
16641                    state.opt.lang = locale_list[0].best;
16642                    res = true;
16643                }
16644                return res;
16645            };
16646        };
16647        this.tests.push(maketest(locale_list,locale_default,locale_bares));
16648    }
16649};
16650
16651CSL.Attributes["@alternative-node-internal"] = function (state) {
16652    if (!this.tests) {this.tests = []; };
16653    var maketest = function () {
16654        return function() {
16655            return !state.tmp.abort_alternative;
16656        };
16657    };
16658    var me = this;
16659    this.tests.push(maketest(me));
16660};
16661
16662CSL.Attributes["@locale-internal"] = function (state, arg) {
16663    if (!this.tests) {this.tests = []; };
16664    var langspec, lang, lst, i, ilen;
16665        // For if and if-else
16666
16667        // Split argument
16668        lst = arg.split(/\s+/);
16669
16670        // Expand each list element
16671        this.locale_bares = [];
16672        for (i = 0, ilen = lst.length; i < ilen; i += 1) {
16673            // Parse out language string
16674            lang = lst[i];
16675        
16676            // Analyze the locale
16677            langspec = CSL.localeResolve(lang, state.opt["default-locale"][0]);
16678            if (lst[i].length === 2) {
16679                // For fallback
16680                this.locale_bares.push(langspec.bare);
16681            }
16682            // Load the locale terms etc.
16683            state.localeConfigure(langspec);
16684            
16685            // Replace string with locale spec object
16686            lst[i] = langspec;
16687        }
16688        // Set locale tag on node
16689        this.locale_default = state.opt["default-locale"][0];
16690        // The locale to set on node children if match is successful
16691        this.locale = lst[0].best;
16692        // Locales to test
16693        this.locale_list = lst.slice();
16694        
16695        // check for variable value
16696        // Closure probably not necessary here.
16697        var maketest = function (me) {
16698            return function (Item) {
16699                var ret, res;
16700                ret = [];
16701                res = false;
16702                var langspec = false;
16703                if (Item.language) {
16704                    lang = Item.language;
16705                    langspec = CSL.localeResolve(lang, state.opt["default-locale"][0]);
16706                    if (langspec.best === state.opt["default-locale"][0]) {
16707                        langspec = false;
16708                    }
16709                }
16710                if (langspec) {
16711                    // We attempt to match a specific locale from the
16712                    // list of parameters.  If that fails, we fall back
16713                    // to the base locale of the first element.  The
16714                    // locale applied is always the first local 
16715                    // in the list of parameters (or base locale, for a 
16716                    // single two-character language code) 
16717                    for (i = 0, ilen = me.locale_list.length; i < ilen; i += 1) {
16718                        if (langspec.best === me.locale_list[i].best) {
16719                            state.opt.lang = me.locale;
16720                            state.tmp.last_cite_locale = me.locale;
16721                            // Set empty group open tag with locale set marker
16722                            state.output.openLevel("empty");
16723                            state.output.current.value().new_locale = me.locale;
16724                            res = true;
16725                            break;
16726                        }
16727                    }
16728                    if (!res && me.locale_bares.indexOf(langspec.bare) > -1) {
16729                        state.opt.lang = me.locale;
16730                        state.tmp.last_cite_locale = me.locale;
16731                        // Set empty group open tag with locale set marker
16732                        state.output.openLevel("empty");
16733                        state.output.current.value().new_locale = me.locale;
16734                        res = true;
16735                    }
16736                }
16737                return res;
16738            };
16739        };
16740        var me = this;
16741        this.tests.push(maketest(me));
16742};
16743
16744
16745CSL.Attributes["@court-class"] = function (state, arg) {
16746    if (!this.tests) {this.tests = []; };
16747	var tryclasses = arg.split(/\s+/);
16748    var maketest = function (tryclass) {
16749        return function(Item) {
16750            var cls = CSL.GET_COURT_CLASS(state, Item);
16751            if (cls === tryclass) {
16752                return true;
16753            } else {
16754                return false;
16755            }
16756        };
16757    };
16758    for (var i=0,ilen=tryclasses.length; i<ilen; i++) {
16759        this.tests.push(maketest(tryclasses[i]));
16760    }
16761};
16762
16763CSL.Attributes["@container-multiple"] = function (state, arg) {
16764    if (!this.tests) {this.tests = []; };
16765	var retval = "true" === arg ? true : false;
16766    var maketest = function (retval) {
16767        return function(Item) {
16768            if (!state.tmp.container_item_count[Item.container_id]) {
16769                return !retval;
16770            } else if (state.tmp.container_item_count[Item.container_id] > 1) {
16771                return retval;
16772            }
16773            return !retval;
16774        };
16775    };
16776    this.tests.push(maketest(retval));
16777};
16778
16779CSL.Attributes["@container-subsequent"] = function (state, arg) {
16780    if (!this.tests) {this.tests = []; };
16781	var retval = "true" === arg ? true : false;
16782    var maketest = function (retval) {
16783        return function(Item) {
16784            if (state.tmp.container_item_pos[Item.container_id] > 1) {
16785                return retval;
16786            }
16787            return !retval;
16788        };
16789    };
16790    this.tests.push(maketest(retval));
16791};
16792
16793CSL.Attributes["@has-subunit"] = function (state, arg) {
16794    if (!this.tests) {this.tests = []; };
16795    var maketest = function(namevar) {
16796        return function (Item) {
16797            var subunit_count = 0;
16798            for (var i in Item[namevar]) {
16799                var name = Item[namevar][i];
16800                if (!name.given) {
16801                    var institution = name.literal ? name.literal : name.family;
16802                    var length = institution.split("|").length;
16803                    if (subunit_count === 0 || length < subunit_count) {
16804                        subunit_count = length;
16805                    }
16806                }
16807            }
16808            return (subunit_count > 1);
16809        };
16810    };
16811    this.tests.push(maketest(arg));
16812}
16813
16814CSL.Attributes["@cite-form"] = function (state, arg) {
16815    if (!this.tests) {this.tests = []; };
16816    var maketest = function(citeForm) {
16817        return function (Item) {
16818            if (Item["cite-form"] === citeForm) {
16819                return true;
16820            }
16821            return false;
16822        };
16823    };
16824    this.tests.push(maketest(arg));
16825}
16826
16827CSL.Attributes["@disable-duplicate-year-suppression"] = function (state, arg) {
16828	state.opt.disable_duplicate_year_suppression = arg.split(/\s+/);
16829}
16830
16831CSL.Attributes["@consolidate-containers"] = function (state, arg) {
16832    CSL.Attributes["@track-containers"](state, arg);
16833    var args = arg.split(/\s+/);
16834    state.bibliography.opt.consolidate_containers = args;
16835}
16836
16837CSL.Attributes["@track-containers"] = function (state, arg) {
16838    var args = arg.split(/\s+/);
16839    if (!state.bibliography.opt.track_container_items) {
16840        state.bibliography.opt.track_container_items = [];
16841    }
16842    if (!state.bibliography.opt.consolidate_containers) {
16843        state.bibliography.opt.consolidate_containers = [];
16844    }
16845    state.bibliography.opt.track_container_items = state.bibliography.opt.track_container_items.concat(args);
16846}
16847
16848// These are not evaluated as conditions immediately: they only
16849// set parameters that are picked up during processing.
16850CSL.Attributes["@parallel-first"] = function (state, arg) {
16851    state.opt.parallel.enable = true;
16852    var vars = arg.split(/\s+/);
16853    if (!state.opt.track_repeat) {
16854        state.opt.track_repeat = {};
16855    }
16856    this.parallel_first = {};
16857    for (var i in vars) {
16858        var v = vars[i];
16859        this.parallel_first[v] = true;
16860        state.opt.track_repeat[v] = true;
16861    }
16862};
16863CSL.Attributes["@parallel-last"] = function (state, arg) {
16864    state.opt.parallel.enable = true;
16865    var vars = arg.split(/\s+/);
16866    if (!state.opt.track_repeat) {
16867        state.opt.track_repeat = {};
16868    }
16869    this.parallel_last = {};
16870    for (var i in vars) {
16871        var v = vars[i];
16872        this.parallel_last[v] = true;
16873        state.opt.track_repeat[v] = true;
16874    }
16875};
16876CSL.Attributes["@parallel-last-to-first"] = function (state, arg) {
16877    state.opt.parallel.enable = true;
16878    var vars = arg.split(/\s+/);
16879    this.parallel_last_to_first = {};
16880    for (var i=0,ilen=vars.length;i<ilen;i++) {
16881        this.parallel_last_to_first[vars[i]] = true;
16882    }
16883};
16884CSL.Attributes["@parallel-delimiter-override"] = function (state, arg) {
16885    state.opt.parallel.enable = true;
16886    this.strings.set_parallel_delimiter_override = arg;
16887};
16888CSL.Attributes["@parallel-delimiter-override-on-suppress"] = function (state, arg) {
16889    state.opt.parallel.enable = true;
16890    this.strings.set_parallel_delimiter_override_on_suppress = arg;
16891};
16892CSL.Attributes["@no-repeat"] = function (state, arg) {
16893    state.opt.parallel.enable = true;
16894    var vars = arg.split(/\s+/);
16895    if (!state.opt.track_repeat) {
16896        state.opt.track_repeat = {};
16897    }
16898    this.non_parallel = {};
16899    for (var i in vars) {
16900        var v = vars[i];
16901        this.non_parallel[v] = true;
16902        state.opt.track_repeat[v] = true;
16903    }
16904};
16905
16906CSL.Attributes["@require"] = function (state, arg) {
16907    state.opt.use_context_condition = true;
16908    this.strings.require = arg;
16909
16910    // Introduced to constrain rendering of the group with a
16911    // requirement that it either render an alpha term via cs:label or
16912    // cs:text at least once, or render without any label. That
16913    // behaviour is invoked with "label-empty-or-alpha" as arg.
16914
16915    // This attribute is a complement to @label-form and modular
16916    // jurisdiction support, as it makes macros that adapt to shifting
16917    // local term definitions possible.
16918};
16919
16920CSL.Attributes["@reject"] = function (state, arg) {
16921    state.opt.use_context_condition = true;
16922    this.strings.reject = arg;
16923
16924    // Introduced to constrain rendering of the group with a
16925    // requirement that it render some label via cs:label or cs:text,
16926    // and that it NOT be alpha. That behaviour is invoked with
16927    // "label-empty-or-alpha" as arg.
16928
16929    // This attribute is a complement to @label-form and modular
16930    // jurisdiction support, as it makes macros that adapt to shifting
16931    // local term definitions possible.
16932};
16933
16934CSL.Attributes["@require-comma-on-symbol"] = function (state, arg) {
16935    state.opt.require_comma_on_symbol = arg;
16936}
16937
16938CSL.Attributes["@gender"] = function (state, arg) {
16939    this.gender = arg;
16940};
16941
16942CSL.Attributes["@cslid"] = function (state, arg) {
16943    // @cslid is a noop
16944    // The value set on this attribute is used to
16945    // generate reverse lookup wrappers on output when 
16946    // this.development_extensions.csl_reverse_lookup_support is
16947    // set to true in state.js (there is no runtime option,
16948    // it must be set in state.js)
16949    //
16950    // See the @showid method in the html output
16951    // section of formats.js for the function that
16952    // renders the wrappers.
16953    this.cslid = parseInt(arg, 10);
16954};
16955
16956CSL.Attributes["@capitalize-if-first"] = function (state, arg) {
16957    this.strings.capitalize_if_first_override = arg;
16958};
16959
16960CSL.Attributes["@label-capitalize-if-first"] = function (state, arg) {
16961    this.strings.label_capitalize_if_first_override = arg;
16962};
16963
16964CSL.Attributes["@label-form"] = function (state, arg) {
16965    this.strings.label_form_override = arg;
16966};
16967
16968CSL.Attributes["@part-separator"] = function (state, arg) {
16969    this.strings["part-separator"] = arg;
16970};
16971
16972CSL.Attributes["@leading-noise-words"] = function (state, arg) {
16973    this["leading-noise-words"] = arg;
16974};
16975
16976CSL.Attributes["@name-never-short"] = function (state, arg) {
16977    this["name-never-short"] = arg;
16978};
16979
16980CSL.Attributes["@class"] = function (state, arg) {
16981    state.opt["class"] = arg;
16982};
16983
16984CSL.Attributes["@version"] = function (state, arg) {
16985    state.opt.version = arg;
16986};
16987
16988/**
16989 * Store the value attribute on the token.
16990 * @name CSL.Attributes.@value
16991 * @function
16992 */
16993CSL.Attributes["@value"] = function (state, arg) {
16994    this.strings.value = arg;
16995};
16996
16997
16998/**
16999 * Store the name attribute (of a macro or term node)
17000 * on the state object.
17001 * <p>For reference when the closing node of a macro
17002 * or locale definition is encountered.</p>
17003 * @name CSL.Attributes.@name
17004 * @function
17005 */
17006CSL.Attributes["@name"] = function (state, arg) {
17007    this.strings.name = arg;
17008};
17009
17010/**
17011 * Store the form attribute (of a term node) on the state object.
17012 * <p>For reference when the closing node of a macro
17013 * or locale definition is encountered.</p>
17014 * @name CSL.Attributes.@form
17015 * @function
17016 */
17017CSL.Attributes["@form"] = function (state, arg) {
17018    this.strings.form = arg;
17019};
17020
17021CSL.Attributes["@date-parts"] = function (state, arg) {
17022    this.strings["date-parts"] = arg;
17023};
17024
17025CSL.Attributes["@range-delimiter"] = function (state, arg) {
17026    this.strings["range-delimiter"] = arg;
17027};
17028
17029/**
17030 * Store macro tokens in a buffer on the state object.
17031 * <p>For reference when the enclosing text token is
17032 * processed.</p>
17033 * @name CSL.Attributes.@macro
17034 * @function
17035 */
17036CSL.Attributes["@macro"] = function (state, arg) {
17037    this.postponed_macro = arg;
17038};
17039
17040/*
17041 * CSL.Attributes["@prefer-jurisdiction"] = function (state, arg) {
17042 *    this.prefer_jurisdiction = true;
17043 * };
17044 */
17045
17046CSL.Attributes["@term"] = function (state, arg) {
17047    if (arg === "sub verbo") {
17048        this.strings.term = "sub-verbo";
17049    } else {
17050        this.strings.term = arg;
17051    }
17052};
17053
17054
17055/*
17056 * Ignore xmlns attribute.
17057 * <p>This should always be <p>http://purl.org/net/xbiblio/csl</code>
17058 * anyway.  At least for the present we will blindly assume
17059 * that it is.</p>
17060 * @name CSL.Attributes.@xmlns
17061 * @function
17062 */
17063CSL.Attributes["@xmlns"] = function () {};
17064
17065
17066/*
17067 * Store language attribute to a buffer field.
17068 * <p>Will be placed in the appropriate location
17069 * when the element is processed.</p>
17070 * @name CSL.Attributes.@lang
17071 * @function
17072 */
17073CSL.Attributes["@lang"] = function (state, arg) {
17074    if (arg) {
17075        state.build.lang = arg;
17076    }
17077};
17078
17079
17080// Used as a flag during dates processing
17081CSL.Attributes["@lingo"] = function () {};
17082
17083// Used as a flag during dates processing
17084CSL.Attributes["@macro-has-date"] = function () {
17085    this["macro-has-date"] = true;
17086};
17087
17088/*
17089 * Store suffix string on token.
17090 * @name CSL.Attributes.@suffix
17091 * @function
17092 */
17093CSL.Attributes["@suffix"] = function (state, arg) {
17094    this.strings.suffix = arg;
17095};
17096
17097
17098/*
17099 * Store prefix string on token.
17100 * @name CSL.Attributes.@prefix
17101 * @function
17102 */
17103CSL.Attributes["@prefix"] = function (state, arg) {
17104    this.strings.prefix = arg;
17105};
17106
17107
17108/*
17109 * Store delimiter string on token.
17110 * @name CSL.Attributes.@delimiter
17111 * @function
17112 */
17113CSL.Attributes["@delimiter"] = function (state, arg) {
17114    this.strings.delimiter = arg;
17115};
17116
17117
17118/*
17119 * Store match evaluator on token.
17120 */
17121CSL.Attributes["@match"] = function (state, arg) {
17122    this.match = arg;
17123};
17124
17125
17126CSL.Attributes["@names-min"] = function (state, arg) {
17127    var val = parseInt(arg, 10);
17128    if (state[state.build.area].opt.max_number_of_names < val) {
17129        state[state.build.area].opt.max_number_of_names = val;
17130    }
17131    this.strings["et-al-min"] = val;
17132};
17133
17134CSL.Attributes["@names-use-first"] = function (state, arg) {
17135    this.strings["et-al-use-first"] = parseInt(arg, 10);
17136};
17137
17138CSL.Attributes["@names-use-last"] = function (state, arg) {
17139    if (arg === "true") {
17140        this.strings["et-al-use-last"] = true;
17141    } else {
17142        this.strings["et-al-use-last"] = false;
17143    }
17144};
17145
17146CSL.Attributes["@sort"] = function (state, arg) {
17147    if (arg === "descending") {
17148        this.strings.sort_direction = CSL.DESCENDING;
17149    }
17150};
17151
17152CSL.Attributes["@plural"] = function (state, arg) {
17153    // Accepted values of plural attribute differ on cs:text
17154    // and cs:label nodes.
17155    if ("always" === arg || "true" === arg) {
17156        this.strings.plural = 1;
17157    } else if ("never" === arg || "false" === arg) {
17158        this.strings.plural = 0;
17159    } else if ("contextual" === arg) {
17160        this.strings.plural = false;
17161    }
17162};
17163
17164CSL.Attributes["@has-publisher-and-publisher-place"] = function () {
17165    this.strings["has-publisher-and-publisher-place"] = true;
17166};
17167
17168CSL.Attributes["@publisher-delimiter-precedes-last"] = function (state, arg) {
17169    this.strings["publisher-delimiter-precedes-last"] = arg;
17170};
17171
17172CSL.Attributes["@publisher-delimiter"] = function (state, arg) {
17173    this.strings["publisher-delimiter"] = arg;
17174};
17175
17176CSL.Attributes["@publisher-and"] = function (state, arg) {
17177    this.strings["publisher-and"] = arg;
17178};
17179
17180CSL.Attributes["@givenname-disambiguation-rule"] = function (state, arg) {
17181    if (CSL.GIVENNAME_DISAMBIGUATION_RULES.indexOf(arg) > -1) {
17182        state.citation.opt["givenname-disambiguation-rule"] = arg;
17183    }
17184};
17185
17186CSL.Attributes["@collapse"] = function (state, arg) {
17187    // only one collapse value will be honoured.
17188    if (arg) {
17189        state[this.name].opt.collapse = arg;
17190    }
17191};
17192
17193CSL.Attributes["@cite-group-delimiter"] = function (state, arg) {
17194    if (arg) {
17195        state[state.tmp.area].opt.cite_group_delimiter = arg;
17196    }
17197};
17198
17199
17200
17201CSL.Attributes["@names-delimiter"] = function (state, arg) {
17202    state.setOpt(this, "names-delimiter", arg);
17203};
17204
17205CSL.Attributes["@name-form"] = function (state, arg) {
17206    state.setOpt(this, "name-form", arg);
17207};
17208
17209CSL.Attributes["@subgroup-delimiter"] = function (state, arg) {
17210    this.strings["subgroup-delimiter"] = arg;
17211};
17212
17213CSL.Attributes["@subgroup-delimiter-precedes-last"] = function (state, arg) {
17214    this.strings["subgroup-delimiter-precedes-last"] = arg;
17215};
17216
17217
17218CSL.Attributes["@name-delimiter"] = function (state, arg) {
17219    state.setOpt(this, "name-delimiter", arg);
17220};
17221
17222CSL.Attributes["@et-al-min"] = function (state, arg) {
17223    var val = parseInt(arg, 10);
17224    if (state[state.build.area].opt.max_number_of_names < val) {
17225        state[state.build.area].opt.max_number_of_names = val;
17226    }
17227    state.setOpt(this, "et-al-min", val);
17228};
17229
17230CSL.Attributes["@et-al-use-first"] = function (state, arg) {
17231    state.setOpt(this, "et-al-use-first", parseInt(arg, 10));
17232};
17233
17234CSL.Attributes["@et-al-use-last"] = function (state, arg) {
17235    if (arg === "true") {
17236        state.setOpt(this, "et-al-use-last", true);
17237    } else {
17238        state.setOpt(this, "et-al-use-last", false);
17239    }
17240};
17241
17242CSL.Attributes["@et-al-subsequent-min"] = function (state, arg) {
17243    var val = parseInt(arg, 10);
17244    if (state[state.build.area].opt.max_number_of_names < val) {
17245        state[state.build.area].opt.max_number_of_names = val;
17246    }
17247    state.setOpt(this, "et-al-subsequent-min", val);
17248};
17249
17250CSL.Attributes["@et-al-subsequent-use-first"] = function (state, arg) {
17251    state.setOpt(this, "et-al-subsequent-use-first", parseInt(arg, 10));
17252};
17253
17254CSL.Attributes["@suppress-min"] = function (state, arg) {
17255    this.strings["suppress-min"] = parseInt(arg, 10);
17256};
17257
17258CSL.Attributes["@suppress-max"] = function (state, arg) {
17259    this.strings["suppress-max"] = parseInt(arg, 10);
17260};
17261
17262
17263CSL.Attributes["@and"] = function (state, arg) {
17264    state.setOpt(this, "and", arg);
17265};
17266
17267CSL.Attributes["@delimiter-precedes-last"] = function (state, arg) {
17268    state.setOpt(this, "delimiter-precedes-last", arg);
17269};
17270
17271CSL.Attributes["@delimiter-precedes-et-al"] = function (state, arg) {
17272    state.setOpt(this, "delimiter-precedes-et-al", arg);
17273};
17274
17275CSL.Attributes["@initialize-with"] = function (state, arg) {
17276    state.setOpt(this, "initialize-with", arg);
17277};
17278
17279CSL.Attributes["@initialize"] = function (state, arg) {
17280    if (arg === "false") {
17281        state.setOpt(this, "initialize", false);
17282    }
17283};
17284
17285CSL.Attributes["@name-as-reverse-order"] = function (state, arg) {
17286    this["name-as-reverse-order"] = arg;
17287};
17288
17289CSL.Attributes["@name-as-sort-order"] = function (state, arg) {
17290    if (this.name === "style-options") {
17291        this["name-as-sort-order"] = arg;
17292    } else {
17293        state.setOpt(this, "name-as-sort-order", arg);
17294    }
17295};
17296
17297CSL.Attributes["@sort-separator"] = function (state, arg) {
17298    state.setOpt(this, "sort-separator", arg);
17299};
17300
17301CSL.Attributes["@require-match"] = function (state, arg) {
17302    if (arg === "true") {
17303        this.requireMatch = true;
17304    }
17305};
17306
17307CSL.Attributes["@exclude-types"] = function (state, arg) {
17308    state.bibliography.opt.exclude_types = arg.split(/\s+/);
17309};
17310
17311CSL.Attributes["@exclude-with-fields"] = function (state, arg) {
17312    state.bibliography.opt.exclude_with_fields = arg.split(/\s+/);
17313};
17314
17315
17316CSL.Attributes["@year-suffix-delimiter"] = function (state, arg) {
17317    state[this.name].opt["year-suffix-delimiter"] = arg;
17318};
17319
17320CSL.Attributes["@after-collapse-delimiter"] = function (state, arg) {
17321    state[this.name].opt["after-collapse-delimiter"] = arg;
17322};
17323
17324CSL.Attributes["@subsequent-author-substitute"] = function (state, arg) {
17325    state[this.name].opt["subsequent-author-substitute"] = arg;
17326};
17327
17328CSL.Attributes["@subsequent-author-substitute-rule"] = function (state, arg) {
17329    state[this.name].opt["subsequent-author-substitute-rule"] = arg;
17330};
17331
17332CSL.Attributes["@disambiguate-add-names"] = function (state, arg) {
17333    if (arg === "true") {
17334        state.opt["disambiguate-add-names"] = true;
17335    }
17336};
17337
17338CSL.Attributes["@disambiguate-add-givenname"] = function (state, arg) {
17339    if (arg === "true") {
17340        state.opt["disambiguate-add-givenname"] = true;
17341    }
17342};
17343
17344CSL.Attributes["@disambiguate-add-year-suffix"] = function (state, arg) {
17345    if (arg === "true" && state.opt.xclass !== "numeric") {
17346        state.opt["disambiguate-add-year-suffix"] = true;
17347    }
17348};
17349
17350
17351CSL.Attributes["@second-field-align"] = function (state, arg) {
17352    if (arg === "flush" || arg === "margin") {
17353        state[this.name].opt["second-field-align"] = arg;
17354    }
17355};
17356
17357
17358CSL.Attributes["@hanging-indent"] = function (state, arg) {
17359    if (arg === "true") {
17360        if (state.opt.development_extensions.hanging_indent_legacy_number) {
17361            state[this.name].opt.hangingindent = 2;
17362	    } else {
17363            state[this.name].opt.hangingindent = true;
17364	    }
17365    }
17366};
17367
17368
17369CSL.Attributes["@line-spacing"] = function (state, arg) {
17370    if (arg && arg.match(/^[.0-9]+$/)) {
17371        state[this.name].opt["line-spacing"] = parseFloat(arg, 10);
17372    }
17373};
17374
17375
17376CSL.Attributes["@entry-spacing"] = function (state, arg) {
17377    if (arg && arg.match(/^[.0-9]+$/)) {
17378        state[this.name].opt["entry-spacing"] = parseFloat(arg, 10);
17379    }
17380};
17381
17382
17383CSL.Attributes["@near-note-distance"] = function (state, arg) {
17384    state[this.name].opt["near-note-distance"] = parseInt(arg, 10);
17385};
17386
17387CSL.Attributes["@substring"] = function (state, arg) {
17388    this.substring = parseInt(arg, 10);
17389};
17390
17391CSL.Attributes["@text-case"] = function (state, arg) {
17392    var func = function (state, Item) {
17393        if (arg === "normal") {
17394            this.text_case_normal = true;
17395        } else {
17396            this.strings["text-case"] = arg;
17397            if (arg === "title") {
17398                if (Item.jurisdiction) {
17399                    this.strings["text-case"] = "passthrough";
17400                }
17401            }
17402        }
17403    };
17404    this.execs.push(func);
17405};
17406
17407
17408CSL.Attributes["@page-range-format"] = function (state, arg) {
17409    state.opt["page-range-format"] = arg;
17410};
17411
17412
17413CSL.Attributes["@year-range-format"] = function (state, arg) {
17414    state.opt["year-range-format"] = arg;
17415};
17416
17417
17418CSL.Attributes["@default-locale"] = function (state, arg) {
17419    if (this.name === 'style') {
17420        var lst, len, pos, m, ret;
17421        //
17422        // Workaround for Internet Exploder 6 (doesn't recognize
17423        // groups in str.split(/something(braced-group)something/)
17424        //
17425        var m = arg.match(/-x-(sort|translit|translat)-/g);
17426        if (m) {
17427            for (pos = 0, len = m.length; pos < len; pos += 1) {
17428                m[pos] = m[pos].replace(/^-x-/, "").replace(/-$/, "");
17429            }
17430        }
17431        lst = arg.split(/-x-(?:sort|translit|translat)-/);
17432        ret = [lst[0]];
17433        for (pos = 1, len = lst.length; pos < len; pos += 1) {
17434            ret.push(m[pos - 1]);
17435            ret.push(lst[pos]);
17436        }
17437        lst = ret.slice();
17438        len = lst.length;
17439        for (pos = 1; pos < len; pos += 2) {
17440            state.opt[("locale-" + lst[pos])].push(lst[(pos + 1)].replace(/^\s*/g, "").replace(/\s*$/g, ""));
17441        }
17442        if (lst.length) {
17443            state.opt["default-locale"] = lst.slice(0, 1);
17444        } else {
17445            state.opt["default-locale"] = ["en"];
17446        }
17447    } else if (arg === "true") {
17448        this.default_locale = true;
17449    }
17450};
17451
17452CSL.Attributes["@default-locale-sort"] = function (state, arg) {
17453    state.opt["default-locale-sort"] = arg;
17454};
17455
17456CSL.Attributes["@demote-non-dropping-particle"] = function (state, arg) {
17457    state.opt["demote-non-dropping-particle"] = arg;
17458};
17459
17460CSL.Attributes["@initialize-with-hyphen"] = function (state, arg) {
17461    if (arg === "false") {
17462        state.opt["initialize-with-hyphen"] = false;
17463    }
17464};
17465
17466CSL.Attributes["@institution-parts"] = function (state, arg) {
17467    this.strings["institution-parts"] = arg;
17468};
17469
17470CSL.Attributes["@if-short"] = function (state, arg) {
17471    if (arg === "true") {
17472        this.strings["if-short"] = true;
17473    }
17474};
17475
17476CSL.Attributes["@substitute-use-first"] = function (state, arg) {
17477    this.strings["substitute-use-first"] = parseInt(arg, 10);
17478};
17479
17480CSL.Attributes["@use-first"] = function (state, arg) {
17481    this.strings["use-first"] = parseInt(arg, 10);
17482};
17483
17484CSL.Attributes["@use-last"] = function (state, arg) {
17485    this.strings["use-last"] = parseInt(arg, 10);
17486};
17487
17488CSL.Attributes["@stop-first"] = function (state, arg) {
17489    this.strings["stop-first"] = parseInt(arg, 10);
17490};
17491
17492CSL.Attributes["@stop-last"] = function (state, arg) {
17493    this.strings["stop-last"] = parseInt(arg, 10) * -1;
17494};
17495
17496
17497CSL.Attributes["@reverse-order"] = function (state, arg) {
17498    if ("true" === arg) {
17499        this.strings["reverse-order"] = true;
17500    }
17501};
17502
17503CSL.Attributes["@display"] = function (state, arg) {
17504    if (state.bibliography.tokens.length === 2) {
17505        state.opt.using_display = true;
17506    }
17507    this.strings.cls = arg;
17508};
17509
17510
17511/*global CSL: true */
17512
17513
17514/**
17515 * String stack object.
17516 * <p>Numerous string stacks are used to track nested
17517 * parameters at runtime.  This class provides methods
17518 * that remove some of the aggravation of managing
17519 * them.</p>
17520 * @class
17521 */
17522CSL.Stack = function (val, literal) {
17523    this.mystack = [];
17524    if (literal || val) {
17525        this.mystack.push(val);
17526    }
17527    this.tip = this.mystack[0];
17528};
17529
17530/**
17531 * Push a value onto the stack.
17532 * <p>This just does what it says.</p>
17533 */
17534CSL.Stack.prototype.push = function (val, literal) {
17535    if (literal || val) {
17536        this.mystack.push(val);
17537    } else {
17538        this.mystack.push("");
17539    }
17540    this.tip = this.mystack[this.mystack.length - 1];
17541};
17542
17543/**
17544 * Clear the stack
17545 */
17546CSL.Stack.prototype.clear = function () {
17547    this.mystack = [];
17548    this.tip = {};
17549};
17550
17551/**
17552 * Replace the top value on the stack.
17553 * <p>This removes some ugly syntax from the
17554 * main code.</p>
17555 */
17556CSL.Stack.prototype.replace = function (val, literal) {
17557    //
17558    // safety fix after a bug was chased down.  Rhino
17559    // JS will process a negative index without error (!).
17560    if (this.mystack.length === 0) {
17561        CSL.error("Internal CSL processor error: attempt to replace nonexistent stack item with " + val);
17562    }
17563    if (literal || val) {
17564        this.mystack[(this.mystack.length - 1)] = val;
17565    } else {
17566        this.mystack[(this.mystack.length - 1)] = "";
17567    }
17568    this.tip = this.mystack[this.mystack.length - 1];
17569};
17570
17571
17572/**
17573 * Remove the top value from the stack.
17574 * <p>Just does what it says.</p>
17575 */
17576CSL.Stack.prototype.pop = function () {
17577    var ret = this.mystack.pop();
17578    if (this.mystack.length) {
17579        this.tip = this.mystack[this.mystack.length - 1];
17580    } else {
17581        this.tip = {};
17582    }
17583    return ret;
17584};
17585
17586
17587/**
17588 * Return the top value on the stack.
17589 * <p>Removes a little hideous complication from
17590 * the main code.</p>
17591 */
17592CSL.Stack.prototype.value = function () {
17593    return this.mystack.slice(-1)[0];
17594};
17595
17596
17597/**
17598 * Return length (depth) of stack.
17599 * <p>Used to identify if there is content to
17600 * be handled on the stack</p>
17601 */
17602CSL.Stack.prototype.length = function () {
17603    return this.mystack.length;
17604};
17605
17606/*global CSL: true */
17607
17608/**
17609 * Initializes the parallel cite tracking arrays
17610 */
17611CSL.Parallel = function (state) {
17612    this.state = state;
17613};
17614
17615CSL.Parallel.prototype.StartCitation = function (sortedItems, out) {
17616    // This array carries the repeat markers used in rendering the cite.
17617    this.state.tmp.suppress_repeats = [];
17618    if (sortedItems.length < 2) return;
17619    var idxEnd = 0;
17620    var parallelMatchList = false;
17621    var siblingRanges = [];
17622
17623    for (var i=0,ilen=sortedItems.length-1;i<ilen;i++) {
17624        var currItem = sortedItems[i][0];
17625        var nextItem = sortedItems[i+1][0];
17626        var freshMatchList = false;
17627        var info = {};
17628        if (sortedItems[i][0].seeAlso && sortedItems[i][0].seeAlso.length > 0 && !parallelMatchList) {
17629            freshMatchList = true;
17630            parallelMatchList = [sortedItems[i][0].id].concat(sortedItems[i][0].seeAlso);
17631            var tempMatchList = parallelMatchList.slice();
17632            var remainder = sortedItems.slice(i);
17633            remainder[0][1].parallel = "first";
17634            for (var j=0,jlen=remainder.length;j<jlen;j++) {
17635                var itemID = remainder[j][0].id;
17636                var ididx = tempMatchList.indexOf(itemID);
17637                idxEnd = false;
17638                if (ididx === -1) {
17639                    idxEnd = (i+j-1);
17640                } else if ((i+j) === (sortedItems.length-1)) {
17641                    idxEnd = (i+j);
17642                }
17643                if (idxEnd) {
17644                    siblingRanges.push([i, idxEnd]);
17645                    break;
17646                } else {
17647                    tempMatchList = tempMatchList.slice(0, ididx).concat(tempMatchList.slice(ididx+1));
17648                }
17649            }
17650        }
17651        // parallelMatchList/freshMatchList relate only to parallels.
17652        // no-repeat non-parallels are handled in a separate block.
17653        if (i > 0 && freshMatchList) {
17654            this.state.tmp.suppress_repeats[i-1].START = true;
17655            freshMatchList = false;
17656        }
17657        for (var varname in this.state.opt.track_repeat) {
17658            if (!currItem[varname] || !nextItem[varname]) {
17659                // Go ahead and render any value with an empty partner
17660                info[varname] = false;
17661            } else if ("string" === typeof nextItem[varname] || "number" === typeof nextItem[varname]) {
17662                // Simple comparison of string values
17663                if (varname === "title" && currItem["title-short"] && nextItem["title-short"]) {
17664                    var currVal = currItem["title-short"];
17665                    var nextVal = nextItem["title-short"];
17666                } else {
17667                    var currVal = currItem[varname];
17668                    var nextVal = nextItem[varname];
17669                }
17670                if (currVal == nextVal) {
17671                    info[varname] = true;
17672                } else {
17673                    info[varname] = false;
17674                }
17675            } else if ("undefined" === typeof currItem[varname].length) {
17676                // If a date, use only the year
17677                info[varname] = false;
17678                var currYear = currItem[varname].year;
17679                var nextYear = nextItem[varname].year;
17680                if (currYear && nextYear) {
17681                    if (currYear == nextYear) {
17682                        info[varname] = true;
17683                    }
17684                }
17685            } else {
17686                // If a creator value, kludge it
17687                var currVal = JSON.stringify(currItem[varname]);
17688                var nextVal = JSON.stringify(nextItem[varname]);
17689                if (currVal === nextVal) {
17690                    info[varname] = true;
17691                } else {
17692                    info[varname] = false;
17693                }
17694            }
17695        }
17696        if (!parallelMatchList) {
17697            info.ORPHAN = true;
17698        }
17699        if (idxEnd === i) {
17700            info.END = true;
17701            parallelMatchList = false;
17702        }
17703        this.state.tmp.suppress_repeats.push(info);
17704    }
17705    
17706    // if (!this.state.tmp.just_looking) {
17707    //     this.state.sys.print(`${JSON.stringify(this.state.tmp.suppress_repeats, null, 2)}`);
17708    // }
17709    
17710    // Set no-repeat info here?
17711    for (var j=0,jlen=siblingRanges.length;j<jlen;j++) {
17712        var masterID = sortedItems[siblingRanges[j][0]][0].id;
17713        this.state.registry.registry[masterID].master = true;
17714        this.state.registry.registry[masterID].siblings = [];
17715        var start = siblingRanges[j][0];
17716        var end = siblingRanges[j][1];
17717        for (var k=start; k<end; k++) {
17718            this.state.tmp.suppress_repeats[k].SIBLING = true;
17719            var siblingID = sortedItems[k+1][0].id;
17720            sortedItems[k+1][1].parallel = "other";
17721            this.state.registry.registry[masterID].siblings.push(siblingID);
17722        }
17723    }
17724    // this.state.sys.print(JSON.stringify(this.state.tmp.suppress_repeats, null, 2));
17725};
17726
17727CSL.Parallel.prototype.checkRepeats = function(params) {
17728    var idx = this.state.tmp.cite_index;
17729    if (this.state.tmp.suppress_repeats) {
17730        if (params.parallel_first && Object.keys(params.parallel_first).length > 0) {
17731            var arr = [{}].concat(this.state.tmp.suppress_repeats);
17732            var ret = true;
17733            for (var varname in params.parallel_first) {
17734                if (!arr[idx][varname] || arr[idx].START) {
17735                    // true --> suppress the entry
17736                    // Test here evaluates as "all", not "any"
17737                    ret = false;
17738                }
17739            }
17740            return ret;
17741        }
17742        if (params.parallel_last && Object.keys(params.parallel_last).length > 0) {
17743            var arr = this.state.tmp.suppress_repeats.concat([{}]);
17744            var ret = Object.keys(params.parallel_last).length > 0 ? true : false;
17745            for (var varname in params.parallel_last) {
17746                if (!arr[idx][varname] || arr[idx].END) {
17747                    // "all" match, as above.
17748                    ret = false;
17749                }
17750            }
17751            return ret;
17752        }
17753        if (params.non_parallel && Object.keys(params.non_parallel).length > 0) {
17754            var arr = [{}].concat(this.state.tmp.suppress_repeats);
17755            var ret = true;
17756            for (var varname in params.non_parallel) {
17757                if (!arr[idx][varname]) {
17758                    ret = false;
17759                }
17760            }
17761            return ret;
17762        }
17763    }
17764    return false;
17765};
17766
17767/*global CSL: true */
17768
17769
17770CSL.Util = {};
17771
17772CSL.Util.Match = function () {
17773
17774    this.any = function (token, state, tests) {
17775        return function (Item, item) {
17776            for (var i=0, ilen=tests.length; i < ilen; i += 1) {
17777                var result = tests[i](Item, item);
17778                if (result) {
17779                    return true;
17780                }
17781            }
17782            return false;
17783        };
17784    };
17785
17786    this.none = function (token, state, tests) {
17787        return function (Item, item) {
17788            for (var i=0,ilen=tests.length;i<ilen;i+=1) {
17789                var result = tests[i](Item,item);
17790                if (result) {
17791                    return false;
17792                }
17793            }
17794            return true;
17795        };
17796    };
17797
17798    this.all = function (token, state, tests) {
17799        return function (Item, item) {
17800            for (var i=0,ilen=tests.length;i<ilen;i+=1) {
17801                var result = tests[i](Item,item);
17802                if (!result) {
17803                    return false;
17804                }
17805            }
17806            return true;
17807        };
17808    };
17809
17810    this[undefined] = this.all;
17811
17812    this.nand = function (token, state, tests) {
17813        return function (Item, item) {
17814            for (var i=0,ilen=tests.length;i<ilen;i+=1) {
17815                var result = tests[i](Item,item);
17816                if (!result) {
17817                    return true;
17818                }
17819            }
17820            return false;
17821        };
17822    };
17823
17824};
17825
17826/*global CSL: true */
17827
17828/*
17829 * Fields can be transformed by translation/transliteration, or by
17830 * abbreviation.  Transformations are performed in that order.
17831 *
17832 * Renderings of original, translated or transliterated content
17833 * (followed by abbreviation if requested) are placed in the primary
17834 * output slot or the (implicitly punctuated) secondary and tertiary
17835 * output slots according to the settings registered in the
17836 * state.opt['cite-lang-prefs'] arrays. The array has six segments:
17837 * 'persons', 'institutions', 'titles', 'journals', 'publishers', and
17838 * 'places'. Each segment always contains at least one item, and may
17839 * hold values 'orig', 'translit' or 'translat'. The array defaults to
17840 * a single item 'orig'.
17841 *
17842 * All multilingual variables are associated with segments,
17843 * with the exception of 'edition' and 'genre'. These two
17844 * exceptions are always rendered with the first matching
17845 * language form found in state.opt['locale-translit'] or, if
17846 * composing a sort key, state.opt['locale-sort']. No secondary
17847 * slot rendering is performed for this two variables.
17848 *
17849 * The balance of multilingual variables are rendered with
17850 * the first matching value in the transform locales spec
17851 * (no transform, state.opt['locale-translit'], or 
17852 * state.opt['locale-translat']) mapped to the target
17853 * slot.
17854 *
17855 * Full primary+secondary+tertiary rendering is performed only in
17856 * note-style citations and the bibliography.  In-text citations are
17857 * rendered in the primary output slot only, following the same spec
17858 * parameters.
17859 *
17860 *   Optional setters:
17861 *     .setAbbreviationFallback(); fallback flag
17862 *       (if true, a failed abbreviation will fallback to long)
17863 *
17864 *     .setAlternativeVariableName(): alternative variable name in Item,
17865 *       for use as a fallback abbreviation source
17866 *
17867 * Translation/transliteration
17868 *
17869 *   Optional setter:
17870 *     .setTransformFallback():
17871 *       default flag (if true, the original field value will be used as a fallback)
17872 *
17873 * The getTextSubField() method may be used to obtain a string transform
17874 * of a field, without abbreviation, as needed for setting sort keys
17875 * (for example).
17876 *
17877 */
17878
17879CSL.Transform = function (state) {
17880    // Abbreviation families
17881    this.abbrevs = {};
17882    this.abbrevs["default"] = new state.sys.AbbreviationSegments();
17883
17884    function getCountryOrJurisdiction(variable, normalizedKey, quashCountry) {
17885        var value = "";
17886        if (state.sys.getHumanForm) {
17887            if (variable === "country") {
17888                value = state.sys.getHumanForm(normalizedKey.toLowerCase(), false, true);
17889                value = value.split("|")[0];
17890            } else if (variable === "jurisdiction") {
17891                value = state.sys.getHumanForm(normalizedKey.toLowerCase(), false, true);
17892                if (!quashCountry) {
17893                    value = value.split("|").slice(1).join(", ");
17894                } else {
17895                    // Bare country name is rendered by "country", not "jurisdiction"
17896                    value = "";
17897                }
17898            }
17899	    }
17900	    return value;
17901    }
17902    
17903    // Internal function
17904    function abbreviate(state, tok, Item, altvar, basevalue, family_var, use_field) {
17905
17906        var value = "";
17907        var myabbrev_family = CSL.FIELD_CATEGORY_REMAP[family_var];
17908        var preferredJurisdiction;
17909        if (!myabbrev_family) {
17910            return basevalue;
17911        }
17912
17913        var variable = family_var;
17914        var normalizedKey = basevalue;
17915
17916        if (state.sys.normalizeAbbrevsKey) {
17917            normalizedKey = state.sys.normalizeAbbrevsKey(family_var, basevalue);
17918        }
17919        var quashCountry = false;
17920        if (variable === "jurisdiction" && normalizedKey) {
17921            quashCountry = normalizedKey.indexOf(":") === -1;
17922        }
17923        // Fix up jurisdiction codes
17924        if (["jurisdiction", "country"].indexOf(family_var) > -1 && basevalue === basevalue.toLowerCase()) {
17925            normalizedKey = basevalue.toUpperCase();
17926        }
17927        
17928        
17929        // Lazy retrieval of abbreviations.
17930        if (state.sys.getAbbreviation) {
17931
17932            if (["jurisdiction", "country", "language-name", "language-name-original"].indexOf(variable) > -1) {
17933                preferredJurisdiction = "default";
17934            } else if (Item.jurisdiction) {
17935                preferredJurisdiction = Item.jurisdiction;
17936            } else {
17937                preferredJurisdiction = "default";
17938            }
17939            var jurisdiction = state.transform.loadAbbreviation(preferredJurisdiction, myabbrev_family, normalizedKey, Item.language);
17940
17941            // Some rules:
17942            // # variable === "country"
17943            // (1) If an abbreviation is associated with the code, then:
17944            //     (a) return the abbreviated form if form="short"
17945            // (2) Otherwise:
17946            //     (a) return the human-readable country name, or whatever is there if it's not a code
17947            // # variable === "jurisdiction"
17948            // (1) If !!getHumanForm(jurisdictionID, false, false):
17949            //     (a) If the code is top-level (i.e. a country):
17950            //         (i) return nothing -- this is what the "country" variable is for.
17951            //     (b) otherwise:
17952            //         (i) If an abbreviation is associated with the code, then:
17953            //             (A) return the abbreviated form
17954            //         (ii) Otherwise
17955            //             (A) return the human-readable form, with the country name & code removed from the front
17956            // (2) Otherwise:
17957            //     (a) abbreviate as per normal.
17958            // # other variables
17959            // (1) Abbreviate as per normal.
17960
17961            if (state.transform.abbrevs[jurisdiction][myabbrev_family] && normalizedKey) {
17962                // Safe to test presence of abbrev against raw object in this block
17963                var abbrev = state.transform.abbrevs[jurisdiction][myabbrev_family][normalizedKey];
17964                if (tok.strings.form === "short" && abbrev) {
17965                    if (quashCountry) {
17966                        value = "";
17967                    } else {
17968                        value = abbrev;
17969                    }
17970                } else {
17971	                value = getCountryOrJurisdiction(variable, normalizedKey, quashCountry);
17972                }
17973            }
17974        }
17975        
17976        // Was for: 
17977        if (!value 
17978            && (!state.opt.development_extensions.require_explicit_legal_case_title_short || Item.type !== 'legal_case') 
17979            && altvar && Item[altvar] && use_field) {
17980            value = Item[altvar];
17981        }
17982        if (!value && !state.sys.getAbbreviation && state.sys.getHumanForm) {
17983	        value = getCountryOrJurisdiction(variable, normalizedKey, quashCountry);
17984	    }
17985        if (!value && !quashCountry && (!state.sys.getHumanForm || variable !== "jurisdiction")) {
17986            value = basevalue;
17987        }
17988        if (state.opt.development_extensions.force_title_abbrev_fallback) {
17989            if (variable === "title" && value === basevalue && Item["title-short"]) {
17990                value = Item["title-short"];
17991            }
17992        }
17993        return value;
17994    }
17995
17996    function getFieldLocale(Item,field) {
17997        var ret = state.opt["default-locale"][0].slice(0, 2);
17998        var localeRex;
17999        if (state.opt.development_extensions.strict_text_case_locales) {
18000            localeRex = new RegExp("^([a-zA-Z]{2})(?:$|-.*| .*)");
18001        } else {
18002            localeRex = new RegExp("^([a-zA-Z]{2})(?:$|-.*|.*)");
18003        }
18004        if (Item.language) {
18005            var m = ("" + Item.language).match(localeRex);
18006            if (m) {
18007                ret = m[1];
18008            } else {
18009                // Set garbage to "Klingon".
18010                ret = "tlh";
18011            }
18012        }
18013        if (Item.multi && Item.multi && Item.multi.main && Item.multi.main[field]) {
18014            ret = Item.multi.main[field];
18015        }
18016        if (!state.opt.development_extensions.strict_text_case_locales
18017            || state.opt.development_extensions.normalize_lang_keys_to_lowercase) {
18018
18019            ret = ret.toLowerCase();
18020        }
18021        return ret;
18022    }
18023
18024    // Internal functions
18025    function getTextSubField (Item, field, locale_type, use_default, stopOrig, family_var) {
18026        var opt, o, ret, opts;
18027        var usedOrig = stopOrig;
18028        var usingOrig = false;
18029
18030        if (!Item[field]) {
18031            return {
18032                name:"",
18033                usedOrig:stopOrig,
18034                token: CSL.Util.cloneToken(this)
18035            };
18036        }
18037        // If form="short" is selected ("family_var" is a misnomer
18038        // here, it means short-form requested), and the variable
18039        // has a short-form partner (i.e. it is in array
18040        // VARIABLES_WITH_SHORT_FORM), then it is run here as *-short".
18041        var stickyLongForm = false;
18042        if (CSL.VARIABLES_WITH_SHORT_FORM.indexOf(field) > -1
18043            && family_var) {
18044
18045            field = field + "-short";
18046            stickyLongForm = true;
18047        }
18048        var breakMe = false;
vendor: 182,392 bytes, lines 18049-22832
18049        var firstValue = null;
18050        var fieldsToTry = [];
18051        if (field.slice(-6) === "-short") {
18052            fieldsToTry.push(field);
18053            fieldsToTry.push(field.slice(0, -6))
18054        } else {
18055            fieldsToTry.push(field);
18056        }
18057
18058        for (var h=0,hlen=fieldsToTry.length; h<hlen; h++) {
18059            var variantMatch = false;
18060            var field = fieldsToTry[h];
18061
18062            ret = {name:"", usedOrig:stopOrig,locale:getFieldLocale(Item,field)};
18063
18064            opts = state.opt[locale_type] ? state.opt[locale_type].slice() : [];
18065            var hasVal = false;
18066
18067            if (locale_type === 'locale-orig') {
18068                if (!stopOrig) {
18069                    ret.name = Item[field];
18070                    ret.usedOrig = false;
18071                }
18072                hasVal = true;
18073                usingOrig = true;
18074            } else if (use_default && ("undefined" === typeof opts || opts.length === 0)) {
18075                // If we want the original, or if we don't have any specific guidance and we 
18076                // definitely want output, just return the original value.
18077                ret.name = Item[field];
18078                ret.usedOrig = true;
18079                hasVal = true;
18080                usingOrig = true;
18081            }
18082
18083            if (!hasVal) {
18084                for (var i = 0, ilen = opts.length; i < ilen; i += 1) {
18085                    opt = opts[i];
18086                    o = opt.split(/[\-_]/)[0];
18087                    if (opt && Item.multi && Item.multi._keys[field] && Item.multi._keys[field][opt]) {
18088                        ret.name = Item.multi._keys[field][opt];
18089                        ret.locale = opt;
18090                        hasVal = true;
18091                        variantMatch = true;
18092                        usingOrig = false;
18093                        break;
18094                    } else if (o && Item.multi && Item.multi._keys[field] && Item.multi._keys[field][o]) {
18095                        ret.name = Item.multi._keys[field][o];
18096                        ret.locale = o;
18097                        hasVal = true;
18098                        variantMatch = true;
18099                        usingOrig = false;
18100                        break;
18101                    }
18102                }
18103                if (!ret.name && use_default) {
18104                    ret = {name:Item[field], usedOrig:true, locale:getFieldLocale(Item,field)};
18105                    usingOrig = true;
18106                }
18107            }
18108            ret.token = CSL.Util.cloneToken(this);
18109            if (h === 0) {
18110                if (variantMatch) {
18111                    ret.found_variant_ok = true;
18112                }
18113                firstValue = ret;
18114                if (!stickyLongForm && ("undefined" === typeof opts || opts.length === 0)) {
18115                    breakMe = true;
18116                }
18117                if (variantMatch) {
18118                    breakMe = true;
18119                }
18120            } else {
18121                if (!stickyLongForm && !variantMatch && firstValue) {
18122                    ret = firstValue;
18123                    field = fieldsToTry[0];
18124                } else if (variantMatch) {
18125                    ret.found_variant_ok = true;
18126                }
18127            }
18128            if (["title", "container-title"].indexOf(field) > -1) {
18129                if (!usedOrig
18130                    && (!ret.token.strings["text-case"]
18131                        || ret.token.strings["text-case"] === "sentence"
18132                        || ret.token.strings["text-case"] === "normal")) {
18133                    var locale = state.opt.lang;
18134                    var lang;
18135                    if (usingOrig) {
18136                        lang = false;
18137                    } else {
18138                        lang = ret.locale;
18139                    }
18140                    var seg = field.slice(0,-5);
18141                    var sentenceCase = ret.token.strings["text-case"] === "sentence" ? true : false;
18142                    ret.name = CSL.titlecaseSentenceOrNormal(state, Item, seg, lang, sentenceCase);
18143                    delete ret.token.strings["text-case"];
18144                }
18145            }
18146            if (breakMe) {
18147                break;
18148            }
18149        }
18150        return ret;
18151    }
18152    this.getTextSubField = getTextSubField;
18153    
18154    // Setter for abbreviation lists
18155    // This initializes a single abbreviation based on known
18156    // data.
18157    function loadAbbreviation(jurisdiction, category, orig, lang) {
18158        if (!jurisdiction) {
18159            jurisdiction = "default";
18160        }
18161        var country = jurisdiction.split(":")[0];
18162        var domain = CSL.getAbbrevsDomain(state, country, lang);
18163        if (domain) {
18164            jurisdiction += ("@" + domain);
18165        }
18166        if (!orig) {
18167            if (!state.transform.abbrevs[jurisdiction]) {
18168                state.transform.abbrevs[jurisdiction] = new state.sys.AbbreviationSegments();
18169            }
18170            if (!state.transform.abbrevs[jurisdiction][category]) {
18171                state.transform.abbrevs[jurisdiction][category] = {};
18172            }
18173            return jurisdiction;
18174        }
18175        // The getAbbreviation() function should check the
18176        // external DB for the content key. If a value exists
18177        // in this[category] and no value exists in DB, the entry
18178        // in memory is left untouched. If a value does exist in
18179        // DB, the memory value is created.
18180        //
18181        // See testrunner_stdrhino.js for an example.
18182        if (state.sys.getAbbreviation) {
18183            jurisdiction = state.sys.getAbbreviation(state.opt.styleID, state.transform.abbrevs, jurisdiction, category, orig);
18184            if (!jurisdiction) {
18185                jurisdiction = "default";
18186                if (domain) {
18187                    jurisdiction += ("@" + domain);
18188                }
18189            }
18190        }
18191        return jurisdiction;
18192    }
18193    this.loadAbbreviation = loadAbbreviation;
18194
18195    function publisherCheck (tok, Item, primary, family_var) {
18196        var varname = tok.variables[0];
18197        if (state.publisherOutput && primary) {
18198            if (["publisher","publisher-place"].indexOf(varname) === -1) {
18199                return false;
18200            } else {
18201                // In this case, the publisher bundle will be rendered
18202                // at the close of the group, by the closing group node.
18203                state.publisherOutput[varname + "-token"] = tok;
18204                state.publisherOutput.varlist.push(varname);
18205                var lst = primary.split(/;\s*/);
18206                if (lst.length === state.publisherOutput[varname + "-list"].length) {
18207                    state.publisherOutput[varname + "-list"] = lst;
18208                }
18209                // Abbreviate each of the items in the list here!
18210                for (var i = 0, ilen = lst.length; i < ilen; i += 1) {
18211                    lst[i] = abbreviate(state, tok, Item, false, lst[i], family_var, true);
18212                }
18213                state.tmp[varname + "-token"] = tok;
18214                return true;
18215            }
18216        }
18217        return false;
18218    }
18219
18220    function citeFormCheck(Item, value) {
18221        var m = value.match(/^#([0-9]+).*>>>/);
18222        if (m && m[1]) {
18223            Item["cite-form"] = m[1];
18224        }
18225    }
18226    
18227    // The name transform code is placed here to keep similar things
18228    // in one place.  Obviously this module could do with a little
18229    // tidying up.
18230    function quashCheck(jurisdiction, value) {
18231        var m = value.match(/^(?:#[0-9]+)*(?:!((?:[-_a-z]+(?:(?:.*)))(?:,(?:[-_a-z]+(?:(?:.*))))*))*>>>/);
18232        if (m) {
18233            value = value.slice(m[0].length);
18234            if (m[1]) {
18235                var fields = m[1].split(",");
18236                for (var i = 0, ilen = fields.length; i < ilen; i += 1) {
18237                    var rawField = fields[i];
18238                    var mm = rawField.match(/^([-_a-z]+)(?:\:(.*))*$/);
18239                    var field = mm[1];
18240                    // trimmer is not available in getAmbiguousCite
18241                    var trimmer = state.tmp.abbrev_trimmer;
18242                    if (mm[2]) {
18243                        if (trimmer && jurisdiction) {
18244                            if (!trimmer[jurisdiction]) {
18245                                trimmer[jurisdiction] = {};
18246                            }
18247                            trimmer[jurisdiction][field] = mm[2];
18248                        }
18249                    } else if (state.tmp.done_vars.indexOf(field) === -1) {
18250                        if (trimmer && jurisdiction) {
18251                            if (!trimmer.QUASHES[jurisdiction]) {
18252                                trimmer.QUASHES[jurisdiction] = {};
18253                            }
18254                            trimmer.QUASHES[jurisdiction][field] = true;
18255                        }
18256                        state.tmp.done_vars.push(field);
18257                    }
18258                }
18259            }
18260        }
18261        return value;
18262    }
18263    this.quashCheck = quashCheck;
18264
18265    // Return function appropriate to selected options
18266    function getOutputFunction(variables, family_var, abbreviation_fallback, alternative_varname) {
18267        // var mytoken;
18268
18269        // Set the primary_locale and secondary_locale lists appropriately.
18270        // No instance helper function for this; everything can be derived
18271        // from processor settings and rendering context.
18272
18273        var localesets;
18274        var langPrefs = CSL.LangPrefsMap[variables[0]];
18275        if (!langPrefs) {
18276            localesets = false;
18277        } else {
18278            localesets = state.opt['cite-lang-prefs'][langPrefs];
18279        }
18280
18281        return function (state, Item, item) {
18282            var primary, primary_locale, secondary, secondary_locale, tertiary, tertiary_locale, primary_tok;
18283            if (!variables[0] || (!Item[variables[0]] && !Item[alternative_varname])) {
18284                return null;
18285            }
18286            //
18287            // Exploring the edges here.
18288            // "suppress-author" for string variables (mostly titles).
18289            //
18290            if (!state.tmp.just_looking && item && item["suppress-author"]) {
18291                if (!state.tmp.probably_rendered_something && state.tmp.can_substitute.length() > 1) {
18292                    return null;
18293                }
18294            }
18295            var slot = {primary:false, secondary:false, tertiary:false};
18296            if (state.tmp.area.slice(-5) === "_sort") {
18297                slot.primary = 'locale-sort';
18298            } else {
18299                if (localesets && localesets.length === 1 && localesets[0] === "locale-orig") {
18300                    slot.primary = "locale-orig";
18301                    localesets = false;
18302                } else if (localesets && !state.tmp.multi_layout) {
18303                    var slotnames = ["primary", "secondary", "tertiary"];
18304                    for (var i = 0, ilen = slotnames.length; i < ilen; i += 1) {
18305                        if (localesets.length - 1 <  i) {
18306                            break;
18307                        }
18308                        if (localesets[i]) {
18309                            slot[slotnames[i]] = 'locale-' + localesets[i];
18310                        }
18311                    }
18312                } else {
18313                    slot.primary = 'locale-orig';
18314                }
18315            }
18316            
18317            if (variables[0] === "title-short" 
18318                || (state.tmp.area !== "bibliography"
18319                    && !(state.tmp.area === "citation"
18320                         && state.opt.xclass === "note"
18321                         && item && !item.position))) {
18322                
18323                slot.secondary = false;
18324                slot.tertiary = false;
18325            }
18326
18327            if (state.tmp.multi_layout) {
18328                slot.secondary = false;
18329                slot.tertiary = false;
18330            }
18331            
18332            // Problem for multilingual: we really should be
18333            // checking for sanity on the basis of the output
18334            // strings to be actually used. (also below)
18335            if (state.tmp["publisher-list"]) {
18336                if (variables[0] === "publisher") {
18337                    state.tmp["publisher-token"] = this;
18338                } else if (variables[0] === "publisher-place") {
18339                    state.tmp["publisher-place-token"] = this;
18340                }
18341                return null;
18342            }
18343            
18344            // tmp.lang_array carries the current locale IDs of the style
18345            // and the item. Field-level locale IDs are added here, so
18346            // we clone it to allow reset.
18347            var oldLangArray = state.tmp.lang_array.slice();
18348
18349            // True is for transform fallback
18350            var res = getTextSubField.call(this, Item, variables[0], slot.primary, true, null, family_var);
18351            primary = res.name;
18352            primary_locale = res.locale;
18353            var primary_tok = res.token;
18354            var primaryUsedOrig = res.usedOrig;
18355            if (family_var && !res.found_variant_ok) {
18356                primary = abbreviate(state, primary_tok, Item, alternative_varname, primary, family_var, true);
18357                // Suppress subsequent use of another variable if requested by
18358                // hack syntax in this abbreviation short form.
18359                if (primary) {
18360                    // We run quash-check in getAmbiguousCite, to possibly pick up a cite-form value.
18361                    citeFormCheck(Item, primary);
18362                    if (!state.tmp.just_looking) {
18363                        primary = quashCheck(Item.jurisdiction, primary);
18364                    }
18365                }
18366            }
18367            if (publisherCheck(this, Item, primary, family_var)) {
18368                state.tmp.lang_array = oldLangArray;
18369                return null;
18370            }
18371
18372            // No fallback for secondary and tertiary
18373            secondary = false;
18374            tertiary = false;
18375            var secondary_tok;
18376            var tertiary_tok;
18377            if (slot.secondary) {
18378                res = getTextSubField.call(this, Item, variables[0], slot.secondary, false, res.usedOrig, null, family_var);
18379                secondary = res.name;
18380                secondary_locale = res.locale;
18381                secondary_tok = res.token;
18382                if (family_var && !res.found_variant_ok) {
18383                    if (secondary) {
18384                        // The abbreviate() function could use a cleanup, after Zotero correct to use title-short
18385                        secondary = abbreviate(state, secondary_tok, Item, false, secondary, family_var, true);
18386                    }
18387                }
18388                //print("XXX secondary_locale: "+secondary_locale);
18389            }
18390            if (slot.tertiary) {
18391                res = getTextSubField.call(this, Item, variables[0], slot.tertiary, false, res.usedOrig, null, family_var);
18392                tertiary = res.name;
18393                tertiary_locale = res.locale;
18394                tertiary_tok = res.token;
18395                if (family_var && !res.found_variant_ok) {
18396                    if (tertiary) {
18397                        // The abbreviate() function could use a cleanup, after Zotero correct to use title-short
18398                        tertiary = abbreviate(state, tertiary_tok, Item, false, tertiary, family_var, true);
18399                    }
18400                }
18401                //print("XXX tertiary_locale: "+tertiary_locale);
18402            }
18403            
18404            // Decoration of primary (currently translit only) goes here
18405            var primaryPrefix;
18406            if (slot.primary === "locale-translit") {
18407                primaryPrefix = state.opt.citeAffixes[langPrefs][slot.primary].prefix;
18408            }                
18409            // XXX This should probably protect against italics at higher
18410            // levels.
18411
18412            if (primaryPrefix === "<i>" && variables[0] === 'title' && !primaryUsedOrig) {
18413                var hasItalic = false;
18414                for (var i = 0, ilen = primary_tok.decorations.length; i < ilen; i += 1) {
18415                    if (primary_tok.decorations[i][0] === "@font-style"
18416                        && primary_tok.decorations[i][1] === "italic") {
18417                        
18418                        hasItalic = true;
18419                    }
18420                }
18421                if (!hasItalic) {
18422                    primary_tok.decorations.push(["@font-style", "italic"]);
18423                }
18424            }
18425
18426            //print("XXX "+primary_tok.strings["text-case"]);
18427            if (primary_locale !== "en" && primary_tok.strings["text-case"] === "title") {
18428                primary_tok.strings["text-case"] = "passthrough";
18429            }
18430            
18431            if ("title" === variables[0]) {
18432                primary = CSL.demoteNoiseWords(state, primary, this["leading-noise-words"]);
18433            }
18434            if (secondary || tertiary) {
18435
18436                state.output.openLevel("empty");
18437
18438                // A little too aggressive maybe.
18439                primary_tok.strings.suffix = primary_tok.strings.suffix.replace(/[ .,]+$/,"");
18440                if (primary_locale) {
18441                    state.tmp.lang_array = [primary_locale].concat(oldLangArray);
18442                }
18443                CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, null, null, primary_tok, primary_tok.strings.prefix + primary);
18444                state.output.append(primary, primary_tok);
18445                state.tmp.probably_rendered_something = true;
18446
18447                if (primary === secondary) {
18448                    secondary = false;
18449                }
18450                if (secondary) {
18451                    secondary_tok.strings.prefix = state.opt.citeAffixes[langPrefs][slot.secondary].prefix;
18452                    secondary_tok.strings.suffix = state.opt.citeAffixes[langPrefs][slot.secondary].suffix;
18453                    // Add a space if empty
18454                    if (!secondary_tok.strings.prefix) {
18455                        secondary_tok.strings.prefix = " ";
18456                    }
18457                    // Remove quotes
18458                    for (var i = secondary_tok.decorations.length - 1; i > -1; i += -1) {
18459                        if (['@quotes/true', '@font-style/italic', '@font-style/oblique', '@font-weight/bold'].indexOf(secondary_tok.decorations[i].join('/')) > -1) {
18460                            secondary_tok.decorations = secondary_tok.decorations.slice(0, i).concat(secondary_tok.decorations.slice(i + 1));
18461                        }
18462                    }
18463                    if (secondary_locale !== "en" && secondary_tok.strings["text-case"] === "title") {
18464                        secondary_tok.strings["text-case"] = "passthrough";
18465                    }
18466                    if (secondary_locale) {
18467                        state.tmp.lang_array = [secondary_locale].concat(oldLangArray);
18468                    }
18469                    var secondary_outer = new CSL.Token();
18470                    secondary_outer.decorations.push(["@font-style", "normal"]);
18471                    secondary_outer.decorations.push(["@font-weight", "normal"]);
18472                    state.output.openLevel(secondary_outer);
18473                    state.output.append(secondary, secondary_tok);
18474                    state.output.closeLevel();
18475
18476                    var blob_obj = state.output.current.value();
18477                    var blobs_pos = state.output.current.value().blobs.length - 1;
18478                    // Suppress supplementary multilingual info on subsequent
18479                    // partners of a parallel cite?
18480                }
18481                if (primary === tertiary) {
18482                    tertiary = false;
18483                }
18484                
18485                if (tertiary) {
18486                    tertiary_tok.strings.prefix = state.opt.citeAffixes[langPrefs][slot.tertiary].prefix;
18487                    tertiary_tok.strings.suffix = state.opt.citeAffixes[langPrefs][slot.tertiary].suffix;
18488                    // Add a space if empty
18489                    if (!tertiary_tok.strings.prefix) {
18490                        tertiary_tok.strings.prefix = " ";
18491                    }
18492                    // Remove quotes
18493                    for (var i = tertiary_tok.decorations.length - 1; i > -1; i += -1) {
18494                        if (['@quotes/true', '@font-style/italic', '@font-style/oblique', '@font-weight/bold'].indexOf(tertiary_tok.decorations[i].join('/')) > -1) {
18495                            tertiary_tok.decorations = tertiary_tok.decorations.slice(0, i).concat(tertiary_tok.decorations.slice(i + 1));
18496                        }
18497                    }
18498                    if (tertiary_locale !== "en" && tertiary_tok.strings["text-case"] === "title") {
18499                        tertiary_tok.strings["text-case"] = "passthrough";
18500                    }
18501                    if (tertiary_locale) {
18502                        state.tmp.lang_array = [tertiary_locale].concat(oldLangArray);
18503                    }
18504                    var tertiary_outer = new CSL.Token();
18505                    tertiary_outer.decorations.push(["@font-style", "normal"]);
18506                    tertiary_outer.decorations.push(["@font-weight", "normal"]);
18507                    state.output.openLevel(tertiary_outer);
18508                    state.output.append(tertiary, tertiary_tok);
18509                    state.output.closeLevel();
18510
18511                    var blob_obj = state.output.current.value();
18512                    var blobs_pos = state.output.current.value().blobs.length - 1;
18513                    // Suppress supplementary multilingual info on subsequent
18514                    // partners of a parallel cite?
18515                    // See note above.
18516                }
18517
18518                state.output.closeLevel();
18519            } else {
18520                if (primary_locale) {
18521                    state.tmp.lang_array = [primary_locale].concat(oldLangArray);
18522                }
18523                CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, null, null, primary_tok, primary_tok.strings.prefix + primary);
18524                state.output.append(primary, primary_tok);
18525                state.tmp.probably_rendered_something = true;
18526            }
18527
18528            state.tmp.lang_array = oldLangArray;
18529                
18530            if (state.tmp.can_block_substitute) {
18531                state.tmp.name_node.children.push(state.output.current.value());
18532            }
18533            return null;
18534        };
18535    }
18536    this.getOutputFunction = getOutputFunction;
18537};
18538
18539/*global CSL: true */
18540
18541/**
18542 * Style token.
18543 * <p>This class provides the tokens that define
18544 * the runtime version of the style.  The tokens are
18545 * instantiated by {@link CSL.Core.Build}, but the token list
18546 * must be post-processed with
18547 * {@link CSL.Core.Configure} before it can be used to generate
18548 * citations.</p>
18549 * @param {String} name The node name represented by this token.
18550 * @param {Int} tokentype A flag indicating whether this token
18551 * marks the start of a node, the end of a node, or is a singleton.
18552 * @class
18553 */
18554CSL.Token = function (name, tokentype, conditional) {
18555    /**
18556     * Name of the element.
18557     * <p>This corresponds to the element name of the
18558     * relevant tag in the CSL file.
18559     */
18560    this.name = name;
18561    /**
18562     * Strings and other static content specific to the element.
18563     */
18564    this.strings = {};
18565    this.strings.delimiter = undefined;
18566    this.strings.prefix = "";
18567    this.strings.suffix = "";
18568    /**
18569     * Formatting parameters.
18570     * <p>This is a placeholder at instantiation.  It is
18571     * replaced by the result of {@link CSL.setDecorations}
18572     * when the tag is created and configured during {@link CSL.Core.Build}
18573     * by {@link CSL.XmlToToken}.  The parameters for particular
18574     * formatting attributes are stored as string arrays, which
18575     * map to formatting functions at runtime,
18576     * when the output format is known.  Note that the order in which
18577     * parameters are registered is fixed by the constant
18578     * {@link CSL.FORMAT_KEY_SEQUENCE}.
18579     */
18580    this.decorations = [];
18581    this.variables = [];
18582    /**
18583     * Element functions.
18584     * <p>Functions implementing the styling behaviour of the element
18585     * are pushed into this array in the {@link CSL.Core.Build} phase.
18586     */
18587    this.execs = [];
18588    /**
18589     * Token type.
18590     * <p>This is a flag constant indicating whether the token represents
18591     * a start tag, an end tag, or is a singleton.</p>
18592     */
18593    this.tokentype = tokentype;
18594
18595    // Conditional attributes added to bare tokens at runtime
18596    
18597    /**
18598     * Condition evaluator.
18599     * <p>This is a placeholder that receives a single function, and is
18600     * only relevant for a conditional branching tag (<code>if</code> or
18601     * <code>else-if</code>).  The function implements the argument to
18602     * the <code>match=</code> attribute (<code>any</code>, <code>all</code>
18603     * or <code>none</code>), by executing the functions registered in the
18604     * <code>tests</code> array (see below), and reacting accordingly.  This
18605     * function is invoked by the execution wrappers found in
18606     * {@link CSL.Engine}.</p>
18607     */
18608    // this.evaluator = false;
18609    /**
18610     * Conditions.
18611     * <p>Functions that evaluate to true or false, implementing
18612     * various posisble attributes to the conditional branching tags,
18613     * are registered here during {@link CSL.Core.Build}.
18614     * </p>
18615     */
18616    // this.tests = [];
18617    /**
18618     * Jump point on success.
18619     * <p>This holds the list jump point to be used when the
18620     * <code>evaluator</code> function of a conditional tag
18621     * returns true (success).  The jump index value is set during the
18622     * back-to-front token pass performed during {@link CSL.Core.Configure}.
18623     * </p>
18624     */
18625    // this.succeed = false;
18626    /**
18627     * Jump point on failure.
18628     * <p>This holds the list jump point to be used when the
18629     * <code>evaluator</code> function of a conditional tag
18630     * returns false (failure).  Jump index values are set during the
18631     * back-to-front token pass performed during {@link CSL.Core.Configure}.
18632     * </p>
18633     */
18634    // this.fail = false;
18635    /**
18636     * Index of next token.
18637     * <p>This holds the index of the next token in the
18638     * token list, which is the default "jump-point" for ordinary
18639     * processing.  Jump index values are set during the
18640     * back-to-front token pass performed during {@link CSL.Core.Configure}.
18641     * </p>
18642     */
18643    // this.next = false;
18644};
18645
18646// Have needed this for yonks
18647CSL.Util.cloneToken = function (token) {
18648    var newtok, key, pos, len;
18649    if ("string" === typeof token) {
18650        return token;
18651    }
18652    newtok = new CSL.Token(token.name, token.tokentype);
18653    for (var key in token.strings) {
18654        if (token.strings.hasOwnProperty(key)) {
18655            newtok.strings[key] = token.strings[key];
18656        }
18657    }
18658    if (token.decorations) {
18659        newtok.decorations = [];
18660        for (pos = 0, len = token.decorations.length; pos < len; pos += 1) {
18661            newtok.decorations.push(token.decorations[pos].slice());
18662        }
18663    }
18664    if (token.variables) {
18665        newtok.variables = token.variables.slice();
18666    }
18667    // Probably overkill; this is only used for cloning formatting
18668    // tokens.
18669    if (token.execs) {
18670        newtok.execs = token.execs.slice();
18671        if (token.tests) {
18672            newtok.tests = token.tests.slice();
18673        }
18674    }
18675    return newtok;
18676};
18677
18678/*global CSL: true */
18679
18680/**
18681 * Ambiguous Cite Configuration Object
18682 * @class
18683 */
18684CSL.AmbigConfig = function () {
18685    this.maxvals = [];
18686    this.minval = 1;
18687    this.names = [];
18688    this.givens = [];
18689    this.year_suffix = false;
18690    this.disambiguate = 0;
18691};
18692
18693/*global CSL: true */
18694
18695CSL.Blob = function (str, token, levelname) {
18696    var len, pos, key;
18697    this.levelname = levelname;
18698    //print(levelname);
18699    if (token) {
18700        this.strings = {"prefix":"","suffix":""};
18701        for (var key in token.strings) {
18702            if (token.strings.hasOwnProperty(key)) {
18703                this.strings[key] = token.strings[key];
18704            }
18705        }
18706        this.decorations = [];
18707        if (token.decorations === undefined) {
18708            len = 0;
18709        } else {
18710            len = token.decorations.length;
18711        }
18712        for (pos = 0; pos < len; pos += 1) {
18713            this.decorations.push(token.decorations[pos].slice());
18714        }
18715    } else {
18716        this.strings = {};
18717        this.strings.prefix = "";
18718        this.strings.suffix = "";
18719        this.strings.delimiter = "";
18720        this.decorations = [];
18721    }
18722    if ("string" === typeof str) {
18723        this.blobs = str;
18724    } else if (str) {
18725        this.blobs = [str];
18726    } else {
18727        this.blobs = [];
18728    }
18729    this.alldecor = [this.decorations];
18730};
18731
18732
18733CSL.Blob.prototype.push = function (blob) {
18734    if ("string" === typeof this.blobs) {
18735        CSL.error("Attempt to push blob onto string object");
18736    } else if (false !== blob) {
18737        blob.alldecor = blob.alldecor.concat(this.alldecor);
18738        this.blobs.push(blob);
18739    }
18740};
18741
18742/*global CSL: true */
18743
18744/**
18745 * An output instance object representing a number or a range
18746 *
18747 * with attributes next and start, and
18748 * methods isRange(), renderStart(), renderEnd() and renderRange().
18749 * At render time, the output queue will perform optional
18750 * collapsing of these objects in the queue, according to
18751 * configurable options, and apply any decorations registered
18752 * in the object to the output elements.
18753 * @namespace Range object and friends.
18754 */
18755
18756CSL.NumericBlob = function (state, particle, num, mother_token, id) {
18757    // item id is used to assure that prefix delimiter is invoked only
18758    // when joining blobs across items
18759    this.id = id;
18760    this.alldecor = [];
18761    this.num = num;
18762    this.particle = particle;
18763    this.blobs = num.toString();
18764    this.status = CSL.START;
18765    this.strings = {};
18766    if (mother_token) {
18767        if (mother_token.strings["text-case"]) {
18768            var textCase = mother_token.strings["text-case"];
18769            this.particle = CSL.Output.Formatters[textCase](state, this.particle);
18770            this.blobs = CSL.Output.Formatters[textCase](state, this.blobs);
18771        }
18772        this.gender = mother_token.gender;
18773        this.decorations = mother_token.decorations;
18774        this.strings.prefix = mother_token.strings.prefix;
18775        this.strings.suffix = mother_token.strings.suffix;
18776        this.strings["text-case"] = mother_token.strings["text-case"];
18777        this.successor_prefix = mother_token.successor_prefix;
18778        this.range_prefix = mother_token.range_prefix;
18779        this.splice_prefix = mother_token.splice_prefix;
18780        this.formatter = mother_token.formatter;
18781        if (!this.formatter) {
18782            this.formatter =  new CSL.Output.DefaultFormatter();
18783        }
18784        if (this.formatter) {
18785            this.type = this.formatter.format(1);
18786        }
18787    } else {
18788        this.decorations = [];
18789        this.strings.prefix = "";
18790        this.strings.suffix = "";
18791        this.successor_prefix = "";
18792        this.range_prefix = "";
18793        this.splice_prefix = "";
18794        this.formatter = new CSL.Output.DefaultFormatter();
18795    }
18796};
18797
18798
18799CSL.NumericBlob.prototype.setFormatter = function (formatter) {
18800    this.formatter = formatter;
18801    this.type = this.formatter.format(1);
18802};
18803
18804
18805CSL.Output.DefaultFormatter = function () {};
18806
18807CSL.Output.DefaultFormatter.prototype.format = function (num) {
18808    return num.toString();
18809};
18810
18811CSL.NumericBlob.prototype.checkNext = function (next,start) {
18812    if (start) {
18813        this.status = CSL.START;
18814        if ("object" === typeof next) {
18815            if (next.num === (this.num + 1)) {
18816                next.status = CSL.SUCCESSOR;
18817            } else {
18818                next.status = CSL.SEEN;
18819            }
18820        }
18821    } else if (! next || !next.num || this.type !== next.type || next.num !== (this.num + 1)) {
18822        if (this.status === CSL.SUCCESSOR_OF_SUCCESSOR) {
18823            this.status = CSL.END;
18824        }
18825        if ("object" === typeof next) { 
18826           next.status = CSL.SEEN;
18827        }
18828    } else { // next number is in the sequence
18829        if (this.status === CSL.START || this.status === CSL.SEEN) {
18830            next.status = CSL.SUCCESSOR;
18831        } else if (this.status === CSL.SUCCESSOR || this.status === CSL.SUCCESSOR_OF_SUCCESSOR) {
18832            if (this.range_prefix) {
18833                next.status = CSL.SUCCESSOR_OF_SUCCESSOR;
18834                this.status = CSL.SUPPRESS;
18835            } else {
18836                next.status = CSL.SUCCESSOR;
18837            }
18838        }
18839        // wakes up the correct delimiter.
18840        //if (this.status === CSL.SEEN) {
18841        //    this.status = CSL.SUCCESSOR;
18842        //}
18843    }
18844};
18845
18846
18847CSL.NumericBlob.prototype.checkLast = function (last) {
18848    // Used to adjust final non-range join
18849    if (this.status === CSL.SEEN 
18850    || (last.num !== (this.num - 1) && this.status === CSL.SUCCESSOR)) {
18851        this.status = CSL.SUCCESSOR;
18852        return true;
18853    }
18854    return false;
18855};
18856
18857/*global CSL: true */
18858
18859CSL.Util.fixDateNode = function (parent, pos, node) {
18860    var form, variable, datexml, subnode, partname, attr, val, prefix, suffix, children, subchildren, display, cslid;
18861    
18862    var lingo = this.cslXml.getAttributeValue(node, "lingo");
18863
18864    var default_locale = this.cslXml.getAttributeValue(node, "default-locale");
18865
18866    // Raise date flag, used to control inclusion of year-suffix key in sorts
18867    // This may be a little reckless: not sure what happens on no-date conditions
18868    this.build.date_key = true;
18869
18870    form = this.cslXml.getAttributeValue(node, "form");
18871    var lingo;
18872    if (default_locale) {
18873        lingo = this.opt["default-locale"][0];
18874    } else {
18875        lingo = this.cslXml.getAttributeValue(node, "lingo");
18876    }
18877
18878    if (!this.getDate(form, default_locale)) {
18879        return parent;
18880    }
18881
18882    var dateparts = this.cslXml.getAttributeValue(node, "date-parts");
18883
18884    variable = this.cslXml.getAttributeValue(node, "variable");
18885    prefix = this.cslXml.getAttributeValue(node, "prefix");
18886    suffix = this.cslXml.getAttributeValue(node, "suffix");
18887    display = this.cslXml.getAttributeValue(node, "display");
18888    cslid = this.cslXml.getAttributeValue(node, "cslid");
18889
18890    //
18891    // Xml: Copy a node
18892    //
18893    datexml = this.cslXml.nodeCopy(this.getDate(form, default_locale));
18894    this.cslXml.setAttribute(datexml, 'lingo', this.opt.lang);
18895    this.cslXml.setAttribute(datexml, 'form', form);
18896    this.cslXml.setAttribute(datexml, 'date-parts', dateparts);
18897    this.cslXml.setAttribute(datexml, "cslid", cslid);
18898    //
18899    // Xml: Set attribute
18900    //
18901    this.cslXml.setAttribute(datexml, 'variable', variable);
18902    this.cslXml.setAttribute(datexml, 'default-locale', default_locale);
18903    //
18904    // Xml: Set flag
18905    //
18906    if (prefix) {
18907        //
18908        // Xml: Set attribute
18909        //
18910        this.cslXml.setAttribute(datexml, "prefix", prefix);
18911    }
18912    if (suffix) {
18913        //
18914        // Xml: Set attribute
18915        //
18916        this.cslXml.setAttribute(datexml, "suffix", suffix);
18917    }
18918    if (display) {
18919        //
18920        // Xml: Set attribute
18921        //
18922        this.cslXml.setAttribute(datexml, "display", display);
18923    }
18924    //
18925    // Step through any date-part children of the layout date node,
18926    // and lay their attributes onto the corresponding node in the
18927    // locale template node copy.
18928    //
18929    // tests: language_BaseLocale
18930    // tests: date_LocalizedTextInStyleLocaleWithTextCase
18931    //
18932    children = this.cslXml.children(datexml);
18933    for (var key in children) {
18934        subnode = children[key];
18935        if ("date-part" === this.cslXml.nodename(subnode)) {
18936            partname = this.cslXml.getAttributeValue(subnode, "name");
18937            if (default_locale) {
18938                this.cslXml.setAttributeOnNodeIdentifiedByNameAttribute(datexml, "date-part", partname, "@default-locale", "true");
18939            }
18940        }
18941    }
18942
18943    children = this.cslXml.children(node);
18944    for (var key in children) {
18945        subnode = children[key];
18946        if ("date-part" === this.cslXml.nodename(subnode)) {
18947            partname = this.cslXml.getAttributeValue(subnode, "name");
18948            subchildren = this.cslXml.attributes(subnode);
18949            for (attr in subchildren) {
18950                if ("@name" === attr) {
18951                    continue;
18952                }
18953                if (lingo && lingo !== this.opt.lang) {
18954                    if (["@suffix", "@prefix", "@form"].indexOf(attr) > -1) {
18955                        continue;
18956                    }
18957                }
18958                val = subchildren[attr];
18959                this.cslXml.setAttributeOnNodeIdentifiedByNameAttribute(datexml, "date-part", partname, attr, val);
18960            }
18961        }
18962    }
18963    
18964    if ("year" === this.cslXml.getAttributeValue(node, "date-parts")) {
18965
18966        //
18967        // Xml: Find one node by attribute and delete
18968        //
18969        this.cslXml.deleteNodeByNameAttribute(datexml, 'month');
18970        //
18971        // Xml: Find one node by attribute and delete
18972        //
18973        this.cslXml.deleteNodeByNameAttribute(datexml, 'day');
18974        
18975    } else if ("year-month" === this.cslXml.getAttributeValue(node, "date-parts")) {
18976        //
18977        // Xml: Find one node by attribute and delete
18978        //
18979        this.cslXml.deleteNodeByNameAttribute(datexml, 'day');
18980    } else if ("month-day" === this.cslXml.getAttributeValue(node, "date-parts")) {
18981        //
18982        // Xml: Get child nodes
18983        //
18984        var childNodes = this.cslXml.children(datexml);
18985        for (var i=1,ilen=this.cslXml.numberofnodes(childNodes);i<ilen;i++) {
18986            //
18987            // Xml: Get attribute value (for string comparison)
18988            //
18989            if (this.cslXml.getAttributeValue(childNodes[i], 'name') === "year") {
18990                //
18991                // Xml: Set attribute value
18992                //
18993                this.cslXml.setAttribute(childNodes[i-1], "suffix", "");
18994                break;
18995            }
18996        }
18997        //
18998        // Xml: Find one node by attribute and delete
18999        //
19000        this.cslXml.deleteNodeByNameAttribute(datexml, 'year');
19001    }
19002    return this.cslXml.insertChildNodeAfter(parent, node, pos, datexml);
19003};
19004
19005/*global CSL: true */
19006
19007CSL.dateMacroAsSortKey = function (state, Item) {
19008    CSL.dateAsSortKey.call(this, state, Item, true);
19009};
19010
19011
19012CSL.dateAsSortKey = function (state, Item, isMacro) {
19013    var dp, elem, value, e, yr, prefix, i, ilen;
19014    var variable = this.variables[0];
19015    var macroFlag = "empty";
19016    if (isMacro && state.tmp.extension) {
19017        macroFlag = "macro-with-date";
19018    }
19019    dp = Item[variable];
19020    if ("undefined" === typeof dp) {
19021        dp = {"date-parts": [[0]] };
19022    }
19023    if ("undefined" === typeof this.dateparts) {
19024        this.dateparts = ["year", "month", "day"];
19025    }
19026    if (dp.raw) {
19027        dp = state.fun.dateparser.parseDateToArray(dp.raw);
19028    } else if (dp["date-parts"]) {
19029        dp = state.dateParseArray(dp);
19030    }
19031    if ("undefined" === typeof dp) {
19032        dp = {};
19033    }
19034    if (dp.year) {
19035        for (i = 0, ilen = CSL.DATE_PARTS_INTERNAL.length; i < ilen; i += 1) {
19036            elem = CSL.DATE_PARTS_INTERNAL[i];
19037            value = 0;
19038            e = elem;
19039            if (e.slice(-4) === "_end") {
19040                e = e.slice(0, -4);
19041            }
19042            if (dp[elem] && this.dateparts.indexOf(e) > -1) {
19043                value = dp[elem];
19044            }
19045            if (elem.slice(0, 4) === "year") {
19046                yr = CSL.Util.Dates[e].numeric(state, value);
19047                var prefix = "1";
19048                if (yr[0] === "-") {
19049                    prefix = "0";
19050                    yr = yr.slice(1);
19051                    yr = 9999 - parseInt(yr, 10);
19052                }
19053                state.output.append(CSL.Util.Dates[elem.slice(0, 4)].numeric(state, (prefix + yr)), macroFlag);
19054            } else {
19055                value = CSL.Util.Dates[e]["numeric-leading-zeros"](state, value);
19056                // Ugh.
19057                if (!value) {
19058                    value = "00";
19059                }
19060                state.output.append(value, macroFlag);
19061            }
19062        }
19063    }
19064};
19065
19066CSL.Engine.prototype.dateParseArray = function (date_obj) {
19067    var ret, field, dp, exts;
19068    ret = {};
19069    for (field in date_obj) {
19070        if (field === "date-parts") {
19071            dp = date_obj["date-parts"];
19072            if (dp.length > 1) {
19073                if (dp[0].length !== dp[1].length) {
19074                    CSL.error("CSL data error: element mismatch in date range input.");
19075                }
19076            }
19077            exts = ["", "_end"];
19078            for (var i = 0, ilen = dp.length; i < ilen; i += 1) {
19079                for (var j = 0, jlen = CSL.DATE_PARTS.length; j < jlen; j += 1) {
19080                    if (isNaN(parseInt(dp[i][j], 10))) {
19081                        ret[(CSL.DATE_PARTS[j] + exts[i])] = undefined;
19082                    } else {
19083                        ret[(CSL.DATE_PARTS[j] + exts[i])] = parseInt(dp[i][j], 10);
19084                    }
19085                }
19086            }
19087        } else if (date_obj.hasOwnProperty(field)) {
19088
19089            // XXXX: temporary workaround
19090
19091            if (field === "literal" && "object" === typeof date_obj.literal && "string" === typeof date_obj.literal.part) {
19092                CSL.debug("Warning: fixing up weird literal date value");
19093                ret.literal = date_obj.literal.part;
19094            } else {
19095                ret[field] = date_obj[field];
19096            }
19097        }
19098    }
19099    return ret;
19100};
19101
19102/*global CSL: true */
19103
19104CSL.Util.Names = {};
19105
19106CSL.Util.Names.compareNamesets = CSL.NameOutput.prototype._compareNamesets;
19107
19108/**
19109 * Un-initialize a name (quash caps after first character)
19110 */
19111CSL.Util.Names.unInitialize = function (state, name) {
19112    var i, ilen, namelist, punctlist, ret;
19113    if (!name) {
19114        return "";
19115    }
19116    namelist = name.split(/(?:\-|\s+)/);
19117    punctlist = name.match(/(\-|\s+)/g);
19118    ret = "";
19119    for (i = 0, ilen = namelist.length; i < ilen; i += 1) {
19120        // if (CSL.ALL_ROMANESQUE_REGEXP.exec(namelist[i].slice(0,-1)) 
19121        //    && namelist[i] 
19122        //    && namelist[i] !== namelist[i].toUpperCase()) {
19123
19124            // More or less like this, to address the following fault report:
19125            // http://forums.zotero.org/discussion/17610/apsa-problems-with-capitalization-of-mc-mac-etc/
19126
19127            // Leaving the name string untouched because name capitalization is varied and wonderful.
19128            // https://github.com/Juris-M/citeproc-js/issues/43
19129            
19130            //namelist[i] = namelist[i].slice(0, 1) + namelist[i].slice(1, 2).toLowerCase() + namelist[i].slice(2);
19131        // }
19132        ret += namelist[i];
19133        if (i < ilen - 1) {
19134            ret += punctlist[i];
19135        }
19136    }
19137    return ret;
19138};
19139
19140/**
19141 * Initialize a name.
19142 */
19143CSL.Util.Names.initializeWith = function (state, name, terminator, normalizeOnly) {
19144    var i, ilen, mm, lst, ret;
19145    if (!name) {
19146        return "";
19147    }
19148    if (!terminator) {
19149        terminator = "";
19150    }
19151    if (["Lord", "Lady"].indexOf(name) > -1
19152        || (!name.replace(/^(?:<[^>]+>)*/, "").match(CSL.STARTSWITH_ROMANESQUE_REGEXP)
19153            && !terminator.match("%s"))) {
19154        return name;
19155    }
19156
19157    if (state.opt["initialize-with-hyphen"] === false) {
19158        name = name.replace(/\-/g, " ");
19159    }
19160
19161    // We need to suss out what is a set of initials or abbreviation,
19162    // so that they can be selectively normalized. Steps might be:
19163    //   (1) Split the string
19164    //   (2) Step through the string, deleting periods and, if initalize="false", then
19165    //       (a) note abbreviations and initials (separately).
19166    //   (3) If initialize="false" then:
19167    //       (a) Do the thing below, but only pushing terminator; or else
19168    //       (b) Do the thing below
19169
19170    name = name.replace(/\s*\-\s*/g, "-").replace(/\s+/g, " ");
19171    name = name.replace(/-([a-z])/g, "\u2013$1");
19172
19173    for (var i=name.length-2; i>-1; i += -1) {
19174        if (name.slice(i, i+1) === "." && name.slice(i+1, i+2) !== " ") {
19175            name = name.slice(0, i) + ". " + name.slice(i+1);
19176        }
19177    }
19178
19179    // (1) Split the string
19180    var nameSplits = CSL.Output.Formatters.nameDoppel.split(name);
19181    var namelist = [];
19182    namelist = [nameSplits.strings[0]];
19183
19184    if (nameSplits.tags.length === 0) {
19185        var mmm = namelist[0].match(/[^\.]+$/);
19186        if (mmm && mmm[0].length === 1 && mmm[0] !== mmm[0].toLowerCase()) {
19187            namelist[0] += ".";
19188        }
19189    }
19190
19191    for (i = 1, ilen = nameSplits.strings.length; i < ilen; i += 1) {
19192        namelist.push(nameSplits.tags[i - 1]);
19193        namelist.push(nameSplits.strings[i]);
19194    }
19195
19196    // Use doInitializeName or doNormalizeName, depending on requirements.
19197    if (normalizeOnly) {
19198        ret = this.doNormalize(state, namelist, terminator);
19199    } else {
19200        ret = this.doInitialize(state, namelist, terminator);
19201    }
19202    ret = ret.replace(/\u2013([a-z])/g, "-$1");
19203    return ret;
19204};
19205
19206CSL.Util.Names.notag = function(str) {
19207    return str.replace(/^(?:<[^>]+>)*/, "");
19208};
19209
19210CSL.Util.Names.mergetag = function(state, tagstr, newstr) {
19211    var m = tagstr.match(/(?:-*<[^>]+>-*)/g);
19212    if (!m) {
19213        return newstr;
19214    } else {
19215        tagstr = m.join("");
19216    }
19217    m = newstr.match(/^(.*[^\s])*(\s+)$/);
19218    if (m) {
19219        m[1] = m[1] ? m[1] : "";
19220        newstr = m[1] + tagstr + m[2];
19221    } else {
19222        newstr = newstr + tagstr;
19223    }
19224    return newstr;
19225};
19226
19227CSL.Util.Names.tagonly = function(state, str) {
19228    var m = str.match(/(?:<[^>]+>)+/);
19229    if (!m) {
19230        return str;
19231    } else {
19232        return m.join("");
19233    }
19234};
19235
19236CSL.Util.Names.doNormalize = function (state, namelist, terminator) {
19237    var i, ilen;
19238    // namelist is a flat list of given-name elements and space-like separators between them
19239    terminator = terminator ? terminator : "";
19240    // Flag elements that look like abbreviations
19241    var isAbbrev = [];
19242    for (i = 0, ilen = namelist.length; i < ilen; i += 1) {
19243        if (this.notag(namelist[i]).length > 1 && this.notag(namelist[i]).slice(-1) === ".") {
19244            // namelist[i] = namelist[i].slice(0, -1);
19245            namelist[i] = namelist[i].replace(/^(.*)\.(.*)$/, "$1$2");
19246            isAbbrev.push(true);
19247        } else if (namelist[i].length === 1 && namelist[i].toUpperCase() === namelist[i]) {
19248            isAbbrev.push(true);
19249        } else {
19250            isAbbrev.push(false);
19251        }
19252    }
19253    // Step through the elements of the givenname array
19254    for (i = 0, ilen = namelist.length; i < ilen; i += 2) {
19255        // If the element is not an abbreviation, leave it and its trailing spaces alone
19256        if (isAbbrev[i]) {
19257            // For all elements but the last
19258            if (i < namelist.length - 2) {
19259                // Start from scratch on space-like things following an abbreviation
19260                namelist[i + 1] = this.tagonly(state, namelist[i+1]);
19261                if (!isAbbrev[i+2]) {
19262                    namelist[i + 1] = this.tagonly(state, namelist[i+1]) + " ";
19263                }
19264                
19265                // Add the terminator to the element
19266                // If the following element is not a single-character abbreviation, remove a trailing zero-width non-break space, if present
19267                // These ops may leave some duplicate cruft in the elements and separators. This will be cleaned at the end of the function.
19268                if (namelist[i + 2].length > 1) {
19269                    namelist[i+1] = terminator.replace(/\ufeff$/, "") + namelist[i+1];
19270                } else {
19271                    namelist[i+1] = this.mergetag(state, namelist[i+1], terminator);
19272                }
19273            }
19274            // For the last element (if it is an abbreviation), just append the terminator
19275            if (i === namelist.length - 1) {
19276                namelist[i] = namelist[i] + terminator;
19277            }
19278        }
19279    }
19280    // Remove trailing cruft and duplicate spaces, and return
19281    return namelist.join("").replace(/[\u0009\u000a\u000b\u000c\u000d\u0020\ufeff\u00a0]+$/,"").replace(/\s*\-\s*/g, "-").replace(/[\u0009\u000a\u000b\u000c\u000d\u0020]+/g, " ");
19282};
19283
19284CSL.Util.Names.doInitialize = function (state, namelist, terminator) {
19285    var i, ilen, m, j, jlen, lst, n;
19286    for (i = 0, ilen = namelist.length; i < ilen; i += 2) {
19287        n = namelist[i];
19288        if (!n) {
19289            continue;
19290        }
19291        m = n.match(CSL.NAME_INITIAL_REGEXP);
19292        if (!m && (!n.match(CSL.STARTSWITH_ROMANESQUE_REGEXP) && n.length > 1 && terminator.match("%s"))) {
19293            m = n.match(/(.)(.*)/);
19294        }
19295        if (m && m[2] && m[3]) {
19296            m[1] = m[1] + m[2];
19297            m[2] = "";
19298        }
19299        if (m && m[1].slice(0, 1) === m[1].slice(0, 1).toUpperCase()) {
19300            var extra = "";
19301            if (m[2]) {
19302                var s = "";
19303                lst = m[2].split("");
19304                for (j = 0, jlen = lst.length; j < jlen; j += 1) {
19305                    var c = lst[j];
19306                    if (c === c.toUpperCase()) {
19307                        s += c;
19308                    } else {
19309                        break;
19310                    }
19311                }
19312                if (s.length < m[2].length) {
19313                    extra = CSL.toLocaleLowerCase.call(state, s);
19314                }
19315            }
19316            namelist[i] = m[1] + extra;
19317            if (i < (ilen - 1)) {
19318                if (terminator.match("%s")) {
19319                    namelist[i] = terminator.replace("%s", namelist[i]);
19320                } else {
19321                    if (namelist[i + 1].indexOf("-") > -1) {
19322                        namelist[i + 1] = this.mergetag(state, namelist[i+1].replace("-", ""), terminator) + "-";
19323                    } else {
19324                        namelist[i + 1] = this.mergetag(state, namelist[i+1], terminator);
19325                    }
19326                }
19327            } else {
19328                if (terminator.match("%s")) {
19329                    namelist[i] = terminator.replace("%s", namelist[i]);
19330                } else {
19331                    namelist.push(terminator);
19332                }
19333            }
19334        } else if (n.match(CSL.ROMANESQUE_REGEXP) && (!m || !m[3])) {
19335            namelist[i] = " " + n;
19336        }
19337    }
19338    var ret = namelist.join("");
19339    ret = ret.replace(/[\u0009\u000a\u000b\u000c\u000d\u0020\ufeff\u00a0]+$/,"").replace(/\s*\-\s*/g, "-").replace(/[\u0009\u000a\u000b\u000c\u000d\u0020]+/g, " ");
19340    return ret;
19341};
19342
19343CSL.Util.Names.getRawName = function (name) {
19344    var ret = [];
19345    if (name.literal) {
19346        ret.push(name.literal);
19347    } else {
19348        if (name.given) {
19349            ret.push(name.given);
19350        }
19351        if (name.family) {
19352            ret.push(name.family);
19353        }
19354    }
19355    return ret.join(" ");
19356};
19357
19358/*global CSL: true */
19359
19360/**
19361 * Date mangling functions.
19362 * @namespace Date construction utilities
19363 */
19364CSL.Util.Dates = {};
19365
19366/**
19367 * Year manglers
19368 * <p>short, long</p>
19369 */
19370CSL.Util.Dates.year = {};
19371
19372/**
19373 * Convert year to long form
19374 * <p>This just passes the number back as a string.</p>
19375 */
19376CSL.Util.Dates.year["long"] = function (state, num) {
19377    if (!num) {
19378        if ("boolean" === typeof num) {
19379            num = "";
19380        } else {
19381            num = 0;
19382        }
19383    }
19384    return num.toString();
19385};
19386
19387/**
19388 * Crudely convert to Japanese Imperial form.
19389 * <p>Returns the result as a string.</p>
19390 */
19391CSL.Util.Dates.year.imperial = function (state, num, end) {
19392    var year = "";
19393    if (!num) {
19394        if ("boolean" === typeof num) {
19395            num = "";
19396        } else {
19397            num = 0;
19398        }
19399    }
19400    end = end ? "_end" : "";
19401    var month = state.tmp.date_object["month" + end];
19402    month = month ? ""+month : "1";
19403    while (month.length < 2) {
19404        month = "0" + month;
19405    }
19406    var day = state.tmp.date_object["day" + end];
19407    day = day ? ""+day : "1";
19408    while (day.length < 2) {
19409        day = "0" + day;
19410    }
19411    var date = parseInt(num + month + day, 10);
19412    var label;
19413    var offset;
19414    if (date >= 18680908 && date < 19120730) {
19415        label = '\u660e\u6cbb';
19416        offset = 1867;
19417    } else if (date >= 19120730 && date < 19261225) {
19418        label = '\u5927\u6b63';
19419        offset = 1911;
19420    } else if (date >= 19261225 && date < 19890108) {
19421        label = '\u662d\u548c';
19422        offset = 1925;
19423    } else if (date >= 19890108) {
19424        label = '\u5e73\u6210';
19425        offset = 1988;
19426    }
19427    // This entire "imperial" code block should be cut. Scraped input
19428    // for this will be too ratty to be useful anyway.
19429    if (label && offset) {
19430        var normalizedKey = label;
19431        if (state.sys.normalizeAbbrevsKey) {
19432            // The first argument does not need to specify the exact variable
19433            // name.
19434            normalizedKey = state.sys.normalizeAbbrevsKey("number", label);
19435        }
19436        if (!state.transform.abbrevs['default']['number'][normalizedKey]) {
19437            // loadAbbreviation normally takes an item as fourth argument.
19438            // It is not available here, 
19439            state.transform.loadAbbreviation('default', "number", normalizedKey, null);
19440        }
19441        if (state.transform.abbrevs['default']['number'][normalizedKey]) {
19442            label = state.transform.abbrevs['default']['number'][normalizedKey];
19443        }
19444        year = label + (num - offset);
19445    }
19446    return year;
19447};
19448
19449/**
19450 * Convert year to short form
19451 * <p>Just crops any 4-digit year to the last two digits.</p>
19452 */
19453CSL.Util.Dates.year["short"] = function (state, num) {
19454    num = num.toString();
19455    if (num && num.length === 4) {
19456        return num.substr(2);
19457    }
19458};
19459
19460
19461/**
19462 * Convert year to short form
19463 * <p>Just crops any 4-digit year to the last two digits.</p>
19464 */
19465CSL.Util.Dates.year.numeric = function (state, num) {
19466    var m, pre;
19467    num = "" + num;
19468    var m = num.match(/([0-9]*)$/);
19469    if (m) {
19470        pre = num.slice(0, m[1].length * -1);
19471        num = m[1];
19472    } else {
19473        pre = num;
19474        num = "";
19475    }
19476    while (num.length < 4) {
19477        num = "0" + num;
19478    }
19479    return (pre + num);
19480};
19481
19482
19483/*
19484 * MONTH manglers
19485 * normalize
19486 * long, short, numeric, numeric-leading-zeros
19487 */
19488CSL.Util.Dates.normalizeMonth = function (num, useSeason) {
19489    var ret;
19490    if (!num) {
19491        num = 0;
19492    }
19493    num = "" + num;
19494    if (!num.match(/^[0-9]+$/)) {
19495        num = 0;
19496    }
19497    num = parseInt(num, 10);
19498    if (useSeason) {
19499        var res = {stub: "month-", num: num};
19500        if (res.num < 1 || res.num > 24) {
19501            res.num = 0;
19502        } else {
19503            while (res.num > 16) {
19504                res.num = res.num - 4;
19505            }
19506            if (res.num > 12) {
19507                res.stub = "season-";
19508                res.num = res.num - 12;
19509            }
19510        }
19511        ret = res;
19512    } else {
19513        if (num < 1 || num > 12) {
19514            num = 0;
19515        }
19516        ret = num;
19517    }
19518    return ret;
19519};
19520
19521CSL.Util.Dates.month = {};
19522
19523/**
19524 * Convert month to numeric form
19525 * <p>This just passes the number back as a string.</p>
19526 */
19527CSL.Util.Dates.month.numeric = function (state, num) {
19528    var num = CSL.Util.Dates.normalizeMonth(num);
19529    if (!num) {
19530        num = "";
19531    }
19532    return num;
19533};
19534
19535/**
19536 * Convert month to numeric-leading-zeros form
19537 * <p>This just passes the number back as string padded with zeros.</p>
19538 */
19539CSL.Util.Dates.month["numeric-leading-zeros"] = function (state, num) {
19540    var num = CSL.Util.Dates.normalizeMonth(num);
19541    if (!num) {
19542        num = "";
19543    } else {
19544        num = "" + num;
19545        while (num.length < 2) {
19546            num = "0" + num;
19547        }
19548    }
19549    return num;
19550};
19551
19552/**
19553 * Convert month to long form
19554 * <p>This passes back the month of the locale in long form.</p>
19555 */
19556
19557// Gender is not currently used. Is it needed?
19558
19559CSL.Util.Dates.month["long"] = function (state, num, gender, forceDefaultLocale) {
19560    var res = CSL.Util.Dates.normalizeMonth(num, true);
19561    var num = res.num;
19562    if (!num) {
19563        num = "";
19564    } else {
19565        num = "" + num;
19566        while (num.length < 2) {
19567            num = "0" + num;
19568        }
19569        num = state.getTerm(res.stub + num, "long", 0, 0, false, forceDefaultLocale);
19570    }
19571    return num;
19572};
19573
19574/**
19575 * Convert month to long form
19576 * <p>This passes back the month of the locale in short form.</p>
19577 */
19578
19579// See above.
19580
19581CSL.Util.Dates.month["short"] = function (state, num, gender, forceDefaultLocale) {
19582    var res = CSL.Util.Dates.normalizeMonth(num, true);
19583    var num = res.num;
19584    if (!num) {
19585        num = "";
19586    } else {
19587        num = "" + num;
19588        while (num.length < 2) {
19589            num = "0" + num;
19590        }
19591        num = state.getTerm(res.stub + num, "short", 0, 0, false, forceDefaultLocale);
19592    }
19593    return num;
19594};
19595
19596/*
19597 * DAY manglers
19598 * numeric, numeric-leading-zeros, ordinal
19599 */
19600CSL.Util.Dates.day = {};
19601
19602/**
19603 * Convert day to numeric form
19604 * <p>This just passes the number back as a string.</p>
19605 */
19606CSL.Util.Dates.day.numeric = function (state, num) {
19607    return num.toString();
19608};
19609
19610CSL.Util.Dates.day["long"] = CSL.Util.Dates.day.numeric;
19611
19612/**
19613 * Convert day to numeric-leading-zeros form
19614 * <p>This just passes the number back as a string padded with zeros.</p>
19615 */
19616CSL.Util.Dates.day["numeric-leading-zeros"] = function (state, num) {
19617    if (!num) {
19618        num = 0;
19619    }
19620    num = num.toString();
19621    while (num.length < 2) {
19622        num = "0" + num;
19623    }
19624    return num.toString();
19625};
19626
19627/**
19628 * Convert day to ordinal form
19629 * <p>This will one day pass back the number as a string with the
19630 * ordinal suffix appropriate to the locale.  For the present,
19631 * it just does what is most of the time right for English.</p>
19632 */
19633CSL.Util.Dates.day.ordinal = function (state, num, gender) {
19634    return state.fun.ordinalizer.format(num, gender);
19635};
19636
19637/*global CSL: true */
19638
19639/**
19640 * Helper functions for constructing sort keys.
19641 * @namespace Sort key utilities
19642 */
19643CSL.Util.Sort = {};
19644
19645/**
19646 * Strip prepositions from a string
19647 * <p>Used when generating sort keys.</p>
19648 */
19649CSL.Util.Sort.strip_prepositions = function (str) {
19650    var m;
19651    if ("string" === typeof str) {
19652        m = str.match(/^(([aA]|[aA][nN]|[tT][hH][eE])\s+)/);
19653    }
19654    if (m) {
19655        str = str.substr(m[1].length);
19656    }
19657    return str;
19658};
19659
19660/*global CSL: true */
19661
19662CSL.Util.substituteStart = function (state, target) {
19663    var element_trace, display, bib_first, func, choose_start, if_start, nodetypes;
19664    func = function (state, Item, item) {
19665        for (var i = 0, ilen = this.decorations.length; i < ilen; i += 1) {
19666            if ("@strip-periods" === this.decorations[i][0] && "true" === this.decorations[i][1]) {
19667                state.tmp.strip_periods += 1;
19668                break;
19669            }
19670        }
19671    };
19672    this.execs.push(func);
19673    if (this.decorations && state.opt.development_extensions.csl_reverse_lookup_support) {
19674        this.decorations.reverse();
19675        this.decorations.push(["@showid","true", this.cslid]);
19676        this.decorations.reverse();
19677    }
19678    //
19679    // Contains body code for both substitute and first-field/remaining-fields
19680    // formatting.
19681    //
19682
19683    nodetypes = ["number", "date", "names"];
19684    if (("text" === this.name && !this.postponed_macro) || nodetypes.indexOf(this.name) > -1) {
19685        element_trace = function (state, Item, item) {
19686            if (state.tmp.element_trace.value() === "author" || "names" === this.name) {
19687                if (!state.tmp.just_looking && item && item["author-only"] && state.tmp.area !== "intext") {
19688                    if (!state.tmp.probably_rendered_something) {
19689                    } else {
19690                        state.tmp.element_trace.push("suppress-me");
19691                    }
19692                }
19693                if (!state.tmp.just_looking && item && item["suppress-author"]) {
19694                    if (!state.tmp.probably_rendered_something) {
19695                        state.tmp.element_trace.push("suppress-me");
19696                    }
19697                }
19698            }
19699            else if ("date" === this.name) {
19700                if (!state.tmp.just_looking && item && item["author-only"] && state.tmp.area !== "intext") {
19701                    if (state.tmp.probably_rendered_something) {
19702                        state.tmp.element_trace.push("suppress-me");
19703                    }
19704                }
19705                /*
19706                if (!state.tmp.just_looking && item && item["suppress-author"]) {
19707                    if (state.tmp.probably_rendered_something) {
19708                        //state.tmp.element_trace.push("suppress-me");
19709                    }
19710                }
19711                */
19712            } else {
19713                if (!state.tmp.just_looking && item && item["author-only"] && state.tmp.area !== "intext") {
19714                    // XXX can_block_substitute probably is doing nothing here. The value is always true.
19715                    if (!state.tmp.probably_rendered_something && state.tmp.can_block_substitute) {
19716                    } else {
19717                        state.tmp.element_trace.push("suppress-me");
19718                    }
19719                } else if (item && item["suppress-author"]) {
19720                    state.tmp.element_trace.push("do-not-suppress-me");
19721                }
19722            }
19723        };
19724        this.execs.push(element_trace);
19725    }
19726    display = this.strings.cls;
19727    this.strings.cls = false;
19728    if (state.build.render_nesting_level === 0) {
19729        //
19730        // The markup formerly known as @bibliography/first
19731        //
19732        // Separate second-field-align from the generic display logic.
19733        // There will be some code replication, but not in the
19734        // assembled style.
19735        //
19736        if (state.build.area === "bibliography" && state.bibliography.opt["second-field-align"]) {
19737            bib_first = new CSL.Token("group", CSL.START);
19738            bib_first.decorations = [["@display", "left-margin"]];
19739            func = function (state, Item) {
19740                if (!state.tmp.render_seen) {
19741                    bib_first.strings.first_blob = Item.id;
19742                    state.output.startTag("bib_first", bib_first);
19743                }
19744            };
19745            bib_first.execs.push(func);
19746            target.push(bib_first);
19747        } else if (CSL.DISPLAY_CLASSES.indexOf(display) > -1) {
19748            bib_first = new CSL.Token("group", CSL.START);
19749            bib_first.decorations = [["@display", display]];
19750            func = function (state, Item) {
19751                bib_first.strings.first_blob = Item.id;
19752                state.output.startTag("bib_first", bib_first);
19753            };
19754            bib_first.execs.push(func);
19755            target.push(bib_first);
19756        }
19757        state.build.cls = display;
19758    }
19759    state.build.render_nesting_level += 1;
19760    // Should this be render_nesting_level, with the increment
19761    // below? ... ?
19762    if (state.build.substitute_level.value() === 1) {
19763        //
19764        // All top-level elements in a substitute environment get
19765        // wrapped in conditionals.  The substitute_level variable
19766        // is a stack, because spanned names elements (with their
19767        // own substitute environments) can be nested inside
19768        // a substitute environment.
19769        //
19770        // (okay, we use conditionals a lot more than that.
19771        // we slot them in for author-only as well...)
19772        choose_start = new CSL.Token("choose", CSL.START);
19773        CSL.Node.choose.build.call(choose_start, state, target);
19774        if_start = new CSL.Token("if", CSL.START);
19775        //
19776        // Set a test of the shadow if token to skip this
19777        // macro if we have acquired a name value.
19778
19779        // check for variable
19780        func = function () {
19781            if (state.tmp.can_substitute.value()) {
19782                return true;
19783            }
19784            return false;
19785        };
19786        if_start.tests ? {} : if_start.tests = [];
19787        if_start.tests.push(func);
19788        if_start.test = state.fun.match.any(this, state, if_start.tests);
19789        target.push(if_start);
19790    }
19791
19792    if (state.sys.variableWrapper
19793        && this.variables_real
19794        && this.variables_real.length) {
19795
19796        func = function (state, Item, item) {
19797            if (!state.tmp.just_looking && !state.tmp.suppress_decorations) {
19798                // Attach item data and variable names.
19799                // Do with them what you will.
19800                var variable_entry = new CSL.Token("text", CSL.START);
19801                variable_entry.decorations = [["@showid", "true"]];
19802                state.output.startTag("variable_entry", variable_entry);
19803                var position = null;
19804                if (item) {
19805                    position = item.position;
19806                }
19807                if (!position) {
19808                    position = 0;
19809                }
19810                var positionMap = [
19811                    "first",
19812                    "container-subsequent",
19813                    "subsequent",
19814                    "ibid",
19815                    "ibid-with-locator"
19816                ];
19817                var noteNumber = 0;
19818                if (item && item.noteIndex) {
19819                    noteNumber = item.noteIndex;
19820                }
19821                var firstReferenceNoteNumber = 0;
19822                if (item && item['first-reference-note-number']) {
19823                    firstReferenceNoteNumber = item['first-reference-note-number'];
19824                }
19825                var firstContainerReferenceNoteNumber = 0;
19826                if (item && item['first-container-reference-note-number']) {
19827                    firstContainerReferenceNoteNumber = item['first-container-reference-note-number'];
19828                }
19829                var citationNumber = 0;
19830                // XXX Will this EVER happen?
19831                if (item && item['citation-number']) {
19832                    citationNumber = item['citation-number'];
19833                }
19834                var index = 0;
19835                if (item && item.index) {
19836                    index = item.index;
19837                }
19838                var params = {
19839                    itemData: Item,
19840                    variableNames: this.variables,
19841                    context: state.tmp.area,
19842                    xclass: state.opt.xclass,
19843                    position: positionMap[position],
19844                    "note-number": noteNumber,
19845                    "first-reference-note-number": firstReferenceNoteNumber,
19846                    "first-container-reference-note-number": firstContainerReferenceNoteNumber,
19847                    "citation-number": citationNumber,
19848                    "index": index,
19849                    "mode": state.opt.mode
19850                };
19851                state.output.current.value().params = params;
19852            }
19853        };
19854        this.execs.push(func);
19855    }
19856};
19857
19858
19859CSL.Util.substituteEnd = function (state, target) {
19860    var func, bib_first_end, bib_other, if_end, choose_end, author_substitute, str;
19861
19862    if (state.sys.variableWrapper
19863        && (this.hasVariable || (this.variables_real && this.variables_real.length))) {
19864        
19865        func = function (state) {
19866            if (!state.tmp.just_looking && !state.tmp.suppress_decorations) {
19867                state.output.endTag("variable_entry");
19868            }
19869        };
19870        this.execs.push(func);
19871    }
19872
19873    func = function (state) {
19874        for (var i = 0, ilen = this.decorations.length; i < ilen; i += 1) {
19875            if ("@strip-periods" === this.decorations[i][0] && "true" === this.decorations[i][1]) {
19876                state.tmp.strip_periods += -1;
19877                break;
19878            }
19879        }
19880    };
19881    this.execs.push(func);
19882
19883    state.build.render_nesting_level += -1;
19884    if (state.build.render_nesting_level === 0) {
19885        if (state.build.cls) {
19886            func = function (state) {
19887                state.output.endTag("bib_first");
19888            };
19889            this.execs.push(func);
19890            state.build.cls = false;
19891        } else if (state.build.area === "bibliography" && state.bibliography.opt["second-field-align"]) {
19892            bib_first_end = new CSL.Token("group", CSL.END);
19893            // first func end
19894            func = function (state) {
19895                if (!state.tmp.render_seen) {
19896                    state.output.endTag("bib_first"); // closes bib_first
19897                }
19898            };
19899            bib_first_end.execs.push(func);
19900            target.push(bib_first_end);
19901            bib_other = new CSL.Token("group", CSL.START);
19902            bib_other.decorations = [["@display", "right-inline"]];
19903            func = function (state) {
19904                if (!state.tmp.render_seen) {
19905                    state.tmp.render_seen = true;
19906                    state.output.startTag("bib_other", bib_other);
19907                }
19908            };
19909            bib_other.execs.push(func);
19910            target.push(bib_other);
19911        }
19912    }
19913    if (state.build.substitute_level.value() === 1) {
19914        if_end = new CSL.Token("if", CSL.END);
19915        target.push(if_end);
19916        choose_end = new CSL.Token("choose", CSL.END);
19917        CSL.Node.choose.build.call(choose_end, state, target);
19918    }
19919
19920    if ("names" === this.name || ("text" === this.name && this.variables_real !== "title")) {
19921        author_substitute = new CSL.Token("text", CSL.SINGLETON);
19922        var substitution_name = this.name;
19923        func = function (state, Item) {
19924            if (state.tmp.area !== "bibliography") {
19925                return;
19926            }
19927            if ("string" !== typeof state.bibliography.opt["subsequent-author-substitute"]) {
19928                return;
19929            }
19930            if (this.variables_real && !Item[this.variables_real]) {
19931                return;
19932            }
19933            // The logic of these two is not obvious. The effect is to enable placeholder substitution
19934            // on a text macro name substitution, without printing both the text macro AND the placeholder.
19935            // See https://forums.zotero.org/discussion/comment/350407
19936            if (this.variables_real && substitution_name === "names") {
19937                return;
19938            }
19939
19940            var subrule = state.bibliography.opt["subsequent-author-substitute-rule"];
19941            var i, ilen;
19942            //var text_esc = CSL.getSafeEscape(state);
19943            var printing = !state.tmp.suppress_decorations;
19944            if (printing && state.tmp.subsequent_author_substitute_ok) {
19945                if (state.tmp.rendered_name) {
19946                    if ("partial-each" === subrule || "partial-first" === subrule) {
19947                        var dosub = true;
19948                        var rendered_name = [];
19949                        // This is a wee bit risky, as we're assuming that the name
19950                        // children and the list of stringified names are congruent.
19951                        // That *should* always be true, but you never know.
19952                        for (i = 0, ilen = state.tmp.name_node.children.length; i < ilen; i += 1) {
19953                            var name = state.tmp.rendered_name[i];
19954                            if (dosub
19955                                && state.tmp.last_rendered_name && state.tmp.last_rendered_name.length > (i - 1)
19956                                && name && !name.localeCompare(state.tmp.last_rendered_name[i])) {
19957                                str = new CSL.Blob(state[state.tmp.area].opt["subsequent-author-substitute"]);
19958                                state.tmp.name_node.children[i].blobs = [str];
19959                                if ("partial-first" === subrule) {
19960                                    dosub = false;
19961                                }
19962                            } else {
19963                                dosub = false;
19964                            }
19965                            rendered_name.push(name);
19966                        }
19967                        // might want to slice this?
19968                        state.tmp.last_rendered_name = rendered_name;
19969                    } else if ("complete-each" === subrule) {
19970                        var rendered_name = state.tmp.rendered_name.join(",");
19971                        if (rendered_name) {
19972                            if (state.tmp.last_rendered_name && !rendered_name.localeCompare(state.tmp.last_rendered_name)) {
19973                                for (i = 0, ilen = state.tmp.name_node.children.length; i < ilen; i += 1) {
19974                                    str = new CSL.Blob(state[state.tmp.area].opt["subsequent-author-substitute"]);
19975                                    state.tmp.name_node.children[i].blobs = [str];
19976                                }
19977                            }
19978                            state.tmp.last_rendered_name = rendered_name;
19979                        }
19980                    } else {
19981                        var rendered_name = state.tmp.rendered_name.join(",");
19982                        if (rendered_name) {
19983                            if (state.tmp.last_rendered_name && !rendered_name.localeCompare(state.tmp.last_rendered_name)) {
19984                                str = new CSL.Blob(state[state.tmp.area].opt["subsequent-author-substitute"]);
19985                                if (state.tmp.label_blob) {
19986                                    state.tmp.name_node.top.blobs = [str,state.tmp.label_blob];
19987                                } else if (state.tmp.name_node.top.blobs.length) {
19988                                    state.tmp.name_node.top.blobs[0].blobs = [str];
19989                                } else {
19990                                    state.tmp.name_node.top.blobs = [str];
19991                                }
19992                                state.tmp.substituted_variable = substitution_name;
19993                            }
19994                            state.tmp.last_rendered_name = rendered_name;
19995                        }
19996                    }
19997                    state.tmp.subsequent_author_substitute_ok = false;
19998                }
19999            }
20000        };
20001        this.execs.push(func);
20002    }
20003
20004    if (("text" === this.name && !this.postponed_macro) || ["number", "date", "names"].indexOf(this.name) > -1) {
20005        // element trace
20006        func = function (state, Item) {
20007            // element_trace is a mess, but it's trying to do something simple.
20008            // A queue append is done, and element_trace.value() returns "suppress-me"
20009            // the append is aborted. That's it.
20010            // It seems only to be used on numeric elements of numeric styles ATM.
20011            // If used only for that purpose, it could be greatly simplified.
20012            // If cleaned up, it could do more interesting things, like control
20013            // the suppression of names set later than first position.
20014            if (state.tmp.element_trace.mystack.length>1) {
20015                state.tmp.element_trace.pop();
20016            }
20017        };
20018        this.execs.push(func);
20019    }
20020};
20021
20022/*global CSL: true */
20023
20024CSL.Util.padding = function (num) {
20025    var m = num.match(/\s*(-{0,1}[0-9]+)/);
20026    if (m) {
20027        num = parseInt(m[1], 10);
20028        if (num < 0) {
20029            num = 99999999999999999999 + num;
20030        }
20031        num = "" + num;
20032        while (num.length < 20) {
20033            num = "0" + num;
20034        }
20035    }
20036    return num;
20037};
20038
20039CSL.Util.LongOrdinalizer = function () {};
20040
20041CSL.Util.LongOrdinalizer.prototype.init = function (state) {
20042    this.state = state;
20043};
20044
20045CSL.Util.LongOrdinalizer.prototype.format = function (num, gender) {
20046    if (num < 10) {
20047        num = "0" + num;
20048    }
20049    // Argument true means "loose".
20050    var ret = CSL.Engine.getField(
20051        CSL.LOOSE, 
20052        this.state.locale[this.state.opt.lang].terms,
20053        "long-ordinal-" + num,
20054        "long", 
20055        0, 
20056        gender
20057    );
20058    if (!ret) {
20059        ret = this.state.fun.ordinalizer.format(num, gender);
20060    }
20061    // Probably too optimistic -- what if only renders in _sort?
20062    this.state.tmp.cite_renders_content = true;
20063    return ret;
20064};
20065
20066
20067CSL.Util.Ordinalizer = function (state) {
20068    this.state = state;
20069    this.suffixes = {};
20070};
20071
20072CSL.Util.Ordinalizer.prototype.init = function () {
20073    if (!this.suffixes[this.state.opt.lang]) {
20074        this.suffixes[this.state.opt.lang] = {};
20075        for (var i = 0, ilen = 3; i < ilen; i += 1) {
20076            var gender = [undefined, "masculine", "feminine"][i];
20077            this.suffixes[this.state.opt.lang][gender] = [];
20078            for (var j = 1; j < 5; j += 1) {
20079                var ordinal = this.state.getTerm("ordinal-0" + j, "long", false, gender);
20080                if ("undefined" === typeof ordinal) {
20081                    delete this.suffixes[this.state.opt.lang][gender];
20082                    break;
20083                }
20084                this.suffixes[this.state.opt.lang][gender].push(ordinal);
20085            }
20086        }
20087    }
20088};
20089
20090CSL.Util.Ordinalizer.prototype.format = function (num, gender) {
20091    var str;
20092    num = parseInt(num, 10);
20093    str = "" + num;
20094    var suffix = "";
20095    var trygenders = [];
20096    if (gender) {
20097        trygenders.push(gender);
20098    }
20099    trygenders.push("neuter");
20100    if (this.state.locale[this.state.opt.lang].ord["1.0.1"]) {
20101        suffix = this.state.getTerm("ordinal",false,0,gender);
20102        var trygender;
20103        for (var i = 0, ilen = trygenders.length; i < ilen; i += 1) {
20104            trygender = trygenders[i];
20105            var ordinfo = this.state.locale[this.state.opt.lang].ord["1.0.1"];
20106            if (ordinfo["whole-number"][str] && ordinfo["whole-number"][str][trygender]) {
20107                suffix = this.state.getTerm(this.state.locale[this.state.opt.lang].ord["1.0.1"]["whole-number"][str][trygender],false,0,gender);
20108            } else if (ordinfo["last-two-digits"][str.slice(str.length - 2)] && ordinfo["last-two-digits"][str.slice(str.length - 2)][trygender]) {
20109                suffix = this.state.getTerm(this.state.locale[this.state.opt.lang].ord["1.0.1"]["last-two-digits"][str.slice(str.length - 2)][trygender],false,0,gender);
20110            } else if (ordinfo["last-digit"][str.slice(str.length - 1)] && ordinfo["last-digit"][str.slice(str.length - 1)][trygender]) {
20111                suffix = this.state.getTerm(this.state.locale[this.state.opt.lang].ord["1.0.1"]["last-digit"][str.slice(str.length - 1)][trygender],false,0,gender);
20112            }
20113            if (suffix) {
20114                break;
20115            }
20116        }
20117    } else {
20118        if (!gender) {
20119            // XXX hack to prevent crash on CSL 1.0 styles.
20120            // Reported by Carles.
20121            gender = undefined;
20122        }
20123        this.state.fun.ordinalizer.init();
20124        if ((num / 10) % 10 === 1 || (num > 10 && num < 20)) {
20125            suffix = this.suffixes[this.state.opt.lang][gender][3];
20126        } else if (num % 10 === 1 && num % 100 !== 11) {
20127            suffix = this.suffixes[this.state.opt.lang][gender][0];
20128        } else if (num % 10 === 2 && num % 100 !== 12) {
20129            suffix = this.suffixes[this.state.opt.lang][gender][1];
20130        } else if (num % 10 === 3 && num % 100 !== 13) {
20131            suffix = this.suffixes[this.state.opt.lang][gender][2];
20132        } else {
20133            suffix = this.suffixes[this.state.opt.lang][gender][3];
20134        }
20135    }
20136    str = str += suffix;
20137    return str;
20138};
20139
20140CSL.Util.Romanizer = function () {};
20141
20142CSL.Util.Romanizer.prototype.format = function (num) {
20143    var ret, pos, n, numstr, len;
20144    ret = "";
20145    if (num < 6000) {
20146        numstr = num.toString().split("");
20147        numstr.reverse();
20148        pos = 0;
20149        n = 0;
20150        len = numstr.length;
20151        for (pos = 0; pos < len; pos += 1) {
20152            n = parseInt(numstr[pos], 10);
20153            ret = CSL.ROMAN_NUMERALS[pos][n] + ret;
20154        }
20155    }
20156    return ret;
20157};
20158
20159
20160/**
20161 * Create a suffix formed from a list of arbitrary characters of arbitrary length.
20162 * <p>This is a <i>lot</i> harder than it seems.</p>
20163 */
20164CSL.Util.Suffixator = function (slist) {
20165    if (!slist) {
20166        slist = CSL.SUFFIX_CHARS;
20167    }
20168    this.slist = slist.split(",");
20169};
20170
20171/**
20172 * The format method.
20173 * <p>This method is used in generating ranges.  Every numeric
20174 * formatter (of which Suffixator is one) must be an instantiated
20175 * object with such a "format" method.</p>
20176 */
20177
20178CSL.Util.Suffixator.prototype.format = function (N) {
20179    // Many thanks to Avram Lyon for this code, and good
20180    // riddance to the several functions that it replaces.
20181    var X;
20182    N += 1;
20183    var key = "";
20184    do {
20185        X = ((N % 26) === 0) ? 26 : (N % 26);
20186        var key = this.slist[X-1] + key;
20187        N = (N - X) / 26;
20188    } while ( N !== 0 );
20189    return key;
20190};
20191
20192
20193CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
20194    //print("** processNumber() ItemObject[variable]="+ItemObject[variable]);
20195    var val;
20196
20197    var me = this;
20198
20199    var realVariable = variable;
20200    variable = (variable === "page-first") ? "page" : variable;
20201
20202    var fullformAnd = ",\\s+and\\s+|\\s+and\\s+";
20203    if (this.opt.lang.slice(0, 2) !== "en") {
20204        fullformAnd += "|,\\s+" + this.getTerm("and") + "\\s+|\\s+" + this.getTerm("and") + "\\s+";
20205    }
20206    var symbolAnd = "\\s*&\\s*";
20207    var andRex = new RegExp("^" + symbolAnd+ "$");
20208    var joinerMatchRex = new RegExp("(" + symbolAnd + "|" + fullformAnd + "|;\\s+|,\\s+|\\s*\\\\*[\\-\\u2013]+\\s*)", "g");
20209    var joinerSplitRex = new RegExp("(?:" + symbolAnd + "|" + fullformAnd + "|;\\s+|,\\s+|\\s*\\\\*[\\-\\u2013]+\\s*)");
20210
20211    // This guesses whether the symbol form is defined or not.
20212    // It's the best we can do, because when locales are built, all of the
20213    // holes are filled explictly with fallback values: the symbol form is never undefined.
20214    var localeAnd = this.getTerm("and");
20215    var localeAmpersand = this.getTerm("and", "symbol");
20216    if (localeAnd === localeAmpersand) {
20217        localeAmpersand = "&";
20218    }
20219
20220    // XXXX shadow_numbers should carry an array of objects with
20221    // XXXX full data for each. The test of a number should be
20222    // XXXX a separate function, possibly supported by a splitter
20223    // XXXX method also used here. Keep code for each action in one place,
20224    // XXXX to prevent debugging from becoming a nightmare.
20225
20226    // The capture pattern below would apply affixes to all sub-elements,
20227    // which is not what we want. Sub-elements should nest within, or
20228    // affixes should be edited. The latter is probably easier to handle.
20229    
20230    // values = [
20231    //   {
20232    //     label: "sec.",
20233    //     label-form: "plural",
20234    //     value: 100,
20235    //     styling: [object],
20236    //     numeric: true
20237    //     joiningSuffix: " & ",
20238    //   },
20239    //   {
20240    //     label: "sec.",
20241    //     label-form: "none",
20242    //     value: 103,
20243    //     styling: [object],
20244    //     numeric: true,
20245    //     joiningSuffix: ""
20246    //   }
20247    // ]
20248    
20249    function normalizeFieldValue(str) {
20250        str = str.trim();
20251        var m = str.match(/^([^ ]+)/);
20252        if (m && !CSL.STATUTE_SUBDIV_STRINGS[m[1]]) {
20253            var embeddedLabel = null;
20254            if (["locator", "locator-extra", "page"].indexOf(variable) > -1) {
20255                if (ItemObject.label) {
20256                    embeddedLabel = CSL.STATUTE_SUBDIV_STRINGS_REVERSE[ItemObject.label];
20257                } else {
20258                    embeddedLabel = "p.";
20259                }
20260            } else {
20261                embeddedLabel = CSL.STATUTE_SUBDIV_STRINGS_REVERSE[variable];
20262            }
20263            if (embeddedLabel) {
20264                str = embeddedLabel + " " + str;
20265            }
20266        }
20267        return str;
20268    }
20269    
20270
20271    function composeNumberInfo(origLabel, label, val, joiningSuffix, parsePosition) {
20272        joiningSuffix = joiningSuffix ? joiningSuffix : "";
20273        var info = {};
20274
20275        if (!label && !CSL.STATUTE_SUBDIV_STRINGS_REVERSE[variable]) {
20276            label = "var:"+ variable;
20277        }
20278
20279        if (label) {
20280            var m = label.match(/(\s*)([^\s]+)(\s*)/);
20281            if (realVariable === "page" && parsePosition === 0 && ["p.", "pp."].indexOf(m[2]) === -1) {
20282                info.gotosleepability = true;
20283                info.labelVisibility = true;
20284            } else {
20285                info.labelVisibility = false;
20286            }
20287            info.label = m[2];
20288            info.origLabel = origLabel;
20289            info.labelSuffix = m[3] ? m[3] : "";
20290            info.plural = 0;
20291        }
20292        
20293        var m = val.match(/^([0-9]*[a-zA-Z]+0*)?([0-9]+(?:[a-zA-Z]*|[-,a-zA-Z]+))$/);
20294        //var m = val.match(/^([0-9]*[a-zA-Z]0*)([0-9]+(?:[a-zA-Z]*|[-,a-zA-Z]+))$/);
20295        if (m) {
20296            info.particle = m[1] ? m[1] : "";
20297            info.value = m[2];
20298        } else {
20299            info.particle = "";
20300            info.value = val;
20301        }
20302        info.joiningSuffix = joiningSuffix.replace(/\s*-\s*/, "-");
20303        return info;
20304    }
20305
20306    function fixupSubsections(elems) {
20307        // This catches things like p. 12a-c, recombining content to yield
20308        // numeric true despite the hyphen.
20309        for (var i=elems.length-2;i>-1;i-=2) {
20310            if (elems[i] === "-"
20311               && elems[i-1].match(/^(?:(?:[a-z]|[a-z][a-z]|[a-z][a-z][a-z]|[a-z][a-z][a-z][a-z])\.  *)*[0-9]+[,a-zA-Z]+$/)
20312               && elems[i+1].match(/^[,a-zA-Z]+$/)) {
20313                elems[i-1] = elems.slice(i-1,i+2).join("");
20314                elems = elems.slice(0,i).concat(elems.slice(i+2));
20315            }
20316        }
20317        return elems;
20318    }
20319
20320    function parseString(str, defaultLabel) {
20321        defaultLabel = defaultLabel ? defaultLabel : "";
20322        
20323        str = normalizeFieldValue(str, defaultLabel);
20324
20325        var jmrex, jsrex, mystr;
20326        if ("page" === variable) {
20327            if (str.indexOf("\u2013") > -1) {
20328                str = str.replace(/\u2013/g, "-");
20329            }
20330        }
20331        if (str.indexOf("\\-") > -1) {
20332            jmrex = new RegExp(joinerMatchRex.source.replace("\\-", ""));
20333            jsrex = new RegExp(joinerSplitRex.source.replace("\\-", ""));
20334            var lst = str.split("\\-");
20335            for (var i=0,ilen=lst.length;i<ilen;i++) {
20336                lst[i] = lst[i].replace(/\-/g, "\u2013");
20337            }
20338            mystr = lst.join("\\-");
20339            mystr = mystr.replace(/\\/g, "");
20340        } else {
20341            jmrex = joinerMatchRex;
20342            jsrex = joinerSplitRex;
20343            mystr = str;
20344        }
20345        // jmrex = joinerMatchRex;
20346        // jsrex = joinerSplitRex;
20347        
20348        // Split chunks and collate delimiters.
20349        var elems = [];
20350        var m = mystr.match(jmrex);
20351        if (m) {
20352            var lst = mystr.split(jsrex);
20353            for (var i=0, ilen=m.length; i<ilen; i++) {
20354                if (m[i].match(andRex)) {
20355                    if (lst[i].match(/[a-zA-Z]$/) && lst[i].match(/^[a-zA-Z]/)) {
20356                        m[i] = localeAmpersand;
20357                    } else {
20358                        m[i] = " " + localeAmpersand + " ";
20359                    }
20360                }
20361            }
20362            var recombine = false;
20363            for (var i in lst) {
20364                if (("" + lst[i]).replace(/^[a-z]\.\s+/, "").match(/[^\s0-9ivxlcmIVXLCM]/)) {
20365                    //recombine = true;
20366                    break;
20367                }
20368            }
20369            if (recombine) {
20370                elems = [mystr];
20371            } else {
20372                for (var i=0,ilen=lst.length-1; i<ilen; i++) {
20373                    elems.push(lst[i]);
20374                    elems.push(m[i]);
20375                }
20376                elems.push(lst[lst.length-1]);
20377                //print("ELEMS: "+elems);
20378                elems = fixupSubsections(elems);
20379                //print("  fixup: "+elems);
20380            }
20381        } else {
20382            var elems = [mystr];
20383        }
20384        // Split elements within each chunk build list of value objects.
20385        var values = [];
20386        var label = defaultLabel;
20387        var origLabel = "";
20388        for (var i=0,ilen=elems.length;i<ilen;i += 2) {
20389            
20390            // AHA! HERE'S THE CULPRIT!!!
20391            // Words up to four characters are treated as honorary short-form labels.
20392            // Some valid labels are longer than four chars, so we stir those in explicitly
20393            
20394            var m = elems[i].match(/((?:^| )(?:[a-z]|[a-z][a-z]|[a-z][a-z][a-z]|[a-z][a-z][a-z][a-z]|subpara|subch|amend|bibliog|annot|illus|princ|intro|sched|subdiv|subsec)(?:\.| ) *)/g);
20395            if (m) {
20396                var lst = elems[i].split(/(?:(?:^| )(?:[a-z]|[a-z][a-z]|[a-z][a-z][a-z]|[a-z][a-z][a-z][a-z]|subpara|subch|amend|bibliog|annot|illus|princ|intro|sched|subdiv|subsec)(?:\.| ) *)/);
20397                // Head off disaster by merging parsed labels on non-numeric values into content
20398                for (var j=lst.length-1;j>0;j--) {
20399                    if (lst[j-1] && (!lst[j].match(/^[0-9]+([-;,:a-zA-Z]*)$/) || !lst[j-1].match(/^[0-9]+([-;,:a-zA-Z]*)$/))) {
20400                        lst[j-1] = lst[j-1] + m[j-1] + lst[j];
20401                        lst = lst.slice(0,j).concat(lst.slice(j+1));
20402                        m = m.slice(0,j-1).concat(m.slice(j));
20403                    }
20404                }
20405                // merge bad leading label into content
20406                if (m.length > 0) {
20407                    var slug = m[0].trim();
20408                    var notAlabel = !CSL.STATUTE_SUBDIV_STRINGS[slug]
20409                        || "undefined" === typeof me.getTerm(CSL.STATUTE_SUBDIV_STRINGS[slug])
20410                        || (["locator", "number", "locator-extra", "page"].indexOf(variable) === -1 && CSL.STATUTE_SUBDIV_STRINGS[slug] !== variable);
20411                    if (notAlabel) {
20412                        if (i === 0) {
20413                            m = m.slice(1);
20414                            lst[0] = lst[0] + " " + slug + " " + lst[1];
20415                            lst = lst.slice(0,1).concat(lst.slice(2));
20416                        }
20417                    } else {
20418                        origLabel = slug;
20419                    }
20420                }
20421
20422                for (var j=0,jlen=lst.length; j<jlen; j++) {
20423                    if (lst[j] || j === (lst.length-1)) {
20424                        var filteredOrigLabel;
20425                        label = m[j-1] ? m[j-1] : label;
20426                        if (origLabel === label.trim()) {
20427                            filteredOrigLabel = "";
20428                        } else {
20429                            filteredOrigLabel = origLabel;
20430                        }
20431                        //var origLabel = j > 1 ? m[j-1] : "";
20432                        mystr = lst[j] ? lst[j].trim() : "";
20433                        if (j === (lst.length-1)) {
20434                            values.push(composeNumberInfo(filteredOrigLabel, label, mystr, elems[i+1], i));
20435                        } else {
20436                            values.push(composeNumberInfo(filteredOrigLabel, label, mystr, null, i));
20437                        }
20438                    }
20439                }
20440            } else {
20441                var filteredOrigLabel;
20442                if (origLabel === label.trim()) {
20443                    filteredOrigLabel = "";
20444                } else {
20445                    filteredOrigLabel = origLabel;
20446                }
20447                values.push(composeNumberInfo(filteredOrigLabel, label, elems[i], elems[i+1]));
20448            }
20449        }
20450        return values;
20451    }
20452
20453    function setSpaces(values) {
20454        // Add space joins (is this really right?)
20455        for (var i=0,ilen=values.length-1;i<ilen;i++) {
20456            if (!values[i].joiningSuffix && values[i+1].label) {
20457                values[i].joiningSuffix = " ";
20458            }
20459        }
20460    }
20461
20462    function fixNumericAndCount(values, i, currentLabelInfo) {
20463        var master = values[currentLabelInfo.pos];
20464        var val = values[i].value;
20465        var isEscapedHyphen = master.joiningSuffix === "\\-";
20466        if (val.particle && val.particle !== master.particle) {
20467            currentLabelInfo.collapsible = false;
20468        }
20469        var mVal = val.match(/^[0-9]+([-,:a-zA-Z]*)$/);
20470        var mCurrentLabel = master.value.match(/^(?:[0-9]+|[ixv]+)([-,:a-zA-Z]*|\-[\-0-9]+)$/);
20471        if (!val || !mVal || !mCurrentLabel || isEscapedHyphen) {
20472            currentLabelInfo.collapsible = false;
20473            if (!val || !mCurrentLabel) {
20474                currentLabelInfo.numeric = false;
20475            }
20476            if (isEscapedHyphen) {
20477                currentLabelInfo.count--;
20478            }
20479        }
20480        if ((mVal && mVal[1]) || (mCurrentLabel && mCurrentLabel[1])) {
20481            currentLabelInfo.collapsible = false;
20482        }
20483        if (undefined === values[i].collapsible) {
20484            for (var j=i,jlen=i+currentLabelInfo.count;j<jlen;j++) {
20485                if (isNaN(parseInt(values[j].value)) && !values[j].value.match(/^[ivxlcmIVXLCM]+$/)) {
20486                    values[j].collapsible = false;
20487                } else {
20488                    values[j].collapsible = true;
20489                }
20490            }
20491            currentLabelInfo.collapsible = values[i].collapsible;
20492        }
20493        var isCollapsible = currentLabelInfo.collapsible;
20494        for (var j=currentLabelInfo.pos,jlen=(currentLabelInfo.pos + currentLabelInfo.count); j<jlen; j++) {
20495            if (currentLabelInfo.count > 1 && isCollapsible) {
20496                values[j].plural = 1;
20497            }
20498            values[j].numeric = currentLabelInfo.numeric;
20499            values[j].collapsible = currentLabelInfo.collapsible;
20500        }
20501    }
20502
20503    function fixLabelVisibility(values, groupStartPos, currentLabelInfo) {
20504        if (currentLabelInfo.label.slice(0, 4) !== "var:") {
20505            if (currentLabelInfo.pos === 0) {
20506                if (["locator", "number", "locator-extra", "page"].indexOf(variable) > -1) {
20507                    // Actually, shouldn't we do this always?
20508                    if ("undefined" === typeof me.getTerm(CSL.STATUTE_SUBDIV_STRINGS[currentLabelInfo.label])) {
20509                        values[currentLabelInfo.pos].labelVisibility = true;
20510                    }
20511                }
20512                // If there is an explicit
20513                // label embedded at the start of a field that
20514                // does not match the context, it should be
20515                // marked for rendering.
20516                if (["locator", "number", "locator-extra", "page"].indexOf(variable) === -1) {
20517                    if (CSL.STATUTE_SUBDIV_STRINGS[currentLabelInfo.label] !== variable) {
20518                        values[0].labelVisibility = true;
20519                    }
20520                }
20521            } else {
20522                // Also, mark initial mid-field labels for
20523                // rendering.
20524                //if (values[i-1].label !== values[i].label && currentLabelInfo.label.slice(0, 4) !== "var:") {
20525                values[currentLabelInfo.pos].labelVisibility = true;
20526                //}
20527            }
20528        }
20529    }
20530    
20531    function setPluralsAndNumerics(values) {
20532        if (values.length === 0) {
20533            return;
20534        }
20535        var groupStartPos = 0;
20536        var groupCount = 1;
20537        
20538        for (var i=1,ilen=values.length;i<ilen;i++) {
20539            var lastVal = values[i-1];
20540            var thisVal = values[i];
20541            if (lastVal.label === thisVal.label && lastVal.particle === lastVal.particle) {
20542                groupCount++;
20543            } else {
20544                var currentLabelInfo = JSON.parse(JSON.stringify(values[groupStartPos]));
20545                currentLabelInfo.pos = groupStartPos;
20546                currentLabelInfo.count = groupCount;
20547                currentLabelInfo.numeric = true;
20548                fixNumericAndCount(values, groupStartPos, currentLabelInfo);
20549                if (lastVal.label !== thisVal.label) {
20550                    fixLabelVisibility(values, groupStartPos, currentLabelInfo);
20551                }
20552                groupStartPos = i;
20553                groupCount = 1;
20554            }
20555        }
20556        // Not sure why this repetition is necessary?
20557        var currentLabelInfo = JSON.parse(JSON.stringify(values[groupStartPos]));
20558        currentLabelInfo.pos = groupStartPos;
20559        currentLabelInfo.count = groupCount;
20560        currentLabelInfo.numeric = true;
20561        fixNumericAndCount(values, groupStartPos, currentLabelInfo);
20562        fixLabelVisibility(values, groupStartPos, currentLabelInfo);
20563        if (values.length && values[0].numeric && variable.slice(0, 10) === "number-of-") {
20564            if (parseInt(ItemObject[realVariable], 10) > 1) {
20565                values[0].plural = 1;
20566            }
20567        }
20568    }        
20569
20570    function stripHyphenBackslash(joiningSuffix) {
20571        return joiningSuffix.replace("\\-", "-");
20572    }
20573
20574    function setStyling(values) {
20575        var masterNode = CSL.Util.cloneToken(node);
20576        var masterStyling = new CSL.Token();
20577        if (!me.tmp.just_looking) {
20578            // Per discussion @ https://discourse.citationstyles.org/t/formatting-attributes-and-hyphen/1518
20579            masterStyling.decorations = masterNode.decorations;
20580            masterNode.decorations = [];
20581            //for (var j=masterNode.decorations.length-1;j>-1;j--) {
20582            //    if (masterNode.decorations[j][0] === "@quotes") {
20583            //        // Add to styling
20584            //        masterStyling.decorations = masterStyling.decorations.concat(masterNode.decorations.slice(j, j+1));
20585            //        // Remove from node
20586            //        masterNode.decorations = masterNode.decorations.slice(0, j).concat(masterNode.decorations.slice(j+1));
20587            //    }
20588            //}
20589            masterStyling.strings.prefix = masterNode.strings.prefix;
20590            masterNode.strings.prefix = "";
20591            masterStyling.strings.suffix = masterNode.strings.suffix;
20592            masterNode.strings.suffix = "";
20593        }
20594        var masterLabel = values.length ? values[0].label : null;
20595        if (values.length) {
20596            for (var i=0,ilen=values.length; i<ilen; i++) {
20597                var val = values[i];
20598                // Clone node, make styling parameters on each instance sane.
20599                var newnode = CSL.Util.cloneToken(masterNode);
20600                newnode.gender = node.gender;
20601                if (masterLabel === val.label) {
20602                    newnode.formatter = node.formatter;
20603                }
20604                if (val.numeric) {
20605                    newnode.successor_prefix = val.successor_prefix;
20606                }
20607                newnode.strings.suffix = newnode.strings.suffix + stripHyphenBackslash(val.joiningSuffix);
20608                val.styling = newnode;
20609            }
20610            if (!me.tmp.just_looking) {
20611                if (values[0].value.slice(0,1) === "\"" && values[values.length-1].value.slice(-1) === "\"") {
20612                    values[0].value = values[0].value.slice(1);
20613                    values[values.length-1].value = values[values.length-1].value.slice(0,-1);
20614                    masterStyling.decorations.push(["@quotes", true]);
20615                }
20616            }
20617        }
20618        return masterStyling;
20619    }
20620
20621    function checkTerm(variable, val) {
20622        var ret = true;
20623        if (["locator", "locator-extra", "page"].indexOf(variable) > -1) {
20624            var label;
20625            if (val.origLabel) {
20626                label = val.origLabel;
20627            } else {
20628                label = val.label;
20629            }
20630            ret = !!me.getTerm(CSL.STATUTE_SUBDIV_STRINGS[label]);
20631        }
20632        return ret;
20633    }
20634
20635    function checkPage(variable, val) {
20636        return "page" === variable
20637            || (["locator", "locator-extra"].indexOf(variable) > -1 && (["p."].indexOf(val.label) > -1 || ["p."].indexOf(val.origLabel) > -1));
20638    }
20639    
20640    function fixupRangeDelimiter(variable, val, rangeDelimiter, isNumeric) {
20641        var isPage = checkPage(variable, val);
20642        var hasTerm = checkTerm(variable, val);
20643        if (hasTerm && rangeDelimiter === "-") {
20644            if (isNumeric) {
20645                if (isPage || ["locator", "locator-extra", "issue", "volume", "edition", "number"].indexOf(variable) > -1) {
20646                    rangeDelimiter = me.getTerm("page-range-delimiter");
20647                    if (!rangeDelimiter) {
20648                        rangeDelimiter = "\u2013";
20649                    }
20650                }
20651                if (variable === "collection-number") {
20652                    rangeDelimiter = me.getTerm("year-range-delimiter");
20653                    if (!rangeDelimiter) {
20654                        rangeDelimiter = "\u2013";
20655                    }
20656                }
20657            }
20658        }
20659        //if (rangeDelimiter === "\\-") {
20660        //    rangeDelimiter = "-";
20661        //}
20662        return rangeDelimiter;
20663    }
20664
20665    function manglePageNumbers(values, i, currentInfo) {
20666        if (i<1) {
20667            return;
20668        }
20669        if (currentInfo.count !== 2) {
20670            return;
20671        }
20672        if (values[i-1].particle !== values[i].particle) {
20673            return;
20674        }
20675        if (values[i-1].joiningSuffix !== "-") {
20676            currentInfo.count = 1;
20677            return;
20678        }
20679        if (!me.opt["page-range-format"] && (parseInt(values[i-1].value, 10) > parseInt(values[i].value, 10))) {
20680            values[i-1].joiningSuffix = fixupRangeDelimiter(variable, values[i], values[i-1].joiningSuffix, true);
20681            return;
20682        }
20683        var val = values[i];
20684
20685        var isPage = checkPage(variable, val);
20686        var str;
20687        if (isPage && !isNaN(parseInt(values[i-1].value)) && !isNaN(parseInt(values[i].value))) {
20688            str = values[i-1].particle + values[i-1].value + " - " + values[i].particle + values[i].value;
20689            str = me.fun.page_mangler(str);
20690        } else {
20691            // if (("" + values[i-1].value).match(/[0-9]$/) && ("" + values[i].value).match(/^[0-9]/)) {
20692            if (("" + values[i-1].value).match(/^([0-9]+|[ivxlcmIVXLCM]+)$/) && ("" + values[i].value).match(/^([0-9]+|[ivxlcmIVXLCM]+)$/)) {
20693                values[i-1].joiningSuffix = me.getTerm("page-range-delimiter");
20694            }
20695            str = values[i-1].value + stripHyphenBackslash(values[i-1].joiningSuffix) + values[i].value;
20696        }
20697        var m = str.match(/^((?:[0-9]*[a-zA-Z]+0*))?([0-9]+[a-z]*)(\s*[^0-9]+\s*)([-,a-zA-Z]?0*)([0-9]+[a-z]*)$/);
20698        // var m = str.match(/^((?:[0-9]*[a-zA-Z]+0*))?([0-9]+[a-z]*)(\s*[^0-9]+\s*)([-,a-zA-Z]?0*)([0-9]+[a-z]*)$/);
20699        if (m) {
20700            var rangeDelimiter = m[3];
20701            rangeDelimiter = fixupRangeDelimiter(variable, val, rangeDelimiter, values[i].numeric);
20702            values[i-1].particle = m[1];
20703            values[i-1].value = m[2];
20704            values[i-1].joiningSuffix = rangeDelimiter;
20705            values[i].particle = m[4];
20706            values[i].value = m[5];
20707        }
20708        currentInfo.count = 0;
20709    }
20710    
20711    function fixRanges(values) {
20712
20713        if (!node) {
20714            return;
20715        }
20716        if (["page", "chapter-number", "collection-number", "edition", "issue", "number", "number-of-pages", "number-of-volumes", "volume", "locator", "locator-extra"].indexOf(variable) === -1) {
20717            return;
20718        }
20719
20720        var currentInfo = {
20721            count: 0,
20722            label: null,
20723            lastHadRangeDelimiter: false
20724        };
20725
20726        for (var i=0,ilen=values.length; i<ilen; i++) {
20727            var val = values[i];
20728            if (!val.collapsible) {
20729                currentInfo.count = 0;
20730                currentInfo.label = null;
20731                var isNumeric = val.numeric;
20732                val.joiningSuffix = fixupRangeDelimiter(variable, val, val.joiningSuffix, isNumeric);
20733            } else if (currentInfo.label === val.label && val.joiningSuffix === "-") {
20734                // So if there is a hyphen here, and none previous, reset to 1
20735                currentInfo.count = 1;
20736            } else if (currentInfo.label === val.label && val.joiningSuffix !== "-") {
20737                // If there is NO hyphen here, count up
20738                currentInfo.count++;
20739                if (currentInfo.count === 2) {
20740                    manglePageNumbers(values, i, currentInfo);
20741                }
20742            } else if (currentInfo.label !== val.label) {
20743                // If the label doesn't match and count is 2, process
20744                currentInfo.label = val.label;
20745                currentInfo.count = 1;
20746            } else {
20747                // Safety belt: label doesn't match and count is some other value, so reset to 1
20748                // This never happens, though.
20749                currentInfo.count = 1;
20750                currentInfo.label = val.label;
20751            }
20752        }
20753        // Finally clear, if needed
20754        if (currentInfo.count === 2) {
20755            manglePageNumbers(values, values.length-1, currentInfo);
20756        }
20757    }
20758
20759    function setVariableParams(shadow_numbers, realVariable, values) {
20760        var obj = shadow_numbers[realVariable];
20761        if (values.length) {
20762            obj.numeric = values[0].numeric;
20763            obj.collapsible = values[0].collapsible;
20764            obj.plural = values[0].plural;
20765            obj.label = CSL.STATUTE_SUBDIV_STRINGS[values[0].label];
20766            if (variable === "number" && obj.label === "issue" && me.getTerm("number")) {
20767                obj.label = "number";
20768            }
20769        }
20770    }
20771
20772    // Split out the labels and values.
20773
20774    // short-circuit if object exists: if numeric, set styling, no other action
20775    if (node && this.tmp.shadow_numbers[realVariable] && this.tmp.shadow_numbers[realVariable].values.length) {
20776        var values = this.tmp.shadow_numbers[realVariable].values;
20777        fixRanges(values);
20778        //if (!this.tmp.shadow_numbers[variable].masterStyling && !this.tmp.just_looking) {
20779            this.tmp.shadow_numbers[realVariable].masterStyling = setStyling(values);
20780        //}
20781        return;
20782    }
20783
20784    // info.styling = node;
20785
20786    // This carries value, pluralization and numeric info for use in other contexts.
20787    // XXX We used to use one set of params for the entire variable value.
20788    // XXX Now params are set on individual objects, of which there may be several after parsing.
20789    if (!this.tmp.shadow_numbers[realVariable]) {
20790        this.tmp.shadow_numbers[realVariable] = {
20791            values:[]
20792        };
20793    }
20794    //this.tmp.shadow_numbers[variable].values = [];
20795    //this.tmp.shadow_numbers[variable].plural = 0;
20796    //this.tmp.shadow_numbers[variable].numeric = false;
20797    //this.tmp.shadow_numbers[variable].label = false;
20798
20799    if (!ItemObject) {
20800        return;
20801    }
20802
20803    // Possibly apply multilingual transform
20804    var languageRole = CSL.LangPrefsMap[variable];
20805    if (languageRole) {
20806        var localeType = this.opt["cite-lang-prefs"][languageRole][0];
20807        val = this.transform.getTextSubField(ItemObject, realVariable, "locale-"+localeType, true);
20808        val = val.name;
20809    } else {
20810        val = ItemObject[realVariable];
20811    }
20812
20813    if (val && realVariable === "number" && ItemObject.type === "legal_case") {
20814        val = val.replace(/[\\]*-/g, "\\-");
20815    }
20816
20817    // XXX HOLDING THIS
20818    // Apply short form ONLY if first element tests is-numeric=false
20819    if (val && this.sys.getAbbreviation) {
20820        // RefMe bug report: print("XX D'oh! (3): "+num);
20821        // true as the fourth argument suppresses update of the UI
20822
20823        // No need for this.
20824        //val = ("" + val).replace(/^\"/, "").replace(/\"$/, "");
20825        if (this.sys.normalizeAbbrevsKey) {
20826            var normval = this.sys.normalizeAbbrevsKey(realVariable, val);
20827        } else {
20828            var normval = val;
20829        }
20830        var jurisdiction = this.transform.loadAbbreviation(ItemObject.jurisdiction, "number", normval, ItemObject.language);
20831        if (this.transform.abbrevs[jurisdiction].number) {
20832            if (this.transform.abbrevs[jurisdiction].number[normval]) {
20833                val = this.transform.abbrevs[jurisdiction].number[normval];
20834            } else {
20835                
20836                // *** This is terrible ***
20837                
20838                // Strings rendered via cs:number should not be added to the abbreviations
20839                // UI unless they test non-numeric. The test happens below.
20840                if ("undefined" !== typeof this.transform.abbrevs[jurisdiction].number[normval]) {
20841                    delete this.transform.abbrevs[jurisdiction].number[normval];
20842                }
20843            }
20844        }
20845    }
20846
20847    //   {
20848    //     label: "sec.",
20849    //     labelForm: "plural",
20850    //     labelVisibility: true,
20851    //     value: 100,
20852    //     styling: [object],
20853    //     numeric: true
20854    //     joiningSuffix: " & ",
20855    //   },
20856
20857    // Process only if there is a value.
20858    if ("undefined" !== typeof val && ("string" === typeof val || "number" === typeof val)) {
20859        if ("number" === typeof val) {
20860            val = "" + val;
20861        }
20862        var defaultLabel = CSL.STATUTE_SUBDIV_STRINGS_REVERSE[variable];
20863
20864        if (this.tmp.shadow_numbers[realVariable].values.length === 0) {
20865            // XXX
20866            var values = parseString(val, defaultLabel);
20867
20868            setSpaces(values);
20869            //print("setSpaces(): "+JSON.stringify(values, null, 2));
20870
20871            setPluralsAndNumerics(values);
20872            //print("setPluralsAndNumerics(): "+JSON.stringify(values, null, 2));
20873
20874            for (var obj of values) {
20875                if (!obj.numeric) obj.plural = 0;
20876            }
20877            this.tmp.shadow_numbers[realVariable].values = values;
20878            // me.sys.print(JSON.stringify(values))
20879
20880            if (node) {
20881                fixRanges(values);
20882                
20883                this.tmp.shadow_numbers[realVariable].masterStyling = setStyling(values);
20884                // me.sys.print("setStyling(): "+JSON.stringify(values, null, 2));
20885            }
20886            setVariableParams(this.tmp.shadow_numbers, realVariable, values);
20887        }
20888        
20889        // hack in support for non-numeric numerics like "91 Civ. 5442 (RPP)|91 Civ. 5471"
20890        var info = this.tmp.shadow_numbers[realVariable];
20891        if (variable === "number") {
20892            if (info.values.length === 1 && info.values[0].value.indexOf("|") > -1) {
20893                info.values[0].value = info.values[0].value.replace(/\|/g, ", ");
20894                info.values[0].numeric = true;
20895                info.values[0].plural = 1;
20896                info.values[0].collapsible = false;
20897                info.numeric = true;
20898                info.plural = 1;
20899                info.collapsible = false;
20900            }
20901        }
20902        if (info.values.length === 1) {
20903            if (info.values[0].value.match(/^[0-9]+(?:\/[0-9]+)+$/)) {
20904                info.values[0].numeric = true;
20905                info.values[0].plural = 0;
20906                info.values[0].collapsible = false;
20907                info.numeric = true;
20908                info.plural = 0;
20909                info.collapsible = false;
20910            }
20911        }
20912        if (variable === "page") {
20913            if (info.values.length > 0) {
20914                if (info.values[0].gotosleepability) {
20915                    info.labelForm = "short";
20916                }
20917            }
20918        }
20919        //this.sys.print("OK "+JSON.stringify(values, ["label", "origLabel", "labelSuffix", "particle", "collapsible", "value", "numeric", "joiningSuffix", "labelVisibility", "plural"], 2));
20920    }
20921};
20922
20923CSL.Util.outputNumericField = function(state, varname, itemID) {
20924
20925    state.output.openLevel(state.tmp.shadow_numbers[varname].masterStyling);
20926    var masterStyling = state.tmp.shadow_numbers[varname].masterStyling;
20927    var nums = state.tmp.shadow_numbers[varname].values;
20928    var masterLabel = nums.length ? nums[0].label : null;
20929    var labelForm = state.tmp.shadow_numbers[varname].labelForm;
20930    var tryStatic = state.tmp.group_context.tip.label_static;
20931    var embeddedLabelForm;
20932    if (labelForm) {
20933        embeddedLabelForm = labelForm;
20934    } else {
20935        embeddedLabelForm = "short";
20936        //labelForm = "short";
20937    }
20938    var labelCapitalizeIfFirst = state.tmp.shadow_numbers[varname].labelCapitalizeIfFirst;
20939    var labelDecorations = state.tmp.shadow_numbers[varname].labelDecorations;
20940    var lastLabelName = null;
20941
20942    for (var i=0,ilen=nums.length;i<ilen;i++) {
20943        var num = nums[i];
20944        var label = "";
20945        var labelName;
20946        if (num.label) {
20947            if ('var:' === num.label.slice(0,4)) {
20948                labelName = num.label.slice(4);
20949            } else {
20950                labelName = CSL.STATUTE_SUBDIV_STRINGS[num.label];
20951            }
20952            if (labelName) {
20953                // Simplify this some day.
20954                if (num.label === masterLabel) {
20955                    if (tryStatic) {
20956                        label = state.getTerm(labelName, "static", num.plural);
20957                        if (label.indexOf("%s") === -1) {
20958                            label = "";
20959                        }
20960                    }
20961                    if (!label) {
20962                        label = state.getTerm(labelName, labelForm, num.plural);
20963                    }
20964                } else {
20965                    if (tryStatic) {
20966                        label = state.getTerm(labelName, "static", num.plural);
20967                        if (label.indexOf("%s") === -1) {
20968                            label = "";
20969                        }
20970                    }
20971                    if (!label) {
20972                        label = state.getTerm(labelName, embeddedLabelForm, num.plural);
20973                    }
20974                }
20975                if (labelCapitalizeIfFirst) {
20976                    label = CSL.Output.Formatters["capitalize-first"](state, label);
20977                }
20978            }
20979        }
20980        var labelPlaceholderPos = -1;
20981        if (label) {
20982            labelPlaceholderPos = label.indexOf("%s");
20983        }
20984        var numStyling = CSL.Util.cloneToken(num.styling);
20985        numStyling.formatter = num.styling.formatter;
20986        numStyling.type = num.styling.type;
20987        numStyling.num = num.styling.num;
20988        numStyling.gender = num.styling.gender;
20989        
20990        if (labelPlaceholderPos > 0 && labelPlaceholderPos < (label.length-2)) {
20991            numStyling.strings.prefix += label.slice(0,labelPlaceholderPos);
20992            numStyling.strings.suffix = label.slice(labelPlaceholderPos+2) + numStyling.strings.suffix;
20993        } else if (num.labelVisibility) {
20994            if (!label) {
20995                label = num.label;
20996                labelName = num.label;
20997            }
20998            if (labelPlaceholderPos > 0) {
20999                var prefixLabelStyling = new CSL.Token();
21000                prefixLabelStyling.decorations = labelDecorations;
21001                state.output.append(label.slice(0,labelPlaceholderPos), prefixLabelStyling);
21002            } else if (labelPlaceholderPos === (label.length-2) || labelPlaceholderPos === -1) {
21003                // And add a trailing delimiter.
21004                state.output.append(label+num.labelSuffix, "empty");
21005            }
21006        }
21007        CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, masterStyling.strings.prefix, null, masterStyling, `${num.particle}${num.value}`);
21008        if (num.collapsible) {
21009            var blob;
21010            if (num.value.match(/^[1-9][0-9]*$/) && Number.isSafeInteger(parseInt(num.value, 10))) {
21011                blob = new CSL.NumericBlob(state, num.particle, parseInt(num.value, 10), numStyling, itemID);
21012            } else {
21013                blob = new CSL.NumericBlob(state, num.particle, num.value, numStyling, itemID);
21014            }
21015            if ("undefined" === typeof blob.gender) {
21016                blob.gender = state.locale[state.opt.lang]["noun-genders"][varname];
21017            }
21018            state.output.append(blob, "literal");
21019        } else {
21020            state.output.append(num.particle + num.value, numStyling);
21021        }
21022        if (labelPlaceholderPos === 0 && labelPlaceholderPos < (label.length-2)) {
21023            // Only and always if this is the last entry of this label
21024            if (lastLabelName === null) {
21025                lastLabelName = labelName;
21026            }
21027            if (labelName !== lastLabelName || i === (nums.length-1)) {
21028                var suffixLabelStyling = new CSL.Token();
21029                suffixLabelStyling.decorations = labelDecorations;
21030                state.output.append(label.slice(labelPlaceholderPos+2), suffixLabelStyling);
21031            }
21032        }
21033        lastLabelName = labelName;
21034        state.tmp.term_predecessor = true;
21035    }
21036    state.output.closeLevel();
21037};
21038
21039/*global CSL: true */
21040
21041CSL.Util.PageRangeMangler = {};
21042
21043CSL.Util.PageRangeMangler.getFunction = function (state, rangeType) {
21044    var rangerex, pos, len, stringify, listify, expand, minimize, minimize_internal, chicago15, chicago16, lst, m, b, e, ret, begin, end, ret_func;
21045    
21046    var range_delimiter = state.getTerm(rangeType + "-range-delimiter");
21047
21048    rangerex = /([0-9]*[a-zA-Z]+0*)?([0-9]+[a-z]*)\s*(?:\u2013|-)\s*([0-9]*[a-zA-Z]+0*)?([0-9]+[a-z]*)/;
21049
21050    stringify = function (lst) {
21051        len = lst.length;
21052        for (pos = 1; pos < len; pos += 2) {
21053            if ("object" === typeof lst[pos]) {
21054                lst[pos] = lst[pos].join("");
21055            }
21056        }
21057        var ret = lst.join("");
21058        ret = ret.replace(/([^\\])\-/g, "$1"+state.getTerm(rangeType + "-range-delimiter"));
21059        return ret;
21060    };
21061
21062    listify = function (str) {
21063        var m, lst, ret;
21064        // Normalized delimiter form, for use in regexps
21065        var hyphens = "\\s+\\-\\s+";
21066        // Normalize delimiters to hyphen wrapped in single spaces
21067        var this_range_delimiter = range_delimiter === "-" ? "" : range_delimiter;
21068        var delimRex = new RegExp("([^\\\\])[-" + this_range_delimiter + "\\u2013]", "g");
21069        str = str.replace(delimRex, "$1 - ").replace(/\s+-\s+/g, " - ");
21070        // Workaround for Internet Explorer
21071        //var rexm = new RegExp("((?:[0-9]*[a-zA-Z]+)?[0-9]+" + hyphens + "(?:[0-9]*[a-zA-Z]+)?[0-9]+)", "g");
21072        //var rexlst = new RegExp("(?:[0-9]*[a-zA-Z]+)?[0-9]+" + hyphens + "(?:[0-9]*[a-zA-Z]+)?[0-9]+");
21073        var rexm = new RegExp("((?:[0-9]*[a-zA-Z]+0*)?[0-9]+[a-z]*" + hyphens + "(?:[0-9]*[a-zA-Z]+0*)?[0-9]+[a-z]*)", "g");
21074        var rexlst = new RegExp("(?:[0-9]*[a-zA-Z]+0*)?[0-9]+[a-z]*" + hyphens + "(?:[0-9]*[a-zA-Z]+0*)?[0-9]+[a-z]*");
21075        m = str.match(rexm);
21076        lst = str.split(rexlst);
21077        if (lst.length === 0) {
21078            ret = m;
21079        } else {
21080            ret = [lst[0]];
21081            for (pos = 1, len = lst.length; pos < len; pos += 1) {
21082                ret.push(m[pos - 1].replace(/\s*\-\s*/g, "-"));
21083                ret.push(lst[pos]);
21084            }
21085        }
21086        return ret;
21087    };
21088
21089    expand = function (str) {
21090        str = "" + str;
21091        lst = listify(str);
21092        len = lst.length;
21093        for (pos = 1; pos < len; pos += 2) {
21094            m = lst[pos].match(rangerex);
21095            if (m) {
21096                if (!m[3] || m[1] === m[3]) {
21097                    if (m[4].length < m[2].length) {
21098                        m[4] = m[2].slice(0, (m[2].length - m[4].length)) + m[4];
21099                    }
21100                    if (parseInt(m[2], 10) < parseInt(m[4], 10)) {
21101                        m[3] = range_delimiter + (m[1] ? m[1] : "");
21102                        lst[pos] = m.slice(1);
21103                    }
21104                }
21105            }
21106            if ("string" === typeof lst[pos]) {
21107                lst[pos] = lst[pos].replace(/\-/g, range_delimiter);
21108            }
21109        }
21110        return lst;
21111    };
21112
21113    minimize = function (lst, minchars, isyear) {
21114        len = lst.length;
21115        for (var i = 1, ilen = lst.length; i < ilen; i += 2) {
21116            if ("object" === typeof lst[i]) {
21117                lst[i][3] = minimize_internal(lst[i][1], lst[i][3], minchars, isyear);
21118                if (lst[i][2].slice(1) === lst[i][0]) {
21119                    lst[i][2] = range_delimiter;
21120                }
21121            }
21122        }
21123        return stringify(lst);
21124    };
21125
21126    minimize_internal = function (begin, end, minchars, isyear) {
21127        if (!minchars) {
21128            minchars = 0;
21129        }
21130        b = ("" + begin).split("");
21131        e = ("" + end).split("");
21132        ret = e.slice();
21133        ret.reverse();
21134        if (b.length === e.length) {
21135            for (var i = 0, ilen = b.length; i < ilen; i += 1) {
21136                if (b[i] === e[i] && ret.length > minchars) {
21137                    ret.pop();
21138                } else {
21139                    if (minchars && isyear && ret.length === 3) {
21140                        var front = b.slice(0, i);
21141                        front.reverse();
21142                        ret = ret.concat(front);
21143                    }
21144                    break;
21145                }
21146            }
21147        }
21148        ret.reverse();
21149        return ret.join("");
21150    };
21151
21152    chicago15 = function (lst) {
21153        len = lst.length;
21154        for (pos = 1; pos < len; pos += 2) {
21155            if ("object" === typeof lst[pos]) {
21156                m = lst[pos];
21157                begin = parseInt(m[1], 10);
21158                end = parseInt(m[3], 10);
21159                if (begin > 100 && begin % 100 && parseInt((begin / 100), 10) === parseInt((end / 100), 10)) {
21160                    m[3] = "" + (end % 100);
21161                } else if (begin >= 10000) {
21162                    m[3] = "" + (end % 1000);
21163                }
21164            }
21165            if (m[2].slice(1) === m[0]) {
21166                m[2] = range_delimiter;
21167            }
21168        }
21169        return stringify(lst);
21170    };
21171
21172    chicago16 = function (lst) {
21173        len = lst.length;
21174        for (pos = 1; pos < len; pos += 2) {
21175            if ("object" === typeof lst[pos]) {
21176                m = lst[pos];
21177                begin = parseInt(m[1], 10);
21178                end = parseInt(m[3], 10);
21179                e = "" + end;
21180                if (begin > 100 && begin % 100) {
21181                    for (var i = 2; i < e.length; i++) {
21182                        var divisor = Math.pow(10, i);
21183                        if (Math.floor(begin / divisor) === Math.floor(end / divisor)) {
21184                            m[3] = "" + (end % divisor);
21185                            break;
21186                        }
21187                    }
21188                }
21189            }
21190            if (m[2].slice(1) === m[0]) {
21191                m[2] = range_delimiter;
21192            }
21193        }
21194        return stringify(lst);
21195    };
21196
21197    //
21198    // The top-level option handlers.
21199    //
21200    var sniff = function (str, func, minchars, isyear) {
21201        var ret;
21202		str = "" + str;
21203		var lst = expand(str);
21204        var ret = func(lst, minchars, isyear);
21205        return ret;
21206    };
21207    if (!state.opt[rangeType + "-range-format"]) {
21208        ret_func = function (str) {
21209            //return str.replace("-", "\u2013", "g");
21210            return sniff(str, stringify);
21211        };
21212    } else if (state.opt[rangeType + "-range-format"] === "expanded") {
21213        ret_func = function (str) {
21214            return sniff(str, stringify);
21215        };
21216    } else if (state.opt[rangeType + "-range-format"] === "minimal") {
21217        ret_func = function (str) {
21218            return sniff(str, minimize);
21219        };
21220    } else if (state.opt[rangeType + "-range-format"] === "minimal-two") {
21221        ret_func = function (str, isyear) {
21222            return sniff(str, minimize, 2, isyear);
21223        };
21224    } else if (state.opt[rangeType + "-range-format"] === "chicago") {
21225        ret_func = function (str) {
21226            return sniff(str, chicago15);
21227        };
21228    } else if (state.opt[rangeType + "-range-format"] === "chicago-15") {
21229        ret_func = function (str) {
21230            return sniff(str, chicago15);
21231        };
21232    } else if (state.opt[rangeType + "-range-format"] === "chicago-16") {
21233        ret_func = function (str) {
21234            return sniff(str, chicago16);
21235        };
21236    }
21237
21238    return ret_func;
21239};
21240
21241
21242/*global CSL: true */
21243
21244// Use a state machine
21245
21246// Okay, good!
21247// Needs some tweaks:
21248// 1. First pass: quotes only
21249//    Special: Convert all sandwiched single-quote markup to apostrophe
21250// 2. Second pass: tags
21251
21252CSL.Util.FlipFlopper = function(state) {
21253    
21254    /**
21255     * INTERNAL
21256     */
21257
21258    var _nestingState = [];
21259
21260    var _nestingData = {
21261        "<span class=\"nocase\">": {
21262            type: "nocase",
21263            opener: "<span class=\"nocase\">",
21264            closer: "</span>",
21265            attr: null,
21266            outer: null,
21267            flipflop: null
21268        },
21269        "<span class=\"nodecor\">": {
21270            type: "nodecor",
21271            opener: "<span class=\"nodecor\">",
21272            closer: "</span>",
21273            attr: "@class",
21274            outer: "nodecor",
21275            flipflop: {
21276                "nodecor": "nodecor"
21277            }
21278        },
21279        "<span style=\"font-variant:small-caps;\">": {
21280            type: "tag",
21281            opener: "<span style=\"font-variant:small-caps;\">",
21282            closer: "</span>",
21283            attr: "@font-variant",
21284            outer: "small-caps",
21285            flipflop: {
21286                "small-caps": "normal",
21287                "normal": "small-caps"
21288            }
21289        },
21290        "<sc>": {
21291            type: "tag",
21292            opener: "<sc>",
21293            closer: "</sc>",
21294            attr: "@font-variant",
21295            outer: "small-caps",
21296            flipflop: {
21297                "small-caps": "normal",
21298                "normal": "small-caps"
21299            }
21300        },
21301        "<i>": {
21302            type: "tag",
21303            opener: "<i>",
21304            closer: "</i>",
21305            attr: "@font-style",
21306            outer: "italic",
21307            flipflop: {
21308                "italic": "normal",
21309                "normal": "italic"
21310            }
21311        },
21312        "<b>": {
21313            type: "tag",
21314            opener: "<b>",
21315            closer: "</b>",
21316            attr: "@font-weight",
21317            outer: "bold",
21318            flipflop: {
21319                "bold": "normal",
21320                "normal": "bold"
21321            }
21322        },
21323        "<sup>": {
21324            type: "tag",
21325            opener: "<sup>",
21326            closer: "</sup>",
21327            attr: "@vertical-align",
21328            outer: "sup",
21329            flipflop: {
21330                "sub": "sup",
21331                "sup": "sup"
21332            }
21333        },
21334        "<sub>": {
21335            type: "tag",
21336            opener: "<sub>",
21337            closer: "</sub>",
21338            attr: "@vertical-align",
21339            outer: "sub",
21340            flipflop: {
21341                "sup": "sub",
21342                "sub": "sub"
21343            }
21344        },
21345        " \"": {
21346            type: "quote",
21347            opener: " \"",
21348            closer: "\"",
21349            attr: "@quotes",
21350            outer: "true",
21351            flipflop: {
21352                "true": "inner",
21353                "inner": "true",
21354                "false": "true"
21355            }
21356        },
21357        " \'": {
21358            type: "quote",
21359            opener: " \'",
21360            closer: "\'",
21361            attr: "@quotes",
21362            outer: "inner",
21363            flipflop: {
21364                "true": "inner",
21365                "inner": "true",
21366                "false": "true"
21367            }
21368        }
21369    };
21370
21371    _nestingData["(\""] = _nestingData[" \""];
21372    _nestingData["(\'"] = _nestingData[" \'"];
21373
21374    var localeOpenQuote = state.getTerm("open-quote");
21375    var localeCloseQuote = state.getTerm("close-quote");
21376    var localeOpenInnerQuote = state.getTerm("open-inner-quote");
21377    var localeCloseInnerQuote = state.getTerm("close-inner-quote");
21378
21379    // If locale uses straight quotes, do not register them. All will be well.
21380    // Otherwise, clone straight-quote data, and adjust.
21381    if (localeOpenQuote && localeCloseQuote && [" \""," \'","\"","\'"].indexOf(localeOpenQuote) === -1) {
21382        _nestingData[localeOpenQuote] = JSON.parse(JSON.stringify(_nestingData[" \""]));
21383        _nestingData[localeOpenQuote].opener = localeOpenQuote;
21384        _nestingData[localeOpenQuote].closer = localeCloseQuote;
21385    }
21386    
21387    if (localeOpenInnerQuote && localeCloseInnerQuote && [" \""," \'","\"","\'"].indexOf(localeOpenInnerQuote) === -1) {
21388        _nestingData[localeOpenInnerQuote] = JSON.parse(JSON.stringify(_nestingData[" \'"]));
21389        _nestingData[localeOpenInnerQuote].opener = localeOpenInnerQuote;
21390        _nestingData[localeOpenInnerQuote].closer = localeCloseInnerQuote;
21391    }
21392    
21393    function _setOuterQuoteForm(quot) {
21394        var flip = {
21395            " \'": " \"",
21396            " \"": " \'",
21397            "(\"": "(\'",
21398            "(\'": "(\""
21399        };
21400        _nestingData[quot].outer = "true";
21401        _nestingData[flip[quot]].outer = "inner";
21402    }
21403    
21404    function _getNestingOpenerParams(opener) {
21405        var openers = [];
21406        var keys = Object.keys(_nestingData);
21407        for (var i = 0, l = keys.length; i < l; i++) {
21408            var key = keys[i];
21409            if (_nestingData[opener].type !== "quote" || !_nestingData[opener]) {
21410                openers.push(key);
21411            }
21412        }
21413        var ret = _nestingData[opener];
21414        ret.opener = new RegExp("^(?:" + openers.map(function(str){
21415            return str.replace("(", "\\(");
21416        }).join("|") + ")");
21417        return ret;
21418    }
21419
21420    var _nestingParams = (function() {
21421        var ret = {};
21422        var keys = Object.keys(_nestingData);
21423        for (var i = 0, l = keys.length; i < l; i++) {
21424            var key = keys[i];
21425            ret[key] = _getNestingOpenerParams(key);
21426        }
21427        return ret;
21428    }());
21429
21430    var _tagRex = (function() {
21431        var openers = [];
21432        var closers = [];
21433        var vals = {};
21434        for (var opener in _nestingParams) {
21435            openers.push(opener);
21436            vals[_nestingParams[opener].closer] = true;
21437        }
21438        var keys = Object.keys(vals);
21439        for (var i = 0, l = keys.length; i < l; i++) {
21440            var closer = keys[i];
21441            closers.push(closer);
21442        }
21443
21444        var all = openers.concat(closers).map(function(str){
21445            return str.replace("(", "\\(");
21446        }).join("|");
21447
21448        return {
21449            matchAll: new RegExp("((?:" + all + "))", "g"),
21450            splitAll: new RegExp("(?:" + all + ")", "g"),
21451            open: new RegExp("(^(?:" + openers.map(function(str){
21452                return str.replace("(", "\\(");
21453            }).join("|") + ")$)"),
21454            close: new RegExp("(^(?:" + closers.join("|") + ")$)"),
21455        };
21456    }());
21457
21458    function _tryOpen(tag, pos) {
21459        var params = _nestingState[_nestingState.length - 1];
21460        if (!params || tag.match(params.opener)) {
21461            _nestingState.push({
21462                type: _nestingParams[tag].type,
21463                opener: _nestingParams[tag].opener,
21464                closer: _nestingParams[tag].closer,
21465                pos: pos
21466            });
21467            return false;
21468        } else {
21469            _nestingState.pop();
21470            _nestingState.push({
21471                type: _nestingParams[tag].type,
21472                opener: _nestingParams[tag].opener,
21473                closer: _nestingParams[tag].closer,
21474                pos: pos
21475            });
21476            return {
21477                fixtag: params.pos
21478            };
21479        }
21480    }
21481    
21482    function _tryClose(tag, pos) {
21483        var params = _nestingState[_nestingState.length - 1];
21484        if (params && tag === params.closer) {
21485            _nestingState.pop();
21486            if (params.type === "nocase") {
21487                return {
21488                    nocase: {
21489                        open: params.pos,
21490                        close: pos
21491                    }
21492                };
21493            } else {
21494                return false;
21495            }
21496        } else {
21497            if (params) {
21498                return {
21499                    fixtag: params.pos
21500                };
21501            } else {
21502                return {
21503                    fixtag: pos
21504                };
21505            }
21506        }
21507    }
21508    
21509    function _pushNestingState(tag, pos) {
21510        if (tag.match(_tagRex.open)) {
21511            return _tryOpen(tag, pos);
21512        } else {
21513            return _tryClose(tag, pos);
21514        }
21515    }
21516    
21517    function _doppelString(str) {
21518        var forcedSpaces = [];
21519        // Normalize markup
21520        str = str.replace(/(<span)\s+(style=\"font-variant:)\s*(small-caps);?\"[^>]*(>)/g, "$1 $2$3;\"$4");
21521        str = str.replace(/(<span)\s+(class=\"no(?:case|decor)\")[^>]*(>)/g, "$1 $2$3");
21522
21523        var match = str.match(_tagRex.matchAll);
21524        if (!match) {
21525            return {
21526                tags: [],
21527                strings: [str],
21528                forcedSpaces: []
21529            };
21530        }
21531        var split = str.split(_tagRex.splitAll);
21532
21533        for (var i=0,ilen=match.length-1;i<ilen;i++) {
21534            if (_nestingData[match[i]]) {
21535                if (split[i+1] === "" && ["\"", "'"].indexOf(match[i+1]) > -1) {
21536                    match[i+1] = " " + match[i+1];
21537                    forcedSpaces.push(true);
21538                } else {
21539                    forcedSpaces.push(false);
21540                }
21541            }
21542        }
21543        return {
21544            tags: match,
21545            strings: split,
21546            forcedSpaces: forcedSpaces
21547        };
21548    }
21549
21550    var TagReg = function(blob) {
21551        var _stack = [];
21552        this.set = function (tag) {
21553            var attr = _nestingData[tag].attr;
21554            var decor = null;
21555            for (var i=_stack.length-1;i>-1;i--) {
21556                var _decor = _stack[i];
21557                if (_decor[0] === attr) {
21558                    decor = _decor;
21559                    break;
21560                }
21561            }
21562            if (!decor) {
21563                var allTheDecor = [state[state.tmp.area].opt.layout_decorations].concat(blob.alldecor);
21564                outer:
21565                for (var i=allTheDecor.length-1;i>-1;i--) {
21566                    var decorset = allTheDecor[i];
21567                    if (!decorset) {
21568                        continue;
21569                    }
21570                    for (var j=decorset.length-1;j>-1;j--) {
21571                        var _decor = decorset[j];
21572                        if (_decor[0] === attr) {
21573                            decor = _decor;
21574                            break outer;
21575                        }
21576                    }
21577                }
21578            }
21579            if (!decor) {
21580                decor = [attr, _nestingData[tag].outer];
21581            } else {
21582                decor = [attr, _nestingData[tag].flipflop[decor[1]]];
21583            }
21584            _stack.push(decor);
21585        };
21586        this.pair = function () {
21587            return _stack[_stack.length-1];
21588        };
21589        this.pop = function () {
21590            _stack.pop();
21591        };
21592    };
21593    
21594    function _apostropheForce(tag, str) {
21595        if (tag === "\'") {
21596            if (str && str.match(/^[^\,\.\?\:\;\ ]/)) {
21597                return "\u2019";
21598            }
21599        } else if (tag === " \'" && str && str.match(/^[\ ]/)) {
21600            return " \u2019";
21601        }
21602        return false;
21603    }
21604
21605    function _undoppelToQueue(blob, doppel, leadingSpace) {
21606        var firstString = true;
21607        var tagReg = new TagReg(blob);
21608        blob.blobs = [];
21609        function Stack (blob) {
21610            this.stack = [blob];
21611            this.latest = blob;
21612            this.addStyling = function(str, decor) {
21613                if (firstString) {
21614                    if (str.slice(0, 1) === " ") {
21615                        str = str.slice(1);
21616                    }
21617                    if (str.slice(0, 1) === " ") {
21618                        str = str.slice(1);
21619                    }
21620                    firstString = false;
21621                }
21622                this.latest = this.stack[this.stack.length-1];
21623                if (decor) {
21624                    if ("string" === typeof this.latest.blobs) {
21625                        var child = new CSL.Blob();
21626                        child.blobs = this.latest.blobs;
21627                        child.alldecor = this.latest.alldecor.slice();
21628                        this.latest.blobs = [child];
21629                    }
21630                    var tok = new CSL.Token();
21631                    var newblob = new CSL.Blob(null, tok);
21632                    newblob.alldecor = this.latest.alldecor.slice();
21633                    
21634                    // AHA! Bad naming. There is _decorset from the list, and
21635                    // there WAS decorset that we are building. Dumb. Fix the
21636                    // names and fix it up.
21637                    
21638                    if (decor[0] === "@class" && decor[1] === "nodecor") {
21639                        var newdecorset = [];
21640                        var seen = {};
21641                        var allTheDecor = [state[state.tmp.area].opt.layout_decorations].concat(newblob.alldecor);
21642                        for (var i=allTheDecor.length-1;i>-1;i--) {
21643                            var _decorset = allTheDecor[i];
21644                            if (!_decorset) {
21645                                continue;
21646                            }
21647                            for (var j=_decorset.length-1;j>-1;j--) {
21648                                var _olddecor = _decorset[j];
21649                                if (["@font-weight", "@font-style", "@font-variant"].indexOf(_olddecor[0]) > -1
21650                                    && !seen[_olddecor[0]]) {
21651                                    
21652                                    if (decor[1] !== "normal") {
21653                                        newblob.decorations.push([_olddecor[0], "normal"]);
21654                                        newdecorset.push([_olddecor[0], "normal"]);
21655                                    }
21656                                    seen[_olddecor[0]] = true;
21657                                }
21658                            }
21659                        }
21660                        newblob.alldecor.push(newdecorset);
21661                        
21662                    } else {
21663                        newblob.decorations.push(decor);
21664                        newblob.alldecor.push([decor]);
21665                    }
21666                    this.latest.blobs.push(newblob);
21667                    this.stack.push(newblob);
21668                    this.latest = newblob;
21669                    if (str) {
21670                        var tok = new CSL.Token();
21671                        var newblob = new CSL.Blob(null, tok);
21672                        newblob.blobs = str;
21673                        newblob.alldecor = this.latest.alldecor.slice();
21674                        this.latest.blobs.push(newblob);
21675                    }
21676                } else {
21677                    if (str) {
21678                        var child = new CSL.Blob();
21679                        child.blobs = str;
21680                        child.alldecor = this.latest.alldecor.slice();
21681                        this.latest.blobs.push(child);
21682                    }
21683                }
21684            };
21685            this.popStyling = function() {
21686                this.stack.pop();
21687            };
21688        }
21689        var stack = new Stack(blob);
21690        if (doppel.strings.length) {
21691            var str = doppel.strings[0];
21692            if (leadingSpace) {
21693                str = " " + str;
21694            }
21695            stack.addStyling(str);
21696        }
21697        for (var i=0,ilen=doppel.tags.length;i<ilen;i++) {
21698            var tag = doppel.tags[i];
21699            var str = doppel.strings[i+1];
21700            if (tag.match(_tagRex.open)) {
21701                tagReg.set(tag);
21702                stack.addStyling(str, tagReg.pair());
21703            } else {
21704                tagReg.pop();
21705                stack.popStyling();
21706                stack.addStyling(str);
21707            }
21708        }
21709    }
21710
21711    /**
21712     * PUBLIC
21713     */
21714
21715    this.processTags = function (blob) {
21716        var str = blob.blobs;
21717        var leadingSpace = false;
21718        if (str.slice(0, 1) === " " && !str.match(/^\s+[\'\"]/)) {
21719            leadingSpace = true;
21720        }
21721        var rex = new RegExp("(" + CSL.ROMANESQUE_REGEXP.source + ")\u2019(" + CSL.ROMANESQUE_REGEXP.source + ")", "g");
21722        var str = " " + str.replace(rex, "$1\'$2");
21723        var doppel = _doppelString(str);
21724        if (doppel.tags.length === 0) {
21725            return;
21726        }
21727        var quoteFormSeen = false;
21728        
21729    	for (var i=0,ilen=doppel.tags.length;i<ilen;i++) {
21730            var tag = doppel.tags[i];
21731            var str = doppel.strings[i+1];
21732            var apostrophe = _apostropheForce(tag, str);
21733            if (apostrophe) {
21734                doppel.strings[i+1] = apostrophe + doppel.strings[i+1];
21735                doppel.tags[i] = "";
21736            } else {
21737                var tagInfo;
21738                while (true) {
21739                    tagInfo = _pushNestingState(tag, i);
21740                    if (tagInfo) {
21741                        if (Object.keys(tagInfo).indexOf("fixtag") > -1) {
21742                            if (tag.match(_tagRex.close)
21743                                && tag === "\'") {
21744                                
21745                                doppel.strings[i+1] = "\u2019" + doppel.strings[i+1];
21746                                doppel.tags[i] = "";
21747                            } else {
21748                                var failedTag = doppel.tags[tagInfo.fixtag];
21749                                if (doppel.forcedSpaces[tagInfo.fixtag-1]) {
21750                                    failedTag = failedTag.slice(1);
21751                                }
21752                                doppel.strings[tagInfo.fixtag+1] = failedTag + doppel.strings[tagInfo.fixtag+1];
21753                                doppel.tags[tagInfo.fixtag] = "";
21754                            }
21755                            if (_nestingState.length > 0) {
21756                                if (tag !== "\'") {
21757                                    _nestingState.pop();
21758                                } else {
21759                                    break;
21760                                }
21761                            } else {
21762                                break;
21763                            }
21764                        } else if (tagInfo.nocase) {
21765                            doppel.tags[tagInfo.nocase.open] = "";
21766                            doppel.tags[tagInfo.nocase.close] = "";
21767                            break;
21768                        } else {
21769                            break;
21770                        }
21771                    } else {
21772                        break;
21773                    }
21774                }
21775                if (tagInfo && (tagInfo.fixtag|| tagInfo.fixtag === 0)) {
21776                    doppel.strings[i+1] = doppel.tags[i] + doppel.strings[i+1];
21777                    doppel.tags[i] = "";
21778                }
21779            }
21780        }
21781        // Stray tags are neutralized here
21782        for (var i=_nestingState.length-1;i>-1;i--) {
21783            var tagPos = _nestingState[i].pos;
21784            var tag = doppel.tags[tagPos];
21785            if (tag === " \'" || tag === "\'") {
21786
21787                doppel.strings[tagPos+1] = " \u2019" + doppel.strings[tagPos+1];
21788            } else {
21789                doppel.strings[tagPos+1] = doppel.tags[tagPos] + doppel.strings[tagPos+1];
21790            }
21791            doppel.tags[tagPos] = "";
21792            _nestingState.pop();
21793        }
21794        for (var i=doppel.tags.length-1;i>-1;i--) {
21795            if (!doppel.tags[i]) {
21796                doppel.tags = doppel.tags.slice(0,i).concat(doppel.tags.slice(i+1));
21797                doppel.strings[i] = doppel.strings[i] + doppel.strings[i+1];
21798                doppel.strings = doppel.strings.slice(0,i+1).concat(doppel.strings.slice(i+2));
21799            }
21800        }
21801        // Sniff initial (outer) quote form (single or double) and configure parser
21802        // Also add leading spaces.
21803        for (var i=0,ilen=doppel.tags.length;i<ilen;i++) {
21804            var tag = doppel.tags[i];
21805            var forcedSpace = doppel.forcedSpaces[i-1];
21806            if ([" \"", " \'", "(\"", "(\'"].indexOf(tag) > -1) {
21807                if (!quoteFormSeen) {
21808                    _setOuterQuoteForm(tag);
21809                    quoteFormSeen = true;
21810                }
21811                if (!forcedSpace) {
21812                    doppel.strings[i] += tag.slice(0, 1);
21813                }
21814            }
21815        }
21816        //print(JSON.stringify(doppel, null, 2))
21817        //print(_undoppelString(doppel));
21818        _undoppelToQueue(blob, doppel, leadingSpace);
21819    };
21820};
21821
21822/*global CSL: true */
21823
21824CSL.Output.Formatters = (function () {
21825    var rexStr = "(?:\u2018|\u2019|\u201C|\u201D| \"| \'|\"|\'|[-\u2013\u2014\/.,;?!:]|\\[|\\]|\\(|\\)|<span style=\"font-variant: small-caps;\">|<span class=\"no(?:case|decor)\">|<\/span>|<\/?(?:i|sc|b|sub|sup)>)";
21826    var tagDoppel = new CSL.Doppeler(rexStr, function(str) {
21827        return str.replace(/(<span)\s+(class=\"no(?:case|decor)\")[^>]*(>)/g, "$1 $2$3").replace(/(<span)\s+(style=\"font-variant:)\s*(small-caps);?(\")[^>]*(>)/g, "$1 $2 $3;$4$5");
21828    });
21829    var rexNameStr = "(?:[-\\s]*<\\/*(?:span\s+class=\"no(?:case|decor)\"|i|sc|b|sub|sup)>[-\\s]*|[-\\s]+)";
21830    var nameDoppel = new CSL.Doppeler(rexNameStr);
21831    
21832    var wordDoppel = new CSL.Doppeler("(?:[\u00A0\u0020\u00A0\u2000-\u200B\u205F\u3000]+)");
21833    
21834    /**
21835     * INTERNAL
21836     */
21837
21838    var _tagParams = {
21839        "<span style=\"font-variant: small-caps;\">": "</span>",
21840        "<span class=\"nocase\">": "</span>",
21841        "<span class=\"nodecor\">": "</span>",
21842        "<sc>": "</sc>",
21843        "<sub>": "</sub>",
21844        "<sup>": "</sup>"
21845    };
21846
21847    function _capitalise (word) {
21848        // Weird stuff is (.) transpiled with regexpu
21849        //   https://github.com/mathiasbynens/regexpu
21850        var m = word.match(/(^\s*)((?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))(.*)/);
21851        // Do not uppercase lone Greek letters
21852        // (No case transforms in Greek citations, but chars used in titles to science papers)
21853        if (m && !(m[2].match(/^[\u0370-\u03FF]$/) && !m[3])) {
21854            return m[1] + CSL.toLocaleUpperCase.call(this, m[2]) + m[3];
21855        }
21856        return word;
21857    }
21858
21859    function _textcaseEngine(config, string) {
21860        if (!string) {
21861            return "";
21862        }
21863        config.doppel = tagDoppel.split(string);
21864        var quoteParams = {
21865            " \"": {
21866                opener: " \'",
21867                closer: "\""
21868            },
21869            " \'": {
21870                opener: " \"",
21871                closer: "\'"
21872            },
21873            "\u2018": {
21874                opener: "\u2018",
21875                closer: "\u2019"
21876            },
21877            "\u201C": {
21878                opener: "\u201C",
21879                closer: "\u201D"
21880            },
21881        };
21882        function tryOpen(tag, pos) {
21883            if (config.quoteState.length === 0 || tag === config.quoteState[config.quoteState.length - 1].opener) {
21884                config.quoteState.push({
21885                    opener: quoteParams[tag].opener,
21886                    closer: quoteParams[tag].closer,
21887                    pos: pos
21888                });
21889                return false;
21890            } else {
21891                var prevPos = config.quoteState[config.quoteState.length-1].pos;
21892                config.quoteState.pop();
21893                config.quoteState.push({
21894                    opener: quoteParams[tag].opener,
21895                    closer: quoteParams[tag].closer,
21896                    positions: pos
21897                });
21898                return prevPos;
21899            }
21900        }
21901        function tryClose(tag, pos) {
21902            if (config.quoteState.length > 0 && tag === config.quoteState[config.quoteState.length - 1].closer) {
21903                config.quoteState.pop();
21904            } else {
21905                return pos;
21906            }
21907        }
21908        function pushQuoteState(tag, pos) {
21909            var isOpener = ["\u201C", "\u2018", " \"", " \'"].indexOf(tag) > -1 ? true : false;
21910            if (isOpener) {
21911                return tryOpen(tag, pos);
21912            } else {
21913                return tryClose(tag, pos);
21914            }
21915        }
21916        function quoteFix (tag, positions) {
21917            var m = tag.match(/(^(?:\u2018|\u2019|\u201C|\u201D|\"|\')|(?: \"| \')$)/);
21918            if (m) {
21919                return pushQuoteState(m[1], positions);
21920            }
21921        }
21922        // Run state machine
21923        if (config.doppel.strings.length && config.doppel.strings[0].trim()) {
21924            config.doppel.strings[0] = config.capitaliseWords(config.doppel.strings[0], 0, config.doppel.tags[0]);
21925        }
21926
21927    	for (var i=0,ilen=config.doppel.tags.length;i<ilen;i++) {
21928            var tag = config.doppel.tags[i];
21929            var str = config.doppel.strings[i+1];
21930
21931            if (config.tagState !== null) {
21932                // Evaluate tag state for current string
21933                if (_tagParams[tag]) {
21934                    config.tagState.push(_tagParams[tag]);
21935                } else if (config.tagState.length && tag === config.tagState[config.tagState.length - 1]) {
21936                    config.tagState.pop();
21937                }
21938            }
21939
21940            if (config.afterPunct !== null) {
21941                // Evaluate punctuation state of current string
21942                if (tag.match(/[\!\?\:]$/)) {
21943                    config.afterPunct = true;
21944                }
21945            }
21946
21947            // Process if outside tag scope, else noop for upper-casing
21948            if (config.tagState.length === 0) {
21949                config.doppel.strings[i+1] = config.capitaliseWords(str, i+1, config.doppel,config.doppel.tags[i+1]);
21950                
21951            } else if (config.doppel.strings[i+1].trim()) {
21952                config.lastWordPos = null;
21953            }
21954            
21955            if (config.quoteState !== null) {
21956                // Evaluate quote state of current string and fix chars that have flown
21957                var quotePos = quoteFix(tag, i);
21958                if (quotePos || quotePos === 0) {
21959                    var origChar = config.doppel.origStrings[quotePos+1].slice(0, 1);
21960                    config.doppel.strings[quotePos+1] = origChar + config.doppel.strings[quotePos+1].slice(1);
21961                    config.lastWordPos = null;
21962                }
21963            }
21964
21965            // If there was a printable string, unset first-word and after-punctuation
21966            if (config.isFirst) {
21967                if (str.trim()) {
21968                    config.isFirst = false;
21969                }
21970            }
21971            if (config.afterPunct) {
21972                if (str.trim()) {
21973                    config.afterPunct = false;
21974                }
21975            }
21976        }
21977        if (config.quoteState) {
21978            for (var i=0,ilen=config.quoteState.length;i<ilen;i++) {
21979                var quotePos = config.quoteState[i].pos;
21980                // Test for quotePos avoids a crashing error:
21981                //   https://github.com/citation-style-language/test-suite/blob/master/processor-tests/humans/flipflop_OrphanQuote.txt
21982                if (typeof quotePos !== 'undefined') {
21983                    var origChar = config.doppel.origStrings[quotePos+1].slice(0, 1);
21984                    config.doppel.strings[quotePos+1] = origChar + config.doppel.strings[quotePos+1].slice(1);
21985                }
21986            }
21987        }
21988        // Specially capitalize the last word if necessary (invert stop-word list)
21989        if (config.lastWordPos) {
21990            var lastWords = wordDoppel.split(config.doppel.strings[config.lastWordPos.strings]);
21991            var lastWord = lastWords.strings[config.lastWordPos.words];
21992            if (lastWord.length > 1 && CSL.toLocaleLowerCase.call(this, lastWord).match(config.skipWordsRex)) {
21993                lastWord = _capitalise.call(this, lastWord);
21994                lastWords.strings[config.lastWordPos.words] = lastWord;
21995            }
21996            config.doppel.strings[config.lastWordPos.strings] = wordDoppel.join(lastWords);
21997        }
21998
21999        // Recombine the string
22000        return tagDoppel.join(config.doppel);
22001    }
22002
22003    /**
22004     * PUBLIC
22005     */
22006
22007    /**
22008     * A noop that just delivers the string.
22009     */
22010    function passthrough (state, str) {
22011        return str;
22012    }
22013
22014    /**
22015     * Force all letters in the string to lowercase, skipping nocase spans
22016     */
22017    function lowercase(state, string) {
22018        var config = {
22019            quoteState: null,
22020            capitaliseWords: function(str) {
22021                var words = str.split(" ");
22022                for (var i=0,ilen=words.length;i<ilen;i++) {
22023                    var word = words[i];
22024                    if (word) {
22025                        words[i] = CSL.toLocaleLowerCase.call(state, word);
22026                    }
22027                }
22028                return words.join(" ");
22029            },
22030            skipWordsRex: null,
22031            tagState: [],
22032            afterPunct: null,
22033            isFirst: null
22034        };
22035        return _textcaseEngine.call(state, config, string);
22036    }
22037
22038    /**
22039     * Force all letters in the string to uppercase.
22040     */
22041    function uppercase(state, string) {
22042        var config = {
22043            quoteState: null,
22044            capitaliseWords: function(str) {
22045                var words = str.split(" ");
22046                for (var i=0,ilen=words.length;i<ilen;i++) {
22047                    var word = words[i];
22048                    if (word) {
22049                        // Okay.
22050                        // So we need to pick up an array of locales from state.tmp.
22051                        // This function is invoked in the context of queue.js, so
22052                        // the item is not available here. Three levels to be included
22053                        // in the array:
22054                        // 1. Field language tag, if any
22055                        // 2. Item language tag, if any
22056                        // 3. Value of state.opt.lang
22057                        words[i] = CSL.toLocaleUpperCase.call(state, word);
22058                    }
22059                }
22060                return words.join(" ");
22061            },
22062            skipWordsRex: null,
22063            tagState: [],
22064            afterPunct: null,
22065            isFirst: null
22066        };
22067        return _textcaseEngine.call(state, config, string);
22068    }
22069
22070    /**
22071     * Similar to <b>capitalize_first</b>, but force the
22072     * subsequent characters to lowercase.
22073     */
22074    function sentence(state, string) {
22075        var config = {
22076            quoteState: [],
22077            capitaliseWords: function(str) {
22078                var words = str.split(" ");
22079                for (var i=0,ilen=words.length;i<ilen;i++) {
22080                    var word = words[i];
22081                    if (word) {
22082                        if (config.isFirst) {
22083                            words[i] = _capitalise.call(state, word);
22084                            config.isFirst = false;
22085                        } else {
22086                            words[i] = CSL.toLocaleLowerCase.call(state, word);
22087                        }
22088                    }
22089                }
22090                return words.join(" ");
22091            },
22092            skipWordsRex: null,
22093            tagState: [],
22094            afterPunct: null,
22095            isFirst: true
22096        };
22097        return _textcaseEngine.call(state, config, string);
22098    }
22099
22100    function title(state, string) {
22101        var config = {
22102            quoteState: [],
22103            capitaliseWords: function(str, i, followingTag) {
22104                if (str.trim()) {
22105                    var wordle = wordDoppel.split(str);
22106                    var words = wordle.strings;
22107                    for (var j=0,jlen=words.length;j<jlen;j++) {
22108                        var word = words[j];
22109                        if (!word) {
22110                            continue;
22111                        }
22112                        let lcase = CSL.toLocaleLowerCase.call(state, word);
22113                        let capitalize = false;
22114                        if (word.length > 1 && !lcase.match(config.skipWordsRex)) {
22115                            // Capitalize every word that is not a stop-word
22116                            capitalize = true;
22117                        } else if (j === (words.length - 1) && followingTag === "-") {
22118                            capitalize = true;
22119                        } else if (config.isFirst) {
22120                            // Capitalize first word, even if a stop-word
22121                            capitalize = true;
22122                        } else if (config.afterPunct) {
22123                            // Capitalize after punctuation
22124                            capitalize = true;
22125                        }
22126                        // Don't capitalize if word already contains capitalization
22127                        if (capitalize && word === lcase) {
22128                            words[j] = _capitalise.call(state, word);
22129                        }
22130                        config.afterPunct = false;
22131                        config.isFirst = false;
22132                        config.lastWordPos = {
22133                            strings: i,
22134                            words: j
22135                        };
22136                    }
22137                    str = wordDoppel.join(wordle);
22138                }
22139                return str;
22140            },
22141            skipWordsRex: state.locale[state.opt.lang].opts["skip-words-regexp"],
22142            tagState: [],
22143            afterPunct: false,
22144            isFirst: true
22145        };
22146        return _textcaseEngine.call(state, config, string);
22147    }
22148    
22149    
22150    /**
22151     * Force capitalization of the first letter in the string, leave
22152     * the rest of the characters untouched.
22153     */
22154    function capitalizeFirst(state, string) {
22155        var config = {
22156            quoteState: [],
22157            capitaliseWords: function(str) {
22158                var wordle = wordDoppel.split(str);
22159                var words = wordle.strings;
22160                for (var i=0,ilen=words.length;i<ilen;i++) {
22161                    var word = words[i];
22162                    if (word) {
22163                        if (config.isFirst) {
22164                            // Don't capitalize if word already contains capitalization
22165                            if (word === CSL.toLocaleLowerCase.call(state, word)) {
22166                                words[i] = _capitalise.call(state, word);
22167                            }
22168                            config.isFirst = false;
22169                            break;
22170                        }
22171                    }
22172                }
22173                return wordDoppel.join(wordle);
22174            },
22175            skipWordsRex: null,
22176            tagState: [],
22177            afterPunct: null,
22178            isFirst: true
22179        };
22180        return _textcaseEngine.call(state, config, string);
22181    }
22182
22183    /**
22184     * Force the first letter of each space-delimited
22185     * word in the string to uppercase, and leave the remainder
22186     * of the string untouched.  Single characters are forced
22187     * to uppercase.
22188     */
22189    function capitalizeAll (state, string) {
22190        var config = {
22191            quoteState: [],
22192            capitaliseWords: function(str) {
22193                var wordle = wordDoppel.split(str);
22194                var words = wordle.strings;
22195                for (var i=0,ilen=words.length;i<ilen;i++) {
22196                    var word = words[i];
22197                    if (word) {
22198                        // Don't capitalize if word already contains capitalization
22199                        if (word === CSL.toLocaleLowerCase.call(state, word)) {
22200                            words[i] = _capitalise.call(state, word);
22201                        }
22202                    }
22203                }
22204                return wordDoppel.join(wordle);
22205            },
22206            skipWordsRex: null,
22207            tagState: [],
22208            afterPunct: null,
22209            isFirst: null
22210        };
22211        return _textcaseEngine.call(state, config, string);
22212    }
22213    return {
22214        nameDoppel: nameDoppel,
22215        passthrough: passthrough,
22216        lowercase: lowercase,
22217        uppercase: uppercase,
22218        sentence: sentence,
22219        title: title,
22220        "capitalize-first": capitalizeFirst,
22221        "capitalize-all": capitalizeAll
22222    };
22223}());
22224
22225/*global CSL: true */
22226
22227
22228/**
22229 * Output specifications.
22230 * @class
22231 */
22232CSL.Output.Formats = function () {};
22233
22234/**
22235 * HTML output format specification.
22236 * <p>The headline says it all.  The source code for this
22237 * object can be used as a template for producing other
22238 * output modes.</p>
22239 */
22240CSL.Output.Formats.prototype.html = {
22241    //
22242    // text_escape: Format-specific function for escaping text destined
22243    // for output.  Takes the text to be escaped as sole argument.  Function
22244    // will be run only once across each portion of text to be escaped, it
22245    // need not be idempotent.
22246    //
22247    "text_escape": function (text) {
22248        // Numeric entities, in case the output is processed as
22249        // xml in an environment in which HTML named entities are
22250        // not declared.
22251        if (!text) {
22252            text = "";
22253        }
22254        return text.replace(/&/g, "&#38;")
22255            .replace(/</g, "&#60;")
22256            .replace(/>/g, "&#62;")
22257            .replace(/\s\s/g, "\u00A0 ")
22258            .replace(CSL.SUPERSCRIPTS_REGEXP,
22259                     function(aChar) {
22260                         // return "&#60;sup&#62;" + CSL.SUPERSCRIPTS[aChar] + "&#60;/sup&#62;";
22261                         return "<sup>" + CSL.SUPERSCRIPTS[aChar] + "</sup>";
22262                     });
22263    },
22264    "bibstart": "<div class=\"csl-bib-body\">\n",
22265    "bibend": "</div>",
22266    "@font-style/italic": "<i>%%STRING%%</i>",
22267    "@font-style/oblique": "<em>%%STRING%%</em>",
22268    "@font-style/normal": "<span style=\"font-style:normal;\">%%STRING%%</span>",
22269    "@font-variant/small-caps": "<span style=\"font-variant:small-caps;\">%%STRING%%</span>",
22270    "@passthrough/true": CSL.Output.Formatters.passthrough,
22271    "@font-variant/normal": "<span style=\"font-variant:normal;\">%%STRING%%</span>",
22272    "@font-weight/bold": "<b>%%STRING%%</b>",
22273    "@font-weight/normal": "<span style=\"font-weight:normal;\">%%STRING%%</span>",
22274    "@font-weight/light": false,
22275    "@text-decoration/none": "<span style=\"text-decoration:none;\">%%STRING%%</span>",
22276    "@text-decoration/underline": "<span style=\"text-decoration:underline;\">%%STRING%%</span>",
22277    "@vertical-align/sup": "<sup>%%STRING%%</sup>",
22278    "@vertical-align/sub": "<sub>%%STRING%%</sub>",
22279    "@vertical-align/baseline": "<span style=\"baseline\">%%STRING%%</span>",
22280    "@strip-periods/true": CSL.Output.Formatters.passthrough,
22281    "@strip-periods/false": CSL.Output.Formatters.passthrough,
22282    "@quotes/true": function (state, str) {
22283        if ("undefined" === typeof str) {
22284            return state.getTerm("open-quote");
22285        }
22286        return state.getTerm("open-quote") + str + state.getTerm("close-quote");
22287    },
22288    "@quotes/inner": function (state, str) {
22289        if ("undefined" === typeof str) {
22290            //
22291            // Mostly right by being wrong (for apostrophes)
22292            //
22293            return "\u2019";
22294        }
22295        return state.getTerm("open-inner-quote") + str + state.getTerm("close-inner-quote");
22296    },
22297    "@quotes/false": false,
22298    //"@bibliography/body": function (state,str){
22299    //    return "<div class=\"csl-bib-body\">\n"+str+"</div>";
22300    //},
22301    "@cite/entry": function (state, str) {
22302        return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt);
22303	},
22304    "@bibliography/entry": function (state, str) {
22305        // Test for this.item_id to add decorations to
22306        // bibliography output of individual entries.
22307        //
22308        // Full item content can be obtained from
22309        // state.registry.registry[id].ref, using
22310        // CSL variable keys.
22311        //
22312        // Example:
22313        //
22314        //   print(state.registry.registry[this.item_id].ref["title"]);
22315        //
22316        // At present, for parallel citations, only the
22317        // id of the master item is supplied on this.item_id.
22318        var insert = "";
22319        if (state.sys.embedBibliographyEntry) {
22320            insert = state.sys.embedBibliographyEntry(this.item_id) + "\n";
22321        }
22322        return "  <div class=\"csl-entry\">" + str + "</div>\n" + insert;
22323    },
22324    "@display/block": function (state, str) {
22325        return "\n\n    <div class=\"csl-block\">" + str + "</div>\n";
22326    },
22327    "@display/left-margin": function (state, str) {
22328        return "\n    <div class=\"csl-left-margin\">" + str + "</div>";
22329    },
22330    "@display/right-inline": function (state, str) {
22331        return "<div class=\"csl-right-inline\">" + str + "</div>\n  ";
22332    },
22333    "@display/indent": function (state, str) {
22334        return "<div class=\"csl-indent\">" + str + "</div>\n  ";
22335    },
22336    "@showid/true": function (state, str, cslid) {
22337        if (!state.tmp.just_looking && ! state.tmp.suppress_decorations) {
22338            if (cslid) {
22339                return "<span class=\"" + state.opt.nodenames[cslid] + "\" cslid=\"" + cslid + "\">" + str + "</span>";
22340            } else if (this.params && "string" === typeof str) {
22341                var prePunct = "";
22342                if (str) {
22343                    var m = str.match(CSL.VARIABLE_WRAPPER_PREPUNCT_REX);
22344                    prePunct = m[1];
22345                    str = m[2];
22346                }
22347                var postPunct = "";
22348                if (str && CSL.SWAPPING_PUNCTUATION.indexOf(str.slice(-1)) > -1) {
22349                    postPunct = str.slice(-1);
22350                    str = str.slice(0,-1);
22351                }
22352                return state.sys.variableWrapper(this.params, prePunct, str, postPunct);
22353            } else {
22354                return str;
22355            }
22356        } else {
22357            return str;
22358        }
22359    },
22360    "@URL/true": function (state, str) {
22361        return "<a href=\"" + str + "\">" + str + "</a>";
22362    },
22363    "@DOI/true": function (state, str) {
22364        var doiurl = str;
22365        if (!str.match(/^https?:\/\//)) {
22366            doiurl = "https://doi.org/" + str;
22367        }
22368        return "<a href=\"" + doiurl + "\">" + str + "</a>";
22369    }
22370};
22371
22372/**
22373 * Plain text output specification.
22374 *
22375 * (Code contributed by Simon Kornblith, Center for History and New Media,
22376 * George Mason University.)
22377 */
22378CSL.Output.Formats.prototype.text = {
22379    //
22380    // text_escape: Format-specific function for escaping text destined
22381    // for output.  Takes the text to be escaped as sole argument.  Function
22382    // will be run only once across each portion of text to be escaped, it
22383    // need not be idempotent.
22384    //
22385    "text_escape": function (text) {
22386        if (!text) {
22387            text = "";
22388        }
22389        return text;
22390    },
22391    "bibstart": "",
22392    "bibend": "",
22393    "@font-style/italic": false,
22394    "@font-style/oblique": false,
22395    "@font-style/normal": false,
22396    "@font-variant/small-caps": false,
22397    "@passthrough/true": CSL.Output.Formatters.passthrough,
22398    "@font-variant/normal": false,
22399    "@font-weight/bold": false,
22400    "@font-weight/normal": false,
22401    "@font-weight/light": false,
22402    "@text-decoration/none": false,
22403    "@text-decoration/underline": false,
22404    "@vertical-align/baseline": false,
22405    "@vertical-align/sup": false,
22406    "@vertical-align/sub": false,
22407    "@strip-periods/true": CSL.Output.Formatters.passthrough,
22408    "@strip-periods/false": CSL.Output.Formatters.passthrough,
22409    "@quotes/true": function (state, str) {
22410        if ("undefined" === typeof str) {
22411            return state.getTerm("open-quote");
22412        }
22413        return state.getTerm("open-quote") + str + state.getTerm("close-quote");
22414    },
22415    "@quotes/inner": function (state, str) {
22416        if ("undefined" === typeof str) {
22417            //
22418            // Mostly right by being wrong (for apostrophes)
22419            //
22420            return "\u2019";
22421        }
22422        return state.getTerm("open-inner-quote") + str + state.getTerm("close-inner-quote");
22423    },
22424    "@quotes/false": false,
22425    //"@bibliography/body": function (state,str){
22426    //    return "<div class=\"csl-bib-body\">\n"+str+"</div>";
22427    //},
22428    "@cite/entry": function (state, str) {
22429		return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt);
22430	},
22431    "@bibliography/entry": function (state, str) {
22432        return str+"\n";
22433    },
22434    "@display/block": function (state, str) {
22435        return "\n"+str;
22436    },
22437    "@display/left-margin": function (state, str) {
22438        return str + " ";
22439    },
22440    "@display/right-inline": function (state, str) {
22441        return str;
22442    },
22443    "@display/indent": function (state, str) {
22444        return "\n    "+str;
22445    },
22446    "@showid/true": function (state, str) {
22447        return str;
22448    },
22449    "@URL/true": function (state, str) {
22450        return str;
22451    },
22452    "@DOI/true": function (state, str) {
22453        return str;
22454    }
22455};
22456
22457/**
22458 * Plain text output specification.
22459 *
22460 * (Code contributed by Simon Kornblith, Center for History and New Media,
22461 * George Mason University.)
22462 */
22463CSL.Output.Formats.prototype.rtf = {
22464    //
22465    // text_escape: Format-specific function for escaping text destined
22466    // for output.  Takes the text to be escaped as sole argument.  Function
22467    // will be run only once across each portion of text to be escaped, it
22468    // need not be idempotent.
22469    //
22470    "text_escape": function (text) {
22471        if (!text) {
22472            text = "";
22473        }
22474        return text
22475        .replace(/([\\{}])/g, "\\$1")
22476        .replace(CSL.SUPERSCRIPTS_REGEXP,
22477                 function(aChar) {
22478                     return "\\super " + CSL.SUPERSCRIPTS[aChar] + "\\nosupersub{}";
22479                 })
22480        .replace(/[\u007F-\uFFFF]/g,
22481                 function(aChar) { return "\\uc0\\u"+aChar.charCodeAt(0).toString()+"{}"; })
22482        .split("\t").join("\\tab{}");
22483    },
22484    "@passthrough/true": CSL.Output.Formatters.passthrough,
22485    "@font-style/italic":"{\\i{}%%STRING%%}",
22486    "@font-style/normal":"{\\i0{}%%STRING%%}",
22487    "@font-style/oblique":"{\\i{}%%STRING%%}",
22488    "@font-variant/small-caps":"{\\scaps %%STRING%%}",
22489    "@font-variant/normal":"{\\scaps0{}%%STRING%%}",
22490    "@font-weight/bold":"{\\b{}%%STRING%%}",
22491    "@font-weight/normal":"{\\b0{}%%STRING%%}",
22492    "@font-weight/light":false,
22493    "@text-decoration/none":false,
22494    "@text-decoration/underline":"{\\ul{}%%STRING%%}",
22495    "@vertical-align/baseline":false,
22496    "@vertical-align/sup":"\\super %%STRING%%\\nosupersub{}",
22497    "@vertical-align/sub":"\\sub %%STRING%%\\nosupersub{}",
22498    "@strip-periods/true": CSL.Output.Formatters.passthrough,
22499    "@strip-periods/false": CSL.Output.Formatters.passthrough,
22500    "@quotes/true": function (state, str) {
22501        if ("undefined" === typeof str) {
22502            return CSL.Output.Formats.rtf.text_escape(state.getTerm("open-quote"));
22503        }
22504        return CSL.Output.Formats.rtf.text_escape(state.getTerm("open-quote")) + str + CSL.Output.Formats.rtf.text_escape(state.getTerm("close-quote"));
22505    },
22506    "@quotes/inner": function (state, str) {
22507        if ("undefined" === typeof str) {
22508            return CSL.Output.Formats.rtf.text_escape("\u2019");
22509        }
22510        return CSL.Output.Formats.rtf.text_escape(state.getTerm("open-inner-quote")) + str + CSL.Output.Formats.rtf.text_escape(state.getTerm("close-inner-quote"));
22511    },
22512    "@quotes/false": false,
22513    "bibstart":"{\\rtf ",
22514    "bibend":"}",
22515    "@display/block": "\\line{}%%STRING%%\\line\r\n",
22516    "@cite/entry": function (state, str) {
22517        // If wrapCitationEntry does not exist, cite/entry 
22518        // is not applied.
22519		return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt);
22520	},
22521    "@bibliography/entry": function(state,str){
22522        return str;
22523    },
22524    "@display/left-margin": function(state,str){
22525        return str+"\\tab ";
22526    },
22527    "@display/right-inline": function (state, str) {
22528        return str+"\r\n";
22529    },
22530    "@display/indent": function (state, str) {
22531        return "\n\\tab "+str+"\\line\r\n";
22532    },
22533    "@showid/true": function (state, str) {
22534        if (!state.tmp.just_looking && ! state.tmp.suppress_decorations) {
22535            var prePunct = "";
22536            if (str) {
22537                var m = str.match(CSL.VARIABLE_WRAPPER_PREPUNCT_REX);
22538                prePunct = m[1];
22539                str = m[2];
22540            }
22541            var postPunct = "";
22542            if (str && CSL.SWAPPING_PUNCTUATION.indexOf(str.slice(-1)) > -1) {
22543                postPunct = str.slice(-1);
22544                str = str.slice(0,-1);
22545            }
22546            return state.sys.variableWrapper(this.params, prePunct, str, postPunct);
22547        } else {
22548            return str;
22549        }
22550    },
22551    "@URL/true": function (state, str) {
22552        return str;
22553    },
22554    "@DOI/true": function (state, str) {
22555        return str;
22556    }
22557};
22558
22559/*
22560
22561    This does not seem to work in Zotero plugins. For some reason the scope of the link does not
22562    close when interpreted by the LibreOffice. Perhaps this creates a field within a field,
22563    and that is not allowed?
22564
22565    "@URL/true": function (state, str) {
22566        return "\\field{\\*\\fldinst{HYPERLINK \"" + str + "\"}}{\\fldrslt{"+ str +"}}";
22567    },
22568    "@DOI/true": function (state, str) {
22569        return "\\field{\\*\\fldinst{HYPERLINK \"https://doi.org/" + str + "\"}}{\\fldrslt{"+ str +"}}";
22570    }
22571*/
22572
22573/**
22574 * AsciiDoc output specification.
22575 *
22576 * See http://asciidoc.org/ or https://asciidoctor.org/
22577 */
22578CSL.Output.Formats.prototype.asciidoc = {
22579    "text_escape": function (text) {
22580        if (!text) {
22581            text = "";
22582        }
22583        return text.replace("*", "pass:[*]", "g")
22584            .replace("_", "pass:[_]", "g")
22585            .replace("#", "pass:[#]", "g")
22586            .replace("^", "pass:[^]", "g")
22587            .replace("~", "pass:[~]", "g")
22588            .replace("[[", "pass:[[[]", "g")
22589            .replace("  ", "&#160; ", "g")
22590            .replace(CSL.SUPERSCRIPTS_REGEXP, function(aChar) {
22591                return "^" + CSL.SUPERSCRIPTS[aChar] + "^";
22592            });
22593    },
22594    "bibstart": "",
22595    "bibend": "",
22596    "@passthrough/true": CSL.Output.Formatters.passthrough,
22597    "@font-style/italic": "__%%STRING%%__",
22598    "@font-style/oblique": "__%%STRING%%__",
22599    "@font-style/normal": false,
22600    "@font-variant/small-caps": "[small-caps]#%%STRING%%#",
22601    "@font-variant/normal": false,
22602    "@font-weight/bold": "**%%STRING%%**",
22603    "@font-weight/normal": false,
22604    "@font-weight/light": false,
22605    "@text-decoration/none": false,
22606    "@text-decoration/underline": "[underline]##%%STRING%%##",
22607    "@vertical-align/sup": "^^%%STRING%%^^",
22608    "@vertical-align/sub": "~~%%STRING%%~~",
22609    "@vertical-align/baseline": false,
22610    "@strip-periods/true": CSL.Output.Formatters.passthrough,
22611    "@strip-periods/false": CSL.Output.Formatters.passthrough,
22612    "@quotes/true": function (state, str) {
22613        if ("undefined" === typeof str) {
22614            return "``";
22615        }
22616        return "``" + str + "''";
22617    },
22618    "@quotes/inner": function (state, str) {
22619        if ("undefined" === typeof str) {
22620            return "`";
22621        }
22622        return "`" + str + "'";
22623    },
22624    "@quotes/false": false,
22625    "@cite/entry": function (state, str) {
22626        // if wrapCitationEntry does not exist, cite/entry is not applied
22627        return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt);
22628    },
22629    "@bibliography/entry": function (state, str) {
22630        return str + "\n";
22631    },
22632    "@display/block": function (state, str) {
22633        return str;
22634    },
22635    "@display/left-margin": function (state, str) {
22636        return str;
22637    },
22638    "@display/right-inline": function (state, str) {
22639        return " " + str;
22640    },
22641    "@display/indent": function (state, str) {
22642        return " " + str;
22643    },
22644    "@showid/true": function (state, str) {
22645        if (!state.tmp.just_looking && !state.tmp.suppress_decorations && this.params && "string" === typeof str) {
22646            var prePunct = "";
22647            if (str) {
22648                var m = str.match(CSL.VARIABLE_WRAPPER_PREPUNCT_REX);
22649                prePunct = m[1];
22650                str = m[2];
22651            }
22652            var postPunct = "";
22653            if (str && CSL.SWAPPING_PUNCTUATION.indexOf(str.slice(-1)) > -1) {
22654                postPunct = str.slice(-1);
22655                str = str.slice(0,-1);
22656            }
22657            return state.sys.variableWrapper(this.params, prePunct, str, postPunct);
22658        } else {
22659            return str;
22660        }
22661    },
22662    "@URL/true": function (state, str) {
22663        // AsciiDoc renders URLs automatically as links
22664        return str;
22665    },
22666    "@DOI/true": function (state, str) {
22667        var doiurl = str;
22668        if (!str.match(/^https?:\/\//)) {
22669            doiurl = "https://doi.org/" + str;
22670        }
22671        return doiurl + "[" + str + "]";
22672    }
22673};
22674
22675/**
22676 * Output specification for XSL-FO (Extensible Stylesheet
22677 * Language - Formatting Objects)
22678 *
22679 * See https://www.w3.org/TR/xsl11/#fo-section
22680 */
22681CSL.Output.Formats.prototype.fo = {
22682    "text_escape": function (text) {
22683        if (!text) {
22684            text = "";
22685        }
22686        return text.replace(/&/g, "&#38;")
22687            .replace(/</g, "&#60;")
22688            .replace(/>/g, "&#62;")
22689            .replace("  ", "&#160; ", "g")
22690            .replace(CSL.SUPERSCRIPTS_REGEXP, function(aChar) {
22691                return "<fo:inline vertical-align=\"super\">" + CSL.SUPERSCRIPTS[aChar] + "</fo:inline>";
22692            });
22693    },
22694    "bibstart": "",
22695    "bibend": "",
22696    "@passthrough/true": CSL.Output.Formatters.passthrough,
22697    "@font-style/italic": "<fo:inline font-style=\"italic\">%%STRING%%</fo:inline>",
22698    "@font-style/oblique": "<fo:inline font-style=\"oblique\">%%STRING%%</fo:inline>",
22699    "@font-style/normal": "<fo:inline font-style=\"normal\">%%STRING%%</fo:inline>",
22700    "@font-variant/small-caps": "<fo:inline font-variant=\"small-caps\">%%STRING%%</fo:inline>",
22701    "@font-variant/normal": "<fo:inline font-variant=\"normal\">%%STRING%%</fo:inline>",
22702    "@font-weight/bold": "<fo:inline font-weight=\"bold\">%%STRING%%</fo:inline>",
22703    "@font-weight/normal": "<fo:inline font-weight=\"normal\">%%STRING%%</fo:inline>",
22704    "@font-weight/light": "<fo:inline font-weight=\"lighter\">%%STRING%%</fo:inline>",
22705    "@text-decoration/none": "<fo:inline text-decoration=\"none\">%%STRING%%</fo:inline>",
22706    "@text-decoration/underline": "<fo:inline text-decoration=\"underline\">%%STRING%%</fo:inline>",
22707    "@vertical-align/sup": "<fo:inline vertical-align=\"super\">%%STRING%%</fo:inline>",
22708    "@vertical-align/sub": "<fo:inline vertical-align=\"sub\">%%STRING%%</fo:inline>",
22709    "@vertical-align/baseline": "<fo:inline vertical-align=\"baseline\">%%STRING%%</fo:inline>",
22710    "@strip-periods/true": CSL.Output.Formatters.passthrough,
22711    "@strip-periods/false": CSL.Output.Formatters.passthrough,
22712    "@quotes/true": function (state, str) {
22713        if ("undefined" === typeof str) {
22714            return state.getTerm("open-quote");
22715        }
22716        return state.getTerm("open-quote") + str + state.getTerm("close-quote");
22717    },
22718    "@quotes/inner": function (state, str) {
22719        if ("undefined" === typeof str) {
22720            return "\u2019";
22721        }
22722        return state.getTerm("open-inner-quote") + str + state.getTerm("close-inner-quote");
22723    },
22724    "@quotes/false": false,
22725    "@cite/entry": function (state, str) {
22726        return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt);
22727    },
22728    "@bibliography/entry": function (state, str) {
22729        var indent = "";
22730        if (state.bibliography && state.bibliography.opt && state.bibliography.opt.hangingindent) {
22731            var hi = state.bibliography.opt.hangingindent;
22732            indent = " start-indent=\"" + hi +"em\" text-indent=\"-" + hi + "em\"";
22733        }
22734        var insert = "";
22735        if (state.sys.embedBibliographyEntry) {
22736            insert = state.sys.embedBibliographyEntry(this.item_id) + "\n";
22737        }
22738        return "<fo:block id=\"" + this.system_id + "\"" + indent + ">" + str + "</fo:block>\n" + insert;
22739    },
22740    "@display/block": function (state, str) {
22741        return "\n  <fo:block>" + str + "</fo:block>\n";
22742    },
22743    "@display/left-margin": function (state, str) {
22744        return "\n  <fo:table table-layout=\"fixed\" width=\"100%\">\n    " +
22745                "<fo:table-column column-number=\"1\" column-width=\"$$$__COLUMN_WIDTH_1__$$$\"/>\n    " +
22746                "<fo:table-column column-number=\"2\" column-width=\"proportional-column-width(1)\"/>\n    " +
22747                "<fo:table-body>\n      " +
22748                    "<fo:table-row>\n        " +
22749                        "<fo:table-cell>\n          " +
22750                            "<fo:block>" + str + "</fo:block>\n        " +
22751                        "</fo:table-cell>\n        ";
22752    },
22753    "@display/right-inline": function (state, str) {
22754        return "<fo:table-cell>\n          " +
22755                "<fo:block>" + str + "</fo:block>\n        " +
22756            "</fo:table-cell>\n      " +
22757            "</fo:table-row>\n    " +
22758            "</fo:table-body>\n  " +
22759            "</fo:table>\n";
22760    },
22761    "@display/indent": function (state, str) {
22762        return "<fo:block margin-left=\"2em\">" + str + "</fo:block>\n";
22763    },
22764    "@showid/true": function (state, str) {
22765        if (!state.tmp.just_looking && !state.tmp.suppress_decorations && this.params && "string" === typeof str) {
22766            var prePunct = "";
22767            if (str) {
22768                var m = str.match(CSL.VARIABLE_WRAPPER_PREPUNCT_REX);
22769                prePunct = m[1];
22770                str = m[2];
22771            }
22772            var postPunct = "";
22773            if (str && CSL.SWAPPING_PUNCTUATION.indexOf(str.slice(-1)) > -1) {
22774                postPunct = str.slice(-1);
22775                str = str.slice(0,-1);
22776            }
22777            return state.sys.variableWrapper(this.params, prePunct, str, postPunct);
22778        } else {
22779            return str;
22780        }
22781    },
22782    "@URL/true": function (state, str) {
22783        return "<fo:basic-link external-destination=\"url('" + str + "')\">" + str + "</fo:basic-link>";
22784    },
22785    "@DOI/true": function (state, str) {
22786        var doiurl = str;
22787        if (!str.match(/^https?:\/\//)) {
22788            doiurl = "https://doi.org/" + str;
22789        }
22790        return "<fo:basic-link external-destination=\"url('" + doiurl + "')\">" + str + "</fo:basic-link>";
22791    }
22792};
22793
22794/**
22795 * LaTeX .bbl output.
22796 *
22797 * (Code contributed by Egon Willighagen, based on the prototype.text code.)
22798 */
22799CSL.Output.Formats.prototype.latex = {
22800    "text_escape": function (text) {
22801        if (!text) {
22802            text = "";
22803        }
22804        return text;
22805    },
22806    "bibstart": "\\begin{thebibliography}{4}",
22807    "bibend": "\\end{thebibliography}",
22808    "@font-style/italic": "{\\em %%STRING%%}",
22809    "@font-style/oblique": false,
22810    "@font-style/normal": false,
22811    "@font-variant/small-caps": false,
22812    "@passthrough/true": CSL.Output.Formatters.passthrough,
22813    "@font-variant/normal": false,
22814    "@font-weight/bold": "{\\bf %%STRING%%}",
22815    "@font-weight/normal": false,
22816    "@font-weight/light": false,
22817    "@text-decoration/none": false,
22818    "@text-decoration/underline": false,
22819    "@vertical-align/baseline": false,
22820    "@vertical-align/sup": false,
22821    "@vertical-align/sub": false,
22822    "@strip-periods/true": CSL.Output.Formatters.passthrough,
22823    "@strip-periods/false": CSL.Output.Formatters.passthrough,
22824    "@quotes/true": function (state, str) {
22825        if ("undefined" === typeof str) {
22826            return state.getTerm("open-quote");
22827        }
22828        return state.getTerm("open-quote") + str + state.getTerm("close-quote");
22829    },
22830    "@quotes/inner": function (state, str) {
22831        if ("undefined" === typeof str) {
22832            //
22833            // Mostly right by being wrong (for apostrophes)
22834            //
22835            return "\u2019";
22836        }
22837        return state.getTerm("open-inner-quote") + str + state.getTerm("close-inner-quote");
22838    },
22839    "@quotes/false": false,
22840    //"@bibliography/body": function (state,str){
22841    //    return "<div class=\"csl-bib-body\">\n"+str+"</div>";
22842    //},
22843    "@cite/entry": function (state, str) {
22844		return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt);
22845	},
22846    "@bibliography/entry": function (state, str) {
22847        return "\\bibitem{" + state.sys.embedBibliographyEntry(this.item_id) + "}\n";
22848    },
22849    "@display/block": function (state, str) {
22850        return "\n"+str;
22851    },
22852    "@display/left-margin": function (state, str) {
22853        return str;
22854    },
22855    "@display/right-inline": function (state, str) {
22856        return str;
22857    },
22858    "@display/indent": function (state, str) {
22859        return "\n    "+str;
22860    },
22861    "@showid/true": function (state, str, cslid) {
22862        return str;
22863    },
22864    "@URL/true": function (state, str) {
22865        return str;
22866    },
22867    "@DOI/true": function (state, str) {
22868        return str;
22869    }
22870};
22871
22872CSL.Output.Formats = new CSL.Output.Formats();
22873
22874/*global CSL: true */
22875
22876
22877//
22878// Time for a rewrite of this module.
22879//
22880// Simon has pointed out that list and hash behavior can
22881// be obtained by ... just using a list and a hash.  This
22882// is faster for batched operations, because sorting is
22883// greatly optimized.  Since most of the interaction
22884// with plugins at runtime will involve batches of
22885// references, there will be solid gains if the current,
22886// one-reference-at-a-time approach implemented here
22887// can be replaced with something that leverages the native
22888// sort method of the Array() type.
22889//
22890// That's going to take some redesign, but it will simplify
22891// things in the long run, so it might as well happen now.
22892//
22893// We'll keep makeCitationCluster and makeBibliography as
22894// simple methods that return a string.  Neither should
22895// have any effect on internal state.  This will be a change
22896// in behavior for makeCitationCluster.
22897//
22898// A new updateItems command will be introduced, to replace
22899// insertItems.  It will be a simple list of IDs, in the
22900// sequence of first reference in the document.
22901//
22902// The calling application should always invoke updateItems
22903// before makeCitationCluster.
22904//
22905
22906//
22907// should allow batched registration of items by
22908// key.  should behave as an update, with deletion
22909// of items and the tainting of disambiguation
22910// partner sets affected by a deletes and additions.
22911//
22912//
22913// we'll need a reset method, to clear the decks
22914// in the citation area and start over.
22915
22916/**
22917 * Registry of cited items.
22918 * <p>This is a persistent store of disambiguation and
22919 * sort order information relating to individual items
22920 * for which rendering is requested.  Item data is stored
22921 * in a hash, with the item key as hash key, for quick
22922 * retrieval.  A virtual sequence within the hashed store
22923 * is maintained on the fly as items are added to the
22924 * store, using <code>*_next</code> and <code>*_prev</code>
22925 * attributes on each item.  A separate hash of items
22926 * based on their undisambiguated cite form is
22927 * maintained, and the item id list and disambiguation
22928 * level for each set of disambiguation partners is shared
22929 * through the registry item.</p>
22930 * @class
22931 */
22932CSL.Registry = function (state) {
22933    this.debug = false;
22934    this.state = state;
22935    this.registry = {};
22936    this.reflist = [];
22937    this.refhash = {};
22938    this.namereg = new CSL.Registry.NameReg(state);
22939    this.citationreg = new CSL.Registry.CitationReg(state);
22940    // See CSL.NameOutput.prototype.outputNames
22941    // and CSL.Registry.prototype.doinserts
22942    this.authorstrings = {};
22943    
22944    // for parallel delimiter support
22945    this.masterMap = {};
22946    
22947    //
22948    // shared scratch vars
22949    this.mylist = [];
22950    this.myhash = {};
22951    this.deletes = [];
22952    this.inserts = [];
22953    this.uncited = {};
22954    this.refreshes = {};
22955    this.akeys = {};
22956    this.oldseq = {};
22957    this.return_data = {};
22958    //
22959    // each ambig is a list of the ids of other objects
22960    // that have the same base-level rendering
22961    this.ambigcites = {};
22962    this.ambigresets = {};
22963    this.sorter = new CSL.Registry.Comparifier(state, "bibliography_sort");
22964    //this.modes = CSL.getModes.call(this.state);
22965    //this.checkerator = new CSL.Checkerator();
22966
vendor: 81,607 bytes, lines 22967-25088
22967    this.getSortedIds = function () {
22968        var ret = [];
22969        for (var i = 0, ilen = this.reflist.length; i < ilen; i += 1) {
22970            ret.push("" + this.reflist[i].id);
22971        }
22972        return ret;
22973    };
22974
22975    this.getSortedRegistryItems = function () {
22976        var ret = [];
22977        for (var i = 0, ilen = this.reflist.length; i < ilen; i += 1) {
22978            ret.push(this.reflist[i]);
22979        }
22980        return ret;
22981    };
22982};
22983
22984//
22985// Here's the sequence of operations to be performed on
22986// update:
22987//
22988//  1.  (o) [init] Receive list as function argument, store as hash and as list.
22989//  2.  (o) [init] Initialize refresh list.  Never needs sorting, only hash required.
22990
22991//  3.  (o) [dodeletes] Delete loop.
22992//  3a. (o) [dodeletes] Delete names in items to be deleted from names reg.
22993//  3b. (o) [dodeletes] Complement refreshes list with items affected by
22994//      possible name changes.  We'll actually perform the refresh once
22995//      all of the necessary data and parameters have been established
22996//      in the registry.
22997//  3c. (o) [dodeletes] Delete all items to be deleted from their disambig pools.
22998//  3d. (o) [dodeletes] Delete all items in deletion list from hash.
22999
23000//  4.  (o) [doinserts] Insert loop.
23001//  4a. (o) [doinserts] Retrieve entries for items to insert.
23002//  4b. (o) [doinserts] Generate ambig key.
23003//  4c. (o) [doinserts] Add names in items to be inserted to names reg
23004//      (implicit in getAmbiguousCite).
23005//  4d. (o) [doinserts] Record ambig pool key on akey list (used for updating further
23006//      down the chain).
23007//  4e. (o) [doinserts] Create registry token.
23008//  4f. (o) [doinserts] Add item ID to hash.
23009//  4g. (o) [doinserts] Set and record the base token to hold disambiguation
23010//      results ("disambig" in the object above).
23011//  5.  (o) [rebuildlist] Create "new" list of hash pointers, in the order given
23012//          in the argument to the update function.
23013//  6.  (o) [rebuildlist] Apply citation numbers to new list.
23014//  7.  (o) [dorefreshes] Refresh items requiring update.
23015
23016
23017
23018//  5. (o) [delnames] Delete names in items to be deleted from names reg, and obtain IDs
23019//         of other items that would be affected by changes around that surname.
23020//  6. (o) [delnames] Complement delete and insert lists with items affected by
23021//         possible name changes.
23022//  7. (o) [delambigs] Delete all items to be deleted from their disambig pools.
23023//  8. (o) [delhash] Delete all items in deletion list from hash.
23024
23025//  9. (o) [addtohash] Retrieve entries for items to insert.
23026// 10. (o) [addtohash] Add items to be inserted to their disambig pools.
23027// 11. (o) [addtohash] Add names in items to be inserted to names reg
23028//         (implicit in getAmbiguousCite).
23029// 12. (o) [addtohash] Create registry token for each item to be inserted.
23030// 13. (o) [addtohash] Add items for insert to hash.
23031
23032// 14. (o) [buildlist] Create "new" list of hash pointers, in the order given in the argument
23033//         to the update function.
23034// 15. (o) [renumber] Apply citation numbers to new list.
23035// 16. (o) [setdisambigs] Set disambiguation parameters on each inserted item token.
23036// 17. (o) [setsortkeys] Set sort keys on each item token.
23037// 18. (o) [sorttokens] Resort token list
23038// 19. (o) [renumber] Reset citation numbers on list items
23039//
23040
23041CSL.Registry.prototype.init = function (itemIDs, uncited_flag) {
23042    var i, ilen;
23043    this.oldseq = {};
23044    //  1. Receive list as function argument, store as hash and as list.
23045    //
23046    // Result:
23047    //   this.mylist: a list of all itemIDs of referenced items, cited and uncited.
23048    //   this.myhash: a hash of index positions in this.mylist.
23049    //   this.uncited: hash of uncited itemIDs.
23050    //
23051    // Proceed as follows.
23052    //
23053    if (uncited_flag) {
23054        // If uncited_flag is non-nil, add any missing itemIDs to this.mylist
23055        // from itemIDs input list, and set the itemIDs in itemIDs on this.uncited.
23056        this.uncited = {};
23057        for (var i=0,ilen=itemIDs.length;i<ilen; i += 1) {
23058            if (!this.myhash[itemIDs[i]]) {
23059                this.mylist.push("" + itemIDs[i]);
23060            }
23061            this.uncited[itemIDs[i]] = true;
23062            this.myhash[itemIDs[i]] = true;
23063        }
23064    } else {
23065        // If uncited_flag is nil, remove duplicate itemIDs from itemIDs input
23066        // list, set the result on this.mylist, and add missing itemIDs to
23067        // this.mylist from itemIDs input list.
23068        for (var key in this.uncited) {
23069            itemIDs.push(key);
23070        }
23071        var myhash = {};
23072        for (i=itemIDs.length-1;i>-1; i += -1) {
23073            if (myhash[itemIDs[i]]) {
23074                itemIDs = itemIDs.slice(0, i).concat(itemIDs.slice(i + 1));
23075            } else {
23076                myhash[itemIDs[i]] = true;
23077            }
23078        }
23079        this.mylist = itemIDs;
23080        this.myhash = myhash;
23081    }
23082    //
23083    //  2. Initialize refresh list.  Never needs sorting, only hash required.
23084    //
23085    this.refreshes = {};
23086    this.touched = {};
23087    this.ambigsTouched = {};
23088    this.ambigresets = {};
23089};
23090
23091CSL.Registry.prototype.dopurge = function (myhash) {
23092    // Remove any uncited items not in myhash
23093    for (var i=this.mylist.length-1;i>-1;i+=-1) {
23094        // Might not want to be quite this restrictive.
23095        if (this.citationreg.citationsByItemId) {
23096            if ((!this.citationreg.citationsByItemId || !this.citationreg.citationsByItemId[this.mylist[i]]) && !myhash[this.mylist[i]]) {
23097                delete this.myhash[this.mylist[i]];
23098                delete this.uncited[this.mylist[i]];
23099                this.mylist = this.mylist.slice(0,i).concat(this.mylist.slice(i+1));
23100            }
23101        }
23102    }
23103    this.dodeletes(this.myhash);
23104};
23105
23106CSL.Registry.prototype.dodeletes = function (myhash) {
23107    var otheritems, key, ambig, pos, len, items, kkey, mypos, id;
23108    if ("string" === typeof myhash) {
23109        var key = myhash;
23110        myhash = {};
23111        myhash[key] = true;
23112    }
23113    //
23114    //  3. Delete loop.
23115    //
23116    for (var key in this.registry) {
23117        if (!myhash[key]) {
23118            // skip items explicitly marked as uncited
23119            if (this.uncited[key]) {
23120                continue;
23121            }
23122            //
23123            //  3a. Delete names in items to be deleted from names reg.
23124            //
23125            otheritems = this.namereg.delitems(key);
23126            //
23127            //  3b. Complement refreshes list with items affected by
23128            //      possible name changes.  We'll actually perform the refresh once
23129            //      all of the necessary data and parameters have been established
23130            //      in the registry.
23131            //
23132            for (kkey in otheritems) {
23133                this.refreshes[kkey] = true;
23134            }
23135            //
23136            //  3c. Delete all items to be deleted from their disambig pools.
23137            //
23138            ambig = this.registry[key].ambig;
23139            mypos = this.ambigcites[ambig].indexOf(key);
23140            if (mypos > -1) {
23141                items = this.ambigcites[ambig].slice();
23142                this.ambigcites[ambig] = items.slice(0, mypos).concat(items.slice(mypos+1, items.length));
23143                this.ambigresets[ambig] = this.ambigcites[ambig].length;
23144            }
23145            //
23146            // XX. What we've missed is to provide an update of all
23147            // items sharing the same ambig  += -1 the remaining items in
23148            // ambigcites.  So let's do that here, just in case the
23149            // names update above doesn't catch them all.
23150            //
23151            len = this.ambigcites[ambig].length;
23152            for (pos = 0; pos < len; pos += 1) {
23153                id = "" + this.ambigcites[ambig][pos];
23154                this.refreshes[id] = true;
23155            }
23156            //
23157            // 3d-0. Remove parallel id references and realign
23158            // parallel ID refs.
23159            //
23160            if (this.registry[key].siblings) {
23161                if (this.registry[key].siblings.length == 1) {
23162                    var loneSiblingID = this.registry[key].siblings[0];
23163                    if (this.registry[loneSiblingID].siblings) {
23164                        this.registry[loneSiblingID].siblings.pop();
23165                        this.registry[loneSiblingID].master = true;
23166                        // this.registry[loneSiblingID].parallel = false;
23167                    }
23168                } else if (this.registry[key].siblings.length > 1) {
23169                    var removeIDs = [key];
23170                    if (this.registry[key].master) {
23171                        var newmasterID = this.registry[key].siblings[0];
23172                        var newmaster = this.registry[newmasterID];
23173                        newmaster.master = true;
23174                        // newmaster.parallel_delimiter is set externally, if at all
23175                        // newmaster.parallel = false;
23176                        removeIDs.push(newmasterID);
23177                        // for (var k = 0, klen = this.registry[key].siblings.length; k < klen; k += 1) {
23178                        //     this.registry[this.registry[key].siblings[k]].parallel = newmasterID;
23179                        // }
23180                    }
23181                    var buffer = [];
23182                    for (var k = this.registry[key].siblings.length - 1; k > -1; k += -1) {
23183                        var siblingID = this.registry[key].siblings.pop();
23184                        if (removeIDs.indexOf(siblingID) === -1) {
23185                            buffer.push(siblingID);
23186                        }
23187                    }
23188                    for (var k = buffer.length - 1; k > -1; k += -1) {
23189                        this.registry[key].siblings.push(buffer[k]);
23190                    }
23191                }
23192            }
23193            //
23194            // 3d-1. Remove item from reflist
23195            for (var i=this.reflist.length-1;i>-1;i--) {
23196                if (this.reflist[i].id === key) {
23197                    this.reflist = this.reflist.slice(0, i).concat(this.reflist.slice(i+1));
23198                }
23199            }
23200            //
23201            //  3d. Delete all items in deletion list from hash.
23202            //
23203            delete this.registry[key];
23204            delete this.refhash[key];
23205
23206            // For processCitationCluster()
23207            this.return_data.bibchange = true;
23208        }
23209    }
23210    // Disabled.  See formats.js for code.
23211    // this.state.fun.decorate.items_delete( this.state.output[this.state.opt.mode].tmp, myhash );
23212};
23213
23214CSL.Registry.prototype.doinserts = function (mylist) {
23215    var item, Item, akey, newitem, abase, i, ilen;
23216    if ("string" === typeof mylist) {
23217        mylist = [mylist];
23218    }
23219    //
23220    //  4. Insert loop.
23221    //
23222    for (var i = 0, ilen = mylist.length; i < ilen; i += 1) {
23223        item = mylist[i];
23224        if (!this.registry[item]) {
23225            //
23226            //  4a. Retrieve entries for items to insert.
23227            //
23228            Item = this.state.retrieveItem(item);
23229
23230            //
23231            //  4b. Generate ambig key.
23232            //
23233            // AND
23234            //
23235            //  4c. Add names in items to be inserted to names reg
23236            //      (implicit in getAmbiguousCite).
23237            //
23238            akey = CSL.getAmbiguousCite.call(this.state, Item);
23239            this.ambigsTouched[akey] = true;
23240            //
23241            //  4d. Record ambig pool key on akey list (used for updating further
23242            //      down the chain).
23243            //
23244            if (!Item.legislation_id) {
23245                this.akeys[akey] = true;
23246            }
23247            //
23248            //  4e. Create registry token.
23249            //
23250            newitem = {
23251                "id": "" + item,
23252                "seq": 0,
23253                "offset": 0,
23254                "sortkeys": false,
23255                "ambig": false,
23256                "rendered": false,
23257                "disambig": false,
23258                "ref": Item,
23259                "newItem": true
23260            };
23261            //
23262            //
23263            //  4f. Add item ID to hash.
23264            //
23265            this.registry[item] = newitem;
23266            //
23267            //  4f(a). Add first reference note number
23268            //         (this may be redundant)
23269            if (this.citationreg.citationsByItemId && this.citationreg.citationsByItemId[item]) {
23270                this.registry[item]["first-reference-note-number"] = this.citationreg.citationsByItemId[item][0].properties.noteIndex;
23271            }
23272
23273            //
23274            //  4g. Set and record the base token to hold disambiguation
23275            //      results ("disambig" in the object above).
23276            //
23277            abase = CSL.getAmbigConfig.call(this.state);
23278            this.registerAmbigToken(akey, item, abase);
23279
23280            //if (!this.ambigcites[akey]){
23281            //    this.ambigcites[akey] = [];
23282            //}
23283            //CSL.debug("Run: "+item+"("+this.ambigcites[akey]+")");
23284            //if (this.ambigcites[akey].indexOf(item) === -1){
23285            //    CSL.debug("  Add: "+item);
23286            //    this.ambigcites[akey].push(item);
23287            //}
23288            //
23289            //  4h. Make a note that this item needs its sort keys refreshed.
23290            //
23291            this.touched[item] = true;
23292            // For processCitationCluster()
23293            this.return_data.bibchange = true;
23294        }
23295    }
23296    // Disabled.  See formats.js for code.
23297    // this.state.fun.decorate.items_add( this.state.output[this.state.opt.mode].tmp, mylist );
23298};
23299
23300/*
23301// No longer required.
23302CSL.Registry.prototype.douncited = function () {
23303    var pos, len;
23304    var cited_len = this.mylist.length - this.uncited.length;
23305    for (pos = 0, len = cited_len; pos < len; pos += 1) {
23306        this.registry[this.mylist[pos]].uncited = false;
23307    }
23308    for (pos = cited_len, len = this.mylist.length; pos < len; pos += 1) {
23309        this.registry[this.mylist[pos]].uncited = true;
23310    }
23311};
23312*/
23313
23314CSL.Registry.prototype.rebuildlist = function (nosort) {
23315    var len, pos, item, Item;
23316    //
23317    //  5. Create "new" list of hash pointers, in the order given in the argument
23318    //     to the update function.
23319    //
23320    //
23321    // XXX Keep reflist in place.
23322    //
23323    if (!nosort) {
23324        this.reflist_inserts = [];
23325        //
23326        //  6. Apply citation numbers to new list,
23327        //     saving off old sequence numbers as we go.
23328        //
23329        // XXX Just memo inserts -- actual insert happens below, at last "sort"
23330        //
23331        len = this.mylist.length;
23332        for (pos = 0; pos < len; pos += 1) {
23333            item = this.mylist[pos];
23334            Item = this.registry[item];
23335            if (Item.newItem) {
23336                this.reflist_inserts.push(Item);
23337            }
23338            this.oldseq[item] = this.registry[item].seq;
23339            this.registry[item].seq = (pos + 1);
23340        }
23341    } else {
23342        this.reflist = [];
23343        len = this.mylist.length;
23344        for (pos = 0; pos < len; pos += 1) {
23345            item = this.mylist[pos];
23346            Item = this.registry[item];
23347            this.reflist.push(Item);
23348            this.oldseq[item] = this.registry[item].seq;
23349            this.registry[item].seq = (pos + 1);
23350        }
23351    }
23352};
23353
23354/*
23355 * Okay, at this point we should have a numbered list
23356 * of registry tokens in the notional order requested,
23357 * with sequence numbers to reconstruct the ordering
23358 * if the list is remangled.  So far so good.
23359 */
23360
23361CSL.Registry.prototype.dorefreshes = function () {
23362    var key, regtoken, Item, akey, abase;
23363    //
23364    //  7. Refresh items requiring update.
23365    //
23366    // It looks like we need to do four things on each cite for refresh:
23367    // (1) Generate the akey for the cite.
23368    // (2) Register it on the ambig token.
23369    // (3) Register the akey in this.akeys
23370    // (4) Register the item ID in this.touched
23371    //
23372    for (var key in this.refreshes) {
23373        regtoken = this.registry[key];
23374        if (!regtoken) {
23375            continue;
23376        }
23377        regtoken.sortkeys = undefined;
23378        Item = this.state.refetchItem(key);
23379        var akey = regtoken.ambig;
23380
23381        if ("undefined" === typeof akey) {
23382            this.state.tmp.disambig_settings = false;
23383            akey = CSL.getAmbiguousCite.call(this.state, Item);
23384            abase = CSL.getAmbigConfig.call(this.state);
23385            this.registerAmbigToken(akey, key, abase);
23386        }
23387        for (var akkey in this.ambigresets) {
23388            if (this.ambigresets[akkey] === 1) {
23389                var loneKey = this.ambigcites[akey][0];
23390                var Item = this.state.refetchItem(loneKey);
23391                this.registry[loneKey].disambig = new CSL.AmbigConfig();
23392                this.state.tmp.disambig_settings = false;
23393                var akey = CSL.getAmbiguousCite.call(this.state, Item);
23394                var abase = CSL.getAmbigConfig.call(this.state);
23395                this.registerAmbigToken(akey, loneKey, abase);
23396            }
23397        }
23398        this.state.tmp.taintedItemIDs[key] = true;
23399        this.ambigsTouched[akey] = true;
23400        if (!Item.legislation_id) {
23401            this.akeys[akey] = true;
23402        }
23403        this.touched[key] = true;
23404    }
23405};
23406
23407/*
23408 * Main disambiguation  += -1 can everything for disambiguation be
23409 * crunched into this function?
23410 */
23411CSL.Registry.prototype.setdisambigs = function () {
23412    //
23413    // Okay, more changes.  Here is where we resolve all disambiguation
23414    // issues for cites touched by the update.  The this.ambigcites set is
23415    // based on the complete short form of citations, and is the basis on
23416    // which names are added and minimal adding of initials or given names
23417    // is performed.
23418    //
23419
23420    //
23421    //  8.  Set disambiguation parameters on each inserted item token.
23422    //
23423    for (var akey in this.ambigsTouched) {
23424        //
23425        // Disambiguation is fully encapsulated.
23426        // Disambiguator will run only if there are multiple
23427        // items, and at least one disambiguation mode is
23428        // in effect.
23429        this.state.disambiguate.run(akey);
23430    }
23431    this.ambigsTouched = {};
23432    this.akeys = {};
23433};
23434
23435
23436
23437CSL.Registry.prototype.renumber = function () {
23438    var len, pos, item;
23439    //
23440    // 19. Reset citation numbers on list items
23441    //
23442    if (this.state.bibliography_sort.opt.citation_number_sort_direction === CSL.DESCENDING) {
23443        this.state.bibliography_sort.tmp.citation_number_map = {};
23444    }
23445    len = this.reflist.length;
23446    for (pos = 0; pos < len; pos += 1) {
23447        item = this.reflist[pos];
23448        // save the overhead of rerenderings if citation-number is not
23449        // used in the style.
23450        item.seq = (pos + 1);
23451        if (this.state.bibliography_sort.opt.citation_number_sort_direction === CSL.DESCENDING) {
23452            this.state.bibliography_sort.tmp.citation_number_map[item.seq] = (this.reflist.length - item.seq + 1);
23453        }
23454        // update_mode is set to CSL.NUMERIC if citation-number is rendered
23455        // in citations.
23456        if (this.state.opt.update_mode === CSL.NUMERIC && item.seq != this.oldseq[item.id]) {
23457            this.state.tmp.taintedItemIDs[item.id] = true;
23458        }
23459        if (item.seq != this.oldseq[item.id]) {
23460            this.return_data.bibchange = true;
23461        }
23462    }
23463};
23464
23465CSL.Registry.prototype.setsortkeys = function () {
23466    var key;
23467    //
23468    // 17. Set sort keys on each item token.
23469    //
23470    for (var i = 0, ilen = this.mylist.length; i < ilen; i += 1) {
23471        var key = this.mylist[i];
23472        // The last of these conditions may create some thrashing on styles that do not require sorting.
23473        if (this.touched[key] || this.state.tmp.taintedItemIDs[key] || !this.registry[key].sortkeys) {
23474            this.registry[key].sortkeys = CSL.getSortKeys.call(this.state, this.state.retrieveItem(key), "bibliography_sort");
23475        }
23476    }
23477};
23478
23479CSL.Registry.prototype._insertItem = function(element, array) {
23480    array.splice(this._locationOf(element, array) + 1, 0, element);
23481    return array;
23482};
23483
23484CSL.Registry.prototype._locationOf = function(element, array, start, end) {
23485    if (array.length === 0) {
23486        return -1;
23487    }
23488    start = start || 0;
23489    end = end || array.length;
23490    var pivot = (start + end) >> 1;  // should be faster than dividing by 2
23491    
23492    var c = this.sorter.compareKeys(element, array[pivot]);
23493    if (end - start <= 1) {
23494        return c == -1 ? pivot - 1 : pivot;
23495    }
23496    switch (c) {
23497        case -1: return this._locationOf(element, array, start, pivot);
23498        case 0: return pivot;
23499        case 1: return this._locationOf(element, array, pivot, end);
23500    }
23501};
23502
23503CSL.Registry.prototype.sorttokens = function (nosort) {
23504    var len, item, Item, pos;
23505    //
23506    // 18. Resort token list.
23507    //
23508    if (!nosort) {
23509        this.reflist_inserts = [];
23510        len = this.mylist.length;
23511        for (pos = 0; pos < len; pos += 1) {
23512            item = this.mylist[pos];
23513            Item = this.registry[item];
23514            if (Item.newItem) {
23515                this.reflist_inserts.push(Item);
23516            }
23517        }
23518        // There is a thin possibility that tainted items in a sorted list
23519        // will change position due to disambiguation. We cover for that here.
23520        for (var key in this.state.tmp.taintedItemIDs) {
23521            if (this.registry[key] && !this.registry[key].newItem) {
23522                // Move tainted items from reflist to reflist_inserts
23523                for (var i=this.reflist.length-1;i>-1;i--) {
23524                    if (this.reflist[i].id === key) {
23525                        this.reflist_inserts.push(this.reflist[i]);
23526                        this.reflist = this.reflist.slice(0, i).concat(this.reflist.slice(i+1));
23527                    }
23528                }
23529            }
23530        }
23531        for (var i=0,ilen=this.reflist_inserts.length;i<ilen;i++) {
23532            var Item = this.reflist_inserts[i];
23533            delete Item.newItem;
23534            this.reflist = this._insertItem(Item, this.reflist);
23535        }
23536        for (pos = 0; pos < len; pos += 1) {
23537            item = this.mylist[pos];
23538            Item = this.registry[item];
23539            this.registry[item].seq = (pos + 1);
23540        }
23541    }
23542};
23543
23544/**
23545 * Compare two sort keys
23546 * <p>Nested, because keys are an array.</p>
23547 */
23548CSL.Registry.Comparifier = function (state, keyset) {
23549    var sort_directions, len, pos, compareKeys;
23550    var sortCompare = CSL.getSortCompare.call(state, state.opt["default-locale-sort"]);
23551    sort_directions = state[keyset].opt.sort_directions;
23552    this.compareKeys = function (a, b) {
23553        len = a.sortkeys ? a.sortkeys.length : 0;
23554        for (pos = 0; pos < len; pos += 1) {
23555            //
23556            // for ascending sort 1 uses 1, -1 uses -1.
23557            // For descending sort, the values are reversed.
23558            //
23559            // Need to handle undefined values.  No way around it.
23560            // So have to screen .localeCompare (which is also
23561            // needed) from undefined values.  Everywhere, in all
23562            // compares.
23563            //
23564            var cmp = 0;
23565            if (a.sortkeys[pos] === b.sortkeys[pos]) {
23566                cmp = 0;
23567            } else if ("undefined" === typeof a.sortkeys[pos]) {
23568                cmp = sort_directions[pos][1];
23569            } else if ("undefined" === typeof b.sortkeys[pos]) {
23570                cmp = sort_directions[pos][0];
23571            } else {
23572                // cmp = a.sortkeys[pos].localeCompare(b.sortkeys[pos]);
23573                cmp = sortCompare(a.sortkeys[pos], b.sortkeys[pos]);
23574            }
23575            if (0 < cmp) {
23576                return sort_directions[pos][1];
23577            } else if (0 > cmp) {
23578                return sort_directions[pos][0];
23579            }
23580        }
23581        if (a.seq > b.seq) {
23582            return 1;
23583        } else if (a.seq < b.seq) {
23584            return -1;
23585        }
23586        return 0;
23587    };
23588    compareKeys = this.compareKeys;
23589    this.compareCompositeKeys = function (a, b) {
23590        return compareKeys(a[1], b[1]);
23591    };
23592};
23593
23594
23595/**
23596 * Compare two disambiguation tokens by their registry sort order
23597 * <p>Disambiguation lists need to be sorted this way, to
23598 * obtain the correct year-suffix when that's used.</p>
23599 */
23600CSL.Registry.prototype.compareRegistryTokens = function (a, b) {
23601    if (a.seq > b.seq) {
23602        return 1;
23603    } else if (a.seq < b.seq) {
23604        return -1;
23605    }
23606    return 0;
23607};
23608
23609CSL.Registry.prototype.registerAmbigToken = function (akey, id, ambig_config) {
23610    //SNIP-START
23611    if (!this.registry[id]) {
23612        CSL.debug("Warning: unregistered item: itemID=("+id+"), akey=("+akey+")");
23613    }
23614    //SNIP-END
23615    // Taint if number of names to be included has changed
23616    if (this.registry[id] && this.registry[id].disambig && this.registry[id].disambig.names) {
23617        for (var i = 0, ilen = ambig_config.names.length; i < ilen; i += 1) {
23618            var new_names_params = ambig_config.names[i];
23619            var old_names_params = this.registry[id].disambig.names[i];
23620            if (new_names_params !== old_names_params) {
23621                this.state.tmp.taintedItemIDs[id] = true;
23622            } else if (ambig_config.givens[i]) {
23623                // Compare givenses only if the number of names is aligned.
23624                for (var j=0,jlen=ambig_config.givens[i].length;j<jlen;j+=1) {
23625                    var new_gnames_params = ambig_config.givens[i][j];
23626                    var old_gnames_params = this.registry[id].disambig.givens[i][j];
23627                    if (new_gnames_params !== old_gnames_params) {
23628                        this.state.tmp.taintedItemIDs[id] = true;
23629                    }
23630                }
23631            }
23632        }
23633    }
23634
23635    if (!this.ambigcites[akey]) {
23636        this.ambigcites[akey] = [];
23637    }
23638    if (this.ambigcites[akey].indexOf("" + id) === -1) {
23639        this.ambigcites[akey].push("" + id);
23640    }
23641    this.registry[id].ambig = akey;
23642    this.registry[id].disambig = CSL.cloneAmbigConfig(ambig_config);
23643};
23644
23645
23646/**
23647 * Get the sort key of an item, without decorations
23648 * <p>This is used internally by the Registry.</p>
23649 */
23650CSL.getSortKeys = function (Item, key_type) {
23651    var area, root, extension, strip_prepositions, len, pos;
23652    //SNIP-START
23653    if (false) {
23654        CSL.debug("KEY TYPE: " + key_type);
23655    }
23656    //SNIP-END
23657    area = this.tmp.area;
23658    root = this.tmp.root;
23659    extension = this.tmp.extension;
23660    strip_prepositions = CSL.Util.Sort.strip_prepositions;
23661    this.tmp.area = key_type;
23662    // Gawdawful, this.
23663    this.tmp.root = key_type.indexOf("_") > -1 ? key_type.slice(0,-5) : key_type;
23664    this.tmp.extension = "_sort";
23665    this.tmp.disambig_override = true;
23666    this.tmp.disambig_request = false;
23667    this.tmp.suppress_decorations = true;
23668    CSL.getCite.call(this, Item);
23669    this.tmp.suppress_decorations = false;
23670    this.tmp.disambig_override = false;
23671    len = this[key_type].keys.length;
23672    for (pos = 0; pos < len; pos += 1) {
23673        this[key_type].keys[pos] = strip_prepositions(this[key_type].keys[pos]);
23674    }
23675    //SNIP-START
23676    if (false) {
23677        CSL.debug("sort keys (" + key_type + "): " + this[key_type].keys);
23678    }
23679    //SNIP-END
23680    
23681    this.tmp.area = area;
23682    this.tmp.root = root;
23683    this.tmp.extension = extension;
23684    return this[key_type].keys;
23685};
23686
23687
23688/*global CSL: true */
23689
23690CSL.Registry.NameReg = function (state) {
23691    var pkey, ikey, skey, dagopt, gdropt, items, strip_periods, set_keys, evalname, delitems, addname, myitems;
23692    this.state = state;
23693    this.namereg = {};
23694    this.nameind = {};
23695    // used for restoring state following preview
23696    this.nameindpkeys = {};
23697    //
23698    // family, initials form, fullname (with given stripped of periods)
23699    //
23700    // keys registered, indexed by ID
23701    this.itemkeyreg = {};
23702
23703    strip_periods = function (str) {
23704        if (!str) {
23705            str = "";
23706        }
23707        return str.replace(/\./g, " ").replace(/\s+/g, " ").replace(/\s+$/,"");
23708    };
23709
23710    set_keys = function (state, itemid, nameobj) {
23711        pkey = strip_periods(nameobj.family);
23712
23713        if (state.opt["demote-non-dropping-particle"] === "never" && nameobj["non-dropping-particle"] && nameobj["family"]) {
23714            pkey = `${pkey} ${nameobj["non-dropping-particle"]}`;
23715        }
23716        
23717        skey = strip_periods(nameobj.given);
23718        // Drop lowercase suffixes (such as et al.) from given name field
23719        // for disambiguation purposes.
23720        var m = skey.match(/[,\!]* ([^,]+)$/);
23721        if (m && m[1] === m[1].toLowerCase()) {
23722            skey = skey.replace(/[,\!]* [^,]+$/, "");
23723        }
23724        // The %s terminator enables normal initialization behavior
23725        // with non-Byzantine names.
23726        ikey = CSL.Util.Names.initializeWith(state, skey, "%s");
23727        if (state.citation.opt["givenname-disambiguation-rule"] === "by-cite") {
23728            pkey = "" + itemid + pkey;
23729        }
23730    };
23731
23732    evalname = function (item_id, nameobj, namenum, request_base, form, initials) {
23733        var param;
23734        // XXX THIS CAN NO LONGER HAPPEN
23735        if (state.tmp.area.slice(0, 12) === "bibliography" && !form) {
23736            if ("string" === typeof initials) {
23737                return 1;
23738            } else {
23739                return 2;
23740            }
23741        }
23742        var res = state.nameOutput.getName(nameobj, "locale-translit", true);
23743        nameobj = res.name;
23744        set_keys(this.state, "" + item_id, nameobj);
23745        //
23746        // possible options are:
23747        //
23748        // <option disambiguate-add-givenname value="true"/> (a)
23749        // <option disambiguate-add-givenname value="all-names"/> (a)
23750        // <option disambiguate-add-givenname value="all-names-with-initials"/> (b)
23751        // <option disambiguate-add-givenname value="primary-name"/> (d)
23752        // <option disambiguate-add-givenname value="primary-name-with-initials"/> (e)
23753        // <option disambiguate-add-givenname value="by-cite"/> (g)
23754        //
23755        param = 2;
23756        dagopt = state.opt["disambiguate-add-givenname"];
23757        gdropt = state.citation.opt["givenname-disambiguation-rule"];
23758        var gdropt_orig = gdropt;
23759        if (gdropt === "by-cite") {
23760            gdropt = "all-names";
23761        }
23762        //
23763        // set initial value
23764        //
23765        if ("short" === form) {
23766            param = 0;
23767        } else if ("string" === typeof initials) {
23768            param = 1;
23769        }
23770        //
23771        // give literals a pass
23772        if ("undefined" === typeof this.namereg[pkey] || "undefined" === typeof this.namereg[pkey].ikey[ikey]) {
23773            return param;
23774        }
23775        //
23776        // adjust value upward if appropriate -- only if running
23777        // a non-names-global disambiguation strategy
23778        //
23779        if (gdropt_orig === "by-cite" && param <= request_base) {
23780            //param = request_base;
23781            return request_base;
23782        }
23783        if (!dagopt) {
23784            return param;
23785        }
23786        if ("string" === typeof gdropt && gdropt.slice(0, 12) === "primary-name" && namenum > 0) {
23787            return param;
23788        }
23789        //
23790        // the last composite condition is for backward compatibility
23791        //
23792        if (!gdropt || gdropt === "all-names" || gdropt === "primary-name") {
23793            if (this.namereg[pkey].count > 1) {
23794                param = 1;
23795            }
23796            if ((this.namereg[pkey].ikey 
23797                 && this.namereg[pkey].ikey[ikey].count > 1)
23798                || (this.namereg[pkey].count > 1 
23799                    && "string" !== typeof initials)) {
23800
23801                param = 2;
23802            }
23803        } else if (gdropt === "all-names-with-initials" || gdropt === "primary-name-with-initials") {
23804            if (this.namereg[pkey].count > 1) {
23805                param = 1;
23806            } else {
23807                param = 0;
23808            }
23809        }
23810        if (!state.registry.registry[item_id]) {
23811            if (form == "short") {
23812                return 0;
23813            } else if ("string" == typeof initials) {
23814                return 1;
23815            }
23816        } else {
23817            return param;
23818        }
23819    };
23820
23821    //
23822    // The operation of this function does not show up in the
23823    // standard test suite, but it has been hand-tested with
23824    // a print trace, and seems to work okay.
23825    //
23826    delitems = function (ids) {
23827        var pos, len, posB, id, fullkey;
23828        if ("string" === typeof ids || "number" === typeof ids) {
23829            ids = ["" + ids];
23830        }
23831        // ret carries the IDs of other items using this name.
23832        var ret = {};
23833        len = ids.length;
23834        for (pos = 0; pos < len; pos += 1) {
23835            id = "" + ids[pos];
23836            if (!this.nameind[id]) {
23837                continue;
23838            }
23839            for (fullkey in this.nameind[id]) {
23840                if (this.nameind[id].hasOwnProperty(fullkey)) {
23841                    var key = fullkey.split("::");
23842                    pkey = key[0];
23843                    ikey = key[1];
23844                    skey = key[2];
23845                    // Skip names that have been deleted already.
23846                    // Needed to clear integration DisambiguateAddGivenname1.txt
23847                    // and integration DisambiguateAddGivenname2.txt
23848                    if ("undefined" === typeof this.namereg[pkey]) {
23849                        continue;
23850                    }
23851
23852                    // ????
23853                    //posA = this.namereg[pkey].items.indexOf(posA);
23854
23855                    items = this.namereg[pkey].items;
23856                    // This was really, really unperceptive. They key elements
23857                    // have absolutely nothing to do with whether there was ever
23858                    // a registration at each key level.
23859                    if (skey && this.namereg[pkey].ikey[ikey] && this.namereg[pkey].ikey[ikey].skey[skey]) {
23860                        myitems = this.namereg[pkey].ikey[ikey].skey[skey].items;
23861                        posB = myitems.indexOf("" + id);
23862                        if (posB > -1) {
23863                            this.namereg[pkey].ikey[ikey].skey[skey].items = myitems.slice(0, posB).concat(myitems.slice([(posB + 1)]));
23864                        }
23865                        if (this.namereg[pkey].ikey[ikey].skey[skey].items.length === 0) {
23866                            delete this.namereg[pkey].ikey[ikey].skey[skey];
23867                            this.namereg[pkey].ikey[ikey].count += -1;
23868                            if (this.namereg[pkey].ikey[ikey].count < 2) {
23869                                for (var i = 0, ilen = this.namereg[pkey].ikey[ikey].items.length; i < ilen; i += 1) {
23870                                    state.tmp.taintedItemIDs[this.namereg[pkey].ikey[ikey].items[i]] = true;
23871                                }
23872                            }
23873                        }
23874                    }
23875                    if (ikey && this.namereg[pkey].ikey[ikey]) {
23876                        posB = this.namereg[pkey].ikey[ikey].items.indexOf("" + id);
23877                        if (posB > -1) {
23878                            items = this.namereg[pkey].ikey[ikey].items.slice();
23879                            this.namereg[pkey].ikey[ikey].items = items.slice(0, posB).concat(items.slice([posB + 1]));
23880                        }
23881                        if (this.namereg[pkey].ikey[ikey].items.length === 0) {
23882                            delete this.namereg[pkey].ikey[ikey];
23883                            this.namereg[pkey].count += -1;
23884                            if (this.namereg[pkey].count < 2) {
23885                                for (var i = 0, ilen = this.namereg[pkey].items.length; i < ilen; i += 1) {
23886                                    state.tmp.taintedItemIDs[this.namereg[pkey].items[i]] = true;
23887                                }
23888                            }
23889                        }
23890                    }
23891                    if (pkey) {
23892                        posB = this.namereg[pkey].items.indexOf("" + id);
23893                        if (posB > -1) {
23894                            items = this.namereg[pkey].items.slice();
23895                            this.namereg[pkey].items = items.slice(0, posB).concat(items.slice([posB + 1], items.length));
23896                        }
23897                        if (this.namereg[pkey].items.length < 2) {
23898                            delete this.namereg[pkey];
23899                        }
23900                    }
23901                    delete this.nameind[id][fullkey];
23902                }
23903            }
23904            delete this.nameind[id];
23905            delete this.nameindpkeys[id];
23906        }
23907        return ret;
23908    };
23909    //
23910    // Run ALL
23911    // renderings with disambiguate-add-givenname set to a value
23912    // with the by-cite behaviour, and then set the names-based
23913    // expanded form when the final makeCitationCluster rendering
23914    // is output.  This could be done with a single var set on
23915    // the state object in the execution wrappers that run the
23916    // style.
23917    //
23918    addname = function (item_id, nameobj, pos) {
23919        var i, ilen;
23920        var res = state.nameOutput.getName(nameobj, "locale-translit", true);
23921        nameobj = res.name;
23922
23923        if (state.citation.opt["givenname-disambiguation-rule"]
23924            && state.citation.opt["givenname-disambiguation-rule"].slice(0, 8) === "primary-"
23925            && pos !== 0) {
23926                return;
23927        }
23928
23929        // A hack. Safe if the name object is used only here, for disambiguation purposes.
23930        /*
23931        */
23932        
23933        //CSL.debug("INS");
23934        set_keys(this.state, "" + item_id, nameobj);
23935        // pkey, ikey and skey should be stored in separate cascading objects.
23936        // there should also be a kkey, on each, which holds the item ids using
23937        // that form of the name.
23938        //
23939        // (later note: well, we seem to have slipped a notch here.
23940        // Adding lists of IDs all over the place here makes no sense;
23941        // the lists need to include _only_ the items currently rendered
23942        // at the given level, and the place to do that is in evalname,
23943        // and in delnames, not here.)
23944        if (pkey) {
23945            if ("undefined" === typeof this.namereg[pkey]) {
23946                this.namereg[pkey] = {};
23947                this.namereg[pkey].count = 0;
23948                this.namereg[pkey].ikey = {};
23949                this.namereg[pkey].items = [item_id];
23950            } else if (this.namereg[pkey].items.indexOf(item_id) === -1) {
23951                this.namereg[pkey].items.push(item_id);
23952            }
23953//            if (this.namereg[pkey].items.indexOf(item_id) === -1) {
23954//                this.namereg[pkey].items.push(item_id);
23955//            }
23956        }
23957        if (pkey && ikey) {
23958            if ("undefined" === typeof this.namereg[pkey].ikey[ikey]) {
23959                this.namereg[pkey].ikey[ikey] = {};
23960                this.namereg[pkey].ikey[ikey].count = 0;
23961                this.namereg[pkey].ikey[ikey].skey = {};
23962                this.namereg[pkey].ikey[ikey].items = [item_id];
23963                this.namereg[pkey].count += 1;
23964                if (this.namereg[pkey].count === 2) {
23965                    for (var i = 0, ilen = this.namereg[pkey].items.length; i < ilen; i += 1) {
23966                        state.tmp.taintedItemIDs[this.namereg[pkey].items[i]] = true;
23967                    }
23968                }
23969            } else if (this.namereg[pkey].ikey[ikey].items.indexOf(item_id) === -1) {
23970                this.namereg[pkey].ikey[ikey].items.push(item_id);
23971            }
23972//            if (this.namereg[pkey].ikey[ikey].items.indexOf(item_id) === -1) {
23973//                this.namereg[pkey].ikey[ikey].items.push(item_id);
23974//            }
23975        }
23976        if (pkey && ikey && skey) {
23977            if ("undefined" === typeof this.namereg[pkey].ikey[ikey].skey[skey]) {
23978                this.namereg[pkey].ikey[ikey].skey[skey] = {};
23979                this.namereg[pkey].ikey[ikey].skey[skey].items = [item_id];
23980                this.namereg[pkey].ikey[ikey].count += 1;
23981                if (this.namereg[pkey].ikey[ikey].count === 2) {
23982                    for (var i = 0, ilen = this.namereg[pkey].ikey[ikey].items.length; i < ilen; i += 1) {
23983                        state.tmp.taintedItemIDs[this.namereg[pkey].ikey[ikey].items[i]] = true;
23984                    }
23985                }
23986            } else if (this.namereg[pkey].ikey[ikey].skey[skey].items.indexOf(item_id) === -1) {
23987                this.namereg[pkey].ikey[ikey].skey[skey].items.push(item_id);
23988            }
23989//            if (this.namereg[pkey].ikey[ikey].skey[skey].items.indexOf(item_id) === -1) {
23990//                this.namereg[pkey].ikey[ikey].skey[skey].items.push(item_id);
23991//            }
23992        }
23993        if ("undefined" === typeof this.nameind[item_id]) {
23994            this.nameind[item_id] = {};
23995            this.nameindpkeys[item_id] = {};
23996        }
23997        //CSL.debug("INS-A: [" + pkey + "] [" + ikey + "] [" + skey + "]");
23998        if (pkey) {
23999            this.nameind[item_id][pkey + "::" + ikey + "::" + skey] = true;
24000            this.nameindpkeys[item_id][pkey] = this.namereg[pkey];
24001        }
24002        //CSL.debug("INS-B");
24003    };
24004    this.addname = addname;
24005    this.delitems = delitems;
24006    this.evalname = evalname;
24007};
24008
24009/*global CSL: true */
24010
24011CSL.Registry.CitationReg = function () {
24012    this.citationById = {};
24013    this.citationByIndex = [];
24014};
24015
24016/*global CSL: true */
24017
24018CSL.Disambiguation = function (state) {
24019    this.state = state;
24020    this.sys = this.state.sys;
24021    this.registry = state.registry.registry;
24022    this.ambigcites = state.registry.ambigcites;
24023    this.configModes();
24024    this.debug = false;
24025};
24026
24027CSL.Disambiguation.prototype.run = function(akey) {
24028    if (!this.modes.length) {
24029        return;
24030    }
24031    //SNIP-START
24032    if (this.debug) {
24033        this.state.sys.print("[A] === RUN ===");
24034    }
24035    //SNIP-END
24036    this.akey = akey;
24037    if (this.initVars(akey)) {
24038        this.runDisambig();
24039    }
24040
24041};
24042
24043CSL.Disambiguation.prototype.runDisambig = function () {
24044    var ismax;
24045    //SNIP-START
24046    if (this.debug) {
24047        this.state.sys.print("[C] === runDisambig() ===");
24048    }
24049    //SNIP-END
24050    this.initGivens = true;
24051    //
24052    // Length of list may change during processing
24053    while (this.lists.length) {
24054        this.gnameset = 0;
24055        this.gname = 0;
24056        this.clashes = [1, 0];
24057        // each list is scanned repeatedly until all
24058        // items either succeed or ultimately fail.
24059        while(this.lists[0][1].length) {
24060            this.listpos = 0;
24061            if (!this.base) {
24062                this.base = this.lists[0][0];
24063            }
24064            ismax = this.incrementDisambig();
24065            this.scanItems(this.lists[0]);
24066            this.evalScan(ismax);
24067        }
24068        this.lists = this.lists.slice(1);
24069    }
24070};
24071
24072CSL.Disambiguation.prototype.scanItems = function (list) {
24073    var pos, len, otherItem;
24074    //SNIP-START
24075    if (this.debug) {
24076        this.state.sys.print("[2] === scanItems() ===");
24077    }
24078    //SNIP-END
24079
24080    this.Item = list[1][0];
24081    this.ItemCite = CSL.getAmbiguousCite.call(this.state, this.Item, this.base, true);
24082
24083    this.scanlist = list[1];
24084    this.partners = [];
24085    this.partners.push(this.Item);
24086    this.nonpartners = [];
24087    var clashes = 0;
24088
24089    for (var pos = 1, len = list[1].length; pos < len; pos += 1) {
24090        otherItem = list[1][pos];
24091        var otherItemCite = CSL.getAmbiguousCite.call(this.state, otherItem, this.base, true);
24092        //SNIP-START
24093        if (this.debug) {
24094            if (pos > 1) {
24095                this.state.sys.print("  -----------");
24096            }
24097        }
24098        //SNIP-END
24099        if (this.ItemCite === otherItemCite) {
24100            //SNIP-START
24101            if (this.debug) {
24102                this.state.sys.print("  [CLASH]--> "+this.Item.id+": "+this.ItemCite);
24103                this.state.sys.print("             "+otherItem.id+": "+otherItemCite);
24104            }
24105            //SNIP-END
24106            clashes += 1;
24107            this.partners.push(otherItem);
24108        } else {
24109            //SNIP-START
24110            if (this.debug) {
24111                this.state.sys.print("  [clear]--> "+this.Item.id+": "+this.ItemCite);
24112                this.state.sys.print("             "+otherItem.id+": "+otherItemCite);
24113            }
24114            //SNIP-END
24115            this.nonpartners.push(otherItem);
24116        }
24117    }
24118    this.clashes[0] = this.clashes[1];
24119    this.clashes[1] = clashes;
24120};
24121
24122CSL.Disambiguation.prototype.evalScan = function (maxed) {
24123    this[this.modes[this.modeindex]](maxed);
24124    if (maxed) {
24125        if (this.modeindex < this.modes.length - 1) {
24126            this.modeindex += 1;
24127        } else {
24128            this.lists[this.listpos + 1] = [this.base, []];
24129        }
24130    }
24131};
24132
24133CSL.Disambiguation.prototype.disNames = function (ismax) {
24134    var i, ilen;
24135    
24136    //SNIP-START
24137    if (this.debug) {
24138        this.state.sys.print("[3] == disNames() ==");
24139        //this.state.sys.print("       partners: "+[this.partners[i].id for (i in this.partners)].join(", "));
24140        //this.state.sys.print("    nonpartners: "+[this.nonpartners[i].id for (i in this.nonpartners)].join(", "));
24141    }
24142    //SNIP-END
24143
24144    // New design
24145    // this.base is a forward-only counter. Values are never
24146    // reduced, and the counter object is never overwritten.
24147    // It is methodically pushed forward in single-unit increments
24148    // in incrementDisambig() until disNames() wipes out the list.
24149
24150    // this.betterbase is cloned from this.base exactly once,
24151    // at the start of a disambiguation run. Whenever an operation
24152    // results in improvement, the just-incremented elements
24153    // identified as this.base.names[this.gnameset] (number of
24154    // names)and as this.base.givens[this.gnameset][this.gname]
24155    // (level of given name) are copied from this.base.
24156
24157    // The this.base object is used to control disambiguation
24158    // renderings. These will be more fully expanded than the final
24159    // text, but the flip side of the fact that the extra data does
24160    // not contribute anything to disambiguation is that leaving
24161    // it in does no harm -- think of it as the Cold Dark Matter of
24162    // disambiguation.
24163
24164    if (this.clashes[1] === 0 && this.nonpartners.length === 1) {
24165        this.captureStepToBase();
24166        //SNIP-START
24167        if (this.debug) {
24168            this.state.sys.print("  ** RESOLUTION [a]: lone partner, one nonpartner");
24169            this.state.sys.print("  registering "+this.partners[0].id+" and "+this.nonpartners[0].id);
24170        }
24171        //SNIP-END
24172        this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.betterbase);
24173        this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, this.betterbase);
24174        this.lists[this.listpos] = [this.betterbase, []];
24175    } else if (this.clashes[1] === 0) {
24176        this.captureStepToBase();
24177        //SNIP-START
24178        if (this.debug) {
24179            this.state.sys.print("  ** RESOLUTION [b]: lone partner, unknown number of remaining nonpartners");
24180            this.state.sys.print("  registering "+this.partners[0].id);
24181        }
24182        //SNIP-END
24183        this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, this.betterbase);
24184        this.lists[this.listpos] = [this.betterbase, this.nonpartners];
24185        if (this.nonpartners.length) {
24186            this.initGivens = true;
24187        }
24188    } else if (this.nonpartners.length === 1) {
24189        this.captureStepToBase();
24190        //SNIP-START
24191        if (this.debug) {
24192            this.state.sys.print("  ** RESOLUTION [c]: lone nonpartner, unknown number of partners remaining");
24193            this.state.sys.print("  registering "+this.nonpartners[0].id);
24194        }
24195        //SNIP-END
24196        this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.betterbase);
24197        //this.lists[this.listpos] = [this.betterbase, this.partners];
24198        this.lists[this.listpos] = [this.betterbase, this.partners];
24199    } else if (this.clashes[1] < this.clashes[0]) {
24200        this.captureStepToBase();
24201        //SNIP-START
24202        if (this.debug) {
24203            this.state.sys.print("  ** RESOLUTION [d]: better result, but no entries safe to register");
24204        }
24205        //SNIP-END
24206        this.lists[this.listpos] = [this.betterbase, this.partners];
24207        this.lists.push([this.betterbase, this.nonpartners]);
24208    } else {
24209        //SNIP-START
24210        if (this.debug) {
24211            this.state.sys.print("  ** RESOLUTION [e]: no improvement, and clashes remain");
24212        }
24213        //SNIP-END
24214        if (ismax) {
24215            this.lists[this.listpos] = [this.betterbase, this.nonpartners];
24216            this.lists.push([this.betterbase, this.partners]);
24217            if (this.modeindex === this.modes.length - 1) {
24218                //SNIP-START
24219                if (this.debug) {
24220                    this.state.sys.print("     (registering clashing entries because we've run out of options)");
24221                }
24222                //SNIP-END
24223                for (var i = 0, ilen = this.partners.length; i < ilen; i += 1) {
24224                    this.state.registry.registerAmbigToken(this.akey, "" + this.partners[i].id, this.betterbase);
24225                }
24226                this.lists[this.listpos] = [this.betterbase, []];
24227            }
24228        }
24229    }
24230};
24231
24232CSL.Disambiguation.prototype.disExtraText = function () {
24233    //SNIP-START
24234    if (this.debug) {
24235        this.state.sys.print("[3] === disExtraText ==");
24236    }
24237    //SNIP-END
24238    
24239    var done = false;
24240
24241    if (this.clashes[1] === 0 && this.nonpartners.length < 2) {
24242        done = true;
24243    }
24244
24245    // If first encounter in this cycle and multiple modes are
24246    // available, decrement mode and reset base
24247    if (!done && (!this.base.disambiguate || this.state.tmp.disambiguate_count !== this.state.tmp.disambiguate_maxMax)) {
24248        // Rerun everything on each subcycle? This doesn't work currently.
24249        //this.initVars(this.akey)
24250        this.modeindex = 0;
24251        this.base.disambiguate = this.state.tmp.disambiguate_count;
24252        this.betterbase.disambiguate = this.state.tmp.disambiguate_count;
24253        if (!this.base.disambiguate) {
24254            // Evaluate here?
24255            this.initGivens = true;
24256            // If disambiguate is false set to true
24257            this.base.disambiguate = 1;
24258            // There may be changes
24259            for (var i = 0, ilen = this.lists[this.listpos][1].length; i < ilen; i += 1) {
24260                this.state.tmp.taintedItemIDs[this.lists[this.listpos][1][i].id] = true;
24261            }
24262        } else {
24263            this.disNames();
24264        }
24265    } else if (done || this.state.tmp.disambiguate_count === this.state.tmp.disambiguate_maxMax) {
24266        if (done || this.modeindex === this.modes.length - 1) {
24267            // If this is the end, disambiguation failed.
24268            // Discard disambiguate=true (?) and set parameters
24269            var base = this.lists[this.listpos][0];
24270            for (var i = 0, ilen = this.lists[this.listpos][1].length; i < ilen; i += 1) {
24271                this.state.tmp.taintedItemIDs[this.lists[this.listpos][1][i].id] = true;
24272                this.state.registry.registerAmbigToken(this.akey, "" + this.lists[this.listpos][1][i].id, base);
24273            }
24274            this.lists[this.listpos] = [this.betterbase, []];
24275        } else {
24276            // If this is followed by year-suffix, keep
24277            // parameters and set disambiguate=true since it MIGHT
24278            // include the date, needed for year-suffix.
24279            // This may be a bit over-aggressive for cases in which the
24280            // disambiguate condition does not add the date
24281            this.modeindex = this.modes.length - 1;
24282            var base = this.lists[this.listpos][0];
24283            base.disambiguate = true;
24284            for (var i = 0, ilen = this.lists[this.listpos][1].length; i < ilen; i += 1) {
24285                // Always tainting here might be a little over-aggressive, but a taint may be required.
24286                this.state.tmp.taintedItemIDs[this.lists[this.listpos][1][i].id] = true;
24287                this.state.registry.registerAmbigToken(this.akey, "" + this.lists[this.listpos][1][i].id, base);
24288            }
24289        }
24290    }
24291};
24292
24293CSL.Disambiguation.prototype.disYears = function () {
24294    var pos, len, tokens, token;
24295    //SNIP-START
24296    if (this.debug) {
24297        this.state.sys.print("[3] === disYears ==");
24298    }
24299    //SNIP-END
24300    tokens = [];
24301    var base = this.lists[this.listpos][0];
24302    if (this.clashes[1]) {
24303        // That is, if the initial increment on the ambigs group returns no
24304        // clashes, don't apply suffix. The condition is a necessary failsafe.
24305		// In original submission order
24306		for (var i = 0, ilen = this.state.registry.mylist.length; i < ilen; i += 1) {
24307			var origid = this.state.registry.mylist[i];
24308			for (var j = 0, jlen = this.lists[this.listpos][1].length; j < jlen; j += 1) {
24309				var token = this.lists[this.listpos][1][j];
24310				// Warning: token.id can be number. This should be fixed at a higher level in citeproc-js if poss.
24311				if (token.id == origid) {
24312					tokens.push(this.registry[token.id]);
24313					break;
24314				}
24315			}
24316		}
24317    }
24318    tokens.sort(this.state.registry.sorter.compareKeys);
24319    for (var pos = 0, len = tokens.length; pos < len; pos += 1) {
24320        base.year_suffix = ""+pos;
24321        var oldBase = this.state.registry.registry[tokens[pos].id].disambig;
24322        this.state.registry.registerAmbigToken(this.akey, "" + tokens[pos].id, base);
24323        if (CSL.ambigConfigDiff(oldBase,base)) {
24324            this.state.tmp.taintedItemIDs[tokens[pos].id] = true;
24325        }
24326    }
24327    this.lists[this.listpos] = [this.betterbase, []];
24328};
24329
24330CSL.Disambiguation.prototype.incrementDisambig = function () {
24331    //SNIP-START
24332    if (this.debug) {
24333        this.state.sys.print("\n[1] === incrementDisambig() ===");
24334    }
24335    //SNIP-END
24336    if (this.initGivens) {
24337        this.initGivens = false;
24338        return false;
24339    }
24340    var maxed = false;
24341    var increment_names = true;
24342    if ("disNames" === this.modes[this.modeindex]) {
24343        // this.gnameset: the index pos of the current nameset
24344        // this.gname: the index pos of the current name w/in the current nameset
24345        
24346        // Stages:
24347        // - Increment givenname (optional)
24348        // - Add a name (optional)
24349        // - Move to next nameset
24350
24351        // Incrementing is done forward-only on this.base. Values
24352        // that improve disambiguation results are copied to
24353        // this.betterbase, which is used to set the disambig parameters
24354        // in the processor registry.
24355        
24356
24357        // Increment
24358        // Max val is always true if a level is inactive.
24359        increment_names = false;
24360        if ("number" !== typeof this.givensMax) {
24361            increment_names = true;
24362        }
24363        var increment_namesets = false;
24364        if ("number" !== typeof this.namesMax) {
24365            increment_namesets = true;
24366        }
24367        if ("number" === typeof this.givensMax) {
24368            if (this.base.givens.length && this.base.givens[this.gnameset][this.gname] < this.givensMax) {
24369                this.base.givens[this.gnameset][this.gname] += 1;
24370            } else {
24371                increment_names = true;
24372            }
24373        }
24374        if ("number" === typeof this.namesMax 
24375            && increment_names) {
24376            if (this.state.opt["disambiguate-add-names"]) {
24377                increment_namesets = false;
24378                if (this.gname < this.namesMax) {
24379                    this.base.names[this.gnameset] += 1;
24380                    this.gname += 1;
24381                } else {
24382                    increment_namesets = true;
24383                }
24384            } else {
24385                increment_namesets = true;
24386            }
24387        }
24388        if ("number" === typeof this.namesetsMax && increment_namesets) {
24389            if (this.gnameset < this.namesetsMax) {
24390                this.gnameset += 1;
24391                this.base.names[this.gnameset] = 1;
24392                this.gname = 0;
24393            }
24394        }
24395        //SNIP-START
24396        if (this.debug) {
24397            this.state.sys.print("    ------------------");
24398            this.state.sys.print("    incremented values");
24399            this.state.sys.print("    ------------------");
24400            this.state.sys.print("    | gnameset: "+this.gnameset);
24401            this.state.sys.print("    | gname: "+this.gname);
24402            this.state.sys.print("    | names value: "+this.base.names[this.gnameset]);
24403            if (this.base.givens.length) {
24404                this.state.sys.print("    | givens value: "+this.base.givens[this.gnameset][this.gname]);
24405            } else {
24406                this.state.sys.print("    | givens value: nil");
24407            }
24408            this.state.sys.print("    | namesetsMax: "+this.namesetsMax);
24409            this.state.sys.print("    | namesMax: "+this.namesMax);
24410            this.state.sys.print("    | givensMax: "+this.givensMax);
24411        }
24412        //SNIP-END
24413        if (("number" !== typeof this.namesetsMax || this.namesetsMax === -1 || this.gnameset === this.namesetsMax)
24414            && (!this.state.opt["disambiguate-add-names"] || "number" !== typeof this.namesMax || this.gname === this.namesMax)
24415            && ("number" != typeof this.givensMax || "undefined" === typeof this.base.givens[this.gnameset] || "undefined" === typeof this.base.givens[this.gnameset][this.gname] || this.base.givens[this.gnameset][this.gname] === this.givensMax)) {
24416  
24417
24418            maxed = true;
24419            //SNIP-START
24420            if (this.debug) {
24421                this.state.sys.print("    MAXED");
24422            }
24423            //SNIP-END
24424        }
24425    } else if ("disExtraText" === this.modes[this.modeindex]) {
24426        this.base.disambiguate += 1;
24427        this.betterbase.disambiguate += 1;
24428    }
24429    return maxed;
24430};
24431
24432CSL.Disambiguation.prototype.initVars = function (akey) {
24433    var i, ilen, myIds, myItemBundles, myItems;
24434    //SNIP-START
24435    if (this.debug) {
24436        this.state.sys.print("[B] === initVars() ===");
24437    }
24438    //SNIP-END
24439    this.lists = [];
24440    this.base = false;
24441    this.betterbase = false;
24442    this.akey = akey;
24443
24444    this.maxNamesByItemId = {};
24445
24446
24447    myItemBundles = [];
24448    myIds = this.ambigcites[akey];
24449    if (!myIds || !myIds.length) {
24450        return false;
24451    }
24452    var myItem = this.state.refetchItem("" + myIds[0]);
24453    this.getCiteData(myItem);
24454    this.base = CSL.getAmbigConfig.call(this.state);
24455    if (myIds && myIds.length > 1) {
24456        myItemBundles.push([this.maxNamesByItemId[myItem.id], myItem]);
24457        // Build a composite list of Items and associated
24458        // max names. This is messy, but it's the only
24459        // way to get the items sorted by the number of names
24460        // to be disambiguated. If they are in descending order
24461        // with name expansions, the processor will hang.
24462        for (var i = 1, ilen = myIds.length; i < ilen; i += 1) {
24463            myItem = this.state.refetchItem("" + myIds[i]);
24464            this.getCiteData(myItem, this.base);
24465            myItemBundles.push([this.maxNamesByItemId[myItem.id], myItem]);
24466        }
24467        myItemBundles.sort(
24468            function (a, b) {
24469                if (a[0] > b[0]) {
24470                    return 1;
24471                } else if (a[0] < b[0]) {
24472                    return -1;
24473                } else {
24474                    if (a[1].id > b[1].id) {
24475                        return 1;
24476                    } else if (a[1].id < b[1].id) {
24477                        return -1;
24478                    } else {
24479                        return 0;
24480                    }
24481                }
24482            }
24483        );
24484        myItems = [];
24485        for (var i = 0, ilen = myItemBundles.length; i < ilen; i += 1) {
24486            myItems.push(myItemBundles[i][1]);
24487        }
24488        this.lists.push([this.base, myItems]);
24489        this.Item = this.lists[0][1][0];
24490    } else {
24491        this.Item = this.state.refetchItem("" + myIds[0]);
24492    }
24493
24494    this.modeindex = 0;
24495    if (this.state.citation.opt["disambiguate-add-names"] || true) {
24496        this.namesMax = this.maxNamesByItemId[this.Item.id][0];
24497    } else {
24498        var namesMax = this.base.names[0];
24499        for (var i=1,ilen=this.base.names.length;i<ilen;i+=1){
24500            namesMax = Math.max(namesMax,this.base.names.names[i]);
24501        }
24502    }
24503
24504
24505    this.padBase(this.base);
24506    this.padBase(this.betterbase);
24507    this.base.year_suffix = false;
24508    this.base.disambiguate = false;
24509    this.betterbase.year_suffix = false;
24510    this.betterbase.disambiguate = false;
24511    if (this.state.citation.opt["givenname-disambiguation-rule"] === "by-cite"
24512       && this.state.opt["disambiguate-add-givenname"]) {
24513        this.givensMax = 2;
24514    }
24515    return true;
24516};
24517
24518
24519CSL.Disambiguation.prototype.padBase = function (base) {
24520    for (var i = 0, ilen = base.names.length; i < ilen; i += 1) {
24521        if (!base.givens[i]) {
24522            base.givens[i] = [];
24523        }
24524        for (var j=0,jlen=base.names[i];j<jlen;j+=1) {
24525            if (!base.givens[i][j]) {
24526                base.givens[i][j] = 0;
24527            }
24528        }
24529    }
24530};
24531
24532/**
24533 * Set available modes for disambiguation
24534 */
24535CSL.Disambiguation.prototype.configModes = function () {
24536    var dagopt, gdropt;
24537    // Modes are function names prototyped to this instance.
24538    this.modes = [];
24539    dagopt = this.state.opt["disambiguate-add-givenname"];
24540    gdropt = this.state.citation.opt["givenname-disambiguation-rule"];
24541    if (this.state.opt['disambiguate-add-names'] || (dagopt && gdropt === "by-cite")) {
24542        this.modes.push("disNames");
24543    }
24544
24545    if (this.state.opt.development_extensions.prioritize_disambiguate_condition) {
24546        if (this.state.opt.has_disambiguate) {
24547            this.modes.push("disExtraText");
24548        }
24549        if (this.state.opt["disambiguate-add-year-suffix"]) {
24550            this.modes.push("disYears");
24551        }
24552    } else {
24553        if (this.state.opt["disambiguate-add-year-suffix"]) {
24554            this.modes.push("disYears");
24555        }
24556        if (this.state.opt.has_disambiguate) {
24557            this.modes.push("disExtraText");
24558        }
24559    }
24560};
24561
24562CSL.Disambiguation.prototype.getCiteData = function(Item, base) {
24563    // Initialize base if first set item seen
24564    if (!this.maxNamesByItemId[Item.id]) {
24565        CSL.getAmbiguousCite.call(this.state, Item, base);
24566        base = CSL.getAmbigConfig.call(this.state);
24567        this.maxNamesByItemId[Item.id] = CSL.getMaxVals.call(this.state);
24568        this.state.registry.registry[Item.id].disambig.givens = this.state.tmp.disambig_settings.givens.slice();
24569        // Slice the nested lists as well. Without this, disambiguate_YearSuffixFiftyTwoEntriesByCite fails.
24570        for (var i=0,ilen=this.state.registry.registry[Item.id].disambig.givens.length;i<ilen;i+=1) {
24571            this.state.registry.registry[Item.id].disambig.givens[i] = this.state.tmp.disambig_settings.givens[i].slice();
24572        }
24573        this.namesetsMax = this.state.registry.registry[Item.id].disambig.names.length - 1;
24574        if (!this.base) {
24575            this.base = base;
24576            this.betterbase = CSL.cloneAmbigConfig(base);
24577        }
24578        if (base.names.length < this.base.names.length) {
24579            // I don't know what would happen with discrepancies in the number
24580            // of namesets rendered on items, so we use the fewer of the two
24581            // and limit the other to that size.
24582            this.base = base;
24583        }
24584        // Padding. Within namesets, we use the longer of the two throughout.
24585        for (var i = 0, ilen = base.names.length; i < ilen; i += 1) {
24586            if (base.names[i] > this.base.names[i]) {
24587                // XXX The old must have been wrong surely. The new, I'm not sure.
24588                //this.base.givens[i] = this.base.givens[i].concat(this.base.givens[i].slice(this.base.names[i]));
24589                this.base.givens[i] = base.givens[i].slice();
24590                this.base.names[i] = base.names[i];
24591                this.betterbase.names = this.base.names.slice();
24592                this.betterbase.givens = this.base.givens.slice();
24593                this.padBase(this.base);
24594                this.padBase(this.betterbase);
24595            }
24596        }
24597        // This shouldn't be necessary
24598        // getAmbiguousCite() should return a valid and complete
24599        // givens segment under all conditions, but it does not
24600        // do so for institution authors, so we clean up after it
24601        // here.
24602        // Relevant test: sort_ChicagoYearSuffix2
24603        this.betterbase.givens = this.base.givens.slice();
24604        for (var j = 0, jlen = this.base.givens.length; j < jlen; j += 1) {
24605            this.betterbase.givens[j] = this.base.givens[j].slice();
24606        }
24607    }
24608};
24609
24610CSL.Disambiguation.prototype.captureStepToBase = function() {
24611    // Be paranoid about the presence of givens
24612    if (this.state.citation.opt["givenname-disambiguation-rule"] === "by-cite"
24613        && this.base.givens && this.base.givens.length) {
24614        if ("undefined" !== typeof this.base.givens[this.gnameset][this.gname]) {
24615            if (this.betterbase.givens.length < this.base.givens.length) {
24616                this.betterbase.givens = JSON.parse(JSON.stringify(this.base.givens));
24617            }
24618            this.betterbase.givens[this.gnameset][this.gname] = this.base.givens[this.gnameset][this.gname];
24619        }
24620    }
24621    this.betterbase.names[this.gnameset] = this.base.names[this.gnameset];
24622};
24623
24624CSL.Engine.prototype.getJurisdictionList = function (jurisdiction) {
24625    var jurisdictionList = [];
24626    var jurisdictionElems = jurisdiction.split(":");
24627    for (var j=jurisdictionElems.length;j>0;j--) {
24628        var composedID = jurisdictionElems.slice(0,j).join(":");
24629        jurisdictionList.push(composedID);
24630        if (this.opt.jurisdiction_fallbacks[composedID]) {
24631            var fallback = this.opt.jurisdiction_fallbacks[composedID];
24632            jurisdictionList.push(fallback);
24633        }
24634    }
24635    if (jurisdictionList.indexOf("us") === -1) {
24636        jurisdictionList.push("us");
24637    }
24638    return jurisdictionList;
24639};
24640
24641CSL.Engine.prototype.loadStyleModule = function (jurisdiction, xmlSource, skipFallback) {
24642    var myFallback = null;
24643    var macroCount = 0;
24644    this.juris[jurisdiction] = {};
24645    var myXml = CSL.setupXml(xmlSource);
24646    myXml.addMissingNameNodes(myXml.dataObj);
24647    myXml.addInstitutionNodes(myXml.dataObj);
24648    myXml.insertPublisherAndPlace(myXml.dataObj);
24649    myXml.flagDateMacros(myXml.dataObj);
24650    var myNodes = myXml.getNodesByName(myXml.dataObj, "law-module");
24651    for (var i=0,ilen=myNodes.length;i<ilen;i++) {
24652        var myTypes = myXml.getAttributeValue(myNodes[i],"types");
24653        if (myTypes) {
24654            this.juris[jurisdiction].types = {};
24655            myTypes =  myTypes.split(/\s+/);
24656            for (var j=0,jlen=myTypes.length;j<jlen;j++) {
24657                this.juris[jurisdiction].types[myTypes[j]] = true;
24658            }
24659        }
24660        if (!skipFallback) {
24661            myFallback = myXml.getAttributeValue(myNodes[i],"fallback");
24662            if (myFallback) {
24663                if (jurisdiction !== "us") {
24664                    this.opt.jurisdiction_fallbacks[jurisdiction] = myFallback;
24665                }
24666            }
24667        }
24668    }
24669    var lang = this.opt.lang ? this.opt.lang : this.opt["default-locale"][0];
24670    CSL.SET_COURT_CLASSES(this, lang, myXml, myXml.dataObj);
24671    
24672    if (!this.juris[jurisdiction].types) {
24673        this.juris[jurisdiction].types = CSL.MODULE_TYPES;
24674    }
24675    var myNodes = myXml.getNodesByName(myXml.dataObj, "macro");
24676    for (var i=0,ilen=myNodes.length;i<ilen;i++) {
24677        var myName = myXml.getAttributeValue(myNodes[i], "name");
24678        if (!CSL.MODULE_MACROS[myName]) {
24679            CSL.debug("CSL: skipping non-modular macro name \"" + myName + "\" in module context");
24680            continue;
24681        }
24682        macroCount++;
24683        this.juris[jurisdiction][myName] = [];
24684        // Must use the same XML parser for style and modules.
24685        this.buildTokenLists(myNodes[i], this.juris[jurisdiction][myName]);
24686        this.configureTokenList(this.juris[jurisdiction][myName]);
24687    }
24688    //if (macroCount < Object.keys(CSL.MODULE_MACROS).length) {
24689    //    var missing = [];
24690    //    throw "CSL ERROR: Incomplete jurisdiction style module for: " + jurisdiction;
24691    //}
24692    return myFallback;
24693};
24694
24695CSL.Engine.prototype.retrieveAllStyleModules = function (jurisdictionList) {
24696    var ret = {};
24697    var preferences = this.locale[this.opt.lang].opts["jurisdiction-preference"];
24698    preferences = preferences ? preferences : [];
24699    preferences = [""].concat(preferences);
24700    for (var i=preferences.length-1;i>-1;i--) {
24701        var preference = preferences[i];
24702        for (var j=0,jlen=jurisdictionList.length;j<jlen;j++) {
24703            var jurisdiction = jurisdictionList[j];
24704            // If we've "seen" it, we have it already, or we're not going to get it.
24705            if (this.opt.jurisdictions_seen[jurisdiction]) {
24706                continue;
24707            }
24708            // Try to get the module
24709            var res = this.sys.retrieveStyleModule(jurisdiction, preference);
24710            // If we fail and we've run out of preferences, mark as "seen"
24711            // Otherwise mark as "seen" if we get something.
24712            if ((!res && !preference) || res) {
24713                this.opt.jurisdictions_seen[jurisdiction] = true;
24714            }
24715            // Don't memo unless get got style code.
24716            if (!res) {
24717                continue;
24718            }
24719            ret[jurisdiction] = res;
24720        }
24721    }
24722    // Give 'em what we got.
24723    return ret;
24724};
24725
24726CSL.ParticleList = (function() {
24727	var always_dropping_1 = [[[0,1], null]];
24728	var always_dropping_3 = [[[0,3], null]];
24729	var always_non_dropping_1 = [[null, [0,1]]];
24730	var always_non_dropping_2 = [[null, [0,2]]];
24731	var always_non_dropping_3 = [[null, [0,3]]];
24732	var either_1 = [[null, [0,1]],[[0,1],null]];
24733	var either_2 = [[null, [0,2]],[[0,2],null]];
24734	var either_1_dropping_best = [[[0,1],null],[null, [0,1]]];
24735	var either_2_dropping_best = [[[0,2],null],[null, [0,2]]];
24736	var either_3_dropping_best = [[[0,3],null],[null, [0,3]]];
24737	var non_dropping_2_alt_dropping_1_non_dropping_1 = [[null, [0,2]], [[0,1], [1,2]]];
24738	var PARTICLES = [
24739		["'s", always_non_dropping_1],
24740		["'s-", always_non_dropping_1],
24741		["'t", always_non_dropping_1],
24742		["a", 	always_non_dropping_1],
24743		["aan 't", always_non_dropping_2],
24744		["aan de", always_non_dropping_2],
24745		["aan den", always_non_dropping_2],
24746		["aan der", always_non_dropping_2],
24747		["aan het", always_non_dropping_2],
24748		["aan t", always_non_dropping_2],
24749		["aan", always_non_dropping_1],
24750		["ad-", either_1],
24751		["adh-", either_1],
24752		["af", either_1],
24753		["al", either_1],
24754		["al-", either_1],
24755		["am de", always_non_dropping_2],
24756		["am", always_non_dropping_1],
24757		["an-", either_1],
24758		["ar-", either_1],
24759		["as-", either_1],
24760		["ash-", either_1],
24761		["at-", either_1],
24762		["ath-", either_1],
24763		["auf dem", either_2_dropping_best],
24764		["auf den", either_2_dropping_best],
24765		["auf der", either_2_dropping_best],
24766		["auf ter", always_non_dropping_2],
24767		["auf", either_1_dropping_best],
24768		["aus 'm", either_2_dropping_best],
24769		["aus dem", either_2_dropping_best],
24770		["aus den", either_2_dropping_best],
24771		["aus der", either_2_dropping_best],
24772		["aus m", either_2_dropping_best],
24773		["aus", either_1_dropping_best],
24774		["aus'm", either_2_dropping_best],
24775		["az-", either_1],
24776		["aš-", either_1],
24777		["aḍ-", either_1],
24778		["aḏ-", either_1],
24779		["aá¹£-", either_1],
24780		["aá¹­-", either_1],
24781		["aṯ-", either_1],
24782		["aẓ-", either_1],
24783		["ben", always_non_dropping_1],
24784		["bij 't", always_non_dropping_2],
24785		["bij de", always_non_dropping_2],
24786		["bij den", always_non_dropping_2],
24787		["bij het", always_non_dropping_2],
24788		["bij t", always_non_dropping_2],
24789		["bij", always_non_dropping_1],
24790		["bin", always_non_dropping_1],
24791		["boven d", always_non_dropping_2],
24792		["boven d'", always_non_dropping_2],
24793		["d", always_non_dropping_1],
24794		["d'", either_1],
24795		["da", either_1],
24796		["dal", always_non_dropping_1],
24797		["dal'", always_non_dropping_1],
24798		["dall'", always_non_dropping_1],
24799		["dalla", always_non_dropping_1],
24800		["das", either_1],
24801		["de die le", always_non_dropping_3],
24802		["de die", always_non_dropping_2],
24803		["de l", always_non_dropping_2],
24804		["de l'", always_non_dropping_2],
24805		["de la", non_dropping_2_alt_dropping_1_non_dropping_1],
24806		["de las", non_dropping_2_alt_dropping_1_non_dropping_1],
24807		["de le", always_non_dropping_2],
24808		["de li", either_2],
24809		["de van der", always_non_dropping_3],
24810		["de", either_1],
24811		["de'", either_1],
24812		["deca", always_non_dropping_1],
24813		["degli", either_1],
24814		["dei", either_1],
24815		["del", either_1],
24816		["dela", always_dropping_1],
24817		["dell'", either_1],
24818		["della", either_1],
24819		["delle", either_1],
24820		["dello", either_1],
24821		["den", either_1],
24822		["der", either_1],
24823		["des", either_1],
24824		["di", either_1],
24825		["die le", always_non_dropping_2],
24826		["do", always_non_dropping_1],
24827		["don", always_non_dropping_1],
24828		["dos", either_1],
24829		["du", either_1],
24830		["ed-", either_1],
24831		["edh-", either_1],
24832		["el", either_1],
24833		["el-", either_1],
24834		["en-", either_1],
24835		["er-", either_1],
24836		["es-", either_1],
24837		["esh-", either_1],
24838		["et-", either_1],
24839		["eth-", either_1],
24840		["ez-", either_1],
24841		["eš-", either_1],
24842		["eḍ-", either_1],
24843		["eḏ-", either_1],
24844		["eá¹£-", either_1],
24845		["eá¹­-", either_1],
24846		["eṯ-", either_1],
24847		["eẓ-", either_1],
24848		["het", always_non_dropping_1],
24849		["i", always_non_dropping_1],
24850		["il", always_dropping_1],
24851		["im", always_non_dropping_1],
24852		["in 't", always_non_dropping_2],
24853		["in de", always_non_dropping_2],
24854		["in den", always_non_dropping_2],
24855		["in der", either_2],
24856		["in het", always_non_dropping_2],
24857		["in t", always_non_dropping_2],
24858		["in", always_non_dropping_1],
24859		["l", always_non_dropping_1],
24860		["l'", always_non_dropping_1],
24861		["la", always_non_dropping_1],
24862		["las", always_non_dropping_1],
24863		["le", always_non_dropping_1],
24864		["les", either_1],
24865		["lo", either_1],
24866		["los", always_non_dropping_1],
24867		["lou", always_non_dropping_1],
24868		["of", always_non_dropping_1],
24869		["onder 't", always_non_dropping_2],
24870		["onder de", always_non_dropping_2],
24871		["onder den", always_non_dropping_2],
24872		["onder het", always_non_dropping_2],
24873		["onder t", always_non_dropping_2],
24874		["onder", always_non_dropping_1],
24875		["op 't", always_non_dropping_2],
24876		["op de", either_2],
24877		["op den", always_non_dropping_2],
24878		["op der", always_non_dropping_2],
24879		["op gen", always_non_dropping_2],
24880		["op het", always_non_dropping_2],
24881		["op t", always_non_dropping_2],
24882		["op ten", always_non_dropping_2],
24883		["op", always_non_dropping_1],
24884		["over 't", always_non_dropping_2],
24885		["over de", always_non_dropping_2],
24886		["over den", always_non_dropping_2],
24887		["over het", always_non_dropping_2],
24888		["over t", always_non_dropping_2],
24889		["over", always_non_dropping_1],
24890		["s", always_non_dropping_1],
24891		["s'", always_non_dropping_1],
24892		["sen", always_dropping_1],
24893		["t", always_non_dropping_1],
24894		["te", always_non_dropping_1],
24895		["ten", always_non_dropping_1],
24896		["ter", always_non_dropping_1],
24897		["tho", always_non_dropping_1],
24898		["thoe", always_non_dropping_1],
24899		["thor", always_non_dropping_1],
24900		["to", always_non_dropping_1],
24901		["toe", always_non_dropping_1],
24902		["tot", always_non_dropping_1],
24903		["uijt 't", always_non_dropping_2],
24904		["uijt de", always_non_dropping_2],
24905		["uijt den", always_non_dropping_2],
24906		["uijt te de", always_non_dropping_3],
24907		["uijt ten", always_non_dropping_2],
24908		["uijt", always_non_dropping_1],
24909		["uit 't", always_non_dropping_2],
24910		["uit de", always_non_dropping_2],
24911		["uit den", always_non_dropping_2],
24912		["uit het", always_non_dropping_2],
24913		["uit t", always_non_dropping_2],
24914		["uit te de", always_non_dropping_3],
24915		["uit ten", always_non_dropping_2],
24916		["uit", always_non_dropping_1],
24917		["unter", always_non_dropping_1],
24918		["v", always_non_dropping_1],
24919		["v.", always_non_dropping_1],
24920		["v.d.", always_non_dropping_1],
24921		["van 't", always_non_dropping_2],
24922		["van de l", always_non_dropping_3],
24923		["van de l'", always_non_dropping_3],
24924		["van de", always_non_dropping_2],
24925		["van de", always_non_dropping_2],
24926		["van den", always_non_dropping_2],
24927		["van der", always_non_dropping_2],
24928		["van gen", always_non_dropping_2],
24929		["van het", always_non_dropping_2],
24930		["van la", always_non_dropping_2],
24931		["van t", always_non_dropping_2],
24932		["van ter", always_non_dropping_2],
24933		["van van de", always_non_dropping_3],
24934		["van", either_1],
24935		["vander", always_non_dropping_1],
24936		["vd", always_non_dropping_1],
24937		["ver", always_non_dropping_1],
24938		["vom und zum", always_dropping_3],
24939		["vom", either_1],
24940		["von 't", always_non_dropping_2],
24941		["von dem", either_2_dropping_best],
24942		["von den", either_2_dropping_best],
24943		["von der", either_2_dropping_best],
24944		["von t", always_non_dropping_2],
24945		["von und zu", either_3_dropping_best],
24946		["von zu", either_2_dropping_best],
24947		["von", either_1_dropping_best],
24948		["voor 't", always_non_dropping_2],
24949		["voor de", always_non_dropping_2],
24950		["voor den", always_non_dropping_2],
24951		["voor in 't", always_non_dropping_3],
24952		["voor in t", always_non_dropping_3],
24953		["voor", always_non_dropping_1],
24954		["vor der", either_2_dropping_best],
24955		["vor", either_1_dropping_best],
24956		["z", always_dropping_1],
24957		["ze", always_dropping_1],
24958		["zu", either_1_dropping_best],
24959		["zum", either_1],
24960		["zur", either_1]
24961	];
24962    return PARTICLES;
24963}());
24964
24965CSL.parseParticles = (function(){
24966    function splitParticles(nameValue, firstNameFlag, caseOverride) {
24967		// Parse particles out from name fields.
24968		// * nameValue (string) is the field content to be parsed.
24969		// * firstNameFlag (boolean) parse trailing particles
24970		//	 (default is to parse leading particles)
24971		// * caseOverride (boolean) include all but one word in particle set
24972		//	 (default is to include only words with lowercase first char)
24973        //   [caseOverride is not used in this application]
24974		// Returns an array with:
24975		// * (boolean) flag indicating whether a particle was found
24976		// * (string) the name after removal of particles
24977		// * (array) the list of particles found
24978		var origNameValue = nameValue;
24979		nameValue = caseOverride ? nameValue.toLowerCase() : nameValue;
24980		var particleList = [];
24981		var rex;
24982        var hasParticle;
24983		if (firstNameFlag) {
24984			nameValue = nameValue.split("").reverse().join("");
24985			rex = CSL.PARTICLE_GIVEN_REGEXP;
24986		} else {
24987			rex = CSL.PARTICLE_FAMILY_REGEXP;
24988		}
24989		var m = nameValue.match(rex);
24990		while (m) {
24991			var m1 = firstNameFlag ? m[1].split("").reverse().join("") : m[1];
24992			var firstChar = m ? m1 : false;
24993			var firstChar = firstChar ? m1.replace(/^[-\'\u02bb\u2019\s]*(.).*$/, "$1") : false;
24994			hasParticle = firstChar ? firstChar.toUpperCase() !== firstChar : false;
24995			if (!hasParticle) {
24996                break;
24997            }
24998			if (firstNameFlag) {
24999				particleList.push(origNameValue.slice(m1.length * -1));
25000				origNameValue = origNameValue.slice(0,m1.length * -1);
25001			} else {
25002				particleList.push(origNameValue.slice(0,m1.length));
25003				origNameValue = origNameValue.slice(m1.length);
25004			}
25005			//particleList.push(m1);
25006			nameValue = m[2];
25007			m = nameValue.match(rex);
25008		}
25009		if (firstNameFlag) {
25010			nameValue = nameValue.split("").reverse().join("");
25011			particleList.reverse();
25012			for (var i=1,ilen=particleList.length;i<ilen;i++) {
25013				if (particleList[i].slice(0, 1) == " ") {
25014					particleList[i-1] += " ";
25015				}
25016			}
25017			for (var i=0,ilen=particleList.length;i<ilen;i++) {
25018				if (particleList[i].slice(0, 1) == " ") {
25019					particleList[i] = particleList[i].slice(1);
25020				}
25021			}
25022			nameValue = origNameValue.slice(0, nameValue.length);
25023		} else {
25024			nameValue = origNameValue.slice(nameValue.length * -1);
25025		}
25026		return [hasParticle, nameValue, particleList];
25027	}
25028    function trimLast(str) {
25029        var lastChar = str.slice(-1);
25030        str = str.trim();
25031        if (lastChar === " " && ["\'", "\u2019"].indexOf(str.slice(-1)) > -1) {
25032            str += " ";
25033        }
25034        return str;
25035    }
25036    function parseSuffix(nameObj) {
25037        if (!nameObj.suffix && nameObj.given) {
25038            var m = nameObj.given.match(/(\s*,!*\s*)/);
25039            if (m) {
25040                var idx = nameObj.given.indexOf(m[1]);
25041                var possible_suffix = nameObj.given.slice(idx + m[1].length);
25042                var possible_comma = nameObj.given.slice(idx, idx + m[1].length).replace(/\s*/g, "");
25043                if (possible_suffix.replace(/\./g, "") === 'et al' && !nameObj["dropping-particle"]) {
25044                    // This hack covers the case where "et al." is explicitly used in the
25045                    // authorship information of the work.
25046                    nameObj["dropping-particle"] = possible_suffix;
25047                    nameObj["comma-dropping-particle"] = ",";
25048                } else {
25049                    if (possible_comma.length === 2) {
25050                        nameObj["comma-suffix"] = true;
25051                    }
25052                    nameObj.suffix = possible_suffix;
25053                }
25054                nameObj.given = nameObj.given.slice(0, idx);
25055            }
25056        }
25057    }
25058    return function(nameObj) {
25059        // Extract and set non-dropping particle(s) from family name field
25060        var res = splitParticles(nameObj.family);
25061        var lastNameValue = res[1];
25062        var lastParticleList = res[2];
25063        nameObj.family = lastNameValue;
25064        var nonDroppingParticle = trimLast(lastParticleList.join(""));
25065        if (nonDroppingParticle) {
25066            nameObj['non-dropping-particle'] = nonDroppingParticle;
25067        }
25068        // Split off suffix first of all
25069        parseSuffix(nameObj);
25070        // Extract and set dropping particle(s) from given name field
25071        var res = splitParticles(nameObj.given, true);
25072        var firstNameValue = res[1];
25073        var firstParticleList = res[2];
25074        nameObj.given = firstNameValue;
25075        var droppingParticle = firstParticleList.join("").trim();
25076        if (droppingParticle) {
25077            nameObj['dropping-particle'] = droppingParticle;
25078        }
25079    };
25080}());
25081
25082
25083module.exports = CSL
25084
25085/***/ })
25086/******/ ]);
25087});
25088//# sourceMappingURL=citeproc.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.