diff --git a/lib/citeproc-js/.github/workflows/ci.yml b/lib/citeproc-js/.github/workflows/ci.yml new file mode 100644 index 0000000..dea8fae --- /dev/null +++ b/lib/citeproc-js/.github/workflows/ci.yml @@ -0,0 +1,17 @@ +name: CI tests +on: [push] +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v2 + with: + submodules: recursive + - name: install node + uses: actions/setup-node@v2 + with: + node-version: 16.x + - run: npm install -g mocha + - run: npm install -g citeproc-test-runner + - run: cslrun -a --reporter spec diff --git a/lib/citeproc-js/.gitignore b/lib/citeproc-js/.gitignore index 8eeb58a..217cd96 100644 --- a/lib/citeproc-js/.gitignore +++ b/lib/citeproc-js/.gitignore @@ -1,4 +1,7 @@ *~ +.cslTestFixtures +ignored node_modules test .cslValidationPos +cslrun.yaml \ No newline at end of file diff --git a/lib/citeproc-js/.travis.yml b/lib/citeproc-js/.travis.yml deleted file mode 100644 index 69b937b..0000000 --- a/lib/citeproc-js/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: node_js -node_js: - - "7" -before_script: - - npm install -g mocha - - npm install -g citeproc-test-runner -script: cslrun -a diff --git a/lib/citeproc-js/CHANGES.txt b/lib/citeproc-js/CHANGES.txt index 8635b86..55a4792 100644 --- a/lib/citeproc-js/CHANGES.txt +++ b/lib/citeproc-js/CHANGES.txt @@ -1,3 +1,166 @@ +========================= +Changes in version 1.4.61 +========================= + +- Apply text-case to numeric variables with numeric content. + +========================= +Changes in version 1.4.60 +========================= + +- Limit en-dash equivalence to hyphen at 1.4.50 to the page + variable. + +========================= +Changes in version 1.4.59 +========================= + +- Fix bug in names abbreviation fallback from institution-entire + to institution-part. + +========================= +Changes in version 1.4.58 +========================= + +- Smarten up is-multiple + + https://github.com/Juris-M/citeproc-js/issues/174 + +========================= +Changes in version 1.4.57 +========================= + +- Preserve bare hyphens in docket numbers. + +========================= +Changes in version 1.4.56 +========================= + +- Change is-multiple test attribute to test for presence of + comma-space, space-and-space or ampersand. + +========================= +Changes in version 1.4.55 +========================= + +- Implement cite-form flag, supplied via abbreviation markup as + discussed here: + + https://github.com/Juris-M/legal-resource-registry/issues/34#issuecomment-749249240 + +========================= +Changes in version 1.4.54 +========================= + +- Support in-text markup in name elements. + + https://github.com/Juris-M/citeproc-js/issues/173 + +========================= +Changes in version 1.4.53 +========================= + +- Remove sort mask from macros calling an empty date, and set date + keys as pure numeric strings + + https://github.com/citation-style-language/styles/pull/5169 + +========================= +Changes in version 1.4.52 +========================= + +- Add is-multiple test. Returns true if the target variable contains one or more + spaces. + +========================= +Changes in version 1.4.51 +========================= + +- Fix crashing bug triggered by parallels initialization failure (affects CSL-M styles only). + +========================= +Changes in version 1.4.50 +========================= + +- Fix page-range-format to work equally with hyphen and en-dash. + +========================= +Changes in version 1.4.49 +========================= + +- Fix in-text backref behavior (previously completely non-functional). + +========================= +Changes in version 1.4.48 +========================= + +- Fix disambig with demote-non-dropping-particle=never + + https://github.com/Juris-M/citeproc-js/issues/171 + +========================= +Changes in version 1.4.47 +========================= + +- Changes to institution name attributes + - Apply use-first and use-last to elements as entered, not as reversed + - Add stop-first to mirror stop-last + - Render as entered by default, require explicit reverse-order to reverse + - Make code less unreadable + +- Remove implicit institution name truncation + +- Recognize strings and terms in cs:substitute + +========================= +Changes in version 1.4.46 +========================= + +- Enable trailing-punctuation suppression in in-text styles. + +========================= +Changes in version 1.4.45 +========================= + +- Fix bug in parallel-referencing code. + +- Update schema (fix missing or misplaced attributes and elements), + fix syntax issues in style modules, update style tests. + +========================= +Changes in version 1.4.44 +========================= + +- Fix a bug in fallback from institution-entire to institution-part + +========================= +Changes in version 1.4.43 +========================= + +- Implement fallback attribute on cs:law-module + https://github.com/Juris-M/zotero/issues/95 + +========================= +Changes in version 1.4.42 +========================= + +- Fall back from institution-entire abbrev to institution-part abbrev + +- Adjust output of NL legal_case in module + +========================= +Changes in version 1.4.41 +========================= + +- Make availableAbbrevDomains persistent, extend abbrev key + normalization to number and classic + +========================= +Changes in version 1.4.40 +========================= + +- Implement item-level locale arbitration of jurisdiction-preference for abbrevs + ========================= Changes in version 1.4.39 ========================= diff --git a/lib/citeproc-js/attic/from-docs-substitutions.txt b/lib/citeproc-js/attic/from-docs-substitutions.txt new file mode 100644 index 0000000..9691543 --- /dev/null +++ b/lib/citeproc-js/attic/from-docs-substitutions.txt @@ -0,0 +1,8 @@ +.. |CCBYSA| raw:: html + +
+ +.. |CCBYSA-SUBDIR| raw:: html + +
+ diff --git a/lib/citeproc-js/attic/name_MarkupBug.txt b/lib/citeproc-js/attic/name_MarkupBug.txt new file mode 100644 index 0000000..04c1b8f --- /dev/null +++ b/lib/citeproc-js/attic/name_MarkupBug.txt @@ -0,0 +1,58 @@ +>>===== MODE =====>> +bibliography +<<===== MODE =====<< + + + +>>===== RESULT =====>> +
+
Doe, J. Q.
+
+<<===== RESULT =====<< + + +>>===== CSL =====>> + + + +<<===== CSL =====<< + + +>>===== INPUT =====>> +[ + { + "author": [ + { + "family": "Doe", + "given": "John Quiggly" + } + ], + "id": "ITEM-1", + "type": "book" + } +] +<<===== INPUT =====<< diff --git a/lib/citeproc-js/citeproc.js b/lib/citeproc-js/citeproc.js index d10bb55..34020a9 100644 --- a/lib/citeproc-js/citeproc.js +++ b/lib/citeproc-js/citeproc.js @@ -59,7 +59,7 @@ Copyright (c) 2009-2019 Frank Bennett var CSL = { - PROCESSOR_VERSION: "1.4.39", + PROCESSOR_VERSION: "1.4.61", error: function(str) { // default error function if ("undefined" === typeof Error) { @@ -342,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", @@ -1458,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 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 = {}; @@ -7534,11 +7530,10 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, } if (suprame) { item[1].position = CSL.POSITION_CONTAINER_SUBSEQUENT; - if (first_ref[first_id]) { - item[1].position = CSL.POSITION_SUBSEQUENT; - } - if (!first_ref[first_id]) { + if ("undefined" === typeof first_ref[first_id]) { first_ref[first_id] = onecitation.properties.noteIndex; + } else { + item[1].position = CSL.POSITION_SUBSEQUENT; } } if (suprame || ibidme) { @@ -7573,6 +7568,8 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, item[1]["near-note"] = true; } 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) { @@ -8014,7 +8011,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) { @@ -9392,14 +9389,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+/); + } + } } } } @@ -10279,7 +10295,7 @@ CSL.Node["date-part"] = { 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); + number = new CSL.NumericBlob(state, false, num, this, Item.id); this.successor_prefix = state[state.build.area].opt.layout_delimiter; this.splice_prefix = state[state.build.area].opt.layout_delimiter; formatter = new CSL.Util.Suffixator(CSL.SUFFIX_CHARS); @@ -11196,9 +11212,9 @@ CSL.Node.key = { if ("" === keystring) { keystring = undefined; } - if ("string" !== typeof keystring || state.tmp.empty_date) { + if ("string" !== typeof keystring) { keystring = undefined; - state.tmp.empty_date = false; + //state.tmp.empty_date = false; } state[state[state.tmp.area].root + "_sort"].keys.push(keystring); state.tmp.value = []; @@ -12122,7 +12138,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); @@ -12334,8 +12353,8 @@ CSL.NameOutput.prototype.truncatePersonalNameLists = function () { } } } - // Could be factored out to a separate function for clarity. - if (this.etal_min === 1 && this.etal_use_first === 1 + if (this.state.opt.development_extensions.etal_min_etal_usefirst_hack + && this.etal_min === 1 && this.etal_use_first === 1 && !(this.state.tmp.extension || this.state.tmp.just_looking)) { chopvar = v; @@ -12589,7 +12608,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) { @@ -14043,8 +14062,8 @@ CSL.NameOutput.prototype._nameSuffix = function (name) { var str = name.suffix, ret; - if ("string" === typeof this.state.inheritOpt(this.name, "initialize-with")) { - str = CSL.Util.Names.initializeWith(this.state, name.suffix, this.state.inheritOpt(this.name, "initialize-with"), true); + if (str && "string" === typeof this.state.inheritOpt(this.name, "initialize-with")) { + str = CSL.Util.Names.initializeWith(this.state, str, this.state.inheritOpt(this.name, "initialize-with"), true); } str = this._stripPeriods("family", str); @@ -14279,56 +14298,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", longNameStr, 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 (use_short_form) { - var delimiter = this.institution.strings.delimiter; - var use_first = this.institution.strings["use-first"]; - if (use_first) { - if (!delimiter) { - delimiter = "|"; + 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]; } - var buf = []; - for (var i=0,ilen=short_form.length;i -1) { + if (shortNameStr === longNameStr) { + shortNameStr = ""; + } } } - 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 -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)); + } + } + } + if (this.state.opt.development_extensions.legacy_institution_name_ordering) { + ret["short"].reverse(); + } + ret["short"] = this._trimInstitution(ret["short"]); + if (this.institution.strings["reverse-order"]) { + ret["short"].reverse(); + } + // trimmer is not available in getAmbiguousCite + if (!this.state.tmp.just_looking) { + if (this.Item.jurisdiction) { + let jurisdiction = this.Item.jurisdiction; + var trimmer = this.state.tmp.abbrev_trimmer; + if (trimmer && trimmer[jurisdiction] && trimmer[jurisdiction][varname]) { + for (var i=0,ilen=ret["short"].length;i 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(); - } +CSL.NameOutput.prototype._quashChecks = function (jurisdiction, str) { + var str = this.state.transform.quashCheck(jurisdiction, str); + // If the abbreviation has date cut-offs, find the most recent + // abbreviation within scope. + var lst = str.split(/>>[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; + } + 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"]; - } - 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); - } + // this.state.sys.print("use-last OK"); + use_last = this.institution.strings["use-last"]; } - } - 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-first"]) { + // this.state.sys.print("stop-first OK"); + stop_first = this.institution.strings["stop-first"]; } - if (!use_first) { - use_first = 0; - } - } - if (false === append_last) { - if (!use_first) { - append_last = subunits.length; - } else { - append_last = 0; + if ("undefined" !== typeof this.institution.strings["stop-last"]) { + stop_last = this.institution.strings["stop-last"]; } - } - // 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; }; @@ -15372,7 +15390,7 @@ CSL.Node.text = { if (state.opt.citation_number_slug) { state.output.append(state.opt.citation_number_slug, this); } else { - number = new CSL.NumericBlob(false, num, this, Item.id); + number = new CSL.NumericBlob(state, false, num, this, Item.id); if (state.tmp.in_cite_predecessor) { number.suppress_splice_prefix = true; } @@ -15406,7 +15424,7 @@ CSL.Node.text = { if (state[state.tmp.area].opt.cite_group_delimiter) { this.successor_prefix = state[state.tmp.area].opt.cite_group_delimiter; } - number = new CSL.NumericBlob(false, num, this, Item.id); + number = new CSL.NumericBlob(state, false, num, this, Item.id); formatter = new CSL.Util.Suffixator(CSL.SUFFIX_CHARS); number.setFormatter(formatter); state.output.append(number, "literal"); @@ -15489,6 +15507,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; @@ -15696,6 +15719,11 @@ CSL.Node.text = { // true flags that this is a literal-value term 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 @@ -16370,6 +16398,22 @@ CSL.Attributes["@is-plural"] = function (state, arg) { this.tests.push(func); }; +CSL.Attributes["@is-multiple"] = function (state, arg) { + if (!this.tests) {this.tests = []; }; + var func = function (Item) { + var val = ("" + Item[arg]); + var lst = val.split(/(?:,\s|\s(?:tot\sen\smet|līdz|oraz|and|bis|έως|και|och|až|do|en|et|in|ir|ja|og|sa|to|un|und|és|și|i|u|y|à|e|a|и|-|–)\s|—|\&)/); + if (lst.length > 1) { + return true; + } + return false; + }; + this.tests.push(func); +}; + + + + CSL.Attributes["@locale"] = function (state, arg) { if (!this.tests) {this.tests = []; }; var ret, langspec, lang, lst, i, ilen; @@ -16639,6 +16683,19 @@ CSL.Attributes["@has-subunit"] = function (state, arg) { this.tests.push(maketest(arg)); } +CSL.Attributes["@cite-form"] = function (state, arg) { + if (!this.tests) {this.tests = []; }; + var maketest = function(citeForm) { + return function (Item) { + if (Item["cite-form"] === citeForm) { + return true; + } + return false; + }; + }; + this.tests.push(maketest(arg)); +} + CSL.Attributes["@disable-duplicate-year-suppression"] = function (state, arg) { state.opt.disable_duplicate_year_suppression = arg.split(/\s+/); } @@ -16689,6 +16746,7 @@ CSL.Attributes["@parallel-last"] = function (state, arg) { } }; CSL.Attributes["@parallel-last-to-first"] = function (state, arg) { + state.opt.parallel.enable = true; var vars = arg.split(/\s+/); this.parallel_last_to_first = {}; for (var i=0,ilen=vars.length;i 0 && !parallelMatchList) { freshMatchList = true; parallelMatchList = [sortedItems[i][0].id].concat(sortedItems[i][0].seeAlso); var tempMatchList = parallelMatchList.slice(); @@ -17744,7 +17808,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" @@ -17962,10 +18026,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(); @@ -17983,9 +18052,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; @@ -18017,36 +18089,44 @@ CSL.Transform = function (state) { return false; } - + function citeFormCheck(Item, value) { + var m = value.match(/^#([0-9]+).*>>>/); + if (m && m[1]) { + Item["cite-form"] = m[1]; + } + } + // The name transform code is placed here to keep similar things // in one place. Obviously this module could do with a little // tidying up. function quashCheck(jurisdiction, value) { - var m = value.match(/^!((?:[-_a-z]+(?:(?:.*)))(?:,(?:[-_a-z]+(?:(?:.*))))*)>>>/); + var m = value.match(/^(?:#[0-9]+)*(?:!((?:[-_a-z]+(?:(?:.*)))(?:,(?:[-_a-z]+(?:(?:.*))))*))*>>>/); if (m) { - var fields = m[1].split(","); value = value.slice(m[0].length); - for (var i = 0, ilen = fields.length; i < ilen; i += 1) { - var rawField = fields[i]; - var mm = rawField.match(/^([-_a-z]+)(?:\:(.*))*$/); - var field = mm[1]; - // trimmer is not available in getAmbiguousCite - var trimmer = state.tmp.abbrev_trimmer; - if (mm[2]) { - if (trimmer && jurisdiction) { - if (!trimmer[jurisdiction]) { - trimmer[jurisdiction] = {}; + if (m[1]) { + var fields = m[1].split(","); + for (var i = 0, ilen = fields.length; i < ilen; i += 1) { + var rawField = fields[i]; + var mm = rawField.match(/^([-_a-z]+)(?:\:(.*))*$/); + var field = mm[1]; + // trimmer is not available in getAmbiguousCite + var trimmer = state.tmp.abbrev_trimmer; + if (mm[2]) { + if (trimmer && jurisdiction) { + if (!trimmer[jurisdiction]) { + trimmer[jurisdiction] = {}; + } + trimmer[jurisdiction][field] = mm[2]; } - trimmer[jurisdiction][field] = mm[2]; - } - } else if (state.tmp.done_vars.indexOf(field) === -1) { - if (trimmer && jurisdiction) { - if (!trimmer.QUASHES[jurisdiction]) { - trimmer.QUASHES[jurisdiction] = {}; + } else if (state.tmp.done_vars.indexOf(field) === -1) { + if (trimmer && jurisdiction) { + if (!trimmer.QUASHES[jurisdiction]) { + trimmer.QUASHES[jurisdiction] = {}; + } + trimmer.QUASHES[jurisdiction][field] = true; } - trimmer.QUASHES[jurisdiction][field] = true; + state.tmp.done_vars.push(field); } - state.tmp.done_vars.push(field); } } } @@ -18149,7 +18229,8 @@ CSL.Transform = function (state) { // Suppress subsequent use of another variable if requested by // hack syntax in this abbreviation short form. if (primary) { - // The abbreviate() function could use a cleanup, after Zotero correct to use title-short + // We run quash-check in getAmbiguousCite, to possibly pick up a cite-form value. + citeFormCheck(Item, primary); if (!state.tmp.just_looking) { primary = quashCheck(Item.jurisdiction, primary); } @@ -18544,7 +18625,7 @@ CSL.Blob.prototype.push = function (blob) { * @namespace Range object and friends. */ -CSL.NumericBlob = function (particle, num, mother_token, id) { +CSL.NumericBlob = function (state, particle, num, mother_token, id) { // item id is used to assure that prefix delimiter is invoked only // when joining blobs across items this.id = id; @@ -18555,6 +18636,11 @@ CSL.NumericBlob = function (particle, num, mother_token, id) { this.status = CSL.START; this.strings = {}; if (mother_token) { + if (mother_token.strings["text-case"]) { + var textCase = mother_token.strings["text-case"]; + this.particle = CSL.Output.Formatters[textCase](state, this.particle); + this.blobs = CSL.Output.Formatters[textCase](state, this.blobs); + } this.gender = mother_token.gender; this.decorations = mother_token.decorations; this.strings.prefix = mother_token.strings.prefix; @@ -18805,9 +18891,6 @@ CSL.dateAsSortKey = function (state, Item, isMacro) { dp = Item[variable]; if ("undefined" === typeof dp) { dp = {"date-parts": [[0]] }; - if (!dp.year) { - state.tmp.empty_date = true; - } } if ("undefined" === typeof this.dateparts) { this.dateparts = ["year", "month", "day"]; @@ -18820,32 +18903,34 @@ CSL.dateAsSortKey = function (state, Item, isMacro) { if ("undefined" === typeof dp) { dp = {}; } - for (i = 0, ilen = CSL.DATE_PARTS_INTERNAL.length; i < ilen; i += 1) { - elem = CSL.DATE_PARTS_INTERNAL[i]; - value = 0; - e = elem; - if (e.slice(-4) === "_end") { - e = e.slice(0, -4); - } - if (dp[elem] && this.dateparts.indexOf(e) > -1) { - value = dp[elem]; - } - if (elem.slice(0, 4) === "year") { - yr = CSL.Util.Dates[e].numeric(state, value); - var prefix = "Y"; - if (yr[0] === "-") { - prefix = "X"; - yr = yr.slice(1); - yr = 9999 - parseInt(yr, 10); - } - state.output.append(CSL.Util.Dates[elem.slice(0, 4)].numeric(state, (prefix + yr)), macroFlag); - } else { - value = CSL.Util.Dates[e]["numeric-leading-zeros"](state, value); - // Ugh. - if (!value) { - value = "00"; + if (dp.year) { + for (i = 0, ilen = CSL.DATE_PARTS_INTERNAL.length; i < ilen; i += 1) { + elem = CSL.DATE_PARTS_INTERNAL[i]; + value = 0; + e = elem; + if (e.slice(-4) === "_end") { + e = e.slice(0, -4); + } + if (dp[elem] && this.dateparts.indexOf(e) > -1) { + value = dp[elem]; + } + if (elem.slice(0, 4) === "year") { + yr = CSL.Util.Dates[e].numeric(state, value); + var prefix = "1"; + if (yr[0] === "-") { + prefix = "0"; + yr = yr.slice(1); + yr = 9999 - parseInt(yr, 10); + } + state.output.append(CSL.Util.Dates[elem.slice(0, 4)].numeric(state, (prefix + yr)), macroFlag); + } else { + value = CSL.Util.Dates[e]["numeric-leading-zeros"](state, value); + // Ugh. + if (!value) { + value = "00"; + } + state.output.append(value, macroFlag); } - state.output.append(value, macroFlag); } } }; @@ -18936,16 +19021,15 @@ CSL.Util.Names.initializeWith = function (state, name, terminator, normalizeOnly terminator = ""; } if (["Lord", "Lady"].indexOf(name) > -1 - || (!name.match(CSL.STARTSWITH_ROMANESQUE_REGEXP) + || (!name.replace(/^(?:<[^>]+>)*/, "").match(CSL.STARTSWITH_ROMANESQUE_REGEXP) && !terminator.match("%s"))) { return name; } - var namelist = name; + if (state.opt["initialize-with-hyphen"] === false) { - namelist = namelist.replace(/\-/g, " "); + name = name.replace(/\-/g, " "); } - // Oh boy. // We need to suss out what is a set of initials or abbreviation, // so that they can be selectively normalized. Steps might be: // (1) Split the string @@ -18955,50 +19039,72 @@ CSL.Util.Names.initializeWith = function (state, name, terminator, normalizeOnly // (a) Do the thing below, but only pushing terminator; or else // (b) Do the thing below - // (1) Split the string - namelist = namelist.replace(/\s*\-\s*/g, "-").replace(/\s+/g, " "); - namelist = namelist.replace(/-([a-z])/g, "\u2013$1"); + name = name.replace(/\s*\-\s*/g, "-").replace(/\s+/g, " "); + name = name.replace(/-([a-z])/g, "\u2013$1"); - for (var i=namelist.length-2; i>-1; i += -1) { - if (namelist.slice(i, i+1) === "." && namelist.slice(i+1, i+2) !== " ") { - namelist = namelist.slice(0, i) + ". " + namelist.slice(i+1); + for (var i=name.length-2; i>-1; i += -1) { + if (name.slice(i, i+1) === "." && name.slice(i+1, i+2) !== " ") { + name = name.slice(0, i) + ". " + name.slice(i+1); } } - // Workaround for Internet Explorer - //namelist = namelist.split(/(\-|\s+)/); - // Workaround for Internet Explorer - mm = namelist.match(/[\-\s]+/g); - lst = namelist.split(/[\-\s]+/); - if (mm === null) { - var mmm = lst[0].match(/[^\.]+$/); + // (1) Split the string + var nameSplits = CSL.Output.Formatters.nameDoppel.split(name); + var namelist = []; + namelist = [nameSplits.strings[0]]; + + if (nameSplits.tags.length === 0) { + var mmm = namelist[0].match(/[^\.]+$/); if (mmm && mmm[0].length === 1 && mmm[0] !== mmm[0].toLowerCase()) { - lst[0] += "."; + namelist[0] += "."; } } - if (lst.length === 0) { - // This doesn't make much sense, and may be impossible. - namelist = mm; - } else { - namelist = [lst[0]]; - for (i = 1, ilen = lst.length; i < ilen; i += 1) { - namelist.push(mm[i - 1]); - namelist.push(lst[i]); - } + for (i = 1, ilen = nameSplits.strings.length; i < ilen; i += 1) { + namelist.push(nameSplits.tags[i - 1]); + namelist.push(nameSplits.strings[i]); } - lst = namelist; // Use doInitializeName or doNormalizeName, depending on requirements. if (normalizeOnly) { - ret = CSL.Util.Names.doNormalize(state, lst, terminator); + ret = this.doNormalize(state, namelist, terminator); } else { - ret = CSL.Util.Names.doInitialize(state, lst, terminator); + ret = this.doInitialize(state, namelist, terminator); } ret = ret.replace(/\u2013([a-z])/g, "-$1"); return ret; }; +CSL.Util.Names.notag = function(str) { + return str.replace(/^(?:<[^>]+>)*/, ""); +}; + +CSL.Util.Names.mergetag = function(state, tagstr, newstr) { + var m = tagstr.match(/(?:-*<[^>]+>-*)/g); + if (!m) { + return newstr; + } else { + tagstr = m.join(""); + } + m = newstr.match(/^(.*[^\s])*(\s+)$/); + if (m) { + m[1] = m[1] ? m[1] : ""; + newstr = m[1] + tagstr + m[2]; + } else { + newstr = newstr + tagstr; + } + return newstr; +}; + +CSL.Util.Names.tagonly = function(state, str) { + var m = str.match(/(?:<[^>]+>)+/); + if (!m) { + return str; + } else { + return m.join(""); + } +}; + CSL.Util.Names.doNormalize = function (state, namelist, terminator) { var i, ilen; // namelist is a flat list of given-name elements and space-like separators between them @@ -19006,8 +19112,9 @@ CSL.Util.Names.doNormalize = function (state, namelist, terminator) { // Flag elements that look like abbreviations var isAbbrev = []; for (i = 0, ilen = namelist.length; i < ilen; i += 1) { - if (namelist[i].length > 1 && namelist[i].slice(-1) === ".") { - namelist[i] = namelist[i].slice(0, -1); + if (this.notag(namelist[i]).length > 1 && this.notag(namelist[i]).slice(-1) === ".") { + // namelist[i] = namelist[i].slice(0, -1); + namelist[i] = namelist[i].replace(/^(.*)\.(.*)$/, "$1$2"); isAbbrev.push(true); } else if (namelist[i].length === 1 && namelist[i].toUpperCase() === namelist[i]) { isAbbrev.push(true); @@ -19022,19 +19129,18 @@ CSL.Util.Names.doNormalize = function (state, namelist, terminator) { // For all elements but the last if (i < namelist.length - 2) { // Start from scratch on space-like things following an abbreviation - namelist[i + 1] = ""; - + namelist[i + 1] = this.tagonly(state, namelist[i+1]); if (!isAbbrev[i+2]) { - namelist[i + 1] = " "; + namelist[i + 1] = this.tagonly(state, namelist[i+1]) + " "; } // Add the terminator to the element // If the following element is not a single-character abbreviation, remove a trailing zero-width non-break space, if present // These ops may leave some duplicate cruft in the elements and separators. This will be cleaned at the end of the function. if (namelist[i + 2].length > 1) { - namelist[i] = namelist[i] + terminator.replace(/\ufeff$/, ""); + namelist[i+1] = terminator.replace(/\ufeff$/, "") + namelist[i+1]; } else { - namelist[i] = namelist[i] + terminator; + namelist[i+1] = this.mergetag(state, namelist[i+1], terminator); } } // For the last element (if it is an abbreviation), just append the terminator @@ -19085,9 +19191,9 @@ CSL.Util.Names.doInitialize = function (state, namelist, terminator) { namelist[i] = terminator.replace("%s", namelist[i]); } else { if (namelist[i + 1].indexOf("-") > -1) { - namelist[i + 1] = terminator + namelist[i + 1]; + namelist[i + 1] = this.mergetag(state, namelist[i+1].replace("-", ""), terminator) + "-"; } else { - namelist[i + 1] = terminator; + namelist[i + 1] = this.mergetag(state, namelist[i+1], terminator); } } } else { @@ -19121,14 +19227,6 @@ CSL.Util.Names.getRawName = function (name) { return ret.join(" "); }; -// deleted CSL.Util.Names.initNameSlices() -// no longer used. - -// deleted CSL.Util.Names,rescueNameElements() -// apparently not used. - - - /*global CSL: true */ /** @@ -19198,6 +19296,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) { @@ -19206,7 +19306,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]; @@ -19986,7 +20088,7 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) { if (localeAnd === localeAmpersand) { localeAmpersand = "&"; } - + // XXXX shadow_numbers should carry an array of objects with // XXXX full data for each. The test of a number should be // XXXX a separate function, possibly supported by a splitter @@ -20093,6 +20195,11 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) { str = normalizeFieldValue(str, defaultLabel); var jmrex, jsrex, mystr; + if ("page" === variable) { + 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("\\-", "")); @@ -20575,6 +20682,10 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) { val = ItemObject[realVariable]; } + if (val && realVariable === "number" && ItemObject.type === "legal_case") { + val = val.replace(/[\\]*-/g, "\\-"); + } + // XXX HOLDING THIS // Apply short form ONLY if first element tests is-numeric=false if (val && this.sys.getAbbreviation) { @@ -20583,16 +20694,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]; } } } @@ -20762,9 +20880,9 @@ CSL.Util.outputNumericField = function(state, varname, itemID) { if (num.collapsible) { var blob; if (num.value.match(/^[1-9][0-9]*$/)) { - blob = new CSL.NumericBlob(num.particle, parseInt(num.value, 10), numStyling, itemID); + blob = new CSL.NumericBlob(state, num.particle, parseInt(num.value, 10), numStyling, itemID); } else { - blob = new CSL.NumericBlob(num.particle, num.value, numStyling, itemID); + blob = new CSL.NumericBlob(state, num.particle, num.value, numStyling, itemID); } if ("undefined" === typeof blob.gender) { blob.gender = state.locale[state.opt.lang]["noun-genders"][varname]; @@ -21547,6 +21665,8 @@ CSL.Output.Formatters = (function () { var tagDoppel = new CSL.Doppeler(rexStr, function(str) { return str.replace(/(]*(>)/g, "$1 $2$3").replace(/(]*(>)/g, "$1 $2 $3;$4$5"); }); + var rexNameStr = "(?:[-\\s]*<\\/*(?:span\s+class=\"no(?:case|decor)\"|i|sc|b|sub|sup)>[-\\s]*|[-\\s]+)"; + var nameDoppel = new CSL.Doppeler(rexNameStr); var wordDoppel = new CSL.Doppeler("(?:[\u0020\u00A0\u2000-\u200B\u205F\u3000]+)"); @@ -21917,6 +22037,7 @@ CSL.Output.Formatters = (function () { return _textcaseEngine.call(state, config, string); } return { + nameDoppel: nameDoppel, passthrough: passthrough, lowercase: lowercase, uppercase: uppercase, @@ -22798,8 +22919,9 @@ CSL.Registry.prototype.dopurge = function (myhash) { for (var i=this.mylist.length-1;i>-1;i+=-1) { // Might not want to be quite this restrictive. if (this.citationreg.citationsByItemId) { - if (!this.citationreg.citationsByItemId[this.mylist[i]] && !myhash[this.mylist[i]]) { + if ((!this.citationreg.citationsByItemId || !this.citationreg.citationsByItemId[this.mylist[i]]) && !myhash[this.mylist[i]]) { delete this.myhash[this.mylist[i]]; + delete this.uncited[this.mylist[i]]; this.mylist = this.mylist.slice(0,i).concat(this.mylist.slice(i+1)); } } @@ -23624,6 +23746,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. @@ -24319,7 +24447,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"); @@ -24327,6 +24460,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 -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", @@ -1458,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 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 = {}; @@ -7534,11 +7530,10 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, } if (suprame) { item[1].position = CSL.POSITION_CONTAINER_SUBSEQUENT; - if (first_ref[first_id]) { - item[1].position = CSL.POSITION_SUBSEQUENT; - } - if (!first_ref[first_id]) { + if ("undefined" === typeof first_ref[first_id]) { first_ref[first_id] = onecitation.properties.noteIndex; + } else { + item[1].position = CSL.POSITION_SUBSEQUENT; } } if (suprame || ibidme) { @@ -7573,6 +7568,8 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, item[1]["near-note"] = true; } 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) { @@ -8014,7 +8011,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) { @@ -9392,14 +9389,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+/); + } + } } } } @@ -10279,7 +10295,7 @@ CSL.Node["date-part"] = { 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); + number = new CSL.NumericBlob(state, false, num, this, Item.id); this.successor_prefix = state[state.build.area].opt.layout_delimiter; this.splice_prefix = state[state.build.area].opt.layout_delimiter; formatter = new CSL.Util.Suffixator(CSL.SUFFIX_CHARS); @@ -11196,9 +11212,9 @@ CSL.Node.key = { if ("" === keystring) { keystring = undefined; } - if ("string" !== typeof keystring || state.tmp.empty_date) { + if ("string" !== typeof keystring) { keystring = undefined; - state.tmp.empty_date = false; + //state.tmp.empty_date = false; } state[state[state.tmp.area].root + "_sort"].keys.push(keystring); state.tmp.value = []; @@ -12122,7 +12138,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); @@ -12334,8 +12353,8 @@ CSL.NameOutput.prototype.truncatePersonalNameLists = function () { } } } - // Could be factored out to a separate function for clarity. - if (this.etal_min === 1 && this.etal_use_first === 1 + if (this.state.opt.development_extensions.etal_min_etal_usefirst_hack + && this.etal_min === 1 && this.etal_use_first === 1 && !(this.state.tmp.extension || this.state.tmp.just_looking)) { chopvar = v; @@ -12589,7 +12608,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) { @@ -14043,8 +14062,8 @@ CSL.NameOutput.prototype._nameSuffix = function (name) { var str = name.suffix, ret; - if ("string" === typeof this.state.inheritOpt(this.name, "initialize-with")) { - str = CSL.Util.Names.initializeWith(this.state, name.suffix, this.state.inheritOpt(this.name, "initialize-with"), true); + if (str && "string" === typeof this.state.inheritOpt(this.name, "initialize-with")) { + str = CSL.Util.Names.initializeWith(this.state, str, this.state.inheritOpt(this.name, "initialize-with"), true); } str = this._stripPeriods("family", str); @@ -14279,56 +14298,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", longNameStr, 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 (use_short_form) { - var delimiter = this.institution.strings.delimiter; - var use_first = this.institution.strings["use-first"]; - if (use_first) { - if (!delimiter) { - delimiter = "|"; + 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]; } - var buf = []; - for (var i=0,ilen=short_form.length;i -1) { + if (shortNameStr === longNameStr) { + shortNameStr = ""; + } } } - 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 -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)); + } + } + } + if (this.state.opt.development_extensions.legacy_institution_name_ordering) { + ret["short"].reverse(); + } + ret["short"] = this._trimInstitution(ret["short"]); + if (this.institution.strings["reverse-order"]) { + ret["short"].reverse(); + } + // trimmer is not available in getAmbiguousCite + if (!this.state.tmp.just_looking) { + if (this.Item.jurisdiction) { + let jurisdiction = this.Item.jurisdiction; + var trimmer = this.state.tmp.abbrev_trimmer; + if (trimmer && trimmer[jurisdiction] && trimmer[jurisdiction][varname]) { + for (var i=0,ilen=ret["short"].length;i 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(); - } +CSL.NameOutput.prototype._quashChecks = function (jurisdiction, str) { + var str = this.state.transform.quashCheck(jurisdiction, str); + // If the abbreviation has date cut-offs, find the most recent + // abbreviation within scope. + var lst = str.split(/>>[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; + } + 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"]; - } - 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); - } + // this.state.sys.print("use-last OK"); + use_last = this.institution.strings["use-last"]; } - } - 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-first"]) { + // this.state.sys.print("stop-first OK"); + stop_first = this.institution.strings["stop-first"]; } - if (!use_first) { - use_first = 0; - } - } - if (false === append_last) { - if (!use_first) { - append_last = subunits.length; - } else { - append_last = 0; + if ("undefined" !== typeof this.institution.strings["stop-last"]) { + stop_last = this.institution.strings["stop-last"]; } - } - // 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; }; @@ -15372,7 +15390,7 @@ CSL.Node.text = { if (state.opt.citation_number_slug) { state.output.append(state.opt.citation_number_slug, this); } else { - number = new CSL.NumericBlob(false, num, this, Item.id); + number = new CSL.NumericBlob(state, false, num, this, Item.id); if (state.tmp.in_cite_predecessor) { number.suppress_splice_prefix = true; } @@ -15406,7 +15424,7 @@ CSL.Node.text = { if (state[state.tmp.area].opt.cite_group_delimiter) { this.successor_prefix = state[state.tmp.area].opt.cite_group_delimiter; } - number = new CSL.NumericBlob(false, num, this, Item.id); + number = new CSL.NumericBlob(state, false, num, this, Item.id); formatter = new CSL.Util.Suffixator(CSL.SUFFIX_CHARS); number.setFormatter(formatter); state.output.append(number, "literal"); @@ -15489,6 +15507,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; @@ -15696,6 +15719,11 @@ CSL.Node.text = { // true flags that this is a literal-value term 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 @@ -16370,6 +16398,22 @@ CSL.Attributes["@is-plural"] = function (state, arg) { this.tests.push(func); }; +CSL.Attributes["@is-multiple"] = function (state, arg) { + if (!this.tests) {this.tests = []; }; + var func = function (Item) { + var val = ("" + Item[arg]); + var lst = val.split(/(?:,\s|\s(?:tot\sen\smet|līdz|oraz|and|bis|έως|και|och|až|do|en|et|in|ir|ja|og|sa|to|un|und|és|și|i|u|y|à|e|a|и|-|–)\s|—|\&)/); + if (lst.length > 1) { + return true; + } + return false; + }; + this.tests.push(func); +}; + + + + CSL.Attributes["@locale"] = function (state, arg) { if (!this.tests) {this.tests = []; }; var ret, langspec, lang, lst, i, ilen; @@ -16639,6 +16683,19 @@ CSL.Attributes["@has-subunit"] = function (state, arg) { this.tests.push(maketest(arg)); } +CSL.Attributes["@cite-form"] = function (state, arg) { + if (!this.tests) {this.tests = []; }; + var maketest = function(citeForm) { + return function (Item) { + if (Item["cite-form"] === citeForm) { + return true; + } + return false; + }; + }; + this.tests.push(maketest(arg)); +} + CSL.Attributes["@disable-duplicate-year-suppression"] = function (state, arg) { state.opt.disable_duplicate_year_suppression = arg.split(/\s+/); } @@ -16689,6 +16746,7 @@ CSL.Attributes["@parallel-last"] = function (state, arg) { } }; CSL.Attributes["@parallel-last-to-first"] = function (state, arg) { + state.opt.parallel.enable = true; var vars = arg.split(/\s+/); this.parallel_last_to_first = {}; for (var i=0,ilen=vars.length;i 0 && !parallelMatchList) { freshMatchList = true; parallelMatchList = [sortedItems[i][0].id].concat(sortedItems[i][0].seeAlso); var tempMatchList = parallelMatchList.slice(); @@ -17744,7 +17808,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" @@ -17962,10 +18026,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(); @@ -17983,9 +18052,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; @@ -18017,36 +18089,44 @@ CSL.Transform = function (state) { return false; } - + function citeFormCheck(Item, value) { + var m = value.match(/^#([0-9]+).*>>>/); + if (m && m[1]) { + Item["cite-form"] = m[1]; + } + } + // The name transform code is placed here to keep similar things // in one place. Obviously this module could do with a little // tidying up. function quashCheck(jurisdiction, value) { - var m = value.match(/^!((?:[-_a-z]+(?:(?:.*)))(?:,(?:[-_a-z]+(?:(?:.*))))*)>>>/); + var m = value.match(/^(?:#[0-9]+)*(?:!((?:[-_a-z]+(?:(?:.*)))(?:,(?:[-_a-z]+(?:(?:.*))))*))*>>>/); if (m) { - var fields = m[1].split(","); value = value.slice(m[0].length); - for (var i = 0, ilen = fields.length; i < ilen; i += 1) { - var rawField = fields[i]; - var mm = rawField.match(/^([-_a-z]+)(?:\:(.*))*$/); - var field = mm[1]; - // trimmer is not available in getAmbiguousCite - var trimmer = state.tmp.abbrev_trimmer; - if (mm[2]) { - if (trimmer && jurisdiction) { - if (!trimmer[jurisdiction]) { - trimmer[jurisdiction] = {}; + if (m[1]) { + var fields = m[1].split(","); + for (var i = 0, ilen = fields.length; i < ilen; i += 1) { + var rawField = fields[i]; + var mm = rawField.match(/^([-_a-z]+)(?:\:(.*))*$/); + var field = mm[1]; + // trimmer is not available in getAmbiguousCite + var trimmer = state.tmp.abbrev_trimmer; + if (mm[2]) { + if (trimmer && jurisdiction) { + if (!trimmer[jurisdiction]) { + trimmer[jurisdiction] = {}; + } + trimmer[jurisdiction][field] = mm[2]; } - trimmer[jurisdiction][field] = mm[2]; - } - } else if (state.tmp.done_vars.indexOf(field) === -1) { - if (trimmer && jurisdiction) { - if (!trimmer.QUASHES[jurisdiction]) { - trimmer.QUASHES[jurisdiction] = {}; + } else if (state.tmp.done_vars.indexOf(field) === -1) { + if (trimmer && jurisdiction) { + if (!trimmer.QUASHES[jurisdiction]) { + trimmer.QUASHES[jurisdiction] = {}; + } + trimmer.QUASHES[jurisdiction][field] = true; } - trimmer.QUASHES[jurisdiction][field] = true; + state.tmp.done_vars.push(field); } - state.tmp.done_vars.push(field); } } } @@ -18149,7 +18229,8 @@ CSL.Transform = function (state) { // Suppress subsequent use of another variable if requested by // hack syntax in this abbreviation short form. if (primary) { - // The abbreviate() function could use a cleanup, after Zotero correct to use title-short + // We run quash-check in getAmbiguousCite, to possibly pick up a cite-form value. + citeFormCheck(Item, primary); if (!state.tmp.just_looking) { primary = quashCheck(Item.jurisdiction, primary); } @@ -18544,7 +18625,7 @@ CSL.Blob.prototype.push = function (blob) { * @namespace Range object and friends. */ -CSL.NumericBlob = function (particle, num, mother_token, id) { +CSL.NumericBlob = function (state, particle, num, mother_token, id) { // item id is used to assure that prefix delimiter is invoked only // when joining blobs across items this.id = id; @@ -18555,6 +18636,11 @@ CSL.NumericBlob = function (particle, num, mother_token, id) { this.status = CSL.START; this.strings = {}; if (mother_token) { + if (mother_token.strings["text-case"]) { + var textCase = mother_token.strings["text-case"]; + this.particle = CSL.Output.Formatters[textCase](state, this.particle); + this.blobs = CSL.Output.Formatters[textCase](state, this.blobs); + } this.gender = mother_token.gender; this.decorations = mother_token.decorations; this.strings.prefix = mother_token.strings.prefix; @@ -18805,9 +18891,6 @@ CSL.dateAsSortKey = function (state, Item, isMacro) { dp = Item[variable]; if ("undefined" === typeof dp) { dp = {"date-parts": [[0]] }; - if (!dp.year) { - state.tmp.empty_date = true; - } } if ("undefined" === typeof this.dateparts) { this.dateparts = ["year", "month", "day"]; @@ -18820,32 +18903,34 @@ CSL.dateAsSortKey = function (state, Item, isMacro) { if ("undefined" === typeof dp) { dp = {}; } - for (i = 0, ilen = CSL.DATE_PARTS_INTERNAL.length; i < ilen; i += 1) { - elem = CSL.DATE_PARTS_INTERNAL[i]; - value = 0; - e = elem; - if (e.slice(-4) === "_end") { - e = e.slice(0, -4); - } - if (dp[elem] && this.dateparts.indexOf(e) > -1) { - value = dp[elem]; - } - if (elem.slice(0, 4) === "year") { - yr = CSL.Util.Dates[e].numeric(state, value); - var prefix = "Y"; - if (yr[0] === "-") { - prefix = "X"; - yr = yr.slice(1); - yr = 9999 - parseInt(yr, 10); - } - state.output.append(CSL.Util.Dates[elem.slice(0, 4)].numeric(state, (prefix + yr)), macroFlag); - } else { - value = CSL.Util.Dates[e]["numeric-leading-zeros"](state, value); - // Ugh. - if (!value) { - value = "00"; + if (dp.year) { + for (i = 0, ilen = CSL.DATE_PARTS_INTERNAL.length; i < ilen; i += 1) { + elem = CSL.DATE_PARTS_INTERNAL[i]; + value = 0; + e = elem; + if (e.slice(-4) === "_end") { + e = e.slice(0, -4); + } + if (dp[elem] && this.dateparts.indexOf(e) > -1) { + value = dp[elem]; + } + if (elem.slice(0, 4) === "year") { + yr = CSL.Util.Dates[e].numeric(state, value); + var prefix = "1"; + if (yr[0] === "-") { + prefix = "0"; + yr = yr.slice(1); + yr = 9999 - parseInt(yr, 10); + } + state.output.append(CSL.Util.Dates[elem.slice(0, 4)].numeric(state, (prefix + yr)), macroFlag); + } else { + value = CSL.Util.Dates[e]["numeric-leading-zeros"](state, value); + // Ugh. + if (!value) { + value = "00"; + } + state.output.append(value, macroFlag); } - state.output.append(value, macroFlag); } } }; @@ -18936,16 +19021,15 @@ CSL.Util.Names.initializeWith = function (state, name, terminator, normalizeOnly terminator = ""; } if (["Lord", "Lady"].indexOf(name) > -1 - || (!name.match(CSL.STARTSWITH_ROMANESQUE_REGEXP) + || (!name.replace(/^(?:<[^>]+>)*/, "").match(CSL.STARTSWITH_ROMANESQUE_REGEXP) && !terminator.match("%s"))) { return name; } - var namelist = name; + if (state.opt["initialize-with-hyphen"] === false) { - namelist = namelist.replace(/\-/g, " "); + name = name.replace(/\-/g, " "); } - // Oh boy. // We need to suss out what is a set of initials or abbreviation, // so that they can be selectively normalized. Steps might be: // (1) Split the string @@ -18955,50 +19039,72 @@ CSL.Util.Names.initializeWith = function (state, name, terminator, normalizeOnly // (a) Do the thing below, but only pushing terminator; or else // (b) Do the thing below - // (1) Split the string - namelist = namelist.replace(/\s*\-\s*/g, "-").replace(/\s+/g, " "); - namelist = namelist.replace(/-([a-z])/g, "\u2013$1"); + name = name.replace(/\s*\-\s*/g, "-").replace(/\s+/g, " "); + name = name.replace(/-([a-z])/g, "\u2013$1"); - for (var i=namelist.length-2; i>-1; i += -1) { - if (namelist.slice(i, i+1) === "." && namelist.slice(i+1, i+2) !== " ") { - namelist = namelist.slice(0, i) + ". " + namelist.slice(i+1); + for (var i=name.length-2; i>-1; i += -1) { + if (name.slice(i, i+1) === "." && name.slice(i+1, i+2) !== " ") { + name = name.slice(0, i) + ". " + name.slice(i+1); } } - // Workaround for Internet Explorer - //namelist = namelist.split(/(\-|\s+)/); - // Workaround for Internet Explorer - mm = namelist.match(/[\-\s]+/g); - lst = namelist.split(/[\-\s]+/); - if (mm === null) { - var mmm = lst[0].match(/[^\.]+$/); + // (1) Split the string + var nameSplits = CSL.Output.Formatters.nameDoppel.split(name); + var namelist = []; + namelist = [nameSplits.strings[0]]; + + if (nameSplits.tags.length === 0) { + var mmm = namelist[0].match(/[^\.]+$/); if (mmm && mmm[0].length === 1 && mmm[0] !== mmm[0].toLowerCase()) { - lst[0] += "."; + namelist[0] += "."; } } - if (lst.length === 0) { - // This doesn't make much sense, and may be impossible. - namelist = mm; - } else { - namelist = [lst[0]]; - for (i = 1, ilen = lst.length; i < ilen; i += 1) { - namelist.push(mm[i - 1]); - namelist.push(lst[i]); - } + for (i = 1, ilen = nameSplits.strings.length; i < ilen; i += 1) { + namelist.push(nameSplits.tags[i - 1]); + namelist.push(nameSplits.strings[i]); } - lst = namelist; // Use doInitializeName or doNormalizeName, depending on requirements. if (normalizeOnly) { - ret = CSL.Util.Names.doNormalize(state, lst, terminator); + ret = this.doNormalize(state, namelist, terminator); } else { - ret = CSL.Util.Names.doInitialize(state, lst, terminator); + ret = this.doInitialize(state, namelist, terminator); } ret = ret.replace(/\u2013([a-z])/g, "-$1"); return ret; }; +CSL.Util.Names.notag = function(str) { + return str.replace(/^(?:<[^>]+>)*/, ""); +}; + +CSL.Util.Names.mergetag = function(state, tagstr, newstr) { + var m = tagstr.match(/(?:-*<[^>]+>-*)/g); + if (!m) { + return newstr; + } else { + tagstr = m.join(""); + } + m = newstr.match(/^(.*[^\s])*(\s+)$/); + if (m) { + m[1] = m[1] ? m[1] : ""; + newstr = m[1] + tagstr + m[2]; + } else { + newstr = newstr + tagstr; + } + return newstr; +}; + +CSL.Util.Names.tagonly = function(state, str) { + var m = str.match(/(?:<[^>]+>)+/); + if (!m) { + return str; + } else { + return m.join(""); + } +}; + CSL.Util.Names.doNormalize = function (state, namelist, terminator) { var i, ilen; // namelist is a flat list of given-name elements and space-like separators between them @@ -19006,8 +19112,9 @@ CSL.Util.Names.doNormalize = function (state, namelist, terminator) { // Flag elements that look like abbreviations var isAbbrev = []; for (i = 0, ilen = namelist.length; i < ilen; i += 1) { - if (namelist[i].length > 1 && namelist[i].slice(-1) === ".") { - namelist[i] = namelist[i].slice(0, -1); + if (this.notag(namelist[i]).length > 1 && this.notag(namelist[i]).slice(-1) === ".") { + // namelist[i] = namelist[i].slice(0, -1); + namelist[i] = namelist[i].replace(/^(.*)\.(.*)$/, "$1$2"); isAbbrev.push(true); } else if (namelist[i].length === 1 && namelist[i].toUpperCase() === namelist[i]) { isAbbrev.push(true); @@ -19022,19 +19129,18 @@ CSL.Util.Names.doNormalize = function (state, namelist, terminator) { // For all elements but the last if (i < namelist.length - 2) { // Start from scratch on space-like things following an abbreviation - namelist[i + 1] = ""; - + namelist[i + 1] = this.tagonly(state, namelist[i+1]); if (!isAbbrev[i+2]) { - namelist[i + 1] = " "; + namelist[i + 1] = this.tagonly(state, namelist[i+1]) + " "; } // Add the terminator to the element // If the following element is not a single-character abbreviation, remove a trailing zero-width non-break space, if present // These ops may leave some duplicate cruft in the elements and separators. This will be cleaned at the end of the function. if (namelist[i + 2].length > 1) { - namelist[i] = namelist[i] + terminator.replace(/\ufeff$/, ""); + namelist[i+1] = terminator.replace(/\ufeff$/, "") + namelist[i+1]; } else { - namelist[i] = namelist[i] + terminator; + namelist[i+1] = this.mergetag(state, namelist[i+1], terminator); } } // For the last element (if it is an abbreviation), just append the terminator @@ -19085,9 +19191,9 @@ CSL.Util.Names.doInitialize = function (state, namelist, terminator) { namelist[i] = terminator.replace("%s", namelist[i]); } else { if (namelist[i + 1].indexOf("-") > -1) { - namelist[i + 1] = terminator + namelist[i + 1]; + namelist[i + 1] = this.mergetag(state, namelist[i+1].replace("-", ""), terminator) + "-"; } else { - namelist[i + 1] = terminator; + namelist[i + 1] = this.mergetag(state, namelist[i+1], terminator); } } } else { @@ -19121,14 +19227,6 @@ CSL.Util.Names.getRawName = function (name) { return ret.join(" "); }; -// deleted CSL.Util.Names.initNameSlices() -// no longer used. - -// deleted CSL.Util.Names,rescueNameElements() -// apparently not used. - - - /*global CSL: true */ /** @@ -19198,6 +19296,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) { @@ -19206,7 +19306,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]; @@ -19986,7 +20088,7 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) { if (localeAnd === localeAmpersand) { localeAmpersand = "&"; } - + // XXXX shadow_numbers should carry an array of objects with // XXXX full data for each. The test of a number should be // XXXX a separate function, possibly supported by a splitter @@ -20093,6 +20195,11 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) { str = normalizeFieldValue(str, defaultLabel); var jmrex, jsrex, mystr; + if ("page" === variable) { + 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("\\-", "")); @@ -20575,6 +20682,10 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) { val = ItemObject[realVariable]; } + if (val && realVariable === "number" && ItemObject.type === "legal_case") { + val = val.replace(/[\\]*-/g, "\\-"); + } + // XXX HOLDING THIS // Apply short form ONLY if first element tests is-numeric=false if (val && this.sys.getAbbreviation) { @@ -20583,16 +20694,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]; } } } @@ -20762,9 +20880,9 @@ CSL.Util.outputNumericField = function(state, varname, itemID) { if (num.collapsible) { var blob; if (num.value.match(/^[1-9][0-9]*$/)) { - blob = new CSL.NumericBlob(num.particle, parseInt(num.value, 10), numStyling, itemID); + blob = new CSL.NumericBlob(state, num.particle, parseInt(num.value, 10), numStyling, itemID); } else { - blob = new CSL.NumericBlob(num.particle, num.value, numStyling, itemID); + blob = new CSL.NumericBlob(state, num.particle, num.value, numStyling, itemID); } if ("undefined" === typeof blob.gender) { blob.gender = state.locale[state.opt.lang]["noun-genders"][varname]; @@ -21547,6 +21665,8 @@ CSL.Output.Formatters = (function () { var tagDoppel = new CSL.Doppeler(rexStr, function(str) { return str.replace(/(]*(>)/g, "$1 $2$3").replace(/(]*(>)/g, "$1 $2 $3;$4$5"); }); + var rexNameStr = "(?:[-\\s]*<\\/*(?:span\s+class=\"no(?:case|decor)\"|i|sc|b|sub|sup)>[-\\s]*|[-\\s]+)"; + var nameDoppel = new CSL.Doppeler(rexNameStr); var wordDoppel = new CSL.Doppeler("(?:[\u0020\u00A0\u2000-\u200B\u205F\u3000]+)"); @@ -21917,6 +22037,7 @@ CSL.Output.Formatters = (function () { return _textcaseEngine.call(state, config, string); } return { + nameDoppel: nameDoppel, passthrough: passthrough, lowercase: lowercase, uppercase: uppercase, @@ -22798,8 +22919,9 @@ CSL.Registry.prototype.dopurge = function (myhash) { for (var i=this.mylist.length-1;i>-1;i+=-1) { // Might not want to be quite this restrictive. if (this.citationreg.citationsByItemId) { - if (!this.citationreg.citationsByItemId[this.mylist[i]] && !myhash[this.mylist[i]]) { + if ((!this.citationreg.citationsByItemId || !this.citationreg.citationsByItemId[this.mylist[i]]) && !myhash[this.mylist[i]]) { delete this.myhash[this.mylist[i]]; + delete this.uncited[this.mylist[i]]; this.mylist = this.mylist.slice(0,i).concat(this.mylist.slice(i+1)); } } @@ -23624,6 +23746,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. @@ -24319,7 +24447,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"); @@ -24327,6 +24460,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>===== MODE =====>> +citation +<<===== MODE =====<< + + + +>>===== RESULT =====>> +cite-form match OK on Pick. +<<===== RESULT =====<< + +>>== ABBREVIATIONS ==>> +{ + "default": { + "container-title": { + "Pickwick Papers": "#1>>>Pick." + } + } +} +<<== ABBREVIATIONS ==<< + + +>>===== CSL =====>> + +<<===== CSL =====<< + + +>>===== INPUT =====>> +[ + { + "id": "ITEM-1", + "type": "article-journal", + "container-title": "Pickwick Papers" + } +] +<<===== INPUT =====<< diff --git a/lib/citeproc-js/fixtures/local/abbrevs_CiteFormFlagDisambig.txt b/lib/citeproc-js/fixtures/local/abbrevs_CiteFormFlagDisambig.txt new file mode 100644 index 0000000..ce22275 --- /dev/null +++ b/lib/citeproc-js/fixtures/local/abbrevs_CiteFormFlagDisambig.txt @@ -0,0 +1,193 @@ +>>===== MODE =====>> +citation +<<===== MODE =====<< + + + +>>===== RESULT =====>> +..[0] Darrel Jones, 100(1) Named J. 10; Smith, 200(2) Other J. 20 +..[1] Daniel Jones, 100(1) Named J. 10; Smith, 200(2) Other J. 20 +>>[2] Noakes, 300 Better Journal 30 +<<===== RESULT =====<< + +>>== ABBREVIATIONS ==>> +{ + "default": { + "container-title": { + "Named Journal": "#1>>>Named J.", + "Other Journal": "#1>>>Other J." + } + } +} +<<== ABBREVIATIONS ==<< + +>>===== CITATIONS =====>> +[ + [ + { + "citationID": "CITATION-1", + "citationItems": [ + { + "id": "ITEM-1" + }, + { + "id": "ITEM-3" + } + ], + "properties": { + "noteIndex": 1 + } + }, + [], + [] + ], + [ + { + "citationID": "CITATION-2", + "citationItems": [ + { + "id": "ITEM-2" + }, + { + "id": "ITEM-4" + } + ], + "properties": { + "noteIndex": 2 + } + }, + [["CITATION-1", 1]], + [] + ], + [ + { + "citationID": "CITATION-3", + "citationItems": [ + { + "id": "ITEM-5" + } + ], + "properties": { + "noteIndex": 3 + } + }, + [["CITATION-1", 1], ["CITATION-2", 2]], + [] + ] +] +<<===== CITATIONS =====<< + + +>>===== CSL =====>> + +<<===== CSL =====<< + + +>>===== INPUT =====>> +[ + { + "id": "ITEM-1", + "type": "article-journal", + "author": [ + { + "family": "Jones", + "given": "Darrel" + } + ], + "volume": "100", + "issue": "1", + "container-title": "Named Journal", + "page": "10" + }, + { + "id": "ITEM-2", + "type": "article-journal", + "author": [ + { + "family": "Jones", + "given": "Daniel" + } + ], + "volume": "100", + "issue": "1", + "container-title": "Named Journal", + "page": "10" + }, + { + "id": "ITEM-3", + "type": "article-journal", + "author": [ + { + "family": "Smith", + "given": "William" + } + ], + "volume": "200", + "issue": "2", + "container-title": "Other Journal", + "page": "20" + }, + { + "id": "ITEM-4", + "type": "article-journal", + "author": [ + { + "family": "Smith", + "given": "William" + } + ], + "volume": "200", + "issue": "2", + "container-title": "Other Journal", + "page": "20" + }, + { + "id": "ITEM-5", + "type": "article-journal", + "author": [ + { + "family": "Noakes", + "given": "Richard" + } + ], + "volume": "300", + "issue": "3", + "container-title": "Better Journal", + "page": "30" + } +] +<<===== INPUT =====<< diff --git a/lib/citeproc-js/fixtures/local/abbrevs_MissingSegment.txt b/lib/citeproc-js/fixtures/local/abbrevs_MissingSegment.txt index 7362b54..1f4969b 100644 --- a/lib/citeproc-js/fixtures/local/abbrevs_MissingSegment.txt +++ b/lib/citeproc-js/fixtures/local/abbrevs_MissingSegment.txt @@ -34,7 +34,7 @@ Division of Errors and Omissions, Court of Appeal - + diff --git a/lib/citeproc-js/fixtures/local/bugreports_dailDebates.txt b/lib/citeproc-js/fixtures/local/bugreports_dailDebates.txt index 2e1299e..ce5246f 100644 --- a/lib/citeproc-js/fixtures/local/bugreports_dailDebates.txt +++ b/lib/citeproc-js/fixtures/local/bugreports_dailDebates.txt @@ -401,7 +401,7 @@ citation - + diff --git a/lib/citeproc-js/fixtures/local/citeprocjs_AffiliationSpoofingSubsequentSliceTwoNamesInstitution.txt b/lib/citeproc-js/fixtures/local/citeprocjs_AffiliationSpoofingSubsequentSliceTwoNamesInstitution.txt index b602b5d..ec45981 100644 --- a/lib/citeproc-js/fixtures/local/citeprocjs_AffiliationSpoofingSubsequentSliceTwoNamesInstitution.txt +++ b/lib/citeproc-js/fixtures/local/citeprocjs_AffiliationSpoofingSubsequentSliceTwoNamesInstitution.txt @@ -1,6 +1,7 @@ >>===== OPTIONS =====>> { - "spoof_institutional_affiliations": true + "spoof_institutional_affiliations": true, + "etal_min_etal_usefirst_hack": true } <<===== OPTIONS =====<< diff --git a/lib/citeproc-js/fixtures/local/citeprocjs_AuthorListing.txt b/lib/citeproc-js/fixtures/local/citeprocjs_AuthorListing.txt index 1d138d5..c1310c3 100644 --- a/lib/citeproc-js/fixtures/local/citeprocjs_AuthorListing.txt +++ b/lib/citeproc-js/fixtures/local/citeprocjs_AuthorListing.txt @@ -4,6 +4,12 @@ bibliography Not the cleanest of tests. +>>===== OPTIONS =====>> +{ + "etal_min_etal_usefirst_hack": true +} +<<===== OPTIONS =====<< + >>===== RESULT =====>>
diff --git a/lib/citeproc-js/fixtures/local/citeprocjs_SubsequentSliceTwoNamesInstitution.txt b/lib/citeproc-js/fixtures/local/citeprocjs_SubsequentSliceTwoNamesInstitution.txt index ee14588..62952f0 100644 --- a/lib/citeproc-js/fixtures/local/citeprocjs_SubsequentSliceTwoNamesInstitution.txt +++ b/lib/citeproc-js/fixtures/local/citeprocjs_SubsequentSliceTwoNamesInstitution.txt @@ -2,7 +2,11 @@ bibliography <<===== MODE =====<< - +>>===== OPTIONS =====>> +{ + "etal_min_etal_usefirst_hack": true +} +<<===== OPTIONS =====<< >>===== RESULT =====>>
diff --git a/lib/citeproc-js/fixtures/local/disambiguate_PreferSingleName.txt b/lib/citeproc-js/fixtures/local/disambiguate_PreferSingleName.txt new file mode 100644 index 0000000..e7f483b --- /dev/null +++ b/lib/citeproc-js/fixtures/local/disambiguate_PreferSingleName.txt @@ -0,0 +1,74 @@ +>>===== MODE =====>> +citation +<<===== MODE =====<< + +>>===== RESULT =====>> +Kurtz Camembert et al. / Kurt Camembert et al. +<<===== RESULT =====<< + +>>===== INPUT =====>> +[ + { + "id": "1", + "type": "book", + "author": [ + { + "family": "Camembert", + "given": "Kurtz" + }, + { + "family": "Rossi", + "given": "Amadeus" + }, + { + "family": "Ignoble", + "given": "Ignatius" + } + ] + }, + { + "id": "2", + "type": "book", + "author": [ + { + "family": "Camembert", + "given": "Kurt" + }, + { + "family": "Rossi", + "given": "Amadeus" + }, + { + "family": "Idiosyncratic", + "given": "Ignatius" + } + ] + } +] +<<===== INPUT =====<< + +>>===== CSL =====>> + + + +<<===== CSL =====<< + diff --git a/lib/citeproc-js/fixtures/local/disambiguate_WithConsolidation.txt b/lib/citeproc-js/fixtures/local/disambiguate_WithConsolidation.txt new file mode 100644 index 0000000..9364043 --- /dev/null +++ b/lib/citeproc-js/fixtures/local/disambiguate_WithConsolidation.txt @@ -0,0 +1,128 @@ +>>===== MODE =====>> +citation +<<===== MODE =====<< + + + +>>===== RESULT =====>> +>>[0] SchUG idF BGBl I 80/2020; SchUG idF BGBl I 117/2008. +>>[1] SchUG idF BGBl I 80/2020; SchUG idF BGBl I 117/2008. +<<===== RESULT =====<< + + +>>===== CITATIONS =====>> +[ + [ + { + "citationID": "CITATION-1", + "citationItems": [ + { + "id": "ITEM-1" + }, + { + "id": "ITEM-2" + } + ], + "properties": { + "noteIndex": 1 + } + }, + [], + [] + ], + [ + { + "citationID": "CITATION-2", + "citationItems": [ + { + "id": "ITEM-1" + }, + { + "id": "ITEM-2" + } + ], + "properties": { + "noteIndex": 2 + } + }, + [ + [ + "CITATION-1", + 1 + ] + ], + [] + ] +] +<<===== CITATIONS =====<< + + +>>===== CSL =====>> + +<<===== CSL =====<< + + +>>===== INPUT =====>> +[ + { + "id": "ITEM-1", + "type": "legislation", + "title": "Schulunterrichtsgesetz", + "references": "BGBl I 80/2020", + "shortTitle": "SchUG", + "jurisdiction": "at" + }, + { + "id": "ITEM-2", + "type": "legislation", + "title": "Schulunterrichtsgesetz", + "references": "BGBl I 117/2008", + "shortTitle": "SchUG", + "jurisdiction": "at" + } +] +<<===== INPUT =====<< diff --git a/lib/citeproc-js/fixtures/local/institutions_AffiliationSpoofingMixedPeopleAndOrganizationsAlwaysUseFirst.txt b/lib/citeproc-js/fixtures/local/institutions_AffiliationSpoofingMixedPeopleAndOrganizationsAlwaysUseFirst.txt index bb31b55..50770b6 100644 --- a/lib/citeproc-js/fixtures/local/institutions_AffiliationSpoofingMixedPeopleAndOrganizationsAlwaysUseFirst.txt +++ b/lib/citeproc-js/fixtures/local/institutions_AffiliationSpoofingMixedPeopleAndOrganizationsAlwaysUseFirst.txt @@ -39,6 +39,7 @@ Noakes with Roe, Tiny Subcommittee, Big Ministry and Doe et al., Trivial Working