Plugin Directory

Changeset 506635


Ignore:
Timestamp:
02/17/2012 04:24:56 PM (14 years ago)
Author:
knowledgeblog
Message:

Multiple updates for 1.5 release.

Location:
kcite/trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • kcite/trunk/kcite-citeproc/citeproc.js

    r473946 r506635  
    3232 * The Initial Developer of the Original Code is Frank G. Bennett,
    3333 * Jr. All portions of the code written by Frank G. Bennett, Jr. are
    34  * Copyright (c) 2009 and 2010 Frank G. Bennett, Jr. All Rights Reserved.
     34 * Copyright (c) 2009, 2010 and 2011 Frank G. Bennett, Jr. All Rights Reserved.
    3535 *
    3636 * Alternatively, the contents of this file may be used under the
     
    5858}
    5959var CSL = {
     60    STATUTE_SUBDIV_GROUPED_REGEX: /((?:^| )(?:pt\.|ch\.|subch\.|sec\.|art\.|para\.))/g,
     61    STATUTE_SUBDIV_PLAIN_REGEX: /(?:(?:^| )(?:pt\.|ch\.|subch\.|sec\.|art\.|para\.))/,
     62    STATUTE_SUBDIV_STRINGS: {
     63        "pt.": "part",
     64        "ch.": "chapter",
     65        "subch.": "subchapter",
     66        "sec.": "section",
     67        "art.": "article",
     68        "para.": "paragraph"
     69    },
     70    NestedBraces: [
     71        ["(", "["],
     72        [")", "]"]
     73    ],
     74    checkNestedBraceOpen: new RegExp(".*\\("),
     75    checkNestedBraceClose: new RegExp(".*\\)"),
     76    LangPrefsMap: {
     77        "title":"titles",
     78        "title-short":"titles",
     79        "container-title":"titles",
     80        "collection-title":"titles",
     81        "publisher":"publishers",
     82        "authority":"publishers",
     83        "publisher-place": "places",
     84        "event-place": "places"
     85    },
     86    AbbreviationSegments: function () {
     87        this["container-title"] = {};
     88        this["collection-title"] = {};
     89        this["institution-entire"] = {};
     90        this["institution-part"] = {};
     91        this["nickname"] = {};
     92        this["number"] = {};
     93        this["title"] = {};
     94        this["place"] = {};
     95        this["hereinafter"] = {};
     96        this["classic"] = {};
     97        this["container-phrase"] = {};
     98        this["title-phrase"] = {};
     99    },
    60100    GENDERS: ["masculine", "feminine"],
    61101    ERROR_NO_RENDERED_FORM: 1,
     
    87127    POSITION_TEST_VARS: ["position", "first-reference-note-number", "near-note"],
    88128    AREAS: ["citation", "citation_sort", "bibliography", "bibliography_sort"],
    89     MULTI_FIELDS: ["publisher", "publisher-place", "event-place", "title","container-title", "collection-title", "institution", "authority","edition","genre","title-short"],
     129    MULTI_FIELDS: ["event", "publisher", "publisher-place", "event-place", "title", "container-title", "collection-title", "authority","edition","genre","title-short","subjurisdiction","medium"],
    90130    CITE_FIELDS: ["first-reference-note-number", "locator", "locator-revision"],
    91131    MINIMAL_NAME_FIELDS: ["literal", "family"],
    92132    SWAPPING_PUNCTUATION: [".", "!", "?", ":",","],
    93133    TERMINAL_PUNCTUATION: [":", ".", ";", "!", "?", " "],
    94     SPLICE_PUNCTUATION: [".", "!", "?", ":", ";", ","],
    95134    NONE: 0,
    96135    NUMERIC: 1,
     
    133172    PARALLEL_MATCH_VARS: ["container-title"],
    134173    PARALLEL_TYPES: ["legal_case",  "legislation", "bill"],
    135     PARALLEL_COLLAPSING_MID_VARSET: ["volume", "container-title", "section"],
     174    PARALLEL_COLLAPSING_MID_VARSET: ["volume", "issue", "container-title", "section", "collection-number"],
    136175    LOOSE: 0,
    137176    STRICT: 1,
     
    395434        "\u06E6": "\u064A"
    396435    },
     436    LOCATOR_LABELS_REGEXP: new RegExp("^((ch|col|fig|no|l|n|op|p|para|pt|sec|sv|vrs|vol)\\.)\\s+(.*)"),
     437    LOCATOR_LABELS_MAP: {
     438        "ch": "chapter",
     439        "col": "column",
     440        "fig": "figure",
     441        "no": "issue",
     442        "l": "line",
     443        "n": "note",
     444        "op": "opus",
     445        "p": "page",
     446        "para": "para",
     447        "pt": "part",
     448        "sec": "section",
     449        "sv": "sub-verbo",
     450        "vrs": "verse",
     451        "vol": "volume"
     452    },
    397453    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"),
    398454    locale: {},
     
    536592CSL.Output.Queue.prototype.startTag = function (name, token) {
    537593    var tokenstore = {};
    538     if (this.state.tmp["doing-macro-with-date"] && this.state.tmp.area.slice(-5) === "_sort") {
     594    if (this.state.tmp["doing-macro-with-date"] && this.state.tmp.extension) {
    539595        token = this.empty;
    540596        name = "empty";
     
    560616        blob = new CSL.Blob(undefined, this.formats.value()[token], token);
    561617    }
     618    if (this.nestedBraces) {
     619        blob.strings.prefix = blob.strings.prefix.replace(this.nestedBraces[0][0], this.nestedBraces[0][1]);
     620        blob.strings.prefix = blob.strings.prefix.replace(this.nestedBraces[1][0], this.nestedBraces[1][1]);
     621        blob.strings.suffix = blob.strings.suffix.replace(this.nestedBraces[0][0], this.nestedBraces[0][1]);
     622        blob.strings.suffix = blob.strings.suffix.replace(this.nestedBraces[1][0], this.nestedBraces[1][1]);
     623    }
    562624    curr = this.current.value();
    563625    curr.push(blob);
     
    570632    this.current.pop();
    571633};
    572 CSL.Output.Queue.prototype.append = function (str, tokname, notSerious) {
     634CSL.Output.Queue.prototype.append = function (str, tokname, notSerious, ignorePredecessor, noStripPeriods) {
    573635    var token, blob, curr;
    574636    var useblob = true;
     
    613675        this.last_char_rendered = str.slice(-1);
    614676        str = str.replace(/\s+'/g, "  \'").replace(/^'/g, " \'");
    615         this.state.tmp.term_predecessor = true;
     677        if (!ignorePredecessor) {
     678            this.state.tmp.term_predecessor = true;
     679        }
    616680    }
    617681    blob = new CSL.Blob(str, token);
     682    if (this.nestedBraces) {
     683        blob.strings.prefix = blob.strings.prefix.replace(this.nestedBraces[0][0], this.nestedBraces[0][1]);
     684        blob.strings.prefix = blob.strings.prefix.replace(this.nestedBraces[1][0], this.nestedBraces[1][1]);
     685        blob.strings.suffix = blob.strings.suffix.replace(this.nestedBraces[0][0], this.nestedBraces[0][1]);
     686        blob.strings.suffix = blob.strings.suffix.replace(this.nestedBraces[1][0], this.nestedBraces[1][1]);
     687    }
    618688    curr = this.current.value();
    619689    if ("undefined" === typeof curr && this.current.mystack.length === 0) {
     
    622692    }
    623693    if ("string" === typeof blob.blobs) {
    624         if (this.state.tmp.strip_periods) {
     694        if (this.state.tmp.strip_periods && !noStripPeriods) {
    625695            blob.blobs = blob.blobs.replace(/\./g, "");
    626696        }
    627         this.state.tmp.term_predecessor = true;
     697        if (!ignorePredecessor) {
     698            this.state.tmp.term_predecessor = true;
     699        }
    628700    }
    629701    if (!notSerious) {
     
    646718CSL.Output.Queue.prototype.string = function (state, myblobs, blob) {
    647719    var i, ilen, j, jlen, b;
    648     var txt_esc = CSL.getSafeEscape(this.state.opt.mode, this.state.tmp.area);
     720    var txt_esc = CSL.getSafeEscape(this.state);
    649721    var blobs = myblobs.slice();
    650722    var ret = [];
     
    685757                }
    686758                if (b && b.length) {
    687                     b = txt_esc(blobjr.strings.prefix) + b + txt_esc(blobjr.strings.suffix);
     759                    b = txt_esc(blobjr.strings.prefix, state.tmp.nestedBraces) + b + txt_esc(blobjr.strings.suffix, state.tmp.nestedBraces);
    688760                    ret.push(b);
    689761                    if (state.tmp.count_offset_characters) {
     
    706778            span_split = (parseInt(i, 10) + 1);
    707779            if (i < ret.length - 1  && "object" === typeof ret[i + 1]) {
    708                 if (!ret[i + 1].UGLY_DELIMITER_SUPPRESS_HACK) {
     780                if (blob_delimiter && !ret[i + 1].UGLY_DELIMITER_SUPPRESS_HACK) {
    709781                    ret[i] += txt_esc(blob_delimiter);
    710782                } else {
     
    735807        if (b && b.length) {
    736808            use_prefix = blob.strings.prefix;
    737             b = txt_esc(use_prefix) + b + txt_esc(use_suffix);
     809            b = txt_esc(use_prefix, state.tmp.nestedBraces) + b + txt_esc(use_suffix, state.tmp.nestedBraces);
    738810            if (state.tmp.count_offset_characters) {
    739811                state.tmp.offset_characters += (use_prefix.length + use_suffix.length);
     
    788860CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, has_more) {
    789861    var state, ret, ret_last_char, use_delim, i, blob, pos, len, ppos, llen, pppos, lllen, res, str, params, txt_esc;
    790     txt_esc = CSL.getSafeEscape(this.state.opt.mode, this.state.tmp.area);
     862    txt_esc = CSL.getSafeEscape(this.state);
    791863    if (!delim) {
    792864        delim = "";
     
    10471119                          TERMS.indexOf(doblob.strings.suffix.slice(-1)) > -1)) {
    10481120                            blob.strings.suffix = blob.strings.suffix.slice(1);
    1049                     }
    1050                 }
    1051             }
    1052             if ("string" === typeof doblob.blobs && doblob.blobs) {
    1053                 for (var ppos = doblob.decorations.length - 1; ppos > -1; ppos += -1) {
    1054                     var params = doblob.decorations[ppos];
    1055                     if (params[0] === "@strip-periods" && params[1] === "true") {
    1056                         doblob.blobs = state.fun.decorate[params[0]][params[1]](state, doblob.blobs);
    1057                         doblob.decorations = doblob.decorations.slice(0, ppos).concat(doblob.decorations.slice(ppos + 1));
    10581121                    }
    10591122                }
     
    12811344    if (hasDate) {
    12821345        func = function (state, Item) {
    1283             if (state.tmp.area.slice(-5) === "_sort") {
     1346            if (state.tmp.extension) {
    12841347                state.tmp["doing-macro-with-date"] = true;
    12851348            }
     
    12951358    CSL.buildStyle.call(this, navi);
    12961359    end_of_macro = new CSL.Token("group", CSL.END);
     1360    if (macro_key_token.decorations) {
     1361        end_of_macro.decorations = macro_key_token.decorations.slice();
     1362    }
    12971363    if (hasDate) {
    12981364        func = function (state, Item) {
    1299             if (state.tmp.area.slice(-5) === "_sort") {
     1365            if (state.tmp.extension) {
    13001366                state.tmp["doing-macro-with-date"] = false;
    13011367            }
     
    17181784CSL.Engine = function (sys, style, lang, forceLang) {
    17191785    var attrs, langspec, localexml, locale;
    1720     this.processor_version = "1.0.223";
     1786    this.processor_version = "1.0.278";
    17211787    this.csl_version = "1.0";
    17221788    this.sys = sys;
     
    17241790    if ("string" !== typeof style) {
    17251791        style = "";
     1792    }
     1793    if (CSL.getAbbreviation) {
     1794        this.sys.getAbbreviation = CSL.getAbbreviation;
    17261795    }
    17271796    this.parallel = new CSL.Parallel(this);
     
    20192088    return ret;
    20202089};
     2090CSL.ITERATION = 0;
    20212091CSL.Engine.prototype.retrieveItem = function (id) {
    20222092    var Item, m, pos, len, mm;
     2093    CSL.ITERATION += 1;
    20232094    if (this.registry.generate.genIDs["" + id]) {
    20242095        Item = this.registry.generate.items["" + id];
     
    20312102    }
    20322103    if (this.opt.development_extensions.field_hack && Item.note) {
    2033         m = CSL.NOTE_FIELDS_REGEXP.exec(Item.note);
     2104        m = Item.note.match(CSL.NOTE_FIELDS_REGEXP);
    20342105        if (m) {
     2106            var names = {};
    20352107            for (pos = 0, len = m.length; pos < len; pos += 1) {
    2036                 mm = CSL.NOTE_FIELD_REGEXP.exec(m[pos]);
    2037                 if (!Item[mm[1]]) {
    2038                     if (CSL.DATE_VARIABLES.indexOf(mm[1]) > -1) {
    2039                         Item[mm[1]] = {raw:mm[2]};
    2040                     } else {
    2041                         Item[mm[1]] = mm[2].replace(/^\s+/, "").replace(/\s+$/, "");
    2042                     }
    2043                 }
    2044             }
     2108                mm = m[pos].match(CSL.NOTE_FIELD_REGEXP);
     2109                if (!Item[mm[1]] && CSL.DATE_VARIABLES.indexOf(mm[1]) > -1) {
     2110                    Item[mm[1]] = {raw:mm[2]};
     2111                } else if (!Item[mm[1]] && CSL.NAME_VARIABLES.indexOf(mm[1]) > -1) {
     2112                    if (!Item[mm[1]]) {
     2113                        Item[mm[1]] = []
     2114                    }
     2115                    var lst = mm[2].split(/\s*\|\|\s*/)
     2116                    if (lst.length === 1) {
     2117                        Item[mm[1]].push({family:lst[0],isInstitution:true});
     2118                    } else if (lst.length === 2) {
     2119                        Item[mm[1]].push({family:lst[0],given:lst[1]});
     2120                    }
     2121                } else if (!Item[mm[1]] || mm[1] === "type") {
     2122                    Item[mm[1]] = mm[2].replace(/^\s+/, "").replace(/\s+$/, "");
     2123                }
     2124                Item.note.replace(CSL.NOTE_FIELD_REGEXP, "")
     2125            }
     2126        }
     2127    }
     2128    if (this.opt.development_extensions.jurisdiction_subfield && Item.jurisdiction) {
     2129        var subjurisdictions = Item.jurisdiction.split(";");
     2130        if (subjurisdictions.length > 1) {
     2131            Item.subjurisdiction = subjurisdictions.slice(0,2).join(";");
    20452132        }
    20462133    }
     
    20682155};
    20692156CSL.Engine.prototype.fixOpt = function (token, name, localname) {
    2070     if ("citation" === token.name || "bibliography" === token.name) {
    2071         if (! this[token.name].opt[name] && "undefined" !== this.opt[name]) {
     2157    if (["citation", "bibliography"].indexOf(token.name) > -1) {
     2158        if (! this[token.name].opt[name] && "undefined" !== typeof this.opt[name]) {
    20722159            this[token.name].opt[name] = this.opt[name];
    20732160        }
    20742161    }
    20752162    if ("name" === token.name || "names" === token.name) {
    2076         if ("undefined" === typeof token.strings[localname] && "undefined" !== typeof this[this.build.area].opt[name]) {
    2077             token.strings[localname] = this[this.build.area].opt[name];
     2163        if ("undefined" === typeof token.strings[localname] && "undefined" !== typeof this[this.build.root].opt[name]) {
     2164            token.strings[localname] = this[this.build.root].opt[name];
    20782165        }
    20792166    }
     
    21932280CSL.Engine.prototype.setLangTagsForCslTransliteration = function (tags) {
    21942281    var i, ilen;
    2195     this.opt['locale-pri'] = [];   
     2282    this.opt['locale-translit'] = [];   
    21962283    for (i = 0, ilen = tags.length; i < ilen; i += 1) {
    2197         this.opt['locale-pri'].push(tags[i]);
     2284        this.opt['locale-translit'].push(tags[i]);
    21982285    }
    21992286};
    22002287CSL.Engine.prototype.setLangTagsForCslTranslation = function (tags) {
    22012288    var i, ilen;
    2202     this.opt['locale-sec'] = [];
     2289    this.opt['locale-translat'] = [];
    22032290    for (i = 0, ilen = tags.length; i < ilen; i += 1) {
    2204         this.opt['locale-sec'].push(tags[i]);
    2205     }
    2206 };
    2207 CSL.Engine.prototype.setOriginalCreatorNameFormsOption = function (arg) {
    2208     if (arg) {
    2209         this.opt["locale-show-original-names"] = true;
    2210     } else {
    2211         this.opt["locale-show-original-names"] = false;
    2212     }
    2213 };
    2214 CSL.Engine.prototype.setOriginalCreatorNameFormatOption = function (arg) {
    2215     if (arg) {
    2216         this.opt["locale-use-original-name-format"] = true;
    2217     } else {
    2218         this.opt["locale-use-original-name-format"] = false;
    2219     }
    2220 };
    2221 CSL.Engine.prototype.setSuppressTitleTransliterationOption = function (arg) {
    2222     if (arg) {
    2223         this.opt["locale-suppress-title-transliteration"] = true;
    2224     } else {
    2225         this.opt["locale-suppress-title-transliteration"] = false;
    2226     }
     2291        this.opt['locale-translat'].push(tags[i]);
     2292    }
     2293};
     2294CSL.Engine.prototype.setLangPrefsForCites = function (params) {
     2295    var opt = this.opt['cite-lang-prefs'];
     2296    for (var segment in params) {
     2297        var supplements = [];
     2298        while (params[segment].length > 1) {
     2299            supplements.push(params[segment].pop());
     2300        }
     2301        var sortval = {orig:1,translit:2,translat:3};
     2302        if (supplements.length === 2 && sortval[supplements[0]] < sortval[supplements[1]]) {
     2303            supplements.reverse();
     2304        }
     2305        while (supplements.length) {
     2306            params[segment].push(supplements.pop());
     2307        }
     2308        var lst = opt[segment];
     2309        while (lst.length) {
     2310            lst.pop();
     2311        }
     2312        for (var i = 0, ilen = params[segment].length; i < ilen; i += 1) {
     2313            lst.push(params[segment][i]);
     2314        }
     2315    }
    22272316};
    22282317CSL.Engine.prototype.setAutoVietnameseNamesOption = function (arg) {
     
    22332322    }
    22342323};
     2324CSL.Engine.prototype.setAbbreviations = function (arg) {
     2325    if (this.sys.setAbbreviations) {
     2326        this.sys.setAbbreviations(arg);
     2327    }
     2328}
    22352329CSL.Engine.Opt = function () {
    22362330    this.has_disambiguate = false;
     
    22382332    this.dates = {};
    22392333    this["locale-sort"] = [];
    2240     this["locale-pri"] = [];
    2241     this["locale-sec"] = [];
     2334    this["locale-translit"] = [];
     2335    this["locale-translat"] = [];
    22422336    this["default-locale"] = [];
    2243     this["locale-use-original-name-format"] = false;
    22442337    this["noun-genders"] = {};
    22452338    this.update_mode = CSL.NONE;
     
    22602353    this.development_extensions.locator_date_and_revision = true;
    22612354    this.development_extensions.locator_parsing_for_plurals = true;
     2355    this.development_extensions.locator_label_parse = true;
    22622356    this.development_extensions.raw_date_parsing = true;
    22632357    this.development_extensions.clean_up_csl_flaws = true;
     2358    this.development_extensions.flip_parentheses_to_braces = true;
     2359    this.development_extensions.parse_section_variable = true;
     2360    this.development_extensions.jurisdiction_subfield = true;
    22642361    this.gender = {};
     2362    this['cite-lang-prefs'] = {
     2363        persons:['orig'],
     2364        institutions:['orig'],
     2365        titles:['orig','translat'],
     2366        publishers:['orig'],
     2367        places:['orig']
     2368    }
    22652369};
    22662370CSL.Engine.Tmp = function () {
     
    22722376    this.namepart_type = false;
    22732377    this.area = "citation";
     2378    this.root = "citation";
     2379    this.extension = "";
    22742380    this.can_substitute = new CSL.Stack(0, CSL.LITERAL);
    22752381    this.element_rendered_ok = false;
    22762382    this.element_trace = new CSL.Stack("style");
    22772383    this.nameset_counter = 0;
    2278     this.term_sibling = new CSL.Stack([false, false, false], CSL.LITERAL);
     2384    this.group_context = new CSL.Stack([false, false, false], CSL.LITERAL);
    22792385    this.term_predecessor = false;
    22802386    this.jump = new CSL.Stack(0, CSL.LITERAL);
     
    23242430    this.lang = false;
    23252431    this.area = "citation";
     2432    this.root = "citation";
     2433    this.extension = "";
    23262434    this.substitute_level = new CSL.Stack(0, CSL.LITERAL);
     2435    this.names_level = 0;
    23272436    this.render_nesting_level = 0;
    23282437    this.render_seen = false;
     
    25142623                    break;
    25152624                }
    2516                 var name = this.transform.name(this, names[j], this.opt["locale-pri"]);
     2625                var res = this.nameOutput.getName(names[j], "locale-translit", true);
     2626                var name = res.name;
    25172627                if (name && name.family) {
    25182628                    myname = name.family;
     
    27532863            this.parallel.StartCitation(sortedItems);
    27542864            this.output.queue[0].strings.delimiter = ", ";
     2865            this.tmp.term_predecessor = false;
    27552866            entry_item_ids.push("" + CSL.getCite.call(this, item));
    27562867            skips[item.id] = true;
     
    27652876            this.parallel.PruneOutputQueue();
    27662877        } else if (!this.registry.registry[item.id].siblings) {
     2878            this.tmp.term_predecessor = false;
    27672879            entry_item_ids.push("" + CSL.getCite.call(this, item));
    27682880        }
     
    28792991            }
    28802992        }
     2993        if (this.opt.development_extensions.locator_label_parse) {
     2994            if (item.locator && (!item.label || item.label === 'page')) {
     2995                var m = CSL.LOCATOR_LABELS_REGEXP.exec(item.locator);
     2996                if (m) {
     2997                    item.label = CSL.LOCATOR_LABELS_MAP[m[2]];
     2998                    item.locator = m[3];
     2999                }
     3000            }
     3001        }
    28813002        Item = this.retrieveItem("" + item.id);
    28823003        var newitem = [Item, item];
     
    29613082    var citations;
    29623083    if (this.opt.update_mode === CSL.POSITION) {
     3084        var citationsInNote = {};
    29633085        for (i = 0; i < 2; i += 1) {
    29643086            citations = [textCitations, noteCitations][i];
     
    29693091                if (!onecitation.properties.noteIndex) {
    29703092                    onecitation.properties.noteIndex = 0;
     3093                }
     3094                if (!citationsInNote[onecitation.properties.noteIndex]) {
     3095                    citationsInNote[onecitation.properties.noteIndex] = 1;
     3096                } else {
     3097                    citationsInNote[onecitation.properties.noteIndex] += 1;
    29713098                }
    29723099                for (k = 0, klen = citations[j].sortedItems.length; k < klen; k += 1) {
     
    30003127                            var useme = false;
    30013128                            if ((citations[(j - 1)].sortedItems[0][1].id  == item[1].id && citations[j - 1].properties.noteIndex >= (citations[j].properties.noteIndex - 1)) || citations[(j - 1)].sortedItems[0][1].id == this.registry.registry[item[1].id].parallel) {
    3002                                 useme = true;
     3129                                if (citationsInNote[citations[j - 1].properties.noteIndex] === 1 || citations[j - 1].properties.noteIndex === 0) {
     3130                                    useme = true;
     3131                                }
    30033132                            }
    30043133                            for (n = 0, nlen = items.slice(1).length; n < nlen; n += 1) {
     
    32013330CSL.getAmbiguousCite = function (Item, disambig) {
    32023331    var use_parallels, ret;
     3332    var oldTermSiblingLayer = this.tmp.group_context.value().slice();
    32033333    if (disambig) {
    32043334        this.tmp.disambig_request = disambig;
     
    32203350    this.tmp.suppress_decorations = false;
    32213351    this.parallel.use_parallels = use_parallels;
     3352    this.tmp.group_context.replace(oldTermSiblingLayer, "literal");
    32223353    return ret;
    32233354};
     
    32253356    if (last_collapsed && ! this.tmp.have_collapsed && "string" === typeof this.citation.opt["after-collapse-delimiter"]) {
    32263357        this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
    3227     } else if (this.tmp.have_collapsed && this.opt.xclass === "in-text") {
     3358    } else if (this.tmp.have_collapsed && this.opt.xclass === "in-text" && this.opt.update_mode !== CSL.NUMERIC) {
    32283359        this.tmp.splice_delimiter = ", ";
    32293360    } else if (this.tmp.cite_locales[pos - 1]) {
     
    32413372    var result, objects, myparams, len, pos, item, last_collapsed, params, empties, composite, compie, myblobs, Item, llen, ppos, obj, preceding_item, txt_esc, error_object;
    32423373    this.tmp.last_primary_names_string = false;
    3243     txt_esc = CSL.Output.Formats[this.opt.mode].text_escape;
     3374    this.tmp.nestedBraces = false;
     3375    txt_esc = CSL.getSafeEscape(this);
    32443376    this.tmp.area = "citation";
    32453377    result = "";
     
    32533385        this.registry.citationreg.citationById[citationID].properties.backref_index = false;
    32543386        this.registry.citationreg.citationById[citationID].properties.backref_citation = false;
     3387    }
     3388    if (this.opt.xclass === "note") {
     3389        var parasets = [];
     3390        var lastTitle = false;
     3391        var lastPosition = false;
     3392        var lastID = false;
     3393        for (var i=0, ilen = inputList.length; i < ilen; i += 1) {
     3394            var type = inputList[i][0].type;
     3395            var title = inputList[i][0].title;
     3396            var position = inputList[i][1].position;
     3397            var id = inputList[i][0].id;
     3398            if (title && type === "legal_case" && id !== lastID && position) {
     3399                if (title !== lastTitle || parasets.length === 0) {
     3400                    var lst = [];
     3401                    parasets.push(lst);
     3402                }
     3403                lst.push(inputList[i][1]);
     3404            }
     3405            lastTitle = title;
     3406            lastPosition = position;
     3407            lastID = id;
     3408        }
     3409        for (var i=0, ilen=parasets.length; i < ilen; i += 1) {
     3410            var lst = parasets[i];
     3411            if (lst.length < 2) {
     3412                continue;
     3413            }
     3414            var locatorInLastPosition = lst.slice(-1)[0].locator;
     3415            if (locatorInLastPosition) {
     3416                for (var j=0, jlen=lst.length - 1; j < jlen; j += 1) {
     3417                    if (lst[j].locator) {
     3418                        locatorInLastPosition = false;
     3419                    }
     3420                }
     3421            }
     3422            if (locatorInLastPosition) {
     3423                lst[0].locator = locatorInLastPosition;
     3424                delete lst.slice(-1)[0].locator;
     3425                lst[0].label = lst.slice(-1)[0].label;
     3426                if (lst.slice(-1)[0].label) {
     3427                    delete lst.slice(-1)[0].label;
     3428                }
     3429            }
     3430       }
    32553431    }
    32563432    myparams = [];
     
    33003476        myparams.push(params);
    33013477    }
     3478    this.tmp.has_purged_parallel = false;
    33023479    this.parallel.PruneOutputQueue(this);
    33033480    empties = 0;
     
    33563533        }
    33573534        if ("object" === typeof composite && composite.length === 0 && !item["suppress-author"]) {
    3358             composite.push("[CSL STYLE ERROR: reference with no printed form.]");
     3535            if (this.tmp.has_purged_parallel) {
     3536                composite.push("");
     3537            } else {
     3538                composite.push("[CSL STYLE ERROR: reference with no printed form.]");
     3539            }
    33593540        }
    33603541        if (objects.length && "string" === typeof composite[0]) {
     
    33633544            if (tmpstr && tmpstr.slice(0, 1) === ",") {
    33643545                objects.push(tmpstr);
    3365             } else {
     3546            } else if ("string" == typeof objects.slice(-1)[0] && objects.slice(-1)[0].slice(-1) === ",") {
     3547                objects.push(" " + tmpstr)
     3548            } else if (tmpstr) {
    33663549                objects.push(txt_esc(this.tmp.splice_delimiter) + tmpstr);
    33673550            }
     
    33983581            use_layout_suffix = use_layout_suffix.slice(1);
    33993582        }
     3583        this.output.nestedBraces = false;
    34003584        result = txt_esc(this.citation.opt.layout_prefix) + result + txt_esc(use_layout_suffix);
    34013585        if (!this.tmp.suppress_decorations) {
     
    34733657    this.tmp.shadow_numbers = {};
    34743658    this.tmp.first_name_string = false;
     3659    if (this.opt.development_extensions.flip_parentheses_to_braces && item && item.prefix) {
     3660        var openBrace = CSL.checkNestedBraceOpen.exec(item.prefix);
     3661        var closeBrace = CSL.checkNestedBraceClose.exec(item.prefix);
     3662        if (openBrace) {
     3663            if (!closeBrace) {
     3664                this.output.nestedBraces = CSL.NestedBraces;
     3665            } else if (closeBrace[0].length < openBrace[0].length) {
     3666                this.output.nestedBraces = CSL.NestedBraces;
     3667            } else {
     3668                this.output.nestedBraces = false;
     3669            }
     3670        } else if (closeBrace) {
     3671            this.output.nestedBraces = false;
     3672        }
     3673    }
    34753674};
    34763675CSL.citeEnd = function (Item, item) {
     
    34863685    this.tmp.disambig_request = false;
    34873686    this.tmp.cite_locales.push(this.tmp.last_cite_locale);
     3687    if (this.opt.development_extensions.flip_parentheses_to_braces && item && item.suffix) {
     3688        var openBrace = CSL.checkNestedBraceOpen.exec(item.suffix);
     3689        var closeBrace = CSL.checkNestedBraceClose.exec(item.suffix);
     3690        if (closeBrace) {
     3691            if (!openBrace) {
     3692                this.output.nestedBraces = false;
     3693            } else if (openBrace[0].length < closeBrace[0].length) {
     3694                this.output.nestedBraces = false;
     3695            } else {
     3696                this.output.nestedBraces = CSL.NestedBraces;
     3697            }
     3698        } else if (openBrace) {
     3699            this.output.nestedBraces = CSL.NestedBraces;
     3700        }
     3701    }
    34883702};
    34893703CSL.Node = {};
     
    34913705    build: function (state, target) {
    34923706        if (this.tokentype === CSL.START) {
     3707            state.build.area = "bibliography";
     3708            state.build.root = "bibliography";
    34933709            state.fixOpt(this, "names-delimiter", "delimiter");
    34943710            state.fixOpt(this, "name-delimiter", "delimiter");
     
    35073723            state.fixOpt(this, "et-al-subsequent-min", "et-al-subsequent-min");
    35083724            state.fixOpt(this, "et-al-subsequent-use-first", "et-al-subsequent-use-first");
    3509             state.build.area_return = state.build.area;
    3510             state.build.area = "bibliography";
    3511         }
    3512         if (this.tokentype === CSL.END) {
    3513             state.build.area = state.build.area_return;
    35143725        }
    35153726        target.push(this);
     
    35483759    ret.base = CSL.LANG_BASES[langlst[0]];
    35493760    if ("undefined" === typeof ret.base) {
    3550         CSL.debug("Warning: unknown locale "+langstr+", setting to en-US");
    3551         return {base:"en-US", best:"en-US", bare:"en"};
     3761        CSL.debug("Warning: unknown locale "+langstr+", setting fallback to en-US");
     3762        return {base:"en-US", best:langstr, bare:"en"};
    35523763    }
    35533764    if (langlst.length === 1 || langlst[1] === "x") {
     
    35903801        this.locale[lang_out].terms = {};
    35913802        this.locale[lang_out].opts = {};
     3803        this.locale[lang_out].opts["skip-words"] = CSL.SKIP_WORDS;
    35923804        this.locale[lang_out].dates = {};
    35933805    }
     
    36693881            for (attrname in attributes) {
    36703882                if (attributes.hasOwnProperty(attrname)) {
    3671                     if (attributes[attrname] === "true") {
    3672                         this.locale[lang_out].opts[attrname.slice(1)] = true;
    3673                     } else {
    3674                         this.locale[lang_out].opts[attrname.slice(1)] = false;
     3883                    if (attrname === "@punctuation-in-quote") {
     3884                        if (attributes[attrname] === "true") {
     3885                            this.locale[lang_out].opts[attrname.slice(1)] = true;
     3886                        } else {
     3887                            this.locale[lang_out].opts[attrname.slice(1)] = false;
     3888                        }
     3889                    } else if (attrname === "@skip-words") {
     3890                        var skip_words = attributes[attrname].split(/\s+/);
     3891                        this.locale[lang_out].opts[attrname.slice(1)] = skip_words;
    36753892                    }
    36763893                }
     
    37053922            state.fixOpt(this, "et-al-subsequent-min", "et-al-subsequent-min");
    37063923            state.fixOpt(this, "et-al-subsequent-use-first", "et-al-subsequent-use-first");
    3707             state.build.area_return = state.build.area;
    37083924            state.build.area = "citation";
    37093925        }
     
    37203936            }
    37213937            state.citation.srt = new CSL.Registry.Comparifier(state, "citation_sort");
    3722             state.build.area = state.build.area_return;
    37233938        }
    37243939    }
     
    37303945            state.build.date_parts = [];
    37313946            state.build.date_variables = this.variables;
    3732             if (!state.build.sort_flag) {
     3947            if (!state.build.extension) {
    37333948                CSL.Util.substituteStart.call(this, state, target);
    37343949            }
    3735             if (state.build.area.slice(-5) === "_sort") {
     3950            if (state.build.extension) {
    37363951                func = CSL.dateMacroAsSortKey;
    37373952            } else {
     
    37743989                        }
    37753990                        dp = dpx.slice();
    3776                         if (state.tmp.area.slice(-5) !== "_sort" && ("" + Item.volume) === "" + state.tmp.date_object.year && this.dateparts.length === 1 && this.dateparts[0] === "year") {
     3991                        if (!state.tmp.extension && ("" + Item.volume) === "" + state.tmp.date_object.year && this.dateparts.length === 1 && this.dateparts[0] === "year") {
    37773992                            for (key in state.tmp.date_object) {
    37783993                                if (state.tmp.date_object.hasOwnProperty(key)) {
     
    38064021            this.execs.push(func);
    38074022        }
    3808         if (!state.build.sort_flag && (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON)) {
     4023        if (!state.build.extension && (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON)) {
    38094024            func = function (state, Item) {
    38104025                state.output.endTag();
     
    38154030        target.push(this);
    38164031        if (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON) {
    3817             if (!state.build.sort_flag) {
     4032            if (!state.build.extension) {
    38184033                CSL.Util.substituteEnd.call(this, state, target);
    38194034            }
     
    39934208                    value = "" + state.tmp.date_object.season;
    39944209                    if (value && value.match(/^[1-4]$/)) {
    3995                         state.tmp.term_sibling.replace([false, false, true]);
     4210                        state.tmp.group_context.replace([false, false, true]);
    39964211                        state.output.append(state.getTerm(("season-0" + value)), this);
    39974212                    } else if (value) {
     
    40054220                    state.tmp.has_done_year_suffix = true;
    40064221                    num = parseInt(state.registry.registry[Item.id].disambig.year_suffix, 10);
    4007                     number = new CSL.NumericBlob(num, this);
     4222                    number = new CSL.NumericBlob(num, this, Item.id);
    40084223                    this.successor_prefix = state[state.build.area].opt.layout_delimiter;
    40094224                    this.splice_prefix = state[state.build.area].opt.layout_delimiter;
     
    40994314CSL.Node.group = {
    41004315    build: function (state, target) {
    4101         var func, execs, outer_area;
     4316        var func, execs;
    41024317        if (this.tokentype === CSL.START) {
    41034318            CSL.Util.substituteStart.call(this, state, target);
     
    41064321            }
    41074322            func = function (state, Item) {
    4108                 state.tmp.term_sibling.push([false, false, false], CSL.LITERAL);
    4109             };
    4110             this.execs.push(func);
    4111             func = function (state, Item) {
    41124323                state.output.startTag("group", this);
     4324                if (state.tmp.group_context.mystack.length) {
     4325                    state.output.current.value().parent = state.tmp.group_context.value()[4];
     4326                }
     4327                state.tmp.group_context.push([false, false, false, false, state.output.current.value()], CSL.LITERAL);
     4328                if (this.strings.oops) {
     4329                    state.tmp.group_context.value()[3] = this.strings.oops;
     4330                }
    41134331            };
    41144332            execs = [];
     
    41174335            if (this.strings["has-publisher-and-publisher-place"]) {
    41184336                state.build["publisher-special"] = true;
    4119                 outer_area = state.build.area.replace(/_sort$/, "");
    4120                 if ("string" === typeof state[outer_area].opt["name-delimiter"]) {
    4121                     func = function (state, Item) {
    4122                         if (Item.publisher && Item["publisher-place"]) {
    4123                             var publisher_lst = Item.publisher.split(/;\s*/);
    4124                             var publisher_place_lst = Item["publisher-place"].split(/;\s*/);
    4125                             if (publisher_lst.length > 1
    4126                                 && publisher_lst.length === publisher_place_lst.length) {
    4127                                 state.publisherOutput = new CSL.PublisherOutput(state);
    4128                                 state.publisherOutput["publisher-list"] = publisher_lst;
    4129                                 state.publisherOutput["publisher-place-list"] = publisher_place_lst;
    4130                                 state.publisherOutput.group_tok = this;
    4131                             }
     4337                func = function (state, Item) {
     4338                    if (this.strings["subgroup-delimiter"]
     4339                        && Item.publisher && Item["publisher-place"]) {
     4340                        var publisher_lst = Item.publisher.split(/;\s*/);
     4341                        var publisher_place_lst = Item["publisher-place"].split(/;\s*/);
     4342                        if (publisher_lst.length > 1
     4343                            && publisher_lst.length === publisher_place_lst.length) {
     4344                            state.publisherOutput = new CSL.PublisherOutput(state, this);
     4345                            state.publisherOutput["publisher-list"] = publisher_lst;
     4346                            state.publisherOutput["publisher-place-list"] = publisher_place_lst;
    41324347                        }
    4133                     };
    4134                     this.execs.push(func);
    4135                 }
     4348                    }
     4349                };
     4350                this.execs.push(func);
    41364351            }
    41374352        } else {
    41384353            if (state.build["publisher-special"]) {
    41394354                state.build["publisher-special"] = false;
    4140                 outer_area = state.build.area.replace(/_sort$/, "");
    4141                 if ("string" === typeof state[outer_area].opt["name-delimiter"]) {
     4355                if ("string" === typeof state[state.build.root].opt["name-delimiter"]) {
    41424356                    func = function (state, Item) {
    41434357                        if (state.publisherOutput) {
    4144                             state.publisherOutput.name_delimiter = state[outer_area].opt["name-delimiter"];
    4145                             state.publisherOutput.delimiter_precedes_last = state[outer_area].opt["delimiter-precedes-last"];
    4146                             state.publisherOutput.and = state[outer_area].opt.and;
    41474358                            state.publisherOutput.render();
    41484359                            state.publisherOutput = false;
     
    41534364            }
    41544365            func = function (state, Item) {
    4155                 var flag = state.tmp.term_sibling.value();
     4366                var flag = state.tmp.group_context.pop();
    41564367                state.output.endTag();
    4157                 if (!flag[2] && (flag[1] || (!flag[1] && !flag[0]))) {
     4368                var upperflag = state.tmp.group_context.value();
     4369                if (flag[1]) {
     4370                    state.tmp.group_context.value()[1] = true;
     4371                }
     4372                if (flag[2] || (flag[0] && !flag[1])) {
     4373                    state.tmp.group_context.value()[2] = true;
     4374                } else {
    41584375                    if (state.output.current.value().blobs) {
    41594376                        state.output.current.value().blobs.pop();
    41604377                    }
    4161                 }
    4162                 state.tmp.term_sibling.pop();
    4163                 if ((flag[2] || (!flag[1] && flag[0])) && state.tmp.term_sibling.mystack.length > 1) {
    4164                     state.tmp.term_sibling.replace([false, false, true]);
     4378                    if (state.tmp.group_context.value()[3]) {
     4379                        state.output.current.mystack[state.output.current.mystack.length - 2].strings.delimiter = state.tmp.group_context.value()[3];
     4380                    }
    41654381                }
    41664382            };
     
    42294445    build: function (state, target) {
    42304446        if ([CSL.SINGLETON, CSL.START].indexOf(this.tokentype) > -1) {
    4231             if ("string" === typeof state.build.name_delimiter) {
     4447            if ("string" === typeof state.build.name_delimiter && !this.strings.delimiter) {
    42324448                this.strings.delimiter = state.build.name_delimiter;
    42334449            }
    4234             var func = function (state, Item) {
    4235                 var myand, and_default_prefix, and_suffix;
    4236                 if ("text" === this.strings.and) {
    4237                     myand = state.getTerm("and", "long", 0);
    4238                 } else if ("symbol" === this.strings.and) {
    4239                     myand = "&";
    4240                 }
    4241                 if (state.nameOutput.name.and_term) {
    4242                     myand = state.getTerm("and", "long", 0);
    4243                 }
    4244                 if (CSL.STARTSWITH_ROMANESQUE_REGEXP.test(myand)) {
    4245                     and_default_prefix = " ";
    4246                     and_suffix = " ";
    4247                 } else {
    4248                     and_default_prefix = "";
    4249                     and_suffix = "";
    4250                 }
     4450            var myand, and_default_prefix, and_suffix;
     4451            if ("text" === this.strings.and) {
     4452                this.and_term = state.getTerm("and", "long", 0);
     4453            } else if ("symbol" === this.strings.and) {
     4454                this.and_term = "&";
     4455            }
     4456            if ("undefined" === typeof this.and_term && state.build.and_term) {
     4457                this.and_term = state.getTerm("and", "long", 0);
     4458            }
     4459            if (CSL.STARTSWITH_ROMANESQUE_REGEXP.test(this.and_term)) {
     4460                this.and_prefix_single = " ";
     4461                this.and_prefix_multiple = ", ";
     4462                if ("string" === typeof this.strings.delimiter) {
     4463                    this.and_prefix_multiple = this.strings.delimiter;
     4464                }
     4465                this.and_suffix = " ";
     4466            } else {
     4467                this.and_prefix_single = "";
     4468                this.and_prefix_multiple = "";
     4469                this.and_suffix = "";
     4470            }
     4471            if (this.strings["delimiter-precedes-last"] === "always") {
     4472                this.and_prefix_single = this.strings.delimiter;
     4473            } else if (this.strings["delimiter-precedes-last"] === "never") {
     4474                if (this.and_prefix_multiple) {
     4475                    this.and_prefix_multiple = " ";
     4476                }
     4477            }
     4478            func = function (state, Item) {
    42514479                this.and = {};
    4252                 this.and.single = new CSL.Blob(myand);
    4253                 this.and.single.strings.suffix = and_suffix;
    4254                 this.and.multiple = new CSL.Blob(myand);
    4255                 this.and.multiple.strings.suffix = and_suffix;
    4256                 if (this.strings["delimiter-precedes-last"] === "always") {
    4257                     this.and.single.strings.prefix = this.strings.delimiter;
    4258                     this.and.multiple.strings.prefix = this.strings.delimiter;
    4259                 } else if (this.strings["delimiter-precedes-last"] === "contextual") {
    4260                     this.and.single.strings.prefix = and_default_prefix;
    4261                     this.and.multiple.strings.prefix = this.strings.delimiter;
    4262                 } else {
    4263                     this.and.single.strings.prefix = and_default_prefix;
    4264                     this.and.multiple.strings.prefix = and_default_prefix;
     4480                if ("undefined" !== typeof this.and_term) {
     4481                    state.output.append(this.and_term, "empty", true);
     4482                    this.and.single = state.output.pop();
     4483                    this.and.single.strings.prefix = this.and_prefix_single;
     4484                    this.and.single.strings.suffix = this.and_suffix;
     4485                    state.output.append(this.and_term, "empty", true);
     4486                    this.and.multiple = state.output.pop();
     4487                    this.and.multiple.strings.prefix = this.and_prefix_multiple;
     4488                    this.and.multiple.strings.suffix = this.and_suffix;
     4489                } else if ("undefined" !== this.strings.delimiter) {
     4490                    this.and.single = new CSL.Blob(this.strings.delimiter);
     4491                    this.and.single.strings.prefix = "";
     4492                    this.and.single.strings.suffix = "";
     4493                    this.and.multiple = new CSL.Blob(this.strings.delimiter);
     4494                    this.and.multiple.strings.prefix = "";
     4495                    this.and.multiple.strings.suffix = "";
    42654496                }
    42664497                state.nameOutput.institution = this;
     
    43944625                } else if ("title" === variable) {
    43954626                    state.transform.init("empty", "title");
    4396                     state.transform.setTransformLocale("locale-sort");
    43974627                    state.transform.setTransformFallback(true);
    43984628                    func = state.transform.getOutputFunction(this.variables);
     
    44914721                    if (item && item.prefix) {
    44924722                        sp = "";
    4493                         if (item.prefix.match(CSL.ENDSWITH_ROMANESQUE_REGEXP)) {
     4723                        var prefix = item.prefix.replace(/<[^>]+>/g, "").replace(/\s+$/, "").replace(/^\s+/, "");
     4724                        if (prefix.match(CSL.ENDSWITH_ROMANESQUE_REGEXP)) {
    44944725                            sp = " ";
    44954726                        }
    4496                         state.output.append((item.prefix + sp), this);
     4727                        var ignorePredecessor = false;
     4728                        if (CSL.TERMINAL_PUNCTUATION.slice(0,-1).indexOf(prefix.slice(-1)) > -1
     4729                            && prefix[0] != prefix[0].toLowerCase()) {
     4730                            state.tmp.term_predecessor = false;
     4731                            ignorePredecessor = true;
     4732                        }
     4733                        prefix = (item.prefix + sp).replace(/\s+/g, " ")
     4734                        state.output.append(prefix, this, false, ignorePredecessor);
    44974735                    }
    44984736                };
     
    46174855    this.names = names;
    46184856    this.variables = names.variables;
    4619     if (this.nameset_base === 0 && !this._first_creator_variable) {
    4620         this._first_creator_variable = this.variables[0];
    4621     }
    46224857    this.state.tmp.value = [];
    46234858    for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
     
    46304865    this.name = undefined;
    46314866    this.institutionpart = {};
     4867    this.state.tmp.group_context.value()[1] = true;
     4868    if (!this.state.tmp.value.length) {
     4869        return;
     4870    }
     4871    this.state.tmp.group_context.value()[2] = false;
    46324872};
    46334873CSL.NameOutput.prototype.reinit = function (names) {
    4634     if (!this._hasValues()) {
     4874    if (this.state.tmp.can_substitute.value()) {
    46354875        this.nameset_offset = 0;
    46364876        this.variables = names.variables;
    4637     }
    4638 };
    4639 CSL.NameOutput.prototype._hasValues = function () {
    4640     for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
    4641         var v = this.variables[i];
    4642         if (this.Item[v]) {
    4643             return true;
    4644         }
    4645     }
    4646     return false;
     4877        var oldval = this.state.tmp.value.slice();
     4878        this.state.tmp.value = [];
     4879        for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
     4880            if (this.Item[this.variables[i]] && this.Item[this.variables[i]].length) {
     4881                this.state.tmp.value = this.state.tmp.value.concat(this.Item[this.variables[i]]);
     4882            }
     4883        }
     4884        if (this.state.tmp.value.length) {
     4885            this.state.tmp.can_substitute.replace(false, CSL.LITERAL);
     4886        }
     4887        this.state.tmp.value = oldval;
     4888    }
    46474889};
    46484890CSL.NameOutput.prototype.outputNames = function () {
    46494891    var i, ilen;
    46504892    var variables = this.variables;
     4893    if (this.institution.and) {
     4894        if (!this.institution.and.single.blobs && !this.institution.and.single.blobs.length) {
     4895            this.institution.and.single.blobs = this.name.and.single.blobs;
     4896        }
     4897        if (!this.institution.and.single.blobs && !this.institution.and.multiple.blobs.length) {
     4898            this.institution.and.multiple.blobs = this.name.and.multiple.blobs;
     4899        }
     4900    }
    46514901    this.variable_offset = {};
    46524902    if (this.family) {
     
    46754925    this.constrainNames();
    46764926    if (this.name.strings.form === "count") {
    4677         if (this.state.tmp.area.slice(-5) === "_sort" || this.names_count != 0) {
     4927        if (this.state.tmp.extension || this.names_count != 0) {
    46784928            this.state.output.append(this.names_count, "empty");
    4679         } else {
    4680             this.state.tmp.term_sibling.value()[1] = true;
    4681             this.state.tmp.term_sibling.value()[2] = false;
     4929            this.state.tmp.group_context.value()[2] = true;
    46824930        }
    46834931        return;
     
    47224970    this.state.output.append(blob, this.names);
    47234971    this.state.tmp.name_node.top = this.state.output.current.value();
    4724     this.state.tmp.name_node.string = this.state.output.string(this.state, this.state.tmp.name_node.top.blobs, false);
     4972    var oldSuppressDecorations = this.state.tmp.suppress_decorations;
     4973    this.state.tmp.suppress_decorations = true;
     4974    var lastBlob = this.state.tmp.name_node.top.blobs.pop();
     4975    var name_node_string = this.state.output.string(this.state, lastBlob.blobs, false);
     4976    this.state.tmp.name_node.top.blobs.push(lastBlob);
     4977    if (name_node_string) {
     4978        this.state.tmp.name_node.string = name_node_string;
     4979    }
     4980    this.state.tmp.suppress_decorations = oldSuppressDecorations;
    47254981    if (this.state.tmp.name_node.string && !this.state.tmp.first_name_string) {
    47264982        this.state.tmp.first_name_string = this.state.tmp.name_node.string;
    47274983    }
    4728     if ("undefined" === typeof this.Item.type) {
     4984    if ("classic" === this.Item.type) {
    47294985        var author_title = [];
    4730         if (this.state.tmp.name_node.string) {
    4731             author_title.push(this.state.tmp.name_node.string);
     4986        if (this.state.tmp.first_name_string) {
     4987            author_title.push(this.state.tmp.first_name_string);
    47324988        }
    47334989        if (this.Item.title) {
     
    47354991        }
    47364992        author_title = author_title.join(", ");
    4737         if (author_title) {
    4738             this.state.transform.loadAbbreviation("classic", author_title);
    4739             if (this.state.transform.abbrevs.classic[author_title]) {
     4993        if (author_title && this.state.sys.getAbbreviation) {
     4994            this.state.transform.loadAbbreviation("default", "classic", author_title);
     4995            if (this.state.transform.abbrevs["default"].classic[author_title]) {
    47404996                this.state.tmp.done_vars.push("title");
    4741                 this.state.output.append(this.state.transform.abbrevs.classic[author_title], "empty", true);
     4997                this.state.output.append(this.state.transform.abbrevs["default"].classic[author_title], "empty", true);
    47424998                blob = this.state.output.pop();
    4743                 this.state.tmp.name_node.top.blobs = [blob];
    4744             }
    4745         }
    4746     }
    4747     if (this.Item.type === "personal_communication") {
     4999                this.state.tmp.name_node.top.blobs.pop();
     5000                this.state.tmp.name_node.top.blobs.push(blob);
     5001            }
     5002        }
     5003    }
     5004    if (this.Item.type === "personal_communication" || this.Item.type === "interview") {
    47485005        var author = "";
    4749         if (this.state.tmp.name_node.string) {
    4750             author = this.state.tmp.name_node.string;
    4751         }
    4752         if (author) {
    4753             this.state.transform.loadAbbreviation("nickname", author);
    4754         }
    4755         if (this.state.transform.abbrevs.nickname[author]) {
    4756             this.state.output.append(this.state.transform.abbrevs.nickname[author], "empty", true)
    4757             blob = this.state.output.pop();
    4758             this.state.tmp.name_node.top.blobs = [blob];
     5006        author = this.state.tmp.name_node.string;
     5007        if (author && this.state.sys.getAbbreviation && !(this.item && this.item["suppress-author"])) {
     5008            this.state.transform.loadAbbreviation("default", "nickname", author);
     5009            var myLocalName = this.state.transform.abbrevs["default"].nickname[author];
     5010            if (myLocalName) {
     5011                if (myLocalName === "{suppress}") {
     5012                    this.state.tmp.name_node.top.blobs.pop();
     5013                    this.state.tmp.group_context.value()[2] = false;
     5014                } else {
     5015                    this.state.output.append(myLocalName, "empty", true)
     5016                    blob = this.state.output.pop();
     5017                    this.state.tmp.name_node.top.blobs = [blob];
     5018                }
     5019            }
    47595020        }
    47605021    }
     
    48165077CSL.NameOutput.prototype._collapseAuthor = function () {
    48175078    var myqueue, mystr, oldchars;
     5079    if (this.nameset_base === 0 && this.Item[this.variables[0]] && !this._first_creator_variable) {
     5080        this._first_creator_variable = this.variables[0];
     5081    }
    48185082    if ((this.item && this.item["suppress-author"] && this._first_creator_variable == this.variables[0])
    48195083        || (this.state[this.state.tmp.area].opt.collapse
     
    48855149    }
    48865150    if (this.etal_min === 1 && this.etal_use_first === 1
    4887         && !(this.state.tmp.area === "bibliography_sort"
    4888              || this.state.tmp.area === "citation_sort"
     5151        && !(this.state.tmp.extension
    48895152             || this.state.tmp.just_looking)) {
    48905153        chopvar = v;
     
    49405203        }
    49415204    }
    4942     for (v in this.freeters) {
    4943         this._transformNameset(this.freeters[v]);
    4944     }
    4945     for (v in this.persons) {
    4946         for (i = 0, ilen = this.persons[v].length; i < ilen; i += 1) {
    4947             this._transformNameset(this.persons[v][i]);
    4948         }
    4949         this._transformNameset(this.institutions[v]);
    4950         for (i = 0, ilen = this.institutions[v].length; i < ilen; i += 1) {
    4951             this.state.transform.loadAbbreviation("institution", this.institutions[v][i].literal);
    4952         }
    4953     }
    49545205    for (i = 0, ilen = this.variables.length; i < ilen; i += 1) {
    49555206        if (this.institutions[v].length) {
     
    49595210            if (this.persons[v][i].length) {
    49605211                this.nameset_offset += 1;
    4961             }
    4962             this.institutions[v][i] = this._splitInstitution(this.institutions[v][i], v, i);
    4963         }
    4964     }
    4965     for (v in this.institutions) {
    4966         for (i = 0, ilen = this.institutions[v].length; i < ilen; i += 1) {
    4967             var long_form = this.institutions[v][i]["long"];
    4968             if (this.state.transform.abbrevs.institution[long_form.join(", ")]) {
    4969                 var short_form = this.state.transform.abbrevs.institution[long_form.join(", ")].split(", ");
    4970                 if (short_form.length === long_form.length) {
    4971                     this.institutions[v][i]["short"] = short_form;
    4972                 }
    49735212            }
    49745213        }
     
    50835322    }
    50845323};
    5085 CSL.NameOutput.prototype._splitInstitution = function (value, v, i) {
    5086     var ret = {};
    5087     ret["long"] = this._trimInstitution(value.literal.split(/\s*\|\s*/), v, i);
    5088     var str = value.literal;
    5089     if (str) {
    5090         if (str.slice(0,1) === '"' && str.slice(-1) === '"') {
    5091             ret["short"] = [str.slice(1,-1)];
    5092         } else {
    5093             ret["short"] = this._trimInstitution(str.split(/\s*\|\s*/), v, i);
    5094         }
    5095     } else {
    5096         ret["short"] = false;
    5097     }
    5098     return ret;
    5099 };
    5100 CSL.NameOutput.prototype._trimInstitution = function (subunits, v, i) {
    5101     var s;
    5102     var use_first = this.institution.strings["use-first"];
    5103     if (!use_first) {
    5104         if (this.persons[v][i].length === 0) {
    5105             use_first = this.institution.strings["substitute-use-first"];
    5106         }
    5107     }
    5108     if (!use_first) {
    5109         use_first = 0;
    5110     }
    5111     var append_last = this.institution.strings["use-last"];
    5112     if (!append_last) {
    5113         append_last = 0;
    5114     }
    5115     if (use_first || append_last) {
    5116         s = subunits.slice();
    5117         subunits = subunits.slice(0, use_first);
    5118         s = s.slice(use_first);
    5119         if (append_last) {
    5120             if (append_last > s.length) {
    5121                 append_last = s.length;
    5122             }
    5123             if (append_last) {
    5124                 subunits = subunits.concat(s.slice((s.length - append_last)));
    5125             }
    5126         }
    5127     }
    5128     return subunits;
    5129 };
    51305324CSL.NameOutput.prototype.joinPersons = function (blobs, pos) {
    51315325    var ret;
     
    51345328    } else if (this.etal_spec[pos] === 2) {
    51355329        ret = this._joinEllipsis(blobs, "name");
     5330    } else if (!this.state.tmp.sort_key_flag) {
     5331        ret = this._joinAnd(blobs, "name");
    51365332    } else {
    5137         ret = this._joinAnd(blobs, "name");
     5333        ret = this._join(blobs, " ");
    51385334    }
    51395335    return ret;
     
    54865682        for (var j = 0, jlen = this.institutions[v].length; j < jlen; j += 1) {
    54875683            var institution, institution_short, institution_long, short_style, long_style;
     5684            var name = this.institutions[v][j];
     5685            var j, ret, optLangTag, jlen, key, localesets;
     5686            if (this.state.tmp.extension) {
     5687                localesets = ["sort"];
     5688            } else if (name.isInstitution) {
     5689                localesets = this.state.opt['cite-lang-prefs'].institutions;
     5690            } else {
     5691                localesets = this.state.opt['cite-lang-prefs'].persons;
     5692            }
     5693            slot = {primary:false,secondary:false,tertiary:false};
     5694            if (localesets) {
     5695                var slotnames = ["primary", "secondary", "tertiary"];
     5696                for (var k = 0, klen = slotnames.length; k < klen; k += 1) {
     5697                    if (localesets.length - 1 <  j) {
     5698                        break;
     5699                    }
     5700                    if (localesets[k]) {
     5701                        slot[slotnames[k]] = 'locale-' + localesets[k];
     5702                    }
     5703                }
     5704            } else {
     5705                slot.primary = 'locale-translat';
     5706            }
     5707            if (this.state.tmp.area !== "bibliography"
     5708                && !(this.state.tmp.area === "citation"
     5709                     && this.state.opt.xclass === "note"
     5710                     && this.item && !this.item.position)) {
     5711                slot.secondary = false;
     5712                slot.tertiary = false;
     5713            }
     5714            var res;
     5715            res = this.getName(name, slot.primary, true);
     5716            var primary = res.name;
     5717            var usedOrig = res.usedOrig;
     5718            if (primary) {
     5719                primary = this.fixupInstitution(primary, v, j);
     5720            }
     5721            secondary = false;
     5722            if (slot.secondary) {
     5723                res = this.getName(name, slot.secondary, false, usedOrig);
     5724                secondary = res.name;
     5725                usedOrig = res.usedOrig;
     5726                if (secondary) {
     5727                    secondary = this.fixupInstitution(secondary, v, j);
     5728                }
     5729            }
     5730            tertiary = false;
     5731            if (slot.tertiary) {
     5732                res = this.getName(name, slot.tertiary, false, usedOrig);
     5733                tertiary = res.name;
     5734                if (tertiary) {
     5735                    tertiary = this.fixupInstitution(tertiary, v, j);
     5736                }
     5737            }
    54885738            switch (this.institution.strings["institution-parts"]) {
    54895739            case "short":
    5490                 if (this.institutions[v][j]["short"].length) {
     5740                if (primary["short"].length) {
    54915741                    short_style = this._getShortStyle();
    5492                     institution = [this._renderOneInstitutionPart(this.institutions[v][j]["short"], short_style)];
     5742                    institution = [this._renderOneInstitutionPart(primary["short"], short_style)];
    54935743                } else {
    5494                     long_style = this._getLongStyle(v, j);
    5495                     institution = [this._renderOneInstitutionPart(this.institutions[v][j]["long"], long_style)];
     5744                    long_style = this._getLongStyle(primary, v, j);
     5745                    institution = [this._renderOneInstitutionPart(primary["long"], long_style)];
    54965746                }
    54975747                break;
    54985748            case "short-long":
    5499                 long_style = this._getLongStyle(v, j);
     5749                long_style = this._getLongStyle(primary, v, j);
    55005750                short_style = this._getShortStyle();
    5501                 institution_short = this._renderOneInstitutionPart(this.institutions[v][j]["short"], short_style);
    5502                 institution_long = this._renderOneInstitutionPart(this.institutions[v][j]["long"], long_style);
     5751                institution_short = this._renderOneInstitutionPart(primary["short"], short_style);
     5752                institution_long = this._composeOneInstitutionPart([primary, secondary, tertiary], long_style);
    55035753                institution = [institution_short, institution_long];
    55045754                break;
    55055755            case "long-short":
    5506                 long_style = this._getLongStyle(v, j);
     5756                long_style = this._getLongStyle(primary, v, j);
    55075757                short_style = this._getShortStyle();
    5508                 institution_short = this._renderOneInstitutionPart(this.institutions[v][j]["short"], short_style);
    5509                 institution_long = this._renderOneInstitutionPart(this.institutions[v][j]["long"], long_style);
     5758                institution_short = this._renderOneInstitutionPart(primary["short"], short_style);
     5759                institution_long = this._composeOneInstitutionPart([primary, secondary, tertiary], long_style, true);
    55105760                institution = [institution_long, institution_short];
    55115761                break;
    55125762            default:
    5513                 long_style = this._getLongStyle(v, j);
    5514                 institution = [this._renderOneInstitutionPart(this.institutions[v][j]["long"], long_style)];
     5763                long_style = this._getLongStyle(primary, v, j);
     5764                institution = [this._composeOneInstitutionPart([primary, secondary, tertiary], long_style)];
    55155765                break;
    55165766            }
     
    55195769    }
    55205770};
     5771CSL.NameOutput.prototype._composeOneInstitutionPart = function (names, style) {
     5772    var primary = false, secondary = false, tertiary = false;
     5773    if (names[0]) {
     5774        primary = this._renderOneInstitutionPart(names[0]["long"], style);
     5775    }
     5776    if (names[1]) {
     5777        secondary = this._renderOneInstitutionPart(names[1]["long"], style);
     5778    }
     5779    if (names[2]) {
     5780        tertiary = this._renderOneInstitutionPart(names[2]["long"], style);
     5781    }
     5782    var institutionblob;
     5783    if (secondary || tertiary) {
     5784        var multiblob = this._join([secondary, tertiary], ", ");
     5785        var group_tok = new CSL.Token();
     5786        group_tok.strings.prefix = " [";
     5787        group_tok.strings.suffix = "]";
     5788        this.state.output.openLevel(group_tok);
     5789        this.state.output.append(multiblob);
     5790        this.state.output.closeLevel();
     5791        multiblob = this.state.output.pop();
     5792        institutionblob = this._join([primary, multiblob], "");
     5793    } else {
     5794        institutionblob = primary;
     5795    }
     5796    return institutionblob;
     5797}
    55215798CSL.NameOutput.prototype._renderOneInstitutionPart = function (blobs, style) {
    55225799    for (var i = 0, ilen = blobs.length; i < ilen; i += 1) {
    55235800        if (blobs[i]) {
    5524             this.state.output.append(blobs[i], style, true);
     5801            var str = blobs[i];
     5802            if (this.state.tmp.strip_periods) {
     5803                str = str.replace(/\./g, "");
     5804            } else {
     5805                for (var j = 0, jlen = style.decorations.length; j < jlen; j += 1) {
     5806                    if ("@strip-periods" === style.decorations[j][0] && "true" === style.decorations[j][1]) {
     5807                        str = str.replace(/\./g, "");
     5808                        break;
     5809                    }
     5810                }
     5811            }
     5812            this.state.tmp.group_context.value()[2] = true;
     5813            this.state.tmp.can_substitute.replace(false, CSL.LITERAL);
     5814            this.state.output.append(str, style, true);
    55255815            blobs[i] = this.state.output.pop();
    55265816        }
    55275817    }
    5528     return this._join(blobs, this.name.strings.delimiter);
     5818    if ("undefined" === typeof this.institution.strings["part-separator"]) {
     5819        this.institution.strings["part-separator"] = this.name.strings.delimiter;
     5820    }
     5821    return this._join(blobs, this.institution.strings["part-separator"]);
    55295822};
    55305823CSL.NameOutput.prototype._renderPersonalNames = function (values, pos) {
     
    55335826        var names = [];
    55345827        for (var i = 0, ilen = values.length; i < ilen; i += 1) {
    5535             var val = values[i];
    5536             names.push(this._renderOnePersonalName(val, pos, i));
     5828            var name = values[i];
     5829            var j, ret, optLangTag, jlen, key, localesets;
     5830            if (this.state.tmp.extension) {
     5831                localesets = ["sort"];
     5832            } else if (name.isInstitution) {
     5833                localesets = this.state.opt['cite-lang-prefs'].institutions;
     5834            } else {
     5835                localesets = this.state.opt['cite-lang-prefs'].persons;
     5836            }
     5837            slot = {primary:false,secondary:false,tertiary:false};
     5838            if (localesets) {
     5839                var slotnames = ["primary", "secondary", "tertiary"];
     5840                for (var j = 0, jlen = slotnames.length; j < jlen; j += 1) {
     5841                    if (localesets.length - 1 <  j) {
     5842                        break;
     5843                    }
     5844                    slot[slotnames[j]] = 'locale-' + localesets[j];
     5845                }
     5846            } else {
     5847                slot.primary = 'locale-translat';
     5848            }
     5849            if (this.state.tmp.sort_key_flag || (this.state.tmp.area !== "bibliography"
     5850                && !(this.state.tmp.area === "citation"
     5851                     && this.state.opt.xclass === "note"
     5852                     && this.item && !this.item.position))) {
     5853                slot.secondary = false;
     5854                slot.tertiary = false;
     5855            }
     5856            var res = this.getName(name, slot.primary, true);
     5857            var primary = this._renderOnePersonalName(res.name, pos, i);
     5858            secondary = false;
     5859            if (slot.secondary) {
     5860                res = this.getName(name, slot.secondary, false, res.usedOrig);
     5861                if (res.name) {
     5862                    secondary = this._renderOnePersonalName(res.name, pos, i);
     5863                }
     5864            }
     5865            tertiary = false;
     5866            if (slot.tertiary) {
     5867                res = this.getName(name, slot.tertiary, false, res.usedOrig);
     5868                if (res.name) {
     5869                    tertiary = this._renderOnePersonalName(res.name, pos, i);
     5870                }
     5871            }
     5872            var personblob;
     5873            if (secondary || tertiary) {
     5874                var multiblob = this._join([secondary, tertiary], ", ");
     5875                var group_tok = new CSL.Token();
     5876                group_tok.strings.prefix = " [";
     5877                group_tok.strings.suffix = "]";
     5878                this.state.output.openLevel(group_tok);
     5879                this.state.output.append(multiblob);
     5880                this.state.output.closeLevel();
     5881                multiblob = this.state.output.pop();
     5882                personblob = this._join([primary, multiblob], "");
     5883            } else {
     5884                personblob = primary;
     5885            }
     5886            names.push(personblob);
    55375887        }
    55385888        ret = this.joinPersons(names, pos);
     
    55715921        if (this.state.opt["demote-non-dropping-particle"] === "never") {
    55725922            first = this._join([non_dropping_particle, family, dropping_particle], " ");
    5573             merged = this._join([first, given], sort_sep);
    5574             blob = this._join([merged, suffix], suffix_sep);
     5923            merged = this._join([first, given], " ");
     5924            blob = this._join([merged, suffix], " ");
    55755925        } else {
    55765926            second = this._join([given, dropping_particle, non_dropping_particle], " ");
    5577             merged = this._join([family, second], sort_sep);
    5578             blob = this._join([merged, suffix], suffix_sep);
     5927            merged = this._join([family, second], " ");
     5928            blob = this._join([merged, suffix], " ");
    55795929        }
    55805930    } else if (this.name.strings["name-as-sort-order"] === "all" || (this.name.strings["name-as-sort-order"] === "first" && i === 0)) {
     5931        if (["Lord", "Lady"].indexOf(name.given) > -1) {
     5932            sort_sep = ", ";
     5933        }
    55815934        if (["always", "display-and-sort"].indexOf(this.state.opt["demote-non-dropping-particle"]) > -1) {
    55825935            second = this._join([given, dropping_particle], (name["comma-dropping-particle"] + " "));
     
    56285981        blob = this._join([given, second], (name["comma-dropping-particle"] + " "));
    56295982    }
     5983    this.state.tmp.group_context.value()[2] = true;
     5984    this.state.tmp.can_substitute.replace(false, CSL.LITERAL);
    56305985    this.state.tmp.name_node.children.push(blob);
    56315986    return blob;
     
    56425997        literal:value.literal,
    56435998        family:value.family,
     5999        isInstitution:value.isInstitution,
    56446000        given:value.given,
    56456001        suffix:value.suffix,
     
    56506006        "parse-names":value["parse-names"],
    56516007        "comma-dropping-particle": "",
    5652         block_initialize:value.block_initialize
     6008        block_initialize:value.block_initialize,
     6009        multi:value.multi
    56536010    };
    56546011    this._parseName(name);
    56556012    return name;
    5656 };
    5657 CSL.NameOutput.prototype._transformNameset = function (nameset) {
    5658     for (var i = 0, ilen = nameset.length; i < ilen; i += 1) {
    5659         nameset[i] = this.state.transform.name(this.state, nameset[i], this.state.opt["locale-pri"]);
    5660         nameset[i] = this._normalizeNameInput(nameset[i]);
    5661     }
    56626013};
    56636014CSL.NameOutput.prototype._stripPeriods = function (tokname, str) {
     
    57366087    return false;
    57376088};
    5738 CSL.NameOutput.prototype._getLongStyle = function (v, i) {
     6089CSL.NameOutput.prototype._getLongStyle = function (name, v, i) {
    57396090    var long_style, short_style;
    5740     if (this.institutions[v][i]["short"].length) {
     6091    if (name["short"].length) {
    57416092        if (this.institutionpart["long-with-short"]) {
    57426093            long_style = this.institutionpart["long-with-short"];
     
    58146165    }
    58156166};
     6167CSL.NameOutput.prototype.getName = function (name, slotLocaleset, fallback, stopOrig) {
     6168    if (stopOrig && slotLocaleset === 'locale-orig') {
     6169        return {name:false,usedOrig:stopOrig};
     6170    }
     6171    if (!name.family) {
     6172        name.family = "";
     6173    }
     6174    if (!name.given) {
     6175        name.given = "";
     6176    }
     6177    var static_ordering_freshcheck = false;
     6178    var block_initialize = false;
     6179    var transliterated = false;
     6180    var static_ordering_val = this.getStaticOrder(name);
     6181    var foundTag = true;
     6182    if (slotLocaleset !== 'locale-orig') {
     6183        foundTag = false;
     6184        if (name.multi) {
     6185            var langTags = this.state.opt[slotLocaleset]
     6186            for (i = 0, ilen = langTags.length; i < ilen; i += 1) {
     6187                langTag = langTags[i];
     6188                if (name.multi._key[langTag]) {
     6189                    foundTag = true;
     6190                    name = name.multi._key[langTag];
     6191                    transliterated = true;
     6192                    if (!this.state.opt['locale-use-original-name-format'] && false) {
     6193                        static_ordering_freshcheck = true;
     6194                    } else {
     6195                        if ((name.family.replace('"','','g') + name.given).match(CSL.ROMANESQUE_REGEXP)) {
     6196                            block_initialize = true;
     6197                        }
     6198                    }
     6199                    break;
     6200                }
     6201            }
     6202        }
     6203    }
     6204    if (!fallback && !foundTag) {
     6205        return {name:false,usedOrig:stopOrig};
     6206    }
     6207    if (!name.family) {
     6208        name.family = "";
     6209    }
     6210    if (!name.given) {
     6211        name.given = "";
     6212    }
     6213    name = {
     6214        family:name.family,
     6215        given:name.given,
     6216        "non-dropping-particle":name["non-dropping-particle"],
     6217        "dropping-particle":name["dropping-particle"],
     6218        suffix:name.suffix,
     6219        "static-ordering":static_ordering_val,
     6220        "parse-names":name["parse-names"],
     6221        "comma-suffix":name["comma-suffix"],
     6222        "comma-dropping-particle":name["comma-dropping-particle"],
     6223        transliterated:transliterated,
     6224        block_initialize:block_initialize,
     6225        literal:name.literal,
     6226        isInstitution:name.isInstitution,
     6227    };
     6228    if (static_ordering_freshcheck &&
     6229        !this.getStaticOrder(name, true)) {
     6230        name["static-ordering"] = false;
     6231    }
     6232    if (!name.literal && (!name.given && name.family && name.isInstitution)) {
     6233        name.literal = name.family;
     6234    }
     6235    if (name.literal) {
     6236        delete name.family;
     6237        delete name.given;
     6238    }
     6239    name = this._normalizeNameInput(name);
     6240    var usedOrig;
     6241    if (stopOrig) {
     6242        usedOrig = stopOrig;
     6243    } else {
     6244        usedOrig = !foundTag;
     6245    }
     6246    return {name:name,usedOrig:usedOrig};
     6247}
     6248CSL.NameOutput.prototype.fixupInstitution = function (name, varname, listpos) {
     6249    name = this._splitInstitution(name, varname, listpos);
     6250    if (this.institution.strings["reverse-order"]) {
     6251        name["long"].reverse();
     6252    }
     6253    var long_form = name["long"];
     6254    var short_form = long_form.slice();
     6255    if (this.state.sys.getAbbreviation) {
     6256        var jurisdiction = this.Item.jurisdiction;
     6257        for (var j = 0, jlen = long_form.length; j < jlen; j += 1) {
     6258            jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", long_form[j]);
     6259            if (this.state.transform.abbrevs[jurisdiction]["institution-part"][long_form[j]]) {
     6260                short_form[j] = this.state.transform.abbrevs[jurisdiction]["institution-part"][long_form[j]];
     6261            }
     6262        }
     6263    }
     6264    name["short"] = short_form;
     6265    return name;
     6266}
     6267CSL.NameOutput.prototype.getStaticOrder = function (name, refresh) {
     6268    var static_ordering_val = false;
     6269    if (!refresh && name["static-ordering"]) {
     6270        static_ordering_val = true;
     6271    } else if (!(name.family.replace('"', '', 'g') + name.given).match(CSL.ROMANESQUE_REGEXP)) {
     6272        static_ordering_val = true;
     6273    } else if (name.multi && name.multi.main && name.multi.main.slice(0,2) == 'vn') {
     6274        static_ordering_val = true;
     6275    } else {
     6276        if (this.state.opt['auto-vietnamese-names']
     6277            && (CSL.VIETNAMESE_NAMES.exec(name.family + " " + name.given)
     6278                && CSL.VIETNAMESE_SPECIALS.exec(name.family + name.given))) {
     6279            static_ordering_val = true;
     6280        }
     6281    }
     6282    return static_ordering_val;
     6283}
     6284CSL.NameOutput.prototype._splitInstitution = function (value, v, i) {
     6285    var ret = {};
     6286    var splitInstitution = value.literal.replace(/\s*\|\s*/g, "|");
     6287    splitInstitution = splitInstitution.split("|");
     6288    if (this.institution.strings.form === "short" && this.state.sys.getAbbreviation) {
     6289        var jurisdiction = this.Item.jurisdiction;
     6290        for (var j = splitInstitution.length; j > 0; j += -1) {
     6291            var str = splitInstitution.slice(0, j).join("|");
     6292            jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-entire", str);
     6293            if (this.state.transform.abbrevs[jurisdiction]["institution-entire"][str]) {
     6294                var splitLst = this.state.transform.abbrevs[jurisdiction]["institution-entire"][str];
     6295                var splitSplitLst = splitLst.split(/>>[0-9]{4}>>/);
     6296                var m = splitLst.match(/>>([0-9]{4})>>/);
     6297                splitLst = splitSplitLst.pop();
     6298                if (splitSplitLst.length > 0 && this.Item.issued && this.Item.issued.year) {
     6299                    for (var k=m.length - 1; k > 0; k += -1) {
     6300                        if (parseInt(this.Item.issued.year, 10) >= parseInt(m[k], 10)) {
     6301                            break;
     6302                        }
     6303                        splitLst = splitSplitLst.pop();
     6304                    }
     6305                }
     6306                splitLst = splitLst.replace(/\s*\|\s*/g, "|");
     6307                splitLst = splitLst.split("|");
     6308                splitInstitution = splitLst.concat(splitInstitution.slice(j));
     6309            }
     6310        }
     6311    }
     6312    splitInstitution.reverse();
     6313    ret["long"] = this._trimInstitution(splitInstitution, v, i);
     6314    return ret;
     6315};
     6316CSL.NameOutput.prototype._trimInstitution = function (subunits, v, i) {
     6317    var use_first = false;
     6318    var append_last = false;
     6319    var stop_last = false;
     6320    var s = subunits.slice();
     6321    if (this.institution) {
     6322        if ("undefined" !== typeof this.institution.strings["use-first"]) {
     6323            use_first = this.institution.strings["use-first"];
     6324        }
     6325        if ("undefined" !== typeof this.institution.strings["stop-last"]) {
     6326            s = s.slice(0, this.institution.strings["stop-last"]);
     6327            subunits = subunits.slice(0, this.institution.strings["stop-last"]);
     6328        }
     6329        if ("undefined" !== typeof this.institution.strings["use-last"]) {
     6330            append_last = this.institution.strings["use-last"];
     6331        }
     6332    }
     6333    if (false === use_first) {
     6334        if (this.persons[v].length === 0) {
     6335            use_first = this.institution.strings["substitute-use-first"];
     6336        }
     6337        if (!use_first) {
     6338            use_first = 0;
     6339        }
     6340    }
     6341    if (false === append_last) {
     6342        if (!use_first) {
     6343            append_last = subunits.length;
     6344        } else {
     6345            append_last = 0;
     6346        }
     6347    }
     6348    if (use_first > subunits.length - append_last) {
     6349        use_first = subunits.length - append_last;
     6350    }
     6351    if (stop_last) {
     6352        append_last = 0;
     6353    }
     6354    subunits = subunits.slice(0, use_first);
     6355    s = s.slice(use_first);
     6356    if (append_last) {
     6357        if (append_last > s.length) {
     6358            append_last = s.length;
     6359        }
     6360        if (append_last) {
     6361            subunits = subunits.concat(s.slice((s.length - append_last)));
     6362        }
     6363    }
     6364    return subunits;
     6365};
    58166366CSL.NameOutput.prototype.setEtAlParameters = function () {
    58176367    var i, ilen, j, jlen;
     
    58676417        myterm = node.strings.term;
    58686418    }
    5869     var plural = 0;
    5870     if ("locator" === node.strings.term) {
    5871         if (item && item.locator) {
    5872             if (state.opt.development_extensions.locator_parsing_for_plurals) {
    5873                 if (!state.tmp.shadow_numbers.locator) {
    5874                     state.processNumber(false, item, "locator");
    5875                 }
    5876                 plural = state.tmp.shadow_numbers.locator.plural;
    5877             } else {
    5878                 plural = CSL.evaluateStringPluralism(item.locator);
    5879             }
    5880         }
    5881     } else if (["page", "page-first"].indexOf(node.variables[0]) > -1) {
    5882         plural = CSL.evaluateStringPluralism(Item[myterm]);
    5883     } else {
    5884         if (!state.tmp.shadow_numbers[myterm]) {
    5885             state.processNumber(false, Item, myterm);
    5886         }
    5887         plural = state.tmp.shadow_numbers[myterm].plural;
     6419    var plural = node.strings.plural;
     6420    if ("number" !== typeof plural) {
     6421        if ("locator" === node.strings.term) {
     6422            if (item && item.locator) {
     6423                if (state.opt.development_extensions.locator_parsing_for_plurals) {
     6424                    if (!state.tmp.shadow_numbers.locator) {
     6425                        state.processNumber(false, item, "locator");
     6426                    }
     6427                    plural = state.tmp.shadow_numbers.locator.plural;
     6428                } else {
     6429                    plural = CSL.evaluateStringPluralism(item.locator);
     6430                }
     6431            }
     6432        } else if (["page", "page-first"].indexOf(node.variables[0]) > -1) {
     6433            plural = CSL.evaluateStringPluralism(Item[myterm]);
     6434        } else {
     6435            if (!state.tmp.shadow_numbers[myterm]) {
     6436                state.processNumber(false, Item, myterm);
     6437            }
     6438            plural = state.tmp.shadow_numbers[myterm].plural;
     6439        }
    58886440    }
    58896441    return CSL.castLabel(state, node, myterm, plural);
    58906442};
    58916443CSL.evaluateStringPluralism = function (str) {
    5892     if (str && str.match(/(?:[0-9],\s*[0-9]|\s+and\s+|&|[0-9]\s*[\-\u2013]\s*[0-9])/)) {
    5893         return 1;
    5894     } else {
    5895         return 0;
    5896     }
     6444    if (str) {
     6445        var m = str.match(/(?:[0-9],\s*[0-9]|\s+and\s+|&|([0-9]+)\s*[\-\u2013]\s*([0-9]+))/)
     6446        if (m && (!m[1] || parseInt(m[1]) < parseInt(m[2]))) {
     6447            return 1
     6448        }
     6449    }
     6450    return 0;
    58976451};
    58986452CSL.castLabel = function (state, node, term, plural, mode) {
     
    59106464    return ret;
    59116465};
    5912 CSL.PublisherOutput = function (state) {
     6466CSL.PublisherOutput = function (state, group_tok) {
    59136467    this.state = state;
     6468    this.group_tok = group_tok;
    59146469    this.varlist = [];
    59156470};
     
    59236478CSL.PublisherOutput.prototype.composeAndBlob = function () {
    59246479    this.and_blob = {};
    5925     var and_term;
    5926     if (this.and === "text") {
     6480    var and_term = false;
     6481    if (this.group_tok.strings.and === "text") {
    59276482        and_term = this.state.getTerm("and");
    5928     } else if (this.and === "symbol") {
     6483    } else if (this.group_tok.strings.and === "symbol") {
    59296484        and_term = "&";
    59306485    }
     
    59346489    this.state.output.append(and_term, tok, true);
    59356490    var no_delim = this.state.output.pop();
    5936     tok.strings.prefix = this.name_delimiter;
     6491    tok.strings.prefix = this.group_tok.strings["subgroup-delimiter"];
    59376492    this.state.output.append(and_term, tok, true);
    59386493    var with_delim = this.state.output.pop();
    5939     if (this.delimiter_precedes_last === "always") {
    5940         this.and_blob.single = with_delim;
    5941     } else if (this.delimiter_precedes_last === "never") {
    5942         this.and_blob.single = no_delim;
    5943         this.and_blob.multiple = no_delim;
    5944     } else {
    5945         this.and_blob.single = no_delim;
    5946         this.and_blob.multiple = with_delim;
     6494    this.and_blob.single = false;
     6495    this.and_blob.multiple = false;
     6496    if (and_term) {
     6497        if (this.group_tok.strings["subgroup-delimiter-precedes-last"] === "always") {
     6498            this.and_blob.single = with_delim;
     6499        } else if (this.group_tok.strings["subgroup-delimiter-precedes-last"] === "never") {
     6500            this.and_blob.single = no_delim;
     6501            this.and_blob.multiple = no_delim;
     6502        } else {
     6503            this.and_blob.single = no_delim;
     6504            this.and_blob.multiple = with_delim;
     6505        }
    59476506    }
    59486507};
     
    59696528    var blobs = this["publisher-list"];
    59706529    var delim = this.name_delimiter;
    5971     var publishers = this._join(blobs, delim, this.and_blob.single, this.and_blob.multiple, this.group_tok);
     6530    var publishers = this._join(blobs, this.group_tok.strings["subgroup-delimiter"], this.and_blob.single, this.and_blob.multiple, this.group_tok);
    59726531    this.state.output.append(publishers, "literal");
    59736532};
     
    61126671                this.and_term = "&";
    61136672            }
     6673            state.build.and_term = this.and_term;
    61146674            if (CSL.STARTSWITH_ROMANESQUE_REGEXP.test(this.and_term)) {
    61156675                this.and_prefix_single = " ";
     
    61376697                this.ellipsis_prefix_multiple =  this.strings.delimiter;
    61386698                this.ellipsis_suffix = " ";
    6139             }
    6140             if (this.strings["delimiter-precedes-et-al"] === "always") {
    6141                 this.and_prefix_single = this.strings.delimiter;
    6142             } else if (this.strings["delimiter-precedes-last"] === "never") {
    6143                 if (this.and_prefix_multiple) {
    6144                     this.and_prefix_multiple = " ";
    6145                 }
    61466699            }
    61476700            func = function (state, Item) {
     
    62126765        if (this.tokentype === CSL.START) {
    62136766            state.build.names_flag = true;
    6214             func = function (state, Item) {
     6767            state.build.names_level += 1;
     6768            func = function (state, Item, item) {
    62156769                state.tmp.can_substitute.push(true);
    6216             };
    6217             this.execs.push(func);
    6218             func = function (state, Item, item) {
    62196770                state.parallel.StartVariable("names");
    6220             };
    6221             this.execs.push(func);
    6222             func = function (state, Item, item) {
    62236771                state.nameOutput.init(this);
    62246772            };
     
    62296777                var key = ["family", "given", "et-al"][i];
    62306778                this[key] = state.build[key];
    6231                 state.build[key] = undefined;
    6232             }
     6779                if (state.build.names_level === 1) {
     6780                    state.build[key] = undefined;
     6781                }
     6782            }
     6783            state.build.names_level += -1;
    62336784            this.label = state.build.name_label;
    62346785            state.build.name_label = undefined;
     
    63476898            var values = state.tmp.shadow_numbers[varname].values;
    63486899            var blob;
     6900            var newstr = ""
    63496901            if (state.opt["page-range-format"]
    63506902                && !this.strings.prefix && !this.strings.suffix
    63516903                && !this.strings.form) {
    6352                 var newstr = ""
    63536904                for (var i = 0, ilen = values.length; i < ilen; i += 1) {
    63546905                    newstr += values[i][1];
    63556906                }
    6356                 newstr = state.fun.page_mangler(newstr);
     6907            }
     6908            if (newstr && !newstr.match(/^[-.\u20130-9]+$/)) {
    63576909                state.output.append(newstr, this);
    63586910            } else {
    63596911                state.output.openLevel("empty");
    63606912                for (var i = 0, ilen = values.length; i < ilen; i += 1) {
    6361                     var blob = new CSL[values[i][0]](values[i][1], values[i][2]);
     6913                    var blob = new CSL[values[i][0]](values[i][1], values[i][2], Item.id);
    63626914                    if (i > 0) {
    63636915                        blob.strings.prefix = blob.strings.prefix.replace(/^\s*/, "");
     
    63666918                        blob.strings.suffix = blob.strings.suffix.replace(/\s*$/, "");
    63676919                    }
    6368                     state.output.append(blob, "literal");
     6920                    state.output.append(blob, "literal", false, false, true);
    63696921                }
    63706922                state.output.closeLevel("empty");
     
    63846936                state.opt.sort_citations = true;
    63856937            }
    6386             state.build.sort_flag  = true;
    6387             state.build.area_return = state.build.area;
    6388             state.build.area = state.build.area + "_sort";
     6938            state.build.area = state.build.root + "_sort";
     6939            state.build.extension = "_sort";
     6940            var func = function (state, Item) {
     6941            }
     6942            this.execs.push(func);
    63896943        }
    63906944        if (this.tokentype === CSL.END) {
    6391             state.build.area = state.build.area_return;
    6392             state.build.sort_flag  = false;
    6393         }
     6945            state.build.area = state.build.root;
     6946            state.build.extension = "";
     6947        }
     6948        target.push(this);
    63946949    }
    63956950};
     
    64326987            if ("citation-number" === this.variables_real[0] || "year-suffix" === this.variables_real[0] || "citation-label" === this.variables_real[0]) {
    64336988                if (this.variables_real[0] === "citation-number") {
    6434                     if (state.build.area === "citation") {
     6989                    if (state.build.root === "citation") {
    64356990                        state.opt.update_mode = CSL.NUMERIC;
    64366991                    }
    6437                     if (state.build.area === "bibliography") {
     6992                    if (state.build.root === "bibliography") {
    64386993                        state.opt.bib_mode = CSL.NUMERIC;
    64396994                    }
     
    64657020                                state.output.append(state.opt.citation_number_slug, this);
    64667021                            } else {
    6467                                 number = new CSL.NumericBlob(num, this);
     7022                                number = new CSL.NumericBlob(num, this, Item.id);
    64687023                                state.output.append(number, "literal");
    64697024                            }
     
    64837038                        if (state.registry.registry[Item.id] && state.registry.registry[Item.id].disambig.year_suffix !== false && !state.tmp.just_looking) {
    64847039                            num = parseInt(state.registry.registry[Item.id].disambig.year_suffix, 10);
    6485                             number = new CSL.NumericBlob(num, this);
     7040                            number = new CSL.NumericBlob(num, this, Item.id);
    64867041                            formatter = new CSL.Util.Suffixator(CSL.SUFFIX_CHARS);
    64877042                            number.setFormatter(formatter);
    64887043                            state.output.append(number, "literal");
    64897044                            firstoutput = false;
    6490                             len = state.tmp.term_sibling.mystack.length;
     7045                            len = state.tmp.group_context.mystack.length;
    64917046                            for (pos = 0; pos < len; pos += 1) {
    6492                                 flag = state.tmp.term_sibling.mystack[pos];
     7047                                flag = state.tmp.group_context.mystack[pos];
    64937048                                if (!flag[2] && (flag[1] || (!flag[1] && !flag[0]))) {
    64947049                                    firstoutput = true;
     
    65227077            } else {
    65237078                if (this.strings.term) {
    6524                     func = function (state, Item) {
     7079                    func = function (state, Item, item) {
    65257080                        var gender = state.opt.gender[Item.type];
    65267081                        var term = this.strings.term;
     
    65287083                        var myterm;
    65297084                        if (term !== "") {
    6530                             flag = state.tmp.term_sibling.value();
     7085                            flag = state.tmp.group_context.value();
    65317086                            flag[0] = true;
    6532                             state.tmp.term_sibling.replace(flag);
     7087                            state.tmp.group_context.replace(flag);
    65337088                        }
    65347089                        if (!state.tmp.term_predecessor) {
     
    65667121                        if (form === "short") {
    65677122                            state.transform.init(this, this.variables_real[0], this.variables_real[0]);
    6568                         } else {
    6569                             state.transform.init(this, this.variables_real[0]);
    6570                         }
    6571                         if (state.build.area.slice(-5) === "_sort") {
    6572                             state.transform.init(this, this.variables_real[0], this.variables_real[0]);
    6573                             state.transform.setTransformLocale("locale-sort");
    6574                             state.transform.setTransformFallback(true);
    6575                             func = state.transform.getOutputFunction(this.variables);
    6576                         } else if (form === "short") {
    6577                              if (["title", "container-title", "collection-title"].indexOf(this.variables_real[0]) > -1) {
    6578                                  state.transform.setTransformLocale("locale-sec");
    6579                              } else {
    6580                                  state.transform.setTransformLocale("locale-pri");
    6581                              }
    6582                              state.transform.setTransformFallback(true);
    6583                              state.transform.setAbbreviationFallback(true);
    65847123                            if (this.variables_real[0] === "container-title") {
    65857124                                state.transform.setAlternativeVariableName("journalAbbreviation");
    65867125                            } else if (this.variables_real[0] === "title") {
    65877126                                state.transform.setAlternativeVariableName("shortTitle");
    6588                             } else if (["publisher", "publisher-place", "event-place", "edition"].indexOf(this.variables_real[0]) > -1) {
    6589                                 state.transform.setTransformLocale("default-locale");
    65907127                            }
    6591                             func = state.transform.getOutputFunction(this.variables);
    6592                         } else if (["title-short","title", "container-title", "collection-title"].indexOf(this.variables_real[0]) > -1) {
    6593                             state.transform.setTransformLocale("locale-sec");
     7128                        } else {
     7129                            state.transform.init(this, this.variables_real[0]);
     7130                        }
     7131                        if (state.build.extension) {
     7132                            state.transform.init(this, this.variables_real[0], this.variables_real[0]);
    65947133                            state.transform.setTransformFallback(true);
    65957134                            func = state.transform.getOutputFunction(this.variables);
    65967135                        } else {
    6597                             state.transform.setTransformLocale("locale-pri");
    65987136                            state.transform.setTransformFallback(true);
    6599                             if (["publisher", "publisher-place", "edition"].indexOf(this.variables_real[0]) > -1) {
    6600                                 state.transform.setTransformLocale("default-locale");
    6601                             }
     7137                            state.transform.setAbbreviationFallback(true);
    66027138                            func = state.transform.getOutputFunction(this.variables);
    6603                         }
     7139                        }
    66047140                        if (this.variables_real[0] === "container-title") {
    66057141                            var xfunc = function (state, Item, item) {
     
    66167152                                    var locator = "" + item[this.variables[0]];
    66177153                                    locator = locator.replace(/--*/g,"\u2013");
    6618                                     state.output.append(locator, this);
     7154                                    var m = locator.match(/^([0-9]+)\s*\u2013\s*([0-9]+)$/)
     7155                                    if (m) {
     7156                                        if (parseInt(m[1]) >= parseInt(m[2])) {
     7157                                            locator = m[1] + "-" + m[2];
     7158                                        }
     7159                                    }
     7160                                    state.output.append(locator, this, false, false, true);
    66197161                                }
    66207162                            };
     
    66297171                                        value = value.slice(0, idx);
    66307172                                    }
    6631                                     state.output.append(value, this);
     7173                                    state.output.append(value, this, false, false, true);
    66327174                                }
    66337175                            };
     
    66377179                                if (value) {
    66387180                                    value = state.fun.page_mangler(value);
    6639                                     state.output.append(value, this);
     7181                                    state.output.append(value, this, false, false, true);
    66407182                                }
    66417183                            };
     
    66527194                        } else if (this.variables_real[0] === "hereinafter") {
    66537195                            func = function (state, Item) {
    6654                                 var hereinafter_key = state.transform.getHereinafter(Item);
    6655                                 var value = state.transform.abbrevs.hereinafter[hereinafter_key];
    6656                                 if (hereinafter_key) {
     7196                                var hereinafter_info = state.transform.getHereinafter(Item);
     7197                                var value = state.transform.abbrevs[hereinafter_info[0]].hereinafter[hereinafter_info[1]];
     7198                                if (value) {
     7199                                    state.tmp.group_context.value()[2] = true;
     7200                                    state.output.append(value, this);
     7201                                }
     7202                            };
     7203                        } else if (this.variables_real[0] === "URL") {
     7204                            func = function (state, Item) {
     7205                                var value;
     7206                                if (this.variables[0]) {
     7207                                    value = state.getVariable(Item, this.variables[0], form);
     7208                                    if (value) {
     7209                                        state.output.append(value, this, false, false, true);
     7210                                    }
     7211                                }
     7212                            };
     7213                        } else if (this.variables_real[0] === "section") {
     7214                            func = function (state, Item) {
     7215                                var value;
     7216                                value = state.getVariable(Item, this.variables[0], form);
     7217                                if (value) {
     7218                                    if ((Item.type === "bill" || Item.type === "legislation")
     7219                                       && state.opt.development_extensions.parse_section_variable) {
     7220                                        value = "" + value;
     7221                                        var m = value.match(CSL.STATUTE_SUBDIV_GROUPED_REGEX);
     7222                                        if (m) {
     7223                                            var splt = value.split(CSL.STATUTE_SUBDIV_PLAIN_REGEX);
     7224                                            var lst = [];
     7225                                            if (!lst[0]) {
     7226                                                var parsed = false;
     7227                                                for (var i=1, ilen=splt.length; i < ilen; i += 1) {
     7228                                                    var subdiv = m[i - 1].replace(/^\s*/, "");
     7229                                                    subdiv = CSL.STATUTE_SUBDIV_STRINGS[subdiv];
     7230                                                    var plural = false;
     7231                                                    if (splt[i].match(/(?:&|, | and )/)) {
     7232                                                        plural = true;
     7233                                                    }
     7234                                                    subdiv = state.getTerm(subdiv, "symbol", plural);
     7235                                                    if (subdiv) {
     7236                                                        parsed = true;
     7237                                                    }
     7238                                                    lst.push(subdiv);
     7239                                                    lst.push(splt[i].replace(/\s*,*\s*$/, "").replace(/^\s*,*\s*/, ""));
     7240                                                }
     7241                                                if (parsed) {
     7242                                                    value = lst.join(" ");
     7243                                                }
     7244                                            }
     7245                                        }
     7246                                    }
    66577247                                    state.output.append(value, this);
    66587248                                }
     
    66787268                    func = function (state, Item) {
    66797269                        var flag;
    6680                         flag = state.tmp.term_sibling.value();
     7270                        flag = state.tmp.group_context.value();
    66817271                        flag[0] = true;
    6682                         state.tmp.term_sibling.replace(flag);
     7272                        state.tmp.group_context.replace(flag);
    66837273                        state.output.append(this.strings.value, this);
    66847274                    };
     
    66927282};
    66937283CSL.Attributes = {};
     7284CSL.Attributes["@has-year-only"] = function (state, arg) {
     7285    trydates = arg.split(/\s+/);
     7286    func = function (state, Item, item) {
     7287        var ret = [];
     7288        for (var i = 0, ilen = trydates.length; i < ilen; i += 1) {
     7289            var trydate = Item[trydates[i]];
     7290            if (!trydate || trydate.month || trydate.season) {
     7291                ret.push(false)
     7292            } else {
     7293                ret.push(true)
     7294            }
     7295        }
     7296        return ret;
     7297    };
     7298    this.tests.push(func);
     7299}
     7300CSL.Attributes["@has-month-or-season-only"] = function (state, arg) {
     7301    trydates = arg.split(/\s+/);
     7302    func = function (state, Item, item) {
     7303        var ret = [];
     7304        for (var i = 0, ilen = trydates.length; i < ilen; i += 1) {
     7305            var trydate = Item[trydates[i]];
     7306            if (!trydate || (!trydate.month && !trydate.season) || trydate.day) {
     7307                ret.push(false)
     7308            } else {
     7309                ret.push(true)
     7310            }
     7311        }
     7312        return ret;
     7313    };
     7314    this.tests.push(func);
     7315}
     7316CSL.Attributes["@has-day-only"] = function (state, arg) {
     7317    trydates = arg.split(/\s+/);
     7318    func = function (state, Item, item) {
     7319        var ret = [];
     7320        for (var i = 0, ilen = trydates.length; i < ilen; i += 1) {
     7321            var trydate = Item[trydates[i]];
     7322            if (!trydate || !trydate.day) {
     7323                ret.push(false)
     7324            } else {
     7325                ret.push(true)
     7326            }
     7327        }
     7328        return ret;
     7329    };
     7330    this.tests.push(func);
     7331}
     7332CSL.Attributes["@part-separator"] = function (state, arg) {
     7333    this.strings["part-separator"] = arg;
     7334}
     7335CSL.Attributes["@context"] = function (state, arg) {
     7336    var func = function (state, Item) {
     7337        var area = state.tmp.area.slice(0, arg.length);
     7338        var result = false;
     7339        if (area === arg) {
     7340            result = true;
     7341        }
     7342        return result;
     7343    };
     7344    this.tests.push(func);
     7345};
    66947346CSL.Attributes["@trigger-fields"] = function (state, arg) {
    66957347    var mylst = arg.split(/\s+/);
     
    67727424                    this.variables.push(variables[pos]);
    67737425                }
    6774                 if ("hereinafter" === variables[pos]) {
    6775                     var hereinafter_key = state.transform.getHereinafter(Item);
    6776                     state.transform.loadAbbreviation("hereinafter", hereinafter_key);
     7426                if ("hereinafter" === variables[pos] && state.sys.getAbbreviation) {
     7427                    var hereinafter_info = state.transform.getHereinafter(Item);
     7428                    state.transform.loadAbbreviation(hereinafter_info[0], "hereinafter", hereinafter_info[1]);
    67777429                }
    67787430                if (state.tmp.can_block_substitute) {
     
    67907442                if (variable === "page-first") {
    67917443                    variable = "page";
     7444                }
     7445                if (variable === "authority"
     7446                    && "string" === typeof Item[variable]
     7447                    && "names" === this.name) {
     7448                    Item[variable] = [{family:Item[variable],isInstitution:true}]
    67927449                }
    67937450                if (this.strings.form === "short" && !Item[variable]) {
     
    68417498                } else if ("object" === typeof Item[variable]) {
    68427499                    if (Item[variable].length) {
    6843                         output = true;
    68447500                    }
    68457501                    break;
     
    68557511                }
    68567512            }
    6857             flag = state.tmp.term_sibling.value();
     7513            flag = state.tmp.group_context.value();
    68587514            if (output) {
    68597515                if (variable !== "citation-number" || state.tmp.area !== "bibliography") {
     
    68617517                }
    68627518                flag[2] = true;
    6863                 state.tmp.term_sibling.replace(flag);
     7519                state.tmp.group_context.replace(flag);
    68647520                state.tmp.can_substitute.replace(false,  CSL.LITERAL);
    68657521            } else {
     
    68777533                x = false;
    68787534                myitem = Item;
    6879                 if (item && ["locator", "first-reference-note-number", "locator-date"].indexOf(variable) > -1) {
     7535                if (item && ["locator", "locator-revision", "first-reference-note-number", "locator-date"].indexOf(variable) > -1) {
    68807536                    myitem = item;
    68817537                }
    6882                 if (myitem[variable]) {
     7538                if (variable === "hereinafter" && state.sys.getAbbreviation) {
     7539                    var hereinafter_info = state.transform.getHereinafter(myitem);
     7540                    state.transform.loadAbbreviation(hereinafter_info[0], "hereinafter", hereinafter_info[1]);
     7541                    if (state.transform.abbrevs[hereinafter_info[0]].hereinafter[hereinafter_info[1]]) {
     7542                        x = true
     7543                    }
     7544                } else if (myitem[variable]) {
    68837545                    if ("number" === typeof myitem[variable] || "string" === typeof myitem[variable]) {
    68847546                        x = true;
     
    69877649    var lex = arg.split(/\s+/);
    69887650    var func = function (state, Item) {
     7651        var mylex = false;
    69897652        var ret = false;
    6990         var mylex = false;
    69917653        if (Item.jurisdiction) {
    69927654            mylex = Item.jurisdiction;
    69937655        } else if (Item.language) {
    6994             var m = Item.language.match(/^.*-x-lex-([.a-zA-Z]+).*$/);
     7656            var m = Item.language.match(/^.*-x-lex-([.;a-zA-Z]+).*$/);
    69957657            if (m) {
    69967658                mylex = m[1];
    69977659            }
    69987660        }
    6999         for (var i = 0, ilen = lex.length; i < ilen; i += 1) {
    7000             if (mylex === lex[i]) {
    7001                 ret = true;
    7002                 break;
     7661        if (mylex) {
     7662            var mylexlst = mylex.split(";");
     7663            outerLoop: for (var i = 0, ilen = lex.length; i < ilen; i += 1) {
     7664                if (!lex[i]) {
     7665                    continue;
     7666                }
     7667                var lexlst = lex[i].split(";");
     7668                innerLoop: for (var j = 0, jlen = lexlst.length; j < jlen; j += 1) {
     7669                    if (mylexlst[j] && mylexlst[j] === lexlst[j] && j === lexlst.length - 1) {
     7670                        ret = true;
     7671                        break outerLoop;
     7672                    }
     7673                }
    70037674            }
    70047675        }
     
    70417712                }
    70427713            }
    7043             if (!state.tmp.shadow_numbers[variables[pos]].numeric) {
     7714            if (!state.tmp.shadow_numbers[variables[pos]].numeric
     7715                && !(variables[pos] === 'title'
     7716                     && Item[variables[pos]]
     7717                     && Item[variables[pos]].slice(-1) === "" + parseInt(Item[variables[pos]].slice(-1)))) {
    70447718                numeric = false;
    70457719                break;
     
    71257799    var tryposition;
    71267800    state.opt.update_mode = CSL.POSITION;
    7127     var factory = function (tryposition) {
    7128         return  function (state, Item, item) {
    7129             if (state.tmp.area === "bibliography" || state.tmp.area === "bibliography_sort") {
    7130                 return false;
    7131             }
    7132             if (item && "undefined" === typeof item.position) {
    7133                 item.position = 0;
    7134             }
    7135             if (item && typeof item.position === "number") {
    7136                 if (item.position === 0 && tryposition === 0) {
    7137                     return true;
    7138                 } else if (tryposition > 0 && item.position >= tryposition) {
    7139                     return true;
    7140                 }
    7141             } else if (tryposition === 0) {
     7801    if ("near-note" === arg) {
     7802        var near_note_func = function (state, Item, item) {
     7803            if (item && item["near-note"]) {
    71427804                return true;
    71437805            }
    71447806            return false;
    71457807        };
    7146     };
    7147     var near_note_func = function (state, Item, item) {
    7148         if (item && item["near-note"]) {
    7149             return true;
    7150         }
    7151         return false;
    7152     };
    7153     var lst = arg.split(/\s+/);
    7154     for (var i = 0, ilen = lst.length; i < ilen; i += 1) {
    7155         if (lst[i] === "first") {
    7156             tryposition = CSL.POSITION_FIRST;
    7157         } else if (lst[i] === "subsequent") {
    7158             tryposition = CSL.POSITION_SUBSEQUENT;
    7159         } else if (lst[i] === "ibid") {
    7160             tryposition = CSL.POSITION_IBID;
    7161         } else if (lst[i] === "ibid-with-locator") {
    7162             tryposition = CSL.POSITION_IBID_WITH_LOCATOR;
    7163         }
    7164         var func = factory(tryposition);
    7165         this.tests.push(func);
    7166         if (lst[i] === "near-note") {
    7167             this.tests.push(near_note_func);
    7168         }
    7169     }
    7170 };
     7808        this.tests.push(near_note_func);
     7809    } else {
     7810        var factory = function (tryposition) {
     7811            return  function (state, Item, item) {
     7812                if (state.tmp.area === "bibliography") {
     7813                    return false;
     7814                }
     7815                if (item && "undefined" === typeof item.position) {
     7816                    item.position = 0;
     7817                }
     7818                if (item && typeof item.position === "number") {
     7819                    if (item.position === 0 && tryposition === 0) {
     7820                        return true;
     7821                    } else if (tryposition > 0 && item.position >= tryposition) {
     7822                        return true;
     7823                    }
     7824                } else if (tryposition === 0) {
     7825                    return true;
     7826                }
     7827                return false;
     7828            };
     7829        };
     7830        var lst = arg.split(/\s+/);
     7831        for (var i = 0, ilen = lst.length; i < ilen; i += 1) {
     7832            if (lst[i] === "first") {
     7833                tryposition = CSL.POSITION_FIRST;
     7834            } else if (lst[i] === "subsequent") {
     7835                tryposition = CSL.POSITION_SUBSEQUENT;
     7836            } else if (lst[i] === "ibid") {
     7837                tryposition = CSL.POSITION_IBID;
     7838            } else if (lst[i] === "ibid-with-locator") {
     7839                tryposition = CSL.POSITION_IBID_WITH_LOCATOR;
     7840            }
     7841            var func = factory(tryposition);
     7842            this.tests.push(func);
     7843        }
     7844    }
     7845}
    71717846CSL.Attributes["@disambiguate"] = function (state, arg) {
    71727847    if (this.tokentype === CSL.START && ["if", "else-if"].indexOf(this.name) > -1) {
     
    71997874    state.setOpt(this, "name-form", arg);
    72007875};
     7876CSL.Attributes["@subgroup-delimiter"] = function (state, arg) {
     7877    this.strings["subgroup-delimiter"] = arg;
     7878};
     7879CSL.Attributes["@subgroup-delimiter-precedes-last"] = function (state, arg) {
     7880    this.strings["subgroup-delimiter-precedes-last"] = arg;
     7881};
    72017882CSL.Attributes["@name-delimiter"] = function (state, arg) {
    72027883    state.setOpt(this, "name-delimiter", arg);
     
    73388019CSL.Attributes["@default-locale"] = function (state, arg) {
    73398020    var lst, len, pos, m, ret;
    7340     m = arg.match(/-x-(sort|pri|sec|name)-/g);
     8021    m = arg.match(/-x-(sort|translit|translat)-/g);
    73418022    if (m) {
    73428023        for (pos = 0, len = m.length; pos < len; pos += 1) {
     
    73448025        }
    73458026    }
    7346     lst = arg.split(/-x-(?:sort|pri|sec|name)-/);
     8027    lst = arg.split(/-x-(?:sort|translit|translat)-/);
    73478028    ret = [lst[0]];
    73488029    for (pos = 1, len = lst.length; pos < len; pos += 1) {
     
    73838064    this.strings["use-first"] = parseInt(arg, 10);
    73848065};
     8066CSL.Attributes["@stop-last"] = function (state, arg) {
     8067    this.strings["stop-last"] = parseInt(arg, 10) * -1;
     8068}
     8069CSL.Attributes["@oops"] = function (state, arg) {
     8070    this.strings.oops = arg;
     8071}
    73858072CSL.Attributes["@use-last"] = function (state, arg) {
    7386     if (arg.match(/^[0-9]+$/)) {
    7387         this.strings["use-last"] = parseInt(arg, 10);
    7388     }
     8073    this.strings["use-last"] = parseInt(arg, 10);
    73898074};
    73908075CSL.Attributes["@reverse-order"] = function (state, arg) {
     
    75298214};
    75308215CSL.Transform = function (state) {
    7531     var debug = false, abbreviations, token, fieldname, subsection, opt;
     8216    var debug = false, abbreviations, token, fieldname, abbrev_family, opt;
    75328217    this.abbrevs = {};
    7533     this.abbrevs["container-title"] = {};
    7534     this.abbrevs["collection-title"] = {};
    7535     this.abbrevs.institution = {};
    7536     this.abbrevs.nickname = {};
    7537     this.abbrevs.place = {};
    7538     this.abbrevs.title = {};
    7539     this.abbrevs.hereinafter = {};
    7540     this.abbrevs.classic = {};
    7541     function init(t, f, x) {
    7542         token = t;
    7543         fieldname = f;
    7544         subsection = x;
     8218    this.abbrevs["default"] = new CSL.AbbreviationSegments();
     8219    function init(mytoken, myfieldname, myabbrev_family) {
     8220        token = mytoken;
     8221        fieldname = myfieldname;
     8222        abbrev_family = myabbrev_family;
    75458223        opt = {
    75468224            abbreviation_fallback: false,
    75478225            alternative_varname: false,
    7548             transform_locale: false,
    75498226            transform_fallback: false
    75508227        };
    75518228    }
    75528229    this.init = init;
    7553     function abbreviate(state, Item, altvar, basevalue, mysubsection, use_field) {
     8230    function abbreviate(state, Item, altvar, basevalue, myabbrev_family, use_field) {
    75548231        var value;
    7555         if (!mysubsection) {
     8232        if (!myabbrev_family) {
    75568233            return basevalue;
    75578234        }
    7558         if (["publisher-place", "event-place"].indexOf(mysubsection) > -1) {
    7559             mysubsection = "place";
    7560         }
    7561         if (["publisher", "authority"].indexOf(mysubsection) > -1) {
    7562             mysubsection = "institution";
    7563         }
    7564         state.transform.loadAbbreviation(mysubsection, basevalue);
     8235        if (["publisher-place", "event-place", "subjurisdiction"].indexOf(myabbrev_family) > -1) {
     8236            myabbrev_family = "place";
     8237        }
     8238        if (["publisher", "authority"].indexOf(myabbrev_family) > -1) {
     8239            myabbrev_family = "institution-part";
     8240        }
     8241        if (["genre", "event", "medium"].indexOf(myabbrev_family) > -1) {
     8242            myabbrev_family = "title";
     8243        }
     8244        if (["title-short"].indexOf(myabbrev_family) > -1) {
     8245            myabbrev_family = "title";
     8246        }
    75658247        value = "";
    7566         if (state.transform.abbrevs[mysubsection] && basevalue) {
    7567             if (state.transform.abbrevs[mysubsection][basevalue]) {
    7568                 value = state.transform.abbrevs[mysubsection][basevalue];
    7569             } else if ("string" != typeof state.transform.abbrevs[mysubsection][basevalue]) {
    7570             }
    7571         }
    7572         if (!value && Item[altvar] && use_field) {
     8248        if (state.sys.getAbbreviation) {
     8249            var jurisdiction = state.transform.loadAbbreviation(Item.jurisdiction, myabbrev_family, basevalue);
     8250            if (state.transform.abbrevs[jurisdiction][myabbrev_family] && basevalue && state.sys.getAbbreviation) {
     8251                if (state.transform.abbrevs[jurisdiction][myabbrev_family][basevalue]) {
     8252                    value = state.transform.abbrevs[jurisdiction][myabbrev_family][basevalue];
     8253                }
     8254            }
     8255        }
     8256        if (!value && altvar && Item[altvar] && use_field) {
    75738257            value = Item[altvar];
    75748258        }
     
    75788262        return value;
    75798263    }
    7580     function getTextSubField(Item, field, locale_type, use_default) {
     8264    function getTextSubField(Item, field, locale_type, use_default, stopOrig) {
    75818265        var m, lst, opt, o, oo, pos, key, ret, len, myret, opts;
     8266        var usedOrig = stopOrig;
    75828267        if (!Item[field]) {
    7583             return "";
    7584         }
    7585         ret = "";
     8268            return {name:"", usedOrig:stopOrig};
     8269        }
     8270        ret = {name:"", usedOrig:stopOrig};
    75868271        opts = state.opt[locale_type];
    7587         if ("undefined" === typeof opts) {
    7588             opts = state.opt["default-locale"];
     8272        if (locale_type === 'locale-orig') {
     8273            if (stopOrig) {
     8274                ret = {name:"", usedOrig:stopOrig};
     8275            } else {
     8276                ret = {name:Item[field], usedOrig:false};
     8277            }
     8278            return ret;
     8279        } else if (use_default && ("undefined" === typeof opts || opts.length === 0)) {
     8280            return {name:Item[field], usedOrig:true};
    75898281        }
    75908282        for (var i = 0, ilen = opts.length; i < ilen; i += 1) {
     
    75928284            o = opt.split(/[\-_]/)[0];
    75938285            if (opt && Item.multi && Item.multi._keys[field] && Item.multi._keys[field][opt]) {
    7594                 ret = Item.multi._keys[field][opt];
     8286                ret.name = Item.multi._keys[field][opt];
    75958287                break;
    75968288            } else if (o && Item.multi && Item.multi._keys[field] && Item.multi._keys[field][o]) {
    7597                 ret = Item.multi._keys[field][o];
     8289                ret.name = Item.multi._keys[field][o];
    75988290                break;
    75998291            }
    76008292        }
    7601         if (!ret && use_default) {
    7602             ret = Item[field];
     8293        if (!ret.name && use_default) {
     8294            ret = {name:Item[field], usedOrig:true};
    76038295        }
    76048296        return ret;
     
    76128304    }
    76138305    this.setAlternativeVariableName = setAlternativeVariableName;
    7614     function setTransformLocale(s) {
    7615         opt.transform_locale = s;
    7616     }
    7617     this.setTransformLocale = setTransformLocale;
    76188306    function setTransformFallback(b) {
    76198307        opt.transform_fallback = b;
    76208308    }
    76218309    this.setTransformFallback = setTransformFallback;
    7622     function loadAbbreviation(vartype, key) {
     8310    function loadAbbreviation(jurisdiction, category, orig) {
    76238311        var pos, len;
    7624         if (!this.abbrevs[vartype] || !key) {
    7625             return;
    7626         }
    7627         if (state.sys.getAbbreviation && !this.abbrevs[vartype][key]) {
    7628             state.sys.getAbbreviation(this.abbrevs, vartype, key);
    7629         }
     8312        if (!jurisdiction) {
     8313            jurisdiction = "default";
     8314        }
     8315        if (!orig) {
     8316            if (!this.abbrevs[jurisdiction]) {
     8317                this.abbrevs[jurisdiction] = new CSL.AbbreviationSegments();
     8318            }
     8319            return jurisdiction;
     8320        }
     8321        if (state.sys.getAbbreviation) {
     8322            var tryList = ['default'];
     8323            if (jurisdiction !== 'default') {
     8324                var workLst = jurisdiction.split(/\s*;\s*/);
     8325                for (var i=0, ilen=workLst.length; i < ilen; i += 1) {
     8326                    tryList.push(workLst.slice(0,i+1).join(';'));
     8327                }
     8328            }
     8329            for (var i=tryList.length - 1; i > -1; i += -1) {
     8330                if (!this.abbrevs[tryList[i]]) {
     8331                    this.abbrevs[tryList[i]] = new CSL.AbbreviationSegments();
     8332                }
     8333                if (!this.abbrevs[tryList[i]][category][orig]) {
     8334                    state.sys.getAbbreviation(state.opt.styleID, this.abbrevs, tryList[i], category, orig);
     8335                }
     8336                if (this.abbrevs[tryList[i]][category][orig]) {
     8337                    if (i < tryList.length) {
     8338                        this.abbrevs[jurisdiction][category][orig] = this.abbrevs[tryList[i]][category][orig];
     8339                    }
     8340                    break;
     8341                }
     8342            }
     8343        }
     8344        return jurisdiction;
    76308345    }
    76318346    this.loadAbbreviation = loadAbbreviation;
    7632     function publisherCheck (varname, primary, tok) {
     8347    function publisherCheck (tok, Item, primary, myabbrev_family) {
     8348        var varname = tok.variables[0];
    76338349        if (state.publisherOutput && primary) {
    76348350            if (["publisher","publisher-place"].indexOf(varname) === -1) {
     
    76398355                var lst = primary.split(/;\s*/);
    76408356                if (lst.length === state.publisherOutput[varname + "-list"].length) {
    7641                     state.tmp[varname + "-list"] = lst;
     8357                    state.publisherOutput[varname + "-list"] = lst;
     8358                }
     8359                for (var i = 0, ilen = lst.length; i < ilen; i += 1) {
     8360                    lst[i] = abbreviate(state, Item, false, lst[i], myabbrev_family, true);
    76428361                }
    76438362                state.tmp[varname + "-token"] = tok;
     
    76488367    }
    76498368    function getOutputFunction(variables) {
    7650         var mytoken, mysubsection, myfieldname, abbreviation_fallback, alternative_varname, transform_locale, transform_fallback, getTextSubfield;
    7651         mytoken = CSL.Util.cloneToken(token); // the token isn't needed, is it?
    7652         mysubsection = subsection;
     8369        var myabbrev_family, myfieldname, abbreviation_fallback, alternative_varname, transform_locale, transform_fallback, getTextSubfield;
     8370        myabbrev_family = abbrev_family;
    76538371        myfieldname = fieldname;
    76548372        abbreviation_fallback = opt.abbreviation_fallback;
    76558373        alternative_varname = opt.alternative_varname;
    7656         transform_locale = opt.transform_locale;
    76578374        transform_fallback = opt.transform_fallback;
    7658         if (transform_locale === "locale-sec") {
    7659             return function (state, Item) {
    7660                 var primary, secondary, primary_tok, secondary_tok, key;
    7661                 if (!variables[0]) {
    7662                     return null;
    7663                 }
    7664                 if (state.tmp["publisher-list"]) {
    7665                     if (variables[0] === "publisher") {
    7666                         state.tmp["publisher-token"] = this;
    7667                     } else if (variables[0] === "publisher-place") {
    7668                         state.tmp["publisher-place-token"] = this;
    7669                     }
    7670                     return null;
    7671                 }
    7672                 if (state.opt["locale-suppress-title-transliteration"]
    7673                     || !((state.tmp.area === 'bibliography'
    7674                         || (state.opt.xclass === "note" &&
    7675                             state.tmp.area === "citation"))
    7676                         )
    7677                     ) {
    7678                     primary = Item[myfieldname];
    7679                 } else {
    7680                     primary = getTextSubField(Item, myfieldname, "locale-pri", transform_fallback);
    7681                 }
    7682                 if (mysubsection) {
    7683                     primary = abbreviate(state, Item, alternative_varname, primary, mysubsection, true);
    7684                 }
    7685                 secondary = getTextSubField(Item, myfieldname, "locale-sec");
    7686                 if ("demote" === this["leading-noise-words"]) {
    7687                     primary = CSL.demoteNoiseWords(primary);
    7688                     secondary = CSL.demoteNoiseWords(secondary);
    7689                 }
    7690                 if (secondary && ((state.tmp.area === 'bibliography' || (state.opt.xclass === "note" && state.tmp.area === "citation")))) {
    7691                     if (mysubsection) {
    7692                         secondary = abbreviate(state, Item, alternative_varname, secondary, mysubsection, true);
    7693                     }
    7694                     primary_tok = CSL.Util.cloneToken(this);
    7695                     primary_tok.strings.suffix = "";
    7696                     secondary_tok = new CSL.Token("text", CSL.SINGLETON);
    7697                     secondary_tok.strings.suffix = "]" + this.strings.suffix;
    7698                     secondary_tok.strings.prefix = " [";
    7699                     state.output.append(primary, primary_tok);
    7700                     state.output.append(secondary, secondary_tok);
    7701                 } else {
    7702                     state.output.append(primary, this);
    7703                 }
     8375        var localesets;
     8376        var langPrefs = CSL.LangPrefsMap[myfieldname];
     8377        if (!langPrefs) {
     8378            localesets = false;
     8379        } else {
     8380            localesets = state.opt['cite-lang-prefs'][langPrefs];
     8381        }
     8382        return function (state, Item, item, usedOrig) {
     8383            var primary, secondary, tertiary, primary_tok, group_tok, key;
     8384            if (!variables[0]) {
    77048385                return null;
    7705             };
    7706         } else {
    7707             return function (state, Item) {
    7708                 var primary;
    7709                 if (!variables[0]) {
    7710                     return null;
    7711                 }
    7712                 primary = getTextSubField(Item, myfieldname, transform_locale, transform_fallback);
    7713                 primary = abbreviate(state, Item, alternative_varname, primary, mysubsection, true);
    7714                 if (publisherCheck(variables[0], primary, this)) {
    7715                     return null;
    7716                 } else {
    7717                     if ("demote" === this["leading-noise-words"]) {
    7718                         primary = CSL.demoteNoiseWords(primary);
    7719                     }
    7720                     state.output.append(primary, this);
     8386            }
     8387            var slot = {primary:false, secondary:false, tertiary:false};
     8388            if (state.tmp.area.slice(-5) === "_sort") {
     8389                slot.primary = 'locale-sort';
     8390            } else {
     8391                if (localesets) {
     8392                    var slotnames = ["primary", "secondary", "tertiary"];
     8393                    for (var i = 0, ilen = slotnames.length; i < ilen; i += 1) {
     8394                        if (localesets.length - 1 <  i) {
     8395                            break;
     8396                        }
     8397                        if (localesets[i]) {
     8398                            slot[slotnames[i]] = 'locale-' + localesets[i];
     8399                        }
     8400                    }
     8401                } else {
     8402                    slot.primary = 'locale-translat';
     8403                }
     8404            }
     8405            if ((state.tmp.area !== "bibliography"
     8406                 && !(state.tmp.area === "citation"
     8407                      && state.opt.xclass === "note"
     8408                      && item && !item.position))
     8409                || myabbrev_family) {
     8410                slot.secondary = false;
     8411                slot.tertiary = false;
     8412            }
     8413            if (state.tmp["publisher-list"]) {
     8414                if (variables[0] === "publisher") {
     8415                    state.tmp["publisher-token"] = this;
     8416                } else if (variables[0] === "publisher-place") {
     8417                    state.tmp["publisher-place-token"] = this;
    77218418                }
    77228419                return null;
    7723             };
    7724         }
     8420            }
     8421            var res = getTextSubField(Item, myfieldname, slot.primary, true);
     8422            primary = res.name;
     8423            if (publisherCheck(this, Item, primary, myabbrev_family)) {
     8424                return null;
     8425            }
     8426            secondary = false;
     8427            tertiary = false;
     8428            if (slot.secondary) {
     8429                res = getTextSubField(Item, myfieldname, slot.secondary, false, res.usedOrig);
     8430                secondary = res.name;
     8431            }
     8432            if (slot.tertiary) {
     8433                res = getTextSubField(Item, myfieldname, slot.tertiary, false, res.usedOrig);
     8434                tertiary = res.name;
     8435            }
     8436            if (myabbrev_family) {
     8437                primary = abbreviate(state, Item, alternative_varname, primary, myabbrev_family, true);
     8438                secondary = abbreviate(state, Item, false, secondary, myabbrev_family, true);
     8439                tertiary = abbreviate(state, Item, false, tertiary, myabbrev_family, true);
     8440            }
     8441            if ("demote" === this["leading-noise-words"]) {
     8442                primary = CSL.demoteNoiseWords(state, primary);
     8443                secondary = CSL.demoteNoiseWords(state, secondary);
     8444                tertiary = CSL.demoteNoiseWords(state, tertiary);
     8445            }
     8446            if (secondary || tertiary) {
     8447                primary_tok = CSL.Util.cloneToken(this);
     8448                primary_tok.strings.suffix = "";
     8449                state.output.append(primary, primary_tok);
     8450                group_tok = new CSL.Token();
     8451                group_tok.strings.prefix = " [";
     8452                group_tok.strings.delimiter = ", ";
     8453                group_tok.strings.suffix = "]" + this.strings.suffix;
     8454                state.output.openLevel(group_tok);
     8455                if (secondary) {
     8456                    state.output.append(secondary);
     8457                }
     8458                if (tertiary) {
     8459                    state.output.append(tertiary);
     8460                }
     8461                state.output.closeLevel();
     8462            } else {
     8463                state.output.append(primary, this);
     8464            }
     8465            return null;
     8466        };
    77258467    }
    77268468    this.getOutputFunction = getOutputFunction;
    7727     function getStaticOrder (name, refresh) {
    7728         var static_ordering_val = false;
    7729         if (!refresh && name["static-ordering"]) {
    7730             static_ordering_val = true;
    7731         } else if (!(name.family.replace('"', '', 'g') + name.given).match(CSL.ROMANESQUE_REGEXP)) {
    7732             static_ordering_val = true;
    7733         } else if (name.multi && name.multi.main && name.multi.main.slice(0,2) == 'vn') {
    7734             static_ordering_val = true;
    7735         } else {
    7736             if (state.opt['auto-vietnamese-names']
    7737                 && (CSL.VIETNAMESE_NAMES.exec(name.family + " " + name.given)
    7738                     && CSL.VIETNAMESE_SPECIALS.exec(name.family + name.given))) {
    7739                 static_ordering_val = true;
    7740             }
    7741         }
    7742         return static_ordering_val;
    7743     }
    7744     function getName (state, name, langTags) {
    7745         var i, ret, optLangTag, ilen, key, langTag;
    7746         if (state.tmp.area.slice(-5) === "_sort") {
    7747              langTags = state.opt["locale-sort"];
    7748         }
    7749         if ("string" === typeof langTags) {
    7750             langTags = [langTags];
    7751         }
    7752         if (!name.family) {
    7753             name.family = "";
    7754         }
    7755         if (!name.given) {
    7756             name.given = "";
    7757         }
    7758         var static_ordering_freshcheck = false;
    7759         var block_initialize = false;
    7760         var transliterated = false;
    7761         var static_ordering_val = getStaticOrder(name);
    7762         if (langTags && name.multi) {
    7763             for (i = 0, ilen = langTags.length; i < ilen; i += 1) {
    7764                 langTag = langTags[i];
    7765                 if (name.multi._key[langTag]) {
    7766                     name = name.multi._key[langTag];
    7767                     transliterated = true;
    7768                     if (!state.opt['locale-use-original-name-format']) {
    7769                         static_ordering_freshcheck = true;
    7770                     } else {
    7771                         if ((name.family.replace('"','','g') + name.given).match(CSL.ROMANESQUE_REGEXP)) {
    7772                             block_initialize = true;
    7773                         }
    7774                     }
    7775                     break;
    7776                 }
    7777             }
    7778         }
    7779         name = {
    7780             family:name.family,
    7781             given:name.given,
    7782             "non-dropping-particle":name["non-dropping-particle"],
    7783             "dropping-particle":name["dropping-particle"],
    7784             suffix:name.suffix,
    7785             "static-ordering":static_ordering_val,
    7786             "parse-names":name["parse-names"],
    7787             "comma-suffix":name["comma-suffix"],
    7788             "comma-dropping-particle":name["comma-dropping-particle"],
    7789             transliterated:transliterated,
    7790             block_initialize:block_initialize,
    7791             literal:name.literal,
    7792             isInstitution:name.isInstitution
    7793         };
    7794         if (static_ordering_freshcheck &&
    7795             !getStaticOrder(name, true)) {
    7796             name["static-ordering"] = false;
    7797         }
    7798         if (!name.literal && (!name.given && name.family && name.isInstitution)) {
    7799             name.literal = name.family;
    7800         }
    7801         if (name.literal) {
    7802             delete name.family;
    7803             delete name.given;
    7804         }
    7805         return name;
    7806     }
    7807     this.name = getName;
    78088469    function getHereinafter (Item) {
    78098470        var hereinafter_author_title = [];
     
    78348495            }
    78358496        }
     8497        var jurisdiction = "default";
    78368498        if (Item.jurisdiction) {
    7837             hereinafter_metadata.push("jurisdiction:" + Item.jurisdiction);
     8499            jurisdiction = Item.jurisdiction;
    78388500        }
    78398501        hereinafter_metadata = hereinafter_metadata.join(", ");
     
    78428504        }
    78438505        var hereinafter_key = hereinafter_author_title.join(", ") + hereinafter_metadata;
    7844         return hereinafter_key;
     8506        return [jurisdiction, hereinafter_key];
    78458507    }
    78468508    this.getHereinafter = getHereinafter;
     
    78518513    this.try_cite = true;
    78528514    this.use_parallels = true;
     8515    this.midVars = ["hereinafter", "section", "volume", "container-title", "collection-number", "issue", "page", "page-first", "locator"];
    78538516};
    78548517CSL.Parallel.prototype.isMid = function (variable) {
    7855     return ["section", "volume", "container-title", "issue", "page", "page-first", "locator"].indexOf(variable) > -1;
     8518    return (this.midVars.indexOf(variable) > -1);
    78568519};
    78578520CSL.Parallel.prototype.StartCitation = function (sortedItems, out) {
     
    78698532            this.out = this.state.output.queue;
    78708533        }
     8534        this.master_was_neutral_cite = true;
    78718535    }
    78728536};
     
    78828546        }
    78838547        this.try_cite = true;
    7884         len = CSL.PARALLEL_MATCH_VARS.length;
    7885         for (pos = 0; pos < len; pos += 1) {
    7886             x = CSL.PARALLEL_MATCH_VARS[pos];
    7887             if (!Item[x] || CSL.PARALLEL_TYPES.indexOf(Item.type) === -1) {
    7888                 this.try_cite = false;
    7889                 if (this.in_series) {
    7890                     this.in_series = false;
    7891                 }
     8548        var has_required_var = false;
     8549        for (var i = 0, ilen = CSL.PARALLEL_MATCH_VARS.length; i < ilen; i += 1) {
     8550            if (Item[CSL.PARALLEL_MATCH_VARS[i]]) {
     8551                has_required_var = true;
    78928552                break;
     8553            }
     8554        }
     8555        if (!has_required_var || CSL.PARALLEL_TYPES.indexOf(Item.type) === -1) {
     8556            this.try_cite = true;
     8557            if (this.in_series) {
     8558                this.in_series = false;
    78938559            }
    78948560        }
     
    79358601CSL.Parallel.prototype.StartVariable = function (variable) {
    79368602    if (this.use_parallels && (this.try_cite || this.force_collapse)) {
    7937         this.variable = variable;
     8603        if (variable === "container-title" && this.sets.value().length === 0) {
     8604            this.master_was_neutral_cite = false;
     8605        }
    79388606        this.data = {};
    79398607        this.data.value = "";
     
    79428610        if (this.target === "front" && is_mid) {
    79438611            this.target = "mid";
    7944         } else if (this.target === "mid" && !is_mid && this.cite.Item.title) {
     8612        } else if (this.target === "mid" && !is_mid && this.cite.Item.title && variable !== "names") {
    79458613            this.target = "back";
    79468614        } else if (this.target === "back" && is_mid) {
     
    79488616            this.in_series = false;
    79498617        }
     8618        if (variable === "names") {
     8619            this.variable = variable + ":" + this.target;
     8620        } else {
     8621            this.variable = variable;
     8622        }
    79508623        if (variable === "number") {
    7951             this.cite.front.push(variable);
     8624            this.cite.front.push(this.variable);
    79528625        } else if (CSL.PARALLEL_COLLAPSING_MID_VARSET.indexOf(variable) > -1) {
    7953             this.cite.front.push(variable);
     8626            this.cite.front.push(this.variable);
    79548627        } else {
    7955             this.cite[this.target].push(variable);
     8628            this.cite[this.target].push(this.variable);
    79568629        }
    79578630    }
     
    79838656        if (this.sets.value().length > 0) {
    79848657            var prev = this.sets.value()[(this.sets.value().length - 1)];
     8658            if (this.target === "front" && this.variable === "issued") {
     8659                if (this.data.value && this.master_was_neutral_cite) {
     8660                    this.target = "mid";
     8661                }
     8662            }
    79858663            if (this.target === "front") {
    7986                 if (!(!prev[this.variable] && !this.data.value) && (!prev[this.variable] || this.data.value !== prev[this.variable].value)) {
    7987                     this.in_series = false;
     8664                if ((prev[this.variable] || this.data.value) && (!prev[this.variable] || this.data.value !== prev[this.variable].value)) {
     8665                    if ("issued" !== this.variable) {
     8666                        this.in_series = false;
     8667                    }
    79888668                }
    79898669            } else if (this.target === "mid") {
     
    80208700            use_journal_info = true;
    80218701        }
     8702        if (this.cite.front_collapse["collection-number"] === false) {
     8703            use_journal_info = true;
     8704        }
    80228705        if (this.cite.front_collapse.section === false) {
    80238706            use_journal_info = true;
     
    80368719            if (container_title_pos > -1) {
    80378720                this.cite.front = this.cite.front.slice(0,container_title_pos).concat(this.cite.front.slice(container_title_pos + 1));
     8721            }
     8722            collection_number_pos = this.cite.front.indexOf("collection-number");
     8723            if (collection_number_pos > -1) {
     8724                this.cite.front = this.cite.front.slice(0,collection_number_pos).concat(this.cite.front.slice(collection_number_pos + 1));
    80388725            }
    80398726        }
     
    80548741            }
    80558742        } else {
    8056             this.cite.back_forceme = this.sets.value().slice(-1)[0].back_forceme;
     8743            var idx = this.cite.front.indexOf("issued");
     8744            if (idx === -1 || this.master_was_neutral_cite) {
     8745                this.cite.back_forceme = this.sets.value().slice(-1)[0].back_forceme;
     8746            }
     8747            if (idx > -1) {
     8748                var prev = this.sets.value()[this.sets.value().length - 1];
     8749                if (!prev.issued) {
     8750                    this.cite.front = this.cite.front.slice(0, idx).concat(this.cite.front.slice(idx + 1));
     8751                }
     8752            }
     8753            if (this.master_was_neutral_cite && this.cite.mid.indexOf("names:mid") > -1) {
     8754                this.cite.front.push("names:mid");
     8755            }
    80578756        }
    80588757        this.sets.value().push(this.cite);
     
    81508849                    b = cite[varname].blobs[ppos];
    81518850                    b[0].blobs = b[0].blobs.slice(0, b[1]).concat(b[0].blobs.slice((b[1] + 1)));
     8851                    this.state.tmp.has_purged_parallel = true;
     8852                    if (b[0] && b[0].strings && "string" == typeof b[0].strings.oops
     8853                        && b[0].parent && b[0].parent) {
     8854                        b[0].parent.parent.strings.delimiter = b[0].strings.oops;
     8855                    }
    81528856                }
    81538857            }
     
    82098913    this.levelname = levelname;
    82108914    if (token) {
    8211         this.strings = {};
     8915        this.strings = {"prefix":"","suffix":""};
    82128916        for (key in token.strings) {
    82138917            if (token.strings.hasOwnProperty(key)) {
     
    82488952    }
    82498953};
    8250 CSL.NumericBlob = function (num, mother_token) {
     8954CSL.NumericBlob = function (num, mother_token, id) {
     8955    this.id = id;
    82518956    this.alldecor = [];
    82528957    this.num = num;
     
    82898994};
    82908995CSL.NumericBlob.prototype.checkNext = function (next) {
    8291     if (! next || !next.num || this.type !== next.type || next.num !== (this.num + 1)) {
     8996    if (next && this.id == next.id) {
     8997        this.status = CSL.START;
     8998    } else if (! next || !next.num || this.type !== next.type || next.num !== (this.num + 1)) {
    82928999        if (this.status === CSL.SUCCESSOR_OF_SUCCESSOR) {
    82939000            this.status = CSL.END;
     
    84129119    }
    84139120    namelist = namelist.replace(/\s*\-\s*/g, "-").replace(/\s+/g, " ");
     9121    namelist = namelist.replace(/-([a-z])/g, "\u2013$1")
    84149122    mm = namelist.match(/[\-\s]+/g);
    84159123    lst = namelist.split(/[\-\s]+/);
     
    84439151        ret = CSL.Util.Names.doInitialize(state, lst, terminator);
    84449152    }
     9153    ret = ret.replace(/\u2013([a-z])/g, "-$1")
    84459154    return ret;
    84469155};
     
    84519160        if (namelist[i].length > 1 && namelist[i].slice(-1) === ".") {
    84529161            namelist[i] = namelist[i].slice(0, -1);
     9162            isAbbrev.push(true);
     9163        } else if (namelist[i].length === 1 && namelist[i].toUpperCase() === namelist[i]) {
    84539164            isAbbrev.push(true);
    84549165        } else {
     
    87909501        func = function (state, Item) {
    87919502            var i, ilen;
    8792             var text_esc = CSL.getSafeEscape(state.opt.mode, state.tmp.area);
     9503            var text_esc = CSL.getSafeEscape(state);
    87939504            var printing = !state.tmp.suppress_decorations;
    87949505            if (printing && state.tmp.area === "bibliography") {
     
    89059616    if ((num / 10) % 10 === 1 || (num > 10 && num < 20)) {
    89069617        str += this.suffixes[gender][3];
    8907     } else if (num % 10 === 1) {
     9618    } else if (num % 10 === 1 && num % 100 !== 11) {
    89089619        str += this.suffixes[gender][0];
    8909     } else if (num % 10 === 2) {
     9620    } else if (num % 10 === 2 && num % 100 !== 12) {
    89109621        str += this.suffixes[gender][1];
    8911     } else if (num % 10 === 3) {
     9622    } else if (num % 10 === 3 && num % 100 !== 13) {
    89129623        str += this.suffixes[gender][2];
    89139624    } else {
     
    89659676            num = num.slice(1, -1);
    89669677        }
    8967         num = num.replace(/\s*\-\s*/, "\u2013", "g");
     9678        if (num.indexOf("&") > -1 || num.indexOf("--") > -1) {
     9679            this.tmp.shadow_numbers[variable].plural = 1;
     9680        }
    89689681        if (this.variable === "page-first") {
    89699682            m = num.split(/\s*(?:&|,|-)\s*/);
     
    89759688            }
    89769689        }
    8977         var lst = num.split(/(?:,\s+|\s*[\-\u2013]\s*|\s*&\s*)/);
    8978         var m = num.match(/(,\s+|\s*[\-\u2013]\s*|\s*&\s*)/g);
     9690        var lst = num.split(/(?:,\s+|\s*\\*[\-\u2013]+\s*|\s*&\s*)/);
     9691        var m = num.match(/(,\s+|\s*\\*[\-\u2013]+\s*|\s*&\s*)/g);
    89799692        var elements = [];
    89809693        for (var i = 0, ilen = lst.length - 1; i < ilen; i += 1) {
     
    89909703                if (elements[i]) {
    89919704                    if (elements[i].match(/[0-9]/)) {
    8992                         count = count + 1;
     9705                        if (elements[i - 1] && elements[i - 1].match(/^\s*\\*[\-\u2013]+\s*$/)) {
     9706                            var middle = this.tmp.shadow_numbers[variable].values.slice(-1);
     9707                            if (middle[0][1].indexOf("\\") == -1) {
     9708                                if (elements[i - 2] && ("" + elements[i - 2]).match(/[0-9]+$/)
     9709                                    && elements[i].match(/^[0-9]+/)
     9710                                    && parseInt(elements[i - 2]) < parseInt(elements[i].replace(/[^0-9].*/,""))) {
     9711                                    var start = this.tmp.shadow_numbers[variable].values.slice(-2);
     9712                                    middle[0][1] = "\u2013";
     9713                                    if (this.opt["page-range-format"] ) {
     9714                                        var newstr = this.fun.page_mangler(start[0][1] +"-"+elements[i]);
     9715                                        newstr = newstr.split(/\u2013/);
     9716                                        elements[i] = newstr[1];
     9717                                    }
     9718                                    count = count + 1;
     9719                                }
     9720                                if (middle[0][1].indexOf("--") > -1) {
     9721                                    middle[0][1] = middle[0][1].replace(/--*/, "\u2013");
     9722                                }
     9723                            } else {
     9724                                middle[0][1] = middle[0][1].replace(/\\/, "", "g");
     9725                            }
     9726                        } else {
     9727                            count = count + 1;
     9728                        }
    89939729                    }
    89949730                    var subelements = elements[i].split(/\s+/);
     
    90159751                        this.tmp.shadow_numbers[variable].values.push(["NumericBlob", elements[i], node]);
    90169752                    } else {
    9017                         this.tmp.shadow_numbers[variable].values.push(["Blob", elements[i], node]);
     9753                        var str = elements[i];
     9754                        if (this.sys.getAbbreviation) {
     9755                            var jurisdiction = this.transform.loadAbbreviation(ItemObject.jurisdiction, "number", elements[i]);
     9756                            if (this.transform.abbrevs[jurisdiction].number[str]) {
     9757                                str = this.transform.abbrevs[jurisdiction].number[str];
     9758                            }
     9759                        }
     9760                        this.tmp.shadow_numbers[variable].values.push(["Blob", str, node]);
    90189761                    }
    90199762                }
     
    90289771        } else {
    90299772             this.tmp.shadow_numbers[variable].numeric = numeric;
    9030        }
     9773        }
    90319774        if (count > 1) {
    90329775            this.tmp.shadow_numbers[variable].plural = 1;
     
    90509793        return ret;
    90519794    };
    9052     listify = function (str) {
     9795    listify = function (str, hyphens) {
    90539796        var m, lst, ret;
    90549797        str = str.replace("\u2013", "-", "g");
    9055         m = str.match(/([a-zA-Z]*[0-9]+\s*-\s*[a-zA-Z]*[0-9]+)/g);
    9056         lst = str.split(/[a-zA-Z]*[0-9]+\s*-\s*[a-zA-Z]*[0-9]+/);
     9798        var rexm = new RegExp("([a-zA-Z]*[0-9]+" + hyphens + "[a-zA-Z]*[0-9]+)", "g");
     9799        var rexlst = new RegExp("[a-zA-Z]*[0-9]+" + hyphens + "[a-zA-Z]*[0-9]+");
     9800        m = str.match(rexm);
     9801        lst = str.split(rexlst);
    90579802        if (lst.length === 0) {
    90589803            ret = m;
     
    90609805            ret = [lst[0]];
    90619806            for (pos = 1, len = lst.length; pos < len; pos += 1) {
    9062                 ret.push(m[pos - 1]);
     9807                ret.push(m[pos - 1].replace(/\s*\-\s*/, "-", "g"));
    90639808                ret.push(lst[pos]);
    90649809            }
     
    90669811        return ret;
    90679812    };
    9068     expand = function (str) {
     9813    expand = function (str, hyphens) {
    90699814        str = "" + str;
    9070         lst = listify(str);
     9815        lst = listify(str, hyphens);
    90719816        len = lst.length;
    90729817        for (pos = 1; pos < len; pos += 2) {
     
    91439888        return stringify(lst);
    91449889    };
     9890    var sniff = function (str, func, minchars, isyear) {
     9891        var ret;
     9892        str = "" + str;
     9893        var lst;
     9894        if (!str.match(/[^\-\u20130-9 ,&]/)) {
     9895            lst = expand(str, "-");
     9896            ret = func(lst, minchars, isyear);
     9897        } else {
     9898            lst = expand(str, "\\s+\\-\\s+");
     9899            ret = func(lst, minchars, isyear);
     9900        }
     9901        return ret;
     9902    }
    91459903    if (!state.opt["page-range-format"]) {
    91469904        ret_func = function (str) {
     
    91499907    } else if (state.opt["page-range-format"] === "expanded") {
    91509908        ret_func = function (str) {
    9151             var lst = expand(str);
    9152             return stringify(lst);
     9909            return sniff(str, stringify);
    91539910        };
    91549911    } else if (state.opt["page-range-format"] === "minimal") {
    91559912        ret_func = function (str) {
    9156             var lst = expand(str);
    9157             return minimize(lst);
     9913            return sniff(str, minimize);
    91589914        };
    91599915    } else if (state.opt["page-range-format"] === "minimal-two") {
    91609916        ret_func = function (str, isyear) {
    9161             var lst = expand(str);
    9162             return minimize(lst, 2, isyear);
     9917            return sniff(str, minimize, 2, isyear);
    91639918        };
    91649919    } else if (state.opt["page-range-format"] === "chicago") {
    91659920        ret_func = function (str) {
    9166             var lst = expand(str);
    9167             return chicago(lst);
     9921            return sniff(str, chicago);
    91689922        };
    91699923    }
     
    926610020};
    926710021CSL.Util.FlipFlopper.prototype.init = function (str, blob) {
    9268     this.txt_esc = CSL.getSafeEscape(this.state.opt.mode, this.state.tmp.area);
    9269     str = this._normalizeString(str);
     10022    this.txt_esc = CSL.getSafeEscape(this.state);
    927010023    if (!blob) {
    927110024        this.strs = this.getSplitStrings(str);
     
    927910032};
    928010033CSL.Util.FlipFlopper.prototype._normalizeString = function (str) {
    9281     for (var i = 0, ilen = 2; i < ilen; i += 1) {
    9282         str = str.replace(this.quotechars[i + 2], this.quotechars[0]);
    9283         str = str.replace(this.quotechars[i + 4], this.quotechars[1]);
     10034    if (str.indexOf(this.quotechars[0]) > -1) {
     10035        for (var i = 0, ilen = 2; i < ilen; i += 1) {
     10036            if (this.quotechars[i + 2]) {
     10037                str = str.replace(this.quotechars[i + 2], this.quotechars[0]);
     10038            }
     10039        }
     10040    }
     10041    if (str.indexOf(this.quotechars[1]) > -1) {
     10042        for (var i = 0, ilen = 2; i < ilen; i += 1) {
     10043            if (this.quotechars[i + 4]) {
     10044                str = str.replace(this.quotechars[i + 4], this.quotechars[1]);
     10045            }
     10046        }
    928410047    }
    928510048    return str;
     
    928710050CSL.Util.FlipFlopper.prototype.getSplitStrings = function (str) {
    928810051    var strs, pos, len, newstr, head, tail, expected_closers, expected_openers, expected_flips, tagstack, badTagStack, posA, sameAsOpen, openRev, flipRev, tag, ibeenrunned, posB, wanted_closer, posC, sep, resplice, params, lenA, lenB, lenC, badTagPos, mx, myret;
     10052    str = this._normalizeString(str);
    928910053    mx = str.match(this.allTagsRexMatch);
    929010054    strs = str.split(this.allTagsRexSplit);
     
    949310257};
    949410258CSL.Output.Formatters = {};
    9495 CSL.getSafeEscape = function(outputModeOpt, outputArea) {
    9496     if (["bibliography", "citation"].indexOf(outputArea) > -1) {
    9497         return CSL.Output.Formats[outputModeOpt].text_escape;
     10259CSL.getSafeEscape = function(state) {
     10260    if (["bibliography", "citation"].indexOf(state.tmp.area) > -1) {
     10261        return CSL.Output.Formats[state.opt.mode].text_escape;
    949810262    } else {
    949910263        return function (txt) { return txt; };
     
    954410308CSL.Output.Formatters.title = function (state, string) {
    954510309    var str, words, isAllUpperCase, newString, lastWordIndex, previousWordIndex, upperCaseVariant, lowerCaseVariant, pos, skip, notfirst, notlast, aftercolon, len, idx, tmp, skipword, ppos, mx, lst, myret;
     10310    var SKIP_WORDS = state.locale[state.opt.lang].opts["skip-words"];
    954610311    str = CSL.Output.Formatters.doppelString(string, CSL.TAG_ESCAPE);
    954710312    if (!string) {
     
    956610331            var totallyskip = false;
    956710332            if (!isAllUpperCase || (words.length === 1 && words[pos].length < 4)) {
    9568                 if (words[pos] === upperCaseVariant) {
     10333                if (words[pos] !== lowerCaseVariant) {
    956910334                    totallyskip = true;
    957010335                }
     
    957210337            if (isAllUpperCase || words[pos] === lowerCaseVariant) {
    957310338                skip = false;
    9574                 for (var i = 0, ilen = CSL.SKIP_WORDS.length; i < ilen; i += 1) {
    9575                     skipword = CSL.SKIP_WORDS[i];
     10339                for (var i = 0, ilen = SKIP_WORDS.length; i < ilen; i += 1) {
     10340                    skipword = SKIP_WORDS[i];
    957610341                    idx = lowerCaseVariant.indexOf(skipword);
    957710342                    if (idx > -1) {
     
    963410399    return "";
    963510400};
    9636 CSL.demoteNoiseWords = function (fld) {
     10401CSL.demoteNoiseWords = function (state, fld) {
     10402    var SKIP_WORDS = state.locale[state.opt.lang].opts["skip-words"];
    963710403    if (fld) {
    963810404        fld = fld.split(/\s+/);
     
    964010406        var toEnd = [];
    964110407        for (var j  = fld.length - 1; j > -1; j += -1) {
    9642             if (CSL.SKIP_WORDS.indexOf(fld[j].toLowerCase()) > -1) {
     10408            if (SKIP_WORDS.indexOf(fld[j].toLowerCase()) > -1) {
    964310409                toEnd.push(fld.pop());
    964410410            } else {
     
    985210618    this.ambigcites = {};
    985310619    this.sorter = new CSL.Registry.Comparifier(state, "bibliography_sort");
    9854     this.getSortedIds = function (generatedItems) {
     10620    this.getSortedIds = function () {
    985510621        ret = [];
    985610622        for (i = 0, ilen = this.reflist.length; i < ilen; i += 1) {
     
    987010636    var i, ilen;
    987110637    this.oldseq = {};
     10638    var tmphash = {};
     10639    for (i = myitems.length - 1; i > -1; i += -1) {
     10640        if (tmphash[myitems[i]]) {
     10641            myitems = myitems.slice(0, i).concat(myitems.slice(i + 1));
     10642        } else {
     10643            tmphash[myitems[i]] = true;
     10644        }
     10645    }
    987210646    if (uncited_flag && this.mylist && this.mylist.length) {
    987310647        this.uncited = myitems;
     
    995110725            }
    995210726            if (this.state.sys.getAbbreviation) {
    9953                 for (var field in this.state.transform.abbrevs) {
     10727                for (var field in this.state.transform.abbrevs["default"]) {
    995410728                    switch (field) {
    995510729                    case "place":
    995610730                        if (Item["publisher-place"]) {
    9957                             this.state.sys.getAbbreviation(this.state.transform.abbrevs, field, Item["publisher-place"]);
     10731                            this.state.transform.loadAbbreviation(Item.jurisdiction, "place", Item["publisher-place"]);
    995810732                        } else if (Item["event-place"]) {
    9959                             this.state.sys.getAbbreviation(this.state.transform.abbrevs, field, Item["event-place"]);
     10733                            this.state.transform.loadAbbreviation(Item.jurisdiction, "place", Item["event-place"]);
    996010734                        }
    996110735                        break;
    9962                     case "institution":
     10736                    case "institution-part":
    996310737                        for (var creatorVar in CSL.CREATORS) {
    996410738                            for (var creatorList in Item[creatorVar]) {
     
    997010744                                        }
    997110745                                        if (subOrganizations) {
    9972                                             subOrganizations = subOrganizations.split(/\s*,\s*/);
     10746                                            subOrganizations = subOrganizations.split(/\s*|\s*/);
    997310747                                            for (k = 0, klen = subOrganizations.length; k < klen; k += 1) {
    9974                                                 this.state.sys.getAbbreviation(this.state.transform.abbrevs, field, subOrganizations[k]);
     10748                                                this.state.transform.loadAbbreviation(Item.jurisdiction, "institution-part", subOrganizations[k]);
     10749                                                }
    997510750                                            }
    997610751                                        }
     
    997810753                                }
    997910754                            }
     10755                            break;
     10756                        default:
     10757                            if (Item[field]) {
     10758                                this.state.transform.loadAbbreviation(Item.jurisdiction, field, Item[field]);
     10759                            }
     10760                            break;
    998010761                        }
    9981                         break;
    9982                     default:
    9983                         if (Item[field]) {
    9984                             this.state.sys.getAbbreviation(this.state.transform.abbrevs, field, Item[field]);
    9985                         }
    9986                         break;
    9987                     }
    9988                 }
     10762                    }
    998910763            }
    999010764            akey = CSL.getAmbiguousCite.call(this.state, Item);
     
    1004710821            regtoken.ambig = undefined;
    1004810822            Item = this.state.retrieveItem(key);
     10823            var akey = regtoken.ambig;
    1004910824            if ("undefined" === typeof akey) {
    1005010825                akey = CSL.getAmbiguousCite.call(this.state, Item);
     
    1016710942};
    1016810943CSL.getSortKeys = function (Item, key_type) {
    10169     var area, strip_prepositions, use_parallels, len, pos;
     10944    var area, extension, strip_prepositions, use_parallels, len, pos;
    1017010945    area = this.tmp.area;
     10946    extension = this.tmp.extension;
    1017110947    strip_prepositions = CSL.Util.Sort.strip_prepositions;
    1017210948    this.tmp.area = key_type;
     10949    this.tmp.extension = "_sort";
    1017310950    this.tmp.disambig_override = true;
    1017410951    this.tmp.disambig_request = false;
     
    1018510962    }
    1018610963    this.tmp.area = area;
     10964    this.tmp.extension = extension;
    1018710965    return this[key_type].keys;
    1018810966};
     
    1021710995              return 2;
    1021810996        }
    10219         nameobj = state.transform.name(state, nameobj, state.opt["locale-pri"]);
     10997        var res = state.nameOutput.getName(nameobj, "locale-translit", true);
     10998        nameobj = res.name;
    1022010999        set_keys(this.state, "" + item_id, nameobj);
    1022111000        param = 2;
     
    1023411013            return param;
    1023511014        }
    10236         if (gdropt_orig === "by-cite" && param < request_base) {
    10237             param = request_base;
     11015        if (gdropt_orig === "by-cite" && param <= request_base) {
     11016            return request_base;
    1023811017        }
    1023911018        if (!dagopt) {
     
    1040311182    };
    1040411183    addname = function (item_id, nameobj, pos) {
    10405         nameobj = state.transform.name(state, nameobj, state.opt["locale-pri"]);
     11184        var res = state.nameOutput.getName(nameobj, "locale-translit", true);
     11185        nameobj = res.name;
    1040611186        if (state.opt["givenname-disambiguation-rule"]
    1040711187            && state.opt["givenname-disambiguation-rule"].slice(0, 8) === "primary-"
     
    1045311233    this.ambigcites = state.registry.ambigcites;
    1045411234    this.configModes();
    10455     this.clashes = [1, 0];
     11235    this.debug = false;
    1045611236};
    1045711237CSL.Disambiguation.prototype.run = function(akey) {
     
    1046811248        this.gnameset = 0;
    1046911249        this.gname = 0;
     11250        this.clashes = [1, 0];
    1047011251        while(this.lists[pos][1].length) {
    1047111252            this.listpos = pos;
     
    1047311254                this.base = this.lists[pos][0];
    1047411255            }
    10475             if (this.rerun) {
    10476                 this.rerun = false;
    10477             } else {
    10478                 this.scanItems(this.lists[pos], 0);
    10479             }
    1048011256            var names_used = [];
    1048111257            ismax = this.incrementDisambig();
    1048211258            this.scanItems(this.lists[pos], 1);
    10483             if (this.clashes[1] < this.clashes[0]) {
    10484                 this.base.better = this.base.names.slice();
    10485             }
    1048611259            this.evalScan(ismax);
    1048711260        }
     
    1050211275    ItemCite = tempResult[3];
    1050311276    this.partners.push(Item);
    10504     this.clashes[phase] = 0;
    1050511277    this.nonpartners = [];
     11278    var clashes = 0;
    1050611279    for (pos = 1, len = list[1].length; pos < len; pos += 1) {
    1050711280        otherItem = list[1][pos];
     
    1050911282        var otherItemCite = otherItemData[3];
    1051011283        if (ItemCite === otherItemCite) {
    10511             this.clashes[phase] += 1;
     11284            clashes += 1;
    1051211285            this.partners.push(otherItem);
    1051311286        } else {
     
    1051511288        }
    1051611289    }
     11290    this.clashes[0] = this.clashes[1];
     11291    this.clashes[1] = clashes;
    1051711292};
    1051811293CSL.Disambiguation.prototype.evalScan = function (ismax) {
     
    1052811303        this.lists[this.listpos] = [this.base, []];
    1052911304    } else if (this.clashes[1] === 0) {
    10530         mybase = CSL.cloneAmbigConfig(this.base);
    10531         mybase.year_suffix = false;
    10532         this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, mybase);
     11305        this.betterbase = CSL.cloneAmbigConfig(this.base);
     11306        this.betterbase.year_suffix = false;
     11307        this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, this.betterbase);
    1053311308        this.lists[this.listpos] = [this.base, []];
    1053411309    } else if (this.nonpartners.length === 1) {
    10535         mybase = CSL.cloneAmbigConfig(this.base);
    10536         mybase.year_suffix = false;
    10537         this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, mybase);
     11310        this.betterbase = CSL.cloneAmbigConfig(this.base);
     11311        this.betterbase.year_suffix = false;
     11312        this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.betterbase);
    1053811313        this.lists[this.listpos] = [this.base, this.partners];
    1053911314    } else if (this.clashes[1] < this.clashes[0]) {
     11315        this.betterbase = CSL.cloneAmbigConfig(this.base);
    1054011316        this.lists[this.listpos] = [this.base, this.partners];
    10541         if (this.nonpartners.length === 1) {
    10542             this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.base);
    10543         } else {
    10544             this.lists.push([this.base, this.nonpartners]);
    10545         }
     11317        this.lists.push([this.base, this.nonpartners]);
    1054611318    } else {
    10547         if (ismax || this.advance_mode) {
    10548             if (ismax) {
    10549                 var better = this.lists[this.listpos][0].better;
    10550                 if (better) {
    10551                     this.base.names = better.slice();
    10552                 } else {
    10553                     this.base = new CSL.AmbigConfig();
    10554                 }
    10555                 this.lists[this.listpos] = [this.base, this.nonpartners];
    10556             }
     11319        if (ismax) {
     11320            if (this.betterbase) {
     11321                this.base = CSL.cloneAmbigConfig(this.betterbase);
     11322            }
     11323            this.lists[this.listpos] = [this.base, this.nonpartners];
    1055711324            for (pos = 0, len = this.partners.length; pos < len; pos += 1) {
    1055811325                this.state.registry.registerAmbigToken(this.akey, "" + this.partners[pos].id, this.base);
    1055911326            }
    10560         } else {
    10561             this.rerun = true;
    10562         }
    10563     }
    10564 };
    10565 CSL.Disambiguation.prototype.disGivens = function (ismax) {
    10566     var pos, len, mybase;
    10567     if (this.clashes[1] === 0 && this.nonpartners.length === 1) {
    10568         if (this.clashes[0] === 1) {
    10569             this.base = this.decrementNames();
    10570         }
    10571         mybase = CSL.cloneAmbigConfig(this.base);
    10572         mybase.year_suffix = false;
    10573         this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, this.base);
    10574         this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, mybase);
    10575         this.lists[this.listpos] = [this.base, []];
    10576     } else if (this.clashes[1] === 0) {
    10577         if (this.clashes[0] === 1) {
    10578             this.base = this.decrementNames();
    10579         }
    10580         mybase = CSL.cloneAmbigConfig(this.base);
    10581         mybase.year_suffix = false;
    10582         this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, mybase);
    10583         this.lists[this.listpos] = [this.base, this.nonpartners];
    10584     } else if (this.nonpartners.length === 1) {
    10585         if (this.clashes[0] === 1) {
    10586             this.base = this.decrementNames();
    10587         }
    10588         mybase = CSL.cloneAmbigConfig(this.base);
    10589         mybase.year_suffix = false;
    10590         this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, mybase);
    10591         this.lists[this.listpos] = [this.base, this.partners];
    10592     } else if (this.clashes[1] < this.clashes[0]) {
    10593         this.lists[this.listpos] = [this.base, this.partners];
    10594         if (this.nonpartners.length === 1) {
    10595             this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.base);
    10596         } else {
    10597             this.lists.push([this.base, this.nonpartners]);
    10598         }
    10599     } else {
    10600         this.base = CSL.cloneAmbigConfig(this.oldbase);
    10601         if (ismax || this.advance_mode) {
    10602             if (ismax) {
    10603                 var better = this.lists[this.listpos][0].better;
    10604                 if (better) {
    10605                     this.base.names = better.slice();
    10606                 } else {
    10607                     this.base = new CSL.AmbigConfig();
    10608                 }
    10609                 this.lists[this.listpos] = [this.base, this.nonpartners];
    10610             }
    10611             for (pos = 0, len = this.partners.length; pos < len; pos += 1) {
    10612                 this.state.registry.registerAmbigToken(this.akey, "" + this.partners[pos].id, this.base);
    10613             }
    10614         } else {
    10615             this.rerun = true;
    1061611327        }
    1061711328    }
     
    1061911330CSL.Disambiguation.prototype.disExtraText = function () {
    1062011331    var pos, len, mybase;
    10621     if (this.clashes[1] === 0) {
    10622         mybase = CSL.cloneAmbigConfig(this.base);
    10623         mybase.year_suffix = false;
     11332    mybase = CSL.cloneAmbigConfig(this.base);
     11333    mybase.year_suffix = false;
     11334    if (this.clashes[1] === 0 || this.clashes[1] < this.clashes[0]) {
    1062411335        this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, mybase);
    10625         if (this.nonpartners.length === 1) {
    10626             this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, mybase);
    10627             this.lists[this.listpos] = [this.base,[]];
    10628         } else {
    10629             this.lists[this.listpos] = [this.base, this.nonpartners];
     11336        for (var i=0, ilen=this.partners.length; i < ilen; i += 1) {
     11337            this.state.registry.registerAmbigToken(this.akey, "" + this.partners[i].id, mybase);
     11338        }
     11339        for (var i=0, ilen=this.nonpartners.length; i < ilen; i += 1) {
     11340            this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[i].id, mybase);
    1063011341        }
    1063111342    } else {
    10632         this.base.disambiguate = false;
    10633         this.lists[this.listpos] = [this.base, this.lists[this.listpos][1].slice(1)];
    10634     }
     11343        for (var i=0, ilen=this.partners.length; i < ilen; i += 1) {
     11344            this.state.registry.registerAmbigToken(this.akey, "" + this.partners[i].id, this.betterbase);
     11345        }
     11346    }
     11347    this.lists[this.listpos] = [this.base, []];
    1063511348};
    1063611349CSL.Disambiguation.prototype.disYears = function () {
    1063711350    var pos, len, tokens, token, item;
    1063811351    tokens = [];
    10639     for (pos = 0, len = this.lists[this.listpos][1].length; pos < len; pos += 1) {
    10640         token = this.registry[this.lists[this.listpos][1][pos].id];
    10641         tokens.push(token);
     11352    if (this.clashes[1]) {
     11353        for (pos = 0, len = this.lists[this.listpos][1].length; pos < len; pos += 1) {
     11354            token = this.registry[this.lists[this.listpos][1][pos].id];
     11355            tokens.push(token);
     11356        }
    1064211357    }
    1064311358    tokens.sort(this.state.registry.sorter.compareKeys);
     
    1065911374CSL.Disambiguation.prototype.incrementDisambig = function () {
    1066011375    var val, maxed;
    10661     maxed = false;
    10662     this.oldbase = CSL.cloneAmbigConfig(this.base);
    10663     if (this.advance_mode) {
    10664         this.modeindex += 1;
    10665         this.advance_mode = false;
    10666     }
    10667     if (!maxed && "disNames" === this.modes[this.modeindex]) {
    10668         if (this.base.names[this.nnameset] < this.maxvals[this.nnameset]) {
    10669             this.base.names[this.nnameset] += 1;
     11376    var maxed = false;
     11377    if ("disNames" === this.modes[this.modeindex]) {
     11378        var increment_name = false;
     11379        var increment_nameset = false;
     11380        if (this.state.opt["disambiguate-add-givenname"] && this.state.opt["givenname-disambiguation-rule"] === "by-cite") {
     11381            if (this.base.givens[this.gnameset][this.gname] < 2) {
     11382                this.base.givens[this.gnameset][this.gname] += 1;
     11383            } else {
     11384                this.base.givens[this.gnameset][this.gname] = this.betterbase.givens[this.gnameset][this.gname];
     11385                if (this.gname < this.base.names[this.gnameset]) {
     11386                    this.gname += 1;
     11387                    this.base.names[this.gnameset] += 1;
     11388                    this.base.givens[this.gnameset][this.gname] += 1;
     11389                } else {
     11390                    increment_name = true;
     11391                }
     11392            }
    1067011393        } else {
    10671             if (this.nnameset < (this.base.names.length - 1)) {
    10672                 this.nnameset += 1;
    10673             }
    10674             if (this.base.names[this.nnameset] < this.maxvals[this.nnameset]) {
    10675                 this.base.names[this.nnameset] += 1;
    10676             }
    10677         }
    10678         if (this.nnameset === (this.base.names.length - 1) && this.base.names[this.nnameset] === this.maxvals[this.nnameset]) {
    10679             if (this.modeindex === (this.modes.length - 1)) {
    10680                 return true;
    10681             } else {
    10682                 maxed = false;
    10683             }
    10684         }
    10685     }
    10686     if (!maxed && "disGivens" === this.modes[this.modeindex]) {
    10687         if (this.gname < this.maxvals[this.gnameset]) {
    10688             if (this.base.givens[this.gnameset][this.gname] === this.minval) {
    10689                 this.base.givens[this.gnameset][this.gname] += 1;
    10690             }
    10691             this.base.givens[this.gnameset][this.gname] += 1;
    10692             this.gname += 1;
    10693         } else {
    10694             if (this.gnameset < (this.base.givens.length - 1)) {
    10695                 this.gnameset += 1;
    10696                 this.gname = 0;
    10697             }
    10698             if (this.gname < this.maxvals[this.gnameset]) {
    10699                 this.base.givens[this.gnameset][this.gname] += 1;
    10700                 this.gname += 1;
    10701             }
    10702         }
    10703     }
    10704     if (!maxed && "disExtraText" === this.modes[this.modeindex]) {
    10705         maxed = false;
     11394            increment_name = true;
     11395        }
     11396        if (this.state.opt["disambiguate-add-names"]) {
     11397            if (increment_name) {
     11398                if (this.base.names[this.gnameset] < this.maxvals[this.gnameset]) {
     11399                    this.gname += 1;
     11400                    this.base.names[this.gnameset] += 1;
     11401                } else {
     11402                    increment_nameset = true;
     11403                }
     11404            }
     11405            if (increment_nameset) {
     11406                if (this.gnameset < this.base.names.length - 1) {
     11407                    this.gnameset += 1;
     11408                    this.gname = 0;
     11409                    if (this.state.opt["disambiguate-add-givenname"] && this.state.opt["givenname-disambiguation-rule"] === "by-cite") {
     11410                        this.base.givens[this.gnameset][this.gname] += 1;
     11411                    } else if (this.base.names[this.gnameset] < this.maxvals[this.gnameset]) {
     11412                        this.gname += 1;
     11413                        this.base.names[this.gnameset] += 1;
     11414                    }
     11415                } else {
     11416                    maxed = true;
     11417                    this.base = CSL.cloneAmbigConfig(this.betterbase);
     11418                    if (this.modeindex < this.modes.length - 1) {
     11419                        this.modeindex += 1;
     11420                    }
     11421                }
     11422            }
     11423        } else if (increment_name) {
     11424            maxed = true;
     11425            if (this.modeindex < this.modes.length - 1) {
     11426                this.modeindex += 1;
     11427            }
     11428        }
     11429    }
     11430    if ("disExtraText" === this.modes[this.modeindex]) {
    1070611431        this.base.disambiguate = true;
    10707         if (this.modeindex === (this.modes.length - 1)) {
    10708             return true;
    10709         } else {
    10710             maxed = false;
    10711         }
    10712     }
    10713     if (!maxed && "disYears" === this.modes[this.modeindex]) {
    10714         maxed = false;
    10715     }
    10716     if (!maxed && this.modes[this.modeindex] === "disGivens") {
    10717         if ((this.gnameset >= (this.base.names.length - 1) && ("undefined" === typeof this.maxvals[this.gnameset] || this.gname === this.maxvals[this.gnameset])) || this.base.names.length === 0) {
    10718             if (this.modeindex === (this.modes.length - 1)) {
    10719                 maxed = true;
    10720             } else {
    10721                 this.advance_mode = true;
    10722             }
    10723         }
    10724     }
    10725     if (!maxed && this.modes[this.modeindex] === "disNames") {
    10726         if ((this.nnameset >= (this.base.names.length - 1) && ("undefined" === typeof this.maxvals[this.nnameset] ||this.base.names[this.nnameset] === this.maxvals[this.nnameset])) || this.base.names.length === 0) {
    10727             if (this.modeindex === (this.modes.length - 1)) {
    10728                 maxed = true;
    10729             } else {
    10730                 this.advance_mode = true;
    10731             }
    10732         }
     11432    }
     11433    if ("disYears" === this.modes[this.modeindex]) {
    1073311434    }
    1073411435    return maxed;
     
    1074611447    this.lists = [];
    1074711448    this.base = false;
     11449    this.betterbase = false;
    1074811450    this.akey = akey;
    10749     this.advance_mode = false;
    1075011451    myItemBundles = [];
    1075111452    this.old_desc = {};
     
    1075511456            var myItem = this.state.retrieveItem("" + myIds[i]);
    1075611457            var myDesc = this.getItemDesc(myItem);
     11458            if (!this.betterbase) {
     11459                this.betterbase = CSL.cloneAmbigConfig(myDesc[0]);
     11460                this.base = myDesc[0];
     11461                this.maxvals = myDesc[1];
     11462                this.minval = myDesc[2];
     11463            }
    1075711464            myItemBundles.push([myDesc, myItem]);
    1075811465            this.old_desc[myIds[i]] = [this.state.registry.registry[myIds[i]].disambig.year_suffix, this.state.registry.registry[myIds[i]].disambig.disambiguate];
     
    1078611493    var dagopt, gdropt;
    1078711494    this.modes = [];
    10788     if (this.state.opt["disambiguate-add-names"]) {
    10789         this.modes.push("disNames");
    10790     }
    1079111495    dagopt = this.state.opt["disambiguate-add-givenname"];
    1079211496    gdropt = this.state.opt["givenname-disambiguation-rule"];
    10793     if (dagopt && gdropt === "by-cite") {
    10794         this.modes.push("disGivens");
     11497    if (this.state.opt['disambiguate-add-names'] || (dagopt && gdropt === "by-cite")) {
     11498        this.modes.push("disNames");
    1079511499    }
    1079611500    if (this.state.opt.has_disambiguate) {
     
    1080011504        this.modes.push("disYears");
    1080111505    }
    10802 };
    10803 CSL.Disambiguation.prototype.decrementNames = function () {
    10804     var base_return, do_me, i, j, pos, len, ppos, llen, ids;
    10805     base_return = CSL.cloneAmbigConfig(this.base);
    10806     do_me = false;
    10807     len = base_return.givens.length - 1;
    10808     for (pos = len; pos > -1; pos += -1) {
    10809         llen = base_return.givens[pos].length - 1;
    10810         for (ppos = llen; ppos > -1; ppos += -1) {
    10811             if (base_return.givens[pos][ppos] > this.oldbase.givens[pos][ppos]) {
    10812                 do_me = true;
    10813             }
    10814         }
    10815     }
    10816     if (do_me) {
    10817         len = base_return.givens.length - 1;
    10818         for (pos = len; pos > -1; pos += -1) {
    10819             llen = base_return.givens[pos].length - 1;
    10820             for (ppos = llen; ppos > -1; ppos += -1) {
    10821                 if (base_return.givens[pos][ppos] > this.oldbase.givens[pos][ppos]) {
    10822                     break;
    10823                 }
    10824                 if (ppos < base_return.names[pos]) {
    10825                     base_return.names[pos] += -1;
    10826                 }
    10827             }
    10828         }
    10829     }
    10830     return base_return;
    1083111506};
    1083211507CSL.Registry.CitationReg = function (state) {
  • kcite/trunk/kcite-citeproc/kcite.js

    r473946 r506635  
    5757        citeproc.setOutputFormat( "kcite" );
    5858       
     59        // store all the ids that we are going to use. We register these with
     60        // citeproc, which should mean that references which would otherwise
     61        // be identical, can be disambiguated ("2011a, 2011b").
     62        var cite_ids = [];
     63       
    5964        // select all of the kcite citations
    6065        $(this).find(".kcite").each( function(index){
    61            
    62             var cite = sys.retrieveItem( $(this).attr( "kcite-id" ) )
    63 
     66            var cite_id = $(this).attr( "kcite-id" );
     67            var cite = sys.retrieveItem( cite_id );
    6468            // not sure about closure semantics with jquery -- this might not be necessary
    6569            var kcite_element = $(this);
    6670
    6771            if( cite["resolved"] ){
    68            
     72                cite_ids.push( cite_id );
     73               
    6974                // check here whether resolved == true before proceeding.
    7075                var citation_object = {
     
    8489                // TODO the citation object returned may include errors which we
    8590                // haven't checked for here.
    86                
    87                
    8891                task_queue.push(
    8992                    function(){
    90                         var citation_string = citeproc.
    91                             appendCitationCluster( citation_object )[ 0 ][ 1 ];
     93                        var cite_id = kcite_element.attr( "kcite-id" );
     94                        var cite = sys.retrieveItem( cite_id );
     95
     96                        // the true here should mean that citeproc always
     97                        // returns only a single element array. It doesn't
     98                        // seem to work, as ambiguous cases still return more.
     99                        var citation = citeproc.
     100                            appendCitationCluster( citation_object, true );
     101                        // citeproc's wierd return values. Last element is citation we want.
     102                        // last element again is the HTML.
     103                        var citation_string = citation.pop().pop();
    92104                                       
    93105                        var citation =  "<a href=\"#" +
    94                             kcite_element.attr( "kcite-id" ) + "\">" +
    95                             citation_string + "</a>";
    96                        
     106                                cite_id + "\">" +
     107                                citation_string + "</a>"
     108                                + "<a href=\"" + cite["URL"] + "\">*</a>";
     109                                               
    97110                        kcite_element.html( citation );
    98111                    });
     
    122135               
    123136               
     137               
    124138            }
    125139        });
    126140       
    127                
     141        // update citeproc with all the ids we will use (which will happen
     142        // when we tail recurse).
     143        citeproc.updateItems( cite_ids );
     144       
    128145        var kcite_bib_element = $(this);
    129146       
     
    133150            $.each( citeproc.makeBibliography()[ 1 ],
    134151                    function(index,item){
    135                         bib_string = bib_string + item
     152                        // URL linkify here
     153                        // this is not well done as it will be style dependant.
     154                        var http = item.lastIndexOf("http");
     155                        var url = item.substring
     156                        ( http,item.lastIndexOf(".") );
     157                       
     158                        var bib_item =
     159                            item.substring( 0, http ) +
     160                            "<a href=\"" + url + "\">"
     161                            + url + "</a>.";
     162                        bib_string = bib_string + bib_item;
    136163                    });
    137164       
     
    170197       
    171198        // tail-end recurse with timeout
    172         setTimeout( iter, 2 );
     199        setTimeout( iter, 0.5 );
    173200    };
    174201   
  • kcite/trunk/kcite.php

    r497290 r506635  
    3737  static $bibliography;
    3838 
    39   // debug option -- ignore transients which disables the cache
    40   static $ignore_transients = false;
    4139  // delete any transients as we are going, which deletes the cache
    4240  static $clear_transients = false;
     
    6765   
    6866    //provide links to the bibliography in various formats
    69     add_action('template_redirect', array(__CLASS__, 'bibliography_output'));
     67    //add_action('template_redirect', array(__CLASS__, 'bibliography_output'));
     68
    7069    //add settings menu link to sidebar
    7170    add_action('admin_menu', array(__CLASS__, 'refman_menu'));
     
    7776                array( __CLASS__, 'add_script' ) );
    7877
     78
     79    add_option( "kcite-cache", true );
    7980  }
    8081
     
    169170      }
    170171      else{
    171           $in_text = "$source:$content";
     172          // this needs replacing with a URL
     173          $stub = array
     174              (
     175               "doi" => "http://dx.doi.org",
     176               "pubmed" => "http://www.ncbi.nlm.nih.gov/pubmed",
     177               "arxiv" => "http://arxiv.org/abs"
     178               );
     179         
     180          $url = "$stub[$source]/$content";
     181          $in_text = "<a href=\"$url\">$url</a>";
    172182          $anchor = self::$bibliography->add_cite( $cite );
    173183          return "<span class=\"kcite\" kcite-id=\"ITEM-$anchor\">($in_text)</span>\n";
     
    193203     
    194204      // // get the metadata which we are going to use for the bibliography.
    195       $cites = self::get_arrays($cites);
     205      $cites = self::resolve_metadata($cites);
    196206     
    197207      if( !get_option( "citeproc" ) ){
     
    373383   * This can be used to build the JSON.
    374384   */
    375   private function get_arrays($cites) {
     385  private function resolve_metadata($cites) {
    376386     
    377387      $start_time = time();
     
    390400         
    391401          if ($cite->source == 'doi') {
    392               $cite = self::crossref_doi_lookup($cite);
     402              // should work on datacite or crossref lookup
     403              $cite = self::dx_doi_lookup($cite);
     404             
    393405              //failover to pubmed
    394406              if (!$cite->resolved) {
    395407                  $cite = self::pubmed_doi_lookup($cite);
    396                  
    397                   if (!$cite->resolved) {
    398                       $cite->error = true;
    399                       continue;
    400                   }
    401                  
    402                   $cite = self::array_from_xml($cite);
     408              }
     409
     410              if($cite->resolved && $cite->resolved_from=="crossref" ){
     411                  $cite = self::get_crossref_metadata($cite);
     412                  continue;
     413              }
     414             
     415              if($cite->resolved && $cite->resolved_from=="datacite" ){
     416                  $cite = self::parse_xml($cite);
     417                  $cite = self::get_datacite_metadata($cite);
     418                  continue;
     419              }
     420               
     421              if( $cite->resolved && $cite->resolved_from="pubmed" ){
     422                  $cite = self::parse_xml($cite);
    403423                  $cite = self::get_pubmed_metadata($cite);
    404424                  continue;
    405425              }
    406426             
    407               $cite = self::array_from_xml($cite);
    408               $cite = self::get_crossref_metadata($cite);
     427              // doi we can't find.
     428              $cite->error = true;
    409429              continue;
    410430          }
     
    413433              $cite = self::pubmed_id_lookup($cite);
    414434             
    415                   if (!$cite->resolved) {
     435              if (!$cite->resolved) {
    416436                  $cite->error = true;
    417437                  continue;
     
    419439             
    420440             
    421               $cite = self::array_from_xml($cite);
     441              $cite = self::parse_xml($cite);
    422442              $cite = self::get_pubmed_metadata($cite);
    423443              continue;
    424444          }
    425          
    426           // if we don't recognise the type if will remain unresolved.
    427           // This is okay and will be dealt with later
    428       }
    429 
     445
     446          if( $cite->source == "arxiv"){
     447              $cite = self::arxiv_id_lookup($cite);
     448              if( !$cite->resolved){
     449                  $cite->error = true;
     450                  continue;
     451              }
     452              $cite = self::parse_xml($cite);
     453              $cite = self::get_arxiv_metadata($cite);
     454              continue;
     455          }
     456
     457         
     458          // if we don't recognise the type then we have an error
     459          $cite->error = true;
     460      }
    430461     
    431462      return $cites;
     
    436467   * Attempt to resolve metadata for a citation object
    437468   * @param string $pub_doi A doi representing a reference
    438    * @return
    439    */
    440   private function crossref_doi_lookup($cite) {
    441     //use CrossRef ID provided on the options page
    442     $crossref = get_option('crossref_id');
    443     if (!$crossref) {
    444         //automatically failover to pubmed without trying to connect to crossref
    445         return $cite;
    446     }
    447    
    448     $trans_slug = "crossref-doi" . $cite->identifier;
    449 
    450     // debug code -- blitz transients in the database
    451     if( self::$clear_transients ){
    452         delete_transient( $trans_slug );
    453     }
    454    
    455     // check for transients
    456     if (false === (!self::$ignore_transients && $xml = get_transient( $trans_slug ))) {
    457 
    458         // print( "crossref lookup:$trans_slug: " . date( "H:i:s", time() ) ."\n" );
    459         $url = "http://www.crossref.org/openurl/?noredirect=true&pid="
    460             .$crossref."&format=unixref&id=doi:".$cite->identifier;
    461         $xml = file_get_contents($url, 0);
    462    
    463         if (preg_match('/not found in CrossRef/', $xml)) {
    464             //null will cause failover to PubMed (if no metadata in crossref)
    465             return $cite;
    466         }
    467        
    468 
    469         if (preg_match('/login you supplied is not recognized/', $xml)) {
    470             //null will cause failover to PubMed (if no valid login supplied)
    471             return $cite;
    472         }
    473    
    474         // transient for 1 week -- need to option this.
    475         set_transient( $trans_slug, $xml, 60*60*24*7 );
    476     }
    477    
    478 
    479 
    480     $cite->resolved = true;
    481     $cite->resolution_source=$xml;
    482     $cite->resolved_from="crossref";
     469   * @return
     470   */
     471  private function dx_doi_lookup($cite) {
     472
     473      // slug includes JSON so we ignore XML in the database from previous
     474      // incarnations of kcite.
     475      $crossref_trans_slug = "crossref-doi-json" . $cite->identifier;
     476      $datacite_trans_slug = "datacite-doi-xml" . $cite->identifier;
     477     
     478      // debug code -- blitz transients in the database
     479      if( self::$clear_transients ){
     480          delete_transient( $crossref_trans_slug );
     481          delete_transient( $datacite_trans_slug );
     482      }
     483     
     484      if(get_option("kcite-cache") && $json = get_transient($crossref_trans_slug)){
     485          $cite->resolved = true;
     486          $cite->resolution_source=$json;
     487          $cite->resolved_from="crossref";
     488          return $cite;
     489      }
     490
     491      if(get_option("kcite-cache") && $xml = get_transient($datacite_trans_slug)){
     492          $cite->resolved = true;
     493          $cite->resolution_source=$xml;
     494          $cite->resolved_from="datacite";
     495          return $cite;
     496      }
     497         
     498         
     499      $url = "http://dx.doi.org/{$cite->identifier}";
     500         
     501      // get the metadata with negotiation
     502      $ch = curl_init();
     503      curl_setopt ($ch, CURLOPT_URL, $url );
     504      curl_setopt ($ch, CURLOPT_RETURNTRANSFER, true );
     505      curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, true );
     506      // the order here is important, as both datacite and crossrefs content negotiation is broken.
     507      // crossref only return the highest match, but do check other content
     508      // types. So, should return json. Datacite is broken, so only return the first
     509      // content type, which should be XML.
     510      curl_setopt ($ch, CURLOPT_HTTPHEADER,
     511                   array (
     512                          "Accept: application/x-datacite+xml;q=0.9, application/citeproc+json;q=1.0"
     513                          ));
     514     
     515      // debug
     516      //$fh = fopen('/tmp/curl.log', 'w');
     517      //curl_setopt($ch, CURLOPT_STDERR, $fh );
     518      //curl_setopt($ch, CURLOPT_VERBOSE, true );
     519     
     520      $response = curl_exec ($ch);
     521      $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
     522      $contenttype = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
     523     
     524      // it's probably not a DOI at all. Need to check some more here.
     525      if( curl_errno( $ch ) == 404 ){
     526          curl_close($ch);
     527          return $cite;
     528      }           
     529
     530      curl_close ($ch);
     531               
     532
     533      if( $contenttype == "application/citeproc+json" ){
     534          // crossref DOI
     535          $cite->resolved = true;
     536          $cite->resolution_source=$response;
     537          $cite->resolved_from="crossref";
     538         
     539          // set the transient with a slug so we can distinguish from datacite
     540          set_transient( $crossref_trans_slug, $response, 60*60*24*7 );
     541
     542          return $cite;
     543      }
     544             
     545      if( $contenttype == "application/x-datacite+xml" ){
     546          //datacite DOI
     547          $cite->resolved = true;
     548          $cite->resolution_source=$response;
     549          $cite->resolved_from="datacite";
     550         
     551          // set the transient with a slug so we can distinguish from crossref
     552          set_transient( $datacite_trans_slug, $response, 60*60*24*7 );
     553          return $cite;
     554      }
    483555
    484556    return $cite;
     
    500572      }
    501573   
    502       if (false === (!self::$ignore_transients && $id = get_transient( $trans_slug ))) {
     574      if (false === (get_option( "cache" )
     575                     && $id = get_transient( $trans_slug ))) {
    503576
    504577          // print( "pubmed_doi lookup:$trans_slug " . date( "H:i:s", time() ) . "\n" );
     
    511584         
    512585          if (preg_match('/PhraseNotFound/', $search_xml)) {
    513               //handles DOI lookup failures
    514               $cite->error = true;
    515586              return $cite;
    516587          }
     
    548619      }
    549620   
    550       if (false === (!self::$ignore_transients && $xml = get_transient( $trans_slug ))) {
     621      if (false === (get_option( "kcite-cache") && $xml = get_transient( $trans_slug ))) {
    551622
    552623          // print( "pubmed_id lookup: $trans_slug" . date( "H:i:s", time() ) . "\n" );
     
    562633          set_transient( $trans_slug, $xml, 60*60*24*7 );
    563634      }
    564 
     635     
    565636      $cite->resolved = true;
    566637      $cite->resolution_source = $xml;
     
    569640  }
    570641
     642
     643  private function arxiv_id_lookup($cite){
     644      $trans_slug = "arxiv-id" . $cite->identifier;
     645     
     646      if( self::$clear_transients ){
     647          delete_transient( $trans_slug );
     648      }
     649      if( false === (get_option( "kcite-cache")
     650                     && $xml = get_transient( $trans_slug ))) {
     651     
     652          $fetch = "http://export.arxiv.org/oai2?verb=GetRecord&identifier=oai:arXiv.org:" . $cite->identifier . "&metadataPrefix=arXiv";
     653
     654          $xml = file_get_contents( $fetch, 0 );
     655          // TODO failure handling here
     656          set_transient( $trans_slug, $xml, 60*60*24*7 );
     657      }
     658     
     659      $cite->resolved = true;
     660      $cite->resolution_source = $xml;
     661      $cite->resolved_from = "arxiv";
     662     
     663      return $cite;
     664  }
     665
    571666  /**
    572667   * Parses XML in a citation object into a PhP array
     
    574669   * @return Citation with parsedXML now containing SimpleXMLElement object
    575670   */
    576   private function array_from_xml($cite) {
     671  private function parse_xml($cite) {
    577672      $cite->parsedXML = new SimpleXMLElement( $cite->resolution_source );
    578673      return $cite;
     
    581676  /**
    582677   * Badly named method, restful API showing just the JSON object for the reference list.
    583    * Not fully functional at the moment; works if there are no rewrite rules.
     678   * This is not functional at the moment.
    584679   *
    585680   */
    586   function bibliography_output() {
    587     global $post;
    588     $uri = self::get_requested_uri();
    589     if ($uri[0] == 'json') {
    590         //render the json here
    591         $this_post = get_post($post->ID, ARRAY_A);
    592         $post_content = $this_post['post_content'];
    593         $dois = self::get_cites($post_content);
    594         $metadata = array();
    595         $metadata = self::get_arrays($dois[1]);
    596         $json = self::metadata_to_json($metadata);
    597         echo $json;
    598         exit;
    599     }
    600     elseif ($uri[0] == 'bib') {
    601         //render bibtex here
    602         exit;
    603     }
    604     elseif ($uri[0] == 'ris') {
    605         //render ris here
    606         exit; //prevents rest of page rendering
    607     }
    608   }
     681  // function bibliography_output() {
     682  //   global $post;
     683  //   $uri = self::get_requested_uri();
     684  //   if ($uri[0] == 'json') {
     685  //       //render the json here
     686  //       $this_post = get_post($post->ID, ARRAY_A);
     687  //       $post_content = $this_post['post_content'];
     688  //       $dois = self::get_cites($post_content);
     689  //       $metadata = array();
     690  //       $metadata = self::get_arrays($dois[1]);
     691  //       $json = self::metadata_to_json($metadata);
     692  //       echo $json;
     693  //       exit;
     694  //   }
     695  //   elseif ($uri[0] == 'bib') {
     696  //       //render bibtex here
     697  //       exit;
     698  //   }
     699  //   elseif ($uri[0] == 'ris') {
     700  //       //render ris here
     701  //       exit; //prevents rest of page rendering
     702  //   }
     703  // }
    609704
    610705 
     
    622717      foreach ($cites as $cite) {
    623718          $item_string = "ITEM-".$item_number++;
     719         
     720          if( $cite->json ){
     721             
     722              $item = $cite->json;
     723             
     724              // add a few bits of additional metadata
     725              $item["source"] = $cite->source;
     726              $item["identifier"] = $cite->identifier;
     727              $item["resolved"] = $cite->resolved;
     728              $item["id"] = "$item_string";
     729             
     730
     731              // we finished!
     732              $citep[ $item_string ] = $item;
     733              continue;
     734          }
     735
    624736         
    625737          $item = array();
     
    628740          $item["identifier"] = $cite->identifier;
    629741          $item["resolved"] = $cite->resolved;
    630 
     742         
    631743          // timed out overall, so don't have the metadata
    632744          if( $cite->timeout ){
     
    643755          }
    644756         
    645 
     757         
    646758          // just didn't resolve
    647759          if( !$cite->resolved ){
     
    667779          }
    668780          $item[ "author" ] = $authors;
    669 
     781         
    670782          $item[ "container-title" ] = $cite->journal_title;
    671783         
     
    676788              $date_parts[] = (int)$cite->pub_date[ 'year' ];
    677789              // month and day if existing or nothing
    678               if( ((int)$cite->pub_date[ 'month' ]) > 0 ){
     790             
     791              if(array_key_exists( "month", $cite->pub_date)){
    679792                  $date_parts[] = (int)$cite->pub_date[ 'month' ];
    680                   if( ((int)$cite->pub_date[ 'day' ]) > 0 ){
    681                       $date_parts[] = (int)$cite->pub_date[ 'day' ];
    682                   }
     793              }
     794              if(array_key_exists( "day", $cite->pub_date)){
     795                  $date_parts[] = (int)$cite->pub_date[ 'day' ];
    683796              }
    684797             
     
    686799              $item[ "issued" ] = $issued;
    687800          }
    688 
     801         
    689802          if($cite->first_page){
    690803              $item[ "page" ] =
     
    695808              $item[ "volume" ] = $cite->volume;
    696809          }
    697 
     810         
    698811          if( $cite->issue ){
    699812              $item[ "issue" ] = $cite->issue;
     
    706819          $item[ "type" ] = "article-journal";
    707820         
     821          if( $cite->url ){
     822              $item["URL"] = $cite->url;
     823          }
     824
    708825
    709826          $citep[ $item_string ] = $item;
    710 
     827         
    711828      }
    712829
     
    735852   */
    736853   private function get_crossref_metadata($cite) {
    737    
    738        // dump the XML
    739        //print( "<br> Resolved Data from crossref for:$cite->identifier<br>$cite->resolution_source<br>" );
    740 
    741       // shorted the method a little!
    742       $article = $cite->parsedXML;
    743      
    744       $journal = $article->children()->children()->children();
    745      
    746       foreach ($journal->children() as $child) {
    747           if ($child->getName() == 'journal_metadata') {
    748               $cite->journal_title = (string)$child->full_title;
    749               $cite->abbrv_title = (string)$child->abbrev_title;
    750               continue;
    751           }
    752          
    753           if ($child->getName() == 'journal_issue') {
    754               $cite->issue = (string)$child->issue;
    755               foreach ($child->children() as $issue_info) {
    756                   if ($issue_info->getName() == 'publication_date') {
    757                      
    758 
    759                       $cite->pub_date['month'] = (string)$issue_info->month;
    760                       $cite->pub_date['day'] = (string)$issue_info->day;
    761                       $cite->pub_date['year'] = (string)$issue_info->year;
    762                       continue;
    763                   }
    764                  
    765                   if ($issue_info->getName() == 'journal_volume') {
    766                       $cite->volume = (string)$issue_info->volume;
    767                       continue;
    768                   }
    769               }
    770               continue;
    771           }
    772          
    773 
    774 
    775           if ($child->getName() == 'journal_article') {
    776               foreach ($child->children() as $details) {
    777                   if ($details->getName() == 'titles') {
    778                       $cite->title = (string)$details->children();
    779                       continue;
    780                   }
    781                  
    782                   if ($details->getName() == 'contributors') {
    783                       // pick out just the authors (not editors or what not)
    784                       $people = $details->xpath( '//person_name[@contributor_role="author"]' );
    785                       foreach ($people as $person) {
    786                          
    787                           $author = array();
    788                           $author['given_name'] = (string)$person->given_name;
    789                           $author['surname'] = (string)$person->surname;
    790                           $cite->authors[] = $author;
    791                       }
    792                       continue;
    793                   }
    794                  
    795                  
    796                   if ($details->getName() == 'pages') {
    797                       $cite->first_page = (string)$details->first_page;
    798                       $cite->last_page = (string)$details->last_page;
    799                       continue;
    800                   }
    801                  
    802                   if ($details->getName() == 'doi_data') {
    803                       $cite->reported_doi = (string)$details->doi;
    804                       $cite->resource = (string)$details->resource;
    805                       continue;
    806                   }
    807               }
    808               continue;
    809           }
    810       }
    811 
    812       // Fix section -- need to mark these up as problematic in JSON
    813      
    814       // crossref articles don't always have titles if they are old
    815       if( ! $cite->title ){
    816           $cite->title = "";
    817       }
    818      
    819       return $cite;
    820   }
     854       
     855       $json_decoded = json_decode( $cite->resolution_source, true );
     856
     857       // crossref returns both url and raw DOI. We don't need the later, so delete it.
     858       unset( $json_decoded[ "DOI" ] );
     859       $cite->json = $json_decoded;
     860
     861
     862       return $cite;
     863   }
     864
     865   private function get_datacite_metadata($cite){
     866       
     867       $article = $cite->parsedXML;
     868       $namespaceN = $article->getNamespaces();
     869       
     870       // nasty name space hack
     871       // datacite returns more than one form, but with different name spaces
     872       // which breaks the xpath, even they are the same for my purposes.
     873       $kn = "";
     874       if( $namespaceN[ "" ] == "http://datacite.org/schema/kernel-2.2" ){
     875           $kn = "kn:";
     876           $article->registerXpathNamespace( "kn", "http://datacite.org/schema/kernel-2.2" );
     877       }
     878       
     879       if( $namespaceN[ "" ] == null ){
     880           // kernel 2.0 -- no namespace
     881           // so do nothing.
     882       }
     883       
     884       $journalN = $article->xpath( "//${kn}publisher");
     885       // we get lots of newlines without trim
     886       $cite->journal_title = trim( (string)$journalN[ 0 ] );
     887
     888       // datacite can give multiple titles, it appear
     889       $titleN = $article->xpath( "//${kn}title" );
     890       $cite->title = trim( (string)$titleN[ 0 ] );
     891
     892       $authorN = $article->xpath( "//${kn}creators/${kn}creator/${kn}creatorName" );
     893
     894       foreach( $authorN as $author ){
     895           // this is not the most high tech name parsing ever.
     896           
     897           // names usualy come as Smith, J
     898           list( $last, $first ) =
     899               explode( ",", trim((string)$author) );
     900           
     901           // but sometimes are consortia names
     902           if( $last == null ){
     903               $last = $author;
     904               $first = "";
     905           }                               
     906
     907           $newauthor = array();
     908           $newauthor['surname'] = $last;
     909           $newauthor['given_name'] = $first;
     910           
     911           $cite->authors[] = $newauthor;
     912       }
     913
     914       $yearN = $article->xpath( "//${kn}publicationYear" );
     915       $cite->pub_date[ 'year' ] = (string)$yearN[ 0 ];
     916
     917       $cite->url = "http://dx.doi.org/" . $cite->identifier;
     918   }
    821919
    822920   /**
     
    834932
    835933      $issueN = $article->xpath( "//Article/Journal/JournalIssue/Issue" );
    836       $cite->issue = (string)$issueN[ 0 ];
     934      if( count( $issueN ) > 0 ){
     935          $cite->issue = (string)$issueN[ 0 ];
     936      }
    837937     
    838938      $journal_titleN = $article->xpath( "//Journal/Title" );
    839       $cite->journal_title = (string)$journal_titleN[ 0 ];
     939      if( count( $journal_titleN ) > 0 ){
     940          $cite->journal_title = (string)$journal_titleN[ 0 ];
     941      }
    840942     
    841943      $volN = $article->xpath( "//Journal/Volume" );
    842       $cite->volume = (string)$volN[ 0 ];
     944      if( count( $volN ) > 0 ){
     945          $cite->volume = (string)$volN[ 0 ];
     946      }
    843947
    844948      $abbrN = $article->xpath( "//Journal/ISOAbbreviation" );
    845       $cite->abbrv_title = (string)$abbrN[ 0 ];
     949      if( count( $abbrN ) > 0 ){
     950          $cite->abbrv_title = (string)$abbrN[ 0 ];
     951      }
    846952     
    847953      $artN = $article->xpath( "//ArticleTitle" );
    848       $cite->title = (string)$artN[ 0 ];
     954      if( count( $artN ) > 0 ){
     955          $cite->title = (string)$artN[ 0 ];
     956      }
    849957
    850958      $authN = $article->xpath( "//AuthorList/Author" );
     
    856964            $cite->authors[] = $newauthor;
    857965      }
    858 
     966     
    859967      $artDN = $article->xpath( "//ArticleDate" );
    860968
     
    865973      }
    866974     
    867       $cite->pub_date[ 'month' ] = (string)$artDN[ 0 ]->Month;
    868       $cite->pub_date[ 'day' ] = (string)$artDN[ 0 ]->Day;
    869       $cite->pub_date[ 'year' ] = (string)$artDN[ 0 ]->Year;
    870            
    871      
    872 
     975      if( count( $artDN ) > 0 ){
     976          $cite->pub_date[ 'month' ] = (string)$artDN[ 0 ]->Month;
     977          $cite->pub_date[ 'day' ] = (string)$artDN[ 0 ]->Day;
     978          $cite->pub_date[ 'year' ] = (string)$artDN[ 0 ]->Year;
     979      }
     980     
    873981      $elocN = $article->xpath( "//ELocationID" );
    874       $cite->reported_doi = (string)$elocN[ 0 ];
    875 
     982      if( count( $elocN ) > 0 ){
     983          $cite->reported_doi = (string)$elocN[ 0 ];
     984      }
     985     
     986      $cite->url = "http://www.ncbi.nlm.nih.gov/pubmed/{$cite->identifier}";
    876987
    877988      return $cite;
    878989  }
    879990 
     991
     992   private function get_arxiv_metadata($cite){
     993       
     994       $article = $cite->parsedXML;
     995       $article->registerXpathNamespace( "ar", "http://arxiv.org/OAI/arXiv/" );
     996       $article->registerXpathNamespace( "oai", "http://www.openarchives.org/OAI/2.0/" );
     997       $cite->journal_title = "arXiv";
     998       
     999       $titleN = $article->xpath( "//ar:title" );
     1000       $cite->title = (string)$titleN[ 0 ];
     1001
     1002       $dateN = $article->xpath( "//ar:created" );
     1003       $rawdate = (string)$dateN[ 0 ];
     1004       
     1005       $cite->pub_date['month'] = substr( $rawdate, 5, 2 );
     1006       $cite->pub_date['day'] = substr( $rawdate, 8, 2 );
     1007       $cite->pub_date['year'] = substr( $rawdate, 0, 4 );
     1008       
     1009       $authorN = $article->xpath( "//ar:author" );
     1010       
     1011       foreach( $authorN as $author ){
     1012           $newauthor = array();
     1013           $newauthor['surname'] = (string)$author->keyname;
     1014           $newauthor['given_name'] = (string)$author->forenames;
     1015           $cite->authors[] = $newauthor;
     1016       }
     1017
     1018       $cite->url = "http://arxiv.org/abs/" . $cite->identifier;
     1019         
     1020       return $cite;
     1021   }
     1022
     1023
    8801024  /**
    8811025   * Fetches the URI that the user requested to work out output format.
     
    9481092        }
    9491093
     1094        if ($_POST['kcite-cache']){
     1095            if( $_POST['kcite-cache'] == "True"){
     1096                update_option( 'kcite-cache', true );
     1097            }
     1098            else{
     1099                update_option( 'kcite-cache', false );
     1100            }
     1101        }
     1102
    9501103        if( $_POST['timeout']){
    9511104            update_option( 'timeout', $_POST['timeout'] );
     
    9801133      <th scope="row">Reference timeout<br/><font size='-2'>For how long should kcite attempt to gather bibliographic data before timing out</font></th>
    9811134      <td><input type='text' name="timeout" value='<?php echo get_option('timeout', 5) ?>'></td>
     1135
     1136      <tr>
     1137      <th scope="row">Cache References<br/><font size='-2'>Should kcite cache reference metadata. Set to false for debugging</font></th>
     1138      <td><select name='kcite-cache'>
     1139          <option value='True' <?php if (get_option('kcite-cache')) echo 'selected="true"'; ?>>True</option>
     1140          <option value='False' <?php if (!get_option('kcite-cache')) echo 'selected="true"'; ?>>False</option>
     1141      </select>
     1142      </td>                                                                                                                                                                                                                     
    9821143      </table>
    9831144      <p class="submit">
     
    10561217    public $resource;
    10571218    public $issue;
    1058    
     1219    public $url;
    10591220
    10601221    function equals($citation){
  • kcite/trunk/readme.txt

    r497290 r506635  
    1111== Description ==
    1212
    13 Interprets the &#91;cite&#93; shortcode to produce citations from the appropriate sources, also produces a formatted bibliography at the foot of the post, with appropriate links to articles.
     13Interprets the &#91;cite&#93; shortcode to produce citations from the
     14appropriate sources, also produces a formatted bibliography at the foot of the
     15post, with appropriate links to articles.
    1416
    15 The plugin uses the [CrossRef API](http://www.crossref.org/help/CrossRef_Help.htm) to retrieve metadata for Digital Object Identifiers (DOIs) and [NCBI eUtils](http://eutils.ncbi.nlm.nih.gov/) to retrieve metadata for PubMed Identifiers (PMIDs).
     17This plugin now uses multiple resources to retrieve metadata about the
     18references in question, including CrossRef, DataCite, arXiv and PubMed.
     19
    1620
    1721**Syntax**
     
    2125PMID example - &#91;cite source='pubmed'&#93;17237047&#91;/cite&#93;
    2226
    23 Whichever 'source' is identified as the default (see Installation), will work without the source attribute being set in the shortcode. so:
     27Whichever 'source' is identified as the default (see Installation), will work
     28without the source attribute being set in the shortcode. so:
    2429
    2530&#91;cite&#93;10.1021/jf904082b&#91;/cite&#93;
     
    2732Will be interpreted correctly as long as DOI is set as the default metadata
    2833source.
     34
     35Kcite now supports DOIs from both [CrossRef](http://www.crossref.org) and
     36[DataCite](http://www.datacite.org). Identifiers from
     37[PubMed](http://www.pubmed.org) or [arXiv](http://www.arxiv.org) are directly
     38supported.
    2939
    3040From Kcite 1.4, Citeproc-js
     
    3444the reader to choose.
    3545
    36 Kcite is developed at http://code.google.com/p/knowledgeblog/ in Mercurial.
    37 
     46Kcite is developed at http://code.google.com/p/knowledgeblog/ in Mercurial. To
     47contact the authors, please email knowledgeblog@googlegroups.com.
    3848
    3949== Installation ==
    4050
     511. Kcite now requires the use of libcurl.
    41521. Unzip the downloaded .zip archive to the `/wp-content/plugins/` directory
    42531. Activate the plugin through the 'Plugins' menu in WordPress
     
    4455
    4556== Changelog ==
     57
     58= 1.5 =
     59
     601. Kcite now requires the PHP libcurl support. You may need to install
     61   additional packages on your web server.
     621. From kcite 1.5, we have expanded the range of identifiers.
     63   DataCite DOIs and arXiv IDs are now supported.
     641. Crossref DOIs are now accessed via content negotiation. This should be less
     65   buggy, and reduce server load as it removes a parsing/data integration
     66   step.
     671. DataCite DOIs come via content negotiation also, although still require XML
     68   parsing.
     691. Bug fix to in kcite.js should fix an occasional rendering bug.
     701. Both bibliography and intext citation are now linked. The underlying HTML
     71   is also linked, which should aid machine interpretability.
    4672
    4773= 1.4.4 =
     
    71971. Fixed another regression caused by 1.2 fix. This should fix the error when
    7298   there is no bibliography.
     99
    73100= 1.2 =
    741011. Sadly 1.1 had a regression error in it, which mean it didn't
Note: See TracChangeset for help on using the changeset viewer.