From a249cd16086655816eae422140d149d24847a398 Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Fri, 27 Nov 2020 06:05:36 -0500 Subject: [PATCH] Update citeproc-js to 1.4.50 --- chrome/content/zotero/xpcom/citeproc.js | 1465 +++++++++++++++-------- 1 file changed, 981 insertions(+), 484 deletions(-) diff --git a/chrome/content/zotero/xpcom/citeproc.js b/chrome/content/zotero/xpcom/citeproc.js index 6e3eb721b4..63630b28e8 100644 --- a/chrome/content/zotero/xpcom/citeproc.js +++ b/chrome/content/zotero/xpcom/citeproc.js @@ -59,7 +59,7 @@ Copyright (c) 2009-2019 Frank Bennett var CSL = { - PROCESSOR_VERSION: "1.4.11", + PROCESSOR_VERSION: "1.4.50", error: function(str) { // default error function if ("undefined" === typeof Error) { @@ -96,10 +96,10 @@ var CSL = { return str; }, - LOCATOR_LABELS_REGEXP: new RegExp("^((vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\\.)\\s+(.*)"), + LOCATOR_LABELS_REGEXP: new RegExp("^((vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|rn|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\\.)\\s+(.*)"), - STATUTE_SUBDIV_PLAIN_REGEX: /(?:(?:^| )(?:vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\. *)/, - STATUTE_SUBDIV_PLAIN_REGEX_FRONT: /(?:^\s*[.,;]*\s*(?:vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\. *)/, + STATUTE_SUBDIV_PLAIN_REGEX: /(?:(?:^| )(?:vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|rn|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\. *)/, + STATUTE_SUBDIV_PLAIN_REGEX_FRONT: /(?:^\s*[.,;]*\s*(?:vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|rn|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\. *)/, STATUTE_SUBDIV_STRINGS: { "vrs.": "verse", @@ -139,6 +139,7 @@ var CSL = { "princ.": "principle", "pub.": "publication", "r.": "rule", + "rn.": "randnummer", "sched.": "schedule", "sec.": "section", "ser.": "series,", @@ -187,6 +188,7 @@ var CSL = { "principle": "princ.", "publication": "pub.", "rule": "r.", + "randnummer": "rn.", "schedule": "sched.", "section": "sec.", "series,": "ser.", @@ -236,6 +238,7 @@ var CSL = { "princ": "principle", "pub": "publication", "r": "rule", + "rn": "randnummer", "sched": "schedule", "sec": "section", "ser": "series,", @@ -339,6 +342,34 @@ var CSL = { this["title-phrase"] = {}; }, + getAbbrevsDomain: function (state, country, lang) { + var domain = null; + if (state.opt.availableAbbrevDomains && country && country !== "default") { + var globalDomainPreference = state.locale[state.opt.lang].opts["jurisdiction-preference"]; + var itemDomainPreference = null; + if (state.locale[lang]) { + itemDomainPreference = state.locale[lang].opts["jurisdiction-preference"]; + } + if (itemDomainPreference) { + for (var j=itemDomainPreference.length-1; j > -1; j--) { + if (state.opt.availableAbbrevDomains[country].indexOf(itemDomainPreference[j]) > -1) { + domain = itemDomainPreference[j]; + break; + } + } + } + if (!domain && globalDomainPreference) { + for (var j=globalDomainPreference.length-1; j > -1; j--) { + if (state.opt.availableAbbrevDomains[country].indexOf(globalDomainPreference[j]) > -1) { + domain = globalDomainPreference[j]; + break; + } + } + } + } + return domain; + }, + FIELD_CATEGORY_REMAP: { "title": "title", "container-title": "container-title", @@ -346,7 +377,7 @@ var CSL = { "country": "place", "number": "number", "place": "place", - "archive": "collection-title", + "archive": "container-title", "title-short": "title", "genre": "title", "event": "title", @@ -589,12 +620,21 @@ var CSL = { POSITION_SUBSEQUENT: 1, POSITION_IBID: 2, POSITION_IBID_WITH_LOCATOR: 3, + POSITION_CONTAINER_SUBSEQUENT: 4, + POSITION_MAP: { + "0": 0, + "4": 1, + "1": 2, + "2": 3, + "3": 4 + }, + POSITION_TEST_VARS: ["position", "first-reference-note-number", "near-note"], AREAS: ["citation", "citation_sort", "bibliography", "bibliography_sort", "intext"], - CITE_FIELDS: ["first-reference-note-number", "locator", "locator-extra"], + CITE_FIELDS: ["first-reference-note-number", "first-container-reference-note-number", "locator", "locator-extra"], SWAPPING_PUNCTUATION: [".", "!", "?", ":", ","], TERMINAL_PUNCTUATION: [":", ".", ";", "!", "?", " "], @@ -794,7 +834,10 @@ var CSL = { var narrowSpace = narrowSpaceLocale ? "\u202f" : ""; // XXX In this function, split on split-char, but prefer exact match // XXX of subtitle to a split-char in title if found. - var segments = ["", "container-"]; + var segments = [""]; + if (this.opt.development_extensions.split_container_title) { + segments.push("container-"); + } for (var i=0,ilen=segments.length;i o[0] === "@vertical-align").length > 0) { state.tmp.just_did_number = false; + } else if (token && token.strings.suffix) { + state.tmp.just_did_number = false; + } else if (str) { + if (str.match(/[0-9]$/)) { + state.tmp.just_did_number = true; + } else { + state.tmp.just_did_number = false; + } } } }, EVALUATE_GROUP_CONDITION: function(state, flags) { + if (!state.opt.use_context_condition) return; var testres; + var numbersOnly = flags.condition.test === "comma-safe-numbers-only"; if (flags.condition.test === "empty-label") { testres = !flags.condition.termtxt; } else if (flags.condition.test === "empty-label-no-decor") { testres = !flags.condition.termtxt || flags.condition.termtxt.indexOf("%s") > -1; - } else if (flags.condition.test === "comma-safe") { - var empty = !flags.condition.termtxt; + } else if (["comma-safe", "comma-safe-numbers-only"].indexOf(flags.condition.test) > -1) { + var locale_term = flags.condition.termtxt; var termStartAlpha = false; if (flags.condition.termtxt) { termStartAlpha = flags.condition.termtxt.slice(0,1).match(CSL.ALL_ROMANESQUE_REGEXP); } var num = state.tmp.just_did_number; - if (empty || flags.condition.valueTerm) { - // i.e. Big L. Rev. 100, 102 - // Little L. Rev. 102 - // L. Rev. for Plan 9, 102 - if (num) { + if (num) { + if (flags.condition.valueTerm) { + testres = numbersOnly ? false : true; + } else if (!locale_term) { + testres = true; + } else if (termStartAlpha) { + testres = numbersOnly ? false : true; + } else if (["always", "after-number"].indexOf(state.opt.require_comma_on_symbol) > -1) { testres = true; } else { testres = false; } } else { - if (num) { - if (termStartAlpha || ["always", "after-number"].indexOf(state.opt.require_comma_on_symbol) > -1) { - testres = true; - } else { - testres = false; - } + if (flags.condition.valueTerm) { + testres = false; + } else if (!locale_term) { + testres = false; + } else if (termStartAlpha) { + testres = numbersOnly ? false : true; + } else if (state.opt.require_comma_on_symbol === "always") { + testres = true; } else { - if (termStartAlpha || state.opt.require_comma_on_symbol === "always") { - testres = true; - } else { - testres = false; - } + testres = false; } } } @@ -1306,7 +1373,8 @@ var CSL = { "main_title_from_short_title", "uppercase_subtitles", "force_short_title_casing_alignment", - "implicit_short_title" + "implicit_short_title", + "split_container_title" ], TITLE_SPLIT_REGEXP: (function() { @@ -1418,48 +1486,13 @@ var CSL = { // Okay. We have code for each of the novel modules in the // hierarchy. Load them all into the processor. for (var jurisdiction in res) { - var macroCount = 0; - state.juris[jurisdiction] = {}; - var myXml = CSL.setupXml(res[jurisdiction]); - myXml.addMissingNameNodes(myXml.dataObj); - myXml.addInstitutionNodes(myXml.dataObj); - myXml.insertPublisherAndPlace(myXml.dataObj); - myXml.flagDateMacros(myXml.dataObj); - var myNodes = myXml.getNodesByName(myXml.dataObj, "law-module"); - for (var i=0,ilen=myNodes.length;i -1) { - var varname; var elements = ["type", "title", "jurisdiction", "genre", "volume", "container-title"]; var legislation_id = []; @@ -4294,6 +4325,20 @@ CSL.Engine.prototype.retrieveItem = function (id) { Item.legislation_id = legislation_id.join("::"); } } + if (this.bibliography.opt.track_container_items) { + if (this.bibliography.opt.track_container_items.indexOf(Item.type) > -1) { + var varname; + var elements = ["type", "container-title", "publisher", "edition"]; + var container_id = []; + for (var i = 0, ilen = elements.length; i < ilen; i += 1) { + varname = elements[i]; + if (Item[varname]) { + container_id.push(Item[varname]); + } + } + Item.container_id = container_id.join("::"); + } + } // For authority to name shape in legal styles if (this.opt.development_extensions.force_jurisdiction) { if ("string" === typeof Item.authority) { @@ -4337,11 +4382,11 @@ CSL.Engine.prototype.retrieveItem = function (id) { noHints = true; } if (this.sys.normalizeAbbrevsKey) { - normalizedKey = this.sys.normalizeAbbrevsKey(Item.title); + normalizedKey = this.sys.normalizeAbbrevsKey("title", Item.title); } else { normalizedKey = Item.title; } - var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "title", normalizedKey, Item.type); + var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "title", normalizedKey, Item.language); if (this.transform.abbrevs[jurisdiction].title) { if (this.transform.abbrevs[jurisdiction].title[normalizedKey]) { Item["title-short"] = this.transform.abbrevs[jurisdiction].title[normalizedKey]; @@ -4357,7 +4402,7 @@ CSL.Engine.prototype.retrieveItem = function (id) { } else { normalizedKey = Item["container-title"]; } - var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "container-title", normalizedKey); + var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "container-title", normalizedKey, Item.language); if (this.transform.abbrevs[jurisdiction]["container-title"]) { if (this.transform.abbrevs[jurisdiction]["container-title"][normalizedKey]) { Item["container-title-short"] = this.transform.abbrevs[jurisdiction]["container-title"][normalizedKey]; @@ -4490,7 +4535,7 @@ CSL.Engine.prototype.remapSectionVariable = function (inputList) { CSL.Engine.prototype.setNumberLabels = function (Item) { if (Item.number && ["bill", "gazette", "legislation","regulation","treaty"].indexOf(Item.type) > -1 - && this.opt.development_extensions.static_statute_locator + && this.opt.development_extensions.consolidate_legal_items && !this.tmp.shadow_numbers["number"]) { this.tmp.shadow_numbers["number"] = {}; @@ -6406,6 +6451,10 @@ CSL.Engine.Opt = function () { }; this.has_layout_locale = false; + this.disable_duplicate_year_suppression = []; + this.use_context_condition = false; + + this.jurisdiction_fallbacks = {}; this.development_extensions = {}; this.development_extensions.field_hack = true; @@ -6414,7 +6463,7 @@ CSL.Engine.Opt = function () { this.development_extensions.locator_label_parse = true; this.development_extensions.raw_date_parsing = true; this.development_extensions.clean_up_csl_flaws = true; - this.development_extensions.static_statute_locator = false; + this.development_extensions.consolidate_legal_items = false; this.development_extensions.csl_reverse_lookup_support = false; this.development_extensions.wrap_url_and_doi = false; this.development_extensions.thin_non_breaking_space_html_hack = false; @@ -6435,6 +6484,8 @@ CSL.Engine.Opt = function () { this.development_extensions.force_short_title_casing_alignment = true; this.development_extensions.implicit_short_title = false; this.development_extensions.force_title_abbrev_fallback = false; + this.development_extensions.split_container_title = false; + this.development_extensions.legacy_institution_name_ordering = false; }; CSL.Engine.Tmp = function () { @@ -6978,10 +7029,10 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, } Item = this.retrieveItem("" + item.id); if (Item.id) { - this.transform.loadAbbreviation("default", "hereinafter", Item.id); + this.transform.loadAbbreviation("default", "hereinafter", Item.id, Item.language); } item = CSL.parseLocator.call(this, item); - if (this.opt.development_extensions.static_statute_locator) { + if (this.opt.development_extensions.consolidate_legal_items) { this.remapSectionVariable([[Item,item]]); } if (this.opt.development_extensions.locator_label_parse) { @@ -7197,19 +7248,21 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, var citations; if (this.opt.update_mode === CSL.POSITION) { for (var i = 0; i < 2; i += 1) { - citations = [textCitations, noteCitations][i]; var first_ref = {}; var last_ref = {}; + var first_container_ref = {}; + citations = [textCitations, noteCitations][i]; for (j = 0, jlen = citations.length; j < jlen; j += 1) { var onecitation = citations[j]; if (!citations[j].properties.noteIndex) { citations[j].properties.noteIndex = 0; } citations[j].properties.noteIndex = parseInt(citations[j].properties.noteIndex, 10); - if (j > 0 && citations[j - 1].properties.noteIndex > citations[j].properties.noteIndex) { + if (j > 0 && onecitation.properties.noteIndex && citations[j - 1].properties.noteIndex > onecitation.properties.noteIndex) { citationsInNote = {}; first_ref = {}; last_ref = {}; + first_container_ref = {}; } for (k = 0, klen = onecitation.sortedItems.length; k < klen; k += 1) { if (onecitation.sortedItems[k][1].parallel && onecitation.sortedItems[k][1].parallel !== "first") { @@ -7236,13 +7289,18 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, // for (k = 0, klen = citations[j].sortedItems.length; k < klen; k += 1) { item = citations[j].sortedItems[k]; - var myid = item[0].id; + // Okay ... + // We set up three IDs for use in position evaluation. + // item_id is the real Item.id + // first_id is the legislation_id or Item.id (so statutes backref to first in set, chapters to specific chapter) + // last_id is the legislation_id or container_id (so statute AND chapter distance is from any ref in set) + // (replaces myid) + var item_id = item[0].id; + var first_id = item[0].legislation_id ? item[0].legislation_id : item[0].id; + var last_id = item[0].legislation_id ? item[0].legislation_id : item[0].container_id ? item[0].container_id : item[0].id; var myxloc = item[1]["locator-extra"]; var mylocator = item[1].locator; var mylabel = item[1].label; - if (item[0].legislation_id) { - myid = item[0].legislation_id; - } var incitationid; var incitationxloc; if (k > 0) { @@ -7268,10 +7326,10 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, if (flag === CSL.PREVIEW) { if (onecitation.citationID != citation.citationID) { if ("undefined" === typeof first_ref[item[1].id]) { - first_ref[myid] = onecitation.properties.noteIndex; - last_ref[myid] = onecitation.properties.noteIndex; + first_ref[first_id] = onecitation.properties.noteIndex; + last_ref[last_id] = onecitation.properties.noteIndex; } else { - last_ref[myid] = onecitation.properties.noteIndex; + last_ref[last_id] = onecitation.properties.noteIndex; } continue; } @@ -7279,40 +7337,53 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, var oldvalue = {}; oldvalue.position = item[1].position; oldvalue["first-reference-note-number"] = item[1]["first-reference-note-number"]; + oldvalue["first-container-reference-note-number"] = item[1]["first-container-reference-note-number"]; oldvalue["near-note"] = item[1]["near-note"]; item[1]["first-reference-note-number"] = 0; + item[1]["first-container-reference-note-number"] = 0; item[1]["near-note"] = false; - if (this.registry.citationreg.citationsByItemId[myid]) { + if (this.registry.citationreg.citationsByItemId[item_id]) { if (this.opt.xclass === 'note' && this.opt.has_disambiguate) { - var oldCount = this.registry.registry[myid]["citation-count"]; - var newCount = this.registry.citationreg.citationsByItemId[myid].length; - this.registry.registry[myid]["citation-count"] = this.registry.citationreg.citationsByItemId[myid].length; + var oldCount = this.registry.registry[item[0].id]["citation-count"]; + var newCount = this.registry.citationreg.citationsByItemId[item_id].length; + this.registry.registry[item[0].id]["citation-count"] = this.registry.citationreg.citationsByItemId[item_id].length; if ("number" === typeof oldCount) { var oldCountCheck = (oldCount < 2); var newCountCheck = (newCount < 2); if (oldCountCheck !== newCountCheck) { - for (var l=0,llen=this.registry.citationreg.citationsByItemId[myid].length;l= (thisCitation.properties.noteIndex - 1))) { + if ((oldid == first_id && prevCitation.properties.noteIndex >= (thisCitation.properties.noteIndex - 1))) { var prevxloc = prevCitation.sortedItems[0][1]["locator-extra"]; var thisxloc = thisCitation.sortedItems[0][1]["locator-extra"]; if ((citationsInNote[prevCitation.properties.noteIndex] === 1 || prevCitation.properties.noteIndex === 0) && prevxloc === thisxloc) { @@ -7371,7 +7442,7 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, } else { suprame = true; } - } else if (k > 0 && incitationid == myid && incitationxloc == myxloc) { + } else if (k > 0 && incitationid == first_id && incitationxloc == myxloc) { // Case 2: immediately preceding source in this onecitation // (1) Threshold conditions // (a) there must be an imediately preceding reference to the @@ -7379,7 +7450,7 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, ibidme = true; } else if (k === 0 && j > 0 && prevCitation.properties.noteIndex == thisCitation.properties.noteIndex && prevCitation.sortedItems.length - && oldlastid == myid && oldlastxloc == myxloc) { + && oldlastid == first_id && oldlastxloc == myxloc) { // ... in case there are separate citations in the same note ... // Case 2 [take 2]: immediately preceding source in this onecitation // (1) Threshold conditions @@ -7457,39 +7528,56 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, } } if (suprame) { - item[1].position = CSL.POSITION_SUBSEQUENT; + item[1].position = CSL.POSITION_CONTAINER_SUBSEQUENT; + if ("undefined" === typeof first_ref[first_id]) { + first_ref[first_id] = onecitation.properties.noteIndex; + } else { + item[1].position = CSL.POSITION_SUBSEQUENT; + } } if (suprame || ibidme) { if (onecitation.properties.mode === "author-only") { item[1].position = CSL.POSITION_FIRST; } - if (first_ref[myid] != onecitation.properties.noteIndex) { - item[1]["first-reference-note-number"] = first_ref[myid]; - if (this.registry.registry[myid]) { + if (first_container_ref[last_id] != onecitation.properties.noteIndex) { + item[1]['first-container-reference-note-number'] = first_container_ref[last_id]; + if (this.registry.registry[item[0].id]) { + this.registry.registry[item[0].id]['first-container-reference-note-number'] = first_container_ref[last_id]; + } + } + if (first_ref[first_id] != onecitation.properties.noteIndex) { + item[1]["first-reference-note-number"] = first_ref[first_id]; + if (this.registry.registry[item[0].id]) { // This is either the earliest recorded number, or the number of the current citation, whichever is smaller. - var oldFirst = this.registry.citationreg.citationsByItemId[myid][0].properties.noteIndex; + /* + var oldFirst = this.registry.citationreg.citationsByItemId[item_id][0].properties.noteIndex; var newFirst = onecitation.properties.noteIndex; - this.registry.registry[myid]['first-reference-note-number'] = newFirst < oldFirst ? newFirst: oldFirst; + this.registry.registry[item[0].id]['first-reference-note-number'] = newFirst < oldFirst ? newFirst: oldFirst; + */ + // Try this instead? + this.registry.registry[item[0].id]['first-reference-note-number'] = first_ref[first_id]; } } } } if (onecitation.properties.noteIndex) { - var note_distance = parseInt(onecitation.properties.noteIndex, 10) - parseInt(last_ref[myid], 10); + var note_distance = parseInt(onecitation.properties.noteIndex, 10) - parseInt(last_ref[last_id], 10); if (item[1].position !== CSL.POSITION_FIRST && note_distance <= this.citation.opt["near-note-distance"]) { item[1]["near-note"] = true; } - last_ref[myid] = onecitation.properties.noteIndex; + last_ref[last_id] = onecitation.properties.noteIndex; + } else if (item[1].position !== CSL.POSITION_FIRST) { + item[1]["near-note"] = true; } if (onecitation.citationID != citation.citationID) { for (n = 0, nlen = CSL.POSITION_TEST_VARS.length; n < nlen; n += 1) { var param = CSL.POSITION_TEST_VARS[n]; if (item[1][param] !== oldvalue[param]) { - if (this.registry.registry[myid]) { + if (this.registry.registry[item[0].id]) { if (param === 'first-reference-note-number') { - rerunAkeys[this.registry.registry[myid].ambig] = true; - this.tmp.taintedItemIDs[myid] = true; + rerunAkeys[this.registry.registry[item[0].id].ambig] = true; + this.tmp.taintedItemIDs[item[0].id] = true; } } this.tmp.taintedCitationIDs[onecitation.citationID] = true; @@ -7714,7 +7802,7 @@ CSL.Engine.prototype.makeCitationCluster = function (rawList) { newitem = [Item, item]; inputList.push(newitem); } - if (this.opt.development_extensions.static_statute_locator) { + if (this.opt.development_extensions.consolidate_legal_items) { this.remapSectionVariable(inputList); } if (inputList && inputList.length > 1 && this.citation_sort.tokens.length > 0) { @@ -7747,6 +7835,7 @@ CSL.getAmbiguousCite = function (Item, disambig, visualForm, item) { variable_success: flags.variable_success, output_tip: flags.output_tip, label_form: flags.label_form, + non_parallel: flags.non_parallel, parallel_last: flags.parallel_last, parallel_first: flags.parallel_first, parallel_last_override: flags.parallel_last_override, @@ -7762,7 +7851,7 @@ CSL.getAmbiguousCite = function (Item, disambig, visualForm, item) { this.tmp.disambig_request = false; } var itemSupp = { - position: 1, + position: CSL.POSITION_SUBSEQUENT, "near-note": true }; @@ -7921,7 +8010,7 @@ CSL.getCitationCluster = function (inputList, citation) { //var use_layout_prefix = this.citation.opt.layout_prefix; var suppressTrailingPunctuation = false; - if (this.opt.xclass === "note" && this.citation.opt.suppressTrailingPunctuation) { + if (this.citation.opt.suppressTrailingPunctuation) { suppressTrailingPunctuation = true; } if (citationID) { @@ -8158,16 +8247,25 @@ CSL.getCitationCluster = function (inputList, citation) { if (this.opt.development_extensions.throw_on_empty) { CSL.error("Citation would render no content"); } else { - composite = "[NO_PRINTED_FORM]" + composite = "[NO_PRINTED_FORM]"; } } return composite; } if ("object" === typeof composite && composite.length === 0 && !item["suppress-author"]) { - var errStr = "[CSL STYLE ERROR: reference with no printed form.]"; - var preStr = pos === 0 ? txt_esc(this.citation.opt.layout_prefix) : ""; - var sufStr = pos === (myblobs.length - 1) ? txt_esc(this.citation.opt.layout_suffix) : ""; - composite.push(preStr + errStr + sufStr); + if (pos === 0) { + var errStr = "[CSL STYLE ERROR: reference with no printed form.]"; + var preStr = pos === 0 ? txt_esc(this.citation.opt.layout_prefix) : ""; + var sufStr = pos === (myblobs.length - 1) ? txt_esc(this.citation.opt.layout_suffix) : ""; + composite.push(preStr + errStr + sufStr); + } else if (pos === myblobs.length - 1) { + var tmpobj = objects[objects.length - 1]; + if (typeof tmpobj === "string") { + objects[objects.length -1] += (txt_esc(this.citation.opt.layout_suffix)); + } else if (typeof tmpobj === "object") { + tmpobj.strings.suffix += (txt_esc(this.citation.opt.layout_suffix)); + } + } } if (buffer.length && "string" === typeof composite[0]) { composite.reverse(); @@ -8279,6 +8377,7 @@ CSL.getCite = function (Item, item, prevItemID, blockShadowNumberReset) { } this.tmp.cite_renders_content = false; this.tmp.probably_rendered_something = false; + this.tmp.prevItemID = prevItemID; CSL.citeStart.call(this, Item, item, blockShadowNumberReset); next = 0; @@ -8320,6 +8419,7 @@ CSL.citeStart = function (Item, item, blockShadowNumberReset) { if (!blockShadowNumberReset) { this.tmp.shadow_numbers = {}; } + this.tmp.disambiguate_count = 0; this.tmp.disambiguate_maxMax = 0; this.tmp.same_author_as_previous_cite = false; @@ -8622,6 +8722,34 @@ CSL.getBibliographyEntries = function (bibsection) { var processed_item_ids = []; + var consolidatedIDs = {}; + this.tmp.container_item_count = {}; + input = input.filter(o => { + var ret = o; + if (o.legislation_id) { + if (consolidatedIDs[o.legislation_id]) { + ret = false; + } else { + consolidatedIDs[o.legislation_id] = true; + } + } else if (o.container_id) { + if (!this.tmp.container_item_count[o.container_id]) { + this.tmp.container_item_count[o.container_id] = 0; + } + this.tmp.container_item_count[o.container_id]++; + if (this.bibliography.opt.consolidate_containers.indexOf(o.type) > -1) { + if (consolidatedIDs[o.container_id]) { + ret = false; + } else { + consolidatedIDs[o.container_id] = true; + } + } + } + return ret; + }); + + this.tmp.container_item_pos = {}; + for (i = 0, ilen = input.length; i < ilen; i += 1) { // For paged returns @@ -8707,6 +8835,14 @@ CSL.getBibliographyEntries = function (bibsection) { if (debug) { CSL.debug("BIB: " + item.id); } + + if (item.container_id) { + if (!this.tmp.container_item_pos[item.container_id]) { + this.tmp.container_item_pos[item.container_id] = 0; + } + this.tmp.container_item_pos[item.container_id]++; + } + //SNIP-END bib_entry = new CSL.Token("group", CSL.START); bib_entry.decorations = [["@bibliography", "entry"]].concat(this.bibliography.opt.layout_decorations); @@ -8730,11 +8866,20 @@ CSL.getBibliographyEntries = function (bibsection) { } // Adjust parameters this.parallel.StartCitation(sortedItems); - this.output.queue[0].strings.delimiter = ", "; + if (this.registry.registry[item.id].parallel_delimiter_override) { + this.output.queue[0].strings.delimiter = this.registry.registry[item.id].parallel_delimiter_override; + } else { + this.output.queue[0].strings.delimiter = ", "; + } this.tmp.term_predecessor = false; this.tmp.cite_index = 0; // Run cites for (j = 0, jlen = sortedItems.length; j < jlen; j += 1) { + if (j < (sortedItems.length - 1)) { + this.tmp.parallel_and_not_last = true; + } else { + delete this.tmp.parallel_and_not_last; + } entry_item_ids.push("" + CSL.getCite.call(this, sortedItems[j][0], sortedItems[j][1])); this.tmp.cite_index++; skips[sortedItems[j][0].id] = true; @@ -9243,14 +9388,33 @@ CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) { // Xml: get a list of all "locale" nodes // nodes = myxml.getNodesByName(myxml.dataObj, "locale"); + var foundLocale = false; for (pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) { blob = nodes[pos]; // // Xml: get locale xml:lang // - if (myxml.getAttributeValue(blob, 'lang', 'xml') === lang_in) { + // Iterate over all locales, but for non-matching nodes, + // we set jurisdiction_preference only (processing of the + // chosen one will process the attribute there, + // separately. + if (!foundLocale && myxml.getAttributeValue(blob, 'lang', 'xml') === lang_in) { locale = blob; - break; + foundLocale = true; + } else { + var lang = myxml.getAttributeValue(blob, 'lang', 'xml'); + var style_options = myxml.getNodesByName(blob, 'style-options'); + if (lang && style_options && style_options.length) { + var jurispref = myxml.getAttributeValue(style_options[0], 'jurisdiction-preference'); + if (jurispref) { + if (!this.locale[lang]) { + this.locale[lang] = { + opts: {} + }; + } + this.locale[lang].opts["jurisdiction-preference"] = jurispref.split(/\s+/); + } + } } } } @@ -9815,6 +9979,7 @@ CSL.Node.date = { state.output.startTag("date", this); if (this.variables[0] === "issued" && (Item.type === "legal_case" || Item.type === "legislation") + && state.opt.disable_duplicate_year_suppression.indexOf(Item.country) === -1 && !state.tmp.extension && "" + Item["collection-number"] === "" + state.tmp.date_object.year && this.dateparts.length === 1 @@ -9907,6 +10072,8 @@ CSL.Node["date-part"] = { state.tmp.probably_rendered_something = true; } + var last_string_output = ""; + first_date = true; value = ""; value_end = ""; @@ -9914,6 +10081,7 @@ CSL.Node["date-part"] = { // Render literal only when year is included in date output if (state.tmp.date_object.literal && "year" === this.strings.name) { + last_string_output = state.tmp.date_object.literal; state.output.append(state.tmp.date_object.literal, this); } @@ -10021,11 +10189,13 @@ CSL.Node["date-part"] = { var range_delimiter = state.getTerm("year-range-delimiter"); value_end = value_end.slice(value_end.indexOf(range_delimiter) + 1); } + last_string_output = value_end; state.dateput.append(value_end, this); if (first_date) { state.dateput.current.value().blobs[0].strings.prefix = ""; } } + last_string_output = value; state.output.append(value, this); curr = state.output.current.value(); curr.blobs[(curr.blobs.length - 1)].strings.suffix = ""; @@ -10044,6 +10214,7 @@ CSL.Node["date-part"] = { state.dateput.openLevel(state.tmp.date_token); state.tmp.date_collapse_at = []; } else { + last_string_output = value; state.output.append(value, this); // print("collapse_at: "+state.tmp.date_collapse_at); if (state.tmp.date_collapse_at.indexOf(this.strings.name) > -1) { @@ -10059,14 +10230,17 @@ CSL.Node["date-part"] = { first_date = true; } state.dateput.openLevel("empty"); + last_string_output = value_end; state.dateput.append(value_end, this); if (first_date) { state.dateput.current.value().blobs[0].strings.prefix = ""; } if (bc) { + last_string_output = bc; state.dateput.append(bc); } if (ad) { + last_string_output = ad; state.dateput.append(ad); } state.dateput.closeLevel(); @@ -10074,13 +10248,16 @@ CSL.Node["date-part"] = { } } } else { + last_string_output = value; state.output.append(value, this); } if (bc) { + last_string_output = bc; state.output.append(bc); } if (ad) { + last_string_output = ad; state.output.append(ad); } state.output.closeLevel(); @@ -10102,8 +10279,10 @@ CSL.Node["date-part"] = { // XXXXXXXXXXXXXXXXXXX was replace([false, false, true]); //state.tmp.group_context.replace([false, false, true]); state.tmp.group_context.tip.variable_success = true; + last_string_output = "winter"; state.output.append(state.getTerm(("season-0" + value)), this); } else if (value) { + last_string_output = value; state.output.append(value, this); } } @@ -10112,6 +10291,7 @@ CSL.Node["date-part"] = { if (Item[date_variable] && (value || state.tmp.have_collapsed) && !state.opt.has_year_suffix && "year" === this.strings.name && !state.tmp.just_looking) { if (state.registry.registry[Item.id] && state.registry.registry[Item.id].disambig.year_suffix !== false && !state.tmp.has_done_year_suffix) { state.tmp.has_done_year_suffix = true; + last_string_output = "x"; num = parseInt(state.registry.registry[Item.id].disambig.year_suffix, 10); // first argument is for number particle [a-zA-Z], never present on dates number = new CSL.NumericBlob(false, num, this, Item.id); @@ -10133,7 +10313,12 @@ CSL.Node["date-part"] = { state.output.append(number, "literal"); } } - + if (last_string_output && !state.tmp.group_context.tip.condition) { + state.tmp.just_did_number = last_string_output.match(/[0-9]$/); + if (state.output.current.tip.strings.suffix) { + state.tmp.just_did_number = false; + } + } }; this.execs.push(func); target.push(this); @@ -10219,6 +10404,11 @@ CSL.Node.group = { } if (this.realGroup) { + + if (state.tmp.group_context.tip.condition) { + CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, this.strings.prefix, null, this); + } + var condition = false; var force_suppress = false; @@ -10268,6 +10458,14 @@ CSL.Node.group = { force_suppress: force_suppress, done_vars: state.tmp.group_context.tip.done_vars.slice() }; + if(this.non_parallel) { + var non_parallel = state.tmp.group_context.tip.non_parallel; + if (!non_parallel) { + non_parallel = {}; + } + Object.assign(non_parallel, this.non_parallel); + context.non_parallel = non_parallel; + } if(this.parallel_first) { var parallel_first = state.tmp.group_context.tip.parallel_first; if (!parallel_first) { @@ -10295,6 +10493,8 @@ CSL.Node.group = { } } } + + /* if(this.parallel_last_override) { var parallel_last_override = state.tmp.group_context.tip.parallel_last_override; if (!parallel_last_override) { @@ -10303,6 +10503,7 @@ CSL.Node.group = { Object.assign(parallel_last_override, this.parallel_last_override); context.parallel_last_override = parallel_last_override; } + */ state.tmp.group_context.push(context); if (state.tmp.abbrev_trimmer && this.parallel_last_to_first) { @@ -10439,11 +10640,19 @@ CSL.Node.group = { // quashnonfields func = function (state, Item, item) { + if (!state.tmp.group_context.tip.condition) { + if (state.output.current.tip.strings.suffix) { + state.tmp.just_did_number = false; + } + } state.output.endTag(); if (this.realGroup) { var flags = state.tmp.group_context.pop(); if (flags.parallel_delimiter_override) { state.tmp.group_context.tip.parallel_delimiter_override = flags.parallel_delimiter_override; + if (!state.tmp.just_looking && state.registry.registry[Item.id].master) { + state.registry.registry[Item.id].parallel_delimiter_override = flags.parallel_delimiter_override; + } } if (flags.parallel_delimiter_override_on_suppress) { state.tmp.group_context.tip.parallel_delimiter_override_on_suppress = flags.parallel_delimiter_override_on_suppress; @@ -10470,27 +10679,31 @@ CSL.Node.group = { if (state.tmp.group_context.tip.condition) { state.tmp.group_context.tip.force_suppress = flags.force_suppress; } + if (!flags.force_suppress && (flags.variable_success || (flags.term_intended && !flags.variable_attempt))) { if (!this.isJurisLocatorLabel) { state.tmp.group_context.tip.variable_success = true; } var blobs = state.output.current.value().blobs; var pos = state.output.current.value().blobs.length - 1; - if (!state.tmp.just_looking && (flags.parallel_last || flags.parallel_first || flags.parallel_delimiter_override || flags.parallel_delimiter_override_on_suppress)) { -// HOWDY + + if (!state.tmp.just_looking && (flags.non_parallel || flags.parallel_last || flags.parallel_first || flags.parallel_delimiter_override || flags.parallel_delimiter_override_on_suppress)) { // flags.parallel_last // flags.parallel_first + + // Returns true ONLY if all variables listed on this group are repeats. var hasRepeat = state.parallel.checkRepeats(flags); if (hasRepeat) { if (blobs) { blobs.pop(); } } - if (state.tmp.cite_index > 0 && (hasRepeat || (!flags.parallel_first && !flags.parallel_last))) { + if (state.tmp.cite_index > 0 && (hasRepeat || (!flags.parallel_first && !flags.parallel_last && !flags.non_parallel))) { //state.sys.print(`${state.tmp.cite_index} ${JSON.stringify(state.tmp.suppress_repeats, null, 2)}`) - if (hasRepeat && flags.parallel_delimiter_override_on_suppress && state.tmp.suppress_repeats[state.tmp.cite_index-1].SIBLING) { + var info = state.tmp.suppress_repeats[state.tmp.cite_index-1]; + if (hasRepeat && flags.parallel_delimiter_override_on_suppress && (info.SIBLING || info.ORPHAN)) { state.output.queue.slice(-1)[0].parallel_delimiter = flags.parallel_delimiter_override_on_suppress; - } else if (flags.parallel_delimiter_override && state.tmp.suppress_repeats[state.tmp.cite_index-1].SIBLING) { + } else if (flags.parallel_delimiter_override && info.SIBLING) { state.output.queue.slice(-1)[0].parallel_delimiter = flags.parallel_delimiter_override; } } @@ -10546,7 +10759,6 @@ CSL.Node.group = { } }; - /*global CSL: true */ CSL.Node["if"] = { @@ -10711,7 +10923,8 @@ CSL.Node.institution = { this.and_term = state.tmp.institution_delimiter; } if ("undefined" === typeof this.and_term && state.tmp.and_term) { - this.and_term = state.getTerm("and", "long", 0); + // this.and_term = state.getTerm("and", "long", 0); + this.and_term = state.tmp.and_term; } if (CSL.STARTSWITH_ROMANESQUE_REGEXP.test(this.and_term)) { this.and_prefix_single = " "; @@ -11068,7 +11281,7 @@ CSL.Node.label = { if (termtxt) { state.tmp.group_context.tip.term_intended = true; } - CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, termtxt); + CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, termtxt, null, this); if (termtxt.indexOf("%s") === -1) { // ^ Suppress output here if we have an embedded term if (this.strings.capitalize_if_first) { @@ -11117,21 +11330,24 @@ CSL.Node.layout = { if (state.build.area === "bibliography") { suffix_token = new CSL.Token("text", CSL.SINGLETON); func = function(state) { - var suffix; - if (state.tmp.cite_affixes[state.tmp.area][state.tmp.last_cite_locale]) { - suffix = state.tmp.cite_affixes[state.tmp.area][state.tmp.last_cite_locale].suffix; - } else { - suffix = state.bibliography.opt.layout_suffix; - } + // Suppress suffix on all but the last item in bibliography parallels + if (!state.tmp.parallel_and_not_last) { + var suffix; + if (state.tmp.cite_affixes[state.tmp.area][state.tmp.last_cite_locale]) { + suffix = state.tmp.cite_affixes[state.tmp.area][state.tmp.last_cite_locale].suffix; + } else { + suffix = state.bibliography.opt.layout_suffix; + } - // If @display is used, layout suffix is placed on the last - // immediate child of the layout, which we assume will be a - // @display group node. - var topblob = state.output.current.value(); - if (state.opt.using_display) { - topblob.blobs[topblob.blobs.length-1].strings.suffix = suffix; - } else { - topblob.strings.suffix = suffix; + // If @display is used, layout suffix is placed on the last + // immediate child of the layout, which we assume will be a + // @display group node. + var topblob = state.output.current.value(); + if (state.opt.using_display) { + topblob.blobs[topblob.blobs.length-1].strings.suffix = suffix; + } else { + topblob.strings.suffix = suffix; + } } if (state.bibliography.opt["second-field-align"]) { // closes bib_other @@ -11866,6 +12082,9 @@ CSL.NameOutput.prototype.outputNames = function () { // set on element_trace. // Need to rescue the value for collapse comparison. var namesToken = CSL.Util.cloneToken(this.names); + if (this.state.tmp.group_context.tip.condition) { + CSL.UPDATE_GROUP_CONTEXT_CONDITION(this.state, this.names.strings.prefix, null, this.names); + } this.state.output.append(blob, namesToken); if (this.state.tmp.term_predecessor_name) { this.state.tmp.term_predecessor = true; @@ -11918,7 +12137,10 @@ CSL.NameOutput.prototype.outputNames = function () { } author_title = author_title.join(", "); if (author_title && this.state.sys.getAbbreviation) { - this.state.transform.loadAbbreviation("default", "classic", author_title); + if (this.state.sys.normalizeAbbrevsKey) { + author_title = this.state.sys.normalizeAbbrevsKey("classic", author_title); + } + this.state.transform.loadAbbreviation("default", "classic", author_title, this.Item.language); if (this.state.transform.abbrevs["default"].classic[author_title]) { this.state.tmp.done_vars.push("title"); this.state.output.append(this.state.transform.abbrevs["default"].classic[author_title], "empty", true); @@ -12385,7 +12607,7 @@ CSL.NameOutput.prototype._checkNickname = function (name) { // The first argument does not have to be the exact variable name. normalizedKey = this.state.sys.normalizeAbbrevsKey("author", author); } - this.state.transform.loadAbbreviation("default", "nickname", normalizedKey); + this.state.transform.loadAbbreviation("default", "nickname", normalizedKey, this.Item.language); // XXX Why does this have to happen here? var myLocalName = this.state.transform.abbrevs["default"].nickname[normalizedKey]; if (myLocalName) { @@ -12402,30 +12624,40 @@ CSL.NameOutput.prototype._checkNickname = function (name) { /*global CSL: true */ +CSL.NameOutput.prototype._purgeEmptyBlobs = function (blobs) { + for (var i = blobs.length - 1; i > -1; i += -1) { + if (!blobs[i] || blobs[i].length === 0 || !blobs[i].blobs.length) { + blobs = blobs.slice(0, i).concat(blobs.slice(i + 1)); + } + } + return blobs; +}; + CSL.NameOutput.prototype.joinPersons = function (blobs, pos, j, tokenname) { var ret; + blobs = this._purgeEmptyBlobs(blobs); if (!tokenname) { tokenname = "name"; } if ("undefined" === typeof j) { if (this.etal_spec[pos].freeters === 1) { - ret = this._joinEtAl(blobs, tokenname); + ret = this._joinEtAl(blobs); } else if (this.etal_spec[pos].freeters === 2) { - ret = this._joinEllipsis(blobs, tokenname); + ret = this._joinEllipsis(blobs); } else if (!this.state.tmp.sort_key_flag) { - ret = this._joinAnd(blobs, tokenname); + ret = this._joinAnd(blobs); } else { - ret = this._join(blobs, " "); + ret = this._join(blobs, this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", ")); } } else { if (this.etal_spec[pos].persons[j] === 1) { - ret = this._joinEtAl(blobs, tokenname); + ret = this._joinEtAl(blobs); } else if (this.etal_spec[pos].persons[j] === 2) { - ret = this._joinEllipsis(blobs, tokenname); + ret = this._joinEllipsis(blobs); } else if (!this.state.tmp.sort_key_flag) { - ret = this._joinAnd(blobs, tokenname); + ret = this._joinAnd(blobs); } else { - ret = this._join(blobs, " "); + ret = this._join(blobs, this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", ")); } } return ret; @@ -12434,12 +12666,13 @@ CSL.NameOutput.prototype.joinPersons = function (blobs, pos, j, tokenname) { CSL.NameOutput.prototype.joinInstitutionSets = function (blobs, pos) { var ret; + blobs = this._purgeEmptyBlobs(blobs); if (this.etal_spec[pos].institutions === 1) { ret = this._joinEtAl(blobs, "institution"); } else if (this.etal_spec[pos].institutions === 2) { ret = this._joinEllipsis(blobs, "institution"); } else { - ret = this._joinAnd(blobs, "institution"); + ret = this._joinAnd(blobs); } return ret; }; @@ -12447,24 +12680,59 @@ CSL.NameOutput.prototype.joinInstitutionSets = function (blobs, pos) { CSL.NameOutput.prototype.joinPersonsAndInstitutions = function (blobs) { // - return this._join(blobs, this.state.tmp.name_delimiter); + blobs = this._purgeEmptyBlobs(blobs); + var ret = this._join(blobs, this.state.tmp.name_delimiter); + ret.isInstitution = true; + return ret; }; // LEGACY // This should go away eventually CSL.NameOutput.prototype.joinFreetersAndInstitutionSets = function (blobs) { // Nothing, one or two, never more + blobs = this._purgeEmptyBlobs(blobs); var ret = this._join(blobs, "[never here]", this["with"].single, this["with"].multiple); //var ret = this._join(blobs, ""); return ret; }; -CSL.NameOutput.prototype._joinEtAl = function (blobs, tokenname) { - // - var blob = this._join(blobs, this.state.tmp.name_delimiter); +CSL.NameOutput.prototype._getAfterInvertedName = function(blobs, delimiter, finalJoin) { + if (finalJoin && blobs.length > 1) { + if (this.state.inheritOpt(this.name, "delimiter-precedes-last") === "after-inverted-name") { + var prevBlob = blobs[blobs.length - 2]; + if (prevBlob.blobs.length > 0 && prevBlob.blobs[0].isInverted) { + finalJoin.strings.prefix = delimiter; + } + } + } + return finalJoin; +} + +CSL.NameOutput.prototype._getAndJoin = function (blobs, delimiter) { + var finalJoin = false; + if (blobs.length > 1) { + var singleOrMultiple = "single"; + if (blobs.length > 2) { + singleOrMultiple = "multiple"; + } + if (blobs[blobs.length - 1].isInstitution) { + finalJoin = this.institution.and[singleOrMultiple]; + } else { + finalJoin = this.name.and[singleOrMultiple]; + } + // finalJoin = new CSL.Blob(finalJoin); + finalJoin = JSON.parse(JSON.stringify(finalJoin)); + finalJoin = this._getAfterInvertedName(blobs, delimiter, finalJoin); + } + return finalJoin; +}; + +CSL.NameOutput.prototype._joinEtAl = function (blobs) { + var delimiter = this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", "); + var blob = this._join(blobs, delimiter); // notSerious - this.state.output.openLevel(this._getToken(tokenname)); + this.state.output.openLevel(this._getToken("name")); // Delimiter is applied from separately saved source in this case, // for discriminate application of single and multiple joins. this.state.output.current.value().strings.delimiter = ""; @@ -12479,61 +12747,56 @@ CSL.NameOutput.prototype._joinEtAl = function (blobs, tokenname) { }; -CSL.NameOutput.prototype._joinEllipsis = function (blobs, tokenname) { - return this._join(blobs, this.state.tmp.name_delimiter, this.name.ellipsis.single, this.name.ellipsis.multiple, tokenname); +CSL.NameOutput.prototype._joinEllipsis = function (blobs) { + var delimiter = this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", "); + var finalJoin = false; + if (blobs.length > 1) { + var singleOrMultiple = "single"; + if (blobs.length > 2) { + singleOrMultiple = "multiple"; + } + finalJoin = JSON.parse(JSON.stringify(this.name.ellipsis[singleOrMultiple])); + finalJoin = this._getAfterInvertedName(blobs, delimiter , finalJoin); + + } + return this._join(blobs, delimiter, finalJoin); +}; + +CSL.NameOutput.prototype._joinAnd = function (blobs) { + var delimiter = this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", "); + var finalJoin = this._getAndJoin(blobs, delimiter); + return this._join(blobs, delimiter, finalJoin); }; -CSL.NameOutput.prototype._joinAnd = function (blobs, tokenname) { - return this._join(blobs, this.state.inheritOpt(this[tokenname], "delimiter", (tokenname + "-delimiter"), ", "), this[tokenname].and.single, this[tokenname].and.multiple, tokenname); -}; - - -CSL.NameOutput.prototype._join = function (blobs, delimiter, single, multiple) { +CSL.NameOutput.prototype._join = function (blobs, delimiter, finalJoin) { var i, ilen; if (!blobs) { return false; } - // Eliminate false and empty blobs - for (i = blobs.length - 1; i > -1; i += -1) { - if (!blobs[i] || blobs[i].length === 0 || !blobs[i].blobs.length) { - blobs = blobs.slice(0, i).concat(blobs.slice(i + 1)); - } - } - // XXXX This needs some attention before moving further. - // Code is not sufficiently transparent. + blobs = this._purgeEmptyBlobs(blobs); if (!blobs.length) { return false; - } else if (single && blobs.length === 2) { - // Clone to avoid corruption of style by affix migration during output - if (single) { - single = new CSL.Blob(single.blobs,single); - } - blobs = [blobs[0], single, blobs[1]]; - } else { - var delimiter_offset; - if (multiple) { - delimiter_offset = 2; - } else { - delimiter_offset = 1; - } - // It kind of makes sense down to here. - for (i = 0, ilen = blobs.length - delimiter_offset; i < ilen; i += 1) { - blobs[i].strings.suffix += delimiter; - } - if (blobs.length > 1) { - var blob = blobs.pop(); - if (multiple) { - // Clone to avoid corruption of style by affix migration during output - multiple = new CSL.Blob(multiple.blobs,multiple); - blobs.push(multiple); + } + if (blobs.length > 1) { + if (blobs.length === 2) { + if (!finalJoin) { + blobs[0].strings.suffix += delimiter; } else { - // Clone to avoid corruption of style by affix migration during output - if (single) { - single = new CSL.Blob(single.blobs,single); - } - blobs.push(single); + blobs = [blobs[0], finalJoin, blobs[1]]; } + } else { + var offset; + if (finalJoin) { + offset = 1; + } else { + offset = 0; + } + var blob = blobs.pop(); + for (var i=0,ilen=blobs.length - offset;i -1) { + nbspace = "\u00a0"; + } else { + nbspace = " "; + } + var blob, merged, first, second; if (romanesque === 0) { // XXX handle affixes for given and family blob = this._join([non_dropping_particle, family, given], ""); } else if (romanesque === 1 || name["static-ordering"]) { // entry likes sort order - blob = this._join([non_dropping_particle, family, given], " "); + merged = this._join([non_dropping_particle, family], nbspace); + blob = this._join([merged, given], " "); } else if (name["reverse-ordering"]) { // entry likes reverse order - blob = this._join([given, non_dropping_particle, family], " "); + merged = this._join([non_dropping_particle, family], nbspace); + blob = this._join([given, merged], " "); } else if (this.state.tmp.sort_key_flag) { // ok with no affixes here if (this.state.opt["demote-non-dropping-particle"] === "never") { - first = this._join([non_dropping_particle, family, dropping_particle], " "); - merged = this._join([first, given], this.state.opt.sort_sep); + merged = this._join([non_dropping_particle, family], nbspace); + merged = this._join([merged, dropping_particle], " "); + merged = this._join([merged, given], this.state.opt.sort_sep); blob = this._join([merged, suffix], " "); } else { second = this._join([given, dropping_particle, non_dropping_particle], " "); @@ -13550,7 +13826,7 @@ CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) { if (has_hyphenated_non_dropping_particle) { first = this._join([non_dropping_particle, family], ""); } else { - first = this._join([non_dropping_particle, family], " "); + first = this._join([non_dropping_particle, family], nbspace); } if (first && this.family) { first.strings.prefix = this.family.strings.prefix; @@ -13567,6 +13843,7 @@ CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) { merged = this._join([first, second], sort_sep); blob = this._join([merged, suffix], sort_sep); } + blob.isInverted = true; } else { // plain vanilla if (name["dropping-particle"] && name.family && !name["non-dropping-particle"]) { var dp = name["dropping-particle"]; @@ -13577,18 +13854,11 @@ CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) { } } - var space = " "; - if (this.state.inheritOpt(this.name, "initialize-with") - && this.state.inheritOpt(this.name, "initialize-with").match(/[\u00a0\ufeff]/) - && ["fr", "ru", "cs"].indexOf(this.state.opt["default-locale"][0].slice(0, 2)) > -1) { - space = "\u00a0"; - } - if (has_hyphenated_non_dropping_particle) { second = this._join([non_dropping_particle, family], ""); - second = this._join([dropping_particle, second], space); + second = this._join([dropping_particle, second], nbspace); } else { - second = this._join([dropping_particle, non_dropping_particle, family], space); + second = this._join([dropping_particle, non_dropping_particle, family], nbspace); } second = this._join([second, suffix], suffix_sep); if (second && this.family) { @@ -13602,6 +13872,16 @@ CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) { if (second.strings.prefix) { name["comma-dropping-particle"] = ""; } + + var space; + if (this.state.inheritOpt(this.name, "initialize-with") + && this.state.inheritOpt(this.name, "initialize-with").match(/[\u00a0\ufeff]/) + && givenInfo.initializationLevel === 1) { + + space = nbspace; + } else { + space = " "; + } blob = this._join([given, second], (name["comma-dropping-particle"] + space)); } // XXX Just generally assume for the present that personal names render something @@ -13756,7 +14036,9 @@ CSL.NameOutput.prototype._givenName = function (name, pos, i) { name.given = CSL.Util.Names.unInitialize(this.state, name.given); } } else if (useLevel === 0) { - return false; + return { + blob: false + } } else if (useLevel === 2) { name.given = CSL.Util.Names.unInitialize(this.state, name.given); } @@ -13765,9 +14047,14 @@ CSL.NameOutput.prototype._givenName = function (name, pos, i) { var rendered = this.state.output.append(str, this.given_decor, true); if (rendered) { ret = this.state.output.pop(); - return ret; + return { + blob: ret, + initializationLevel: useLevel + }; } - return false; + return { + blob: false + }; }; CSL.NameOutput.prototype._nameSuffix = function (name) { @@ -14010,56 +14297,95 @@ CSL.NameOutput.prototype.setRenderedName = function (name) { }; CSL.NameOutput.prototype.fixupInstitution = function (name, varname, listpos) { - name = this._splitInstitution(name, varname, listpos); - // XXX This should be embedded in the institution name function. - if (this.institution.strings["reverse-order"]) { - name["long"].reverse(); + if (!name.literal && name.family) { + name.literal = name.family; + delete name.family; } - - var long_form = name["long"]; - var short_form = name["long"].slice(); - var use_short_form = false; - var itemJurisdiction = this.Item.jurisdiction; + var longNameStr = name.literal; + var shortNameStr = longNameStr; + var ret = { + "long": longNameStr.split(/\s*\|\s*/), + "short": shortNameStr.split(/\s*\|\s*/), + }; if (this.state.sys.getAbbreviation) { - var jurisdiction = itemJurisdiction; - for (var j = 0, jlen = long_form.length; j < jlen; j += 1) { - var abbrevKey = long_form[j]; - jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", abbrevKey); - if (this.state.transform.abbrevs[jurisdiction]["institution-part"][abbrevKey]) { - short_form[j] = this.state.transform.abbrevs[jurisdiction]["institution-part"][abbrevKey]; - use_short_form = true; + // Normalize longNameStr and shortNameStr + if (this.institution.strings.form === "short") { + let jurisdiction = this.Item.jurisdiction; + jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-entire", longNameStr, this.Item.language); + if (this.state.transform.abbrevs[jurisdiction]["institution-entire"][longNameStr]) { + longNameStr = this.state.transform.abbrevs[jurisdiction]["institution-entire"][longNameStr]; + } else { + jurisdiction = this.Item.jurisdiction; + jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", abbrevKey, this.Item.language); + if (this.state.transform.abbrevs[jurisdiction]["institution-part"][longNameStr]) { + longNameStr = this.state.transform.abbrevs[jurisdiction]["institution-part"][longNameStr]; + } + } + longNameStr = this._quashChecks(jurisdiction, longNameStr); + } + if (["short", "short-long", "long-short"].indexOf(this.institution.strings["institution-parts"]) > -1) { + let jurisdiction = this.Item.jurisdiction; + jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", shortNameStr, this.Item.language); + if (this.state.transform.abbrevs[jurisdiction]["institution-part"][shortNameStr]) { + shortNameStr = this.state.transform.abbrevs[jurisdiction]["institution-part"][shortNameStr]; + } + shortNameStr = this._quashChecks(jurisdiction, shortNameStr); + if (["short-long", "long-short"].indexOf(this.institution.strings["institution-parts"]) > -1) { + if (shortNameStr === longNameStr) { + shortNameStr = ""; + } } } - } - if (use_short_form) { - var delimiter = this.institution.strings.delimiter; - var use_first = this.institution.strings["use-first"]; - if (use_first) { - if (!delimiter) { - delimiter = "|"; - } - var buf = []; - for (var i=0,ilen=short_form.length;i -1) { + for (var j=ret["short"].length-1; j>-1; j--) { + let jurisdiction = this.Item.jurisdiction; + var abbrevKey = ret["short"][j]; + jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", abbrevKey, this.Item.language); + if (this.state.transform.abbrevs[jurisdiction]["institution-part"][abbrevKey]) { + ret["short"][j] = this.state.transform.abbrevs[jurisdiction]["institution-part"][abbrevKey]; + } + if (ret["short"][j].indexOf("|") > -1) { + let retShort = ret["short"]; + let splitShort = retShort[j].split(/\s*\|\s*/); + ret["short"] = retShort.slice(0, j).concat(splitShort).concat(retShort.slice(j+1)); + } } } - name["short"] = short_form; - } else { - name["short"] = []; - } - // trimmer is not available in getAmbiguousCite - if (!this.state.tmp.just_looking) { - if (itemJurisdiction) { - var trimmer = this.state.tmp.abbrev_trimmer; - if (trimmer && trimmer[itemJurisdiction] && trimmer[itemJurisdiction][varname]) { - for (var i=0,ilen=name["short"].length;i>[0-9]{4}>>/); + var m = str.match(/>>([0-9]{4})>>/); + str = lst.pop(); + var date = this.Item["original-date"] ? this.Item["original-date"] : this.Item["issued"]; + if (date) { + date = parseInt(date.year, 10); + date = isNaN(date) ? false : date; } - var splitInstitution = value.literal.replace(/\s*\|\s*/g, "|"); - // check for total and utter abbreviation IFF form="short" - splitInstitution = splitInstitution.split("|"); - if (this.institution.strings.form === "short" && this.state.sys.getAbbreviation) { - // On a match, drop unused elements to yield a single key. - var itemJurisdiction = this.Item.jurisdiction; - for (var j = splitInstitution.length; j > 0; j += -1) { - var jurisdiction = itemJurisdiction; - var str = splitInstitution.slice(0, j).join("|"); - var abbrevKey = str; - jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-entire", abbrevKey); - if (this.state.transform.abbrevs[jurisdiction]["institution-entire"][abbrevKey]) { - var splitLst = this.state.transform.abbrevs[jurisdiction]["institution-entire"][abbrevKey]; - - splitLst = this.state.transform.quashCheck(itemJurisdiction, splitLst); - - // If the abbreviation has date cut-offs, find the most recent - // abbreviation within scope. - var splitSplitLst = splitLst.split(/>>[0-9]{4}>>/); - var m = splitLst.match(/>>([0-9]{4})>>/); - splitLst = splitSplitLst.pop(); - if (splitSplitLst.length > 0 && this.Item["original-date"] && this.Item["original-date"].year) { - for (var k=m.length - 1; k > 0; k += -1) { - if (parseInt(this.Item["original-date"].year, 10) >= parseInt(m[k], 10)) { - break; - } - splitLst = splitSplitLst.pop(); - } + if (date) { + if (lst.length > 0) { + for (var k=m.length-1; k>0; k--) { + if (date >= parseInt(m[k], 10)) { + break; } - splitLst = splitLst.replace(/\s*\|\s*/g, "|"); - splitInstitution = [splitLst]; - break; + str = lst.pop(); } } + str = str.replace(/\s*\|\s*/g, "|"); } - splitInstitution.reverse(); - //print("into _trimInstitution with splitInstitution, v, i = "+splitInstitution+", "+v+", "+i); - ret["long"] = this._trimInstitution(splitInstitution, v, i); - return ret; -}; + return str; +} -CSL.NameOutput.prototype._trimInstitution = function (subunits, v) { - // +CSL.NameOutput.prototype._trimInstitution = function (subunits) { + // Oh! Good catch in the tests. This happens before abbrevs. + // Won't work that way. Need to do abbrev substitute first, + // and apply this separately to long and to short. var use_first = false; - var append_last = false; - var s = subunits.slice(); var stop_last = false; + var use_last = false; + var stop_first = false; + var s = subunits.slice(); if (this.institution) { + // If use_first, apply stop_last, then apply use_first; + // If use_last, apply stop_first, then apply use_last; if ("undefined" !== typeof this.institution.strings["use-first"]) { + // this.state.sys.print("use-first OK"); use_first = this.institution.strings["use-first"]; } - if ("undefined" !== typeof this.institution.strings["stop-last"]) { - // stop-last is negative when present - stop_last = this.institution.strings["stop-last"]; - } else if ("authority" === v && this.state.tmp.authority_stop_last) { - stop_last = this.state.tmp.authority_stop_last; - } - if (stop_last) { - s = s.slice(0, stop_last); - subunits = subunits.slice(0, stop_last); - } if ("undefined" !== typeof this.institution.strings["use-last"]) { - append_last = this.institution.strings["use-last"]; + // this.state.sys.print("use-last OK"); + use_last = this.institution.strings["use-last"]; } - if ("authority" === v) { - if (stop_last) { - this.state.tmp.authority_stop_last = stop_last; - } - if (append_last) { - this.state.tmp.authority_stop_last += (append_last * -1); - } + if ("undefined" !== typeof this.institution.strings["stop-first"]) { + // this.state.sys.print("stop-first OK"); + stop_first = this.institution.strings["stop-first"]; } - } - if (false === use_first) { - if (this.persons[v].length === 0) { - use_first = this.institution.strings["substitute-use-first"]; + if ("undefined" !== typeof this.institution.strings["stop-last"]) { + stop_last = this.institution.strings["stop-last"]; } - if (!use_first) { - use_first = 0; - } - } - if (false === append_last) { - if (!use_first) { - append_last = subunits.length; - } else { - append_last = 0; - } - } - // Now that we've determined the value of append_last - // (use-last), avoid overlaps. - if (use_first > subunits.length - append_last) { - use_first = subunits.length - append_last; - } - // This could be more clear. use-last takes priority - // in the event of overlap, because of adjustment above - subunits = subunits.slice(0, use_first); - s = s.slice(use_first); - if (append_last) { - if (append_last > s.length) { - append_last = s.length; + if (use_first) { + if (stop_last) { + s = s.slice(0, stop_last * -1); + } + s = s.slice(0, use_first); } - if (append_last) { - subunits = subunits.concat(s.slice((s.length - append_last))); + + if (use_last) { + var ss = subunits.slice(); + if (use_first) { + stop_first = use_first; + } else { + s = []; + } + if (stop_first) { + ss = ss.slice(stop_first); + } + ss = ss.slice(use_last * -1); + s = s.concat(ss); } + subunits = s; } return subunits; }; @@ -14862,9 +15148,10 @@ CSL.Node.number = { // amazing that we reach this. should abort sooner if no content? state.processNumber.call(state, node, item, varname, Item.type); } else { - if (!state.tmp.group_context.tip.condition) { - state.tmp.just_did_number = true; + if (!state.tmp.group_context.tip.condition && Item[varname]) { + state.tmp.just_did_number = ("" + Item[varname]).match(/[0-9]$/); } + // UPDATE_GROUP_CONTEXT_CONDITION is run by processNumber state.processNumber.call(state, node, Item, varname, Item.type); } @@ -15192,7 +15479,7 @@ CSL.Node.text = { if (term !== "") { state.tmp.group_context.tip.term_intended = true; } - CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, term); + CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, term, null, this); // capitalize the first letter of a term, if it is the // first thing rendered in a citation (or if it is @@ -15219,6 +15506,11 @@ CSL.Node.text = { } } state.output.append(myterm, this); + if (state.tmp.can_block_substitute) { + // Black magic here. This causes the cs:substitution condition to pass, + // blocking further rendering within its scope. + state.tmp.can_substitute.replace(false, CSL.LITERAL); + } }; this.execs.push(func); state.build.term = false; @@ -15424,8 +15716,13 @@ CSL.Node.text = { func = function (state) { state.tmp.group_context.tip.term_intended = true; // true flags that this is a literal-value term - CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, this.strings.value, true); + CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, this.strings.value, true, this); state.output.append(this.strings.value, this); + if (state.tmp.can_block_substitute) { + // Black magic here. This causes the cs:substitution condition to pass, + // blocking further rendering within its scope. + state.tmp.can_substitute.replace(false, CSL.LITERAL); + } }; this.execs.push(func); // otherwise no output @@ -15588,13 +15885,13 @@ CSL.Attributes["@position"] = function (state, arg) { state.opt.update_mode = CSL.POSITION; var trypositions = arg.split(/\s+/); var testSubsequentNear = function (Item, item) { - if (item && item.position >= CSL.POSITION_SUBSEQUENT && item["near-note"]) { + if (item && CSL.POSITION_MAP[item.position] >= CSL.POSITION_MAP[CSL.POSITION_SUBSEQUENT] && item["near-note"]) { return true; } return false; }; var testSubsequentNotNear = function (Item, item) { - if (item && item.position == CSL.POSITION_SUBSEQUENT && !item["near-note"]) { + if (item && CSL.POSITION_MAP[item.position] == CSL.POSITION_MAP[CSL.POSITION_SUBSEQUENT] && !item["near-note"]) { return true; } return false; @@ -15610,7 +15907,7 @@ CSL.Attributes["@position"] = function (state, arg) { if (item && typeof item.position === "number") { if (item.position === 0 && tryposition === 0) { return true; - } else if (tryposition > 0 && item.position >= tryposition) { + } else if (tryposition > 0 && CSL.POSITION_MAP[item.position] >= CSL.POSITION_MAP[tryposition]) { return true; } } else if (tryposition === 0) { @@ -15623,6 +15920,8 @@ CSL.Attributes["@position"] = function (state, arg) { var tryposition = trypositions[i]; if (tryposition === "first") { tryposition = CSL.POSITION_FIRST; + } else if (tryposition === "container-subsequent") { + tryposition = CSL.POSITION_CONTAINER_SUBSEQUENT; } else if (tryposition === "subsequent") { tryposition = CSL.POSITION_SUBSEQUENT; } else if (tryposition === "ibid") { @@ -15799,6 +16098,11 @@ CSL.Attributes["@variable"] = function (state, arg) { output = true; } break; + } else if ("first-container-reference-note-number" === variable) { + if (item && item["first-container-reference-note-number"]) { + output = true; + } + break; } else if ("hereinafter" === variable) { if (state.transform.abbrevs["default"].hereinafter[Item.id] && state.sys.getAbbreviation @@ -15853,7 +16157,7 @@ CSL.Attributes["@variable"] = function (state, arg) { var maketest = function (variable) { return function(Item,item){ var myitem = Item; - if (item && ["locator", "locator-extra", "first-reference-note-number", "locator-date"].indexOf(variable) > -1) { + if (item && ["locator", "locator-extra", "first-reference-note-number", "first-container-reference-note-number", "locator-date"].indexOf(variable) > -1) { myitem = item; } // We don't run loadAbbreviation() here; it is run by the application-supplied @@ -15970,6 +16274,28 @@ CSL.Attributes["@jurisdiction"] = function (state, arg) { this.tests.push(maketests(tryjurisdictions)); }; +CSL.Attributes["@country"] = function (state, arg) { + if (!this.tests) {this.tests = []; }; + var trycountries = arg.split(/\s+/); + + // This forces a match=any method, similar to @type + var maketests = function (trycountries) { + return function(Item) { + if (!Item.country) { + return false; + } + var country = Item.country; + for (var i=0,ilen=trycountries.length;i 1) { + return retval; + } + return !retval; + }; + }; + this.tests.push(maketest(retval)); +}; + +CSL.Attributes["@container-subsequent"] = function (state, arg) { + if (!this.tests) {this.tests = []; }; + var retval = "true" === arg ? true : false; + var maketest = function (retval) { + return function(Item) { + if (state.tmp.container_item_pos[Item.container_id] > 1) { + return retval; + } + return !retval; + }; + }; + this.tests.push(maketest(retval)); +}; + CSL.Attributes["@has-subunit"] = function (state, arg) { if (!this.tests) {this.tests = []; }; var maketest = function(namevar) { @@ -16310,6 +16666,27 @@ CSL.Attributes["@has-subunit"] = function (state, arg) { this.tests.push(maketest(arg)); } +CSL.Attributes["@disable-duplicate-year-suppression"] = function (state, arg) { + state.opt.disable_duplicate_year_suppression = arg.split(/\s+/); +} + +CSL.Attributes["@consolidate-containers"] = function (state, arg) { + CSL.Attributes["@track-containers"](state, arg); + var args = arg.split(/\s+/); + state.bibliography.opt.consolidate_containers = args; +} + +CSL.Attributes["@track-containers"] = function (state, arg) { + var args = arg.split(/\s+/); + if (!state.bibliography.opt.track_container_items) { + state.bibliography.opt.track_container_items = []; + } + if (!state.bibliography.opt.consolidate_containers) { + state.bibliography.opt.consolidate_containers = []; + } + state.bibliography.opt.track_container_items = state.bibliography.opt.track_container_items.concat(args); +} + // These are not evaluated as conditions immediately: they only // set parameters that are picked up during processing. CSL.Attributes["@parallel-first"] = function (state, arg) { @@ -16345,23 +16722,28 @@ CSL.Attributes["@parallel-last-to-first"] = function (state, arg) { this.parallel_last_to_first[vars[i]] = true; } }; -CSL.Attributes["@parallel-last-override"] = function (state, arg) { - var vars = arg.split(/\s+/); - this.parallel_last_override = {}; - for (var i in vars) { - var v = vars[i]; - this.parallel_last_override[v] = true; - } -}; CSL.Attributes["@parallel-delimiter-override"] = function (state, arg) { this.strings.set_parallel_delimiter_override = arg; }; CSL.Attributes["@parallel-delimiter-override-on-suppress"] = function (state, arg) { this.strings.set_parallel_delimiter_override_on_suppress = arg; }; - +CSL.Attributes["@no-repeat"] = function (state, arg) { + state.opt.parallel.enable = true; + var vars = arg.split(/\s+/); + if (!state.opt.track_repeat) { + state.opt.track_repeat = {}; + } + this.non_parallel = {}; + for (var i in vars) { + var v = vars[i]; + this.non_parallel[v] = true; + state.opt.track_repeat[v] = true; + } +}; CSL.Attributes["@require"] = function (state, arg) { + state.opt.use_context_condition = true; this.strings.require = arg; // Introduced to constrain rendering of the group with a @@ -16375,6 +16757,7 @@ CSL.Attributes["@require"] = function (state, arg) { }; CSL.Attributes["@reject"] = function (state, arg) { + state.opt.use_context_condition = true; this.strings.reject = arg; // Introduced to constrain rendering of the group with a @@ -16937,14 +17320,18 @@ CSL.Attributes["@use-first"] = function (state, arg) { this.strings["use-first"] = parseInt(arg, 10); }; -CSL.Attributes["@stop-last"] = function (state, arg) { - this.strings["stop-last"] = parseInt(arg, 10) * -1; -}; - CSL.Attributes["@use-last"] = function (state, arg) { this.strings["use-last"] = parseInt(arg, 10); }; +CSL.Attributes["@stop-first"] = function (state, arg) { + this.strings["stop-first"] = parseInt(arg, 10); +}; + +CSL.Attributes["@stop-last"] = function (state, arg) { + this.strings["stop-last"] = parseInt(arg, 10) * -1; +}; + CSL.Attributes["@reverse-order"] = function (state, arg) { if ("true" === arg) { @@ -17071,11 +17458,13 @@ CSL.Parallel.prototype.StartCitation = function (sortedItems, out) { var idxEnd = 0; var parallelMatchList = false; var siblingRanges = []; - + for (var i=0,ilen=sortedItems.length-1;i 0 && !parallelMatchList) { freshMatchList = true; parallelMatchList = [sortedItems[i][0].id].concat(sortedItems[i][0].seeAlso); var tempMatchList = parallelMatchList.slice(); @@ -17098,56 +17487,53 @@ CSL.Parallel.prototype.StartCitation = function (sortedItems, out) { } } } - if (parallelMatchList) { - if (i > 0 && freshMatchList) { - this.state.tmp.suppress_repeats[i-1].START = true; - freshMatchList = false; - } - var currItem = sortedItems[i][0]; - var nextItem = sortedItems[i+1][0]; - for (var varname in this.state.opt.track_repeat) { - // if (varname === "title") { - // this.state.sys.print(`curr: ${currItem}, next: ${JSON.stringify(nextItem, null, 2)}`); - // } - if (!currItem[varname] || !nextItem[varname]) { - // Go ahead and render any value with an empty partner - info[varname] = false; - } else if ("string" === typeof nextItem[varname] || "number" === typeof nextItem[varname]) { - // Simple comparison of string values - if (varname === "title" && currItem["title-short"] && nextItem["title-short"]) { - var currVal = currItem["title-short"]; - var nextVal = nextItem["title-short"]; - } else { - var currVal = currItem[varname]; - var nextVal = nextItem[varname]; - } - if (currVal == nextVal) { - info[varname] = true; - } else { - info[varname] = false; - } - } else if ("undefined" === typeof currItem[varname].length) { - // If a date, use only the year - info[varname] = false; - var currYear = currItem[varname].year; - var nextYear = nextItem[varname].year; - if (currYear && nextYear) { - if (currYear == nextYear) { - info[varname] = true; - } - } + // parallelMatchList/freshMatchList relate only to parallels. + // no-repeat non-parallels are handled in a separate block. + if (i > 0 && freshMatchList) { + this.state.tmp.suppress_repeats[i-1].START = true; + freshMatchList = false; + } + for (var varname in this.state.opt.track_repeat) { + if (!currItem[varname] || !nextItem[varname]) { + // Go ahead and render any value with an empty partner + info[varname] = false; + } else if ("string" === typeof nextItem[varname] || "number" === typeof nextItem[varname]) { + // Simple comparison of string values + if (varname === "title" && currItem["title-short"] && nextItem["title-short"]) { + var currVal = currItem["title-short"]; + var nextVal = nextItem["title-short"]; } else { - // If a creator value, kludge it - var currVal = JSON.stringify(currItem[varname]); - var nextVal = JSON.stringify(nextItem[varname]); - if (currVal === nextVal) { + var currVal = currItem[varname]; + var nextVal = nextItem[varname]; + } + if (currVal == nextVal) { + info[varname] = true; + } else { + info[varname] = false; + } + } else if ("undefined" === typeof currItem[varname].length) { + // If a date, use only the year + info[varname] = false; + var currYear = currItem[varname].year; + var nextYear = nextItem[varname].year; + if (currYear && nextYear) { + if (currYear == nextYear) { info[varname] = true; - } else { - info[varname] = false; } } + } else { + // If a creator value, kludge it + var currVal = JSON.stringify(currItem[varname]); + var nextVal = JSON.stringify(nextItem[varname]); + if (currVal === nextVal) { + info[varname] = true; + } else { + info[varname] = false; + } } - + } + if (!parallelMatchList) { + info.ORPHAN = true; } if (idxEnd === i) { info.END = true; @@ -17155,6 +17541,12 @@ CSL.Parallel.prototype.StartCitation = function (sortedItems, out) { } this.state.tmp.suppress_repeats.push(info); } + + // if (!this.state.tmp.just_looking) { + // this.state.sys.print(`${JSON.stringify(this.state.tmp.suppress_repeats, null, 2)}`); + // } + + // Set no-repeat info here? for (var j=0,jlen=siblingRanges.length;j 0) { var arr = [{}].concat(this.state.tmp.suppress_repeats); - if (arr[idx][varname] && !arr[idx].START) { - return true; - } - } - } - if (params.parallel_last) { - var arr = this.state.tmp.suppress_repeats.concat([{}]); - if (params.parallel_last_override) { - for (var v in params.parallel_last_override) { - if (params.parallel_first && params.parallel_first[v]) { - params.parallel_last[v] = true; + var ret = true; + for (var varname in params.parallel_first) { + if (!arr[idx][varname] || arr[idx].START) { + // true --> suppress the entry + // Test here evaluates as "all", not "any" + ret = false; } } + return ret; } - for (var varname in params.parallel_last) { - if (arr[idx][varname] && !arr[idx].END) { - return true; + if (params.parallel_last && Object.keys(params.parallel_last).length > 0) { + var arr = this.state.tmp.suppress_repeats.concat([{}]); + var ret = Object.keys(params.parallel_last).length > 0 ? true : false; + for (var varname in params.parallel_last) { + if (!arr[idx][varname] || arr[idx].END) { + // "all" match, as above. + ret = false; + } } + return ret; + } + if (params.non_parallel && Object.keys(params.non_parallel).length > 0) { + var arr = [{}].concat(this.state.tmp.suppress_repeats); + var ret = true; + for (var varname in params.non_parallel) { + if (!arr[idx][varname]) { + ret = false; + } + } + return ret; } } return false; @@ -17377,7 +17775,7 @@ CSL.Transform = function (state) { } else { preferredJurisdiction = "default"; } - var jurisdiction = state.transform.loadAbbreviation(preferredJurisdiction, myabbrev_family, normalizedKey, Item.type); + var jurisdiction = state.transform.loadAbbreviation(preferredJurisdiction, myabbrev_family, normalizedKey, Item.language); // Some rules: // # variable === "country" @@ -17595,10 +17993,15 @@ CSL.Transform = function (state) { // Setter for abbreviation lists // This initializes a single abbreviation based on known // data. - function loadAbbreviation(jurisdiction, category, orig, itemType) { + function loadAbbreviation(jurisdiction, category, orig, lang) { if (!jurisdiction) { jurisdiction = "default"; } + var country = jurisdiction.split(":")[0]; + var domain = CSL.getAbbrevsDomain(state, country, lang); + if (domain) { + jurisdiction += ("@" + domain); + } if (!orig) { if (!state.transform.abbrevs[jurisdiction]) { state.transform.abbrevs[jurisdiction] = new state.sys.AbbreviationSegments(); @@ -17616,9 +18019,12 @@ CSL.Transform = function (state) { // // See testrunner_stdrhino.js for an example. if (state.sys.getAbbreviation) { - jurisdiction = state.sys.getAbbreviation(state.opt.styleID, state.transform.abbrevs, jurisdiction, category, orig, itemType, true); + jurisdiction = state.sys.getAbbreviation(state.opt.styleID, state.transform.abbrevs, jurisdiction, category, orig); if (!jurisdiction) { jurisdiction = "default"; + if (domain) { + jurisdiction += ("@" + domain); + } } } return jurisdiction; @@ -17864,6 +18270,7 @@ CSL.Transform = function (state) { if (primary_locale) { state.tmp.lang_array = [primary_locale].concat(oldLangArray); } + CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, null, null, primary_tok, primary_tok.strings.prefix + primary); state.output.append(primary, primary_tok); state.tmp.probably_rendered_something = true; @@ -17943,6 +18350,7 @@ CSL.Transform = function (state) { if (primary_locale) { state.tmp.lang_array = [primary_locale].concat(oldLangArray); } + CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, null, null, primary_tok, primary_tok.strings.prefix + primary); state.output.append(primary, primary_tok); state.tmp.probably_rendered_something = true; } @@ -18829,6 +19237,8 @@ CSL.Util.Dates.year.imperial = function (state, num, end) { label = '\u5e73\u6210'; offset = 1988; } + // This entire "imperial" code block should be cut. Scraped input + // for this will be too ratty to be useful anyway. if (label && offset) { var normalizedKey = label; if (state.sys.normalizeAbbrevsKey) { @@ -18837,7 +19247,9 @@ CSL.Util.Dates.year.imperial = function (state, num, end) { normalizedKey = state.sys.normalizeAbbrevsKey("number", label); } if (!state.transform.abbrevs['default']['number'][normalizedKey]) { - state.transform.loadAbbreviation('default', "number", normalizedKey); + // loadAbbreviation normally takes an item as fourth argument. + // It is not available here, + state.transform.loadAbbreviation('default', "number", normalizedKey, null); } if (state.transform.abbrevs['default']['number'][normalizedKey]) { label = state.transform.abbrevs['default']['number'][normalizedKey]; @@ -19210,6 +19622,7 @@ CSL.Util.substituteStart = function (state, target) { } var positionMap = [ "first", + "container-subsequent", "subsequent", "ibid", "ibid-with-locator" @@ -19222,6 +19635,10 @@ CSL.Util.substituteStart = function (state, target) { if (item && item['first-reference-note-number']) { firstReferenceNoteNumber = item['first-reference-note-number']; } + var firstContainerReferenceNoteNumber = 0; + if (item && item['first-container-reference-note-number']) { + firstContainerReferenceNoteNumber = item['first-container-reference-note-number']; + } var citationNumber = 0; // XXX Will this EVER happen? if (item && item['citation-number']) { @@ -19239,6 +19656,7 @@ CSL.Util.substituteStart = function (state, target) { position: positionMap[position], "note-number": noteNumber, "first-reference-note-number": firstReferenceNoteNumber, + "first-container-reference-note-number": firstContainerReferenceNoteNumber, "citation-number": citationNumber, "index": index, "mode": state.opt.mode @@ -19718,6 +20136,9 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) { str = normalizeFieldValue(str, defaultLabel); var jmrex, jsrex, mystr; + if (str.indexOf("\u2013") > -1) { + str = str.replace(/\u2013/g, "-"); + } if (str.indexOf("\\-") > -1) { jmrex = new RegExp(joinerMatchRex.source.replace("\\-", "")); jsrex = new RegExp(joinerSplitRex.source.replace("\\-", "")); @@ -20208,16 +20629,23 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) { // No need for this. //val = ("" + val).replace(/^\"/, "").replace(/\"$/, ""); - - var jurisdiction = this.transform.loadAbbreviation(ItemObject.jurisdiction, "number", val); + if (this.sys.normalizeAbbrevsKey) { + var normval = this.sys.normalizeAbbrevsKey(realVariable, val); + } else { + var normval = val; + } + var jurisdiction = this.transform.loadAbbreviation(ItemObject.jurisdiction, "number", normval, ItemObject.language); if (this.transform.abbrevs[jurisdiction].number) { - if (this.transform.abbrevs[jurisdiction].number[val]) { - val = this.transform.abbrevs[jurisdiction].number[val]; + if (this.transform.abbrevs[jurisdiction].number[normval]) { + val = this.transform.abbrevs[jurisdiction].number[normval]; } else { + + // *** This is terrible *** + // Strings rendered via cs:number should not be added to the abbreviations // UI unless they test non-numeric. The test happens below. - if ("undefined" !== typeof this.transform.abbrevs[jurisdiction].number[val]) { - delete this.transform.abbrevs[jurisdiction].number[val]; + if ("undefined" !== typeof this.transform.abbrevs[jurisdiction].number[normval]) { + delete this.transform.abbrevs[jurisdiction].number[normval]; } } } @@ -20302,6 +20730,7 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) { CSL.Util.outputNumericField = function(state, varname, itemID) { state.output.openLevel(state.tmp.shadow_numbers[varname].masterStyling); + var masterStyling = state.tmp.shadow_numbers[varname].masterStyling; var nums = state.tmp.shadow_numbers[varname].values; var masterLabel = nums.length ? nums[0].label : null; var labelForm = state.tmp.shadow_numbers[varname].labelForm; @@ -20364,6 +20793,7 @@ CSL.Util.outputNumericField = function(state, varname, itemID) { numStyling.type = num.styling.type; numStyling.num = num.styling.num; numStyling.gender = num.styling.gender; + if (labelPlaceholderPos > 0 && labelPlaceholderPos < (label.length-2)) { numStyling.strings.prefix += label.slice(0,labelPlaceholderPos); numStyling.strings.suffix = label.slice(labelPlaceholderPos+2) + numStyling.strings.suffix; @@ -20381,6 +20811,7 @@ CSL.Util.outputNumericField = function(state, varname, itemID) { state.output.append(label+num.labelSuffix, "empty"); } } + CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, masterStyling.strings.prefix, null, masterStyling, `${num.particle}${num.value}`); if (num.collapsible) { var blob; if (num.value.match(/^[1-9][0-9]*$/)) { @@ -22489,7 +22920,7 @@ CSL.Registry.prototype.dodeletes = function (myhash) { if (this.registry[loneSiblingID].siblings) { this.registry[loneSiblingID].siblings.pop(); this.registry[loneSiblingID].master = true; - this.registry[loneSiblingID].parallel = false; + // this.registry[loneSiblingID].parallel = false; } } else if (this.registry[key].siblings.length > 1) { var removeIDs = [key]; @@ -22497,11 +22928,12 @@ CSL.Registry.prototype.dodeletes = function (myhash) { var newmasterID = this.registry[key].siblings[0]; var newmaster = this.registry[newmasterID]; newmaster.master = true; - newmaster.parallel = false; + // newmaster.parallel_delimiter is set externally, if at all + // newmaster.parallel = false; removeIDs.push(newmasterID); - for (var k = 0, klen = this.registry[key].siblings.length; k < klen; k += 1) { - this.registry[this.registry[key].siblings[k]].parallel = newmasterID; - } + // for (var k = 0, klen = this.registry[key].siblings.length; k < klen; k += 1) { + // this.registry[this.registry[key].siblings[k]].parallel = newmasterID; + // } } var buffer = []; for (var k = this.registry[key].siblings.length - 1; k > -1; k += -1) { @@ -23245,6 +23677,12 @@ CSL.Registry.NameReg = function (state) { && pos !== 0) { return; } + + // A hack. Safe if the name object is used only here, for disambiguation purposes. + if (state.opt["demote-non-dropping-particle"] === "never" && nameobj["non-dropping-particle"] && nameobj["family"]) { + nameobj["family"] = nameobj["non-dropping-particle"] + " " + nameobj["family"]; + } + //CSL.debug("INS"); set_keys(this.state, "" + item_id, nameobj); // pkey, ikey and skey should be stored in separate cascading objects. @@ -23940,7 +24378,12 @@ CSL.Engine.prototype.getJurisdictionList = function (jurisdiction) { var jurisdictionList = []; var jurisdictionElems = jurisdiction.split(":"); for (var j=jurisdictionElems.length;j>0;j--) { - jurisdictionList.push(jurisdictionElems.slice(0,j).join(":")); + var composedID = jurisdictionElems.slice(0,j).join(":"); + jurisdictionList.push(composedID); + if (this.opt.jurisdiction_fallbacks[composedID]) { + var fallback = this.opt.jurisdiction_fallbacks[composedID]; + jurisdictionList.push(fallback); + } } if (jurisdictionList.indexOf("us") === -1) { jurisdictionList.push("us"); @@ -23948,6 +24391,60 @@ CSL.Engine.prototype.getJurisdictionList = function (jurisdiction) { return jurisdictionList; }; +CSL.Engine.prototype.loadStyleModule = function (jurisdiction, xmlSource, skipFallback) { + var myFallback = null; + var macroCount = 0; + this.juris[jurisdiction] = {}; + var myXml = CSL.setupXml(xmlSource); + myXml.addMissingNameNodes(myXml.dataObj); + myXml.addInstitutionNodes(myXml.dataObj); + myXml.insertPublisherAndPlace(myXml.dataObj); + myXml.flagDateMacros(myXml.dataObj); + var myNodes = myXml.getNodesByName(myXml.dataObj, "law-module"); + for (var i=0,ilen=myNodes.length;i