From 2964021661d7bd2eb832a420bd4c6885ed00defa Mon Sep 17 00:00:00 2001
From: Dan Stillman
Date: Tue, 9 Jul 2019 03:04:12 -0400
Subject: [PATCH] Update citeproc-js to 1.2.13
---
chrome/content/zotero/xpcom/citeproc.js | 7171 ++++++++++++++++++++---
1 file changed, 6487 insertions(+), 684 deletions(-)
diff --git a/chrome/content/zotero/xpcom/citeproc.js b/chrome/content/zotero/xpcom/citeproc.js
index 1cf1cc775a..508366d50d 100644
--- a/chrome/content/zotero/xpcom/citeproc.js
+++ b/chrome/content/zotero/xpcom/citeproc.js
@@ -22,9 +22,62 @@ Copyright (c) 2009-2019 Frank Bennett
this program. If not, see or
respectively.
*/
+/*global CSL: true */
+
+/**
+ * A Javascript implementation of the CSL citation formatting language.
+ *
+ *
A configured instance of the process is built in two stages,
+ * using {@link CSL.Core.Build} and {@link CSL.Core.Configure}.
+ * The former sets up hash-accessible locale data and imports the CSL format file
+ * to be applied to the citations,
+ * transforming it into a one-dimensional token list, and
+ * registering functions and parameters on each token as appropriate.
+ * The latter sets jump-point information
+ * on tokens that constitute potential branch
+ * points, in a single back-to-front scan of the token list.
+ * This
+ * yields a token list that can be executed front-to-back by
+ * body methods available on the
+ * {@link CSL.Engine} class.
+ *
+ *
This top-level {@link CSL} object itself carries
+ * constants that are needed during processing.
+ * @namespace A CSL citation formatter.
+ */
+
+// IE6 does not implement Array.indexOf().
+// IE7 neither, according to rumour.
+
+
+// Potential skip words:
+// under; along; out; between; among; outside; inside; amid; amidst; against; toward; towards.
+// See https://forums.zotero.org/discussion/30484/?Focus=159613#Comment_159613
+
+'use strict';
+
+
var CSL = {
- PROCESSOR_VERSION: "1.2.2",
+
+ PROCESSOR_VERSION: "1.2.13",
+
+ error: function(str) { // default error function
+ if ("undefined" === typeof Error) {
+ throw new Error("citeproc-js error: " + str);
+ } else {
+ throw "citeproc-js error: " + str;
+ }
+ },
+ debug: function(str) { // default debug function
+ if ("undefined" === typeof console) {
+ dump("CSL: " + str + "\n");
+ } else {
+ console.log("citeproc-js warning: " + str);
+ }
+ },
+
LOCATOR_LABELS_REGEXP: new RegExp("^((art|ch|subch|col|fig|l|n|no|op|p|pp|para|subpara|supp|pt|r|sec|subsec|sv|sch|tit|vrs|vol)\\.)\\s+(.*)"),
+
STATUTE_SUBDIV_PLAIN_REGEX: /(?:(?:^| )(?:art|bk|ch|subch|col|fig|fol|l|n|no|op|p|pp|para|subpara|supp|pt|r|sec|subsec|sv|sch|tit|vrs|vol)\. *)/,
STATUTE_SUBDIV_PLAIN_REGEX_FRONT: /(?:^\s*[.,;]*\s*(?:art|bk|ch|subch|col|fig|fol|l|n|no|op|p|pp|para|subpara|supp|pt|r|sec|subsec|sv|sch|tit|vrs|vol)\. *)/,
STATUTE_SUBDIV_STRINGS: {
@@ -81,6 +134,7 @@ var CSL = {
"verse": "vrs.",
"volume": "vol."
},
+
LOCATOR_LABELS_MAP: {
"art": "article",
"bk": "book",
@@ -127,12 +181,16 @@ var CSL = {
"gazette": true,
"report": true,
"regulation": true,
- "standard": true
+ "standard": true,
+ "patent": true
},
checkNestedBrace: function(state) {
if (state.opt.xclass === "note") {
this.depth = 0;
this.update = function(str) {
+
+ // Receives affix string, returns with flipped parens.
+
var str = str ? str : "";
var lst = str.split(/([\(\)])/);
for (var i=1,ilen=lst.length;i0 || j>1) && !elems[j-1].match(CSL.NOTE_FIELD_REGEXP)) {
break;
} else {
@@ -285,6 +353,7 @@ var CSL = {
lines[i] = elems.join('');
}
}
+ // Resplit
lines = lines.join('\n').split('\n');
var offset = 0;
var names = {};
@@ -307,8 +376,8 @@ var CSL = {
Item.type = val;
lines[i] = "";
} else if (CSL.DATE_VARIABLES.indexOf(key.replace(/^alt-/, "")) > -1) {
- if (allowDateOverride) {
- Item[key] = {raw: val};
+ if (!Item[key] || allowDateOverride) {
+ Item[key] = CSL.DateParser.parseDateToArray(val);
if (!validFieldsForType || (validFieldsForType[key] && this.isDateString(val))) {
lines[i] = "";
}
@@ -337,6 +406,7 @@ var CSL = {
for (var key in names) {
Item[key] = names[key];
}
+ // Final cleanup for validCslFields only: eliminate blank lines, add blank line to text
if (validFieldsForType) {
if (lines[offset].trim()) {
lines[offset] = '\n' + lines[offset];
@@ -349,11 +419,21 @@ var CSL = {
}
Item.note = lines.join("\n").trim();
},
+
checkPrefixSpaceAppend: function (state, prefix) {
if (!prefix) {
prefix = "";
}
var sp = "";
+ // We need the raw string, without decorations
+ // of any kind. Markup scheme is known, though, so
+ // markup can be safely stripped at string level.
+ //
+ // U+201d = right double quotation mark
+ // U+2019 = right single quotation mark
+ // U+00bb = right double angle bracket (guillemet)
+ // U+202f = non-breaking thin space
+ // U+00a0 = non-breaking space
var test_prefix = prefix.replace(/<[^>]+>/g, "").replace(/["'\u201d\u2019\u00bb\u202f\u00a0 ]+$/g,"");
var test_char = test_prefix.slice(-1);
if (test_prefix.match(CSL.ENDSWITH_ROMANESQUE_REGEXP)) {
@@ -363,9 +443,12 @@ var CSL = {
} else if (test_char.match(/[\)\],0-9]/)) {
sp = " ";
}
+ // Protect against double spaces, which would trigger an extra,
+ // explicit, non-breaking space.
var prefix = (prefix + sp).replace(/\s+/g, " ");
return prefix;
},
+
checkIgnorePredecessor: function(state, prefix) {
var ignorePredecessor = false;
var test_prefix = prefix.replace(/<[^>]+>/g, "").replace(/["'\u201d\u2019\u00bb\u202f\u00a0 ]+$/g,"");
@@ -376,6 +459,7 @@ var CSL = {
}
return false;
},
+
checkSuffixSpacePrepend: function(state, suffix) {
if (!suffix) {
return "";
@@ -385,48 +469,69 @@ var CSL = {
}
return suffix;
},
+
GENDERS: ["masculine", "feminine"],
+
ERROR_NO_RENDERED_FORM: 1,
+
PREVIEW: "Just for laughs.",
ASSUME_ALL_ITEMS_REGISTERED: 2,
+
START: 0,
END: 1,
SINGLETON: 2,
+
SEEN: 6,
SUCCESSOR: 3,
SUCCESSOR_OF_SUCCESSOR: 4,
SUPPRESS: 5,
+
SINGULAR: 0,
PLURAL: 1,
+
LITERAL: true,
+
BEFORE: 1,
AFTER: 2,
+
DESCENDING: 1,
ASCENDING: 2,
+
PRIMARY: 1,
SECONDARY: 2,
+
POSITION_FIRST: 0,
POSITION_SUBSEQUENT: 1,
POSITION_IBID: 2,
POSITION_IBID_WITH_LOCATOR: 3,
+
POSITION_TEST_VARS: ["position", "first-reference-note-number", "near-note"],
+
AREAS: ["citation", "citation_sort", "bibliography", "bibliography_sort", "intext"],
+
CITE_FIELDS: ["first-reference-note-number", "locator", "locator-extra"],
+
SWAPPING_PUNCTUATION: [".", "!", "?", ":", ","],
TERMINAL_PUNCTUATION: [":", ".", ";", "!", "?", " "],
+
+ // update modes
NONE: 0,
NUMERIC: 1,
POSITION: 2,
TRIGRAPH: 3,
+
DATE_PARTS: ["year", "month", "day"],
DATE_PARTS_ALL: ["year", "month", "day", "season"],
DATE_PARTS_INTERNAL: ["year", "month", "day", "year_end", "month_end", "day_end"],
+
NAME_PARTS: ["non-dropping-particle", "family", "given", "dropping-particle", "suffix", "literal"],
+
DISAMBIGUATE_OPTIONS: [
"disambiguate-add-names",
"disambiguate-add-givenname",
"disambiguate-add-year-suffix"
],
+
GIVENNAME_DISAMBIGUATION_RULES: [
"all-names",
"all-names-with-initials",
@@ -434,6 +539,7 @@ var CSL = {
"primary-name-with-initials",
"by-cite"
],
+
NAME_ATTRIBUTES: [
"and",
"delimiter-precedes-last",
@@ -451,28 +557,51 @@ var CSL = {
"suffix",
"delimiter"
],
+
PARALLEL_MATCH_VARS: ["container-title"],
PARALLEL_TYPES: ["bill","gazette","regulation","legislation","legal_case","treaty","article-magazine","article-journal"],
PARALLEL_COLLAPSING_MID_VARSET: ["volume", "issue", "container-title", "section", "collection-number"],
+
LOOSE: 0,
STRICT: 1,
TOLERANT: 2,
+
PREFIX_PUNCTUATION: /[.;:]\s*$/,
SUFFIX_PUNCTUATION: /^\s*[.;:,\(\)]/,
+
NUMBER_REGEXP: /(?:^\d+|\d+$)/,
+ //
+ // \u0400-\u042f are cyrillic and extended cyrillic capitals
+ // this is not fully smart yet. can't do what this was trying to do
+ // with regexps, actually; we want to identify strings with a leading
+ // capital letter, and any subsequent capital letters. Have to compare
+ // locale caps version with existing version, character by character.
+ // hard stuff, but if it breaks, that's what to do.
+ // \u0600-\u06ff is Arabic/Persian
+ // \u200c-\u200e and \u202a-\u202e are special spaces and left-right
+ // control characters
+
+
+
NAME_INITIAL_REGEXP: /^([A-Z\u0e01-\u0e5b\u00c0-\u017f\u0400-\u042f\u0590-\u05d4\u05d6-\u05ff\u0600-\u06ff\u0370\u0372\u0376\u0386\u0388-\u03ab\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03fd-\u03ff])([a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0400-\u052f\u0600-\u06ff\u0370-\u03ff\u1f00-\u1fff]*|)(\.)*/,
ROMANESQUE_REGEXP: /[-0-9a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/,
ROMANESQUE_NOT_REGEXP: /[^a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/g,
STARTSWITH_ROMANESQUE_REGEXP: /^[&a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/,
ENDSWITH_ROMANESQUE_REGEXP: /[.;:&a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]$/,
ALL_ROMANESQUE_REGEXP: /^[a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]+$/,
+
VIETNAMESE_SPECIALS: /[\u00c0-\u00c3\u00c8-\u00ca\u00cc\u00cd\u00d2-\u00d5\u00d9\u00da\u00dd\u00e0-\u00e3\u00e8-\u00ea\u00ec\u00ed\u00f2-\u00f5\u00f9\u00fa\u00fd\u0101\u0103\u0110\u0111\u0128\u0129\u0168\u0169\u01a0\u01a1\u01af\u01b0\u1ea0-\u1ef9]/,
+
VIETNAMESE_NAMES: /^(?:(?:[.AaBbCcDdEeGgHhIiKkLlMmNnOoPpQqRrSsTtUuVvXxYy \u00c0-\u00c3\u00c8-\u00ca\u00cc\u00cd\u00d2-\u00d5\u00d9\u00da\u00dd\u00e0-\u00e3\u00e8-\u00ea\u00ec\u00ed\u00f2-\u00f5\u00f9\u00fa\u00fd\u0101\u0103\u0110\u0111\u0128\u0129\u0168\u0169\u01a0\u01a1\u01af\u01b0\u1ea0-\u1ef9]{2,6})(\s+|$))+$/,
+
NOTE_FIELDS_REGEXP: /\{:(?:[\-_a-z]+|[A-Z]+):[^\}]+\}/g,
NOTE_FIELD_REGEXP: /^([\-_a-z]+|[A-Z]+):\s*([^\}]+)$/,
+
PARTICLE_GIVEN_REGEXP: /^([^ ]+(?:\u02bb |\u2019 | |\' ) *)(.+)$/,
PARTICLE_FAMILY_REGEXP: /^([^ ]+(?:\-|\u02bb|\u2019| |\') *)(.+)$/,
+
DISPLAY_CLASSES: ["block", "left-margin", "right-inline", "indent"],
+
NAME_VARIABLES: [
"author",
"collection-editor",
@@ -507,6 +636,7 @@ var CSL = {
"call-number",
"chapter-number",
"collection-number",
+ "division",
"edition",
"page",
"issue",
@@ -516,9 +646,16 @@ var CSL = {
"number-of-pages",
"number-of-volumes",
"volume",
+ // "section", ??? add this?
"supplement",
"citation-number"
],
+ //var x = new Array();
+ //x = x.concat(["title","container-title","issued","page"]);
+ //x = x.concat(["locator","collection-number","original-date"]);
+ //x = x.concat(["reporting-date","decision-date","filing-date"]);
+ //x = x.concat(["revision-date"]);
+ //NUMERIC_VARIABLES = x.slice();
DATE_VARIABLES: [
"locator-date",
"issued",
@@ -536,13 +673,14 @@ var CSL = {
"container-title"
],
TITLE_FIELD_SPLITS: function(seg) {
- var keys = ["title", "short", "main", "sub"];
+ var keys = ["title", "short", "main", "sub", "subjoin"];
var ret = {};
for (var i=0,ilen=keys.length;i -1) {
+ vals[title.subjoin] = narrowSpace + ": ";
+ }
+ if (vals[title.subjoin].indexOf("-") > -1 || vals[title.subjoin].indexOf("—") > -1) {
+ vals[title.subjoin] = "—";
}
}
if (lang) {
@@ -617,7 +810,14 @@ var CSL = {
}
}
},
+
titlecaseSentenceOrNormal: function(state, Item, seg, lang, sentenceCase) {
+ // Hold on here.
+ // What is seg here?
+ // It's ... either "" or "container-". Which is ugly, but works.
+ // But this ALWAYS returns the full title, never short.
+ // So sentence-casing cannot be applied to short.
+ // Goes unnoticed because forced sentence-casing almost never appears in styles.
var title = CSL.TITLE_FIELD_SPLITS(seg);
var vals = {};
if (lang && Item.multi) {
@@ -630,13 +830,18 @@ var CSL = {
if (Item.multi._keys[title.sub]) {
vals[title.sub] = Item.multi._keys[title.sub][lang];
}
+ if (Item.multi._keys[title.subjoin]) {
+ vals[title.subjoin] = Item.multi._keys[title.subjoin][lang];
+ }
} else {
vals[title.title] = Item[title.title];
vals[title.main] = Item[title.main];
vals[title.sub] = Item[title.sub];
+ vals[title.subjoin] = Item[title.subjoin];
}
if (vals[title.main] && vals[title.sub]) {
var mainTitle = vals[title.main];
+ var subJoin = vals[title.subjoin];
var subTitle = vals[title.sub];
if (sentenceCase) {
mainTitle = CSL.Output.Formatters.sentence(state, mainTitle);
@@ -644,17 +849,41 @@ var CSL = {
} else if (state.opt.development_extensions.uppercase_subtitles) {
subTitle = CSL.Output.Formatters["capitalize-first"](state, subTitle);
}
- return [mainTitle, subTitle].join(vals[title.title].slice(mainTitle.length, -1 * subTitle.length));
- } else {
+ return [mainTitle, subJoin, subTitle].join("");
+ } else if (vals[title.title]) {
if (sentenceCase) {
return CSL.Output.Formatters.sentence(state, vals[title.title]);
+ } else if (state.opt.development_extensions.uppercase_subtitles) {
+ // Split and apply everywhere.
+ var splits = CSL.TITLE_SPLIT(vals[title.title]);
+ for (var i=0,ilen=splits.length; i -1 || splits[i].indexOf("—") > -1) {
+ splits[i] = "—";
+ }
+ }
+ vals[title.title] = splits.join("");
+ return vals[title.title];
} else {
return vals[title.title];
}
+ } else {
+ return "";
}
},
+
getSafeEscape: function(state) {
if (["bibliography", "citation"].indexOf(state.tmp.area) > -1) {
+ // Callback to apply thin space hack
+ // Callback to force LTR/RTL on parens and braces
+ // XXX Is this really necessary?
var callbacks = [];
if (state.opt.development_extensions.thin_non_breaking_space_html_hack && state.opt.mode === "html") {
callbacks.push(function (txt) {
@@ -675,7 +904,9 @@ var CSL = {
return function (txt) { return txt; };
}
},
+
SKIP_WORDS: ["about","above","across","afore","after","against","al", "along","alongside","amid","amidst","among","amongst","anenst","apropos","apud","around","as","aside","astride","at","athwart","atop","barring","before","behind","below","beneath","beside","besides","between","beyond","but","by","circa","despite","down","during","et", "except","for","forenenst","from","given","in","inside","into","lest","like","modulo","near","next","notwithstanding","of","off","on","onto","out","over","per","plus","pro","qua","sans","since","than","through"," thru","throughout","thruout","till","to","toward","towards","under","underneath","until","unto","up","upon","versus","vs.","v.","vs","v","via","vis-à-vis","with","within","without","according to","ahead of","apart from","as for","as of","as per","as regards","aside from","back to","because of","close to","due to","except for","far from","inside of","instead of","near to","next to","on to","out from","out of","outside of","prior to","pursuant to","rather than","regardless of","such as","that of","up to","where as","or", "yet", "so", "for", "and", "nor", "a", "an", "the", "de", "d'", "von", "van", "c", "ca"],
+
FORMAT_KEY_SEQUENCE: [
"@strip-periods",
"@font-style",
@@ -685,6 +916,7 @@ var CSL = {
"@vertical-align",
"@quotes"
],
+
INSTITUTION_KEYS: [
"font-style",
"font-variant",
@@ -692,6 +924,7 @@ var CSL = {
"text-decoration",
"text-case"
],
+
SUFFIX_CHARS: "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z",
ROMAN_NUMERALS: [
[ "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" ],
@@ -699,6 +932,7 @@ var CSL = {
[ "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" ],
[ "", "m", "mm", "mmm", "mmmm", "mmmmm"]
],
+
LANGS: {
"af-ZA":"Afrikaans",
"ar":"Arabic",
@@ -749,6 +983,7 @@ var CSL = {
"zh-CN":"Chinese (CN)",
"zh-TW":"Chinese (TW)"
},
+
LANG_BASES: {
af: "af_ZA",
ar: "ar",
@@ -793,6 +1028,7 @@ var CSL = {
vi: "vi_VN",
zh: "zh_CN"
},
+
SUPERSCRIPTS: {
"\u00AA": "\u0061",
"\u00B2": "\u0032",
@@ -900,6 +1136,7 @@ var CSL = {
"\u06E6": "\u064A"
},
SUPERSCRIPTS_REGEXP: new RegExp("[\u00AA\u00B2\u00B3\u00B9\u00BA\u02B0\u02B1\u02B2\u02B3\u02B4\u02B5\u02B6\u02B7\u02B8\u02E0\u02E1\u02E2\u02E3\u02E4\u1D2C\u1D2D\u1D2E\u1D30\u1D31\u1D32\u1D33\u1D34\u1D35\u1D36\u1D37\u1D38\u1D39\u1D3A\u1D3C\u1D3D\u1D3E\u1D3F\u1D40\u1D41\u1D42\u1D43\u1D44\u1D45\u1D46\u1D47\u1D48\u1D49\u1D4A\u1D4B\u1D4C\u1D4D\u1D4F\u1D50\u1D51\u1D52\u1D53\u1D54\u1D55\u1D56\u1D57\u1D58\u1D59\u1D5A\u1D5B\u1D5C\u1D5D\u1D5E\u1D5F\u1D60\u1D61\u2070\u2071\u2074\u2075\u2076\u2077\u2078\u2079\u207A\u207B\u207C\u207D\u207E\u207F\u2120\u2122\u3192\u3193\u3194\u3195\u3196\u3197\u3198\u3199\u319A\u319B\u319C\u319D\u319E\u319F\u02C0\u02C1\u06E5\u06E6]", "g"),
+
UPDATE_GROUP_CONTEXT_CONDITION: function (state, termtxt, valueTerm) {
if (state.tmp.group_context.tip.condition) {
if (state.tmp.group_context.tip.condition.test) {
@@ -938,6 +1175,8 @@ var CSL = {
}
}
} else {
+ // If not inside a conditional group, raise numeric flag
+ // if and only if the current term string ends in a number.
if (termtxt.slice(-1).match(/[0-9]/)) {
state.tmp.just_did_number = true;
} else {
@@ -945,28 +1184,75 @@ var CSL = {
}
}
},
+
SYS_OPTIONS: [
"prioritize_disambiguate_condition",
"csl_reverse_lookup_support",
"main_title_from_short_title",
"uppercase_subtitles"
- ]
+ ],
+
+ TITLE_SPLIT_REGEXP: (function() {
+ var splits = [
+ "\\.\\s+",
+ "\\!\\s+",
+ "\\?\\s+",
+ "\\s*::*\\s+",
+ "\\s*—\\s*",
+ "\\s+\\-\\s+",
+ "\\s*\\-\\-\\-*\\s*"
+ ]
+ return {
+ match: new RegExp("(" + splits.join("|") + ")", "g"),
+ matchfirst: new RegExp("^(" + splits.join("|") + ")"),
+ split: new RegExp("(?:" + splits.join("|") + ")")
+ }
+ })(),
+
+ TITLE_SPLIT: function(str) {
+ if (!str) {
+ return str;
+ }
+ var m = str.match(CSL.TITLE_SPLIT_REGEXP.match);
+ var lst = str.split(CSL.TITLE_SPLIT_REGEXP.split);
+ for (var i=lst.length-2; i>-1; i--) {
+ lst[i] = lst[i].trim();
+ if (lst[i] && lst[i].slice(-1).toLowerCase() !== lst[i].slice(-1)) {
+ // recombine
+ lst[i] = lst[i] + m[i] + lst[i+1];
+ lst = lst.slice(0, i+1).concat(lst.slice(i+2))
+ } else {
+ // merge
+ lst = lst.slice(0, i+1).concat([m[i]]).concat(lst.slice(i+1))
+ }
+ }
+ return lst;
+ }
};
-if ("undefined" === typeof console) {
- CSL.debug = function (str) {
- dump("CSL: " + str + "\n");
- };
- CSL.error = function (str) {
- dump("CSL error: " + str + "\n");
- };
-} else {
- CSL.debug = function (str) {
- console.log("CSL: " + str);
- };
- CSL.error = function (str) {
- console.log("CSL error: " + str);
- };
-}
+
+/**
+ * Functions for parsing an XML object converted to JSON.
+ */
+
+/*
+ Style and locale JSON should be formatted as follows. Note that
+ an empty literal should be set as an explicit empty strings within
+ children:[]
+
+ {
+ name:"term",
+ children:[
+ ""
+ ],
+ attrs:{
+ name:"author"
+ }
+ }
+
+ The following script will generate correctly formatted JSON
+ from a CSL style or locale file:
+*/
+
CSL.XmlJSON = function (dataObj) {
this.dataObj = dataObj;
this.institution = {
@@ -988,9 +1274,18 @@ CSL.XmlJSON = function (dataObj) {
]
};
};
+
+/**
+ * No need for cleaning with native JSON.
+ */
CSL.XmlJSON.prototype.clean = function (json) {
return json;
};
+
+
+/**
+ * Methods to call on a node.
+ */
CSL.XmlJSON.prototype.getStyleId = function (myjson, styleName) {
var tagName = 'id';
if (styleName) {
@@ -1010,25 +1305,37 @@ CSL.XmlJSON.prototype.getStyleId = function (myjson, styleName) {
}
return ret;
};
+
CSL.XmlJSON.prototype.children = function (myjson) {
+ //print("children()");
if (myjson && myjson.children.length) {
return myjson.children.slice();
} else {
return false;
}
};
+
CSL.XmlJSON.prototype.nodename = function (myjson) {
+ //print("nodename()");
return myjson ? myjson.name : null;
};
+
CSL.XmlJSON.prototype.attributes = function (myjson) {
+ //print("attributes()");
var ret = {};
for (var attrname in myjson.attrs) {
ret["@"+attrname] = myjson.attrs[attrname];
}
return ret;
};
+
+
CSL.XmlJSON.prototype.content = function (myjson) {
+ //print("content()");
+ // xmldom.js and xmle4x.js have "undefined" as default
var ret = "";
+ // This only catches content at first level, but that is good enough
+ // for us.
if (!myjson || !myjson.children) {
return ret;
}
@@ -1039,15 +1346,23 @@ CSL.XmlJSON.prototype.content = function (myjson) {
}
return ret;
};
+
+
CSL.XmlJSON.prototype.namespace = {}
+
CSL.XmlJSON.prototype.numberofnodes = function (myjson) {
+ //print("numberofnodes()");
if (myjson && "number" == typeof myjson.length) {
return myjson.length;
} else {
return 0;
}
};
+
+// getAttributeName() removed. Looks like it was not being used.
+
CSL.XmlJSON.prototype.getAttributeValue = function (myjson,name,namespace) {
+ //print("getAttributeValue()");
var ret = "";
if (namespace) {
name = namespace+":"+name;
@@ -1063,11 +1378,14 @@ CSL.XmlJSON.prototype.getAttributeValue = function (myjson,name,namespace) {
}
return ret;
}
+
CSL.XmlJSON.prototype.getNodeValue = function (myjson,name) {
+ //print("getNodeValue()");
var ret = "";
if (name){
for (var i=0, ilen=myjson.children.length; i < ilen; i += 1) {
if (myjson.children[i].name === name) {
+ // This will always be Object() unless empty
if (myjson.children[i].children.length) {
ret = myjson.children[i];
} else {
@@ -1078,23 +1396,30 @@ CSL.XmlJSON.prototype.getNodeValue = function (myjson,name) {
} else if (myjson) {
ret = myjson;
}
+ // Just being careful here, following the former DOM code. The JSON object we receive
+ // for this should be fully normalized.
if (ret && ret.children && ret.children.length == 1 && "string" === typeof ret.children[0]) {
ret = ret.children[0];
}
return ret;
}
+
CSL.XmlJSON.prototype.setAttributeOnNodeIdentifiedByNameAttribute = function (myjson,nodename,partname,attrname,val) {
+ //print("setAttributeOnNodeIdentifiedByNameAttribute()");
var pos, len, xml, nodes, node;
if (attrname.slice(0,1) === '@'){
attrname = attrname.slice(1);
}
+ // In the one place this is used in citeproc-js code, it doesn't need to recurse.
for (var i=0,ilen=myjson.children.length; i -1) {
+ continue;
+ }
+ var child = node.childNodes.item(j);
+ var subskippers = [];
+ for (var k = 0, klen = child.childNodes.length; k < klen; k += 1) {
+ if (child.childNodes.item(k).nodeType !== 1) {
+ subskippers.push(k);
+ }
+ }
+ if (child.childNodes.length - subskippers.length === 0) {
+ twovars.push(child.getAttribute('variable'));
+ if (child.getAttribute('suffix')
+ || child.getAttribute('prefix')) {
+ twovars = [];
+ break;
+ }
+ }
+ }
+ if (twovars.indexOf("publisher") > -1 && twovars.indexOf("publisher-place") > -1) {
+ node.setAttribute('has-publisher-and-publisher-place', true);
+ }
+ }
+ }
+};
+*/
+
CSL.XmlJSON.prototype.isChildOfSubstitute = function(parents) {
if (parents.length > 0) {
var myparents = parents.slice();
@@ -1231,11 +1617,13 @@ CSL.XmlJSON.prototype.isChildOfSubstitute = function(parents) {
}
return false;
};
+
CSL.XmlJSON.prototype.addMissingNameNodes = function(myjson,parents) {
if (!parents) {
parents = [];
}
if (myjson.name === "names") {
+ // Trawl through children to decide whether a name node is needed here
if (!this.isChildOfSubstitute(parents)) {
var addName = true;
for (var i=0,ilen=myjson.children.length;i[ ]+<").replace(/<\!--.*?-->/g, "");
var lst = str.split("><");
@@ -1364,6 +1773,9 @@ CSL.parseXml = function(str) {
}
}
lst = lst.slice(stylePos);
+ // Combine open/close elements for empty terms,
+ // so that they will be passed through correctly
+ // as empty strings.
for (var i=lst.length-2;i>-1;i--) {
if (lst[i].slice(1).indexOf("<") === -1) {
var stub = lst[i].slice(0, 5);
@@ -1384,6 +1796,7 @@ CSL.parseXml = function(str) {
}
return lst;
}
+
function _decodeHtmlEntities(str) {
return str
.split("&").join("&")
@@ -1398,6 +1811,7 @@ CSL.parseXml = function(str) {
return String.fromCharCode(num);
});
}
+
function _getAttributes(elem) {
var m = elem.match(/([^\'\"= ]+)=(?:\"[^\"]*\"|\'[^\']*\')/g);
if (m) {
@@ -1407,16 +1821,20 @@ CSL.parseXml = function(str) {
}
return m;
}
+
function _getAttribute(elem, attr) {
var rex = RegExp('^.*[ ]+' + attr + '=(\"(?:[^\"]*)\"|\'(?:[^\']*)\').*$');
var m = elem.match(rex);
return m ? m[1].slice(1, -1) : null;
}
+
function _getTagName(elem) {
var rex = RegExp("^<([^ />]+)");
var m = elem.match(rex);
return m ? m[1] : null;
}
+
+
function _castObjectFromOpeningTag(elem) {
var obj = {};
obj.name = _getTagName(elem);
@@ -1434,44 +1852,60 @@ CSL.parseXml = function(str) {
obj.children = [];
return obj;
}
+
function _extractTextFromCompositeElement(elem) {
var m = elem.match(/^.*>([^<]*)<.*$/);
return _decodeHtmlEntities(m[1]);
}
+
function _appendToChildren(obj) {
_stack.slice(-1)[0].push(obj);
}
+
function _extendStackWithNewChildren(obj) {
_stack.push(obj.children);
}
+
function processElement(elem) {
var obj;
if (elem.slice(1).indexOf('<') > -1) {
+ // withtext
var tag = elem.slice(0, elem.indexOf('>')+1);
obj = _castObjectFromOpeningTag(tag);
obj.children = [_extractTextFromCompositeElement(elem)];
_appendToChildren(obj);
} else if (elem.slice(-2) === '/>') {
+ // singleton
obj = _castObjectFromOpeningTag(elem);
+ // Empty term as singleton
if (_getTagName(elem) === 'term') {
obj.children.push('');
}
_appendToChildren(obj);
} else if (elem.slice(0, 2) === '') {
+ // close
_stack.pop();
} else {
+ // open
obj = _castObjectFromOpeningTag(elem);
_appendToChildren(obj)
_extendStackWithNewChildren(obj);
}
}
+
var lst = _listifyString(str);
+
for (var i=0,ilen=lst.length;i 0)
@@ -1542,9 +1986,15 @@ CSL.XmlDOM = function (dataObj) {
case 3:
case 4:
case 8:
+ // Drop comments on the floor as well.
+ //return doc.createTextNode(node.nodeValue);
+ //break;
}
};
this.parser = new DOMParser();
+
+ // This seems horribly tormented, but there might be a reason for it.
+ // Perhaps this was the only way I found to get namespacing to work ... ?
var str = "";
var inst_doc = this.parser.parseFromString(str, "text/xml");
var inst_node = inst_doc.getElementsByTagName("institution");
@@ -1553,6 +2003,11 @@ CSL.XmlDOM = function (dataObj) {
this.institutionpart = inst_part_node.item(0);
this.ns = "http://purl.org/net/xbiblio/csl";
};
+
+/**
+ * No need for cleaning with the DOM, I think. This will probably just be a noop.
+ * But first, let's get XML mode switching up and running.
+ */
CSL.XmlDOM.prototype.clean = function (xml) {
xml = xml.replace(/<\?[^?]+\?>/g, "");
xml = xml.replace(/]+>/g, "");
@@ -1561,6 +2016,11 @@ CSL.XmlDOM.prototype.clean = function (xml) {
xml = xml.replace(/^\n*/, "");
return xml;
};
+
+
+/**
+ * Methods to call on a node.
+ */
CSL.XmlDOM.prototype.getStyleId = function (myxml, styleName) {
var text = "";
var tagName = "id";
@@ -1572,16 +2032,20 @@ CSL.XmlDOM.prototype.getStyleId = function (myxml, styleName) {
node = node.item(0);
}
if (node) {
+ // W3C conformant browsers
text = node.textContent;
}
if (!text) {
+ // Opera, IE 6 & 7
text = node.innerText;
}
if (!text) {
+ // Safari
text = node.innerHTML;
}
return text;
};
+
CSL.XmlDOM.prototype.children = function (myxml) {
var children, pos, len, ret;
if (myxml) {
@@ -1597,10 +2061,12 @@ CSL.XmlDOM.prototype.children = function (myxml) {
return [];
}
};
+
CSL.XmlDOM.prototype.nodename = function (myxml) {
var ret = myxml.nodeName;
return ret;
};
+
CSL.XmlDOM.prototype.attributes = function (myxml) {
var ret, attrs, attr, key, xml, pos, len;
ret = new Object();
@@ -1613,6 +2079,8 @@ CSL.XmlDOM.prototype.attributes = function (myxml) {
}
return ret;
};
+
+
CSL.XmlDOM.prototype.content = function (myxml) {
var ret;
if ("undefined" != typeof myxml.textContent) {
@@ -1624,9 +2092,12 @@ CSL.XmlDOM.prototype.content = function (myxml) {
}
return ret;
};
+
+
CSL.XmlDOM.prototype.namespace = {
"xml":"http://www.w3.org/XML/1998/namespace"
}
+
CSL.XmlDOM.prototype.numberofnodes = function (myxml) {
if (myxml) {
return myxml.length;
@@ -1634,10 +2105,12 @@ CSL.XmlDOM.prototype.numberofnodes = function (myxml) {
return 0;
}
};
+
CSL.XmlDOM.prototype.getAttributeName = function (attr) {
var ret = attr.name;
return ret;
}
+
CSL.XmlDOM.prototype.getAttributeValue = function (myxml,name,namespace) {
var ret = "";
if (namespace) {
@@ -1648,6 +2121,10 @@ CSL.XmlDOM.prototype.getAttributeValue = function (myxml,name,namespace) {
}
return ret;
}
+
+//
+// Can't this be, you know ... simplified?
+//
CSL.XmlDOM.prototype.getNodeValue = function (myxml,name) {
var ret = null;
if (name){
@@ -1676,6 +2153,7 @@ CSL.XmlDOM.prototype.getNodeValue = function (myxml,name) {
}
return ret;
}
+
CSL.XmlDOM.prototype.setAttributeOnNodeIdentifiedByNameAttribute = function (myxml,nodename,partname,attrname,val) {
var pos, len, xml, nodes, node;
if (attrname.slice(0,1) === '@'){
@@ -1690,6 +2168,7 @@ CSL.XmlDOM.prototype.setAttributeOnNodeIdentifiedByNameAttribute = function (myx
node.setAttribute(attrname, val);
}
}
+
CSL.XmlDOM.prototype.deleteNodeByNameAttribute = function (myxml,val) {
var pos, len, node, nodes;
nodes = myxml.childNodes;
@@ -1703,22 +2182,30 @@ CSL.XmlDOM.prototype.deleteNodeByNameAttribute = function (myxml,val) {
}
}
}
+
CSL.XmlDOM.prototype.deleteAttribute = function (myxml,attr) {
myxml.removeAttribute(attr);
}
+
CSL.XmlDOM.prototype.setAttribute = function (myxml,attr,val) {
if (!myxml.ownerDocument) {
myxml = myxml.firstChild;
}
+ // "unknown" to satisfy IE8, which crashes when setAttribute
+ // is checked directly as a property, and report its type as
+ // "unknown".
+ // Many thanks to Phil Lord for tracing the cause of the fault.
if (["function", "unknown"].indexOf(typeof myxml.setAttribute) > -1) {
myxml.setAttribute(attr, val);
}
return false;
}
+
CSL.XmlDOM.prototype.nodeCopy = function (myxml) {
var cloned_node = myxml.cloneNode(true);
return cloned_node;
}
+
CSL.XmlDOM.prototype.getNodesByName = function (myxml,name,nameattrval) {
var ret, nodes, node, pos, len;
ret = [];
@@ -1726,18 +2213,21 @@ CSL.XmlDOM.prototype.getNodesByName = function (myxml,name,nameattrval) {
for (pos = 0, len = nodes.length; pos < len; pos += 1) {
node = nodes.item(pos);
if (nameattrval && !(this.hasAttributes(node) && node.getAttribute("name") == nameattrval)) {
+// if (nameattrval && !(this.attributes && node.attributes.name && node.attributes.name.value == nameattrval)) {
continue;
}
ret.push(node);
}
return ret;
}
+
CSL.XmlDOM.prototype.nodeNameIs = function (myxml,name) {
if (name == myxml.nodeName) {
return true;
}
return false;
}
+
CSL.XmlDOM.prototype.makeXml = function (myxml) {
var ret, topnode;
if (!myxml) {
@@ -1747,12 +2237,14 @@ CSL.XmlDOM.prototype.makeXml = function (myxml) {
var nodetree = this.parser.parseFromString(myxml, "application/xml");
return nodetree.firstChild;
};
+
CSL.XmlDOM.prototype.insertChildNodeAfter = function (parent,node,pos,datexml) {
var myxml, xml;
myxml = this.importNode(node.ownerDocument, datexml);
parent.replaceChild(myxml, node);
return parent;
};
+
CSL.XmlDOM.prototype.insertPublisherAndPlace = function(myxml) {
var group = myxml.getElementsByTagName("group");
for (var i = 0, ilen = group.length; i < ilen; i += 1) {
@@ -1791,6 +2283,7 @@ CSL.XmlDOM.prototype.insertPublisherAndPlace = function(myxml) {
}
}
};
+
CSL.XmlDOM.prototype.isChildOfSubstitute = function(node) {
if (node.parentNode) {
if (node.parentNode.tagName.toLowerCase() === "substitute") {
@@ -1801,6 +2294,7 @@ CSL.XmlDOM.prototype.isChildOfSubstitute = function(node) {
}
return false;
};
+
CSL.XmlDOM.prototype.addMissingNameNodes = function(myxml) {
var nameslist = myxml.getElementsByTagName("names");
for (var i = 0, ilen = nameslist.length; i < ilen; i += 1) {
@@ -1808,12 +2302,15 @@ CSL.XmlDOM.prototype.addMissingNameNodes = function(myxml) {
var namelist = names.getElementsByTagName("name");
if ((!namelist || namelist.length === 0)
&& !this.isChildOfSubstitute(names)) {
+
var doc = names.ownerDocument;
var name = doc.createElement("name");
names.appendChild(name);
}
}
};
+
+
CSL.XmlDOM.prototype.addInstitutionNodes = function(myxml) {
var names, thenames, institution, theinstitution, theinstitutionpart, name, thename, xml, pos, len;
names = myxml.getElementsByTagName("names");
@@ -1851,6 +2348,8 @@ CSL.XmlDOM.prototype.addInstitutionNodes = function(myxml) {
}
}
};
+
+
CSL.XmlDOM.prototype.flagDateMacros = function(myxml) {
var pos, len, thenode, thedate;
var nodes = myxml.getElementsByTagName("macro");
@@ -1862,6 +2361,10 @@ CSL.XmlDOM.prototype.flagDateMacros = function(myxml) {
}
}
};
+
+
+/*global CSL: true */
+
CSL.setupXml = function(xmlObject) {
var dataObj = {};
var parser = null;
@@ -1870,26 +2373,34 @@ CSL.setupXml = function(xmlObject) {
xmlObject = xmlObject.replace("^\uFEFF", "")
.replace(/^\s+/, "");
if (xmlObject.slice(0, 1) === "<") {
+ // Assume serialized XML
dataObj = CSL.parseXml(xmlObject);
} else {
+ // Assume serialized JSON
dataObj = JSON.parse(xmlObject);
}
parser = new CSL.XmlJSON(dataObj);
} else if ("undefined" !== typeof xmlObject.getAttribute) {
+ // Assume DOM instance
parser = new CSL.XmlDOM(xmlObject);
} else if ("undefined" !== typeof xmlObject.toXMLString) {
+ // Assume E4X object
parser = new CSL.XmlE4X(xmlObject);
} else {
+ // Assume JS object
parser = new CSL.XmlJSON(xmlObject);
}
} else {
CSL.error("unable to parse XML input");
}
if (!parser) {
- throw "citeproc-js error: unable to parse CSL style or locale object";
+ CSL.error("citeproc-js error: unable to parse CSL style or locale object");
}
return parser;
};
+
+/*global CSL: true */
+
CSL.getSortCompare = function (default_locale) {
if (CSL.stringCompare) {
return CSL.stringCompare;
@@ -1900,10 +2411,16 @@ CSL.getSortCompare = function (default_locale) {
ignorePunctuation:true,
numeric:true
};
+ // In order, attempt the following:
+ // (1) Set locale collation from processor language
+ // (2) Use localeCompare()
if (!default_locale) {
default_locale = "en-US";
}
strcmp = function (a, b) {
+ //var ret = a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase(),default_locale,strcmp_opts);
+ // print(ret+' ('+a+') :: ('+b+')');
+ //return ret;
return a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase(),default_locale,strcmp_opts);
};
var stripPunct = function (str) {
@@ -1928,17 +2445,24 @@ CSL.getSortCompare = function (default_locale) {
};
return sortCompare;
};
+
+/*global CSL: true */
+
CSL.ambigConfigDiff = function(a, b) {
var pos, len, ppos, llen;
+ // return of true means the ambig configs differ
if (a.names.length !== b.names.length) {
+ //print(" (1)");
return 1;
} else {
for (pos = 0, len = a.names.length; pos < len; pos += 1) {
if (a.names[pos] !== b.names[pos]) {
+ //print(" (2) "+a.names[pos]+" "+b.names[pos]);
return 1;
} else {
for (ppos = 0, llen = a.givens[pos]; ppos < llen; ppos += 1) {
if (a.givens[pos][ppos] !== b.givens[pos][ppos]) {
+ //print(" (3): "+a.givens[pos][ppos]+" "+b.givens[pos][ppos]+" "+pos+"/"+ppos+" "+b.givens[pos]);
return 1;
}
}
@@ -1946,13 +2470,16 @@ CSL.ambigConfigDiff = function(a, b) {
}
}
if (a.disambiguate != b.disambiguate) {
+ //print(" (4) "+a.disambiguate+" "+b.disambiguate);
return 1;
}
if (a.year_suffix !== b.year_suffix) {
+ //print(" (5) "+a.year_suffix+" "+b.year_suffix);
return 1;
}
return 0;
};
+
CSL.cloneAmbigConfig = function (config, oldconfig) {
var i, ilen, j, jlen, param;
var ret = {};
@@ -1962,15 +2489,21 @@ CSL.cloneAmbigConfig = function (config, oldconfig) {
ret.disambiguate = false;
for (i = 0, ilen = config.names.length; i < ilen; i += 1) {
param = config.names[i];
+ // Fixes update bug affecting plugins, without impacting
+ // efficiency with update of large numbers of year-suffixed
+ // items.
ret.names[i] = param;
}
for (i = 0, ilen = config.givens.length; i < ilen; i += 1) {
param = [];
for (j = 0, jlen = config.givens[i].length; j < jlen; j += 1) {
+ // condition at line 312 of disambiguate.js protects against negative
+ // values of j
param.push(config.givens[i][j]);
}
ret.givens.push(param);
}
+ // XXXX Is this necessary at all?
if (oldconfig) {
ret.year_suffix = oldconfig.year_suffix;
ret.disambiguate = oldconfig.disambiguate;
@@ -1980,6 +2513,10 @@ CSL.cloneAmbigConfig = function (config, oldconfig) {
}
return ret;
};
+
+/**
+ * Return current base configuration for disambiguation
+ */
CSL.getAmbigConfig = function () {
var config, ret;
config = this.tmp.disambig_request;
@@ -1989,17 +2526,52 @@ CSL.getAmbigConfig = function () {
var ret = CSL.cloneAmbigConfig(config);
return ret;
};
+
+/**
+ * Return max values for disambiguation
+ */
CSL.getMaxVals = function () {
return this.tmp.names_max.mystack.slice();
};
+
+/**
+ * Return min value for disambiguation
+ */
CSL.getMinVal = function () {
return this.tmp["et-al-min"];
};
+
+/*global CSL: true */
+
+/* For node execution pretty-printing (see below) */
+
+/*
+var INDENT = "";
+*/
+
CSL.tokenExec = function (token, Item, item) {
+ // Called on state object
var next, maybenext, exec, debug;
debug = false;
next = token.next;
maybenext = false;
+
+ /* Pretty-print node executions */
+
+ /*
+ if (["if", "else-if", "else"].indexOf(token.name) === -1) {
+ if (token.tokentype == 1) {
+ INDENT = INDENT.slice(0, -2);
+ }
+ }
+ this.sys.print(INDENT + "---> Token: " + token.name + " (" + token.tokentype + ") in " + this.tmp.area + ", " + this.output.current.mystack.length);
+ if (["if", "else-if", "else"].indexOf(token.name) === -1) {
+ if (token.tokentype == 0) {
+ INDENT += " ";
+ }
+ }
+ */
+
var record = function (result) {
if (result) {
this.tmp.jump.replace("succeed");
@@ -2019,13 +2591,28 @@ CSL.tokenExec = function (token, Item, item) {
next = maybenext;
}
}
+ //SNIP-START
+ if (debug) {
+ CSL.debug(token.name + " (" + token.tokentype + ") ---> done");
+ }
+ //SNIP-END
return next;
};
+
+/**
+ * Macro expander.
+ *
Called on the state object.
+ */
CSL.expandMacro = function (macro_key_token, target) {
var mkey, macro_nodes, end_of_macro, func;
+
mkey = macro_key_token.postponed_macro;
+
var sort_direction = macro_key_token.strings.sort_direction;
+
+ // Decorations and affixes are in wrapper applied in cs:text
macro_key_token = new CSL.Token("group", CSL.START);
+
var hasDate = false;
var macroid = false;
macro_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, 'macro', mkey);
@@ -2042,20 +2629,28 @@ CSL.expandMacro = function (macro_key_token, target) {
};
macro_key_token.execs.push(func);
}
+
if (this.build.macro_stack.indexOf(mkey) > -1) {
- throw "CSL processor error: call to macro \"" + mkey + "\" would cause an infinite loop";
+ CSL.error("CSL processor error: call to macro \"" + mkey + "\" would cause an infinite loop");
} else {
this.build.macro_stack.push(mkey);
}
+
macro_key_token.cslid = macroid;
+
if (CSL.MODULE_MACROS[mkey]) {
macro_key_token.juris = mkey;
this.opt.update_mode = CSL.POSITION;
}
+ // Macro group is treated as a real node in the style
CSL.Node.group.build.call(macro_key_token, this, target, true);
+
+ // Node does not exist in the CSL
if (!this.cslXml.getNodeValue(macro_nodes)) {
- throw "CSL style error: undefined macro \"" + mkey + "\"";
+ CSL.error("CSL style error: undefined macro \"" + mkey + "\"");
}
+
+ // Let's macro
var mytarget = CSL.getMacroTarget.call(this, mkey);
if (mytarget) {
CSL.buildMacro.call(this, mytarget, macro_nodes);
@@ -2074,8 +2669,11 @@ CSL.expandMacro = function (macro_key_token, target) {
text_node.execs.push(func);
target.push(text_node);
}
+
+ // Decorations and affixes are in wrapper applied in cs:text
end_of_macro = new CSL.Token("group", CSL.END);
end_of_macro.strings.sort_direction = sort_direction;
+
if (hasDate) {
func = function (state) {
if (state.tmp.extension) {
@@ -2087,9 +2685,12 @@ CSL.expandMacro = function (macro_key_token, target) {
if (macro_key_token.juris) {
end_of_macro.juris = mkey;
}
+ // Macro group is treated as a real node in the style
CSL.Node.group.build.call(end_of_macro, this, target, true);
+
this.build.macro_stack.pop();
};
+
CSL.getMacroTarget = function (mkey) {
var mytarget = false;
if (this.build.extension) {
@@ -2100,6 +2701,7 @@ CSL.getMacroTarget = function (mkey) {
}
return mytarget;
};
+
CSL.buildMacro = function (mytarget, macro_nodes) {
var builder = CSL.makeBuilder(this, mytarget);
var mynode;
@@ -2110,14 +2712,33 @@ CSL.buildMacro = function (mytarget, macro_nodes) {
}
builder(mynode);
};
+
CSL.configureMacro = function (mytarget) {
if (!this.build.extension) {
this.configureTokenList(mytarget);
}
};
+
+
+/**
+ * Convert XML node to token.
+ *
This is called on an XML node. After extracting the name and attribute
+ * information from the node, it performs three operations. Attribute information
+ * relating to output formatting is stored on the node as an array of tuples,
+ * which fixes the sequence of execution of output functions to be invoked
+ * in the next phase of processing. Other attribute information is reduced
+ * to functions, and is pushed into an array on the token in no particular
+ * order, for later execution. The element name is used as a key to
+ * invoke the relevant build method of the target element.
+ * Element methods are defined in {@link CSL.Node}.
+ * @param {Object} state The state object returned by {@link CSL.Engine}.
+ * @param {Int} tokentype A CSL namespace constant (CSL.START,
+ * CSL.END or CSL.SINGLETON.
+ */
CSL.XmlToToken = function (state, tokentype, explicitTarget, var_stack) {
var name, txt, attrfuncs, attributes, decorations, token, key, target;
name = state.cslXml.nodename(this);
+ //CSL.debug(tokentype + " : " + name);
if (state.build.skip && state.build.skip !== name) {
return;
}
@@ -2129,13 +2750,16 @@ CSL.XmlToToken = function (state, tokentype, explicitTarget, var_stack) {
return;
}
if (!CSL.Node[state.cslXml.nodename(this)]) {
- throw "Undefined node name \"" + name + "\".";
+ CSL.error("Undefined node name \"" + name + "\".");
}
attrfuncs = [];
attributes = state.cslXml.attributes(this);
decorations = CSL.setDecorations.call(this, state, attributes);
token = new CSL.Token(name, tokentype);
if (tokentype !== CSL.END || name === "if" || name === "else-if" || name === "layout") {
+ //
+ // xml: more xml stuff
+ //
for (var key in attributes) {
if (attributes.hasOwnProperty(key)) {
if (tokentype === CSL.END && key !== "@language" && key !== "@locale") {
@@ -2146,8 +2770,7 @@ CSL.XmlToToken = function (state, tokentype, explicitTarget, var_stack) {
try {
CSL.Attributes[key].call(token, state, "" + attributes[key]);
} catch (e) {
- CSL.error(e);
- throw "CSL processor error, " + key + " attribute: " + e;
+ CSL.error(key + " attribute: " + e);
}
} else {
CSL.debug("warning: undefined attribute \""+key+"\" in style");
@@ -2165,26 +2788,47 @@ CSL.XmlToToken = function (state, tokentype, explicitTarget, var_stack) {
token.variables = var_stack.pop();
}
}
+ //
+ // !!!!!: eliminate diversion of tokens to separate
+ // token list (formerly used for reading in macros
+ // and terms).
+ //
if (explicitTarget) {
target = explicitTarget;
} else {
target = state[state.build.area].tokens;
}
+ // True flags real nodes in the style
CSL.Node[name].build.call(token, state, target, true);
};
+
+
+
+/*global CSL: true */
+
+
CSL.DateParser = function () {
+
+ /*
+ * Fixed values
+ */
+
+ // jse imperial years
var epochPairs = [
["\u660E\u6CBB", 1867],
["\u5927\u6B63", 1911],
["\u662D\u548C", 1925],
["\u5E73\u6210", 1988]
];
+
+ // years by jse imperial epoch
var epochYearByName = {};
for (var i=0,ilen=epochPairs.length; i lst[i].length || abbrevLength > this.monthSets[j][k].length) {
CSL.debug("unable to disambiguate month string in date parser: "+lst[i]);
break;
} else {
+ // Mark both new entry and existing abbrev for extension
abbrevLength += 1;
}
}
@@ -2284,11 +2971,14 @@ CSL.DateParser = function () {
}
}
}
+ // Insert here
if (!skip) {
this.monthSets[i].push(lst[i]);
this.monthAbbrevs[i].push(lst[i].slice(0, insert));
}
}
+
+ // Compose
this.monthRexes = [];
this.monthRexStrs = [];
for (var i=0,ilen=this.monthAbbrevs.length; i -1) {
this._parseNumericDate(thedate, dateDelim, suff, element);
continue;
}
+ //
+ // If it's an obvious year, record it.
+ //
if (element.match(/[0-9]{4}/)) {
thedate[("year" + suff)] = element.replace(/^0*/, "");
continue;
}
+ //
+ // If it's a fuzzy marker, record it.
+ //
if (element === "~" || element === "?" || element === "c" || element.match(/^cir/)) {
thedate.circa = true;
}
+ //
+ // If it's a month, record it.
+ //
for (var k=0,klen=this.monthRexes.length; k -1) {
thedate.year = (thedate.year * -1);
}
return thedate;
};
+
this.parseDateToArray = function(txt) {
return this.convertDateObjectToArray(this.parseDateToObject(txt));
};
+
this.parseDateToString = function(txt) {
return this.convertDateObjectToString(this.parseDateToObject(txt));
};
+
this.parse = function(txt) {
return this.parseDateToObject(txt);
};
+
+ /*
+
+ * Setup
+ */
+
this.setOrderMonthDay();
this.resetDateParserMonths();
};
CSL.DateParser = new CSL.DateParser();
+
+/*global CSL: true */
+
CSL.Engine = function (sys, style, lang, forceLang) {
var attrs, langspec;
this.processor_version = CSL.PROCESSOR_VERSION;
this.csl_version = "1.0";
this.sys = sys;
+
if (typeof Object.assign != 'function') {
+ // Must be writable: true, enumerable: false, configurable: true
Object.defineProperty(Object, "assign", {
value: function assign(target) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}
+
var to = Object(target);
+
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
+
if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
+ // Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
@@ -2595,9 +3394,14 @@ CSL.Engine = function (sys, style, lang, forceLang) {
configurable: true
});
}
+
+ // XXX This may be excess code. Given the normalization performed on
+ // XXX the output queue before variableWrapper() is run, a single
+ // XXX space should be the most cruft that we ever see before a variable.
if (sys.variableWrapper) {
CSL.VARIABLE_WRAPPER_PREPUNCT_REX = new RegExp('^([' + [" "].concat(CSL.SWAPPING_PUNCTUATION).join("") + ']*)(.*)');
}
+ // XXXX This should be restored -- temporarily suspended for testing of JSON style support.
if (CSL.retrieveStyleModule) {
this.sys.retrieveStyleModule = CSL.retrieveStyleModule;
}
@@ -2609,28 +3413,46 @@ CSL.Engine = function (sys, style, lang, forceLang) {
}
this.sys.AbbreviationSegments = CSL.AbbreviationSegments;
this.parallel = new CSL.Parallel(this);
+ //this.parallel.use_parallels = true;
+
this.transform = new CSL.Transform(this);
+ // true or false
this.setParseNames = function (val) {
this.opt['parse-names'] = val;
};
+
this.opt = new CSL.Engine.Opt();
this.tmp = new CSL.Engine.Tmp();
this.build = new CSL.Engine.Build();
this.fun = new CSL.Engine.Fun(this);
+
this.configure = new CSL.Engine.Configure();
+ // Build citation before citation_sort in order to pick up
+ // state.opt.update_mode, needed it determine whether
+ // a grouped sort should be performed.
this.citation_sort = new CSL.Engine.CitationSort();
this.bibliography_sort = new CSL.Engine.BibliographySort();
this.citation = new CSL.Engine.Citation(this);
this.bibliography = new CSL.Engine.Bibliography();
this.intext = new CSL.Engine.InText();
+
this.output = new CSL.Output.Queue(this);
+
+ //this.render = new CSL.Render(this);
+ //
+ // This latter queue is used for formatting date chunks
+ // before they are folded back into the main queue.
+ //
this.dateput = new CSL.Output.Queue(this);
+
this.cslXml = CSL.setupXml(style);
+
for (var i in CSL.SYS_OPTIONS) {
var option = CSL.SYS_OPTIONS[i];
if ("boolean" === typeof this.sys[option]) {
this.opt.development_extensions[option] = this.sys[option];
}
+
}
if (this.opt.development_extensions.uppercase_subtitles) {
this.opt.development_extensions.main_title_from_short_title = true;
@@ -2651,6 +3473,7 @@ CSL.Engine = function (sys, style, lang, forceLang) {
};
this.setCslNodeIds(this.cslXml.dataObj, "style");
}
+ // Preprocessing ops for the XML input
this.cslXml.addMissingNameNodes(this.cslXml.dataObj);
this.cslXml.addInstitutionNodes(this.cslXml.dataObj);
this.cslXml.insertPublisherAndPlace(this.cslXml.dataObj);
@@ -2659,12 +3482,23 @@ CSL.Engine = function (sys, style, lang, forceLang) {
if ("undefined" === typeof attrs["@sort-separator"]) {
this.cslXml.setAttribute(this.cslXml.dataObj, "sort-separator", ", ");
}
+ // This setting does the right thing and seems not to be side-effects
this.opt["initialize-with-hyphen"] = true;
+
+ // Locale resolution
+ //
+ // (1) Get three locale strings
+ // -- default-locale (stripped)
+ // -- processor-locale
+ // -- en_US
+
this.setStyleAttributes();
+
this.opt.xclass = this.cslXml.getAttributeValue(this.cslXml.dataObj, "class");
this.opt["class"] = this.opt.xclass;
this.opt.styleID = this.cslXml.getStyleId(this.cslXml.dataObj);
this.opt.styleName = this.cslXml.getStyleId(this.cslXml.dataObj, true);
+
if (this.opt.version.slice(0,4) === "1.1m") {
this.opt.development_extensions.static_statute_locator = true;
this.opt.development_extensions.handle_parallel_articles = true;
@@ -2673,6 +3507,10 @@ CSL.Engine = function (sys, style, lang, forceLang) {
this.opt.development_extensions.require_explicit_legal_case_title_short = true;
this.opt.development_extensions.force_jurisdiction = true;
}
+ // We seem to have two language specs flying around:
+ // this.opt["default-locale"], and this.opt.lang
+ // Keeping them aligned for safety's sake, pending
+ // eventual cleanup.
if (lang) {
lang = lang.replace("_", "-");
lang = CSL.normalizeLocaleStr(lang);
@@ -2703,43 +3541,82 @@ CSL.Engine = function (sys, style, lang, forceLang) {
if (!this.opt["default-locale-sort"]) {
this.opt["default-locale-sort"] = this.opt["default-locale"][0];
}
+ // Test processor against JS engine locale mess to find a field separator that works
if ('dale|'.localeCompare('daleb', this.opt["default-locale-sort"]) > -1) {
this.opt.sort_sep = "@";
} else {
this.opt.sort_sep = "|";
}
this.localeConfigure(langspec);
+
+ // Build skip-word regexp
function makeRegExp(lst) {
var lst = lst.slice();
var ret = new RegExp( "(?:(?:[?!:]*\\s+|-|^)(?:" + lst.join("|") + ")(?=[!?:]*\\s+|-|$))", "g");
return ret;
}
this.locale[this.opt.lang].opts["skip-words-regexp"] = makeRegExp(this.locale[this.opt.lang].opts["skip-words"]);
+
this.output.adjust = new CSL.Output.Queue.adjust(this.getOpt('punctuation-in-quote'));
+
this.registry = new CSL.Registry(this);
+
+ // XXX For modular jurisdiction support, parameterize buildTokenLists().
+ // XXX Feed as arguments:
+ // XXX * actual node to be walked (cslXml)
+ // XXX * actual target array
+
this.macros = {};
+
this.build.area = "citation";
var area_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, this.build.area);
this.buildTokenLists(area_nodes, this[this.build.area].tokens);
+
this.build.area = "bibliography";
var area_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, this.build.area);
this.buildTokenLists(area_nodes, this[this.build.area].tokens);
+
this.build.area = "intext";
var area_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, this.build.area);
this.buildTokenLists(area_nodes, this[this.build.area].tokens);
+
this.juris = {};
+
this.configureTokenLists();
+
this.disambiguate = new CSL.Disambiguation(this);
+
this.splice_delimiter = false;
+
+ //
+ // date parser
+ //
this.fun.dateparser = CSL.DateParser;
+ //
+ // flip-flopper for inline markup
+ //
this.fun.flipflopper = new CSL.Util.FlipFlopper(this);
+ //
+ // utility functions for quotes
+ //
this.setCloseQuotesArray();
+ //
+ // configure ordinal numbers generator
+ //
this.fun.ordinalizer.init(this);
+ //
+ // configure long ordinal numbers generator
+ //
this.fun.long_ordinalizer.init(this);
+ //
+ // set up page mangler
+ //
this.fun.page_mangler = CSL.Util.PageRangeMangler.getFunction(this, "page");
this.fun.year_mangler = CSL.Util.PageRangeMangler.getFunction(this, "year");
+
this.setOutputFormat("html");
};
+
CSL.Engine.prototype.setCloseQuotesArray = function () {
var ret;
ret = [];
@@ -2749,6 +3626,8 @@ CSL.Engine.prototype.setCloseQuotesArray = function () {
ret.push("'");
this.opt.close_quotes_array = ret;
};
+
+// Walker for preparsed XML input
CSL.makeBuilder = function (me, target) {
var var_stack = [];
var node_stack = [];
@@ -2793,6 +3672,8 @@ CSL.makeBuilder = function (me, target) {
}
return buildStyle;
};
+
+
CSL.Engine.prototype.buildTokenLists = function (area_nodes, target) {
if (!this.cslXml.getNodeValue(area_nodes)) {
return;
@@ -2806,17 +3687,29 @@ CSL.Engine.prototype.buildTokenLists = function (area_nodes, target) {
}
builder(mynode);
};
+
+
CSL.Engine.prototype.setStyleAttributes = function () {
var dummy, attributes, attrname;
+ // Protect against DOM engines that deliver a top-level document
+ // (needed for createElement) that does not contain our top-level node.
+ //
+ // The string coercion on this.cslXml.tagName addresses a bizarre
+ // condition on the top-level node in jsdom running under node.js, in which:
+ // (1) typeof this.cslXml.tagName === "undefined"; and
+ // (2) !this.cslXml.tagName === false
+ // Coerced, it becomes an empty string.
var dummy = {};
dummy.name = this.cslXml.nodename(this.cslXml.dataObj);
attributes = this.cslXml.attributes(this.cslXml.dataObj);
for (attrname in attributes) {
if (attributes.hasOwnProperty(attrname)) {
+ // attr = attributes[key];
CSL.Attributes[attrname].call(dummy, this, attributes[attrname]);
}
}
};
+
CSL.Engine.prototype.getTerm = function (term, form, plural, gender, mode, forceDefaultLocale) {
if (term && term.match(/[A-Z]/) && term === term.toUpperCase()) {
CSL.debug("Warning: term key is in uppercase form: "+term);
@@ -2829,12 +3722,14 @@ CSL.Engine.prototype.getTerm = function (term, form, plural, gender, mode, force
lang = this.opt.lang;
}
var ret = CSL.Engine.getField(CSL.LOOSE, this.locale[lang].terms, term, form, plural, gender);
+ // XXXXX Temporary, until locale term is deployed in CSL.
if (!ret && term === "range-delimiter") {
ret = "\u2013";
}
+ // XXXXX Not so good if mode is neither strict nor tolerant ...
if (typeof ret === "undefined") {
if (mode === CSL.STRICT) {
- throw "Error in getTerm: term \"" + term + "\" does not exist.";
+ CSL.error("Error in getTerm: term \"" + term + "\" does not exist.");
} else if (mode === CSL.TOLERANT) {
ret = "";
}
@@ -2844,6 +3739,7 @@ CSL.Engine.prototype.getTerm = function (term, form, plural, gender, mode, force
}
return ret;
};
+
CSL.Engine.prototype.getDate = function (form, forceDefaultLocale) {
var lang;
if (forceDefaultLocale) {
@@ -2857,6 +3753,7 @@ CSL.Engine.prototype.getDate = function (form, forceDefaultLocale) {
return false;
}
};
+
CSL.Engine.prototype.getOpt = function (arg) {
if ("undefined" !== typeof this.locale[this.opt.lang].opts[arg]) {
return this.locale[this.opt.lang].opts[arg];
@@ -2864,9 +3761,13 @@ CSL.Engine.prototype.getOpt = function (arg) {
return false;
}
};
+
+
+
CSL.Engine.prototype.getVariable = function (Item, varname, form, plural) {
return CSL.Engine.getField(CSL.LOOSE, Item, varname, form, plural);
};
+
CSL.Engine.prototype.getDateNum = function (ItemField, partname) {
if ("undefined" === typeof ItemField) {
return 0;
@@ -2874,12 +3775,13 @@ CSL.Engine.prototype.getDateNum = function (ItemField, partname) {
return ItemField[partname];
}
};
+
CSL.Engine.getField = function (mode, hash, term, form, plural, gender) {
var ret, forms, f, pos, len, hashterm;
ret = "";
if ("undefined" === typeof hash[term]) {
if (mode === CSL.STRICT) {
- throw "Error in getField: term \"" + term + "\" does not exist.";
+ CSL.error("Error in getField: term \"" + term + "\" does not exist.");
} else {
return undefined;
}
@@ -2918,10 +3820,13 @@ CSL.Engine.getField = function (mode, hash, term, form, plural, gender) {
}
return ret;
};
+
CSL.Engine.prototype.configureTokenLists = function () {
var area, pos, len;
+ //for each (var area in ["citation", "citation_sort", "bibliography","bibliography_sort"]) {
len = CSL.AREAS.length;
for (pos = 0; pos < len; pos += 1) {
+ //var ret = [];
area = CSL.AREAS[pos];
var tokens = this[area].tokens;
this.configureTokenList(tokens);
@@ -2929,12 +3834,15 @@ CSL.Engine.prototype.configureTokenLists = function () {
this.version = CSL.version;
return this.state;
};
+
CSL.Engine.prototype.configureTokenList = function (tokens) {
var dateparts_master, token, dateparts, part, ppos, pppos, llen, lllen;
dateparts_master = ["year", "month", "day"];
llen = tokens.length - 1;
for (ppos = llen; ppos > -1; ppos += -1) {
token = tokens[ppos];
+ //token.pos = ppos;
+ //ret.push(token);
if ("date" === token.name && CSL.END === token.tokentype) {
dateparts = [];
}
@@ -2957,6 +3865,7 @@ CSL.Engine.prototype.configureTokenList = function (tokens) {
}
}
};
+
CSL.Engine.prototype.refetchItems = function (ids) {
var ret = [];
for (var i = 0, ilen = ids.length; i < ilen; i += 1) {
@@ -2964,16 +3873,24 @@ CSL.Engine.prototype.refetchItems = function (ids) {
}
return ret;
};
+
CSL.ITERATION = 0;
+
+// Wrapper for sys.retrieveItem supplied by calling application.
+// Adds experimental fields embedded in the note field for
+// style development trial and testing purposes.
CSL.Engine.prototype.retrieveItem = function (id) {
var Item, m, i;
+
if (!this.tmp.loadedItemIDs[id]) {
this.tmp.loadedItemIDs[id] = true;
} else {
return this.registry.refhash[id];
}
+
if (this.opt.development_extensions.normalize_lang_keys_to_lowercase &&
"boolean" === typeof this.opt.development_extensions.normalize_lang_keys_to_lowercase) {
+ // This is a hack. Should properly be configured by a processor method after build.
for (var i=0,ilen=this.opt["default-locale"].length; i<]/)) {
+ // Attempt to split field in two
var m = Item.language.match(/(.*?)([<>])(.*)/);
if (m[2] === "<") {
Item["language-name"] = m[1];
@@ -3046,6 +3971,7 @@ CSL.Engine.prototype.retrieveItem = function (id) {
}
}
}
+
if (Item.page) {
Item["page-first"] = Item.page;
var num = "" + Item.page;
@@ -3054,13 +3980,17 @@ CSL.Engine.prototype.retrieveItem = function (id) {
Item["page-first"] = m[0];
}
}
+ // Optional development extensions
if (this.opt.development_extensions.field_hack && Item.note) {
+ // false is for validFieldsForType (all conforming entries scrubbed when false)
CSL.parseNoteFieldHacks(Item, false, this.opt.development_extensions.allow_field_hack_date_override);
}
+ // not including locator-date
for (var key in Item) {
if (CSL.DATE_VARIABLES.indexOf(key.replace(/^alt-/, "")) > -1) {
var dateobj = Item[key];
if (dateobj) {
+ // raw date parsing is harmless, but can be disabled if desired
if (this.opt.development_extensions.raw_date_parsing) {
if (dateobj.raw && (!dateobj["date-parts"] || dateobj["date-parts"].length === 0)) {
dateobj = this.fun.dateparser.parseDateToObject(dateobj.raw);
@@ -3072,6 +4002,7 @@ CSL.Engine.prototype.retrieveItem = function (id) {
}
if (this.opt.development_extensions.static_statute_locator) {
if (Item.type && ["bill","gazette","legislation","regulation","treaty"].indexOf(Item.type) > -1) {
+
var varname;
var elements = ["type", "title", "jurisdiction", "genre", "volume", "container-title"];
var legislation_id = [];
@@ -3093,6 +4024,7 @@ CSL.Engine.prototype.retrieveItem = function (id) {
Item.legislation_id = legislation_id.join("::");
}
}
+ // For authority to name shape in legal styles
if (this.opt.development_extensions.force_jurisdiction) {
if ("string" === typeof Item.authority) {
Item.authority = [
@@ -3113,11 +4045,14 @@ CSL.Engine.prototype.retrieveItem = function (id) {
}
}
}
+ // Add getAbbreviation() call for title-short and container-title-short
if (!Item["title-short"]) {
Item["title-short"] = Item.shortTitle;
}
+ // Add support for main_title_from_short_title
if (this.opt.development_extensions.main_title_from_short_title) {
- CSL.extractTitleAndSubtitle(Item);
+ var narrowSpaceLocale = this.opt["default-locale"][0].slice(0, 2).toLowerCase() === "fr";
+ CSL.extractTitleAndSubtitle.call(this, Item, narrowSpaceLocale);
}
var isLegalType = ["bill","legal_case","legislation","gazette","regulation"].indexOf(Item.type) > -1;
if (this.opt.development_extensions.force_jurisdiction && isLegalType) {
@@ -3175,12 +4110,16 @@ CSL.Engine.prototype.retrieveItem = function (id) {
}
return this.registry.refhash[id];
};
+
CSL.Engine.prototype.refetchItem = function (id) {
return this.registry.refhash[id];
};
+
CSL.Engine.prototype.refetchItem = function (id) {
return this.registry.refhash[id];
}
+
+// Executed during style build
CSL.Engine.prototype.setOpt = function (token, name, value) {
if (token.name === "style" || token.name === "cslstyle") {
this.opt.inheritedAttributes[name] = value;
@@ -3192,6 +4131,8 @@ CSL.Engine.prototype.setOpt = function (token, name, value) {
token.strings[name] = value;
}
};
+
+// Executed at runtime, since macros can occur in the context of citation or bibliography
CSL.Engine.prototype.inheritOpt = function (token, attrname, parentname, defaultValue) {
if ("undefined" !== typeof token.strings[attrname]) {
return token.strings[attrname];
@@ -3204,11 +4145,24 @@ CSL.Engine.prototype.inheritOpt = function (token, attrname, parentname, default
}
}
};
+
CSL.Engine.prototype.remapSectionVariable = function (inputList) {
+ // We have items with a value in the section field (on Item) that must
+ // be mapped to the locator field (on item). We simply prepend it as
+ // a string here, and handle all parsing of the resulting string
+ // in processNumber(). Plurals and numeric are set in processNumber().
+
+ // Because the target is in the citation item (lowercase), the
+ // remapping cannot take place when the Item data is received.
+ // Citation provides a list of Item/item pairs, hence the iteration
+ // used here.
for (var i = 0, ilen = inputList.length; i < ilen; i += 1) {
var Item = inputList[i][0];
var item = inputList[i][1];
+
if (["bill","gazette","legislation","regulation","treaty"].indexOf(Item.type) > -1) {
+ // If a locator value exists, then
+ // (a) Leave be an overriding label at the start of the locator field, defaulting to label value
if (item.locator) {
item.locator = item.locator.trim();
var m = item.locator.match(CSL.STATUTE_SUBDIV_PLAIN_REGEX_FRONT);
@@ -3220,6 +4174,8 @@ CSL.Engine.prototype.remapSectionVariable = function (inputList) {
}
}
}
+ // If a section value exists, then
+ // (a) Apply an overriding label at the start of the section field, defaulting to sec.
var sectionMasterLabel = null;
if (Item.section) {
Item.section = Item.section.trim();
@@ -3231,10 +4187,17 @@ CSL.Engine.prototype.remapSectionVariable = function (inputList) {
sectionMasterLabel = m[0].trim();
}
}
+ // If section is nil, then
+ // (a) Do nothing
if (Item.section) {
+ // If section exists and locator is nil
+ // (a) Set section string in locator field
if (!item.locator) {
item.locator = Item.section;
} else {
+ // If both section and locator exist, then
+ // (a) If locator starts with p., remove p., merge with space or no-space, and set in locator field
+ // (b) If locator starts with non-p., prepend section value to locator with space, and set in locator field
var m = item.locator.match(/^([^ ]*)\s*(.*)/);
var space = " ";
if (m) {
@@ -3249,32 +4212,43 @@ CSL.Engine.prototype.remapSectionVariable = function (inputList) {
}
item.locator = Item.section + space + item.locator;
}
+ //Item.section = "";
}
item.label = "";
+ // And that's it. Pre-parse complete.
}
}
};
+
+
CSL.Engine.prototype.setNumberLabels = function (Item) {
if (Item.number
&& ["bill", "gazette", "legislation","regulation","treaty"].indexOf(Item.type) > -1
&& this.opt.development_extensions.static_statute_locator
&& !this.tmp.shadow_numbers["number"]) {
+
this.tmp.shadow_numbers["number"] = {};
this.tmp.shadow_numbers["number"].values = [];
this.tmp.shadow_numbers["number"].plural = 0;
this.tmp.shadow_numbers["number"].numeric = false;
this.tmp.shadow_numbers["number"].label = false;
+
+ // Labels embedded in number variable
var value = "" + Item.number;
value = value.split("\\").join("");
+ // Get first word, parse out labels only if it parses
var firstword = value.split(/\s+/)[0];
var firstlabel = CSL.STATUTE_SUBDIV_STRINGS[firstword];
if (firstlabel) {
+ // Get list and match
var splt = value.split(CSL.STATUTE_SUBDIV_PLAIN_REGEX);
if (splt.length > 1) {
+ // Convert matches to localized form
var lst = [];
for (var j=1, jlen=splt.length; j < jlen; j += 1) {
lst.push(splt[j].replace(/\s*$/, "").replace(/^\s*/, ""));
}
+ // Preemptively save to shadow_numbers
value = lst.join(" ");
} else {
value = splt[0];
@@ -3288,15 +4262,32 @@ CSL.Engine.prototype.setNumberLabels = function (Item) {
}
}
};
+
+/*global CSL: true */
+
CSL.substituteOne = function (template) {
return function (state, list) {
if (!list) {
return "";
} else {
+ // ("string" === typeof list)
return template.replace("%%STRING%%", list);
}
};
};
+
+
+/**
+ * Two-tiered substitutions gadget.
+ *
This is used for
+ * options like (now defunct) "font-family", where the option value
+ * cannot be determined until the attribute is processed.
+ * Need for this function might be reviewed at some point ...
+ * @param {String} template A template containing
+ * %%STRING%% and %%PARAM%%
+ * placeholders. See {@link CSL.Output.Formats.html} for
+ * examples.
+ */
CSL.substituteTwo = function (template) {
return function (param) {
var template2 = template.replace("%%PARAM%%", param);
@@ -3304,17 +4295,25 @@ CSL.substituteTwo = function (template) {
if (!list) {
return "";
} else {
+ //("string" === typeof list){
return template2.replace("%%STRING%%", list);
}
};
};
};
+
+/**
+ * Generate string functions for designated output mode.
+ *
Only "html" (the default) is supported at present.
+ * @param {String} mode Either "html" or "rtf", eventually.
+ */
CSL.Mode = function (mode) {
var decorations, params, param, func, val, args;
decorations = {};
params = CSL.Output.Formats[mode];
for (param in params) {
if (true) {
+
if ("@" !== param.slice(0, 1)) {
decorations[param] = params[param];
continue;
@@ -3322,6 +4321,7 @@ CSL.Mode = function (mode) {
func = false;
val = params[param];
args = param.split('/');
+
if (typeof val === "string" && val.indexOf("%%STRING%%") > -1) {
if (val.indexOf("%%PARAM%%") > -1) {
func = CSL.substituteTwo(val);
@@ -3333,8 +4333,9 @@ CSL.Mode = function (mode) {
} else if (typeof val === "function") {
func = val;
} else {
- throw "CSL.Compiler: Bad " + mode + " config entry for " + param + ": " + val;
+ CSL.error("Bad " + mode + " config entry for " + param + ": " + val);
}
+
if (args.length === 1) {
decorations[args[0]] = func;
} else if (args.length === 2) {
@@ -3347,8 +4348,24 @@ CSL.Mode = function (mode) {
}
return decorations;
};
+
+
+/**
+ * Generate a separate list of formatting attributes.
+ *
This generates a list of tuples containing attribute
+ * information relevant to output formatting, in the order
+ * fixed in the constant {@link CSL.FORMAT_KEY_SEQUENCE}.
+ * This function is called during {@link CSL.Core.Build}.
+ * Formatting hints are distilled to functions
+ * later, in the second compilation pass ({@link CSL.Core.Configure}).
+ * @param {Object} state The state object returned by
+ * {@link CSL.Engine}.
+ * @param {Object} attributes The hash object containing
+ * the attributes and values extracted from an XML node.
+ */
CSL.setDecorations = function (state, attributes) {
var ret, key, pos;
+ // This applies a fixed processing sequence
ret = [];
for (pos in CSL.FORMAT_KEY_SEQUENCE) {
if (true) {
@@ -3361,10 +4378,12 @@ CSL.setDecorations = function (state, attributes) {
}
return ret;
};
+
CSL.Doppeler = function(rexStr, stringMangler) {
var matchRex = new RegExp("(" + rexStr + ")", "g");
var splitRex = new RegExp(rexStr, "g");
this.split = function (str) {
+ // Normalize markup
if (stringMangler) {
str = stringMangler(str);
}
@@ -3382,6 +4401,7 @@ CSL.Doppeler = function(rexStr, stringMangler) {
}
var tag = match[i];
if (tag === "\'" && split[i+1].length > 0) {
+ // Fixes https://forums.zotero.org/discussion/comment/294317
split[i+1] = match[i] + split[i+1];
match[i] = "";
}
@@ -3402,7 +4422,9 @@ CSL.Doppeler = function(rexStr, stringMangler) {
return lst.join("");
};
};
+
CSL.Engine.prototype.normalDecorIsOrphan = function (blob, params) {
+ //print("params: "+JSON.stringify(params));
if (params[1] === "normal") {
var use_param = false;
var all_the_decor;
@@ -3412,8 +4434,11 @@ CSL.Engine.prototype.normalDecorIsOrphan = function (blob, params) {
all_the_decor = blob.alldecor;
}
for (var k = all_the_decor.length - 1; k > -1; k += -1) {
+ //print(" all decor: "+JSON.stringify(all_the_decor[k]));
for (var n = all_the_decor[k].length - 1; n > -1; n += -1) {
+ //print(" superior param"+n+": "+all_the_decor[k][n][0]);
if (all_the_decor[k][n][0] === params[0]) {
+ //print(" HIT!");
if (all_the_decor[k][n][1] !== "normal") {
use_param = true;
}
@@ -3426,6 +4451,10 @@ CSL.Engine.prototype.normalDecorIsOrphan = function (blob, params) {
}
return false;
};
+
+/*global CSL: true */
+
+
CSL.Engine.prototype.getCitationLabel = function (Item) {
var label = "";
var params = this.getTrigraphParams();
@@ -3454,6 +4483,7 @@ CSL.Engine.prototype.getCitationLabel = function (Item) {
if (name && name.family) {
myname = name.family;
myname = myname.replace(/^([ \'\u2019a-z]+\s+)/, "");
+
} else if (name && name.literal) {
myname = name.literal;
}
@@ -3477,6 +4507,7 @@ CSL.Engine.prototype.getCitationLabel = function (Item) {
}
}
if (!label) {
+ // Try for something using title
if (Item.title) {
var skipWords = this.locale[this.opt.lang].opts["skip-words"];
var lst = Item.title.split(/\s+/);
@@ -3505,11 +4536,12 @@ CSL.Engine.prototype.getCitationLabel = function (Item) {
label = label + year;
return label;
};
+
CSL.Engine.prototype.getTrigraphParams = function () {
var params = [];
var ilst = this.opt.trigraph.split(":");
if (!this.opt.trigraph || this.opt.trigraph.slice(0,1) !== "A") {
- throw "Bad trigraph definition: "+this.opt.trigraph;
+ CSL.error("Bad trigraph definition: "+this.opt.trigraph);
}
for (var i = 0, ilen = ilst.length; i < ilen; i += 1) {
var str = ilst[i];
@@ -3526,13 +4558,16 @@ CSL.Engine.prototype.getTrigraphParams = function () {
config.year += 1;
break;
default:
- throw "Invalid character in trigraph definition: "+this.opt.trigraph;
+ CSL.error("Invalid character in trigraph definition: "+this.opt.trigraph);
}
}
params.push(config);
}
return params;
};
+
+/*global CSL: true */
+
CSL.Engine.prototype.setOutputFormat = function (mode) {
this.opt.mode = mode;
this.fun.decorate = CSL.Mode(mode);
@@ -3541,6 +4576,7 @@ CSL.Engine.prototype.setOutputFormat = function (mode) {
this.output[mode].tmp = {};
}
};
+
CSL.Engine.prototype.getSortFunc = function () {
return function (a,b) {
a = a.split("-");
@@ -3562,6 +4598,7 @@ CSL.Engine.prototype.getSortFunc = function () {
}
};
};
+
CSL.Engine.prototype.setLangTagsForCslSort = function (tags) {
var i, ilen;
if (tags) {
@@ -3572,6 +4609,7 @@ CSL.Engine.prototype.setLangTagsForCslSort = function (tags) {
}
this.opt['locale-sort'].sort(this.getSortFunc());
};
+
CSL.Engine.prototype.setLangTagsForCslTransliteration = function (tags) {
var i, ilen;
this.opt['locale-translit'] = [];
@@ -3582,6 +4620,7 @@ CSL.Engine.prototype.setLangTagsForCslTransliteration = function (tags) {
}
this.opt['locale-translit'].sort(this.getSortFunc());
};
+
CSL.Engine.prototype.setLangTagsForCslTranslation = function (tags) {
var i, ilen;
this.opt['locale-translat'] = [];
@@ -3592,6 +4631,7 @@ CSL.Engine.prototype.setLangTagsForCslTranslation = function (tags) {
}
this.opt['locale-translat'].sort(this.getSortFunc());
};
+
CSL.Engine.prototype.setLangPrefsForCites = function (obj, conv) {
var opt = this.opt['cite-lang-prefs'];
if (!conv) {
@@ -3600,12 +4640,17 @@ CSL.Engine.prototype.setLangPrefsForCites = function (obj, conv) {
};
}
var segments = ['Persons', 'Institutions', 'Titles', 'Journals', 'Publishers', 'Places'];
+ // Set values in place
for (var i = 0, ilen = segments.length; i < ilen; i += 1) {
var clientSegment = conv(segments[i]);
var citeprocSegment = segments[i].toLowerCase();
if (!obj[clientSegment]) {
continue;
}
+ //
+ // Normalize the sequence of secondary and tertiary
+ // in the provided obj segment list.
+ //
var supplements = [];
while (obj[clientSegment].length > 1) {
supplements.push(obj[clientSegment].pop());
@@ -3617,6 +4662,9 @@ CSL.Engine.prototype.setLangPrefsForCites = function (obj, conv) {
while (supplements.length) {
obj[clientSegment].push(supplements.pop());
}
+ //
+ // normalization done.
+ //
var lst = opt[citeprocSegment];
while (lst.length) {
lst.pop();
@@ -3626,6 +4674,7 @@ CSL.Engine.prototype.setLangPrefsForCites = function (obj, conv) {
}
}
};
+
CSL.Engine.prototype.setLangPrefsForCiteAffixes = function (affixList) {
if (affixList && affixList.length === 48) {
var affixes = this.opt.citeAffixes;
@@ -3639,6 +4688,7 @@ CSL.Engine.prototype.setLangPrefsForCiteAffixes = function (affixList) {
if ((count % 8) === 4) {
if (!affixes[settings[i]]["locale-"+forms[j]].prefix
&& !affixes[settings[i]]["locale-"+forms[j]].suffix) {
+
value = affixList[count] ? affixList[count] : "";
affixes[settings[i]]["locale-" + forms[j]].prefix = value;
value = affixList[count] ? affixList[count + 1] : "";
@@ -3656,6 +4706,7 @@ CSL.Engine.prototype.setLangPrefsForCiteAffixes = function (affixList) {
this.opt.citeAffixes = affixes;
}
};
+
CSL.Engine.prototype.setAutoVietnameseNamesOption = function (arg) {
if (arg) {
this.opt["auto-vietnamese-names"] = true;
@@ -3663,15 +4714,24 @@ CSL.Engine.prototype.setAutoVietnameseNamesOption = function (arg) {
this.opt["auto-vietnamese-names"] = false;
}
};
+
CSL.Engine.prototype.setAbbreviations = function (arg) {
if (this.sys.setAbbreviations) {
this.sys.setAbbreviations(arg);
}
};
+
CSL.Engine.prototype.setSuppressTrailingPunctuation = function (arg) {
this.citation.opt.suppressTrailingPunctuation = !!arg;
};
+
+/*global CSL: true */
+
CSL.Output = {};
+/**
+ * Output queue object.
+ * @class
+ */
CSL.Output.Queue = function (state) {
this.levelname = ["top"];
this.state = state;
@@ -3682,7 +4742,15 @@ CSL.Output.Queue = function (state) {
this.formats = new CSL.Stack(tokenstore);
this.current = new CSL.Stack(this.queue);
};
+
+// XXX This works, but causes a mismatch in api_cite
+// Could insert a placeholder
+// Better to have a function that spits out an independent blob.
+// Is that possible though?
+// Okay. Use queue.append() with fake_queue instead.
CSL.Output.Queue.prototype.pop = function () {
+ // For some reason, state.output.current.value() here can be an array,
+ // not a blob ... ?
var drip = this.current.value();
if (drip.length) {
return drip.pop();
@@ -3690,10 +4758,12 @@ CSL.Output.Queue.prototype.pop = function () {
return drip.blobs.pop();
}
};
+
CSL.Output.Queue.prototype.getToken = function (name) {
var ret = this.formats.value()[name];
return ret;
};
+
CSL.Output.Queue.prototype.mergeTokenStrings = function (base, modifier) {
var base_token, modifier_token, ret, key;
base_token = this.formats.value()[base];
@@ -3720,6 +4790,8 @@ CSL.Output.Queue.prototype.mergeTokenStrings = function (base, modifier) {
}
return ret;
};
+
+// Store a new output format token based on another
CSL.Output.Queue.prototype.addToken = function (name, modifier, token) {
var newtok, attr;
newtok = new CSL.Token("output");
@@ -3733,12 +4805,17 @@ CSL.Output.Queue.prototype.addToken = function (name, modifier, token) {
}
}
newtok.decorations = token.decorations;
+
}
if ("string" === typeof modifier) {
newtok.strings.delimiter = modifier;
}
this.formats.value()[name] = newtok;
};
+
+//
+// newFormat adds a new bundle of formatting tokens to
+// the queue's internal stack of such bundles
CSL.Output.Queue.prototype.pushFormats = function (tokenstore) {
if (!tokenstore) {
tokenstore = {};
@@ -3746,9 +4823,12 @@ CSL.Output.Queue.prototype.pushFormats = function (tokenstore) {
tokenstore.empty = this.empty;
this.formats.push(tokenstore);
};
+
+
CSL.Output.Queue.prototype.popFormats = function () {
this.formats.pop();
};
+
CSL.Output.Queue.prototype.startTag = function (name, token) {
var tokenstore = {};
if (this.state.tmp["doing-macro-with-date"] && this.state.tmp.extension) {
@@ -3759,20 +4839,29 @@ CSL.Output.Queue.prototype.startTag = function (name, token) {
this.pushFormats(tokenstore);
this.openLevel(name);
};
+
CSL.Output.Queue.prototype.endTag = function (name) {
this.closeLevel(name);
this.popFormats();
};
+
+//
+// newlevel adds a new blob object to the end of the current
+// list, and adjusts the current pointer so that subsequent
+// appends are made to blob list of the new object.
+
CSL.Output.Queue.prototype.openLevel = function (token) {
var blob, curr;
if ("object" === typeof token) {
+ // delimiter, prefix, suffix, decorations from token
blob = new CSL.Blob(undefined, token);
} else if ("undefined" === typeof token) {
blob = new CSL.Blob(undefined, this.formats.value().empty, "empty");
} else {
if (!this.formats.value() || !this.formats.value()[token]) {
- throw "CSL processor error: call to nonexistent format token \"" + token + "\"";
+ CSL.error("CSL processor error: call to nonexistent format token \"" + token + "\"");
}
+ // delimiter, prefix, suffix, decorations from token
blob = new CSL.Blob(undefined, this.formats.value()[token], token);
}
curr = this.current.value();
@@ -3782,7 +4871,17 @@ CSL.Output.Queue.prototype.openLevel = function (token) {
curr.push(blob);
this.current.push(blob);
};
+
+/**
+ * "merge" used to be real complicated, now it's real simple.
+ */
CSL.Output.Queue.prototype.closeLevel = function (name) {
+ // CLEANUP: Okay, so this.current.value() holds the blob at the
+ // end of the current list. This is wrong. It should
+ // be the parent, so that we have the choice of reading
+ // the affixes and decorations, or appending to its
+ // content. The code that manipulates blobs will be
+ // much simpler that way.
if (name && name !== this.current.value().levelname) {
CSL.error("Level mismatch error: wanted " + name + " but found " + this.current.value().levelname);
}
@@ -3791,12 +4890,21 @@ CSL.Output.Queue.prototype.closeLevel = function (name) {
blob.strings.suffix = this.checkNestedBrace.update(blob.strings.suffix);
}
};
+
+//
+// append does the same thing as newlevel, except
+// that the blob it pushes has text content,
+// and the current pointer is not moved after the push.
+
CSL.Output.Queue.prototype.append = function (str, tokname, notSerious, ignorePredecessor, noStripPeriods) {
var token, blob, curr;
var useblob = true;
if (notSerious) {
ignorePredecessor = true;
}
+ // XXXXX Nasty workaround, but still an improvement
+ // over the reverse calls to the cs:date node build
+ // function that we had before.
if (this.state.tmp["doing-macro-with-date"] && !notSerious) {
if (tokname !== "macro-with-date") {
return false;
@@ -3814,6 +4922,7 @@ CSL.Output.Queue.prototype.append = function (str, tokname, notSerious, ignorePr
if (!notSerious
&& this.state.tmp.element_trace
&& this.state.tmp.element_trace.value() === "suppress-me") {
+
return false;
}
blob = false;
@@ -3828,18 +4937,30 @@ CSL.Output.Queue.prototype.append = function (str, tokname, notSerious, ignorePr
token = tokname;
}
if (!token) {
- throw "CSL processor error: unknown format token name: " + tokname;
+ CSL.error("CSL processor error: unknown format token name: " + tokname);
}
+ // Unset delimiters must be left undefined until they reach the queue
+ // in order to discriminate unset from explicitly empty delimiters
+ // when inheriting a default value from a superior node. [??? really ???]
if (token.strings && "undefined" === typeof token.strings.delimiter) {
token.strings.delimiter = "";
}
if ("string" === typeof str && str.length) {
+
+ // Source (;?!»«): http://en.wikipedia.org/wiki/Space_(punctuation)#Breaking_and_non-breaking_spaces
+ // Source (:): http://forums.zotero.org/discussion/4933/localized-quotes/#Comment_88384
str = str.replace(/ ([:;?!\u00bb])/g, "\u202f$1").replace(/\u00ab /g, "\u00ab\u202f");
+
this.last_char_rendered = str.slice(-1);
+ // This, and not the str argument below on flipflop, is the
+ // source of the flipflopper string source.
str = str.replace(/\s+'/g, " \'");
if (!notSerious) {
+ // this condition for sort_LeadingApostropheOnNameParticle
str = str.replace(/^'/g, " \'");
}
+
+ // signal whether we end with terminal punctuation?
if (!ignorePredecessor) {
this.state.tmp.term_predecessor = true;
this.state.tmp.in_cite_predecessor = true;
@@ -3850,6 +4971,8 @@ CSL.Output.Queue.prototype.append = function (str, tokname, notSerious, ignorePr
blob = new CSL.Blob(str, token);
curr = this.current.value();
if ("undefined" === typeof curr && this.current.mystack.length === 0) {
+ // XXXX An operation like this is missing somewhere, this should NOT be necessary.
+ // Addresses error triggered in multi-layouts.
this.current.mystack.push([]);
curr = this.current.value();
}
@@ -3861,9 +4984,10 @@ CSL.Output.Queue.prototype.append = function (str, tokname, notSerious, ignorePr
this.state.tmp.term_predecessor_name = true;
}
}
- if (!notSerious) {
- this.state.parallel.AppendBlobPointer(curr);
- }
+ //
+ // Caution: The parallel detection machinery will blow up if tracking
+ // variables are not properly initialized elsewhere.
+ //
if ("string" === typeof str) {
if ("string" === typeof blob.blobs) {
if (blob.blobs.slice(0, 1) !== " ") {
@@ -3880,6 +5004,14 @@ CSL.Output.Queue.prototype.append = function (str, tokname, notSerious, ignorePr
}
}
if (blob.strings["text-case"]) {
+ //
+ // This one is _particularly_ hard to follow. It's not obvious,
+ // but the blob already contains the input string at this
+ // point, as blob.blobs -- it's a terminal node, as it were.
+ // The str variable also contains the input string, but
+ // that copy is not used for onward processing. We have to
+ // apply our changes to the blob copy.
+ //
blob.blobs = CSL.Output.Formatters[blob.strings["text-case"]](this.state, str);
}
if (this.state.tmp.strip_periods && !noStripPeriods) {
@@ -3895,6 +5027,11 @@ CSL.Output.Queue.prototype.append = function (str, tokname, notSerious, ignorePr
}
}
}
+ //
+ // XXX: Beware superfluous code in your code. str in this
+ // case is not the source of the final rendered string.
+ // See note above.
+ //
curr.push(blob);
this.state.fun.flipflopper.processTags(blob);
} else if (useblob) {
@@ -3904,31 +5041,45 @@ CSL.Output.Queue.prototype.append = function (str, tokname, notSerious, ignorePr
}
return true;
};
+
CSL.Output.Queue.prototype.string = function (state, myblobs, blob) {
var i, ilen, j, jlen, b;
+ //if (blob && blob.strings.delimiter) {
+ // print("DELIMITER: "+blob.strings.delimiter+" on "+[x.blobs[0].num for each (x in myblobs)]);
+ //}
+ //var blobs, ret, blob_delimiter, i, params, blobjr, last_str, last_char, b, use_suffix, qres, addtoret, span_split, j, res, blobs_start, blobs_end, key, pos, len, ppos, llen, ttype, ltype, terminal, leading, delimiters, use_prefix, txt_esc;
var txt_esc = CSL.getSafeEscape(this.state);
var blobs = myblobs.slice();
var ret = [];
+
if (blobs.length === 0) {
return ret;
}
+
var blob_delimiter = "";
if (blob) {
blob_delimiter = blob.strings.delimiter;
} else {
+ //print("=== Setting false to start ===");
state.tmp.count_offset_characters = false;
state.tmp.offset_characters = 0;
}
+
if (blob && blob.new_locale) {
blob.old_locale = state.opt.lang;
state.opt.lang = blob.new_locale;
}
+
var blobjr, use_suffix, use_prefix, params;
for (var i = 0, ilen = blobs.length; i < ilen; i += 1) {
blobjr = blobs[i];
+
if (blobjr.strings.first_blob) {
+ // Being the Item.id of the the entry being rendered.
+ //print(" -- turning on counting");
state.tmp.count_offset_characters = blobjr.strings.first_blob;
}
+
if ("string" === typeof blobjr.blobs) {
if ("number" === typeof blobjr.num) {
ret.push(blobjr);
@@ -3937,8 +5088,11 @@ CSL.Output.Queue.prototype.string = function (state, myblobs, blob) {
blobjr.blobs = blobjr.particle + blobjr.blobs;
blobjr.particle = "";
}
+ // (skips empty strings)
+ //b = txt_esc(blobjr.blobs);
b = txt_esc(blobjr.blobs);
var blen = b.length;
+
if (!state.tmp.suppress_decorations) {
for (j = 0, jlen = blobjr.decorations.length; j < jlen; j += 1) {
params = blobjr.decorations[j];
@@ -3951,11 +5105,17 @@ CSL.Output.Queue.prototype.string = function (state, myblobs, blob) {
b = state.fun.decorate[params[0]][params[1]].call(blobjr, state, b, params[2]);
}
}
+ //
+ // because we will rip out portions of the output
+ // queue before rendering, group wrappers need
+ // to produce no output if they are found to be
+ // empty.
if (b && b.length) {
b = txt_esc(blobjr.strings.prefix) + b + txt_esc(blobjr.strings.suffix);
if (state.opt.development_extensions.csl_reverse_lookup_support && !state.tmp.suppress_decorations) {
for (j = 0, jlen = blobjr.decorations.length; j < jlen; j += 1) {
params = blobjr.decorations[j];
+
if (params[0] === "@showid") {
b = state.fun.decorate[params[0]][params[1]].call(blobjr, state, b, params[2]);
}
@@ -3970,6 +5130,7 @@ CSL.Output.Queue.prototype.string = function (state, myblobs, blob) {
} else if (blobjr.blobs.length) {
var addtoret = state.output.string(state, blobjr.blobs, blobjr);
if (blob) {
+ // Patch up world-class weird bug in the ill-constructed code of mine.
if ("string" !== addtoret && addtoret.length > 1 && blobjr.strings.delimiter) {
var numberSeen = false;
for (var j=0,jlen=addtoret.length;j 0; pos += -1) {
if (blobs[pos].checkLast) {
@@ -4136,11 +5328,17 @@ CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite, parent
}
if ("string" === typeof blob) {
ret += txt_esc(use_delim);
+ // XXX Blob should be run through flipflop and flattened here.
+ // (I think it must be a fragment of text around a numeric
+ // variable)
ret += blob;
if (state.tmp.count_offset_characters) {
+ //state.tmp.offset_characters += (use_delim.length + blob.length);
state.tmp.offset_characters += (use_delim.length);
}
} else if (in_cite) {
+ // pass
+ // Okay, so this does it -- but we're now not able to return a string!
if (ret) {
ret = [ret, blob];
} else {
@@ -4152,7 +5350,9 @@ CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite, parent
} else {
str = blob.formatter.format(blob.num, blob.gender);
}
+ // Workaround to get a more or less accurate value.
var strlen = str.replace(/<[^>]*>/g, "").length;
+ // notSerious
this.append(str, "empty", true);
var str_blob = this.pop();
var count_offset_characters = state.tmp.count_offset_characters;
@@ -4177,16 +5377,23 @@ CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite, parent
str = txt_esc(blob.strings.prefix) + str + txt_esc(blob.strings.suffix);
var addme = "";
if (blob.status === CSL.END) {
+ //print(" CSL.END");
addme = txt_esc(blob.range_prefix);
} else if (blob.status === CSL.SUCCESSOR) {
+ //print(" CSL.SUCCESSOR");
addme = txt_esc(blob.successor_prefix);
} else if (blob.status === CSL.START) {
+ //print(" CSL.START");
if (pos > 0 && !blob.suppress_splice_prefix) {
addme = txt_esc(blob.splice_prefix);
} else {
addme = "";
}
} else if (blob.status === CSL.SEEN) {
+ //print(" CSL.SEEN");
+
+ // THIS IS NOT THE PROPER FUNCTION OF CSL.SEEN, IS IT?
+
addme = txt_esc(blob.splice_prefix);
}
ret += addme;
@@ -4198,10 +5405,13 @@ CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite, parent
}
return ret;
};
+
CSL.Output.Queue.purgeEmptyBlobs = function (parent) {
+ //print("START1");
if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) {
return;
}
+ // back-to-front, bottom-first
for (var i=parent.blobs.length-1;i>-1;i--) {
CSL.Output.Queue.purgeEmptyBlobs(parent.blobs[i]);
var child = parent.blobs[i];
@@ -4216,17 +5426,27 @@ CSL.Output.Queue.purgeEmptyBlobs = function (parent) {
}
}
}
+ //print(" end");
};
+
+// Adjustments to be made:
+//
+// * Never migrate beyond a @quotes node
+// * Never migrate into a num node.
+
CSL.Output.Queue.adjust = function (punctInQuote) {
+
var NO_SWAP_IN = {
";": true,
":": true
};
+
var NO_SWAP_OUT = {
".": true,
"!": true,
"?": true
};
+
var LtoR_MAP = {
"!": {
".": "!",
@@ -4271,6 +5491,7 @@ CSL.Output.Queue.adjust = function (punctInQuote) {
".": ";"
}
};
+
var SWAP_IN = {};
var SWAP_OUT = {};
var PUNCT = {};
@@ -4287,6 +5508,7 @@ CSL.Output.Queue.adjust = function (punctInQuote) {
}
PUNCT_OR_SPACE[" "] = true;
PUNCT_OR_SPACE[" "] = true;
+
var RtoL_MAP = {};
for (var key in LtoR_MAP) {
for (var subkey in LtoR_MAP[key]) {
@@ -4296,9 +5518,11 @@ CSL.Output.Queue.adjust = function (punctInQuote) {
RtoL_MAP[subkey][key] = LtoR_MAP[key][subkey];
}
}
+
function blobIsNumber(blob) {
return ("number" === typeof blob.num || (blob.blobs && blob.blobs.length === 1 && "number" === typeof blob.blobs[0].num));
}
+
function blobEndsInNumber(blob) {
if ("number" === typeof blob.num) {
return true;
@@ -4310,6 +5534,7 @@ CSL.Output.Queue.adjust = function (punctInQuote) {
return true;
}
}
+
function blobHasDecorations(blob,includeQuotes) {
var ret = false;
var decorlist = ['@font-style','@font-variant','@font-weight','@text-decoration','@vertical-align'];
@@ -4326,6 +5551,7 @@ CSL.Output.Queue.adjust = function (punctInQuote) {
}
return ret;
}
+
function blobHasDescendantQuotes(blob) {
if (blob.decorations) {
for (var i=0,ilen=blob.decorations.length;i-1;i--) {
this.upward(parent.blobs[i]);
var parentStrings = parent.strings;
var childStrings = parent.blobs[i].strings;
if (i === 0) {
+ // Remove leading space on first-position child node prefix if there is a trailing space on the node prefix above
if (" " === parentStrings.prefix.slice(-1) && " " === childStrings.prefix.slice(0, 1)) {
childStrings.prefix = childStrings.prefix.slice(1);
}
+ // Migrate leading punctuation or space on a first-position prefix upward
var childChar = childStrings.prefix.slice(0, 1);
if (!parentDecorations && PUNCT_OR_SPACE[childChar] && !parentStrings.prefix) {
parentStrings.prefix += childChar;
@@ -4465,7 +5708,9 @@ CSL.Output.Queue.adjust = function (punctInQuote) {
}
}
if (i === (parent.blobs.length - 1)) {
+ // Migrate trailing space ONLY on a last-position suffix upward, controlling for duplicates
var childChar = childStrings.suffix.slice(-1);
+ // ZZZ Loosened to fix initialized names wrapped in a span and followed by a period
if (!parentDecorations && [" "].indexOf(childChar) > -1) {
if (parentStrings.suffix.slice(0,1) !== childChar) {
parentStrings.suffix = childChar + parentStrings.suffix;
@@ -4474,31 +5719,53 @@ CSL.Output.Queue.adjust = function (punctInQuote) {
}
}
if (parentStrings.delimiter && i > 0) {
+ // Remove leading space on mid-position child node prefix if there is a trailing space on delimiter above
if (PUNCT_OR_SPACE[parentStrings.delimiter.slice(-1)]
&& parentStrings.delimiter.slice(-1) === childStrings.prefix.slice(0, 1)) {
+
childStrings.prefix = childStrings.prefix.slice(1);
}
}
+ // Siblings are handled in adjustNearsideSuffixes()
}
+ //print(" end");
}
+
function leftward (parent) {
+ // Terminus if no blobs
if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) {
return;
}
+
for (var i=parent.blobs.length-1;i>-1;i--) {
this.leftward(parent.blobs[i]);
+ // This is a delicate one.
+ //
+ // Migrate if:
+ // * there is no umbrella delimiter [ok]
+ // * neither the child nor its sibling is a number [ok]
+ // * decorations exist neither on the child nor on the sibling [ok]
+ // * sibling prefix char is a swapping char [ok]
+ //
+ // Suppress without migration if:
+ // * sibling prefix char matches child suffix char or
+ // * child suffix is empty and sibling prefix char match last field char
if ((i < parent.blobs.length -1) && !parent.strings.delimiter) {
+ // If there is a trailing swappable character on a sibling prefix with no intervening delimiter, copy it to suffix,
+ // controlling for duplicates
var child = parent.blobs[i];
var childChar = child.strings.suffix.slice(-1);
var sibling = parent.blobs[i+1];
var siblingChar = sibling.strings.prefix.slice(0, 1);
var hasDecorations = blobHasDecorations(child) || blobHasDecorations(sibling);
var hasNumber = "number" === typeof childChar || "number" === typeof siblingChar;
+
if (!hasDecorations && !hasNumber && PUNCT[siblingChar] && !hasNumber) {
var suffixAndPrefixMatch = siblingChar === child.strings.suffix.slice(-1);
var suffixAndFieldMatch = (!child.strings.suffix && "string" === typeof child.blobs && child.blobs.slice(-1) === siblingChar);
if (!suffixAndPrefixMatch && !suffixAndFieldMatch) {
mergeChars(child, 'suffix', sibling, 'prefix');
+ //child.strings.suffix += siblingChar;
} else {
sibling.strings.prefix = sibling.strings.prefix.slice(1);
}
@@ -4506,17 +5773,26 @@ CSL.Output.Queue.adjust = function (punctInQuote) {
}
}
}
+
function downward (parent) {
+ //print("START3");
+ // Terminus if no blobs
if (parent.blobs && "string" == typeof parent.blobs) {
if (PUNCT[parent.strings.suffix.slice(0,1)]
&& parent.strings.suffix.slice(0,1) === parent.blobs.slice(-1)) {
+
parent.strings.suffix = parent.strings.suffix.slice(1);
}
return;
} else if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) {
return;
}
+ //if (top) {
+ // print("JSON "+JSON.stringify(parent, ["strings", "decorations", "blobs", "prefix", "suffix", "delimiter"], 2));
+ //}
+
var parentStrings = parent.strings;
+ // Check for numeric child
var someChildrenAreNumbers = false;
for (var i=0,ilen=parent.blobs.length;i-1;i--) {
@@ -4536,14 +5813,30 @@ CSL.Output.Queue.adjust = function (punctInQuote) {
parentStrings.delimiter = parentStrings.delimiter.slice(1);
}
}
+ // back-to-front, top-first
for (var i=parent.blobs.length-1;i>-1;i--) {
var child = parent.blobs[i];
var childStrings = parent.blobs[i].strings;
var childDecorations = blobHasDecorations(child, true);
var childIsNumber = blobIsNumber(child);
+
if (i === (parent.blobs.length - 1)) {
+
+ //if (blobHasDescendantQuotes(child)) {
+ // print("JSON "+JSON.stringify(parent, ["strings", "decorations", "blobs", "prefix", "suffix", "delimiter"]));
+ //}
+
if (true || !someChildrenAreNumbers) {
+ // If we have decorations, drill down to see if there are quotes below.
+ // If so, we allow migration anyway.
+ // Original discussion is here:
+ // https://forums.zotero.org/discussion/37091/citeproc-bug-punctuation-in-quotes/
var parentChar = parentStrings.suffix.slice(0, 1);
+
+ // Hmm.
+ // Consider writing out the matching child from blobHasDescendant functions.
+ // It should save some cycles, and produce the same result.
+
var allowMigration = false;
if (PUNCT[parentChar]) {
allowMigration = blobHasDescendantMergingPunctuation(parentChar,child);
@@ -4569,39 +5862,68 @@ CSL.Output.Queue.adjust = function (punctInQuote) {
if (childStrings.suffix.slice(-1) === " " && parentStrings.suffix.slice(0,1) === " ") {
parentStrings.suffix = parentStrings.suffix.slice(1);
}
+ // More duplicates control
if (PUNCT_OR_SPACE[childStrings.suffix.slice(0,1)]) {
if ("string" === typeof child.blobs && child.blobs.slice(-1) === childStrings.suffix.slice(0,1)) {
+ // Remove parent punctuation of it duplicates the last character of a field
childStrings.suffix = childStrings.suffix.slice(1);
}
if (childStrings.suffix.slice(-1) === parentStrings.suffix.slice(0, 1)) {
+ // Remove duplicate punctuation on child suffix
parentStrings.suffix = parentStrings.suffix.slice(0, -1);
}
}
}
+ // Squash dupes
if (matchLastChar(parent,parent.strings.suffix.slice(0,1))) {
parent.strings.suffix = parent.strings.suffix.slice(1);
}
} else if (parentStrings.delimiter) {
+ // Remove trailing space on mid-position child node suffix if there is a leading space on delimiter above
if (PUNCT_OR_SPACE[parentStrings.delimiter.slice(0,1)]
&& parentStrings.delimiter.slice(0, 1) === childStrings.suffix.slice(-1)) {
+
parent.blobs[i].strings.suffix = parent.blobs[i].strings.suffix.slice(0, -1);
+
}
} else {
+ // Otherwise it's a sibling. We don't care about moving spaces here, just suppress a duplicate
var siblingStrings = parent.blobs[i+1].strings;
if (!blobIsNumber(child)
&& !childDecorations
&& PUNCT_OR_SPACE[childStrings.suffix.slice(-1)]
&& childStrings.suffix.slice(-1) === siblingStrings.prefix.slice(0, 1)) {
+
siblingStrings.prefix = siblingStrings.prefix.slice(1);
}
}
+ // If field content ends with swappable punctuation, suppress swappable punctuation in style suffix.
if (!childIsNumber && !childDecorations && PUNCT[childStrings.suffix.slice(0,1)]
&& "string" === typeof child.blobs) {
+
mergeChars(child, 'blobs', child, 'suffix');
}
this.downward(parent.blobs[i]);
}
+/*
+ if (top) {
+
+ var seen = [];
+ print(JSON.stringify(parent, function(key, val) {
+ if (!val || key === 'alldecor') return;
+ if (typeof val == "object") {
+ if (seen.indexOf(val) >= 0)
+ return
+ seen.push(val)
+ }
+ return val
+ },2));
+ }
+*/
+
+ //print(" end");
}
+ // Abstract out a couple of utility functions, used in fix() below.
function swapToTheLeft (child) {
var childChar = child.strings.suffix.slice(0,1);
if ("string" === typeof child.blobs) {
@@ -4631,11 +5953,21 @@ CSL.Output.Queue.adjust = function (punctInQuote) {
}
}
}
+
function fix (parent) {
+ // Terminus if no blobs
if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) {
return;
}
+
+ //print("START4");
+ // Do the swap, front-to-back, bottom-first
var lastChar;
+
+ // XXX Two things to fix with this:
+ // XXX (1) Stalls after one character
+ // XXX (2) Moves colon and semicolon, both of which SHOULD stall
+
for (var i=0,ilen=parent.blobs.length;i
this.opt = {
inheritedAttributes: {}
};
+
this.tokens = [];
+ // Placeholder function
this.srt = new CSL.Registry.Comparifier(state, "citation_sort");
+ //
+ // configuration array to hold the collapse
+ // options, if any.
this.opt.collapse = [];
+ //
+ // disambiguate options
this.opt["disambiguate-add-names"] = false;
this.opt["disambiguate-add-givenname"] = false;
this.opt["disambiguate-add-year-suffix"] = false;
this.opt["givenname-disambiguation-rule"] = "by-cite";
this.opt["near-note-distance"] = 5;
+
this.opt.topdecor = [];
this.opt.layout_decorations = [];
this.opt.layout_prefix = "";
this.opt.layout_suffix = "";
this.opt.layout_delimiter = "";
+ //
+ // sorting
this.opt.sort_locales = [];
this.opt.max_number_of_names = 0;
this.root = "citation";
};
+
+
CSL.Engine.Bibliography = function () {
this.opt = {
inheritedAttributes: {}
};
this.tokens = [];
+
this.opt.collapse = [];
+
this.opt.topdecor = [];
this.opt.layout_decorations = [];
this.opt.layout_prefix = "";
@@ -4949,21 +6504,30 @@ CSL.Engine.Bibliography = function () {
this.opt.layout_delimiter = "";
this.opt["line-spacing"] = 1;
this.opt["entry-spacing"] = 1;
+ //
+ // sorting
this.opt.sort_locales = [];
this.opt.max_number_of_names = 0;
this.root = "bibliography";
};
+
+
CSL.Engine.BibliographySort = function () {
this.tokens = [];
this.opt = {};
this.opt.sort_directions = [];
this.opt.topdecor = [];
+ // Holds the final citation-number sort direction, for use
+ // in applying numbers in cs:citation and cs:bibliography.
+ // Value is exclusively controlled by cs:key in bibliography_sort
this.opt.citation_number_sort_direction = CSL.ASCENDING;
this.opt.citation_number_secondary = false;
this.tmp = {};
this.keys = [];
this.root = "bibliography";
};
+
+
CSL.Engine.CitationSort = function () {
this.tokens = [];
this.opt = {};
@@ -4972,36 +6536,61 @@ CSL.Engine.CitationSort = function () {
this.opt.topdecor = [];
this.root = "citation";
};
+
CSL.Engine.InText = function () {
+ // InText options area.
+ // Holds a mixture of persistent and ephemeral
+ // options and scratch data used during processing of
+ // a citation.
this.opt = {
inheritedAttributes: {}
};
+
this.tokens = [];
+ // Placeholder function
+ //this.srt = new CSL.Registry.Comparifier(state, "citation_sort");
+ //
+ // configuration array to hold the collapse
+ // options, if any.
this.opt.collapse = [];
+ //
+ // disambiguate options
this.opt["disambiguate-add-names"] = false;
this.opt["disambiguate-add-givenname"] = false;
this.opt["disambiguate-add-year-suffix"] = false;
this.opt["givenname-disambiguation-rule"] = "by-cite";
this.opt["near-note-distance"] = 5;
+
this.opt.topdecor = [];
this.opt.layout_decorations = [];
this.opt.layout_prefix = "";
this.opt.layout_suffix = "";
this.opt.layout_delimiter = "";
+ //
+ // sorting
this.opt.sort_locales = [];
this.opt.max_number_of_names = 0;
this.root = "intext";
};
+
+/*global CSL: true */
+
CSL.Engine.prototype.previewCitationCluster = function (citation, citationsPre, citationsPost, newMode) {
+ // Generate output for a hypothetical citation at the current position,
+ // Leave the registry in the same state in which it was found.
+ //print("################### previewCitationCluster() #################");
var oldMode = this.opt.mode;
this.setOutputFormat(newMode);
+ // Avoids generating unwanted ibids, if the citationID already exists in document
if (citation.citationID) {
delete citation.citationID;
}
var ret = this.processCitationCluster(citation, citationsPre, citationsPost, CSL.PREVIEW);
+
this.setOutputFormat(oldMode);
return ret[1];
};
+
CSL.Engine.prototype.appendCitationCluster = function (citation) {
var citationsPre = [];
var len = this.registry.citationreg.citationByIndex.length;
@@ -5009,22 +6598,54 @@ CSL.Engine.prototype.appendCitationCluster = function (citation) {
var c = this.registry.citationreg.citationByIndex[pos];
citationsPre.push(["" + c.citationID, c.properties.noteIndex]);
}
+ // Drop the data segment to return a list of pos/string pairs.
return this.processCitationCluster(citation, citationsPre, [])[1];
};
+
+
CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, citationsPost, flag) {
var c, preCitation, postCitation, i, ilen, j, jlen, k, klen, n, nlen, key, Item, item, noteCitations, textCitations, m, citationsInNote;
this.debug = false;
this.tmp.loadedItemIDs = {};
+
+ // Revert citation dereference from 2ffc4664ae
+ //citation = JSON.parse(JSON.stringify(citation));
+
+ //print("################### processCitationCluster() #################");
this.tmp.citation_errors = [];
this.registry.return_data = {"bibchange": false};
+
+ // make sure this citation has a unique ID, and register it in citationById.
this.setCitationId(citation);
+
var oldCitationList;
var oldItemList;
var oldAmbigs;
if (flag === CSL.PREVIEW) {
+ //SNIP-START
+ if (this.debug) {
+ CSL.debug("****** start state save *********");
+ }
+ //SNIP-END
+ //
+ // Simplify.
+
+ // Take a slice of existing citations.
oldCitationList = this.registry.citationreg.citationByIndex.slice();
+
+ // Take a slice of current items, for later use with update.
oldItemList = this.registry.reflist.slice();
+
+ // Make a list of preview citation ref objects. Omit the current
+ // citation, because it will not exist in registry if: (a) this is
+ // a new citation; or (b) the calling application is assigning
+ // new citationIDs for every transaction.
var newCitationList = citationsPre.concat(citationsPost);
+
+ // Make a full list of desired ids, for use in preview update,
+ // and a hash list of same while we're at it.
+ // First step through known citations, then step through
+ // the items in the citation for preview.
var newItemIds = {};
var newItemIdsList = [];
for (var i = 0, ilen = newCitationList.length; i < ilen; i += 1) {
@@ -5038,6 +6659,8 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
newItemIds[citation.citationItems[j].id] = true;
newItemIdsList.push("" + citation.citationItems[j].id);
}
+
+ // Clone and save off disambigs of items that will be lost.
oldAmbigs = {};
for (var i = 0, ilen = oldItemList.length; i < ilen; i += 1) {
if (!newItemIds[oldItemList[i].id]) {
@@ -5050,11 +6673,34 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
}
}
}
+
+ // Update items. This will produce the base name data and sort things.
+ // Possibly unnecessary?
+ //this.updateItems(this.registry.mylist.concat(tmpItems));
+
+ //SNIP-START
+ if (this.debug) {
+ CSL.debug("****** end state save *********");
+ }
+ //SNIP-END
}
+
this.tmp.taintedCitationIDs = {};
var sortedItems = [];
+
+ // Styles that use note backreferencing with a by-cite
+ // givenname disambiguation rule include the note number
+ // in the cite for disambiguation purposes. Correct resolution
+ // of disambiguate="true" conditions on first-reference cites
+ // in certain editing scenarios (e.g. where a cite is moved across
+ // notes) requires that disambiguation be rerun on cites
+ // affected by the edit.
var rerunAkeys = {};
+
+ // retrieve item data and compose items for use in rendering
+ // attach pointer to item data to shared copy for good measure
for (var i = 0, ilen = citation.citationItems.length; i < ilen; i += 1) {
+ // Protect against caller-side overwrites to locator strings etc
item = {};
for (var key in citation.citationItems[i]) {
item[key] = citation.citationItems[i][key];
@@ -5083,7 +6729,13 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
sortedItems.push(newitem);
citation.citationItems[i].item = Item;
}
+
+ // ZZZ sort stuff moved from here.
+
+ // attach the sorted list to the citation item
citation.sortedItems = sortedItems;
+
+ // build reconstituted citations list in current document order
var citationByIndex = [];
var citationById = {};
var lastNotePos;
@@ -5091,11 +6743,11 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
preCitation = citationsPre[i];
if (this.opt.development_extensions.strict_inputs) {
if (citationById[preCitation[0]]) {
- throw "Previously referenced citationID " + preCitation[0] + " encountered in citationsPre";
+ CSL.error("Previously referenced citationID " + preCitation[0] + " encountered in citationsPre");
}
if (preCitation[1]) {
if (lastNotePos > preCitation[1]) {
- throw "Note index sequence is not sane at citationsPre[" + i + "]";
+ CSL.debug("Note index sequence is not sane at citationsPre[" + i + "]");
}
lastNotePos = preCitation[1];
}
@@ -5111,11 +6763,11 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
}
if (this.opt.development_extensions.strict_inputs) {
if (citationById[citation.citationID]) {
- throw "Citation with previously referenced citationID " + citation.citationID;
+ CSL.error("Citation with previously referenced citationID " + citation.citationID);
}
if (citation.properties.noteIndex) {
if (lastNotePos > citation.properties.noteIndex) {
- throw "Note index sequence is not sane for citation " + citation.citationID;
+ CSL.debug("Note index sequence is not sane for citation " + citation.citationID);
}
lastNotePos = citation.properties.noteIndex;
}
@@ -5126,11 +6778,11 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
postCitation = citationsPost[i];
if (this.opt.development_extensions.strict_inputs) {
if (citationById[postCitation[0]]) {
- throw "Previously referenced citationID " + postCitation[0] + " encountered in citationsPost";
+ CSL.error("Previously referenced citationID " + postCitation[0] + " encountered in citationsPost");
}
if (postCitation[1]) {
if (lastNotePos > postCitation[1]) {
- throw "Note index sequence is not sane at postCitation[" + i + "]";
+ CSL.debug("Note index sequence is not sane at postCitation[" + i + "]");
}
lastNotePos = postCitation[1];
}
@@ -5141,6 +6793,32 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
}
this.registry.citationreg.citationByIndex = citationByIndex;
this.registry.citationreg.citationById = citationById;
+
+ //
+ // The processor provides three facilities to support
+ // updates following position reevaluation.
+ //
+ // (1) The updateItems() function reports tainted ItemIDs
+ // to state.tmp.taintedItemIDs.
+ //
+ // (2) The processor memos the type of style referencing as
+ // CSL.NONE, CSL.NUMERIC or CSL.POSITION in state.opt.update_mode.
+ //
+ // XXXX: NO LONGER
+ // (3) For citations containing cites with backreference note numbers,
+ // a string image of the rendered citation is held in
+ // citation.properties.backref_citation, and a list of
+ // ItemIDs to be used to update the backreference note numbers
+ // is memoed at citation.properties.backref_index. When such
+ // citations change position, they can be updated with a
+ // series of simple find and replace operations, without
+ // need for rerendering.
+ //
+
+ //
+ // Position evaluation!
+ //
+ // set positions in reconstituted list, noting taints
this.registry.citationreg.citationsByItemId = {};
if (this.opt.update_mode === CSL.POSITION) {
textCitations = [];
@@ -5169,25 +6847,55 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
}
}
}
+ //
+ // update bibliography items here
+ //
if (flag !== CSL.ASSUME_ALL_ITEMS_REGISTERED) {
+ //SNIP-START
+ if (this.debug) {
+ CSL.debug("****** start update items *********");
+ }
+ //SNIP-END
+ // true signals implicit updateItems (will not rerun sys.retrieveItem())
this.updateItems(update_items, null, null, true);
+ //SNIP-START
+ if (this.debug) {
+ CSL.debug("****** endo update items *********");
+ }
+ //SNIP-END
}
+
if (!this.opt.citation_number_sort && sortedItems && sortedItems.length > 1 && this.citation_sort.tokens.length > 0) {
for (var i = 0, ilen = sortedItems.length; i < ilen; i += 1) {
sortedItems[i][1].sortkeys = CSL.getSortKeys.call(this, sortedItems[i][0], "citation_sort");
}
+
+ /*
+ * Grouped sort stuff (start)
+ */
+
if (this.opt.grouped_sort && !citation.properties.unsorted) {
+ // Insert authorstring as key.
for (var i = 0, ilen = sortedItems.length; i < ilen; i += 1) {
var sortkeys = sortedItems[i][1].sortkeys;
this.tmp.authorstring_request = true;
+ // Run getAmbiguousCite() with the current disambig
+ // parameters, and pick up authorstring from the registry.
var mydisambig = this.registry.registry[sortedItems[i][0].id].disambig;
+
this.tmp.authorstring_request = true;
CSL.getAmbiguousCite.call(this, sortedItems[i][0], mydisambig);
var authorstring = this.registry.authorstrings[sortedItems[i][0].id];
this.tmp.authorstring_request = false;
+
sortedItems[i][1].sortkeys = [authorstring].concat(sortkeys);
}
+
sortedItems.sort(this.citation.srt.compareCompositeKeys);
+ // Replace authorstring key in items with same (authorstring) with the
+ // keystring of first normal key. This forces grouped sorts,
+ // as discussed here:
+ // https://github.com/citation-style-language/schema/issues/40
var lastauthor = false;
var thiskey = false;
var thisauthor = false;
@@ -5200,10 +6908,18 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
lastauthor = thisauthor;
}
}
+ /*
+ * Grouped sort stuff (end)
+ */
+
if (!citation.properties.unsorted) {
sortedItems.sort(this.citation.srt.compareCompositeKeys);
}
}
+
+ // evaluate parallels
+ this.parallel.StartCitation(citation.sortedItems);
+
var citations;
if (this.opt.update_mode === CSL.POSITION) {
for (var i = 0; i < 2; i += 1) {
@@ -5222,14 +6938,28 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
last_ref = {};
}
for (k = 0, klen = onecitation.sortedItems.length; k < klen; k += 1) {
- if (!this.registry.registry[onecitation.sortedItems[k][1].id].parallel) {
- if (!citationsInNote[onecitation.properties.noteIndex]) {
- citationsInNote[onecitation.properties.noteIndex] = 1;
- } else {
- citationsInNote[onecitation.properties.noteIndex] += 1;
- }
+ if (onecitation.sortedItems[k][1].parallel && onecitation.sortedItems[k][1].parallel !== "first") {
+ continue;
+ }
+ if (!citationsInNote[onecitation.properties.noteIndex]) {
+ citationsInNote[onecitation.properties.noteIndex] = 1;
+ } else {
+ citationsInNote[onecitation.properties.noteIndex] += 1;
}
}
+ // Set the following:
+ //
+ // (1) position as required (as per current Zotero)
+ // (2) first-reference-note-number as required (on onecitation item)
+ // (3) near-note as required (on onecitation item, according to
+ // state.opt["near-note-distance"] parameter)
+ // (4) state.registry.citationreg.citationsByItemId.
+ //
+ // Any state changes caused by unsetting or resetting should
+ // trigger a single entry for the citations in
+ // state.tmp.taintedCitationIDs (can block on presence of
+ // state.registry.citationreg.citationsByItemId).
+ //
for (k = 0, klen = citations[j].sortedItems.length; k < klen; k += 1) {
item = citations[j].sortedItems[k];
var myid = item[0].id;
@@ -5240,12 +6970,23 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
}
var incitationid;
if (k > 0) {
+ // incitationid is only reached in the else branch
+ // following "undefined" === typeof first_ref[myid]
+ // below
if (onecitation.sortedItems[k - 1][0].legislation_id) {
incitationid = onecitation.sortedItems[k - 1][0].legislation_id;
} else {
incitationid = onecitation.sortedItems[k - 1][1].id;
+ //if (onecitation.sortedItems[k-1][1].parallel === "last") {
+ for (var l=k-2; l>-1; l--) {
+ if (onecitation.sortedItems[l][1].parallel === "first") {
+ incitationid = onecitation.sortedItems[l][1].id;
+ }
+ }
+ //}
}
}
+ // Don't touch item data of other cites when previewing
if (flag === CSL.PREVIEW) {
if (onecitation.citationID != citation.citationID) {
if ("undefined" === typeof first_ref[item[1].id]) {
@@ -5286,6 +7027,7 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
}
}
var oldlastid;
+
if ("undefined" === typeof first_ref[myid] && onecitation.properties.mode !== "author-only") {
first_ref[myid] = onecitation.properties.noteIndex;
if (this.registry.registry[myid]) {
@@ -5294,57 +7036,69 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
last_ref[myid] = onecitation.properties.noteIndex;
item[1].position = CSL.POSITION_FIRST;
} else {
+ //
+ // backward-looking position evaluation happens here.
+ //
+ //
+ //
var ibidme = false;
var suprame = false;
+ // XXX Ugly, but This is used in the second else-if branch condition below.
if (j > 0) {
- try {
- var old_last_id_offset = 1;
- if (citations[j-1].properties.mode === "author-only" && j > 1) {
- old_last_id_offset = 2;
- }
- oldlastid = citations[j - old_last_id_offset].sortedItems.slice(-1)[0][1].id;
- } catch (e) {
- var err = "CSL Error\n";
- err += " " + e;
- err += " in citation object " + citations[j - 1].citationID + " at index " + (j - 1);
- throw err;
+ var old_last_id_offset = 1;
+ if (citations[j-1].properties.mode === "author-only" && j > 1) {
+ old_last_id_offset = 2;
}
+ oldlastid = citations[j - old_last_id_offset].sortedItems.slice(-1)[0][1].id;
if (citations[j - 1].sortedItems[0].slice(-1)[0].legislation_id) {
oldlastid = citations[j - 1].sortedItems[0].slice(-1)[0].legislation_id;
}
}
if (j > 0 && parseInt(k, 10) === 0 && citations[j - 1].properties.noteIndex !== citations[j].properties.noteIndex) {
- var items = citations[(j - 1)].sortedItems;
+ // Case 1: source in previous onecitation
+ // (1) Threshold conditions
+ // (a) there must be a previous onecitation with one item
+ // (b) this item must be the first in this onecitation
+ // (c) the previous onecitation must contain a reference
+ // to the same item ...
+ // (d) the note numbers must be the same or consecutive.
+ // (this has some jiggery-pokery in it for parallels)
var useme = false;
+ // XXX Can oldid be equated with oldlastid, I wonder ...
var oldid = citations[j - 1].sortedItems[0][0].id;
if (citations[j - 1].sortedItems[0][0].legislation_id) {
oldid = citations[j - 1].sortedItems[0][0].legislation_id;
}
- if ((oldid == myid && citations[j - 1].properties.noteIndex >= (citations[j].properties.noteIndex - 1)) || citations[j - 1].sortedItems[0][1].id == this.registry.registry[item[1].id].parallel) {
+ if ((oldid == myid && citations[j - 1].properties.noteIndex >= (citations[j].properties.noteIndex - 1))) {
if (citationsInNote[citations[j - 1].properties.noteIndex] === 1 || citations[j - 1].properties.noteIndex === 0) {
useme = true;
}
}
- for (n = 0, nlen = items.slice(1).length; n < nlen; n += 1) {
- var itmp = items.slice(1)[n];
- if (!this.registry.registry[itmp[1].id].parallel || this.registry.registry[itmp[1].id].parallel == this.registry.registry[itmp[1].id]) {
- useme = false;
- }
- }
if (useme) {
ibidme = true;
} else {
suprame = true;
}
} else if (k > 0 && incitationid == myid) {
+ // Case 2: immediately preceding source in this onecitation
+ // (1) Threshold conditions
+ // (a) there must be an imediately preceding reference to the
+ // same item in this onecitation; and
ibidme = true;
} else if (k === 0 && j > 0 && citations[j - 1].properties.noteIndex == citations[j].properties.noteIndex
&& citations[j - 1].sortedItems.length
&& oldlastid == myid) {
+ // ... in case there are separate citations in the same note ...
+ // Case 2 [take 2]: immediately preceding source in this onecitation
+ // (1) Threshold conditions
+ // (a) there must be an imediately preceding reference to the
+ // same item in this onecitation; and
ibidme = true;
} else {
+ // everything else is definitely subsequent
suprame = true;
}
+ // conditions
var prev, prev_locator, prev_label, curr_locator, curr_label;
if (ibidme) {
if (k > 0) {
@@ -5373,20 +7127,39 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
curr_locator = mylocator;
}
}
+ // triage
if (ibidme && prev_locator && !curr_locator) {
ibidme = false;
suprame = true;
+
}
if (ibidme) {
if (!prev_locator && curr_locator) {
+ // (a) if the previous onecitation had no locator
+ // and this onecitation has one, use ibid+pages
item[1].position = CSL.POSITION_IBID_WITH_LOCATOR;
} else if (!prev_locator && !curr_locator) {
+ // (b) if the previous onecitation had no locator
+ // and this onecitation also has none, use ibid
item[1].position = CSL.POSITION_IBID;
+ //print("setting ibid in cmd_cite()");
} else if (prev_locator && curr_locator === prev_locator) {
+ // (c) if the previous onecitation had a locator
+ // (page number, etc.) and this onecitation has
+ // a locator that is identical, use ibid
+
item[1].position = CSL.POSITION_IBID;
+ //print("setting ibid in cmd_cite() [2]");
} else if (prev_locator && curr_locator && curr_locator !== prev_locator) {
+ // (d) if the previous onecitation had a locator,
+ // and this onecitation has one that differs,
+ // use ibid+pages
item[1].position = CSL.POSITION_IBID_WITH_LOCATOR;
} else {
+ // (e) if the previous onecitation had a locator
+ // and this onecitation has none, use subsequent
+ //
+ // ... and everything else would be subsequent also
ibidme = false; // just to be clear
suprame = true;
}
@@ -5401,6 +7174,7 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
if (first_ref[myid] != onecitation.properties.noteIndex) {
item[1]["first-reference-note-number"] = first_ref[myid];
if (this.registry.registry[myid]) {
+ // This is either the earliest recorded number, or the number of the current citation, whichever is smaller.
var oldFirst = this.registry.citationreg.citationsByItemId[myid][0].properties.noteIndex;
var newFirst = onecitation.properties.noteIndex;
this.registry.registry[myid]['first-reference-note-number'] = newFirst < oldFirst ? newFirst: oldFirst;
@@ -5449,6 +7223,8 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
for (var key in this.tmp.taintedItemIDs) {
if (this.tmp.taintedItemIDs.hasOwnProperty(key)) {
citations = this.registry.citationreg.citationsByItemId[key];
+ // Current citation may be tainted but will not exist
+ // during previewing.
if (citations) {
for (var i = 0, ilen = citations.length; i < ilen; i += 1) {
this.tmp.taintedCitationIDs[citations[i].citationID] = true;
@@ -5456,32 +7232,78 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
}
}
}
+
var ret = [];
if (flag === CSL.PREVIEW) {
+ // If previewing, return only a rendered string
+ //SNIP-START
+ if (this.debug) {
+ CSL.debug("****** start run processor *********");
+ }
+ //SNIP-END
try {
ret = this.process_CitationCluster.call(this, citation.sortedItems, citation);
} catch (e) {
CSL.error("Error running CSL processor for preview: "+e);
}
+
+ //SNIP-START
+ if (this.debug) {
+ CSL.debug("****** end run processor *********");
+ CSL.debug("****** start state restore *********");
+ }
+ //SNIP-END
+ // Wind out anything related to new items added for the preview.
+ // This means (1) names, (2) disambig state for affected items,
+ // (3) keys registered in the ambigs pool arrays, and (4) registry
+ // items.
+ //
+
+ // restore sliced citations
this.registry.citationreg.citationByIndex = oldCitationList;
this.registry.citationreg.citationById = {};
for (var i = 0, ilen = oldCitationList.length; i < ilen; i += 1) {
this.registry.citationreg.citationById[oldCitationList[i].citationID] = oldCitationList[i];
}
+
+ //SNIP-START
+ if (this.debug) {
+ CSL.debug("****** start final update *********");
+ }
+ //SNIP-END
var oldItemIds = [];
for (var i = 0, ilen = oldItemList.length; i < ilen; i += 1) {
oldItemIds.push("" + oldItemList[i].id);
}
this.updateItems(oldItemIds, null, null, true);
+ //SNIP-START
+ if (this.debug) {
+ CSL.debug("****** end final update *********");
+ }
+ //SNIP-END
+ // Roll back disambig states
for (var key in oldAmbigs) {
if (oldAmbigs.hasOwnProperty(key)) {
this.registry.registry[key].disambig = oldAmbigs[key];
}
}
+ //SNIP-START
+ if (this.debug) {
+ CSL.debug("****** end state restore *********");
+ }
+ //SNIP-END
} else {
+ // Rerun cites that have moved across citations or had a change
+ // in their number of subsequent references, so that disambiguate
+ // and subsequent-reference-count conditions are applied
+ // correctly in output.
for (var rerunAkey in rerunAkeys) {
this.disambiguate.run(rerunAkey, citation);
}
+ // Run taints only if not previewing
+ //
+ // Push taints to the return object
+ //
var obj;
for (var key in this.tmp.taintedCitationIDs) {
if (key == citation.citationID) {
@@ -5494,6 +7316,7 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
}
mycitation.sortedItems.sort(this.citation.srt.compareCompositeKeys);
}
+ // For error reporting
this.tmp.citation_pos = mycitation.properties.index;
this.tmp.citation_note_index = mycitation.properties.noteIndex;
this.tmp.citation_id = "" + mycitation.citationID;
@@ -5505,14 +7328,23 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
}
this.tmp.taintedItemIDs = {};
this.tmp.taintedCitationIDs = {};
+
+ // For error reporting again
this.tmp.citation_pos = citation.properties.index;
this.tmp.citation_note_index = citation.properties.noteIndex;
this.tmp.citation_id = "" + citation.citationID;
+
obj = [];
obj.push(citationsPre.length);
obj.push(this.process_CitationCluster.call(this, sortedItems, citation));
obj.push(citation.citationID);
ret.push(obj);
+ //
+ // note for posterity: Rhino and Spidermonkey produce different
+ // sort results for items with matching keys. That discrepancy
+ // turned up a subtle bug in the parallel detection code, trapped
+ // at line 266, above, and in line 94 of util_parallel.js.
+ //
ret.sort(function (a, b) {
if (a[0] > b[0]) {
return 1;
@@ -5522,13 +7354,19 @@ CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre,
return 0;
}
});
+ //
+ // In normal rendering, return is a list of two-part arrays, with the first element
+ // a citation index number, and the second the text to be inserted.
+ //
}
this.registry.return_data.citation_errors = this.tmp.citation_errors.slice();
return [this.registry.return_data, ret];
};
+
CSL.Engine.prototype.process_CitationCluster = function (sortedItems, citation) {
var str = "";
- this.parallel.StartCitation(sortedItems);
+ // Parallels must be evaluated in the calling function
+ //this.parallel.StartCitation(sortedItems);
if (citation && citation.properties && citation.properties.mode === "composite") {
citation.properties.mode = "author-only";
var firstChunk = CSL.getCitationCluster.call(this, sortedItems, citation);
@@ -5537,6 +7375,7 @@ CSL.Engine.prototype.process_CitationCluster = function (sortedItems, citation)
if (citation.properties.infix) {
this.output.append(citation.properties.infix);
secondChunk = this.output.string(this, this.output.queue);
+ // Had no idea this could return a single-element array! Go figure.
if ("object" === typeof secondChunk) {
secondChunk = secondChunk.join("");
}
@@ -5555,6 +7394,7 @@ CSL.Engine.prototype.process_CitationCluster = function (sortedItems, citation)
}
return str;
};
+
CSL.Engine.prototype.makeCitationCluster = function (rawList) {
var inputList, newitem, str, pos, len, item, Item;
inputList = [];
@@ -5565,6 +7405,7 @@ CSL.Engine.prototype.makeCitationCluster = function (rawList) {
item[key] = rawList[pos][key];
}
Item = this.retrieveItem("" + item.id);
+ // Code block is copied from processCitationCluster() above
if (this.opt.development_extensions.locator_label_parse) {
if (item.locator && ["bill","gazette","legislation","regulation","treaty"].indexOf(Item.type) === -1 && (!item.label || item.label === 'page')) {
var m = CSL.LOCATOR_LABELS_REGEXP.exec(item.locator);
@@ -5598,6 +7439,16 @@ CSL.Engine.prototype.makeCitationCluster = function (rawList) {
var str = CSL.getCitationCluster.call(this, inputList);
return str;
};
+
+
+/**
+ * Get the undisambiguated version of a cite, without decorations
+ *
This is used internally by the Registry.
+ *
+ * [object] CSL Item
+ * [object] disambiguation parameters
+ * [boolean] If true, include first-reference-note-number value in cite
+ */
CSL.getAmbiguousCite = function (Item, disambig, visualForm, item) {
var ret;
var flags = this.tmp.group_context.tip;
@@ -5607,7 +7458,10 @@ CSL.getAmbiguousCite = function (Item, disambig, visualForm, item) {
variable_success: flags.variable_success,
output_tip: flags.output_tip,
label_form: flags.label_form,
- parallel_conditions: flags.parallel_conditions,
+ parallel_condition: flags.parallel_condition,
+ parallel_result: flags.parallel_result,
+ no_repeat_condition: flags.no_repeat_condition,
+ parallel_repeats: flags.parallel_result,
condition: flags.condition,
force_suppress: flags.force_suppress,
done_vars: flags.done_vars.slice()
@@ -5621,10 +7475,12 @@ CSL.getAmbiguousCite = function (Item, disambig, visualForm, item) {
position: 1,
"near-note": true
};
+
if (item) {
itemSupp.locator = item.locator;
itemSupp.label = item.label;
}
+
if (this.registry.registry[Item.id]
&& this.registry.citationreg.citationsByItemId
&& this.registry.citationreg.citationsByItemId[Item.id]
@@ -5636,11 +7492,13 @@ CSL.getAmbiguousCite = function (Item, disambig, visualForm, item) {
}
this.tmp.area = "citation";
this.tmp.root = "citation";
- this.parallel.use_parallels = (this.parallel.use_parallels === true || this.parallel.use_parallels === null) ? null : false;
var origSuppressDecorations = this.tmp.suppress_decorations;
this.tmp.suppress_decorations = true;
this.tmp.just_looking = true;
+
+ // Do not reset shadow_numbers when running ambiguous cites
CSL.getCite.call(this, Item, itemSupp, null, false);
+ // !!!
for (var i=0,ilen=this.output.queue.length;iSplice evaluation is done during cite
+ * rendering, and this method returns the
+ * result. Evaluation requires three items
+ * of information from the preceding cite, if
+ * one is present: the names used; the years
+ * used; and the suffix appended to the
+ * citation. These details are copied into
+ * the state object before processing begins,
+ * and are cleared by the processor on
+ * completion of the run.
+ */
+
CSL.getSpliceDelimiter = function (last_locator, last_collapsed, pos) {
+ //print(pos + " after-collapse-delimiter="+this.citation.opt["after-collapse-delimiter"] + "\n cite_group_delimiter=" + this.tmp.use_cite_group_delimiter + "\n last_collapsed=" +last_collapsed + "\n have_collapsed=" +this.tmp.have_collapsed + "\n last_locator=" + last_locator)
if (undefined !== this.citation.opt["after-collapse-delimiter"]) {
if (last_locator) {
this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
@@ -5676,16 +7550,52 @@ CSL.getSpliceDelimiter = function (last_locator, last_collapsed, pos) {
if (this.tmp.have_collapsed && this.opt.xclass === "in-text" && this.opt.update_mode !== CSL.NUMERIC) {
this.tmp.splice_delimiter = ", ";
} else if (this.tmp.cite_locales[pos - 1]) {
+ //
+ // Must have a value to take effect. Use zero width space to force empty delimiter.
var alt_affixes = this.tmp.cite_affixes[this.tmp.area][this.tmp.cite_locales[pos - 1]];
if (alt_affixes && alt_affixes.delimiter) {
this.tmp.splice_delimiter = alt_affixes.delimiter;
}
} else if (!this.tmp.splice_delimiter) {
+ // This happens when no delimiter is set on cs:layout under cs:citation
this.tmp.splice_delimiter = "";
}
}
+
+/*
+ if (last_locator && "string" === typeof this.citation.opt["after-collapse-delimiter"]) {
+ this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
+ } else if (last_collapsed && !this.tmp.have_collapsed && "string" === typeof this.citation.opt["after-collapse-delimiter"]) {
+ this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
+ } else if (!last_collapsed && !this.tmp.have_collapsed && "string" === typeof this.citation.opt["after-collapse-delimiter"] && !this.citation.opt.collapse === "year-suffix") {
+ this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"];
+ } else if (this.tmp.use_cite_group_delimiter) {
+ this.tmp.splice_delimiter = this.citation.opt.cite_group_delimiter;
+ } else if (this.tmp.have_collapsed && this.opt.xclass === "in-text" && this.opt.update_mode !== CSL.NUMERIC) {
+ this.tmp.splice_delimiter = ", ";
+ } else if (this.tmp.cite_locales[pos - 1]) {
+ //
+ // Must have a value to take effect. Use zero width space to force empty delimiter.
+ var alt_affixes = this.tmp.cite_affixes[this.tmp.area][this.tmp.cite_locales[pos - 1]];
+ if (alt_affixes && alt_affixes.delimiter) {
+ this.tmp.splice_delimiter = alt_affixes.delimiter;
+ }
+ } else if (!this.tmp.splice_delimiter) {
+ // This happens when no delimiter is set on cs:layout under cs:citation
+ this.tmp.splice_delimiter = "";
+ }
+*/
+ // Paranoia
+ //if (!this.tmp.splice_delimiter) {
+ // this.tmp.splice_delimiter = "";
+ //}
return this.tmp.splice_delimiter;
};
+
+/*
+ * Compose individual cites into a single string, with
+ * flexible inter-cite splicing.
+ */
CSL.getCitationCluster = function (inputList, citation) {
var result, objects, myparams, len, pos, item, last_collapsed, params, empties, composite, compie, myblobs, Item, llen, ppos, obj, preceding_item, txt_esc, error_object, citationID, authorOnly, suppressAuthor;
var citation_prefix = "";
@@ -5712,16 +7622,23 @@ CSL.getCitationCluster = function (inputList, citation) {
this.tmp.last_years_used = [];
this.tmp.backref_index = [];
this.tmp.cite_locales = [];
+
var use_layout_prefix = this.output.checkNestedBrace.update(this.citation.opt.layout_prefix + citation_prefix);
+ //var use_layout_prefix = this.citation.opt.layout_prefix;
+
var suppressTrailingPunctuation = false;
if (this.opt.xclass === "note" && this.citation.opt.suppressTrailingPunctuation) {
suppressTrailingPunctuation = true;
}
if (citationID) {
+ //this.registry.citationreg.citationById[citationID].properties.backref_index = false;
+ //this.registry.citationreg.citationById[citationID].properties.backref_citation = false;
if (this.registry.citationreg.citationById[citationID].properties["suppress-trailing-punctuation"]) {
suppressTrailingPunctuation = true;
}
}
+
+ // Adjust locator positions if that looks like a sensible thing to do.
if (this.opt.xclass === "note") {
var parasets = [];
var lastTitle = false;
@@ -5734,6 +7651,7 @@ CSL.getCitationCluster = function (inputList, citation) {
var position = inputList[i][1].position;
var id = inputList[i][0].id;
if (title && type === "legal_case" && id !== lastID && position) {
+ // Start a fresh sublist if the item title does not match the last one
if (title !== lastTitle || parasets.length === 0) {
lst = [];
parasets.push(lst);
@@ -5744,11 +7662,13 @@ CSL.getCitationCluster = function (inputList, citation) {
lastPosition = position;
lastID = id;
}
+ // We now have a list of sublists, each w/matching titles
for (i=0, ilen=parasets.length; i < ilen; i += 1) {
lst = parasets[i];
if (lst.length < 2) {
continue;
}
+ // Get the locator in last position, but only if it's the only one in the set.
var locatorInLastPosition = lst.slice(-1)[0].locator;
if (locatorInLastPosition) {
for (var j=0, jlen=lst.length - 1; j < jlen; j += 1) {
@@ -5757,6 +7677,7 @@ CSL.getCitationCluster = function (inputList, citation) {
}
}
}
+ // move the locator here, if it's called for.
if (locatorInLastPosition) {
lst[0].locator = locatorInLastPosition;
delete lst.slice(-1)[0].locator;
@@ -5788,6 +7709,8 @@ CSL.getCitationCluster = function (inputList, citation) {
last_locator = !!inputList[pos-1][1].locator;
}
params = {};
+
+ // Reset shadow_numbers here, suppress reset in getCite()
this.tmp.shadow_numbers = {};
if (!this.tmp.just_looking && this.opt.hasPlaceholderTerm) {
var output = this.output;
@@ -5796,13 +7719,17 @@ CSL.getCitationCluster = function (inputList, citation) {
CSL.getAmbiguousCite.call(this, Item, null, false, item);
this.output = output;
}
+
this.tmp.in_cite_predecessor = false;
+ // true is to block reset of shadow numbers
if (pos > 0) {
CSL.getCite.call(this, Item, item, "" + inputList[(pos - 1)][0].id, true);
} else {
this.tmp.term_predecessor = false;
CSL.getCite.call(this, Item, item, null, true);
}
+
+ // Make a note of any errors
if (!this.tmp.cite_renders_content) {
error_object = {
citationID: "" + this.tmp.citation_id,
@@ -5814,17 +7741,19 @@ CSL.getCitationCluster = function (inputList, citation) {
};
this.tmp.citation_errors.push(error_object);
}
- if (pos === (inputList.length - 1)) {
- this.parallel.ComposeSet();
- }
params.splice_delimiter = CSL.getSpliceDelimiter.call(this, last_locator, last_collapsed, pos);
+ // XXX This appears to be superfluous.
if (item && item["author-only"]) {
this.tmp.suppress_decorations = true;
}
+
if (pos > 0) {
preceding_item = inputList[pos - 1][1];
- var precedingEndsInPeriodOrComma = preceding_item.suffix && [".", ","].indexOf(preceding_item.suffix.slice(-1)) > -1;
- var currentStartsWithPeriodOrComma = !preceding_item.suffix && item.prefix && [".", ","].indexOf(item.prefix.slice(0, 1)) > -1;
+
+ // XXX OR if preceding suffix is empty, and the current prefix begins with a full stop.
+
+ var precedingEndsInPeriodOrComma = preceding_item.suffix && [";", ".", ","].indexOf(preceding_item.suffix.slice(-1)) > -1;
+ var currentStartsWithPeriodOrComma = !preceding_item.suffix && item.prefix && [";", ".", ","].indexOf(item.prefix.slice(0, 1)) > -1;
if (precedingEndsInPeriodOrComma || currentStartsWithPeriodOrComma) {
var spaceidx = params.splice_delimiter.indexOf(" ");
if (spaceidx > -1 && !currentStartsWithPeriodOrComma) {
@@ -5836,28 +7765,44 @@ CSL.getCitationCluster = function (inputList, citation) {
}
params.suppress_decorations = this.tmp.suppress_decorations;
params.have_collapsed = this.tmp.have_collapsed;
+ //
+ // XXXXX: capture parameters to an array, which
+ // will be of the same length as this.output.queue,
+ // corresponding to each element.
+ //
myparams.push(params);
if (item["author-only"]) {
break;
}
}
- this.tmp.has_purged_parallel = false;
- this.parallel.PruneOutputQueue(this);
+
+ this.parallel.purgeGroupsIfParallel();
+ //
+ // output.queue is a simple array. do a slice
+ // of it to get each cite item, setting params from
+ // the array that was built in the preceding loop.
+ //
empties = 0;
myblobs = this.output.queue.slice();
+
var citation_suffix = "";
if (citation) {
citation_suffix = CSL.checkSuffixSpacePrepend(this, citation.properties.suffix);
}
var suffix = this.citation.opt.layout_suffix;
var last_locale = this.tmp.cite_locales[this.tmp.cite_locales.length - 1];
+ //
+ // Must have a value to take effect. Use zero width space to force empty suffix.
if (last_locale && this.tmp.cite_affixes[this.tmp.area][last_locale] && this.tmp.cite_affixes[this.tmp.area][last_locale].suffix) {
suffix = this.tmp.cite_affixes[this.tmp.area][last_locale].suffix;
}
if (CSL.TERMINAL_PUNCTUATION.slice(0, -1).indexOf(suffix.slice(0, 1)) > -1) {
suffix = suffix.slice(0, 1);
}
+ //print("=== FROM CITE ===");
suffix = this.output.checkNestedBrace.update(citation_suffix + suffix);
+
+
for (var i=0,ilen=this.output.queue.length;i -1
+ // && this.tmp.last_chr === use_layout_suffix.slice(0, 1)) {
+ // use_layout_suffix = use_layout_suffix.slice(1);
+ //}
if (!this.tmp.suppress_decorations) {
len = this.citation.opt.layout_decorations.length;
for (pos = 0; pos < len; pos += 1) {
params = this.citation.opt.layout_decorations[pos];
+ // The "normal" formats in some output modes expect
+ // a superior nested decoration environment, and
+ // so should produce no output here.
if (params[1] === "normal") {
continue;
}
@@ -5985,15 +7950,26 @@ CSL.getCitationCluster = function (inputList, citation) {
this.tmp.suppress_decorations = false;
if (!result) {
if (this.opt.development_extensions.throw_on_empty) {
- var error = new Error("Citation would render no content");
- error.code = "ECSEMPTY";
- throw error;
+ CSL.error("Citation would render no content");
} else {
result = "[NO_PRINTED_FORM]"
}
}
return result;
};
+
+/*
+ * Render a single cite item.
+ *
+ * This is called on the state object, with a single
+ * Item as input. It iterates exactly once over the style
+ * citation tokens, and leaves the result of rendering in
+ * the top-level list in the relevant *.opt.output
+ * stack, as a list item consisting of a single string.
+ *
+ * (This is dual-purposed for generating individual
+ * entries in a bibliography.)
+ */
CSL.getCite = function (Item, item, prevItemID, blockShadowNumberReset) {
var next, error_object;
var areaOrig = this.tmp.area;
@@ -6002,16 +7978,19 @@ CSL.getCite = function (Item, item, prevItemID, blockShadowNumberReset) {
}
this.tmp.cite_renders_content = false;
this.tmp.probably_rendered_something = false;
- this.parallel.StartCite(Item, item, prevItemID);
+
CSL.citeStart.call(this, Item, item, blockShadowNumberReset);
next = 0;
this.tmp.name_node = {};
this.nameOutput = new CSL.NameOutput(this, Item, item);
+
+ // rerun?
while (next < this[this.tmp.area].tokens.length) {
next = CSL.tokenExec.call(this, this[this.tmp.area].tokens[next], Item, item);
}
+
CSL.citeEnd.call(this, Item, item);
- this.parallel.CloseCite(this);
+ // Odd place for this, but it seems to fit here
if (!this.tmp.cite_renders_content && !this.tmp.just_looking) {
if (this.tmp.area === "bibliography") {
error_object = {
@@ -6025,6 +8004,8 @@ CSL.getCite = function (Item, item, prevItemID, blockShadowNumberReset) {
this.tmp.area = areaOrig;
return "" + Item.id;
};
+
+
CSL.citeStart = function (Item, item, blockShadowNumberReset) {
if (!blockShadowNumberReset) {
this.tmp.shadow_numbers = {};
@@ -6039,6 +8020,7 @@ CSL.citeStart = function (Item, item, blockShadowNumberReset) {
}
this.tmp.lastchr = "";
if (this.tmp.area === "citation" && this.citation.opt.collapse && this.citation.opt.collapse.length) {
+ //this.tmp.have_collapsed = "year";
this.tmp.have_collapsed = true;
} else {
this.tmp.have_collapsed = false;
@@ -6065,6 +8047,7 @@ CSL.citeStart = function (Item, item, blockShadowNumberReset) {
}
}
if (this.opt["disambiguate-add-givenname"]) {
+ // This is weird and delicate and not fully understood
this.tmp.disambig_request = this.tmp.disambig_settings;
this.tmp.disambig_settings.givens = this.registry.registry[Item.id].disambig.givens.slice();
this.tmp.disambig_request.givens = this.registry.registry[Item.id].disambig.givens.slice();
@@ -6078,22 +8061,33 @@ CSL.citeStart = function (Item, item, blockShadowNumberReset) {
}
}
}
+
this.tmp.names_used = [];
this.tmp.nameset_counter = 0;
this.tmp.years_used = [];
this.tmp.names_max.clear();
+
this.tmp.splice_delimiter = this[this.tmp.area].opt.layout_delimiter;
+ //this.tmp.splice_delimiter = this[this.tmp.area].opt.delimiter;
+
this.bibliography_sort.keys = [];
this.citation_sort.keys = [];
+
this.tmp.has_done_year_suffix = false;
this.tmp.last_cite_locale = false;
+ // SAVE PARAMETERS HERE, IF APPROPRIATE
+ // (promiscuous addition of global parameters => death by a thousand cuts)
if (!this.tmp.just_looking && item && !item.position && this.registry.registry[Item.id]) {
this.tmp.disambig_restore = CSL.cloneAmbigConfig(this.registry.registry[Item.id].disambig);
}
+ // XXX This only applied to the "number" variable itself? Huh?
+ //this.setNumberLabels(Item);
this.tmp.first_name_string = false;
this.tmp.authority_stop_last = 0;
};
+
CSL.citeEnd = function (Item, item) {
+ // RESTORE PARAMETERS IF APPROPRIATE
if (this.tmp.disambig_restore && this.registry.registry[Item.id]) {
this.registry.registry[Item.id].disambig.names = this.tmp.disambig_restore.names.slice();
this.registry.registry[Item.id].disambig.givens = this.tmp.disambig_restore.givens.slice();
@@ -6102,7 +8096,9 @@ CSL.citeEnd = function (Item, item) {
}
}
this.tmp.disambig_restore = false;
+
if (item && item.suffix) {
+ //this.tmp.last_suffix_used = this.tmp.suffix.value();
this.tmp.last_suffix_used = item.suffix;
} else {
this.tmp.last_suffix_used = "";
@@ -6110,24 +8106,38 @@ CSL.citeEnd = function (Item, item) {
this.tmp.last_years_used = this.tmp.years_used.slice();
this.tmp.last_names_used = this.tmp.names_used.slice();
this.tmp.cut_var = false;
+
+ // This is a hack, in a way; I have lost track of where
+ // the disambig (name rendering) settings used for rendering work their way
+ // into the registry. This resets defaults to the subsequent form,
+ // when first cites are rendered.
+ //if (this.tmp.disambig_restore && this.registry.registry[Item.id]) {
+ // this.registry.registry[Item.id].disambig = this.tmp.disambig_restore;
+ //}
+ //this.tmp.disambig_restore = false;
this.tmp.disambig_request = false;
+
this.tmp.cite_locales.push(this.tmp.last_cite_locale);
+
if (this.tmp.issued_date && this.tmp.renders_collection_number) {
var buf = [];
for (var i = this.tmp.issued_date.list.length - 1; i > this.tmp.issued_date.pos; i += -1) {
buf.push(this.tmp.issued_date.list.pop());
}
+ // Throw away the unwanted blob
this.tmp.issued_date.list.pop();
+ // Put the other stuff back
for (i = buf.length - 1; i > -1; i += -1) {
this.tmp.issued_date.list.push(buf.pop());
}
- if (this.parallel.use_parallels) {
- this.parallel.cite.issued = false;
- }
}
this.tmp.issued_date = false;
this.tmp.renders_collection_number = false;
+
};
+
+/*global CSL: true */
+
CSL.Engine.prototype.makeBibliography = function (bibsection) {
var debug, ret, params, maxoffset, item, len, pos, tok, tokk, tokkk, entry_ids, entry_strings;
debug = false;
@@ -6153,6 +8163,7 @@ CSL.Engine.prototype.makeBibliography = function (bibsection) {
}
}
}
+ // API change: added in version 1.0.51
if (!this.bibliography.tokens.length) {
return false;
}
@@ -6160,10 +8171,36 @@ CSL.Engine.prototype.makeBibliography = function (bibsection) {
this.opt.citation_number_slug = bibsection;
bibsection = false;
}
+ //SNIP-START
+ if (debug) {
+ len = this.bibliography.tokens.length;
+ for (pos = 0; pos < len; pos += 1) {
+ tok = this.bibliography.tokens[pos];
+ CSL.debug("bibtok: " + tok.name);
+ }
+ CSL.debug("---");
+ len = this.citation.tokens.length;
+ for (pos = 0; pos < len; pos += 1) {
+ tokk = this.citation.tokens[pos];
+ CSL.debug("cittok: " + tok.name);
+ }
+ CSL.debug("---");
+ len = this.bibliography_sort.tokens.length;
+ for (pos = 0; pos < len; pos += 1) {
+ tokkk = this.bibliography_sort.tokens[pos];
+ CSL.debug("bibsorttok: " + tok.name);
+ }
+ }
+ //SNIP-END
+
+ // For paged returns
ret = CSL.getBibliographyEntries.call(this, bibsection);
entry_ids = ret[0];
entry_strings = ret[1];
+
+ // For paged returns
var done = ret[2];
+
params = {
"maxoffset": 0,
"entryspacing": this.bibliography.opt["entry-spacing"],
@@ -6189,9 +8226,14 @@ CSL.Engine.prototype.makeBibliography = function (bibsection) {
}
params.bibstart = this.fun.decorate.bibstart;
params.bibend = this.fun.decorate.bibend;
+
this.opt.citation_number_slug = false;
return [params, entry_strings];
};
+
+/*
+ * Compose individual cites into a single string.
+ */
CSL.getBibliographyEntries = function (bibsection) {
var ret, input, include, anymatch, allmatch, bib_entry, res, item, spec, lllen, pppos, topblobs, entry_item_ids, debug, collapse_parallel, i, ilen, siblings, skips, sortedItems, eyetem, entry_item_data, j, jlen;
ret = [];
@@ -6201,11 +8243,16 @@ CSL.getBibliographyEntries = function (bibsection) {
this.tmp.last_rendered_name = false;
this.tmp.bibliography_errors = [];
this.tmp.bibliography_pos = 0;
+
+ // For paged returns: disable generated entries and
+ // do not fetch full items as a batch (input variable
+ // consists of ids only in this case)
if (bibsection && bibsection.page_start && bibsection.page_length) {
input = this.registry.getSortedIds();
} else {
input = this.refetchItems(this.registry.getSortedIds());
}
+
this.tmp.disambig_override = true;
function eval_string(a, b) {
if (a === b) {
@@ -6239,7 +8286,10 @@ CSL.getBibliographyEntries = function (bibsection) {
}
}
}
+
skips = {};
+
+ // For paged returns
var page_item_count;
if (bibsection && bibsection.page_start && bibsection.page_length) {
page_item_count = 0;
@@ -6252,8 +8302,12 @@ CSL.getBibliographyEntries = function (bibsection) {
}
}
}
+
var processed_item_ids = [];
+
for (i = 0, ilen = input.length; i < ilen; i += 1) {
+
+ // For paged returns
if (bibsection && bibsection.page_start && bibsection.page_length) {
if (skips[input[i]]) {
continue;
@@ -6271,6 +8325,9 @@ CSL.getBibliographyEntries = function (bibsection) {
if (bibsection) {
include = true;
if (bibsection.include) {
+ //
+ // Opt-in: these are OR-ed.
+ //
include = false;
for (j = 0, jlen = bibsection.include.length; j < jlen; j += 1) {
spec = bibsection.include[j];
@@ -6280,6 +8337,9 @@ CSL.getBibliographyEntries = function (bibsection) {
}
}
} else if (bibsection.exclude) {
+ //
+ // Opt-out: these are also OR-ed.
+ //
anymatch = false;
for (j = 0, jlen = bibsection.exclude.length; j < jlen; j += 1) {
spec = bibsection.exclude[j];
@@ -6292,6 +8352,9 @@ CSL.getBibliographyEntries = function (bibsection) {
include = false;
}
} else if (bibsection.select) {
+ //
+ // Multiple condition opt-in: these are AND-ed.
+ //
include = false;
allmatch = true;
for (j = 0, jlen = bibsection.select.length; j < jlen; j += 1) {
@@ -6305,6 +8368,9 @@ CSL.getBibliographyEntries = function (bibsection) {
}
}
if (bibsection.quash) {
+ //
+ // Stop criteria: These are AND-ed.
+ //
allmatch = true;
for (j = 0, jlen = bibsection.quash.length; j < jlen; j += 1) {
spec = bibsection.quash[j];
@@ -6320,6 +8386,11 @@ CSL.getBibliographyEntries = function (bibsection) {
continue;
}
}
+ //SNIP-START
+ if (debug) {
+ CSL.debug("BIB: " + item.id);
+ }
+ //SNIP-END
bib_entry = new CSL.Token("group", CSL.START);
bib_entry.decorations = [["@bibliography", "entry"]].concat(this.bibliography.opt.layout_decorations);
this.output.startTag("bib_entry", bib_entry);
@@ -6328,38 +8399,61 @@ CSL.getBibliographyEntries = function (bibsection) {
} else {
this.output.current.value().system_id = item.id;
}
- sortedItems = [[{id: "" + item.id}, item]];
+
+ // 2019-06-25 Hacked to conform to new parallels evaluation method
entry_item_ids = [];
if (this.registry.registry[item.id].master
&& !(bibsection && bibsection.page_start && bibsection.page_length)) {
+
+ sortedItems = [[item, {id: item.id}]];
+ var siblings = this.registry.registry[item.id].siblings;
+ for (var j=0,jlen=siblings.length; j init <--");
+ }
+ //SNIP-END
this.registry.init(idList);
+
if (rerun_ambigs) {
for (var ambig in this.registry.ambigcites) {
this.registry.ambigsTouched[ambig] = true;
}
}
+
this.registry.dodeletes(this.registry.myhash);
+
this.registry.doinserts(this.registry.mylist);
+
this.registry.dorefreshes();
+
+ // *** affects reflist
this.registry.rebuildlist(nosort);
+
this.registry.setsortkeys();
+
+ // taints always
this.registry.setdisambigs();
+
+ // *** affects reflist
this.registry.sorttokens(nosort);
+
+ // *** affects reflist
+ // taints if numbered style
this.registry.renumber();
+
+ // taints always
+ //this.registry.yearsuffix();
+
this.tmp.extension = oldExtension;
this.tmp.area = oldArea;
this.tmp.root = oldRoot;
+
return this.registry.getSortedIds();
};
+
CSL.Engine.prototype.updateUncitedItems = function (idList, nosort) {
var idHash;
var oldArea = this.tmp.area;
@@ -6572,6 +8757,7 @@ CSL.Engine.prototype.updateUncitedItems = function (idList, nosort) {
this.tmp.root = "citation";
this.tmp.extension = "";
this.tmp.loadedItemIDs = {};
+ // This should be a utility function
if (!idList) {
idList = [];
}
@@ -6589,20 +8775,38 @@ CSL.Engine.prototype.updateUncitedItems = function (idList, nosort) {
}
}
}
+
+ // prepare extended list of items
this.registry.init(idList, true);
+
+ // Use purge instead of delete.
+ // this.registry.dodeletes(this.registry.myhash);
this.registry.dopurge(idHash);
+
+ // everything else is the same as updateItems()
this.registry.doinserts(this.registry.mylist);
+
this.registry.dorefreshes();
+
this.registry.rebuildlist(nosort);
+
this.registry.setsortkeys();
+
this.registry.setdisambigs();
+
this.registry.sorttokens(nosort);
+
this.registry.renumber();
+
this.tmp.extension = oldExtension;
this.tmp.area = oldArea;
this.tmp.root = oldRoot;
+
return this.registry.getSortedIds();
};
+
+/*global CSL: true */
+
CSL.localeResolve = function (langstr, defaultLocale) {
var ret, langlst;
if (!defaultLocale) {
@@ -6612,9 +8816,13 @@ CSL.localeResolve = function (langstr, defaultLocale) {
langstr = defaultLocale;
}
ret = {};
+ //if ("undefined" === typeof langstr) {
+ // langstr = "en_US";
+ //}
langlst = langstr.split(/[\-_]/);
ret.base = CSL.LANG_BASES[langlst[0]];
if ("undefined" === typeof ret.base) {
+ //CSL.debug("Warning: unknown locale "+langstr+", setting fallback to "+defaultLocale);
return {base:defaultLocale, best:langstr, bare:langlst[0]};
}
if (langlst.length === 1) {
@@ -6629,6 +8837,8 @@ CSL.localeResolve = function (langstr, defaultLocale) {
ret.bare = langlst[0];
return ret;
};
+
+// Use call to invoke this.
CSL.Engine.prototype.localeConfigure = function (langspec, beShy) {
var localexml;
if (beShy && this.locale[langspec.best]) {
@@ -6674,39 +8884,64 @@ CSL.Engine.prototype.localeConfigure = function (langspec, beShy) {
this.opt.lang = this.opt.lang.toLowerCase();
}
};
+
+//
+// XXXXX: Got it. The locales objects need to be reorganized,
+// with a top-level local specifier, and terms, opts, dates
+// below.
+//
CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) {
var blob, locale, nodes, attributes, pos, term, form, termname, styleopts, date, attrname, len, genderform, target, i, ilen;
lang_in = lang_in.replace("_", "-");
lang_out = lang_out.replace("_", "-");
+
if (this.opt.development_extensions.normalize_lang_keys_to_lowercase) {
lang_in = lang_in.toLowerCase();
lang_out = lang_out.toLowerCase();
}
+
if (!this.locale[lang_out]) {
this.locale[lang_out] = {};
this.locale[lang_out].terms = {};
this.locale[lang_out].opts = {};
+ // Set default skip words. Can be overridden in locale by attribute on style-options node.
this.locale[lang_out].opts["skip-words"] = CSL.SKIP_WORDS;
+ // Initialise leading noise word to false. Actual assignment is below. Empty by default, can be overridden in locale by attribute on style-options node.
if (!this.locale[lang_out].opts["leading-noise-words"]) {
this.locale[lang_out].opts["leading-noise-words"] = [];
}
this.locale[lang_out].dates = {};
+ // For ordinals
this.locale[lang_out].ord = {'1.0.1':false,keys:{}};
this.locale[lang_out]["noun-genders"] = {};
}
+
+ //
+ // Xml: Test if node is "locale" (nb: ns declarations need to be invoked
+ // on every access to the xml object; bundle this with the functions
+ //
locale = myxml.makeXml();
if (myxml.nodeNameIs(myxml.dataObj, 'locale')) {
locale = myxml.dataObj;
} else {
+ //
+ // Xml: get a list of all "locale" nodes
+ //
nodes = myxml.getNodesByName(myxml.dataObj, "locale");
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) {
locale = blob;
break;
}
}
}
+ //
+ // Xml: get a list of any cs:type nodes within locale
+ //
nodes = myxml.getNodesByName(locale, 'type');
for (i = 0, ilen = myxml.numberofnodes(nodes); i < ilen; i += 1) {
var typenode = nodes[i];
@@ -6714,6 +8949,12 @@ CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) {
var gender = myxml.getAttributeValue(typenode, 'gender');
this.opt.gender[type] = gender;
}
+ //
+ // Xml: get a list of term nodes within locale
+ //
+
+ // If we are setting CSL 1.0.1 ordinals inside a style, wipe the
+ // slate clean and start over.
var hasCslOrdinals101 = myxml.getNodesByName(locale, 'term', 'ordinal').length;
if (hasCslOrdinals101) {
for (var key in this.locale[lang_out].ord.keys) {
@@ -6721,12 +8962,17 @@ CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) {
}
this.locale[lang_out].ord = {"1.0.1":false,keys:{}};
}
+
nodes = myxml.getNodesByName(locale, 'term');
+ // Collect ordinals info as for 1.0.1, but save only if 1.0.1 toggle triggers
var ordinals101 = {"last-digit":{},"last-two-digits":{},"whole-number":{}};
var ordinals101_toggle = false;
var genderized_terms = {};
for (pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) {
term = nodes[pos];
+ //
+ // Xml: get string value of attribute
+ //
termname = myxml.getAttributeValue(term, 'name');
if (termname === "sub verbo") {
termname = "sub-verbo";
@@ -6762,15 +9008,26 @@ CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) {
}
form = "long";
genderform = false;
+ //
+ // Xml: get string value of form attribute, if any
+ //
if (myxml.getAttributeValue(term, 'form')) {
form = myxml.getAttributeValue(term, 'form');
}
+ //
+ // Xml: get string value of gender attribute, if any
+ //
if (myxml.getAttributeValue(term, 'gender-form')) {
genderform = myxml.getAttributeValue(term, 'gender-form');
}
+ //
+ // Xml: set global gender assignment for variable associated
+ // with term name
+ //
if (myxml.getAttributeValue(term, 'gender')) {
this.locale[lang_out]["noun-genders"][termname] = myxml.getAttributeValue(term, 'gender');
}
+ // Work on main segment or gender-specific sub-segment as appropriate
if (genderform) {
this.locale[lang_out].terms[termname][genderform] = {};
this.locale[lang_out].terms[termname][genderform][form] = [];
@@ -6780,16 +9037,31 @@ CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) {
this.locale[lang_out].terms[termname][form] = [];
target = this.locale[lang_out].terms[termname];
}
+ //
+ // Xml: test of existence of node
+ //
if (myxml.numberofnodes(myxml.getNodesByName(term, 'multiple'))) {
+ //
+ // Xml: get string value of attribute, plus
+ // Xml: get string value of node content
+ //
target[form][0] = myxml.getNodeValue(term, 'single');
if (target[form][0].indexOf("%s") > -1) {
this.opt.hasPlaceholderTerm = true;
}
+ //
+ // Xml: get string value of attribute, plus
+ // Xml: get string value of node content
+ //
target[form][1] = myxml.getNodeValue(term, 'multiple');
if (target[form][1].indexOf("%s") > -1) {
this.opt.hasPlaceholderTerm = true;
}
} else {
+ //
+ // Xml: get string value of attribute, plus
+ // Xml: get string value of node content
+ //
target[form] = myxml.getNodeValue(term);
if (target[form].indexOf("%s") > -1) {
this.opt.hasPlaceholderTerm = true;
@@ -6802,6 +9074,8 @@ CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) {
if (!this.locale[lang_out].terms.supplement["long"]) {
this.locale[lang_out].terms.supplement["long"] = ["supplement", "supplements"];
}
+ // If locale had a CSL 1.0.1-style ordinal definition, install the logic object
+ // and iterate over gendered terms, filling in default values for use by getTerm.
if (ordinals101_toggle) {
for (var ikey in genderized_terms) {
var gender_segments = {};
@@ -6815,10 +9089,13 @@ CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) {
}
if (!form_segments) {
if (gender_segments.feminine) {
+ // Link each feminine form segment to default
+ // (no need to filter, these will not have gender segments mixed in)
for (var jkey in gender_segments.feminine) {
this.locale[lang_out].terms[ikey][jkey] = gender_segments.feminine[jkey];
}
} else if (gender_segments.masculine) {
+ // Otherwise link each masculine form segment to default
for (var jkey in gender_segments.masculine) {
this.locale[lang_out].terms[ikey][jkey] = gender_segments.masculine[jkey];
}
@@ -6827,6 +9104,9 @@ CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) {
}
this.locale[lang_out].ord['1.0.1'] = ordinals101;
}
+
+ // Iterate over main segments, and fill in any holes in gender-specific data
+ // sub-segments
for (termname in this.locale[lang_out].terms) {
for (i = 0, ilen = 2; i < ilen; i += 1) {
genderform = CSL.GENDERS[i];
@@ -6839,21 +9119,29 @@ CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) {
}
}
}
+ //
+ // Xml: get list of nodes by node type
+ //
nodes = myxml.getNodesByName(locale, 'style-options');
for (pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) {
if (true) {
styleopts = nodes[pos];
+ //
+ // Xml: get list of attributes on a node
+ //
attributes = myxml.attributes(styleopts);
for (attrname in attributes) {
if (attributes.hasOwnProperty(attrname)) {
if (attrname === "@punctuation-in-quote" || attrname === "@limit-day-ordinals-to-day-1") {
if (attributes[attrname] === "true") {
+ // trim off leading @
this.locale[lang_out].opts[attrname.slice(1)] = true;
} else {
+ // trim off leading @
this.locale[lang_out].opts[attrname.slice(1)] = false;
}
} else if (attrname === "@jurisdiction-preference") {
- var jurisdiction_preference = attributes[attrname].split(/\s*,\s*/);
+ var jurisdiction_preference = attributes[attrname].split(/\s+/);
this.locale[lang_out].opts[attrname.slice(1)] = jurisdiction_preference;
} else if (attrname === "@skip-words") {
var skip_words = attributes[attrname].split(/\s*,\s*/);
@@ -6862,18 +9150,21 @@ CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) {
var val = attributes[attrname].split(/\s*,\s*/);
this.locale[lang_out].opts["leading-noise-words"] = val;
} else if (attrname === "@name-as-sort-order") {
+ // Fallback is okay here.
this.locale[lang_out].opts["name-as-sort-order"] = {};
var lst = attributes[attrname].split(/\s+/);
for (var i=0,ilen=lst.length;i -1) {
+ //
+ // Use ghost dateput queue
+ //
if ("" + value_end !== "0") {
+ //
+ // XXXXX: It's a workaround. It's ugly.
+ // There's another one above.
+ //
if (state.dateput.queue.length === 0) {
first_date = true;
}
@@ -7301,6 +9777,7 @@ CSL.Node["date-part"] = {
} else {
state.output.append(value, this);
}
+
if (bc) {
state.output.append(bc);
}
@@ -7308,10 +9785,23 @@ CSL.Node["date-part"] = {
state.output.append(ad);
}
state.output.closeLevel();
+ //state.output.endTag();
} else if ("month" === this.strings.name) {
+ // XXX The simpler solution here will be to
+ // directly install season and season_end on
+ // month, with a value of 13, 14, 15, 16, or
+ // (to allow correct ranging with Down Under
+ // dates) 17 or 18. That will allow ranging
+ // to take place in the normal way. With this
+ // "approach", it doesn't.
+ //
+ // No value for this target variable
+ //
if (state.tmp.date_object.season) {
value = "" + state.tmp.date_object.season;
if (value && value.match(/^[1-4]$/)) {
+ // XXXXXXXXXXXXXXXXXXX was replace([false, false, true]);
+ //state.tmp.group_context.replace([false, false, true]);
state.tmp.group_context.tip.variable_success = true;
state.output.append(state.getTerm(("season-0" + value)), this);
} else if (value) {
@@ -7324,6 +9814,7 @@ CSL.Node["date-part"] = {
if (state.registry.registry[Item.id] && state.registry.registry[Item.id].disambig.year_suffix !== false && !state.tmp.has_done_year_suffix) {
state.tmp.has_done_year_suffix = true;
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);
this.successor_prefix = state[state.build.area].opt.layout_delimiter;
this.splice_prefix = state[state.build.area].opt.layout_delimiter;
@@ -7343,12 +9834,20 @@ CSL.Node["date-part"] = {
state.output.append(number, "literal");
}
}
+
};
this.execs.push(func);
target.push(this);
}
};
+
+
+
+/*global CSL: true */
+
CSL.Node["else-if"] = {
+ //
+ // these function are the same as those in if, might just clone
build: function (state, target) {
CSL.Conditions.TopNode.call(this, state, target);
target.push(this);
@@ -7357,6 +9856,9 @@ CSL.Node["else-if"] = {
CSL.Conditions.Configure.call(this, state, pos);
}
};
+
+/*global CSL: true */
+
CSL.Node["else"] = {
build: function (state, target) {
target.push(this);
@@ -7367,6 +9869,10 @@ CSL.Node["else"] = {
}
}
};
+
+
+/*global CSL: true */
+
CSL.Node["et-al"] = {
build: function (state, target) {
if (state.build.area === "citation" || state.build.area === "bibliography") {
@@ -7381,6 +9887,9 @@ CSL.Node["et-al"] = {
target.push(this);
}
};
+
+/*global CSL: true */
+
CSL.Node.group = {
build: function (state, target, realGroup) {
var func, execs, done_vars;
@@ -7393,28 +9902,38 @@ CSL.Node.group = {
if (!this.juris) {
target.push(this);
}
+
+ // newoutput
func = function (state) {
state.output.startTag("group", this);
+
if (this.strings.label_form_override) {
if (!state.tmp.group_context.tip.label_form) {
state.tmp.group_context.tip.label_form = this.strings.label_form_override;
}
}
+
if (this.strings.label_capitalize_if_first_override) {
if (!state.tmp.group_context.tip.label_capitalize_if_first) {
state.tmp.group_context.tip.label_capitalize_if_first = this.strings.label_capitalize_if_first_override;
}
}
+
if (this.realGroup) {
var condition = false;
var force_suppress = false;
+
+ // XXX Can we do something better for length here?
if (state.tmp.group_context.mystack.length) {
state.output.current.value().parent = state.tmp.group_context.tip.output_tip;
}
+
+ // fieldcontextflag
var label_form = state.tmp.group_context.tip.label_form;
if (!label_form) {
label_form = this.strings.label_form_override;
}
+
var label_capitalize_if_first = state.tmp.group_context.tip.label_capitalize_if_first;
if (!label_capitalize_if_first) {
label_capitalize_if_first = this.strings.label_capitalize_if_first;
@@ -7422,6 +9941,7 @@ CSL.Node.group = {
if (state.tmp.group_context.tip.condition) {
condition = state.tmp.group_context.tip.condition;
force_suppress = state.tmp.group_context.tip.force_suppress;
+ //force_suppress: false;
} else if (this.strings.reject) {
condition = {
test: this.strings.reject,
@@ -7436,6 +9956,14 @@ CSL.Node.group = {
};
done_vars = [];
}
+ // CONDITION
+ //if (!state.tmp.just_looking) {
+ // print(" pushing condition[" + state.tmp.group_context.mystack.length + "]: "+condition+" "+force_suppress);
+ //}
+ //if (!state.tmp.just_looking) {
+ // var params = ["variable_success", "force_suppress","term_intended", "variable_attempt"]
+ // print("PUSH parent="+JSON.stringify(state.tmp.group_context.tip, params))
+ //}
state.tmp.group_context.push({
old_term_predecessor: state.tmp.term_predecessor,
term_intended: false,
@@ -7445,19 +9973,38 @@ CSL.Node.group = {
output_tip: state.output.current.tip,
label_form: label_form,
label_capitalize_if_first: label_capitalize_if_first,
- parallel_conditions: this.strings.set_parallel_condition,
+ parallel_condition: this.strings.set_parallel_condition,
+ no_repeat_condition: this.strings.set_no_repeat_condition,
+ parallel_result: undefined,
+ parallel_repeats: undefined,
condition: condition,
force_suppress: force_suppress,
done_vars: state.tmp.group_context.tip.done_vars.slice()
});
+ //if (!state.tmp.just_looking) {
+ // print(" flags="+JSON.stringify(state.tmp.group_context.tip, params))
+ //}
}
};
+ //
+ // Paranoia. Assure that this init function is the first executed.
execs = [];
execs.push(func);
this.execs = execs.concat(this.execs);
+
+ // "Special handling" for nodes that contain only
+ // publisher and place, with no affixes. For such
+ // nodes only, parallel publisher/place pairs
+ // will be parsed out and properly joined, piggybacking on
+ // join parameters set on cs:citation or cs:bibliography.
if (this.strings["has-publisher-and-publisher-place"]) {
+ // Pass variable string values to the closing
+ // tag via a global, iff they conform to expectations.
state.build["publisher-special"] = true;
if (this.strings["subgroup-delimiter"]) {
+ // Set the handling function only if name-delimiter
+ // is set on the parent cs:citation or cs:bibliography
+ // node.
func = function (state, Item) {
if (Item.publisher && Item["publisher-place"]) {
var publisher_lst = Item.publisher.split(/;\s*/);
@@ -7473,18 +10020,48 @@ CSL.Node.group = {
this.execs.push(func);
}
}
+
if (this.juris) {
+ // "Special handling" for jurisdiction macros
+ // We try to instantiate these as standalone token lists.
+ // If available, the token list is executed,
+ // the result is written directly into output,
+ // and control returns here.
+
+ // So we'll have something like this:
+ // * expandMacro() in util_node.js flags juris- macros
+ // on build. [DONE]
+ // * Those are picked up here, and
+ // - A runtime function attempts to fetch and instantiate
+ // the macros in separate token lists under a segment
+ // opened for the jurisdiction. We assume that the
+ // jurisdiction has a full set of macros. That will need
+ // to be enforced by validation. [DONE HERE, function is TODO]
+ // - Success or failure is marked in a runtime flag object
+ // (in citeproc.opt). [DONE]
+ // - After the instantiation function comes a test, for
+ // juris- macros only, which either runs diverted code,
+ // or proceeds as per normal through the token list. [TODO]
+ // I think that's all there is to it.
+
+ // Code for fetching an instantiating?
+
var choose_start = new CSL.Token("choose", CSL.START);
CSL.Node.choose.build.call(choose_start, state, target);
+
var if_start = new CSL.Token("if", CSL.START);
+
func = (function (macroName) {
return function (Item) {
if (!state.sys.retrieveStyleModule || !CSL.MODULE_MACROS[macroName] || !Item.jurisdiction) {
return false;
}
var jurisdictionList = state.getJurisdictionList(Item.jurisdiction);
+ // Set up a list of jurisdictions here, we will reuse it
if (!state.opt.jurisdictions_seen[jurisdictionList[0]]) {
var res = state.retrieveAllStyleModules(jurisdictionList);
+ // 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] = {};
@@ -7512,11 +10089,17 @@ CSL.Node.group = {
}
macroCount++;
state.juris[jurisdiction][myName] = [];
+ // Must use the same XML parser for style and modules.
state.buildTokenLists(myNodes[i], state.juris[jurisdiction][myName]);
state.configureTokenList(state.juris[jurisdiction][myName]);
}
+ //if (macroCount < Object.keys(CSL.MODULE_MACROS).length) {
+ // var missing = [];
+ // throw "CSL ERROR: Incomplete jurisdiction style module for: " + jurisdiction;
+ //}
}
}
+ // Identify the best jurisdiction for the item and return true, otherwise return false
for (var i=0,ilen=jurisdictionList.length;i -1) {
state.tmp.done_vars = state.tmp.done_vars.slice(0, i).concat(state.tmp.done_vars.slice(i+1));
@@ -7612,6 +10221,7 @@ CSL.Node.group = {
}
};
this.execs.push(func);
+
if (this.juris) {
var else_end = new CSL.Token("else", CSL.END);
CSL.Node["else"].build.call(else_end, state, target);
@@ -7619,6 +10229,7 @@ CSL.Node.group = {
CSL.Node.choose.build.call(choose_end, state, target);
}
}
+
if (this.tokentype === CSL.END) {
if (!this.juris) {
target.push(this);
@@ -7630,6 +10241,10 @@ CSL.Node.group = {
}
}
};
+
+
+/*global CSL: true */
+
CSL.Node["if"] = {
build: function (state, target) {
CSL.Conditions.TopNode.call(this, state, target);
@@ -7639,6 +10254,8 @@ CSL.Node["if"] = {
CSL.Conditions.Configure.call(this, state, pos);
}
};
+
+
CSL.Node["conditions"] = {
build: function (state) {
if (this.tokentype === CSL.START) {
@@ -7649,6 +10266,7 @@ CSL.Node["conditions"] = {
}
}
};
+
CSL.Node["condition"] = {
build: function (state) {
if (this.tokentype === CSL.SINGLETON) {
@@ -7657,7 +10275,9 @@ CSL.Node["condition"] = {
}
}
};
+
CSL.Conditions = {};
+
CSL.Conditions.TopNode = function (state) {
var func;
if (this.tokentype === CSL.START || this.tokentype === CSL.SINGLETON) {
@@ -7665,30 +10285,41 @@ CSL.Conditions.TopNode = function (state) {
state.opt.lang = this.locale;
}
if (!this.tests || !this.tests.length) {
+ // Set up the condition compiler with our current context
state.tmp.conditions = new CSL.Conditions.Engine(state, this);
} else {
+ // The usual.
this.test = state.fun.match[this.match](this, state, this.tests);
}
- func = function(state) {
- state.tmp.condition_counter++;
+ if (state.build.substitute_level.value() === 0) {
+ func = function(state) {
+ state.tmp.condition_counter++;
+ }
+ this.execs.push(func);
}
- this.execs.push(func);
}
if (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON) {
- func = function (state) {
- state.tmp.condition_counter--;
- if (state.tmp.condition_lang_counter_arr.length > 0) {
- var counter = state.tmp.condition_lang_counter_arr.slice(-1)[0];
- if (counter === state.tmp.condition_counter) {
- state.opt.lang = state.tmp.condition_lang_val_arr.pop();
- state.tmp.condition_lang_counter_arr.pop();
+ if (state.build.substitute_level.value() === 0) {
+ func = function (state) {
+ state.tmp.condition_counter--;
+ if (state.tmp.condition_lang_counter_arr.length > 0) {
+ var counter = state.tmp.condition_lang_counter_arr.slice(-1)[0];
+ if (counter === state.tmp.condition_counter) {
+ state.opt.lang = state.tmp.condition_lang_val_arr.pop();
+ state.tmp.condition_lang_counter_arr.pop();
+ }
}
- }
- if (this.locale_default) {
- state.output.current.value().old_locale = this.locale_default;
- state.output.closeLevel("empty");
- state.opt.lang = this.locale_default;
- }
+ if (this.locale_default) {
+ // Open output tag with locale marker
+ state.output.current.value().old_locale = this.locale_default;
+ state.output.closeLevel("empty");
+ state.opt.lang = this.locale_default;
+ }
+ };
+ this.execs.push(func);
+ }
+ // closingjump
+ func = function (state) {
var next = this[state.tmp.jump.value()];
return next;
};
@@ -7698,34 +10329,45 @@ CSL.Conditions.TopNode = function (state) {
}
}
};
+
CSL.Conditions.Configure = function (state, pos) {
if (this.tokentype === CSL.START) {
+ // jump index on failure
this.fail = state.configure.fail.slice(-1)[0];
this.succeed = this.next;
state.configure.fail[(state.configure.fail.length - 1)] = pos;
} else if (this.tokentype === CSL.SINGLETON) {
+ // jump index on failure
this.fail = this.next;
this.succeed = state.configure.succeed.slice(-1)[0];
state.configure.fail[(state.configure.fail.length - 1)] = pos;
} else {
+ // jump index on success
this.succeed = state.configure.succeed.slice(-1)[0];
this.fail = this.next;
}
};
+
CSL.Conditions.Engine = function (state, token) {
this.token = token;
this.state = state;
};
+
CSL.Conditions.Engine.prototype.addTest = function (test) {
this.token.tests ? {} : this.token.tests = [];
this.token.tests.push(test);
};
+
CSL.Conditions.Engine.prototype.addMatch = function (match) {
this.token.match = match;
};
+
CSL.Conditions.Engine.prototype.matchCombine = function () {
this.token.test = this.state.fun.match[this.token.match](this.token, this.state, this.token.tests);
};
+
+/*global CSL: true */
+
CSL.Node.info = {
build: function (state) {
if (this.tokentype === CSL.START) {
@@ -7735,15 +10377,24 @@ CSL.Node.info = {
}
}
};
+
+
+/*global CSL: true */
+
CSL.Node.institution = {
build: function (state, target) {
if ([CSL.SINGLETON, CSL.START].indexOf(this.tokentype) > -1) {
+
var func = function (state) {
if ("string" === typeof this.strings.delimiter) {
state.tmp.institution_delimiter = this.strings.delimiter;
} else {
state.tmp.institution_delimiter = state.tmp.name_delimiter;
}
+
+ // This is the same code for the same result as in node_name.js,
+ // but when cs:institution comes on stream, it may produce
+ // different results.
if ("text" === state.inheritOpt(this, "and")) {
this.and_term = state.getTerm("and", "long", 0);
} else if ("symbol" === state.inheritOpt(this, "and")) {
@@ -7773,10 +10424,13 @@ CSL.Node.institution = {
if (state.inheritOpt(this, "delimiter-precedes-last") === "always") {
this.and_prefix_single = state.tmp.institution_delimiter;
} else if (state.inheritOpt(this, "delimiter-precedes-last") === "never") {
+ // Slightly fragile: could test for charset here to make
+ // this more certain.
if (this.and_prefix_multiple) {
this.and_prefix_multiple = " ";
}
}
+
this.and = {};
if ("undefined" !== typeof this.and_term) {
state.output.append(this.and_term, "empty", true);
@@ -7807,6 +10461,9 @@ CSL.Node.institution = {
}
}
};
+
+/*global CSL: true */
+
CSL.Node["institution-part"] = {
build: function (state, target) {
var func;
@@ -7829,38 +10486,60 @@ CSL.Node["institution-part"] = {
target.push(this);
}
};
+
+/*global CSL: true */
+
CSL.Node.key = {
build: function (state, target) {
+
target = state[state.build.root + "_sort"].tokens;
+
var func;
var debug = false;
var start_key = new CSL.Token("key", CSL.START);
+
state.tmp.root = state.build.root;
+
+ // The params object for build and runtime (tmp) really shouldn't have been separated.
+ // Oh, well.
start_key.strings["et-al-min"] = state.inheritOpt(this, "et-al-min");
start_key.strings["et-al-use-first"] = state.inheritOpt(this, "et-al-use-first");
start_key.strings["et-al-use-last"] = state.inheritOpt(this, "et-al-use-last");
+
+
+ // initialize done vars
func = function (state) {
state.tmp.done_vars = [];
};
start_key.execs.push(func);
+
+ // initialize output queue
func = function (state) {
state.output.openLevel("empty");
};
start_key.execs.push(func);
+
+ // sort direction
var sort_direction = [];
if (this.strings.sort_direction === CSL.DESCENDING) {
+ //print("sort: descending on "+state.tmp.area);
sort_direction.push(1);
sort_direction.push(-1);
} else {
+ //print("sort: ascending");
sort_direction.push(-1);
sort_direction.push(1);
}
state[state.build.area].opt.sort_directions.push(sort_direction);
+
if (CSL.DATE_VARIABLES.indexOf(this.variables[0]) > -1) {
state.build.date_key = true;
}
+
+ // et al init
func = function (state) {
state.tmp.sort_key_flag = true;
+ //print("== key node function ==");
if (state.inheritOpt(this, "et-al-min")) {
state.tmp["et-al-min"] = state.inheritOpt(this, "et-al-min");
}
@@ -7869,17 +10548,25 @@ CSL.Node.key = {
}
if ("boolean" === typeof state.inheritOpt(this, "et-al-use-last")) {
state.tmp["et-al-use-last"] = state.inheritOpt(this, "et-al-use-last");
+ //print(" set tmp et-al-use-last: "+this.strings["et-al-use-last"])
}
};
start_key.execs.push(func);
target.push(start_key);
+
+ //
+ // ops to initialize the key's output structures
if (this.variables.length) {
var variable = this.variables[0];
if (CSL.NAME_VARIABLES.indexOf(variable) > -1) {
+ //
+ // Start tag
var names_start_token = new CSL.Token("names", CSL.START);
names_start_token.tokentype = CSL.START;
names_start_token.variables = this.variables;
CSL.Node.names.build.call(names_start_token, state, target);
+ //
+ // Name tag
var name_token = new CSL.Token("name", CSL.SINGLETON);
name_token.tokentype = CSL.SINGLETON;
name_token.strings["name-as-sort-order"] = "all";
@@ -7888,9 +10575,13 @@ CSL.Node.key = {
name_token.strings["et-al-min"] = state.inheritOpt(this, "et-al-min");
name_token.strings["et-al-use-first"] = state.inheritOpt(this, "et-al-use-first");
CSL.Node.name.build.call(name_token, state, target);
+ //
+ // Institution tag
var institution_token = new CSL.Token("institution", CSL.SINGLETON);
institution_token.tokentype = CSL.SINGLETON;
CSL.Node.institution.build.call(institution_token, state, target);
+ //
+ // End tag
var names_end_token = new CSL.Token("names", CSL.END);
names_end_token.tokentype = CSL.END;
CSL.Node.names.build.call(names_end_token, state, target);
@@ -7899,6 +10590,9 @@ CSL.Node.key = {
single_text.strings.sort_direction = this.strings.sort_direction;
single_text.dateparts = this.dateparts;
if (CSL.NUMERIC_VARIABLES.indexOf(variable) > -1) {
+ // citation-number is virtualized. As a sort key it has no effect on registry
+ // sort order per se, but if set to DESCENDING, it reverses the sequence of numbers representing
+ // bib entries.
if (variable === "citation-number") {
func = function (state, Item) {
if (state.tmp.area === "bibliography_sort") {
@@ -7914,6 +10608,7 @@ CSL.Node.key = {
var num = state.registry.registry[Item.id].seq;
}
if (num) {
+ // Code currently in util_number.js
num = CSL.Util.padding("" + num);
}
state.output.append(num, this);
@@ -7922,7 +10617,9 @@ CSL.Node.key = {
func = function (state, Item) {
var num = false;
num = Item[variable];
+ // XXX What if this is NaN?
if (num) {
+ // Code currently in util_number.js
num = CSL.Util.padding(num);
}
state.output.append(num, this);
@@ -7952,18 +10649,41 @@ CSL.Node.key = {
target.push(single_text);
}
} else { // macro
+ //
+ // if it's not a variable, it's a macro
var token = new CSL.Token("text", CSL.SINGLETON);
token.strings.sort_direction = this.strings.sort_direction;
token.postponed_macro = this.postponed_macro;
CSL.expandMacro.call(state, token, target);
}
+ //
+ // ops to output the key string result to an array go
+ // on the closing "key" tag before it is pushed.
+ // Do not close the level.
var end_key = new CSL.Token("key", CSL.END);
+
+ // Eliminated at revision 1.0.159.
+ // Was causing non-fatal error "wanted empty but found group".
+ // Possible contributor to weird "PAGES" bug?
+ //func = function (state, Item) {
+ //state.output.closeLevel("empty");
+ //};
+ //end_key.execs.push(func);
+
+ // store key for use
func = function (state) {
var keystring = state.output.string(state, state.output.queue);
if (state.sys.normalizeUnicode) {
keystring = state.sys.normalizeUnicode(keystring);
}
keystring = keystring ? (keystring.split(" ").join(state.opt.sort_sep) + state.opt.sort_sep) : "";
+ //SNIP-START
+ if (debug) {
+ CSL.debug("keystring: " + keystring + " " + typeof keystring);
+ }
+ //print("keystring: (" + keystring + ") " + typeof keystring + " " + state.tmp.area);
+ //SNIP-END
+ //state.sys.print("keystring: (" + keystring + ") " + typeof keystring + " " + state.tmp.area);
if ("" === keystring) {
keystring = undefined;
}
@@ -7975,10 +10695,14 @@ CSL.Node.key = {
state.tmp.value = [];
};
end_key.execs.push(func);
+
+ // Set year-suffix key on anything that looks like a date
if (state.build.date_key) {
if (state.build.area === "citation" && state.build.extension === "_sort") {
+ // ascending sort always
state[state.build.area].opt.sort_directions.push([-1,1]);
func = function (state, Item) {
+ // year-suffix Key
var year_suffix = state.registry.registry[Item.id].disambig.year_suffix;
if (!year_suffix) {
year_suffix = 0;
@@ -7990,7 +10714,14 @@ CSL.Node.key = {
}
state.build.date_key = false;
}
+
+ // reset key params
func = function (state) {
+ // state.tmp.name_quash = new Object();
+
+ // XXX This should work, should be necessary, but doesn't and isn't.
+ //state.output.closeLevel("empty");
+
state.tmp["et-al-min"] = undefined;
state.tmp["et-al-use-first"] = undefined;
state.tmp["et-al-use-last"] = undefined;
@@ -8000,21 +10731,35 @@ CSL.Node.key = {
target.push(end_key);
}
};
+
+/*global CSL: true */
+
CSL.Node.label = {
build: function (state, target) {
+
if (this.strings.term) {
+ // Non-names labels
var func = function (state, Item, item) {
+ // Must accomplish this without touching strings
+ // shared with the calling application: "sub verbo"
+ // and "sub-verbo" must both pass, as they stand.
+ //if (item && item.label === "sub verbo") {
+ // item.label = "sub-verbo";
+ //}
+ // This is abstracted away, because the same
+ // logic must be run in cs:names.
var termtxt = CSL.evaluateLabel(this, state, Item, item);
if (item && this.strings.term === "locator") {
- state.parallel.StartVariable("label");
- state.parallel.AppendToVariable(item.label);
+
item.section_form_override = this.strings.form;
+
}
if (termtxt) {
state.tmp.group_context.tip.term_intended = true;
}
CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, termtxt);
if (termtxt.indexOf("%s") === -1) {
+ // ^ Suppress output here if we have an embedded term
if (this.strings.capitalize_if_first) {
if (!state.tmp.term_predecessor && !(state.opt["class"] === "in-text" && state.tmp.area === "citation")) {
termtxt = CSL.Output.Formatters["capitalize-first"](state, termtxt);
@@ -8022,15 +10767,14 @@ CSL.Node.label = {
}
state.output.append(termtxt, this);
}
- if (item && this.strings.term === "locator") {
- state.parallel.CloseVariable();
- }
};
this.execs.push(func);
} else {
if (!this.strings.form) {
this.strings.form = "long";
}
+ // Names labels
+ // Picked up in names END
var namevars = state.build.names_variables[state.build.names_variables.length-1];
var namelabels = state.build.name_label[state.build.name_label.length-1];
for (var i = 0, ilen = namevars.length; i < ilen; i += 1) {
@@ -8051,9 +10795,13 @@ CSL.Node.label = {
target.push(this);
}
};
+
+/*global CSL: true */
+
CSL.Node.layout = {
build: function (state, target) {
var func, prefix_token, suffix_token, tok;
+
function setSuffix() {
if (state.build.area === "bibliography") {
suffix_token = new CSL.Token("text", CSL.SINGLETON);
@@ -8064,6 +10812,10 @@ CSL.Node.layout = {
} else {
suffix = state.bibliography.opt.layout_suffix;
}
+
+ // If @display is used, layout suffix is placed on the last
+ // immediate child of the layout, which we assume will be a
+ // @display group node.
var topblob = state.output.current.value();
if (state.opt.using_display) {
topblob.blobs[topblob.blobs.length-1].strings.suffix = suffix;
@@ -8071,6 +10823,7 @@ CSL.Node.layout = {
topblob.strings.suffix = suffix;
}
if (state.bibliography.opt["second-field-align"]) {
+ // closes bib_other
state.output.endTag("bib_other");
}
};
@@ -8078,18 +10831,22 @@ CSL.Node.layout = {
target.push(suffix_token);
}
}
+
if (this.tokentype === CSL.START) {
+
if (this.locale_raw) {
state.build.current_default_locale = this.locale_raw;
} else {
state.build.current_default_locale = state.opt["default-locale"];
}
+
func = function (state, Item, item) {
if (state.opt.development_extensions.apply_citation_wrapper
&& state.sys.wrapCitationEntry
&& !state.tmp.just_looking
&& Item.system_id
&& state.tmp.area === "citation") {
+
var cite_entry = new CSL.Token("group", CSL.START);
cite_entry.decorations = [["@cite", "entry"]];
state.output.startTag("cite_entry", cite_entry);
@@ -8102,34 +10859,50 @@ CSL.Node.layout = {
};
this.execs.push(func);
}
+
+ // XXX Works, but using state.tmp looks wrong here? We're in the build layer ...
if (this.tokentype === CSL.START && !state.tmp.cite_affixes[state.build.area]) {
+ //
+ // done_vars is used to prevent the repeated
+ // rendering of variables
+ //
+ // initalize done vars
func = function (state, Item) {
+
state.tmp.done_vars = [];
if (state.opt.suppressedJurisdictions[Item["country"]]
&& Item["country"]
&& ["treaty", "patent"].indexOf(Item.type) === -1) {
+
state.tmp.done_vars.push("country");
}
if (!state.tmp.just_looking && state.registry.registry[Item.id] && state.registry.registry[Item.id].parallel) {
state.tmp.done_vars.push("first-reference-note-number");
}
+ //CSL.debug(" === init rendered_name === ");
state.tmp.rendered_name = false;
};
this.execs.push(func);
+ // set opt delimiter
func = function (state) {
+ // just in case
state.tmp.sort_key_flag = false;
};
this.execs.push(func);
+
+ // reset nameset counter [all nodes]
func = function (state) {
state.tmp.nameset_counter = 0;
};
this.execs.push(func);
+
func = function (state, Item) {
var tok = new CSL.Token();
state.output.openLevel(tok);
};
this.execs.push(func);
target.push(this);
+
if (state.build.area === "citation") {
prefix_token = new CSL.Token("text", CSL.SINGLETON);
func = function (state, Item, item) {
@@ -8146,6 +10919,8 @@ CSL.Node.layout = {
target.push(prefix_token);
}
}
+
+ // Cast token to be used in one of the configurations below.
var my_tok;
if (this.locale_raw) {
my_tok = new CSL.Token("dummy", CSL.START);
@@ -8156,32 +10931,53 @@ CSL.Node.layout = {
state.tmp.cite_affixes[state.build.area] = {};
}
}
+
if (this.tokentype === CSL.START) {
state.build.layout_flag = true;
+
+ // Only run the following once, to set up the final layout node ...
if (!this.locale_raw) {
+ //
+ // save out decorations for flipflop processing [final node only]
+ //
state[state.tmp.area].opt.topdecor = [this.decorations];
state[(state.tmp.area + "_sort")].opt.topdecor = [this.decorations];
+
state[state.build.area].opt.layout_prefix = this.strings.prefix;
state[state.build.area].opt.layout_suffix = this.strings.suffix;
state[state.build.area].opt.layout_delimiter = this.strings.delimiter;
+
state[state.build.area].opt.layout_decorations = this.decorations;
+
+ // Only do this if we're running conditionals
if (state.tmp.cite_affixes[state.build.area]) {
+ // if build_layout_locale_flag is true,
+ // write cs:else START to the token list.
tok = new CSL.Token("else", CSL.START);
CSL.Node["else"].build.call(tok, state, target);
}
+
} // !this.locale_raw
+
+ // Conditionals
if (this.locale_raw) {
if (!state.build.layout_locale_flag) {
+ // if layout_locale_flag is untrue,
+ // write cs:choose START and cs:if START
+ // to the token list.
var choose_tok = new CSL.Token("choose", CSL.START);
CSL.Node.choose.build.call(choose_tok, state, target);
my_tok.name = "if";
CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw);
CSL.Node["if"].build.call(my_tok, state, target);
} else {
+ // if build_layout_locale_flag is true,
+ // write cs:else-if START to the token list.
my_tok.name = "else-if";
CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw);
CSL.Node["else-if"].build.call(my_tok, state, target);
}
+ // cite_affixes for this node
state.tmp.cite_affixes[state.build.area][my_tok.locale] = {};
state.tmp.cite_affixes[state.build.area][my_tok.locale].delimiter = this.strings.delimiter;
state.tmp.cite_affixes[state.build.area][my_tok.locale].suffix = this.strings.suffix;
@@ -8191,12 +10987,16 @@ CSL.Node.layout = {
if (this.locale_raw) {
setSuffix();
if (!state.build.layout_locale_flag) {
+ // If layout_locale_flag is untrue, write cs:if END
+ // to the token list.
my_tok.name = "if";
my_tok.tokentype = CSL.END;
CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw);
CSL.Node["if"].build.call(my_tok, state, target);
state.build.layout_locale_flag = true;
} else {
+ // If layout_locale_flag is true, write cs:else-if END
+ // to the token list.
my_tok.name = "else-if";
my_tok.tokentype = CSL.END;
CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw);
@@ -8205,7 +11005,10 @@ CSL.Node.layout = {
}
if (!this.locale_raw) {
setSuffix();
+ // Only add this if we're running conditionals
if (state.tmp.cite_affixes[state.build.area]) {
+ // If layout_locale_flag is true, write cs:else END
+ // and cs:choose END to the token list.
if (state.build.layout_locale_flag) {
tok = new CSL.Token("else", CSL.END);
CSL.Node["else"].build.call(tok, state, target);
@@ -8229,6 +11032,8 @@ CSL.Node.layout = {
suffix_token.execs.push(func);
target.push(suffix_token);
}
+
+ // Closes wrapper token
func = function (state) {
state.output.closeLevel();
};
@@ -8239,6 +11044,7 @@ CSL.Node.layout = {
&& !state.tmp.just_looking
&& Item.system_id
&& state.tmp.area === "citation") {
+
state.output.endTag(); // closes citation link wrapper
}
};
@@ -8250,25 +11056,39 @@ CSL.Node.layout = {
}
}
};
+
+/*global CSL: true */
+
CSL.Node.macro = {
build: function () {}
};
+
+/*global CSL: true */
+
CSL.Node.alternative = {
build: function (state, target) {
if (this.tokentype === CSL.START) {
+
var choose_tok = new CSL.Token("choose", CSL.START);
CSL.Node["choose"].build.call(choose_tok, state, target);
+
var if_tok = new CSL.Token("if", CSL.START);
CSL.Attributes["@alternative-node-internal"].call(if_tok, state);
CSL.Node["if"].build.call(if_tok, state, target);
+
var func = function(state, Item) {
+
state.tmp.oldItem = Item;
state.tmp.oldLang = state.opt.lang;
state.tmp.abort_alternative = true;
+
if (Item["language-name"] && Item["language-name-original"]) {
+
var newItem = JSON.parse(JSON.stringify(Item));
+
newItem.language = newItem["language-name"];
var langspec = CSL.localeResolve(newItem.language, state.opt["default-locale"][0]);
+
if (state.opt.multi_layout) {
for (var i in state.opt.multi_layout) {
var locale_list = state.opt.multi_layout[i];
@@ -8286,6 +11106,7 @@ CSL.Node.alternative = {
state.opt.lang = gotlang;
}
}
+
for (var key in newItem) {
if (["id", "type", "language", "multi"].indexOf(key) === -1 && key.slice(0, 4) !== "alt-") {
if (newItem.multi && newItem.multi._keys[key]) {
@@ -8326,14 +11147,17 @@ CSL.Node.alternative = {
}
}
}
+
state.output.openLevel(this);
state.registry.refhash[Item.id] = newItem;
state.nameOutput = new CSL.NameOutput(state, newItem);
};
this.execs.push(func);
target.push(this);
+
var choose_tok = new CSL.Token("choose", CSL.START);
CSL.Node["choose"].build.call(choose_tok, state, target);
+
var if_tok = new CSL.Token("if", CSL.START);
CSL.Attributes["@alternative-node-internal"].call(if_tok, state);
var func = function(state) {
@@ -8341,11 +11165,15 @@ CSL.Node.alternative = {
}
if_tok.execs.push(func);
CSL.Node["if"].build.call(if_tok, state, target);
+
} else if (this.tokentype === CSL.END) {
+
var if_tok = new CSL.Token("if", CSL.END);
CSL.Node["if"].build.call(if_tok, state, target);
+
var choose_tok = new CSL.Token("choose", CSL.END);
CSL.Node["choose"].build.call(choose_tok, state, target);
+
var func = function(state, Item) {
state.output.closeLevel();
state.registry.refhash[Item.id] = state.tmp.oldItem;
@@ -8355,16 +11183,21 @@ CSL.Node.alternative = {
};
this.execs.push(func);
target.push(this);
+
var if_tok = new CSL.Token("if", CSL.END);
CSL.Node["if"].build.call(if_tok, state, target);
+
var choose_tok = new CSL.Token("choose", CSL.END);
CSL.Node["choose"].build.call(choose_tok, state, target);
+
}
}
};
+
CSL.Node["alternative-text"] = {
build: function (state, target) {
if (this.tokentype === CSL.SINGLETON) {
+ // do stuff
var func = function(state, Item) {
var Item = state.refetchItem(Item.id);
CSL.getCite.call(state, Item);
@@ -8374,8 +11207,18 @@ CSL.Node["alternative-text"] = {
target.push(this);
}
};
+
+
+
+/*global CSL: true */
+
CSL.NameOutput = function(state, Item, item) {
this.debug = false;
+ //SNIP-START
+ if (this.debug) {
+ print("(1)");
+ }
+ //SNIP-END
this.state = state;
this.Item = Item;
this.item = item;
@@ -8384,6 +11227,7 @@ CSL.NameOutput = function(state, Item, item) {
this._first_creator_variable = false;
this._please_chop = false;
};
+
CSL.NameOutput.prototype.init = function (names) {
this.requireMatch = names.requireMatch;
if (this.state.tmp.term_predecessor) {
@@ -8395,6 +11239,7 @@ CSL.NameOutput.prototype.init = function (names) {
this.nameset_offset = 0;
this.names = names;
this.variables = names.variables;
+
this.state.tmp.value = [];
this.state.tmp.rendered_name = [];
this.state.tmp.label_blob = false;
@@ -8406,18 +11251,32 @@ CSL.NameOutput.prototype.init = function (names) {
}
}
this["et-al"] = undefined;
+ // REMOVE THIS
this["with"] = undefined;
+
this.name = undefined;
+ // long, long-with-short, short
this.institutionpart = {};
+ // family, given
+ //this.namepart = {};
+ // before, after
+ //this.label = {};
+
this.state.tmp.group_context.tip.variable_attempt = true;
+
this.labelVariable = this.variables[0];
+
if (!this.state.tmp.value.length) {
return;
}
+
+ // Abort and proceed to the next substitution if a match is required,
+ // two variables are called, and they do not match.
var checkCommonTerm = this.checkCommonAuthor(this.requireMatch);
if (checkCommonTerm) {
this.state.tmp.can_substitute.pop();
this.state.tmp.can_substitute.push(true);
+ //this.state.tmp.group_context.mystack[this.state.tmp.group_context.mystack.length-1].variable_success = false;
for (var i in this.variables) {
var idx = this.state.tmp.done_vars.indexOf(this.variables[i]);
if (idx > -1) {
@@ -8428,14 +11287,23 @@ CSL.NameOutput.prototype.init = function (names) {
this.variables = [];
}
};
+
+
CSL.NameOutput.prototype.reinit = function (names, labelVariable) {
this.requireMatch = names.requireMatch;
this.labelVariable = labelVariable;
+
if (this.state.tmp.can_substitute.value()) {
this.nameset_offset = 0;
+ // What-all should be carried across from the subsidiary
+ // names node, and on what conditions? For each attribute,
+ // and decoration, is it an override, or is it additive?
this.variables = names.variables;
+
+ // Not sure why this is necessary. Guards against a memory leak perhaps?
var oldval = this.state.tmp.value.slice();
this.state.tmp.value = [];
+
for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
if (this.Item[this.variables[i]] && this.Item[this.variables[i]].length) {
this.state.tmp.value = this.state.tmp.value.concat(this.Item[this.variables[i]]);
@@ -8444,8 +11312,12 @@ CSL.NameOutput.prototype.reinit = function (names, labelVariable) {
if (this.state.tmp.value.length) {
this.state.tmp.can_substitute.replace(false, CSL.LITERAL);
}
+
this.state.tmp.value = oldval;
+
}
+ // Abort and proceed to the next substitution if a match is required,
+ // two variables are called, and they do not match.
var checkCommonTerm = this.checkCommonAuthor(this.requireMatch);
if (checkCommonTerm) {
this.state.tmp.can_substitute.pop();
@@ -8459,6 +11331,7 @@ CSL.NameOutput.prototype.reinit = function (names, labelVariable) {
this.variables = [];
}
};
+
CSL.NameOutput.prototype.outputNames = function () {
var i, ilen;
var variables = this.variables;
@@ -8470,32 +11343,78 @@ CSL.NameOutput.prototype.outputNames = function () {
this.institution.and.multiple.blobs = this.name.and.multiple.blobs;
}
}
+
this.variable_offset = {};
if (this.family) {
this.family_decor = CSL.Util.cloneToken(this.family);
this.family_decor.strings.prefix = "";
this.family_decor.strings.suffix = "";
+ // Sets text-case value (text-case="title" is suppressed for items
+ // non-English with non-English value in Item.language)
for (i = 0, ilen = this.family.execs.length; i < ilen; i += 1) {
this.family.execs[i].call(this.family_decor, this.state, this.Item);
}
} else {
this.family_decor = false;
}
+
if (this.given) {
this.given_decor = CSL.Util.cloneToken(this.given);
this.given_decor.strings.prefix = "";
this.given_decor.strings.suffix = "";
+ // Sets text-case value (text-case="title" is suppressed for items
+ // non-English with non-English value in Item.language)
for (i = 0, ilen = this.given.execs.length; i < ilen; i += 1) {
this.given.execs[i].call(this.given_decor, this.state, this.Item);
}
} else {
this.given_decor = false;
}
+
+ //SNIP-START
+ if (this.debug) {
+ print("(2)");
+ }
+ //SNIP-END
+ // util_names_etalconfig.js
this.getEtAlConfig();
+ //SNIP-START
+ if (this.debug) {
+ print("(3)");
+ }
+ //SNIP-END
+ // util_names_divide.js
this.divideAndTransliterateNames();
+ //SNIP-START
+ if (this.debug) {
+ print("(4)");
+ }
+ //SNIP-END
+ // util_names_truncate.js
+
this.truncatePersonalNameLists();
+ //SNIP-START
+ if (this.debug) {
+ print("(5)");
+ }
+ //SNIP-END
+
+ //SNIP-START
+ if (this.debug) {
+ print("(6)");
+ }
+ //SNIP-END
+ // util_names_disambig.js
this.disambigNames();
+
+ // util_names_constraints.js
this.constrainNames();
+ //SNIP-START
+ if (this.debug) {
+ print("(7)");
+ }
+ //SNIP-END
+ // form="count"
if (this.name.strings.form === "count") {
if (this.state.tmp.extension || this.names_count != 0) {
this.state.output.append(this.names_count, "empty");
@@ -8503,11 +11422,32 @@ CSL.NameOutput.prototype.outputNames = function () {
}
return;
}
+
+ //SNIP-START
+ if (this.debug) {
+ print("(8)");
+ }
+ //SNIP-END
this.setEtAlParameters();
+ //SNIP-START
+ if (this.debug) {
+ print("(9)");
+ }
+ //SNIP-END
this.setCommonTerm(this.requireMatch);
+ //SNIP-START
+ if (this.debug) {
+ print("(10)");
+ }
+ //SNIP-END
this.state.tmp.name_node = {};
this.state.tmp.name_node.children = [];
this.renderAllNames();
+ //SNIP-START
+ if (this.debug) {
+ print("(11)");
+ }
+ //SNIP-END
var blob_list = [];
for (i = 0, ilen = variables.length; i < ilen; i += 1) {
var v = variables[i];
@@ -8517,9 +11457,19 @@ CSL.NameOutput.prototype.outputNames = function () {
if (!this.state.opt.development_extensions.spoof_institutional_affiliations) {
varblob = this._join([this.freeters[v]], "");
} else {
+ //SNIP-START
+ if (this.debug) {
+ print("(11a)");
+ }
+ //SNIP-END
for (var j = 0, jlen = this.institutions[v].length; j < jlen; j += 1) {
institution_sets.push(this.joinPersonsAndInstitutions([this.persons[v][j], this.institutions[v][j]]));
}
+ //SNIP-START
+ if (this.debug) {
+ print("(11b)");
+ }
+ //SNIP-END
if (this.institutions[v].length) {
var pos = this.nameset_base + this.variable_offset[v];
if (this.freeters[v].length) {
@@ -8527,35 +11477,94 @@ CSL.NameOutput.prototype.outputNames = function () {
}
institutions = this.joinInstitutionSets(institution_sets, pos);
}
+ //SNIP-START
+ if (this.debug) {
+ print("(11c)");
+ }
+ //SNIP-END
var varblob = this.joinFreetersAndInstitutionSets([this.freeters[v], institutions]);
+ //SNIP-START
+ if (this.debug) {
+ print("(11d)");
+ }
+ //SNIP-END
}
if (varblob) {
+ // Apply labels, if any
if (!this.state.tmp.extension) {
varblob = this._applyLabels(varblob, v);
}
blob_list.push(varblob);
}
+ //SNIP-START
+ if (this.debug) {
+ print("(11e)");
+ }
+ //SNIP-END
if (this.common_term) {
break;
}
}
+ //SNIP-START
+ if (this.debug) {
+ print("(12)");
+ }
+ //SNIP-END
this.state.output.openLevel("empty");
this.state.output.current.value().strings.delimiter = this.state.inheritOpt(this.names, "delimiter", "names-delimiter");
+ //SNIP-START
+ if (this.debug) {
+ print("(13)");
+ }
+ //SNIP-END
for (i = 0, ilen = blob_list.length; i < ilen; i += 1) {
+ // notSerious
this.state.output.append(blob_list[i], "literal", true);
}
if (!this.state.tmp.just_looking && blob_list.length > 0) {
this.state.tmp.probably_rendered_something = true;
}
+ //SNIP-START
+ if (this.debug) {
+ print("(14)");
+ }
+ //SNIP-END
this.state.output.closeLevel("empty");
+ //SNIP-START
+ if (this.debug) {
+ print("(15)");
+ }
+ //SNIP-END
var blob = this.state.output.pop();
this.state.tmp.name_node.top = blob;
+ //SNIP-START
+ if (this.debug) {
+ print("(16)");
+ }
+ //SNIP-END
+
+ // Append will drop the names on the floor here if suppress-me is
+ // set on element_trace.
+ // Need to rescue the value for collapse comparison.
var namesToken = CSL.Util.cloneToken(this.names);
this.state.output.append(blob, namesToken);
if (this.state.tmp.term_predecessor_name) {
this.state.tmp.term_predecessor = true;
}
+ //SNIP-START
+ if (this.debug) {
+ print("(17)");
+ }
+ //SNIP-END
+ // Also used in CSL.Util.substituteEnd (which could do with
+ // some cleanup at this writing).
+ //SNIP-START
+ if (this.debug) {
+ print("(18)");
+ }
+ //SNIP-END
if (variables[0] !== "authority") {
+ // Just grab the string values in the name
var name_node_string = [];
var nameobjs = this.Item[variables[0]];
if (nameobjs) {
@@ -8571,6 +11580,13 @@ CSL.NameOutput.prototype.outputNames = function () {
this.state.tmp.name_node.string = name_node_string;
}
}
+ // for classic support
+ // This may be more convoluted than it needs to be. Or maybe not.
+ //
+ // Check for classic abbreviation
+ //
+ // If found, then (1) suppress title rendering, (2) replace the node
+ // with the abbreviation output [and (3) do not run this._collapseAuthor() ?]
if (this.state.tmp.name_node.string && !this.state.tmp.first_name_string) {
this.state.tmp.first_name_string = this.state.tmp.name_node.string;
}
@@ -8594,9 +11610,19 @@ CSL.NameOutput.prototype.outputNames = function () {
}
}
}
+
+ // Let's try something clever here.
this._collapseAuthor();
+
+ // For name_SubstituteOnNamesSpanNamesSpanFail
this.variables = [];
+ //SNIP-START
+ if (this.debug) {
+ print("(19)");
+ }
+ //SNIP-END
};
+
CSL.NameOutput.prototype._applyLabels = function (blob, v) {
var txt;
if (!this.label || !this.label[this.labelVariable]) {
@@ -8614,6 +11640,7 @@ CSL.NameOutput.prototype._applyLabels = function (blob, v) {
plural = 1;
}
}
+ // Some code duplication here, should be factored out.
if (this.label[this.labelVariable].before) {
if ("number" === typeof this.label[this.labelVariable].before.strings.plural) {
plural = this.label[this.labelVariable].before.strings.plural;
@@ -8639,10 +11666,12 @@ CSL.NameOutput.prototype._applyLabels = function (blob, v) {
}
return blob;
};
+
CSL.NameOutput.prototype._buildLabel = function (term, plural, position, v) {
if (this.common_term) {
term = this.common_term;
}
+
var ret = false;
var node = this.label[v][position];
if (node) {
@@ -8650,8 +11679,12 @@ CSL.NameOutput.prototype._buildLabel = function (term, plural, position, v) {
}
return ret;
};
+
+
CSL.NameOutput.prototype._collapseAuthor = function () {
var myqueue, mystr, oldchars;
+ // collapse can be undefined, an array of length zero, and probably
+ // other things ... ugh.
if (this.state.tmp.name_node.top.blobs.length === 0) {
return;
}
@@ -8662,17 +11695,22 @@ CSL.NameOutput.prototype._collapseAuthor = function () {
&& this.state[this.state.tmp.area].opt.collapse.length)
|| (this.state[this.state.tmp.area].opt.cite_group_delimiter
&& this.state[this.state.tmp.area].opt.cite_group_delimiter.length)) {
+
if (this.state.tmp.authorstring_request) {
+ // Avoid running this on every call to getAmbiguousCite()?
mystr = "";
myqueue = this.state.tmp.name_node.top.blobs.slice(-1)[0].blobs;
oldchars = this.state.tmp.offset_characters;
if (myqueue) {
mystr = this.state.output.string(this.state, myqueue, false);
}
+ // Avoid side-effects on character counting: we're only interested
+ // in the final rendering.
this.state.tmp.offset_characters = oldchars;
this.state.registry.authorstrings[this.Item.id] = mystr;
} else if (!this.state.tmp.just_looking
&& !this.state.tmp.suppress_decorations && ((this.state[this.state.tmp.area].opt.collapse && this.state[this.state.tmp.area].opt.collapse.length) || this.state[this.state.tmp.area].opt.cite_group_delimiter && this.state[this.state.tmp.area].opt.cite_group_delimiter)) {
+ // XX1 print("RENDER: "+this.Item.id);
mystr = "";
myqueue = this.state.tmp.name_node.top.blobs.slice(-1)[0].blobs;
oldchars = this.state.tmp.offset_characters;
@@ -8681,22 +11719,36 @@ CSL.NameOutput.prototype._collapseAuthor = function () {
}
if (mystr === this.state.tmp.last_primary_names_string) {
if (this.item["suppress-author"] || (this.state[this.state.tmp.area].opt.collapse && this.state[this.state.tmp.area].opt.collapse.length)) {
+ // XX1 print(" CUT!");
this.state.tmp.name_node.top.blobs.pop();
this.state.tmp.name_node.children = [];
+ // If popped, avoid side-effects on character counting: we're only interested
+ // in things that actually render.
this.state.tmp.offset_characters = oldchars;
}
+ // Needed
if (this.state[this.state.tmp.area].opt.cite_group_delimiter && this.state[this.state.tmp.area].opt.cite_group_delimiter) {
this.state.tmp.use_cite_group_delimiter = true;
}
} else {
+ // XX1 print("remembering: "+mystr);
this.state.tmp.last_primary_names_string = mystr;
+ // XXXXX A little more precision would be nice.
+ // This will clobber variable="author editor" as well as variable="author".
+
if (this.variables.indexOf(this._first_creator_variable) > -1 && this.item && this.item["suppress-author"] && this.Item.type !== "legal_case") {
this.state.tmp.name_node.top.blobs.pop();
this.state.tmp.name_node.children = [];
+ // If popped, avoid side-effects on character counting: we're only interested
+ // in things that actually render.
this.state.tmp.offset_characters = oldchars;
+
+ // A wild guess, but will usually be correct
this.state.tmp.term_predecessor = false;
}
+ // Arcane and probably unnecessarily complicated?
this.state.tmp.have_collapsed = false;
+ // Needed
if (this.state[this.state.tmp.area].opt.cite_group_delimiter && this.state[this.state.tmp.area].opt.cite_group_delimiter) {
this.state.tmp.use_cite_group_delimiter = false;
}
@@ -8704,25 +11756,45 @@ CSL.NameOutput.prototype._collapseAuthor = function () {
}
}
};
+
+/*
+CSL.NameOutput.prototype.suppressNames = function() {
+ suppress_condition = suppress_min && display_names.length >= suppress_min;
+ if (suppress_condition) {
+ continue;
+ }
+}
+*/
+
+/*global CSL: true */
+
CSL.NameOutput.prototype.isPerson = function (value) {
if (value.literal
|| (!value.given && value.family && value.isInstitution)) {
+
return false;
} else {
return true;
}
};
+
+/*global CSL: true */
+
CSL.NameOutput.prototype.truncatePersonalNameLists = function () {
var v, i, ilen, j, jlen, chopvar, values;
+ // XXX Before truncation, make a note of the original number
+ // of names, for use in et-al evaluation.
this.freeters_count = {};
this.persons_count = {};
this.institutions_count = {};
+ // By key is okay here, as we don't care about sequence.
for (v in this.freeters) {
if (this.freeters.hasOwnProperty(v)) {
this.freeters_count[v] = this.freeters[v].length;
this.freeters[v] = this._truncateNameList(this.freeters, v);
}
}
+
for (v in this.persons) {
if (this.persons.hasOwnProperty(v)) {
this.institutions_count[v] = this.institutions[v].length;
@@ -8735,6 +11807,7 @@ 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
&& !(this.state.tmp.extension
|| this.state.tmp.just_looking)) {
@@ -8790,6 +11863,36 @@ CSL.NameOutput.prototype.truncatePersonalNameLists = function () {
}
}
}
+
+ // Transliteration and abbreviation mapping
+
+ // Hmm. This could produce three lists for each nameset:
+ // - primary (transformed in place)
+ // - secondary
+ // - tertiary
+ // with items that produce no result in the secondary and tertiary
+ // transforms set to false. Maybe.
+
+ // Actually that would be insane, so forget it.
+ // What we need is to add suitable parameters to getName(), and merge
+ // the single-name-level operations below into that function. Then the
+ // operation can be applied in util_names_render.js, and the logic
+ // becomes very similar to what we already have running in util_transform.js.
+
+/*
+ for (v in this.freeters) {
+ this._transformNameset(this.freeters[v]);
+ }
+ for (v in this.persons) {
+ for (i = 0, ilen = this.persons[v].length; i < ilen; i += 1) {
+ this._transformNameset(this.persons[v][i]);
+ }
+ this._transformNameset(this.institutions[v]);
+ }
+*/
+
+ // Could also be factored out to a separate function for clarity.
+ // ???? XXX Does this belong?
for (i = 0, ilen = this.variables.length; i < ilen; i += 1) {
if (this.institutions[v].length) {
this.nameset_offset += 1;
@@ -8798,9 +11901,11 @@ CSL.NameOutput.prototype.truncatePersonalNameLists = function () {
if (this.persons[v][j].length) {
this.nameset_offset += 1;
}
+ // this.institutions[v][i] = this._splitInstitution(this.institutions[v][i], v, i);
}
}
};
+
CSL.NameOutput.prototype._truncateNameList = function (container, variable, index) {
var lst;
if ("undefined" === typeof index) {
@@ -8811,11 +11916,17 @@ CSL.NameOutput.prototype._truncateNameList = function (container, variable, inde
if (this.state[this.state[this.state.tmp.area].root].opt.max_number_of_names
&& lst.length > 50
&& lst.length > (this.state[this.state[this.state.tmp.area].root].opt.max_number_of_names + 2)) {
+
+ // Preserve the last name in the list, in case we're rendering with a PI ellipsis (et-al-use-last)
var limit = this.state[this.state[this.state.tmp.area].root].opt.max_number_of_names;
lst = lst.slice(0, limit+1).concat(lst.slice(-1));
}
return lst;
};
+
+
+/*global CSL: true */
+
CSL.NameOutput.prototype.divideAndTransliterateNames = function () {
var i, ilen, j, jlen;
var Item = this.Item;
@@ -8855,6 +11966,7 @@ CSL.NameOutput.prototype.divideAndTransliterateNames = function () {
}
}
};
+
CSL.NameOutput.prototype._normalizeVariableValue = function (Item, variable) {
var names;
if ("string" === typeof Item[variable] || "number" === typeof Item[variable]) {
@@ -8871,6 +11983,7 @@ CSL.NameOutput.prototype._normalizeVariableValue = function (Item, variable) {
}
return names;
};
+
CSL.NameOutput.prototype._getFreeters = function (v, values) {
this.freeters[v] = [];
if (this.state.opt.development_extensions.spoof_institutional_affiliations) {
@@ -8898,6 +12011,7 @@ CSL.NameOutput.prototype._getFreeters = function (v, values) {
this.nameset_offset += 1;
}
};
+
CSL.NameOutput.prototype._getPersonsAndInstitutions = function (v, values) {
this.persons[v] = [];
this.institutions[v] = [];
@@ -8931,11 +12045,13 @@ CSL.NameOutput.prototype._getPersonsAndInstitutions = function (v, values) {
this.institutions[v].reverse();
}
};
+
CSL.NameOutput.prototype._clearValues = function (values) {
for (var i = values.length - 1; i > -1; i += -1) {
values.pop();
}
};
+
CSL.NameOutput.prototype._checkNickname = function (name) {
if (["interview", "personal_communication"].indexOf(this.Item.type) > -1) {
var author = "";
@@ -8943,9 +12059,11 @@ CSL.NameOutput.prototype._checkNickname = function (name) {
if (author && this.state.sys.getAbbreviation && !(this.item && this.item["suppress-author"])) {
var normalizedKey = author;
if (this.state.sys.normalizeAbbrevsKey) {
+ // 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);
+ // XXX Why does this have to happen here?
var myLocalName = this.state.transform.abbrevs["default"].nickname[normalizedKey];
if (myLocalName) {
if (myLocalName === "!here>>>") {
@@ -8958,6 +12076,9 @@ CSL.NameOutput.prototype._checkNickname = function (name) {
}
return name;
};
+
+/*global CSL: true */
+
CSL.NameOutput.prototype.joinPersons = function (blobs, pos, j, tokenname) {
var ret;
if (!tokenname) {
@@ -8986,6 +12107,8 @@ CSL.NameOutput.prototype.joinPersons = function (blobs, pos, j, tokenname) {
}
return ret;
};
+
+
CSL.NameOutput.prototype.joinInstitutionSets = function (blobs, pos) {
var ret;
if (this.etal_spec[pos].institutions === 1) {
@@ -8997,16 +12120,30 @@ CSL.NameOutput.prototype.joinInstitutionSets = function (blobs, pos) {
}
return ret;
};
+
+
CSL.NameOutput.prototype.joinPersonsAndInstitutions = function (blobs) {
+ //
return this._join(blobs, this.state.tmp.name_delimiter);
};
+
+// LEGACY
+// This should go away eventually
CSL.NameOutput.prototype.joinFreetersAndInstitutionSets = function (blobs) {
+ // Nothing, one or two, never more
var ret = this._join(blobs, "[never here]", this["with"].single, this["with"].multiple);
+ //var ret = this._join(blobs, "");
return ret;
};
+
CSL.NameOutput.prototype._joinEtAl = function (blobs, tokenname) {
+ //
var blob = this._join(blobs, this.state.tmp.name_delimiter);
+
+ // notSerious
this.state.output.openLevel(this._getToken(tokenname));
+ // Delimiter is applied from separately saved source in this case,
+ // for discriminate application of single and multiple joins.
this.state.output.current.value().strings.delimiter = "";
this.state.output.append(blob, "literal", true);
if (blobs.length > 1) {
@@ -9017,25 +12154,35 @@ CSL.NameOutput.prototype._joinEtAl = function (blobs, tokenname) {
this.state.output.closeLevel();
return this.state.output.pop();
};
+
+
CSL.NameOutput.prototype._joinEllipsis = function (blobs, tokenname) {
return this._join(blobs, this.state.tmp.name_delimiter, this.name.ellipsis.single, this.name.ellipsis.multiple, tokenname);
};
+
+
CSL.NameOutput.prototype._joinAnd = function (blobs, tokenname) {
return this._join(blobs, this.state.inheritOpt(this[tokenname], "delimiter", (tokenname + "-delimiter"), ", "), this[tokenname].and.single, this[tokenname].and.multiple, tokenname);
};
+
+
CSL.NameOutput.prototype._join = function (blobs, delimiter, single, multiple) {
var i, ilen;
if (!blobs) {
return false;
}
+ // Eliminate false and empty blobs
for (i = blobs.length - 1; i > -1; i += -1) {
if (!blobs[i] || blobs[i].length === 0 || !blobs[i].blobs.length) {
blobs = blobs.slice(0, i).concat(blobs.slice(i + 1));
}
}
+ // XXXX This needs some attention before moving further.
+ // Code is not sufficiently transparent.
if (!blobs.length) {
return false;
} else if (single && blobs.length === 2) {
+ // Clone to avoid corruption of style by affix migration during output
if (single) {
single = new CSL.Blob(single.blobs,single);
}
@@ -9047,15 +12194,18 @@ CSL.NameOutput.prototype._join = function (blobs, delimiter, single, multiple) {
} else {
delimiter_offset = 1;
}
+ // It kind of makes sense down to here.
for (i = 0, ilen = blobs.length - delimiter_offset; i < ilen; i += 1) {
blobs[i].strings.suffix += delimiter;
}
if (blobs.length > 1) {
var blob = blobs.pop();
if (multiple) {
+ // Clone to avoid corruption of style by affix migration during output
multiple = new CSL.Blob(multiple.blobs,multiple);
blobs.push(multiple);
} else {
+ // Clone to avoid corruption of style by affix migration during output
if (single) {
single = new CSL.Blob(single.blobs,single);
}
@@ -9064,7 +12214,13 @@ CSL.NameOutput.prototype._join = function (blobs, delimiter, single, multiple) {
blobs.push(blob);
}
}
+
+ //this.state.output.openLevel(this._getToken(tokenname));
this.state.output.openLevel();
+
+ //this.state.output.openLevel(this._getToken("empty"));
+ // Delimiter is applied from separately saved source in this case,
+ // for discriminate application of single and multiple joins.
if (single && multiple) {
this.state.output.current.value().strings.delimiter = "";
}
@@ -9074,14 +12230,23 @@ CSL.NameOutput.prototype._join = function (blobs, delimiter, single, multiple) {
this.state.output.closeLevel();
return this.state.output.pop();
};
+
+
CSL.NameOutput.prototype._getToken = function (tokenname) {
var token = this[tokenname];
if (tokenname === "institution") {
var newtoken = new CSL.Token();
+ // Which, hmm, is the same thing as "empty"
+ // Oh, well.
+ //newtoken.strings.prefix = token.prefix;
+ //newtoken.strings.suffix = token.suffix;
return newtoken;
}
return token;
};
+
+/*global CSL: true */
+
CSL.NameOutput.prototype.checkCommonAuthor = function(requireMatch) {
if (!requireMatch) {
return false;
@@ -9112,13 +12277,19 @@ CSL.NameOutput.prototype.checkCommonAuthor = function(requireMatch) {
this.state.tmp.done_vars.push(this.variables[0]);
this.state.tmp.done_vars.push(this.variables[1]);
}
+ // This may be counter-intuitive.
+ // This check controls whether we will fail on the this attempt at rendering
+ // and proceed with substitution. If the names match exactly (true), then
+ // we do *not* want to abort and continue with substitution.
return !perfectMatch;
};
+
CSL.NameOutput.prototype.setCommonTerm = function () {
var variables = this.variables;
var varnames = variables.slice();
varnames.sort();
this.common_term = varnames.join("");
+ // When no varnames are on offer
if (!this.common_term) {
return;
}
@@ -9130,6 +12301,8 @@ CSL.NameOutput.prototype.setCommonTerm = function () {
has_term = this.state.getTerm(this.common_term, this.label[this.variables[0]].after.strings.form, 0);
}
}
+
+ // When there is no common term
if (!this.state.locale[this.state.opt.lang].terms[this.common_term]
|| !has_term
|| this.variables.length < 2) {
@@ -9161,6 +12334,7 @@ CSL.NameOutput.prototype.setCommonTerm = function () {
}
}
};
+
CSL.NameOutput.prototype._compareNamesets = function (base_nameset, nameset) {
if (!base_nameset || !nameset || base_nameset.length !== nameset.length) {
return false;
@@ -9175,24 +12349,35 @@ CSL.NameOutput.prototype._compareNamesets = function (base_nameset, nameset) {
}
return true;
};
+
+/*global CSL: true */
+
CSL.NameOutput.prototype.constrainNames = function () {
+ // figure out how many names to include, in light of the disambig params
+ //
this.names_count = 0;
+ //var pos = 0;
var pos;
for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
var v = this.variables[i];
pos = this.nameset_base + i;
+ // Constrain independent authors here
if (this.freeters[v].length) {
this.state.tmp.names_max.push(this.freeters[v].length, "literal");
this._imposeNameConstraints(this.freeters, this.freeters_count, v, pos);
this.names_count += this.freeters[v].length;
}
+
+ // Constrain institutions here
if (this.institutions[v].length) {
this.state.tmp.names_max.push(this.institutions[v].length, "literal");
this._imposeNameConstraints(this.institutions, this.institutions_count, v, pos);
this.persons[v] = this.persons[v].slice(0, this.institutions[v].length);
this.names_count += this.institutions[v].length;
}
+
for (var j = 0, jlen = this.persons[v].length; j < jlen; j += 1) {
+ // Constrain affiliated authors here
if (this.persons[v][j].length) {
this.state.tmp.names_max.push(this.persons[v][j].length, "literal");
this._imposeNameConstraints(this.persons[v], this.persons_count[v], j, pos);
@@ -9201,11 +12386,19 @@ CSL.NameOutput.prototype.constrainNames = function () {
}
}
};
+
CSL.NameOutput.prototype._imposeNameConstraints = function (lst, count, key, pos) {
+ // display_names starts as the original length of this list of names.
var display_names = lst[key];
var discretionary_names_length = this.state.tmp["et-al-min"];
+
+ // Mappings, to allow existing disambiguation machinery to
+ // remain untouched.
if (this.state.tmp.suppress_decorations) {
if (this.state.tmp.disambig_request && this.state.tmp.disambig_request.names[pos]) {
+ // Oh. Trouble.
+ // state.tmp.nameset_counter is the number of the nameset
+ // in the disambiguation try-sequence. Ouch.
discretionary_names_length = this.state.tmp.disambig_request.names[pos];
} else if (count[key] >= this.etal_min) {
discretionary_names_length = this.etal_use_first;
@@ -9213,23 +12406,39 @@ CSL.NameOutput.prototype._imposeNameConstraints = function (lst, count, key, pos
} else {
if (this.state.tmp.disambig_request
&& this.state.tmp.disambig_request.names[pos] > this.etal_use_first) {
+
if (count[key] < this.etal_min) {
discretionary_names_length = count[key];
} else {
discretionary_names_length = this.state.tmp.disambig_request.names[pos];
}
} else if (count[key] >= this.etal_min) {
+ //discretionary_names_length = this.state.tmp["et-al-use-first"];
discretionary_names_length = this.etal_use_first;
}
+ // XXXX: This is a workaround. Under some conditions.
+ // Where namesets disambiguate on one of the two names
+ // dropped here, it is possible for more than one
+ // in-text citation to be close (and indistinguishable)
+ // matches to a single bibliography entry.
+ //
+ //
if (this.etal_use_last && discretionary_names_length > (this.etal_min - 2)) {
discretionary_names_length = this.etal_min - 2;
}
}
var sane = this.etal_min >= this.etal_use_first;
var overlength = count[key] > discretionary_names_length;
+ // This var is used to control contextual join, and
+ // lies about the number of names when forceEtAl is true,
+ // unless normalized.
if (discretionary_names_length > count[key]) {
+
+ // Use actual truncated list length, to avoid overrun.
discretionary_names_length = display_names.length;
}
+ // forceEtAl is relevant when the author list is
+ // truncated to eliminate clutter.
if (sane && overlength) {
if (this.etal_use_last) {
lst[key] = display_names.slice(0, discretionary_names_length).concat(display_names.slice(-1));
@@ -9239,7 +12448,19 @@ CSL.NameOutput.prototype._imposeNameConstraints = function (lst, count, key, pos
}
this.state.tmp.disambig_settings.names[pos] = lst[key].length;
this.state.disambiguate.padBase(this.state.tmp.disambig_settings);
+
+
+ // ???
+ //if (!this.state.tmp.disambig_request) {
+ // this.state.tmp.disambig_settings.givens[pos] = [];
+ //}
};
+
+// Disambiguate names (the number of names is controlled externally, by successive
+// runs of the processor).
+
+/*global CSL: true */
+
CSL.NameOutput.prototype.disambigNames = function () {
var pos;
for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
@@ -9248,6 +12469,7 @@ CSL.NameOutput.prototype.disambigNames = function () {
if (this.freeters[v].length) {
this._runDisambigNames(this.freeters[v], pos);
}
+ // Is this even necessary???
if (this.institutions[v].length) {
if ("undefined" === typeof this.state.tmp.disambig_settings.givens[pos]) {
this.state.tmp.disambig_settings.givens[pos] = [];
@@ -9265,16 +12487,27 @@ CSL.NameOutput.prototype.disambigNames = function () {
}
}
};
+
CSL.NameOutput.prototype._runDisambigNames = function (lst, pos) {
var chk, myform, myinitials, param, i, ilen, paramx;
+ //if (this.state.tmp.root === "bibliography") {
+ // return;
+ //}
for (i = 0, ilen = lst.length; i < ilen; i += 1) {
+ //
+ // register the name in the global names disambiguation
+ // registry
+
if (!lst[i].given && !lst[i].family) {
continue;
}
+
myinitials = this.state.inheritOpt(this.name, "initialize-with");
this.state.registry.namereg.addname("" + this.Item.id, lst[i], i);
chk = this.state.tmp.disambig_settings.givens[pos];
if ("undefined" === typeof chk) {
+ // Holes can appear in the list, probably due to institutional
+ // names that this doesn't touch. Maybe. This fills them up.
for (var j = 0, jlen = pos + 1; j < jlen; j += 1) {
if (!this.state.tmp.disambig_settings.givens[j]) {
this.state.tmp.disambig_settings.givens[j] = [];
@@ -9282,15 +12515,28 @@ CSL.NameOutput.prototype._runDisambigNames = function (lst, pos) {
}
}
chk = this.state.tmp.disambig_settings.givens[pos][i];
+ //if ("undefined" !== typeof chk && this.state.tmp.root === 'citation') {
+ //this.state.tmp.disambig_settings.givens[pos] = [];
+ //chk = undefined;
+ //}
if ("undefined" === typeof chk) {
myform = this.state.inheritOpt(this.name, "form", "name-form", "long");
param = this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, 0, myform, myinitials);
this.state.tmp.disambig_settings.givens[pos].push(param);
}
+ //
+ // set the display mode default for givennames if required
myform = this.state.inheritOpt(this.name, "form", "name-form", "long");
paramx = this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, 0, myform, myinitials);
+ // this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, 0, myform, myinitials);
if (this.state.tmp.disambig_request) {
+ //
+ // fix a request for initials that makes no sense.
+ // can't do this in disambig, because the availability
+ // of initials is not a global parameter.
var val = this.state.tmp.disambig_settings.givens[pos][i];
+ // This is limited to by-cite disambiguation.
+ // 2012-09-13: added lst[i].given check to condition
if (val === 1 &&
this.state.citation.opt["givenname-disambiguation-rule"] === "by-cite" &&
("undefined" === typeof this.state.inheritOpt(this.name, "initialize-with")
@@ -9298,12 +12544,22 @@ CSL.NameOutput.prototype._runDisambigNames = function (lst, pos) {
val = 2;
}
param = val;
+ // 2012-09-13: lst[i].given check protects against personal names
+ // that have no first name element. These were causing an infinite loop,
+ // this prevents that.
if (this.state.opt["disambiguate-add-givenname"] && lst[i].given) {
param = this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, param, this.state.inheritOpt(this.name, "form", "name-form", "long"), this.state.inheritOpt(this.name, "initialize-with"));
}
} else {
+ //
+ // it clicks. here is where we will put the
+ // call to the names register, to get the floor value
+ // for an individual name.
+ //
param = paramx;
}
+ // Need to save off the settings based on subsequent
+ // form, when first cites are rendered.
if (!this.state.tmp.just_looking && this.item && this.item.position === CSL.POSITION_FIRST) {
if (paramx > param) {
param = paramx;
@@ -9314,57 +12570,83 @@ CSL.NameOutput.prototype._runDisambigNames = function (lst, pos) {
if ("string" === typeof myinitials
&& ("undefined" === typeof this.name.strings["initialize"]
|| true === this.name.strings["initialize"])) {
+
this.state.tmp.disambig_settings.use_initials = true;
}
}
}
+ //this.state.registry.registry[this.Item.id].disambig.givens = this.state.tmp.disambig_settings.givens.slice();
};
+
+/*global CSL: true */
+
CSL.NameOutput.prototype.getEtAlConfig = function () {
var item = this.item;
this["et-al"] = {};
+
this.state.output.append(this.etal_term, this.etal_style, true);
this["et-al"].single = this.state.output.pop();
this["et-al"].single.strings.suffix = this.etal_suffix;
this["et-al"].single.strings.prefix = this.etal_prefix_single;
+
this.state.output.append(this.etal_term, this.etal_style, true);
this["et-al"].multiple = this.state.output.pop();
this["et-al"].multiple.strings.suffix = this.etal_suffix;
this["et-al"].multiple.strings.prefix = this.etal_prefix_multiple;
+
+ // Et-al style parameters (may be sidestepped by disambiguation
+ // in util_names_constraints.js)
if ("undefined" === typeof item) {
item = {};
}
+ //print("== getEtAlConfig() == "+this.state.tmp.area);
+
if (item.position) {
if (this.state.inheritOpt(this.name, "et-al-subsequent-min")) {
+ // XX
this.etal_min = this.state.inheritOpt(this.name, "et-al-subsequent-min");
} else {
+ // XX
this.etal_min = this.state.inheritOpt(this.name, "et-al-min");
}
if (this.state.inheritOpt(this.name, "et-al-subsequent-use-first")) {
+ // XX
this.etal_use_first = this.state.inheritOpt(this.name, "et-al-subsequent-use-first");
} else {
+ // XX
this.etal_use_first = this.state.inheritOpt(this.name, "et-al-use-first");
}
} else {
if (this.state.tmp["et-al-min"]) {
this.etal_min = this.state.tmp["et-al-min"];
} else {
+ // XX
this.etal_min = this.state.inheritOpt(this.name, "et-al-min");
}
if (this.state.tmp["et-al-use-first"]) {
this.etal_use_first = this.state.tmp["et-al-use-first"];
} else {
+ // XX
this.etal_use_first = this.state.inheritOpt(this.name, "et-al-use-first");
}
if ("boolean" === typeof this.state.tmp["et-al-use-last"]) {
+ //print(" etal_use_last from tmp: "+this.state.tmp["et-al-use-last"]);
this.etal_use_last = this.state.tmp["et-al-use-last"];
} else {
+ //print(" etal_use_last from name: "+this.name.strings["et-al-use-last"]);
+ // XX
this.etal_use_last = this.state.inheritOpt(this.name, "et-al-use-last");
}
+ //print(" etal_use_last: "+this.etal_use_last);
}
+ // Provided for use as the starting level for disambiguation.
if (!this.state.tmp["et-al-min"]) {
this.state.tmp["et-al-min"] = this.etal_min;
}
};
+
+/*global CSL: true */
+
CSL.NameOutput.prototype.setEtAlParameters = function () {
var i, ilen, j, jlen;
for (i = 0, ilen = this.variables.length; i < ilen; i += 1) {
@@ -9387,6 +12669,7 @@ CSL.NameOutput.prototype.setEtAlParameters = function () {
}
}
};
+
CSL.NameOutput.prototype._setEtAlParameter = function (type, v, j) {
var lst, count;
if (type === "persons") {
@@ -9418,15 +12701,22 @@ CSL.NameOutput.prototype._setEtAlParameter = function (type, v, j) {
}
}
};
+
+/*global CSL: true */
+
CSL.NameOutput.prototype.renderAllNames = function () {
+ // Note that et-al/ellipsis parameters are set on the basis
+ // of rendering order through the whole cite.
var pos;
for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
var v = this.variables[i];
+
if (this.freeters[v].length || this.institutions[v].length) {
if (!this.state.tmp.group_context.tip.condition) {
this.state.tmp.just_did_number = false;
}
}
+
pos = this.nameset_base + i;
if (this.freeters[v].length) {
this.freeters[v] = this._renderNames(v, this.freeters[v], pos);
@@ -9437,20 +12727,32 @@ CSL.NameOutput.prototype.renderAllNames = function () {
}
this.renderInstitutionNames();
};
+
CSL.NameOutput.prototype.renderInstitutionNames = function () {
+ // Institutions are split to string list as
+ // this.institutions[v]["long"] and this.institutions[v]["short"]
for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) {
var v = this.variables[i];
for (var j = 0, jlen = this.institutions[v].length; j < jlen; j += 1) {
var institution;
+
var name = this.institutions[v][j];
+
+
+
+ // XXX Start here for institutions
+ // Figure out the three segments: primary, secondary, tertiary
var j, jlen, localesets;
if (this.state.tmp.extension) {
localesets = ["sort"];
} else if (name.isInstitution || name.literal) {
+ // Will never hit this in this function, but preserving
+ // in case we factor this out.
localesets = this.state.opt['cite-lang-prefs'].institutions;
} else {
localesets = this.state.opt['cite-lang-prefs'].persons;
}
+
var slot = {primary:'locale-orig',secondary:false,tertiary:false};
if (localesets) {
var slotnames = ["primary", "secondary", "tertiary"];
@@ -9469,21 +12771,29 @@ CSL.NameOutput.prototype.renderInstitutionNames = function () {
&& !(this.state.tmp.area === "citation"
&& this.state.opt.xclass === "note"
&& this.item && !this.item.position)) {
+
slot.secondary = false;
slot.tertiary = false;
}
+ // Get normalized name object for a start.
+ // true invokes fallback
this.setRenderedName(name);
+
+ // XXXX FROM HERE (instututions)
var institution = this._renderInstitutionName(v, name, slot, j);
+ //this.institutions[v][j] = this._join(institution, "");
this.institutions[v][j] = institution;
}
}
};
+
CSL.NameOutput.prototype._renderInstitutionName = function (v, name, slot, j) {
var secondary, tertiary, long_style, short_style, institution, institution_short, institution_long;
var res = this.getName(name, slot.primary, true);
var primary = res.name;
var usedOrig = res.usedOrig;
if (primary) {
+ //print("primary, v, j = "+primary+", "+v+", "+j);
primary = this.fixupInstitution(primary, v, j);
}
secondary = false;
@@ -9495,6 +12805,7 @@ CSL.NameOutput.prototype._renderInstitutionName = function (v, name, slot, j) {
secondary = this.fixupInstitution(secondary, v, j);
}
}
+ //Zotero.debug("XXX [2] secondary: "+secondary["long"].literal+", slot.secondary: "+slot.secondary);
tertiary = false;
if (slot.tertiary) {
res = this.getName(name, slot.tertiary, false, usedOrig);
@@ -9529,10 +12840,12 @@ CSL.NameOutput.prototype._renderInstitutionName = function (v, name, slot, j) {
}
switch (this.institution.strings["institution-parts"]) {
case "short":
+ // No multilingual for pure short form institution names.
if (primary["short"].length) {
short_style = this._getShortStyle();
institution = [this._composeOneInstitutionPart([n.s.pri, n.s.sec, n.s.ter], slot, short_style, v)];
} else {
+ // Fail over to long.
long_style = this._getLongStyle(primary, v, j);
institution = [this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v)];
}
@@ -9541,6 +12854,7 @@ CSL.NameOutput.prototype._renderInstitutionName = function (v, name, slot, j) {
long_style = this._getLongStyle(primary, v, j);
short_style = this._getShortStyle();
institution_short = this._renderOneInstitutionPart(primary["short"], short_style);
+ // true is to include multilingual supplement
institution_long = this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v);
institution = [institution_short, institution_long];
break;
@@ -9548,11 +12862,13 @@ CSL.NameOutput.prototype._renderInstitutionName = function (v, name, slot, j) {
long_style = this._getLongStyle(primary, v, j);
short_style = this._getShortStyle();
institution_short = this._renderOneInstitutionPart(primary["short"], short_style);
+ // true is to include multilingual supplement
institution_long = this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v);
institution = [institution_long, institution_short];
break;
default:
long_style = this._getLongStyle(primary, v, j);
+ // true is to include multilingual supplement
institution = [this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v)];
break;
}
@@ -9560,6 +12876,7 @@ CSL.NameOutput.prototype._renderInstitutionName = function (v, name, slot, j) {
this.state.tmp.name_node.children.push(blob);
return blob;
};
+
CSL.NameOutput.prototype._composeOneInstitutionPart = function (names, slot, style) {
var primary = false, secondary = false, tertiary = false, primary_tok, secondary_tok, tertiary_tok;
if (names[0]) {
@@ -9586,14 +12903,18 @@ CSL.NameOutput.prototype._composeOneInstitutionPart = function (names, slot, sty
if (names[2]) {
tertiary = this._renderOneInstitutionPart(names[2], style);
}
+ // Compose
var institutionblob;
if (secondary || tertiary) {
this.state.output.openLevel("empty");
+
this.state.output.append(primary);
+
secondary_tok = CSL.Util.cloneToken(style);
if (slot.secondary) {
secondary_tok.strings.prefix = this.state.opt.citeAffixes.institutions[slot.secondary].prefix;
secondary_tok.strings.suffix = this.state.opt.citeAffixes.institutions[slot.secondary].suffix;
+ // Add a space if empty
if (!secondary_tok.strings.prefix) {
secondary_tok.strings.prefix = " ";
}
@@ -9604,10 +12925,12 @@ CSL.NameOutput.prototype._composeOneInstitutionPart = function (names, slot, sty
this.state.output.openLevel(secondary_outer);
this.state.output.append(secondary, secondary_tok);
this.state.output.closeLevel();
+
tertiary_tok = CSL.Util.cloneToken(style);
if (slot.tertiary) {
tertiary_tok.strings.prefix = this.state.opt.citeAffixes.institutions[slot.tertiary].prefix;
tertiary_tok.strings.suffix = this.state.opt.citeAffixes.institutions[slot.tertiary].suffix;
+ // Add a space if empty
if (!tertiary_tok.strings.prefix) {
tertiary_tok.strings.prefix = " ";
}
@@ -9618,17 +12941,23 @@ CSL.NameOutput.prototype._composeOneInstitutionPart = function (names, slot, sty
this.state.output.openLevel(tertiary_outer);
this.state.output.append(tertiary, tertiary_tok);
this.state.output.closeLevel();
+
this.state.output.closeLevel();
+
institutionblob = this.state.output.pop();
} else {
institutionblob = primary;
}
return institutionblob;
};
+
CSL.NameOutput.prototype._renderOneInstitutionPart = function (blobs, style) {
for (var i = 0, ilen = blobs.length; i < ilen; i += 1) {
if (blobs[i]) {
var str = blobs[i];
+ // XXXXX Cut-and-paste code in multiple locations. This code block should be
+ // collected in a function.
+ // Tag: strip-periods-block
if (this.state.tmp.strip_periods) {
str = str.replace(/\./g, "");
} else {
@@ -9639,6 +12968,7 @@ CSL.NameOutput.prototype._renderOneInstitutionPart = function (blobs, style) {
}
}
}
+ //this.state.output.append(blobs[i], style, true);
this.state.tmp.group_context.tip.variable_success = true;
this.state.tmp.can_substitute.replace(false, CSL.LITERAL);
if (str === "!here>>>") {
@@ -9654,16 +12984,24 @@ CSL.NameOutput.prototype._renderOneInstitutionPart = function (blobs, style) {
}
return this._join(blobs, this.institution.strings["part-separator"]);
};
+
CSL.NameOutput.prototype._renderNames = function (v, values, pos, j) {
+ //
var ret = false;
if (values.length) {
var names = [];
for (var i = 0, ilen = values.length; i < ilen; i += 1) {
var name = values[i];
+
+ // XXX We'll start here with attempts.
+ // Figure out the three segments: primary, secondary, tertiary
var ret, localesets;
+
if (this.state.tmp.extension) {
localesets = ["sort"];
} else if (name.isInstitution || name.literal) {
+ // Will never hit this in this function, but preserving
+ // in case we factor this out.
localesets = this.state.opt['cite-lang-prefs'].institutions;
} else {
localesets = this.state.opt['cite-lang-prefs'].persons;
@@ -9684,10 +13022,15 @@ CSL.NameOutput.prototype._renderNames = function (v, values, pos, j) {
&& !(this.state.tmp.area === "citation"
&& this.state.opt.xclass === "note"
&& this.item && !this.item.position))) {
+
slot.secondary = false;
slot.tertiary = false;
}
+
+ // primary
+ // true is for fallback
this.setRenderedName(name);
+
if (!name.literal && !name.isInstitution) {
var nameBlob = this._renderPersonalName(v, name, slot, pos, i, j);
var nameToken = CSL.Util.cloneToken(this.name);
@@ -9697,11 +13040,16 @@ CSL.NameOutput.prototype._renderNames = function (v, values, pos, j) {
names.push(this._renderInstitutionName(v, name, slot, j));
}
}
+ //ret = this._join(names, "");
ret = this.joinPersons(names, pos, j);
}
return ret;
};
+
+
CSL.NameOutput.prototype._renderPersonalName = function (v, name, slot, pos, i, j) {
+ // XXXX FROM HERE (persons)
+
var res = this.getName(name, slot.primary, true);
var primary = this._renderOnePersonalName(res.name, pos, i, j);
var secondary = false;
@@ -9718,36 +13066,49 @@ CSL.NameOutput.prototype._renderPersonalName = function (v, name, slot, pos, i,
tertiary = this._renderOnePersonalName(res.name, pos, i, j);
}
}
+ // Now compose them to a unit
var personblob;
if (secondary || tertiary) {
+
this.state.output.openLevel("empty");
+
this.state.output.append(primary);
+
var secondary_tok = new CSL.Token();
if (slot.secondary) {
secondary_tok.strings.prefix = this.state.opt.citeAffixes.persons[slot.secondary].prefix;
secondary_tok.strings.suffix = this.state.opt.citeAffixes.persons[slot.secondary].suffix;
+ // Add a space if empty
if (!secondary_tok.strings.prefix) {
secondary_tok.strings.prefix = " ";
}
}
this.state.output.append(secondary, secondary_tok);
+
var tertiary_tok = new CSL.Token();
if (slot.tertiary) {
tertiary_tok.strings.prefix = this.state.opt.citeAffixes.persons[slot.tertiary].prefix;
tertiary_tok.strings.suffix = this.state.opt.citeAffixes.persons[slot.tertiary].suffix;
+ // Add a space if empty
if (!tertiary_tok.strings.prefix) {
tertiary_tok.strings.prefix = " ";
}
}
this.state.output.append(tertiary, tertiary_tok);
+
this.state.output.closeLevel();
+
personblob = this.state.output.pop();
} else {
personblob = primary;
}
return personblob;
};
+
CSL.NameOutput.prototype._isRomanesque = function (name) {
+ // 0 = entirely non-romanesque
+ // 1 = mixed content
+ // 2 = pure romanesque
var ret = 2;
if (!name.family.replace(/\"/g, '').match(CSL.ROMANESQUE_REGEXP)) {
ret = 0;
@@ -9766,8 +13127,10 @@ CSL.NameOutput.prototype._isRomanesque = function (name) {
ret = 1;
}
}
+ //print("name: "+name.given+", multi: "+name.multi+", ret: "+ret);
return ret;
};
+
CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) {
var name = value;
var dropping_particle = this._droppingParticle(name, pos, j);
@@ -9804,14 +13167,17 @@ CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) {
}
}
var has_hyphenated_non_dropping_particle = hasJoiningPunctuation(non_dropping_particle);
+
var blob, merged, first, second;
if (romanesque === 0) {
+ // XXX handle affixes for given and family
blob = this._join([non_dropping_particle, family, given], "");
} else if (romanesque === 1 || name["static-ordering"]) { // entry likes sort order
blob = this._join([non_dropping_particle, family, given], " ");
} else if (name["reverse-ordering"]) { // entry likes reverse order
blob = this._join([given, non_dropping_particle, family], " ");
} else if (this.state.tmp.sort_key_flag) {
+ // ok with no affixes here
if (this.state.opt["demote-non-dropping-particle"] === "never") {
first = this._join([non_dropping_particle, family, dropping_particle], " ");
merged = this._join([first, given], this.state.opt.sort_sep);
@@ -9822,11 +13188,23 @@ CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) {
blob = this._join([merged, suffix], " ");
}
} else if (this.state.inheritOpt(this.name, "name-as-sort-order") === "all" || (this.state.inheritOpt(this.name, "name-as-sort-order") === "first" && i === 0 && (j === 0 || "undefined" === typeof j))) {
+ //
+ // Discretionary sort ordering and inversions
+ //
if (["Lord", "Lady"].indexOf(name.given) > -1) {
sort_sep = ", ";
}
+
+ // XXX Needs a more robust solution than this
+ // XXX See https://forums.zotero.org/discussion/30974/any-idea-why-an-a-author-comes-last-in-the-bibliography/#Item_30
+
+ //if (["always", "display-and-sort"].indexOf(this.state.opt["demote-non-dropping-particle"]) > -1 && !has_hyphenated_non_dropping_particle) {
if (["always", "display-and-sort"].indexOf(this.state.opt["demote-non-dropping-particle"]) > -1) {
+ // Drop non-dropping particle
+ //second = this._join([given, dropping_particle, non_dropping_particle], " ");
second = this._join([given, dropping_particle], (name["comma-dropping-particle"] + " "));
+
+ // This would be a problem with al-Ghazali. Avoided by has_hyphenated_non_dropping_particle check above.
second = this._join([second, non_dropping_particle], " ");
if (second && this.given) {
second.strings.prefix = this.given.strings.prefix;
@@ -9839,6 +13217,13 @@ CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) {
merged = this._join([family, second], sort_sep);
blob = this._join([merged, suffix], sort_sep);
} else {
+ // Don't drop particle.
+ // Don't do this
+ //if (this.state.tmp.area === "bibliography" && !this.state.tmp.term_predecessor && non_dropping_particle) {
+ // if (!has_hyphenated_non_dropping_particle) {
+ // non_dropping_particle.blobs = CSL.Output.Formatters["capitalize-first"](this.state, non_dropping_particle.blobs)
+ // }
+ //}
if (has_hyphenated_non_dropping_particle) {
first = this._join([non_dropping_particle, family], "");
} else {
@@ -9848,27 +13233,34 @@ CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) {
first.strings.prefix = this.family.strings.prefix;
first.strings.suffix = this.family.strings.suffix;
}
+
second = this._join([given, dropping_particle], (name["comma-dropping-particle"] + " "));
+ //second = this._join([given, dropping_particle], " ");
if (second && this.given) {
second.strings.prefix = this.given.strings.prefix;
second.strings.suffix = this.given.strings.suffix;
}
+
merged = this._join([first, second], sort_sep);
blob = this._join([merged, suffix], sort_sep);
}
} else { // plain vanilla
if (name["dropping-particle"] && name.family && !name["non-dropping-particle"]) {
- if (["'","\u02bc","\u2019","-"].indexOf(name["dropping-particle"].slice(-1)) > -1) {
+ var dp = name["dropping-particle"];
+ var apostrophes = ["'","\u02bc","\u2019","-"];
+ if (apostrophes.indexOf(dp.slice(-1)) > -1 && dp.slice(0, -1) !== "de") {
family = this._join([dropping_particle, family], "");
dropping_particle = false;
}
}
+
var space = " ";
if (this.state.inheritOpt(this.name, "initialize-with")
&& this.state.inheritOpt(this.name, "initialize-with").match(/[\u00a0\ufeff]/)
&& ["fr", "ru", "cs"].indexOf(this.state.opt["default-locale"][0].slice(0, 2)) > -1) {
space = "\u00a0";
}
+
if (has_hyphenated_non_dropping_particle) {
second = this._join([non_dropping_particle, family], "");
second = this._join([dropping_particle, second], space);
@@ -9889,12 +13281,25 @@ CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) {
}
blob = this._join([given, second], (name["comma-dropping-particle"] + space));
}
+ // XXX Just generally assume for the present that personal names render something
this.state.tmp.group_context.tip.variable_success = true;
this.state.tmp.can_substitute.replace(false, CSL.LITERAL);
this.state.tmp.term_predecessor = true;
+ // notSerious
+ //this.state.output.append(blob, "literal", true);
+ //var ret = this.state.output.pop();
this.state.tmp.name_node.children.push(blob);
return blob;
};
+
+/*
+ // Do not include given name, dropping particle or suffix in strict short form of name
+
+ // initialize if appropriate
+*/
+
+// Input names should be touched by _normalizeNameInput()
+// exactly once: this is not idempotent.
CSL.NameOutput.prototype._normalizeNameInput = function (value) {
var name = {
literal:value.literal,
@@ -9917,6 +13322,9 @@ CSL.NameOutput.prototype._normalizeNameInput = function (value) {
this._parseName(name);
return name;
};
+
+// _transformNameset() replaced with enhanced transform.name().
+
CSL.NameOutput.prototype._stripPeriods = function (tokname, str) {
var decor_tok = this[tokname + "_decor"];
if (str) {
@@ -9933,6 +13341,7 @@ CSL.NameOutput.prototype._stripPeriods = function (tokname, str) {
}
return str;
};
+
CSL.NameOutput.prototype._nonDroppingParticle = function (name) {
var ndp = name["non-dropping-particle"];
if (ndp && this.state.tmp.sort_key_flag) {
@@ -9944,6 +13353,7 @@ CSL.NameOutput.prototype._nonDroppingParticle = function (name) {
}
return false;
};
+
CSL.NameOutput.prototype._droppingParticle = function (name, pos, j) {
var dp = name["dropping-particle"];
if (dp && this.state.tmp.sort_key_flag) {
@@ -9970,6 +13380,7 @@ CSL.NameOutput.prototype._droppingParticle = function (name, pos, j) {
}
return false;
};
+
CSL.NameOutput.prototype._familyName = function (name) {
var str = this._stripPeriods("family", name.family);
if (this.state.output.append(str, this.family_decor, true)) {
@@ -9977,8 +13388,17 @@ CSL.NameOutput.prototype._familyName = function (name) {
}
return false;
};
+
CSL.NameOutput.prototype._givenName = function (name, pos, i) {
var ret;
+ // citation
+ // use disambig as-is
+ // biblography
+ // use disambig only if it boosts over the default
+ // SO WHAT IS THE DEFAULT?
+ // A: If "form" is short, it's 0.
+ // If "form" is long, initialize-with exists (and initialize is not false) it's 1
+ // If "form" is long, and initialize_with does not exist, it's 2.
var formIsShort = this.state.inheritOpt(this.name, "form", "name-form", "long") !== "long";
var initializeIsTurnedOn = this.state.inheritOpt(this.name, "initialize") !== false;
var hasInitializeWith = "string" === typeof this.state.inheritOpt(this.name, "initialize-with") && !name.block_initialize;
@@ -10017,6 +13437,7 @@ CSL.NameOutput.prototype._givenName = function (name, pos, i) {
} else if (useLevel === 2) {
name.given = CSL.Util.Names.unInitialize(this.state, name.given);
}
+
var str = this._stripPeriods("given", name.given);
var rendered = this.state.output.append(str, this.given_decor, true);
if (rendered) {
@@ -10025,11 +13446,15 @@ CSL.NameOutput.prototype._givenName = function (name, pos, i) {
}
return false;
};
+
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);
}
+
str = this._stripPeriods("family", str);
var toSuffix = '';
if (str && str.slice(-1) === '.') {
@@ -10044,6 +13469,7 @@ CSL.NameOutput.prototype._nameSuffix = function (name) {
}
return false;
};
+
CSL.NameOutput.prototype._getLongStyle = function (name) {
var long_style;
if (name["short"].length) {
@@ -10060,6 +13486,7 @@ CSL.NameOutput.prototype._getLongStyle = function (name) {
}
return long_style;
};
+
CSL.NameOutput.prototype._getShortStyle = function () {
var short_style;
if (this.institutionpart["short"]) {
@@ -10069,6 +13496,7 @@ CSL.NameOutput.prototype._getShortStyle = function () {
}
return short_style;
};
+
CSL.NameOutput.prototype._parseName = function (name) {
if (!name["parse-names"] && "undefined" !== typeof name["parse-names"]) {
return name;
@@ -10082,6 +13510,7 @@ CSL.NameOutput.prototype._parseName = function (name) {
if (name.family
&& (name.family.slice(0, 1) === '"' && name.family.slice(-1) === '"')
|| (!name["parse-names"] && "undefined" !== typeof name["parse-names"])) {
+
name.family = name.family.slice(1, -1);
noparse = true;
name["parse-names"] = 0;
@@ -10096,18 +13525,42 @@ CSL.NameOutput.prototype._parseName = function (name) {
}
}
};
+
+/*
+ * Return a single name object
+ */
+
+// The interface is a mess, but this should serve.
+
CSL.NameOutput.prototype.getName = function (name, slotLocaleset, fallback, stopOrig) {
+
+ // Needs to tell us whether we used orig or not.
+
if (stopOrig && slotLocaleset === 'locale-orig') {
return {name:false,usedOrig:stopOrig};
}
+
+ // Normalize to string
if (!name.family) {
name.family = "";
}
if (!name.given) {
name.given = "";
}
+
+ // Recognized params are:
+ // block-initialize
+ // transliterated
+ // static-ordering
+ // full-form-always
+ // All default to false, except for static-ordering, which is initialized
+ // with a sniff.
var name_params = {};
+ // Determines the default static-order setting based on the characters
+ // used in the headline field. Will be overridden by locale-based
+ // parameters evaluated against explicit lang tags set on the (sub)field.
name_params["static-ordering"] = this.getStaticOrder(name);
+
var foundTag = true;
var langTag;
if (slotLocaleset !== 'locale-orig') {
@@ -10121,6 +13574,7 @@ CSL.NameOutput.prototype.getName = function (name, slotLocaleset, fallback, stop
var isInstitution = name.isInstitution;
name = name.multi._key[langTag];
name.isInstitution = isInstitution;
+ // Set name formatting params
name_params = this.getNameParams(langTag);
name_params.transliterated = true;
break;
@@ -10128,6 +13582,7 @@ CSL.NameOutput.prototype.getName = function (name, slotLocaleset, fallback, stop
}
}
}
+
if (!foundTag) {
langTag = false;
if (name.multi && name.multi.main) {
@@ -10139,9 +13594,12 @@ CSL.NameOutput.prototype.getName = function (name, slotLocaleset, fallback, stop
name_params = this.getNameParams(langTag);
}
}
+
if (!fallback && !foundTag) {
return {name:false,usedOrig:stopOrig};
}
+
+ // Normalize to string (again)
if (!name.family) {
name.family = "";
}
@@ -10152,6 +13610,7 @@ CSL.NameOutput.prototype.getName = function (name, slotLocaleset, fallback, stop
delete name.family;
delete name.given;
}
+ // var clone the item before writing into it
name = {
family:name.family,
given:name.given,
@@ -10171,6 +13630,7 @@ CSL.NameOutput.prototype.getName = function (name, slotLocaleset, fallback, stop
isInstitution:name.isInstitution,
multi:name.multi
};
+
if (!name.literal && (!name.given && name.family && name.isInstitution)) {
name.literal = name.family;
}
@@ -10187,6 +13647,7 @@ CSL.NameOutput.prototype.getName = function (name, slotLocaleset, fallback, stop
}
return {name:name,usedOrig:usedOrig};
};
+
CSL.NameOutput.prototype.getNameParams = function (langTag) {
var ret = {};
var langspec = CSL.localeResolve(this.Item.language, this.state.opt["default-locale"][0]);
@@ -10206,11 +13667,13 @@ CSL.NameOutput.prototype.getNameParams = function (langTag) {
if (name_never_short && name_never_short[field_lang_bare]) {
ret["full-form-always"] = true;
}
+
if (ret["static-ordering"]) {
ret["block-initialize"] = true;
}
return ret;
};
+
CSL.NameOutput.prototype.setRenderedName = function (name) {
if (this.state.tmp.area === "bibliography") {
var strname = "";
@@ -10222,27 +13685,24 @@ CSL.NameOutput.prototype.setRenderedName = function (name) {
this.state.tmp.rendered_name.push(strname);
}
};
+
CSL.NameOutput.prototype.fixupInstitution = function (name, varname, listpos) {
- if (this.state.sys.getHumanForm && "legal_case" === this.Item.type && "authority" === varname) {
- name.literal = this.state.sys.getHumanForm(this.Item.jurisdiction, name.literal, true);
- }
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();
}
+
var long_form = name["long"];
var short_form = name["long"].slice();
var use_short_form = false;
if (this.state.sys.getAbbreviation) {
var jurisdiction = this.Item.jurisdiction;
for (var j = 0, jlen = long_form.length; j < jlen; j += 1) {
- var normalizedKey = long_form[j];
- if (this.state.sys.normalizeAbbrevsKey) {
- normalizedKey = this.state.sys.normalizeAbbrevsKey(varname, long_form[j]);
- }
- jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", normalizedKey);
- if (this.state.transform.abbrevs[jurisdiction]["institution-part"][normalizedKey]) {
- short_form[j] = this.state.transform.abbrevs[jurisdiction]["institution-part"][normalizedKey];
+ 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;
}
}
@@ -10254,6 +13714,8 @@ CSL.NameOutput.prototype.fixupInstitution = function (name, varname, listpos) {
}
return name;
};
+
+
CSL.NameOutput.prototype.getStaticOrder = function (name, refresh) {
var static_ordering_val = false;
if (!refresh && name["static-ordering"]) {
@@ -10268,31 +13730,40 @@ CSL.NameOutput.prototype.getStaticOrder = function (name, refresh) {
if (this.state.opt['auto-vietnamese-names']
&& (CSL.VIETNAMESE_NAMES.exec(name.family + " " + name.given)
&& CSL.VIETNAMESE_SPECIALS.exec(name.family + name.given))) {
+
static_ordering_val = true;
}
}
return static_ordering_val;
};
+
+
CSL.NameOutput.prototype._splitInstitution = function (value, v, i) {
var ret = {};
+ // Due to a bug in Juris-M, there are a small number of items in my accounts that have
+ // a institution parent, and a personal child. The bug that created them was fixed before
+ // release, but this hack keeps them from crashing the processor.
if (!value.literal && value.family) {
value.literal = value.family;
delete value.family;
}
var splitInstitution = value.literal.replace(/\s*\|\s*/g, "|");
+ // check for total and utter abbreviation IFF form="short"
splitInstitution = splitInstitution.split("|");
if (this.institution.strings.form === "short" && this.state.sys.getAbbreviation) {
+ // On a match, drop unused elements to yield a single key.
var jurisdiction = this.Item.jurisdiction;
for (var j = splitInstitution.length; j > 0; j += -1) {
var str = splitInstitution.slice(0, j).join("|");
- var normalizedKey = str;
- if (this.state.sys.normalizeAbbrevsKey) {
- normalizedKey = this.state.sys.normalizeAbbrevsKey(v, str);
- }
- jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-entire", normalizedKey);
- if (this.state.transform.abbrevs[jurisdiction]["institution-entire"][normalizedKey]) {
- var splitLst = this.state.transform.abbrevs[jurisdiction]["institution-entire"][normalizedKey];
+ 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(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();
@@ -10311,10 +13782,13 @@ CSL.NameOutput.prototype._splitInstitution = function (value, v, i) {
}
}
splitInstitution.reverse();
+ //print("into _trimInstitution with splitInstitution, v, i = "+splitInstitution+", "+v+", "+i);
ret["long"] = this._trimInstitution(splitInstitution, v, i);
return ret;
};
+
CSL.NameOutput.prototype._trimInstitution = function (subunits, v) {
+ //
var use_first = false;
var append_last = false;
var s = subunits.slice();
@@ -10324,6 +13798,7 @@ CSL.NameOutput.prototype._trimInstitution = function (subunits, v) {
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;
@@ -10359,9 +13834,14 @@ CSL.NameOutput.prototype._trimInstitution = function (subunits, v) {
append_last = 0;
}
}
+ // Now that we've determined the value of append_last
+ // (use-last), avoid overlaps.
if (use_first > subunits.length - append_last) {
use_first = subunits.length - append_last;
}
+
+ // This could be more clear. use-last takes priority
+ // in the event of overlap, because of adjustment above
subunits = subunits.slice(0, use_first);
s = s.slice(use_first);
if (append_last) {
@@ -10374,11 +13854,15 @@ CSL.NameOutput.prototype._trimInstitution = function (subunits, v) {
}
return subunits;
};
+
+/*global CSL: true */
+
CSL.PublisherOutput = function (state, group_tok) {
this.state = state;
this.group_tok = group_tok;
this.varlist = [];
};
+
CSL.PublisherOutput.prototype.render = function () {
this.clearVars();
this.composeAndBlob();
@@ -10386,6 +13870,15 @@ CSL.PublisherOutput.prototype.render = function () {
this.composePublishers();
this.joinPublishers();
};
+
+
+// XXX Figure out how to adapt this to the House of Lords / House of Commons
+// joint committee case
+
+// name_delimiter
+// delimiter_precedes_last
+// and
+
CSL.PublisherOutput.prototype.composeAndBlob = function () {
this.and_blob = {};
var and_term = false;
@@ -10399,9 +13892,11 @@ CSL.PublisherOutput.prototype.composeAndBlob = function () {
tok.strings.prefix = " ";
this.state.output.append(and_term, tok, true);
var no_delim = this.state.output.pop();
+
tok.strings.prefix = this.group_tok.strings["subgroup-delimiter"];
this.state.output.append(and_term, tok, true);
var with_delim = this.state.output.pop();
+
this.and_blob.single = false;
this.and_blob.multiple = false;
if (and_term) {
@@ -10416,17 +13911,22 @@ CSL.PublisherOutput.prototype.composeAndBlob = function () {
}
}
};
+
+
CSL.PublisherOutput.prototype.composeElements = function () {
for (var i = 0, ilen = 2; i < ilen; i += 1) {
var varname = ["publisher", "publisher-place"][i];
for (var j = 0, jlen = this["publisher-list"].length; j < jlen; j += 1) {
var str = this[varname + "-list"][j];
var tok = this[varname + "-token"];
+ // notSerious
this.state.output.append(str, tok, true);
this[varname + "-list"][j] = this.state.output.pop();
}
}
};
+
+
CSL.PublisherOutput.prototype.composePublishers = function () {
var blobs;
for (var i = 0, ilen = this["publisher-list"].length; i < ilen; i += 1) {
@@ -10434,13 +13934,21 @@ CSL.PublisherOutput.prototype.composePublishers = function () {
this["publisher-list"][i] = this._join(blobs, this.group_tok.strings.delimiter);
}
};
+
+
CSL.PublisherOutput.prototype.joinPublishers = function () {
var blobs = this["publisher-list"];
var publishers = this._join(blobs, this.group_tok.strings["subgroup-delimiter"], this.and_blob.single, this.and_blob.multiple, this.group_tok);
this.state.output.append(publishers, "literal");
};
+
+
+// blobs, delimiter, single, multiple, tokenname
+// Tokenname is a key at top level of this object.
CSL.PublisherOutput.prototype._join = CSL.NameOutput.prototype._join;
CSL.PublisherOutput.prototype._getToken = CSL.NameOutput.prototype._getToken;
+
+
CSL.PublisherOutput.prototype.clearVars = function () {
this.state.tmp["publisher-list"] = false;
this.state.tmp["publisher-place-list"] = false;
@@ -10448,6 +13956,9 @@ CSL.PublisherOutput.prototype.clearVars = function () {
this.state.tmp["publisher-token"] = false;
this.state.tmp["publisher-place-token"] = false;
};
+
+/*global CSL: true */
+
CSL.evaluateLabel = function (node, state, Item, item) {
var myterm;
if ("locator" === node.strings.term) {
@@ -10464,8 +13975,11 @@ CSL.evaluateLabel = function (node, state, Item, item) {
} else {
myterm = node.strings.term;
}
+
+ // Plurals detection.
var plural = node.strings.plural;
if ("number" !== typeof plural) {
+ // (node, ItemObject, variable, type)
var theItem = (item && node.strings.term === "locator") ? item : Item;
if (theItem[node.strings.term]) {
state.processNumber(false, theItem, node.strings.term, Item.type);
@@ -10476,6 +13990,7 @@ CSL.evaluateLabel = function (node, state, Item, item) {
state.tmp.shadow_numbers[node.strings.term].labelCapitalizeIfFirst = node.strings.capitalize_if_first;
state.tmp.shadow_numbers[node.strings.term].labelDecorations = node.decorations.slice();
}
+
if (["locator", "number", "page"].indexOf(node.strings.term) > -1 && state.tmp.shadow_numbers[node.strings.term].label) {
myterm = state.tmp.shadow_numbers[node.strings.term].label;
}
@@ -10488,6 +14003,7 @@ CSL.evaluateLabel = function (node, state, Item, item) {
}
return CSL.castLabel(state, node, myterm, plural, CSL.TOLERANT);
};
+
CSL.castLabel = function (state, node, term, plural, mode) {
var label_form = node.strings.form;
var label_capitalize_if_first = node.strings.capitalize_if_first;
@@ -10501,6 +14017,9 @@ CSL.castLabel = function (state, node, term, plural, mode) {
if (label_capitalize_if_first) {
ret = CSL.Output.Formatters["capitalize-first"](state, ret);
}
+ // XXXXX Cut-and-paste code in multiple locations. This code block should be
+ // collected in a function.
+ // Tag: strip-periods-block
if (state.tmp.strip_periods) {
ret = ret.replace(/\./g, "");
} else {
@@ -10513,6 +14032,9 @@ CSL.castLabel = function (state, node, term, plural, mode) {
}
return ret;
};
+
+/*global CSL: true */
+
CSL.Node.name = {
build: function (state, target) {
var func;
@@ -10524,19 +14046,37 @@ CSL.Node.name = {
} else {
oldTmpRoot = state.tmp.root;
}
+ // Many CSL styles set et-al-[min|use-first]
+ // and et-al-subsequent-[min|use-first] to the same
+ // value.
+ // Set state.opt.update_mode = CSL.POSITION if
+ // et-al-subsequent-min or et-al-subsequent-use-first
+ // are set AND their value differs from their plain
+ // counterparts.
if (state.inheritOpt(this, "et-al-subsequent-min")
&& (state.inheritOpt(this, "et-al-subsequent-min") !== state.inheritOpt(this, "et-al-min"))) {
+
state.opt.update_mode = CSL.POSITION;
}
if (state.inheritOpt(this, "et-al-subsequent-use-first")
&& (state.inheritOpt(this, "et-al-subsequent-use-first") !== state.inheritOpt(this, "et-al-use-first"))) {
+
state.opt.update_mode = CSL.POSITION;
}
+
state.tmp.root = oldTmpRoot;
+
func = function (state) {
+ // Et-al (onward processing in node_etal.js and node_names.js)
+ // XXXXX Why is this necessary? This is available on this.name, right?
state.tmp.etal_term = "et-al";
+
+ // Use default delimiter as fallback, in a way that allows explicit
+ // empty strings.
state.tmp.name_delimiter = state.inheritOpt(this, "delimiter", "name-delimiter", ", ");
state.tmp["delimiter-precedes-et-al"] = state.inheritOpt(this, "delimiter-precedes-et-al");
+
+ // And
if ("text" === state.inheritOpt(this, "and")) {
this.and_term = state.getTerm("and", "long", 0);
} else if ("symbol" === state.inheritOpt(this, "and")) {
@@ -10550,10 +14090,16 @@ CSL.Node.name = {
if (CSL.STARTSWITH_ROMANESQUE_REGEXP.test(this.and_term)) {
this.and_prefix_single = " ";
this.and_prefix_multiple = ", ";
+ // Workaround to allow explicit empty string
+ // on cs:name delimiter.
if ("string" === typeof state.tmp.name_delimiter) {
this.and_prefix_multiple = state.tmp.name_delimiter;
}
this.and_suffix = " ";
+
+ // Really can't inspect these values in the build phase. Sorry.
+ //state.build.name_delimiter = this.strings.delimiter;
+
} else {
this.and_prefix_single = "";
this.and_prefix_multiple = "";
@@ -10562,6 +14108,8 @@ CSL.Node.name = {
if (state.inheritOpt(this, "delimiter-precedes-last") === "always") {
this.and_prefix_single = state.tmp.name_delimiter;
} else if (state.inheritOpt(this, "delimiter-precedes-last") === "never") {
+ // Slightly fragile: could test for charset here to make
+ // this more certain.
if (this.and_prefix_multiple) {
this.and_prefix_multiple = " ";
}
@@ -10573,6 +14121,7 @@ CSL.Node.name = {
this.and_prefix_multiple = " ";
}
}
+
this.and = {};
if (state.inheritOpt(this, "and")) {
state.output.append(this.and_term, "empty", true);
@@ -10584,6 +14133,7 @@ CSL.Node.name = {
this.and.multiple.strings.prefix = this.and_prefix_multiple;
this.and.multiple.strings.suffix = this.and_suffix;
} else if (state.tmp.name_delimiter) {
+ // This is a little weird, but it works.
this.and.single = new CSL.Blob(state.tmp.name_delimiter);
this.and.single.strings.prefix = "";
this.and.single.strings.suffix = "";
@@ -10591,9 +14141,21 @@ CSL.Node.name = {
this.and.multiple.strings.prefix = "";
this.and.multiple.strings.suffix = "";
}
+
this.ellipsis = {};
if (state.inheritOpt(this, "et-al-use-last")) {
+ // We use the dedicated Unicode ellipsis character because
+ // it is recommended by some editors, and can be more easily
+ // identified for find and replace operations.
+ // Source: http://en.wikipedia.org/wiki/Ellipsis#Computer_representations
+ //
+
+ // Eventually, this should be localized as a term in CSL, with some
+ // mechanism for triggering appropriate punctuation handling around
+ // the ellipsis placeholder (Polish is a particularly tough case for that).
this.ellipsis_term = "\u2026";
+ // Similar treatment to "and", above, will be needed
+ // here when this becomes a locale term.
this.ellipsis_prefix_single = " ";
this.ellipsis_prefix_multiple = state.inheritOpt(this, "delimiter", "name-delimiter", ", ");
this.ellipsis_suffix = " ";
@@ -10604,6 +14166,22 @@ CSL.Node.name = {
this.ellipsis.multiple.strings.prefix = this.ellipsis_prefix_multiple;
this.ellipsis.multiple.strings.suffix = this.ellipsis_suffix;
}
+
+ // et-al parameters are annoyingly incomprehensible
+ // again.
+ //
+ // Explanation probably just adds a further layer of
+ // irritation, but what's INTENDED here is that
+ // the state.tmp et-al variables are set from the
+ // cs:key element when composing sort keys, and a
+ // macro containing a name can be called from cs:key.
+ // So when cs:key sets et-al attributes, they are
+ // set on state.tmp, and when the key is finished
+ // processing, the state.tmp variables are reset to
+ // undefined. IN THEORY the state.tmp et-al variables
+ // will not be used in other contexts. I hope.
+ //
+ // Anyway, the current tests now seem to pass.
if ("undefined" === typeof state.tmp["et-al-min"]) {
state.tmp["et-al-min"] = state.inheritOpt(this, "et-al-min");
}
@@ -10611,28 +14189,43 @@ CSL.Node.name = {
state.tmp["et-al-use-first"] = state.inheritOpt(this, "et-al-use-first");
}
if ("undefined" === typeof state.tmp["et-al-use-last"]) {
+ //print(" setting et-al-use-last from name: "+this.strings["et-al-use-last"]);
state.tmp["et-al-use-last"] = state.inheritOpt(this, "et-al-use-last");
}
+
state.nameOutput.name = this;
};
+
state.build.name_flag = true;
+
this.execs.push(func);
}
target.push(this);
}
};
+
+
+
+/*global CSL: true */
+
CSL.Node["name-part"] = {
build: function (state) {
state.build[this.strings.name] = this;
}
};
+
+/*global CSL: true */
+
CSL.Node.names = {
build: function (state, target) {
var func;
+ // CSL.debug = print;
+
if (this.tokentype === CSL.START || this.tokentype === CSL.SINGLETON) {
CSL.Util.substituteStart.call(this, state, target);
state.build.substitute_level.push(1);
}
+
if (this.tokentype === CSL.SINGLETON) {
state.build.names_variables[state.build.names_variables.length-1].concat(this.variables);
for (var i in this.variables) {
@@ -10647,20 +14240,28 @@ CSL.Node.names = {
};
this.execs.push(func);
}
+
if (this.tokentype === CSL.START) {
+
state.build.names_flag = true;
state.build.name_flag = false;
state.build.names_level += 1;
state.build.names_variables.push(this.variables);
state.build.name_label.push({});
+ // init can substitute
+ // init names
func = function (state) {
state.tmp.can_substitute.push(true);
- state.parallel.StartVariable("names",this.variables[0]);
state.nameOutput.init(this);
};
this.execs.push(func);
+
}
+
if (this.tokentype === CSL.END) {
+
+ // Set/reset name blobs if they exist, for processing
+ // by namesOutput()
for (var i = 0, ilen = 3; i < ilen; i += 1) {
var key = ["family", "given", "et-al"][i];
this[key] = state.build[key];
@@ -10668,18 +14269,31 @@ CSL.Node.names = {
state.build[key] = undefined;
}
}
+ // Labels, if any
this.label = state.build.name_label[state.build.name_label.length-1];
state.build.names_level += -1;
state.build.names_variables.pop();
state.build.name_label.pop();
+
+ // The with term. This isn't the right place
+ // for this, but it's all hard-wired at the
+ // moment.
+
+ // "and" and "ellipsis" are set in node_name.js
func = function (state) {
+ // Et-al (strings only)
+ // Blob production has to happen inside nameOutput()
+ // since proper escaping requires access to the output
+ // queue.
if (state.tmp.etal_node) {
this.etal_style = state.tmp.etal_node;
} else {
this.etal_style = "empty";
}
+
this.etal_term = state.getTerm(state.tmp.etal_term, "long", 0);
this.etal_prefix_single = " ";
+ // Should be name delimiter, not hard-wired.
this.etal_prefix_multiple = state.tmp.name_delimiter;
if (state.tmp["delimiter-precedes-et-al"] === "always") {
this.etal_prefix_single = state.tmp.name_delimiter;
@@ -10691,6 +14305,9 @@ CSL.Node.names = {
}
this.etal_suffix = "";
if (!CSL.STARTSWITH_ROMANESQUE_REGEXP.test(this.etal_term)) {
+ // Not sure what the correct treatment is here, but we should not suppress
+ // a comma-space.
+ // https://forums.zotero.org/discussion/76679/delimiter-precedes-et-al-always-dose-not-work-in-locale-zh-cn
if (this.etal_prefix_single === " ") {
this.etal_prefix_single = "";
}
@@ -10701,11 +14318,17 @@ CSL.Node.names = {
this.etal_suffix = "";
}
}
+ // et-al affixes are further adjusted in nameOutput(),
+ // after the term (possibly changed in cs:et-al) is known.
+
+
for (var i = 0, ilen = 3; i < ilen; i += 1) {
var key = ["family", "given"][i];
state.nameOutput[key] = this[key];
}
state.nameOutput["with"] = this["with"];
+
+ // REMOVE THIS
var mywith = "with";
var with_default_prefix = "";
var with_suffix = "";
@@ -10732,7 +14355,12 @@ CSL.Node.names = {
thewith.multiple.strings.prefix = with_default_prefix;
}
state.nameOutput["with"] = thewith;
+
+
+ // XXX label style should be set per variable, since they may differ
+ // XXX with full-form nested names constructs
state.nameOutput.label = this.label;
+
state.nameOutput.etal_style = this.etal_style;
state.nameOutput.etal_term = this.etal_term;
state.nameOutput.etal_prefix_single = this.etal_prefix_single;
@@ -10744,29 +14372,51 @@ CSL.Node.names = {
state.tmp["et-al-use-last"] = undefined;
};
this.execs.push(func);
+
+ // unsets
func = function (state) {
if (!state.tmp.can_substitute.pop()) {
state.tmp.can_substitute.replace(false, CSL.LITERAL);
}
- state.parallel.CloseVariable("names");
+
+ // For posterity ...
+ //
+ // This was enough to fix the issue reported here:
+ //
+ // http://forums.zotero.org/discussion/25223/citeproc-bug-substitute-doesnt-work-correctly-for-title-macro/
+ //
+ // The remainder of the changes applied in the same patch
+ // relate to a label assignments, which were found to be
+ // buggy while working on the issue. The test covering
+ // both problems is here:
+ //
+ // https://bitbucket.org/bdarcus/citeproc-test/src/ab136a6aa8f2/processor-tests/humans/substitute_SuppressOrdinaryVariable.txt
if (state.tmp.can_substitute.mystack.length === 1) {
state.tmp.can_block_substitute = false;
}
};
this.execs.push(func);
+
state.build.name_flag = false;
}
target.push(this);
+
if (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON) {
state.build.substitute_level.pop();
CSL.Util.substituteEnd.call(this, state, target);
}
}
};
+
+/*global CSL: true */
+
CSL.Node.number = {
build: function (state, target) {
var func;
CSL.Util.substituteStart.call(this, state, target);
+ //
+ // This should push a rangeable object to the queue.
+ //
if (this.strings.form === "roman") {
this.formatter = state.fun.romanizer;
} else if (this.strings.form === "ordinal") {
@@ -10780,7 +14430,20 @@ CSL.Node.number = {
if ("undefined" === typeof this.splice_prefix) {
this.splice_prefix = state[state.build.area].opt.layout_delimiter;
}
+ // is this needed?
+ //if ("undefined" === typeof this.splice_prefix){
+ // this.splice_prefix = state[state.tmp.area].opt.layout_delimiter;
+ //}
+ //
+ // Whether we actually stick a number object on
+ // the output queue depends on whether the field
+ // contains a pure number.
+ //
+ // push number or text
func = function (state, Item, item) {
+ // NOTE: this works because this is the ONLY function in this node.
+ // If further functions are added, they need to start with the same
+ // abort condition.
if (this.variables.length === 0) {
return;
}
@@ -10801,20 +14464,43 @@ CSL.Node.number = {
return;
}
}
- state.parallel.StartVariable(this.variables[0]);
- if (this.variables[0] === "locator") {
- state.parallel.AppendToVariable(Item.section);
- } else {
- state.parallel.AppendToVariable(Item[this.variables[0]]);
- }
+
if (varname === 'collection-number' && Item.type === 'legal_case') {
state.tmp.renders_collection_number = true;
}
+
+ // For bill or legislation items that have a label-form
+ // attribute set on the cs:number node rendering the locator,
+ // the form and pluralism of locator terms are controlled
+ // separately from those of the initial label. Form is
+ // straightforward: the label uses the value set on
+ // the cs:label node that renders it, and the embedded
+ // labels use the value of label-form set on the cs:number
+ // node. Both default to "long".
+ //
+ // Pluralism is more complicated. For embedded labels,
+ // pluralism is evaluated using a simple heuristic that
+ // can be found below (it just looks for comma, ampersand etc).
+ // The item.label rendered independently via cs:label
+ // defaults to singular. It is always singular if embedded
+ // labels exist that (when expanded to their valid CSL
+ // value) do not match the value of item.label. Otherwise,
+ // if one or more matching embedded labels exist, the
+ // cs:label is set to plural.
+ //
+ // The code that does all this is divided between this module,
+ // util_static_locator.js, and util_label.js. It's not easy
+ // to follow, but seems to do the job. Let's home for good
+ // luck out there in the wild.
+
var node = this;
+
if (state.tmp.group_context.tip.force_suppress) {
return false;
}
+
if (["locator", "locator-extra"].indexOf(varname) > -1) {
+ // amazing that we reach this. should abort sooner if no content?
state.processNumber.call(state, node, item, varname, Item.type);
} else {
if (!state.tmp.group_context.tip.condition) {
@@ -10822,8 +14508,9 @@ CSL.Node.number = {
}
state.processNumber.call(state, node, Item, varname, Item.type);
}
+
CSL.Util.outputNumericField(state, varname, Item.id);
- state.parallel.CloseVariable("number");
+
if (["locator", "locator-extra"].indexOf(this.variables_real[0]) > -1
&& !state.tmp.just_looking) {
state.tmp.done_vars.push(this.variables_real[0]);
@@ -10832,9 +14519,20 @@ CSL.Node.number = {
};
this.execs.push(func);
target.push(this);
+
CSL.Util.substituteEnd.call(this, state, target);
}
};
+
+/*global CSL: true */
+
+/*
+ * Yikes, these functions were running out of scope for yonks.
+ * now that they are set in the correct token list,
+ * they might be useful for things.
+ * FB 2013.11.09
+*/
+
CSL.Node.sort = {
build: function (state, target) {
target = state[state.build.root + "_sort"].tokens;
@@ -10845,7 +14543,10 @@ CSL.Node.sort = {
}
state.build.area = state.build.root + "_sort";
state.build.extension = "_sort";
+
var func = function (state, Item) {
+ //state.tmp.area = state.tmp.root + "_sort";
+ //state.tmp.extension = "_sort";
if (state.opt.has_layout_locale) {
var langspec = CSL.localeResolve(Item.language, state.opt["default-locale"][0]);
var sort_locales = state[state.tmp.area.slice(0,-5)].opt.sort_locales;
@@ -10867,6 +14568,7 @@ CSL.Node.sort = {
}
};
this.execs.push(func);
+
}
if (this.tokentype === CSL.END) {
state.build.area = state.build.root;
@@ -10876,16 +14578,32 @@ CSL.Node.sort = {
state.opt.lang = state.tmp.lang_sort_hold;
delete state.tmp.lang_sort_hold;
}
+ //state.tmp.area = state.tmp.root;
+ //state.tmp.extension = "";
};
this.execs.push(func);
+ /*
+ var func = function (state, Item) {
+ state.tmp.area = state.tmp.root;
+ state.tmp.extension = "";
+ }
+ this.execs.push(func);
+ */
}
target.push(this);
}
};
+
+
+
+/*global CSL: true */
+
CSL.Node.substitute = {
build: function (state, target) {
var func;
if (this.tokentype === CSL.START) {
+ /* */
+ // set conditional
var choose_start = new CSL.Token("choose", CSL.START);
CSL.Node.choose.build.call(choose_start, state, target);
var if_singleton = new CSL.Token("if", CSL.SINGLETON);
@@ -10898,6 +14616,7 @@ CSL.Node.substitute = {
if_singleton.tests = [func];
if_singleton.test = state.fun.match.any(if_singleton, state, if_singleton.tests);
target.push(if_singleton);
+
func = function (state) {
state.tmp.can_block_substitute = true;
if (state.tmp.value.length && !state.tmp.common_term_match_fail) {
@@ -10907,14 +14626,24 @@ CSL.Node.substitute = {
};
this.execs.push(func);
target.push(this);
+ /* */
}
if (this.tokentype === CSL.END) {
+ //var if_end = new CSL.Token("if", CSL.END);
+ //CSL.Node["if"].build.call(if_end, state, target);
+ /* */
target.push(this);
var choose_end = new CSL.Token("choose", CSL.END);
CSL.Node.choose.build.call(choose_end, state, target);
+ /* */
}
}
};
+
+
+
+/*global CSL: true */
+
CSL.Node.text = {
build: function (state, target) {
var func, form, plural, id, num, number, formatter, firstoutput, specialdelimiter, label, suffix, term;
@@ -10923,7 +14652,9 @@ CSL.Node.text = {
group_start.name = "group";
group_start.tokentype = CSL.START;
CSL.Node.group.build.call(group_start, state, target);
+
CSL.expandMacro.call(state, this, target);
+
var group_end = CSL.Util.cloneToken(this);
group_end.name = "group";
group_end.tokentype = CSL.END;
@@ -10931,14 +14662,24 @@ CSL.Node.text = {
group_end.isJurisLocatorLabel = true;
}
CSL.Node.group.build.call(group_end, state, target);
+
} else {
CSL.Util.substituteStart.call(this, state, target);
+ // ...
+ //
+ // Do non-macro stuff
+
+ // Guess again. this.variables is ephemeral, adjusted by an initial
+ // function set on the node via @variable attribute setup.
+ //variable = this.variables[0];
+
if (!this.variables_real) {
this.variables_real = [];
}
if (!this.variables) {
this.variables = [];
}
+
form = "long";
plural = 0;
if (this.strings.form) {
@@ -10948,19 +14689,27 @@ CSL.Node.text = {
plural = this.strings.plural;
}
if ("citation-number" === this.variables_real[0] || "year-suffix" === this.variables_real[0] || "citation-label" === this.variables_real[0]) {
+ //
+ // citation-number and year-suffix are super special,
+ // because they are rangeables, and require a completely
+ // different set of formatting parameters on the output
+ // queue.
if (this.variables_real[0] === "citation-number") {
+
if (state.build.root === "citation") {
state.opt.update_mode = CSL.NUMERIC;
}
if (state.build.root === "bibliography") {
state.opt.bib_mode = CSL.NUMERIC;
}
+ //this.strings.is_rangeable = true;
if ("citation-number" === state[state.tmp.area].opt.collapse) {
this.range_prefix = state.getTerm("citation-range-delimiter");
}
this.successor_prefix = state[state.build.area].opt.layout_delimiter;
this.splice_prefix = state[state.build.area].opt.layout_delimiter;
func = function (state, Item, item) {
+
id = "" + Item.id;
if (!state.tmp.just_looking) {
if (state.tmp.area.slice(-5) === "_sort" && this.variables[0] === "citation-number") {
@@ -10973,6 +14722,7 @@ CSL.Node.text = {
var num = state.registry.registry[Item.id].seq;
}
if (num) {
+ // Code currently in util_number.js
num = CSL.Util.padding("" + num);
}
state.output.append(num, this);
@@ -10999,8 +14749,11 @@ CSL.Node.text = {
};
this.execs.push(func);
} else if (this.variables_real[0] === "year-suffix") {
+
state.opt.has_year_suffix = true;
+
if (state[state.tmp.area].opt.collapse === "year-suffix-ranged") {
+ //this.range_prefix = "-";
this.range_prefix = state.getTerm("citation-range-delimiter");
}
this.successor_prefix = state[state.build.area].opt.layout_delimiter;
@@ -11009,7 +14762,14 @@ CSL.Node.text = {
}
func = function (state, Item) {
if (state.registry.registry[Item.id] && state.registry.registry[Item.id].disambig.year_suffix !== false && !state.tmp.just_looking) {
+ //state.output.append(state.registry.registry[Item.id].disambig[2],this);
num = parseInt(state.registry.registry[Item.id].disambig.year_suffix, 10);
+
+ //if (state[state.tmp.area].opt.collapse === "year-suffix-ranged") {
+ // //this.range_prefix = "-";
+ // this.range_prefix = state.getTerm("citation-range-delimiter");
+ //}
+ //this.successor_prefix = state[state.tmp.area].opt.layout_delimiter;
if (state[state.tmp.area].opt.cite_group_delimiter) {
this.successor_prefix = state[state.tmp.area].opt.cite_group_delimiter;
}
@@ -11018,6 +14778,7 @@ CSL.Node.text = {
number.setFormatter(formatter);
state.output.append(number, "literal");
firstoutput = false;
+ // XXX Can we do something better for length here?
for (var i=0,ilen=state.tmp.group_context.mystack.length; i -1
|| ["language-name", "language-name-original"].indexOf(this.variables_real[0]) > -1) {
+
+ // multi-fields
+ // Initialize transform factory according to whether
+ // abbreviation is desired.
var abbrevfam = this.variables[0];
var abbrfall = false;
var altvar = false;
@@ -11117,18 +14903,34 @@ CSL.Node.text = {
abbrevfam = false;
}
if (state.build.extension) {
+ // multi-fields for sorting get a sort transform,
+ // (abbreviated if the short form was selected)
transfall = true;
} else {
transfall = true;
abbrfall = true;
}
+
func = state.transform.getOutputFunction(this.variables, abbrevfam, abbrfall, altvar, transfall);
} else {
+ // ordinary fields
if (CSL.CITE_FIELDS.indexOf(this.variables_real[0]) > -1) {
+ // per-cite fields are read from item, rather than Item
func = function (state, Item, item) {
if (item && item[this.variables[0]]) {
+ // Code copied to page variable as well; both
+ // become cs:number in MLZ extended schema
+
+ // If locator, use cs:number. Otherwise, render
+ // normally.
+
+ // XXX The code below is pretty-much copied from
+ // XXX node_number.js. Should be a common function.
+ // XXX BEGIN
state.processNumber(this, item, this.variables[0], Item.type);
CSL.Util.outputNumericField(state, this.variables[0], Item.id);
+ // XXX END
+
if (["locator", "locator-extra"].indexOf(this.variables_real[0]) > -1
&& !state.tmp.just_looking) {
state.tmp.done_vars.push(this.variables_real[0]);
@@ -11136,6 +14938,8 @@ CSL.Node.text = {
}
};
} else if (["page", "page-first", "chapter-number", "collection-number", "edition", "issue", "number", "number-of-pages", "number-of-volumes", "volume"].indexOf(this.variables_real[0]) > -1) {
+ // page gets mangled with the correct collapsing
+ // algorithm
func = function(state, Item) {
state.processNumber(this, Item, this.variables[0], Item.type);
CSL.Util.outputNumericField(state, this.variables[0], Item.id);
@@ -11146,36 +14950,55 @@ CSL.Node.text = {
if (this.variables[0]) {
value = state.getVariable(Item, this.variables[0], form);
if (value) {
+ // true is for non-suppression of periods
if (state.opt.development_extensions.wrap_url_and_doi) {
if (!this.decorations.length || this.decorations[0][0] !== "@" + this.variables[0]) {
+ // Special-casing to fix https://github.com/Juris-M/citeproc-js/issues/57
+ // clone current token, to avoid collateral damage
var clonetoken = CSL.Util.cloneToken(this);
+ // cast a group blob
var groupblob = new CSL.Blob(null, null, "url-wrapper");
+ // set the DOI decoration on the blob
groupblob.decorations.push(["@DOI", "true"]);
if (this.variables_real[0] === "DOI") {
+ // strip a proper DOI prefix
var prefix;
if (this.strings.prefix && this.strings.prefix.match(/^.*https:\/\/doi\.org\/$/)) {
value = value.replace(/^https?:\/\/doi\.org\//, "");
if (value.match(/^https?:\/\//)) {
+ // Do not tamper with another protocol + domain if already set in field value
prefix = "";
} else {
+ // Otherwise https + domain
prefix = "https://doi.org/";
}
+ // set any string prefix on the clone
clonetoken.strings.prefix = this.strings.prefix.slice(0, clonetoken.strings.prefix.length-16);
}
+ // cast a text blob
+ // set the prefix as the content of the blob
var prefixblob = new CSL.Blob(prefix);
+ // cast another text blob
+ // set the value as the content of the second blob
var valueblob = new CSL.Blob(value);
+ // append new text token and clone to group token
groupblob.push(prefixblob);
groupblob.push(valueblob);
+ // append group token to output
state.output.append(groupblob, clonetoken, false, false, true);
} else {
var valueblob = new CSL.Blob(value);
+ // append new text token and clone to group token
groupblob.push(valueblob);
+ // append group token to output
+ //this.decorations = [["@" + this.variables[0], "true"]].concat(this.decorations);
state.output.append(groupblob, clonetoken, false, false, true);
}
} else {
state.output.append(value, this, false, false, true);
}
} else {
+ // This is totally unnecessary, isn't it?
if (this.decorations.length) {
for (var i=this.decorations.length-1; i>-1; i--) {
if (this.decorations[i][0] === "@" + this.variables[0]) {
@@ -11189,6 +15012,9 @@ CSL.Node.text = {
}
};
} else if (this.variables_real[0] === "section") {
+ // Sections for statutes are special. This is an uncommon
+ // variable, so we save the cost of the runtime check
+ // unless it's being used.
func = function (state, Item) {
var value;
value = state.getVariable(Item, this.variables[0], form);
@@ -11205,6 +15031,7 @@ CSL.Node.text = {
}
};
} else {
+ // anything left over just gets output in the normal way.
func = function (state, Item) {
var value;
if (this.variables[0]) {
@@ -11219,17 +15046,16 @@ CSL.Node.text = {
}
}
this.execs.push(func);
- func = function (state) {
- state.parallel.CloseVariable("text");
- };
- this.execs.push(func);
} else if (this.strings.value) {
+ // for the text value attribute.
func = function (state) {
state.tmp.group_context.tip.term_intended = true;
+ // true flags that this is a literal-value term
CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, this.strings.value, true);
state.output.append(this.strings.value, this);
};
this.execs.push(func);
+ // otherwise no output
}
}
target.push(this);
@@ -11237,12 +15063,19 @@ CSL.Node.text = {
}
}
};
+
+
+
+/*global CSL: true */
+
CSL.Node.intext = {
build: function (state, target) {
if (this.tokentype === CSL.START) {
+
state.build.area = "intext";
state.build.root = "intext";
state.build.extension = "";
+
var func = function(state, Item) {
state.tmp.area = "intext";
state.tmp.root = "intext";
@@ -11251,23 +15084,24 @@ CSL.Node.intext = {
this.execs.push(func);
}
if (this.tokentype === CSL.END) {
- state.opt.grouped_sort = state.opt.xclass === "in-text"
- && (state.citation.opt.collapse
- && state.citation.opt.collapse.length)
- || (state.citation.opt.cite_group_delimiter
- && state.citation.opt.cite_group_delimiter.length)
- && state.opt.update_mode !== CSL.POSITION
- && state.opt.update_mode !== CSL.NUMERIC;
- if (state.opt.grouped_sort
- && state.citation_sort.opt.sort_directions.length) {
- state.intext_sort.opt.sort_directions = state.citation_sort.opt.sort_directions;
+
+ // Do whatever cs:citation does with sorting.
+ state.intext_sort = {
+ opt: {
+ sort_directions: state.citation_sort.opt.sort_directions
+ }
}
state.intext.srt = state.citation.srt;
}
target.push(this);
}
};
+
+
+/*global CSL: true */
+
CSL.Attributes = {};
+
CSL.Attributes["@genre"] = function (state, arg) {
this.tests ? {} : this.tests = [];
arg = arg.replace("-", " ");
@@ -11279,6 +15113,7 @@ CSL.Attributes["@genre"] = function (state, arg) {
};
this.tests.push(func);
};
+
CSL.Attributes["@disambiguate"] = function (state, arg) {
this.tests ? {} : this.tests = [];
if (arg === "true") {
@@ -11310,6 +15145,7 @@ CSL.Attributes["@disambiguate"] = function (state, arg) {
this.tests.push(func);
}
};
+
CSL.Attributes["@is-numeric"] = function (state, arg) {
this.tests ? {} : this.tests = [];
var variables = arg.split(/\s+/);
@@ -11341,9 +15177,12 @@ CSL.Attributes["@is-numeric"] = function (state, arg) {
this.tests.push(maketest(variables[i]));
}
};
+
+
CSL.Attributes["@is-uncertain-date"] = function (state, arg) {
this.tests ? {} : this.tests = [];
var variables = arg.split(/\s+/);
+ // Strip off any boolean prefix.
var maketest = function (myvariable) {
return function(Item) {
if (Item[myvariable] && Item[myvariable].circa) {
@@ -11357,10 +15196,13 @@ CSL.Attributes["@is-uncertain-date"] = function (state, arg) {
this.tests.push(maketest(variables[i]));
}
};
+
+
CSL.Attributes["@locator"] = function (state, arg) {
this.tests ? {} : this.tests = [];
var trylabels = arg.replace("sub verbo", "sub-verbo");
trylabels = trylabels.split(/\s+/);
+ // Strip off any boolean prefix.
var maketest = function (trylabel) {
return function(Item, item) {
var label;
@@ -11377,6 +15219,8 @@ CSL.Attributes["@locator"] = function (state, arg) {
this.tests.push(maketest(trylabels[i]));
}
};
+
+
CSL.Attributes["@position"] = function (state, arg) {
this.tests ? {} : this.tests = [];
var tryposition;
@@ -11435,9 +15279,12 @@ CSL.Attributes["@position"] = function (state, arg) {
}
}
};
+
CSL.Attributes["@type"] = function (state, arg) {
this.tests ? {} : this.tests = [];
+ // XXX This is ALWAYS composed as an "any" match
var types = arg.split(/\s+/);
+ // Strip off any boolean prefix.
var maketest = function (mytype) {
return function(Item) {
var ret = (Item.type === mytype);
@@ -11454,20 +15301,35 @@ CSL.Attributes["@type"] = function (state, arg) {
}
this.tests.push(state.fun.match.any(this, state, tests));
};
+
CSL.Attributes["@variable"] = function (state, arg) {
this.tests ? {} : this.tests = [];
var func;
this.variables = arg.split(/\s+/);
this.variables_real = this.variables.slice();
+
+ // First the non-conditional code.
if ("label" === this.name && this.variables[0]) {
this.strings.term = this.variables[0];
} else if (["names", "date", "text", "number"].indexOf(this.name) > -1) {
+ //
+ // An oddity of variable handling is that this.variables
+ // is actually ephemeral; the full list of variables is
+ // held in the variables_real var, and pushed into this.variables
+ // conditionally in order to suppress repeat renderings of
+ // the same item variable. [STILL FUNCTIONAL? 2010.01.15]
+ //
+ // set variable names
func = function (state, Item, item) {
+ // Clear this.variables in place
for (var i = this.variables.length - 1; i > -1; i += -1) {
this.variables.pop();
}
for (var i=0,ilen=this.variables_real.length;i -1
&& "string" === typeof Item[variable]
&& "names" === this.name) {
+
+ // Great! So for each of these, we split.
+ // And we only recombine everything if the length
+ // of all the splits matches.
+
+ // Preflight
var isValid = true;
var rawNames = Item[variable].split(/\s*;\s*/);
var rawMultiNames = {};
@@ -11525,10 +15395,15 @@ CSL.Attributes["@variable"] = function (state, arg) {
}
}
if (variable === "year-suffix") {
+ // year-suffix always signals that it produces output,
+ // even when it doesn't. This permits it to be used with
+ // the "no date" term inside a group used exclusively
+ // to control formatting.
output = true;
break;
} else if (CSL.DATE_VARIABLES.indexOf(variable) > -1) {
if (state.opt.development_extensions.locator_date_and_revision && "locator-date" === variable) {
+ // If locator-date is set, it's valid.
output = true;
break;
}
@@ -11568,6 +15443,7 @@ CSL.Attributes["@variable"] = function (state, arg) {
if (state.transform.abbrevs["default"].hereinafter[Item.id]
&& state.sys.getAbbreviation
&& Item.id) {
+
output = true;
}
break;
@@ -11584,33 +15460,45 @@ CSL.Attributes["@variable"] = function (state, arg) {
break;
}
}
+ //print("-- VAR: "+variable);
+ //flag = state.tmp.group_context.tip;
if (output) {
for (var i=0,ilen=this.variables_real.length;i -1) {
+ // Strip off any boolean prefix.
+ // Now the conditionals.
var maketest = function (variable) {
return function(Item,item){
var myitem = Item;
if (item && ["locator", "locator-extra", "first-reference-note-number", "locator-date"].indexOf(variable) > -1) {
myitem = item;
}
+ // We don't run loadAbbreviation() here; it is run by the application-supplied
+ // retrieveItem() if hereinafter functionality is to be used, so this key will
+ // always exist in memory, possibly with a nil value.
if (variable === "hereinafter" && state.sys.getAbbreviation && myitem.id) {
if (state.transform.abbrevs["default"].hereinafter[myitem.id]) {
return true;
@@ -11619,6 +15507,11 @@ CSL.Attributes["@variable"] = function (state, arg) {
if ("number" === typeof myitem[variable] || "string" === typeof myitem[variable]) {
return true;
} else if ("object" === typeof myitem[variable]) {
+ //
+ // this will turn true only for hash objects
+ // that have at least one attribute, or for a
+ // non-zero-length list
+ //
for (var key in myitem[variable]) {
if (myitem[variable][key]) {
return true;
@@ -11634,10 +15527,13 @@ CSL.Attributes["@variable"] = function (state, arg) {
}
}
};
+
+
CSL.Attributes["@page"] = function (state, arg) {
this.tests ? {} : this.tests = [];
var trylabels = arg.replace("sub verbo", "sub-verbo");
trylabels = trylabels.split(/\s+/);
+ // Strip off any boolean prefix.
var maketest = function (trylabel) {
return function(Item) {
var label;
@@ -11660,6 +15556,9 @@ CSL.Attributes["@page"] = function (state, arg) {
this.tests.push(maketest(trylabels[i]));
}
};
+
+
+// a near duplicate of code above
CSL.Attributes["@number"] = function (state, arg) {
this.tests ? {} : this.tests = [];
var trylabels = arg.split(/\s+/);
@@ -11683,9 +15582,11 @@ CSL.Attributes["@number"] = function (state, arg) {
this.tests.push(maketest(trylabels[i]));
}
};
+
CSL.Attributes["@jurisdiction"] = function (state, arg) {
this.tests ? {} : this.tests = [];
var tryjurisdictions = arg.split(/\s+/);
+ // Strip off any boolean prefix.
for (var i=0,ilen=tryjurisdictions.length;i -1) {
state.opt.lang = me.locale;
state.tmp.last_cite_locale = me.locale;
+ // Set empty group open tag with locale set marker
state.output.openLevel("empty");
state.output.current.value().new_locale = me.locale;
res = true;
@@ -11991,74 +15956,157 @@ CSL.Attributes["@locale-internal"] = function (state, arg) {
var me = this;
this.tests.push(maketest(me));
};
+
+
+// These are not evaluated as conditions immediately: they only
+// set parameters that are picked up during processing.
CSL.Attributes["@is-parallel"] = function (state, arg) {
- var values = arg.split(" ");
- for (var i = 0, ilen = values.length; i < ilen; i += 1) {
- if (values[i] === "true") {
- values[i] = true;
- } else if (values[i] === "false") {
- values[i] = false;
- }
- }
- this.strings.set_parallel_condition = values;
+ this.strings.set_parallel_condition = arg;
};
+CSL.Attributes["@no-repeat"] = function (state, arg) {
+ this.strings.set_no_repeat_condition = arg.split(/\s+/);
+};
+
+
CSL.Attributes["@jurisdiction-depth"] = function (state, arg) {
this.strings.jurisdiction_depth = parseInt(arg, 10);
};
+
+
CSL.Attributes["@require"] = function (state, arg) {
this.strings.require = arg;
+
+ // Introduced to constrain rendering of the group with a
+ // requirement that it either render an alpha term via cs:label or
+ // cs:text at least once, or render without any label. That
+ // behaviour is invoked with "label-empty-or-alpha" as arg.
+
+ // This attribute is a complement to @label-form and modular
+ // jurisdiction support, as it makes macros that adapt to shifting
+ // local term definitions possible.
};
+
CSL.Attributes["@reject"] = function (state, arg) {
this.strings.reject = arg;
+
+ // Introduced to constrain rendering of the group with a
+ // requirement that it render some label via cs:label or cs:text,
+ // and that it NOT be alpha. That behaviour is invoked with
+ // "label-empty-or-alpha" as arg.
+
+ // This attribute is a complement to @label-form and modular
+ // jurisdiction support, as it makes macros that adapt to shifting
+ // local term definitions possible.
};
+
CSL.Attributes["@gender"] = function (state, arg) {
this.gender = arg;
};
+
CSL.Attributes["@cslid"] = function (state, arg) {
+ // @cslid is a noop
+ // The value set on this attribute is used to
+ // generate reverse lookup wrappers on output when
+ // this.development_extensions.csl_reverse_lookup_support is
+ // set to true in state.js (there is no runtime option,
+ // it must be set in state.js)
+ //
+ // See the @showid method in the html output
+ // section of formats.js for the function that
+ // renders the wrappers.
this.cslid = parseInt(arg, 10);
};
+
CSL.Attributes["@capitalize-if-first"] = function (state, arg) {
this.strings.capitalize_if_first_override = arg;
};
+
CSL.Attributes["@label-capitalize-if-first"] = function (state, arg) {
this.strings.label_capitalize_if_first_override = arg;
};
+
CSL.Attributes["@label-form"] = function (state, arg) {
this.strings.label_form_override = arg;
};
+
CSL.Attributes["@part-separator"] = function (state, arg) {
this.strings["part-separator"] = arg;
};
+
CSL.Attributes["@leading-noise-words"] = function (state, arg) {
this["leading-noise-words"] = arg;
};
+
CSL.Attributes["@name-never-short"] = function (state, arg) {
this["name-never-short"] = arg;
};
+
CSL.Attributes["@class"] = function (state, arg) {
state.opt["class"] = arg;
};
+
CSL.Attributes["@version"] = function (state, arg) {
state.opt.version = arg;
};
+
+/**
+ * Store the value attribute on the token.
+ * @name CSL.Attributes.@value
+ * @function
+ */
CSL.Attributes["@value"] = function (state, arg) {
this.strings.value = arg;
};
+
+
+/**
+ * Store the name attribute (of a macro or term node)
+ * on the state object.
+ *
For reference when the closing node of a macro
+ * or locale definition is encountered.
+ * @name CSL.Attributes.@name
+ * @function
+ */
CSL.Attributes["@name"] = function (state, arg) {
this.strings.name = arg;
};
+
+/**
+ * Store the form attribute (of a term node) on the state object.
+ *
For reference when the closing node of a macro
+ * or locale definition is encountered.
+ * @name CSL.Attributes.@form
+ * @function
+ */
CSL.Attributes["@form"] = function (state, arg) {
this.strings.form = arg;
};
+
CSL.Attributes["@date-parts"] = function (state, arg) {
this.strings["date-parts"] = arg;
};
+
CSL.Attributes["@range-delimiter"] = function (state, arg) {
this.strings["range-delimiter"] = arg;
};
+
+/**
+ * Store macro tokens in a buffer on the state object.
+ *
For reference when the enclosing text token is
+ * processed.
http://purl.org/net/xbiblio/csl
+ * anyway. At least for the present we will blindly assume
+ * that it is.
+ * @name CSL.Attributes.@xmlns
+ * @function
+ */
CSL.Attributes["@xmlns"] = function () {};
+
+
+/*
+ * Store language attribute to a buffer field.
+ *
Will be placed in the appropriate location
+ * when the element is processed.
+ * @name CSL.Attributes.@lang
+ * @function
+ */
CSL.Attributes["@lang"] = function (state, arg) {
if (arg) {
state.build.lang = arg;
}
};
+
+
+// Used as a flag during dates processing
CSL.Attributes["@lingo"] = function () {};
+
+// Used as a flag during dates processing
CSL.Attributes["@macro-has-date"] = function () {
this["macro-has-date"] = true;
};
+
+/*
+ * Store suffix string on token.
+ * @name CSL.Attributes.@suffix
+ * @function
+ */
CSL.Attributes["@suffix"] = function (state, arg) {
this.strings.suffix = arg;
};
+
+
+/*
+ * Store prefix string on token.
+ * @name CSL.Attributes.@prefix
+ * @function
+ */
CSL.Attributes["@prefix"] = function (state, arg) {
this.strings.prefix = arg;
};
+
+
+/*
+ * Store delimiter string on token.
+ * @name CSL.Attributes.@delimiter
+ * @function
+ */
CSL.Attributes["@delimiter"] = function (state, arg) {
this.strings.delimiter = arg;
};
+
+
+/*
+ * Store match evaluator on token.
+ */
CSL.Attributes["@match"] = function (state, arg) {
this.match = arg;
};
+
+
CSL.Attributes["@names-min"] = function (state, arg) {
var val = parseInt(arg, 10);
if (state[state.build.area].opt.max_number_of_names < val) {
@@ -12095,9 +16194,11 @@ CSL.Attributes["@names-min"] = function (state, arg) {
}
this.strings["et-al-min"] = val;
};
+
CSL.Attributes["@names-use-first"] = function (state, arg) {
this.strings["et-al-use-first"] = parseInt(arg, 10);
};
+
CSL.Attributes["@names-use-last"] = function (state, arg) {
if (arg === "true") {
this.strings["et-al-use-last"] = true;
@@ -12105,12 +16206,16 @@ CSL.Attributes["@names-use-last"] = function (state, arg) {
this.strings["et-al-use-last"] = false;
}
};
+
CSL.Attributes["@sort"] = function (state, arg) {
if (arg === "descending") {
this.strings.sort_direction = CSL.DESCENDING;
}
};
+
CSL.Attributes["@plural"] = function (state, arg) {
+ // Accepted values of plural attribute differ on cs:text
+ // and cs:label nodes.
if ("always" === arg || "true" === arg) {
this.strings.plural = 1;
} else if ("never" === arg || "false" === arg) {
@@ -12119,49 +16224,68 @@ CSL.Attributes["@plural"] = function (state, arg) {
this.strings.plural = false;
}
};
+
CSL.Attributes["@has-publisher-and-publisher-place"] = function () {
this.strings["has-publisher-and-publisher-place"] = true;
};
+
CSL.Attributes["@publisher-delimiter-precedes-last"] = function (state, arg) {
this.strings["publisher-delimiter-precedes-last"] = arg;
};
+
CSL.Attributes["@publisher-delimiter"] = function (state, arg) {
this.strings["publisher-delimiter"] = arg;
};
+
CSL.Attributes["@publisher-and"] = function (state, arg) {
this.strings["publisher-and"] = arg;
};
+
CSL.Attributes["@newdate"] = function () {};
+
+
CSL.Attributes["@givenname-disambiguation-rule"] = function (state, arg) {
if (CSL.GIVENNAME_DISAMBIGUATION_RULES.indexOf(arg) > -1) {
state.citation.opt["givenname-disambiguation-rule"] = arg;
}
};
+
CSL.Attributes["@collapse"] = function (state, arg) {
+ // only one collapse value will be honoured.
if (arg) {
state[this.name].opt.collapse = arg;
}
};
+
CSL.Attributes["@cite-group-delimiter"] = function (state, arg) {
if (arg) {
state[state.tmp.area].opt.cite_group_delimiter = arg;
}
};
+
+
+
CSL.Attributes["@names-delimiter"] = function (state, arg) {
state.setOpt(this, "names-delimiter", arg);
};
+
CSL.Attributes["@name-form"] = function (state, arg) {
state.setOpt(this, "name-form", arg);
};
+
CSL.Attributes["@subgroup-delimiter"] = function (state, arg) {
this.strings["subgroup-delimiter"] = arg;
};
+
CSL.Attributes["@subgroup-delimiter-precedes-last"] = function (state, arg) {
this.strings["subgroup-delimiter-precedes-last"] = arg;
};
+
+
CSL.Attributes["@name-delimiter"] = function (state, arg) {
state.setOpt(this, "name-delimiter", arg);
};
+
CSL.Attributes["@et-al-min"] = function (state, arg) {
var val = parseInt(arg, 10);
if (state[state.build.area].opt.max_number_of_names < val) {
@@ -12169,9 +16293,11 @@ CSL.Attributes["@et-al-min"] = function (state, arg) {
}
state.setOpt(this, "et-al-min", val);
};
+
CSL.Attributes["@et-al-use-first"] = function (state, arg) {
state.setOpt(this, "et-al-use-first", parseInt(arg, 10));
};
+
CSL.Attributes["@et-al-use-last"] = function (state, arg) {
if (arg === "true") {
state.setOpt(this, "et-al-use-last", true);
@@ -12179,6 +16305,7 @@ CSL.Attributes["@et-al-use-last"] = function (state, arg) {
state.setOpt(this, "et-al-use-last", false);
}
};
+
CSL.Attributes["@et-al-subsequent-min"] = function (state, arg) {
var val = parseInt(arg, 10);
if (state[state.build.area].opt.max_number_of_names < val) {
@@ -12186,35 +16313,46 @@ CSL.Attributes["@et-al-subsequent-min"] = function (state, arg) {
}
state.setOpt(this, "et-al-subsequent-min", val);
};
+
CSL.Attributes["@et-al-subsequent-use-first"] = function (state, arg) {
state.setOpt(this, "et-al-subsequent-use-first", parseInt(arg, 10));
};
+
CSL.Attributes["@suppress-min"] = function (state, arg) {
this.strings["suppress-min"] = parseInt(arg, 10);
};
+
CSL.Attributes["@suppress-max"] = function (state, arg) {
this.strings["suppress-max"] = parseInt(arg, 10);
};
+
+
CSL.Attributes["@and"] = function (state, arg) {
state.setOpt(this, "and", arg);
};
+
CSL.Attributes["@delimiter-precedes-last"] = function (state, arg) {
state.setOpt(this, "delimiter-precedes-last", arg);
};
+
CSL.Attributes["@delimiter-precedes-et-al"] = function (state, arg) {
state.setOpt(this, "delimiter-precedes-et-al", arg);
};
+
CSL.Attributes["@initialize-with"] = function (state, arg) {
state.setOpt(this, "initialize-with", arg);
};
+
CSL.Attributes["@initialize"] = function (state, arg) {
if (arg === "false") {
state.setOpt(this, "initialize", false);
}
};
+
CSL.Attributes["@name-as-reverse-order"] = function (state, arg) {
this["name-as-reverse-order"] = arg;
};
+
CSL.Attributes["@name-as-sort-order"] = function (state, arg) {
if (this.name === "style-options") {
this["name-as-sort-order"] = arg;
@@ -12222,52 +16360,68 @@ CSL.Attributes["@name-as-sort-order"] = function (state, arg) {
state.setOpt(this, "name-as-sort-order", arg);
}
};
+
CSL.Attributes["@sort-separator"] = function (state, arg) {
state.setOpt(this, "sort-separator", arg);
};
+
CSL.Attributes["@require-match"] = function (state, arg) {
if (arg === "true") {
this.requireMatch = true;
}
};
+
CSL.Attributes["@exclude-types"] = function (state, arg) {
state.bibliography.opt.exclude_types = arg.split(/\s+/);
};
+
CSL.Attributes["@exclude-with-fields"] = function (state, arg) {
state.bibliography.opt.exclude_with_fields = arg.split(/\s+/);
};
+
+
CSL.Attributes["@year-suffix-delimiter"] = function (state, arg) {
state[this.name].opt["year-suffix-delimiter"] = arg;
};
+
CSL.Attributes["@after-collapse-delimiter"] = function (state, arg) {
state[this.name].opt["after-collapse-delimiter"] = arg;
};
+
CSL.Attributes["@subsequent-author-substitute"] = function (state, arg) {
state[this.name].opt["subsequent-author-substitute"] = arg;
};
+
CSL.Attributes["@subsequent-author-substitute-rule"] = function (state, arg) {
state[this.name].opt["subsequent-author-substitute-rule"] = arg;
};
+
CSL.Attributes["@disambiguate-add-names"] = function (state, arg) {
if (arg === "true") {
state.opt["disambiguate-add-names"] = true;
}
};
+
CSL.Attributes["@disambiguate-add-givenname"] = function (state, arg) {
if (arg === "true") {
state.opt["disambiguate-add-givenname"] = true;
}
};
+
CSL.Attributes["@disambiguate-add-year-suffix"] = function (state, arg) {
if (arg === "true" && state.opt.xclass !== "numeric") {
state.opt["disambiguate-add-year-suffix"] = true;
}
};
+
+
CSL.Attributes["@second-field-align"] = function (state, arg) {
if (arg === "flush" || arg === "margin") {
state[this.name].opt["second-field-align"] = arg;
}
};
+
+
CSL.Attributes["@hanging-indent"] = function (state, arg) {
if (arg === "true") {
if (state.opt.development_extensions.hanging_indent_legacy_number) {
@@ -12277,19 +16431,26 @@ CSL.Attributes["@hanging-indent"] = function (state, arg) {
}
}
};
+
+
CSL.Attributes["@line-spacing"] = function (state, arg) {
if (arg && arg.match(/^[.0-9]+$/)) {
state[this.name].opt["line-spacing"] = parseFloat(arg, 10);
}
};
+
+
CSL.Attributes["@entry-spacing"] = function (state, arg) {
if (arg && arg.match(/^[.0-9]+$/)) {
state[this.name].opt["entry-spacing"] = parseFloat(arg, 10);
}
};
+
+
CSL.Attributes["@near-note-distance"] = function (state, arg) {
state[this.name].opt["near-note-distance"] = parseInt(arg, 10);
};
+
CSL.Attributes["@text-case"] = function (state, arg) {
var func = function (state, Item) {
if (arg === "normal") {
@@ -12305,15 +16466,25 @@ CSL.Attributes["@text-case"] = function (state, arg) {
};
this.execs.push(func);
};
+
+
CSL.Attributes["@page-range-format"] = function (state, arg) {
state.opt["page-range-format"] = arg;
};
+
+
CSL.Attributes["@year-range-format"] = function (state, arg) {
state.opt["year-range-format"] = arg;
};
+
+
CSL.Attributes["@default-locale"] = function (state, arg) {
if (this.name === 'style') {
var lst, len, pos, m, ret;
+ //
+ // Workaround for Internet Exploder 6 (doesn't recognize
+ // groups in str.split(/something(braced-group)something/)
+ //
var m = arg.match(/-x-(sort|translit|translat)-/g);
if (m) {
for (pos = 0, len = m.length; pos < len; pos += 1) {
@@ -12340,48 +16511,73 @@ CSL.Attributes["@default-locale"] = function (state, arg) {
this.default_locale = true;
}
};
+
CSL.Attributes["@default-locale-sort"] = function (state, arg) {
state.opt["default-locale-sort"] = arg;
};
+
CSL.Attributes["@demote-non-dropping-particle"] = function (state, arg) {
state.opt["demote-non-dropping-particle"] = arg;
};
+
CSL.Attributes["@initialize-with-hyphen"] = function (state, arg) {
if (arg === "false") {
state.opt["initialize-with-hyphen"] = false;
}
};
+
CSL.Attributes["@institution-parts"] = function (state, arg) {
this.strings["institution-parts"] = arg;
};
+
CSL.Attributes["@if-short"] = function (state, arg) {
if (arg === "true") {
this.strings["if-short"] = true;
}
};
+
CSL.Attributes["@substitute-use-first"] = function (state, arg) {
this.strings["substitute-use-first"] = parseInt(arg, 10);
};
+
CSL.Attributes["@use-first"] = function (state, arg) {
this.strings["use-first"] = parseInt(arg, 10);
};
+
CSL.Attributes["@stop-last"] = function (state, arg) {
this.strings["stop-last"] = parseInt(arg, 10) * -1;
};
+
CSL.Attributes["@use-last"] = function (state, arg) {
this.strings["use-last"] = parseInt(arg, 10);
};
+
+
CSL.Attributes["@reverse-order"] = function (state, arg) {
if ("true" === arg) {
this.strings["reverse-order"] = true;
}
};
+
CSL.Attributes["@display"] = function (state, arg) {
if (state.bibliography.tokens.length === 2) {
state.opt.using_display = true;
}
this.strings.cls = arg;
};
+
+
+/*global CSL: true */
+
+
+/**
+ * String stack object.
+ *
Numerous string stacks are used to track nested
+ * parameters at runtime. This class provides methods
+ * that remove some of the aggravation of managing
+ * them.
+ * @class
+ */
CSL.Stack = function (val, literal) {
this.mystack = [];
if (literal || val) {
@@ -12389,6 +16585,11 @@ CSL.Stack = function (val, literal) {
}
this.tip = this.mystack[0];
};
+
+/**
+ * Push a value onto the stack.
+ *
This just does what it says.
+ */
CSL.Stack.prototype.push = function (val, literal) {
if (literal || val) {
this.mystack.push(val);
@@ -12397,13 +16598,26 @@ CSL.Stack.prototype.push = function (val, literal) {
}
this.tip = this.mystack[this.mystack.length - 1];
};
+
+/**
+ * Clear the stack
+ */
CSL.Stack.prototype.clear = function () {
this.mystack = [];
this.tip = {};
};
+
+/**
+ * Replace the top value on the stack.
+ *
This removes some ugly syntax from the
+ * main code.
+ */
CSL.Stack.prototype.replace = function (val, literal) {
+ //
+ // safety fix after a bug was chased down. Rhino
+ // JS will process a negative index without error (!).
if (this.mystack.length === 0) {
- throw "Internal CSL processor error: attempt to replace nonexistent stack item with " + val;
+ CSL.error("Internal CSL processor error: attempt to replace nonexistent stack item with " + val);
}
if (literal || val) {
this.mystack[(this.mystack.length - 1)] = val;
@@ -12412,6 +16626,12 @@ CSL.Stack.prototype.replace = function (val, literal) {
}
this.tip = this.mystack[this.mystack.length - 1];
};
+
+
+/**
+ * Remove the top value from the stack.
+ *
Just does what it says.
+ */
CSL.Stack.prototype.pop = function () {
var ret = this.mystack.pop();
if (this.mystack.length) {
@@ -12421,480 +16641,182 @@ CSL.Stack.prototype.pop = function () {
}
return ret;
};
+
+
+/**
+ * Return the top value on the stack.
+ *
Removes a little hideous complication from
+ * the main code.
Used to identify if there is content to
+ * be handled on the stack
+ */
CSL.Stack.prototype.length = function () {
return this.mystack.length;
};
+
+/*global CSL: true */
+
+/**
+ * Initializes the parallel cite tracking arrays
+ */
CSL.Parallel = function (state) {
this.state = state;
- this.sets = new CSL.Stack([]);
- this.try_cite = true;
- this.use_parallels = false;
- this.midVars = ["section", "volume", "container-title", "collection-number", "issue", "page-first", "page", "number"];
- this.ignoreVarsLawGeneral = ["first-reference-note-number", "locator", "label","page-first","page","genre"];
- this.ignoreVarsLawProceduralHistory = ["issued", "first-reference-note-number", "locator", "label","page-first","page","genre","jurisdiction"];
- this.ignoreVarsOrders = ["first-reference-note-number"];
- this.ignoreVarsOther = ["first-reference-note-number", "locator", "label","section","page-first","page"];
-};
-CSL.Parallel.prototype.isMid = function (variable) {
- return (this.midVars.indexOf(variable) > -1);
+ this.info = {};
};
+
+CSL.Parallel.prototype.setSeriesRels = function(prevID, currID, seriesRels) {
+ if (this.info[prevID][currID]) {
+ if (!seriesRels) {
+ seriesRels = JSON.parse(JSON.stringify(this.info[prevID]));
+ }
+ } else {
+ seriesRels = false;
+ }
+ return seriesRels;
+}
+
+CSL.Parallel.prototype.getRepeats = function(prev, curr) {
+ var rex = /(?:type|id|seeAlso|.*-sub|.*-subjoin|.*-main)/;
+ var ret = {};
+ for (var key in prev) {
+ if (key.match(rex)) {
+ continue;
+ }
+ if (typeof prev[key] === "string") {
+ if (prev[key] && prev[key] === curr[key]) {
+ ret[key] = true;
+ }
+ } else if (typeof prev[key] === "object") {
+ // Could do better than this.
+ if (JSON.stringify(prev[key]) === JSON.stringify(curr[key])) {
+ ret[key] = true;
+ }
+ }
+ }
+ return ret;
+}
+
CSL.Parallel.prototype.StartCitation = function (sortedItems, out) {
this.parallel_conditional_blobs_list = [];
- if (this.use_parallels) {
- this.sortedItems = sortedItems;
- this.sortedItemsPos = -1;
- this.sets.clear();
- this.sets.push([]);
- this.in_series = true;
- this.delim_counter = 0;
- this.delim_pointers = [];
- if (out) {
- this.out = out;
- } else {
- this.out = this.state.output.queue;
- }
- this.master_was_neutral_cite = true;
- }
-};
-CSL.Parallel.prototype.StartCite = function (Item, item, prevItemID) {
- var position, len, pos, curr, master, last_id, prev_locator, curr_locator;
- if (this.use_parallels) {
- if (this.sets.value().length && this.sets.value()[0].itemId == Item.id) {
- this.ComposeSet();
- }
- this.sortedItemsPos += 1;
- if (item) {
- position = item.position;
- }
- this.try_cite = true;
- var has_required_var = false;
- for (var i = 0, ilen = CSL.PARALLEL_MATCH_VARS.length; i < ilen; i += 1) {
- if (Item[CSL.PARALLEL_MATCH_VARS[i]]) {
- has_required_var = true;
- break;
- }
- }
- var basics_ok = true;
- var last_cite = this.sets.value().slice(-1)[0];
- if (last_cite && last_cite.Item) {
- var lastJuris = last_cite.Item.jurisdiction ? last_cite.Item.jurisdiction.split(":")[0] : "";
- var thisJuris = Item.jurisdiction ? Item.jurisdiction.split(":")[0] : "";
- if (last_cite.Item.title !== Item.title) {
- basics_ok = false;
- } else if (lastJuris !== thisJuris) {
- basics_ok = false;
- } else if (last_cite.Item.type !== Item.type) {
- basics_ok = false;
- } else if (["article-journal","article-magazine"].indexOf(Item.type) > -1) {
- if (!this.state.opt.development_extensions.handle_parallel_articles
- || last_cite.Item["container-title"] !== Item["container-title"]) {
- basics_ok = false;
- }
- }
- }
- if (!basics_ok || !has_required_var || CSL.PARALLEL_TYPES.indexOf(Item.type) === -1) {
- this.try_cite = true;
- if (this.in_series) {
- this.in_series = false;
- }
- }
- this.cite = {};
- this.cite.front = [];
- this.cite.mid = [];
- this.cite.back = [];
- this.cite.front_collapse = {};
- this.cite.back_forceme = [];
- this.cite.position = position;
- this.cite.Item = Item;
- this.cite.itemId = "" + Item.id;
- this.cite.prevItemID = "" + prevItemID;
- this.target = "front";
- if (["treaty"].indexOf(Item.type) > -1) {
- this.ignoreVars = this.ignoreVarsOrders;
- } else if (["article-journal","article-magazine"].indexOf(Item.type) > -1) {
- this.ignoreVars = this.ignoreVarsOther;
- } else if (item && item.prefix) {
- this.ignoreVars = this.ignoreVarsLawProceduralHistory;
- this.cite.useProceduralHistory = true;
- var prev = this.sets.value()[(this.sets.value().length - 1)];
- if (prev && prev.back) {
- for (var i=prev.back.length-1;i>-1;i+=-1) {
- if (prev.back[i] && prev[prev.back[i]]) {
- delete prev[prev.back[i]];
+ this.info = {};
+ if (sortedItems.length > 1) {
+ // Harder than it looks.
+ // On a first pass, get the seeAlso of each item.
+ for (var i=0,ilen=sortedItems.length; i 0 && this.sortedItemsPos < this.sortedItems.length) {
- curr = this.sortedItems[this.sortedItemsPos][1];
- last_id = "" + this.sortedItems[(this.sortedItemsPos - 1)][1].id;
- master = this.state.registry.registry[last_id].parallel;
- prev_locator = false;
- if (master == curr.id) {
- len = this.sortedItemsPos - 1;
- for (pos = len; pos > -1; pos += -1) {
- if (this.sortedItems[pos][1].id == Item.id) {
- prev_locator = this.sortedItems[pos][1].locator;
- break;
+ // On a second pass, set an item to FIRST if the current
+ // item is in its seeAlso. The seeAlso of the FIRST item control
+ // until (a) a non-member is encountered at CURRENT, or
+ // (b) the end of the array is reached.
+ // The seeAlso keys are deleted as each is seen.
+ // If neither (a) nor (b), set the current item to MID.
+ // If (a) and the previous item is FIRST, delete its
+ // parallel marker.
+ // If (b) and the current item is FIRST, delete its
+ // parallel marker.
+ // If (a) and the previous item is not FIRST, set it to
+ // LAST, and reset seeAlso from the current item.
+ // If (b) and the current item is not FIRST, set it to
+ // LAST.
+ var seriesRels = false;
+ var masterID = false;
+ for (var i=1,ilen=sortedItems.length; i -1) {
- return;
- }
- if (variable === "container-title" && this.sets.value().length === 0) {
- this.master_was_neutral_cite = false;
- }
- this.data = {};
- this.data.value = "";
- this.data.blobs = [];
- var is_mid = this.isMid(variable);
- if (real_variable === "authority" && this.variable === "names:front" && this.sets.value().length) {
- var prev = this.sets.value()[(this.sets.value().length - 1)].Item;
- var thisAuthority = false;
- if (this.cite.Item.authority && this.cite.Item.authority.length) {
- thisAuthority = this.cite.Item.authority[0].literal;
- }
- var thatAuthority = false;
- if (prev.authority && prev.authority.length) {
- thatAuthority = prev.authority[0].literal;
- }
- if (thisAuthority !== thatAuthority) {
- this.try_cite = true;
- this.in_series = false;
- }
- } else if (this.target === "front" && is_mid) {
- this.target = "mid";
- } else if (this.target === "mid" && !is_mid && this.cite.Item.title && variable !== "names") {
- this.target = "back";
- } else if (this.target === "back" && is_mid) {
- this.try_cite = true;
- this.in_series = false;
- }
- if (variable === "number") {
- this.cite.front.push(this.variable);
- } else if (CSL.PARALLEL_COLLAPSING_MID_VARSET.indexOf(variable) > -1) {
- if (["article-journal","article-magazine"].indexOf(this.cite.Item.type) > -1) {
- this.cite.mid.push(this.variable);
} else {
- this.cite.front.push(this.variable);
- }
- } else {
- this.cite[this.target].push(this.variable);
- }
- }
-};
-CSL.Parallel.prototype.AppendBlobPointer = function (blob) {
- if (this.use_parallels) {
- if (this.ignoreVars.indexOf(this.variable) > -1) {
- return;
- }
- if (this.use_parallels && (this.force_collapse || this.try_cite)) {
- if (["article-journal", "article-magazine"].indexOf(this.cite.Item.type) > -1) {
- if (["volume","page","page-first","issue"].indexOf(this.variable) > -1) {
- return;
- }
- if ("container-title" === this.variable && this.cite.mid.length > 1) {
- return;
- }
- }
- if (this.variable && (this.try_cite || this.force_collapse) && blob && blob.blobs) {
- if (!(this.cite.useProceduralHistory && this.target === "back")) {
- this.data.blobs.push([blob, blob.blobs.length]);
- }
- }
- }
- }
-};
-CSL.Parallel.prototype.AppendToVariable = function (str) {
- if (this.use_parallels) {
- if (this.ignoreVars.indexOf(this.variable) > -1) {
- return;
- }
- if (this.try_cite || this.force_collapse) {
- if (this.target !== "back" || true) {
- this.data.value += "::" + str;
- } else {
- var prev = this.sets.value()[(this.sets.value().length - 1)];
- if (prev) {
- if (prev[this.variable]) {
- if (prev[this.variable].value) {
- this.data.value += "::" + str;
- }
+ if (seriesRels[curr.id]) {
+ sortedItems[i][1].parallel = "mid";
+ if (!sortedItems[i][1].prefix) {
+ sortedItems[i][1].prefix = ", ";
}
- }
- }
- }
- }
-};
-CSL.Parallel.prototype.CloseVariable = function () {
- if (this.use_parallels) {
- if (this.ignoreVars.indexOf(this.variable) > -1) {
- return;
- }
- if (this.try_cite || this.force_collapse) {
- this.cite[this.variable] = this.data;
- if (this.sets.value().length > 0) {
- var prev = this.sets.value()[(this.sets.value().length - 1)];
- if (this.target === "front" && this.variable === "issued") {
- if (this.data.value && this.master_was_neutral_cite) {
- this.target = "mid";
- }
- }
- if (this.target === "front") {
- if ((prev[this.variable] || this.data.value) && (!prev[this.variable] || this.data.value !== prev[this.variable].value)) {
- if ("issued" !== this.variable) {
- this.in_series = false;
- }
- }
- } else if (this.target === "mid") {
- if (CSL.PARALLEL_COLLAPSING_MID_VARSET.indexOf(this.variable) > -1) {
- if (prev[this.variable]) {
- if (prev[this.variable].value === this.data.value) {
- this.cite.front_collapse[this.variable] = true;
- } else {
- this.cite.front_collapse[this.variable] = false;
- }
- } else {
- this.cite.front_collapse[this.variable] = false;
- }
- }
- } else if (this.target === "back") {
- if (prev[this.variable]) {
- if (this.data.value !== prev[this.variable].value
- && this.sets.value().slice(-1)[0].back_forceme.indexOf(this.variable) === -1) {
- this.in_series = false;
- }
- }
- }
- }
- }
- this.variable = false;
- }
-};
-CSL.Parallel.prototype.CloseCite = function () {
- var x, pos, len, use_journal_info, volume_pos, container_title_pos, section_pos;
- if (this.use_parallels && (this.force_collapse || this.try_cite)) {
- use_journal_info = false;
- if (!this.cite.front_collapse["container-title"]) {
- use_journal_info = true;
- }
- if (this.cite.front_collapse.volume === false) {
- use_journal_info = true;
- }
- if (this.cite.front_collapse["collection-number"] === false) {
- use_journal_info = true;
- }
- if (this.cite.front_collapse.section === false) {
- use_journal_info = true;
- }
- if (use_journal_info) {
- this.cite.use_journal_info = true;
- section_pos = this.cite.front.indexOf("section");
- if (section_pos > -1) {
- this.cite.front = this.cite.front.slice(0,section_pos).concat(this.cite.front.slice(section_pos + 1));
- }
- volume_pos = this.cite.front.indexOf("volume");
- if (volume_pos > -1) {
- this.cite.front = this.cite.front.slice(0,volume_pos).concat(this.cite.front.slice(volume_pos + 1));
- }
- container_title_pos = this.cite.front.indexOf("container-title");
- if (container_title_pos > -1) {
- this.cite.front = this.cite.front.slice(0,container_title_pos).concat(this.cite.front.slice(container_title_pos + 1));
- }
- var collection_number_pos = this.cite.front.indexOf("collection-number");
- if (collection_number_pos > -1) {
- this.cite.front = this.cite.front.slice(0,collection_number_pos).concat(this.cite.front.slice(collection_number_pos + 1));
- }
- }
- if (!this.in_series && !this.force_collapse) {
- this.ComposeSet(true);
- }
- if (this.sets.value().length === 0) {
- var has_date = false;
- for (pos = 0, len = this.cite.back.length; pos < len; pos += 1) {
- x = this.cite.back[pos];
- if (x === "issued" && this.cite["issued"] && this.cite["issued"].value) {
- has_date = true;
- break;
- }
- }
- if (!has_date) {
- this.cite.back_forceme.push("issued");
- }
- } else {
- var idx = this.cite.front.indexOf("issued");
- if (idx === -1 || this.master_was_neutral_cite) {
- this.cite.back_forceme = this.sets.value().slice(-1)[0].back_forceme;
- }
- if (idx > -1) {
- var prev = this.sets.value()[this.sets.value().length - 1];
- if (!prev["issued"]) {
- this.cite.front = this.cite.front.slice(0, idx).concat(this.cite.front.slice(idx + 1));
- }
- }
- if (this.master_was_neutral_cite && this.cite.mid.indexOf("names:mid") > -1) {
- this.cite.front.push("names:mid");
- }
- }
- this.sets.value().push(this.cite);
- }
-};
-CSL.Parallel.prototype.ComposeSet = function () {
- var cite, pos, len;
- if (this.use_parallels && (this.force_collapse || this.try_cite)) {
- var lengthCheck = this.sets.value().length;
- if (this.sets.value().length === 1) {
- if (!this.in_series) {
- this.sets.value().pop();
- this.delim_counter += 1;
- }
- } else {
- len = this.sets.value().length;
- for (pos = 0; pos < len; pos += 1) {
- cite = this.sets.value()[pos];
- if (pos === 0) {
- this.delim_counter += 1;
+ delete seriesRels[curr.id];
+ sortedItems[i][1].repeats = this.getRepeats(prev, curr);
+ this.state.registry.registry[masterID].siblings.push(curr.id);
} else {
- if (!cite.Item.title && cite.use_journal_info) {
- this.delim_pointers.push(false);
- } else {
- this.delim_pointers.push(this.delim_counter);
- }
- this.delim_counter += 1;
- }
- if (CSL.POSITION_FIRST === cite.position) {
- if (pos === 0) {
- this.state.registry.registry[cite.itemId].master = true;
- this.state.registry.registry[cite.itemId].siblings = [];
- this.state.registry.registry[cite.itemId].parallel = false;
- } else {
- if (cite.prevItemID) {
- if (!this.state.registry.registry[cite.prevItemID].parallel) {
- this.state.registry.registry[cite.itemId].parallel = cite.prevItemID;
- } else {
- this.state.registry.registry[cite.itemId].parallel = this.state.registry.registry[cite.prevItemID].parallel;
- }
- this.state.registry.registry[cite.itemId].siblings = this.state.registry.registry[cite.prevItemID].siblings;
- if (!this.state.registry.registry[cite.itemId].siblings) {
- this.state.registry.registry[cite.itemId].siblings = [];
- CSL.debug("WARNING: adding missing siblings array to registry object");
- }
- this.state.registry.registry[cite.itemId].siblings.push(cite.itemId);
- }
- }
+ sortedItems[i-1][1].parallel = "last";
+ seriesRels = false;
}
}
- this.sets.push([]);
- }
- if (lengthCheck < 2) {
- this.purgeGroupsIfParallel(false);
- } else {
- this.purgeGroupsIfParallel(true);
- }
- this.in_series = true;
- }
-};
-CSL.Parallel.prototype.PruneOutputQueue = function () {
- var len, pos, series, ppos, llen, cite;
- if (this.use_parallels) {
- len = this.sets.mystack.length;
- for (pos = 0; pos < len; pos += 1) {
- series = this.sets.mystack[pos];
- if (series.length > 1) {
- llen = series.length;
- for (ppos = 0; ppos < llen; ppos += 1) {
- cite = series[ppos];
- if (ppos === 0) {
- this.purgeVariableBlobs(cite, cite.back);
- } else if (ppos === (series.length - 1)) {
- this.purgeVariableBlobs(cite, cite.front.concat(cite.back_forceme));
- } else {
- this.purgeVariableBlobs(cite, cite.front.concat(cite.back));
- }
+ if (i === (sortedItems.length-1)) {
+ if (sortedItems[i][1].parallel === "mid") {
+ sortedItems[i][1].parallel = "last";
}
}
}
}
};
-CSL.Parallel.prototype.purgeVariableBlobs = function (cite, varnames) {
- var len, pos, varname, b, llen, ppos, out;
- if (this.use_parallels) {
- out = this.state.output.current.value();
- if ("undefined" === typeof out.length) {
- out = out.blobs;
- }
- for (pos = 0, len = this.delim_pointers.length; pos < len; pos += 1) {
- ppos = this.delim_pointers[pos];
- if (ppos !== false) {
- out[ppos].parallel_delimiter = ", ";
- }
- }
- len = varnames.length - 1;
- for (pos = len; pos > -1; pos += -1) {
- varname = varnames[pos];
- if (cite[varname]) {
- llen = cite[varname].blobs.length - 1;
- for (ppos = llen; ppos > -1; ppos += -1) {
- b = cite[varname].blobs[ppos];
- b[0].blobs = b[0].blobs.slice(0, b[1]).concat(b[0].blobs.slice((b[1] + 1)));
- this.state.tmp.has_purged_parallel = true;
- if (b[0] && b[0].strings && "string" == typeof b[0].strings.oops
- && b[0].parent && b[0].parent) {
- b[0].parent.parent.strings.delimiter = b[0].strings.oops;
- }
- }
- }
- }
- }
-};
-CSL.Parallel.prototype.purgeGroupsIfParallel = function (original_condition) {
+
+
+CSL.Parallel.prototype.purgeGroupsIfParallel = function () {
for (var i = this.parallel_conditional_blobs_list.length - 1; i > -1; i += -1) {
var obj = this.parallel_conditional_blobs_list[i];
- var purgeme = true;
- for (var j = 0, jlen = obj.conditions.length; j < jlen; j += 1) {
- if (!(!obj.conditions[j] === !!original_condition
- || ("master" === obj.conditions[j]
- && !this.state.registry.registry[obj.id].master)
- || ("servant" === obj.conditions[j]
- && !this.state.registry.registry[obj.id].parallel))) {
- var purgeme = false;
- break;
+ if (!obj.result && !obj.repeats) {
+ //this.state.sys.print(i + " No action [render] " + obj.id);
+ purgeme = false;
+ } else {
+ if (obj.condition) {
+ var purgeme = true;
+ if (obj.result === obj.condition) {
+ //this.state.sys.print(i + " Position match [render] "+obj.id);
+ purgeme = false;
+ }
+ //else {
+ // this.state.sys.print(i + " Position non-match [not-render] "+obj.id + " " + obj.condition + " " + obj.result);
+ //}
+ } else if (obj.repeats) {
+ purgeme = false;
+ var matches = 0;
+ //this.state.sys.print(obj.norepeat)
+ for (var j=0,jlen=obj.norepeat.length; j obj.pos) {
@@ -12910,8 +16832,14 @@ CSL.Parallel.prototype.purgeGroupsIfParallel = function (original_condition) {
this.parallel_conditional_blobs_list.pop();
}
};
+
+/*global CSL: true */
+
+
CSL.Util = {};
+
CSL.Util.Match = function () {
+
this.any = function (token, state, tests) {
return function (Item, item) {
for (var i=0, ilen=tests.length; i < ilen; i += 1) {
@@ -12923,6 +16851,7 @@ CSL.Util.Match = function () {
return false;
};
};
+
this.none = function (token, state, tests) {
return function (Item, item) {
for (var i=0,ilen=tests.length;i -1) {
preferredJurisdiction = "default";
} else if (Item.jurisdiction) {
@@ -13003,7 +16999,29 @@ CSL.Transform = function (state) {
preferredJurisdiction = "default";
}
var jurisdiction = state.transform.loadAbbreviation(preferredJurisdiction, myabbrev_family, normalizedKey, Item.type);
+
+ // Some rules:
+ // # variable === "country"
+ // (1) If an abbreviation is associated with the code, then:
+ // (a) return the abbreviated form if form="short"
+ // (2) Otherwise:
+ // (a) return the human-readable country name, or whatever is there if it's not a code
+ // # variable === "jurisdiction"
+ // (1) If !!getHumanForm(jurisdictionID, false, false):
+ // (a) If the code is top-level (i.e. a country):
+ // (i) return nothing -- this is what the "country" variable is for.
+ // (b) otherwise:
+ // (i) If an abbreviation is associated with the code, then:
+ // (A) return the abbreviated form
+ // (ii) Otherwise
+ // (A) return the human-readable form, with the country name & code removed from the front
+ // (2) Otherwise:
+ // (a) abbreviate as per normal.
+ // # other variables
+ // (1) Abbreviate as per normal.
+
if (state.transform.abbrevs[jurisdiction][myabbrev_family] && normalizedKey) {
+ // Safe to test presence of abbrev against raw object in this block
var abbrev = state.transform.abbrevs[jurisdiction][myabbrev_family][normalizedKey];
if (tok.strings.form === "short" && abbrev) {
if (quashCountry) {
@@ -13016,6 +17034,8 @@ CSL.Transform = function (state) {
}
}
}
+
+ // Was for:
if (!value
&& (!state.opt.development_extensions.require_explicit_legal_case_title_short || Item.type !== 'legal_case')
&& altvar && Item[altvar] && use_field) {
@@ -13029,6 +17049,7 @@ CSL.Transform = function (state) {
}
return value;
}
+
function getFieldLocale(Item,field) {
var ret = state.opt["default-locale"][0].slice(0, 2);
var localeRex;
@@ -13042,6 +17063,7 @@ CSL.Transform = function (state) {
if (m) {
ret = m[1];
} else {
+ // Set garbage to "Klingon".
ret = "tlh";
}
}
@@ -13050,14 +17072,18 @@ CSL.Transform = function (state) {
}
if (!state.opt.development_extensions.strict_text_case_locales
|| state.opt.development_extensions.normalize_lang_keys_to_lowercase) {
+
ret = ret.toLowerCase();
}
return ret;
}
+
+ // Internal functions
function getTextSubField (Item, field, locale_type, use_default, stopOrig, family_var) {
var opt, o, ret, opts;
var usedOrig = stopOrig;
var usingOrig = false;
+
if (!Item[field]) {
return {
name:"",
@@ -13065,9 +17091,14 @@ CSL.Transform = function (state) {
token: CSL.Util.cloneToken(this)
};
}
+ // If form="short" is selected ("family_var" is a misnomer
+ // here, it means short-form requested), and the variable
+ // has a short-form partner (i.e. it is in array
+ // VARIABLES_WITH_SHORT_FORM), then it is run here as *-short".
var stickyLongForm = false;
if (CSL.VARIABLES_WITH_SHORT_FORM.indexOf(field) > -1
&& family_var) {
+
field = field + "-short";
stickyLongForm = true;
}
@@ -13080,12 +17111,16 @@ CSL.Transform = function (state) {
} else {
fieldsToTry.push(field);
}
+
for (var h=0,hlen=fieldsToTry.length; h>>/);
if (m) {
@@ -13226,7 +17284,15 @@ CSL.Transform = function (state) {
return value;
}
this.quashCheck = quashCheck;
+
+ // Return function appropriate to selected options
function getOutputFunction(variables, family_var, abbreviation_fallback, alternative_varname) {
+ // var mytoken;
+
+ // Set the primary_locale and secondary_locale lists appropriately.
+ // No instance helper function for this; everything can be derived
+ // from processor settings and rendering context.
+
var localesets;
var langPrefs = CSL.LangPrefsMap[variables[0]];
if (!langPrefs) {
@@ -13234,11 +17300,16 @@ CSL.Transform = function (state) {
} else {
localesets = state.opt['cite-lang-prefs'][langPrefs];
}
+
return function (state, Item, item) {
var primary, primary_locale, secondary, secondary_locale, tertiary, tertiary_locale, primary_tok;
if (!variables[0] || (!Item[variables[0]] && !Item[alternative_varname])) {
return null;
}
+ //
+ // Exploring the edges here.
+ // "suppress-author" for string variables (mostly titles).
+ //
if (!state.tmp.just_looking && item && item["suppress-author"]) {
if (!state.tmp.probably_rendered_something && state.tmp.can_substitute.length() > 1) {
return null;
@@ -13265,18 +17336,25 @@ CSL.Transform = function (state) {
slot.primary = 'locale-orig';
}
}
+
if (variables[0] === "title-short"
|| (state.tmp.area !== "bibliography"
&& !(state.tmp.area === "citation"
&& state.opt.xclass === "note"
&& item && !item.position))) {
+
slot.secondary = false;
slot.tertiary = false;
}
+
if (state.tmp.multi_layout) {
slot.secondary = false;
slot.tertiary = false;
}
+
+ // Problem for multilingual: we really should be
+ // checking for sanity on the basis of the output
+ // strings to be actually used. (also below)
if (state.tmp["publisher-list"]) {
if (variables[0] === "publisher") {
state.tmp["publisher-token"] = this;
@@ -13285,6 +17363,7 @@ CSL.Transform = function (state) {
}
return null;
}
+ // True is for transform fallback
var res = getTextSubField.call(this, Item, variables[0], slot.primary, true, null, family_var);
primary = res.name;
primary_locale = res.locale;
@@ -13292,13 +17371,18 @@ CSL.Transform = function (state) {
var primaryUsedOrig = res.usedOrig;
if (family_var && !res.found_variant_ok) {
primary = abbreviate(state, primary_tok, Item, alternative_varname, primary, family_var, true);
+ // 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
primary = quashCheck(primary);
}
}
if (publisherCheck(this, Item, primary, family_var)) {
return null;
}
+
+ // No fallback for secondary and tertiary
secondary = false;
tertiary = false;
var secondary_tok;
@@ -13310,9 +17394,11 @@ CSL.Transform = function (state) {
secondary_tok = res.token;
if (family_var && !res.found_variant_ok) {
if (secondary) {
+ // The abbreviate() function could use a cleanup, after Zotero correct to use title-short
secondary = abbreviate(state, secondary_tok, Item, false, secondary, family_var, true);
}
}
+ //print("XXX secondary_locale: "+secondary_locale);
}
if (slot.tertiary) {
res = getTextSubField.call(this, Item, variables[0], slot.tertiary, false, res.usedOrig, null, family_var);
@@ -13321,19 +17407,27 @@ CSL.Transform = function (state) {
tertiary_tok = res.token;
if (family_var && !res.found_variant_ok) {
if (tertiary) {
+ // The abbreviate() function could use a cleanup, after Zotero correct to use title-short
tertiary = abbreviate(state, tertiary_tok, Item, false, tertiary, family_var, true);
}
}
+ //print("XXX tertiary_locale: "+tertiary_locale);
}
+
+ // Decoration of primary (currently translit only) goes here
var primaryPrefix;
if (slot.primary === "locale-translit") {
primaryPrefix = state.opt.citeAffixes[langPrefs][slot.primary].prefix;
}
+ // XXX This should probably protect against italics at higher
+ // levels.
+
if (primaryPrefix === "" && variables[0] === 'title' && !primaryUsedOrig) {
var hasItalic = false;
for (var i = 0, ilen = primary_tok.decorations.length; i < ilen; i += 1) {
if (primary_tok.decorations[i][0] === "@font-style"
&& primary_tok.decorations[i][1] === "italic") {
+
hasItalic = true;
}
}
@@ -13341,23 +17435,33 @@ CSL.Transform = function (state) {
primary_tok.decorations.push(["@font-style", "italic"]);
}
}
+
+ //print("XXX "+primary_tok.strings["text-case"]);
if (primary_locale !== "en" && primary_tok.strings["text-case"] === "title") {
primary_tok.strings["text-case"] = "passthrough";
}
+
if ("title" === variables[0]) {
primary = CSL.demoteNoiseWords(state, primary, this["leading-noise-words"]);
}
+
if (secondary || tertiary) {
+
state.output.openLevel("empty");
+
+ // A little too aggressive maybe.
primary_tok.strings.suffix = primary_tok.strings.suffix.replace(/[ .,]+$/,"");
state.output.append(primary, primary_tok);
state.tmp.probably_rendered_something = true;
+
if (secondary) {
secondary_tok.strings.prefix = state.opt.citeAffixes[langPrefs][slot.secondary].prefix;
secondary_tok.strings.suffix = state.opt.citeAffixes[langPrefs][slot.secondary].suffix;
+ // Add a space if empty
if (!secondary_tok.strings.prefix) {
secondary_tok.strings.prefix = " ";
}
+ // Remove quotes
for (var i = secondary_tok.decorations.length - 1; i > -1; i += -1) {
if (['@quotes/true', '@font-style/italic', '@font-style/oblique', '@font-weight/bold'].indexOf(secondary_tok.decorations[i].join('/')) > -1) {
secondary_tok.decorations = secondary_tok.decorations.slice(0, i).concat(secondary_tok.decorations.slice(i + 1));
@@ -13372,19 +17476,20 @@ CSL.Transform = function (state) {
state.output.openLevel(secondary_outer);
state.output.append(secondary, secondary_tok);
state.output.closeLevel();
+
var blob_obj = state.output.current.value();
var blobs_pos = state.output.current.value().blobs.length - 1;
- if (state.parallel.use_parallels) {
- state.parallel.cite.front.push(variables[0] + ":secondary");
- state.parallel.cite[variables[0] + ":secondary"] = {blobs:[[blob_obj, blobs_pos]]};
- }
+ // Suppress supplementary multilingual info on subsequent
+ // partners of a parallel cite?
}
if (tertiary) {
tertiary_tok.strings.prefix = state.opt.citeAffixes[langPrefs][slot.tertiary].prefix;
tertiary_tok.strings.suffix = state.opt.citeAffixes[langPrefs][slot.tertiary].suffix;
+ // Add a space if empty
if (!tertiary_tok.strings.prefix) {
tertiary_tok.strings.prefix = " ";
}
+ // Remove quotes
for (var i = tertiary_tok.decorations.length - 1; i > -1; i += -1) {
if (['@quotes/true', '@font-style/italic', '@font-style/oblique', '@font-weight/bold'].indexOf(tertiary_tok.decorations[i].join('/')) > -1) {
tertiary_tok.decorations = tertiary_tok.decorations.slice(0, i).concat(tertiary_tok.decorations.slice(i + 1));
@@ -13399,12 +17504,12 @@ CSL.Transform = function (state) {
state.output.openLevel(tertiary_outer);
state.output.append(tertiary, tertiary_tok);
state.output.closeLevel();
+
var blob_obj = state.output.current.value();
var blobs_pos = state.output.current.value().blobs.length - 1;
- if (state.parallel.use_parallels) {
- state.parallel.cite.front.push(variables[0] + ":tertiary");
- state.parallel.cite[variables[0] + ":tertiary"] = {blobs:[[blob_obj, blobs_pos]]};
- }
+ // Suppress supplementary multilingual info on subsequent
+ // partners of a parallel cite?
+ // See note above.
}
state.output.closeLevel();
} else {
@@ -13416,17 +17521,115 @@ CSL.Transform = function (state) {
}
this.getOutputFunction = getOutputFunction;
};
+
+/*global CSL: true */
+
+/**
+ * Style token.
+ *
This class provides the tokens that define
+ * the runtime version of the style. The tokens are
+ * instantiated by {@link CSL.Core.Build}, but the token list
+ * must be post-processed with
+ * {@link CSL.Core.Configure} before it can be used to generate
+ * citations.
+ * @param {String} name The node name represented by this token.
+ * @param {Int} tokentype A flag indicating whether this token
+ * marks the start of a node, the end of a node, or is a singleton.
+ * @class
+ */
CSL.Token = function (name, tokentype, conditional) {
+ /**
+ * Name of the element.
+ *
This corresponds to the element name of the
+ * relevant tag in the CSL file.
+ */
this.name = name;
+ /**
+ * Strings and other static content specific to the element.
+ */
this.strings = {};
this.strings.delimiter = undefined;
this.strings.prefix = "";
this.strings.suffix = "";
+ /**
+ * Formatting parameters.
+ *
This is a placeholder at instantiation. It is
+ * replaced by the result of {@link CSL.setDecorations}
+ * when the tag is created and configured during {@link CSL.Core.Build}
+ * by {@link CSL.XmlToToken}. The parameters for particular
+ * formatting attributes are stored as string arrays, which
+ * map to formatting functions at runtime,
+ * when the output format is known. Note that the order in which
+ * parameters are registered is fixed by the constant
+ * {@link CSL.FORMAT_KEY_SEQUENCE}.
+ */
this.decorations = [];
this.variables = [];
+ /**
+ * Element functions.
+ *
Functions implementing the styling behaviour of the element
+ * are pushed into this array in the {@link CSL.Core.Build} phase.
+ */
this.execs = [];
+ /**
+ * Token type.
+ *
This is a flag constant indicating whether the token represents
+ * a start tag, an end tag, or is a singleton.
+ */
this.tokentype = tokentype;
+
+ // Conditional attributes added to bare tokens at runtime
+
+ /**
+ * Condition evaluator.
+ *
This is a placeholder that receives a single function, and is
+ * only relevant for a conditional branching tag (if or
+ * else-if). The function implements the argument to
+ * the match= attribute (any, all
+ * or none), by executing the functions registered in the
+ * tests array (see below), and reacting accordingly. This
+ * function is invoked by the execution wrappers found in
+ * {@link CSL.Engine}.
Functions that evaluate to true or false, implementing
+ * various posisble attributes to the conditional branching tags,
+ * are registered here during {@link CSL.Core.Build}.
+ *
+ */
+ // this.tests = [];
+ /**
+ * Jump point on success.
+ *
This holds the list jump point to be used when the
+ * evaluator function of a conditional tag
+ * returns true (success). The jump index value is set during the
+ * back-to-front token pass performed during {@link CSL.Core.Configure}.
+ *
+ */
+ // this.succeed = false;
+ /**
+ * Jump point on failure.
+ *
This holds the list jump point to be used when the
+ * evaluator function of a conditional tag
+ * returns false (failure). Jump index values are set during the
+ * back-to-front token pass performed during {@link CSL.Core.Configure}.
+ *
+ */
+ // this.fail = false;
+ /**
+ * Index of next token.
+ *
This holds the index of the next token in the
+ * token list, which is the default "jump-point" for ordinary
+ * processing. Jump index values are set during the
+ * back-to-front token pass performed during {@link CSL.Core.Configure}.
+ *
+ */
+ // this.next = false;
};
+
+// Have needed this for yonks
CSL.Util.cloneToken = function (token) {
var newtok, key, pos, len;
if ("string" === typeof token) {
@@ -13447,6 +17650,8 @@ CSL.Util.cloneToken = function (token) {
if (token.variables) {
newtok.variables = token.variables.slice();
}
+ // Probably overkill; this is only used for cloning formatting
+ // tokens.
if (token.execs) {
newtok.execs = token.execs.slice();
if (token.tests) {
@@ -13455,6 +17660,13 @@ CSL.Util.cloneToken = function (token) {
}
return newtok;
};
+
+/*global CSL: true */
+
+/**
+ * Ambiguous Cite Configuration Object
+ * @class
+ */
CSL.AmbigConfig = function () {
this.maxvals = [];
this.minval = 1;
@@ -13463,9 +17675,13 @@ CSL.AmbigConfig = function () {
this.year_suffix = false;
this.disambiguate = 0;
};
+
+/*global CSL: true */
+
CSL.Blob = function (str, token, levelname) {
var len, pos, key;
this.levelname = levelname;
+ //print(levelname);
if (token) {
this.strings = {"prefix":"","suffix":""};
for (var key in token.strings) {
@@ -13498,15 +17714,34 @@ CSL.Blob = function (str, token, levelname) {
}
this.alldecor = [this.decorations];
};
+
+
CSL.Blob.prototype.push = function (blob) {
if ("string" === typeof this.blobs) {
- throw "Attempt to push blob onto string object";
+ CSL.error("Attempt to push blob onto string object");
} else if (false !== blob) {
blob.alldecor = blob.alldecor.concat(this.alldecor);
this.blobs.push(blob);
}
};
+
+/*global CSL: true */
+
+/**
+ * An output instance object representing a number or a range
+ *
+ * with attributes next and start, and
+ * methods isRange(), renderStart(), renderEnd() and renderRange().
+ * At render time, the output queue will perform optional
+ * collapsing of these objects in the queue, according to
+ * configurable options, and apply any decorations registered
+ * in the object to the output elements.
+ * @namespace Range object and friends.
+ */
+
CSL.NumericBlob = function (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;
this.alldecor = [];
this.num = num;
@@ -13540,14 +17775,20 @@ CSL.NumericBlob = function (particle, num, mother_token, id) {
this.formatter = new CSL.Output.DefaultFormatter();
}
};
+
+
CSL.NumericBlob.prototype.setFormatter = function (formatter) {
this.formatter = formatter;
this.type = this.formatter.format(1);
};
+
+
CSL.Output.DefaultFormatter = function () {};
+
CSL.Output.DefaultFormatter.prototype.format = function (num) {
return num.toString();
};
+
CSL.NumericBlob.prototype.checkNext = function (next,start) {
if (start) {
this.status = CSL.START;
@@ -13576,9 +17817,16 @@ CSL.NumericBlob.prototype.checkNext = function (next,start) {
next.status = CSL.SUCCESSOR;
}
}
+ // wakes up the correct delimiter.
+ //if (this.status === CSL.SEEN) {
+ // this.status = CSL.SUCCESSOR;
+ //}
}
};
+
+
CSL.NumericBlob.prototype.checkLast = function (last) {
+ // Used to adjust final non-range join
if (this.status === CSL.SEEN
|| (last.num !== (this.num - 1) && this.status === CSL.SUCCESSOR)) {
this.status = CSL.SUCCESSOR;
@@ -13586,11 +17834,20 @@ CSL.NumericBlob.prototype.checkLast = function (last) {
}
return false;
};
+
+/*global CSL: true */
+
CSL.Util.fixDateNode = function (parent, pos, node) {
var form, variable, datexml, subnode, partname, attr, val, prefix, suffix, children, subchildren, display, cslid;
+
var lingo = this.cslXml.getAttributeValue(node, "lingo");
+
var default_locale = this.cslXml.getAttributeValue(node, "default-locale");
+
+ // Raise date flag, used to control inclusion of year-suffix key in sorts
+ // This may be a little reckless: not sure what happens on no-date conditions
this.build.date_key = true;
+
form = this.cslXml.getAttributeValue(node, "form");
var lingo;
if (default_locale) {
@@ -13598,31 +17855,61 @@ CSL.Util.fixDateNode = function (parent, pos, node) {
} else {
lingo = this.cslXml.getAttributeValue(node, "lingo");
}
+
if (!this.getDate(form, default_locale)) {
return parent;
}
+
var dateparts = this.cslXml.getAttributeValue(node, "date-parts");
+
variable = this.cslXml.getAttributeValue(node, "variable");
prefix = this.cslXml.getAttributeValue(node, "prefix");
suffix = this.cslXml.getAttributeValue(node, "suffix");
display = this.cslXml.getAttributeValue(node, "display");
cslid = this.cslXml.getAttributeValue(node, "cslid");
+
+ //
+ // Xml: Copy a node
+ //
datexml = this.cslXml.nodeCopy(this.getDate(form, default_locale));
this.cslXml.setAttribute(datexml, 'lingo', this.opt.lang);
this.cslXml.setAttribute(datexml, 'form', form);
this.cslXml.setAttribute(datexml, 'date-parts', dateparts);
this.cslXml.setAttribute(datexml, "cslid", cslid);
+ //
+ // Xml: Set attribute
+ //
this.cslXml.setAttribute(datexml, 'variable', variable);
this.cslXml.setAttribute(datexml, 'default-locale', default_locale);
+ //
+ // Xml: Set flag
+ //
if (prefix) {
+ //
+ // Xml: Set attribute
+ //
this.cslXml.setAttribute(datexml, "prefix", prefix);
}
if (suffix) {
+ //
+ // Xml: Set attribute
+ //
this.cslXml.setAttribute(datexml, "suffix", suffix);
}
if (display) {
+ //
+ // Xml: Set attribute
+ //
this.cslXml.setAttribute(datexml, "display", display);
}
+ //
+ // Step through any date-part children of the layout date node,
+ // and lay their attributes onto the corresponding node in the
+ // locale template node copy.
+ //
+ // tests: language_BaseLocale
+ // tests: date_LocalizedTextInStyleLocaleWithTextCase
+ //
children = this.cslXml.children(datexml);
for (var key in children) {
subnode = children[key];
@@ -13633,6 +17920,7 @@ CSL.Util.fixDateNode = function (parent, pos, node) {
}
}
}
+
children = this.cslXml.children(node);
for (var key in children) {
subnode = children[key];
@@ -13653,26 +17941,55 @@ CSL.Util.fixDateNode = function (parent, pos, node) {
}
}
}
+
if ("year" === this.cslXml.getAttributeValue(node, "date-parts")) {
+
+ //
+ // Xml: Find one node by attribute and delete
+ //
this.cslXml.deleteNodeByNameAttribute(datexml, 'month');
+ //
+ // Xml: Find one node by attribute and delete
+ //
this.cslXml.deleteNodeByNameAttribute(datexml, 'day');
+
} else if ("year-month" === this.cslXml.getAttributeValue(node, "date-parts")) {
+ //
+ // Xml: Find one node by attribute and delete
+ //
this.cslXml.deleteNodeByNameAttribute(datexml, 'day');
} else if ("month-day" === this.cslXml.getAttributeValue(node, "date-parts")) {
+ //
+ // Xml: Get child nodes
+ //
var childNodes = this.cslXml.children(datexml);
for (var i=1,ilen=this.cslXml.numberofnodes(childNodes);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);
}
}
+
+ // Workaround for Internet Explorer
+ //namelist = namelist.split(/(\-|\s+)/);
+ // Workaround for Internet Explorer
mm = namelist.match(/[\-\s]+/g);
lst = namelist.split(/[\-\s]+/);
if (mm === null) {
@@ -13808,7 +18171,9 @@ CSL.Util.Names.initializeWith = function (state, name, terminator, normalizeOnly
lst[0] += ".";
}
}
+
if (lst.length === 0) {
+ // This doesn't make much sense, and may be impossible.
namelist = mm;
} else {
namelist = [lst[0]];
@@ -13818,6 +18183,8 @@ CSL.Util.Names.initializeWith = function (state, name, terminator, normalizeOnly
}
}
lst = namelist;
+
+ // Use doInitializeName or doNormalizeName, depending on requirements.
if (normalizeOnly) {
ret = CSL.Util.Names.doNormalize(state, lst, terminator);
} else {
@@ -13826,9 +18193,12 @@ CSL.Util.Names.initializeWith = function (state, name, terminator, normalizeOnly
ret = ret.replace(/\u2013([a-z])/g, "-$1");
return ret;
};
+
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
terminator = terminator ? 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) === ".") {
@@ -13840,26 +18210,38 @@ CSL.Util.Names.doNormalize = function (state, namelist, terminator) {
isAbbrev.push(false);
}
}
+ // Step through the elements of the givenname array
for (i = 0, ilen = namelist.length; i < ilen; i += 2) {
+ // If the element is not an abbreviation, leave it and its trailing spaces alone
if (isAbbrev[i]) {
+ // For all elements but the last
if (i < namelist.length - 2) {
+ // Start from scratch on space-like things following an abbreviation
namelist[i + 1] = "";
+
if (!isAbbrev[i+2]) {
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$/, "");
} else {
namelist[i] = namelist[i] + terminator;
}
}
+ // For the last element (if it is an abbreviation), just append the terminator
if (i === namelist.length - 1) {
namelist[i] = namelist[i] + terminator;
}
}
}
+ // Remove trailing cruft and duplicate spaces, and return
return namelist.join("").replace(/[\u0009\u000a\u000b\u000c\u000d\u0020\ufeff\u00a0]+$/,"").replace(/\s*\-\s*/g, "-").replace(/[\u0009\u000a\u000b\u000c\u000d\u0020]+/g, " ");
};
+
CSL.Util.Names.doInitialize = function (state, namelist, terminator) {
var i, ilen, m, j, jlen, lst, n;
for (i = 0, ilen = namelist.length; i < ilen; i += 2) {
@@ -13892,6 +18274,7 @@ CSL.Util.Names.doInitialize = function (state, namelist, terminator) {
extra = s.toLocaleLowerCase();
}
}
+ // namelist[i] = m[1].toLocaleUpperCase() + extra;
namelist[i] = m[1] + extra;
if (i < (ilen - 1)) {
if (terminator.match("%s")) {
@@ -13918,6 +18301,7 @@ CSL.Util.Names.doInitialize = function (state, namelist, terminator) {
ret = ret.replace(/[\u0009\u000a\u000b\u000c\u000d\u0020\ufeff\u00a0]+$/,"").replace(/\s*\-\s*/g, "-").replace(/[\u0009\u000a\u000b\u000c\u000d\u0020]+/g, " ");
return ret;
};
+
CSL.Util.Names.getRawName = function (name) {
var ret = [];
if (name.literal) {
@@ -13932,8 +18316,33 @@ 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 */
+
+/**
+ * Date mangling functions.
+ * @namespace Date construction utilities
+ */
CSL.Util.Dates = {};
+
+/**
+ * Year manglers
+ *
short, long
+ */
CSL.Util.Dates.year = {};
+
+/**
+ * Convert year to long form
+ *
This just passes the number back as a string.
+ */
CSL.Util.Dates.year["long"] = function (state, num) {
if (!num) {
if ("boolean" === typeof num) {
@@ -13944,6 +18353,11 @@ CSL.Util.Dates.year["long"] = function (state, num) {
}
return num.toString();
};
+
+/**
+ * Crudely convert to Japanese Imperial form.
+ *
Returns the result as a string.
+ */
CSL.Util.Dates.year.imperial = function (state, num, end) {
var year = "";
if (!num) {
@@ -13983,6 +18397,8 @@ CSL.Util.Dates.year.imperial = function (state, num, end) {
if (label && offset) {
var normalizedKey = label;
if (state.sys.normalizeAbbrevsKey) {
+ // The first argument does not need to specify the exact variable
+ // name.
normalizedKey = state.sys.normalizeAbbrevsKey("number", label);
}
if (!state.transform.abbrevs['default']['number'][normalizedKey]) {
@@ -13995,12 +18411,23 @@ CSL.Util.Dates.year.imperial = function (state, num, end) {
}
return year;
};
+
+/**
+ * Convert year to short form
+ *
Just crops any 4-digit year to the last two digits.
+ */
CSL.Util.Dates.year["short"] = function (state, num) {
num = num.toString();
if (num && num.length === 4) {
return num.substr(2);
}
};
+
+
+/**
+ * Convert year to short form
+ *
Just crops any 4-digit year to the last two digits.
+ */
CSL.Util.Dates.year.numeric = function (state, num) {
var m, pre;
num = "" + num;
@@ -14017,6 +18444,13 @@ CSL.Util.Dates.year.numeric = function (state, num) {
}
return (pre + num);
};
+
+
+/*
+ * MONTH manglers
+ * normalize
+ * long, short, numeric, numeric-leading-zeros
+ */
CSL.Util.Dates.normalizeMonth = function (num, useSeason) {
var ret;
if (!num) {
@@ -14049,7 +18483,13 @@ CSL.Util.Dates.normalizeMonth = function (num, useSeason) {
}
return ret;
};
+
CSL.Util.Dates.month = {};
+
+/**
+ * Convert month to numeric form
+ *
This just passes the number back as a string.
+ */
CSL.Util.Dates.month.numeric = function (state, num) {
var num = CSL.Util.Dates.normalizeMonth(num);
if (!num) {
@@ -14057,6 +18497,11 @@ CSL.Util.Dates.month.numeric = function (state, num) {
}
return num;
};
+
+/**
+ * Convert month to numeric-leading-zeros form
+ *
This just passes the number back as string padded with zeros.
+ */
CSL.Util.Dates.month["numeric-leading-zeros"] = function (state, num) {
var num = CSL.Util.Dates.normalizeMonth(num);
if (!num) {
@@ -14069,6 +18514,14 @@ CSL.Util.Dates.month["numeric-leading-zeros"] = function (state, num) {
}
return num;
};
+
+/**
+ * Convert month to long form
+ *
This passes back the month of the locale in long form.
+ */
+
+// Gender is not currently used. Is it needed?
+
CSL.Util.Dates.month["long"] = function (state, num, gender, forceDefaultLocale) {
var res = CSL.Util.Dates.normalizeMonth(num, true);
var num = res.num;
@@ -14083,6 +18536,14 @@ CSL.Util.Dates.month["long"] = function (state, num, gender, forceDefaultLocale)
}
return num;
};
+
+/**
+ * Convert month to long form
+ *
This passes back the month of the locale in short form.
+ */
+
+// See above.
+
CSL.Util.Dates.month["short"] = function (state, num, gender, forceDefaultLocale) {
var res = CSL.Util.Dates.normalizeMonth(num, true);
var num = res.num;
@@ -14097,11 +18558,27 @@ CSL.Util.Dates.month["short"] = function (state, num, gender, forceDefaultLocale
}
return num;
};
+
+/*
+ * DAY manglers
+ * numeric, numeric-leading-zeros, ordinal
+ */
CSL.Util.Dates.day = {};
+
+/**
+ * Convert day to numeric form
+ *
This just passes the number back as a string.
+ */
CSL.Util.Dates.day.numeric = function (state, num) {
return num.toString();
};
+
CSL.Util.Dates.day["long"] = CSL.Util.Dates.day.numeric;
+
+/**
+ * Convert day to numeric-leading-zeros form
+ *
This just passes the number back as a string padded with zeros.
+ */
CSL.Util.Dates.day["numeric-leading-zeros"] = function (state, num) {
if (!num) {
num = 0;
@@ -14112,10 +18589,29 @@ CSL.Util.Dates.day["numeric-leading-zeros"] = function (state, num) {
}
return num.toString();
};
+
+/**
+ * Convert day to ordinal form
+ *
This will one day pass back the number as a string with the
+ * ordinal suffix appropriate to the locale. For the present,
+ * it just does what is most of the time right for English.
+ */
CSL.Util.Sort.strip_prepositions = function (str) {
var m;
if ("string" === typeof str) {
@@ -14127,9 +18623,18 @@ CSL.Util.Sort.strip_prepositions = function (str) {
}
return str;
};
+
+/*global CSL: true */
+
CSL.Util.substituteStart = function (state, target) {
var element_trace, display, bib_first, func, choose_start, if_start, nodetypes;
- func = function (state) {
+ func = function (state, Item, item) {
+ if (item && item.parallel) {
+ state.tmp.group_context.tip.parallel_result = item.parallel;
+ }
+ if (item && item.repeats && Object.keys(item.repeats).length > 0) {
+ state.tmp.group_context.tip.parallel_repeats = item.repeats;
+ }
for (var i = 0, ilen = this.decorations.length; i < ilen; i += 1) {
if ("@strip-periods" === this.decorations[i][0] && "true" === this.decorations[i][1]) {
state.tmp.strip_periods += 1;
@@ -14143,6 +18648,10 @@ CSL.Util.substituteStart = function (state, target) {
this.decorations.push(["@showid","true", this.cslid]);
this.decorations.reverse();
}
+ //
+ // Contains body code for both substitute and first-field/remaining-fields
+ // formatting.
+ //
nodetypes = ["number", "date", "names"];
if (("text" === this.name && !this.postponed_macro) || nodetypes.indexOf(this.name) > -1) {
element_trace = function (state, Item, item) {
@@ -14165,8 +18674,16 @@ CSL.Util.substituteStart = function (state, target) {
state.tmp.element_trace.push("suppress-me");
}
}
+ /*
+ if (!state.tmp.just_looking && item && item["suppress-author"]) {
+ if (state.tmp.probably_rendered_something) {
+ //state.tmp.element_trace.push("suppress-me");
+ }
+ }
+ */
} else {
if (!state.tmp.just_looking && item && item["author-only"] && state.tmp.area !== "intext") {
+ // XXX can_block_substitute probably is doing nothing here. The value is always true.
if (!state.tmp.probably_rendered_something && state.tmp.can_block_substitute) {
} else {
state.tmp.element_trace.push("suppress-me");
@@ -14181,6 +18698,13 @@ CSL.Util.substituteStart = function (state, target) {
display = this.strings.cls;
this.strings.cls = false;
if (state.build.render_nesting_level === 0) {
+ //
+ // The markup formerly known as @bibliography/first
+ //
+ // Separate second-field-align from the generic display logic.
+ // There will be some code replication, but not in the
+ // assembled style.
+ //
if (state.build.area === "bibliography" && state.bibliography.opt["second-field-align"]) {
bib_first = new CSL.Token("group", CSL.START);
bib_first.decorations = [["@display", "left-margin"]];
@@ -14205,10 +18729,26 @@ CSL.Util.substituteStart = function (state, target) {
state.build.cls = display;
}
state.build.render_nesting_level += 1;
+ // Should this be render_nesting_level, with the increment
+ // below? ... ?
if (state.build.substitute_level.value() === 1) {
+ //
+ // All top-level elements in a substitute environment get
+ // wrapped in conditionals. The substitute_level variable
+ // is a stack, because spanned names elements (with their
+ // own substitute environments) can be nested inside
+ // a substitute environment.
+ //
+ // (okay, we use conditionals a lot more than that.
+ // we slot them in for author-only as well...)
choose_start = new CSL.Token("choose", CSL.START);
CSL.Node.choose.build.call(choose_start, state, target);
if_start = new CSL.Token("if", CSL.START);
+ //
+ // Set a test of the shadow if token to skip this
+ // macro if we have acquired a name value.
+
+ // check for variable
func = function () {
if (state.tmp.can_substitute.value()) {
return true;
@@ -14220,11 +18760,15 @@ CSL.Util.substituteStart = function (state, target) {
if_start.test = state.fun.match.any(this, state, if_start.tests);
target.push(if_start);
}
+
if (state.sys.variableWrapper
&& this.variables_real
&& this.variables_real.length) {
+
func = function (state, Item, item) {
if (!state.tmp.just_looking && !state.tmp.suppress_decorations) {
+ // Attach item data and variable names.
+ // Do with them what you will.
var variable_entry = new CSL.Token("text", CSL.START);
variable_entry.decorations = [["@showid", "true"]];
state.output.startTag("variable_entry", variable_entry);
@@ -14250,6 +18794,7 @@ CSL.Util.substituteStart = function (state, target) {
firstReferenceNoteNumber = item['first-reference-note-number'];
}
var citationNumber = 0;
+ // XXX Will this EVER happen?
if (item && item['citation-number']) {
citationNumber = item['citation-number'];
}
@@ -14275,10 +18820,14 @@ CSL.Util.substituteStart = function (state, target) {
this.execs.push(func);
}
};
+
+
CSL.Util.substituteEnd = function (state, target) {
var func, bib_first_end, bib_other, if_end, choose_end, author_substitute, str;
+
if (state.sys.variableWrapper
&& (this.hasVariable || (this.variables_real && this.variables_real.length))) {
+
func = function (state) {
if (!state.tmp.just_looking && !state.tmp.suppress_decorations) {
state.output.endTag("variable_entry");
@@ -14286,6 +18835,7 @@ CSL.Util.substituteEnd = function (state, target) {
};
this.execs.push(func);
}
+
func = function (state) {
for (var i = 0, ilen = this.decorations.length; i < ilen; i += 1) {
if ("@strip-periods" === this.decorations[i][0] && "true" === this.decorations[i][1]) {
@@ -14295,6 +18845,7 @@ CSL.Util.substituteEnd = function (state, target) {
}
};
this.execs.push(func);
+
state.build.render_nesting_level += -1;
if (state.build.render_nesting_level === 0) {
if (state.build.cls) {
@@ -14305,6 +18856,7 @@ CSL.Util.substituteEnd = function (state, target) {
state.build.cls = false;
} else if (state.build.area === "bibliography" && state.bibliography.opt["second-field-align"]) {
bib_first_end = new CSL.Token("group", CSL.END);
+ // first func end
func = function (state) {
if (!state.tmp.render_seen) {
state.output.endTag("bib_first"); // closes bib_first
@@ -14330,6 +18882,7 @@ CSL.Util.substituteEnd = function (state, target) {
choose_end = new CSL.Token("choose", CSL.END);
CSL.Node.choose.build.call(choose_end, state, target);
}
+
if ("names" === this.name || ("text" === this.name && this.variables_real !== "title")) {
author_substitute = new CSL.Token("text", CSL.SINGLETON);
func = function (state, Item) {
@@ -14345,19 +18898,25 @@ CSL.Util.substituteEnd = function (state, target) {
if (state.tmp.substituted_variable !== this.variables_real) {
return;
}
+
var subrule = state.bibliography.opt["subsequent-author-substitute-rule"];
var i, ilen;
+ //var text_esc = CSL.getSafeEscape(state);
var printing = !state.tmp.suppress_decorations;
if (printing && state.tmp.subsequent_author_substitute_ok) {
if (state.tmp.rendered_name) {
if ("partial-each" === subrule || "partial-first" === subrule) {
var dosub = true;
var rendered_name = [];
+ // This is a wee bit risky, as we're assuming that the name
+ // children and the list of stringified names are congruent.
+ // That *should* always be true, but you never know.
for (i = 0, ilen = state.tmp.name_node.children.length; i < ilen; i += 1) {
var name = state.tmp.rendered_name[i];
if (dosub
&& state.tmp.last_rendered_name && state.tmp.last_rendered_name.length > (i - 1)
&& name && !name.localeCompare(state.tmp.last_rendered_name[i])) {
+
str = new CSL.Blob(state[state.tmp.area].opt["subsequent-author-substitute"]);
state.tmp.name_node.children[i].blobs = [str];
if ("partial-first" === subrule) {
@@ -14368,6 +18927,7 @@ CSL.Util.substituteEnd = function (state, target) {
}
rendered_name.push(name);
}
+ // might want to slice this?
state.tmp.last_rendered_name = rendered_name;
} else if ("complete-each" === subrule) {
var rendered_name = state.tmp.rendered_name.join(",");
@@ -14403,8 +18963,17 @@ CSL.Util.substituteEnd = function (state, target) {
};
this.execs.push(func);
}
+
if (("text" === this.name && !this.postponed_macro) || ["number", "date", "names"].indexOf(this.name) > -1) {
+ // element trace
func = function (state, Item) {
+ // element_trace is a mess, but it's trying to do something simple.
+ // A queue append is done, and element_trace.value() returns "suppress-me"
+ // the append is aborted. That's it.
+ // It seems only to be used on numeric elements of numeric styles ATM.
+ // If used only for that purpose, it could be greatly simplified.
+ // If cleaned up, it could do more interesting things, like control
+ // the suppression of names set later than first position.
if (state.tmp.element_trace.mystack.length>1) {
state.tmp.element_trace.pop();
}
@@ -14412,6 +18981,9 @@ CSL.Util.substituteEnd = function (state, target) {
this.execs.push(func);
}
};
+
+/*global CSL: true */
+
CSL.Util.padding = function (num) {
var m = num.match(/\s*(-{0,1}[0-9]+)/);
if (m) {
@@ -14426,14 +18998,18 @@ CSL.Util.padding = function (num) {
}
return num;
};
+
CSL.Util.LongOrdinalizer = function () {};
+
CSL.Util.LongOrdinalizer.prototype.init = function (state) {
this.state = state;
};
+
CSL.Util.LongOrdinalizer.prototype.format = function (num, gender) {
if (num < 10) {
num = "0" + num;
}
+ // Argument true means "loose".
var ret = CSL.Engine.getField(
CSL.LOOSE,
this.state.locale[this.state.opt.lang].terms,
@@ -14445,13 +19021,17 @@ CSL.Util.LongOrdinalizer.prototype.format = function (num, gender) {
if (!ret) {
ret = this.state.fun.ordinalizer.format(num, gender);
}
+ // Probably too optimistic -- what if only renders in _sort?
this.state.tmp.cite_renders_content = true;
return ret;
};
+
+
CSL.Util.Ordinalizer = function (state) {
this.state = state;
this.suffixes = {};
};
+
CSL.Util.Ordinalizer.prototype.init = function () {
if (!this.suffixes[this.state.opt.lang]) {
this.suffixes[this.state.opt.lang] = {};
@@ -14469,6 +19049,7 @@ CSL.Util.Ordinalizer.prototype.init = function () {
}
}
};
+
CSL.Util.Ordinalizer.prototype.format = function (num, gender) {
var str;
num = parseInt(num, 10);
@@ -14498,6 +19079,8 @@ CSL.Util.Ordinalizer.prototype.format = function (num, gender) {
}
} else {
if (!gender) {
+ // XXX hack to prevent crash on CSL 1.0 styles.
+ // Reported by Carles.
gender = undefined;
}
this.state.fun.ordinalizer.init();
@@ -14516,7 +19099,9 @@ CSL.Util.Ordinalizer.prototype.format = function (num, gender) {
str = str += suffix;
return str;
};
+
CSL.Util.Romanizer = function () {};
+
CSL.Util.Romanizer.prototype.format = function (num) {
var ret, pos, n, numstr, len;
ret = "";
@@ -14533,13 +19118,29 @@ CSL.Util.Romanizer.prototype.format = function (num) {
}
return ret;
};
+
+
+/**
+ * Create a suffix formed from a list of arbitrary characters of arbitrary length.
+ *
This is a lot harder than it seems.
+ */
CSL.Util.Suffixator = function (slist) {
if (!slist) {
slist = CSL.SUFFIX_CHARS;
}
this.slist = slist.split(",");
};
+
+/**
+ * The format method.
+ *
This method is used in generating ranges. Every numeric
+ * formatter (of which Suffixator is one) must be an instantiated
+ * object with such a "format" method.
+ */
+
CSL.Util.Suffixator.prototype.format = function (N) {
+ // Many thanks to Avram Lyon for this code, and good
+ // riddance to the several functions that it replaces.
var X;
N += 1;
var key = "";
@@ -14550,9 +19151,61 @@ CSL.Util.Suffixator.prototype.format = function (N) {
} while ( N !== 0 );
return key;
};
+
+
CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
+ //print("** processNumber() ItemObject[variable]="+ItemObject[variable]);
var val;
+
var me = this;
+
+ var fullformAnd = ",\\s+and\\s+|\\s+and\\s+";
+ if (this.opt.lang.slice(0, 2) !== "en") {
+ fullformAnd += "|,\\s+" + this.getTerm("and") + "\\s+|\\s+" + this.getTerm("and") + "\\s+";
+ }
+ var symbolAnd = "\\s*&\\s*";
+ var andRex = new RegExp("^" + symbolAnd+ "$");
+ var joinerMatchRex = new RegExp("(" + symbolAnd + "|" + fullformAnd + "|;\\s+|,\\s+|\\s*\\\\*[\\-\\u2013]+\\s*)", "g");
+ var joinerSplitRex = new RegExp("(?:" + symbolAnd + "|" + fullformAnd + "|;\\s+|,\\s+|\\s*\\\\*[\\-\\u2013]+\\s*|\\s*&\\s*)");
+
+ // This guesses whether the symbol form is defined or not.
+ // It's the best we can do, because when locales are built, all of the
+ // holes are filled explictly with fallback values: the symbol form is never undefined.
+ var localeAnd = this.getTerm("and");
+ var localeAmpersand = this.getTerm("and", "symbol");
+ 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
+ // XXXX method also used here. Keep code for each action in one place,
+ // XXXX to prevent debugging from becoming a nightmare.
+
+ // The capture pattern below would apply affixes to all sub-elements,
+ // which is not what we want. Sub-elements should nest within, or
+ // affixes should be edited. The latter is probably easier to handle.
+
+ // values = [
+ // {
+ // label: "sec.",
+ // label-form: "plural",
+ // value: 100,
+ // styling: [object],
+ // numeric: true
+ // joiningSuffix: " & ",
+ // },
+ // {
+ // label: "sec.",
+ // label-form: "none",
+ // value: 103,
+ // styling: [object],
+ // numeric: true,
+ // joiningSuffix: ""
+ // }
+ // ]
+
function normalizeFieldValue(str) {
str = str.trim();
var m = str.match(/^([^ ]+)/);
@@ -14573,12 +19226,16 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
}
return str;
}
+
+
function composeNumberInfo(origLabel, label, val, joiningSuffix) {
joiningSuffix = joiningSuffix ? joiningSuffix : "";
var info = {};
+
if (!label && !CSL.STATUTE_SUBDIV_STRINGS_REVERSE[variable]) {
label = "var:"+variable;
}
+
if (label) {
var m = label.match(/(\s*)([^\s]+)(\s*)/);
info.label = m[2];
@@ -14587,7 +19244,9 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
info.plural = 0;
info.labelVisibility = false;
}
+
var m = val.match(/^([0-9]*[a-zA-Z]+0*)?([0-9]+(?:[a-zA-Z]*|[-,a-zA-Z]+))$/);
+ //var m = val.match(/^([0-9]*[a-zA-Z]0*)([0-9]+(?:[a-zA-Z]*|[-,a-zA-Z]+))$/);
if (m) {
info.particle = m[1] ? m[1] : "";
info.value = m[2];
@@ -14598,7 +19257,10 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
info.joiningSuffix = joiningSuffix.replace(/\s*-\s*/, "-");
return info;
}
+
function fixupSubsections(elems) {
+ // This catches things like p. 12a-c, recombining content to yield
+ // numeric true despite the hyphen.
for (var i=elems.length-2;i>-1;i-=2) {
if (elems[i] === "-"
&& elems[i-1].match(/^(?:(?:[a-z]|[a-z][a-z]|[a-z][a-z][a-z]|[a-z][a-z][a-z][a-z])\. *)*[0-9]+[,a-zA-Z]+$/)
@@ -14609,16 +19271,26 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
}
return elems;
}
+
function parseString(str, defaultLabel) {
defaultLabel = defaultLabel ? defaultLabel : "";
+
str = normalizeFieldValue(str, defaultLabel);
+
+ // Split chunks and collate delimiters.
var elems = [];
- var m = str.match(/(;\s+|,\s+|\s*\\*[\-\u2013]+\s*|\s*&\s*)/g);
+ var m = str.match(joinerMatchRex);
if (m) {
- var lst = str.split(/(?:;\s+|,\s+|\s*\\*[\-\u2013]+\s*|\s*&\s*)/);
+ for (var i=0, ilen=m.length; i0;j--) {
if (lst[j-1] && (!lst[j].match(/^[0-9]+([-;,:a-zA-Z]*)$/) || !lst[j-1].match(/^[0-9]+([-;,:a-zA-Z]*)$/))) {
lst[j-1] = lst[j-1] + m[j-1] + lst[j];
@@ -14649,6 +19325,7 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
m = m.slice(0,j-1).concat(m.slice(j));
}
}
+ // merge bad leading label into content
if (m.length > 0) {
var slug = m[0].trim();
var notAlabel = !CSL.STATUTE_SUBDIV_STRINGS[slug]
@@ -14664,6 +19341,7 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
origLabel = slug;
}
}
+
for (var j=0,jlen=lst.length; j 1 ? m[j-1] : "";
str = lst[j] ? lst[j].trim() : "";
if (j === (lst.length-1)) {
values.push(composeNumberInfo(filteredOrigLabel, label, str, elems[i+1]));
@@ -14693,13 +19372,16 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
}
return values;
}
+
function setSpaces(values) {
+ // Add space joins (is this really right?)
for (var i=0,ilen=values.length-1;i -1) {
+ // Actually, shouldn't we do this always?
if (!me.getTerm(CSL.STATUTE_SUBDIV_STRINGS[currentLabelInfo.label])) {
values[currentLabelInfo.pos].labelVisibility = true;
}
}
+ // If there is an explicit
+ // label embedded at the start of a field that
+ // does not match the context, it should be
+ // marked for rendering.
if (["locator", "number", "locator-extra"].indexOf(variable) === -1) {
if (CSL.STATUTE_SUBDIV_STRINGS[currentLabelInfo.label] !== variable) {
values[0].labelVisibility = true;
}
}
} else {
+ // Also, mark initial mid-field labels for
+ // rendering.
+ //if (values[i-1].label !== values[i].label && currentLabelInfo.label.slice(0, 4) !== "var:") {
values[currentLabelInfo.pos].labelVisibility = true;
+ //}
}
}
}
+
function setPluralsAndNumerics(values) {
if (values.length === 0) {
return;
}
var groupStartPos = 0;
var groupCount = 1;
+
for (var i=1,ilen=values.length;i-1;j--) {
+ // if (masterNode.decorations[j][0] === "@quotes") {
+ // // Add to styling
+ // masterStyling.decorations = masterStyling.decorations.concat(masterNode.decorations.slice(j, j+1));
+ // // Remove from node
+ // masterNode.decorations = masterNode.decorations.slice(0, j).concat(masterNode.decorations.slice(j+1));
+ // }
+ //}
masterStyling.strings.prefix = masterNode.strings.prefix;
masterNode.strings.prefix = "";
masterStyling.strings.suffix = masterNode.strings.suffix;
@@ -14812,6 +19518,7 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
if (values.length) {
for (var i=0,ilen=values.length; i -1) {
@@ -14846,10 +19554,12 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
}
return ret;
}
+
function checkPage(variable, val) {
return variable === "page"
|| (["locator", "locator-extra"].indexOf(variable) > -1 && (["p."].indexOf(val.label) > -1 || ["p."].indexOf(val.origLabel) > -1));
}
+
function fixupRangeDelimiter(variable, val, rangeDelimiter, isNumeric) {
var isPage = checkPage(variable, val);
var hasTerm = checkTerm(variable, val);
@@ -14869,8 +19579,12 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
}
}
}
+ //if (rangeDelimiter === "\\-") {
+ // rangeDelimiter = "-";
+ //}
return rangeDelimiter;
}
+
function manglePageNumbers(values, i, currentInfo) {
if (i<1) {
return;
@@ -14890,18 +19604,21 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
return;
}
var val = values[i];
+
var isPage = checkPage(variable, val);
var str;
if (isPage && !isNaN(parseInt(values[i-1].value)) && !isNaN(parseInt(values[i].value))) {
str = values[i-1].particle + values[i-1].value + " - " + values[i].particle + values[i].value;
str = me.fun.page_mangler(str);
} else {
+ // if (("" + values[i-1].value).match(/[0-9]$/) && ("" + values[i].value).match(/^[0-9]/)) {
if (("" + values[i-1].value).match(/^([0-9]+|[ivxlcmIVXLCM]+)$/) && ("" + values[i].value).match(/^([0-9]+|[ivxlcmIVXLCM]+)$/)) {
values[i-1].joiningSuffix = me.getTerm("page-range-delimiter");
}
str = values[i-1].value + stripHyphenBackslash(values[i-1].joiningSuffix) + values[i].value;
}
var m = str.match(/^((?:[0-9]*[a-zA-Z]+0*))?([0-9]+[a-z]*)(\s*[^0-9]+\s*)([-,a-zA-Z]?0*)([0-9]+[a-z]*)$/);
+ // var m = str.match(/^((?:[0-9]*[a-zA-Z]+0*))?([0-9]+[a-z]*)(\s*[^0-9]+\s*)([-,a-zA-Z]?0*)([0-9]+[a-z]*)$/);
if (m) {
var rangeDelimiter = m[3];
rangeDelimiter = fixupRangeDelimiter(variable, val, rangeDelimiter, values[i].numeric);
@@ -14913,18 +19630,22 @@ CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) {
}
currentInfo.count = 0;
}
+
function fixRanges(values) {
+
if (!node) {
return;
}
if (["page", "page-first", "chapter-number", "collection-number", "edition", "issue", "number", "number-of-pages", "number-of-volumes", "volume", "locator", "locator-extra"].indexOf(variable) === -1) {
return;
}
+
var currentInfo = {
count: 0,
label: null,
lastHadRangeDelimiter: false
};
+
for (var i=0,ilen=values.length; i": {
type: "nocase",
@@ -15360,22 +20190,29 @@ CSL.Util.FlipFlopper = function(state) {
}
}
};
+
_nestingData["(\""] = _nestingData[" \""];
_nestingData["(\'"] = _nestingData[" \'"];
+
var localeOpenQuote = state.getTerm("open-quote");
var localeCloseQuote = state.getTerm("close-quote");
var localeOpenInnerQuote = state.getTerm("open-inner-quote");
var localeCloseInnerQuote = state.getTerm("close-inner-quote");
+
+ // If locale uses straight quotes, do not register them. All will be well.
+ // Otherwise, clone straight-quote data, and adjust.
if (localeOpenQuote && localeCloseQuote && [" \""," \'","\"","\'"].indexOf(localeOpenQuote) === -1) {
_nestingData[localeOpenQuote] = JSON.parse(JSON.stringify(_nestingData[" \""]));
_nestingData[localeOpenQuote].opener = localeOpenQuote;
_nestingData[localeOpenQuote].closer = localeCloseQuote;
}
+
if (localeOpenInnerQuote && localeCloseInnerQuote && [" \""," \'","\"","\'"].indexOf(localeOpenInnerQuote) === -1) {
_nestingData[localeOpenInnerQuote] = JSON.parse(JSON.stringify(_nestingData[" \'"]));
_nestingData[localeOpenInnerQuote].opener = localeOpenInnerQuote;
_nestingData[localeOpenInnerQuote].closer = localeCloseInnerQuote;
}
+
function _setOuterQuoteForm(quot) {
var flip = {
" \'": " \"",
@@ -15386,6 +20223,7 @@ CSL.Util.FlipFlopper = function(state) {
_nestingData[quot].outer = "true";
_nestingData[flip[quot]].outer = "inner";
}
+
function _getNestingOpenerParams(opener) {
var openers = [];
var keys = Object.keys(_nestingData);
@@ -15401,6 +20239,7 @@ CSL.Util.FlipFlopper = function(state) {
}).join("|") + ")");
return ret;
}
+
var _nestingParams = (function() {
var ret = {};
var keys = Object.keys(_nestingData);
@@ -15410,6 +20249,7 @@ CSL.Util.FlipFlopper = function(state) {
}
return ret;
}());
+
var _tagRex = (function() {
var openers = [];
var closers = [];
@@ -15423,9 +20263,11 @@ CSL.Util.FlipFlopper = function(state) {
var closer = keys[i];
closers.push(closer);
}
+
var all = openers.concat(closers).map(function(str){
return str.replace("(", "\\(");
}).join("|");
+
return {
matchAll: new RegExp("((?:" + all + "))", "g"),
splitAll: new RegExp("(?:" + all + ")", "g"),
@@ -15435,6 +20277,7 @@ CSL.Util.FlipFlopper = function(state) {
close: new RegExp("(^(?:" + closers.join("|") + ")$)"),
};
}());
+
function _tryOpen(tag, pos) {
var params = _nestingState[_nestingState.length - 1];
if (!params || tag.match(params.opener)) {
@@ -15458,6 +20301,7 @@ CSL.Util.FlipFlopper = function(state) {
};
}
}
+
function _tryClose(tag, pos) {
var params = _nestingState[_nestingState.length - 1];
if (params && tag === params.closer) {
@@ -15484,6 +20328,7 @@ CSL.Util.FlipFlopper = function(state) {
}
}
}
+
function _pushNestingState(tag, pos) {
if (tag.match(_tagRex.open)) {
return _tryOpen(tag, pos);
@@ -15491,13 +20336,17 @@ CSL.Util.FlipFlopper = function(state) {
return _tryClose(tag, pos);
}
}
+
function _nestingFix (tag, pos) {
return _pushNestingState(tag, pos);
}
+
function _doppelString(str) {
var forcedSpaces = [];
+ // Normalize markup
str = str.replace(/(]*(>)/g, "$1 $2$3;\"$4");
str = str.replace(/(]*(>)/g, "$1 $2$3");
+
var match = str.match(_tagRex.matchAll);
if (!match) {
return {
@@ -15507,6 +20356,7 @@ CSL.Util.FlipFlopper = function(state) {
};
}
var split = str.split(_tagRex.splitAll);
+
for (var i=0,ilen=match.length-1;i -1) {
@@ -15523,6 +20373,7 @@ CSL.Util.FlipFlopper = function(state) {
forcedSpaces: forcedSpaces
};
}
+
var TagReg = function(blob) {
var _stack = [];
this.set = function (tag) {
@@ -15566,6 +20417,7 @@ CSL.Util.FlipFlopper = function(state) {
_stack.pop();
};
};
+
function _apostropheForce(tag, str) {
if (tag === "\'") {
if (str && str.match(/^[^\,\.\?\:\;\ ]/)) {
@@ -15576,6 +20428,7 @@ CSL.Util.FlipFlopper = function(state) {
}
return false;
}
+
function _undoppelToQueue(blob, doppel, leadingSpace) {
var firstString = true;
var tagReg = new TagReg(blob);
@@ -15604,6 +20457,11 @@ CSL.Util.FlipFlopper = function(state) {
var tok = new CSL.Token();
var newblob = new CSL.Blob(null, tok);
newblob.alldecor = this.latest.alldecor.slice();
+
+ // AHA! Bad naming. There is _decorset from the list, and
+ // there WAS decorset that we are building. Dumb. Fix the
+ // names and fix it up.
+
if (decor[0] === "@class" && decor[1] === "nodecor") {
var newdecorset = [];
var seen = {};
@@ -15617,6 +20475,7 @@ CSL.Util.FlipFlopper = function(state) {
var _olddecor = _decorset[j];
if (["@font-weight", "@font-style", "@font-variant"].indexOf(_olddecor[0]) > -1
&& !seen[_olddecor[0]]) {
+
if (decor[1] !== "normal") {
newblob.decorations.push([_olddecor[0], "normal"]);
newdecorset.push([_olddecor[0], "normal"]);
@@ -15626,6 +20485,7 @@ CSL.Util.FlipFlopper = function(state) {
}
}
newblob.alldecor.push(newdecorset);
+
} else {
newblob.decorations.push(decor);
newblob.alldecor.push([decor]);
@@ -15674,6 +20534,11 @@ CSL.Util.FlipFlopper = function(state) {
}
}
}
+
+ /**
+ * PUBLIC
+ */
+
this.processTags = function (blob) {
var str = blob.blobs;
var leadingSpace = false;
@@ -15687,6 +20552,11 @@ CSL.Util.FlipFlopper = function(state) {
return;
}
var quoteFormSeen = false;
+ // ZZZ
+ // It is inside THIS loop that we can convert the nocase and nodecor
+ // tags and companion spans to string
+ // Um. Maybe. Or maybe it needs to happen inside _nestingFix() somewhere.
+
for (var i=0,ilen=doppel.tags.length;i -1) {
if (tag.match(_tagRex.close)
&& tag === "\'") {
+
doppel.strings[i+1] = "\u2019" + doppel.strings[i+1];
doppel.tags[i] = "";
} else {
@@ -15737,10 +20608,12 @@ CSL.Util.FlipFlopper = function(state) {
}
}
}
+ // Stray tags are neutralized here
for (var i=_nestingState.length-1;i>-1;i--) {
var tagPos = _nestingState[i].pos;
var tag = doppel.tags[tagPos];
if (tag === " \'" || tag === "\'") {
+
doppel.strings[tagPos+1] = " \u2019" + doppel.strings[tagPos+1];
} else {
doppel.strings[tagPos+1] = doppel.tags[tagPos] + doppel.strings[tagPos+1];
@@ -15755,6 +20628,8 @@ CSL.Util.FlipFlopper = function(state) {
doppel.strings = doppel.strings.slice(0,i+1).concat(doppel.strings.slice(i+2));
}
}
+ // Sniff initial (outer) quote form (single or double) and configure parser
+ // Also add leading spaces.
for (var i=0,ilen=doppel.tags.length;i||<\/span>|<\/?(?:i|sc|b|sub|sup)>)";
var tagDoppel = new CSL.Doppeler(rexStr, function(str) {
return str.replace(/(]*(>)/g, "$1 $2$3").replace(/(]*(>)/g, "$1 $2 $3;$4$5");
});
+
var wordDoppel = new CSL.Doppeler("(?:[\u0020\u00A0\u2000-\u200B\u205F\u3000]+)");
+
+ /**
+ * INTERNAL
+ */
+
var _tagParams = {
"": "",
"": "",
@@ -15785,13 +20671,19 @@ CSL.Output.Formatters = (function () {
"": "",
"": ""
};
+
function _capitalise (word) {
+ // Weird stuff is (.) transpiled with regexpu
+ // https://github.com/mathiasbynens/regexpu
var m = word.match(/(^\s*)((?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))(.*)/);
+ // Do not uppercase lone Greek letters
+ // (No case transforms in Greek citations, but chars used in titles to science papers)
if (m && !(m[2].match(/^[\u0370-\u03FF]$/) && !m[3])) {
return m[1] + m[2].toUpperCase() + m[3];
}
return word;
}
+
function _textcaseEngine(config, string) {
if (!string) {
return "";
@@ -15855,30 +20747,41 @@ CSL.Output.Formatters = (function () {
return pushQuoteState(m[1], positions);
}
}
+ // Run state machine
if (config.doppel.strings.length && config.doppel.strings[0].trim()) {
config.doppel.strings[0] = config.capitaliseWords(config.doppel.strings[0], 0, config.doppel.tags[0]);
}
+
for (var i=0,ilen=config.doppel.tags.length;icapitalize_first, but force the
+ * subsequent characters to lowercase.
+ */
function sentence(state, string) {
var config = {
quoteState: [],
@@ -15985,6 +20916,7 @@ CSL.Output.Formatters = (function () {
};
return _textcaseEngine(config, string);
}
+
function title(state, string) {
var config = {
quoteState: [],
@@ -15999,12 +20931,15 @@ CSL.Output.Formatters = (function () {
continue;
}
if (word.length > 1 && !word.toLowerCase().match(config.skipWordsRex)) {
+ // Capitalize every word that is not a stop-word
words[j] = _capitalise(words[j]);
} else if (j === (words.length - 1) && followingTag === "-") {
words[j] = _capitalise(words[j]);
} else if (config.isFirst) {
+ // Capitalize first word, even if a stop-word
words[j] = _capitalise(words[j]);
} else if (config.afterPunct) {
+ // Capitalize after punctuation
words[j] = _capitalise(words[j]);
}
config.afterPunct = false;
@@ -16025,6 +20960,12 @@ CSL.Output.Formatters = (function () {
};
return _textcaseEngine(config, string);
}
+
+
+ /**
+ * Force capitalization of the first letter in the string, leave
+ * the rest of the characters untouched.
+ */
function capitalizeFirst(state, string) {
var config = {
quoteState: [],
@@ -16049,6 +20990,13 @@ CSL.Output.Formatters = (function () {
};
return _textcaseEngine(config, string);
}
+
+ /**
+ * Force the first letter of each space-delimited
+ * word in the string to uppercase, and leave the remainder
+ * of the string untouched. Single characters are forced
+ * to uppercase.
+ */
function capitalizeAll (state, string) {
var config = {
quoteState: [],
@@ -16079,9 +21027,33 @@ CSL.Output.Formatters = (function () {
"capitalize-all": capitalizeAll
};
}());
+
+/*global CSL: true */
+
+
+/**
+ * Output specifications.
+ * @class
+ */
CSL.Output.Formats = function () {};
+
+/**
+ * HTML output format specification.
+ *
The headline says it all. The source code for this
+ * object can be used as a template for producing other
+ * output modes.
+ */
CSL.Output.Formats.prototype.html = {
+ //
+ // text_escape: Format-specific function for escaping text destined
+ // for output. Takes the text to be escaped as sole argument. Function
+ // will be run only once across each portion of text to be escaped, it
+ // need not be idempotent.
+ //
"text_escape": function (text) {
+ // Numeric entities, in case the output is processed as
+ // xml in an environment in which HTML named entities are
+ // not declared.
if (!text) {
text = "";
}
@@ -16091,6 +21063,7 @@ CSL.Output.Formats.prototype.html = {
.replace(/\s\s/g, "\u00A0 ")
.replace(CSL.SUPERSCRIPTS_REGEXP,
function(aChar) {
+ // return "<sup>" + CSL.SUPERSCRIPTS[aChar] + "</sup>";
return "" + CSL.SUPERSCRIPTS[aChar] + "";
});
},
@@ -16120,15 +21093,34 @@ CSL.Output.Formats.prototype.html = {
},
"@quotes/inner": function (state, str) {
if ("undefined" === typeof str) {
+ //
+ // Mostly right by being wrong (for apostrophes)
+ //
return "\u2019";
}
return state.getTerm("open-inner-quote") + str + state.getTerm("close-inner-quote");
},
"@quotes/false": false,
+ //"@bibliography/body": function (state,str){
+ // return "
\n"+str+"
";
+ //},
"@cite/entry": function (state, str) {
return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt);
},
"@bibliography/entry": function (state, str) {
+ // Test for this.item_id to add decorations to
+ // bibliography output of individual entries.
+ //
+ // Full item content can be obtained from
+ // state.registry.registry[id].ref, using
+ // CSL variable keys.
+ //
+ // Example:
+ //
+ // print(state.registry.registry[this.item_id].ref["title"]);
+ //
+ // At present, for parallel citations, only the
+ // id of the master item is supplied on this.item_id.
var insert = "";
if (state.sys.embedBibliographyEntry) {
insert = state.sys.embedBibliographyEntry(this.item_id) + "\n";
@@ -16182,7 +21174,20 @@ CSL.Output.Formats.prototype.html = {
return "" + str + "";
}
};
+
+/**
+ * Plain text output specification.
+ *
+ * (Code contributed by Simon Kornblith, Center for History and New Media,
+ * George Mason University.)
+ */
CSL.Output.Formats.prototype.text = {
+ //
+ // text_escape: Format-specific function for escaping text destined
+ // for output. Takes the text to be escaped as sole argument. Function
+ // will be run only once across each portion of text to be escaped, it
+ // need not be idempotent.
+ //
"text_escape": function (text) {
if (!text) {
text = "";
@@ -16215,11 +21220,17 @@ CSL.Output.Formats.prototype.text = {
},
"@quotes/inner": function (state, str) {
if ("undefined" === typeof str) {
+ //
+ // Mostly right by being wrong (for apostrophes)
+ //
return "\u2019";
}
return state.getTerm("open-inner-quote") + str + state.getTerm("close-inner-quote");
},
"@quotes/false": false,
+ //"@bibliography/body": function (state,str){
+ // return "
\n"+str+"
";
+ //},
"@cite/entry": function (state, str) {
return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt);
},
@@ -16248,7 +21259,20 @@ CSL.Output.Formats.prototype.text = {
return str;
}
};
+
+/**
+ * Plain text output specification.
+ *
+ * (Code contributed by Simon Kornblith, Center for History and New Media,
+ * George Mason University.)
+ */
CSL.Output.Formats.prototype.rtf = {
+ //
+ // text_escape: Format-specific function for escaping text destined
+ // for output. Takes the text to be escaped as sole argument. Function
+ // will be run only once across each portion of text to be escaped, it
+ // need not be idempotent.
+ //
"text_escape": function (text) {
if (!text) {
text = "";
@@ -16296,6 +21320,8 @@ CSL.Output.Formats.prototype.rtf = {
"bibend":"}",
"@display/block": "\\line{}%%STRING%%\\line\r\n",
"@cite/entry": function (state, str) {
+ // If wrapCitationEntry does not exist, cite/entry
+ // is not applied.
return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt);
},
"@bibliography/entry": function(state,str){
@@ -16335,6 +21361,26 @@ CSL.Output.Formats.prototype.rtf = {
return str;
}
};
+
+/*
+
+ This does not seem to work in Zotero plugins. For some reason the scope of the link does not
+ close when interpreted by the LibreOffice. Perhaps this creates a field within a field,
+ and that is not allowed?
+
+ "@URL/true": function (state, str) {
+ return "\\field{\\*\\fldinst{HYPERLINK \"" + str + "\"}}{\\fldrslt{"+ str +"}}";
+ },
+ "@DOI/true": function (state, str) {
+ return "\\field{\\*\\fldinst{HYPERLINK \"https://doi.org/" + str + "\"}}{\\fldrslt{"+ str +"}}";
+ }
+*/
+
+/**
+ * AsciiDoc output specification.
+ *
+ * See http://asciidoc.org/ or https://asciidoctor.org/
+ */
CSL.Output.Formats.prototype.asciidoc = {
"text_escape": function (text) {
if (!text) {
@@ -16383,6 +21429,7 @@ CSL.Output.Formats.prototype.asciidoc = {
},
"@quotes/false": false,
"@cite/entry": function (state, str) {
+ // if wrapCitationEntry does not exist, cite/entry is not applied
return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt);
},
"@bibliography/entry": function (state, str) {
@@ -16419,6 +21466,7 @@ CSL.Output.Formats.prototype.asciidoc = {
}
},
"@URL/true": function (state, str) {
+ // AsciiDoc renders URLs automatically as links
return str;
},
"@DOI/true": function (state, str) {
@@ -16429,6 +21477,13 @@ CSL.Output.Formats.prototype.asciidoc = {
return doiurl + "[" + str + "]";
}
};
+
+/**
+ * Output specification for XSL-FO (Extensible Stylesheet
+ * Language - Formatting Objects)
+ *
+ * See https://www.w3.org/TR/xsl11/#fo-section
+ */
CSL.Output.Formats.prototype.fo = {
"text_escape": function (text) {
if (!text) {
@@ -16541,6 +21596,12 @@ CSL.Output.Formats.prototype.fo = {
return "" + str + "";
}
};
+
+/**
+ * LaTeX .bbl output.
+ *
+ * (Code contributed by Egon Willighagen, based on the prototype.text code.)
+ */
CSL.Output.Formats.prototype.latex = {
"text_escape": function (text) {
if (!text) {
@@ -16574,11 +21635,17 @@ CSL.Output.Formats.prototype.latex = {
},
"@quotes/inner": function (state, str) {
if ("undefined" === typeof str) {
+ //
+ // Mostly right by being wrong (for apostrophes)
+ //
return "\u2019";
}
return state.getTerm("open-inner-quote") + str + state.getTerm("close-inner-quote");
},
"@quotes/false": false,
+ //"@bibliography/body": function (state,str){
+ // return "
\n"+str+"
";
+ //},
"@cite/entry": function (state, str) {
return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt);
},
@@ -16607,7 +21674,67 @@ CSL.Output.Formats.prototype.latex = {
return str;
}
};
+
CSL.Output.Formats = new CSL.Output.Formats();
+
+/*global CSL: true */
+
+
+//
+// Time for a rewrite of this module.
+//
+// Simon has pointed out that list and hash behavior can
+// be obtained by ... just using a list and a hash. This
+// is faster for batched operations, because sorting is
+// greatly optimized. Since most of the interaction
+// with plugins at runtime will involve batches of
+// references, there will be solid gains if the current,
+// one-reference-at-a-time approach implemented here
+// can be replaced with something that leverages the native
+// sort method of the Array() type.
+//
+// That's going to take some redesign, but it will simplify
+// things in the long run, so it might as well happen now.
+//
+// We'll keep makeCitationCluster and makeBibliography as
+// simple methods that return a string. Neither should
+// have any effect on internal state. This will be a change
+// in behavior for makeCitationCluster.
+//
+// A new updateItems command will be introduced, to replace
+// insertItems. It will be a simple list of IDs, in the
+// sequence of first reference in the document.
+//
+// The calling application should always invoke updateItems
+// before makeCitationCluster.
+//
+
+//
+// should allow batched registration of items by
+// key. should behave as an update, with deletion
+// of items and the tainting of disambiguation
+// partner sets affected by a deletes and additions.
+//
+//
+// we'll need a reset method, to clear the decks
+// in the citation area and start over.
+
+/**
+ * Registry of cited items.
+ *
This is a persistent store of disambiguation and
+ * sort order information relating to individual items
+ * for which rendering is requested. Item data is stored
+ * in a hash, with the item key as hash key, for quick
+ * retrieval. A virtual sequence within the hashed store
+ * is maintained on the fly as items are added to the
+ * store, using *_next and *_prev
+ * attributes on each item. A separate hash of items
+ * based on their undisambiguated cite form is
+ * maintained, and the item id list and disambiguation
+ * level for each set of disambiguation partners is shared
+ * through the registry item.
+ * @class
+ */
CSL.Registry = function (state) {
this.debug = false;
this.state = state;
@@ -16616,7 +21743,12 @@ CSL.Registry = function (state) {
this.refhash = {};
this.namereg = new CSL.Registry.NameReg(state);
this.citationreg = new CSL.Registry.CitationReg(state);
+ // See CSL.NameOutput.prototype.outputNames
+ // and CSL.Registry.prototype.doinserts
this.authorstrings = {};
+
+ //
+ // shared scratch vars
this.mylist = [];
this.myhash = {};
this.deletes = [];
@@ -16626,9 +21758,15 @@ CSL.Registry = function (state) {
this.akeys = {};
this.oldseq = {};
this.return_data = {};
+ //
+ // each ambig is a list of the ids of other objects
+ // that have the same base-level rendering
this.ambigcites = {};
this.ambigresets = {};
this.sorter = new CSL.Registry.Comparifier(state, "bibliography_sort");
+ //this.modes = CSL.getModes.call(this.state);
+ //this.checkerator = new CSL.Checkerator();
+
this.getSortedIds = function () {
var ret = [];
for (var i = 0, ilen = this.reflist.length; i < ilen; i += 1) {
@@ -16636,6 +21774,7 @@ CSL.Registry = function (state) {
}
return ret;
};
+
this.getSortedRegistryItems = function () {
var ret = [];
for (var i = 0, ilen = this.reflist.length; i < ilen; i += 1) {
@@ -16644,10 +21783,79 @@ CSL.Registry = function (state) {
return ret;
};
};
+
+//
+// Here's the sequence of operations to be performed on
+// update:
+//
+// 1. (o) [init] Receive list as function argument, store as hash and as list.
+// 2. (o) [init] Initialize refresh list. Never needs sorting, only hash required.
+
+// 3. (o) [dodeletes] Delete loop.
+// 3a. (o) [dodeletes] Delete names in items to be deleted from names reg.
+// 3b. (o) [dodeletes] Complement refreshes list with items affected by
+// possible name changes. We'll actually perform the refresh once
+// all of the necessary data and parameters have been established
+// in the registry.
+// 3c. (o) [dodeletes] Delete all items to be deleted from their disambig pools.
+// 3d. (o) [dodeletes] Delete all items in deletion list from hash.
+
+// 4. (o) [doinserts] Insert loop.
+// 4a. (o) [doinserts] Retrieve entries for items to insert.
+// 4b. (o) [doinserts] Generate ambig key.
+// 4c. (o) [doinserts] Add names in items to be inserted to names reg
+// (implicit in getAmbiguousCite).
+// 4d. (o) [doinserts] Record ambig pool key on akey list (used for updating further
+// down the chain).
+// 4e. (o) [doinserts] Create registry token.
+// 4f. (o) [doinserts] Add item ID to hash.
+// 4g. (o) [doinserts] Set and record the base token to hold disambiguation
+// results ("disambig" in the object above).
+// 5. (o) [rebuildlist] Create "new" list of hash pointers, in the order given
+// in the argument to the update function.
+// 6. (o) [rebuildlist] Apply citation numbers to new list.
+// 7. (o) [dorefreshes] Refresh items requiring update.
+
+
+
+// 5. (o) [delnames] Delete names in items to be deleted from names reg, and obtain IDs
+// of other items that would be affected by changes around that surname.
+// 6. (o) [delnames] Complement delete and insert lists with items affected by
+// possible name changes.
+// 7. (o) [delambigs] Delete all items to be deleted from their disambig pools.
+// 8. (o) [delhash] Delete all items in deletion list from hash.
+
+// 9. (o) [addtohash] Retrieve entries for items to insert.
+// 10. (o) [addtohash] Add items to be inserted to their disambig pools.
+// 11. (o) [addtohash] Add names in items to be inserted to names reg
+// (implicit in getAmbiguousCite).
+// 12. (o) [addtohash] Create registry token for each item to be inserted.
+// 13. (o) [addtohash] Add items for insert to hash.
+
+// 14. (o) [buildlist] Create "new" list of hash pointers, in the order given in the argument
+// to the update function.
+// 15. (o) [renumber] Apply citation numbers to new list.
+// 16. (o) [setdisambigs] Set disambiguation parameters on each inserted item token.
+// 17. (o) [setsortkeys] Set sort keys on each item token.
+// 18. (o) [sorttokens] Resort token list
+// 19. (o) [renumber] Reset citation numbers on list items
+//
+
CSL.Registry.prototype.init = function (itemIDs, uncited_flag) {
var i, ilen;
this.oldseq = {};
+ // 1. Receive list as function argument, store as hash and as list.
+ //
+ // Result:
+ // this.mylist: a list of all itemIDs of referenced items, cited and uncited.
+ // this.myhash: a hash of index positions in this.mylist.
+ // this.uncited: hash of uncited itemIDs.
+ //
+ // Proceed as follows.
+ //
if (uncited_flag) {
+ // If uncited_flag is non-nil, add any missing itemIDs to this.mylist
+ // from itemIDs input list, and set the itemIDs in itemIDs on this.uncited.
this.uncited = {};
for (var i=0,ilen=itemIDs.length;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]]) {
delete this.myhash[this.mylist[i]];
@@ -16687,6 +21904,7 @@ CSL.Registry.prototype.dopurge = function (myhash) {
}
this.dodeletes(this.myhash);
};
+
CSL.Registry.prototype.dodeletes = function (myhash) {
var otheritems, key, ambig, pos, len, items, kkey, mypos, id;
if ("string" === typeof myhash) {
@@ -16694,15 +21912,31 @@ CSL.Registry.prototype.dodeletes = function (myhash) {
myhash = {};
myhash[key] = true;
}
+ //
+ // 3. Delete loop.
+ //
for (var key in this.registry) {
if (!myhash[key]) {
+ // skip items explicitly marked as uncited
if (this.uncited[key]) {
continue;
}
+ //
+ // 3a. Delete names in items to be deleted from names reg.
+ //
otheritems = this.namereg.delitems(key);
+ //
+ // 3b. Complement refreshes list with items affected by
+ // possible name changes. We'll actually perform the refresh once
+ // all of the necessary data and parameters have been established
+ // in the registry.
+ //
for (kkey in otheritems) {
this.refreshes[kkey] = true;
}
+ //
+ // 3c. Delete all items to be deleted from their disambig pools.
+ //
ambig = this.registry[key].ambig;
mypos = this.ambigcites[ambig].indexOf(key);
if (mypos > -1) {
@@ -16710,11 +21944,21 @@ CSL.Registry.prototype.dodeletes = function (myhash) {
this.ambigcites[ambig] = items.slice(0, mypos).concat(items.slice(mypos+1, items.length));
this.ambigresets[ambig] = this.ambigcites[ambig].length;
}
+ //
+ // XX. What we've missed is to provide an update of all
+ // items sharing the same ambig += -1 the remaining items in
+ // ambigcites. So let's do that here, just in case the
+ // names update above doesn't catch them all.
+ //
len = this.ambigcites[ambig].length;
for (pos = 0; pos < len; pos += 1) {
id = "" + this.ambigcites[ambig][pos];
this.refreshes[id] = true;
}
+ //
+ // 3d-0. Remove parallel id references and realign
+ // parallel ID refs.
+ //
if (this.registry[key].siblings) {
if (this.registry[key].siblings.length == 1) {
var loneSiblingID = this.registry[key].siblings[0];
@@ -16745,31 +21989,63 @@ CSL.Registry.prototype.dodeletes = function (myhash) {
}
}
}
+ //
+ // 3d-1. Remove item from reflist
for (var i=this.reflist.length-1;i>-1;i--) {
if (this.reflist[i].id === key) {
this.reflist = this.reflist.slice(0, i).concat(this.reflist.slice(i+1));
}
}
+ //
+ // 3d. Delete all items in deletion list from hash.
+ //
delete this.registry[key];
delete this.refhash[key];
+
+ // For processCitationCluster()
this.return_data.bibchange = true;
}
}
+ // Disabled. See formats.js for code.
+ // this.state.fun.decorate.items_delete( this.state.output[this.state.opt.mode].tmp, myhash );
};
+
CSL.Registry.prototype.doinserts = function (mylist) {
var item, Item, akey, newitem, abase, i, ilen;
if ("string" === typeof mylist) {
mylist = [mylist];
}
+ //
+ // 4. Insert loop.
+ //
for (var i = 0, ilen = mylist.length; i < ilen; i += 1) {
item = mylist[i];
if (!this.registry[item]) {
+ //
+ // 4a. Retrieve entries for items to insert.
+ //
Item = this.state.retrieveItem(item);
+
+ //
+ // 4b. Generate ambig key.
+ //
+ // AND
+ //
+ // 4c. Add names in items to be inserted to names reg
+ // (implicit in getAmbiguousCite).
+ //
akey = CSL.getAmbiguousCite.call(this.state, Item);
this.ambigsTouched[akey] = true;
+ //
+ // 4d. Record ambig pool key on akey list (used for updating further
+ // down the chain).
+ //
if (!Item.legislation_id) {
this.akeys[akey] = true;
}
+ //
+ // 4e. Create registry token.
+ //
newitem = {
"id": "" + item,
"seq": 0,
@@ -16781,21 +22057,76 @@ CSL.Registry.prototype.doinserts = function (mylist) {
"ref": Item,
"newItem": true
};
+ //
+ //
+ // 4f. Add item ID to hash.
+ //
this.registry[item] = newitem;
+ //
+ // 4f(a). Add first reference note number
+ // (this may be redundant)
if (this.citationreg.citationsByItemId && this.citationreg.citationsByItemId[item]) {
this.registry[item]["first-reference-note-number"] = this.citationreg.citationsByItemId[item][0].properties.noteIndex;
}
+
+ //
+ // 4g. Set and record the base token to hold disambiguation
+ // results ("disambig" in the object above).
+ //
abase = CSL.getAmbigConfig.call(this.state);
this.registerAmbigToken(akey, item, abase);
+
+ //if (!this.ambigcites[akey]){
+ // this.ambigcites[akey] = [];
+ //}
+ //CSL.debug("Run: "+item+"("+this.ambigcites[akey]+")");
+ //if (this.ambigcites[akey].indexOf(item) === -1){
+ // CSL.debug(" Add: "+item);
+ // this.ambigcites[akey].push(item);
+ //}
+ //
+ // 4h. Make a note that this item needs its sort keys refreshed.
+ //
this.touched[item] = true;
+ // For processCitationCluster()
this.return_data.bibchange = true;
}
}
+ // Disabled. See formats.js for code.
+ // this.state.fun.decorate.items_add( this.state.output[this.state.opt.mode].tmp, mylist );
};
+
+/*
+// No longer required.
+CSL.Registry.prototype.douncited = function () {
+ var pos, len;
+ var cited_len = this.mylist.length - this.uncited.length;
+ for (pos = 0, len = cited_len; pos < len; pos += 1) {
+ this.registry[this.mylist[pos]].uncited = false;
+ }
+ for (pos = cited_len, len = this.mylist.length; pos < len; pos += 1) {
+ this.registry[this.mylist[pos]].uncited = true;
+ }
+};
+*/
+
CSL.Registry.prototype.rebuildlist = function (nosort) {
var len, pos, item, Item;
+ //
+ // 5. Create "new" list of hash pointers, in the order given in the argument
+ // to the update function.
+ //
+ //
+ // XXX Keep reflist in place.
+ //
if (!nosort) {
this.reflist_inserts = [];
+ //
+ // 6. Apply citation numbers to new list,
+ // saving off old sequence numbers as we go.
+ //
+ // XXX Just memo inserts -- actual insert happens below, at last "sort"
+ //
len = this.mylist.length;
for (pos = 0; pos < len; pos += 1) {
item = this.mylist[pos];
@@ -16818,8 +22149,25 @@ CSL.Registry.prototype.rebuildlist = function (nosort) {
}
}
};
+
+/*
+ * Okay, at this point we should have a numbered list
+ * of registry tokens in the notional order requested,
+ * with sequence numbers to reconstruct the ordering
+ * if the list is remangled. So far so good.
+ */
+
CSL.Registry.prototype.dorefreshes = function () {
var key, regtoken, Item, akey, abase;
+ //
+ // 7. Refresh items requiring update.
+ //
+ // It looks like we need to do four things on each cite for refresh:
+ // (1) Generate the akey for the cite.
+ // (2) Register it on the ambig token.
+ // (3) Register the akey in this.akeys
+ // (4) Register the item ID in this.touched
+ //
for (var key in this.refreshes) {
regtoken = this.registry[key];
if (!regtoken) {
@@ -16828,6 +22176,7 @@ CSL.Registry.prototype.dorefreshes = function () {
regtoken.sortkeys = undefined;
Item = this.state.refetchItem(key);
var akey = regtoken.ambig;
+
if ("undefined" === typeof akey) {
this.state.tmp.disambig_settings = false;
akey = CSL.getAmbiguousCite.call(this.state, Item);
@@ -16853,25 +22202,56 @@ CSL.Registry.prototype.dorefreshes = function () {
this.touched[key] = true;
}
};
+
+/*
+ * Main disambiguation += -1 can everything for disambiguation be
+ * crunched into this function?
+ */
CSL.Registry.prototype.setdisambigs = function () {
+ //
+ // Okay, more changes. Here is where we resolve all disambiguation
+ // issues for cites touched by the update. The this.ambigcites set is
+ // based on the complete short form of citations, and is the basis on
+ // which names are added and minimal adding of initials or given names
+ // is performed.
+ //
+
+ //
+ // 8. Set disambiguation parameters on each inserted item token.
+ //
for (var akey in this.ambigsTouched) {
+ //
+ // Disambiguation is fully encapsulated.
+ // Disambiguator will run only if there are multiple
+ // items, and at least one disambiguation mode is
+ // in effect.
this.state.disambiguate.run(akey);
}
this.ambigsTouched = {};
this.akeys = {};
};
+
+
+
CSL.Registry.prototype.renumber = function () {
var len, pos, item;
+ //
+ // 19. Reset citation numbers on list items
+ //
if (this.state.bibliography_sort.opt.citation_number_sort_direction === CSL.DESCENDING) {
this.state.bibliography_sort.tmp.citation_number_map = {};
}
len = this.reflist.length;
for (pos = 0; pos < len; pos += 1) {
item = this.reflist[pos];
+ // save the overhead of rerenderings if citation-number is not
+ // used in the style.
item.seq = (pos + 1);
if (this.state.bibliography_sort.opt.citation_number_sort_direction === CSL.DESCENDING) {
this.state.bibliography_sort.tmp.citation_number_map[item.seq] = (this.reflist.length - item.seq + 1);
}
+ // update_mode is set to CSL.NUMERIC if citation-number is rendered
+ // in citations.
if (this.state.opt.update_mode === CSL.NUMERIC && item.seq != this.oldseq[item.id]) {
this.state.tmp.taintedItemIDs[item.id] = true;
}
@@ -16880,19 +22260,26 @@ CSL.Registry.prototype.renumber = function () {
}
}
};
+
CSL.Registry.prototype.setsortkeys = function () {
var key;
+ //
+ // 17. Set sort keys on each item token.
+ //
for (var i = 0, ilen = this.mylist.length; i < ilen; i += 1) {
var key = this.mylist[i];
+ // The last of these conditions may create some thrashing on styles that do not require sorting.
if (this.touched[key] || this.state.tmp.taintedItemIDs[key] || !this.registry[key].sortkeys) {
this.registry[key].sortkeys = CSL.getSortKeys.call(this.state, this.state.retrieveItem(key), "bibliography_sort");
}
}
};
+
CSL.Registry.prototype._insertItem = function(element, array) {
array.splice(this._locationOf(element, array) + 1, 0, element);
return array;
};
+
CSL.Registry.prototype._locationOf = function(element, array, start, end) {
if (array.length === 0) {
return -1;
@@ -16900,6 +22287,7 @@ CSL.Registry.prototype._locationOf = function(element, array, start, end) {
start = start || 0;
end = end || array.length;
var pivot = (start + end) >> 1; // should be faster than dividing by 2
+
var c = this.sorter.compareKeys(element, array[pivot]);
if (end - start <= 1) {
return c == -1 ? pivot - 1 : pivot;
@@ -16910,8 +22298,12 @@ CSL.Registry.prototype._locationOf = function(element, array, start, end) {
case 1: return this._locationOf(element, array, pivot, end);
}
};
+
CSL.Registry.prototype.sorttokens = function (nosort) {
var len, item, Item, pos;
+ //
+ // 18. Resort token list.
+ //
if (!nosort) {
this.reflist_inserts = [];
len = this.mylist.length;
@@ -16922,8 +22314,11 @@ CSL.Registry.prototype.sorttokens = function (nosort) {
this.reflist_inserts.push(Item);
}
}
+ // There is a thin possibility that tainted items in a sorted list
+ // will change position due to disambiguation. We cover for that here.
for (var key in this.state.tmp.taintedItemIDs) {
if (this.registry[key] && !this.registry[key].newItem) {
+ // Move tainted items from reflist to reflist_inserts
for (var i=this.reflist.length-1;i>-1;i--) {
if (this.reflist[i].id === key) {
this.reflist_inserts.push(this.reflist[i]);
@@ -16944,6 +22339,11 @@ CSL.Registry.prototype.sorttokens = function (nosort) {
}
}
};
+
+/**
+ * Compare two sort keys
+ *
Nested, because keys are an array.
+ */
CSL.Registry.Comparifier = function (state, keyset) {
var sort_directions, len, pos, compareKeys;
var sortCompare = CSL.getSortCompare(state.opt["default-locale-sort"]);
@@ -16951,6 +22351,15 @@ CSL.Registry.Comparifier = function (state, keyset) {
this.compareKeys = function (a, b) {
len = a.sortkeys ? a.sortkeys.length : 0;
for (pos = 0; pos < len; pos += 1) {
+ //
+ // for ascending sort 1 uses 1, -1 uses -1.
+ // For descending sort, the values are reversed.
+ //
+ // Need to handle undefined values. No way around it.
+ // So have to screen .localeCompare (which is also
+ // needed) from undefined values. Everywhere, in all
+ // compares.
+ //
var cmp = 0;
if (a.sortkeys[pos] === b.sortkeys[pos]) {
cmp = 0;
@@ -16959,6 +22368,7 @@ CSL.Registry.Comparifier = function (state, keyset) {
} else if ("undefined" === typeof b.sortkeys[pos]) {
cmp = sort_directions[pos][0];
} else {
+ // cmp = a.sortkeys[pos].localeCompare(b.sortkeys[pos]);
cmp = sortCompare(a.sortkeys[pos], b.sortkeys[pos]);
}
if (0 < cmp) {
@@ -16979,6 +22389,13 @@ CSL.Registry.Comparifier = function (state, keyset) {
return compareKeys(a[1], b[1]);
};
};
+
+
+/**
+ * Compare two disambiguation tokens by their registry sort order
+ *
Disambiguation lists need to be sorted this way, to
+ * obtain the correct year-suffix when that's used.
+ */
CSL.Registry.prototype.compareRegistryTokens = function (a, b) {
if (a.seq > b.seq) {
return 1;
@@ -16987,7 +22404,14 @@ CSL.Registry.prototype.compareRegistryTokens = function (a, b) {
}
return 0;
};
+
CSL.Registry.prototype.registerAmbigToken = function (akey, id, ambig_config) {
+ //SNIP-START
+ if (!this.registry[id]) {
+ CSL.debug("Warning: unregistered item: itemID=("+id+"), akey=("+akey+")");
+ }
+ //SNIP-END
+ // Taint if number of names to be included has changed
if (this.registry[id] && this.registry[id].disambig && this.registry[id].disambig.names) {
for (var i = 0, ilen = ambig_config.names.length; i < ilen; i += 1) {
var new_names_params = ambig_config.names[i];
@@ -16995,6 +22419,7 @@ CSL.Registry.prototype.registerAmbigToken = function (akey, id, ambig_config) {
if (new_names_params !== old_names_params) {
this.state.tmp.taintedItemIDs[id] = true;
} else if (ambig_config.givens[i]) {
+ // Compare givenses only if the number of names is aligned.
for (var j=0,jlen=ambig_config.givens[i].length;jThis is used internally by the Registry.
+ */
CSL.getSortKeys = function (Item, key_type) {
var area, root, extension, strip_prepositions, len, pos;
+ //SNIP-START
+ if (false) {
+ CSL.debug("KEY TYPE: " + key_type);
+ }
+ //SNIP-END
area = this.tmp.area;
root = this.tmp.root;
extension = this.tmp.extension;
strip_prepositions = CSL.Util.Sort.strip_prepositions;
this.tmp.area = key_type;
+ // Gawdawful, this.
this.tmp.root = key_type.indexOf("_") > -1 ? key_type.slice(0,-5) : key_type;
this.tmp.extension = "_sort";
this.tmp.disambig_override = true;
@@ -17035,38 +22473,61 @@ CSL.getSortKeys = function (Item, key_type) {
for (pos = 0; pos < len; pos += 1) {
this[key_type].keys[pos] = strip_prepositions(this[key_type].keys[pos]);
}
+ //SNIP-START
+ if (false) {
+ CSL.debug("sort keys (" + key_type + "): " + this[key_type].keys);
+ }
+ //SNIP-END
+
this.tmp.area = area;
this.tmp.root = root;
this.tmp.extension = extension;
return this[key_type].keys;
};
+
+
+/*global CSL: true */
+
CSL.Registry.NameReg = function (state) {
var pkey, ikey, skey, dagopt, gdropt, items, strip_periods, set_keys, evalname, delitems, addname, myitems;
this.state = state;
this.namereg = {};
this.nameind = {};
+ // used for restoring state following preview
this.nameindpkeys = {};
+ //
+ // family, initials form, fullname (with given stripped of periods)
+ //
+ // keys registered, indexed by ID
this.itemkeyreg = {};
+
strip_periods = function (str) {
if (!str) {
str = "";
}
return str.replace(/\./g, " ").replace(/\s+/g, " ").replace(/\s+$/,"");
};
+
set_keys = function (state, itemid, nameobj) {
pkey = strip_periods(nameobj.family);
skey = strip_periods(nameobj.given);
+ // Drop lowercase suffixes (such as et al.) from given name field
+ // for disambiguation purposes.
var m = skey.match(/[,\!]* ([^,]+)$/);
if (m && m[1] === m[1].toLowerCase()) {
skey = skey.replace(/[,\!]* [^,]+$/, "");
}
+ // The %s terminator enables normal initialization behavior
+ // with non-Byzantine names.
ikey = CSL.Util.Names.initializeWith(state, skey, "%s");
if (state.citation.opt["givenname-disambiguation-rule"] === "by-cite") {
pkey = "" + itemid + pkey;
}
};
+
evalname = function (item_id, nameobj, namenum, request_base, form, initials) {
var param;
+ // XXX THIS CAN NO LONGER HAPPEN
if (state.tmp.area.slice(0, 12) === "bibliography" && !form) {
if ("string" === typeof initials) {
return 1;
@@ -17077,6 +22538,16 @@ CSL.Registry.NameReg = function (state) {
var res = state.nameOutput.getName(nameobj, "locale-translit", true);
nameobj = res.name;
set_keys(this.state, "" + item_id, nameobj);
+ //
+ // possible options are:
+ //
+ // (a)
+ // (a)
+ // (b)
+ // (d)
+ // (e)
+ // (g)
+ //
param = 2;
dagopt = state.opt["disambiguate-add-givenname"];
gdropt = state.citation.opt["givenname-disambiguation-rule"];
@@ -17084,15 +22555,25 @@ CSL.Registry.NameReg = function (state) {
if (gdropt === "by-cite") {
gdropt = "all-names";
}
+ //
+ // set initial value
+ //
if ("short" === form) {
param = 0;
} else if ("string" === typeof initials) {
param = 1;
}
+ //
+ // give literals a pass
if ("undefined" === typeof this.namereg[pkey] || "undefined" === typeof this.namereg[pkey].ikey[ikey]) {
return param;
}
+ //
+ // adjust value upward if appropriate -- only if running
+ // a non-names-global disambiguation strategy
+ //
if (gdropt_orig === "by-cite" && param <= request_base) {
+ //param = request_base;
return request_base;
}
if (!dagopt) {
@@ -17101,6 +22582,9 @@ CSL.Registry.NameReg = function (state) {
if ("string" === typeof gdropt && gdropt.slice(0, 12) === "primary-name" && namenum > 0) {
return param;
}
+ //
+ // the last composite condition is for backward compatibility
+ //
if (!gdropt || gdropt === "all-names" || gdropt === "primary-name") {
if (this.namereg[pkey].count > 1) {
param = 1;
@@ -17109,6 +22593,7 @@ CSL.Registry.NameReg = function (state) {
&& this.namereg[pkey].ikey[ikey].count > 1)
|| (this.namereg[pkey].count > 1
&& "string" !== typeof initials)) {
+
param = 2;
}
} else if (gdropt === "all-names-with-initials" || gdropt === "primary-name-with-initials") {
@@ -17128,11 +22613,18 @@ CSL.Registry.NameReg = function (state) {
return param;
}
};
+
+ //
+ // The operation of this function does not show up in the
+ // standard test suite, but it has been hand-tested with
+ // a print trace, and seems to work okay.
+ //
delitems = function (ids) {
var pos, len, posB, id, fullkey;
if ("string" === typeof ids || "number" === typeof ids) {
ids = ["" + ids];
}
+ // ret carries the IDs of other items using this name.
var ret = {};
len = ids.length;
for (pos = 0; pos < len; pos += 1) {
@@ -17146,10 +22638,20 @@ CSL.Registry.NameReg = function (state) {
pkey = key[0];
ikey = key[1];
skey = key[2];
+ // Skip names that have been deleted already.
+ // Needed to clear integration DisambiguateAddGivenname1.txt
+ // and integration DisambiguateAddGivenname2.txt
if ("undefined" === typeof this.namereg[pkey]) {
continue;
}
+
+ // ????
+ //posA = this.namereg[pkey].items.indexOf(posA);
+
items = this.namereg[pkey].items;
+ // This was really, really unperceptive. They key elements
+ // have absolutely nothing to do with whether there was ever
+ // a registration at each key level.
if (skey && this.namereg[pkey].ikey[ikey] && this.namereg[pkey].ikey[ikey].skey[skey]) {
myitems = this.namereg[pkey].ikey[ikey].skey[skey].items;
posB = myitems.indexOf("" + id);
@@ -17200,16 +22702,36 @@ CSL.Registry.NameReg = function (state) {
}
return ret;
};
+ //
+ // Run ALL
+ // renderings with disambiguate-add-givenname set to a value
+ // with the by-cite behaviour, and then set the names-based
+ // expanded form when the final makeCitationCluster rendering
+ // is output. This could be done with a single var set on
+ // the state object in the execution wrappers that run the
+ // style.
+ //
addname = function (item_id, nameobj, pos) {
var i, ilen;
var res = state.nameOutput.getName(nameobj, "locale-translit", true);
nameobj = res.name;
+
if (state.citation.opt["givenname-disambiguation-rule"]
&& state.citation.opt["givenname-disambiguation-rule"].slice(0, 8) === "primary-"
&& pos !== 0) {
return;
}
+ //CSL.debug("INS");
set_keys(this.state, "" + item_id, nameobj);
+ // pkey, ikey and skey should be stored in separate cascading objects.
+ // there should also be a kkey, on each, which holds the item ids using
+ // that form of the name.
+ //
+ // (later note: well, we seem to have slipped a notch here.
+ // Adding lists of IDs all over the place here makes no sense;
+ // the lists need to include _only_ the items currently rendered
+ // at the given level, and the place to do that is in evalname,
+ // and in delnames, not here.)
if (pkey) {
if ("undefined" === typeof this.namereg[pkey]) {
this.namereg[pkey] = {};
@@ -17219,6 +22741,9 @@ CSL.Registry.NameReg = function (state) {
} else if (this.namereg[pkey].items.indexOf(item_id) === -1) {
this.namereg[pkey].items.push(item_id);
}
+// if (this.namereg[pkey].items.indexOf(item_id) === -1) {
+// this.namereg[pkey].items.push(item_id);
+// }
}
if (pkey && ikey) {
if ("undefined" === typeof this.namereg[pkey].ikey[ikey]) {
@@ -17235,6 +22760,9 @@ CSL.Registry.NameReg = function (state) {
} else if (this.namereg[pkey].ikey[ikey].items.indexOf(item_id) === -1) {
this.namereg[pkey].ikey[ikey].items.push(item_id);
}
+// if (this.namereg[pkey].ikey[ikey].items.indexOf(item_id) === -1) {
+// this.namereg[pkey].ikey[ikey].items.push(item_id);
+// }
}
if (pkey && ikey && skey) {
if ("undefined" === typeof this.namereg[pkey].ikey[ikey].skey[skey]) {
@@ -17249,24 +22777,35 @@ CSL.Registry.NameReg = function (state) {
} else if (this.namereg[pkey].ikey[ikey].skey[skey].items.indexOf(item_id) === -1) {
this.namereg[pkey].ikey[ikey].skey[skey].items.push(item_id);
}
+// if (this.namereg[pkey].ikey[ikey].skey[skey].items.indexOf(item_id) === -1) {
+// this.namereg[pkey].ikey[ikey].skey[skey].items.push(item_id);
+// }
}
if ("undefined" === typeof this.nameind[item_id]) {
this.nameind[item_id] = {};
this.nameindpkeys[item_id] = {};
}
+ //CSL.debug("INS-A: [" + pkey + "] [" + ikey + "] [" + skey + "]");
if (pkey) {
this.nameind[item_id][pkey + "::" + ikey + "::" + skey] = true;
this.nameindpkeys[item_id][pkey] = this.namereg[pkey];
}
+ //CSL.debug("INS-B");
};
this.addname = addname;
this.delitems = delitems;
this.evalname = evalname;
};
+
+/*global CSL: true */
+
CSL.Registry.CitationReg = function () {
this.citationById = {};
this.citationByIndex = [];
};
+
+/*global CSL: true */
+
CSL.Disambiguation = function (state) {
this.state = state;
this.sys = this.state.sys;
@@ -17275,22 +22814,39 @@ CSL.Disambiguation = function (state) {
this.configModes();
this.debug = false;
};
+
CSL.Disambiguation.prototype.run = function(akey) {
if (!this.modes.length) {
return;
}
+ //SNIP-START
+ if (this.debug) {
+ print("[A] === RUN ===");
+ }
+ //SNIP-END
this.akey = akey;
if (this.initVars(akey)) {
this.runDisambig();
}
+
};
+
CSL.Disambiguation.prototype.runDisambig = function () {
var ismax;
+ //SNIP-START
+ if (this.debug) {
+ print("[C] === runDisambig() ===");
+ }
+ //SNIP-END
this.initGivens = true;
+ //
+ // Length of list may change during processing
while (this.lists.length) {
this.gnameset = 0;
this.gname = 0;
this.clashes = [1, 0];
+ // each list is scanned repeatedly until all
+ // items either succeed or ultimately fail.
while(this.lists[0][1].length) {
this.listpos = 0;
if (!this.base) {
@@ -17303,28 +22859,57 @@ CSL.Disambiguation.prototype.runDisambig = function () {
this.lists = this.lists.slice(1);
}
};
+
CSL.Disambiguation.prototype.scanItems = function (list) {
var pos, len, otherItem;
+ //SNIP-START
+ if (this.debug) {
+ print("[2] === scanItems() ===");
+ }
+ //SNIP-END
+
this.Item = list[1][0];
this.ItemCite = CSL.getAmbiguousCite.call(this.state, this.Item, this.base, true);
+
this.scanlist = list[1];
this.partners = [];
this.partners.push(this.Item);
this.nonpartners = [];
var clashes = 0;
+
for (var pos = 1, len = list[1].length; pos < len; pos += 1) {
otherItem = list[1][pos];
var otherItemCite = CSL.getAmbiguousCite.call(this.state, otherItem, this.base, true);
+ //SNIP-START
+ if (this.debug) {
+ if (pos > 1) {
+ print(" -----------");
+ }
+ }
+ //SNIP-END
if (this.ItemCite === otherItemCite) {
+ //SNIP-START
+ if (this.debug) {
+ print(" [CLASH]--> "+this.Item.id+": "+this.ItemCite);
+ print(" "+otherItem.id+": "+otherItemCite);
+ }
+ //SNIP-END
clashes += 1;
this.partners.push(otherItem);
} else {
+ //SNIP-START
+ if (this.debug) {
+ print(" [clear]--> "+this.Item.id+": "+this.ItemCite);
+ print(" "+otherItem.id+": "+otherItemCite);
+ }
+ //SNIP-END
this.nonpartners.push(otherItem);
}
}
this.clashes[0] = this.clashes[1];
this.clashes[1] = clashes;
};
+
CSL.Disambiguation.prototype.evalScan = function (maxed) {
this[this.modes[this.modeindex]](maxed);
if (maxed) {
@@ -17335,15 +22920,57 @@ CSL.Disambiguation.prototype.evalScan = function (maxed) {
}
}
};
+
CSL.Disambiguation.prototype.disNames = function (ismax) {
var i, ilen;
+
+ //SNIP-START
+ if (this.debug) {
+ print("[3] == disNames() ==");
+ //print(" partners: "+[this.partners[i].id for (i in this.partners)].join(", "));
+ //print(" nonpartners: "+[this.nonpartners[i].id for (i in this.nonpartners)].join(", "));
+ }
+ //SNIP-END
+
+ // New design
+ // this.base is a forward-only counter. Values are never
+ // reduced, and the counter object is never overwritten.
+ // It is methodically pushed forward in single-unit increments
+ // in incrementDisambig() until disNames() wipes out the list.
+
+ // this.betterbase is cloned from this.base exactly once,
+ // at the start of a disambiguation run. Whenever an operation
+ // results in improvement, the just-incremented elements
+ // identified as this.base.names[this.gnameset] (number of
+ // names)and as this.base.givens[this.gnameset][this.gname]
+ // (level of given name) are copied from this.base.
+
+ // The this.base object is used to control disambiguation
+ // renderings. These will be more fully expanded than the final
+ // text, but the flip side of the fact that the extra data does
+ // not contribute anything to disambiguation is that leaving
+ // it in does no harm -- think of it as the Cold Dark Matter of
+ // disambiguation.
+
if (this.clashes[1] === 0 && this.nonpartners.length === 1) {
this.captureStepToBase();
+ //SNIP-START
+ if (this.debug) {
+ print(" ** RESOLUTION [a]: lone partner, one nonpartner");
+ print(" registering "+this.partners[0].id+" and "+this.nonpartners[0].id);
+ }
+ //SNIP-END
this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.betterbase);
this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, this.betterbase);
this.lists[this.listpos] = [this.betterbase, []];
} else if (this.clashes[1] === 0) {
this.captureStepToBase();
+ //SNIP-START
+ if (this.debug) {
+ print(" ** RESOLUTION [b]: lone partner, unknown number of remaining nonpartners");
+ print(" registering "+this.partners[0].id);
+ }
+ //SNIP-END
this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, this.betterbase);
this.lists[this.listpos] = [this.betterbase, this.nonpartners];
if (this.nonpartners.length) {
@@ -17351,17 +22978,39 @@ CSL.Disambiguation.prototype.disNames = function (ismax) {
}
} else if (this.nonpartners.length === 1) {
this.captureStepToBase();
+ //SNIP-START
+ if (this.debug) {
+ print(" ** RESOLUTION [c]: lone nonpartner, unknown number of partners remaining");
+ print(" registering "+this.nonpartners[0].id);
+ }
+ //SNIP-END
this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.betterbase);
+ //this.lists[this.listpos] = [this.betterbase, this.partners];
this.lists[this.listpos] = [this.betterbase, this.partners];
} else if (this.clashes[1] < this.clashes[0]) {
this.captureStepToBase();
+ //SNIP-START
+ if (this.debug) {
+ print(" ** RESOLUTION [d]: better result, but no entries safe to register");
+ }
+ //SNIP-END
this.lists[this.listpos] = [this.betterbase, this.partners];
this.lists.push([this.betterbase, this.nonpartners]);
} else {
+ //SNIP-START
+ if (this.debug) {
+ print(" ** RESOLUTION [e]: no improvement, and clashes remain");
+ }
+ //SNIP-END
if (ismax) {
this.lists[this.listpos] = [this.betterbase, this.nonpartners];
this.lists.push([this.betterbase, this.partners]);
if (this.modeindex === this.modes.length - 1) {
+ //SNIP-START
+ if (this.debug) {
+ print(" (registering clashing entries because we've run out of options)");
+ }
+ //SNIP-END
for (var i = 0, ilen = this.partners.length; i < ilen; i += 1) {
this.state.registry.registerAmbigToken(this.akey, "" + this.partners[i].id, this.betterbase);
}
@@ -17370,18 +23019,34 @@ CSL.Disambiguation.prototype.disNames = function (ismax) {
}
}
};
+
CSL.Disambiguation.prototype.disExtraText = function () {
+ //SNIP-START
+ if (this.debug) {
+ print("[3] === disExtraText ==");
+ }
+ //SNIP-END
+
var done = false;
+
if (this.clashes[1] === 0 && this.nonpartners.length < 2) {
done = true;
}
+
+ // If first encounter in this cycle and multiple modes are
+ // available, decrement mode and reset base
if (!done && (!this.base.disambiguate || this.state.tmp.disambiguate_count !== this.state.tmp.disambiguate_maxMax)) {
+ // Rerun everything on each subcycle? This doesn't work currently.
+ //this.initVars(this.akey)
this.modeindex = 0;
this.base.disambiguate = this.state.tmp.disambiguate_count;
this.betterbase.disambiguate = this.state.tmp.disambiguate_count;
if (!this.base.disambiguate) {
+ // Evaluate here?
this.initGivens = true;
+ // If disambiguate is false set to true
this.base.disambiguate = 1;
+ // There may be changes
for (var i = 0, ilen = this.lists[this.listpos][1].length; i < ilen; i += 1) {
this.state.tmp.taintedItemIDs[this.lists[this.listpos][1][i].id] = true;
}
@@ -17390,6 +23055,8 @@ CSL.Disambiguation.prototype.disExtraText = function () {
}
} else if (done || this.state.tmp.disambiguate_count === this.state.tmp.disambiguate_maxMax) {
if (done || this.modeindex === this.modes.length - 1) {
+ // If this is the end, disambiguation failed.
+ // Discard disambiguate=true (?) and set parameters
var base = this.lists[this.listpos][0];
for (var i = 0, ilen = this.lists[this.listpos][1].length; i < ilen; i += 1) {
this.state.tmp.taintedItemIDs[this.lists[this.listpos][1][i].id] = true;
@@ -17397,25 +23064,41 @@ CSL.Disambiguation.prototype.disExtraText = function () {
}
this.lists[this.listpos] = [this.betterbase, []];
} else {
+ // If this is followed by year-suffix, keep
+ // parameters and set disambiguate=true since it MIGHT
+ // include the date, needed for year-suffix.
+ // This may be a bit over-aggressive for cases in which the
+ // disambiguate condition does not add the date
this.modeindex = this.modes.length - 1;
var base = this.lists[this.listpos][0];
base.disambiguate = true;
for (var i = 0, ilen = this.lists[this.listpos][1].length; i < ilen; i += 1) {
+ // Always tainting here might be a little over-aggressive, but a taint may be required.
this.state.tmp.taintedItemIDs[this.lists[this.listpos][1][i].id] = true;
this.state.registry.registerAmbigToken(this.akey, "" + this.lists[this.listpos][1][i].id, base);
}
}
}
};
+
CSL.Disambiguation.prototype.disYears = function () {
var pos, len, tokens, token;
+ //SNIP-START
+ if (this.debug) {
+ print("[3] === disYears ==");
+ }
+ //SNIP-END
tokens = [];
var base = this.lists[this.listpos][0];
if (this.clashes[1]) {
+ // That is, if the initial increment on the ambigs group returns no
+ // clashes, don't apply suffix. The condition is a necessary failsafe.
+ // In original submission order
for (var i = 0, ilen = this.state.registry.mylist.length; i < ilen; i += 1) {
var origid = this.state.registry.mylist[i];
for (var j = 0, jlen = this.lists[this.listpos][1].length; j < jlen; j += 1) {
var token = this.lists[this.listpos][1][j];
+ // Warning: token.id can be number. This should be fixed at a higher level in citeproc-js if poss.
if (token.id == origid) {
tokens.push(this.registry[token.id]);
break;
@@ -17434,7 +23117,13 @@ CSL.Disambiguation.prototype.disYears = function () {
}
this.lists[this.listpos] = [this.betterbase, []];
};
+
CSL.Disambiguation.prototype.incrementDisambig = function () {
+ //SNIP-START
+ if (this.debug) {
+ print("\n[1] === incrementDisambig() ===");
+ }
+ //SNIP-END
if (this.initGivens) {
this.initGivens = false;
return false;
@@ -17442,6 +23131,22 @@ CSL.Disambiguation.prototype.incrementDisambig = function () {
var maxed = false;
var increment_names = true;
if ("disNames" === this.modes[this.modeindex]) {
+ // this.gnameset: the index pos of the current nameset
+ // this.gname: the index pos of the current name w/in the current nameset
+
+ // Stages:
+ // - Increment givenname (optional)
+ // - Add a name (optional)
+ // - Move to next nameset
+
+ // Incrementing is done forward-only on this.base. Values
+ // that improve disambiguation results are copied to
+ // this.betterbase, which is used to set the disambig parameters
+ // in the processor registry.
+
+
+ // Increment
+ // Max val is always true if a level is inactive.
increment_names = false;
if ("number" !== typeof this.givensMax) {
increment_names = true;
@@ -17478,10 +23183,35 @@ CSL.Disambiguation.prototype.incrementDisambig = function () {
this.gname = 0;
}
}
+ //SNIP-START
+ if (this.debug) {
+ print(" ------------------");
+ print(" incremented values");
+ print(" ------------------");
+ print(" | gnameset: "+this.gnameset);
+ print(" | gname: "+this.gname);
+ print(" | names value: "+this.base.names[this.gnameset]);
+ if (this.base.givens.length) {
+ print(" | givens value: "+this.base.givens[this.gnameset][this.gname]);
+ } else {
+ print(" | givens value: nil");
+ }
+ print(" | namesetsMax: "+this.namesetsMax);
+ print(" | namesMax: "+this.namesMax);
+ print(" | givensMax: "+this.givensMax);
+ }
+ //SNIP-END
if (("number" !== typeof this.namesetsMax || this.namesetsMax === -1 || this.gnameset === this.namesetsMax)
&& (!this.state.opt["disambiguate-add-names"] || "number" !== typeof this.namesMax || this.gname === this.namesMax)
&& ("number" != typeof this.givensMax || "undefined" === typeof this.base.givens[this.gnameset] || "undefined" === typeof this.base.givens[this.gnameset][this.gname] || this.base.givens[this.gnameset][this.gname] === this.givensMax)) {
+
+
maxed = true;
+ //SNIP-START
+ if (this.debug) {
+ print(" MAXED");
+ }
+ //SNIP-END
}
} else if ("disExtraText" === this.modes[this.modeindex]) {
this.base.disambiguate += 1;
@@ -17489,13 +23219,22 @@ CSL.Disambiguation.prototype.incrementDisambig = function () {
}
return maxed;
};
+
CSL.Disambiguation.prototype.initVars = function (akey) {
var i, ilen, myIds, myItemBundles, myItems;
+ //SNIP-START
+ if (this.debug) {
+ print("[B] === initVars() ===");
+ }
+ //SNIP-END
this.lists = [];
this.base = false;
this.betterbase = false;
this.akey = akey;
+
this.maxNamesByItemId = {};
+
+
myItemBundles = [];
myIds = this.ambigcites[akey];
if (!myIds || !myIds.length) {
@@ -17506,6 +23245,11 @@ CSL.Disambiguation.prototype.initVars = function (akey) {
this.base = CSL.getAmbigConfig.call(this.state);
if (myIds && myIds.length > 1) {
myItemBundles.push([this.maxNamesByItemId[myItem.id], myItem]);
+ // Build a composite list of Items and associated
+ // max names. This is messy, but it's the only
+ // way to get the items sorted by the number of names
+ // to be disambiguated. If they are in descending order
+ // with name expansions, the processor will hang.
for (var i = 1, ilen = myIds.length; i < ilen; i += 1) {
myItem = this.state.refetchItem("" + myIds[i]);
this.getCiteData(myItem, this.base);
@@ -17537,6 +23281,7 @@ CSL.Disambiguation.prototype.initVars = function (akey) {
} else {
this.Item = this.state.refetchItem("" + myIds[0]);
}
+
this.modeindex = 0;
if (this.state.citation.opt["disambiguate-add-names"] || true) {
this.namesMax = this.maxNamesByItemId[this.Item.id][0];
@@ -17546,6 +23291,8 @@ CSL.Disambiguation.prototype.initVars = function (akey) {
namesMax = Math.max(namesMax,this.base.names.names[i]);
}
}
+
+
this.padBase(this.base);
this.padBase(this.betterbase);
this.base.year_suffix = false;
@@ -17558,6 +23305,8 @@ CSL.Disambiguation.prototype.initVars = function (akey) {
}
return true;
};
+
+
CSL.Disambiguation.prototype.padBase = function (base) {
for (var i = 0, ilen = base.names.length; i < ilen; i += 1) {
if (!base.givens[i]) {
@@ -17570,14 +23319,20 @@ CSL.Disambiguation.prototype.padBase = function (base) {
}
}
};
+
+/**
+ * Set available modes for disambiguation
+ */
CSL.Disambiguation.prototype.configModes = function () {
var dagopt, gdropt;
+ // Modes are function names prototyped to this instance.
this.modes = [];
dagopt = this.state.opt["disambiguate-add-givenname"];
gdropt = this.state.citation.opt["givenname-disambiguation-rule"];
if (this.state.opt['disambiguate-add-names'] || (dagopt && gdropt === "by-cite")) {
this.modes.push("disNames");
}
+
if (this.state.opt.development_extensions.prioritize_disambiguate_condition) {
if (this.state.opt.has_disambiguate) {
this.modes.push("disExtraText");
@@ -17594,12 +23349,15 @@ CSL.Disambiguation.prototype.configModes = function () {
}
}
};
+
CSL.Disambiguation.prototype.getCiteData = function(Item, base) {
+ // Initialize base if first set item seen
if (!this.maxNamesByItemId[Item.id]) {
CSL.getAmbiguousCite.call(this.state, Item, base);
base = CSL.getAmbigConfig.call(this.state);
this.maxNamesByItemId[Item.id] = CSL.getMaxVals.call(this.state);
this.state.registry.registry[Item.id].disambig.givens = this.state.tmp.disambig_settings.givens.slice();
+ // Slice the nested lists as well. Without this, disambiguate_YearSuffixFiftyTwoEntriesByCite fails.
for (var i=0,ilen=this.state.registry.registry[Item.id].disambig.givens.length;i this.base.names[i]) {
+ // XXX The old must have been wrong surely. The new, I'm not sure.
+ //this.base.givens[i] = this.base.givens[i].concat(this.base.givens[i].slice(this.base.names[i]));
this.base.givens[i] = base.givens[i].slice();
this.base.names[i] = base.names[i];
this.betterbase.names = this.base.names.slice();
@@ -17621,21 +23385,33 @@ CSL.Disambiguation.prototype.getCiteData = function(Item, base) {
this.padBase(this.betterbase);
}
}
+ // This shouldn't be necessary
+ // getAmbiguousCite() should return a valid and complete
+ // givens segment under all conditions, but it does not
+ // do so for institution authors, so we clean up after it
+ // here.
+ // Relevant test: sort_ChicagoYearSuffix2
this.betterbase.givens = this.base.givens.slice();
for (var j = 0, jlen = this.base.givens.length; j < jlen; j += 1) {
this.betterbase.givens[j] = this.base.givens[j].slice();
}
}
};
+
CSL.Disambiguation.prototype.captureStepToBase = function() {
+ // Be paranoid about the presence of givens
if (this.state.citation.opt["givenname-disambiguation-rule"] === "by-cite"
&& this.base.givens && this.base.givens.length) {
if ("undefined" !== typeof this.base.givens[this.gnameset][this.gname]) {
+ if (this.betterbase.givens.length < this.base.givens.length) {
+ this.betterbase.givens = JSON.parse(JSON.stringify(this.base.givens));
+ }
this.betterbase.givens[this.gnameset][this.gname] = this.base.givens[this.gnameset][this.gname];
}
}
this.betterbase.names[this.gnameset] = this.base.names[this.gnameset];
};
+
CSL.Engine.prototype.getJurisdictionList = function (jurisdiction) {
var jurisdictionList = [];
var jurisdictionElems = jurisdiction.split(":");
@@ -17647,6 +23423,7 @@ CSL.Engine.prototype.getJurisdictionList = function (jurisdiction) {
}
return jurisdictionList;
};
+
CSL.Engine.prototype.retrieveAllStyleModules = function (jurisdictionList) {
var ret = {};
var preferences = this.locale[this.opt.lang].opts["jurisdiction-preference"];
@@ -17656,21 +23433,28 @@ CSL.Engine.prototype.retrieveAllStyleModules = function (jurisdictionList) {
var preference = preferences[i];
for (var j=0,jlen=jurisdictionList.length;j