vendor: 5,150 bytes, lines 1-141
1(function webpackUniversalModuleDefinition(root, factory) { 2 if(typeof exports === 'object' && typeof module === 'object') 3 module.exports = factory(); 4 else if(typeof define === 'function' && define.amd) 5 define([], factory); 6 else if(typeof exports === 'object') 7 exports["citeproc"] = factory(); 8 else 9 root["citeproc"] = factory(); 10})(typeof self !== 'undefined' ? self : this, function() { 11return /******/ (function(modules) { // webpackBootstrap 12/******/ // The module cache 13/******/ var installedModules = {}; 14/******/ 15/******/ // The require function 16/******/ function __webpack_require__(moduleId) { 17/******/ 18/******/ // Check if module is in cache 19/******/ if(installedModules[moduleId]) { 20/******/ return installedModules[moduleId].exports; 21/******/ } 22/******/ // Create a new module (and put it into the cache) 23/******/ var module = installedModules[moduleId] = { 24/******/ i: moduleId, 25/******/ l: false, 26/******/ exports: {} 27/******/ }; 28/******/ 29/******/ // Execute the module function 30/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31/******/ 32/******/ // Flag the module as loaded 33/******/ module.l = true; 34/******/ 35/******/ // Return the exports of the module 36/******/ return module.exports; 37/******/ } 38/******/ 39/******/ 40/******/ // expose the modules object (__webpack_modules__) 41/******/ __webpack_require__.m = modules; 42/******/ 43/******/ // expose the module cache 44/******/ __webpack_require__.c = installedModules; 45/******/ 46/******/ // define getter function for harmony exports 47/******/ __webpack_require__.d = function(exports, name, getter) { 48/******/ if(!__webpack_require__.o(exports, name)) { 49/******/ Object.defineProperty(exports, name, { 50/******/ configurable: false, 51/******/ enumerable: true, 52/******/ get: getter 53/******/ }); 54/******/ } 55/******/ }; 56/******/ 57/******/ // getDefaultExport function for compatibility with non-harmony modules 58/******/ __webpack_require__.n = function(module) { 59/******/ var getter = module && module.__esModule ? 60/******/ function getDefault() { return module['default']; } : 61/******/ function getModuleExports() { return module; }; 62/******/ __webpack_require__.d(getter, 'a', getter); 63/******/ return getter; 64/******/ }; 65/******/ 66/******/ // Object.prototype.hasOwnProperty.call 67/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 68/******/ 69/******/ // __webpack_public_path__ 70/******/ __webpack_require__.p = ""; 71/******/ 72/******/ // Load entry module and return exports 73/******/ return __webpack_require__(__webpack_require__.s = 0); 74/******/ }) 75/************************************************************************/ 76/******/ ([ 77/* 0 */ 78/***/ (function(module, exports, __webpack_require__) { 79 80module.exports = __webpack_require__(1); 81 82 83/***/ }), 84/* 1 */ 85/***/ (function(module, exports, __webpack_require__) { 86 87"use strict"; 88/* 89Copyright (c) 2009-2019 Frank Bennett 90 91 This program is free software: you can redistribute it and/or 92 modify it under EITHER 93 94 * the terms of the Common Public Attribution License (CPAL) as 95 published by the Open Source Initiative, either version 1 of 96 the CPAL, or (at your option) any later version; OR 97 98 * the terms of the GNU Affero General Public License (AGPL) 99 as published by the Free Software Foundation, either version 100 3 of the AGPL, or (at your option) any later version. 101 102 This program is distributed in the hope that it will be useful, 103 but WITHOUT ANY WARRANTY; without even the implied warranty of 104 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 105 Affero General Public License for more details. 106 107 You should have received copies of the Common Public Attribution 108 License and of the GNU Affero General Public License along with 109 this program. If not, see <https://opensource.org/licenses/> or 110 <http://www.gnu.org/licenses/> respectively. 111*/ 112/*global CSL: true */ 113 114/** 115 * A Javascript implementation of the CSL citation formatting language. 116 * 117 * <p>A configured instance of the process is built in two stages, 118 * using {@link CSL.Core.Build} and {@link CSL.Core.Configure}. 119 * The former sets up hash-accessible locale data and imports the CSL format file 120 * to be applied to the citations, 121 * transforming it into a one-dimensional token list, and 122 * registering functions and parameters on each token as appropriate. 123 * The latter sets jump-point information 124 * on tokens that constitute potential branch 125 * points, in a single back-to-front scan of the token list. 126 * This 127 * yields a token list that can be executed front-to-back by 128 * body methods available on the 129 * {@link CSL.Engine} class.</p> 130 * 131 * <p>This top-level {@link CSL} object itself carries 132 * constants that are needed during processing.</p> 133 * @namespace A CSL citation formatter. 134 */ 135 136// IE6 does not implement Array.indexOf(). 137// IE7 neither, according to rumour. 138 139 140// Potential skip words: 141// under; along; out; between; among; outside; inside; amid; amidst; against; toward; towards.
142// See https://forums.zotero.org/discussion/30484/?Focus=159613#Comment_159613 143 144 145 146 147var CSL = { 148 149 PROCESSOR_VERSION: "1.4.61", 150 151 error: function(str) { // default error function 152 if ("undefined" === typeof Error) { 153 throw new Error("citeproc-js error: " + str); 154 } else { 155 throw "citeproc-js error: " + str; 156 } 157 }, 158 debug: function(str) { // default debug function 159 if ("undefined" === typeof console) { 160 dump("CSL: " + str + "\n"); 161 } else { 162 console.log("citeproc-js warning: " + str); 163 } 164 }, 165 166 toLocaleUpperCase(str) { 167 var arr = this.tmp.lang_array; 168 try { 169 str = str.toLocaleUpperCase(arr); 170 } catch (e) { 171 str = str.toUpperCase(); 172 } 173 return str; 174 }, 175 176 toLocaleLowerCase(str) { 177 var arr = this.tmp.lang_array; 178 try { 179 str = str.toLocaleLowerCase(arr); 180 } catch (e) { 181 str = str.toLowerCase(); 182 } 183 return str; 184 }, 185 186 LOCATOR_LABELS_REGEXP: new RegExp("^((vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|rn|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\\.)\\s+(.*)"), 187 188 STATUTE_SUBDIV_PLAIN_REGEX: /(?:(?:^| )(?:vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|rn|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\. *)/, 189 STATUTE_SUBDIV_PLAIN_REGEX_FRONT: /(?:^\s*[.,;]*\s*(?:vrs|sv|subpara|op|subch|add|amend|annot|app|art|bibliog|bk|ch|cl|col|cmt|dec|dept|div|ex|fig|fld|fol|n|hypo|illus|intro|l|no|p|pp|para|pt|pmbl|princ|pub|r|rn|sched|sec|ser|subdiv|subsec|supp|tbl|tit|vol)\. *)/, 190 191 STATUTE_SUBDIV_STRINGS: { 192 "vrs.": "verse", 193 "sv.": "sub-verbo", 194 "subpara.": "subparagraph", 195 "op.": "opus", 196 "subch.": "subchapter", 197 "add.": "addendum", 198 "amend.": "amendment", 199 "annot.": "annotation", 200 "app.": "appendix", 201 "art.": "article", 202 "bibliog.": "bibliography", 203 "bk.": "book", 204 "ch.": "chapter", 205 "cl.": "clause", 206 "col.": "column", 207 "cmt.": "comment", 208 "dec.": "decision", 209 "dept.": "department", 210 "ex.": "example",
vendor: 15,442 bytes, lines 211-677
211 "fig.": "figure", 212 "fld.": "field", 213 "fol.": "folio", 214 "n.": "note", 215 "hypo.": "hypothetical", 216 "illus.": "illustration", 217 "intro.": "introduction", 218 "l.": "line", 219 "no.": "issue", 220 "p.": "page", 221 "pp.": "page", 222 "para.": "paragraph", 223 "pt.": "part", 224 "pmbl.": "preamble", 225 "princ.": "principle", 226 "pub.": "publication", 227 "r.": "rule", 228 "rn.": "randnummer", 229 "sched.": "schedule", 230 "sec.": "section", 231 "ser.": "series,", 232 "subdiv.": "subdivision", 233 "subsec.": "subsection", 234 "supp.": "supplement", 235 "tbl.": "table", 236 "tit.": "title", 237 "vol.": "volume" 238 }, 239 STATUTE_SUBDIV_STRINGS_REVERSE: { 240 "verse": "vrs.", 241 "sub-verbo": "sv.", 242 "sub verbo": "sv.", 243 "subparagraph": "subpara.", 244 "opus": "op.", 245 "subchapter": "subch.", 246 "addendum": "add.", 247 "amendment": "amend.", 248 "annotation": "annot.", 249 "appendix": "app.", 250 "article": "art.", 251 "bibliography": "bibliog.", 252 "book": "bk.", 253 "chapter": "ch.", 254 "clause": "cl.", 255 "column": "col.", 256 "comment": "cmt.", 257 "decision": "dec.", 258 "department": "dept.", 259 "example": "ex.", 260 "figure": "fig.", 261 "field": "fld.", 262 "folio": "fol.", 263 "note": "n.", 264 "hypothetical": "hypo.", 265 "illustration": "illus.", 266 "introduction": "intro.", 267 "line": "l.", 268 "issue": "no.", 269 "page": "p.", 270 "paragraph": "para.", 271 "part": "pt.", 272 "preamble": "pmbl.", 273 "principle": "princ.", 274 "publication": "pub.", 275 "rule": "r.", 276 "randnummer": "rn.", 277 "schedule": "sched.", 278 "section": "sec.", 279 "series,": "ser.", 280 "subdivision": "subdiv.", 281 "subsection": "subsec.", 282 "supplement": "supp.", 283 "table": "tbl.", 284 "title": "tit.", 285 "volume": "vol." 286 }, 287 288 LOCATOR_LABELS_MAP: { 289 "vrs": "verse", 290 "sv": "sub-verbo", 291 "subpara": "subparagraph", 292 "op": "opus", 293 "subch": "subchapter", 294 "add": "addendum", 295 "amend": "amendment", 296 "annot": "annotation", 297 "app": "appendix", 298 "art": "article", 299 "bibliog": "bibliography", 300 "bk": "book", 301 "ch": "chapter", 302 "cl": "clause", 303 "col": "column", 304 "cmt": "comment", 305 "dec": "decision", 306 "dept": "department", 307 "ex": "example", 308 "fig": "figure", 309 "fld": "field", 310 "fol": "folio", 311 "n": "note", 312 "hypo": "hypothetical", 313 "illus": "illustration", 314 "intro": "introduction", 315 "l": "line", 316 "no": "issue", 317 "p": "page", 318 "pp": "page", 319 "para": "paragraph", 320 "pt": "part", 321 "pmbl": "preamble", 322 "princ": "principle", 323 "pub": "publication", 324 "r": "rule", 325 "rn": "randnummer", 326 "sched": "schedule", 327 "sec": "section", 328 "ser": "series,", 329 "subdiv": "subdivision", 330 "subsec": "subsection", 331 "supp": "supplement", 332 "tbl": "table", 333 "tit": "title", 334 "vol": "volume" 335 }, 336 MODULE_MACROS: { 337 "juris-pretitle": true, 338 "juris-title": true, 339 "juris-pretitle-short": true, 340 "juris-title-short": true, 341 "juris-main": true, 342 "juris-main-short": true, 343 "juris-tail": true, 344 "juris-tail-short": true, 345 "juris-locator": true 346 }, 347 MODULE_TYPES: { 348 "legal_case": true, 349 "legislation": true, 350 "bill": true, 351 "hearing": true, 352 "gazette": true, 353 "report": true, 354 "regulation": true, 355 "standard": true, 356 "patent": true, 357 "locator": true 358 }, 359 checkNestedBrace: function(state) { 360 if (state.opt.xclass === "note") { 361 this.depth = 0; 362 this.update = function(str) { 363 364 // Receives affix string, returns with flipped parens. 365 366 var str = str ? str : ""; 367 var lst = str.split(/([\(\)])/); 368 for (var i=1,ilen=lst.length;i<ilen;i += 2) { 369 if (lst[i] === "(") { 370 if (1 === (this.depth % 2)) { 371 lst[i] = "["; 372 } 373 this.depth += 1; 374 } else if (lst[i] === ")") { 375 if (0 === (this.depth % 2)) { 376 lst[i] = "]"; 377 } 378 this.depth -= 1; 379 } 380 } 381 var ret = lst.join(""); 382 return ret; 383 }; 384 } else { 385 this.update = function(str) { 386 return str; 387 }; 388 } 389 }, 390 391 MULTI_FIELDS: ["event", "publisher", "publisher-place", "event-place", "title", "container-title", "collection-title", "authority","genre","title-short","medium","country","jurisdiction","archive","archive-place"], 392 393 LangPrefsMap: { 394 "title":"titles", 395 "title-short":"titles", 396 "event":"titles", 397 "genre":"titles", 398 "medium":"titles", 399 "container-title":"journals", 400 "collection-title":"titles", 401 "archive":"journals", 402 "publisher":"publishers", 403 "authority":"publishers", 404 "publisher-place": "places", 405 "event-place": "places", 406 "archive-place": "places", 407 "jurisdiction": "places", 408 "number": "places", 409 "edition":"places", 410 "issue":"places", 411 "volume":"places" 412 }, 413 414 AbbreviationSegments: function () { 415 this["container-title"] = {}; 416 this["collection-title"] = {}; 417 this["institution-entire"] = {}; 418 this["institution-part"] = {}; 419 this.nickname = {}; 420 this.number = {}; 421 this.title = {}; 422 this.place = {}; 423 this.hereinafter = {}; 424 this.classic = {}; 425 this["container-phrase"] = {}; 426 this["title-phrase"] = {}; 427 }, 428 429 getAbbrevsDomain: function (state, country, lang) { 430 var domain = null; 431 if (state.opt.availableAbbrevDomains && country && country !== "default") { 432 var globalDomainPreference = state.locale[state.opt.lang].opts["jurisdiction-preference"]; 433 var itemDomainPreference = null; 434 if (state.locale[lang]) { 435 itemDomainPreference = state.locale[lang].opts["jurisdiction-preference"]; 436 } 437 if (itemDomainPreference) { 438 for (var j=itemDomainPreference.length-1; j > -1; j--) { 439 if (state.opt.availableAbbrevDomains[country].indexOf(itemDomainPreference[j]) > -1) { 440 domain = itemDomainPreference[j]; 441 break; 442 } 443 } 444 } 445 if (!domain && globalDomainPreference) { 446 for (var j=globalDomainPreference.length-1; j > -1; j--) { 447 if (state.opt.availableAbbrevDomains[country].indexOf(globalDomainPreference[j]) > -1) { 448 domain = globalDomainPreference[j]; 449 break; 450 } 451 } 452 } 453 } 454 return domain; 455 }, 456 457 FIELD_CATEGORY_REMAP: { 458 "title": "title", 459 "container-title": "container-title", 460 "collection-title": "collection-title", 461 "country": "place", 462 "number": "number", 463 "place": "place", 464 "archive": "container-title", 465 "title-short": "title", 466 "genre": "title", 467 "event": "title", 468 "medium": "title", 469 "archive-place": "place", 470 "publisher-place": "place", 471 "event-place": "place", 472 "jurisdiction": "place", 473 "language-name": "place", 474 "language-name-original": "place", 475 "call-number": "number", 476 "chapter-number": "number", 477 "collection-number": "number", 478 "edition": "number", 479 "page": "number", 480 "issue": "number", 481 "locator": "number", 482 "locator-extra": "number", 483 "number-of-pages": "number", 484 "number-of-volumes": "number", 485 "volume": "number", 486 "citation-number": "number", 487 "publisher": "institution-part" 488 }, 489 490 parseLocator: function(item) { 491 if (this.opt.development_extensions.locator_date_and_revision) { 492 // Break out locator elements if necessary 493 if (item.locator) { 494 item.locator = "" + item.locator; 495 var idx = item.locator.indexOf("|"); 496 if (idx > -1) { 497 var raw_locator = item.locator; 498 item.locator = raw_locator.slice(0, idx); 499 raw_locator = raw_locator.slice(idx + 1); 500 var m = raw_locator.match(/^([0-9]{4}-[0-9]{2}-[0-9]{2}).*/); 501 if (m) { 502 item["locator-date"] = this.fun.dateparser.parseDateToObject(m[1]); 503 raw_locator = raw_locator.slice(m[1].length); 504 } 505 item["locator-extra"] = raw_locator.replace(/^\s+/, "").replace(/\s+$/, ""); 506 } 507 } 508 } 509 if (item.locator) { 510 item.locator = ("" + item.locator).replace(/\s+$/, ''); 511 } 512 return item; 513 }, 514 515 normalizeLocaleStr: function(str) { 516 if (!str) { 517 return; 518 } 519 var lst = str.split('-'); 520 lst[0] = lst[0].toLowerCase(); 521 if (lst[1]) { 522 lst[1] = lst[1].toUpperCase(); 523 } 524 return lst.join("-"); 525 }, 526 527 parseNoteFieldHacks: function(Item, validFieldsForType, allowDateOverride) { 528 if ("string" !== typeof Item.note) { 529 return; 530 } 531 var elems = []; 532 var lines = Item.note.split('\n'); 533 // Normalize entries 534 for (var i=0, ilen=lines.length; i<ilen; i++) { 535 var line = lines[i]; 536 var elems = []; 537 var m = line.match(CSL.NOTE_FIELDS_REGEXP); 538 if (m) { 539 var splt = line.split(CSL.NOTE_FIELDS_REGEXP); 540 for (var j=0,jlen=(splt.length-1);j<jlen;j++) { 541 elems.push(splt[j]); 542 elems.push(m[j]); 543 } 544 elems.push(splt[splt.length-1]); 545 for (var j=1,jlen=elems.length;j<jlen;j += 2) { 546 // Abort conversions if preceded by unparseable text 547 if (elems[j-1].trim() && (i>0 || j>1) && !elems[j-1].match(CSL.NOTE_FIELD_REGEXP)) { 548 break; 549 } else { 550 elems[j] = '\n' + elems[j].slice(2,-1).trim() + '\n'; 551 } 552 } 553 lines[i] = elems.join(''); 554 } 555 } 556 // Resplit 557 lines = lines.join('\n').split('\n'); 558 var offset = 0; 559 var names = {}; 560 for (var i=0,ilen=lines.length;i<ilen;i++) { 561 var line = lines[i]; 562 var mm = line.match(CSL.NOTE_FIELD_REGEXP); 563 if (!line.trim()) { 564 continue; 565 } else if (!mm) { 566 if (i === 0) { 567 continue; 568 } else { 569 offset = i; 570 break; 571 } 572 } 573 var key = mm[1]; 574 var val = mm[2].replace(/^\s+/, "").replace(/\s+$/, ""); 575 if (key === "type") { 576 Item.type = val; 577 lines[i] = ""; 578 } else if (CSL.DATE_VARIABLES.indexOf(key.replace(/^alt-/, "")) > -1) { 579 if (!Item[key] || allowDateOverride) { 580 Item[key] = CSL.DateParser.parseDateToArray(val); 581 if (!validFieldsForType || (validFieldsForType[key] && this.isDateString(val))) { 582 lines[i] = ""; 583 } 584 } 585 } else if (!Item[key]) { 586 if (CSL.NAME_VARIABLES.indexOf(key.replace(/^alt-/, "")) > -1) { 587 if (!names[key]) { 588 names[key] = []; 589 } 590 var lst = val.split(/\s*\|\|\s*/); 591 if (lst.length === 1) { 592 names[key].push({literal:lst[0]}); 593 } else if (lst.length === 2) { 594 var name = {family:lst[0],given:lst[1]}; 595 CSL.parseParticles(name); 596 names[key].push(name); 597 } 598 } else { 599 Item[key] = val; 600 } 601 if (!validFieldsForType || validFieldsForType[key]) { 602 lines[i] = ""; 603 } 604 } 605 } 606 for (var key in names) { 607 Item[key] = names[key]; 608 } 609 // Final cleanup for validCslFields only: eliminate blank lines, add blank line to text 610 if (validFieldsForType) { 611 if (lines[offset].trim()) { 612 lines[offset] = '\n' + lines[offset]; 613 } 614 for (var i=offset-1;i>-1;i--) { 615 if (!lines[i].trim()) { 616 lines = lines.slice(0, i).concat(lines.slice(i + 1)); 617 } 618 } 619 } 620 Item.note = lines.join("\n").trim(); 621 }, 622 623 checkPrefixSpaceAppend: function (state, prefix) { 624 if (!prefix) { 625 prefix = ""; 626 } 627 var sp = ""; 628 // We need the raw string, without decorations 629 // of any kind. Markup scheme is known, though, so 630 // markup can be safely stripped at string level. 631 // 632 // U+201d = right double quotation mark 633 // U+2019 = right single quotation mark 634 // U+00bb = right double angle bracket (guillemet) 635 // U+202f = non-breaking thin space 636 // U+00a0 = non-breaking space 637 var test_prefix = prefix.replace(/<[^>]+>/g, "").replace(/["'\u201d\u2019\u00bb\u202f\u00a0 ]+$/g,""); 638 var test_char = test_prefix.slice(-1); 639 if (test_prefix.match(CSL.ENDSWITH_ROMANESQUE_REGEXP)) { 640 sp = " "; 641 } else if (CSL.TERMINAL_PUNCTUATION.slice(0,-1).indexOf(test_char) > -1) { 642 sp = " "; 643 } else if (test_char.match(/[\)\],0-9]/)) { 644 sp = " "; 645 } 646 // Protect against double spaces, which would trigger an extra, 647 // explicit, non-breaking space. 648 var prefix = (prefix + sp).replace(/\s+/g, " "); 649 return prefix; 650 }, 651 652 checkIgnorePredecessor: function(state, prefix) { 653 var ignorePredecessor = false; 654 var test_prefix = prefix.replace(/<[^>]+>/g, "").replace(/["'\u201d\u2019\u00bb\u202f\u00a0 ]+$/g,""); 655 var test_char = test_prefix.slice(-1); 656 if (CSL.TERMINAL_PUNCTUATION.slice(0,-1).indexOf(test_char) > -1 && prefix.trim().indexOf(" ") > -1) { 657 state.tmp.term_predecessor = false; 658 return true; 659 } 660 return false; 661 }, 662 663 checkSuffixSpacePrepend: function(state, suffix) { 664 if (!suffix) { 665 return ""; 666 } 667 if (suffix.match(CSL.STARTSWITH_ROMANESQUE_REGEXP) || ['[','('].indexOf(suffix.slice(0,1)) > -1) { 668 suffix = " " + suffix; 669 } 670 return suffix; 671 }, 672 673 GENDERS: ["masculine", "feminine"], 674 675 ERROR_NO_RENDERED_FORM: 1, 676 677 PREVIEW: "Just for laughs.",
678 ASSUME_ALL_ITEMS_REGISTERED: 2, 679 680 START: 0, 681 END: 1, 682 SINGLETON: 2, 683 684 SEEN: 6, 685 SUCCESSOR: 3, 686 SUCCESSOR_OF_SUCCESSOR: 4, 687 SUPPRESS: 5, 688 689 SINGULAR: 0, 690 PLURAL: 1, 691 692 LITERAL: true, 693 694 BEFORE: 1, 695 AFTER: 2, 696 697 DESCENDING: 1, 698 ASCENDING: 2, 699 700 PRIMARY: 1, 701 SECONDARY: 2, 702 703 POSITION_FIRST: 0, 704 POSITION_SUBSEQUENT: 1, 705 POSITION_IBID: 2, 706 POSITION_IBID_WITH_LOCATOR: 3, 707 POSITION_CONTAINER_SUBSEQUENT: 4, 708 709 POSITION_MAP: { 710 "0": 0, 711 "4": 1, 712 "1": 2, 713 "2": 3, 714 "3": 4 715 }, 716 717 POSITION_TEST_VARS: ["position", "first-reference-note-number", "near-note"], 718 719 AREAS: ["citation", "citation_sort", "bibliography", "bibliography_sort", "intext"], 720 721 CITE_FIELDS: ["first-reference-note-number", "first-container-reference-note-number", "locator", "locator-extra"], 722 723 SWAPPING_PUNCTUATION: [".", "!", "?", ":", ","], 724 TERMINAL_PUNCTUATION: [":", ".", ";", "!", "?", " "], 725 726 // update modes 727 NONE: 0, 728 NUMERIC: 1, 729 POSITION: 2, 730 TRIGRAPH: 3, 731 732 DATE_PARTS: ["year", "month", "day"], 733 DATE_PARTS_ALL: ["year", "month", "day", "season"], 734 DATE_PARTS_INTERNAL: ["year", "month", "day", "year_end", "month_end", "day_end"], 735 736 NAME_PARTS: ["non-dropping-particle", "family", "given", "dropping-particle", "suffix", "literal"], 737 738 DISAMBIGUATE_OPTIONS: [ 739 "disambiguate-add-names", 740 "disambiguate-add-givenname", 741 "disambiguate-add-year-suffix" 742 ], 743 744 GIVENNAME_DISAMBIGUATION_RULES: [ 745 "all-names", 746 "all-names-with-initials", 747 "primary-name", 748 "primary-name-with-initials", 749 "by-cite" 750 ], 751 752 NAME_ATTRIBUTES: [ 753 "and", 754 "delimiter-precedes-last", 755 "delimiter-precedes-et-al", 756 "initialize-with", 757 "initialize", 758 "name-as-sort-order", 759 "sort-separator", 760 "et-al-min", 761 "et-al-use-first", 762 "et-al-subsequent-min", 763 "et-al-subsequent-use-first", 764 "form", 765 "prefix", 766 "suffix", 767 "delimiter" 768 ], 769 770 LOOSE: 0, 771 STRICT: 1, 772 TOLERANT: 2, 773 774 PREFIX_PUNCTUATION: /[.;:]\s*$/, 775 SUFFIX_PUNCTUATION: /^\s*[.;:,\(\)]/, 776 777 NUMBER_REGEXP: /(?:^\d+|\d+$)/, 778 // 779 // \u0400-\u042f are cyrillic and extended cyrillic capitals 780 // this is not fully smart yet. can't do what this was trying to do 781 // with regexps, actually; we want to identify strings with a leading 782 // capital letter, and any subsequent capital letters. Have to compare 783 // locale caps version with existing version, character by character. 784 // hard stuff, but if it breaks, that's what to do. 785 // \u0600-\u06ff is Arabic/Persian 786 // \u200c-\u200e and \u202a-\u202e are special spaces and left-right 787 // control characters 788 789 790 791 NAME_INITIAL_REGEXP: /^([A-Z\u0e01-\u0e5b\u00c0-\u017f\u0400-\u042f\u0590-\u05d4\u05d6-\u05ff\u0600-\u06ff\u0370\u0372\u0376\u0386\u0388-\u03ab\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03fd-\u03ff])([a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0400-\u052f\u0600-\u06ff\u0370-\u03ff\u1f00-\u1fff]*|)(\.)*/, 792 ROMANESQUE_REGEXP: /[-0-9a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/, 793 ROMANESQUE_NOT_REGEXP: /[^a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/g, 794 STARTSWITH_ROMANESQUE_REGEXP: /^[&a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]/, 795 ENDSWITH_ROMANESQUE_REGEXP: /[.;:&a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]$/, 796 ALL_ROMANESQUE_REGEXP: /^[a-zA-Z\u0e01-\u0e5b\u00c0-\u017f\u0370-\u03ff\u0400-\u052f\u0590-\u05d4\u05d6-\u05ff\u1f00-\u1fff\u0600-\u06ff\u200c\u200d\u200e\u0218\u0219\u021a\u021b\u202a-\u202e]+$/, 797 798 VIETNAMESE_SPECIALS: /[\u00c0-\u00c3\u00c8-\u00ca\u00cc\u00cd\u00d2-\u00d5\u00d9\u00da\u00dd\u00e0-\u00e3\u00e8-\u00ea\u00ec\u00ed\u00f2-\u00f5\u00f9\u00fa\u00fd\u0101\u0103\u0110\u0111\u0128\u0129\u
vendor: 18,874 bytes, lines 798-1264
7980168\u0169\u01a0\u01a1\u01af\u01b0\u1ea0-\u1ef9]/, 799 800 VIETNAMESE_NAMES: /^(?:(?:[.AaBbCcDdEeGgHhIiKkLlMmNnOoPpQqRrSsTtUuVvXxYy \u00c0-\u00c3\u00c8-\u00ca\u00cc\u00cd\u00d2-\u00d5\u00d9\u00da\u00dd\u00e0-\u00e3\u00e8-\u00ea\u00ec\u00ed\u00f2-\u00f5\u00f9\u00fa\u00fd\u0101\u0103\u0110\u0111\u0128\u0129\u0168\u0169\u01a0\u01a1\u01af\u01b0\u1ea0-\u1ef9]{2,6})(\s+|$))+$/, 801 802 NOTE_FIELDS_REGEXP: /\{:(?:[\-_a-z]+|[A-Z]+):[^\}]+\}/g, 803 NOTE_FIELD_REGEXP: /^([\-_a-z]+|[A-Z]+):\s*([^\}]+)$/, 804 805 PARTICLE_GIVEN_REGEXP: /^([^ ]+(?:\u02bb |\u2019 | |\' ) *)(.+)$/, 806 PARTICLE_FAMILY_REGEXP: /^([^ ]+(?:\-|\u02bb|\u2019| |\') *)(.+)$/, 807 808 DISPLAY_CLASSES: ["block", "left-margin", "right-inline", "indent"], 809 810 NAME_VARIABLES: [ 811 "author", 812 "chair", 813 "collection-editor", 814 "compiler", 815 "composer", 816 "container-author", 817 "contributor", 818 "curator", 819 "director", 820 "editor", 821 "editor-translator", 822 "editorial-director", 823 "executive-producer", 824 "guest", 825 "host", 826 "illustrator", 827 "interviewer", 828 "narrator", 829 "organizer", 830 "original-author", 831 "performer", 832 "producer", 833 "recipient", 834 "reviewed-author", 835 "script-writer", 836 "series-creator", 837 "translator", 838 "commenter" 839 ], 840 CREATORS: [ 841 "author", 842 "chair", 843 "collection-editor", 844 "compiler", 845 "composer", 846 "container-author", 847 "contributor", 848 "curator", 849 "director", 850 "editor", 851 "editor-translator", 852 "editorial-director", 853 "executive-producer", 854 "guest", 855 "host", 856 "illustrator", 857 "interviewer", 858 "narrator", 859 "organizer", 860 "original-author", 861 "performer", 862 "producer", 863 "recipient", 864 "reviewed-author", 865 "script-writer", 866 "series-creator", 867 "translator", 868 "commenter" 869 ], 870 NUMERIC_VARIABLES: [ 871 "call-number", 872 "chapter-number", 873 "collection-number", 874 "division", 875 "edition", 876 "page", 877 "issue", 878 "locator", 879 "locator-extra", 880 "number", 881 "number-of-pages", 882 "number-of-volumes", 883 "part-number", 884 "printing-number", 885 "section", 886 "supplement-number", 887 "version", 888 "volume", 889 "supplement", // maybe deprecated this? supplement-number should serve this purpose in standard CSL. 890 "citation-number" 891 ], 892 //var x = new Array(); 893 //x = x.concat(["title","container-title","issued","page"]); 894 //x = x.concat(["locator","collection-number","original-date"]); 895 //x = x.concat(["reporting-date","decision-date","filing-date"]); 896 //x = x.concat(["revision-date"]); 897 //NUMERIC_VARIABLES = x.slice(); 898 DATE_VARIABLES: [ 899 "locator-date", 900 "issued", 901 "event-date", 902 "accessed", 903 "original-date", 904 "publication-date", 905 "available-date", 906 "submitted", 907 "alt-issued", 908 "alt-event" 909 ], 910 VARIABLES_WITH_SHORT_FORM: [ 911 "title", 912 "container-title" 913 ], 914 TITLE_FIELD_SPLITS: function(seg) { 915 var keys = ["title", "short", "main", "sub", "subjoin"]; 916 var ret = {}; 917 for (var i=0,ilen=keys.length;i<ilen;i++) { 918 ret[keys[i]] = seg + "title" + (keys[i] === "title" ? "" : "-" + keys[i]); 919 } 920 return ret; 921 }, 922 923 demoteNoiseWords: function (state, fld, drop_or_demote) { 924 var SKIP_WORDS = state.locale[state.opt.lang].opts["leading-noise-words"]; 925 if (fld && drop_or_demote) { 926 fld = fld.split(/\s+/); 927 fld.reverse(); 928 var toEnd = []; 929 for (var j = fld.length - 1; j > -1; j += -1) { 930 if (SKIP_WORDS.indexOf(fld[j].toLowerCase()) > -1) { 931 toEnd.push(fld.pop()); 932 } else { 933 break; 934 } 935 } 936 fld.reverse(); 937 var start = fld.join(" "); 938 var end = toEnd.join(" "); 939 if ("drop" === drop_or_demote || !end) { 940 fld = start; 941 } else if ("demote" === drop_or_demote) { 942 fld = [start, end].join(", "); 943 } 944 } 945 return fld; 946 }, 947 948 extractTitleAndSubtitle: function (Item, narrowSpaceLocale) { 949 var narrowSpace = narrowSpaceLocale ? "\u202f" : ""; 950 // XXX In this function, split on split-char, but prefer exact match 951 // XXX of subtitle to a split-char in title if found. 952 var segments = [""]; 953 if (this.opt.development_extensions.split_container_title) { 954 segments.push("container-"); 955 } 956 for (var i=0,ilen=segments.length;i<ilen;i++) { 957 var seg = segments[i]; 958 var title = CSL.TITLE_FIELD_SPLITS(seg); 959 var langs = [false]; 960 if (Item.multi) { 961 for (var lang in Item.multi._keys[title.short]) { 962 langs.push(lang); 963 } 964 } 965 for (var j=0,jlen=langs.length;j<jlen;j++) { 966 var lang = langs[j]; 967 var vals = {}; 968 if (lang) { 969 if (Item.multi._keys[title.title]) { 970 vals[title.title] = Item.multi._keys[title.title][lang]; 971 } 972 if (Item.multi._keys[title["short"]]) { 973 vals[title["short"]] = Item.multi._keys[title["short"]][lang]; 974 } 975 } else { 976 vals[title.title] = Item[title.title]; 977 vals[title["short"]] = Item[title["short"]]; 978 } 979 vals[title.main] = vals[title.title]; 980 vals[title.sub] = false; 981 var shortTitle = vals[title["short"]]; 982 if (vals[title.title]) { 983 // Rules 984 // TITLE_SPLIT eliminates split-points of period-space preceded by a capital letter. 985 // If short title exists and matches exactly to a split-point, use that split-point only. 986 // Otherwise if there is just one split-point, use that as main/sub split. 987 // Otherwise use all split-points ... which is handled in titleCaseSentenceOrNormal, not here. 988 if (shortTitle && shortTitle.toLowerCase() === vals[title.title].toLowerCase()) { 989 vals[title.main] = vals[title.title]; 990 vals[title.subjoin] = ""; 991 vals[title.sub] = ""; 992 } else if (shortTitle) { 993 // check for valid match to shortTitle 994 var tail = vals[title.title].slice(shortTitle.replace(/[\?\!]+$/, "").length); 995 var top = vals[title.title].replace(tail.replace(/^[\?\!]+/, ""), "").trim(); 996 var m = CSL.TITLE_SPLIT_REGEXP.matchfirst.exec(tail); 997 if (m && top.toLowerCase() === shortTitle.toLowerCase()) { 998 vals[title.main] = top; 999 vals[title.subjoin] = m[1].replace(/[\?\!]+(\s*)$/, "$1"); 1000 vals[title.sub] = tail.replace(CSL.TITLE_SPLIT_REGEXP.matchfirst, ""); 1001 if (this.opt.development_extensions.force_short_title_casing_alignment) { 1002 vals[title["short"]] = vals[title.main]; 1003 } 1004 } else { 1005 var splitTitle = CSL.TITLE_SPLIT(vals[title.title]); 1006 if (splitTitle.length == 3) { 1007 vals[title.main] = splitTitle[0]; 1008 vals[title.subjoin] = splitTitle[1]; 1009 vals[title.sub] = splitTitle[2]; 1010 } else { 1011 vals[title.main] = vals[title.title]; 1012 vals[title.subjoin] = ""; 1013 vals[title.sub] = ""; 1014 } 1015 } 1016 } else { 1017 var splitTitle = CSL.TITLE_SPLIT(vals[title.title]); 1018 if (splitTitle.length == 3) { 1019 vals[title.main] = splitTitle[0]; 1020 vals[title.subjoin] = splitTitle[1]; 1021 vals[title.sub] = splitTitle[2]; 1022 if (this.opt.development_extensions.implicit_short_title && Item.type !== "legal_case") { 1023 if (!Item[title.short] && !vals[title.main].match(/^[\-\.[0-9]+$/)) { 1024 var punct = vals[title.subjoin].trim(); 1025 if (["?", "!"].indexOf(punct) === -1) { 1026 punct = ""; 1027 } 1028 vals[title.short] = vals[title.main] + punct; 1029 } 1030 } 1031 } else { 1032 vals[title.main] = vals[title.title]; 1033 vals[title.subjoin] = ""; 1034 vals[title.sub] = ""; 1035 } 1036 } 1037 if (vals[title.subjoin]) { 1038 if (vals[title.subjoin].match(/([\?\!])/)) { 1039 var m = vals[title.subjoin].match(/(\s*)$/) 1040 vals[title.main] = vals[title.main] + narrowSpace +vals[title.subjoin].trim(); 1041 vals[title.subjoin] = m[1]; 1042 } 1043 } 1044 } 1045 if (vals[title.subjoin]) { 1046 if (vals[title.subjoin].indexOf(":") > -1) { 1047 vals[title.subjoin] = narrowSpace + ": "; 1048 } 1049 if (vals[title.subjoin].indexOf("-") > -1 || vals[title.subjoin].indexOf("â") > -1) { 1050 vals[title.subjoin] = "â"; 1051 } 1052 } 1053 if (lang) { 1054 for (var key in vals) { 1055 if (!Item.multi._keys[key]) { 1056 Item.multi._keys[key] = {}; 1057 } 1058 Item.multi._keys[key][lang] = vals[key]; 1059 } 1060 } else { 1061 for (var key in vals) { 1062 Item[key] = vals[key]; 1063 } 1064 } 1065 } 1066 } 1067 }, 1068 1069 titlecaseSentenceOrNormal: function(state, Item, seg, lang, sentenceCase) { 1070 // Hold on here. 1071 // What is seg here? 1072 // It's ... either "" or "container-". Which is ugly, but works. 1073 // But this ALWAYS returns the full title, never short. 1074 // So sentence-casing cannot be applied to short. 1075 // Goes unnoticed because forced sentence-casing almost never appears in styles. 1076 var title = CSL.TITLE_FIELD_SPLITS(seg); 1077 var vals = {}; 1078 if (lang && Item.multi) { 1079 if (Item.multi._keys[title.title]) { 1080 vals[title.title] = Item.multi._keys[title.title][lang]; 1081 } 1082 if (Item.multi._keys[title.main]) { 1083 vals[title.main] = Item.multi._keys[title.main][lang]; 1084 } 1085 if (Item.multi._keys[title.sub]) { 1086 vals[title.sub] = Item.multi._keys[title.sub][lang]; 1087 } 1088 if (Item.multi._keys[title.subjoin]) { 1089 vals[title.subjoin] = Item.multi._keys[title.subjoin][lang]; 1090 } 1091 } else { 1092 vals[title.title] = Item[title.title]; 1093 vals[title.main] = Item[title.main]; 1094 vals[title.sub] = Item[title.sub]; 1095 vals[title.subjoin] = Item[title.subjoin]; 1096 } 1097 if (vals[title.main] && vals[title.sub]) { 1098 var mainTitle = vals[title.main]; 1099 var subJoin = vals[title.subjoin]; 1100 var subTitle = vals[title.sub]; 1101 if (sentenceCase) { 1102 mainTitle = CSL.Output.Formatters.sentence(state, mainTitle); 1103 subTitle = CSL.Output.Formatters.sentence(state, subTitle); 1104 } else if (state.opt.development_extensions.uppercase_subtitles) { 1105 subTitle = CSL.Output.Formatters["capitalize-first"](state, subTitle); 1106 } 1107 return [mainTitle, subJoin, subTitle].join(""); 1108 } else if (vals[title.title]) { 1109 if (sentenceCase) { 1110 return CSL.Output.Formatters.sentence(state, vals[title.title]); 1111 } else if (state.opt.development_extensions.uppercase_subtitles) { 1112 // Split and apply everywhere. 1113 var splits = CSL.TITLE_SPLIT(vals[title.title]); 1114 for (var i=0,ilen=splits.length; i<ilen; i += 2) { 1115 splits[i] = CSL.Output.Formatters["capitalize-first"](state, splits[i]); 1116 } 1117 for (var i=1, ilen=splits.length-1; i < ilen; i += 2) { 1118 var m = splits[i].match(/([:\?\!] )/); 1119 if (m) { 1120 var narrowSpace = state.opt["default-locale"][0].slice(0, 2).toLowerCase() === "fr" ? "\u202f" : ""; 1121 splits[i] = narrowSpace + m[1]; 1122 } 1123 if (splits[i].indexOf("-") > -1 || splits[i].indexOf("â") > -1) { 1124 splits[i] = "â"; 1125 } 1126 } 1127 vals[title.title] = splits.join(""); 1128 return vals[title.title]; 1129 } else { 1130 return vals[title.title]; 1131 } 1132 } else { 1133 return ""; 1134 } 1135 }, 1136 1137 getSafeEscape: function(state) { 1138 if (["bibliography", "citation"].indexOf(state.tmp.area) > -1) { 1139 // Callback to apply thin space hack 1140 // Callback to force LTR/RTL on parens and braces 1141 // XXX Is this really necessary? 1142 var callbacks = []; 1143 if (state.opt.development_extensions.thin_non_breaking_space_html_hack && state.opt.mode === "html") { 1144 callbacks.push(function (txt) { 1145 return txt.replace(/\u202f/g, '<span style="white-space:nowrap"> </span>'); 1146 }); 1147 } 1148 if (callbacks.length) { 1149 return function (txt) { 1150 for (var i = 0, ilen = callbacks.length; i < ilen; i += 1) { 1151 txt = callbacks[i](txt); 1152 } 1153 return CSL.Output.Formats[state.opt.mode].text_escape(txt); 1154 }; 1155 } else { 1156 return CSL.Output.Formats[state.opt.mode].text_escape; 1157 } 1158 } else { 1159 return function (txt) { return txt; }; 1160 } 1161 }, 1162 1163 SKIP_WORDS: ["about","above","across","afore","after","against","al", "along","alongside","amid","amidst","among","amongst","anenst","apropos","apud","around","as","aside","astride","at","athwart","atop","barring","before","behind","below","beneath","beside","besides","between","beyond","but","by","circa","despite","down","during","et", "except","for","forenenst","from","given","in","inside","into","lest","like","modulo","near","next","notwithstanding","of","off","on","onto","out","over","per","plus","pro","qua","sans","since","than","through"," thru","throughout","thruout","till","to","toward","towards","under","underneath","until","unto","up","upon","versus","vs.","v.","vs","v","via","vis-à -vis","with","within","without","according to","ahead of","apart from","as for","as of","as per","as regards","aside from","back to","because of","close to","due to","except for","far from","inside of","instead of","near to","next to","on to","out from","out of","outside of","prior to","pursuant to","rather than","regardless of","such as","that of","up to","where as","or", "yet", "so", "for", "and", "nor", "a", "an", "the", "de", "d'", "von", "van", "c", "ca"], 1164 1165 FORMAT_KEY_SEQUENCE: [ 1166 "@strip-periods", 1167 "@font-style", 1168 "@font-variant", 1169 "@font-weight", 1170 "@text-decoration", 1171 "@vertical-align", 1172 "@quotes" 1173 ], 1174 1175 INSTITUTION_KEYS: [ 1176 "font-style", 1177 "font-variant", 1178 "font-weight", 1179 "text-decoration", 1180 "text-case" 1181 ], 1182 1183 SUFFIX_CHARS: "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z", 1184 ROMAN_NUMERALS: [ 1185 [ "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" ], 1186 [ "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" ], 1187 [ "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" ], 1188 [ "", "m", "mm", "mmm", "mmmm", "mmmmm"] 1189 ], 1190 1191 LANGS: { 1192 "af-ZA":"Afrikaans", 1193 "ar":"Arabic", 1194 "bg-BG":"Bulgarian", 1195 "ca-AD":"Catalan", 1196 "cs-CZ":"Czech", 1197 "da-DK":"Danish", 1198 "de-AT":"Austrian", 1199 "de-CH":"German (CH)", 1200 "de-DE":"German (DE)", 1201 "el-GR":"Greek", 1202 "en-GB":"English (GB)", 1203 "en-US":"English (US)", 1204 "es-ES":"Spanish", 1205 "et-EE":"Estonian", 1206 "eu":"European", 1207 "fa-IR":"Persian", 1208 "fi-FI":"Finnish", 1209 "fr-CA":"French (CA)", 1210 "fr-FR":"French (FR)", 1211 "he-IL":"Hebrew", 1212 "hr-HR":"Croatian", 1213 "hu-HU":"Hungarian", 1214 "is-IS":"Icelandic", 1215 "it-IT":"Italian", 1216 "ja-JP":"Japanese", 1217 "km-KH":"Khmer", 1218 "ko-KR":"Korean", 1219 "lt-LT":"Lithuanian", 1220 "lv-LV":"Latvian", 1221 "mn-MN":"Mongolian", 1222 "nb-NO":"Norwegian (BokmÃ¥l)", 1223 "nl-NL":"Dutch", 1224 "nn-NO":"Norwegian (Nynorsk)", 1225 "pl-PL":"Polish", 1226 "pt-BR":"Portuguese (BR)", 1227 "pt-PT":"Portuguese (PT)", 1228 "ro-RO":"Romanian", 1229 "ru-RU":"Russian", 1230 "sk-SK":"Slovak", 1231 "sl-SI":"Slovenian", 1232 "sr-RS":"Serbian", 1233 "sv-SE":"Swedish", 1234 "th-TH":"Thai", 1235 "tr-TR":"Turkish", 1236 "uk-UA":"Ukrainian", 1237 "vi-VN":"Vietnamese", 1238 "zh-CN":"Chinese (CN)", 1239 "zh-TW":"Chinese (TW)" 1240 }, 1241 1242 LANG_BASES: { 1243 af: "af_ZA", 1244 ar: "ar", 1245 bg: "bg_BG", 1246 ca: "ca_AD", 1247 cs: "cs_CZ", 1248 da: "da_DK", 1249 de: "de_DE", 1250 el: "el_GR", 1251 en: "en_US", 1252 es: "es_ES", 1253 et: "et_EE", 1254 eu: "eu", 1255 fa: "fa_IR", 1256 fi: "fi_FI", 1257 fr: "fr_FR", 1258 he: "he_IL", 1259 hr: "hr-HR", 1260 hu: "hu_HU", 1261 is: "is_IS", 1262 it: "it_IT", 1263 ja: "ja_JP", 1264 km: "km_KH",
1265 ko: "ko_KR", 1266 lt: "lt_LT", 1267 lv: "lv-LV", 1268 mn: "mn_MN", 1269 nb: "nb_NO", 1270 nl: "nl_NL", 1271 nn: "nn-NO", 1272 pl: "pl_PL", 1273 pt: "pt_PT", 1274 ro: "ro_RO", 1275 ru: "ru_RU", 1276 sk: "sk_SK", 1277 sl: "sl_SI", 1278 sr: "sr_RS", 1279 sv: "sv_SE", 1280 th: "th_TH", 1281 tr: "tr_TR", 1282 uk: "uk_UA", 1283 vi: "vi_VN", 1284 zh: "zh_CN" 1285 }, 1286 1287 SUPERSCRIPTS: { 1288 "\u00AA": "\u0061", 1289 "\u00B2": "\u0032", 1290 "\u00B3": "\u0033", 1291 "\u00B9": "\u0031", 1292 "\u00BA": "\u006F", 1293 "\u02B0": "\u0068", 1294 "\u02B1": "\u0266", 1295 "\u02B2": "\u006A", 1296 "\u02B3": "\u0072", 1297 "\u02B4": "\u0279", 1298 "\u02B5": "\u027B", 1299 "\u02B6": "\u0281", 1300 "\u02B7": "\u0077", 1301 "\u02B8": "\u0079", 1302 "\u02E0": "\u0263", 1303 "\u02E1": "\u006C", 1304 "\u02E2": "\u0073", 1305 "\u02E3": "\u0078", 1306 "\u02E4": "\u0295", 1307 "\u1D2C": "\u0041", 1308 "\u1D2D": "\u00C6", 1309 "\u1D2E": "\u0042", 1310 "\u1D30": "\u0044", 1311 "\u1D31": "\u0045", 1312 "\u1D32": "\u018E", 1313 "\u1D33": "\u0047", 1314 "\u1D34": "\u0048", 1315 "\u1D35": "\u0049", 1316 "\u1D36": "\u004A", 1317 "\u1D37": "\u004B", 1318 "\u1D38": "\u004C", 1319 "\u1D39": "\u004D", 1320 "\u1D3A": "\u004E", 1321 "\u1D3C": "\u004F", 1322 "\u1D3D": "\u0222", 1323 "\u1D3E": "\u0050", 1324 "\u1D3F": "\u0052", 1325 "\u1D40": "\u0054", 1326 "\u1D41": "\u0055", 1327 "\u1D42": "\u0057", 1328 "\u1D43": "\u0061", 1329 "\u1D44": "\u0250", 1330 "\u1D45": "\u0251", 1331 "\u1D46": "\u1D02", 1332 "\u1D47": "\u0062", 1333 "\u1D48": "\u0064", 1334 "\u1D49": "\u0065", 1335 "\u1D4A": "\u0259", 1336 "\u1D4B": "\u025B", 1337 "\u1D4C": "\u025C", 1338 "\u1D4D": "\u0067", 1339 "\u1D4F": "\u006B", 1340 "\u1D50": "\u006D", 1341 "\u1D51": "\u014B", 1342 "\u1D52": "\u006F", 1343 "\u1D53": "\u0254", 1344 "\u1D54": "\u1D16", 1345 "\u1D55": "\u1D17", 1346 "\u1D56": "\u0070", 1347 "\u1D57": "\u0074", 1348 "\u1D58": "\u0075", 1349 "\u1D59": "\u1D1D", 1350 "\u1D5A": "\u026F", 1351 "\u1D5B": "\u0076", 1352 "\u1D5C": "\u1D25", 1353 "\u1D5D": "\u03B2", 1354 "\u1D5E": "\u03B3", 1355 "\u1D5F": "\u03B4", 1356 "\u1D60": "\u03C6", 1357 "\u1D61": "\u03C7", 1358 "\u2070": "\u0030", 1359 "\u2071": "\u0069", 1360 "\u2074": "\u0034", 1361 "\u2075": "\u0035", 1362 "\u2076": "\u0036", 1363 "\u2077": "\u0037", 1364 "\u2078": "\u0038", 1365 "\u2079": "\u0039", 1366 "\u207A": "\u002B", 1367 "\u207B": "\u2212", 1368 "\u207C": "\u003D", 1369 "\u207D": "\u0028", 1370 "\u207E": "\u0029", 1371 "\u207F": "\u006E", 1372 "\u2120": "\u0053\u004D", 1373 "\u2122": "\u0054\u004D", 1374 "\u3192": "\u4E00", 1375 "\u3193": "\u4E8C", 1376 "\u3194": "\u4E09", 1377 "\u3195": "\u56DB", 1378 "\u3196": "\u4E0A", 1379 "\u3197": "\u4E2D", 1380 "\u3198": "\u4E0B", 1381 "\u3199": "\u7532", 1382 "\u319A": "\u4E59", 1383 "\u319B": "\u4E19", 1384 "\u319C": "\u4E01", 1385 "\u319D": "\u5929", 1386 "\u319E": "\u5730", 1387 "\u319F": "\u4EBA", 1388 "\u02C0": "\u0294", 1389 "\u02C1": "\u0295", 1390 "\u06E5": "\u0648", 1391 "\u06E6": "\u064A" 1392 }, 1393 SUPERSCRIPTS_REGEXP: new RegExp("[\u00AA\u00B2\u00B3\u00B9\u00BA\u02B0\u02B1\u02B2\u02B3\u02B4\u02B5\u02B6\u02B7\u02B8\u02E0\u02E1\u02E2\u02E3\u02E4\u1D2C\u1D2D\u1D2E\u1D30\u1D31\u1D32\u1D33\u1D34\u1D35\u1D36\u1D37\u1D38\u1D39\u1D3A\u1D3C\u1D3D\u1D3E\u1D3F\u1D40\u1D41\u1D42\u1D43\u1D44\u1D45\u1D46\u1D47\u1D48\u1D49\u1D4A\u1D4B\u1D4C\u1D4D\u1D4F\u1D50\u1D51\u1D52\u1D53\u1D54\u1D55\u1D56\u1D57\u1D58\u1D59\u1D5A\u1D5B\u1D5C\u1D5D\u1D5E\u1D5F\u1D60\u1D61\u2070\u2071\u2074\u2075\u2076\u2077\u2078\u2079\u207A\u207B\u207C\u207D\u207E\u207F\u2120\u2122\u3192\u3193\u3194\u3195\u3196\u3197\u3198\u3199\u319A\u319B\u319C\u319D\u319E\u319F\u02C0\u02C1\u06E5\u06E6]", "g"), 1394 1395 // I think we need to have separate args for prefix and term, 1396 // since they have different effects between comma-safe and comma-safe-numbers-only. 1397 // Either that, or -- oh, we could just bang the two together for the test where 1398 // necessary. 1399 1400 UPDATE_GROUP_CONTEXT_CONDITION: function (state, str, valueTerm, token, value) {
vendor: 75,357 bytes, lines 1401-3623
1401 if (!state.opt.use_context_condition) return; 1402 var flags = state.tmp.group_context.tip; 1403 if (flags.condition) { 1404 if (!flags.condition.termtxt) { 1405 flags.condition.termtxt = str; 1406 flags.condition.valueTerm = valueTerm; 1407 } 1408 if (!flags.value_seen && flags.condition.test === "comma-safe-numbers-only") { 1409 if (value) { 1410 flags.value_seen = true; 1411 if (!value.match(/^[0-9]/)) { 1412 state.tmp.just_did_number = false; 1413 } 1414 } 1415 } 1416 } else { 1417 // If not inside a conditional group, raise numeric flag 1418 // if and only if the current term string ends in a number. 1419 if (token && token.decorations.filter(o => o[0] === "@vertical-align").length > 0) { 1420 state.tmp.just_did_number = false; 1421 } else if (token && token.strings.suffix) { 1422 state.tmp.just_did_number = false; 1423 } else if (str) { 1424 if (str.match(/[0-9]$/)) { 1425 state.tmp.just_did_number = true; 1426 } else { 1427 state.tmp.just_did_number = false; 1428 } 1429 } 1430 } 1431 }, 1432 1433 EVALUATE_GROUP_CONDITION: function(state, flags) { 1434 if (!state.opt.use_context_condition) return; 1435 var testres; 1436 var numbersOnly = flags.condition.test === "comma-safe-numbers-only"; 1437 if (flags.condition.test === "empty-label") { 1438 testres = !flags.condition.termtxt; 1439 } else if (flags.condition.test === "empty-label-no-decor") { 1440 testres = !flags.condition.termtxt || flags.condition.termtxt.indexOf("%s") > -1; 1441 } else if (["comma-safe", "comma-safe-numbers-only"].indexOf(flags.condition.test) > -1) { 1442 var locale_term = flags.condition.termtxt; 1443 var termStartAlpha = false; 1444 if (flags.condition.termtxt) { 1445 termStartAlpha = flags.condition.termtxt.slice(0,1).match(CSL.ALL_ROMANESQUE_REGEXP); 1446 } 1447 var num = state.tmp.just_did_number; 1448 if (num) { 1449 if (flags.condition.valueTerm) { 1450 testres = numbersOnly ? false : true; 1451 } else if (!locale_term) { 1452 testres = true; 1453 } else if (termStartAlpha) { 1454 testres = numbersOnly ? false : true; 1455 } else if (["always", "after-number"].indexOf(state.opt.require_comma_on_symbol) > -1) { 1456 testres = true; 1457 } else { 1458 testres = false; 1459 } 1460 } else { 1461 if (flags.condition.valueTerm) { 1462 testres = false; 1463 } else if (!locale_term) { 1464 testres = false; 1465 } else if (termStartAlpha) { 1466 testres = numbersOnly ? false : true; 1467 } else if (state.opt.require_comma_on_symbol === "always") { 1468 testres = true; 1469 } else { 1470 testres = false; 1471 } 1472 } 1473 } 1474 if (testres) { 1475 var force_suppress = false; 1476 } else { 1477 var force_suppress = true; 1478 } 1479 if (flags.condition.not) { 1480 force_suppress = !force_suppress; 1481 } 1482 return force_suppress; 1483 }, 1484 1485 SYS_OPTIONS: [ 1486 "prioritize_disambiguate_condition", 1487 "csl_reverse_lookup_support", 1488 "main_title_from_short_title", 1489 "uppercase_subtitles", 1490 "force_short_title_casing_alignment", 1491 "implicit_short_title", 1492 "split_container_title" 1493 ], 1494 1495 TITLE_SPLIT_REGEXP: (function() { 1496 var splits = [ 1497 "\\.\\s+", 1498 "\\!\\s+", 1499 "\\?\\s+", 1500 "\\s*::*\\s+", 1501 "\\s*â\\s*", 1502 "\\s+\\-\\s+", 1503 "\\s*\\-\\-\\-*\\s*" 1504 ] 1505 return { 1506 match: new RegExp("(" + splits.join("|") + ")", "g"), 1507 matchfirst: new RegExp("^(" + splits.join("|") + ")"), 1508 split: new RegExp("(?:" + splits.join("|") + ")") 1509 } 1510 })(), 1511 1512 TITLE_SPLIT: function(str) { 1513 if (!str) { 1514 return str; 1515 } 1516 var m = str.match(CSL.TITLE_SPLIT_REGEXP.match); 1517 var lst = str.split(CSL.TITLE_SPLIT_REGEXP.split); 1518 for (var i=lst.length-2; i>-1; i--) { 1519 lst[i] = lst[i].trim(); 1520 if (lst[i] && lst[i].slice(-1).toLowerCase() !== lst[i].slice(-1)) { 1521 // recombine 1522 lst[i] = lst[i] + m[i] + lst[i+1]; 1523 lst = lst.slice(0, i+1).concat(lst.slice(i+2)) 1524 } else { 1525 // merge 1526 lst = lst.slice(0, i+1).concat([m[i]]).concat(lst.slice(i+1)) 1527 } 1528 } 1529 return lst; 1530 }, 1531 1532 GET_COURT_CLASS: function(state, Item, sortKey){ 1533 // Get authority as a string 1534 var cls = ""; 1535 var authority = null; 1536 var country = Item.jurisdiction ? Item.jurisdiction.split(":")[0] : null; 1537 // inStyle versus in module 1538 var classType = "court_condition_classes"; 1539 if (sortKey) { 1540 classType = "court_key_classes"; 1541 } 1542 if (country && Item.authority) { 1543 if ("string" === typeof Item.authority) { 1544 authority = Item.authority; 1545 } else { 1546 if (Item.authority[0] && Item.authority[0].literal) { 1547 authority = Item.authority[0].literal; 1548 } 1549 } 1550 } 1551 if (authority) { 1552 if (this.lang && state.locale[this.lang].opts[classType] && state.locale[this.lang].opts[classType][country] && state.locale[this.lang].opts[classType][country][authority]) { 1553 cls = state.locale[this.lang].opts[classType][country][authority]; 1554 } else if (state.locale[state.opt["default-locale"][0]].opts[classType] && state.locale[state.opt["default-locale"][0]].opts[classType][country] && state.locale[state.opt["default-locale"][0]].opts[classType][country][authority]) { 1555 cls = state.locale[state.opt["default-locale"][0]].opts[classType][country][authority] 1556 } 1557 } 1558 return cls; 1559 }, 1560 1561 SET_COURT_CLASSES: function(state, lang, myxml, dataObj) { 1562 var nodes = myxml.getNodesByName(dataObj, 'court-class'); 1563 for (var pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) { 1564 var courtclass = nodes[pos]; 1565 var attributes = myxml.attributes(courtclass); 1566 var cls = attributes["@name"]; 1567 var country = attributes["@country"]; 1568 var courts = attributes["@courts"]; 1569 1570 // Okay, this is a hack. 1571 // If state.registry IS NOT yet defined, this is an in-style declaration. 1572 // If state.registry IS defined, this is an in-module declaration. 1573 var classType = "court_key_classes"; 1574 if (state.registry) { 1575 classType = "court_condition_classes"; 1576 } 1577 1578 if (cls && country && courts) { 1579 courts = courts.trim().split(/\s+/); 1580 if (!state.locale[lang].opts[classType]) { 1581 state.locale[lang].opts[classType] = {}; 1582 } 1583 if (!state.locale[lang].opts[classType][country]) { 1584 state.locale[lang].opts[classType][country] = {}; 1585 } 1586 for (var i=0,ilen=courts.length;i<ilen;i++) { 1587 state.locale[lang].opts[classType][country][courts[i]] = cls; 1588 } 1589 } 1590 } 1591 }, 1592 1593 INIT_JURISDICTION_MACROS: function (state, Item, item, macroName) { 1594 if (Item["best-jurisdiction"]) { 1595 return true; 1596 } 1597 if (!state.sys.retrieveStyleModule || !CSL.MODULE_MACROS[macroName] || !Item.jurisdiction) { 1598 return false; 1599 } 1600 var jurisdictionList = state.getJurisdictionList(Item.jurisdiction); 1601 // Set up a list of jurisdictions here, we will reuse it 1602 if (!state.opt.jurisdictions_seen[jurisdictionList[0]]) { 1603 var res = state.retrieveAllStyleModules(jurisdictionList); 1604 // Okay. We have code for each of the novel modules in the 1605 // hierarchy. Load them all into the processor. 1606 for (var jurisdiction in res) { 1607 var fallback = state.loadStyleModule(jurisdiction, res[jurisdiction]); 1608 if (fallback) { 1609 if (!res[fallback]) { 1610 Object.assign(res, state.retrieveAllStyleModules([fallback])); 1611 state.loadStyleModule(fallback, res[fallback], true); 1612 } 1613 } 1614 } 1615 } 1616 var jurisdictionList = state.getJurisdictionList(Item.jurisdiction); 1617 if (state.opt.parallel.enable) { 1618 if (!state.parallel) { 1619 state.parallel = new CSL.Parallel(state); 1620 } 1621 } 1622 // Identify the best jurisdiction for the item and return true, otherwise return false 1623 for (var i=0,ilen=jurisdictionList.length;i<ilen;i++) { 1624 var jurisdiction = jurisdictionList[i]; 1625 if (item) { 1626 if (state.juris[jurisdiction] && !item["best-jurisdiction"] && state.juris[jurisdiction].types.locator) { 1627 Item["best-jurisdiction"] = jurisdiction; 1628 } 1629 } 1630 if(state.juris[jurisdiction] && state.juris[jurisdiction].types[Item.type]) { 1631 Item["best-jurisdiction"] = jurisdiction; 1632 return true; 1633 } 1634 } 1635 return false; 1636 } 1637}; 1638 1639/** 1640 * Functions for parsing an XML object converted to JSON. 1641 */ 1642 1643/* 1644 Style and locale JSON should be formatted as follows. Note that 1645 an empty literal should be set as an explicit empty strings within 1646 children:[] 1647 1648 { 1649 name:"term", 1650 children:[ 1651 "" 1652 ], 1653 attrs:{ 1654 name:"author" 1655 } 1656 } 1657 1658 The following script will generate correctly formatted JSON 1659 from a CSL style or locale file: 1660*/ 1661 1662CSL.XmlJSON = function (dataObj) { 1663 this.dataObj = dataObj; 1664 this.institution = { 1665 name:"institution", 1666 attrs:{ 1667 "institution-parts":"long", 1668 "delimiter":", " 1669 }, 1670 children:[ 1671 { 1672 name:"institution-part", 1673 attrs:{ 1674 name:"long" 1675 }, 1676 children:[] 1677 } 1678 ] 1679 }; 1680}; 1681 1682/** 1683 * No need for cleaning with native JSON. 1684 */ 1685CSL.XmlJSON.prototype.clean = function (json) { 1686 return json; 1687}; 1688 1689 1690/** 1691 * Methods to call on a node. 1692 */ 1693CSL.XmlJSON.prototype.getStyleId = function (myjson, styleName) { 1694 var tagName = 'id'; 1695 if (styleName) { 1696 tagName = 'title'; 1697 } 1698 var ret = ""; 1699 var children = myjson.children; 1700 for (var i=0,ilen=children.length;i<ilen;i++) { 1701 if (children[i].name === 'info') { 1702 var grandkids = children[i].children; 1703 for (var j=0,jlen=grandkids.length;j<jlen;j++) { 1704 if (grandkids[j].name === tagName) { 1705 ret = grandkids[j].children[0]; 1706 } 1707 } 1708 } 1709 } 1710 return ret; 1711}; 1712 1713CSL.XmlJSON.prototype.children = function (myjson) { 1714 //print("children()"); 1715 if (myjson && myjson.children.length) { 1716 return myjson.children.slice(); 1717 } else { 1718 return false; 1719 } 1720}; 1721 1722CSL.XmlJSON.prototype.nodename = function (myjson) { 1723 //print("nodename()"); 1724 return myjson ? myjson.name : null; 1725}; 1726 1727CSL.XmlJSON.prototype.attributes = function (myjson) { 1728 //print("attributes()"); 1729 var ret = {}; 1730 for (var attrname in myjson.attrs) { 1731 ret["@"+attrname] = myjson.attrs[attrname]; 1732 } 1733 return ret; 1734}; 1735 1736 1737CSL.XmlJSON.prototype.content = function (myjson) { 1738 //print("content()"); 1739 // xmldom.js and xmle4x.js have "undefined" as default 1740 var ret = ""; 1741 // This only catches content at first level, but that is good enough 1742 // for us. 1743 if (!myjson || !myjson.children) { 1744 return ret; 1745 } 1746 for (var i=0, ilen=myjson.children.length; i < ilen; i += 1) { 1747 if ("string" === typeof myjson.children[i]) { 1748 ret += myjson.children[i]; 1749 } 1750 } 1751 return ret; 1752}; 1753 1754 1755CSL.XmlJSON.prototype.namespace = {} 1756 1757CSL.XmlJSON.prototype.numberofnodes = function (myjson) { 1758 //print("numberofnodes()"); 1759 if (myjson && "number" == typeof myjson.length) { 1760 return myjson.length; 1761 } else { 1762 return 0; 1763 } 1764}; 1765 1766// getAttributeName() removed. Looks like it was not being used. 1767 1768CSL.XmlJSON.prototype.getAttributeValue = function (myjson,name,namespace) { 1769 //print("getAttributeValue()"); 1770 var ret = ""; 1771 if (namespace) { 1772 name = namespace+":"+name; 1773 } 1774 if (myjson) { 1775 if (myjson.attrs) { 1776 if (myjson.attrs[name]) { 1777 ret = myjson.attrs[name]; 1778 } else { 1779 ret = ""; 1780 } 1781 } 1782 } 1783 return ret; 1784} 1785 1786CSL.XmlJSON.prototype.getNodeValue = function (myjson,name) { 1787 //print("getNodeValue()"); 1788 var ret = ""; 1789 if (name){ 1790 for (var i=0, ilen=myjson.children.length; i < ilen; i += 1) { 1791 if (myjson.children[i].name === name) { 1792 // This will always be Object() unless empty 1793 if (myjson.children[i].children.length) { 1794 ret = myjson.children[i]; 1795 } else { 1796 ret = ""; 1797 } 1798 } 1799 } 1800 } else if (myjson) { 1801 ret = myjson; 1802 } 1803 // Just being careful here, following the former DOM code. The JSON object we receive 1804 // for this should be fully normalized. 1805 if (ret && ret.children && ret.children.length == 1 && "string" === typeof ret.children[0]) { 1806 ret = ret.children[0]; 1807 } 1808 return ret; 1809} 1810 1811CSL.XmlJSON.prototype.setAttributeOnNodeIdentifiedByNameAttribute = function (myjson,nodename,partname,attrname,val) { 1812 //print("setAttributeOnNodeIdentifiedByNameAttribute()"); 1813 var pos, len, xml, nodes, node; 1814 if (attrname.slice(0,1) === '@'){ 1815 attrname = attrname.slice(1); 1816 } 1817 // In the one place this is used in citeproc-js code, it doesn't need to recurse. 1818 for (var i=0,ilen=myjson.children.length; i<ilen; i += 1) { 1819 if (myjson.children[i].name === nodename && myjson.children[i].attrs.name === partname) { 1820 myjson.children[i].attrs[attrname] = val; 1821 } 1822 } 1823} 1824 1825CSL.XmlJSON.prototype.deleteNodeByNameAttribute = function (myjson,val) { 1826 //print("deleteNodeByNameAttribute()"); 1827 var i, ilen; 1828 for (i = 0, ilen = myjson.children.length; i < ilen; i += 1) { 1829 if (!myjson.children[i] || "string" === typeof myjson.children[i]) { 1830 continue; 1831 } 1832 if (myjson.children[i].attrs.name == val) { 1833 myjson.children = myjson.children.slice(0,i).concat(myjson.children.slice(i+1)); 1834 } 1835 } 1836} 1837 1838CSL.XmlJSON.prototype.deleteAttribute = function (myjson,attrname) { 1839 //print("deleteAttribute()"); 1840 var i, ilen; 1841 if ("undefined" !== typeof myjson.attrs[attrname]) { 1842 myjson.attrs.pop(attrname); 1843 } 1844} 1845 1846CSL.XmlJSON.prototype.setAttribute = function (myjson,attr,val) { 1847 //print("setAttribute()"); 1848 myjson.attrs[attr] = val; 1849 return false; 1850} 1851 1852CSL.XmlJSON.prototype.nodeCopy = function (myjson,clone) { 1853 //print("nodeCopy()"); 1854 if (!clone) { 1855 var clone = {}; 1856 } 1857 if ("object" === typeof clone && "undefined" === typeof clone.length) { 1858 // myjson is an object 1859 for (var key in myjson) { 1860 if ("string" === typeof myjson[key]) { 1861 clone[key] = myjson[key]; 1862 } else if ("object" === typeof myjson[key]) { 1863 if ("undefined" === typeof myjson[key].length) { 1864 clone[key] = this.nodeCopy(myjson[key],{}); 1865 } else { 1866 clone[key] = this.nodeCopy(myjson[key],[]); 1867 } 1868 } 1869 } 1870 } else { 1871 // myjson is an array 1872 for (var i=0,ilen=myjson.length;i<ilen; i += 1) { 1873 if ("string" === typeof myjson[i]) { 1874 clone[i] = myjson[i]; 1875 } else { 1876 // If it's at the first level of an array, it's an object. 1877 clone[i] = this.nodeCopy(myjson[i],{}); 1878 } 1879 } 1880 } 1881 return clone; 1882} 1883 1884CSL.XmlJSON.prototype.getNodesByName = function (myjson,name,nameattrval,ret) { 1885 //print("getNodesByName()"); 1886 var nodes, node, pos, len; 1887 if (!ret) { 1888 var ret = []; 1889 } 1890 if (!myjson || !myjson.children) { 1891 return ret; 1892 } 1893 if (name === myjson.name) { 1894 if (nameattrval) { 1895 if (nameattrval === myjson.attrs.name) { 1896 ret.push(myjson); 1897 } 1898 } else { 1899 ret.push(myjson); 1900 } 1901 } 1902 for (var i=0,ilen=myjson.children.length;i<ilen;i+=1){ 1903 if ("object" !== typeof myjson.children[i]) { 1904 continue; 1905 } 1906 this.getNodesByName(myjson.children[i],name,nameattrval,ret); 1907 } 1908 return ret; 1909} 1910 1911CSL.XmlJSON.prototype.nodeNameIs = function (myjson,name) { 1912 //print("nodeNameIs()"); 1913 if (typeof myjson === "undefined") { 1914 return false; 1915 } 1916 if (name == myjson.name) { 1917 return true; 1918 } 1919 return false; 1920} 1921 1922CSL.XmlJSON.prototype.makeXml = function (myjson) { 1923 //print("makeXml()"); 1924 if ("string" === typeof myjson) { 1925 if (myjson.slice(0, 1) === "<") { 1926 myjson = this.jsonStringWalker.walkToObject(myjson); 1927 } else { 1928 myjson = JSON.parse(myjson); 1929 } 1930 } 1931 return myjson; 1932}; 1933 1934CSL.XmlJSON.prototype.insertChildNodeAfter = function (parent,node,pos,datejson) { 1935 //print("insertChildNodeAfter()"); 1936 // Function is misnamed: this replaces the node 1937 for (var i=0,ilen=parent.children.length;i<ilen;i+=1) { 1938 if (node === parent.children[i]) { 1939 parent.children = parent.children.slice(0,i).concat([datejson]).concat(parent.children.slice(i+1)); 1940 break; 1941 } 1942 } 1943 return parent; 1944}; 1945 1946 1947CSL.XmlJSON.prototype.insertPublisherAndPlace = function(myjson) { 1948 if (myjson.name === "group") { 1949 var useme = true; 1950 var mustHaves = ["publisher","publisher-place"]; 1951 for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) { 1952 var haveVarname = mustHaves.indexOf(myjson.children[i].attrs.variable); 1953 var isText = myjson.children[i].name === "text"; 1954 if (isText && haveVarname > -1 && !myjson.children[i].attrs.prefix && !myjson.children[i].attrs.suffix) { 1955 mustHaves = mustHaves.slice(0,haveVarname).concat(mustHaves.slice(haveVarname+1)); 1956 } else { 1957 useme = false; 1958 break; 1959 } 1960 } 1961 if (useme && !mustHaves.length) { 1962 myjson.attrs["has-publisher-and-publisher-place"] = true; 1963 } 1964 } 1965 for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) { 1966 if ("object" === typeof myjson.children[i]) { 1967 this.insertPublisherAndPlace(myjson.children[i]); 1968 } 1969 } 1970} 1971/* 1972CSL.XmlJSON.prototype.insertPublisherAndPlace = function(myxml) { 1973 var group = myxml.getElementsByTagName("group"); 1974 for (var i = 0, ilen = group.length; i < ilen; i += 1) { 1975 var node = group.item(i); 1976 var skippers = []; 1977 for (var j = 0, jlen = node.childNodes.length; j < jlen; j += 1) { 1978 if (node.childNodes.item(j).nodeType !== 1) { 1979 skippers.push(j); 1980 } 1981 } 1982 if (node.childNodes.length - skippers.length === 2) { 1983 var twovars = []; 1984 for (var j = 0, jlen = 2; j < jlen; j += 1) { 1985 if (skippers.indexOf(j) > -1) { 1986 continue; 1987 } 1988 var child = node.childNodes.item(j); 1989 var subskippers = []; 1990 for (var k = 0, klen = child.childNodes.length; k < klen; k += 1) { 1991 if (child.childNodes.item(k).nodeType !== 1) { 1992 subskippers.push(k); 1993 } 1994 } 1995 if (child.childNodes.length - subskippers.length === 0) { 1996 twovars.push(child.getAttribute('variable')); 1997 if (child.getAttribute('suffix') 1998 || child.getAttribute('prefix')) { 1999 twovars = []; 2000 break; 2001 } 2002 } 2003 } 2004 if (twovars.indexOf("publisher") > -1 && twovars.indexOf("publisher-place") > -1) { 2005 node.setAttribute('has-publisher-and-publisher-place', true); 2006 } 2007 } 2008 } 2009}; 2010*/ 2011 2012CSL.XmlJSON.prototype.isChildOfSubstitute = function(parents) { 2013 if (parents.length > 0) { 2014 var myparents = parents.slice(); 2015 var parent = myparents.pop(); 2016 if (parent === "substitute") { 2017 return true; 2018 } else { 2019 return this.isChildOfSubstitute(myparents); 2020 } 2021 } 2022 return false; 2023}; 2024 2025CSL.XmlJSON.prototype.addMissingNameNodes = function(myjson,parents) { 2026 if (!parents) { 2027 parents = []; 2028 } 2029 if (myjson.name === "names") { 2030 // Trawl through children to decide whether a name node is needed here 2031 if (!this.isChildOfSubstitute(parents)) { 2032 var addName = true; 2033 for (var i=0,ilen=myjson.children.length;i<ilen;i++) { 2034 if (myjson.children[i].name === "name") { 2035 addName = false; 2036 break; 2037 } 2038 } 2039 if (addName) { 2040 myjson.children = [{name:"name",attrs:{},children:[]}].concat(myjson.children); 2041 } 2042 } 2043 } 2044 parents.push(myjson.name); 2045 for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) { 2046 if ("object" === typeof myjson.children[i]) { 2047 this.addMissingNameNodes(myjson.children[i],parents); 2048 } 2049 } 2050 parents.pop(); 2051} 2052 2053 2054CSL.XmlJSON.prototype.addInstitutionNodes = function(myjson) { 2055 //print("addInstitutionNodes()"); 2056 var names, thenames, institution, theinstitution, name, thename, xml, pos, len; 2057 // The idea here is to map relevant attributes from name and nampart=family 2058 // to the "long" institution-part node, when and only when forcing insert 2059 // of the default node. 2060 if (myjson.name === "names") { 2061 // do stuff 2062 var attributes = {}; 2063 var insertPos = -1; 2064 for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) { 2065 if (myjson.children[i].name == "name") { 2066 for (var key in myjson.children[i].attrs) { 2067 attributes[key] = myjson.children[i].attrs[key]; 2068 } 2069 attributes.delimiter = myjson.children[i].attrs.delimiter; 2070 attributes.and = myjson.children[i].attrs.and; 2071 insertPos = i; 2072 for (var k=0,klen=myjson.children[i].children.length;k<klen;k+=1) { 2073 if (myjson.children[i].children[k].attrs.name !== 'family') { 2074 continue; 2075 } 2076 for (var key in myjson.children[i].children[k].attrs) { 2077 attributes[key] = myjson.children[i].children[k].attrs[key]; 2078 } 2079 } 2080 } 2081 if (myjson.children[i].name == "institution") { 2082 insertPos = -1; 2083 break; 2084 } 2085 } 2086 if (insertPos > -1) { 2087 var institution = this.nodeCopy(this.institution); 2088 for (var i=0,ilen = CSL.INSTITUTION_KEYS.length;i<ilen;i+=1) { 2089 var attrname = CSL.INSTITUTION_KEYS[i]; 2090 if ("undefined" !== typeof attributes[attrname]) { 2091 institution.children[0].attrs[attrname] = attributes[attrname]; 2092 } 2093 if (attributes.delimiter) { 2094 institution.attrs.delimiter = attributes.delimiter; 2095 } 2096 if (attributes.and) { 2097 institution.attrs.and = attributes.and; 2098 } 2099 } 2100 myjson.children = myjson.children.slice(0,insertPos+1).concat([institution]).concat(myjson.children.slice(insertPos+1)); 2101 } 2102 } 2103 for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) { 2104 if ("string" === typeof myjson.children[i]) { 2105 continue; 2106 } 2107 // Recurse 2108 this.addInstitutionNodes(myjson.children[i]); 2109 } 2110} 2111CSL.XmlJSON.prototype.flagDateMacros = function(myjson) { 2112 // print("flagDateMacros()"); 2113 for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) { 2114 if (myjson.children[i].name === "macro") { 2115 if (this.inspectDateMacros(myjson.children[i])) { 2116 myjson.children[i].attrs["macro-has-date"] = "true"; 2117 } 2118 } 2119 } 2120} 2121CSL.XmlJSON.prototype.inspectDateMacros = function(myjson) { 2122 //print("inspectDateMacros()"); 2123 if (!myjson || !myjson.children) { 2124 return false; 2125 } 2126 if (myjson.name === "date") { 2127 return true; 2128 } else { 2129 for (var i=0,ilen=myjson.children.length;i<ilen;i+=1) { 2130 if (this.inspectDateMacros(myjson.children[i])) { 2131 return true; 2132 } 2133 } 2134 } 2135 return false; 2136} 2137 2138/* 2139 * Clean serialized XML 2140 */ 2141CSL.stripXmlProcessingInstruction = function (xml) { 2142 if (!xml) { 2143 return xml; 2144 } 2145 xml = xml.replace(/^<\?[^?]+\?>/, ""); 2146 xml = xml.replace(/<!--[^>]+-->/g, ""); 2147 xml = xml.replace(/^\s+/g, ""); 2148 xml = xml.replace(/\s+$/g, ""); 2149 return xml; 2150}; 2151 2152 2153/* 2154 * String parser for XML inputs 2155 */ 2156CSL.parseXml = function(str) { 2157 2158 var _pos = 0; 2159 var _obj = {children:[]}; 2160 var _stack = [_obj.children]; 2161 2162 function _listifyString(str) { 2163 str = str.split(/(?:\r\n|\n|\r)/).join(" ").replace(/>[ ]+</g, "><").replace(/<\!--.*?-->/g, ""); 2164 var lst = str.split("><"); 2165 var stylePos = null; 2166 for (var i=0,ilen=lst.length;i<ilen;i++) { 2167 if (i > 0) { 2168 lst[i] = "<" + lst[i]; 2169 } 2170 if (i < (lst.length-1)) { 2171 lst[i] = lst[i] + ">"; 2172 } 2173 if ("number" != typeof stylePos) { 2174 if (lst[i].slice(0, 7) === "<style " || lst[i].slice(0, 8) == "<locale ") { 2175 stylePos = i; 2176 } 2177 } 2178 } 2179 lst = lst.slice(stylePos); 2180 // Combine open/close elements for empty terms, 2181 // so that they will be passed through correctly 2182 // as empty strings. 2183 for (var i=lst.length-2;i>-1;i--) { 2184 if (lst[i].slice(1).indexOf("<") === -1) { 2185 var stub = lst[i].slice(0, 5); 2186 if (lst[i].slice(-2) !== "/>") { 2187 if (stub === "<term") { 2188 if (lst[i+1].slice(0, 6) === "</term") { 2189 lst[i] = lst[i] + lst[i+1]; 2190 lst = lst.slice(0, i+1).concat(lst.slice(i+2)); 2191 } 2192 } else if (["<sing", "<mult"].indexOf(stub) > -1) { 2193 if (lst[i].slice(-2) !== "/>" && lst[i+1].slice(0, 1) === "<") { 2194 lst[i] = lst[i] + lst[i+1]; 2195 lst = lst.slice(0, i+1).concat(lst.slice(i+2)); 2196 } 2197 } 2198 } 2199 } 2200 } 2201 return lst; 2202 } 2203 2204 function _decodeHtmlEntities(str) { 2205 return str 2206 .split("&").join("&") 2207 .split(""").join("\"") 2208 .split(">").join(">").split("<").join("<") 2209 .replace(/&#([0-9]{1,6});/gi, function(match, numStr) { 2210 var num = parseInt(numStr, 10); // read num as normal number 2211 return String.fromCharCode(num); 2212 }) 2213 .replace(/&#x([a-f0-9]{1,6});/gi, function(match, numStr){ 2214 var num = parseInt(numStr, 16); // read num as hex 2215 return String.fromCharCode(num); 2216 }); 2217 } 2218 2219 function _getAttributes(elem) { 2220 var m = elem.match(/([^\'\"= ]+)=(?:\"[^\"]*\"|\'[^\']*\')/g); 2221 if (m) { 2222 for (var i=0,ilen=m.length;i<ilen;i++) { 2223 m[i] = m[i].replace(/=.*/, ""); 2224 } 2225 } 2226 return m; 2227 } 2228 2229 function _getAttribute(elem, attr) { 2230 var rex = RegExp('^.*[ ]+' + attr + '=(\"(?:[^\"]*)\"|\'(?:[^\']*)\').*$'); 2231 var m = elem.match(rex); 2232 return m ? m[1].slice(1, -1) : null; 2233 } 2234 2235 function _getTagName(elem) { 2236 var rex = RegExp("^<([^ />]+)"); 2237 var m = elem.match(rex); 2238 return m ? m[1] : null; 2239 } 2240 2241 2242 function _castObjectFromOpeningTag(elem) { 2243 var obj = {}; 2244 obj.name = _getTagName(elem); 2245 obj.attrs = {}; 2246 var attributes = _getAttributes(elem); 2247 if (attributes) { 2248 for (var i=0,ilen=attributes.length;i<ilen;i++) { 2249 var attr = { 2250 name: attributes[i], 2251 value: _getAttribute(elem, attributes[i]) 2252 } 2253 obj.attrs[attr.name] = _decodeHtmlEntities(attr.value); 2254 } 2255 } 2256 obj.children = []; 2257 return obj; 2258 } 2259 2260 function _extractTextFromCompositeElement(elem) { 2261 var m = elem.match(/^.*>([^<]*)<.*$/); 2262 return _decodeHtmlEntities(m[1]); 2263 } 2264 2265 function _appendToChildren(obj) { 2266 _stack.slice(-1)[0].push(obj); 2267 } 2268 2269 function _extendStackWithNewChildren(obj) { 2270 _stack.push(obj.children); 2271 } 2272 2273 function processElement(elem) { 2274 var obj; 2275 if (elem.slice(1).indexOf('<') > -1) { 2276 // withtext 2277 var tag = elem.slice(0, elem.indexOf('>')+1); 2278 obj = _castObjectFromOpeningTag(tag); 2279 obj.children = [_extractTextFromCompositeElement(elem)]; 2280 _appendToChildren(obj); 2281 } else if (elem.slice(-2) === '/>') { 2282 // singleton 2283 obj = _castObjectFromOpeningTag(elem); 2284 // Empty term as singleton 2285 if (_getTagName(elem) === 'term') { 2286 obj.children.push(''); 2287 } 2288 _appendToChildren(obj); 2289 } else if (elem.slice(0, 2) === '</') { 2290 // close 2291 _stack.pop(); 2292 } else { 2293 // open 2294 obj = _castObjectFromOpeningTag(elem); 2295 _appendToChildren(obj) 2296 _extendStackWithNewChildren(obj); 2297 } 2298 } 2299 2300 var lst = _listifyString(str); 2301 2302 for (var i=0,ilen=lst.length;i<ilen;i++) { 2303 var elem = lst[i]; 2304 processElement(elem); 2305 } 2306 return _obj.children[0]; 2307} 2308 2309/** 2310 * Functions for parsing an XML object using E4X. 2311 */ 2312 2313CSL.XmlDOM = function (dataObj) { 2314 this.dataObj = dataObj; 2315 if ("undefined" == typeof DOMParser) { 2316 DOMParser = function() {}; 2317 DOMParser.prototype.parseFromString = function(str, contentType) { 2318 if ("undefined" != typeof ActiveXObject) { 2319 var xmldata = new ActiveXObject('MSXML.DomDocument'); 2320 xmldata.async = false; 2321 xmldata.loadXML(str); 2322 return xmldata; 2323 } else if ("undefined" != typeof XMLHttpRequest) { 2324 var xmldata = new XMLHttpRequest; 2325 if (!contentType) { 2326 contentType = 'text/xml'; 2327 } 2328 xmldata.open('GET', 'data:' + contentType + ';charset=utf-8,' + encodeURIComponent(str), false); 2329 if(xmldata.overrideMimeType) { 2330 xmldata.overrideMimeType(contentType); 2331 } 2332 xmldata.send(null); 2333 return xmldata.responseXML; 2334 } else if ("undefined" != typeof marknote) { 2335 var parser = new marknote.Parser(); 2336 return parser.parse(str); 2337 } 2338 }; 2339 this.hasAttributes = function (node) { 2340 var ret; 2341 if (node.attributes && node.attributes.length) { 2342 ret = true; 2343 } else { 2344 ret = false; 2345 } 2346 return ret; 2347 }; 2348 } else { 2349 /* 2350 this.hasAttributes = function (node) { 2351 return node["hasAttributes"](); 2352 }; 2353 */ 2354 this.hasAttributes = function (node) { 2355 var ret; 2356 if (node.attributes && node.attributes.length) { 2357 ret = true; 2358 } else { 2359 ret = false; 2360 } 2361 return ret; 2362 }; 2363 } 2364 this.importNode = function (doc, srcElement) { 2365 var ret; 2366 if ("undefined" == typeof doc.importNode) { 2367 ret = this._importNode(doc, srcElement, true); 2368 } else { 2369 ret = doc.importNode(srcElement, true); 2370 } 2371 return ret; 2372 }; 2373 // In case importNode is not available. 2374 // Thx + hat tip to Anthony T. Holdener III 2375 // http://www.alistapart.com/articles/crossbrowserscripting 2376 // cases 3, 4, 8 = text, cdata, comment 2377 this._importNode = function(doc, node, allChildren) { 2378 switch (node.nodeType) { 2379 // element node 2380 case 1: 2381 var newNode = doc.createElement(node.nodeName); 2382 if (node.attributes && node.attributes.length > 0) 2383 for (var i = 0, il = node.attributes.length; i < il;) 2384 newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i++].nodeName)); 2385 if (allChildren && node.childNodes && node.childNodes.length > 0) 2386 for (var i = 0, il = node.childNodes.length; i < il;) 2387 newNode.appendChild(this._importNode(doc, node.childNodes[i++], allChildren)); 2388 return newNode; 2389 break; 2390 case 3: 2391 case 4: 2392 case 8: 2393 // Drop comments on the floor as well. 2394 //return doc.createTextNode(node.nodeValue); 2395 //break; 2396 } 2397 }; 2398 this.parser = new DOMParser(); 2399 2400 // This seems horribly tormented, but there might be a reason for it. 2401 // Perhaps this was the only way I found to get namespacing to work ... ? 2402 var str = "<docco><institution institution-parts=\"long\" delimiter=\", \" substitute-use-first=\"1\" use-last=\"1\"><institution-part name=\"long\"/></institution></docco>"; 2403 var inst_doc = this.parser.parseFromString(str, "text/xml"); 2404 var inst_node = inst_doc.getElementsByTagName("institution"); 2405 this.institution = inst_node.item(0); 2406 var inst_part_node = inst_doc.getElementsByTagName("institution-part"); 2407 this.institutionpart = inst_part_node.item(0); 2408 this.ns = "http://purl.org/net/xbiblio/csl"; 2409}; 2410 2411/** 2412 * No need for cleaning with the DOM, I think. This will probably just be a noop. 2413 * But first, let's get XML mode switching up and running. 2414 */ 2415CSL.XmlDOM.prototype.clean = function (xml) { 2416 xml = xml.replace(/<\?[^?]+\?>/g, ""); 2417 xml = xml.replace(/<![^>]+>/g, ""); 2418 xml = xml.replace(/^\s+/, ""); 2419 xml = xml.replace(/\s+$/, ""); 2420 xml = xml.replace(/^\n*/, ""); 2421 return xml; 2422}; 2423 2424 2425/** 2426 * Methods to call on a node. 2427 */ 2428CSL.XmlDOM.prototype.getStyleId = function (myxml, styleName) { 2429 var text = ""; 2430 var tagName = "id"; 2431 if (styleName) { 2432 tagName = "title"; 2433 } 2434 var node = myxml.getElementsByTagName(tagName); 2435 if (node && node.length) { 2436 node = node.item(0); 2437 } 2438 if (node) { 2439 // W3C conformant browsers 2440 text = node.textContent; 2441 } 2442 if (!text) { 2443 // Opera, IE 6 & 7 2444 text = node.innerText; 2445 } 2446 if (!text) { 2447 // Safari 2448 text = node.innerHTML; 2449 } 2450 return text; 2451}; 2452 2453CSL.XmlDOM.prototype.children = function (myxml) { 2454 var children, pos, len, ret; 2455 if (myxml) { 2456 ret = []; 2457 children = myxml.childNodes; 2458 for (pos = 0, len = children.length; pos < len; pos += 1) { 2459 if (children[pos].nodeName != "#text") { 2460 ret.push(children[pos]); 2461 } 2462 } 2463 return ret; 2464 } else { 2465 return []; 2466 } 2467}; 2468 2469CSL.XmlDOM.prototype.nodename = function (myxml) { 2470 var ret = myxml.nodeName; 2471 return ret; 2472}; 2473 2474CSL.XmlDOM.prototype.attributes = function (myxml) { 2475 var ret, attrs, attr, key, xml, pos, len; 2476 ret = new Object(); 2477 if (myxml && this.hasAttributes(myxml)) { 2478 attrs = myxml.attributes; 2479 for (pos = 0, len=attrs.length; pos < len; pos += 1) { 2480 attr = attrs[pos]; 2481 ret["@" + attr.name] = attr.value; 2482 } 2483 } 2484 return ret; 2485}; 2486 2487 2488CSL.XmlDOM.prototype.content = function (myxml) { 2489 var ret; 2490 if ("undefined" != typeof myxml.textContent) { 2491 ret = myxml.textContent; 2492 } else if ("undefined" != typeof myxml.innerText) { 2493 ret = myxml.innerText; 2494 } else { 2495 ret = myxml.txt; 2496 } 2497 return ret; 2498}; 2499 2500 2501CSL.XmlDOM.prototype.namespace = { 2502 "xml":"http://www.w3.org/XML/1998/namespace" 2503} 2504 2505CSL.XmlDOM.prototype.numberofnodes = function (myxml) { 2506 if (myxml) { 2507 return myxml.length; 2508 } else { 2509 return 0; 2510 } 2511}; 2512 2513CSL.XmlDOM.prototype.getAttributeName = function (attr) { 2514 var ret = attr.name; 2515 return ret; 2516} 2517 2518CSL.XmlDOM.prototype.getAttributeValue = function (myxml,name,namespace) { 2519 var ret = ""; 2520 if (namespace) { 2521 name = namespace+":"+name; 2522 } 2523 if (myxml && this.hasAttributes(myxml) && myxml.getAttribute(name)) { 2524 ret = myxml.getAttribute(name); 2525 } 2526 return ret; 2527} 2528 2529// 2530// Can't this be, you know ... simplified? 2531// 2532CSL.XmlDOM.prototype.getNodeValue = function (myxml,name) { 2533 var ret = null; 2534 if (name){ 2535 var vals = myxml.getElementsByTagName(name); 2536 if (vals.length > 0) { 2537 if ("undefined" != typeof vals[0].textContent) { 2538 ret = vals[0].textContent; 2539 } else if ("undefined" != typeof vals[0].innerText) { 2540 ret = vals[0].innerText; 2541 } else { 2542 ret = vals[0].text; 2543 } 2544 } 2545 } 2546 if (ret === null && myxml && myxml.childNodes && (myxml.childNodes.length == 0 || (myxml.childNodes.length == 1 && myxml.firstChild.nodeName == "#text"))) { 2547 if ("undefined" != typeof myxml.textContent) { 2548 ret = myxml.textContent; 2549 } else if ("undefined" != typeof myxml.innerText) { 2550 ret = myxml.innerText; 2551 } else { 2552 ret = myxml.text; 2553 } 2554 } 2555 if (ret === null) { 2556 ret = myxml; 2557 } 2558 return ret; 2559} 2560 2561CSL.XmlDOM.prototype.setAttributeOnNodeIdentifiedByNameAttribute = function (myxml,nodename,partname,attrname,val) { 2562 var pos, len, xml, nodes, node; 2563 if (attrname.slice(0,1) === '@'){ 2564 attrname = attrname.slice(1); 2565 } 2566 nodes = myxml.getElementsByTagName(nodename); 2567 for (pos = 0, len = nodes.length; pos < len; pos += 1) { 2568 node = nodes[pos]; 2569 if (node.getAttribute("name") != partname) { 2570 continue; 2571 } 2572 node.setAttribute(attrname, val); 2573 } 2574} 2575 2576CSL.XmlDOM.prototype.deleteNodeByNameAttribute = function (myxml,val) { 2577 var pos, len, node, nodes; 2578 nodes = myxml.childNodes; 2579 for (pos = 0, len = nodes.length; pos < len; pos += 1) { 2580 node = nodes[pos]; 2581 if (!node || node.nodeType == node.TEXT_NODE) { 2582 continue; 2583 } 2584 if (this.hasAttributes(node) && node.getAttribute("name") == val) { 2585 myxml.removeChild(nodes[pos]); 2586 } 2587 } 2588} 2589 2590CSL.XmlDOM.prototype.deleteAttribute = function (myxml,attr) { 2591 myxml.removeAttribute(attr); 2592} 2593 2594CSL.XmlDOM.prototype.setAttribute = function (myxml,attr,val) { 2595 if (!myxml.ownerDocument) { 2596 myxml = myxml.firstChild; 2597 } 2598 // "unknown" to satisfy IE8, which crashes when setAttribute 2599 // is checked directly as a property, and report its type as 2600 // "unknown". 2601 // Many thanks to Phil Lord for tracing the cause of the fault. 2602 if (["function", "unknown"].indexOf(typeof myxml.setAttribute) > -1) { 2603 myxml.setAttribute(attr, val); 2604 } 2605 return false; 2606} 2607 2608CSL.XmlDOM.prototype.nodeCopy = function (myxml) { 2609 var cloned_node = myxml.cloneNode(true); 2610 return cloned_node; 2611} 2612 2613CSL.XmlDOM.prototype.getNodesByName = function (myxml,name,nameattrval) { 2614 var ret, nodes, node, pos, len; 2615 ret = []; 2616 nodes = myxml.getElementsByTagName(name); 2617 for (pos = 0, len = nodes.length; pos < len; pos += 1) { 2618 node = nodes.item(pos); 2619 if (nameattrval && !(this.hasAttributes(node) && node.getAttribute("name") == nameattrval)) { 2620// if (nameattrval && !(this.attributes && node.attributes.name && node.attributes.name.value == nameattrval)) { 2621 continue; 2622 } 2623 ret.push(node); 2624 } 2625 return ret; 2626} 2627 2628CSL.XmlDOM.prototype.nodeNameIs = function (myxml,name) { 2629 if (name == myxml.nodeName) { 2630 return true; 2631 } 2632 return false; 2633} 2634 2635CSL.XmlDOM.prototype.makeXml = function (myxml) { 2636 var ret, topnode; 2637 if (!myxml) { 2638 myxml = "<docco><bogus/></docco>"; 2639 } 2640 myxml = myxml.replace(/\s*<\?[^>]*\?>\s*\n*/g, ""); 2641 var nodetree = this.parser.parseFromString(myxml, "application/xml"); 2642 return nodetree.firstChild; 2643}; 2644 2645CSL.XmlDOM.prototype.insertChildNodeAfter = function (parent,node,pos,datexml) { 2646 var myxml, xml; 2647 myxml = this.importNode(node.ownerDocument, datexml); 2648 parent.replaceChild(myxml, node); 2649 return parent; 2650}; 2651 2652CSL.XmlDOM.prototype.insertPublisherAndPlace = function(myxml) { 2653 var group = myxml.getElementsByTagName("group"); 2654 for (var i = 0, ilen = group.length; i < ilen; i += 1) { 2655 var node = group.item(i); 2656 var skippers = []; 2657 for (var j = 0, jlen = node.childNodes.length; j < jlen; j += 1) { 2658 if (node.childNodes.item(j).nodeType !== 1) { 2659 skippers.push(j); 2660 } 2661 } 2662 if (node.childNodes.length - skippers.length === 2) { 2663 var twovars = []; 2664 for (var j = 0, jlen = 2; j < jlen; j += 1) { 2665 if (skippers.indexOf(j) > -1) { 2666 continue; 2667 } 2668 var child = node.childNodes.item(j); 2669 var subskippers = []; 2670 for (var k = 0, klen = child.childNodes.length; k < klen; k += 1) { 2671 if (child.childNodes.item(k).nodeType !== 1) { 2672 subskippers.push(k); 2673 } 2674 } 2675 if (child.childNodes.length - subskippers.length === 0) { 2676 twovars.push(child.getAttribute('variable')); 2677 if (child.getAttribute('suffix') 2678 || child.getAttribute('prefix')) { 2679 twovars = []; 2680 break; 2681 } 2682 } 2683 } 2684 if (twovars.indexOf("publisher") > -1 && twovars.indexOf("publisher-place") > -1) { 2685 node.setAttribute('has-publisher-and-publisher-place', true); 2686 } 2687 } 2688 } 2689}; 2690 2691CSL.XmlDOM.prototype.isChildOfSubstitute = function(node) { 2692 if (node.parentNode) { 2693 if (node.parentNode.tagName.toLowerCase() === "substitute") { 2694 return true; 2695 } else { 2696 return this.isChildOfSubstitute(node.parentNode); 2697 } 2698 } 2699 return false; 2700}; 2701 2702CSL.XmlDOM.prototype.addMissingNameNodes = function(myxml) { 2703 var nameslist = myxml.getElementsByTagName("names"); 2704 for (var i = 0, ilen = nameslist.length; i < ilen; i += 1) { 2705 var names = nameslist.item(i); 2706 var namelist = names.getElementsByTagName("name"); 2707 if ((!namelist || namelist.length === 0) 2708 && !this.isChildOfSubstitute(names)) { 2709 2710 var doc = names.ownerDocument; 2711 var name = doc.createElement("name"); 2712 names.appendChild(name); 2713 } 2714 } 2715}; 2716 2717 2718CSL.XmlDOM.prototype.addInstitutionNodes = function(myxml) { 2719 var names, thenames, institution, theinstitution, theinstitutionpart, name, thename, xml, pos, len; 2720 names = myxml.getElementsByTagName("names"); 2721 for (pos = 0, len = names.length; pos < len; pos += 1) { 2722 thenames = names.item(pos); 2723 name = thenames.getElementsByTagName("name"); 2724 if (name.length == 0) { 2725 continue; 2726 } 2727 institution = thenames.getElementsByTagName("institution"); 2728 if (institution.length == 0) { 2729 theinstitution = this.importNode(myxml.ownerDocument, this.institution); 2730 theinstitutionpart = theinstitution.getElementsByTagName("institution-part").item(0); 2731 thename = name.item(0); 2732 thenames.insertBefore(theinstitution, thename.nextSibling); 2733 for (var j = 0, jlen = CSL.INSTITUTION_KEYS.length; j < jlen; j += 1) { 2734 var attrname = CSL.INSTITUTION_KEYS[j]; 2735 var attrval = thename.getAttribute(attrname); 2736 if (attrval) { 2737 theinstitutionpart.setAttribute(attrname, attrval); 2738 } 2739 } 2740 var nameparts = thename.getElementsByTagName("name-part"); 2741 for (var j = 0, jlen = nameparts.length; j < jlen; j += 1) { 2742 if ('family' === nameparts[j].getAttribute('name')) { 2743 for (var k = 0, klen = CSL.INSTITUTION_KEYS.length; k < klen; k += 1) { 2744 var attrname = CSL.INSTITUTION_KEYS[k]; 2745 var attrval = nameparts[j].getAttribute(attrname); 2746 if (attrval) { 2747 theinstitutionpart.setAttribute(attrname, attrval); 2748 } 2749 } 2750 } 2751 } 2752 } 2753 } 2754}; 2755 2756 2757CSL.XmlDOM.prototype.flagDateMacros = function(myxml) { 2758 var pos, len, thenode, thedate; 2759 var nodes = myxml.getElementsByTagName("macro"); 2760 for (pos = 0, len = nodes.length; pos < len; pos += 1) { 2761 thenode = nodes.item(pos); 2762 thedate = thenode.getElementsByTagName("date"); 2763 if (thedate.length) { 2764 thenode.setAttribute('macro-has-date', 'true'); 2765 } 2766 } 2767}; 2768 2769 2770/*global CSL: true */ 2771 2772CSL.setupXml = function(xmlObject) { 2773 var dataObj = {}; 2774 var parser = null; 2775 if ("undefined" !== typeof xmlObject) { 2776 if ("string" === typeof xmlObject) { 2777 xmlObject = xmlObject.replace("^\uFEFF", "") 2778 .replace(/^\s+/, ""); 2779 if (xmlObject.slice(0, 1) === "<") { 2780 // Assume serialized XML 2781 dataObj = CSL.parseXml(xmlObject); 2782 } else { 2783 // Assume serialized JSON 2784 dataObj = JSON.parse(xmlObject); 2785 } 2786 parser = new CSL.XmlJSON(dataObj); 2787 } else if ("undefined" !== typeof xmlObject.getAttribute) { 2788 // Assume DOM instance 2789 parser = new CSL.XmlDOM(xmlObject); 2790 } else if ("undefined" !== typeof xmlObject.toXMLString) { 2791 // Assume E4X object 2792 parser = new CSL.XmlE4X(xmlObject); 2793 } else { 2794 // Assume JS object 2795 parser = new CSL.XmlJSON(xmlObject); 2796 } 2797 } else { 2798 CSL.error("unable to parse XML input"); 2799 } 2800 if (!parser) { 2801 CSL.error("citeproc-js error: unable to parse CSL style or locale object"); 2802 } 2803 return parser; 2804}; 2805 2806/*global CSL: true */ 2807 2808CSL.getSortCompare = function (default_locale) { 2809 if (CSL.stringCompare) { 2810 return CSL.stringCompare; 2811 } 2812 var me = this; 2813 var strcmp; 2814 var strcmp_opts = { 2815 sensitivity:"base", 2816 ignorePunctuation:true, 2817 numeric:true 2818 }; 2819 // In order, attempt the following: 2820 // (1) Set locale collation from processor language 2821 // (2) Use localeCompare() 2822 if (!default_locale) { 2823 default_locale = "en-US"; 2824 } 2825 strcmp = function (a, b) { 2826 return CSL.toLocaleLowerCase.call(me, a).localeCompare(CSL.toLocaleLowerCase.call(me, b),default_locale,strcmp_opts); 2827 }; 2828 var stripPunct = function (str) { 2829 return str.replace(/^[\[\]\'\"]*/g, ""); 2830 }; 2831 var getBracketPreSort = function () { 2832 if (!strcmp("[x","x")) { 2833 return false; 2834 } else { 2835 return function (a, b) { 2836 return strcmp(stripPunct(a), stripPunct(b)); 2837 }; 2838 } 2839 }; 2840 var bracketPreSort = getBracketPreSort(); 2841 var sortCompare = function (a, b) { 2842 if (bracketPreSort) { 2843 return bracketPreSort(a, b); 2844 } else { 2845 return strcmp(a, b); 2846 } 2847 }; 2848 return sortCompare; 2849}; 2850 2851/*global CSL: true */ 2852 2853CSL.ambigConfigDiff = function(a, b) { 2854 var pos, len, ppos, llen; 2855 // return of true means the ambig configs differ 2856 if (a.names.length !== b.names.length) { 2857 //print(" (1)"); 2858 return 1; 2859 } else { 2860 for (pos = 0, len = a.names.length; pos < len; pos += 1) { 2861 if (a.names[pos] !== b.names[pos]) { 2862 //print(" (2) "+a.names[pos]+" "+b.names[pos]); 2863 return 1; 2864 } else { 2865 for (ppos = 0, llen = a.givens[pos]; ppos < llen; ppos += 1) { 2866 if (a.givens[pos][ppos] !== b.givens[pos][ppos]) { 2867 //print(" (3): "+a.givens[pos][ppos]+" "+b.givens[pos][ppos]+" "+pos+"/"+ppos+" "+b.givens[pos]); 2868 return 1; 2869 } 2870 } 2871 } 2872 } 2873 } 2874 if (a.disambiguate != b.disambiguate) { 2875 //print(" (4) "+a.disambiguate+" "+b.disambiguate); 2876 return 1; 2877 } 2878 if (a.year_suffix !== b.year_suffix) { 2879 //print(" (5) "+a.year_suffix+" "+b.year_suffix); 2880 return 1; 2881 } 2882 return 0; 2883}; 2884 2885CSL.cloneAmbigConfig = function (config, oldconfig) { 2886 var i, ilen, j, jlen, param; 2887 var ret = {}; 2888 ret.names = []; 2889 ret.givens = []; 2890 ret.year_suffix = false; 2891 ret.disambiguate = false; 2892 for (i = 0, ilen = config.names.length; i < ilen; i += 1) { 2893 param = config.names[i]; 2894 // Fixes update bug affecting plugins, without impacting 2895 // efficiency with update of large numbers of year-suffixed 2896 // items. 2897 ret.names[i] = param; 2898 } 2899 for (i = 0, ilen = config.givens.length; i < ilen; i += 1) { 2900 param = []; 2901 for (j = 0, jlen = config.givens[i].length; j < jlen; j += 1) { 2902 // condition at line 312 of disambiguate.js protects against negative 2903 // values of j 2904 param.push(config.givens[i][j]); 2905 } 2906 ret.givens.push(param); 2907 } 2908 // XXXX Is this necessary at all? 2909 if (oldconfig) { 2910 ret.year_suffix = oldconfig.year_suffix; 2911 ret.disambiguate = oldconfig.disambiguate; 2912 } else { 2913 ret.year_suffix = config.year_suffix; 2914 ret.disambiguate = config.disambiguate; 2915 } 2916 return ret; 2917}; 2918 2919/** 2920 * Return current base configuration for disambiguation 2921 */ 2922CSL.getAmbigConfig = function () { 2923 var config, ret; 2924 config = this.tmp.disambig_request; 2925 if (!config) { 2926 config = this.tmp.disambig_settings; 2927 } 2928 var ret = CSL.cloneAmbigConfig(config); 2929 return ret; 2930}; 2931 2932/** 2933 * Return max values for disambiguation 2934 */ 2935CSL.getMaxVals = function () { 2936 return this.tmp.names_max.mystack.slice(); 2937}; 2938 2939/** 2940 * Return min value for disambiguation 2941 */ 2942CSL.getMinVal = function () { 2943 return this.tmp["et-al-min"]; 2944}; 2945 2946/*global CSL: true */ 2947 2948/* For node execution pretty-printing (see below) */ 2949 2950/* 2951var INDENT = ""; 2952*/ 2953 2954CSL.tokenExec = function (token, Item, item) { 2955 // Called on state object 2956 var next, maybenext, exec, debug; 2957 debug = false; 2958 next = token.next; 2959 maybenext = false; 2960 2961 /* Pretty-print node executions */ 2962 2963 /* 2964 if (["if", "else-if", "else"].indexOf(token.name) === -1) { 2965 if (token.tokentype == 1) { 2966 INDENT = INDENT.slice(0, -2); 2967 } 2968 } 2969 this.sys.print(INDENT + "---> Token: " + token.name + " (" + token.tokentype + ") in " + this.tmp.area + ", " + this.output.current.mystack.length); 2970 if (["if", "else-if", "else"].indexOf(token.name) === -1) { 2971 if (token.tokentype == 0) { 2972 INDENT += " "; 2973 } 2974 } 2975 */ 2976 2977 var record = function (result) { 2978 if (result) { 2979 this.tmp.jump.replace("succeed"); 2980 return token.succeed; 2981 } else { 2982 this.tmp.jump.replace("fail"); 2983 return token.fail; 2984 } 2985 }; 2986 if (token.test) { 2987 next = record.call(this,token.test(Item, item)); 2988 } 2989 for (var i=0,ilen=token.execs.length;i<ilen;i++) { 2990 exec = token.execs[i]; 2991 maybenext = exec.call(token, this, Item, item); 2992 if (maybenext) { 2993 next = maybenext; 2994 } 2995 } 2996 //SNIP-START 2997 if (debug) { 2998 CSL.debug(token.name + " (" + token.tokentype + ") ---> done"); 2999 } 3000 //SNIP-END 3001 return next; 3002}; 3003 3004/** 3005 * Macro expander. 3006 * <p>Called on the state object.</p> 3007 */ 3008CSL.expandMacro = function (macro_key_token, target) { 3009 var mkey, macro_nodes, end_of_macro, func; 3010 3011 mkey = macro_key_token.postponed_macro; 3012 3013 var sort_direction = macro_key_token.strings.sort_direction; 3014 3015 // Decorations and affixes are in wrapper applied in cs:text 3016 macro_key_token = new CSL.Token("group", CSL.START); 3017 3018 var hasDate = false; 3019 var macroid = false; 3020 macro_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, 'macro', mkey); 3021 if (macro_nodes.length) { 3022 macroid = this.cslXml.getAttributeValue(macro_nodes[0],'cslid'); 3023 hasDate = this.cslXml.getAttributeValue(macro_nodes[0], "macro-has-date"); 3024 } 3025 if (hasDate) { 3026 mkey = mkey + "@" + this.build.current_default_locale; 3027 func = function (state) { 3028 if (state.tmp.extension) { 3029 state.tmp["doing-macro-with-date"] = true; 3030 } 3031 }; 3032 macro_key_token.execs.push(func); 3033 } 3034 3035 if (this.build.macro_stack.indexOf(mkey) > -1) { 3036 CSL.error("CSL processor error: call to macro \"" + mkey + "\" would cause an infinite loop"); 3037 } else { 3038 this.build.macro_stack.push(mkey); 3039 } 3040 3041 macro_key_token.cslid = macroid; 3042 3043 if (CSL.MODULE_MACROS[mkey]) { 3044 macro_key_token.juris = mkey; 3045 this.opt.update_mode = CSL.POSITION; 3046 } 3047 // Macro group is treated as a real node in the style 3048 CSL.Node.group.build.call(macro_key_token, this, target, true); 3049 3050 // Node does not exist in the CSL 3051 if (!this.cslXml.getNodeValue(macro_nodes)) { 3052 CSL.error("CSL style error: undefined macro \"" + mkey + "\""); 3053 } 3054 3055 // Let's macro 3056 var mytarget = CSL.getMacroTarget.call(this, mkey); 3057 if (mytarget) { 3058 CSL.buildMacro.call(this, mytarget, macro_nodes); 3059 CSL.configureMacro.call(this, mytarget); 3060 } 3061 if (!this.build.extension) { 3062 var func = (function(macro_name) { 3063 return function (state, Item, item) { 3064 var next = 0; 3065 while (next < state.macros[macro_name].length) { 3066 next = CSL.tokenExec.call(state, state.macros[macro_name][next], Item, item); 3067 } 3068 }; 3069 }(mkey)); 3070 var text_node = new CSL.Token("text", CSL.SINGLETON); 3071 text_node.execs.push(func); 3072 target.push(text_node); 3073 } 3074 3075 // Decorations and affixes are in wrapper applied in cs:text 3076 end_of_macro = new CSL.Token("group", CSL.END); 3077 end_of_macro.strings.sort_direction = sort_direction; 3078 3079 if (hasDate) { 3080 func = function (state) { 3081 if (state.tmp.extension) { 3082 state.tmp["doing-macro-with-date"] = false; 3083 } 3084 }; 3085 end_of_macro.execs.push(func); 3086 } 3087 if (macro_key_token.juris) { 3088 end_of_macro.juris = mkey; 3089 } 3090 // Macro group is treated as a real node in the style 3091 CSL.Node.group.build.call(end_of_macro, this, target, true); 3092 3093 this.build.macro_stack.pop(); 3094}; 3095 3096CSL.getMacroTarget = function (mkey) { 3097 var mytarget = false; 3098 if (this.build.extension) { 3099 mytarget = this[this.build.root + this.build.extension].tokens; 3100 } else if (!this.macros[mkey]) { 3101 mytarget = []; 3102 this.macros[mkey] = mytarget; 3103 } 3104 return mytarget; 3105}; 3106 3107CSL.buildMacro = function (mytarget, macro_nodes) { 3108 var builder = CSL.makeBuilder(this, mytarget); 3109 var mynode; 3110 if ("undefined" === typeof macro_nodes.length) { 3111 mynode = macro_nodes; 3112 } else { 3113 mynode = macro_nodes[0]; 3114 } 3115 builder(mynode); 3116}; 3117 3118CSL.configureMacro = function (mytarget) { 3119 if (!this.build.extension) { 3120 this.configureTokenList(mytarget); 3121 } 3122}; 3123 3124 3125/** 3126 * Convert XML node to token. 3127 * <p>This is called on an XML node. After extracting the name and attribute 3128 * information from the node, it performs three operations. Attribute information 3129 * relating to output formatting is stored on the node as an array of tuples, 3130 * which fixes the sequence of execution of output functions to be invoked 3131 * in the next phase of processing. Other attribute information is reduced 3132 * to functions, and is pushed into an array on the token in no particular 3133 * order, for later execution. The element name is used as a key to 3134 * invoke the relevant <code>build</code> method of the target element. 3135 * Element methods are defined in {@link CSL.Node}.</p> 3136 * @param {Object} state The state object returned by {@link CSL.Engine}. 3137 * @param {Int} tokentype A CSL namespace constant (<code>CSL.START</code>, 3138 * <code>CSL.END</code> or <code>CSL.SINGLETON</code>. 3139 */ 3140CSL.XmlToToken = function (state, tokentype, explicitTarget, var_stack) { 3141 var name, txt, attrfuncs, attributes, decorations, token, key, target; 3142 name = state.cslXml.nodename(this); 3143 //CSL.debug(tokentype + " : " + name); 3144 if (state.build.skip && state.build.skip !== name) { 3145 return; 3146 } 3147 if (!name) { 3148 txt = state.cslXml.content(this); 3149 if (txt) { 3150 state.build.text = txt; 3151 } 3152 return; 3153 } 3154 if (!CSL.Node[state.cslXml.nodename(this)]) { 3155 CSL.error("Undefined node name \"" + name + "\"."); 3156 } 3157 attrfuncs = []; 3158 attributes = state.cslXml.attributes(this); 3159 decorations = CSL.setDecorations.call(this, state, attributes); 3160 token = new CSL.Token(name, tokentype); 3161 if (tokentype !== CSL.END || name === "if" || name === "else-if" || name === "layout") { 3162 // 3163 // xml: more xml stuff 3164 // 3165 for (var key in attributes) { 3166 if (attributes.hasOwnProperty(key)) { 3167 if (tokentype === CSL.END && key !== "@language" && key !== "@locale") { 3168 continue; 3169 } 3170 if (attributes.hasOwnProperty(key)) { 3171 if (CSL.Attributes[key]) { 3172 try { 3173 CSL.Attributes[key].call(token, state, "" + attributes[key]); 3174 } catch (e) { 3175 CSL.error(key + " attribute: " + e); 3176 } 3177 } else { 3178 CSL.debug("warning: undefined attribute \""+key+"\" in style"); 3179 } 3180 } 3181 } 3182 } 3183 token.decorations = decorations; 3184 if (CSL.DATE_VARIABLES.indexOf(attributes['@variable']) > -1) { 3185 var_stack.push(token.variables); 3186 } 3187 } else if (tokentype === CSL.END && attributes['@variable']) { 3188 token.hasVariable = true; 3189 if (CSL.DATE_VARIABLES.indexOf(attributes['@variable']) > -1) { 3190 token.variables = var_stack.pop(); 3191 } 3192 } 3193 // 3194 // !!!!!: eliminate diversion of tokens to separate 3195 // token list (formerly used for reading in macros 3196 // and terms). 3197 // 3198 if (explicitTarget) { 3199 target = explicitTarget; 3200 } else { 3201 target = state[state.build.area].tokens; 3202 } 3203 // True flags real nodes in the style 3204 CSL.Node[name].build.call(token, state, target, true); 3205}; 3206 3207 3208 3209/*global CSL: true */ 3210 3211 3212CSL.DateParser = function () { 3213 3214 /* 3215 * Fixed values 3216 */ 3217 3218 // jse imperial years 3219 var epochPairs = [ 3220 ["\u660E\u6CBB", 1867], 3221 ["\u5927\u6B63", 1911], 3222 ["\u662D\u548C", 1925], 3223 ["\u5E73\u6210", 1988] 3224 ]; 3225 3226 // years by jse imperial epoch 3227 var epochYearByName = {}; 3228 for (var i=0,ilen=epochPairs.length; i<ilen; i++) { 3229 var key = epochPairs[i][0]; 3230 var val = epochPairs[i][1]; 3231 epochYearByName[key] = val; 3232 } 3233 3234 var epochMatchStrings = []; 3235 var epochMap = {}; 3236 for (var i=0,ilen=epochPairs.length; i<ilen; i++) { 3237 var pair = epochPairs[i]; 3238 var val = pair[0]; 3239 epochMatchStrings.push(val); 3240 epochMap[pair[0]] = pair[1]; 3241 } 3242 var epochMatchString = epochMatchStrings.join("|"); 3243 3244 // regular expression to trap year name and year 3245 // (splitter and matcher, to cope with ancient JS implementations) 3246 var epochSplitter = new RegExp("(?:" + epochMatchString + ")(?:[0-9]+)"); 3247 var epochMatcher = new RegExp("(?:" + epochMatchString + ")(?:[0-9]+)", "g"); 3248 3249 // regular expression for month or day kanji label 3250 var kanjiMonthDay = /(\u6708|\u5E74)/g; 3251 3252 // regular expression for year kanji label 3253 var kanjiYear = /\u65E5/g; 3254 3255 // regular expression for double-width Japanese range marker 3256 var kanjiRange = /\u301c/g; 3257 3258 // parsing regexps for normalized strings 3259 // raw materials 3260 var yearLast = "(?:[?0-9]{1,2}%%NUMD%%){0,2}[?0-9]{4}(?![0-9])"; 3261 var yearFirst = "[?0-9]{4}(?:%%NUMD%%[?0-9]{1,2}){0,2}(?![0-9])"; 3262 var numberVal = "[?0-9]{1,3}"; 3263 var rangeSeparator = "[%%DATED%%]"; 3264 var fuzzyChar = "[?~]"; 3265 var chars = "[^\-\/\~\?0-9]+"; 3266 var rexString = "(" + yearFirst + "|" + yearLast + "|" + numberVal + "|" + rangeSeparator + "|" + fuzzyChar + "|" + chars + ")"; 3267 // composed regexps 3268 var rexDash = new RegExp(rexString.replace(/%%NUMD%%/g, "-").replace(/%%DATED%%/g, "-")); 3269 var rexDashSlash = new RegExp(rexString.replace(/%%NUMD%%/g, "-").replace(/%%DATED%%/g, "\/")); 3270 var rexSlashDash = new RegExp(rexString.replace(/%%NUMD%%/g, "\/").replace(/%%DATED%%/g, "-")); 3271 3272 /* 3273 * Mutable values 3274 */ 3275 3276 // months 3277 var monthString = "january february march april may june july august september october november december spring summer fall winter spring summer"; 3278 this.monthStrings = monthString.split(" "); 3279 3280 /* 3281 * Configuration functions 3282 */ 3283 3284 this.setOrderDayMonth = function() { 3285 // preferred ordering for numeric dates 3286 this.monthGuess = 1; 3287 this.dayGuess = 0; 3288 }; 3289 3290 this.setOrderMonthDay = function() { 3291 // preferred ordering for numeric dates 3292 this.monthGuess = 0; 3293 this.dayGuess = 1; 3294 }; 3295 3296 this.resetDateParserMonths = function() { 3297 // Function to reset months to default. 3298 this.monthSets = []; 3299 for (var i=0,ilen=this.monthStrings.length; i<ilen; i++) { 3300 this.monthSets.push([this.monthStrings[i]]); 3301 } 3302 this.monthAbbrevs = []; 3303 for (var i=0,ilen=this.monthSets.length; i<ilen; i++) { 3304 this.monthAbbrevs.push([]); 3305 for (var j=0,jlen=this.monthSets[i].length; j<jlen; j++) { 3306 this.monthAbbrevs[i].push(this.monthSets[i][0].slice(0, 3)); 3307 } 3308 } 3309 this.monthRexes = []; 3310 for (var i=0,ilen=this.monthAbbrevs.length; i<ilen; i++) { 3311 this.monthRexes.push(new RegExp("(?:" + this.monthAbbrevs[i].join("|") + ")")); 3312 } 3313 }; 3314 3315 this.addDateParserMonths = function(lst) { 3316 // Extend list of months with an additional set of month abbreviations, 3317 // extending strings as required to resolve ambiguities. 3318 3319 // Normalize string to list 3320 if ("string" === typeof lst) { 3321 lst = lst.split(/\s+/); 3322 } 3323 3324 // Check that there are twelve (or sixteen) to add 3325 if (lst.length !== 12 && lst.length !== 16) { 3326 CSL.debug("month [+season] list of "+lst.length+", expected 12 or 16. Ignoring."); 3327 return; 3328 } 3329 3330 // Extend as necessary to resolve ambiguities 3331 // For each new month string ... 3332 for (var i=0,ilen=lst.length; i<ilen; i++) { 3333 var abbrevLength = null; 3334 var skip = false; 3335 var insert = 3; 3336 var extendedSets = {}; 3337 for (var j=0,jlen=this.monthAbbrevs.length; j<jlen; j++) { 3338 extendedSets[j] = {}; 3339 if (j === i) { 3340 // Mark for skipping if same as an existing abbreviation of same month 3341 for (var k=0,klen=this.monthAbbrevs[i].length; k<klen; k++) { 3342 if (this.monthAbbrevs[i][k] === lst[i].slice(0, this.monthAbbrevs[i][k].length)) { 3343 skip = true; 3344 break; 3345 } 3346 } 3347 } else { 3348 // Mark for extending if same as existing abbreviation of any expression of another month 3349 for (var k=0,klen=this.monthAbbrevs[j].length; k<klen; k++) { 3350 abbrevLength = this.monthAbbrevs[j][k].length; 3351 if (this.monthAbbrevs[j][k] === lst[i].slice(0, abbrevLength)) { 3352 while (this.monthSets[j][k].slice(0, abbrevLength) === lst[i].slice(0, abbrevLength)) { 3353 // Abort when full length is hit, otherwise extend 3354 if (abbrevLength > lst[i].length || abbrevLength > this.monthSets[j][k].length) { 3355 CSL.debug("unable to disambiguate month string in date parser: "+lst[i]); 3356 break; 3357 } else { 3358 // Mark both new entry and existing abbrev for extension 3359 abbrevLength += 1; 3360 } 3361 } 3362 insert = abbrevLength; 3363 extendedSets[j][k] = abbrevLength; 3364 } 3365 } 3366 } 3367 for (var jKey in extendedSets) { 3368 for (var kKey in extendedSets[jKey]) { 3369 abbrevLength = extendedSets[jKey][kKey]; 3370 jKey = parseInt(jKey, 10); 3371 kKey = parseInt(kKey, 10); 3372 this.monthAbbrevs[jKey][kKey] = this.monthSets[jKey][kKey].slice(0, abbrevLength); 3373 } 3374 } 3375 } 3376 // Insert here 3377 if (!skip) { 3378 this.monthSets[i].push(lst[i]); 3379 this.monthAbbrevs[i].push(lst[i].slice(0, insert)); 3380 } 3381 } 3382 3383 // Compose 3384 this.monthRexes = []; 3385 this.monthRexStrs = []; 3386 for (var i=0,ilen=this.monthAbbrevs.length; i<ilen; i++) { 3387 this.monthRexes.push(new RegExp("^(?:" + this.monthAbbrevs[i].join("|") + ")")); 3388 this.monthRexStrs.push("^(?:" + this.monthAbbrevs[i].join("|") + ")"); 3389 } 3390 if (this.monthAbbrevs.length === 18) { 3391 for (var i=12,ilen=14; i<ilen; i++) { 3392 this.monthRexes[i+4] = new RegExp("^(?:" + this.monthAbbrevs[i].join("|") + ")"); 3393 this.monthRexStrs[i+4] = "^(?:" + this.monthAbbrevs[i].join("|") + ")"; 3394 } 3395 } 3396 }; 3397 3398 /* 3399 * Conversion functions 3400 */ 3401 3402 this.convertDateObjectToArray = function (thedate) { 3403 // Converts object in place and returns object 3404 thedate["date-parts"] = []; 3405 thedate["date-parts"].push([]); 3406 var slicelen = 0; 3407 var part; 3408 for (var i=0,ilen=3; i<ilen; i++) { 3409 part = ["year", "month", "day"][i]; 3410 if (!thedate[part]) { 3411 break; 3412 } 3413 slicelen += 1; 3414 thedate["date-parts"][0].push(thedate[part]); 3415 delete thedate[part]; 3416 } 3417 thedate["date-parts"].push([]); 3418 for (var i=0, ilen=slicelen; i<ilen; i++) { 3419 part = ["year_end", "month_end", "day_end"][i]; 3420 if (!thedate[part]) { 3421 break; 3422 } 3423 thedate["date-parts"][1].push(thedate[part]); 3424 delete thedate[part]; 3425 } 3426 if (thedate["date-parts"][0].length !== thedate["date-parts"][1].length) { 3427 thedate["date-parts"].pop(); 3428 } 3429 return thedate; 3430 }; 3431 3432 // XXXX String output is currently unable to represent ranges 3433 this.convertDateObjectToString = function(thedate) { 3434 // Returns string 3435 var ret = []; 3436 for (var i = 0, ilen = 3; i < ilen; i += 1) { 3437 if (thedate[CSL.DATE_PARTS_ALL[i]]) { 3438 ret.push(thedate[CSL.DATE_PARTS_ALL[i]]); 3439 } else { 3440 break; 3441 } 3442 } 3443 return ret.join("-"); 3444 }; 3445 3446 /* 3447 * Utility function 3448 */ 3449 3450 this._parseNumericDate = function (ret, delim, suff, txt) { 3451 if (!suff) { 3452 suff = ""; 3453 } 3454 var lst = txt.split(delim); 3455 3456 for (var i=0, ilen=lst.length; i<ilen; i++) { 3457 if (lst[i].length === 4) { 3458 ret[("year" + suff)] = lst[i].replace(/^0*/, ""); 3459 if (!i) { 3460 lst = lst.slice(1); 3461 } else { 3462 lst = lst.slice(0, i); 3463 } 3464 break; 3465 } 3466 } 3467 for (var i=0,ilen=lst.length; i<ilen; i++) { 3468 lst[i] = parseInt(lst[i], 10); 3469 } 3470 if (lst.length === 1 || (lst.length === 2 && !lst[1])) { 3471 var month = lst[0]; 3472 if (month) { 3473 ret[("month" + suff)] = "" + lst[0]; 3474 } 3475 } else if (lst.length === 2) { 3476 if (lst[this.monthGuess] > 12) { 3477 var month = lst[this.dayGuess]; 3478 var day = lst[this.monthGuess]; 3479 if (month) { 3480 ret[("month" + suff)] = "" + month; 3481 if (day) { 3482 ret[("day" + suff)] = "" + day; 3483 } 3484 } 3485 } else { 3486 var month = lst[this.monthGuess]; 3487 var day = lst[this.dayGuess]; 3488 if (month) { 3489 ret[("month" + suff)] = "" + month; 3490 if (day) { 3491 ret[("day" + suff)] = "" + day; 3492 } 3493 } 3494 } 3495 } 3496 }; 3497 3498 /* 3499 * Parsing functions 3500 */ 3501 3502 this.parseDateToObject = function (txt) { 3503 // 3504 // Normalize the format and the year if it's a Japanese date 3505 // 3506 var orig = txt; 3507 var slashPos = -1; 3508 var dashPos = -1; 3509 var yearIsNegative = false; 3510 var lst; 3511 if (txt) { 3512 txt = txt.replace(/^(.*[0-9])T[0-9].*/, "$1"); 3513 // If string leads with a minus sign, strip and memo it. 3514 if (txt.slice(0, 1) === "-") { 3515 yearIsNegative = true; 3516 txt = txt.slice(1); 3517 } 3518 3519 // If string is a number of 1 to 3 characters only, treat as year. 3520 if (txt.match(/^[0-9]{1,3}$/)) { 3521 while (txt.length < 4) { 3522 txt = "0" + txt; 3523 } 3524 } 3525 3526 // Normalize to string 3527 txt = "" + txt; 3528 // Remove things that look like times 3529 txt = txt.replace(/\s*[0-9]{2}:[0-9]{2}(?::[0-9]+)/,""); 3530 var m = txt.match(kanjiMonthDay); 3531 if (m) { 3532 txt = txt.replace(/\s+/g, ""); 3533 txt = txt.replace(kanjiYear, ""); 3534 txt = txt.replace(kanjiMonthDay, "-"); 3535 txt = txt.replace(kanjiRange, "/"); 3536 txt = txt.replace(/\-\//g, "/"); 3537 txt = txt.replace(/-$/g,""); 3538 3539 // Tortuous workaround for IE6 3540 var slst = txt.split(epochSplitter); 3541 lst = []; 3542 var mm = txt.match(epochMatcher); 3543 if (mm) { 3544 var mmx = []; 3545 for (var i=0,ilen=mm.length; i<ilen; i++) { 3546 mmx = mmx.concat(mm[i].match(/([^0-9]+)([0-9]+)/).slice(1)); 3547 } 3548 for (var i=0,ilen=slst.length; i<ilen; i++) { 3549 lst.push(slst[i]); 3550 if (i !== (ilen - 1)) { 3551 // pos is undeclared, and multiplying by 2 here is insane. 3552 var mmpos = (i * 2); 3553 lst.push(mmx[mmpos]); 3554 lst.push(mmx[mmpos + 1]); 3555 } 3556 } 3557 } else { 3558 lst = slst; 3559 } 3560 // workaround duly applied, this now works 3561 for (var i=1,ilen=lst.length; i<ilen; i+=3) { 3562 lst[i + 1] = epochMap[lst[i]] + parseInt(lst[i + 1], 10); 3563 lst[i] = ""; 3564 } 3565 txt = lst.join(""); 3566 txt = txt.replace(/\s*-\s*$/, "").replace(/\s*-\s*\//, "/"); 3567 // 3568 // normalize date and identify delimiters 3569 // 3570 txt = txt.replace(/\.\s*$/, ""); 3571 3572 // not sure what this is meant to do 3573 txt = txt.replace(/\.(?! )/, ""); 3574 3575 slashPos = txt.indexOf("/"); 3576 dashPos = txt.indexOf("-"); 3577 } 3578 } 3579 // drop punctuation from a.d., b.c. 3580 txt = txt.replace(/([A-Za-z])\./g, "$1"); 3581 3582 var number = ""; 3583 var note = ""; 3584 var thedate = {}; 3585 var rangeDelim; 3586 var dateDelim; 3587 if (txt.slice(0, 1) === "\"" && txt.slice(-1) === "\"") { 3588 thedate.literal = txt.slice(1, -1); 3589 return thedate; 3590 } 3591 if (slashPos > -1 && dashPos > -1) { 3592 var slashCount = txt.split("/"); 3593 if (slashCount.length > 3) { 3594 rangeDelim = "-"; 3595 txt = txt.replace(/\_/g, "-"); 3596 dateDelim = "/"; 3597 lst = txt.split(rexSlashDash); 3598 } else { 3599 rangeDelim = "/"; 3600 txt = txt.replace(/\_/g, "/"); 3601 dateDelim = "-"; 3602 lst = txt.split(rexDashSlash); 3603 } 3604 } else { 3605 txt = txt.replace(/\//g, "-"); 3606 txt = txt.replace(/\_/g, "-"); 3607 rangeDelim = "-"; 3608 dateDelim = "-"; 3609 lst = txt.split(rexDash); 3610 } 3611 var ret = []; 3612 for (var i=0,ilen=lst.length; i<ilen; i++) { 3613 var m = lst[i].match(/^\s*([\-\/]|[^\-\/\~\?0-9]+|[\-~?0-9]+)\s*$/); 3614 if (m) { 3615 ret.push(m[1]); 3616 } 3617 } 3618 // 3619 // Phase 2 3620 // 3621 var delimPos = ret.indexOf(rangeDelim); 3622 var delims = []; 3623 var isRange = false;
3624 if (delimPos > -1) { 3625 delims.push([0, delimPos]); 3626 delims.push([(delimPos + 1), ret.length]); 3627 isRange = true; 3628 } else { 3629 delims.push([0, ret.length]); 3630 } 3631 // 3632 // For each side of a range divide ... 3633 // 3634 var suff = ""; 3635 3636 for (var i=0,ilen=delims.length; i<ilen; i++) { 3637 var delim = delims[i]; 3638 // 3639 // Process each element ... 3640 // 3641 var date = ret.slice(delim[0], delim[1]); 3642 outer: 3643 for (var j=0,jlen=date.length; j<jlen; j++) { 3644 var element = date[j]; 3645 // 3646 // If it's a numeric date, process it. 3647 // 3648 if (element.indexOf(dateDelim) > -1) { 3649 this._parseNumericDate(thedate, dateDelim, suff, element); 3650 continue; 3651 } 3652 // 3653 // If it's an obvious year, record it. 3654 // 3655 if (element.match(/[0-9]{4}/)) { 3656 thedate[("year" + suff)] = element.replace(/^0*/, ""); 3657 continue; 3658 } 3659 // 3660 // If it's a fuzzy marker, record it. 3661 // 3662 if (element === "~" || element === "?" || element === "c" || element.match(/^cir/)) { 3663 thedate.circa = true; 3664 } 3665 // 3666 // If it's a month, record it. 3667 // 3668 for (var k=0,klen=this.monthRexes.length; k<klen; k++) { 3669 if (element.toLocaleLowerCase().match(this.monthRexes[k])) { 3670 thedate[("month" + suff)] = "" + (parseInt(k, 10) + 1); 3671 continue outer; 3672 } 3673 } 3674 // 3675 // If it's a number, make a note of it 3676 // 3677 if (element.match(/^[0-9]+$/)) { 3678 number = element; 3679 } 3680 // 3681 // If it's a BC or AD marker, make a year of 3682 // any note. Separate, reverse the sign of the year 3683 // if it's BC. 3684 // 3685 if (element.toLocaleLowerCase().match(/^bc/) && number) { 3686 thedate[("year" + suff)] = "" + (number * -1); 3687 number = ""; 3688 continue; 3689 } 3690 if (element.toLocaleLowerCase().match(/^ad/) && number) { 3691 thedate[("year" + suff)] = "" + number; 3692 number = ""; 3693 continue; 3694 } 3695 // 3696 // If it's cruft, make a note of it 3697 // 3698 if (element.toLocaleLowerCase().match(/(?:mic|tri|hil|eas)/) && !thedate[("season" + suff)]) { 3699 note = element; 3700 continue; 3701 } 3702 } 3703 // 3704 // If at the end of the string there's still a note 3705 // hanging around, make a day of it. 3706 // 3707 if (number) { 3708 thedate[("day" + suff)] = number; 3709 number = ""; 3710 } 3711 // 3712 // If at the end of the string there's cruft lying 3713 // around, and the season field is empty, put the 3714 // cruft there. 3715 // 3716 if (note && !thedate[("season" + suff)]) { 3717 thedate[("season" + suff)] = note.trim(); 3718 note = ""; 3719 } 3720 suff = "_end"; 3721 } 3722 // 3723 // update any missing elements on each side of the divide 3724 // from the other 3725 // 3726 if (isRange) { 3727 for (var j=0,jlen=CSL.DATE_PARTS_ALL.length; j<jlen; j++) { 3728 var item = CSL.DATE_PARTS_ALL[j]; 3729 if (thedate[item] && !thedate[(item + "_end")]) { 3730 thedate[(item + "_end")] = thedate[item]; 3731 } else if (!thedate[item] && thedate[(item + "_end")]) { 3732 thedate[item] = thedate[(item + "_end")]; 3733 } 3734 } 3735 } 3736 // 3737 // If there's no year, or if there only a year and a day, it's a failure; pass through the literal 3738 // 3739 if (!thedate.year || (thedate.year && thedate.day && !thedate.month)) { 3740 thedate = { "literal": orig }; 3741 } 3742 var parts = ["year", "month", "day", "year_end", "month_end", "day_end"]; 3743 for (var i=0,ilen=parts.length; i<ilen; i++) { 3744 var part = parts[i]; 3745 if ("string" === typeof thedate[part] && thedate[part].match(/^[0-9]+$/)) { 3746 thedate[part] = parseInt(thedate[part], 10); 3747 } 3748 3749 }
vendor: 103,346 bytes, lines 3750-6572
3750 if (yearIsNegative && Object.keys(thedate).indexOf("year") > -1) { 3751 thedate.year = (thedate.year * -1); 3752 } 3753 return thedate; 3754 }; 3755 3756 this.parseDateToArray = function(txt) { 3757 return this.convertDateObjectToArray(this.parseDateToObject(txt)); 3758 }; 3759 3760 this.parseDateToString = function(txt) { 3761 return this.convertDateObjectToString(this.parseDateToObject(txt)); 3762 }; 3763 3764 this.parse = function(txt) { 3765 return this.parseDateToObject(txt); 3766 }; 3767 3768 /* 3769 3770 * Setup 3771 */ 3772 3773 this.setOrderMonthDay(); 3774 this.resetDateParserMonths(); 3775}; 3776CSL.DateParser = new CSL.DateParser(); 3777 3778/*global CSL: true */ 3779 3780CSL.Engine = function (sys, style, lang, forceLang) { 3781 var attrs, langspec; 3782 this.processor_version = CSL.PROCESSOR_VERSION; 3783 this.csl_version = "1.0"; 3784 this.sys = sys; 3785 3786 if (typeof Object.assign != 'function') { 3787 // Must be writable: true, enumerable: false, configurable: true 3788 Object.defineProperty(Object, "assign", { 3789 value: function assign(target) { // .length of function is 2 3790 'use strict'; 3791 if (target == null) { // TypeError if undefined or null 3792 throw new TypeError('Cannot convert undefined or null to object'); 3793 } 3794 3795 var to = Object(target); 3796 3797 for (var index = 1; index < arguments.length; index++) { 3798 var nextSource = arguments[index]; 3799 3800 if (nextSource != null) { // Skip over if undefined or null 3801 for (var nextKey in nextSource) { 3802 // Avoid bugs when hasOwnProperty is shadowed 3803 if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 3804 to[nextKey] = nextSource[nextKey]; 3805 } 3806 } 3807 } 3808 } 3809 return to; 3810 }, 3811 writable: true, 3812 configurable: true 3813 }); 3814 } 3815 3816 // XXX This may be excess code. Given the normalization performed on 3817 // XXX the output queue before variableWrapper() is run, a single 3818 // XXX space should be the most cruft that we ever see before a variable. 3819 if (sys.variableWrapper) { 3820 CSL.VARIABLE_WRAPPER_PREPUNCT_REX = new RegExp('^([' + [" "].concat(CSL.SWAPPING_PUNCTUATION).join("") + ']*)(.*)'); 3821 } 3822 // XXXX This should be restored -- temporarily suspended for testing of JSON style support. 3823 if (CSL.retrieveStyleModule) { 3824 this.sys.retrieveStyleModule = CSL.retrieveStyleModule; 3825 } 3826 if (CSL.getAbbreviation) { 3827 this.sys.getAbbreviation = CSL.getAbbreviation; 3828 } 3829 if (this.sys.stringCompare) { 3830 CSL.stringCompare = this.sys.stringCompare; 3831 } 3832 this.sys.AbbreviationSegments = CSL.AbbreviationSegments; 3833 3834 this.transform = new CSL.Transform(this); 3835 // true or false 3836 this.setParseNames = function (val) { 3837 this.opt['parse-names'] = val; 3838 }; 3839 3840 this.opt = new CSL.Engine.Opt(); 3841 this.tmp = new CSL.Engine.Tmp(); 3842 this.build = new CSL.Engine.Build(); 3843 this.fun = new CSL.Engine.Fun(this); 3844 3845 this.configure = new CSL.Engine.Configure(); 3846 // Build citation before citation_sort in order to pick up 3847 // state.opt.update_mode, needed it determine whether 3848 // a grouped sort should be performed. 3849 this.citation_sort = new CSL.Engine.CitationSort(); 3850 this.bibliography_sort = new CSL.Engine.BibliographySort(); 3851 this.citation = new CSL.Engine.Citation(this); 3852 this.bibliography = new CSL.Engine.Bibliography(); 3853 this.intext = new CSL.Engine.InText(); 3854 3855 this.output = new CSL.Output.Queue(this); 3856 3857 //this.render = new CSL.Render(this); 3858 // 3859 // This latter queue is used for formatting date chunks 3860 // before they are folded back into the main queue. 3861 // 3862 this.dateput = new CSL.Output.Queue(this); 3863 3864 this.cslXml = CSL.setupXml(style); 3865 3866 for (var i in CSL.SYS_OPTIONS) { 3867 var option = CSL.SYS_OPTIONS[i]; 3868 if ("boolean" === typeof this.sys[option]) { 3869 this.opt.development_extensions[option] = this.sys[option]; 3870 } 3871 3872 } 3873 if (this.opt.development_extensions.uppercase_subtitles || this.opt.development_extensions.implicit_short_title) { 3874 this.opt.development_extensions.main_title_from_short_title = true; 3875 } 3876 if (this.opt.development_extensions.csl_reverse_lookup_support) { 3877 this.build.cslNodeId = 0; 3878 this.setCslNodeIds = function(myxml, nodename) { 3879 var children = this.cslXml.children(myxml); 3880 this.cslXml.setAttribute(myxml, 'cslid', this.build.cslNodeId); 3881 this.opt.nodenames.push(nodename); 3882 this.build.cslNodeId += 1; 3883 for (var i = 0, ilen = this.cslXml.numberofnodes(children); i < ilen; i += 1) { 3884 nodename = this.cslXml.nodename(children[i]); 3885 if (nodename) { 3886 this.setCslNodeIds(children[i], nodename); 3887 } 3888 } 3889 }; 3890 this.setCslNodeIds(this.cslXml.dataObj, "style"); 3891 } 3892 // Preprocessing ops for the XML input 3893 this.cslXml.addMissingNameNodes(this.cslXml.dataObj); 3894 this.cslXml.addInstitutionNodes(this.cslXml.dataObj); 3895 this.cslXml.insertPublisherAndPlace(this.cslXml.dataObj); 3896 this.cslXml.flagDateMacros(this.cslXml.dataObj); 3897 attrs = this.cslXml.attributes(this.cslXml.dataObj); 3898 if ("undefined" === typeof attrs["@sort-separator"]) { 3899 this.cslXml.setAttribute(this.cslXml.dataObj, "sort-separator", ", "); 3900 } 3901 // This setting does the right thing and seems not to be side-effects 3902 this.opt["initialize-with-hyphen"] = true; 3903 3904 // Locale resolution 3905 // 3906 // (1) Get three locale strings 3907 // -- default-locale (stripped) 3908 // -- processor-locale 3909 // -- en_US 3910 3911 this.setStyleAttributes(); 3912 3913 this.opt.xclass = this.cslXml.getAttributeValue(this.cslXml.dataObj, "class"); 3914 this.opt["class"] = this.opt.xclass; 3915 this.opt.styleID = this.cslXml.getStyleId(this.cslXml.dataObj); 3916 this.opt.styleName = this.cslXml.getStyleId(this.cslXml.dataObj, true); 3917 3918 if (this.opt.version.slice(0,4) === "1.1m") { 3919 this.opt.development_extensions.consolidate_legal_items = true; 3920 this.opt.development_extensions.consolidate_container_items = true; 3921 this.opt.development_extensions.main_title_from_short_title = true; 3922 this.opt.development_extensions.expect_and_symbol_form = true; 3923 this.opt.development_extensions.require_explicit_legal_case_title_short = true; 3924 this.opt.development_extensions.force_jurisdiction = true; 3925 this.opt.development_extensions.force_title_abbrev_fallback = true; 3926 } 3927 // We seem to have two language specs flying around: 3928 // this.opt["default-locale"], and this.opt.lang 3929 // Keeping them aligned for safety's sake, pending 3930 // eventual cleanup. 3931 if (lang) { 3932 lang = lang.replace("_", "-"); 3933 lang = CSL.normalizeLocaleStr(lang); 3934 } 3935 if (this.opt["default-locale"][0]) { 3936 this.opt["default-locale"][0] = this.opt["default-locale"][0].replace("_", "-"); 3937 this.opt["default-locale"][0] = CSL.normalizeLocaleStr(this.opt["default-locale"][0]); 3938 } 3939 if (lang && forceLang) { 3940 this.opt["default-locale"] = [lang]; 3941 } 3942 if (lang && !forceLang && this.opt["default-locale"][0]) { 3943 lang = this.opt["default-locale"][0]; 3944 } 3945 if (this.opt["default-locale"].length === 0) { 3946 if (!lang) { 3947 lang = "en-US"; 3948 } 3949 this.opt["default-locale"].push("en-US"); 3950 } 3951 if (!lang) { 3952 lang = this.opt["default-locale"][0]; 3953 } 3954 langspec = CSL.localeResolve(lang); 3955 this.opt.lang = langspec.best; 3956 this.opt["default-locale"][0] = langspec.best; 3957 this.locale = {}; 3958 if (!this.opt["default-locale-sort"]) { 3959 this.opt["default-locale-sort"] = this.opt["default-locale"][0]; 3960 } 3961 // Test processor against JS engine locale mess to find a field separator that works 3962 if ('dale|'.localeCompare('daleb', this.opt["default-locale-sort"]) > -1) { 3963 this.opt.sort_sep = "@"; 3964 } else { 3965 this.opt.sort_sep = "|"; 3966 } 3967 this.localeConfigure(langspec); 3968 3969 // Build skip-word regexp 3970 function makeRegExp(lst) { 3971 var lst = lst.slice(); 3972 var ret = new RegExp( "(?:(?:[?!:]*\\s+|-|^)(?:" + lst.join("|") + ")(?=[!?:]*\\s+|-|$))", "g"); 3973 return ret; 3974 } 3975 this.locale[this.opt.lang].opts["skip-words-regexp"] = makeRegExp(this.locale[this.opt.lang].opts["skip-words"]); 3976 3977 this.output.adjust = new CSL.Output.Queue.adjust(this.getOpt('punctuation-in-quote')); 3978 3979 this.registry = new CSL.Registry(this); 3980 3981 // XXX For modular jurisdiction support, parameterize buildTokenLists(). 3982 // XXX Feed as arguments: 3983 // XXX * actual node to be walked (cslXml) 3984 // XXX * actual target array 3985 3986 this.macros = {}; 3987 3988 this.build.area = "citation"; 3989 var area_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, this.build.area); 3990 this.buildTokenLists(area_nodes, this[this.build.area].tokens); 3991 3992 this.build.area = "bibliography"; 3993 var area_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, this.build.area); 3994 this.buildTokenLists(area_nodes, this[this.build.area].tokens); 3995 3996 this.build.area = "intext"; 3997 var area_nodes = this.cslXml.getNodesByName(this.cslXml.dataObj, this.build.area); 3998 this.buildTokenLists(area_nodes, this[this.build.area].tokens); 3999 4000 if (this.opt.parallel.enable) { 4001 this.parallel = new CSL.Parallel(this); 4002 } 4003 4004 this.juris = {}; 4005 4006 this.configureTokenLists(); 4007 4008 this.disambiguate = new CSL.Disambiguation(this); 4009 4010 this.splice_delimiter = false; 4011 4012 // 4013 // date parser 4014 // 4015 this.fun.dateparser = CSL.DateParser; 4016 // 4017 // flip-flopper for inline markup 4018 // 4019 this.fun.flipflopper = new CSL.Util.FlipFlopper(this); 4020 // 4021 // utility functions for quotes 4022 // 4023 this.setCloseQuotesArray(); 4024 // 4025 // configure ordinal numbers generator 4026 // 4027 this.fun.ordinalizer.init(this); 4028 // 4029 // configure long ordinal numbers generator 4030 // 4031 this.fun.long_ordinalizer.init(this); 4032 // 4033 // set up page mangler 4034 // 4035 this.fun.page_mangler = CSL.Util.PageRangeMangler.getFunction(this, "page"); 4036 this.fun.year_mangler = CSL.Util.PageRangeMangler.getFunction(this, "year"); 4037 4038 this.setOutputFormat("html"); 4039}; 4040 4041CSL.Engine.prototype.setCloseQuotesArray = function () { 4042 var ret; 4043 ret = []; 4044 ret.push(this.getTerm("close-quote")); 4045 ret.push(this.getTerm("close-inner-quote")); 4046 ret.push('"'); 4047 ret.push("'"); 4048 this.opt.close_quotes_array = ret; 4049}; 4050 4051// Walker for preparsed XML input 4052CSL.makeBuilder = function (me, target) { 4053 var var_stack = []; 4054 var node_stack = []; 4055 function runStart (node) { 4056 node_stack.push(node); 4057 CSL.XmlToToken.call(node, me, CSL.START, target, var_stack); 4058 } 4059 function runEnd () { 4060 var node = node_stack.pop(); 4061 CSL.XmlToToken.call(node, me, CSL.END, target, var_stack); 4062 } 4063 function runSingle (node) { 4064 CSL.XmlToToken.call(node, me, CSL.SINGLETON, target, var_stack); 4065 } 4066 function buildStyle (nodes, parent, node_stack) { 4067 if (!node_stack) { 4068 node_stack = []; 4069 } 4070 if (!nodes) { 4071 nodes = []; 4072 } 4073 if ("undefined" === typeof nodes.length) { 4074 nodes = [nodes]; 4075 } 4076 for (var i=0; i<nodes.length; i++) { 4077 var node = nodes[i]; 4078 if (me.cslXml.nodename(node) === null) { 4079 continue; 4080 } 4081 if (parent && me.cslXml.nodename(node) === "date") { 4082 CSL.Util.fixDateNode.call(me, parent, i, node); 4083 node = me.cslXml.children(parent)[i]; 4084 } 4085 if (me.cslXml.numberofnodes(me.cslXml.children(node))) { 4086 runStart(node); 4087 buildStyle(me.cslXml.children(node), node, node_stack); 4088 runEnd(); 4089 } else { 4090 runSingle(node); 4091 } 4092 } 4093 } 4094 return buildStyle; 4095}; 4096 4097 4098CSL.Engine.prototype.buildTokenLists = function (area_nodes, target) { 4099 if (!this.cslXml.getNodeValue(area_nodes)) { 4100 return; 4101 } 4102 var builder = CSL.makeBuilder(this, target); 4103 var mynode; 4104 if ("undefined" === typeof area_nodes.length) { 4105 mynode = area_nodes; 4106 } else { 4107 mynode = area_nodes[0]; 4108 } 4109 builder(mynode); 4110}; 4111 4112 4113CSL.Engine.prototype.setStyleAttributes = function () { 4114 var dummy, attributes, attrname; 4115 // Protect against DOM engines that deliver a top-level document 4116 // (needed for createElement) that does not contain our top-level node. 4117 // 4118 // The string coercion on this.cslXml.tagName addresses a bizarre 4119 // condition on the top-level node in jsdom running under node.js, in which: 4120 // (1) typeof this.cslXml.tagName === "undefined"; and 4121 // (2) !this.cslXml.tagName === false 4122 // Coerced, it becomes an empty string. 4123 var dummy = {}; 4124 dummy.name = this.cslXml.nodename(this.cslXml.dataObj); 4125 attributes = this.cslXml.attributes(this.cslXml.dataObj); 4126 for (attrname in attributes) { 4127 if (attributes.hasOwnProperty(attrname)) { 4128 // attr = attributes[key]; 4129 CSL.Attributes[attrname].call(dummy, this, attributes[attrname]); 4130 } 4131 } 4132}; 4133 4134CSL.Engine.prototype.getTerm = function (term, form, plural, gender, mode, forceDefaultLocale) { 4135 if (term && term.match(/[A-Z]/) && term === term.toUpperCase()) { 4136 CSL.debug("Warning: term key is in uppercase form: "+term); 4137 term = term.toLowerCase(); 4138 } 4139 var lang; 4140 if (forceDefaultLocale) { 4141 lang = this.opt["default-locale"][0]; 4142 } else { 4143 lang = this.opt.lang; 4144 } 4145 var ret = CSL.Engine.getField(CSL.LOOSE, this.locale[lang].terms, term, form, plural, gender); 4146 // XXXXX Temporary, until locale term is deployed in CSL. 4147 if (!ret && term === "range-delimiter") { 4148 ret = "\u2013"; 4149 } 4150 // XXXXX Not so good if mode is neither strict nor tolerant ... 4151 if (typeof ret === "undefined") { 4152 if (mode === CSL.STRICT) { 4153 CSL.error("Error in getTerm: term \"" + term + "\" does not exist."); 4154 } else if (mode === CSL.TOLERANT) { 4155 ret = ""; 4156 } 4157 } 4158 if (ret) { 4159 this.tmp.cite_renders_content = true; 4160 } 4161 return ret; 4162}; 4163 4164CSL.Engine.prototype.getDate = function (form, forceDefaultLocale) { 4165 var lang; 4166 if (forceDefaultLocale) { 4167 lang = this.opt["default-locale"]; 4168 } else { 4169 lang = this.opt.lang; 4170 } 4171 if (this.locale[lang].dates[form]) { 4172 return this.locale[lang].dates[form]; 4173 } else { 4174 return false; 4175 } 4176}; 4177 4178CSL.Engine.prototype.getOpt = function (arg) { 4179 if ("undefined" !== typeof this.locale[this.opt.lang].opts[arg]) { 4180 return this.locale[this.opt.lang].opts[arg]; 4181 } else { 4182 return false; 4183 } 4184}; 4185 4186 4187 4188CSL.Engine.prototype.getVariable = function (Item, varname, form, plural) { 4189 return CSL.Engine.getField(CSL.LOOSE, Item, varname, form, plural); 4190}; 4191 4192CSL.Engine.prototype.getDateNum = function (ItemField, partname) { 4193 if ("undefined" === typeof ItemField) { 4194 return 0; 4195 } else { 4196 return ItemField[partname]; 4197 } 4198}; 4199 4200CSL.Engine.getField = function (mode, hash, term, form, plural, gender) { 4201 var ret, forms, f, pos, len, hashterm; 4202 ret = ""; 4203 if ("undefined" === typeof hash[term]) { 4204 if (mode === CSL.STRICT) { 4205 CSL.error("Error in getField: term \"" + term + "\" does not exist."); 4206 } else { 4207 return undefined; 4208 } 4209 } 4210 if (gender && hash[term][gender]) { 4211 hashterm = hash[term][gender]; 4212 } else { 4213 hashterm = hash[term]; 4214 } 4215 forms = []; 4216 if (form === "symbol") { 4217 forms = ["symbol", "short"]; 4218 } else if (form === "verb-short") { 4219 forms = ["verb-short", "verb"]; 4220 } else if (form !== "long") { 4221 forms = [form]; 4222 } 4223 forms = forms.concat(["long"]); 4224 len = forms.length; 4225 for (pos = 0; pos < len; pos += 1) { 4226 f = forms[pos]; 4227 if ("string" === typeof hashterm || "number" === typeof hashterm) { 4228 ret = hashterm; 4229 } else if ("undefined" !== typeof hashterm[f]) { 4230 if ("string" === typeof hashterm[f] || "number" === typeof hashterm[f]) { 4231 ret = hashterm[f]; 4232 } else { 4233 if ("number" === typeof plural) { 4234 ret = hashterm[f][plural]; 4235 } else { 4236 ret = hashterm[f][0]; 4237 } 4238 } 4239 break; 4240 } 4241 } 4242 return ret; 4243}; 4244 4245CSL.Engine.prototype.configureTokenLists = function () { 4246 var area, pos, len; 4247 //for each (var area in ["citation", "citation_sort", "bibliography","bibliography_sort"]) { 4248 len = CSL.AREAS.length; 4249 for (pos = 0; pos < len; pos += 1) { 4250 //var ret = []; 4251 area = CSL.AREAS[pos]; 4252 var tokens = this[area].tokens; 4253 this.configureTokenList(tokens); 4254 } 4255 this.version = CSL.version; 4256 return this.state; 4257}; 4258 4259CSL.Engine.prototype.configureTokenList = function (tokens) { 4260 var dateparts_master, token, dateparts, part, ppos, pppos, llen, lllen; 4261 dateparts_master = ["year", "month", "day"]; 4262 llen = tokens.length - 1; 4263 for (ppos = llen; ppos > -1; ppos += -1) { 4264 token = tokens[ppos]; 4265 //token.pos = ppos; 4266 //ret.push(token); 4267 if ("date" === token.name && CSL.END === token.tokentype) { 4268 dateparts = []; 4269 } 4270 if ("date-part" === token.name && token.strings.name) { 4271 lllen = dateparts_master.length; 4272 for (pppos = 0; pppos < lllen; pppos += 1) { 4273 part = dateparts_master[pppos]; 4274 if (part === token.strings.name) { 4275 dateparts.push(token.strings.name); 4276 } 4277 } 4278 } 4279 if ("date" === token.name && CSL.START === token.tokentype) { 4280 dateparts.reverse(); 4281 token.dateparts = dateparts; 4282 } 4283 token.next = (ppos + 1); 4284 if (token.name && CSL.Node[token.name].configure) { 4285 CSL.Node[token.name].configure.call(token, this, ppos); 4286 } 4287 } 4288}; 4289 4290CSL.Engine.prototype.refetchItems = function (ids) { 4291 var ret = []; 4292 for (var i = 0, ilen = ids.length; i < ilen; i += 1) { 4293 ret.push(this.refetchItem("" + ids[i])); 4294 } 4295 return ret; 4296}; 4297 4298CSL.ITERATION = 0; 4299 4300// Wrapper for sys.retrieveItem supplied by calling application. 4301// Adds experimental fields embedded in the note field for 4302// style development trial and testing purposes. 4303CSL.Engine.prototype.retrieveItem = function (id) { 4304 var Item, m, i; 4305 4306 if (!this.tmp.loadedItemIDs[id]) { 4307 this.tmp.loadedItemIDs[id] = true; 4308 } else { 4309 return this.registry.refhash[id]; 4310 } 4311 4312 if (this.opt.development_extensions.normalize_lang_keys_to_lowercase && 4313 "boolean" === typeof this.opt.development_extensions.normalize_lang_keys_to_lowercase) { 4314 // This is a hack. Should properly be configured by a processor method after build. 4315 for (var i=0,ilen=this.opt["default-locale"].length; i<ilen; i+=1) { 4316 this.opt["default-locale"][i] = this.opt["default-locale"][i].toLowerCase(); 4317 } 4318 for (var i=0,ilen=this.opt["locale-translit"].length; i<ilen; i+=1) { 4319 this.opt["locale-translit"][i] = this.opt["locale-translit"][i].toLowerCase(); 4320 } 4321 for (var i=0,ilen=this.opt["locale-translat"].length; i<ilen; i+=1) { 4322 this.opt["locale-translat"][i] = this.opt["locale-translat"][i].toLowerCase(); 4323 } 4324 this.opt.development_extensions.normalize_lang_keys_to_lowercase = 100; 4325 } 4326 4327 //Zotero.debug("XXX === ITERATION " + CSL.ITERATION + " "+ id +" ==="); 4328 CSL.ITERATION += 1; 4329 4330 Item = JSON.parse(JSON.stringify(this.sys.retrieveItem("" + id))); 4331 4332 // Optionally normalize keys to lowercase() 4333 if (this.opt.development_extensions.normalize_lang_keys_to_lowercase) { 4334 if (Item.multi) { 4335 if (Item.multi._keys) { 4336 for (var field in Item.multi._keys) { 4337 for (var key in Item.multi._keys[field]) { 4338 if (key !== key.toLowerCase()) { 4339 Item.multi._keys[field][key.toLowerCase()] = Item.multi._keys[field][key]; 4340 delete Item.multi._keys[field][key]; 4341 } 4342 } 4343 } 4344 } 4345 if (Item.multi.main) { 4346 for (var field in Item.multi.main) { 4347 Item.multi.main[field] = Item.multi.main[field].toLowerCase(); 4348 } 4349 } 4350 } 4351 for (var i=0, ilen=CSL.NAME_VARIABLES.length; i>ilen; i+=1) { 4352 var ctype = CSL.NAME_VARIABLES[i]; 4353 if (Item[ctype] && Item[ctype].multi) { 4354 for (var j=0, jlen=Item[ctype].length; j<jlen; j+=1) { 4355 var creator = Item[ctype][j]; 4356 if (creator.multi) { 4357 if (creator.multi._key) { 4358 for (var key in creator.multi._key) { 4359 if (key !== key.toLowerCase()) { 4360 creator.multi._key[key.toLowerCase()] = creator.multi._key[key]; 4361 delete creator.multi._key[key]; 4362 } 4363 } 4364 } 4365 if (creator.multi.main) { 4366 creator.multi.main = creator.multi.main.toLowerCase(); 4367 } 4368 } 4369 } 4370 } 4371 } 4372 } 4373 4374 // Normalize language field into "language" and "language-original" 4375 if (Item.language && Item.language.match(/[><]/)) { 4376 // Attempt to split field in two 4377 var m = Item.language.match(/(.*?)([<>])(.*)/); 4378 if (m[2] === "<") { 4379 Item["language-name"] = m[1]; 4380 Item["language-name-original"] = m[3]; 4381 } else { 4382 Item["language-name"] = m[3]; 4383 Item["language-name-original"] = m[1]; 4384 } 4385 if (this.opt.multi_layout) { 4386 if (Item["language-name-original"]) { 4387 Item.language = Item["language-name-original"]; 4388 } 4389 } else { 4390 if (Item["language-name"]) { 4391 Item.language = Item["language-name"]; 4392 } 4393 } 4394 } 4395 4396 if (Item.page) { 4397 Item["page-first"] = Item.page; 4398 var num = "" + Item.page; 4399 var m = num.split(/\s*(?:&|, |-|\u2013)\s*/); 4400 if (m[0].slice(-1) !== "\\") { 4401 Item["page-first"] = m[0]; 4402 } 4403 } 4404 // Optional development extensions 4405 if (this.opt.development_extensions.field_hack && Item.note) { 4406 // false is for validFieldsForType (all conforming entries scrubbed when false) 4407 CSL.parseNoteFieldHacks(Item, false, this.opt.development_extensions.allow_field_hack_date_override); 4408 } 4409 // not including locator-date 4410 for (var key in Item) { 4411 if (CSL.DATE_VARIABLES.indexOf(key.replace(/^alt-/, "")) > -1) { 4412 var dateobj = Item[key]; 4413 if (dateobj) { 4414 // raw date parsing is harmless, but can be disabled if desired 4415 if (this.opt.development_extensions.raw_date_parsing) { 4416 if (dateobj.raw && (!dateobj["date-parts"] || dateobj["date-parts"].length === 0)) { 4417 dateobj = this.fun.dateparser.parseDateToObject(dateobj.raw); 4418 } 4419 } 4420 Item[key] = this.dateParseArray(dateobj); 4421 } 4422 } 4423 } 4424 if (this.opt.development_extensions.consolidate_legal_items) { 4425 if (Item.type && ["bill","gazette","legislation","regulation","treaty"].indexOf(Item.type) > -1) { 4426 var varname; 4427 var elements = ["type", "title", "jurisdiction", "genre", "volume", "container-title"]; 4428 var legislation_id = []; 4429 for (var i = 0, ilen = elements.length; i < ilen; i += 1) { 4430 varname = elements[i]; 4431 if (Item[varname]) { 4432 legislation_id.push(Item[varname]); 4433 } 4434 } 4435 elements = ["original-date", "issued"]; 4436 for (var i = 0, ilen=elements.length; i < ilen; i += 1) { 4437 varname = elements[i]; 4438 if (Item[varname] && Item[varname].year) { 4439 var value = Item[varname].year; 4440 legislation_id.push(value); 4441 break; 4442 } 4443 } 4444 Item.legislation_id = legislation_id.join("::"); 4445 } 4446 } 4447 if (this.bibliography.opt.track_container_items) { 4448 if (this.bibliography.opt.track_container_items.indexOf(Item.type) > -1) { 4449 var varname; 4450 var elements = ["type", "container-title", "publisher", "edition"]; 4451 var container_id = []; 4452 for (var i = 0, ilen = elements.length; i < ilen; i += 1) { 4453 varname = elements[i]; 4454 if (Item[varname]) { 4455 container_id.push(Item[varname]); 4456 } 4457 } 4458 Item.container_id = container_id.join("::"); 4459 } 4460 } 4461 // For authority to name shape in legal styles 4462 if (this.opt.development_extensions.force_jurisdiction) { 4463 if ("string" === typeof Item.authority) { 4464 Item.authority = [ 4465 { 4466 literal: Item.authority, 4467 multi: { 4468 _key: {} 4469 } 4470 } 4471 ]; 4472 if (Item.multi && Item.multi._keys && Item.multi._keys.authority) { 4473 Item.authority[0].multi._key = {}; 4474 for (var key in Item.multi._keys.authority) { 4475 Item.authority[0].multi._key[key] = { 4476 literal: Item.multi._keys.authority[key] 4477 }; 4478 } 4479 } 4480 } 4481 } 4482 // Add getAbbreviation() call for title-short and container-title-short 4483 if (!Item["title-short"]) { 4484 Item["title-short"] = Item.shortTitle; 4485 } 4486 // Add support for main_title_from_short_title 4487 if (this.opt.development_extensions.main_title_from_short_title) { 4488 var narrowSpaceLocale = this.opt["default-locale"][0].slice(0, 2).toLowerCase() === "fr"; 4489 CSL.extractTitleAndSubtitle.call(this, Item, narrowSpaceLocale); 4490 } 4491 var isLegalType = ["bill","legal_case","legislation","gazette","regulation"].indexOf(Item.type) > -1; 4492 if (this.opt.development_extensions.force_jurisdiction && isLegalType) { 4493 if (!Item.jurisdiction) { 4494 Item.jurisdiction = "us"; 4495 } 4496 } 4497 var normalizedKey; 4498 if (!isLegalType && Item.title && this.sys.getAbbreviation) { 4499 var noHints = false; 4500 if (!Item.jurisdiction) { 4501 noHints = true; 4502 } 4503 if (this.sys.normalizeAbbrevsKey) { 4504 normalizedKey = this.sys.normalizeAbbrevsKey("title", Item.title); 4505 } else { 4506 normalizedKey = Item.title; 4507 } 4508 var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "title", normalizedKey, Item.language); 4509 if (this.transform.abbrevs[jurisdiction].title) { 4510 if (this.transform.abbrevs[jurisdiction].title[normalizedKey]) { 4511 Item["title-short"] = this.transform.abbrevs[jurisdiction].title[normalizedKey]; 4512 } 4513 } 4514 } 4515 if (!Item["container-title-short"]) { 4516 Item["container-title-short"] = Item.journalAbbreviation; 4517 } 4518 if (Item["container-title"] && this.sys.getAbbreviation) { 4519 if (this.sys.normalizeAbbrevsKey) { 4520 normalizedKey = this.sys.normalizeAbbrevsKey(Item["container-title"]); 4521 } else { 4522 normalizedKey = Item["container-title"]; 4523 } 4524 var jurisdiction = this.transform.loadAbbreviation(Item.jurisdiction, "container-title", normalizedKey, Item.language); 4525 if (this.transform.abbrevs[jurisdiction]["container-title"]) { 4526 if (this.transform.abbrevs[jurisdiction]["container-title"][normalizedKey]) { 4527 Item["container-title-short"] = this.transform.abbrevs[jurisdiction]["container-title"][normalizedKey]; 4528 } 4529 } 4530 } 4531 if (Item.jurisdiction) { 4532 Item.country = Item.jurisdiction.split(":")[0]; 4533 } 4534 if (this.registry.refhash[id]) { 4535 if (JSON.stringify(this.registry.refhash[id]) != JSON.stringify(Item)) { 4536 for (var key in this.registry.refhash[id]) { 4537 delete this.registry.refhash[id][key]; 4538 } 4539 this.tmp.taintedItemIDs[Item.id] = true; 4540 Object.assign(this.registry.refhash[id], Item); 4541 } 4542 } else { 4543 this.registry.refhash[id] = Item; 4544 } 4545 return this.registry.refhash[id]; 4546}; 4547 4548CSL.Engine.prototype.refetchItem = function (id) { 4549 return this.registry.refhash[id]; 4550}; 4551 4552// Executed during style build 4553CSL.Engine.prototype.setOpt = function (token, name, value) { 4554 if (token.name === "style" || token.name === "cslstyle") { 4555 this.opt.inheritedAttributes[name] = value; 4556 this.citation.opt.inheritedAttributes[name] = value; 4557 this.bibliography.opt.inheritedAttributes[name] = value; 4558 } else if (["citation", "bibliography"].indexOf(token.name) > -1) { 4559 this[token.name].opt.inheritedAttributes[name] = value; 4560 } else { 4561 token.strings[name] = value; 4562 } 4563}; 4564 4565// Executed at runtime, since macros can occur in the context of citation or bibliography 4566CSL.Engine.prototype.inheritOpt = function (token, attrname, parentname, defaultValue) { 4567 if ("undefined" !== typeof token.strings[attrname]) { 4568 return token.strings[attrname]; 4569 } else { 4570 var parentValue = this[this.tmp.root].opt.inheritedAttributes[parentname ? parentname : attrname]; 4571 if ("undefined" !== typeof parentValue) { 4572 return parentValue; 4573 } else { 4574 return defaultValue; 4575 } 4576 } 4577}; 4578 4579CSL.Engine.prototype.remapSectionVariable = function (inputList) { 4580 // We have items with a value in the section field (on Item) that must 4581 // be mapped to the locator field (on item). We simply prepend it as 4582 // a string here, and handle all parsing of the resulting string 4583 // in processNumber(). Plurals and numeric are set in processNumber(). 4584 4585 // Because the target is in the citation item (lowercase), the 4586 // remapping cannot take place when the Item data is received. 4587 // Citation provides a list of Item/item pairs, hence the iteration 4588 // used here. 4589 for (var i = 0, ilen = inputList.length; i < ilen; i += 1) { 4590 var Item = inputList[i][0]; 4591 var item = inputList[i][1]; 4592 4593 if (["bill","gazette","legislation","regulation","treaty"].indexOf(Item.type) > -1) { 4594 // If a locator value exists, then 4595 // (a) Leave be an overriding label at the start of the locator field, defaulting to label value 4596 if (item.locator) { 4597 item.locator = item.locator.trim(); 4598 var m = item.locator.match(CSL.STATUTE_SUBDIV_PLAIN_REGEX_FRONT); 4599 if (!m) { 4600 if (item.label) { 4601 item.locator = CSL.STATUTE_SUBDIV_STRINGS_REVERSE[item.label] + " " + item.locator; 4602 } else { 4603 item.locator = "p. " + item.locator; 4604 } 4605 } 4606 } 4607 // If a section value exists, then 4608 // (a) Apply an overriding label at the start of the section field, defaulting to sec. 4609 var sectionMasterLabel = null; 4610 if (Item.section) { 4611 Item.section = Item.section.trim(); 4612 var m = Item.section.match(CSL.STATUTE_SUBDIV_PLAIN_REGEX_FRONT); 4613 if (!m) { 4614 Item.section = "sec. " + Item.section; 4615 sectionMasterLabel = "sec."; 4616 } else { 4617 sectionMasterLabel = m[0].trim(); 4618 } 4619 } 4620 // If section is nil, then 4621 // (a) Do nothing 4622 if (Item.section) { 4623 // If section exists and locator is nil 4624 // (a) Set section string in locator field 4625 if (!item.locator) { 4626 item.locator = Item.section; 4627 } else { 4628 // If both section and locator exist, then 4629 // (a) If locator starts with p., remove p., merge with space or no-space, and set in locator field 4630 // (b) If locator starts with non-p., prepend section value to locator with space, and set in locator field 4631 var m = item.locator.match(/^([^ ]*)\s*(.*)/); 4632 var space = " "; 4633 if (m) { 4634 if (m[1] === "p." && sectionMasterLabel !== "p.") { 4635 item.locator = m[2]; 4636 } 4637 if (["[", "(", ".", ",", ";", ":", "?"].indexOf(item.locator.slice(0, 1)) > -1) { 4638 space = ""; 4639 } 4640 } else { 4641 space = ""; 4642 } 4643 item.locator = Item.section + space + item.locator; 4644 } 4645 //Item.section = ""; 4646 } 4647 item.label = ""; 4648 // And that's it. Pre-parse complete. 4649 } 4650 } 4651}; 4652 4653 4654CSL.Engine.prototype.setNumberLabels = function (Item) { 4655 if (Item.number 4656 && ["bill", "gazette", "legislation","regulation","treaty"].indexOf(Item.type) > -1 4657 && this.opt.development_extensions.consolidate_legal_items 4658 && !this.tmp.shadow_numbers["number"]) { 4659 4660 this.tmp.shadow_numbers["number"] = {}; 4661 this.tmp.shadow_numbers["number"].values = []; 4662 this.tmp.shadow_numbers["number"].plural = 0; 4663 this.tmp.shadow_numbers["number"].numeric = false; 4664 this.tmp.shadow_numbers["number"].label = false; 4665 4666 // Labels embedded in number variable 4667 var value = "" + Item.number; 4668 value = value.split("\\").join(""); 4669 // Get first word, parse out labels only if it parses 4670 var firstword = value.split(/\s+/)[0]; 4671 var firstlabel = CSL.STATUTE_SUBDIV_STRINGS[firstword]; 4672 if (firstlabel) { 4673 // Get list and match 4674 var splt = value.split(CSL.STATUTE_SUBDIV_PLAIN_REGEX); 4675 if (splt.length > 1) { 4676 // Convert matches to localized form 4677 var lst = []; 4678 for (var j=1, jlen=splt.length; j < jlen; j += 1) { 4679 lst.push(splt[j].replace(/\s*$/, "").replace(/^\s*/, "")); 4680 } 4681 // Preemptively save to shadow_numbers 4682 value = lst.join(" "); 4683 } else { 4684 value = splt[0]; 4685 } 4686 this.tmp.shadow_numbers["number"].label = firstlabel; 4687 this.tmp.shadow_numbers["number"].values.push(["Blob", value, false]); 4688 this.tmp.shadow_numbers["number"].numeric = false; 4689 } else { 4690 this.tmp.shadow_numbers["number"].values.push(["Blob", value, false]); 4691 this.tmp.shadow_numbers["number"].numeric = true; 4692 } 4693 } 4694}; 4695 4696/*global CSL: true */ 4697 4698CSL.substituteOne = function (template) { 4699 return function (state, list) { 4700 if (!list) { 4701 return ""; 4702 } else { 4703 // ("string" === typeof list) 4704 return template.replace("%%STRING%%", list); 4705 } 4706 }; 4707}; 4708 4709 4710/** 4711 * Two-tiered substitutions gadget. 4712 * <p>This is used for 4713 * options like (now defunct) "font-family", where the option value 4714 * cannot be determined until the attribute is processed. 4715 * Need for this function might be reviewed at some point ...</p> 4716 * @param {String} template A template containing 4717 * <code>%%STRING%%</code> and <code>%%PARAM%%</code> 4718 * placeholders. See {@link CSL.Output.Formats.html} for 4719 * examples. 4720 */ 4721CSL.substituteTwo = function (template) { 4722 return function (param) { 4723 var template2 = template.replace("%%PARAM%%", param); 4724 return function (state, list) { 4725 if (!list) { 4726 return ""; 4727 } else { 4728 //("string" === typeof list){ 4729 return template2.replace("%%STRING%%", list); 4730 } 4731 }; 4732 }; 4733}; 4734 4735/** 4736 * Generate string functions for designated output mode. 4737 * <p>Only "html" (the default) is supported at present.</p> 4738 * @param {String} mode Either "html" or "rtf", eventually. 4739 */ 4740CSL.Mode = function (mode) { 4741 var decorations, params, param, func, val, args; 4742 decorations = {}; 4743 params = CSL.Output.Formats[mode]; 4744 for (param in params) { 4745 if (true) { 4746 4747 if ("@" !== param.slice(0, 1)) { 4748 decorations[param] = params[param]; 4749 continue; 4750 } 4751 func = false; 4752 val = params[param]; 4753 args = param.split('/'); 4754 4755 if (typeof val === "string" && val.indexOf("%%STRING%%") > -1) { 4756 if (val.indexOf("%%PARAM%%") > -1) { 4757 func = CSL.substituteTwo(val); 4758 } else { 4759 func = CSL.substituteOne(val); 4760 } 4761 } else if (typeof val === "boolean" && !val) { 4762 func = CSL.Output.Formatters.passthrough; 4763 } else if (typeof val === "function") { 4764 func = val; 4765 } else { 4766 CSL.error("Bad " + mode + " config entry for " + param + ": " + val); 4767 } 4768 4769 if (args.length === 1) { 4770 decorations[args[0]] = func; 4771 } else if (args.length === 2) { 4772 if (!decorations[args[0]]) { 4773 decorations[args[0]] = {}; 4774 } 4775 decorations[args[0]][args[1]] = func; 4776 } 4777 } 4778 } 4779 return decorations; 4780}; 4781 4782 4783/** 4784 * Generate a separate list of formatting attributes. 4785 * <p>This generates a list of tuples containing attribute 4786 * information relevant to output formatting, in the order 4787 * fixed in the constant {@link CSL.FORMAT_KEY_SEQUENCE}. 4788 * This function is called during {@link CSL.Core.Build}. 4789 * Formatting hints are distilled to functions 4790 * later, in the second compilation pass ({@link CSL.Core.Configure}).</p> 4791 * @param {Object} state The state object returned by 4792 * {@link CSL.Engine}. 4793 * @param {Object} attributes The hash object containing 4794 * the attributes and values extracted from an XML node. 4795 */ 4796CSL.setDecorations = function (state, attributes) { 4797 var ret, key, pos; 4798 // This applies a fixed processing sequence 4799 ret = []; 4800 for (pos in CSL.FORMAT_KEY_SEQUENCE) { 4801 if (true) { 4802 var key = CSL.FORMAT_KEY_SEQUENCE[pos]; 4803 if (attributes[key]) { 4804 ret.push([key, attributes[key]]); 4805 delete attributes[key]; 4806 } 4807 } 4808 } 4809 return ret; 4810}; 4811 4812CSL.Doppeler = function(rexStr, stringMangler) { 4813 var matchRex = new RegExp("(" + rexStr + ")", "g"); 4814 var splitRex = new RegExp(rexStr, "g"); 4815 this.split = function (str) { 4816 // Normalize markup 4817 if (stringMangler) { 4818 str = stringMangler(str); 4819 } 4820 var match = str.match(matchRex); 4821 if (!match) { 4822 return { 4823 tags: [], 4824 strings: [str] 4825 }; 4826 } 4827 var split = str.split(splitRex); 4828 for (var i=match.length-1; i> -1; i--) { 4829 if (typeof match[i] === "number") { 4830 match[i] = ""; 4831 } 4832 var tag = match[i]; 4833 if (tag === "\'" && split[i+1].length > 0) { 4834 // Fixes https://forums.zotero.org/discussion/comment/294317 4835 split[i+1] = match[i] + split[i+1]; 4836 match[i] = ""; 4837 } 4838 } 4839 return { 4840 tags: match, 4841 strings: split, 4842 origStrings: split.slice() 4843 }; 4844 }; 4845 this.join = function (obj) { 4846 var lst = obj.strings.slice(-1); 4847 for (var i=obj.tags.length-1; i>-1; i--) { 4848 lst.push(obj.tags[i]); 4849 lst.push(obj.strings[i]); 4850 } 4851 lst.reverse(); 4852 return lst.join(""); 4853 }; 4854}; 4855 4856CSL.Engine.prototype.normalDecorIsOrphan = function (blob, params) { 4857 //print("params: "+JSON.stringify(params)); 4858 if (params[1] === "normal") { 4859 var use_param = false; 4860 var all_the_decor; 4861 if (this.tmp.area === "citation") { 4862 all_the_decor = [this.citation.opt.layout_decorations].concat(blob.alldecor); 4863 } else { 4864 all_the_decor = blob.alldecor; 4865 } 4866 for (var k = all_the_decor.length - 1; k > -1; k += -1) { 4867 //print(" all decor: "+JSON.stringify(all_the_decor[k])); 4868 for (var n = all_the_decor[k].length - 1; n > -1; n += -1) { 4869 //print(" superior param"+n+": "+all_the_decor[k][n][0]); 4870 if (all_the_decor[k][n][0] === params[0]) { 4871 //print(" HIT!"); 4872 if (all_the_decor[k][n][1] !== "normal") { 4873 use_param = true; 4874 } 4875 } 4876 } 4877 } 4878 if (!use_param) { 4879 return true; 4880 } 4881 } 4882 return false; 4883}; 4884 4885/*global CSL: true */ 4886 4887 4888CSL.Engine.prototype.getCitationLabel = function (Item) { 4889 var label = ""; 4890 var params = this.getTrigraphParams(); 4891 var config = params[0]; 4892 var myname = this.getTerm("reference", "short", 0); 4893 if ("undefined" === typeof myname) { 4894 myname = "reference"; 4895 } 4896 myname = myname.replace(".", ""); 4897 myname = myname.slice(0, 1).toUpperCase() + myname.slice(1); 4898 for (var i = 0, ilen = CSL.NAME_VARIABLES.length; i < ilen; i += 1) { 4899 var n = CSL.NAME_VARIABLES[i]; 4900 if (Item[n]) { 4901 var names = Item[n]; 4902 if (names.length > params.length) { 4903 config = params[params.length - 1]; 4904 } else { 4905 config = params[names.length - 1]; 4906 } 4907 for (var j = 0, jlen = names.length; j < jlen; j += 1) { 4908 if (j === config.authors.length) { 4909 break; 4910 } 4911 var res = this.nameOutput.getName(names[j], "locale-translit", true); 4912 var name = res.name; 4913 if (name && name.family) { 4914 myname = name.family; 4915 myname = myname.replace(/^([ \'\u2019a-z]+\s+)/, ""); 4916 4917 } else if (name && name.literal) { 4918 myname = name.literal; 4919 } 4920 var m = myname.toLowerCase().match(/^(a\s+|the\s+|an\s+)/); 4921 if (m) { 4922 myname = myname.slice(m[1].length); 4923 } 4924 myname = myname.replace(CSL.ROMANESQUE_NOT_REGEXP, ""); 4925 if (!myname) { 4926 break; 4927 } 4928 myname = myname.slice(0, config.authors[j]); 4929 if (myname.length > 1) { 4930 myname = myname.slice(0, 1).toUpperCase() + myname.slice(1).toLowerCase(); 4931 } else if (myname.length === 1) { 4932 myname = myname.toUpperCase(); 4933 } 4934 label += myname; 4935 } 4936 break; 4937 } 4938 } 4939 if (!label) { 4940 // Try for something using title 4941 if (Item.title) { 4942 var skipWords = this.locale[this.opt.lang].opts["skip-words"]; 4943 var lst = Item.title.split(/\s+/); 4944 for (var i = lst.length - 1; i > -1; i--) { 4945 if (skipWords.indexOf(lst[i]) > -1) { 4946 lst = lst.slice(0, i).concat(lst.slice(i + 1)); 4947 } 4948 } 4949 var str = lst.join(''); 4950 str = str.slice(0, params[0].authors[0]); 4951 if (str.length > 1) { 4952 str = str.slice(0, 1).toUpperCase() + str.slice(1).toLowerCase(); 4953 } else if (str.length === 1) { 4954 str = str.toUpperCase(); 4955 } 4956 label = str; 4957 } 4958 } 4959 var year = "0000"; 4960 if (Item.issued) { 4961 if (Item.issued.year) { 4962 year = "" + Item.issued.year; 4963 } 4964 } 4965 year = year.slice((config.year * -1)); 4966 label = label + year; 4967 return label; 4968}; 4969 4970CSL.Engine.prototype.getTrigraphParams = function () { 4971 var params = []; 4972 var ilst = this.opt.trigraph.split(":"); 4973 if (!this.opt.trigraph || this.opt.trigraph.slice(0,1) !== "A") { 4974 CSL.error("Bad trigraph definition: "+this.opt.trigraph); 4975 } 4976 for (var i = 0, ilen = ilst.length; i < ilen; i += 1) { 4977 var str = ilst[i]; 4978 var config = {authors:[], year:0}; 4979 for (var j = 0, jlen = str.length; j < jlen; j += 1) { 4980 switch (str.slice(j,j+1)) { 4981 case "A": 4982 config.authors.push(1); 4983 break; 4984 case "a": 4985 config.authors[config.authors.length - 1] += 1; 4986 break; 4987 case "0": 4988 config.year += 1; 4989 break; 4990 default: 4991 CSL.error("Invalid character in trigraph definition: "+this.opt.trigraph); 4992 } 4993 } 4994 params.push(config); 4995 } 4996 return params; 4997}; 4998 4999/*global CSL: true */ 5000 5001CSL.Engine.prototype.setOutputFormat = function (mode) { 5002 this.opt.mode = mode; 5003 this.fun.decorate = CSL.Mode(mode); 5004 if (!this.output[mode]) { 5005 this.output[mode] = {}; 5006 this.output[mode].tmp = {}; 5007 } 5008}; 5009 5010CSL.Engine.prototype.getSortFunc = function () { 5011 return function (a,b) { 5012 a = a.split("-"); 5013 b = b.split("-"); 5014 if (a.length < b.length) { 5015 return 1; 5016 } else if (a.length > b.length) { 5017 return -1; 5018 } else { 5019 a = a.slice(-1)[0]; 5020 b = b.slice(-1)[0]; 5021 if (a.length < b.length) { 5022 return 1; 5023 } else if (a.length > b.length) { 5024 return -1; 5025 } else { 5026 return 0; 5027 } 5028 } 5029 }; 5030}; 5031 5032CSL.Engine.prototype.setLangTagsForCslSort = function (tags) { 5033 var i, ilen; 5034 if (tags) { 5035 this.opt['locale-sort'] = []; 5036 for (i = 0, ilen = tags.length; i < ilen; i += 1) { 5037 this.opt['locale-sort'].push(tags[i]); 5038 } 5039 } 5040 this.opt['locale-sort'].sort(this.getSortFunc()); 5041}; 5042 5043CSL.Engine.prototype.setLangTagsForCslTransliteration = function (tags) { 5044 var i, ilen; 5045 this.opt['locale-translit'] = []; 5046 if (tags) { 5047 for (i = 0, ilen = tags.length; i < ilen; i += 1) { 5048 this.opt['locale-translit'].push(tags[i]); 5049 } 5050 } 5051 this.opt['locale-translit'].sort(this.getSortFunc()); 5052}; 5053 5054CSL.Engine.prototype.setLangTagsForCslTranslation = function (tags) { 5055 var i, ilen; 5056 this.opt['locale-translat'] = []; 5057 if (tags) { 5058 for (i = 0, ilen = tags.length; i < ilen; i += 1) { 5059 this.opt['locale-translat'].push(tags[i]); 5060 } 5061 } 5062 this.opt['locale-translat'].sort(this.getSortFunc()); 5063}; 5064 5065CSL.Engine.prototype.setLangPrefsForCites = function (obj, conv) { 5066 var opt = this.opt['cite-lang-prefs']; 5067 if (!conv) { 5068 conv = function (key) { 5069 return key.toLowerCase(); 5070 }; 5071 } 5072 var segments = ['Persons', 'Institutions', 'Titles', 'Journals', 'Publishers', 'Places']; 5073 // Set values in place 5074 for (var i = 0, ilen = segments.length; i < ilen; i += 1) { 5075 var clientSegment = conv(segments[i]); 5076 var citeprocSegment = segments[i].toLowerCase(); 5077 if (!obj[clientSegment]) { 5078 continue; 5079 } 5080 // 5081 // Normalize the sequence of secondary and tertiary 5082 // in the provided obj segment list. 5083 // 5084 var supplements = []; 5085 while (obj[clientSegment].length > 1) { 5086 supplements.push(obj[clientSegment].pop()); 5087 } 5088 var sortval = {orig:1,translit:2,translat:3}; 5089 if (supplements.length === 2 && sortval[supplements[0]] < sortval[supplements[1]]) { 5090 supplements.reverse(); 5091 } 5092 while (supplements.length) { 5093 obj[clientSegment].push(supplements.pop()); 5094 } 5095 // 5096 // normalization done. 5097 // 5098 var lst = opt[citeprocSegment]; 5099 while (lst.length) { 5100 lst.pop(); 5101 } 5102 for (var j = 0, jlen = obj[clientSegment].length; j < jlen; j += 1) { 5103 lst.push(obj[clientSegment][j]); 5104 } 5105 } 5106}; 5107 5108CSL.Engine.prototype.setLangPrefsForCiteAffixes = function (affixList) { 5109 if (affixList && affixList.length === 48) { 5110 var affixes = this.opt.citeAffixes; 5111 var count = 0; 5112 var settings = ["persons", "institutions", "titles", "journals", "publishers", "places"]; 5113 var forms = ["translit", "orig", "translit", "translat"]; 5114 var value; 5115 for (var i = 0, ilen = settings.length; i < ilen; i += 1) { 5116 for (var j = 0, jlen = forms.length; j < jlen; j += 1) { 5117 value = ""; 5118 if ((count % 8) === 4) { 5119 if (!affixes[settings[i]]["locale-"+forms[j]].prefix 5120 && !affixes[settings[i]]["locale-"+forms[j]].suffix) { 5121 5122 value = affixList[count] ? affixList[count] : ""; 5123 affixes[settings[i]]["locale-" + forms[j]].prefix = value; 5124 value = affixList[count] ? affixList[count + 1] : ""; 5125 affixes[settings[i]]["locale-" + forms[j]].suffix = value; 5126 } 5127 } else { 5128 value = affixList[count] ? affixList[count] : ""; 5129 affixes[settings[i]]["locale-" + forms[j]].prefix = value; 5130 value = affixList[count] ? affixList[count + 1] : ""; 5131 affixes[settings[i]]["locale-" + forms[j]].suffix = value; 5132 } 5133 count += 2; 5134 } 5135 } 5136 this.opt.citeAffixes = affixes; 5137 } 5138}; 5139 5140CSL.Engine.prototype.setAutoVietnameseNamesOption = function (arg) { 5141 if (arg) { 5142 this.opt["auto-vietnamese-names"] = true; 5143 } else { 5144 this.opt["auto-vietnamese-names"] = false; 5145 } 5146}; 5147 5148CSL.Engine.prototype.setAbbreviations = function (arg) { 5149 if (this.sys.setAbbreviations) { 5150 this.sys.setAbbreviations(arg); 5151 } 5152}; 5153 5154CSL.Engine.prototype.setSuppressTrailingPunctuation = function (arg) { 5155 this.citation.opt.suppressTrailingPunctuation = !!arg; 5156}; 5157 5158/*global CSL: true */ 5159 5160CSL.Output = {}; 5161/** 5162 * Output queue object. 5163 * @class 5164 */ 5165CSL.Output.Queue = function (state) { 5166 this.levelname = ["top"]; 5167 this.state = state; 5168 this.queue = []; 5169 this.empty = new CSL.Token("empty"); 5170 var tokenstore = {}; 5171 tokenstore.empty = this.empty; 5172 this.formats = new CSL.Stack(tokenstore); 5173 this.current = new CSL.Stack(this.queue); 5174}; 5175 5176// XXX This works, but causes a mismatch in api_cite 5177// Could insert a placeholder 5178// Better to have a function that spits out an independent blob. 5179// Is that possible though? 5180// Okay. Use queue.append() with fake_queue instead. 5181CSL.Output.Queue.prototype.pop = function () { 5182 // For some reason, state.output.current.value() here can be an array, 5183 // not a blob ... ? 5184 var drip = this.current.value(); 5185 if (drip.length) { 5186 return drip.pop(); 5187 } else { 5188 return drip.blobs.pop(); 5189 } 5190}; 5191 5192CSL.Output.Queue.prototype.getToken = function (name) { 5193 var ret = this.formats.value()[name]; 5194 return ret; 5195}; 5196 5197CSL.Output.Queue.prototype.mergeTokenStrings = function (base, modifier) { 5198 var base_token, modifier_token, ret, key; 5199 base_token = this.formats.value()[base]; 5200 modifier_token = this.formats.value()[modifier]; 5201 ret = base_token; 5202 if (modifier_token) { 5203 if (!base_token) { 5204 base_token = new CSL.Token(base, CSL.SINGLETON); 5205 base_token.decorations = []; 5206 } 5207 ret = new CSL.Token(base, CSL.SINGLETON); 5208 var key = ""; 5209 for (var key in base_token.strings) { 5210 if (base_token.strings.hasOwnProperty(key)) { 5211 ret.strings[key] = base_token.strings[key]; 5212 } 5213 } 5214 for (var key in modifier_token.strings) { 5215 if (modifier_token.strings.hasOwnProperty(key)) { 5216 ret.strings[key] = modifier_token.strings[key]; 5217 } 5218 } 5219 ret.decorations = base_token.decorations.concat(modifier_token.decorations); 5220 } 5221 return ret; 5222}; 5223 5224// Store a new output format token based on another 5225CSL.Output.Queue.prototype.addToken = function (name, modifier, token) { 5226 var newtok, attr; 5227 newtok = new CSL.Token("output"); 5228 if ("string" === typeof token) { 5229 token = this.formats.value()[token]; 5230 } 5231 if (token && token.strings) { 5232 for (attr in token.strings) { 5233 if (token.strings.hasOwnProperty(attr)) { 5234 newtok.strings[attr] = token.strings[attr]; 5235 } 5236 } 5237 newtok.decorations = token.decorations; 5238 5239 } 5240 if ("string" === typeof modifier) { 5241 newtok.strings.delimiter = modifier; 5242 } 5243 this.formats.value()[name] = newtok; 5244}; 5245 5246// 5247// newFormat adds a new bundle of formatting tokens to 5248// the queue's internal stack of such bundles 5249CSL.Output.Queue.prototype.pushFormats = function (tokenstore) { 5250 if (!tokenstore) { 5251 tokenstore = {}; 5252 } 5253 tokenstore.empty = this.empty; 5254 this.formats.push(tokenstore); 5255}; 5256 5257 5258CSL.Output.Queue.prototype.popFormats = function () { 5259 this.formats.pop(); 5260}; 5261 5262CSL.Output.Queue.prototype.startTag = function (name, token) { 5263 var tokenstore = {}; 5264 if (this.state.tmp["doing-macro-with-date"] && this.state.tmp.extension) { 5265 token = this.empty; 5266 name = "empty"; 5267 } 5268 tokenstore[name] = token; 5269 this.pushFormats(tokenstore); 5270 this.openLevel(name); 5271}; 5272 5273CSL.Output.Queue.prototype.endTag = function (name) { 5274 this.closeLevel(name); 5275 this.popFormats(); 5276}; 5277 5278// 5279// newlevel adds a new blob object to the end of the current 5280// list, and adjusts the current pointer so that subsequent 5281// appends are made to blob list of the new object. 5282 5283CSL.Output.Queue.prototype.openLevel = function (token) { 5284 var blob, curr; 5285 if ("object" === typeof token) { 5286 // delimiter, prefix, suffix, decorations from token 5287 blob = new CSL.Blob(undefined, token); 5288 } else if ("undefined" === typeof token) { 5289 blob = new CSL.Blob(undefined, this.formats.value().empty, "empty"); 5290 } else { 5291 if (!this.formats.value() || !this.formats.value()[token]) { 5292 CSL.error("CSL processor error: call to nonexistent format token \"" + token + "\""); 5293 } 5294 // delimiter, prefix, suffix, decorations from token 5295 blob = new CSL.Blob(undefined, this.formats.value()[token], token); 5296 } 5297 curr = this.current.value(); 5298 if (!this.state.tmp.just_looking && this.checkNestedBrace) { 5299 blob.strings.prefix = this.checkNestedBrace.update(blob.strings.prefix); 5300 } 5301 curr.push(blob); 5302 this.current.push(blob); 5303}; 5304 5305/** 5306 * "merge" used to be real complicated, now it's real simple. 5307 */ 5308CSL.Output.Queue.prototype.closeLevel = function (name) { 5309 // CLEANUP: Okay, so this.current.value() holds the blob at the 5310 // end of the current list. This is wrong. It should 5311 // be the parent, so that we have the choice of reading 5312 // the affixes and decorations, or appending to its 5313 // content. The code that manipulates blobs will be 5314 // much simpler that way. 5315 if (name && name !== this.current.value().levelname) { 5316 CSL.error("Level mismatch error: wanted " + name + " but found " + this.current.value().levelname); 5317 } 5318 var blob = this.current.pop(); 5319 if (!this.state.tmp.just_looking && this.checkNestedBrace) { 5320 blob.strings.suffix = this.checkNestedBrace.update(blob.strings.suffix); 5321 } 5322}; 5323 5324// 5325// append does the same thing as newlevel, except 5326// that the blob it pushes has text content, 5327// and the current pointer is not moved after the push. 5328 5329CSL.Output.Queue.prototype.append = function (str, tokname, notSerious, ignorePredecessor, noStripPeriods) { 5330 var token, blob, curr; 5331 var useblob = true; 5332 if (notSerious) { 5333 ignorePredecessor = true; 5334 } 5335 // XXXXX Nasty workaround, but still an improvement 5336 // over the reverse calls to the cs:date node build 5337 // function that we had before. 5338 if (this.state.tmp["doing-macro-with-date"] && !notSerious) { 5339 if (tokname !== "macro-with-date") { 5340 return false; 5341 } 5342 if (tokname === "macro-with-date") { 5343 tokname = "empty"; 5344 } 5345 } 5346 if ("undefined" === typeof str) { 5347 return false; 5348 } 5349 if ("number" === typeof str) { 5350 str = "" + str; 5351 } 5352 if (!notSerious 5353 && this.state.tmp.element_trace 5354 && this.state.tmp.element_trace.value() === "suppress-me") { 5355 5356 return false; 5357 } 5358 blob = false; 5359 if (!tokname) { 5360 token = this.formats.value().empty; 5361 } else if (tokname === "literal") { 5362 token = true; 5363 useblob = false; 5364 } else if ("string" === typeof tokname) { 5365 token = this.formats.value()[tokname]; 5366 } else { 5367 token = tokname; 5368 } 5369 if (!token) { 5370 CSL.error("CSL processor error: unknown format token name: " + tokname); 5371 } 5372 // Unset delimiters must be left undefined until they reach the queue 5373 // in order to discriminate unset from explicitly empty delimiters 5374 // when inheriting a default value from a superior node. [??? really ???] 5375 if (token.strings && "undefined" === typeof token.strings.delimiter) { 5376 token.strings.delimiter = ""; 5377 } 5378 if ("string" === typeof str && str.length) { 5379 5380 // Source (;?!»«): http://en.wikipedia.org/wiki/Space_(punctuation)#Breaking_and_non-breaking_spaces 5381 // Source (:): http://forums.zotero.org/discussion/4933/localized-quotes/#Comment_88384 5382 str = str.replace(/ ([:;?!\u00bb])/g, "\u202f$1").replace(/\u00ab /g, "\u00ab\u202f"); 5383 5384 this.last_char_rendered = str.slice(-1); 5385 // This, and not the str argument below on flipflop, is the 5386 // source of the flipflopper string source. 5387 str = str.replace(/\s+'/g, " \'"); 5388 if (!notSerious) { 5389 // this condition for sort_LeadingApostropheOnNameParticle 5390 str = str.replace(/^'/g, " \'"); 5391 } 5392 5393 // signal whether we end with terminal punctuation? 5394 if (!ignorePredecessor) { 5395 this.state.tmp.term_predecessor = true; 5396 this.state.tmp.in_cite_predecessor = true; 5397 } else if (notSerious) { 5398 this.state.tmp.term_predecessor_name = true; 5399 } 5400 } 5401 blob = new CSL.Blob(str, token); 5402 curr = this.current.value(); 5403 if ("undefined" === typeof curr && this.current.mystack.length === 0) { 5404 // XXXX An operation like this is missing somewhere, this should NOT be necessary. 5405 // Addresses error triggered in multi-layouts. 5406 this.current.mystack.push([]); 5407 curr = this.current.value(); 5408 } 5409 if ("string" === typeof blob.blobs) { 5410 if (!ignorePredecessor) { 5411 this.state.tmp.term_predecessor = true; 5412 this.state.tmp.in_cite_predecessor = true; 5413 } else if (notSerious) { 5414 this.state.tmp.term_predecessor_name = true; 5415 } 5416 } 5417 // 5418 // Caution: The parallel detection machinery will blow up if tracking 5419 // variables are not properly initialized elsewhere. 5420 // 5421 if ("string" === typeof str) { 5422 if ("string" === typeof blob.blobs) { 5423 if (blob.blobs.slice(0, 1) !== " ") { 5424 var blobPrefix = ""; 5425 var blobBlobs = blob.blobs; 5426 while (CSL.TERMINAL_PUNCTUATION.indexOf(blobBlobs.slice(0, 1)) > -1) { 5427 blobPrefix = blobPrefix + blobBlobs.slice(0, 1); 5428 blobBlobs = blobBlobs.slice(1); 5429 } 5430 if (blobBlobs && blobPrefix) { 5431 blob.strings.prefix = blob.strings.prefix + blobPrefix; 5432 blob.blobs = blobBlobs; 5433 } 5434 } 5435 } 5436 if (blob.strings["text-case"]) { 5437 // 5438 // This one is _particularly_ hard to follow. It's not obvious, 5439 // but the blob already contains the input string at this 5440 // point, as blob.blobs -- it's a terminal node, as it were. 5441 // The str variable also contains the input string, but 5442 // that copy is not used for onward processing. We have to 5443 // apply our changes to the blob copy. 5444 // 5445 blob.blobs = CSL.Output.Formatters[blob.strings["text-case"]](this.state, str); 5446 } 5447 if (this.state.tmp.strip_periods && !noStripPeriods) { 5448 blob.blobs = blob.blobs.replace(/\.([^a-z]|$)/g, "$1"); 5449 } 5450 for (var i = blob.decorations.length - 1; i > -1; i += -1) { 5451 if (blob.decorations[i][0] === "@quotes" && blob.decorations[i][1] !== "false") { 5452 blob.punctuation_in_quote = this.state.getOpt("punctuation-in-quote"); 5453 } 5454 if (!blob.blobs.match(CSL.ROMANESQUE_REGEXP)) { 5455 if (blob.decorations[i][0] === "@font-style") { 5456 blob.decorations = blob.decorations.slice(0, i).concat(blob.decorations.slice(i + 1)); 5457 } 5458 } 5459 } 5460 // 5461 // XXX: Beware superfluous code in your code. str in this 5462 // case is not the source of the final rendered string. 5463 // See note above. 5464 // 5465 curr.push(blob); 5466 this.state.fun.flipflopper.processTags(blob); 5467 } else if (useblob) { 5468 curr.push(blob); 5469 } else { 5470 curr.push(str); 5471 } 5472 return true; 5473}; 5474 5475CSL.Output.Queue.prototype.string = function (state, myblobs, blob) { 5476 var i, ilen, j, jlen, b; 5477 //if (blob && blob.strings.delimiter) { 5478 // print("DELIMITER: "+blob.strings.delimiter+" on "+[x.blobs[0].num for each (x in myblobs)]); 5479 //} 5480 //var blobs, ret, blob_delimiter, i, params, blobjr, last_str, last_char, b, use_suffix, qres, addtoret, span_split, j, res, blobs_start, blobs_end, key, pos, len, ppos, llen, ttype, ltype, terminal, leading, delimiters, use_prefix, txt_esc; 5481 var txt_esc = CSL.getSafeEscape(this.state); 5482 var blobs = myblobs.slice(); 5483 var ret = []; 5484 5485 if (blobs.length === 0) { 5486 return ret; 5487 } 5488 5489 var blob_delimiter = ""; 5490 if (blob) { 5491 blob_delimiter = blob.strings.delimiter; 5492 } else { 5493 //print("=== Setting false to start ==="); 5494 state.tmp.count_offset_characters = false; 5495 state.tmp.offset_characters = 0; 5496 } 5497 5498 if (blob && blob.new_locale) { 5499 blob.old_locale = state.opt.lang; 5500 state.opt.lang = blob.new_locale; 5501 } 5502 5503 var blobjr, use_suffix, use_prefix, params; 5504 for (var i = 0, ilen = blobs.length; i < ilen; i += 1) { 5505 blobjr = blobs[i]; 5506 5507 if (blobjr.strings.first_blob) { 5508 // Being the Item.id of the the entry being rendered. 5509 //print(" -- turning on counting"); 5510 state.tmp.count_offset_characters = blobjr.strings.first_blob; 5511 } 5512 5513 if ("string" === typeof blobjr.blobs) { 5514 if ("number" === typeof blobjr.num) { 5515 ret.push(blobjr); 5516 } else if (blobjr.blobs) { 5517 if (blobjr.particle) { 5518 blobjr.blobs = blobjr.particle + blobjr.blobs; 5519 blobjr.particle = ""; 5520 } 5521 // (skips empty strings) 5522 //b = txt_esc(blobjr.blobs); 5523 b = txt_esc(blobjr.blobs); 5524 var blen = b.length; 5525 5526 if (!state.tmp.suppress_decorations) { 5527 for (j = 0, jlen = blobjr.decorations.length; j < jlen; j += 1) { 5528 params = blobjr.decorations[j]; 5529 if (params[0] === "@showid") { 5530 continue; 5531 } 5532 if (state.normalDecorIsOrphan(blobjr, params)) { 5533 continue; 5534 } 5535 b = state.fun.decorate[params[0]][params[1]].call(blobjr, state, b, params[2]); 5536 } 5537 } 5538 // 5539 // because we will rip out portions of the output 5540 // queue before rendering, group wrappers need 5541 // to produce no output if they are found to be 5542 // empty. 5543 if (b && b.length) { 5544 b = txt_esc(blobjr.strings.prefix) + b + txt_esc(blobjr.strings.suffix); 5545 if (state.opt.development_extensions.csl_reverse_lookup_support && !state.tmp.suppress_decorations) { 5546 for (j = 0, jlen = blobjr.decorations.length; j < jlen; j += 1) { 5547 params = blobjr.decorations[j]; 5548 5549 if (params[0] === "@showid") { 5550 b = state.fun.decorate[params[0]][params[1]].call(blobjr, state, b, params[2]); 5551 } 5552 } 5553 } 5554 ret.push(b); 5555 if (state.tmp.count_offset_characters) { 5556 state.tmp.offset_characters += (blen + blobjr.strings.suffix.length + blobjr.strings.prefix.length); 5557 } 5558 } 5559 } 5560 } else if (blobjr.blobs.length) { 5561 var addtoret = state.output.string(state, blobjr.blobs, blobjr); 5562 if (blob) { 5563 // Patch up world-class weird bug in the ill-constructed code of mine. 5564 if ("string" !== addtoret && addtoret.length > 1 && blobjr.strings.delimiter) { 5565 var numberSeen = false; 5566 for (var j=0,jlen=addtoret.length;j<jlen;j++) { 5567 if ("string" !== typeof addtoret[j]) { 5568 numberSeen = true; 5569 } else if (numberSeen) { 5570 addtoret[j] = (blobjr.strings.delimiter + addtoret[j]); 5571 } 5572 } 5573 } 5574 } 5575 ret = ret.concat(addtoret); 5576 } 5577 if (blobjr.strings.first_blob && state.registry.registry[blobjr.strings.first_blob]) { 5578 // The Item.id of the entry being rendered. 5579 state.registry.registry[blobjr.strings.first_blob].offset = state.tmp.offset_characters; 5580 state.tmp.count_offset_characters = false; 5581 } 5582 } 5583 5584 // Provide delimiters on adjacent numeric blobs 5585 for (i=0,ilen=ret.length - 1;i<ilen;i+=1) { 5586 if ("number" === typeof ret[i].num && "number" === typeof ret[i+1].num && !ret[i+1].UGLY_DELIMITER_SUPPRESS_HACK) { 5587 // XXX watch this 5588 ret[i].strings.suffix = ret[i].strings.suffix + (blob_delimiter ? blob_delimiter : ""); 5589 ret[i+1].successor_prefix = ""; 5590 ret[i+1].UGLY_DELIMITER_SUPPRESS_HACK = true; 5591 } 5592 } 5593 5594 var span_split = 0; 5595 for (var i = 0, ilen = ret.length; i < ilen; i += 1) { 5596 if ("string" === typeof ret[i]) { 5597 span_split = (parseInt(i, 10) + 1); 5598 if (i < ret.length - 1 && "object" === typeof ret[i + 1]) { 5599 if (blob_delimiter && !ret[i + 1].UGLY_DELIMITER_SUPPRESS_HACK) { 5600 ret[i] += txt_esc(blob_delimiter); 5601 } 5602 // One bite of the apple 5603 ret[i + 1].UGLY_DELIMITER_SUPPRESS_HACK = true; 5604 } 5605 //span_split = ret.length; 5606 //print("XXX ret: "+ret+" -- "+blob_delimiter); 5607 } 5608 } 5609/* 5610 if (blob && (blob.decorations.length || blob.strings.suffix || blob.strings.prefix)) { 5611 span_split = ret.length; 5612 } 5613*/ 5614 if (blob && (blob.decorations.length || blob.strings.suffix)) { 5615 span_split = ret.length; 5616 } else if (blob && blob.strings.prefix) { 5617 for (var i=0,ilen=ret.length;i<ilen;i++) { 5618 if ("undefined" !== typeof ret[i].num) { 5619 span_split = i; 5620 if (i === 0) { 5621 ret[i].strings.prefix = blob.strings.prefix + ret[i].strings.prefix; 5622 } 5623 break; 5624 } 5625 } 5626 } 5627 5628 var blobs_start = state.output.renderBlobs(ret.slice(0, span_split), blob_delimiter, false, blob); 5629 if (blobs_start && blob && (blob.decorations.length || blob.strings.suffix || blob.strings.prefix)) { 5630 if (!state.tmp.suppress_decorations) { 5631 for (var i = 0, ilen = blob.decorations.length; i < ilen; i += 1) { 5632 params = blob.decorations[i]; 5633 if (["@cite","@bibliography", "@display", "@showid"].indexOf(params[0]) > -1) { 5634 continue; 5635 } 5636 if (state.normalDecorIsOrphan(blobjr, params)) { 5637 continue; 5638 } 5639 if (!params[0]) continue; 5640 if ("string" === typeof blobs_start) { 5641 blobs_start = state.fun.decorate[params[0]][params[1]].call(blob, state, blobs_start, params[2]); 5642 } 5643 } 5644 } 5645 // 5646 // XXXX: cut-and-paste warning. same as a code block above. 5647 // 5648 b = blobs_start; 5649 use_suffix = blob.strings.suffix; 5650 if (b && b.length) { 5651 use_prefix = blob.strings.prefix; 5652 b = txt_esc(use_prefix) + b + txt_esc(use_suffix); 5653 if (state.tmp.count_offset_characters) { 5654 state.tmp.offset_characters += (use_prefix.length + use_suffix.length); 5655 } 5656 } 5657 blobs_start = b; 5658 if (!state.tmp.suppress_decorations) { 5659 for (var i = 0, ilen = blob.decorations.length; i < ilen; i += 1) { 5660 params = blob.decorations[i]; 5661 if (["@cite","@bibliography", "@display", "@showid"].indexOf(params[0]) === -1) { 5662 continue; 5663 } 5664 if ("string" === typeof blobs_start) { 5665 blobs_start = state.fun.decorate[params[0]][params[1]].call(blob, state, blobs_start, params[2]); 5666 } 5667 } 5668 } 5669 } 5670 5671 var blobs_end = ret.slice(span_split, ret.length); 5672 if (!blobs_end.length && blobs_start) { 5673 ret = [blobs_start]; 5674 } else if (blobs_end.length && !blobs_start) { 5675 ret = blobs_end; 5676 } else if (blobs_start && blobs_end.length) { 5677 ret = [blobs_start].concat(blobs_end); 5678 } 5679 // 5680 // Blobs is now definitely a string with 5681 // trailing blobs. Return it. 5682 if ("undefined" === typeof blob) { 5683 this.queue = []; 5684 this.current.mystack = []; 5685 this.current.mystack.push(this.queue); 5686 if (state.tmp.suppress_decorations) { 5687 ret = state.output.renderBlobs(ret, undefined, false); 5688 } 5689 } else if ("boolean" === typeof blob) { 5690 ret = state.output.renderBlobs(ret, undefined, true); 5691 } 5692 5693 if (blob && blob.new_locale) { 5694 state.opt.lang = blob.old_locale; 5695 } 5696 //if (!blob && !state.tmp.just_looking) { 5697 // print("QUEUE ("+ state.tmp.just_looking +"): "+JSON.stringify(state.output.queue, ["num", "strings", "decorations", "blobs", "prefix", "suffix", "delimiter"], 2)); 5698 //} 5699 return ret; 5700}; 5701 5702CSL.Output.Queue.prototype.clearlevel = function () { 5703 var blob, pos, len; 5704 blob = this.current.value(); 5705 len = blob.blobs.length; 5706 for (pos = 0; pos < len; pos += 1) { 5707 blob.blobs.pop(); 5708 } 5709}; 5710 5711CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite, parent) { 5712 var state, ret, ret_last_char, use_delim, blob, pos, len, ppos, llen, str, params, txt_esc; 5713 txt_esc = CSL.getSafeEscape(this.state); 5714 if (!delim) { 5715 delim = ""; 5716 } 5717 state = this.state; 5718 ret = ""; 5719 ret_last_char = []; 5720 use_delim = ""; 5721 len = blobs.length; 5722 if (this.state.tmp.area === "citation" && !this.state.tmp.just_looking && len === 1 && typeof blobs[0] === "object" && parent) { 5723 blobs[0].strings.prefix = parent.strings.prefix + blobs[0].strings.prefix; 5724 blobs[0].strings.suffix = blobs[0].strings.suffix + parent.strings.suffix; 5725 blobs[0].decorations = blobs[0].decorations.concat(parent.decorations); 5726 blobs[0].params = parent.params; 5727 return blobs[0]; 5728 } 5729 var start = true; 5730 for (pos = 0; pos < len; pos += 1) { 5731 if (blobs[pos].checkNext) { 5732 blobs[pos].checkNext(blobs[pos + 1],start); 5733 start = false; 5734 } else if (blobs[pos+1] && blobs[pos+1].splice_prefix) { 5735 start = false; 5736 //blobs[pos+1].checkNext(blobs[pos + 1],start); 5737 } else { 5738 start = true; 5739 } 5740 } 5741 5742 // print("LEN="+len+" "+JSON.stringify(blobs, null, 2)); 5743 // Fix last non-range join 5744 var doit = true; 5745 for (pos = blobs.length - 1; pos > 0; pos += -1) { 5746 if (blobs[pos].checkLast) { 5747 if (doit && blobs[pos].checkLast(blobs[pos - 1])) { 5748 doit = false; 5749 } 5750 } else { 5751 doit = true; 5752 } 5753 } 5754 len = blobs.length; 5755 for (pos = 0; pos < len; pos += 1) { 5756 blob = blobs[pos]; 5757 if (ret) { 5758 use_delim = delim; 5759 } 5760 if ("string" === typeof blob) { 5761 ret += txt_esc(use_delim); 5762 // XXX Blob should be run through flipflop and flattened here. 5763 // (I think it must be a fragment of text around a numeric 5764 // variable) 5765 ret += blob; 5766 if (state.tmp.count_offset_characters) { 5767 //state.tmp.offset_characters += (use_delim.length + blob.length); 5768 state.tmp.offset_characters += (use_delim.length); 5769 } 5770 } else if (in_cite) { 5771 // pass 5772 // Okay, so this does it -- but we're now not able to return a string! 5773 if (ret) { 5774 ret = [ret, blob]; 5775 } else { 5776 ret = [blob]; 5777 } 5778 } else if (blob.status !== CSL.SUPPRESS) { 5779 if (blob.particle) { 5780 str = blob.particle + blob.num; 5781 } else { 5782 str = blob.formatter.format(blob.num, blob.gender); 5783 } 5784 // Workaround to get a more or less accurate value. 5785 var strlen = str.replace(/<[^>]*>/g, "").length; 5786 // notSerious 5787 this.append(str, "empty", true); 5788 var str_blob = this.pop(); 5789 var count_offset_characters = state.tmp.count_offset_characters; 5790 str = this.string(state, [str_blob], false); 5791 state.tmp.count_offset_characters = count_offset_characters; 5792 if (blob.strings["text-case"]) { 5793 str = CSL.Output.Formatters[blob.strings["text-case"]](this.state, str); 5794 } 5795 if (str && this.state.tmp.strip_periods) { 5796 str = str.replace(/\.([^a-z]|$)/g, "$1"); 5797 } 5798 if (!state.tmp.suppress_decorations) { 5799 llen = blob.decorations.length; 5800 for (ppos = 0; ppos < llen; ppos += 1) { 5801 params = blob.decorations[ppos]; 5802 if (state.normalDecorIsOrphan(blob, params)) { 5803 continue; 5804 } 5805 str = state.fun.decorate[params[0]][params[1]].call(blob, state, str, params[2]); 5806 } 5807 } 5808 str = txt_esc(blob.strings.prefix) + str + txt_esc(blob.strings.suffix); 5809 var addme = ""; 5810 if (blob.status === CSL.END) { 5811 //print(" CSL.END"); 5812 addme = txt_esc(blob.range_prefix); 5813 } else if (blob.status === CSL.SUCCESSOR) { 5814 //print(" CSL.SUCCESSOR"); 5815 addme = txt_esc(blob.successor_prefix); 5816 } else if (blob.status === CSL.START) { 5817 //print(" CSL.START"); 5818 if (pos > 0 && !blob.suppress_splice_prefix) { 5819 addme = txt_esc(blob.splice_prefix); 5820 } else { 5821 addme = ""; 5822 } 5823 } else if (blob.status === CSL.SEEN) { 5824 //print(" CSL.SEEN"); 5825 5826 // THIS IS NOT THE PROPER FUNCTION OF CSL.SEEN, IS IT? 5827 5828 addme = txt_esc(blob.splice_prefix); 5829 } 5830 ret += addme; 5831 ret += str; 5832 if (state.tmp.count_offset_characters) { 5833 state.tmp.offset_characters += (addme.length + blob.strings.prefix.length + strlen + blob.strings.suffix.length); 5834 } 5835 } 5836 } 5837 return ret; 5838}; 5839 5840CSL.Output.Queue.purgeEmptyBlobs = function (parent) { 5841 //print("START1"); 5842 if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) { 5843 return; 5844 } 5845 // back-to-front, bottom-first 5846 for (var i=parent.blobs.length-1;i>-1;i--) { 5847 CSL.Output.Queue.purgeEmptyBlobs(parent.blobs[i]); 5848 var child = parent.blobs[i]; 5849 if (!child || !child.blobs || !child.blobs.length) { 5850 var buf = []; 5851 while ((parent.blobs.length-1) > i) { 5852 buf.push(parent.blobs.pop()); 5853 } 5854 parent.blobs.pop(); 5855 while (buf.length) { 5856 parent.blobs.push(buf.pop()); 5857 } 5858 } 5859 } 5860 //print(" end"); 5861}; 5862 5863// Adjustments to be made: 5864// 5865// * Never migrate beyond a @quotes node 5866// * Never migrate into a num node. 5867 5868CSL.Output.Queue.adjust = function (punctInQuote) { 5869 5870 var NO_SWAP_IN = { 5871 ";": true, 5872 ":": true 5873 }; 5874 5875 var NO_SWAP_OUT = { 5876 ".": true, 5877 "!": true, 5878 "?": true 5879 }; 5880 5881 var LtoR_MAP = { 5882 "!": { 5883 ".": "!", 5884 "?": "!?", 5885 ":": "!", 5886 ",": "!,", 5887 ";": "!;" 5888 }, 5889 "?": { 5890 "!": "?!", 5891 ".": "?", 5892 ":": "?", 5893 ",": "?,", 5894 ";": "?;" 5895 }, 5896 ".": { 5897 "!": ".!", 5898 "?": ".?", 5899 ":": ".:", 5900 ",": ".,", 5901 ";": ".;" 5902 }, 5903 ":": { 5904 "!": "!", 5905 "?": "?", 5906 ".": ":", 5907 ",": ":,", 5908 ";": ":;" 5909 }, 5910 ",": { 5911 "!": ",!", 5912 "?": ",?", 5913 ":": ",:", 5914 ".": ",.", 5915 ";": ",;" 5916 }, 5917 ";": { 5918 "!": "!", 5919 "?": "?", 5920 ":": ";", 5921 ",": ";,", 5922 ".": ";" 5923 } 5924 }; 5925 5926 var SWAP_IN = {}; 5927 var SWAP_OUT = {}; 5928 var PUNCT = {}; 5929 var PUNCT_OR_SPACE = {}; 5930 for (var key in LtoR_MAP) { 5931 PUNCT[key] = true; 5932 PUNCT_OR_SPACE[key] = true; 5933 if (!NO_SWAP_IN[key]) { 5934 SWAP_IN[key] = true; 5935 } 5936 if (!NO_SWAP_OUT[key]) { 5937 SWAP_OUT[key] = true; 5938 } 5939 } 5940 PUNCT_OR_SPACE[" "] = true; 5941 PUNCT_OR_SPACE[" "] = true; 5942 5943 var RtoL_MAP = {}; 5944 for (var key in LtoR_MAP) { 5945 for (var subkey in LtoR_MAP[key]) { 5946 if (!RtoL_MAP[subkey]) { 5947 RtoL_MAP[subkey] = {}; 5948 } 5949 RtoL_MAP[subkey][key] = LtoR_MAP[key][subkey]; 5950 } 5951 } 5952 5953 function blobIsNumber(blob) { 5954 return ("number" === typeof blob.num || (blob.blobs && blob.blobs.length === 1 && "number" === typeof blob.blobs[0].num)); 5955 } 5956 5957 function blobEndsInNumber(blob) { 5958 if ("number" === typeof blob.num) { 5959 return true; 5960 } 5961 if (!blob.blobs || "object" !== typeof blob.blobs) { 5962 return false; 5963 } 5964 if (blobEndsInNumber(blob.blobs[blob.blobs.length-1])) { 5965 return true; 5966 } 5967 } 5968 5969 function blobHasDecorations(blob,includeQuotes) { 5970 var ret = false; 5971 var decorlist = ['@font-style','@font-variant','@font-weight','@text-decoration','@vertical-align']; 5972 if (includeQuotes) { 5973 decorlist.push('@quotes'); 5974 } 5975 if (blob.decorations) { 5976 for (var i=0,ilen=blob.decorations.length;i<ilen;i++) { 5977 if (decorlist.indexOf(blob.decorations[i][0]) > -1) { 5978 ret = true; 5979 break; 5980 } 5981 } 5982 } 5983 return ret; 5984 } 5985 5986 function blobHasDescendantQuotes(blob) { 5987 if (blob.decorations) { 5988 for (var i=0,ilen=blob.decorations.length;i<ilen;i++) { 5989 if (blob.decorations[i][0] === '@quotes' && blob.decorations[i][1] !== "false") { 5990 return true; 5991 } 5992 } 5993 } 5994 if ("object" !== typeof blob.blobs) { 5995 return false; 5996 } 5997 return blobHasDescendantQuotes(blob.blobs[blob.blobs.length-1]); 5998 //if (blobHasDescendantQuotes(blob.blobs[blob.blobs.length-1])) { 5999 // return true 6000 //}; 6001 //return false; 6002 } 6003 6004 function blobHasDescendantMergingPunctuation(parentChar,blob) { 6005 var childChar = blob.strings.suffix.slice(-1); 6006 if (!childChar && "string" === typeof blob.blobs) { 6007 childChar = blob.blobs.slice(-1); 6008 } 6009 var mergedChars = RtoL_MAP[parentChar][childChar]; 6010 if (mergedChars && mergedChars.length === 1) { 6011 return true; 6012 } 6013 if ("object" !== typeof blob.blobs) { 6014 return false; 6015 } 6016 if (blobHasDescendantMergingPunctuation(parentChar,blob.blobs[blob.blobs.length-1])) { 6017 return true; 6018 } 6019 return false; 6020 } 6021 6022 function matchLastChar(blob, chr) { 6023 if (!PUNCT[chr]) { 6024 return false; 6025 } 6026 if ("string" === typeof blob.blobs) { 6027 6028 if (blob.blobs.slice(-1) === chr) { 6029 return true; 6030 } else { 6031 return false; 6032 } 6033 } else { 6034 var child = blob.blobs[blob.blobs.length-1]; 6035 if (child) { 6036 var childChar = child.strings.suffix.slice(-1); 6037 if (!childChar) { 6038 return matchLastChar(child,chr); 6039 } else if (child.strings.suffix.slice(-1) == chr) { 6040 return true; 6041 } else { 6042 return false; 6043 } 6044 } else { 6045 return false; 6046 } 6047 } 6048 } 6049 6050 function mergeChars (First, first, Second, second, merge_right) { 6051 var FirstStrings = "blobs" === first ? First : First.strings; 6052 var SecondStrings = "blobs" === second ? Second: Second.strings; 6053 var firstChar = FirstStrings[first].slice(-1); 6054 var secondChar = SecondStrings[second].slice(0,1); 6055 function cullRight () { 6056 SecondStrings[second] = SecondStrings[second].slice(1); 6057 } 6058 function cullLeft () { 6059 FirstStrings[first] = FirstStrings[first].slice(0,-1); 6060 } 6061 function addRight (chr) { 6062 SecondStrings[second] = chr + SecondStrings[second]; 6063 } 6064 function addLeft (chr) { 6065 FirstStrings[first] += chr; 6066 } 6067 var cull = merge_right ? cullLeft : cullRight; 6068 function matchOnRight () { 6069 return RtoL_MAP[secondChar]; 6070 } 6071 function matchOnLeft () { 6072 return LtoR_MAP[firstChar]; 6073 } 6074 var match = merge_right ? matchOnLeft : matchOnRight; 6075 function mergeToRight () { 6076 var chr = LtoR_MAP[firstChar][secondChar]; 6077 if ("string" === typeof chr) { 6078 cullLeft(); 6079 cullRight(); 6080 addRight(chr); 6081 } else { 6082 addRight(firstChar); 6083 cullLeft(); 6084 } 6085 } 6086 function mergeToLeft () { 6087 var chr = RtoL_MAP[secondChar][firstChar]; 6088 if ("string" === typeof chr) { 6089 cullLeft(); 6090 cullRight(); 6091 addLeft(chr); 6092 } else { 6093 addLeft(secondChar); 6094 cullRight(); 6095 } 6096 } 6097 var merge = merge_right ? mergeToRight: mergeToLeft; 6098 6099 var isDuplicate = firstChar === secondChar; 6100 if (isDuplicate) { 6101 cull(); 6102 } else { 6103 if (match()) { 6104 merge(); 6105 } 6106 } 6107 } 6108 6109 function upward (parent) { 6110 //print("START2"); 6111 // Terminus if no blobs 6112 if (parent.blobs && "string" == typeof parent.blobs) { 6113 if (PUNCT[parent.strings.suffix.slice(0,1)] 6114 && parent.strings.suffix.slice(0,1) === parent.blobs.slice(-1)) { 6115 6116 parent.strings.suffix = parent.strings.suffix.slice(1); 6117 } 6118 return; 6119 } else if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) { 6120 return; 6121 } 6122 6123 // back-to-front, bottom-first 6124 var parentDecorations = blobHasDecorations(parent,true); 6125 for (var i=parent.blobs.length-1;i>-1;i--) { 6126 this.upward(parent.blobs[i]); 6127 var parentStrings = parent.strings; 6128 var childStrings = parent.blobs[i].strings; 6129 if (i === 0) { 6130 // Remove leading space on first-position child node prefix if there is a trailing space on the node prefix above 6131 if (" " === parentStrings.prefix.slice(-1) && " " === childStrings.prefix.slice(0, 1)) { 6132 childStrings.prefix = childStrings.prefix.slice(1); 6133 } 6134 // Migrate leading punctuation or space on a first-position prefix upward 6135 var childChar = childStrings.prefix.slice(0, 1); 6136 if (!parentDecorations && PUNCT_OR_SPACE[childChar] && !parentStrings.prefix) { 6137 parentStrings.prefix += childChar; 6138 childStrings.prefix = childStrings.prefix.slice(1); 6139 } 6140 } 6141 if (i === (parent.blobs.length - 1)) { 6142 // Migrate trailing space ONLY on a last-position suffix upward, controlling for duplicates 6143 var childChar = childStrings.suffix.slice(-1); 6144 // ZZZ Loosened to fix initialized names wrapped in a span and followed by a period 6145 if (!parentDecorations && [" "].indexOf(childChar) > -1) { 6146 if (parentStrings.suffix.slice(0,1) !== childChar) { 6147 parentStrings.suffix = childChar + parentStrings.suffix; 6148 } 6149 childStrings.suffix = childStrings.suffix.slice(0, -1); 6150 } 6151 } 6152 if (parentStrings.delimiter && i > 0) { 6153 // Remove leading space on mid-position child node prefix if there is a trailing space on delimiter above 6154 if (PUNCT_OR_SPACE[parentStrings.delimiter.slice(-1)] 6155 && parentStrings.delimiter.slice(-1) === childStrings.prefix.slice(0, 1)) { 6156 6157 childStrings.prefix = childStrings.prefix.slice(1); 6158 } 6159 } 6160 // Siblings are handled in adjustNearsideSuffixes() 6161 } 6162 //print(" end"); 6163 } 6164 6165 function leftward (parent) { 6166 // Terminus if no blobs 6167 if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) { 6168 return; 6169 } 6170 6171 for (var i=parent.blobs.length-1;i>-1;i--) { 6172 this.leftward(parent.blobs[i]); 6173 // This is a delicate one. 6174 // 6175 // Migrate if: 6176 // * there is no umbrella delimiter [ok] 6177 // * neither the child nor its sibling is a number [ok] 6178 // * decorations exist neither on the child nor on the sibling [ok] 6179 // * sibling prefix char is a swapping char [ok] 6180 // 6181 // Suppress without migration if: 6182 // * sibling prefix char matches child suffix char or 6183 // * child suffix is empty and sibling prefix char match last field char 6184 if ((i < parent.blobs.length -1) && !parent.strings.delimiter) { 6185 // If there is a trailing swappable character on a sibling prefix with no intervening delimiter, copy it to suffix, 6186 // controlling for duplicates 6187 var child = parent.blobs[i]; 6188 var childChar = child.strings.suffix.slice(-1); 6189 var sibling = parent.blobs[i+1]; 6190 var siblingChar = sibling.strings.prefix.slice(0, 1); 6191 var hasDecorations = blobHasDecorations(child) || blobHasDecorations(sibling); 6192 var hasNumber = "number" === typeof childChar || "number" === typeof siblingChar; 6193 6194 if (!hasDecorations && !hasNumber && PUNCT[siblingChar] && !hasNumber) { 6195 var suffixAndPrefixMatch = siblingChar === child.strings.suffix.slice(-1); 6196 var suffixAndFieldMatch = (!child.strings.suffix && "string" === typeof child.blobs && child.blobs.slice(-1) === siblingChar); 6197 if (!suffixAndPrefixMatch && !suffixAndFieldMatch) { 6198 mergeChars(child, 'suffix', sibling, 'prefix'); 6199 //child.strings.suffix += siblingChar; 6200 } else { 6201 sibling.strings.prefix = sibling.strings.prefix.slice(1); 6202 } 6203 } 6204 } 6205 } 6206 } 6207 6208 function downward (parent) { 6209 //print("START3"); 6210 // Terminus if no blobs 6211 if (parent.blobs && "string" == typeof parent.blobs) { 6212 if (PUNCT[parent.strings.suffix.slice(0,1)] 6213 && parent.strings.suffix.slice(0,1) === parent.blobs.slice(-1)) { 6214 6215 parent.strings.suffix = parent.strings.suffix.slice(1); 6216 } 6217 return; 6218 } else if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) { 6219 return; 6220 } 6221 //if (top) { 6222 // print("JSON "+JSON.stringify(parent, ["strings", "decorations", "blobs", "prefix", "suffix", "delimiter"], 2)); 6223 //} 6224 6225 var parentStrings = parent.strings; 6226 // Check for numeric child 6227 var someChildrenAreNumbers = false; 6228 for (var i=0,ilen=parent.blobs.length;i<ilen;i++) { 6229 if (blobIsNumber(parent.blobs[i])) { 6230 someChildrenAreNumbers = true; 6231 break; 6232 } 6233 } 6234 if (true) { 6235 // If there is a leading swappable character on delimiter, copy it to suffixes IFF none of the targets are numbers 6236 if (parentStrings.delimiter && PUNCT[parentStrings.delimiter.slice(0, 1)]) { 6237 var delimChar = parentStrings.delimiter.slice(0, 1); 6238 for (var i=parent.blobs.length-2;i>-1;i--) { 6239 var childStrings = parent.blobs[i].strings; 6240 if (childStrings.suffix.slice(-1) !== delimChar) { 6241 childStrings.suffix += delimChar; 6242 } 6243 } 6244 parentStrings.delimiter = parentStrings.delimiter.slice(1); 6245 } 6246 } 6247 // back-to-front, top-first 6248 for (var i=parent.blobs.length-1;i>-1;i--) { 6249 var child = parent.blobs[i]; 6250 var childStrings = parent.blobs[i].strings; 6251 var childDecorations = blobHasDecorations(child, true); 6252 var childIsNumber = blobIsNumber(child); 6253 6254 if (i === (parent.blobs.length - 1)) { 6255 6256 //if (blobHasDescendantQuotes(child)) { 6257 // print("JSON "+JSON.stringify(parent, ["strings", "decorations", "blobs", "prefix", "suffix", "delimiter"])); 6258 //} 6259 6260 if (true) { 6261 // If we have decorations, drill down to see if there are quotes below. 6262 // If so, we allow migration anyway. 6263 // Original discussion is here: 6264 // https://forums.zotero.org/discussion/37091/citeproc-bug-punctuation-in-quotes/ 6265 var parentChar = parentStrings.suffix.slice(0, 1); 6266 6267 // Hmm. 6268 // Consider writing out the matching child from blobHasDescendant functions. 6269 // It should save some cycles, and produce the same result. 6270 6271 var allowMigration = false; 6272 if (PUNCT[parentChar]) { 6273 allowMigration = blobHasDescendantMergingPunctuation(parentChar,child); 6274 if (!allowMigration && punctInQuote) { 6275 allowMigration = blobHasDescendantQuotes(child); 6276 } 6277 } 6278 if (allowMigration) { 6279 if (PUNCT[parentChar]) { 6280 if (!blobEndsInNumber(child)) { 6281 if ("string" === typeof child.blobs) { 6282 mergeChars(child, 'blobs', parent, 'suffix'); 6283 } else { 6284 mergeChars(child, 'suffix', parent, 'suffix'); 6285 } 6286 if (parentStrings.suffix.slice(0,1) === ".") { 6287 childStrings.suffix += parentStrings.suffix.slice(0,1); 6288 parentStrings.suffix = parentStrings.suffix.slice(1); 6289 } 6290 } 6291 } 6292 } 6293 if (childStrings.suffix.slice(-1) === " " && parentStrings.suffix.slice(0,1) === " ") { 6294 parentStrings.suffix = parentStrings.suffix.slice(1); 6295 } 6296 // More duplicates control 6297 if (PUNCT_OR_SPACE[childStrings.suffix.slice(0,1)]) { 6298 if ("string" === typeof child.blobs && child.blobs.slice(-1) === childStrings.suffix.slice(0,1)) { 6299 // Remove parent punctuation of it duplicates the last character of a field 6300 childStrings.suffix = childStrings.suffix.slice(1); 6301 } 6302 if (childStrings.suffix.slice(-1) === parentStrings.suffix.slice(0, 1)) { 6303 // Remove duplicate punctuation on child suffix 6304 parentStrings.suffix = parentStrings.suffix.slice(0, -1); 6305 } 6306 } 6307 } 6308 // Squash dupes 6309 if (matchLastChar(parent,parent.strings.suffix.slice(0,1))) { 6310 parent.strings.suffix = parent.strings.suffix.slice(1); 6311 } 6312 } else if (parentStrings.delimiter) { 6313 // Remove trailing space on mid-position child node suffix if there is a leading space on delimiter above 6314 if (PUNCT_OR_SPACE[parentStrings.delimiter.slice(0,1)] 6315 && parentStrings.delimiter.slice(0, 1) === childStrings.suffix.slice(-1)) { 6316 6317 parent.blobs[i].strings.suffix = parent.blobs[i].strings.suffix.slice(0, -1); 6318 6319 } 6320 } else { 6321 // Otherwise it's a sibling. We don't care about moving spaces here, just suppress a duplicate 6322 var siblingStrings = parent.blobs[i+1].strings; 6323 if (!blobIsNumber(child) 6324 && !childDecorations 6325 && PUNCT_OR_SPACE[childStrings.suffix.slice(-1)] 6326 && childStrings.suffix.slice(-1) === siblingStrings.prefix.slice(0, 1)) { 6327 6328 siblingStrings.prefix = siblingStrings.prefix.slice(1); 6329 } 6330 } 6331 // If field content ends with swappable punctuation, suppress swappable punctuation in style suffix. 6332 if (!childIsNumber && !childDecorations && PUNCT[childStrings.suffix.slice(0,1)] 6333 && "string" === typeof child.blobs) { 6334 6335 mergeChars(child, 'blobs', child, 'suffix'); 6336 } 6337 this.downward(parent.blobs[i]); 6338 } 6339/* 6340 if (top) { 6341 6342 var seen = []; 6343 print(JSON.stringify(parent, function(key, val) { 6344 if (!val || key === 'alldecor') return; 6345 if (typeof val == "object") { 6346 if (seen.indexOf(val) >= 0) 6347 return 6348 seen.push(val) 6349 } 6350 return val 6351 },2)); 6352 } 6353*/ 6354 6355 //print(" end"); 6356 } 6357 // Abstract out a couple of utility functions, used in fix() below. 6358 function swapToTheLeft (child) { 6359 var childChar = child.strings.suffix.slice(0,1); 6360 if ("string" === typeof child.blobs) { 6361 while (SWAP_IN[childChar]) { 6362 mergeChars(child, 'blobs', child, 'suffix'); 6363 childChar = child.strings.suffix.slice(0,1); 6364 } 6365 } else { 6366 while (SWAP_IN[childChar]) { 6367 mergeChars(child.blobs[child.blobs.length-1], 'suffix', child, 'suffix'); 6368 childChar = child.strings.suffix.slice(0,1); 6369 } 6370 } 6371 } 6372 function swapToTheRight (child) { 6373 if ("string" === typeof child.blobs) { 6374 var childChar = child.blobs.slice(-1); 6375 while (SWAP_OUT[childChar]) { 6376 mergeChars(child, 'blobs', child, 'suffix', true); 6377 childChar = child.blobs.slice(-1); 6378 } 6379 } else { 6380 var childChar = child.blobs[child.blobs.length-1].strings.suffix.slice(-1); 6381 while (SWAP_OUT[childChar]) { 6382 mergeChars(child.blobs[child.blobs.length-1], 'suffix', child, 'suffix', true); 6383 childChar = child.blobs[child.blobs.length-1].strings.suffix.slice(-1); 6384 } 6385 } 6386 } 6387 6388 function fix (parent) { 6389 // Terminus if no blobs 6390 if ("object" !== typeof parent || "object" !== typeof parent.blobs || !parent.blobs.length) { 6391 return; 6392 } 6393 6394 //print("START4"); 6395 // Do the swap, front-to-back, bottom-first 6396 var lastChar; 6397 6398 // XXX Two things to fix with this: 6399 // XXX (1) Stalls after one character 6400 // XXX (2) Moves colon and semicolon, both of which SHOULD stall 6401 6402 for (var i=0,ilen=parent.blobs.length;i<ilen;i++) { 6403 var child = parent.blobs[i]; 6404 var quoteSwap = false; 6405 for (var j=0,jlen=child.decorations.length;j<jlen;j++) { 6406 var decoration = child.decorations[j]; 6407 if (decoration[0] === "@quotes" && decoration[1] !== "false") { 6408 quoteSwap = true; 6409 } 6410 } 6411 if (quoteSwap) { 6412 if (punctInQuote) { 6413 swapToTheLeft(child); 6414 } else { 6415 swapToTheRight(child); 6416 } 6417 } 6418 lastChar = this.fix(parent.blobs[i]); 6419 if (child.blobs && "string" === typeof child.blobs) { 6420 lastChar = child.blobs.slice(-1); 6421 } 6422 } 6423 return lastChar; 6424 } 6425 this.upward = upward; 6426 this.leftward = leftward; 6427 this.downward = downward; 6428 this.fix = fix; 6429}; 6430 6431/*global CSL: true */ 6432 6433CSL.Engine.Opt = function () { 6434 this.parallel = { 6435 enable: false, 6436 }, 6437 this.has_disambiguate = false; 6438 this.mode = "html"; 6439 this.dates = {}; 6440 this.jurisdictions_seen = {}; 6441 this.suppressedJurisdictions = {}; 6442 this.inheritedAttributes = {}; 6443 this["locale-sort"] = []; 6444 this["locale-translit"] = []; 6445 this["locale-translat"] = []; 6446 this.citeAffixes = { 6447 persons:{ 6448 "locale-orig":{ 6449 prefix:"", 6450 suffix:"" 6451 }, 6452 "locale-translit":{ 6453 prefix:"", 6454 suffix:"" 6455 }, 6456 "locale-translat":{ 6457 prefix:"", 6458 suffix:"" 6459 } 6460 }, 6461 institutions:{ 6462 "locale-orig":{ 6463 prefix:"", 6464 suffix:"" 6465 }, 6466 "locale-translit":{ 6467 prefix:"", 6468 suffix:"" 6469 }, 6470 "locale-translat":{ 6471 prefix:"", 6472 suffix:"" 6473 } 6474 }, 6475 titles:{ 6476 "locale-orig":{ 6477 prefix:"", 6478 suffix:"" 6479 }, 6480 "locale-translit":{ 6481 prefix:"", 6482 suffix:"" 6483 }, 6484 "locale-translat":{ 6485 prefix:"", 6486 suffix:"" 6487 } 6488 }, 6489 journals:{ 6490 "locale-orig":{ 6491 prefix:"", 6492 suffix:"" 6493 }, 6494 "locale-translit":{ 6495 prefix:"", 6496 suffix:"" 6497 }, 6498 "locale-translat":{ 6499 prefix:"", 6500 suffix:"" 6501 } 6502 }, 6503 publishers:{ 6504 "locale-orig":{ 6505 prefix:"", 6506 suffix:"" 6507 }, 6508 "locale-translit":{ 6509 prefix:"", 6510 suffix:"" 6511 }, 6512 "locale-translat":{ 6513 prefix:"", 6514 suffix:"" 6515 } 6516 }, 6517 places:{ 6518 "locale-orig":{ 6519 prefix:"", 6520 suffix:"" 6521 }, 6522 "locale-translit":{ 6523 prefix:"", 6524 suffix:"" 6525 }, 6526 "locale-translat":{ 6527 prefix:"", 6528 suffix:"" 6529 } 6530 } 6531 }; 6532 this["default-locale"] = []; 6533 this.update_mode = CSL.NONE; 6534 this.bib_mode = CSL.NONE; 6535 this.sort_citations = false; 6536 /* 6537 * Default values. 6538 * The various et-al values are set globally, 6539 * and the appropriate value is set by the names start 6540 * tag at runtime, depending on whether the Item is a 6541 * first or a subsequent reference. 6542 */ 6543 this["et-al-min"] = 0; 6544 this["et-al-use-first"] = 1; 6545 this["et-al-use-last"] = false; 6546 this["et-al-subsequent-min"] = false; 6547 this["et-al-subsequent-use-first"] = false; 6548 6549 this["demote-non-dropping-particle"] = "display-and-sort"; 6550 // default of true, because none of our consuming 6551 // applications so far store the various prefixes and 6552 // suffixes we support in separate fields. 6553 this["parse-names"] = true; 6554 // this["auto-vietnamese-names"] = true; 6555 6556 this.citation_number_slug = false; 6557 this.trigraph = "Aaaa00:AaAa00:AaAA00:AAAA00"; 6558 6559 this.nodenames = []; 6560 6561 this.gender = {}; 6562 this['cite-lang-prefs'] = { 6563 persons:['orig'], 6564 institutions:['orig'], 6565 titles:['orig'], 6566 journals:['orig'], 6567 publishers:['orig'], 6568 places:['orig'], 6569 number:['orig'] 6570 }; 6571 6572 this.has_layout_locale = false;
6573 this.disable_duplicate_year_suppression = []; 6574 this.use_context_condition = false; 6575 6576 this.jurisdiction_fallbacks = {}; 6577 6578 this.development_extensions = {}; 6579 this.development_extensions.field_hack = true; 6580 this.development_extensions.allow_field_hack_date_override = true; 6581 this.development_extensions.locator_date_and_revision = true; 6582 this.development_extensions.locator_label_parse = true; 6583 this.development_extensions.raw_date_parsing = true; 6584 this.development_extensions.clean_up_csl_flaws = true; 6585 this.development_extensions.consolidate_legal_items = false; 6586 this.development_extensions.csl_reverse_lookup_support = false; 6587 this.development_extensions.wrap_url_and_doi = false; 6588 this.development_extensions.thin_non_breaking_space_html_hack = false; 6589 this.development_extensions.apply_citation_wrapper = false; 6590 this.development_extensions.main_title_from_short_title = false; 6591 this.development_extensions.uppercase_subtitles = false; 6592 this.development_extensions.normalize_lang_keys_to_lowercase = false; 6593 this.development_extensions.strict_text_case_locales = false; 6594 this.development_extensions.expect_and_symbol_form = false; 6595 this.development_extensions.require_explicit_legal_case_title_short = false; 6596 this.development_extensions.spoof_institutional_affiliations = false; 6597 this.development_extensions.force_jurisdiction = false; 6598 this.development_extensions.parse_names = true; 6599 this.development_extensions.hanging_indent_legacy_number = false; 6600 this.development_extensions.throw_on_empty = false; 6601 this.development_extensions.strict_inputs = true; 6602 this.development_extensions.prioritize_disambiguate_condition = false; 6603 this.development_extensions.force_short_title_casing_alignment = true; 6604 this.development_extensions.implicit_short_title = false; 6605 this.development_extensions.force_title_abbrev_fallback = false; 6606 this.development_extensions.split_container_title = false; 6607 this.development_extensions.legacy_institution_name_ordering = false; 6608 this.development_extensions.etal_min_etal_usefirst_hack = false; 6609}; 6610 6611CSL.Engine.Tmp = function () { 6612 // 6613 // scratch variable to display the total 6614 // number of names in all rendered variables 6615 // in a cite. initialized to zero by the 6616 // citation element, incremented by each 6617 // name variable actually rendered 6618 this.names_max = new CSL.Stack(); 6619 this.names_base = new CSL.Stack(); 6620 this.givens_base = new CSL.Stack(); 6621 // 6622 // this holds the field values collected by the @value 6623 // and @variable attributes, for processing by the 6624 // element functions. 6625 this.value = []; 6626 /** 6627 * Object to hold the decorations declared by a name-part 6628 * element. 6629 */ 6630 this.namepart_decorations = {}; 6631 /** 6632 * String variable to hold the type of a name-part 6633 * element. 6634 */ 6635 this.namepart_type = false;
vendor: 463,898 bytes, lines 6636-18015
6636 // 6637 // scratch variable to flag whether we are processing 6638 // a citation or a bibiliography. this diverts token and 6639 // configuration to the appropriateo objects inside 6640 // state. the default is "citation". 6641 this.area = "citation"; 6642 this.root = "citation"; 6643 this.extension = ""; 6644 // 6645 // controls the implicit conditional wrappers applied 6646 // to top-level elements inside a names substitute span. 6647 // false by default, names start tag pushes a new true level, 6648 // names end tag pops it. Output value check in @variable 6649 // function of attributes.js sets level to false. closing names 6650 // tag maps a false value to superior level. 6651 this.can_substitute = new CSL.Stack(0, CSL.LITERAL); 6652 // 6653 // notes whether the formatted elements of a date span 6654 // rendered anything. controls whether literal fallback 6655 // is used. 6656 this.element_rendered_ok = false; 6657 // 6658 // element_trace keeps a record of rendered elements. 6659 // used to implement author-only. 6660 // 6661 this.element_trace = new CSL.Stack("style"); 6662 // 6663 // counter for total namesets 6664 this.nameset_counter = 0; 6665 // 6666 ///// this.fun.check_for_output = CSL.check_for_output; 6667 // 6668 // stack flag used for term handling. Set to true 6669 // if at least one variable has tried to render, and 6670 // no variables had content. 6671 this.group_context = new CSL.Stack({ 6672 term_intended: false, 6673 variable_attempt: false, 6674 variable_success: false, 6675 output_tip: undefined, 6676 label_form: undefined, 6677 parallel_first: undefined, 6678 parallel_last: undefined, 6679 parallel_delimiter_override: undefined, 6680 condition: false, 6681 force_suppress: false, 6682 done_vars: [] 6683 }); 6684 // 6685 // boolean flag used to control first-letter capitalization 6686 // of terms. Set to true if any item preceding the term 6687 // being handled has rendered successfully, otherwise 6688 // false. 6689 this.term_predecessor = false; 6690 // 6691 // boolean flag to control use of layout delimiter 6692 // immediately before numbers. This hack is needed for 6693 // some numeric styles. 6694 this.in_cite_predecessor = false; 6695 // 6696 // stack flag used to control jumps in the closing 6697 // token of a conditional. 6698 this.jump = new CSL.Stack(0, CSL.LITERAL); 6699 // 6700 // holds string parameters for group formatting, between 6701 // the start of a group and the closing token. 6702 this.decorations = new CSL.Stack(); 6703 // 6704 // token store stack. 6705 this.tokenstore_stack = new CSL.Stack(); 6706 6707 // for collapsing 6708 this.last_suffix_used = ""; 6709 this.last_names_used = []; 6710 this.last_years_used = []; 6711 this.years_used = []; 6712 this.names_used = []; 6713 6714 this.taintedItemIDs = {}; 6715 this.taintedCitationIDs = {}; 6716 // 6717 // scratch stack containing initialize-with strings or null values 6718 this.initialize_with = new CSL.Stack(); 6719 // 6720 // this is used to set a requested set of 6721 // disambiguation parameters in the output. 6722 // for the array elements, the base array 6723 // (either zero for each nameset, or full-up 6724 // if givens are already used) is set 6725 // during names processing, if no value 6726 // is set in the processor before a rendering 6727 // run. to simplify things for the calling 6728 // function, these are just bog-standard arrays, 6729 // and can be safely overwritten. 6730 this.disambig_request = false; 6731 // 6732 // scratch variable to toggle an attempt to set a 6733 // name in sort order rather than display 6734 // order. 6735 this["name-as-sort-order"] = false; 6736 // 6737 // suppress decorations (used for generating 6738 // sort keys and disambiguation keys) 6739 this.suppress_decorations = false; 6740 // 6741 // empty settings array, used to report settings used 6742 // if disambig_request is not set at runtime 6743 this.disambig_settings = new CSL.AmbigConfig(); 6744 // 6745 // sort key array 6746 this.bib_sort_keys = []; 6747 // 6748 // holds the prefix between the start of a group 6749 // and the closing token. 6750 this.prefix = new CSL.Stack("", CSL.LITERAL); 6751 // 6752 // holds the suffix between the start of a group 6753 // and the closing token. 6754 this.suffix = new CSL.Stack("", CSL.LITERAL); 6755 // 6756 // holds the group delimiter between the start of a group 6757 // and the closing token. 6758 this.delimiter = new CSL.Stack("", CSL.LITERAL); 6759 // 6760 // Used for conditional locale switching. 6761 this.cite_locales = []; 6762 this.cite_affixes = { 6763 citation: false, 6764 bibliography: false, 6765 citation_sort: false, 6766 bibliography_sort: false 6767 }; 6768 this.strip_periods = 0; 6769 this.shadow_numbers = {}; 6770 this.authority_stop_last = 0; 6771 this.loadedItemIDs = {}; 6772 // 6773 // Push/pop array for set/unset of opt.lang setting, used 6774 // in if locale="XX" to force terms to language of item. 6775 // @locale tests track their nesting level in a counter, 6776 // and push the current value of state.opt.lang to one array, 6777 // and the counter value to another. On the way back up, 6778 // closing node decrements the counter, compares its value 6779 // with the trailing value on the array, and pops both 6780 // arrays, resetting state.opt.lang to the previous value. 6781 // A hack to solve a surprisingly difficult problem caused 6782 // by the use of an execution stack for the nested structure. 6783 this.condition_counter = 0; //incremented/decremented on ALL conditions 6784 this.condition_lang_val_arr = []; 6785 this.condition_lang_counter_arr = []; 6786}; 6787 6788 6789CSL.Engine.Fun = function (state) { 6790 // 6791 // matcher 6792 this.match = new CSL.Util.Match(); 6793 // 6794 // utility to get standard suffixes for disambiguation 6795 this.suffixator = new CSL.Util.Suffixator(CSL.SUFFIX_CHARS); 6796 // 6797 // utility to romanize a numeric value 6798 this.romanizer = new CSL.Util.Romanizer(); 6799 // 6800 // utility to make an ordinal form of a number 6801 this.ordinalizer = new CSL.Util.Ordinalizer(state); 6802 // 6803 // utility to make the long ordinal form of a number, if possible 6804 this.long_ordinalizer = new CSL.Util.LongOrdinalizer(); 6805}; 6806 6807 6808CSL.Engine.Build = function () { 6809 // Alternate et-al term 6810 // Holds the localization key of the alternative term 6811 // to be used for et-al in a names environment. Reduced 6812 // to a term object when the element tag is processed during 6813 // Build. 6814 this["alternate-term"] = false; 6815 // 6816 // flags that we are in the bibliography area. 6817 // used by sort. 6818 this.in_bibliography = false; 6819 // 6820 // scratch variable to alter behaviour when processing 6821 // locale files 6822 this.in_style = false; 6823 // 6824 // used to ignore info 6825 this.skip = false; 6826 // 6827 // the macro ATTRIBUTE stores a macro name on this 6828 // scratch variable anywhere outside the layout area 6829 // during build. The macro name is picked up when 6830 // the token is encountered inside the layout area, 6831 // either through a direct call, or as part of a nested 6832 // macro expansion, and the macro content is exploded 6833 // into the token list. 6834 this.postponed_macro = false; 6835 // 6836 // used especially for controlling macro expansion 6837 // during Build. 6838 this.layout_flag = false; 6839 // 6840 // (was buffer_name) 6841 // scratch variable to hold the name of a macro 6842 // or a term until its children have been collected. 6843 this.name = false; 6844 this.names_variables = [[]]; 6845 this.name_label = [{}]; 6846 // 6847 // scratch variable to hold the value of a form 6848 // attribute until other attributes needed for 6849 // processing have been collected. 6850 this.form = false; 6851 this.term = false; 6852 // 6853 // the macros themselves are discarded after Build 6854 this.macro = {}; 6855 // 6856 // the macro build stack. used to raise an error 6857 // when macros would attempt to call themselves. 6858 this.macro_stack = []; 6859 // 6860 // stores the content of an XML text node during processing 6861 this.text = false; 6862 // 6863 // this is a scratch variable for holding an attribute 6864 // value during processing 6865 this.lang = false; 6866 // 6867 // should be able to run uninitialized; may attract some 6868 // cruft this way. 6869 this.area = "citation"; 6870 this.root = "citation"; 6871 this.extension = ""; 6872 // 6873 // controls the application of implicit conditional wrappers 6874 // to top-level elements inside a names substitute span. 6875 // zero by default, build of names tag pushes a 6876 // new level with value 1. group start tag increments by 1, 6877 // group end tag decrements by 1. conditional wrappers are 6878 // only applied if value is exactly 1. 6879 this.substitute_level = new CSL.Stack(0, CSL.LITERAL); 6880 this.names_level = 0; 6881 this.render_nesting_level = 0; 6882 this.render_seen = false; 6883 this.bibliography_key_pos = 0; 6884}; 6885 6886 6887CSL.Engine.Configure = function () { 6888 // 6889 // the fail and succeed arrays are used for stack 6890 // processing during configure. 6891 this.tests = []; 6892 this.fail = []; 6893 this.succeed = []; 6894}; 6895 6896 6897CSL.Engine.Citation = function (state) { 6898 // Citation options area. 6899 // Holds a mixture of persistent and ephemeral 6900 // options and scratch data used during processing of 6901 // a citation.</p> 6902 this.opt = { 6903 inheritedAttributes: {} 6904 }; 6905 6906 this.tokens = []; 6907 // Placeholder function 6908 this.srt = new CSL.Registry.Comparifier(state, "citation_sort"); 6909 // 6910 // configuration array to hold the collapse 6911 // options, if any. 6912 this.opt.collapse = []; 6913 // 6914 // disambiguate options 6915 this.opt["disambiguate-add-names"] = false; 6916 this.opt["disambiguate-add-givenname"] = false; 6917 this.opt["disambiguate-add-year-suffix"] = false; 6918 this.opt["givenname-disambiguation-rule"] = "by-cite"; 6919 this.opt["near-note-distance"] = 5; 6920 6921 this.opt.topdecor = []; 6922 this.opt.layout_decorations = []; 6923 this.opt.layout_prefix = ""; 6924 this.opt.layout_suffix = ""; 6925 this.opt.layout_delimiter = ""; 6926 // 6927 // sorting 6928 this.opt.sort_locales = []; 6929 this.opt.max_number_of_names = 0; 6930 this.root = "citation"; 6931}; 6932 6933 6934CSL.Engine.Bibliography = function () { 6935 this.opt = { 6936 inheritedAttributes: {} 6937 }; 6938 this.tokens = []; 6939 6940 this.opt.collapse = []; 6941 6942 this.opt.topdecor = []; 6943 this.opt.layout_decorations = []; 6944 this.opt.layout_prefix = ""; 6945 this.opt.layout_suffix = ""; 6946 this.opt.layout_delimiter = ""; 6947 this.opt["line-spacing"] = 1; 6948 this.opt["entry-spacing"] = 1; 6949 // 6950 // sorting 6951 this.opt.sort_locales = []; 6952 this.opt.max_number_of_names = 0; 6953 this.root = "bibliography"; 6954}; 6955 6956 6957CSL.Engine.BibliographySort = function () { 6958 this.tokens = []; 6959 this.opt = {}; 6960 this.opt.sort_directions = []; 6961 this.opt.topdecor = []; 6962 // Holds the final citation-number sort direction, for use 6963 // in applying numbers in cs:citation and cs:bibliography. 6964 // Value is exclusively controlled by cs:key in bibliography_sort 6965 this.opt.citation_number_sort_direction = CSL.ASCENDING; 6966 this.opt.citation_number_secondary = false; 6967 this.tmp = {}; 6968 this.keys = []; 6969 this.root = "bibliography"; 6970}; 6971 6972 6973CSL.Engine.CitationSort = function () { 6974 this.tokens = []; 6975 this.opt = {}; 6976 this.opt.sort_directions = []; 6977 this.keys = []; 6978 this.opt.topdecor = []; 6979 this.root = "citation"; 6980}; 6981 6982CSL.Engine.InText = function () { 6983 // InText options area. 6984 // Holds a mixture of persistent and ephemeral 6985 // options and scratch data used during processing of 6986 // a citation.</p> 6987 this.opt = { 6988 inheritedAttributes: {} 6989 }; 6990 6991 this.tokens = []; 6992 // Placeholder function 6993 //this.srt = new CSL.Registry.Comparifier(state, "citation_sort"); 6994 // 6995 // configuration array to hold the collapse 6996 // options, if any. 6997 this.opt.collapse = []; 6998 // 6999 // disambiguate options 7000 this.opt["disambiguate-add-names"] = false; 7001 this.opt["disambiguate-add-givenname"] = false; 7002 this.opt["disambiguate-add-year-suffix"] = false; 7003 this.opt["givenname-disambiguation-rule"] = "by-cite"; 7004 this.opt["near-note-distance"] = 5; 7005 7006 this.opt.topdecor = []; 7007 this.opt.layout_decorations = []; 7008 this.opt.layout_prefix = ""; 7009 this.opt.layout_suffix = ""; 7010 this.opt.layout_delimiter = ""; 7011 // 7012 // sorting 7013 this.opt.sort_locales = []; 7014 this.opt.max_number_of_names = 0; 7015 this.root = "intext"; 7016}; 7017 7018/*global CSL: true */ 7019 7020CSL.Engine.prototype.previewCitationCluster = function (citation, citationsPre, citationsPost, newMode) { 7021 // Generate output for a hypothetical citation at the current position, 7022 // Leave the registry in the same state in which it was found. 7023 //print("################### previewCitationCluster() #################"); 7024 var oldMode = this.opt.mode; 7025 this.setOutputFormat(newMode); 7026 // Avoids generating unwanted ibids, if the citationID already exists in document 7027 if (citation.citationID) { 7028 delete citation.citationID; 7029 } 7030 var ret = this.processCitationCluster(citation, citationsPre, citationsPost, CSL.PREVIEW); 7031 7032 this.setOutputFormat(oldMode); 7033 return ret[1]; 7034}; 7035 7036CSL.Engine.prototype.appendCitationCluster = function (citation) { 7037 var citationsPre = []; 7038 var len = this.registry.citationreg.citationByIndex.length; 7039 for (var pos = 0; pos < len; pos += 1) { 7040 var c = this.registry.citationreg.citationByIndex[pos]; 7041 citationsPre.push(["" + c.citationID, c.properties.noteIndex]); 7042 } 7043 // Drop the data segment to return a list of pos/string pairs. 7044 return this.processCitationCluster(citation, citationsPre, [])[1]; 7045}; 7046 7047 7048CSL.Engine.prototype.processCitationCluster = function (citation, citationsPre, citationsPost, flag) { 7049 var c, preCitation, postCitation, i, ilen, j, jlen, k, klen, n, nlen, key, Item, item, noteCitations, textCitations, m, citationsInNote; 7050 this.debug = false; 7051 this.tmp.loadedItemIDs = {}; 7052 7053 // Revert citation dereference from 2ffc4664ae 7054 //citation = JSON.parse(JSON.stringify(citation)); 7055 7056 //print("################### processCitationCluster() #################"); 7057 this.tmp.citation_errors = []; 7058 this.registry.return_data = {"bibchange": false}; 7059 7060 // make sure this citation has a unique ID, and register it in citationById. 7061 this.setCitationId(citation); 7062 7063 var oldCitationList; 7064 var oldItemList; 7065 var oldAmbigs; 7066 if (flag === CSL.PREVIEW) { 7067 //SNIP-START 7068 if (this.debug) { 7069 CSL.debug("****** start state save *********"); 7070 } 7071 //SNIP-END 7072 // 7073 // Simplify. 7074 7075 // Take a slice of existing citations. 7076 oldCitationList = this.registry.citationreg.citationByIndex.slice(); 7077 7078 // Take a slice of current items, for later use with update. 7079 oldItemList = this.registry.reflist.slice(); 7080 7081 // Make a list of preview citation ref objects. Omit the current 7082 // citation, because it will not exist in registry if: (a) this is 7083 // a new citation; or (b) the calling application is assigning 7084 // new citationIDs for every transaction. 7085 var newCitationList = citationsPre.concat(citationsPost); 7086 7087 // Make a full list of desired ids, for use in preview update, 7088 // and a hash list of same while we're at it. 7089 // First step through known citations, then step through 7090 // the items in the citation for preview. 7091 var newItemIds = {}; 7092 var newItemIdsList = []; 7093 for (var i = 0, ilen = newCitationList.length; i < ilen; i += 1) { 7094 c = this.registry.citationreg.citationById[newCitationList[i][0]]; 7095 for (j = 0, jlen = c.citationItems.length; j < jlen; j += 1) { 7096 newItemIds[c.citationItems[j].id] = true; 7097 newItemIdsList.push("" + c.citationItems[j].id); 7098 } 7099 } 7100 for (j = 0, jlen = citation.citationItems.length; j < jlen; j += 1) { 7101 newItemIds[citation.citationItems[j].id] = true; 7102 newItemIdsList.push("" + citation.citationItems[j].id); 7103 } 7104 7105 // Clone and save off disambigs of items that will be lost. 7106 oldAmbigs = {}; 7107 for (var i = 0, ilen = oldItemList.length; i < ilen; i += 1) { 7108 if (!newItemIds[oldItemList[i].id]) { 7109 var oldAkey = this.registry.registry[oldItemList[i].id].ambig; 7110 var ids = this.registry.ambigcites[oldAkey]; 7111 if (ids) { 7112 for (j = 0, jlen = ids.length; j < jlen; j += 1) { 7113 oldAmbigs[ids[j]] = CSL.cloneAmbigConfig(this.registry.registry[ids[j]].disambig); 7114 } 7115 } 7116 } 7117 } 7118 7119 // Update items. This will produce the base name data and sort things. 7120 // Possibly unnecessary? 7121 //this.updateItems(this.registry.mylist.concat(tmpItems)); 7122 7123 //SNIP-START 7124 if (this.debug) { 7125 CSL.debug("****** end state save *********"); 7126 } 7127 //SNIP-END 7128 } 7129 7130 this.tmp.taintedCitationIDs = {}; 7131 var sortedItems = []; 7132 7133 // Styles that use note backreferencing with a by-cite 7134 // givenname disambiguation rule include the note number 7135 // in the cite for disambiguation purposes. Correct resolution 7136 // of disambiguate="true" conditions on first-reference cites 7137 // in certain editing scenarios (e.g. where a cite is moved across 7138 // notes) requires that disambiguation be rerun on cites 7139 // affected by the edit. 7140 var rerunAkeys = {}; 7141 7142 // retrieve item data and compose items for use in rendering 7143 // attach pointer to item data to shared copy for good measure 7144 for (var i = 0, ilen = citation.citationItems.length; i < ilen; i += 1) { 7145 // Protect against caller-side overwrites to locator strings etc 7146 item = {}; 7147 for (var key in citation.citationItems[i]) { 7148 item[key] = citation.citationItems[i][key]; 7149 } 7150 Item = this.retrieveItem("" + item.id); 7151 if (Item.id) { 7152 this.transform.loadAbbreviation("default", "hereinafter", Item.id, Item.language); 7153 } 7154 item = CSL.parseLocator.call(this, item); 7155 if (this.opt.development_extensions.consolidate_legal_items) { 7156 this.remapSectionVariable([[Item,item]]); 7157 } 7158 if (this.opt.development_extensions.locator_label_parse) { 7159 if (item.locator && ["bill","gazette","legislation","regulation","treaty"].indexOf(Item.type) === -1 && (!item.label || item.label === 'page')) { 7160 var m = CSL.LOCATOR_LABELS_REGEXP.exec(item.locator); 7161 if (m) { 7162 var tryLabel = CSL.LOCATOR_LABELS_MAP[m[2]]; 7163 if (this.getTerm(tryLabel)) { 7164 item.label = tryLabel; 7165 item.locator = m[3]; 7166 } 7167 } 7168 } 7169 } 7170 var newitem = [Item, item]; 7171 sortedItems.push(newitem); 7172 citation.citationItems[i].item = Item; 7173 } 7174 7175 // ZZZ sort stuff moved from here. 7176 7177 // attach the sorted list to the citation item 7178 citation.sortedItems = sortedItems; 7179 7180 // build reconstituted citations list in current document order 7181 var citationByIndex = []; 7182 var citationById = {}; 7183 var lastNotePos; 7184 for (i=0, ilen=citationsPre.length; i<ilen; i += 1) { 7185 preCitation = citationsPre[i]; 7186 if (this.opt.development_extensions.strict_inputs) { 7187 if (citationById[preCitation[0]]) { 7188 CSL.error("Previously referenced citationID " + preCitation[0] + " encountered in citationsPre"); 7189 } 7190 if (preCitation[1]) { 7191 if (lastNotePos > preCitation[1]) { 7192 CSL.debug("Note index sequence is not sane at citationsPre[" + i + "]"); 7193 } 7194 lastNotePos = preCitation[1]; 7195 } 7196 } 7197 this.registry.citationreg.citationById[preCitation[0]].properties.noteIndex = preCitation[1]; 7198 citationByIndex.push(this.registry.citationreg.citationById[preCitation[0]]); 7199 citationById[preCitation[0]] = this.registry.citationreg.citationById[preCitation[0]]; 7200 } 7201 if (!citation.properties) { 7202 citation.properties = { 7203 noteIndex: 0 7204 }; 7205 } 7206 if (this.opt.development_extensions.strict_inputs) { 7207 if (citationById[citation.citationID]) { 7208 CSL.error("Citation with previously referenced citationID " + citation.citationID); 7209 } 7210 if (citation.properties.noteIndex) { 7211 if (lastNotePos > citation.properties.noteIndex) { 7212 CSL.debug("Note index sequence is not sane for citation " + citation.citationID); 7213 } 7214 lastNotePos = citation.properties.noteIndex; 7215 } 7216 } 7217 citationByIndex.push(citation); 7218 citationById[citation.citationID] = citation; 7219 for (i=0, ilen=citationsPost.length; i<ilen; i += 1) { 7220 postCitation = citationsPost[i]; 7221 if (this.opt.development_extensions.strict_inputs) { 7222 if (citationById[postCitation[0]]) { 7223 CSL.error("Previously referenced citationID " + postCitation[0] + " encountered in citationsPost"); 7224 } 7225 if (postCitation[1]) { 7226 if (lastNotePos > postCitation[1]) { 7227 CSL.debug("Note index sequence is not sane at postCitation[" + i + "]"); 7228 } 7229 lastNotePos = postCitation[1]; 7230 } 7231 } 7232 this.registry.citationreg.citationById[postCitation[0]].properties.noteIndex = postCitation[1]; 7233 citationByIndex.push(this.registry.citationreg.citationById[postCitation[0]]); 7234 citationById[postCitation[0]] = this.registry.citationreg.citationById[postCitation[0]]; 7235 } 7236 this.registry.citationreg.citationByIndex = citationByIndex; 7237 this.registry.citationreg.citationById = citationById; 7238 7239 // 7240 // The processor provides three facilities to support 7241 // updates following position reevaluation. 7242 // 7243 // (1) The updateItems() function reports tainted ItemIDs 7244 // to state.tmp.taintedItemIDs. 7245 // 7246 // (2) The processor memos the type of style referencing as 7247 // CSL.NONE, CSL.NUMERIC or CSL.POSITION in state.opt.update_mode. 7248 // 7249 // XXXX: NO LONGER 7250 // (3) For citations containing cites with backreference note numbers, 7251 // a string image of the rendered citation is held in 7252 // citation.properties.backref_citation, and a list of 7253 // ItemIDs to be used to update the backreference note numbers 7254 // is memoed at citation.properties.backref_index. When such 7255 // citations change position, they can be updated with a 7256 // series of simple find and replace operations, without 7257 // need for rerendering. 7258 // 7259 7260 // 7261 // Position evaluation! 7262 // 7263 // set positions in reconstituted list, noting taints 7264 this.registry.citationreg.citationsByItemId = {}; 7265 if (this.opt.update_mode === CSL.POSITION) { 7266 textCitations = []; 7267 noteCitations = []; 7268 citationsInNote = {}; 7269 } 7270 var update_items = []; 7271 for (var i = 0, ilen = citationByIndex.length; i < ilen; i += 1) { 7272 citationByIndex[i].properties.index = i; 7273 for (j = 0, jlen = citationByIndex[i].sortedItems.length; j < jlen; j += 1) { 7274 item = citationByIndex[i].sortedItems[j]; 7275 if (!this.registry.citationreg.citationsByItemId[item[1].id]) { 7276 this.registry.citationreg.citationsByItemId[item[1].id] = []; 7277 update_items.push("" + item[1].id); 7278 } 7279 if (this.registry.citationreg.citationsByItemId[item[1].id].indexOf(citationByIndex[i]) === -1) { 7280 this.registry.citationreg.citationsByItemId[item[1].id].push(citationByIndex[i]); 7281 } 7282 } 7283 if (this.opt.update_mode === CSL.POSITION) { 7284 if (citationByIndex[i].properties.noteIndex) { 7285 noteCitations.push(citationByIndex[i]); 7286 } else { 7287 citationByIndex[i].properties.noteIndex = 0; 7288 textCitations.push(citationByIndex[i]); 7289 } 7290 } 7291 } 7292 // 7293 // update bibliography items here 7294 // 7295 if (flag !== CSL.ASSUME_ALL_ITEMS_REGISTERED) { 7296 //SNIP-START 7297 if (this.debug) { 7298 CSL.debug("****** start update items *********"); 7299 } 7300 //SNIP-END 7301 // true signals implicit updateItems (will not rerun sys.retrieveItem()) 7302 this.updateItems(update_items, null, null, true); 7303 //SNIP-START 7304 if (this.debug) { 7305 CSL.debug("****** endo update items *********"); 7306 } 7307 //SNIP-END 7308 } 7309 7310 if (!this.opt.citation_number_sort && sortedItems && sortedItems.length > 1 && this.citation_sort.tokens.length > 0) { 7311 for (var i = 0, ilen = sortedItems.length; i < ilen; i += 1) { 7312 sortedItems[i][1].sortkeys = CSL.getSortKeys.call(this, sortedItems[i][0], "citation_sort"); 7313 } 7314 7315 /* 7316 * Grouped sort stuff (start) 7317 */ 7318 7319 if (this.opt.grouped_sort && !citation.properties.unsorted) { 7320 // Insert authorstring as key. 7321 for (var i = 0, ilen = sortedItems.length; i < ilen; i += 1) { 7322 var sortkeys = sortedItems[i][1].sortkeys; 7323 this.tmp.authorstring_request = true; 7324 // Run getAmbiguousCite() with the current disambig 7325 // parameters, and pick up authorstring from the registry. 7326 var mydisambig = this.registry.registry[sortedItems[i][0].id].disambig; 7327 7328 this.tmp.authorstring_request = true; 7329 CSL.getAmbiguousCite.call(this, sortedItems[i][0], mydisambig); 7330 var authorstring = this.registry.authorstrings[sortedItems[i][0].id]; 7331 this.tmp.authorstring_request = false; 7332 7333 sortedItems[i][1].sortkeys = [authorstring].concat(sortkeys); 7334 } 7335 7336 sortedItems.sort(this.citation.srt.compareCompositeKeys); 7337 // Replace authorstring key in items with same (authorstring) with the 7338 // keystring of first normal key. This forces grouped sorts, 7339 // as discussed here: 7340 // https://github.com/citation-style-language/schema/issues/40 7341 var lastauthor = false; 7342 var thiskey = false; 7343 var thisauthor = false; 7344 for (var i = 0, ilen = sortedItems.length; i < ilen; i += 1) { 7345 if (sortedItems[i][1].sortkeys[0] !== lastauthor) { 7346 thisauthor = sortedItems[i][1].sortkeys[0]; 7347 thiskey = sortedItems[i][1].sortkeys[1]; 7348 } 7349 sortedItems[i][1].sortkeys[0] = "" + thiskey + i; 7350 lastauthor = thisauthor; 7351 } 7352 } 7353 /* 7354 * Grouped sort stuff (end) 7355 */ 7356 7357 if (!citation.properties.unsorted) { 7358 sortedItems.sort(this.citation.srt.compareCompositeKeys); 7359 } 7360 } 7361 7362 // evaluate parallels 7363 7364 if (this.opt.parallel.enable) { 7365 this.parallel.StartCitation(citation.sortedItems); 7366 } 7367 7368 var citations; 7369 if (this.opt.update_mode === CSL.POSITION) { 7370 for (var i = 0; i < 2; i += 1) { 7371 var first_ref = {}; 7372 var last_ref = {}; 7373 var first_container_ref = {}; 7374 citations = [textCitations, noteCitations][i]; 7375 for (j = 0, jlen = citations.length; j < jlen; j += 1) { 7376 var onecitation = citations[j]; 7377 if (!citations[j].properties.noteIndex) { 7378 citations[j].properties.noteIndex = 0; 7379 } 7380 citations[j].properties.noteIndex = parseInt(citations[j].properties.noteIndex, 10); 7381 if (j > 0 && onecitation.properties.noteIndex && citations[j - 1].properties.noteIndex > onecitation.properties.noteIndex) { 7382 citationsInNote = {}; 7383 first_ref = {}; 7384 last_ref = {}; 7385 first_container_ref = {}; 7386 } 7387 for (k = 0, klen = onecitation.sortedItems.length; k < klen; k += 1) { 7388 if (onecitation.sortedItems[k][1].parallel && onecitation.sortedItems[k][1].parallel !== "first") { 7389 continue; 7390 } 7391 if (!citationsInNote[onecitation.properties.noteIndex]) { 7392 citationsInNote[onecitation.properties.noteIndex] = 1; 7393 } else { 7394 citationsInNote[onecitation.properties.noteIndex] += 1; 7395 } 7396 } 7397 // Set the following: 7398 // 7399 // (1) position as required (as per current Zotero) 7400 // (2) first-reference-note-number as required (on onecitation item) 7401 // (3) near-note as required (on onecitation item, according to 7402 // state.opt["near-note-distance"] parameter) 7403 // (4) state.registry.citationreg.citationsByItemId. 7404 // 7405 // Any state changes caused by unsetting or resetting should 7406 // trigger a single entry for the citations in 7407 // state.tmp.taintedCitationIDs (can block on presence of 7408 // state.registry.citationreg.citationsByItemId). 7409 // 7410 for (k = 0, klen = citations[j].sortedItems.length; k < klen; k += 1) { 7411 item = citations[j].sortedItems[k]; 7412 // Okay ... 7413 // We set up three IDs for use in position evaluation. 7414 // item_id is the real Item.id 7415 // first_id is the legislation_id or Item.id (so statutes backref to first in set, chapters to specific chapter) 7416 // last_id is the legislation_id or container_id (so statute AND chapter distance is from any ref in set) 7417 // (replaces myid) 7418 var item_id = item[0].id; 7419 var first_id = item[0].legislation_id ? item[0].legislation_id : item[0].id; 7420 var last_id = item[0].legislation_id ? item[0].legislation_id : item[0].container_id ? item[0].container_id : item[0].id; 7421 var myxloc = item[1]["locator-extra"]; 7422 var mylocator = item[1].locator; 7423 var mylabel = item[1].label; 7424 var incitationid; 7425 var incitationxloc; 7426 if (k > 0) { 7427 // incitationid is only reached in the else branch 7428 // following "undefined" === typeof first_ref[myid] 7429 // below 7430 if (onecitation.sortedItems[k - 1][0].legislation_id) { 7431 incitationid = onecitation.sortedItems[k - 1][0].legislation_id; 7432 } else { 7433 incitationid = onecitation.sortedItems[k - 1][1].id; 7434 incitationxloc = onecitation.sortedItems[k - 1][1]["locator-extra"]; 7435 //if (onecitation.sortedItems[k-1][1].parallel === "last") { 7436 for (var l=k-2; l>-1; l--) { 7437 if (onecitation.sortedItems[l][1].parallel === "first") { 7438 incitationid = onecitation.sortedItems[l][1].id; 7439 incitationxloc = onecitation.sortedItems[l][1]["locator-extra"]; 7440 } 7441 } 7442 //} 7443 } 7444 } 7445 // Don't touch item data of other cites when previewing 7446 if (flag === CSL.PREVIEW) { 7447 if (onecitation.citationID != citation.citationID) { 7448 if ("undefined" === typeof first_ref[item[1].id]) { 7449 first_ref[first_id] = onecitation.properties.noteIndex; 7450 last_ref[last_id] = onecitation.properties.noteIndex; 7451 } else { 7452 last_ref[last_id] = onecitation.properties.noteIndex; 7453 } 7454 continue; 7455 } 7456 } 7457 var oldvalue = {}; 7458 oldvalue.position = item[1].position; 7459 oldvalue["first-reference-note-number"] = item[1]["first-reference-note-number"]; 7460 oldvalue["first-container-reference-note-number"] = item[1]["first-container-reference-note-number"]; 7461 oldvalue["near-note"] = item[1]["near-note"]; 7462 item[1]["first-reference-note-number"] = 0; 7463 item[1]["first-container-reference-note-number"] = 0; 7464 item[1]["near-note"] = false; 7465 if (this.registry.citationreg.citationsByItemId[item_id]) { 7466 if (this.opt.xclass === 'note' && this.opt.has_disambiguate) { 7467 var oldCount = this.registry.registry[item[0].id]["citation-count"]; 7468 var newCount = this.registry.citationreg.citationsByItemId[item_id].length; 7469 this.registry.registry[item[0].id]["citation-count"] = this.registry.citationreg.citationsByItemId[item_id].length; 7470 if ("number" === typeof oldCount) { 7471 var oldCountCheck = (oldCount < 2); 7472 var newCountCheck = (newCount < 2); 7473 if (oldCountCheck !== newCountCheck) { 7474 for (var l=0,llen=this.registry.citationreg.citationsByItemId[item_id].length;l<llen;l++) { 7475 rerunAkeys[this.registry.registry[item[0].id].ambig] = true; 7476 this.tmp.taintedCitationIDs[this.registry.citationreg.citationsByItemId[item_id][l].citationID] = true; 7477 } 7478 } 7479 } else { 7480 for (var l=0,llen=this.registry.citationreg.citationsByItemId[item_id].length;l<llen;l++) { 7481 rerunAkeys[this.registry.registry[item[0].id].ambig] = true; 7482 this.tmp.taintedCitationIDs[this.registry.citationreg.citationsByItemId[item_id][l].citationID] = true; 7483 } 7484 } 7485 } 7486 } 7487 var oldlastid; 7488 var oldlastxloc; 7489 7490 // Okay, chill. 7491 // The first test needs to be for presence of last_ref[last_id]. Everything 7492 // after in subsequent evaluation depends on that. 7493 7494 // HOWEVER, despite starting with this test, we need to catch every member 7495 // of the set, and set its first-container-reference-note-number to point at the 7496 // first. 7497 7498 // ALSO, despite starting with this test, we need to set first-reference-note-number 7499 // on every item. 7500 7501 // So ... we run an independent test on first_ref[first_id]], and let this ride. 7502 7503 if ("undefined" === typeof last_ref[last_id] && onecitation.properties.mode !== "author-only") { 7504 first_ref[first_id] = onecitation.properties.noteIndex; 7505 last_ref[last_id] = onecitation.properties.noteIndex; 7506 first_container_ref[last_id] = onecitation.properties.noteIndex; 7507 item[1].position = CSL.POSITION_FIRST; 7508 } else { 7509 // 7510 // backward-looking position evaluation happens here. 7511 // 7512 // 7513 // 7514 var ibidme = false; 7515 var suprame = false; 7516 var prevCitation = null; 7517 if (j > 0) { 7518 var prevCitation = citations[j-1]; 7519 } 7520 var thisCitation = citations[j]; 7521 // XXX Ugly, but This is used in the second else-if branch condition below. 7522 if (j > 0) { 7523 var old_last_id_offset = 1; 7524 if (prevCitation.properties.mode === "author-only" && j > 1) { 7525 old_last_id_offset = 2; 7526 } 7527 var adjusted_offset = (j - old_last_id_offset); 7528 if (citations[adjusted_offset].sortedItems.length) { 7529 oldlastid = citations[adjusted_offset].sortedItems.slice(-1)[0][1].id; 7530 oldlastxloc = citations[j - old_last_id_offset].sortedItems.slice(-1)[0][1]["locator-extra"]; 7531 } 7532 if (prevCitation.sortedItems.length) { 7533 if (prevCitation.sortedItems[0].slice(-1)[0].legislation_id) { 7534 oldlastid = prevCitation.sortedItems[0].slice(-1)[0].legislation_id; 7535 } 7536 } 7537 } 7538 if (j > 0 && k === 0 && prevCitation.properties.noteIndex !== thisCitation.properties.noteIndex) { 7539 // Case 1: source in previous onecitation 7540 // (1) Threshold conditions 7541 // (a) there must be a previous onecitation with one item 7542 // (b) this item must be the first in this onecitation 7543 // (c) the previous onecitation must contain a reference 7544 // to the same item ... 7545 // (d) the note numbers must be the same or consecutive. 7546 // (this has some jiggery-pokery in it for parallels) 7547 var useme = false; 7548 // XXX Can oldid be equated with oldlastid, I wonder ... 7549 var oldid = prevCitation.sortedItems[0][0].id; 7550 if (prevCitation.sortedItems[0][0].legislation_id) { 7551 oldid = prevCitation.sortedItems[0][0].legislation_id; 7552 } 7553 if ((oldid == first_id && prevCitation.properties.noteIndex >= (thisCitation.properties.noteIndex - 1))) { 7554 var prevxloc = prevCitation.sortedItems[0][1]["locator-extra"]; 7555 var thisxloc = thisCitation.sortedItems[0][1]["locator-extra"]; 7556 if ((citationsInNote[prevCitation.properties.noteIndex] === 1 || prevCitation.properties.noteIndex === 0) && prevxloc === thisxloc) { 7557 useme = true; 7558 } 7559 } 7560 if (useme) { 7561 ibidme = true; 7562 } else { 7563 suprame = true; 7564 } 7565 } else if (k > 0 && incitationid == first_id && incitationxloc == myxloc) { 7566 // Case 2: immediately preceding source in this onecitation 7567 // (1) Threshold conditions 7568 // (a) there must be an imediately preceding reference to the 7569 // same item in this onecitation; and 7570 ibidme = true; 7571 } else if (k === 0 && j > 0 && prevCitation.properties.noteIndex == thisCitation.properties.noteIndex 7572 && prevCitation.sortedItems.length 7573 && oldlastid == first_id && oldlastxloc == myxloc) { 7574 // ... in case there are separate citations in the same note ... 7575 // Case 2 [take 2]: immediately preceding source in this onecitation 7576 // (1) Threshold conditions 7577 // (a) there must be an imediately preceding reference to the 7578 // same item in this onecitation; and 7579 ibidme = true; 7580 } else { 7581 // everything else is definitely subsequent 7582 suprame = true; 7583 } 7584 // conditions 7585 var prev, prev_locator, prev_label, curr_locator, curr_label; 7586 if (ibidme) { 7587 if (k > 0) { 7588 prev = onecitation.sortedItems[(k - 1)][1]; 7589 } else { 7590 prev = citations[(j - 1)].sortedItems[0][1]; 7591 } 7592 if (prev.locator) { 7593 if (prev.label) { 7594 prev_label = prev.label; 7595 } else { 7596 prev_label = ""; 7597 } 7598 prev_locator = "" + prev.locator + prev_label; 7599 } else { 7600 prev_locator = prev.locator; 7601 } 7602 if (mylocator) { 7603 if (mylabel) { 7604 curr_label = mylabel; 7605 } else { 7606 curr_label = ""; 7607 } 7608 curr_locator = "" + mylocator + curr_label; 7609 } else { 7610 curr_locator = mylocator; 7611 } 7612 } 7613 // triage 7614 if (ibidme && prev_locator && !curr_locator) { 7615 ibidme = false; 7616 suprame = true; 7617 7618 } 7619 if (ibidme) { 7620 if (!prev_locator && curr_locator) { 7621 // (a) if the previous onecitation had no locator 7622 // and this onecitation has one, use ibid+pages 7623 item[1].position = CSL.POSITION_IBID_WITH_LOCATOR; 7624 } else if (!prev_locator && !curr_locator) { 7625 // (b) if the previous onecitation had no locator 7626 // and this onecitation also has none, use ibid 7627 item[1].position = CSL.POSITION_IBID; 7628 //print("setting ibid in cmd_cite()"); 7629 } else if (prev_locator && curr_locator === prev_locator) { 7630 // (c) if the previous onecitation had a locator 7631 // (page number, etc.) and this onecitation has 7632 // a locator that is identical, use ibid 7633 7634 item[1].position = CSL.POSITION_IBID; 7635 //print("setting ibid in cmd_cite() [2]"); 7636 } else if (prev_locator && curr_locator && curr_locator !== prev_locator) { 7637 // (d) if the previous onecitation had a locator, 7638 // and this onecitation has one that differs, 7639 // use ibid+pages 7640 item[1].position = CSL.POSITION_IBID_WITH_LOCATOR; 7641 } else { 7642 // (e) if the previous onecitation had a locator 7643 // and this onecitation has none, use subsequent 7644 // 7645 // ... and everything else would be subsequent also 7646 ibidme = false; // just to be clear 7647 suprame = true; 7648 } 7649 } 7650 if (suprame) { 7651 item[1].position = CSL.POSITION_CONTAINER_SUBSEQUENT; 7652 if ("undefined" === typeof first_ref[first_id]) { 7653 first_ref[first_id] = onecitation.properties.noteIndex; 7654 } else { 7655 item[1].position = CSL.POSITION_SUBSEQUENT; 7656 } 7657 } 7658 if (suprame || ibidme) { 7659 if (onecitation.properties.mode === "author-only") { 7660 item[1].position = CSL.POSITION_FIRST; 7661 } 7662 if (first_container_ref[last_id] != onecitation.properties.noteIndex) { 7663 item[1]['first-container-reference-note-number'] = first_container_ref[last_id]; 7664 if (this.registry.registry[item[0].id]) { 7665 this.registry.registry[item[0].id]['first-container-reference-note-number'] = first_container_ref[last_id]; 7666 } 7667 } 7668 if (first_ref[first_id] != onecitation.properties.noteIndex) { 7669 item[1]["first-reference-note-number"] = first_ref[first_id]; 7670 if (this.registry.registry[item[0].id]) { 7671 // This is either the earliest recorded number, or the number of the current citation, whichever is smaller. 7672 /* 7673 var oldFirst = this.registry.citationreg.citationsByItemId[item_id][0].properties.noteIndex; 7674 var newFirst = onecitation.properties.noteIndex; 7675 this.registry.registry[item[0].id]['first-reference-note-number'] = newFirst < oldFirst ? newFirst: oldFirst; 7676 */ 7677 // Try this instead? 7678 this.registry.registry[item[0].id]['first-reference-note-number'] = first_ref[first_id]; 7679 } 7680 } 7681 } 7682 } 7683 if (onecitation.properties.noteIndex) { 7684 var note_distance = parseInt(onecitation.properties.noteIndex, 10) - parseInt(last_ref[last_id], 10); 7685 if (item[1].position !== CSL.POSITION_FIRST 7686 && note_distance <= this.citation.opt["near-note-distance"]) { 7687 item[1]["near-note"] = true; 7688 } 7689 last_ref[last_id] = onecitation.properties.noteIndex; 7690 } else if (item[1].position !== CSL.POSITION_FIRST) { 7691 item[1]["near-note"] = true; 7692 } 7693 if (onecitation.citationID != citation.citationID) { 7694 for (n = 0, nlen = CSL.POSITION_TEST_VARS.length; n < nlen; n += 1) { 7695 var param = CSL.POSITION_TEST_VARS[n]; 7696 if (item[1][param] !== oldvalue[param]) { 7697 if (this.registry.registry[item[0].id]) { 7698 if (param === 'first-reference-note-number') { 7699 rerunAkeys[this.registry.registry[item[0].id].ambig] = true; 7700 this.tmp.taintedItemIDs[item[0].id] = true; 7701 } 7702 } 7703 this.tmp.taintedCitationIDs[onecitation.citationID] = true; 7704 } 7705 } 7706 } 7707 if (this.sys.variableWrapper) { 7708 item[1].index = onecitation.properties.index; 7709 item[1].noteIndex = onecitation.properties.noteIndex; 7710 } 7711 } 7712 } 7713 } 7714 } 7715 if (this.opt.citation_number_sort && sortedItems && sortedItems.length > 1 && this.citation_sort.tokens.length > 0) { 7716 if (!citation.properties.unsorted) { 7717 for (var i = 0, ilen = sortedItems.length; i < ilen; i += 1) { 7718 sortedItems[i][1].sortkeys = CSL.getSortKeys.call(this, sortedItems[i][0], "citation_sort"); 7719 } 7720 sortedItems.sort(this.citation.srt.compareCompositeKeys); 7721 } 7722 } 7723 for (var key in this.tmp.taintedItemIDs) { 7724 if (this.tmp.taintedItemIDs.hasOwnProperty(key)) { 7725 citations = this.registry.citationreg.citationsByItemId[key]; 7726 // Current citation may be tainted but will not exist 7727 // during previewing. 7728 if (citations) { 7729 for (var i = 0, ilen = citations.length; i < ilen; i += 1) { 7730 this.tmp.taintedCitationIDs[citations[i].citationID] = true; 7731 } 7732 } 7733 } 7734 } 7735 7736 var ret = []; 7737 if (flag === CSL.PREVIEW) { 7738 // If previewing, return only a rendered string 7739 //SNIP-START 7740 if (this.debug) { 7741 CSL.debug("****** start run processor *********"); 7742 } 7743 //SNIP-END 7744 try { 7745 ret = this.process_CitationCluster.call(this, citation.sortedItems, citation); 7746 } catch (e) { 7747 CSL.error("Error running CSL processor for preview: "+e); 7748 } 7749 7750 //SNIP-START 7751 if (this.debug) { 7752 CSL.debug("****** end run processor *********"); 7753 CSL.debug("****** start state restore *********"); 7754 } 7755 //SNIP-END 7756 // Wind out anything related to new items added for the preview. 7757 // This means (1) names, (2) disambig state for affected items, 7758 // (3) keys registered in the ambigs pool arrays, and (4) registry 7759 // items. 7760 // 7761 7762 // restore sliced citations 7763 this.registry.citationreg.citationByIndex = oldCitationList; 7764 this.registry.citationreg.citationById = {}; 7765 for (var i = 0, ilen = oldCitationList.length; i < ilen; i += 1) { 7766 this.registry.citationreg.citationById[oldCitationList[i].citationID] = oldCitationList[i]; 7767 } 7768 7769 //SNIP-START 7770 if (this.debug) { 7771 CSL.debug("****** start final update *********"); 7772 } 7773 //SNIP-END 7774 var oldItemIds = []; 7775 for (var i = 0, ilen = oldItemList.length; i < ilen; i += 1) { 7776 oldItemIds.push("" + oldItemList[i].id); 7777 } 7778 this.updateItems(oldItemIds, null, null, true); 7779 //SNIP-START 7780 if (this.debug) { 7781 CSL.debug("****** end final update *********"); 7782 } 7783 //SNIP-END 7784 // Roll back disambig states 7785 for (var key in oldAmbigs) { 7786 if (oldAmbigs.hasOwnProperty(key)) { 7787 this.registry.registry[key].disambig = oldAmbigs[key]; 7788 } 7789 } 7790 //SNIP-START 7791 if (this.debug) { 7792 CSL.debug("****** end state restore *********"); 7793 } 7794 //SNIP-END 7795 } else { 7796 // Rerun cites that have moved across citations or had a change 7797 // in their number of subsequent references, so that disambiguate 7798 // and subsequent-reference-count conditions are applied 7799 // correctly in output. 7800 for (var rerunAkey in rerunAkeys) { 7801 this.disambiguate.run(rerunAkey, citation); 7802 } 7803 // Run taints only if not previewing 7804 // 7805 // Push taints to the return object 7806 // 7807 var obj; 7808 for (var key in this.tmp.taintedCitationIDs) { 7809 if (key == citation.citationID) { 7810 continue; 7811 } 7812 var mycitation = this.registry.citationreg.citationById[key]; 7813 if (!mycitation.properties.unsorted) { 7814 for (var i = 0, ilen = mycitation.sortedItems.length; i < ilen; i += 1) { 7815 mycitation.sortedItems[i][1].sortkeys = CSL.getSortKeys.call(this, mycitation.sortedItems[i][0], "citation_sort"); 7816 } 7817 mycitation.sortedItems.sort(this.citation.srt.compareCompositeKeys); 7818 } 7819 // For error reporting 7820 this.tmp.citation_pos = mycitation.properties.index; 7821 this.tmp.citation_note_index = mycitation.properties.noteIndex; 7822 this.tmp.citation_id = "" + mycitation.citationID; 7823 obj = []; 7824 obj.push(mycitation.properties.index); 7825 obj.push(this.process_CitationCluster.call(this, mycitation.sortedItems, mycitation)); 7826 obj.push(mycitation.citationID); 7827 ret.push(obj); 7828 } 7829 this.tmp.taintedItemIDs = {}; 7830 this.tmp.taintedCitationIDs = {}; 7831 7832 // For error reporting again 7833 this.tmp.citation_pos = citation.properties.index; 7834 this.tmp.citation_note_index = citation.properties.noteIndex; 7835 this.tmp.citation_id = "" + citation.citationID; 7836 7837 obj = []; 7838 obj.push(citationsPre.length); 7839 obj.push(this.process_CitationCluster.call(this, sortedItems, citation)); 7840 obj.push(citation.citationID); 7841 ret.push(obj); 7842 // 7843 // note for posterity: Rhino and Spidermonkey produce different 7844 // sort results for items with matching keys. That discrepancy 7845 // turned up a subtle bug in the parallel detection code, trapped 7846 // at line 266, above, and in line 94 of util_parallel.js. 7847 // 7848 ret.sort(function (a, b) { 7849 if (a[0] > b[0]) { 7850 return 1; 7851 } else if (a[0] < b[0]) { 7852 return -1; 7853 } else { 7854 return 0; 7855 } 7856 }); 7857 // 7858 // In normal rendering, return is a list of two-part arrays, with the first element 7859 // a citation index number, and the second the text to be inserted. 7860 // 7861 } 7862 this.registry.return_data.citation_errors = this.tmp.citation_errors.slice(); 7863 return [this.registry.return_data, ret]; 7864}; 7865 7866CSL.Engine.prototype.process_CitationCluster = function (sortedItems, citation) { 7867 var str = ""; 7868 if (citation && citation.properties && citation.properties.mode === "composite") { 7869 citation.properties.mode = "author-only"; 7870 var firstChunk = CSL.getCitationCluster.call(this, sortedItems, citation); 7871 citation.properties.mode = "suppress-author"; 7872 var secondChunk = ""; 7873 if (citation.properties.infix) { 7874 this.output.append(citation.properties.infix); 7875 secondChunk = this.output.string(this, this.output.queue); 7876 // Had no idea this could return a single-element array! Go figure. 7877 if ("object" === typeof secondChunk) { 7878 secondChunk = secondChunk.join(""); 7879 } 7880 } 7881 var thirdChunk = CSL.getCitationCluster.call(this, sortedItems, citation); 7882 citation.properties.mode = "composite"; 7883 if (firstChunk && secondChunk && CSL.SWAPPING_PUNCTUATION.concat(["\u2019", "\'"]).indexOf(secondChunk[0]) > -1) { 7884 firstChunk += secondChunk; 7885 secondChunk = false; 7886 } 7887 str = [firstChunk, secondChunk, thirdChunk].filter(function(obj) { 7888 return obj; 7889 }).join(" "); 7890 } else { 7891 str = CSL.getCitationCluster.call(this, sortedItems, citation); 7892 } 7893 return str; 7894}; 7895 7896CSL.Engine.prototype.makeCitationCluster = function (rawList) { 7897 var inputList, newitem, str, pos, len, item, Item; 7898 inputList = []; 7899 len = rawList.length; 7900 for (pos = 0; pos < len; pos += 1) { 7901 item = {}; 7902 for (var key in rawList[pos]) { 7903 item[key] = rawList[pos][key]; 7904 } 7905 Item = this.retrieveItem("" + item.id); 7906 // Code block is copied from processCitationCluster() above 7907 if (this.opt.development_extensions.locator_label_parse) { 7908 if (item.locator && ["bill","gazette","legislation","regulation","treaty"].indexOf(Item.type) === -1 && (!item.label || item.label === 'page')) { 7909 var m = CSL.LOCATOR_LABELS_REGEXP.exec(item.locator); 7910 if (m) { 7911 var tryLabel = CSL.LOCATOR_LABELS_MAP[m[2]]; 7912 if (this.getTerm(tryLabel)) { 7913 item.label = tryLabel; 7914 item.locator = m[3]; 7915 } 7916 } 7917 } 7918 } 7919 if (item.locator) { 7920 item.locator = ("" + item.locator).replace(/\s+$/, ''); 7921 } 7922 newitem = [Item, item]; 7923 inputList.push(newitem); 7924 } 7925 if (this.opt.development_extensions.consolidate_legal_items) { 7926 this.remapSectionVariable(inputList); 7927 } 7928 if (inputList && inputList.length > 1 && this.citation_sort.tokens.length > 0) { 7929 len = inputList.length; 7930 for (pos = 0; pos < len; pos += 1) { 7931 inputList[pos][1].sortkeys = CSL.getSortKeys.call(this, inputList[pos][0], "citation_sort"); 7932 } 7933 inputList.sort(this.citation.srt.compareCompositeKeys); 7934 } 7935 this.tmp.citation_errors = []; 7936 var str = CSL.getCitationCluster.call(this, inputList); 7937 return str; 7938}; 7939 7940 7941/** 7942 * Get the undisambiguated version of a cite, without decorations 7943 * <p>This is used internally by the Registry.</p> 7944 * 7945 * [object] CSL Item 7946 * [object] disambiguation parameters 7947 * [boolean] If true, include first-reference-note-number value in cite 7948 */ 7949CSL.getAmbiguousCite = function (Item, disambig, visualForm, item) { 7950 var ret; 7951 var flags = this.tmp.group_context.tip; 7952 var oldTermSiblingLayer = { 7953 term_intended: flags.term_intended, 7954 variable_attempt: flags.variable_attempt, 7955 variable_success: flags.variable_success, 7956 output_tip: flags.output_tip, 7957 label_form: flags.label_form, 7958 non_parallel: flags.non_parallel, 7959 parallel_last: flags.parallel_last, 7960 parallel_first: flags.parallel_first, 7961 parallel_last_override: flags.parallel_last_override, 7962 parallel_delimiter_override: flags.parallel_delimiter_override, 7963 parallel_delimiter_override_on_suppress: flags.parallel_delimiter_override_on_suppress, 7964 condition: flags.condition, 7965 force_suppress: flags.force_suppress, 7966 done_vars: flags.done_vars.slice() 7967 }; 7968 if (disambig) { 7969 this.tmp.disambig_request = disambig; 7970 } else { 7971 this.tmp.disambig_request = false; 7972 } 7973 var itemSupp = { 7974 position: CSL.POSITION_SUBSEQUENT, 7975 "near-note": true 7976 }; 7977 7978 if (item) { 7979 itemSupp.locator = item.locator; 7980 itemSupp.label = item.label; 7981 } 7982 7983 if (this.registry.registry[Item.id] 7984 && this.registry.citationreg.citationsByItemId 7985 && this.registry.citationreg.citationsByItemId[Item.id] 7986 && this.registry.citationreg.citationsByItemId[Item.id].length 7987 && visualForm) { 7988 if (this.citation.opt["givenname-disambiguation-rule"] === "by-cite") { 7989 itemSupp['first-reference-note-number'] = this.registry.registry[Item.id]['first-reference-note-number']; 7990 } 7991 } 7992 this.tmp.area = "citation"; 7993 this.tmp.root = "citation"; 7994 var origSuppressDecorations = this.tmp.suppress_decorations; 7995 this.tmp.suppress_decorations = true; 7996 this.tmp.just_looking = true; 7997 7998 CSL.getCite.call(this, Item, itemSupp, null, false); 7999 // !!! 8000 for (var i=0,ilen=this.output.queue.length;i<ilen;i+=1) { 8001 CSL.Output.Queue.purgeEmptyBlobs(this.output.queue[i]); 8002 } 8003 if (this.opt.development_extensions.clean_up_csl_flaws) { 8004 for (var j=0,jlen=this.output.queue.length;j<jlen;j+=1) { 8005 this.output.adjust.upward(this.output.queue[j]); 8006 this.output.adjust.leftward(this.output.queue[j]); 8007 this.output.adjust.downward(this.output.queue[j]); 8008 this.output.adjust.fix(this.output.queue[j]); 8009 } 8010 } 8011 var ret = this.output.string(this, this.output.queue); 8012 this.tmp.just_looking = false; 8013 this.tmp.suppress_decorations = origSuppressDecorations; 8014 // Cache the result. 8015 this.tmp.group_context.replace(oldTermSiblingLayer); 8016 return ret; 8017}; 8018 8019/** 8020 * Return delimiter for use in join 8021 * <p>Splice evaluation is done during cite 8022 * rendering, and this method returns the 8023 * result. Evaluation requires three items 8024 * of information from the preceding cite, if 8025 * one is present: the names used; the years 8026 * used; and the suffix appended to the 8027 * citation. These details are copied into 8028 * the state object before processing begins, 8029 * and are cleared by the processor on 8030 * completion of the run.</p> 8031 */ 8032 8033CSL.getSpliceDelimiter = function (last_locator, last_collapsed, pos) { 8034 //print(pos + " after-collapse-delimiter="+this.citation.opt["after-collapse-delimiter"] + "\n cite_group_delimiter=" + this.tmp.use_cite_group_delimiter + "\n last_collapsed=" +last_collapsed + "\n have_collapsed=" +this.tmp.have_collapsed + "\n last_locator=" + last_locator) 8035 if (undefined !== this.citation.opt["after-collapse-delimiter"]) { 8036 if (last_locator) { 8037 this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"]; 8038 } else if (last_collapsed && !this.tmp.have_collapsed) { 8039 this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"]; 8040 } else if (!last_collapsed && !this.tmp.have_collapsed && this.citation.opt.collapse !== "year-suffix") { 8041 this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"]; 8042 } else { 8043 this.tmp.splice_delimiter = this.citation.opt.layout_delimiter; 8044 } 8045 } else if (this.tmp.use_cite_group_delimiter) { 8046 this.tmp.splice_delimiter = this.citation.opt.cite_group_delimiter; 8047 } else { 8048 if (this.tmp.have_collapsed && this.opt.xclass === "in-text" && this.opt.update_mode !== CSL.NUMERIC) { 8049 this.tmp.splice_delimiter = ", "; 8050 } else if (this.tmp.cite_locales[pos - 1]) { 8051 // 8052 // Must have a value to take effect. Use zero width space to force empty delimiter. 8053 var alt_affixes = this.tmp.cite_affixes[this.tmp.area][this.tmp.cite_locales[pos - 1]]; 8054 if (alt_affixes && alt_affixes.delimiter) { 8055 this.tmp.splice_delimiter = alt_affixes.delimiter; 8056 } 8057 } else if (!this.tmp.splice_delimiter) { 8058 // This happens when no delimiter is set on cs:layout under cs:citation 8059 this.tmp.splice_delimiter = ""; 8060 } 8061 } 8062 8063/* 8064 if (last_locator && "string" === typeof this.citation.opt["after-collapse-delimiter"]) { 8065 this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"]; 8066 } else if (last_collapsed && !this.tmp.have_collapsed && "string" === typeof this.citation.opt["after-collapse-delimiter"]) { 8067 this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"]; 8068 } else if (!last_collapsed && !this.tmp.have_collapsed && "string" === typeof this.citation.opt["after-collapse-delimiter"] && !this.citation.opt.collapse === "year-suffix") { 8069 this.tmp.splice_delimiter = this.citation.opt["after-collapse-delimiter"]; 8070 } else if (this.tmp.use_cite_group_delimiter) { 8071 this.tmp.splice_delimiter = this.citation.opt.cite_group_delimiter; 8072 } else if (this.tmp.have_collapsed && this.opt.xclass === "in-text" && this.opt.update_mode !== CSL.NUMERIC) { 8073 this.tmp.splice_delimiter = ", "; 8074 } else if (this.tmp.cite_locales[pos - 1]) { 8075 // 8076 // Must have a value to take effect. Use zero width space to force empty delimiter. 8077 var alt_affixes = this.tmp.cite_affixes[this.tmp.area][this.tmp.cite_locales[pos - 1]]; 8078 if (alt_affixes && alt_affixes.delimiter) { 8079 this.tmp.splice_delimiter = alt_affixes.delimiter; 8080 } 8081 } else if (!this.tmp.splice_delimiter) { 8082 // This happens when no delimiter is set on cs:layout under cs:citation 8083 this.tmp.splice_delimiter = ""; 8084 } 8085*/ 8086 // Paranoia 8087 //if (!this.tmp.splice_delimiter) { 8088 // this.tmp.splice_delimiter = ""; 8089 //} 8090 return this.tmp.splice_delimiter; 8091}; 8092 8093/* 8094 * Compose individual cites into a single string, with 8095 * flexible inter-cite splicing. 8096 */ 8097CSL.getCitationCluster = function (inputList, citation) { 8098 var result, objects, myparams, len, pos, item, last_collapsed, params, empties, composite, compie, myblobs, Item, llen, ppos, obj, preceding_item, txt_esc, error_object, citationID, authorOnly, suppressAuthor; 8099 var citation_prefix = ""; 8100 this.output.checkNestedBrace = new CSL.checkNestedBrace(this); 8101 if (citation) { 8102 citationID = citation.citationID; 8103 authorOnly = citation.properties.mode === "author-only" ? !!citation.properties.mode : false; 8104 if (this.opt.xclass !== "note") { 8105 suppressAuthor = citation.properties.mode === "suppress-author" ? !!citation.properties.mode : false; 8106 } 8107 if (citation.properties.prefix) { 8108 citation_prefix = CSL.checkPrefixSpaceAppend(this, citation.properties.prefix); 8109 } 8110 } 8111 inputList = inputList ? inputList : []; 8112 this.tmp.last_primary_names_string = false; 8113 txt_esc = CSL.getSafeEscape(this); 8114 this.tmp.area = "citation"; 8115 this.tmp.root = "citation"; 8116 result = ""; 8117 objects = []; 8118 this.tmp.last_suffix_used = ""; 8119 this.tmp.last_names_used = []; 8120 this.tmp.last_years_used = []; 8121 this.tmp.backref_index = []; 8122 this.tmp.cite_locales = []; 8123 if (!this.tmp.just_looking) { 8124 this.tmp.abbrev_trimmer = { 8125 QUASHES: {} 8126 }; 8127 } 8128 8129 var use_layout_prefix = this.output.checkNestedBrace.update(this.citation.opt.layout_prefix + citation_prefix); 8130 //var use_layout_prefix = this.citation.opt.layout_prefix; 8131 8132 var suppressTrailingPunctuation = false; 8133 if (this.citation.opt.suppressTrailingPunctuation) { 8134 suppressTrailingPunctuation = true; 8135 } 8136 if (citationID) { 8137 //this.registry.citationreg.citationById[citationID].properties.backref_index = false; 8138 //this.registry.citationreg.citationById[citationID].properties.backref_citation = false; 8139 if (this.registry.citationreg.citationById[citationID].properties["suppress-trailing-punctuation"]) { 8140 suppressTrailingPunctuation = true; 8141 } 8142 } 8143 8144 // Adjust locator positions if that looks like a sensible thing to do. 8145 if (this.opt.xclass === "note") { 8146 var parasets = []; 8147 var lastTitle = false; 8148 var lastPosition = false; 8149 var lastID = false; 8150 var lst = []; 8151 for (var i=0, ilen = inputList.length; i < ilen; i += 1) { 8152 var type = inputList[i][0].type; 8153 var title = inputList[i][0].title; 8154 var position = inputList[i][1].position; 8155 var id = inputList[i][0].id; 8156 if (title && type === "legal_case" && id !== lastID && position) { 8157 // Start a fresh sublist if the item title does not match the last one 8158 if (title !== lastTitle || parasets.length === 0) { 8159 lst = []; 8160 parasets.push(lst); 8161 } 8162 lst.push(inputList[i][1]); 8163 } 8164 lastTitle = title; 8165 lastPosition = position; 8166 lastID = id; 8167 } 8168 // We now have a list of sublists, each w/matching titles 8169 for (i=0, ilen=parasets.length; i < ilen; i += 1) { 8170 lst = parasets[i]; 8171 if (lst.length < 2) { 8172 continue; 8173 } 8174 // Get the locator in last position, but only if it's the only one in the set. 8175 var locatorInLastPosition = lst.slice(-1)[0].locator; 8176 if (locatorInLastPosition) { 8177 for (var j=0, jlen=lst.length - 1; j < jlen; j += 1) { 8178 if (lst[j].locator) { 8179 locatorInLastPosition = false; 8180 } 8181 } 8182 } 8183 // move the locator here, if it's called for. 8184 if (locatorInLastPosition) { 8185 lst[0].locator = locatorInLastPosition; 8186 delete lst.slice(-1)[0].locator; 8187 lst[0].label = lst.slice(-1)[0].label; 8188 if (lst.slice(-1)[0].label) { 8189 delete lst.slice(-1)[0].label; 8190 } 8191 } 8192 } 8193 } 8194 myparams = []; 8195 len = inputList.length; 8196 if (inputList[0] && inputList[0][1]) { 8197 if (authorOnly) { 8198 delete inputList[0][1]["suppress-author"]; 8199 inputList[0][1]["author-only"] = true; 8200 } else if (suppressAuthor) { 8201 delete inputList[0][1]["author-only"]; 8202 inputList[0][1]["suppress-author"] = true; 8203 } 8204 } 8205 if (this.opt.parallel.enable) { 8206 this.parallel.StartCitation(inputList); 8207 } 8208 for (pos = 0; pos < len; pos += 1) { 8209 8210 // Also for parallels only 8211 this.tmp.cite_index = pos; 8212 8213 Item = inputList[pos][0]; 8214 item = inputList[pos][1]; 8215 item = CSL.parseLocator.call(this, item); 8216 last_collapsed = this.tmp.have_collapsed; 8217 var last_locator = false; 8218 if (pos > 0 && inputList[pos-1][1]) { 8219 last_locator = !!inputList[pos-1][1].locator; 8220 } 8221 params = {}; 8222 8223 // Reset shadow_numbers here, suppress reset in getCite() 8224 this.tmp.shadow_numbers = {}; 8225 if (!this.tmp.just_looking && this.opt.hasPlaceholderTerm) { 8226 var output = this.output; 8227 this.output = new CSL.Output.Queue(this); 8228 this.output.adjust = new CSL.Output.Queue.adjust(); 8229 CSL.getAmbiguousCite.call(this, Item, null, false, item); 8230 this.output = output; 8231 } 8232 8233 this.tmp.in_cite_predecessor = false; 8234 // true is to block reset of shadow numbers 8235 8236 8237 if (pos > 0) { 8238 CSL.getCite.call(this, Item, item, "" + inputList[(pos - 1)][0].id, true); 8239 } else { 8240 this.tmp.term_predecessor = false; 8241 CSL.getCite.call(this, Item, item, null, true); 8242 } 8243 8244 // Make a note of any errors 8245 if (!this.tmp.cite_renders_content) { 8246 error_object = { 8247 citationID: "" + this.tmp.citation_id, 8248 index: this.tmp.citation_pos, 8249 noteIndex: this.tmp.citation_note_index, 8250 itemID: "" + Item.id, 8251 citationItems_pos: pos, 8252 error_code: CSL.ERROR_NO_RENDERED_FORM 8253 }; 8254 this.tmp.citation_errors.push(error_object); 8255 } 8256 params.splice_delimiter = CSL.getSpliceDelimiter.call(this, last_locator, last_collapsed, pos); 8257 // XXX This appears to be superfluous. 8258 if (item && item["author-only"]) { 8259 this.tmp.suppress_decorations = true; 8260 } 8261 8262 if (pos > 0) { 8263 preceding_item = inputList[pos - 1][1]; 8264 8265 // XXX OR if preceding suffix is empty, and the current prefix begins with a full stop. 8266 8267 var precedingEndsInPeriodOrComma = preceding_item.suffix && [";", ".", ","].indexOf(preceding_item.suffix.slice(-1)) > -1; 8268 var currentStartsWithPeriodOrComma = !preceding_item.suffix && item.prefix && [";", ".", ","].indexOf(item.prefix.slice(0, 1)) > -1; 8269 if (precedingEndsInPeriodOrComma || currentStartsWithPeriodOrComma) { 8270 var spaceidx = params.splice_delimiter.indexOf(" "); 8271 if (spaceidx > -1 && !currentStartsWithPeriodOrComma) { 8272 params.splice_delimiter = params.splice_delimiter.slice(spaceidx); 8273 } else { 8274 params.splice_delimiter = ""; 8275 } 8276 } 8277 } 8278 params.suppress_decorations = this.tmp.suppress_decorations; 8279 params.have_collapsed = this.tmp.have_collapsed; 8280 // 8281 // XXXXX: capture parameters to an array, which 8282 // will be of the same length as this.output.queue, 8283 // corresponding to each element. 8284 // 8285 myparams.push(params); 8286 if (item["author-only"]) { 8287 break; 8288 } 8289 } 8290 // 8291 // output.queue is a simple array. do a slice 8292 // of it to get each cite item, setting params from 8293 // the array that was built in the preceding loop. 8294 // 8295 empties = 0; 8296 myblobs = this.output.queue.slice(); 8297 8298 var citation_suffix = ""; 8299 if (citation) { 8300 citation_suffix = CSL.checkSuffixSpacePrepend(this, citation.properties.suffix); 8301 } 8302 var suffix = this.citation.opt.layout_suffix; 8303 var last_locale = this.tmp.cite_locales[this.tmp.cite_locales.length - 1]; 8304 // 8305 // Must have a value to take effect. Use zero width space to force empty suffix. 8306 if (last_locale && this.tmp.cite_affixes[this.tmp.area][last_locale] && this.tmp.cite_affixes[this.tmp.area][last_locale].suffix) { 8307 suffix = this.tmp.cite_affixes[this.tmp.area][last_locale].suffix; 8308 } 8309 if (CSL.TERMINAL_PUNCTUATION.slice(0, -1).indexOf(suffix.slice(0, 1)) > -1) { 8310 suffix = suffix.slice(0, 1); 8311 } 8312 //print("=== FROM CITE ==="); 8313 suffix = this.output.checkNestedBrace.update(citation_suffix + suffix); 8314 8315 8316 for (var i=0,ilen=this.output.queue.length;i<ilen;i+=1) { 8317 CSL.Output.Queue.purgeEmptyBlobs(this.output.queue[i]); 8318 } 8319 if (!this.tmp.suppress_decorations && this.output.queue.length) { 8320 if (!(this.opt.development_extensions.apply_citation_wrapper 8321 && this.sys.wrapCitationEntry 8322 && !this.tmp.just_looking 8323 && this.tmp.area === "citation")) { 8324 8325 if (!suppressTrailingPunctuation) { 8326 this.output.queue[this.output.queue.length - 1].strings.suffix = suffix; 8327 } 8328 this.output.queue[0].strings.prefix = use_layout_prefix; 8329 } 8330 } 8331 if (this.opt.development_extensions.clean_up_csl_flaws) { 8332 for (var j=0,jlen=this.output.queue.length;j<jlen;j+=1) { 8333 //print("OUTPUT[5]: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2)) 8334 this.output.adjust.upward(this.output.queue[j]); 8335 //print("OUTPUT[4]: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2)) 8336 this.output.adjust.leftward(this.output.queue[j]); 8337 //print("OUTPUT[3]: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2)) 8338 this.output.adjust.downward(this.output.queue[j]); 8339 //print("OUTPUT[2]: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2)) 8340 this.tmp.last_chr = this.output.adjust.fix(this.output.queue[j]); 8341 //print("OUTPUT[1]: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations','num'],2)) 8342 } 8343 } 8344 //print("this.tmp.last_chr="+this.tmp.last_chr); 8345 for (pos = 0, len = myblobs.length; pos < len; pos += 1) { 8346 var buffer = []; 8347 this.output.queue = [myblobs[pos]]; 8348 this.tmp.suppress_decorations = myparams[pos].suppress_decorations; 8349 this.tmp.splice_delimiter = myparams[pos].splice_delimiter; 8350 // 8351 // oh, one last second thought on delimiters ... 8352 // 8353 8354 if (myblobs[pos].parallel_delimiter) { 8355 this.tmp.splice_delimiter = myblobs[pos].parallel_delimiter; 8356 } 8357 this.tmp.have_collapsed = myparams[pos].have_collapsed; 8358 8359 composite = this.output.string(this, this.output.queue); 8360 8361 this.tmp.suppress_decorations = false; 8362 // meaningless assignment 8363 // this.tmp.handle_ranges = false; 8364 if ("string" === typeof composite) { 8365 this.tmp.suppress_decorations = false; 8366 if (!composite) { 8367 if (this.opt.development_extensions.throw_on_empty) { 8368 CSL.error("Citation would render no content"); 8369 } else { 8370 composite = "[NO_PRINTED_FORM]"; 8371 } 8372 } 8373 return composite; 8374 } 8375 if ("object" === typeof composite && composite.length === 0 && !item["suppress-author"]) { 8376 if (pos === 0) { 8377 var errStr = "[CSL STYLE ERROR: reference with no printed form.]"; 8378 var preStr = pos === 0 ? txt_esc(this.citation.opt.layout_prefix) : ""; 8379 var sufStr = pos === (myblobs.length - 1) ? txt_esc(this.citation.opt.layout_suffix) : ""; 8380 composite.push(preStr + errStr + sufStr); 8381 } else if (pos === myblobs.length - 1) { 8382 var tmpobj = objects[objects.length - 1]; 8383 if (typeof tmpobj === "string") { 8384 objects[objects.length -1] += (txt_esc(this.citation.opt.layout_suffix)); 8385 } else if (typeof tmpobj === "object") { 8386 tmpobj.strings.suffix += (txt_esc(this.citation.opt.layout_suffix)); 8387 } 8388 } 8389 } 8390 if (buffer.length && "string" === typeof composite[0]) { 8391 composite.reverse(); 8392 var tmpstr = composite.pop(); 8393 if (tmpstr && tmpstr.slice(0, 1) === ",") { 8394 buffer.push(tmpstr); 8395 } else if ("string" == typeof buffer.slice(-1)[0] && buffer.slice(-1)[0].slice(-1) === ",") { 8396 buffer.push(" " + tmpstr); 8397 } else if (tmpstr) { 8398 buffer.push(txt_esc(this.tmp.splice_delimiter) + tmpstr); 8399 } 8400 } else { 8401 composite.reverse(); 8402 compie = composite.pop(); 8403 if ("undefined" !== typeof compie) { 8404 if (buffer.length && "string" === typeof buffer[buffer.length - 1]) { 8405 buffer[buffer.length - 1] += compie.successor_prefix; 8406 } 8407 buffer.push(compie); 8408 } 8409 } 8410 // Seems odd, but this was unnecessary and broken. 8411 //composite.reverse(); 8412 llen = composite.length; 8413 for (ppos = 0; ppos < llen; ppos += 1) { 8414 obj = composite[ppos]; 8415 if ("string" === typeof obj) { 8416 buffer.push(txt_esc(this.tmp.splice_delimiter) + obj); 8417 continue; 8418 } 8419 compie = composite.pop(); 8420 if ("undefined" !== typeof compie) { 8421 buffer.push(compie); 8422 } 8423 } 8424 if (buffer.length === 0 && !inputList[pos][1]["suppress-author"]) { 8425 empties += 1; 8426 } 8427 if (buffer.length > 1 && typeof buffer[0] !== "string") { 8428 buffer = [this.output.renderBlobs(buffer)]; 8429 } 8430 if (buffer.length) { 8431 if ("string" === typeof buffer[0]) { 8432 if (pos > 0) { 8433 buffer[0] = txt_esc(this.tmp.splice_delimiter) + buffer[0]; 8434 } 8435 } else { 8436 if (pos > 0) { 8437 buffer[0].splice_prefix = this.tmp.splice_delimiter; 8438 } else { 8439 buffer[0].splice_prefix = ""; 8440 } 8441 } 8442 } 8443 objects = objects.concat(buffer); 8444 } 8445 // print("OBJECTS="+objects); 8446 result += this.output.renderBlobs(objects); 8447 8448 if (result) { 8449 //if (CSL.TERMINAL_PUNCTUATION.indexOf(this.tmp.last_chr) > -1 8450 // && this.tmp.last_chr === use_layout_suffix.slice(0, 1)) { 8451 // use_layout_suffix = use_layout_suffix.slice(1); 8452 //} 8453 if (!this.tmp.suppress_decorations) { 8454 len = this.citation.opt.layout_decorations.length; 8455 for (pos = 0; pos < len; pos += 1) { 8456 params = this.citation.opt.layout_decorations[pos]; 8457 // The "normal" formats in some output modes expect 8458 // a superior nested decoration environment, and 8459 // so should produce no output here. 8460 if (params[1] === "normal") { 8461 continue; 8462 } 8463 if (!item || !item["author-only"]) { 8464 result = this.fun.decorate[params[0]][params[1]](this, result); 8465 } 8466 } 8467 } 8468 } 8469 this.tmp.suppress_decorations = false; 8470 if (!result) { 8471 if (this.opt.development_extensions.throw_on_empty) { 8472 CSL.error("Citation would render no content"); 8473 } else { 8474 result = "[NO_PRINTED_FORM]" 8475 } 8476 } 8477 return result; 8478}; 8479 8480/* 8481 * Render a single cite item. 8482 * 8483 * This is called on the state object, with a single 8484 * Item as input. It iterates exactly once over the style 8485 * citation tokens, and leaves the result of rendering in 8486 * the top-level list in the relevant *.opt.output 8487 * stack, as a list item consisting of a single string. 8488 * 8489 * (This is dual-purposed for generating individual 8490 * entries in a bibliography.) 8491 */ 8492CSL.getCite = function (Item, item, prevItemID, blockShadowNumberReset) { 8493 var next, error_object; 8494 var areaOrig = this.tmp.area; 8495 if (item && item["author-only"] && this.intext && this.intext.tokens.length > 0) { 8496 this.tmp.area = "intext"; 8497 } 8498 this.tmp.cite_renders_content = false; 8499 this.tmp.probably_rendered_something = false; 8500 this.tmp.prevItemID = prevItemID; 8501 8502 CSL.citeStart.call(this, Item, item, blockShadowNumberReset); 8503 next = 0; 8504 this.tmp.name_node = {}; 8505 this.nameOutput = new CSL.NameOutput(this, Item, item); 8506 8507 // rerun? 8508 while (next < this[this.tmp.area].tokens.length) { 8509 next = CSL.tokenExec.call(this, this[this.tmp.area].tokens[next], Item, item); 8510 } 8511 8512 CSL.citeEnd.call(this, Item, item); 8513 // Odd place for this, but it seems to fit here 8514 if (!this.tmp.cite_renders_content && !this.tmp.just_looking) { 8515 if (this.tmp.area === "bibliography") { 8516 error_object = { 8517 index: this.tmp.bibliography_pos, 8518 itemID: "" + Item.id, 8519 error_code: CSL.ERROR_NO_RENDERED_FORM 8520 }; 8521 this.tmp.bibliography_errors.push(error_object); 8522 } 8523 } 8524 this.tmp.area = areaOrig; 8525 return "" + Item.id; 8526}; 8527 8528 8529CSL.citeStart = function (Item, item, blockShadowNumberReset) { 8530 this.tmp.lang_array = []; 8531 if (Item.language) { 8532 // Guard against garbage locales in user input 8533 var m = Item.language.match(/^([a-zA-Z]+).*/); 8534 if (m) { 8535 this.tmp.lang_array.push(m[1].toLowerCase()); 8536 } 8537 } 8538 this.tmp.lang_array.push(this.opt.lang); 8539 if (!blockShadowNumberReset) { 8540 this.tmp.shadow_numbers = {}; 8541 } 8542 8543 this.tmp.disambiguate_count = 0; 8544 this.tmp.disambiguate_maxMax = 0; 8545 this.tmp.same_author_as_previous_cite = false; 8546 if (!this.tmp.suppress_decorations) { 8547 this.tmp.subsequent_author_substitute_ok = true; 8548 } else { 8549 this.tmp.subsequent_author_substitute_ok = false; 8550 } 8551 this.tmp.lastchr = ""; 8552 if (this.tmp.area === "citation" && this.citation.opt.collapse && this.citation.opt.collapse.length) { 8553 //this.tmp.have_collapsed = "year"; 8554 this.tmp.have_collapsed = true; 8555 } else { 8556 this.tmp.have_collapsed = false; 8557 } 8558 this.tmp.render_seen = false; 8559 if (this.tmp.disambig_request && ! this.tmp.disambig_override) { 8560 this.tmp.disambig_settings = this.tmp.disambig_request; 8561 } else if (this.registry.registry[Item.id] && ! this.tmp.disambig_override) { 8562 this.tmp.disambig_request = this.registry.registry[Item.id].disambig; 8563 this.tmp.disambig_settings = this.registry.registry[Item.id].disambig; 8564 } else { 8565 this.tmp.disambig_settings = new CSL.AmbigConfig(); 8566 } 8567 if (this.tmp.area !== 'citation') { 8568 if (!this.registry.registry[Item.id]) { 8569 this.tmp.disambig_restore = new CSL.AmbigConfig(); 8570 } else { 8571 this.tmp.disambig_restore = CSL.cloneAmbigConfig(this.registry.registry[Item.id].disambig); 8572 if (this.tmp.area === 'bibliography' && this.tmp.disambig_settings && this.tmp.disambig_override) { 8573 if (this.opt["disambiguate-add-names"]) { 8574 this.tmp.disambig_settings.names = this.registry.registry[Item.id].disambig.names.slice(); 8575 if (this.tmp.disambig_request) { 8576 this.tmp.disambig_request.names = this.registry.registry[Item.id].disambig.names.slice(); 8577 } 8578 } 8579 if (this.opt["disambiguate-add-givenname"]) { 8580 // This is weird and delicate and not fully understood 8581 this.tmp.disambig_request = this.tmp.disambig_settings; 8582 this.tmp.disambig_settings.givens = this.registry.registry[Item.id].disambig.givens.slice(); 8583 this.tmp.disambig_request.givens = this.registry.registry[Item.id].disambig.givens.slice(); 8584 for (var i=0,ilen=this.tmp.disambig_settings.givens.length;i<ilen;i+=1) { 8585 this.tmp.disambig_settings.givens[i] = this.registry.registry[Item.id].disambig.givens[i].slice(); 8586 } 8587 for (var i=0,ilen=this.tmp.disambig_request.givens.length;i<ilen;i+=1) { 8588 this.tmp.disambig_request.givens[i] = this.registry.registry[Item.id].disambig.givens[i].slice(); 8589 } 8590 } 8591 } 8592 } 8593 } 8594 8595 this.tmp.names_used = []; 8596 this.tmp.nameset_counter = 0; 8597 this.tmp.years_used = []; 8598 this.tmp.names_max.clear(); 8599 if (!this.tmp.just_looking) { 8600 if (!item || item.parallel === "first" || !item.parallel) { 8601 this.tmp.abbrev_trimmer = { 8602 QUASHES: {} 8603 }; 8604 } 8605 } 8606 8607 this.tmp.splice_delimiter = this[this.tmp.area].opt.layout_delimiter; 8608 //this.tmp.splice_delimiter = this[this.tmp.area].opt.delimiter; 8609 8610 this.bibliography_sort.keys = []; 8611 this.citation_sort.keys = []; 8612 8613 this.tmp.has_done_year_suffix = false; 8614 this.tmp.last_cite_locale = false; 8615 // SAVE PARAMETERS HERE, IF APPROPRIATE 8616 // (promiscuous addition of global parameters => death by a thousand cuts) 8617 if (!this.tmp.just_looking && item && !item.position && this.registry.registry[Item.id]) { 8618 this.tmp.disambig_restore = CSL.cloneAmbigConfig(this.registry.registry[Item.id].disambig); 8619 } 8620 // XXX This only applied to the "number" variable itself? Huh? 8621 //this.setNumberLabels(Item); 8622 this.tmp.first_name_string = false; 8623 this.tmp.authority_stop_last = 0; 8624}; 8625 8626CSL.citeEnd = function (Item, item) { 8627 // RESTORE PARAMETERS IF APPROPRIATE 8628 if (this.tmp.disambig_restore && this.registry.registry[Item.id]) { 8629 this.registry.registry[Item.id].disambig.names = this.tmp.disambig_restore.names.slice(); 8630 this.registry.registry[Item.id].disambig.givens = this.tmp.disambig_restore.givens.slice(); 8631 for (var i=0,ilen=this.registry.registry[Item.id].disambig.givens.length;i<ilen;i+=1) { 8632 this.registry.registry[Item.id].disambig.givens[i] = this.tmp.disambig_restore.givens[i].slice(); 8633 } 8634 } 8635 this.tmp.disambig_restore = false; 8636 8637 if (item && item.suffix) { 8638 //this.tmp.last_suffix_used = this.tmp.suffix.value(); 8639 this.tmp.last_suffix_used = item.suffix; 8640 } else { 8641 this.tmp.last_suffix_used = ""; 8642 } 8643 this.tmp.last_years_used = this.tmp.years_used.slice(); 8644 this.tmp.last_names_used = this.tmp.names_used.slice(); 8645 this.tmp.cut_var = false; 8646 8647 // This is a hack, in a way; I have lost track of where 8648 // the disambig (name rendering) settings used for rendering work their way 8649 // into the registry. This resets defaults to the subsequent form, 8650 // when first cites are rendered. 8651 //if (this.tmp.disambig_restore && this.registry.registry[Item.id]) { 8652 // this.registry.registry[Item.id].disambig = this.tmp.disambig_restore; 8653 //} 8654 //this.tmp.disambig_restore = false; 8655 this.tmp.disambig_request = false; 8656 8657 this.tmp.cite_locales.push(this.tmp.last_cite_locale); 8658 8659 if (this.tmp.issued_date && this.tmp.renders_collection_number) { 8660 var buf = []; 8661 for (var i = this.tmp.issued_date.list.length - 1; i > this.tmp.issued_date.pos; i += -1) { 8662 buf.push(this.tmp.issued_date.list.pop()); 8663 } 8664 // Throw away the unwanted blob 8665 this.tmp.issued_date.list.pop(); 8666 // Put the other stuff back 8667 for (i = buf.length - 1; i > -1; i += -1) { 8668 this.tmp.issued_date.list.push(buf.pop()); 8669 } 8670 } 8671 this.tmp.issued_date = false; 8672 this.tmp.renders_collection_number = false; 8673 8674}; 8675 8676/*global CSL: true */ 8677 8678CSL.Engine.prototype.makeBibliography = function (bibsection) { 8679 var debug, ret, params, maxoffset, item, len, pos, tok, tokk, tokkk, entry_ids, entry_strings; 8680 debug = false; 8681 if (!bibsection && (this.bibliography.opt.exclude_types || this.bibliography.opt.exclude_with_fields)) { 8682 bibsection = { 8683 exclude: [] 8684 }; 8685 if (this.bibliography.opt.exclude_types) { 8686 for (var i in this.bibliography.opt.exclude_types) { 8687 var val = this.bibliography.opt.exclude_types[i]; 8688 bibsection.exclude.push({ 8689 field: "type", 8690 value: val 8691 }); 8692 } 8693 } 8694 if (this.bibliography.opt.exclude_with_fields) { 8695 for (var i in this.bibliography.opt.exclude_with_fields) { 8696 var field = this.bibliography.opt.exclude_with_fields[i]; 8697 bibsection.exclude.push({ 8698 field: field, value: true 8699 }); 8700 } 8701 } 8702 } 8703 // API change: added in version 1.0.51 8704 if (!this.bibliography.tokens.length) { 8705 return false; 8706 } 8707 if ("string" === typeof bibsection) { 8708 this.opt.citation_number_slug = bibsection; 8709 bibsection = false; 8710 } 8711 //SNIP-START 8712 if (debug) { 8713 len = this.bibliography.tokens.length; 8714 for (pos = 0; pos < len; pos += 1) { 8715 tok = this.bibliography.tokens[pos]; 8716 CSL.debug("bibtok: " + tok.name); 8717 } 8718 CSL.debug("---"); 8719 len = this.citation.tokens.length; 8720 for (pos = 0; pos < len; pos += 1) { 8721 tokk = this.citation.tokens[pos]; 8722 CSL.debug("cittok: " + tok.name); 8723 } 8724 CSL.debug("---"); 8725 len = this.bibliography_sort.tokens.length; 8726 for (pos = 0; pos < len; pos += 1) { 8727 tokkk = this.bibliography_sort.tokens[pos]; 8728 CSL.debug("bibsorttok: " + tok.name); 8729 } 8730 } 8731 //SNIP-END 8732 8733 // For paged returns 8734 ret = CSL.getBibliographyEntries.call(this, bibsection); 8735 entry_ids = ret[0]; 8736 entry_strings = ret[1]; 8737 8738 // For paged returns 8739 var done = ret[2]; 8740 8741 params = { 8742 "maxoffset": 0, 8743 "entryspacing": this.bibliography.opt["entry-spacing"], 8744 "linespacing": this.bibliography.opt["line-spacing"], 8745 "second-field-align": false, 8746 "entry_ids": entry_ids, 8747 "bibliography_errors": this.tmp.bibliography_errors.slice(), 8748 "done": done 8749 }; 8750 if (this.bibliography.opt["second-field-align"]) { 8751 params["second-field-align"] = this.bibliography.opt["second-field-align"]; 8752 } 8753 maxoffset = 0; 8754 len = this.registry.reflist.length; 8755 for (pos = 0; pos < len; pos += 1) { 8756 item = this.registry.reflist[pos]; 8757 if (item.offset > params.maxoffset) { 8758 params.maxoffset = item.offset; 8759 } 8760 } 8761 if (this.bibliography.opt.hangingindent) { 8762 params.hangingindent = this.bibliography.opt.hangingindent; 8763 } 8764 params.bibstart = this.fun.decorate.bibstart; 8765 params.bibend = this.fun.decorate.bibend; 8766 8767 this.opt.citation_number_slug = false; 8768 return [params, entry_strings]; 8769}; 8770 8771/* 8772 * Compose individual cites into a single string. 8773 */ 8774CSL.getBibliographyEntries = function (bibsection) { 8775 var ret, input, include, anymatch, allmatch, bib_entry, res, item, spec, lllen, pppos, topblobs, entry_item_ids, debug, i, ilen, siblings, skips, sortedItems, eyetem, entry_item_data, j, jlen; 8776 ret = []; 8777 entry_item_data = []; 8778 this.tmp.area = "bibliography"; 8779 this.tmp.root = "bibliography"; 8780 this.tmp.last_rendered_name = false; 8781 this.tmp.bibliography_errors = []; 8782 this.tmp.bibliography_pos = 0; 8783 8784 // For paged returns: disable generated entries and 8785 // do not fetch full items as a batch (input variable 8786 // consists of ids only in this case) 8787 if (bibsection && bibsection.page_start && bibsection.page_length) { 8788 input = this.registry.getSortedIds(); 8789 } else { 8790 input = this.refetchItems(this.registry.getSortedIds()); 8791 } 8792 8793 this.tmp.disambig_override = true; 8794 function eval_string(a, b) { 8795 if (a === b) { 8796 return true; 8797 } 8798 return false; 8799 } 8800 function eval_list(a, lst) { 8801 lllen = lst.length; 8802 for (pppos = 0; pppos < lllen; pppos += 1) { 8803 if (eval_string(a, lst[pppos])) { 8804 return true; 8805 } 8806 } 8807 return false; 8808 } 8809 function eval_spec(a, b) { 8810 if ("boolean" === typeof a || !a) { 8811 if (a) { 8812 return !!b; 8813 } else { 8814 return !b; 8815 } 8816 } else { 8817 if ("string" === typeof b) { 8818 return eval_string(a, b); 8819 } else if (!b) { 8820 return false; 8821 } else { 8822 return eval_list(a, b); 8823 } 8824 } 8825 } 8826 8827 skips = {}; 8828 8829 // For paged returns 8830 var page_item_count; 8831 if (bibsection && bibsection.page_start && bibsection.page_length) { 8832 page_item_count = 0; 8833 if (bibsection.page_start !== true) { 8834 for (i = 0, ilen = input.length; i < ilen; i += 1) { 8835 skips[input[i]] = true; 8836 if (bibsection.page_start == input[i]) { 8837 break; 8838 } 8839 } 8840 } 8841 } 8842 8843 var processed_item_ids = []; 8844 8845 var consolidatedIDs = {}; 8846 this.tmp.container_item_count = {}; 8847 input = input.filter(o => { 8848 var ret = o; 8849 if (o.legislation_id) { 8850 if (consolidatedIDs[o.legislation_id]) { 8851 ret = false; 8852 } else { 8853 consolidatedIDs[o.legislation_id] = true; 8854 } 8855 } else if (o.container_id) { 8856 if (!this.tmp.container_item_count[o.container_id]) { 8857 this.tmp.container_item_count[o.container_id] = 0; 8858 } 8859 this.tmp.container_item_count[o.container_id]++; 8860 if (this.bibliography.opt.consolidate_containers.indexOf(o.type) > -1) { 8861 if (consolidatedIDs[o.container_id]) { 8862 ret = false; 8863 } else { 8864 consolidatedIDs[o.container_id] = true; 8865 } 8866 } 8867 } 8868 return ret; 8869 }); 8870 8871 this.tmp.container_item_pos = {}; 8872 8873 for (i = 0, ilen = input.length; i < ilen; i += 1) { 8874 8875 // For paged returns 8876 if (bibsection && bibsection.page_start && bibsection.page_length) { 8877 if (skips[input[i]]) { 8878 continue; 8879 } 8880 item = this.refetchItem(input[i]); 8881 if (page_item_count === bibsection.page_length) { 8882 break; 8883 } 8884 } else { 8885 item = input[i]; 8886 if (skips[item.id]) { 8887 continue; 8888 } 8889 } 8890 if (bibsection) { 8891 include = true; 8892 if (bibsection.include) { 8893 // 8894 // Opt-in: these are OR-ed. 8895 // 8896 include = false; 8897 for (j = 0, jlen = bibsection.include.length; j < jlen; j += 1) { 8898 spec = bibsection.include[j]; 8899 if (eval_spec(spec.value, item[spec.field])) { 8900 include = true; 8901 break; 8902 } 8903 } 8904 } else if (bibsection.exclude) { 8905 // 8906 // Opt-out: these are also OR-ed. 8907 // 8908 anymatch = false; 8909 for (j = 0, jlen = bibsection.exclude.length; j < jlen; j += 1) { 8910 spec = bibsection.exclude[j]; 8911 if (eval_spec(spec.value, item[spec.field])) { 8912 anymatch = true; 8913 break; 8914 } 8915 } 8916 if (anymatch) { 8917 include = false; 8918 } 8919 } else if (bibsection.select) { 8920 // 8921 // Multiple condition opt-in: these are AND-ed. 8922 // 8923 include = false; 8924 allmatch = true; 8925 for (j = 0, jlen = bibsection.select.length; j < jlen; j += 1) { 8926 spec = bibsection.select[j]; 8927 if (!eval_spec(spec.value, item[spec.field])) { 8928 allmatch = false; 8929 } 8930 } 8931 if (allmatch) { 8932 include = true; 8933 } 8934 } 8935 if (bibsection.quash) { 8936 // 8937 // Stop criteria: These are AND-ed. 8938 // 8939 allmatch = true; 8940 for (j = 0, jlen = bibsection.quash.length; j < jlen; j += 1) { 8941 spec = bibsection.quash[j]; 8942 if (!eval_spec(spec.value, item[spec.field])) { 8943 allmatch = false; 8944 } 8945 } 8946 if (allmatch) { 8947 include = false; 8948 } 8949 } 8950 if (!include) { 8951 continue; 8952 } 8953 } 8954 //SNIP-START 8955 if (debug) { 8956 CSL.debug("BIB: " + item.id); 8957 } 8958 8959 if (item.container_id) { 8960 if (!this.tmp.container_item_pos[item.container_id]) { 8961 this.tmp.container_item_pos[item.container_id] = 0; 8962 } 8963 this.tmp.container_item_pos[item.container_id]++; 8964 } 8965 8966 //SNIP-END 8967 bib_entry = new CSL.Token("group", CSL.START); 8968 bib_entry.decorations = [["@bibliography", "entry"]].concat(this.bibliography.opt.layout_decorations); 8969 this.output.startTag("bib_entry", bib_entry); 8970 if (item.system_id && this.sys.embedBibliographyEntry) { 8971 this.output.current.value().item_id = item.system_id; 8972 } else { 8973 this.output.current.value().system_id = item.id; 8974 } 8975 8976 // 2019-06-25 Hacked to conform to new parallels evaluation method 8977 // 2020-04-25 Revised to work with latest, and final, parallel-first/parallel-last attributes 8978 entry_item_ids = []; 8979 if (this.registry.registry[item.id].master 8980 && !(bibsection && bibsection.page_start && bibsection.page_length)) { 8981 // Fetch item content 8982 sortedItems = [[item, {id: item.id}]]; 8983 siblings = this.registry.registry[item.id].siblings; 8984 for (var j=0,jlen=siblings.length; j<jlen; j++) { 8985 sortedItems.push([this.refetchItem(siblings[j]), {id: siblings[j]}]); 8986 } 8987 // Adjust parameters 8988 this.parallel.StartCitation(sortedItems); 8989 if (this.registry.registry[item.id].parallel_delimiter_override) { 8990 this.output.queue[0].strings.delimiter = this.registry.registry[item.id].parallel_delimiter_override; 8991 } else { 8992 this.output.queue[0].strings.delimiter = ", "; 8993 } 8994 this.tmp.term_predecessor = false; 8995 this.tmp.cite_index = 0; 8996 // Run cites 8997 for (j = 0, jlen = sortedItems.length; j < jlen; j += 1) { 8998 if (j < (sortedItems.length - 1)) { 8999 this.tmp.parallel_and_not_last = true; 9000 } else { 9001 delete this.tmp.parallel_and_not_last; 9002 } 9003 entry_item_ids.push("" + CSL.getCite.call(this, sortedItems[j][0], sortedItems[j][1])); 9004 this.tmp.cite_index++; 9005 skips[sortedItems[j][0].id] = true; 9006 } 9007 } else if (!this.registry.registry[item.id].siblings) { 9008 this.tmp.term_predecessor = false; 9009 this.tmp.cite_index = 0; 9010 entry_item_ids.push("" + CSL.getCite.call(this, item)); 9011 if (bibsection && bibsection.page_start && bibsection.page_length) { 9012 page_item_count += 1; 9013 } 9014 //skips[item.id] = true; 9015 } 9016 // For RDF support 9017 entry_item_data.push(""); 9018 9019 this.tmp.bibliography_pos += 1; 9020 9021 processed_item_ids.push(entry_item_ids); 9022 // 9023 // XXX: loop to render parallels goes here 9024 // XXX: just have to mark them somehow ... 9025 // 9026 this.output.endTag("bib_entry"); 9027 // 9028 // place layout prefix on first blob of each cite, and suffix 9029 // on the last non-empty blob of each cite. there be dragons 9030 // here. 9031 // 9032 if (this.output.queue[0].blobs.length && this.output.queue[0].blobs[0].blobs.length) { 9033 // The output queue stuff needs cleaning up. the result of 9034 // output.current.value() is sometimes a blob, sometimes its list 9035 // of blobs. this inconsistency is a source of confusion, and 9036 // should be cleaned up across the code base in the first 9037 // instance, before making any other changes to output code. 9038 if (!this.output.queue[0].blobs[0].blobs[0].strings) { 9039 topblobs = this.output.queue[0].blobs; 9040 } else { 9041 topblobs = this.output.queue[0].blobs[0].blobs; 9042 } 9043 topblobs[0].strings.prefix = this.bibliography.opt.layout_prefix + topblobs[0].strings.prefix; 9044 } 9045 for (j=0,jlen=this.output.queue.length;j<jlen;j+=1) { 9046 CSL.Output.Queue.purgeEmptyBlobs(this.output.queue[j]); 9047 //print("XXX: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2)) 9048 } 9049 for (j=0,jlen=this.output.queue.length;j<jlen;j+=1) { 9050 this.output.adjust.upward(this.output.queue[j]); 9051 this.output.adjust.leftward(this.output.queue[j]); 9052 this.output.adjust.downward(this.output.queue[j],true); 9053 this.output.adjust.fix(this.output.queue[j]); 9054 //print("OUTPUT: "+JSON.stringify(this.output.queue[j],['strings','prefix','suffix','delimiter','blobs','decorations'],2)) 9055 } 9056 9057 //print("DUMP "+JSON.stringify(this.output.queue, ["strings", "decorations", "prefix", "suffix", "delimiter", "blobs"], 2)); 9058 9059 // XXX Need to account for numeric blobs in input. 9060 // XXX No idea how this could have worked previously. 9061 9062 //print("BLOBS "+this.output.queue[0].blobs[0].blobs); 9063 9064 //print("JSON "+JSON.stringify(this.output.queue[0].blobs, null, 2)); 9065 9066 res = this.output.string(this, this.output.queue)[0]; 9067 9068 if (!res && this.opt.update_mode === CSL.NUMERIC) { 9069 var err = (ret.length + 1) + ". [CSL STYLE ERROR: reference with no printed form.]"; 9070 res = CSL.Output.Formats[this.opt.mode]["@bibliography/entry"](this, err); 9071 } 9072 if (res) { 9073 ret.push(res); 9074 } 9075 } 9076 9077 var done = false; 9078 if (bibsection && bibsection.page_start && bibsection.page_length) { 9079 var last_expected_id = input.slice(-1)[0]; 9080 var last_seen_id = processed_item_ids.slice(-1)[0]; 9081 if (!last_expected_id || !last_seen_id || last_expected_id == last_seen_id) { 9082 done = true; 9083 } 9084 } 9085 this.tmp.disambig_override = false; 9086 9087 // XXX done 9088 return [processed_item_ids, ret, done]; 9089}; 9090 9091/*global CSL: true */ 9092 9093 9094CSL.Engine.prototype.setCitationId = function (citation, force) { 9095 var ret, id, direction; 9096 ret = false; 9097 if (!citation.citationID || force) { 9098 id = Math.floor(Math.random() * 100000000000000); 9099 while (true) { 9100 direction = 0; 9101 if (!this.registry.citationreg.citationById[id]) { 9102 // In case the ID is used as an HTML identifier in the 9103 // calling application. 9104 // https://github.com/Juris-M/citeproc-js/issues/22 9105 citation.citationID = "a" + id.toString(32); 9106 break; 9107 } else if (!direction && id < 50000000000000) { 9108 direction = 1; 9109 } else { 9110 direction = -1; 9111 } 9112 if (direction === 1) { 9113 id += 1; 9114 } else { 9115 id += -1; 9116 } 9117 } 9118 ret = "" + id; 9119 } 9120 this.registry.citationreg.citationById[citation.citationID] = citation; 9121 return ret; 9122}; 9123 9124CSL.Engine.prototype.rebuildProcessorState = function (citations, mode, uncitedItemIDs) { 9125 // Rebuilds the processor from scratch, based on a list of citation 9126 // objects. In a dynamic application, once the internal state of processor 9127 // is established, citations should edited with individual invocations 9128 // of processCitationCluster(). 9129 9130 // citations is a list of citation objects in document order. 9131 // mode is one of "html", "text" or "rtf". 9132 // uncitedItemIDs is a list of itemIDs or a JS object with itemIDs as keys. 9133 // Returns a list of [citationID,noteIndex,string] triples in document order. 9134 // Set citation.properties.noteIndex to 0 for in-text citations. 9135 // It is not necessary to run updateItems() before this function. 9136 if (!citations) { 9137 citations = []; 9138 } 9139 if (!mode) { 9140 mode = 'html'; 9141 } 9142 var doneIDs = {}; 9143 var itemIDs = []; 9144 for (var i=0,ilen=citations.length;i<ilen;i+=1) { 9145 for (var j=0,jlen=citations[i].citationItems.length;j<jlen;j+=1) { 9146 var itemID = "" + citations[i].citationItems[j].id; 9147 if (!doneIDs[itemID]) { 9148 itemIDs.push(itemID); 9149 } 9150 doneIDs[itemID] = true; 9151 } 9152 } 9153 this.updateItems(itemIDs); 9154 var pre = []; 9155 var post = []; 9156 var ret = []; 9157 var oldMode = this.opt.mode; 9158 this.setOutputFormat(mode); 9159 for (var i=0,ilen=citations.length;i<ilen;i+=1) { 9160 // res contains a result report and a list of [index,string] pairs 9161 // index begins at 0 9162 var res = this.processCitationCluster(citations[i],pre,post,CSL.ASSUME_ALL_ITEMS_REGISTERED); 9163 pre.push([citations[i].citationID,citations[i].properties.noteIndex]); 9164 for (var j=0,jlen=res[1].length;j<jlen;j+=1) { 9165 var index = res[1][j][0]; 9166 ret[index] = [ 9167 pre[index][0], 9168 pre[index][1], 9169 res[1][j][1] 9170 ]; 9171 } 9172 } 9173 this.updateUncitedItems(uncitedItemIDs); 9174 this.setOutputFormat(oldMode); 9175 return ret; 9176}; 9177 9178 9179CSL.Engine.prototype.restoreProcessorState = function (citations) { 9180 var i, ilen, j, jlen, item, Item, newitem, citationList, itemList, sortedItems; 9181 9182 // This function is deprecated. 9183 // Use rebuildProcessorState() instead. 9184 9185 // Quickly restore state from citation details retained by 9186 // calling application. 9187 // 9188 // if citations are provided, position details and sortkeys 9189 // on the citation objects are are assumed to be correct. Item 9190 // data is retrieved, and sortedItems arrays are created and 9191 // sorted as required by the current style. 9192 // 9193 // If citations is an empty list or nil, reset processor to 9194 // empty state. 9195 citationList = []; 9196 itemList = []; 9197 if (!citations) { 9198 citations = []; 9199 } 9200 // Adjust citationIDs to avoid duplicates, save off index numbers 9201 var indexNumbers = []; 9202 var citationIds = {}; 9203 for (i = 0, ilen = citations.length; i < ilen; i += 1) { 9204 if (citationIds[citations[i].citationID]) { 9205 this.setCitationId(citations[i], true); 9206 } 9207 citationIds[citations[i].citationID] = true; 9208 indexNumbers.push(citations[i].properties.index); 9209 } 9210 // Slice citations and sort by their declared index positions, if any, 9211 // then reassign index and noteIndex numbers. 9212 var oldCitations = citations.slice(); 9213 oldCitations.sort( 9214 function (a,b) { 9215 if (a.properties.index < b.properties.index) { 9216 return -1; 9217 } else if (a.properties.index > b.properties.index) { 9218 return 1; 9219 } else { 9220 return 0; 9221 } 9222 } 9223 ); 9224 for (i = 0, ilen = oldCitations.length; i < ilen; i += 1) { 9225 oldCitations[i].properties.index = i; 9226 } 9227 for (i = 0, ilen = oldCitations.length; i < ilen; i += 1) { 9228 sortedItems = []; 9229 for (j = 0, jlen = oldCitations[i].citationItems.length; j < jlen; j += 1) { 9230 item = oldCitations[i].citationItems[j]; 9231 if ("undefined" === typeof item.sortkeys) { 9232 item.sortkeys = []; 9233 } 9234 Item = this.retrieveItem("" + item.id); 9235 newitem = [Item, item]; 9236 sortedItems.push(newitem); 9237 oldCitations[i].citationItems[j].item = Item; 9238 itemList.push("" + item.id); 9239 } 9240 if (!oldCitations[i].properties.unsorted) { 9241 sortedItems.sort(this.citation.srt.compareCompositeKeys); 9242 } 9243 oldCitations[i].sortedItems = sortedItems; 9244 // Save citation data in registry 9245 this.registry.citationreg.citationById[oldCitations[i].citationID] = oldCitations[i]; 9246 } 9247 // Register Items 9248 this.updateItems(itemList); 9249 9250 // Construct citationList from original copy 9251 for (i = 0, ilen = citations.length; i < ilen; i += 1) { 9252 citationList.push(["" + citations[i].citationID, citations[i].properties.noteIndex]); 9253 } 9254 9255 var ret = []; 9256 if (citations && citations.length) { 9257 // Rendering one citation restores remainder of processor state. 9258 // If citations is empty, rest to empty state. 9259 ret = this.processCitationCluster(citations[0], [], citationList.slice(1)); 9260 } else { 9261 this.registry = new CSL.Registry(this); 9262 this.tmp = new CSL.Engine.Tmp(); 9263 this.disambiguate = new CSL.Disambiguation(this); 9264 } 9265 return ret; 9266}; 9267 9268 9269CSL.Engine.prototype.updateItems = function (idList, nosort, rerun_ambigs, implicitUpdate) { 9270 var debug = false; 9271 var oldArea = this.tmp.area; 9272 var oldRoot = this.tmp.root; 9273 var oldExtension = this.tmp.extension; 9274 if (this.bibliography_sort.tokens.length === 0) { 9275 nosort = true; 9276 } 9277 this.tmp.area = "citation"; 9278 this.tmp.root = "citation"; 9279 this.tmp.extension = ""; 9280 if (!implicitUpdate) { 9281 this.tmp.loadedItemIDs = {}; 9282 } 9283 //CSL.debug = print 9284 //SNIP-START 9285 if (debug) { 9286 CSL.debug("--> init <--"); 9287 } 9288 //SNIP-END 9289 this.registry.init(idList); 9290 9291 if (rerun_ambigs) { 9292 for (var ambig in this.registry.ambigcites) { 9293 this.registry.ambigsTouched[ambig] = true; 9294 } 9295 } 9296 9297 this.registry.dodeletes(this.registry.myhash); 9298 9299 this.registry.doinserts(this.registry.mylist); 9300 9301 this.registry.dorefreshes(); 9302 9303 // *** affects reflist 9304 this.registry.rebuildlist(nosort); 9305 9306 this.registry.setsortkeys(); 9307 9308 // taints always 9309 this.registry.setdisambigs(); 9310 9311 // *** affects reflist 9312 this.registry.sorttokens(nosort); 9313 9314 // *** affects reflist 9315 // taints if numbered style 9316 this.registry.renumber(); 9317 9318 // taints always 9319 //this.registry.yearsuffix(); 9320 9321 this.tmp.extension = oldExtension; 9322 this.tmp.area = oldArea; 9323 this.tmp.root = oldRoot; 9324 9325 return this.registry.getSortedIds(); 9326}; 9327 9328CSL.Engine.prototype.updateUncitedItems = function (idList, nosort) { 9329 var idHash; 9330 var oldArea = this.tmp.area; 9331 var oldRoot = this.tmp.root; 9332 var oldExtension = this.tmp.extension; 9333 if (this.bibliography_sort.tokens.length === 0) { 9334 nosort = true; 9335 } 9336 this.tmp.area = "citation"; 9337 this.tmp.root = "citation"; 9338 this.tmp.extension = ""; 9339 this.tmp.loadedItemIDs = {}; 9340 // This should be a utility function 9341 if (!idList) { 9342 idList = []; 9343 } 9344 if ("object" == typeof idList) { 9345 if ("undefined" == typeof idList.length) { 9346 idHash = idList; 9347 idList = []; 9348 for (var key in idHash) { 9349 idList.push(key); 9350 } 9351 } else if ("number" == typeof idList.length) { 9352 idHash = {}; 9353 for (var i=0,ilen=idList.length;i<ilen;i+=1) { 9354 idHash[idList[i]] = true; 9355 } 9356 } 9357 } 9358 9359 // prepare extended list of items 9360 this.registry.init(idList, true); 9361 9362 // Use purge instead of delete. 9363 // this.registry.dodeletes(this.registry.myhash); 9364 this.registry.dopurge(idHash); 9365 9366 // everything else is the same as updateItems() 9367 this.registry.doinserts(this.registry.mylist); 9368 9369 this.registry.dorefreshes(); 9370 9371 this.registry.rebuildlist(nosort); 9372 9373 this.registry.setsortkeys(); 9374 9375 this.registry.setdisambigs(); 9376 9377 this.registry.sorttokens(nosort); 9378 9379 this.registry.renumber(); 9380 9381 this.tmp.extension = oldExtension; 9382 this.tmp.area = oldArea; 9383 this.tmp.root = oldRoot; 9384 9385 return this.registry.getSortedIds(); 9386}; 9387 9388/*global CSL: true */ 9389 9390CSL.localeResolve = function (langstr, defaultLocale) { 9391 var ret, langlst; 9392 if (!defaultLocale) { 9393 defaultLocale = "en-US"; 9394 } 9395 if (!langstr) { 9396 langstr = defaultLocale; 9397 } 9398 ret = {}; 9399 //if ("undefined" === typeof langstr) { 9400 // langstr = "en_US"; 9401 //} 9402 langlst = langstr.split(/[\-_]/); 9403 ret.base = CSL.LANG_BASES[langlst[0]]; 9404 if ("undefined" === typeof ret.base) { 9405 //CSL.debug("Warning: unknown locale "+langstr+", setting fallback to "+defaultLocale); 9406 return {base:defaultLocale, best:langstr, bare:langlst[0]}; 9407 } 9408 if (langlst.length === 1) { 9409 ret.generic = true; 9410 } 9411 if (langlst.length === 1 || langlst[1] === "x") { 9412 ret.best = ret.base.replace("_", "-"); 9413 } else { 9414 ret.best = langlst.slice(0, 2).join("-"); 9415 } 9416 ret.base = ret.base.replace("_", "-"); 9417 ret.bare = langlst[0]; 9418 return ret; 9419}; 9420 9421// Use call to invoke this. 9422CSL.Engine.prototype.localeConfigure = function (langspec, beShy) { 9423 var localexml; 9424 if (beShy && this.locale[langspec.best]) { 9425 return; 9426 } 9427 if (langspec.best === "en-US") { 9428 localexml = CSL.setupXml(this.sys.retrieveLocale("en-US")); 9429 this.localeSet(localexml, "en-US", langspec.best); 9430 } else if (langspec.best !== "en-US") { 9431 if (langspec.base !== langspec.best) { 9432 localexml = CSL.setupXml(this.sys.retrieveLocale(langspec.base)); 9433 this.localeSet(localexml, langspec.base, langspec.best); 9434 } 9435 localexml = CSL.setupXml(this.sys.retrieveLocale(langspec.best)); 9436 this.localeSet(localexml, langspec.best, langspec.best); 9437 } 9438 this.localeSet(this.cslXml, "", langspec.best); 9439 this.localeSet(this.cslXml, langspec.bare, langspec.best); 9440 if (langspec.base !== langspec.best) { 9441 this.localeSet(this.cslXml, langspec.base, langspec.best); 9442 } 9443 this.localeSet(this.cslXml, langspec.best, langspec.best); 9444 if ("undefined" === typeof this.locale[langspec.best].terms["page-range-delimiter"]) { 9445 if (["fr", "pt"].indexOf(langspec.best.slice(0, 2).toLowerCase()) > -1) { 9446 this.locale[langspec.best].terms["page-range-delimiter"] = "-"; 9447 } else { 9448 this.locale[langspec.best].terms["page-range-delimiter"] = "\u2013"; 9449 } 9450 } 9451 if ("undefined" === typeof this.locale[langspec.best].terms["year-range-delimiter"]) { 9452 this.locale[langspec.best].terms["year-range-delimiter"] = "\u2013"; 9453 } 9454 if ("undefined" === typeof this.locale[langspec.best].terms["citation-range-delimiter"]) { 9455 this.locale[langspec.best].terms["citation-range-delimiter"] = "\u2013"; 9456 } 9457 if (this.opt.development_extensions.normalize_lang_keys_to_lowercase) { 9458 var localeLists = ["default-locale","locale-sort","locale-translit","locale-translat"]; 9459 for (var i=0,ilen=localeLists.length;i<ilen;i+=1) { 9460 for (var j=0,jlen=this.opt[localeLists[i]].length;j<jlen;j+=1) { 9461 this.opt[localeLists[i]][j] = this.opt[localeLists[i]][j].toLowerCase(); 9462 } 9463 } 9464 this.opt.lang = this.opt.lang.toLowerCase(); 9465 } 9466}; 9467 9468// 9469// XXXXX: Got it. The locales objects need to be reorganized, 9470// with a top-level local specifier, and terms, opts, dates 9471// below. 9472// 9473CSL.Engine.prototype.localeSet = function (myxml, lang_in, lang_out) { 9474 var blob, locale, nodes, attributes, pos, term, form, termname, styleopts, date, attrname, len, genderform, target, i, ilen; 9475 lang_in = lang_in.replace("_", "-"); 9476 lang_out = lang_out.replace("_", "-"); 9477 9478 if (this.opt.development_extensions.normalize_lang_keys_to_lowercase) { 9479 lang_in = lang_in.toLowerCase(); 9480 lang_out = lang_out.toLowerCase(); 9481 } 9482 9483 if (!this.locale[lang_out]) { 9484 this.locale[lang_out] = {}; 9485 this.locale[lang_out].terms = {}; 9486 this.locale[lang_out].opts = {}; 9487 // Set default skip words. Can be overridden in locale by attribute on style-options node. 9488 this.locale[lang_out].opts["skip-words"] = CSL.SKIP_WORDS; 9489 // Initialise leading noise word to false. Actual assignment is below. Empty by default, can be overridden in locale by attribute on style-options node. 9490 if (!this.locale[lang_out].opts["leading-noise-words"]) { 9491 this.locale[lang_out].opts["leading-noise-words"] = []; 9492 } 9493 this.locale[lang_out].dates = {}; 9494 // For ordinals 9495 this.locale[lang_out].ord = {'1.0.1':false,keys:{}}; 9496 this.locale[lang_out]["noun-genders"] = {}; 9497 } 9498 9499 // 9500 // Xml: Test if node is "locale" (nb: ns declarations need to be invoked 9501 // on every access to the xml object; bundle this with the functions 9502 // 9503 locale = myxml.makeXml(); 9504 if (myxml.nodeNameIs(myxml.dataObj, 'locale')) { 9505 locale = myxml.dataObj; 9506 } else { 9507 // 9508 // Xml: get a list of all "locale" nodes 9509 // 9510 nodes = myxml.getNodesByName(myxml.dataObj, "locale"); 9511 var foundLocale = false; 9512 for (pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) { 9513 blob = nodes[pos]; 9514 // 9515 // Xml: get locale xml:lang 9516 // 9517 // Iterate over all locales, but for non-matching nodes, 9518 // we set jurisdiction_preference only (processing of the 9519 // chosen one will process the attribute there, 9520 // separately. 9521 if (!foundLocale && myxml.getAttributeValue(blob, 'lang', 'xml') === lang_in) { 9522 locale = blob; 9523 foundLocale = true; 9524 } else { 9525 var lang = myxml.getAttributeValue(blob, 'lang', 'xml'); 9526 var style_options = myxml.getNodesByName(blob, 'style-options'); 9527 if (lang && style_options && style_options.length) { 9528 var jurispref = myxml.getAttributeValue(style_options[0], 'jurisdiction-preference'); 9529 if (jurispref) { 9530 if (!this.locale[lang]) { 9531 this.locale[lang] = { 9532 opts: {} 9533 }; 9534 } 9535 this.locale[lang].opts["jurisdiction-preference"] = jurispref.split(/\s+/); 9536 } 9537 } 9538 } 9539 } 9540 } 9541 // 9542 // Xml: get a list of any cs:type nodes within locale 9543 // 9544 nodes = myxml.getNodesByName(locale, 'type'); 9545 for (i = 0, ilen = myxml.numberofnodes(nodes); i < ilen; i += 1) { 9546 var typenode = nodes[i]; 9547 var type = myxml.getAttributeValue(typenode, 'name'); 9548 var gender = myxml.getAttributeValue(typenode, 'gender'); 9549 this.opt.gender[type] = gender; 9550 } 9551 // 9552 // Xml: get a list of term nodes within locale 9553 // 9554 9555 // If we are setting CSL 1.0.1 ordinals inside a style, wipe the 9556 // slate clean and start over. 9557 var hasCslOrdinals101 = myxml.getNodesByName(locale, 'term', 'ordinal').length; 9558 if (hasCslOrdinals101) { 9559 for (var key in this.locale[lang_out].ord.keys) { 9560 delete this.locale[lang_out].terms[key]; 9561 } 9562 this.locale[lang_out].ord = {"1.0.1":false,keys:{}}; 9563 } 9564 9565 nodes = myxml.getNodesByName(locale, 'term'); 9566 // Collect ordinals info as for 1.0.1, but save only if 1.0.1 toggle triggers 9567 var ordinals101 = {"last-digit":{},"last-two-digits":{},"whole-number":{}}; 9568 var ordinals101_toggle = false; 9569 var genderized_terms = {}; 9570 for (pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) { 9571 term = nodes[pos]; 9572 // 9573 // Xml: get string value of attribute 9574 // 9575 termname = myxml.getAttributeValue(term, 'name'); 9576 if (termname === "sub verbo") { 9577 termname = "sub-verbo"; 9578 } 9579 if (termname.slice(0,7) === "ordinal") { 9580 if (termname === "ordinal") { 9581 ordinals101_toggle = true; 9582 } else { 9583 var match = myxml.getAttributeValue(term, 'match'); 9584 var termstub = termname.slice(8); 9585 var genderform = myxml.getAttributeValue(term, 'gender-form'); 9586 if (!genderform) { 9587 genderform = "neuter"; 9588 } 9589 if (!match) { 9590 match = "last-two-digits"; 9591 if (termstub.slice(0,1) === "0") { 9592 match = "last-digit"; 9593 } 9594 } 9595 if (termstub.slice(0,1) === "0") { 9596 termstub = termstub.slice(1); 9597 } 9598 if (!ordinals101[match][termstub]) { 9599 ordinals101[match][termstub] = {}; 9600 } 9601 ordinals101[match][termstub][genderform] = termname; 9602 } 9603 this.locale[lang_out].ord.keys[termname] = true; 9604 } 9605 if ("undefined" === typeof this.locale[lang_out].terms[termname]) { 9606 this.locale[lang_out].terms[termname] = {}; 9607 } 9608 form = "long"; 9609 genderform = false; 9610 // 9611 // Xml: get string value of form attribute, if any 9612 // 9613 if (myxml.getAttributeValue(term, 'form')) { 9614 form = myxml.getAttributeValue(term, 'form'); 9615 } 9616 // 9617 // Xml: get string value of gender attribute, if any 9618 // 9619 if (myxml.getAttributeValue(term, 'gender-form')) { 9620 genderform = myxml.getAttributeValue(term, 'gender-form'); 9621 } 9622 // 9623 // Xml: set global gender assignment for variable associated 9624 // with term name 9625 // 9626 if (myxml.getAttributeValue(term, 'gender')) { 9627 this.locale[lang_out]["noun-genders"][termname] = myxml.getAttributeValue(term, 'gender'); 9628 } 9629 // Work on main segment or gender-specific sub-segment as appropriate 9630 if (genderform) { 9631 this.locale[lang_out].terms[termname][genderform] = {}; 9632 this.locale[lang_out].terms[termname][genderform][form] = []; 9633 target = this.locale[lang_out].terms[termname][genderform]; 9634 genderized_terms[termname] = true; 9635 } else { 9636 this.locale[lang_out].terms[termname][form] = []; 9637 target = this.locale[lang_out].terms[termname]; 9638 } 9639 // 9640 // Xml: test of existence of node 9641 // 9642 if (myxml.numberofnodes(myxml.getNodesByName(term, 'multiple'))) { 9643 // 9644 // Xml: get string value of attribute, plus 9645 // Xml: get string value of node content 9646 // 9647 target[form][0] = myxml.getNodeValue(term, 'single'); 9648 if (target[form][0].indexOf("%s") > -1) { 9649 this.opt.hasPlaceholderTerm = true; 9650 } 9651 // 9652 // Xml: get string value of attribute, plus 9653 // Xml: get string value of node content 9654 // 9655 target[form][1] = myxml.getNodeValue(term, 'multiple'); 9656 if (target[form][1].indexOf("%s") > -1) { 9657 this.opt.hasPlaceholderTerm = true; 9658 } 9659 } else { 9660 // 9661 // Xml: get string value of attribute, plus 9662 // Xml: get string value of node content 9663 // 9664 target[form] = myxml.getNodeValue(term); 9665 if (target[form].indexOf("%s") > -1) { 9666 this.opt.hasPlaceholderTerm = true; 9667 } 9668 } 9669 } 9670 if (!this.locale[lang_out].terms.supplement) { 9671 this.locale[lang_out].terms.supplement = {}; 9672 } 9673 if (!this.locale[lang_out].terms.supplement["long"]) { 9674 this.locale[lang_out].terms.supplement["long"] = ["supplement", "supplements"]; 9675 } 9676 // If locale had a CSL 1.0.1-style ordinal definition, install the logic object 9677 // and iterate over gendered terms, filling in default values for use by getTerm. 9678 if (ordinals101_toggle) { 9679 for (var ikey in genderized_terms) { 9680 var gender_segments = {}; 9681 var form_segments = 0; 9682 for (var jkey in this.locale[lang_out].terms[ikey]) { 9683 if (["masculine","feminine"].indexOf(jkey) > -1) { 9684 gender_segments[jkey] = this.locale[lang_out].terms[ikey][jkey]; 9685 } else { 9686 form_segments += 1; 9687 } 9688 } 9689 if (!form_segments) { 9690 if (gender_segments.feminine) { 9691 // Link each feminine form segment to default 9692 // (no need to filter, these will not have gender segments mixed in) 9693 for (var jkey in gender_segments.feminine) { 9694 this.locale[lang_out].terms[ikey][jkey] = gender_segments.feminine[jkey]; 9695 } 9696 } else if (gender_segments.masculine) { 9697 // Otherwise link each masculine form segment to default 9698 for (var jkey in gender_segments.masculine) { 9699 this.locale[lang_out].terms[ikey][jkey] = gender_segments.masculine[jkey]; 9700 } 9701 } 9702 } 9703 } 9704 this.locale[lang_out].ord['1.0.1'] = ordinals101; 9705 } 9706 9707 // Iterate over main segments, and fill in any holes in gender-specific data 9708 // sub-segments 9709 for (termname in this.locale[lang_out].terms) { 9710 for (i = 0, ilen = 2; i < ilen; i += 1) { 9711 genderform = CSL.GENDERS[i]; 9712 if (this.locale[lang_out].terms[termname][genderform]) { 9713 for (form in this.locale[lang_out].terms[termname]) { 9714 if (!this.locale[lang_out].terms[termname][genderform][form]) { 9715 this.locale[lang_out].terms[termname][genderform][form] = this.locale[lang_out].terms[termname][form]; 9716 } 9717 } 9718 } 9719 } 9720 } 9721 // 9722 // Xml: get list of nodes by node type 9723 // 9724 nodes = myxml.getNodesByName(locale, 'style-options'); 9725 for (pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) { 9726 if (true) { 9727 styleopts = nodes[pos]; 9728 // 9729 // Xml: get list of attributes on a node 9730 // 9731 attributes = myxml.attributes(styleopts); 9732 for (attrname in attributes) { 9733 if (attributes.hasOwnProperty(attrname)) { 9734 if (attrname === "@punctuation-in-quote" || attrname === "@limit-day-ordinals-to-day-1") { 9735 if (attributes[attrname] === "true") { 9736 // trim off leading @ 9737 this.locale[lang_out].opts[attrname.slice(1)] = true; 9738 } else { 9739 // trim off leading @ 9740 this.locale[lang_out].opts[attrname.slice(1)] = false; 9741 } 9742 } else if (attrname === "@jurisdiction-preference") { 9743 var jurisdiction_preference = attributes[attrname].split(/\s+/); 9744 this.locale[lang_out].opts[attrname.slice(1)] = jurisdiction_preference; 9745 } else if (attrname === "@skip-words") { 9746 var skip_words = attributes[attrname].split(/\s*,\s*/); 9747 this.locale[lang_out].opts[attrname.slice(1)] = skip_words; 9748 } else if (attrname === "@leading-noise-words") { 9749 var val = attributes[attrname].split(/\s*,\s*/); 9750 this.locale[lang_out].opts["leading-noise-words"] = val; 9751 } else if (attrname === "@name-as-sort-order") { 9752 // Fallback is okay here. 9753 this.locale[lang_out].opts["name-as-sort-order"] = {}; 9754 var lst = attributes[attrname].split(/\s+/); 9755 for (var i=0,ilen=lst.length;i<ilen;i+=1) { 9756 this.locale[lang_out].opts["name-as-sort-order"][lst[i]] = true; 9757 } 9758 } else if (attrname === "@name-as-reverse-order") { 9759 // Fallback is okay here. 9760 this.locale[lang_out].opts["name-as-reverse-order"] = {}; 9761 var lst = attributes[attrname].split(/\s+/); 9762 for (var i=0,ilen=lst.length;i<ilen;i+=1) { 9763 this.locale[lang_out].opts["name-as-reverse-order"][lst[i]] = true; 9764 } 9765 } else if (attrname === "@name-never-short") { 9766 // Here too. 9767 this.locale[lang_out].opts["name-never-short"] = {}; 9768 var lst = attributes[attrname].split(/\s+/); 9769 for (var i=0,ilen=lst.length;i<ilen;i+=1) { 9770 this.locale[lang_out].opts["name-never-short"][lst[i]] = true; 9771 } 9772 } 9773 } 9774 } 9775 } 9776 } 9777 // 9778 // Xml: get list of nodes by type 9779 // 9780 nodes = myxml.getNodesByName(locale, 'date'); 9781 for (pos = 0, len = myxml.numberofnodes(nodes); pos < len; pos += 1) { 9782 if (true) { 9783 var date = nodes[pos]; 9784 // 9785 // Xml: get string value of attribute 9786 // 9787 this.locale[lang_out].dates[myxml.getAttributeValue(date, "form")] = date; 9788 } 9789 } 9790 // 9791 // Xml: get list of nodes by node type 9792 // 9793 CSL.SET_COURT_CLASSES(this, lang_out, myxml, locale); 9794}; 9795 9796 9797CSL.getLocaleNames = function (myxml, preferredLocale) { 9798 var stylexml = CSL.setupXml(myxml); 9799 9800 function extendLocaleList(localeList, locale) { 9801 var forms = ["base", "best"]; 9802 if (locale) { 9803 var normalizedLocale = CSL.localeResolve(locale); 9804 for (var i=0,ilen=forms.length;i<ilen;i++) { 9805 if (normalizedLocale[forms[i]] && localeList.indexOf(normalizedLocale[forms[i]]) === -1) { 9806 localeList.push(normalizedLocale[forms[i]]); 9807 } 9808 } 9809 } 9810 } 9811 9812 var localeIDs = ["en-US"]; 9813 9814 function sniffLocaleOnOneNodeName(nodeName) { 9815 var nodes = stylexml.getNodesByName(stylexml.dataObj, nodeName); 9816 for (var i=0,ilen=nodes.length;i<ilen;i++) { 9817 var nodeLocales = stylexml.getAttributeValue(nodes[i], "locale"); 9818 if (nodeLocales) { 9819 nodeLocales = nodeLocales.split(/ +/); 9820 for (var j=0,jlen=nodeLocales.length;j<jlen;j++) { 9821 this.extendLocaleList(localeIDs, nodeLocales[j]); 9822 } 9823 } 9824 } 9825 } 9826 9827 extendLocaleList(localeIDs, preferredLocale); 9828 9829 var styleNode = stylexml.getNodesByName(stylexml.dataObj, "style")[0]; 9830 var defaultLocale = stylexml.getAttributeValue(styleNode, "default-locale"); 9831 extendLocaleList(localeIDs, defaultLocale); 9832 9833 var nodeNames = ["layout", "if", "else-if", "condition"]; 9834 for (var i=0,ilen=nodeNames.length;i<ilen;i++) { 9835 sniffLocaleOnOneNodeName(stylexml, localeIDs, nodeNames[i]); 9836 } 9837 return localeIDs; 9838}; 9839 9840/*global CSL: true */ 9841 9842CSL.Node = {}; 9843 9844CSL.Node.bibliography = { 9845 build: function (state, target) { 9846 if (this.tokentype === CSL.START) { 9847 9848 state.build.area = "bibliography"; 9849 state.build.root = "bibliography"; 9850 state.build.extension = ""; 9851 9852 var func = function(state) { 9853 state.tmp.area = "bibliography"; 9854 state.tmp.root = "bibliography"; 9855 state.tmp.extension = ""; 9856 }; 9857 this.execs.push(func); 9858 9859 } 9860 target.push(this); 9861 } 9862}; 9863 9864 9865/*global CSL: true */ 9866 9867CSL.Node.choose = { 9868 build: function (state, target) { 9869 var func; 9870 if (this.tokentype === CSL.START) { 9871 //open condition 9872 func = function (state) { 9873 state.tmp.jump.push(undefined, CSL.LITERAL); 9874 }; 9875 } 9876 if (this.tokentype === CSL.END) { 9877 //close condition 9878 func = function (state) { 9879 state.tmp.jump.pop(); 9880 }; 9881 } 9882 this.execs.push(func); 9883 target.push(this); 9884 }, 9885 9886 configure: function (state, pos) { 9887 if (this.tokentype === CSL.END) { 9888 state.configure.fail.push((pos)); 9889 state.configure.succeed.push((pos)); 9890 } else { 9891 state.configure.fail.pop(); 9892 state.configure.succeed.pop(); 9893 } 9894 } 9895}; 9896 9897/*global CSL: true */ 9898 9899CSL.Node.citation = { 9900 build: function (state, target) { 9901 if (this.tokentype === CSL.START) { 9902 9903 state.build.area = "citation"; 9904 state.build.root = "citation"; 9905 state.build.extension = ""; 9906 9907 9908 var func = function(state) { 9909 state.tmp.area = "citation"; 9910 state.tmp.root = "citation"; 9911 state.tmp.extension = ""; 9912 }; 9913 this.execs.push(func); 9914 9915/* 9916 state.build.root = "citation"; 9917 9918 OK state.fixOpt(this, "names-delimiter", "delimiter"); 9919 OK state.fixOpt(this, "name-delimiter", "delimiter"); 9920 OK state.fixOpt(this, "name-form", "form"); 9921 OK state.fixOpt(this, "and", "and"); 9922 OK state.fixOpt(this, "delimiter-precedes-last", "delimiter-precedes-last"); 9923 OK state.fixOpt(this, "delimiter-precedes-et-al", "delimiter-precedes-et-al"); 9924 OK state.fixOpt(this, "initialize-with", "initialize-with"); 9925 OK state.fixOpt(this, "initialize", "initialize"); 9926 OK state.fixOpt(this, "name-as-sort-order", "name-as-sort-order"); 9927 OK state.fixOpt(this, "sort-separator", "sort-separator"); 9928 9929 OK state.fixOpt(this, "et-al-min", "et-al-min"); 9930 OK state.fixOpt(this, "et-al-use-first", "et-al-use-first"); 9931 OK state.fixOpt(this, "et-al-use-last", "et-al-use-last"); 9932 state.fixOpt(this, "et-al-subsequent-min", "et-al-subsequent-min"); 9933 state.fixOpt(this, "et-al-subsequent-use-first", "et-al-subsequent-use-first"); 9934*/ 9935 } 9936 if (this.tokentype === CSL.END) { 9937 9938 // Open an extra key at first position for use in 9939 // grouped sorts. 9940 // print("in cs:citation END"); 9941 state.opt.grouped_sort = state.opt.xclass === "in-text" 9942 && (state.citation.opt.collapse 9943 && state.citation.opt.collapse.length) 9944 || (state.citation.opt.cite_group_delimiter 9945 && state.citation.opt.cite_group_delimiter.length) 9946 && state.opt.update_mode !== CSL.POSITION 9947 && state.opt.update_mode !== CSL.NUMERIC; 9948 9949 if (state.opt.grouped_sort 9950 && state.citation_sort.opt.sort_directions.length) { 9951 9952 var firstkey = state.citation_sort.opt.sort_directions[0].slice(); 9953 //print("extending sort keys "+state.citation_sort.opt.sort_directions+" with "+firstkey); 9954 state.citation_sort.opt.sort_directions = [firstkey].concat(state.citation_sort.opt.sort_directions); 9955 // print("new key directions in effect: "+state.citation_sort.opt.sort_directions); 9956 } 9957 // print("creating new comparifier"); 9958 state.citation.srt = new CSL.Registry.Comparifier(state, "citation_sort"); 9959 } 9960 target.push(this); 9961 } 9962}; 9963 9964 9965/*global CSL: true */ 9966 9967CSL.Node["#comment"] = { 9968 // This is a comment in the CSL file. 9969 build: function () { 9970 // Save some space in the log files -- no need to mention this, really. 9971 // CSL.debug("CSL processor warning: comment node reached"); 9972 } 9973}; 9974 9975/*global CSL: true */ 9976 9977CSL.Node.date = { 9978 build: function (state, target) { 9979 var func, date_obj, len, pos, part, dpx, parts, mypos, start, end; 9980 if (this.tokentype === CSL.START || this.tokentype === CSL.SINGLETON) { 9981 // used to collect rendered date part names in node_datepart, 9982 // for passing through to node_key, for use in dates embedded 9983 // in macros 9984 state.dateput.string(state, state.dateput.queue); 9985 state.tmp.date_token = CSL.Util.cloneToken(this); 9986 state.tmp.date_token.strings.prefix = ""; 9987 state.tmp.date_token.strings.suffix = ""; 9988 state.dateput.openLevel(this); 9989 state.build.date_parts = []; 9990 state.build.date_variables = this.variables; 9991 if (!state.build.extension) { 9992 CSL.Util.substituteStart.call(this, state, target); 9993 } 9994 if (state.build.extension) { 9995 func = CSL.dateMacroAsSortKey; 9996 } else { 9997 func = function (state, Item, item) { 9998 var dp; 9999 state.tmp.element_rendered_ok = false; 10000 state.tmp.donesies = []; 10001 state.tmp.dateparts = []; 10002 dp = []; 10003 //if (this.variables.length && Item[this.variables[0]]){ 10004 if (this.variables.length 10005 && !(state.tmp.just_looking 10006 && this.variables[0] === "accessed")) { 10007 10008 date_obj = Item[this.variables[0]]; 10009 if ("undefined" === typeof date_obj) { 10010 date_obj = {"date-parts": [[0]] }; 10011 if (state.opt.development_extensions.locator_date_and_revision) { 10012 if (item && this.variables[0] === "locator-date" && item["locator-date"]) { 10013 date_obj = item["locator-date"]; 10014 } 10015 } 10016 } 10017 state.tmp.date_object = date_obj; 10018 // 10019 // Call a function here to analyze the 10020 // data and set the name of the date-part that 10021 // should collapse for this range, if any. 10022 // 10023 // (1) build a filtered list, in y-m-d order, 10024 // consisting only of items that are (a) in the 10025 // date-parts and (b) in the *_end data. 10026 // (note to self: remember that season is a 10027 // fallback var when month and day are empty) 10028 10029 //if ("undefined" === typeof this.dateparts) { 10030 // this.dateparts = ["year", "month", "day"]; 10031 //} 10032 len = this.dateparts.length; 10033 for (pos = 0; pos < len; pos += 1) { 10034 part = this.dateparts[pos]; 10035 if ("undefined" !== typeof state.tmp.date_object[(part + "_end")]) { 10036 dp.push(part); 10037 } else if (part === "month" && "undefined" !== typeof state.tmp.date_object.season_end) { 10038 dp.push(part); 10039 } 10040 } 10041 dpx = []; 10042 parts = ["year", "month", "day"]; 10043 len = parts.length; 10044 for (pos = 0; pos < len; pos += 1) { 10045 if (dp.indexOf(parts[pos]) > -1) { 10046 dpx.push(parts[pos]); 10047 } 10048 } 10049 dp = dpx.slice(); 10050 // 10051 // (2) Reverse the list and step through in 10052 // reverse order, popping each item if the 10053 // primary and *_end data match. 10054 mypos = 2; 10055 len = dp.length; 10056 for (pos = 0; pos < len; pos += 1) { 10057 part = dp[pos]; 10058 start = state.tmp.date_object[part]; 10059 end = state.tmp.date_object[(part + "_end")]; 10060 if (start !== end) { 10061 mypos = pos; 10062 break; 10063 } 10064 } 10065 10066 // 10067 // (3) When finished, the first item in the 10068 // list, if any, is the date-part where 10069 // the collapse should occur. 10070 10071 // XXXXX: was that it? 10072 state.tmp.date_collapse_at = dp.slice(mypos); 10073 // 10074 // The collapse itself will be done by appending 10075 // string output for the date, less suffix, 10076 // placing a delimiter on output, then then 10077 // doing the *_end of the range, dropping only 10078 // the prefix. That should give us concise expressions 10079 // of ranges. 10080 // 10081 // Numeric dates should not collapse, though, 10082 // and should probably use a slash delimiter. 10083 // Scope for configurability will remain (all over 10084 // the place), but this will do to get this feature 10085 // started. 10086 // 10087 } else { 10088 state.tmp.date_object = false; 10089 } 10090 }; 10091 } 10092 this.execs.push(func); 10093 10094 // newoutput 10095 func = function (state, Item) { 10096 if (!Item[this.variables[0]]) { 10097 return; 10098 } 10099 state.output.startTag("date", this); 10100 if (this.variables[0] === "issued" 10101 && (Item.type === "legal_case" || Item.type === "legislation") 10102 && state.opt.disable_duplicate_year_suppression.indexOf(Item.country) === -1 10103 && !state.tmp.extension 10104 && "" + Item["collection-number"] === "" + state.tmp.date_object.year 10105 && this.dateparts.length === 1 10106 && this.dateparts[0] === "year") { 10107 10108 // Set up to (maybe) suppress the year if we're not sorting, and 10109 // it's the same as the collection-number, and we would render 10110 // only the year, with not month or day, and this is a legal_case item. 10111 // We save a pointer to the blob parent and its position here. The 10112 // blob will be popped from output if at the end of processing for 10113 // this cite we find that we have rendered the collection-number 10114 // variable also. 10115 for (var key in state.tmp.date_object) { 10116 if (state.tmp.date_object.hasOwnProperty(key)) { 10117 if (key.slice(0, 4) === "year") { 10118 state.tmp.issued_date = {}; 10119 var lst = state.output.current.mystack.slice(-2)[0].blobs; 10120 state.tmp.issued_date.list = lst; 10121 state.tmp.issued_date.pos = lst.length - 1; 10122 } 10123 } 10124 } 10125 } 10126 }; 10127 this.execs.push(func); 10128 } 10129 10130 if (!state.build.extension && (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON)) { 10131 // mergeoutput 10132 func = function (state, Item) { 10133 if (!Item[this.variables[0]]) { 10134 return; 10135 } 10136 state.output.endTag(); 10137 }; 10138 this.execs.push(func); 10139 } 10140 target.push(this); 10141 10142 if (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON) { 10143 if (!state.build.extension) { 10144 CSL.Util.substituteEnd.call(this, state, target); 10145 } 10146 } 10147 } 10148}; 10149 10150/*global CSL: true */ 10151 10152CSL.Node["date-part"] = { 10153 build: function (state, target) { 10154 var func, pos, len, first_date, value, value_end, real, have_collapsed, invoked, precondition, known_year, bc, ad, bc_end, ad_end, ready, curr, dcurr, number, num, formatter, item, blob; 10155 if (!this.strings.form) { 10156 this.strings.form = "long"; 10157 } 10158 // used in node_date, to send a list of rendering date parts 10159 // to node_key, for dates embedded in macros. 10160 state.build.date_parts.push(this.strings.name); 10161 // 10162 // Set delimiter here, if poss. 10163 // 10164 10165 var date_variable = state.build.date_variables[0]; 10166 10167 function formatAndStrip(myform, gender, val) { 10168 if (!val) { 10169 return val; 10170 } 10171 val = "" + CSL.Util.Dates[this.strings.name][myform](state, val, gender, this.default_locale); 10172 if ("month" === this.strings.name) { 10173 if (state.tmp.strip_periods) { 10174 val = val.replace(/\./g, ""); 10175 } else { 10176 for (var i = 0, ilen = this.decorations.length; i < ilen; i += 1) { 10177 if ("@strip-periods" === this.decorations[i][0] && "true" === this.decorations[i][1]) { 10178 val = val.replace(/\./g, ""); 10179 break; 10180 } 10181 } 10182 } 10183 } 10184 return val; 10185 } 10186 10187 func = function (state, Item) { 10188 10189 if (!state.tmp.date_object) { 10190 return; 10191 } else { 10192 state.tmp.probably_rendered_something = true; 10193 } 10194 10195 var last_string_output = ""; 10196 10197 first_date = true; 10198 value = ""; 10199 value_end = ""; 10200 state.tmp.donesies.push(this.strings.name); 10201 10202 // Render literal only when year is included in date output 10203 if (state.tmp.date_object.literal && "year" === this.strings.name) { 10204 last_string_output = state.tmp.date_object.literal; 10205 state.output.append(state.tmp.date_object.literal, this); 10206 } 10207 10208 if (state.tmp.date_object) { 10209 value = state.tmp.date_object[this.strings.name]; 10210 value_end = state.tmp.date_object[(this.strings.name + "_end")]; 10211 } 10212 if ("year" === this.strings.name && value === 0 && !state.tmp.suppress_decorations) { 10213 value = false; 10214 } 10215 real = !state.tmp.suppress_decorations; 10216 have_collapsed = state.tmp.have_collapsed; 10217 invoked = state[state.tmp.area].opt.collapse === "year-suffix" || state[state.tmp.area].opt.collapse === "year-suffix-ranged"; 10218 precondition = state.opt["disambiguate-add-year-suffix"]; 10219 if (real && precondition && invoked) { 10220 state.tmp.years_used.push(value); 10221 known_year = state.tmp.last_years_used.length >= state.tmp.years_used.length; 10222 if (known_year && have_collapsed) { 10223 if (state.tmp.last_years_used[(state.tmp.years_used.length - 1)] === value) { 10224 value = false; 10225 } 10226 } 10227 } 10228 if ("undefined" !== typeof value) { 10229 bc = false; 10230 ad = false; 10231 bc_end = false; 10232 ad_end = false; 10233 if ("year" === this.strings.name) { 10234 if (parseInt(value, 10) < 500 && parseInt(value, 10) > 0) { 10235 ad = state.getTerm("ad"); 10236 } 10237 if (parseInt(value, 10) < 0) { 10238 bc = state.getTerm("bc"); 10239 value = (parseInt(value, 10) * -1); 10240 } 10241 if (value_end) { 10242 if (parseInt(value_end, 10) < 500 && parseInt(value_end, 10) > 0) { 10243 ad_end = state.getTerm("ad"); 10244 } 10245 if (parseInt(value_end, 10) < 0) { 10246 bc_end = state.getTerm("bc"); 10247 value_end = (parseInt(value_end, 10) * -1); 10248 } 10249 } 10250 } 10251 10252 // For gendered locales 10253 var monthnameid = ""+state.tmp.date_object.month; 10254 while (monthnameid.length < 2) { 10255 monthnameid = "0"+monthnameid; 10256 } 10257 monthnameid = "month-"+monthnameid; 10258 var gender = state.locale[state.opt.lang]["noun-genders"][monthnameid]; 10259 if (this.strings.form) { 10260 var myform = this.strings.form; 10261 var myform_end = this.strings.form; 10262 if (this.strings.name === "day") { 10263 if (myform === "ordinal" && state.locale[state.opt.lang].opts["limit-day-ordinals-to-day-1"]) { 10264 if (value != 1) { 10265 myform = "numeric"; 10266 } 10267 if (value_end != 1) { 10268 myform_end = "numeric"; 10269 } 10270 } 10271 } 10272 value = formatAndStrip.call(this, myform, gender, value); 10273 value_end = formatAndStrip.call(this, myform_end, gender, value_end); 10274 } 10275 state.output.openLevel("empty"); 10276 if (state.tmp.date_collapse_at.length) { 10277 //state.output.startTag(this.strings.name,this); 10278 ready = true; 10279 len = state.tmp.date_collapse_at.length; 10280 for (pos = 0; pos < len; pos += 1) { 10281 item = state.tmp.date_collapse_at[pos]; 10282 if (state.tmp.donesies.indexOf(item) === -1) { 10283 ready = false; 10284 break; 10285 } 10286 } 10287 if (ready) { 10288 if ("" + value_end !== "0") { 10289 if (state.dateput.queue.length === 0) { 10290 first_date = true; 10291 } 10292 10293 // OK! So if the actual data has no month, day or season, 10294 // and we reach this block, then we can combine the dates 10295 // to a string, run minimial-two, and output the trailing 10296 // year right here. No impact on other functionality. 10297 10298 if (state.opt["year-range-format"] 10299 && state.opt["year-range-format"] !== "expanded" 10300 && !state.tmp.date_object.day 10301 && !state.tmp.date_object.month 10302 && !state.tmp.date_object.season 10303 && this.strings.name === "year" 10304 && value && value_end) { 10305 10306 // second argument adjusts collapse as required for years 10307 // See OSCOLA section 1.3.2 10308 value_end = state.fun.year_mangler(value + "-" + value_end, true); 10309 var range_delimiter = state.getTerm("year-range-delimiter"); 10310 value_end = value_end.slice(value_end.indexOf(range_delimiter) + 1); 10311 } 10312 last_string_output = value_end; 10313 state.dateput.append(value_end, this); 10314 if (first_date) { 10315 blob = state.dateput.current.value().blobs[0]; 10316 if (blob) { 10317 blob.strings.prefix = ""; 10318 } 10319 10320 } 10321 } 10322 last_string_output = value; 10323 state.output.append(value, this); 10324 curr = state.output.current.value(); 10325 blob = curr.blobs[(curr.blobs.length - 1)]; 10326 if (blob) { 10327 blob.strings.suffix = ""; 10328 } 10329 10330 if (this.strings["range-delimiter"]) { 10331 state.output.append(this.strings["range-delimiter"]); 10332 } else { 10333 state.output.append(state.getTerm("year-range-delimiter"), "empty"); 10334 } 10335 state.dateput.closeLevel(); 10336 dcurr = state.dateput.current.value(); 10337 curr.blobs = curr.blobs.concat(dcurr); 10338 // This may leave the stack pointer on a lower level. 10339 // It's not a problem because the stack will be clobbered 10340 // when the queue is initialized by the next cs:date node. 10341 state.dateput.string(state, state.dateput.queue); 10342 state.dateput.openLevel(state.tmp.date_token); 10343 state.tmp.date_collapse_at = []; 10344 } else { 10345 last_string_output = value; 10346 state.output.append(value, this); 10347 // print("collapse_at: "+state.tmp.date_collapse_at); 10348 if (state.tmp.date_collapse_at.indexOf(this.strings.name) > -1) { 10349 // 10350 // Use ghost dateput queue 10351 // 10352 if ("" + value_end !== "0") { 10353 // 10354 // XXXXX: It's a workaround. It's ugly. 10355 // There's another one above. 10356 // 10357 if (state.dateput.queue.length === 0) { 10358 first_date = true; 10359 } 10360 state.dateput.openLevel("empty"); 10361 last_string_output = value_end; 10362 state.dateput.append(value_end, this); 10363 if (first_date) { 10364 blob = state.dateput.current.value().blobs[0]; 10365 if (blob) { 10366 blob.strings.prefix = ""; 10367 } 10368 } 10369 if (bc) { 10370 last_string_output = bc; 10371 state.dateput.append(bc); 10372 } 10373 if (ad) { 10374 last_string_output = ad; 10375 state.dateput.append(ad); 10376 } 10377 state.dateput.closeLevel(); 10378 } 10379 } 10380 } 10381 } else { 10382 last_string_output = value; 10383 state.output.append(value, this); 10384 } 10385 10386 if (bc) { 10387 last_string_output = bc; 10388 state.output.append(bc); 10389 } 10390 if (ad) { 10391 last_string_output = ad; 10392 state.output.append(ad); 10393 } 10394 state.output.closeLevel(); 10395 //state.output.endTag(); 10396 } else if ("month" === this.strings.name) { 10397 // XXX The simpler solution here will be to 10398 // directly install season and season_end on 10399 // month, with a value of 13, 14, 15, 16, or 10400 // (to allow correct ranging with Down Under 10401 // dates) 17 or 18. That will allow ranging 10402 // to take place in the normal way. With this 10403 // "approach", it doesn't. 10404 // 10405 // No value for this target variable 10406 // 10407 if (state.tmp.date_object.season) { 10408 value = "" + state.tmp.date_object.season; 10409 if (value && value.match(/^[1-4]$/)) { 10410 // XXXXXXXXXXXXXXXXXXX was replace([false, false, true]); 10411 //state.tmp.group_context.replace([false, false, true]); 10412 state.tmp.group_context.tip.variable_success = true; 10413 last_string_output = "winter"; 10414 state.output.append(state.getTerm(("season-0" + value)), this); 10415 } else if (value) { 10416 last_string_output = value; 10417 state.output.append(value, this); 10418 } 10419 } 10420 } 10421 state.tmp.value = []; 10422 if (Item[date_variable] && (value || state.tmp.have_collapsed) && !state.opt.has_year_suffix && "year" === this.strings.name && !state.tmp.just_looking) { 10423 if (state.registry.registry[Item.id] && state.registry.registry[Item.id].disambig.year_suffix !== false && !state.tmp.has_done_year_suffix) { 10424 state.tmp.has_done_year_suffix = true; 10425 last_string_output = "x"; 10426 num = parseInt(state.registry.registry[Item.id].disambig.year_suffix, 10); 10427 // first argument is for number particle [a-zA-Z], never present on dates 10428 number = new CSL.NumericBlob(state, false, num, this, Item.id); 10429 this.successor_prefix = state[state.build.area].opt.layout_delimiter; 10430 this.splice_prefix = state[state.build.area].opt.layout_delimiter; 10431 formatter = new CSL.Util.Suffixator(CSL.SUFFIX_CHARS); 10432 number.setFormatter(formatter); 10433 if (state[state.tmp.area].opt.collapse === "year-suffix-ranged") { 10434 number.range_prefix = state.getTerm("citation-range-delimiter"); 10435 } 10436 if (state[state.tmp.area].opt.cite_group_delimiter) { 10437 number.successor_prefix = state[state.tmp.area].opt.cite_group_delimiter; 10438 } else if (state[state.tmp.area].opt["year-suffix-delimiter"]) { 10439 number.successor_prefix = state[state.tmp.area].opt["year-suffix-delimiter"]; 10440 } else { 10441 number.successor_prefix = state[state.tmp.area].opt.layout_delimiter; 10442 } 10443 number.UGLY_DELIMITER_SUPPRESS_HACK = true; 10444 state.output.append(number, "literal"); 10445 } 10446 } 10447 if (last_string_output && !state.tmp.group_context.tip.condition) { 10448 state.tmp.just_did_number = last_string_output.match(/[0-9]$/); 10449 if (state.output.current.tip.strings.suffix) { 10450 state.tmp.just_did_number = false; 10451 } 10452 } 10453 }; 10454 this.execs.push(func); 10455 target.push(this); 10456 } 10457}; 10458 10459/*global CSL: true */ 10460 10461CSL.Node["else-if"] = { 10462 // 10463 // these function are the same as those in if, might just clone 10464 build: function (state, target) { 10465 CSL.Conditions.TopNode.call(this, state, target); 10466 target.push(this); 10467 }, 10468 configure: function (state, pos) { 10469 CSL.Conditions.Configure.call(this, state, pos); 10470 } 10471}; 10472 10473/*global CSL: true */ 10474 10475CSL.Node["else"] = { 10476 build: function (state, target) { 10477 target.push(this); 10478 }, 10479 configure: function (state, pos) { 10480 if (this.tokentype === CSL.START) { 10481 state.configure.fail[(state.configure.fail.length - 1)] = pos; 10482 } 10483 } 10484}; 10485 10486 10487/*global CSL: true */ 10488 10489CSL.Node["et-al"] = { 10490 build: function (state, target) { 10491 if (state.build.area === "citation" || state.build.area === "bibliography") { 10492 var func = function (state) { 10493 state.tmp.etal_node = this; 10494 if ("string" === typeof this.strings.term) { 10495 state.tmp.etal_term = this.strings.term; 10496 } 10497 }; 10498 this.execs.push(func); 10499 } 10500 target.push(this); 10501 } 10502}; 10503 10504/*global CSL: true */ 10505 10506CSL.Node.group = { 10507 build: function (state, target, realGroup) { 10508 var func, execs; 10509 this.realGroup = realGroup; 10510 if (this.tokentype === CSL.START) { 10511 CSL.Util.substituteStart.call(this, state, target); 10512 if (state.build.substitute_level.value()) { 10513 state.build.substitute_level.replace((state.build.substitute_level.value() + 1)); 10514 } 10515 if (!this.juris) { 10516 target.push(this); 10517 } 10518 10519 // newoutput 10520 func = function (state) { 10521 state.output.startTag("group", this); 10522 10523 if (this.strings.label_form_override) { 10524 if (!state.tmp.group_context.tip.label_form) { 10525 state.tmp.group_context.tip.label_form = this.strings.label_form_override; 10526 } 10527 } 10528 10529 if (this.strings.label_capitalize_if_first_override) { 10530 if (!state.tmp.group_context.tip.label_capitalize_if_first) { 10531 state.tmp.group_context.tip.label_capitalize_if_first = this.strings.label_capitalize_if_first_override; 10532 } 10533 } 10534 10535 if (this.realGroup) { 10536 10537 if (state.tmp.group_context.tip.condition) { 10538 CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, this.strings.prefix, null, this); 10539 } 10540 10541 var condition = false; 10542 var force_suppress = false; 10543 10544 // XXX Can we do something better for length here? 10545 if (state.tmp.group_context.mystack.length) { 10546 state.output.current.value().parent = state.tmp.group_context.tip.output_tip; 10547 } 10548 10549 // fieldcontextflag 10550 var label_form = state.tmp.group_context.tip.label_form; 10551 if (!label_form) { 10552 label_form = this.strings.label_form_override; 10553 } 10554 10555 var label_capitalize_if_first = state.tmp.group_context.tip.label_capitalize_if_first; 10556 if (!label_capitalize_if_first) { 10557 label_capitalize_if_first = this.strings.label_capitalize_if_first; 10558 } 10559 if (state.tmp.group_context.tip.condition) { 10560 condition = state.tmp.group_context.tip.condition; 10561 force_suppress = state.tmp.group_context.tip.force_suppress; 10562 //force_suppress: false; 10563 } else if (this.strings.reject) { 10564 condition = { 10565 test: this.strings.reject, 10566 not: true 10567 }; 10568 } else if (this.strings.require) { 10569 condition = { 10570 test: this.strings.require, 10571 not: false 10572 }; 10573 } 10574 var context = { 10575 old_term_predecessor: state.tmp.term_predecessor, 10576 term_intended: false, 10577 variable_attempt: false, 10578 variable_success: false, 10579 variable_success_parent: state.tmp.group_context.tip.variable_success, 10580 output_tip: state.output.current.tip, 10581 label_form: label_form, 10582 label_static: state.tmp.group_context.tip.label_static, 10583 label_capitalize_if_first: label_capitalize_if_first, 10584 parallel_delimiter_override: this.strings.set_parallel_delimiter_override, 10585 parallel_delimiter_override_on_suppress: this.strings.set_parallel_delimiter_override_on_suppress, 10586 condition: condition, 10587 force_suppress: force_suppress, 10588 done_vars: state.tmp.group_context.tip.done_vars.slice() 10589 }; 10590 if(this.non_parallel) { 10591 var non_parallel = state.tmp.group_context.tip.non_parallel; 10592 if (!non_parallel) { 10593 non_parallel = {}; 10594 } 10595 Object.assign(non_parallel, this.non_parallel); 10596 context.non_parallel = non_parallel; 10597 } 10598 if(this.parallel_first) { 10599 var parallel_first = state.tmp.group_context.tip.parallel_first; 10600 if (!parallel_first) { 10601 parallel_first = {}; 10602 } 10603 Object.assign(parallel_first, this.parallel_first); 10604 context.parallel_first = parallel_first; 10605 } 10606 if(this.parallel_last) { 10607 var parallel_last = state.tmp.group_context.tip.parallel_last; 10608 if (!parallel_last) { 10609 parallel_last = {}; 10610 } 10611 Object.assign(parallel_last, this.parallel_last); 10612 context.parallel_last = parallel_last; 10613 } 10614 if (state.tmp.abbrev_trimmer && state.tmp.abbrev_trimmer.LAST_TO_FIRST && context.parallel_last) { 10615 if (!context.parallel_first) { 10616 context.parallel_first = {}; 10617 } 10618 for (var varname in state.tmp.abbrev_trimmer.LAST_TO_FIRST) { 10619 if (context.parallel_last[varname]) { 10620 context.parallel_first[varname] = true; 10621 delete context.parallel_last[varname]; 10622 } 10623 } 10624 } 10625 10626 /* 10627 if(this.parallel_last_override) { 10628 var parallel_last_override = state.tmp.group_context.tip.parallel_last_override; 10629 if (!parallel_last_override) { 10630 parallel_last_override = {}; 10631 } 10632 Object.assign(parallel_last_override, this.parallel_last_override); 10633 context.parallel_last_override = parallel_last_override; 10634 } 10635 */ 10636 state.tmp.group_context.push(context); 10637 10638 if (state.tmp.abbrev_trimmer && this.parallel_last_to_first) { 10639 if (!state.tmp.abbrev_trimmer.LAST_TO_FIRST) { 10640 state.tmp.abbrev_trimmer.LAST_TO_FIRST = {}; 10641 } 10642 for (var varname in this.parallel_last_to_first) { 10643 state.tmp.abbrev_trimmer.LAST_TO_FIRST[varname] = true; 10644 } 10645 } 10646 10647 //if (!state.tmp.just_looking) { 10648 // print(" flags="+JSON.stringify(state.tmp.group_context.tip, params)) 10649 //} 10650 } 10651 }; 10652 // 10653 // Paranoia. Assure that this init function is the first executed. 10654 execs = []; 10655 execs.push(func); 10656 this.execs = execs.concat(this.execs); 10657 10658 // "Special handling" for nodes that contain only 10659 // publisher and place, with no affixes. For such 10660 // nodes only, parallel publisher/place pairs 10661 // will be parsed out and properly joined, piggybacking on 10662 // join parameters set on cs:citation or cs:bibliography. 10663 if (this.strings["has-publisher-and-publisher-place"]) { 10664 // Pass variable string values to the closing 10665 // tag via a global, iff they conform to expectations. 10666 state.build["publisher-special"] = true; 10667 if (this.strings["subgroup-delimiter"]) { 10668 // Set the handling function only if name-delimiter 10669 // is set on the parent cs:citation or cs:bibliography 10670 // node. 10671 func = function (state, Item) { 10672 if (Item.publisher && Item["publisher-place"]) { 10673 var publisher_lst = Item.publisher.split(/;\s*/); 10674 var publisher_place_lst = Item["publisher-place"].split(/;\s*/); 10675 if (publisher_lst.length > 1 10676 && publisher_lst.length === publisher_place_lst.length) { 10677 state.publisherOutput = new CSL.PublisherOutput(state, this); 10678 state.publisherOutput["publisher-list"] = publisher_lst; 10679 state.publisherOutput["publisher-place-list"] = publisher_place_lst; 10680 } 10681 } 10682 }; 10683 this.execs.push(func); 10684 } 10685 } 10686 10687 if (this.juris) { 10688 // "Special handling" for jurisdiction macros 10689 // We try to instantiate these as standalone token lists. 10690 // If available, the token list is executed, 10691 // the result is written directly into output, 10692 // and control returns here. 10693 10694 // So we'll have something like this: 10695 // * expandMacro() in util_node.js flags juris- macros 10696 // on build. [DONE] 10697 // * Those are picked up here, and 10698 // - A runtime function attempts to fetch and instantiate 10699 // the macros in separate token lists under a segment 10700 // opened for the jurisdiction. We assume that the 10701 // jurisdiction has a full set of macros. That will need 10702 // to be enforced by validation. [DONE HERE, function is TODO] 10703 // - Success or failure is marked in a runtime flag object 10704 // (in citeproc.opt). [DONE] 10705 // - After the instantiation function comes a test, for 10706 // juris- macros only, which either runs diverted code, 10707 // or proceeds as per normal through the token list. [TODO] 10708 // I think that's all there is to it. 10709 10710 // Code for fetching an instantiating? 10711 10712 var choose_start = new CSL.Token("choose", CSL.START); 10713 CSL.Node.choose.build.call(choose_start, state, target); 10714 10715 var if_start = new CSL.Token("if", CSL.START); 10716 10717 func = (function (macroName) { 10718 return function (Item, item) { 10719 return CSL.INIT_JURISDICTION_MACROS(state, Item, item, macroName); 10720 } 10721 }(this.juris)); 10722 10723 if_start.tests ? {} : if_start.tests = []; 10724 if_start.tests.push(func); 10725 if_start.test = state.fun.match.any(if_start, state, if_start.tests); 10726 target.push(if_start); 10727 var text_node = new CSL.Token("text", CSL.SINGLETON); 10728 func = function (state, Item, item) { 10729 // This will run the juris- token list. 10730 var itemItem = Item; 10731 if (item && item["best-jurisdiction"] && this.juris === "juris-locator") { 10732 itemItem = item; 10733 } 10734 var next = 0; 10735 if (state.juris[itemItem["best-jurisdiction"]][this.juris]) { 10736 while (next < state.juris[itemItem["best-jurisdiction"]][this.juris].length) { 10737 next = CSL.tokenExec.call(state, state.juris[itemItem["best-jurisdiction"]][this.juris][next], Item, item); 10738 } 10739 } 10740 }; 10741 text_node.juris = this.juris; 10742 text_node.execs.push(func); 10743 target.push(text_node); 10744 10745 var if_end = new CSL.Token("if", CSL.END); 10746 CSL.Node["if"].build.call(if_end, state, target); 10747 var else_start = new CSL.Token("else", CSL.START); 10748 CSL.Node["else"].build.call(else_start, state, target); 10749 } 10750 } 10751 10752 if (this.tokentype === CSL.END) { 10753 10754 // Unbundle and print publisher lists 10755 // Same constraints on creating the necessary function here 10756 // as above. The full content of the group formatting token 10757 // is apparently not available on the closing tag here, 10758 // hence the global flag on state.build. 10759 if (state.build["publisher-special"]) { 10760 state.build["publisher-special"] = false; 10761 func = function (state) { 10762 if (state.publisherOutput) { 10763 state.publisherOutput.render(); 10764 state.publisherOutput = false; 10765 } 10766 }; 10767 this.execs.push(func); 10768 } 10769 10770 // quashnonfields 10771 func = function (state, Item, item) { 10772 if (!state.tmp.group_context.tip.condition) { 10773 if (state.output.current.tip.strings.suffix) { 10774 state.tmp.just_did_number = false; 10775 } 10776 } 10777 state.output.endTag(); 10778 if (this.realGroup) { 10779 var flags = state.tmp.group_context.pop(); 10780 if (flags.parallel_delimiter_override) { 10781 state.tmp.group_context.tip.parallel_delimiter_override = flags.parallel_delimiter_override; 10782 if (!state.tmp.just_looking && state.registry.registry[Item.id].master) { 10783 state.registry.registry[Item.id].parallel_delimiter_override = flags.parallel_delimiter_override; 10784 } 10785 } 10786 if (flags.parallel_delimiter_override_on_suppress) { 10787 state.tmp.group_context.tip.parallel_delimiter_override_on_suppress = flags.parallel_delimiter_override_on_suppress; 10788 } 10789 if (state.tmp.area === "bibliography_sort") { 10790 var citationNumberIdx = flags.done_vars.indexOf("citation-number"); 10791 if (this.strings.sort_direction && citationNumberIdx > -1 && state.tmp.group_context.length() == 1) { 10792 if (this.strings.sort_direction === CSL.DESCENDING) { 10793 state.bibliography_sort.opt.citation_number_sort_direction = CSL.DESCENDING; 10794 } else { 10795 state.bibliography_sort.opt.citation_number_sort_direction = CSL.ASCENDING; 10796 } 10797 flags.done_vars = flags.done_vars.slice(0, citationNumberIdx).concat(flags.done_vars.slice(citationNumberIdx + 1)) 10798 } 10799 } 10800 //var params = ["condition", "variable_success", "force_suppress","term_intended", "variable_attempt"] 10801 //if (!state.tmp.just_looking) { 10802 // print("POP parent="+JSON.stringify(state.tmp.group_context.tip, params)) 10803 // print(" flags="+JSON.stringify(flags, params)); 10804 //} 10805 if (flags.condition) { 10806 flags.force_suppress = CSL.EVALUATE_GROUP_CONDITION(state, flags); 10807 } 10808 if (state.tmp.group_context.tip.condition) { 10809 state.tmp.group_context.tip.force_suppress = flags.force_suppress; 10810 } 10811 10812 if (!flags.force_suppress && (flags.variable_success || (flags.term_intended && !flags.variable_attempt))) { 10813 if (!this.isJurisLocatorLabel) { 10814 state.tmp.group_context.tip.variable_success = true; 10815 } 10816 var blobs = state.output.current.value().blobs; 10817 var pos = state.output.current.value().blobs.length - 1; 10818 10819 if (!state.tmp.just_looking && (flags.non_parallel || flags.parallel_last || flags.parallel_first || flags.parallel_delimiter_override || flags.parallel_delimiter_override_on_suppress)) { 10820 // flags.parallel_last 10821 // flags.parallel_first 10822 10823 // Returns true ONLY if all variables listed on this group are repeats. 10824 var hasRepeat = state.parallel.checkRepeats(flags); 10825 if (hasRepeat) { 10826 if (blobs) { 10827 blobs.pop(); 10828 } 10829 } 10830 if (state.tmp.cite_index > 0 && (hasRepeat || (!flags.parallel_first && !flags.parallel_last && !flags.non_parallel))) { 10831 //state.sys.print(`${state.tmp.cite_index} ${JSON.stringify(state.tmp.suppress_repeats, null, 2)}`) 10832 var info = state.tmp.suppress_repeats[state.tmp.cite_index-1]; 10833 if (hasRepeat && flags.parallel_delimiter_override_on_suppress && (info.SIBLING || info.ORPHAN)) { 10834 state.output.queue.slice(-1)[0].parallel_delimiter = flags.parallel_delimiter_override_on_suppress; 10835 } else if (flags.parallel_delimiter_override && info.SIBLING) { 10836 state.output.queue.slice(-1)[0].parallel_delimiter = flags.parallel_delimiter_override; 10837 } 10838 } 10839 } 10840 } else { 10841 state.tmp.term_predecessor = flags.old_term_predecessor; 10842 state.tmp.group_context.tip.variable_attempt = flags.variable_attempt; 10843 if (flags.force_suppress && !state.tmp.group_context.tip.condition) { 10844 state.tmp.group_context.tip.variable_attempt = true; 10845 state.tmp.group_context.tip.variable_success = flags.variable_success_parent; 10846 } 10847 if (flags.force_suppress) { 10848 // 2019-04-15 10849 // This is removing variables done within the group we're leaveing from global 10850 // done_vars? How does that make sense? 10851 // Ah. This is a FAILURE. So removing from done_vars allows it to re-render 10852 // later in the cite if desired. 10853 // Currently no tests fail from removing the condition, but leaving it in. 10854 for (var i=0,ilen=flags.done_vars.length;i<ilen;i++) { 10855 var doneVar = flags.done_vars[i]; 10856 for (var j=0,jlen=state.tmp.done_vars.length; j<jlen; j++) { 10857 if (state.tmp.done_vars[j] === doneVar) { 10858 state.tmp.done_vars = state.tmp.done_vars.slice(0, j).concat(state.tmp.done_vars.slice(j+1)); 10859 } 10860 } 10861 } 10862 } 10863 if (state.output.current.value().blobs) { 10864 state.output.current.value().blobs.pop(); 10865 } 10866 } 10867 } 10868 }; 10869 this.execs.push(func); 10870 10871 if (this.juris) { 10872 var else_end = new CSL.Token("else", CSL.END); 10873 CSL.Node["else"].build.call(else_end, state, target); 10874 var choose_end = new CSL.Token("choose", CSL.END); 10875 CSL.Node.choose.build.call(choose_end, state, target); 10876 } 10877 } 10878 10879 if (this.tokentype === CSL.END) { 10880 if (!this.juris) { 10881 target.push(this); 10882 } 10883 if (state.build.substitute_level.value()) { 10884 state.build.substitute_level.replace((state.build.substitute_level.value() - 1)); 10885 } 10886 CSL.Util.substituteEnd.call(this, state, target); 10887 } 10888 } 10889}; 10890 10891/*global CSL: true */ 10892 10893CSL.Node["if"] = { 10894 build: function (state, target) { 10895 CSL.Conditions.TopNode.call(this, state, target); 10896 target.push(this); 10897 }, 10898 configure: function (state, pos) { 10899 CSL.Conditions.Configure.call(this, state, pos); 10900 } 10901}; 10902 10903 10904CSL.Node["conditions"] = { 10905 build: function (state) { 10906 if (this.tokentype === CSL.START) { 10907 state.tmp.conditions.addMatch(this.match); 10908 } 10909 if (this.tokentype === CSL.END) { 10910 state.tmp.conditions.matchCombine(); 10911 } 10912 } 10913}; 10914 10915CSL.Node["condition"] = { 10916 build: function (state) { 10917 if (this.tokentype === CSL.SINGLETON) { 10918 var test = state.fun.match[this.match](this, state, this.tests); 10919 state.tmp.conditions.addTest(test); 10920 } 10921 } 10922}; 10923 10924CSL.Conditions = {}; 10925 10926CSL.Conditions.TopNode = function (state) { 10927 var func; 10928 if (this.tokentype === CSL.START || this.tokentype === CSL.SINGLETON) { 10929 if (this.locale) { 10930 state.opt.lang = this.locale; 10931 } 10932 if (!this.tests || !this.tests.length) { 10933 // Set up the condition compiler with our current context 10934 state.tmp.conditions = new CSL.Conditions.Engine(state, this); 10935 } else { 10936 // The usual. 10937 this.test = state.fun.match[this.match](this, state, this.tests); 10938 } 10939 if (state.build.substitute_level.value() === 0) { 10940 func = function(state) { 10941 state.tmp.condition_counter++; 10942 } 10943 this.execs.push(func); 10944 } 10945 } 10946 if (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON) { 10947 if (state.build.substitute_level.value() === 0) { 10948 func = function (state) { 10949 state.tmp.condition_counter--; 10950 if (state.tmp.condition_lang_counter_arr.length > 0) { 10951 var counter = state.tmp.condition_lang_counter_arr.slice(-1)[0]; 10952 if (counter === state.tmp.condition_counter) { 10953 state.opt.lang = state.tmp.condition_lang_val_arr.pop(); 10954 state.tmp.condition_lang_counter_arr.pop(); 10955 } 10956 } 10957 if (this.locale_default) { 10958 // Open output tag with locale marker 10959 state.output.current.value().old_locale = this.locale_default; 10960 state.output.closeLevel("empty"); 10961 state.opt.lang = this.locale_default; 10962 } 10963 }; 10964 this.execs.push(func); 10965 } 10966 // closingjump 10967 func = function (state) { 10968 var next = this[state.tmp.jump.value()]; 10969 return next; 10970 }; 10971 this.execs.push(func); 10972 if (this.locale_default) { 10973 state.opt.lang = this.locale_default; 10974 } 10975 } 10976}; 10977 10978CSL.Conditions.Configure = function (state, pos) { 10979 if (this.tokentype === CSL.START) { 10980 // jump index on failure 10981 this.fail = state.configure.fail.slice(-1)[0]; 10982 this.succeed = this.next; 10983 state.configure.fail[(state.configure.fail.length - 1)] = pos; 10984 } else if (this.tokentype === CSL.SINGLETON) { 10985 // jump index on failure 10986 this.fail = this.next; 10987 this.succeed = state.configure.succeed.slice(-1)[0]; 10988 state.configure.fail[(state.configure.fail.length - 1)] = pos; 10989 } else { 10990 // jump index on success 10991 this.succeed = state.configure.succeed.slice(-1)[0]; 10992 this.fail = this.next; 10993 } 10994}; 10995 10996CSL.Conditions.Engine = function (state, token) { 10997 this.token = token; 10998 this.state = state; 10999}; 11000 11001CSL.Conditions.Engine.prototype.addTest = function (test) { 11002 this.token.tests ? {} : this.token.tests = []; 11003 this.token.tests.push(test); 11004}; 11005 11006CSL.Conditions.Engine.prototype.addMatch = function (match) { 11007 this.token.match = match; 11008}; 11009 11010CSL.Conditions.Engine.prototype.matchCombine = function () { 11011 this.token.test = this.state.fun.match[this.token.match](this.token, this.state, this.token.tests); 11012}; 11013 11014/*global CSL: true */ 11015 11016CSL.Node.info = { 11017 build: function (state) { 11018 if (this.tokentype === CSL.START) { 11019 state.build.skip = "info"; 11020 } else { 11021 state.build.skip = false; 11022 } 11023 } 11024}; 11025 11026 11027/*global CSL: true */ 11028 11029CSL.Node.institution = { 11030 build: function (state, target) { 11031 if ([CSL.SINGLETON, CSL.START].indexOf(this.tokentype) > -1) { 11032 11033 var func = function (state) { 11034 if ("string" === typeof this.strings.delimiter) { 11035 state.tmp.institution_delimiter = this.strings.delimiter; 11036 } else { 11037 state.tmp.institution_delimiter = state.tmp.name_delimiter; 11038 } 11039 11040 // This is the same code for the same result as in node_name.js, 11041 // but when cs:institution comes on stream, it may produce 11042 // different results. 11043 if ("text" === state.inheritOpt(this, "and")) { 11044 this.and_term = state.getTerm("and", "long", 0); 11045 } else if ("symbol" === state.inheritOpt(this, "and")) { 11046 if (state.opt.development_extensions.expect_and_symbol_form) { 11047 this.and_term = state.getTerm("and", "symbol", 0); 11048 } else { 11049 this.and_term = "&"; 11050 } 11051 } else if ("none" === state.inheritOpt(this, "and")) { 11052 this.and_term = state.tmp.institution_delimiter; 11053 } 11054 if ("undefined" === typeof this.and_term && state.tmp.and_term) { 11055 // this.and_term = state.getTerm("and", "long", 0); 11056 this.and_term = state.tmp.and_term; 11057 } 11058 if (CSL.STARTSWITH_ROMANESQUE_REGEXP.test(this.and_term)) { 11059 this.and_prefix_single = " "; 11060 this.and_prefix_multiple = ", "; 11061 if ("string" === typeof state.tmp.institution_delimiter) { 11062 this.and_prefix_multiple = state.tmp.institution_delimiter; 11063 } 11064 this.and_suffix = " "; 11065 } else { 11066 this.and_prefix_single = ""; 11067 this.and_prefix_multiple = ""; 11068 this.and_suffix = ""; 11069 } 11070 if (state.inheritOpt(this, "delimiter-precedes-last") === "always") { 11071 this.and_prefix_single = state.tmp.institution_delimiter; 11072 } else if (state.inheritOpt(this, "delimiter-precedes-last") === "never") { 11073 // Slightly fragile: could test for charset here to make 11074 // this more certain. 11075 if (this.and_prefix_multiple) { 11076 this.and_prefix_multiple = " "; 11077 } 11078 } 11079 11080 this.and = {}; 11081 if ("undefined" !== typeof this.and_term) { 11082 state.output.append(this.and_term, "empty", true); 11083 this.and.single = state.output.pop(); 11084 this.and.single.strings.prefix = this.and_prefix_single; 11085 this.and.single.strings.suffix = this.and_suffix; 11086 state.output.append(this.and_term, "empty", true); 11087 this.and.multiple = state.output.pop(); 11088 this.and.multiple.strings.prefix = this.and_prefix_multiple; 11089 this.and.multiple.strings.suffix = this.and_suffix; 11090 } else if ("undefined" !== this.strings.delimiter) { 11091 this.and.single = new CSL.Blob(state.tmp.institution_delimiter); 11092 this.and.single.strings.prefix = ""; 11093 this.and.single.strings.suffix = ""; 11094 this.and.multiple = new CSL.Blob(state.tmp.institution_delimiter); 11095 this.and.multiple.strings.prefix = ""; 11096 this.and.multiple.strings.suffix = ""; 11097 } 11098 state.nameOutput.institution = this; 11099 }; 11100 this.execs.push(func); 11101 } 11102 target.push(this); 11103 }, 11104 configure: function (state) { 11105 if ([CSL.SINGLETON, CSL.START].indexOf(this.tokentype) > -1) { 11106 state.build.has_institution = true; 11107 } 11108 } 11109}; 11110 11111/*global CSL: true */ 11112 11113CSL.Node["institution-part"] = { 11114 build: function (state, target) { 11115 var func; 11116 if ("long" === this.strings.name) { 11117 if (this.strings["if-short"]) { 11118 func = function (state) { 11119 state.nameOutput.institutionpart["long-with-short"] = this; 11120 }; 11121 } else { 11122 func = function (state) { 11123 state.nameOutput.institutionpart["long"] = this; 11124 }; 11125 } 11126 } else if ("short" === this.strings.name) { 11127 func = function (state) { 11128 state.nameOutput.institutionpart["short"] = this; 11129 }; 11130 } 11131 this.execs.push(func); 11132 target.push(this); 11133 } 11134}; 11135 11136/*global CSL: true */ 11137 11138CSL.Node.key = { 11139 build: function (state, target) { 11140 11141 target = state[state.build.root + "_sort"].tokens; 11142 11143 var func; 11144 var debug = false; 11145 var start_key = new CSL.Token("key", CSL.START); 11146 11147 state.tmp.root = state.build.root; 11148 11149 // The params object for build and runtime (tmp) really shouldn't have been separated. 11150 // Oh, well. 11151 start_key.strings["et-al-min"] = state.inheritOpt(this, "et-al-min"); 11152 start_key.strings["et-al-use-first"] = state.inheritOpt(this, "et-al-use-first"); 11153 start_key.strings["et-al-use-last"] = state.inheritOpt(this, "et-al-use-last"); 11154 11155 11156 // initialize done vars 11157 func = function (state) { 11158 state.tmp.done_vars = []; 11159 }; 11160 start_key.execs.push(func); 11161 11162 // initialize output queue 11163 func = function (state) { 11164 state.output.openLevel("empty"); 11165 }; 11166 start_key.execs.push(func); 11167 11168 // sort direction 11169 var sort_direction = []; 11170 if (this.strings.sort_direction === CSL.DESCENDING) { 11171 //print("sort: descending on "+state.tmp.area); 11172 sort_direction.push(1); 11173 sort_direction.push(-1); 11174 } else { 11175 //print("sort: ascending"); 11176 sort_direction.push(-1); 11177 sort_direction.push(1); 11178 } 11179 state[state.build.area].opt.sort_directions.push(sort_direction); 11180 11181 if (CSL.DATE_VARIABLES.indexOf(this.variables[0]) > -1) { 11182 state.build.date_key = true; 11183 } 11184 11185 // et al init 11186 func = function (state) { 11187 state.tmp.sort_key_flag = true; 11188 //print("== key node function =="); 11189 if (state.inheritOpt(this, "et-al-min")) { 11190 state.tmp["et-al-min"] = state.inheritOpt(this, "et-al-min"); 11191 } 11192 if (state.inheritOpt(this, "et-al-use-first")) { 11193 state.tmp["et-al-use-first"] = state.inheritOpt(this, "et-al-use-first"); 11194 } 11195 if ("boolean" === typeof state.inheritOpt(this, "et-al-use-last")) { 11196 state.tmp["et-al-use-last"] = state.inheritOpt(this, "et-al-use-last"); 11197 //print(" set tmp et-al-use-last: "+this.strings["et-al-use-last"]) 11198 } 11199 }; 11200 start_key.execs.push(func); 11201 target.push(start_key); 11202 11203 // 11204 // ops to initialize the key's output structures 11205 if (this.variables.length) { 11206 var variable = this.variables[0]; 11207 if (CSL.NAME_VARIABLES.indexOf(variable) > -1) { 11208 // 11209 // Start tag 11210 var names_start_token = new CSL.Token("names", CSL.START); 11211 names_start_token.tokentype = CSL.START; 11212 names_start_token.variables = this.variables; 11213 CSL.Node.names.build.call(names_start_token, state, target); 11214 // 11215 // Name tag 11216 var name_token = new CSL.Token("name", CSL.SINGLETON); 11217 name_token.tokentype = CSL.SINGLETON; 11218 name_token.strings["name-as-sort-order"] = "all"; 11219 name_token.strings["sort-separator"] = " "; 11220 name_token.strings["et-al-use-last"] = state.inheritOpt(this, "et-al-use-last"); 11221 name_token.strings["et-al-min"] = state.inheritOpt(this, "et-al-min"); 11222 name_token.strings["et-al-use-first"] = state.inheritOpt(this, "et-al-use-first"); 11223 CSL.Node.name.build.call(name_token, state, target); 11224 // 11225 // Institution tag 11226 var institution_token = new CSL.Token("institution", CSL.SINGLETON); 11227 institution_token.tokentype = CSL.SINGLETON; 11228 CSL.Node.institution.build.call(institution_token, state, target); 11229 // 11230 // End tag 11231 var names_end_token = new CSL.Token("names", CSL.END); 11232 names_end_token.tokentype = CSL.END; 11233 CSL.Node.names.build.call(names_end_token, state, target); 11234 } else { 11235 var single_text = new CSL.Token("text", CSL.SINGLETON); 11236 single_text.strings.sort_direction = this.strings.sort_direction; 11237 single_text.dateparts = this.dateparts; 11238 if (CSL.NUMERIC_VARIABLES.indexOf(variable) > -1) { 11239 // citation-number is virtualized. As a sort key it has no effect on registry 11240 // sort order per se, but if set to DESCENDING, it reverses the sequence of numbers representing 11241 // bib entries. 11242 if (variable === "citation-number") { 11243 func = function (state, Item) { 11244 if (state.tmp.area === "bibliography_sort") { 11245 if (this.strings.sort_direction === CSL.DESCENDING) { 11246 state.bibliography_sort.opt.citation_number_sort_direction = CSL.DESCENDING; 11247 } else { 11248 state.bibliography_sort.opt.citation_number_sort_direction = CSL.ASCENDING; 11249 } 11250 } 11251 if (state.tmp.area === "citation_sort" && state.bibliography_sort.tmp.citation_number_map) { 11252 var num = state.bibliography_sort.tmp.citation_number_map[state.registry.registry[Item.id].seq]; 11253 } else { 11254 var num = state.registry.registry[Item.id].seq; 11255 } 11256 if (num) { 11257 // Code currently in util_number.js 11258 num = CSL.Util.padding("" + num); 11259 } 11260 state.output.append(num, this); 11261 }; 11262 } else { 11263 func = function (state, Item) { 11264 var num = false; 11265 num = Item[variable]; 11266 // XXX What if this is NaN? 11267 if (num) { 11268 // Code currently in util_number.js 11269 num = CSL.Util.padding(num); 11270 } 11271 state.output.append(num, this); 11272 }; 11273 } 11274 } else if (variable === "citation-label") { 11275 func = function (state, Item) { 11276 var trigraph = state.getCitationLabel(Item); 11277 state.output.append(trigraph, this); 11278 }; 11279 } else if (CSL.DATE_VARIABLES.indexOf(variable) > -1) { 11280 func = CSL.dateAsSortKey; 11281 single_text.variables = this.variables; 11282 } else if ("title" === variable) { 11283 var abbrevfam = "title"; 11284 var abbrfall = false; 11285 var altvar = false; 11286 var transfall = true; 11287 func = state.transform.getOutputFunction(this.variables, abbrevfam, abbrfall, altvar, transfall); 11288 } else if ("court-class" === variable) { 11289 func = function(state, Item, item) { 11290 CSL.INIT_JURISDICTION_MACROS(state, Item, item, "juris-main") 11291 // true is for sortKey mode 11292 var cls = CSL.GET_COURT_CLASS(state, Item, true); 11293 state.output.append(cls, "empty"); 11294 } 11295 } else { 11296 func = function (state, Item) { 11297 var varval = Item[variable]; 11298 state.output.append(varval, "empty"); 11299 }; 11300 } 11301 single_text.execs.push(func); 11302 target.push(single_text); 11303 } 11304 } else { // macro 11305 // 11306 // if it's not a variable, it's a macro 11307 var token = new CSL.Token("text", CSL.SINGLETON); 11308 token.strings.sort_direction = this.strings.sort_direction; 11309 token.postponed_macro = this.postponed_macro; 11310 CSL.expandMacro.call(state, token, target); 11311 } 11312 // 11313 // ops to output the key string result to an array go 11314 // on the closing "key" tag before it is pushed. 11315 // Do not close the level. 11316 var end_key = new CSL.Token("key", CSL.END); 11317 11318 // Eliminated at revision 1.0.159. 11319 // Was causing non-fatal error "wanted empty but found group". 11320 // Possible contributor to weird "PAGES" bug? 11321 //func = function (state, Item) { 11322 //state.output.closeLevel("empty"); 11323 //}; 11324 //end_key.execs.push(func); 11325 11326 // store key for use 11327 func = function (state) { 11328 var keystring = state.output.string(state, state.output.queue); 11329 if (state.sys.normalizeUnicode) { 11330 keystring = state.sys.normalizeUnicode(keystring); 11331 } 11332 keystring = keystring ? (keystring.split(" ").join(state.opt.sort_sep) + state.opt.sort_sep) : ""; 11333 //SNIP-START 11334 if (debug) { 11335 CSL.debug("keystring: " + keystring + " " + typeof keystring); 11336 } 11337 //print("keystring: (" + keystring + ") " + typeof keystring + " " + state.tmp.area); 11338 //SNIP-END 11339 //state.sys.print("keystring: (" + keystring + ") " + typeof keystring + " " + state.tmp.area); 11340 if ("" === keystring) { 11341 keystring = undefined; 11342 } 11343 if ("string" !== typeof keystring) { 11344 keystring = undefined; 11345 //state.tmp.empty_date = false; 11346 } 11347 state[state[state.tmp.area].root + "_sort"].keys.push(keystring); 11348 state.tmp.value = []; 11349 }; 11350 end_key.execs.push(func); 11351 11352 // Set year-suffix key on anything that looks like a date 11353 if (state.build.date_key) { 11354 if (state.build.area === "citation" && state.build.extension === "_sort") { 11355 // ascending sort always 11356 state[state.build.area].opt.sort_directions.push([-1,1]); 11357 func = function (state, Item) { 11358 // year-suffix Key 11359 var year_suffix = state.registry.registry[Item.id].disambig.year_suffix; 11360 if (!year_suffix) { 11361 year_suffix = 0; 11362 } 11363 var key = CSL.Util.padding("" + year_suffix); 11364 state[state.tmp.area].keys.push(key); 11365 }; 11366 end_key.execs.push(func); 11367 } 11368 state.build.date_key = false; 11369 } 11370 11371 // reset key params 11372 func = function (state) { 11373 // state.tmp.name_quash = new Object(); 11374 11375 // XXX This should work, should be necessary, but doesn't and isn't. 11376 //state.output.closeLevel("empty"); 11377 11378 state.tmp["et-al-min"] = undefined; 11379 state.tmp["et-al-use-first"] = undefined; 11380 state.tmp["et-al-use-last"] = undefined; 11381 state.tmp.sort_key_flag = false; 11382 }; 11383 end_key.execs.push(func); 11384 target.push(end_key); 11385 } 11386}; 11387 11388/*global CSL: true */ 11389 11390CSL.Node.label = { 11391 build: function (state, target) { 11392 11393 if (this.strings.term) { 11394 // Non-names labels 11395 var func = function (state, Item, item) { 11396 // Must accomplish this without touching strings 11397 // shared with the calling application: "sub verbo" 11398 // and "sub-verbo" must both pass, as they stand. 11399 //if (item && item.label === "sub verbo") { 11400 // item.label = "sub-verbo"; 11401 //} 11402 // This is abstracted away, because the same 11403 // logic must be run in cs:names. 11404 var termtxt = CSL.evaluateLabel(this, state, Item, item); 11405 if (item && this.strings.term === "locator") { 11406 11407 item.section_form_override = this.strings.form; 11408 11409 } 11410 if (termtxt) { 11411 state.tmp.group_context.tip.term_intended = true; 11412 } 11413 CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, termtxt, null, this); 11414 if (termtxt.indexOf("%s") === -1) { 11415 // ^ Suppress output here if we have an embedded term 11416 if (this.strings.capitalize_if_first) { 11417 if (!state.tmp.term_predecessor && !(state.opt["class"] === "in-text" && state.tmp.area === "citation")) { 11418 termtxt = CSL.Output.Formatters["capitalize-first"](state, termtxt); 11419 } 11420 } 11421 state.output.append(termtxt, this); 11422 } 11423 }; 11424 this.execs.push(func); 11425 } else { 11426 if (!this.strings.form) { 11427 this.strings.form = "long"; 11428 } 11429 // Names labels 11430 // Picked up in names END 11431 var namevars = state.build.names_variables[state.build.names_variables.length-1]; 11432 var namelabels = state.build.name_label[state.build.name_label.length-1]; 11433 for (var i = 0, ilen = namevars.length; i < ilen; i += 1) { 11434 if (!namelabels[namevars[i]]) { 11435 namelabels[namevars[i]] = {}; 11436 } 11437 } 11438 if (!state.build.name_flag) { 11439 for (var i = 0, ilen = namevars.length; i < ilen; i += 1) { 11440 namelabels[namevars[i]].before = this; 11441 } 11442 } else { 11443 for (var i = 0, ilen = namevars.length; i < ilen; i += 1) { 11444 namelabels[namevars[i]].after = this; 11445 } 11446 } 11447 } 11448 target.push(this); 11449 } 11450}; 11451 11452/*global CSL: true */ 11453 11454CSL.Node.layout = { 11455 build: function (state, target) { 11456 var func, prefix_token, suffix_token, tok; 11457 11458 function setSuffix() { 11459 if (state.build.area === "bibliography") { 11460 suffix_token = new CSL.Token("text", CSL.SINGLETON); 11461 func = function(state) { 11462 // Suppress suffix on all but the last item in bibliography parallels 11463 if (!state.tmp.parallel_and_not_last) { 11464 var suffix; 11465 if (state.tmp.cite_affixes[state.tmp.area][state.tmp.last_cite_locale]) { 11466 suffix = state.tmp.cite_affixes[state.tmp.area][state.tmp.last_cite_locale].suffix; 11467 } else { 11468 suffix = state.bibliography.opt.layout_suffix; 11469 } 11470 11471 // If @display is used, layout suffix is placed on the last 11472 // immediate child of the layout, which we assume will be a 11473 // @display group node. 11474 var topblob = state.output.current.value(); 11475 if (state.opt.using_display) { 11476 topblob.blobs[topblob.blobs.length-1].strings.suffix = suffix; 11477 } else { 11478 topblob.strings.suffix = suffix; 11479 } 11480 } 11481 if (state.bibliography.opt["second-field-align"]) { 11482 // closes bib_other 11483 state.output.endTag("bib_other"); 11484 } 11485 }; 11486 suffix_token.execs.push(func); 11487 target.push(suffix_token); 11488 } 11489 } 11490 11491 if (this.tokentype === CSL.START) { 11492 11493 if (this.locale_raw) { 11494 state.build.current_default_locale = this.locale_raw; 11495 } else { 11496 state.build.current_default_locale = state.opt["default-locale"]; 11497 } 11498 11499 func = function (state, Item, item) { 11500 if (state.opt.development_extensions.apply_citation_wrapper 11501 && state.sys.wrapCitationEntry 11502 && !state.tmp.just_looking 11503 && Item.system_id 11504 && state.tmp.area === "citation") { 11505 11506 var cite_entry = new CSL.Token("group", CSL.START); 11507 cite_entry.decorations = [["@cite", "entry"]]; 11508 state.output.startTag("cite_entry", cite_entry); 11509 state.output.current.value().item_id = Item.system_id; 11510 if (item) { 11511 state.output.current.value().locator_txt = item.locator_txt; 11512 state.output.current.value().suffix_txt = item.suffix_txt; 11513 } 11514 } 11515 }; 11516 this.execs.push(func); 11517 } 11518 11519 // XXX Works, but using state.tmp looks wrong here? We're in the build layer ... 11520 if (this.tokentype === CSL.START && !state.tmp.cite_affixes[state.build.area]) { 11521 // 11522 // done_vars is used to prevent the repeated 11523 // rendering of variables 11524 // 11525 // initalize done vars 11526 func = function (state, Item, item) { 11527 11528 state.tmp.done_vars = []; 11529 if (item && item["author-only"]) { 11530 state.tmp.done_vars.push("locator"); 11531 } 11532 if (state.opt.suppressedJurisdictions[Item["country"]] 11533 && Item["country"] 11534 && ["treaty", "patent"].indexOf(Item.type) === -1) { 11535 11536 state.tmp.done_vars.push("country"); 11537 } 11538 if (!state.tmp.just_looking && state.registry.registry[Item.id] && state.registry.registry[Item.id].parallel) { 11539 state.tmp.done_vars.push("first-reference-note-number"); 11540 } 11541 // trimmer is not available in getAmbiguousCite 11542 if (!state.tmp.just_looking && state.tmp.abbrev_trimmer && Item.jurisdiction) { 11543 for (var field in state.tmp.abbrev_trimmer.QUASHES[Item.jurisdiction]) { 11544 state.tmp.done_vars.push(field); 11545 } 11546 } 11547 11548 //CSL.debug(" === init rendered_name === "); 11549 state.tmp.rendered_name = false; 11550 }; 11551 this.execs.push(func); 11552 // set opt delimiter 11553 func = function (state) { 11554 // just in case 11555 state.tmp.sort_key_flag = false; 11556 }; 11557 this.execs.push(func); 11558 11559 // reset nameset counter [all nodes] 11560 func = function (state) { 11561 state.tmp.nameset_counter = 0; 11562 }; 11563 this.execs.push(func); 11564 11565 func = function (state, Item) { 11566 var tok = new CSL.Token(); 11567 state.output.openLevel(tok); 11568 }; 11569 this.execs.push(func); 11570 target.push(this); 11571 11572 if (state.build.area === "citation") { 11573 prefix_token = new CSL.Token("text", CSL.SINGLETON); 11574 func = function (state, Item, item) { 11575 if (item && item.prefix) { 11576 var prefix = CSL.checkPrefixSpaceAppend(state, item.prefix); 11577 if (!state.tmp.just_looking) { 11578 prefix = state.output.checkNestedBrace.update(prefix); 11579 } 11580 var ignorePredecessor = CSL.checkIgnorePredecessor(state, prefix); 11581 state.output.append(prefix, this, false, ignorePredecessor); 11582 } 11583 }; 11584 prefix_token.execs.push(func); 11585 target.push(prefix_token); 11586 } 11587 } 11588 11589 // Cast token to be used in one of the configurations below. 11590 var my_tok; 11591 if (this.locale_raw) { 11592 my_tok = new CSL.Token("dummy", CSL.START); 11593 my_tok.locale = this.locale_raw; 11594 my_tok.strings.delimiter = this.strings.delimiter; 11595 my_tok.strings.suffix = this.strings.suffix; 11596 if (!state.tmp.cite_affixes[state.build.area]) { 11597 state.tmp.cite_affixes[state.build.area] = {}; 11598 } 11599 } 11600 11601 if (this.tokentype === CSL.START) { 11602 state.build.layout_flag = true; 11603 11604 // Only run the following once, to set up the final layout node ... 11605 if (!this.locale_raw) { 11606 // 11607 // save out decorations for flipflop processing [final node only] 11608 // 11609 state[state.tmp.area].opt.topdecor = [this.decorations]; 11610 state[(state.tmp.area + "_sort")].opt.topdecor = [this.decorations]; 11611 11612 state[state.build.area].opt.layout_prefix = this.strings.prefix; 11613 state[state.build.area].opt.layout_suffix = this.strings.suffix; 11614 state[state.build.area].opt.layout_delimiter = this.strings.delimiter; 11615 11616 state[state.build.area].opt.layout_decorations = this.decorations; 11617 11618 // Only do this if we're running conditionals 11619 if (state.tmp.cite_affixes[state.build.area]) { 11620 // if build_layout_locale_flag is true, 11621 // write cs:else START to the token list. 11622 tok = new CSL.Token("else", CSL.START); 11623 CSL.Node["else"].build.call(tok, state, target); 11624 } 11625 11626 } // !this.locale_raw 11627 11628 // Conditionals 11629 if (this.locale_raw) { 11630 if (!state.build.layout_locale_flag) { 11631 // if layout_locale_flag is untrue, 11632 // write cs:choose START and cs:if START 11633 // to the token list. 11634 var choose_tok = new CSL.Token("choose", CSL.START); 11635 CSL.Node.choose.build.call(choose_tok, state, target); 11636 my_tok.name = "if"; 11637 CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw); 11638 CSL.Node["if"].build.call(my_tok, state, target); 11639 } else { 11640 // if build_layout_locale_flag is true, 11641 // write cs:else-if START to the token list. 11642 my_tok.name = "else-if"; 11643 CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw); 11644 CSL.Node["else-if"].build.call(my_tok, state, target); 11645 } 11646 // cite_affixes for this node 11647 state.tmp.cite_affixes[state.build.area][my_tok.locale] = {}; 11648 state.tmp.cite_affixes[state.build.area][my_tok.locale].delimiter = this.strings.delimiter; 11649 state.tmp.cite_affixes[state.build.area][my_tok.locale].suffix = this.strings.suffix; 11650 } 11651 } 11652 if (this.tokentype === CSL.END) { 11653 if (this.locale_raw) { 11654 setSuffix(); 11655 if (!state.build.layout_locale_flag) { 11656 // If layout_locale_flag is untrue, write cs:if END 11657 // to the token list. 11658 my_tok.name = "if"; 11659 my_tok.tokentype = CSL.END; 11660 CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw); 11661 CSL.Node["if"].build.call(my_tok, state, target); 11662 state.build.layout_locale_flag = true; 11663 } else { 11664 // If layout_locale_flag is true, write cs:else-if END 11665 // to the token list. 11666 my_tok.name = "else-if"; 11667 my_tok.tokentype = CSL.END; 11668 CSL.Attributes["@locale-internal"].call(my_tok, state, this.locale_raw); 11669 CSL.Node["else-if"].build.call(my_tok, state, target); 11670 } 11671 } 11672 if (!this.locale_raw) { 11673 setSuffix(); 11674 // Only add this if we're running conditionals 11675 if (state.tmp.cite_affixes[state.build.area]) { 11676 // If layout_locale_flag is true, write cs:else END 11677 // and cs:choose END to the token list. 11678 if (state.build.layout_locale_flag) { 11679 tok = new CSL.Token("else", CSL.END); 11680 CSL.Node["else"].build.call(tok, state, target); 11681 tok = new CSL.Token("choose", CSL.END); 11682 CSL.Node.choose.build.call(tok, state, target); 11683 } 11684 } 11685 state.build_layout_locale_flag = true; 11686 if (state.build.area === "citation") { 11687 suffix_token = new CSL.Token("text", CSL.SINGLETON); 11688 func = function (state, Item, item) { 11689 var sp; 11690 if (item && item.suffix) { 11691 var suffix = CSL.checkSuffixSpacePrepend(state, item.suffix); 11692 if (!state.tmp.just_looking) { 11693 suffix = state.output.checkNestedBrace.update(suffix); 11694 } 11695 state.output.append((suffix), this); 11696 } 11697 }; 11698 suffix_token.execs.push(func); 11699 target.push(suffix_token); 11700 } 11701 11702 // Closes wrapper token 11703 func = function (state) { 11704 state.output.closeLevel(); 11705 }; 11706 this.execs.push(func); 11707 func = function (state, Item) { 11708 if (state.opt.development_extensions.apply_citation_wrapper 11709 && state.sys.wrapCitationEntry 11710 && !state.tmp.just_looking 11711 && Item.system_id 11712 && state.tmp.area === "citation") { 11713 11714 state.output.endTag(); // closes citation link wrapper 11715 } 11716 }; 11717 this.execs.push(func); 11718 target.push(this); 11719 state.build.layout_flag = false; 11720 state.build.layout_locale_flag = false; 11721 } // !this.layout_raw 11722 } 11723 } 11724}; 11725 11726/*global CSL: true */ 11727 11728CSL.Node.macro = { 11729 build: function () {} 11730}; 11731 11732/*global CSL: true */ 11733 11734CSL.Node.alternative = { 11735 build: function (state, target) { 11736 if (this.tokentype === CSL.START) { 11737 11738 var choose_tok = new CSL.Token("choose", CSL.START); 11739 CSL.Node["choose"].build.call(choose_tok, state, target); 11740 11741 var if_tok = new CSL.Token("if", CSL.START); 11742 CSL.Attributes["@alternative-node-internal"].call(if_tok, state); 11743 CSL.Node["if"].build.call(if_tok, state, target); 11744 11745 var func = function(state, Item) { 11746 11747 state.tmp.oldItem = Item; 11748 state.tmp.oldLang = state.opt.lang; 11749 state.tmp.abort_alternative = true; 11750 11751 if (Item["language-name"] && Item["language-name-original"]) { 11752 11753 var newItem = JSON.parse(JSON.stringify(Item)); 11754 11755 newItem.language = newItem["language-name"]; 11756 var langspec = CSL.localeResolve(newItem.language, state.opt["default-locale"][0]); 11757 11758 if (state.opt.multi_layout) { 11759 for (var i in state.opt.multi_layout) { 11760 var locale_list = state.opt.multi_layout[i]; 11761 var gotlang = false; 11762 for (var j in locale_list) { 11763 var tryspec = locale_list[j]; 11764 if (langspec.best === tryspec.best || langspec.base === tryspec.base || langspec.bare === tryspec.bare) { 11765 gotlang = locale_list[0].best; 11766 break; 11767 } 11768 } 11769 if (!gotlang) { 11770 gotlang = state.opt["default-locale"][0]; 11771 } 11772 state.opt.lang = gotlang; 11773 } 11774 } 11775 11776 for (var key in newItem) { 11777 if (["id", "type", "language", "multi"].indexOf(key) === -1 && key.slice(0, 4) !== "alt-") { 11778 if (newItem.multi && newItem.multi._keys[key]) { 11779 var deleteme = true; 11780 for (var lang in newItem.multi._keys[key]) { 11781 if (langspec.bare === lang.replace(/^([a-zA-Z]+).*/, "$1")) { 11782 deleteme = false; 11783 break; 11784 } 11785 } 11786 if (deleteme) { 11787 delete newItem[key]; 11788 } 11789 } else { 11790 delete newItem[key]; 11791 } 11792 } 11793 } 11794 for (var key in newItem) { 11795 if (key.slice(0, 4) === "alt-") { 11796 newItem[key.slice(4)] = newItem[key]; 11797 state.tmp.abort_alternative = false; 11798 } else { 11799 if (newItem.multi && newItem.multi._keys) { 11800 if (!newItem["alt-" + key] && newItem.multi._keys[key]) { 11801 if (newItem.multi._keys[key][langspec.best]) { 11802 newItem[key] = newItem.multi._keys[key][langspec.best]; 11803 state.tmp.abort_alternative = false; 11804 } else if (newItem.multi._keys[key][langspec.base]) { 11805 newItem[key] = newItem.multi._keys[key][langspec.base]; 11806 state.tmp.abort_alternative = false; 11807 } else if (newItem.multi._keys[key][langspec.bare]) { 11808 newItem[key] = newItem.multi._keys[key][langspec.bare]; 11809 state.tmp.abort_alternative = false; 11810 } 11811 } 11812 } 11813 } 11814 } 11815 } 11816 11817 state.output.openLevel(this); 11818 state.registry.refhash[Item.id] = newItem; 11819 state.nameOutput = new CSL.NameOutput(state, newItem); 11820 }; 11821 this.execs.push(func); 11822 target.push(this); 11823 11824 var choose_tok = new CSL.Token("choose", CSL.START); 11825 CSL.Node["choose"].build.call(choose_tok, state, target); 11826 11827 var if_tok = new CSL.Token("if", CSL.START); 11828 CSL.Attributes["@alternative-node-internal"].call(if_tok, state); 11829 var func = function(state) { 11830 state.tmp.abort_alternative = true; 11831 } 11832 if_tok.execs.push(func); 11833 CSL.Node["if"].build.call(if_tok, state, target); 11834 11835 } else if (this.tokentype === CSL.END) { 11836 11837 var if_tok = new CSL.Token("if", CSL.END); 11838 CSL.Node["if"].build.call(if_tok, state, target); 11839 11840 var choose_tok = new CSL.Token("choose", CSL.END); 11841 CSL.Node["choose"].build.call(choose_tok, state, target); 11842 11843 var func = function(state, Item) { 11844 state.output.closeLevel(); 11845 state.registry.refhash[Item.id] = state.tmp.oldItem; 11846 state.opt.lang = state.tmp.oldLang; 11847 state.nameOutput = new CSL.NameOutput(state, state.tmp.oldItem); 11848 state.tmp.abort_alternative = false; 11849 }; 11850 this.execs.push(func); 11851 target.push(this); 11852 11853 var if_tok = new CSL.Token("if", CSL.END); 11854 CSL.Node["if"].build.call(if_tok, state, target); 11855 11856 var choose_tok = new CSL.Token("choose", CSL.END); 11857 CSL.Node["choose"].build.call(choose_tok, state, target); 11858 11859 } 11860 } 11861}; 11862 11863CSL.Node["alternative-text"] = { 11864 build: function (state, target) { 11865 if (this.tokentype === CSL.SINGLETON) { 11866 // do stuff 11867 var func = function(state, Item) { 11868 var Item = state.refetchItem(Item.id); 11869 CSL.getCite.call(state, Item); 11870 }; 11871 this.execs.push(func); 11872 } 11873 target.push(this); 11874 } 11875}; 11876 11877 11878 11879/*global CSL: true */ 11880 11881CSL.NameOutput = function(state, Item, item) { 11882 this.debug = false; 11883 this.state = state; 11884 //SNIP-START 11885 if (this.debug) { 11886 this.state.sys.print("(1)"); 11887 } 11888 //SNIP-END 11889 this.Item = Item; 11890 this.item = item; 11891 this.nameset_base = 0; 11892 this.etal_spec = {}; 11893 this._first_creator_variable = false; 11894 this._please_chop = false; 11895}; 11896 11897CSL.NameOutput.prototype.init = function (names) { 11898 this.requireMatch = names.requireMatch; 11899 if (this.state.tmp.term_predecessor) { 11900 this.state.tmp.subsequent_author_substitute_ok = false; 11901 } 11902 if (this.nameset_offset) { 11903 this.nameset_base = this.nameset_base + this.nameset_offset; 11904 } 11905 this.nameset_offset = 0; 11906 this.names = names; 11907 this.variables = names.variables; 11908 11909 this.state.tmp.value = []; 11910 this.state.tmp.rendered_name = []; 11911 this.state.tmp.label_blob = false; 11912 this.state.tmp.etal_node = false; 11913 this.state.tmp.etal_term = false; 11914 for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) { 11915 if (this.Item[this.variables[i]] && this.Item[this.variables[i]].length) { 11916 this.state.tmp.value = this.state.tmp.value.concat(this.Item[this.variables[i]]); 11917 } 11918 } 11919 this["et-al"] = undefined; 11920 // REMOVE THIS 11921 this["with"] = undefined; 11922 11923 this.name = undefined; 11924 // long, long-with-short, short 11925 this.institutionpart = {}; 11926 // family, given 11927 //this.namepart = {}; 11928 // before, after 11929 //this.label = {}; 11930 11931 this.state.tmp.group_context.tip.variable_attempt = true; 11932 11933 this.labelVariable = this.variables[0]; 11934 11935 if (!this.state.tmp.value.length) { 11936 return; 11937 } 11938 11939 // Abort and proceed to the next substitution if a match is required, 11940 // two variables are called, and they do not match. 11941 var checkCommonTerm = this.checkCommonAuthor(this.requireMatch); 11942 if (checkCommonTerm) { 11943 this.state.tmp.can_substitute.pop(); 11944 this.state.tmp.can_substitute.push(true); 11945 //this.state.tmp.group_context.mystack[this.state.tmp.group_context.mystack.length-1].variable_success = false; 11946 for (var i in this.variables) { 11947 var idx = this.state.tmp.done_vars.indexOf(this.variables[i]); 11948 if (idx > -1) { 11949 this.state.tmp.done_vars = this.state.tmp.done_vars.slice(0, idx).concat(this.state.tmp.done_vars.slice(i+1)); 11950 } 11951 } 11952 this.state.tmp.common_term_match_fail = true; 11953 this.variables = []; 11954 } 11955}; 11956 11957 11958CSL.NameOutput.prototype.reinit = function (names, labelVariable) { 11959 this.requireMatch = names.requireMatch; 11960 this.labelVariable = labelVariable; 11961 11962 if (this.state.tmp.can_substitute.value()) { 11963 this.nameset_offset = 0; 11964 // What-all should be carried across from the subsidiary 11965 // names node, and on what conditions? For each attribute, 11966 // and decoration, is it an override, or is it additive? 11967 this.variables = names.variables; 11968 11969 // Not sure why this is necessary. Guards against a memory leak perhaps? 11970 var oldval = this.state.tmp.value.slice(); 11971 this.state.tmp.value = []; 11972 11973 for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) { 11974 if (this.Item[this.variables[i]] && this.Item[this.variables[i]].length) { 11975 this.state.tmp.value = this.state.tmp.value.concat(this.Item[this.variables[i]]); 11976 } 11977 } 11978 if (this.state.tmp.value.length) { 11979 this.state.tmp.can_substitute.replace(false, CSL.LITERAL); 11980 } 11981 11982 this.state.tmp.value = oldval; 11983 11984 } 11985 // Abort and proceed to the next substitution if a match is required, 11986 // two variables are called, and they do not match. 11987 var checkCommonTerm = this.checkCommonAuthor(this.requireMatch); 11988 if (checkCommonTerm) { 11989 this.state.tmp.can_substitute.pop(); 11990 this.state.tmp.can_substitute.push(true); 11991 for (var i in this.variables) { 11992 var idx = this.state.tmp.done_vars.indexOf(this.variables[i]); 11993 if (idx > -1) { 11994 this.state.tmp.done_vars = this.state.tmp.done_vars.slice(0, idx).concat(this.state.tmp.done_vars.slice(i+1)); 11995 } 11996 } 11997 this.variables = []; 11998 } 11999}; 12000 12001CSL.NameOutput.prototype.outputNames = function () { 12002 var i, ilen; 12003 var variables = this.variables; 12004 if (this.institution.and) { 12005 if (!this.institution.and.single.blobs || !this.institution.and.single.blobs.length) { 12006 this.institution.and.single.blobs = this.name.and.single.blobs; 12007 } 12008 if (!this.institution.and.multiple.blobs || !this.institution.and.multiple.blobs.length) { 12009 this.institution.and.multiple.blobs = this.name.and.multiple.blobs; 12010 } 12011 } 12012 12013 this.variable_offset = {}; 12014 if (this.family) { 12015 this.family_decor = CSL.Util.cloneToken(this.family); 12016 this.family_decor.strings.prefix = ""; 12017 this.family_decor.strings.suffix = ""; 12018 // Sets text-case value (text-case="title" is suppressed for items 12019 // non-English with non-English value in Item.language) 12020 for (i = 0, ilen = this.family.execs.length; i < ilen; i += 1) { 12021 this.family.execs[i].call(this.family_decor, this.state, this.Item); 12022 } 12023 } else { 12024 this.family_decor = false; 12025 } 12026 12027 if (this.given) { 12028 this.given_decor = CSL.Util.cloneToken(this.given); 12029 this.given_decor.strings.prefix = ""; 12030 this.given_decor.strings.suffix = ""; 12031 // Sets text-case value (text-case="title" is suppressed for items 12032 // non-English with non-English value in Item.language) 12033 for (i = 0, ilen = this.given.execs.length; i < ilen; i += 1) { 12034 this.given.execs[i].call(this.given_decor, this.state, this.Item); 12035 } 12036 } else { 12037 this.given_decor = false; 12038 } 12039 12040 //SNIP-START 12041 if (this.debug) { 12042 this.state.sys.print("(2)"); 12043 } 12044 //SNIP-END 12045 // util_names_etalconfig.js 12046 this.getEtAlConfig(); 12047 //SNIP-START 12048 if (this.debug) { 12049 this.state.sys.print("(3)"); 12050 } 12051 //SNIP-END 12052 // util_names_divide.js 12053 this.divideAndTransliterateNames(); 12054 //SNIP-START 12055 if (this.debug) { 12056 this.state.sys.print("(4)"); 12057 } 12058 //SNIP-END 12059 // util_names_truncate.js 12060 12061 this.truncatePersonalNameLists(); 12062 //SNIP-START 12063 if (this.debug) { 12064 this.state.sys.print("(5)"); 12065 } 12066 //SNIP-END 12067 12068 //SNIP-START 12069 if (this.debug) { 12070 this.state.sys.print("(6)"); 12071 } 12072 //SNIP-END 12073 // util_names_disambig.js 12074 this.disambigNames(); 12075 12076 // util_names_constraints.js 12077 this.constrainNames(); 12078 //SNIP-START 12079 if (this.debug) { 12080 this.state.sys.print("(7)"); 12081 } 12082 //SNIP-END 12083 // form="count" 12084 if (this.name.strings.form === "count") { 12085 if (this.state.tmp.extension || this.names_count != 0) { 12086 this.state.output.append(this.names_count, "empty"); 12087 this.state.tmp.group_context.tip.variable_success = true; 12088 } 12089 return; 12090 } 12091 12092 //SNIP-START 12093 if (this.debug) { 12094 this.state.sys.print("(8)"); 12095 } 12096 //SNIP-END 12097 this.setEtAlParameters(); 12098 //SNIP-START 12099 if (this.debug) { 12100 this.state.sys.print("(9)"); 12101 } 12102 //SNIP-END 12103 this.setCommonTerm(this.requireMatch); 12104 //SNIP-START 12105 if (this.debug) { 12106 this.state.sys.print("(10)"); 12107 } 12108 //SNIP-END 12109 this.renderAllNames(); 12110 //SNIP-START 12111 if (this.debug) { 12112 this.state.sys.print("(11)"); 12113 } 12114 //SNIP-END 12115 var blob_list = []; 12116 for (i = 0, ilen = variables.length; i < ilen; i += 1) { 12117 var v = variables[i]; 12118 var institution_sets = []; 12119 var institutions = false; 12120 var varblob = null; 12121 if (!this.state.opt.development_extensions.spoof_institutional_affiliations) { 12122 varblob = this._join([this.freeters[v]], ""); 12123 } else { 12124 //SNIP-START 12125 if (this.debug) { 12126 this.state.sys.print("(11a)"); 12127 } 12128 //SNIP-END 12129 for (var j = 0, jlen = this.institutions[v].length; j < jlen; j += 1) { 12130 institution_sets.push(this.joinPersonsAndInstitutions([this.persons[v][j], this.institutions[v][j]])); 12131 } 12132 //SNIP-START 12133 if (this.debug) { 12134 this.state.sys.print("(11b)"); 12135 } 12136 //SNIP-END 12137 if (this.institutions[v].length) { 12138 var pos = this.nameset_base + this.variable_offset[v]; 12139 if (this.freeters[v].length) { 12140 pos += 1; 12141 } 12142 institutions = this.joinInstitutionSets(institution_sets, pos); 12143 } 12144 //SNIP-START 12145 if (this.debug) { 12146 this.state.sys.print("(11c)"); 12147 } 12148 //SNIP-END 12149 var varblob = this.joinFreetersAndInstitutionSets([this.freeters[v], institutions]); 12150 //SNIP-START 12151 if (this.debug) { 12152 this.state.sys.print("(11d)"); 12153 } 12154 //SNIP-END 12155 } 12156 if (varblob) { 12157 // Apply labels, if any 12158 if (!this.state.tmp.extension) { 12159 varblob = this._applyLabels(varblob, v); 12160 } 12161 blob_list.push(varblob); 12162 } 12163 //SNIP-START 12164 if (this.debug) { 12165 this.state.sys.print("(11e)"); 12166 } 12167 //SNIP-END 12168 if (this.common_term) { 12169 break; 12170 } 12171 } 12172 //SNIP-START 12173 if (this.debug) { 12174 this.state.sys.print("(12)"); 12175 } 12176 //SNIP-END 12177 this.state.output.openLevel("empty"); 12178 this.state.output.current.value().strings.delimiter = this.state.inheritOpt(this.names, "delimiter", "names-delimiter"); 12179 //SNIP-START 12180 if (this.debug) { 12181 this.state.sys.print("(13)"); 12182 } 12183 //SNIP-END 12184 for (i = 0, ilen = blob_list.length; i < ilen; i += 1) { 12185 // notSerious 12186 this.state.output.append(blob_list[i], "literal", true); 12187 } 12188 if (!this.state.tmp.just_looking && blob_list.length > 0) { 12189 this.state.tmp.probably_rendered_something = true; 12190 } 12191 //SNIP-START 12192 if (this.debug) { 12193 this.state.sys.print("(14)"); 12194 } 12195 //SNIP-END 12196 this.state.output.closeLevel("empty"); 12197 //SNIP-START 12198 if (this.debug) { 12199 this.state.sys.print("(15)"); 12200 } 12201 //SNIP-END 12202 var blob = this.state.output.pop(); 12203 this.state.tmp.name_node.top = blob; 12204 //SNIP-START 12205 if (this.debug) { 12206 this.state.sys.print("(16)"); 12207 } 12208 //SNIP-END 12209 12210 // Append will drop the names on the floor here if suppress-me is 12211 // set on element_trace. 12212 // Need to rescue the value for collapse comparison. 12213 var namesToken = CSL.Util.cloneToken(this.names); 12214 if (this.state.tmp.group_context.tip.condition) { 12215 CSL.UPDATE_GROUP_CONTEXT_CONDITION(this.state, this.names.strings.prefix, null, this.names); 12216 } 12217 this.state.output.append(blob, namesToken); 12218 if (this.state.tmp.term_predecessor_name) { 12219 this.state.tmp.term_predecessor = true; 12220 } 12221 //SNIP-START 12222 if (this.debug) { 12223 this.state.sys.print("(17)"); 12224 } 12225 //SNIP-END 12226 // Also used in CSL.Util.substituteEnd (which could do with 12227 // some cleanup at this writing). 12228 //SNIP-START 12229 if (this.debug) { 12230 this.state.sys.print("(18)"); 12231 } 12232 //SNIP-END 12233 if (variables[0] !== "authority") { 12234 // Just grab the string values in the name 12235 var name_node_string = []; 12236 var nameobjs = this.Item[variables[0]]; 12237 if (nameobjs) { 12238 for (var i = 0, ilen = nameobjs.length; i < ilen; i += 1) { 12239 var substring = CSL.Util.Names.getRawName(nameobjs[i]); 12240 if (substring) { 12241 name_node_string.push(substring); 12242 } 12243 } 12244 } 12245 name_node_string = name_node_string.join(", "); 12246 if (name_node_string) { 12247 this.state.tmp.name_node.string = name_node_string; 12248 } 12249 } 12250 // for classic support 12251 // This may be more convoluted than it needs to be. Or maybe not. 12252 // 12253 // Check for classic abbreviation 12254 // 12255 // If found, then (1) suppress title rendering, (2) replace the node 12256 // with the abbreviation output [and (3) do not run this._collapseAuthor() ?] 12257 if (this.state.tmp.name_node.string && !this.state.tmp.first_name_string) { 12258 this.state.tmp.first_name_string = this.state.tmp.name_node.string; 12259 } 12260 if ("classic" === this.Item.type) { 12261 if (this.state.tmp.first_name_string) { 12262 var author_title = []; 12263 author_title.push(this.state.tmp.first_name_string); 12264 if (this.Item.title) { 12265 author_title.push(this.Item.title); 12266 } 12267 author_title = author_title.join(", "); 12268 if (author_title && this.state.sys.getAbbreviation) { 12269 if (this.state.sys.normalizeAbbrevsKey) { 12270 author_title = this.state.sys.normalizeAbbrevsKey("classic", author_title); 12271 } 12272 this.state.transform.loadAbbreviation("default", "classic", author_title, this.Item.language); 12273 if (this.state.transform.abbrevs["default"].classic[author_title]) { 12274 this.state.tmp.done_vars.push("title"); 12275 this.state.output.append(this.state.transform.abbrevs["default"].classic[author_title], "empty", true); 12276 blob = this.state.output.pop(); 12277 this.state.tmp.name_node.top.blobs.pop(); 12278 this.state.tmp.name_node.top.blobs.push(blob); 12279 } 12280 } 12281 } 12282 } 12283 12284 // Let's try something clever here. 12285 this._collapseAuthor(); 12286 12287 // For name_SubstituteOnNamesSpanNamesSpanFail 12288 this.variables = []; 12289 12290 // Reset stop-last after rendering 12291 this.state.tmp.authority_stop_last = 0; 12292 12293 //SNIP-START 12294 if (this.debug) { 12295 this.state.sys.print("(19)"); 12296 } 12297 //SNIP-END 12298}; 12299 12300CSL.NameOutput.prototype._applyLabels = function (blob, v) { 12301 var txt; 12302 if (!this.label || !this.label[this.labelVariable]) { 12303 return blob; 12304 } 12305 var plural = 0; 12306 var num = this.freeters_count[v] + this.institutions_count[v]; 12307 if (num > 1) { 12308 plural = 1; 12309 } else { 12310 for (var i = 0, ilen = this.persons[v].length; i < ilen; i += 1) { 12311 num += this.persons_count[v][i]; 12312 } 12313 if (num > 1) { 12314 plural = 1; 12315 } 12316 } 12317 // Some code duplication here, should be factored out. 12318 if (this.label[this.labelVariable].before) { 12319 if ("number" === typeof this.label[this.labelVariable].before.strings.plural) { 12320 plural = this.label[this.labelVariable].before.strings.plural; 12321 } 12322 txt = this._buildLabel(v, plural, "before", this.labelVariable); 12323 this.state.output.openLevel("empty"); 12324 this.state.output.append(txt, this.label[this.labelVariable].before, true); 12325 this.state.output.append(blob, "literal", true); 12326 this.state.output.closeLevel("empty"); 12327 blob = this.state.output.pop(); 12328 } else if (this.label[this.labelVariable].after) { 12329 if ("number" === typeof this.label[this.labelVariable].after.strings.plural) { 12330 plural = this.label[this.labelVariable].after.strings.plural; 12331 } 12332 txt = this._buildLabel(v, plural, "after", this.labelVariable); 12333 this.state.output.openLevel("empty"); 12334 this.state.output.append(blob, "literal", true); 12335 this.state.output.append(txt, this.label[this.labelVariable].after, true); 12336 this.state.tmp.label_blob = this.state.output.pop(); 12337 this.state.output.append(this.state.tmp.label_blob,"literal",true); 12338 this.state.output.closeLevel("empty"); 12339 blob = this.state.output.pop(); 12340 } 12341 return blob; 12342}; 12343 12344CSL.NameOutput.prototype._buildLabel = function (term, plural, position, v) { 12345 if (this.common_term) { 12346 term = this.common_term; 12347 } 12348 12349 var ret = false; 12350 var node = this.label[v][position]; 12351 if (node) { 12352 ret = CSL.castLabel(this.state, node, term, plural, CSL.TOLERANT); 12353 } 12354 return ret; 12355}; 12356 12357 12358CSL.NameOutput.prototype._collapseAuthor = function () { 12359 var myqueue, mystr, oldchars; 12360 // collapse can be undefined, an array of length zero, and probably 12361 // other things ... ugh. 12362 if (this.state.tmp.name_node.top.blobs.length === 0) { 12363 return; 12364 } 12365 if (this.nameset_base === 0 && this.Item[this.variables[0]] && !this._first_creator_variable) { 12366 this._first_creator_variable = this.variables[0]; 12367 } 12368 if ((this.state[this.state.tmp.area].opt.collapse 12369 && this.state[this.state.tmp.area].opt.collapse.length) 12370 || (this.state[this.state.tmp.area].opt.cite_group_delimiter 12371 && this.state[this.state.tmp.area].opt.cite_group_delimiter.length)) { 12372 12373 if (this.state.tmp.authorstring_request) { 12374 // Avoid running this on every call to getAmbiguousCite()? 12375 mystr = ""; 12376 myqueue = this.state.tmp.name_node.top.blobs.slice(-1)[0].blobs; 12377 oldchars = this.state.tmp.offset_characters; 12378 if (myqueue) { 12379 mystr = this.state.output.string(this.state, myqueue, false); 12380 } 12381 // Avoid side-effects on character counting: we're only interested 12382 // in the final rendering. 12383 this.state.tmp.offset_characters = oldchars; 12384 this.state.registry.authorstrings[this.Item.id] = mystr; 12385 } else if (!this.state.tmp.just_looking 12386 && !this.state.tmp.suppress_decorations && ((this.state[this.state.tmp.area].opt.collapse && this.state[this.state.tmp.area].opt.collapse.length) || this.state[this.state.tmp.area].opt.cite_group_delimiter && this.state[this.state.tmp.area].opt.cite_group_delimiter)) { 12387 // XX1 this.state.sys.print("RENDER: "+this.Item.id); 12388 mystr = ""; 12389 myqueue = this.state.tmp.name_node.top.blobs.slice(-1)[0].blobs; 12390 oldchars = this.state.tmp.offset_characters; 12391 if (myqueue) { 12392 mystr = this.state.output.string(this.state, myqueue, false); 12393 } 12394 if (mystr === this.state.tmp.last_primary_names_string) { 12395 if (this.item["suppress-author"] || (this.state[this.state.tmp.area].opt.collapse && this.state[this.state.tmp.area].opt.collapse.length)) { 12396 // XX1 this.state.sys.print(" CUT!"); 12397 this.state.tmp.name_node.top.blobs.pop(); 12398 this.state.tmp.name_node.children = []; 12399 // If popped, avoid side-effects on character counting: we're only interested 12400 // in things that actually render. 12401 this.state.tmp.offset_characters = oldchars; 12402 } 12403 // Needed 12404 if (this.state[this.state.tmp.area].opt.cite_group_delimiter && this.state[this.state.tmp.area].opt.cite_group_delimiter) { 12405 this.state.tmp.use_cite_group_delimiter = true; 12406 } 12407 } else { 12408 // XX1 this.state.sys.print("remembering: "+mystr); 12409 this.state.tmp.last_primary_names_string = mystr; 12410 // XXXXX A little more precision would be nice. 12411 // This will clobber variable="author editor" as well as variable="author". 12412 12413 if (this.variables.indexOf(this._first_creator_variable) > -1 && this.item && this.item["suppress-author"] && this.Item.type !== "legal_case") { 12414 this.state.tmp.name_node.top.blobs.pop(); 12415 this.state.tmp.name_node.children = []; 12416 // If popped, avoid side-effects on character counting: we're only interested 12417 // in things that actually render. 12418 this.state.tmp.offset_characters = oldchars; 12419 12420 // A wild guess, but will usually be correct 12421 this.state.tmp.term_predecessor = false; 12422 } 12423 // Arcane and probably unnecessarily complicated? 12424 this.state.tmp.have_collapsed = false; 12425 // Needed 12426 if (this.state[this.state.tmp.area].opt.cite_group_delimiter && this.state[this.state.tmp.area].opt.cite_group_delimiter) { 12427 this.state.tmp.use_cite_group_delimiter = false; 12428 } 12429 } 12430 } 12431 } 12432}; 12433 12434/* 12435CSL.NameOutput.prototype.suppressNames = function() { 12436 suppress_condition = suppress_min && display_names.length >= suppress_min; 12437 if (suppress_condition) { 12438 continue; 12439 } 12440} 12441*/ 12442 12443/*global CSL: true */ 12444 12445CSL.NameOutput.prototype.isPerson = function (value) { 12446 if (value.literal 12447 || (!value.given && value.family && value.isInstitution)) { 12448 12449 return false; 12450 } else { 12451 return true; 12452 } 12453}; 12454 12455/*global CSL: true */ 12456 12457CSL.NameOutput.prototype.truncatePersonalNameLists = function () { 12458 var v, i, ilen, j, jlen, chopvar, values; 12459 // XXX Before truncation, make a note of the original number 12460 // of names, for use in et-al evaluation. 12461 this.freeters_count = {}; 12462 this.persons_count = {}; 12463 this.institutions_count = {}; 12464 // By key is okay here, as we don't care about sequence. 12465 for (v in this.freeters) { 12466 if (this.freeters.hasOwnProperty(v)) { 12467 this.freeters_count[v] = this.freeters[v].length; 12468 this.freeters[v] = this._truncateNameList(this.freeters, v); 12469 } 12470 } 12471 12472 for (v in this.persons) { 12473 if (this.persons.hasOwnProperty(v)) { 12474 this.institutions_count[v] = this.institutions[v].length; 12475 this._truncateNameList(this.institutions, v); 12476 this.persons[v] = this.persons[v].slice(0, this.institutions[v].length); 12477 this.persons_count[v] = []; 12478 for (j = 0, jlen = this.persons[v].length; j < jlen; j += 1) { 12479 this.persons_count[v][j] = this.persons[v][j].length; 12480 this.persons[v][j] = this._truncateNameList(this.persons, v, j); 12481 } 12482 } 12483 } 12484 if (this.state.opt.development_extensions.etal_min_etal_usefirst_hack 12485 && this.etal_min === 1 && this.etal_use_first === 1 12486 && !(this.state.tmp.extension 12487 || this.state.tmp.just_looking)) { 12488 chopvar = v; 12489 } else { 12490 chopvar = false; 12491 } 12492 if (chopvar || this._please_chop) { 12493 for (i = 0, ilen = this.variables.length; i < ilen; i += 1) { 12494 v = this.variables[i]; 12495 if (this.freeters[v].length) { 12496 if (this._please_chop === v) { 12497 this.freeters[v] = this.freeters[v].slice(1); 12498 this.freeters_count[v] += -1; 12499 this._please_chop = false; 12500 } else if (chopvar && !this._please_chop) { 12501 this.freeters[v] = this.freeters[v].slice(0, 1); 12502 this.freeters_count[v] = 1; 12503 this.institutions[v] = []; 12504 this.persons[v] = []; 12505 this._please_chop = chopvar; 12506 } 12507 } 12508 for (var j=0,jlen = this.persons[v].length;j<jlen;j++) { 12509 if (this.persons[v][j].length) { 12510 if (this._please_chop === v) { 12511 this.persons[v][j] = this.persons[v][j].slice(1); 12512 this.persons_count[v][j] += -1; 12513 this._please_chop = false; 12514 break; 12515 } else if (chopvar && !this._please_chop) { 12516 this.freeters[v] = this.persons[v][j].slice(0, 1); 12517 this.freeters_count[v] = 1; 12518 this.institutions[v] = []; 12519 this.persons[v] = []; 12520 values = []; 12521 this._please_chop = chopvar; 12522 break; 12523 } 12524 } 12525 } 12526 if (this.institutions[v].length) { 12527 if (this._please_chop === v) { 12528 this.institutions[v] = this.institutions[v].slice(1); 12529 this.institutions_count[v] += -1; 12530 this._please_chop = false; 12531 } else if (chopvar && !this._please_chop) { 12532 this.institutions[v] = this.institutions[v].slice(0, 1); 12533 this.institutions_count[v] = 1; 12534 values = []; 12535 this._please_chop = chopvar; 12536 } 12537 } 12538 } 12539 } 12540 12541 // Transliteration and abbreviation mapping 12542 12543 // Hmm. This could produce three lists for each nameset: 12544 // - primary (transformed in place) 12545 // - secondary 12546 // - tertiary 12547 // with items that produce no result in the secondary and tertiary 12548 // transforms set to false. Maybe. 12549 12550 // Actually that would be insane, so forget it. 12551 // What we need is to add suitable parameters to getName(), and merge 12552 // the single-name-level operations below into that function. Then the 12553 // operation can be applied in util_names_render.js, and the logic 12554 // becomes very similar to what we already have running in util_transform.js. 12555 12556/* 12557 for (v in this.freeters) { 12558 this._transformNameset(this.freeters[v]); 12559 } 12560 for (v in this.persons) { 12561 for (i = 0, ilen = this.persons[v].length; i < ilen; i += 1) { 12562 this._transformNameset(this.persons[v][i]); 12563 } 12564 this._transformNameset(this.institutions[v]); 12565 } 12566*/ 12567 12568 // Could also be factored out to a separate function for clarity. 12569 // ???? XXX Does this belong? 12570 for (i = 0, ilen = this.variables.length; i < ilen; i += 1) { 12571 if (this.institutions[v].length) { 12572 this.nameset_offset += 1; 12573 } 12574 for (var j=0,jlen=this.persons[v].length;j<jlen;j++) { 12575 if (this.persons[v][j].length) { 12576 this.nameset_offset += 1; 12577 } 12578 // this.institutions[v][i] = this._splitInstitution(this.institutions[v][i], v, i); 12579 } 12580 } 12581}; 12582 12583CSL.NameOutput.prototype._truncateNameList = function (container, variable, index) { 12584 var lst; 12585 if ("undefined" === typeof index) { 12586 lst = container[variable]; 12587 } else { 12588 lst = container[variable][index]; 12589 } 12590 if (this.state[this.state[this.state.tmp.area].root].opt.max_number_of_names 12591 && lst.length > 50 12592 && lst.length > (this.state[this.state[this.state.tmp.area].root].opt.max_number_of_names + 2)) { 12593 12594 // Preserve the last name in the list, in case we're rendering with a PI ellipsis (et-al-use-last) 12595 var limit = this.state[this.state[this.state.tmp.area].root].opt.max_number_of_names; 12596 lst = lst.slice(0, limit+1).concat(lst.slice(-1)); 12597 } 12598 return lst; 12599}; 12600 12601 12602/*global CSL: true */ 12603 12604CSL.NameOutput.prototype.divideAndTransliterateNames = function () { 12605 var i, ilen, j, jlen; 12606 var Item = this.Item; 12607 var variables = this.variables; 12608 this.varnames = variables.slice(); 12609 this.freeters = {}; 12610 this.persons = {}; 12611 this.institutions = {}; 12612 for (i = 0, ilen = variables.length; i < ilen; i += 1) { 12613 var v = variables[i]; 12614 this.variable_offset[v] = this.nameset_offset; 12615 var values = this._normalizeVariableValue(Item, v); 12616 if (this.name.strings["suppress-min"] && values.length >= this.name.strings["suppress-min"]) { 12617 values = []; 12618 } 12619 if (this.name.strings["suppress-max"] && values.length <= this.name.strings["suppress-max"]) { 12620 values = []; 12621 } 12622 this._getFreeters(v, values); 12623 this._getPersonsAndInstitutions(v, values); 12624 if (this.state.opt.development_extensions.spoof_institutional_affiliations) { 12625 if (this.name.strings["suppress-min"] === 0) { 12626 this.freeters[v] = []; 12627 for (j = 0, jlen = this.persons[v].length; j < jlen; j += 1) { 12628 this.persons[v][j] = []; 12629 } 12630 } else if (this.institution.strings["suppress-min"] === 0) { 12631 this.institutions[v] = []; 12632 this.freeters[v] = this.freeters[v].concat(this.persons[v]); 12633 for (j = 0, jlen = this.persons[v].length; j < jlen; j += 1) { 12634 for (var k = 0, klen = this.persons[v][j].length; k < klen; k += 1) { 12635 this.freeters[v].push(this.persons[v][j][k]); 12636 } 12637 } 12638 this.persons[v] = []; 12639 } 12640 } 12641 } 12642}; 12643 12644CSL.NameOutput.prototype._normalizeVariableValue = function (Item, variable) { 12645 var names; 12646 if ("string" === typeof Item[variable] || "number" === typeof Item[variable]) { 12647 CSL.debug("name variable \"" + variable + "\" is string or number, not array. Attempting to fix."); 12648 names = [{literal: Item[variable] + ""}]; 12649 } else if (!Item[variable]) { 12650 names = []; 12651 } else if ("number" !== typeof Item[variable].length) { 12652 CSL.debug("name variable \"" + variable + "\" is object, not array. Attempting to fix."); 12653 Item[variable] = [Item[variable]]; 12654 names = Item[variable].slice(); 12655 } else { 12656 names = Item[variable].slice(); 12657 } 12658 return names; 12659}; 12660 12661CSL.NameOutput.prototype._getFreeters = function (v, values) { 12662 this.freeters[v] = []; 12663 if (this.state.opt.development_extensions.spoof_institutional_affiliations) { 12664 for (var i=values.length-1;i>-1;i--) { 12665 if (this.isPerson(values[i])) { 12666 var value = this._checkNickname(values.pop()); 12667 if (value) { 12668 this.freeters[v].push(value); 12669 } 12670 } else { 12671 break; 12672 } 12673 } 12674 } else { 12675 for (var i=values.length-1;i>-1;i--) { 12676 var value = values.pop(); 12677 if (this.isPerson(value)) { 12678 var value = this._checkNickname(value); 12679 } 12680 this.freeters[v].push(value); 12681 } 12682 } 12683 this.freeters[v].reverse(); 12684 if (this.freeters[v].length) { 12685 this.nameset_offset += 1; 12686 } 12687}; 12688 12689CSL.NameOutput.prototype._getPersonsAndInstitutions = function (v, values) { 12690 this.persons[v] = []; 12691 this.institutions[v] = []; 12692 if (!this.state.opt.development_extensions.spoof_institutional_affiliations) { 12693 return; 12694 } 12695 var persons = []; 12696 var has_affiliates = false; 12697 var first = true; 12698 for (var i = values.length - 1; i > -1; i += -1) { 12699 if (this.isPerson(values[i])) { 12700 var value = this._checkNickname(values[i]); 12701 if (value) { 12702 persons.push(value); 12703 } 12704 } else { 12705 has_affiliates = true; 12706 this.institutions[v].push(values[i]); 12707 if (!first) { 12708 persons.reverse(); 12709 this.persons[v].push(persons); 12710 persons = []; 12711 } 12712 first = false; 12713 } 12714 } 12715 if (has_affiliates) { 12716 persons.reverse(); 12717 this.persons[v].push(persons); 12718 this.persons[v].reverse(); 12719 this.institutions[v].reverse(); 12720 } 12721}; 12722 12723CSL.NameOutput.prototype._clearValues = function (values) { 12724 for (var i = values.length - 1; i > -1; i += -1) { 12725 values.pop(); 12726 } 12727}; 12728 12729CSL.NameOutput.prototype._checkNickname = function (name) { 12730 if (["interview", "personal_communication"].indexOf(this.Item.type) > -1) { 12731 var author = ""; 12732 author = CSL.Util.Names.getRawName(name); 12733 if (author && this.state.sys.getAbbreviation && !(this.item && this.item["suppress-author"])) { 12734 var normalizedKey = author; 12735 if (this.state.sys.normalizeAbbrevsKey) { 12736 // The first argument does not have to be the exact variable name. 12737 normalizedKey = this.state.sys.normalizeAbbrevsKey("author", author); 12738 } 12739 this.state.transform.loadAbbreviation("default", "nickname", normalizedKey, this.Item.language); 12740 // XXX Why does this have to happen here? 12741 var myLocalName = this.state.transform.abbrevs["default"].nickname[normalizedKey]; 12742 if (myLocalName) { 12743 if (myLocalName === "!here>>>") { 12744 name = false; 12745 } else { 12746 name = {family:myLocalName,given:''}; 12747 } 12748 } 12749 } 12750 } 12751 return name; 12752}; 12753 12754/*global CSL: true */ 12755 12756CSL.NameOutput.prototype._purgeEmptyBlobs = function (blobs) { 12757 for (var i = blobs.length - 1; i > -1; i += -1) { 12758 if (!blobs[i] || blobs[i].length === 0 || !blobs[i].blobs.length) { 12759 blobs = blobs.slice(0, i).concat(blobs.slice(i + 1)); 12760 } 12761 } 12762 return blobs; 12763}; 12764 12765CSL.NameOutput.prototype.joinPersons = function (blobs, pos, j, tokenname) { 12766 var ret; 12767 blobs = this._purgeEmptyBlobs(blobs); 12768 if (!tokenname) { 12769 tokenname = "name"; 12770 } 12771 if ("undefined" === typeof j) { 12772 if (this.etal_spec[pos].freeters === 1) { 12773 ret = this._joinEtAl(blobs); 12774 } else if (this.etal_spec[pos].freeters === 2) { 12775 ret = this._joinEllipsis(blobs); 12776 } else if (!this.state.tmp.sort_key_flag) { 12777 ret = this._joinAnd(blobs); 12778 } else { 12779 ret = this._join(blobs, this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", ")); 12780 } 12781 } else { 12782 if (this.etal_spec[pos].persons[j] === 1) { 12783 ret = this._joinEtAl(blobs); 12784 } else if (this.etal_spec[pos].persons[j] === 2) { 12785 ret = this._joinEllipsis(blobs); 12786 } else if (!this.state.tmp.sort_key_flag) { 12787 ret = this._joinAnd(blobs); 12788 } else { 12789 ret = this._join(blobs, this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", ")); 12790 } 12791 } 12792 return ret; 12793}; 12794 12795 12796CSL.NameOutput.prototype.joinInstitutionSets = function (blobs, pos) { 12797 var ret; 12798 blobs = this._purgeEmptyBlobs(blobs); 12799 if (this.etal_spec[pos].institutions === 1) { 12800 ret = this._joinEtAl(blobs, "institution"); 12801 } else if (this.etal_spec[pos].institutions === 2) { 12802 ret = this._joinEllipsis(blobs, "institution"); 12803 } else { 12804 ret = this._joinAnd(blobs); 12805 } 12806 return ret; 12807}; 12808 12809 12810CSL.NameOutput.prototype.joinPersonsAndInstitutions = function (blobs) { 12811 // 12812 blobs = this._purgeEmptyBlobs(blobs); 12813 var ret = this._join(blobs, this.state.tmp.name_delimiter); 12814 ret.isInstitution = true; 12815 return ret; 12816}; 12817 12818// LEGACY 12819// This should go away eventually 12820CSL.NameOutput.prototype.joinFreetersAndInstitutionSets = function (blobs) { 12821 // Nothing, one or two, never more 12822 blobs = this._purgeEmptyBlobs(blobs); 12823 var ret = this._join(blobs, "[never here]", this["with"].single, this["with"].multiple); 12824 //var ret = this._join(blobs, ""); 12825 return ret; 12826}; 12827 12828CSL.NameOutput.prototype._getAfterInvertedName = function(blobs, delimiter, finalJoin) { 12829 if (finalJoin && blobs.length > 1) { 12830 if (this.state.inheritOpt(this.name, "delimiter-precedes-last") === "after-inverted-name") { 12831 var prevBlob = blobs[blobs.length - 2]; 12832 if (prevBlob.blobs.length > 0 && prevBlob.blobs[0].isInverted) { 12833 finalJoin.strings.prefix = delimiter; 12834 } 12835 } 12836 } 12837 return finalJoin; 12838} 12839 12840CSL.NameOutput.prototype._getAndJoin = function (blobs, delimiter) { 12841 var finalJoin = false; 12842 if (blobs.length > 1) { 12843 var singleOrMultiple = "single"; 12844 if (blobs.length > 2) { 12845 singleOrMultiple = "multiple"; 12846 } 12847 if (blobs[blobs.length - 1].isInstitution) { 12848 finalJoin = this.institution.and[singleOrMultiple]; 12849 } else { 12850 finalJoin = this.name.and[singleOrMultiple]; 12851 } 12852 // finalJoin = new CSL.Blob(finalJoin); 12853 finalJoin = JSON.parse(JSON.stringify(finalJoin)); 12854 finalJoin = this._getAfterInvertedName(blobs, delimiter, finalJoin); 12855 } 12856 return finalJoin; 12857}; 12858 12859CSL.NameOutput.prototype._joinEtAl = function (blobs) { 12860 var delimiter = this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", "); 12861 var blob = this._join(blobs, delimiter); 12862 12863 // notSerious 12864 this.state.output.openLevel(this._getToken("name")); 12865 // Delimiter is applied from separately saved source in this case, 12866 // for discriminate application of single and multiple joins. 12867 this.state.output.current.value().strings.delimiter = ""; 12868 this.state.output.append(blob, "literal", true); 12869 if (blobs.length > 1) { 12870 this.state.output.append(this["et-al"].multiple, "literal", true); 12871 } else if (blobs.length === 1) { 12872 this.state.output.append(this["et-al"].single, "literal", true); 12873 } 12874 this.state.output.closeLevel(); 12875 return this.state.output.pop(); 12876}; 12877 12878 12879CSL.NameOutput.prototype._joinEllipsis = function (blobs) { 12880 var delimiter = this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", "); 12881 var finalJoin = false; 12882 if (blobs.length > 1) { 12883 var singleOrMultiple = "single"; 12884 if (blobs.length > 2) { 12885 singleOrMultiple = "multiple"; 12886 } 12887 finalJoin = JSON.parse(JSON.stringify(this.name.ellipsis[singleOrMultiple])); 12888 finalJoin = this._getAfterInvertedName(blobs, delimiter , finalJoin); 12889 12890 } 12891 return this._join(blobs, delimiter, finalJoin); 12892}; 12893 12894CSL.NameOutput.prototype._joinAnd = function (blobs) { 12895 var delimiter = this.state.inheritOpt(this.name, "delimiter", "name-delimiter", ", "); 12896 var finalJoin = this._getAndJoin(blobs, delimiter); 12897 return this._join(blobs, delimiter, finalJoin); 12898}; 12899 12900 12901CSL.NameOutput.prototype._join = function (blobs, delimiter, finalJoin) { 12902 var i, ilen; 12903 if (!blobs) { 12904 return false; 12905 } 12906 blobs = this._purgeEmptyBlobs(blobs); 12907 if (!blobs.length) { 12908 return false; 12909 } 12910 if (blobs.length > 1) { 12911 if (blobs.length === 2) { 12912 if (!finalJoin) { 12913 blobs[0].strings.suffix += delimiter; 12914 } else { 12915 blobs = [blobs[0], finalJoin, blobs[1]]; 12916 } 12917 } else { 12918 var offset; 12919 if (finalJoin) { 12920 offset = 1; 12921 } else { 12922 offset = 0; 12923 } 12924 var blob = blobs.pop(); 12925 for (var i=0,ilen=blobs.length - offset;i<ilen;i++) { 12926 blobs[i].strings.suffix += delimiter; 12927 } 12928 blobs.push(finalJoin); 12929 blobs.push(blob); 12930 } 12931 } 12932 12933 //this.state.output.openLevel(this._getToken(tokenname)); 12934 this.state.output.openLevel(); 12935 12936 //this.state.output.openLevel(this._getToken("empty")); 12937 // Delimiter is applied from separately saved source in this case, 12938 // for discriminate application of single and multiple joins. 12939 for (i = 0, ilen = blobs.length; i < ilen; i += 1) { 12940 this.state.output.append(blobs[i], false, true); 12941 } 12942 this.state.output.closeLevel(); 12943 return this.state.output.pop(); 12944}; 12945 12946 12947CSL.NameOutput.prototype._getToken = function (tokenname) { 12948 var token = this[tokenname]; 12949 if (tokenname === "institution") { 12950 var newtoken = new CSL.Token(); 12951 // Which, hmm, is the same thing as "empty" 12952 // Oh, well. 12953 //newtoken.strings.prefix = token.prefix; 12954 //newtoken.strings.suffix = token.suffix; 12955 return newtoken; 12956 } 12957 return token; 12958}; 12959 12960/*global CSL: true */ 12961 12962CSL.NameOutput.prototype.checkCommonAuthor = function(requireMatch) { 12963 if (!requireMatch) { 12964 return false; 12965 } 12966 var common_term = false; 12967 if (this.variables.length === 2) { 12968 var variables = this.variables; 12969 var varnames = variables.slice(); 12970 varnames.sort(); 12971 common_term = varnames.join(""); 12972 } 12973 if (!common_term) { 12974 return false; 12975 } 12976 var has_term = false; 12977 if (this.state.locale[this.state.opt.lang].terms[common_term]) { 12978 has_term = true; 12979 } 12980 if (!has_term) { 12981 this.state.tmp.done_vars.push(this.variables[0]); 12982 this.state.tmp.done_vars.push(this.variables[1]); 12983 return false; 12984 } 12985 var firstSet = this.Item[this.variables[0]]; 12986 var secondSet = this.Item[this.variables[1]]; 12987 var perfectMatch = this._compareNamesets(firstSet, secondSet); 12988 if (perfectMatch === true) { 12989 this.state.tmp.done_vars.push(this.variables[0]); 12990 this.state.tmp.done_vars.push(this.variables[1]); 12991 } 12992 // This may be counter-intuitive. 12993 // This check controls whether we will fail on the this attempt at rendering 12994 // and proceed with substitution. If the names match exactly (true), then 12995 // we do *not* want to abort and continue with substitution. 12996 return !perfectMatch; 12997}; 12998 12999CSL.NameOutput.prototype.setCommonTerm = function () { 13000 var variables = this.variables; 13001 var varnames = variables.slice(); 13002 varnames.sort(); 13003 this.common_term = varnames.join(""); 13004 // When no varnames are on offer 13005 if (!this.common_term) { 13006 return; 13007 } 13008 var has_term = false; 13009 if (this.label && this.label[this.variables[0]]) { 13010 if (this.label[this.variables[0]].before) { 13011 has_term = this.state.getTerm(this.common_term, this.label[this.variables[0]].before.strings.form, 0); 13012 } else if (this.label[this.variables[0]].after) { 13013 has_term = this.state.getTerm(this.common_term, this.label[this.variables[0]].after.strings.form, 0); 13014 } 13015 } 13016 13017 // When there is no common term 13018 if (!this.state.locale[this.state.opt.lang].terms[this.common_term] 13019 || !has_term 13020 || this.variables.length < 2) { 13021 this.common_term = false; 13022 return; 13023 } 13024 var freeters_offset = 0; 13025 for (var i = 0, ilen = this.variables.length - 1; i < ilen; i += 1) { 13026 var v = this.variables[i]; 13027 var vv = this.variables[i + 1]; 13028 if (this.freeters[v].length || this.freeters[vv].length) { 13029 if (this.etal_spec[v].freeters !== this.etal_spec[vv].freeters 13030 || !this._compareNamesets(this.freeters[v], this.freeters[vv])) { 13031 this.common_term = false; 13032 return; 13033 } 13034 freeters_offset += 1; 13035 } 13036 if (this.persons[v].length !== this.persons[vv].length) { 13037 this.common_term = false; 13038 return; 13039 } 13040 for (var j = 0, jlen = this.persons[v].length; j < jlen; j += 1) { 13041 if (this.etal_spec[v].persons[j] !== this.etal_spec[vv].persons[j] 13042 || !this._compareNamesets(this.persons[v][j], this.persons[vv][j])) { 13043 this.common_term = false; 13044 return; 13045 } 13046 } 13047 } 13048}; 13049 13050CSL.NameOutput.prototype._compareNamesets = function (base_nameset, nameset) { 13051 if (!base_nameset || !nameset || base_nameset.length !== nameset.length) { 13052 return false; 13053 } 13054 for (var i = 0, ilen = nameset.length; i < ilen; i += 1) { 13055 for (var j = 0, jlen = CSL.NAME_PARTS.length; j < jlen; j += 1) { 13056 var part = CSL.NAME_PARTS[j]; 13057 if (!base_nameset[i] || base_nameset[i][part] != nameset[i][part]) { 13058 return false; 13059 } 13060 } 13061 } 13062 return true; 13063}; 13064 13065/*global CSL: true */ 13066 13067CSL.NameOutput.prototype.constrainNames = function () { 13068 // figure out how many names to include, in light of the disambig params 13069 // 13070 this.names_count = 0; 13071 //var pos = 0; 13072 var pos; 13073 for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) { 13074 var v = this.variables[i]; 13075 pos = this.nameset_base + i; 13076 // Constrain independent authors here 13077 if (this.freeters[v].length) { 13078 this.state.tmp.names_max.push(this.freeters[v].length, "literal"); 13079 this._imposeNameConstraints(this.freeters, this.freeters_count, v, pos); 13080 this.names_count += this.freeters[v].length; 13081 } 13082 13083 // Constrain institutions here 13084 if (this.institutions[v].length) { 13085 this.state.tmp.names_max.push(this.institutions[v].length, "literal"); 13086 this._imposeNameConstraints(this.institutions, this.institutions_count, v, pos); 13087 this.persons[v] = this.persons[v].slice(0, this.institutions[v].length); 13088 this.names_count += this.institutions[v].length; 13089 } 13090 13091 for (var j = 0, jlen = this.persons[v].length; j < jlen; j += 1) { 13092 // Constrain affiliated authors here 13093 if (this.persons[v][j].length) { 13094 this.state.tmp.names_max.push(this.persons[v][j].length, "literal"); 13095 this._imposeNameConstraints(this.persons[v], this.persons_count[v], j, pos); 13096 this.names_count += this.persons[v][j].length; 13097 } 13098 } 13099 } 13100}; 13101 13102CSL.NameOutput.prototype._imposeNameConstraints = function (lst, count, key, pos) { 13103 // display_names starts as the original length of this list of names. 13104 var display_names = lst[key]; 13105 var discretionary_names_length = this.state.tmp["et-al-min"]; 13106 13107 // Mappings, to allow existing disambiguation machinery to 13108 // remain untouched. 13109 if (this.state.tmp.suppress_decorations) { 13110 if (this.state.tmp.disambig_request && this.state.tmp.disambig_request.names[pos]) { 13111 // Oh. Trouble. 13112 // state.tmp.nameset_counter is the number of the nameset 13113 // in the disambiguation try-sequence. Ouch. 13114 discretionary_names_length = this.state.tmp.disambig_request.names[pos]; 13115 } else if (count[key] >= this.etal_min) { 13116 discretionary_names_length = this.etal_use_first; 13117 } 13118 } else { 13119 if (this.state.tmp.disambig_request 13120 && this.state.tmp.disambig_request.names[pos] > this.etal_use_first) { 13121 13122 if (count[key] < this.etal_min) { 13123 discretionary_names_length = count[key]; 13124 } else { 13125 discretionary_names_length = this.state.tmp.disambig_request.names[pos]; 13126 } 13127 } else if (count[key] >= this.etal_min) { 13128 //discretionary_names_length = this.state.tmp["et-al-use-first"]; 13129 discretionary_names_length = this.etal_use_first; 13130 } 13131 // XXXX: This is a workaround. Under some conditions. 13132 // Where namesets disambiguate on one of the two names 13133 // dropped here, it is possible for more than one 13134 // in-text citation to be close (and indistinguishable) 13135 // matches to a single bibliography entry. 13136 // 13137 // 13138 if (this.etal_use_last && discretionary_names_length > (this.etal_min - 2)) { 13139 discretionary_names_length = this.etal_min - 2; 13140 } 13141 } 13142 var sane = this.etal_min >= this.etal_use_first; 13143 var overlength = count[key] > discretionary_names_length; 13144 // This var is used to control contextual join, and 13145 // lies about the number of names when forceEtAl is true, 13146 // unless normalized. 13147 if (discretionary_names_length > count[key]) { 13148 13149 // Use actual truncated list length, to avoid overrun. 13150 discretionary_names_length = display_names.length; 13151 } 13152 // forceEtAl is relevant when the author list is 13153 // truncated to eliminate clutter. 13154 if (sane && overlength) { 13155 if (this.etal_use_last) { 13156 lst[key] = display_names.slice(0, discretionary_names_length).concat(display_names.slice(-1)); 13157 } else { 13158 lst[key] = display_names.slice(0, discretionary_names_length); 13159 } 13160 } 13161 this.state.tmp.disambig_settings.names[pos] = lst[key].length; 13162 this.state.disambiguate.padBase(this.state.tmp.disambig_settings); 13163 13164 13165 // ??? 13166 //if (!this.state.tmp.disambig_request) { 13167 // this.state.tmp.disambig_settings.givens[pos] = []; 13168 //} 13169}; 13170 13171// Disambiguate names (the number of names is controlled externally, by successive 13172// runs of the processor). 13173 13174/*global CSL: true */ 13175 13176CSL.NameOutput.prototype.disambigNames = function () { 13177 var pos; 13178 for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) { 13179 var v = this.variables[i]; 13180 pos = this.nameset_base + i; 13181 if (this.freeters[v].length) { 13182 this._runDisambigNames(this.freeters[v], pos); 13183 } 13184 // Is this even necessary??? 13185 if (this.institutions[v].length) { 13186 if ("undefined" === typeof this.state.tmp.disambig_settings.givens[pos]) { 13187 this.state.tmp.disambig_settings.givens[pos] = []; 13188 } 13189 for (var j=0,jlen=this.institutions[v].length;j<jlen;j+=1) { 13190 if ("undefined" === typeof this.state.tmp.disambig_settings.givens[pos][j]) { 13191 this.state.tmp.disambig_settings.givens[pos].push(2); 13192 } 13193 } 13194 } 13195 for (var j = 0, jlen = this.persons[v].length; j < jlen; j += 1) { 13196 if (this.persons[v][j].length) { 13197 this._runDisambigNames(this.persons[v][j], pos); 13198 } 13199 } 13200 } 13201}; 13202 13203CSL.NameOutput.prototype._runDisambigNames = function (lst, pos) { 13204 var chk, myform, myinitials, param, i, ilen, paramx; 13205 //if (this.state.tmp.root === "bibliography") { 13206 // return; 13207 //} 13208 for (i = 0, ilen = lst.length; i < ilen; i += 1) { 13209 // 13210 // register the name in the global names disambiguation 13211 // registry 13212 13213 if (!lst[i].given && !lst[i].family) { 13214 continue; 13215 } 13216 13217 myinitials = this.state.inheritOpt(this.name, "initialize-with"); 13218 this.state.registry.namereg.addname("" + this.Item.id, lst[i], i); 13219 chk = this.state.tmp.disambig_settings.givens[pos]; 13220 if ("undefined" === typeof chk) { 13221 // Holes can appear in the list, probably due to institutional 13222 // names that this doesn't touch. Maybe. This fills them up. 13223 for (var j = 0, jlen = pos + 1; j < jlen; j += 1) { 13224 if (!this.state.tmp.disambig_settings.givens[j]) { 13225 this.state.tmp.disambig_settings.givens[j] = []; 13226 } 13227 } 13228 } 13229 chk = this.state.tmp.disambig_settings.givens[pos][i]; 13230 //if ("undefined" !== typeof chk && this.state.tmp.root === 'citation') { 13231 //this.state.tmp.disambig_settings.givens[pos] = []; 13232 //chk = undefined; 13233 //} 13234 if ("undefined" === typeof chk) { 13235 myform = this.state.inheritOpt(this.name, "form", "name-form", "long"); 13236 param = this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, 0, myform, myinitials); 13237 this.state.tmp.disambig_settings.givens[pos].push(param); 13238 } 13239 // 13240 // set the display mode default for givennames if required 13241 myform = this.state.inheritOpt(this.name, "form", "name-form", "long"); 13242 paramx = this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, 0, myform, myinitials); 13243 // this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, 0, myform, myinitials); 13244 if (this.state.tmp.disambig_request) { 13245 // 13246 // fix a request for initials that makes no sense. 13247 // can't do this in disambig, because the availability 13248 // of initials is not a global parameter. 13249 var val = this.state.tmp.disambig_settings.givens[pos][i]; 13250 // This is limited to by-cite disambiguation. 13251 // 2012-09-13: added lst[i].given check to condition 13252 if (val === 1 && 13253 this.state.citation.opt["givenname-disambiguation-rule"] === "by-cite" && 13254 ("undefined" === typeof this.state.inheritOpt(this.name, "initialize-with") 13255 || "undefined" === typeof lst[i].given)) { 13256 val = 2; 13257 } 13258 param = val; 13259 // 2012-09-13: lst[i].given check protects against personal names 13260 // that have no first name element. These were causing an infinite loop, 13261 // this prevents that. 13262 if (this.state.opt["disambiguate-add-givenname"] && lst[i].given) { 13263 param = this.state.registry.namereg.evalname("" + this.Item.id, lst[i], i, param, this.state.inheritOpt(this.name, "form", "name-form", "long"), this.state.inheritOpt(this.name, "initialize-with")); 13264 } 13265 } else { 13266 // 13267 // it clicks. here is where we will put the 13268 // call to the names register, to get the floor value 13269 // for an individual name. 13270 // 13271 param = paramx; 13272 } 13273 // Need to save off the settings based on subsequent 13274 // form, when first cites are rendered. 13275 if (!this.state.tmp.just_looking && this.item && this.item.position === CSL.POSITION_FIRST) { 13276 if (paramx > param) { 13277 param = paramx; 13278 } 13279 } 13280 if (!this.state.tmp.sort_key_flag) { 13281 this.state.tmp.disambig_settings.givens[pos][i] = param; 13282 if ("string" === typeof myinitials 13283 && ("undefined" === typeof this.name.strings["initialize"] 13284 || true === this.name.strings["initialize"])) { 13285 13286 this.state.tmp.disambig_settings.use_initials = true; 13287 } 13288 } 13289 } 13290 //this.state.registry.registry[this.Item.id].disambig.givens = this.state.tmp.disambig_settings.givens.slice(); 13291}; 13292 13293/*global CSL: true */ 13294 13295CSL.NameOutput.prototype.getEtAlConfig = function () { 13296 var item = this.item; 13297 this["et-al"] = {}; 13298 13299 this.state.output.append(this.etal_term, this.etal_style, true); 13300 this["et-al"].single = this.state.output.pop(); 13301 this["et-al"].single.strings.suffix = this.etal_suffix; 13302 this["et-al"].single.strings.prefix = this.etal_prefix_single; 13303 13304 this.state.output.append(this.etal_term, this.etal_style, true); 13305 this["et-al"].multiple = this.state.output.pop(); 13306 this["et-al"].multiple.strings.suffix = this.etal_suffix; 13307 this["et-al"].multiple.strings.prefix = this.etal_prefix_multiple; 13308 13309 // Et-al style parameters (may be sidestepped by disambiguation 13310 // in util_names_constraints.js) 13311 if ("undefined" === typeof item) { 13312 item = {}; 13313 } 13314 //print("== getEtAlConfig() == "+this.state.tmp.area); 13315 13316 if (item.position) { 13317 if (this.state.inheritOpt(this.name, "et-al-subsequent-min")) { 13318 // XX 13319 this.etal_min = this.state.inheritOpt(this.name, "et-al-subsequent-min"); 13320 } else { 13321 // XX 13322 this.etal_min = this.state.inheritOpt(this.name, "et-al-min"); 13323 } 13324 if (this.state.inheritOpt(this.name, "et-al-subsequent-use-first")) { 13325 // XX 13326 this.etal_use_first = this.state.inheritOpt(this.name, "et-al-subsequent-use-first"); 13327 } else { 13328 // XX 13329 this.etal_use_first = this.state.inheritOpt(this.name, "et-al-use-first"); 13330 } 13331 } else { 13332 if (this.state.tmp["et-al-min"]) { 13333 this.etal_min = this.state.tmp["et-al-min"]; 13334 } else { 13335 // XX 13336 this.etal_min = this.state.inheritOpt(this.name, "et-al-min"); 13337 } 13338 if (this.state.tmp["et-al-use-first"]) { 13339 this.etal_use_first = this.state.tmp["et-al-use-first"]; 13340 } else { 13341 // XX 13342 this.etal_use_first = this.state.inheritOpt(this.name, "et-al-use-first"); 13343 } 13344 if ("boolean" === typeof this.state.tmp["et-al-use-last"]) { 13345 //print(" etal_use_last from tmp: "+this.state.tmp["et-al-use-last"]); 13346 this.etal_use_last = this.state.tmp["et-al-use-last"]; 13347 } else { 13348 //print(" etal_use_last from name: "+this.name.strings["et-al-use-last"]); 13349 // XX 13350 this.etal_use_last = this.state.inheritOpt(this.name, "et-al-use-last"); 13351 } 13352 //print(" etal_use_last: "+this.etal_use_last); 13353 } 13354 // Provided for use as the starting level for disambiguation. 13355 if (!this.state.tmp["et-al-min"]) { 13356 this.state.tmp["et-al-min"] = this.etal_min; 13357 } 13358}; 13359 13360/*global CSL: true */ 13361 13362CSL.NameOutput.prototype.setEtAlParameters = function () { 13363 var i, ilen, j, jlen; 13364 for (i = 0, ilen = this.variables.length; i < ilen; i += 1) { 13365 var v = this.variables[i]; 13366 if ("undefined" === typeof this.etal_spec[v]) { 13367 this.etal_spec[v] = {freeters:0,institutions:0,persons:[]}; 13368 } 13369 this.etal_spec[this.nameset_base + i] = this.etal_spec[v]; 13370 if (this.freeters[v].length) { 13371 this._setEtAlParameter("freeters", v); 13372 } 13373 for (j = 0, jlen = this.persons[v].length; j < jlen; j += 1) { 13374 if ("undefined" === typeof this.etal_spec[v][j]) { 13375 this.etal_spec[v].persons[j] = 0; 13376 } 13377 this._setEtAlParameter("persons", v, j); 13378 } 13379 if (this.institutions[v].length) { 13380 this._setEtAlParameter("institutions", v); 13381 } 13382 } 13383}; 13384 13385CSL.NameOutput.prototype._setEtAlParameter = function (type, v, j) { 13386 var lst, count; 13387 if (type === "persons") { 13388 lst = this.persons[v][j]; 13389 count = this.persons_count[v][j]; 13390 } else { 13391 lst = this[type][v]; 13392 count = this[type + "_count"][v]; 13393 } 13394 if (lst.length < count && !this.state.tmp.sort_key_flag) { 13395 if (this.etal_use_last) { 13396 if (type === "persons") { 13397 this.etal_spec[v].persons[j] = 2; 13398 } else { 13399 this.etal_spec[v][type] = 2; 13400 } 13401 } else { 13402 if (type === "persons") { 13403 this.etal_spec[v].persons[j] = 1; 13404 } else { 13405 this.etal_spec[v][type] = 1; 13406 } 13407 } 13408 } else { 13409 if (type === "persons") { 13410 this.etal_spec[v].persons[j] = 0; 13411 } else { 13412 this.etal_spec[v][type] = 0; 13413 } 13414 } 13415}; 13416 13417/*global CSL: true */ 13418 13419CSL.NameOutput.prototype.renderAllNames = function () { 13420 // Note that et-al/ellipsis parameters are set on the basis 13421 // of rendering order through the whole cite. 13422 var pos; 13423 for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) { 13424 var v = this.variables[i]; 13425 13426 if (this.freeters[v].length || this.institutions[v].length) { 13427 if (!this.state.tmp.group_context.tip.condition) { 13428 this.state.tmp.just_did_number = false; 13429 } 13430 } 13431 13432 pos = this.nameset_base + i; 13433 if (this.freeters[v].length) { 13434 this.freeters[v] = this._renderNames(v, this.freeters[v], pos); 13435 } 13436 for (var j = 0, jlen = this.institutions[v].length; j < jlen; j += 1) { 13437 this.persons[v][j] = this._renderNames(v, this.persons[v][j], pos, j); 13438 } 13439 } 13440 this.renderInstitutionNames(); 13441}; 13442 13443CSL.NameOutput.prototype.renderInstitutionNames = function () { 13444 // Institutions are split to string list as 13445 // this.institutions[v]["long"] and this.institutions[v]["short"] 13446 for (var i = 0, ilen = this.variables.length; i < ilen; i += 1) { 13447 var v = this.variables[i]; 13448 for (var j = 0, jlen = this.institutions[v].length; j < jlen; j += 1) { 13449 var institution; 13450 13451 var name = this.institutions[v][j]; 13452 13453 13454 13455 // XXX Start here for institutions 13456 // Figure out the three segments: primary, secondary, tertiary 13457 var j, jlen, localesets; 13458 if (this.state.tmp.extension) { 13459 localesets = ["sort"]; 13460 } else if (name.isInstitution || name.literal) { 13461 // Will never hit this in this function, but preserving 13462 // in case we factor this out. 13463 localesets = this.state.opt['cite-lang-prefs'].institutions; 13464 } else { 13465 localesets = this.state.opt['cite-lang-prefs'].persons; 13466 } 13467 13468 var slot = {primary:'locale-orig',secondary:false,tertiary:false}; 13469 if (localesets) { 13470 var slotnames = ["primary", "secondary", "tertiary"]; 13471 for (var k = 0, klen = slotnames.length; k < klen; k += 1) { 13472 if (localesets.length - 1 < k) { 13473 break; 13474 } 13475 if (localesets[k]) { 13476 slot[slotnames[k]] = 'locale-' + localesets[k]; 13477 } 13478 } 13479 } else { 13480 slot.primary = 'locale-translat'; 13481 } 13482 if (this.state.tmp.area !== "bibliography" 13483 && !(this.state.tmp.area === "citation" 13484 && this.state.opt.xclass === "note" 13485 && this.item && !this.item.position)) { 13486 13487 slot.secondary = false; 13488 slot.tertiary = false; 13489 } 13490 // Get normalized name object for a start. 13491 // true invokes fallback 13492 this.setRenderedName(name); 13493 13494 // XXXX FROM HERE (instututions) 13495 var institution = this._renderInstitutionName(v, name, slot, j); 13496 13497 //this.institutions[v][j] = this._join(institution, ""); 13498 this.institutions[v][j] = institution; 13499 } 13500 } 13501}; 13502 13503CSL.NameOutput.prototype._renderInstitutionName = function (v, name, slot, j) { 13504 var secondary, tertiary, long_style, short_style, institution, institution_short, institution_long; 13505 var res = this.getName(name, slot.primary, true); 13506 var primary = res.name; 13507 var usedOrig = res.usedOrig; 13508 if (primary) { 13509 //print("primary, v, j = "+primary+", "+v+", "+j); 13510 primary = this.fixupInstitution(primary, v, j); 13511 } 13512 secondary = false; 13513 if (slot.secondary) { 13514 res = this.getName(name, slot.secondary, false, usedOrig); 13515 var secondary = res.name; 13516 usedOrig = res.usedOrig; 13517 if (secondary) { 13518 secondary = this.fixupInstitution(secondary, v, j); 13519 } 13520 } 13521 //Zotero.debug("XXX [2] secondary: "+secondary["long"].literal+", slot.secondary: "+slot.secondary); 13522 tertiary = false; 13523 if (slot.tertiary) { 13524 res = this.getName(name, slot.tertiary, false, usedOrig); 13525 tertiary = res.name; 13526 if (tertiary) { 13527 tertiary = this.fixupInstitution(tertiary, v, j); 13528 } 13529 } 13530 var n = { 13531 l: { 13532 pri: false, 13533 sec: false, 13534 ter: false 13535 }, 13536 s: { 13537 pri: false, 13538 sec: false, 13539 ter: false 13540 } 13541 }; 13542 if (primary) { 13543 n.l.pri = primary["long"]; 13544 n.s.pri = primary["short"].length ? primary["short"] : primary["long"]; 13545 } 13546 if (secondary) { 13547 n.l.sec = secondary["long"]; 13548 n.s.sec = secondary["short"].length ? secondary["short"] : secondary["long"]; 13549 } 13550 if (tertiary) { 13551 n.l.ter = tertiary["long"]; 13552 n.s.ter = tertiary["short"].length ? tertiary["short"] : tertiary["long"]; 13553 } 13554 switch (this.institution.strings["institution-parts"]) { 13555 case "short": 13556 // No multilingual for pure short form institution names. 13557 if (primary["short"].length) { 13558 short_style = this._getShortStyle(); 13559 institution = [this._composeOneInstitutionPart([n.s.pri, n.s.sec, n.s.ter], slot, short_style, v)]; 13560 } else { 13561 // Fail over to long. 13562 long_style = this._getLongStyle(primary, v, j); 13563 institution = [this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v)]; 13564 } 13565 break; 13566 case "short-long": 13567 long_style = this._getLongStyle(primary, v, j); 13568 short_style = this._getShortStyle(); 13569 institution_short = this._renderOneInstitutionPart(primary["short"], short_style); 13570 // true is to include multilingual supplement 13571 institution_long = this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v); 13572 institution = [institution_short, institution_long]; 13573 break; 13574 case "long-short": 13575 long_style = this._getLongStyle(primary, v, j); 13576 short_style = this._getShortStyle(); 13577 institution_short = this._renderOneInstitutionPart(primary["short"], short_style); 13578 // true is to include multilingual supplement 13579 institution_long = this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v); 13580 institution = [institution_long, institution_short]; 13581 break; 13582 default: 13583 long_style = this._getLongStyle(primary, v, j); 13584 // true is to include multilingual supplement 13585 institution = [this._composeOneInstitutionPart([n.l.pri, n.l.sec, n.l.ter], slot, long_style, v)]; 13586 break; 13587 } 13588 var blob = this._join(institution, " "); 13589 if (blob) { 13590 blob.isInstitution = true; 13591 } 13592 this.state.tmp.name_node.children.push(blob); 13593 return blob; 13594}; 13595 13596CSL.NameOutput.prototype._composeOneInstitutionPart = function (names, slot, style) { 13597 var primary = false, secondary = false, tertiary = false, primary_tok, secondary_tok, tertiary_tok; 13598 if (names[0]) { 13599 primary_tok = CSL.Util.cloneToken(style); 13600 if (this.state.opt.citeAffixes[slot.primary]){ 13601 if ("<i>" === this.state.opt.citeAffixes.institutions[slot.primary].prefix) { 13602 var hasItalic = false; 13603 for (var i = 0, ilen = primary_tok.decorations.length; i < ilen; i += 1) { 13604 if (style.decorations[i][0] === "@font-style" 13605 && primary_tok.decorations[i][1] === "italic") { 13606 hasItalic = true; 13607 } 13608 } 13609 if (!hasItalic) { 13610 primary_tok.decorations.push(["@font-style", "italic"]); 13611 } 13612 } 13613 } 13614 primary = this._renderOneInstitutionPart(names[0], primary_tok); 13615 } 13616 if (names[1]) { 13617 secondary = this._renderOneInstitutionPart(names[1], style); 13618 } 13619 if (names[2]) { 13620 tertiary = this._renderOneInstitutionPart(names[2], style); 13621 } 13622 // Compose 13623 var institutionblob; 13624 if (secondary || tertiary) { 13625 this.state.output.openLevel("empty"); 13626 13627 this.state.output.append(primary); 13628 13629 secondary_tok = CSL.Util.cloneToken(style); 13630 if (slot.secondary) { 13631 secondary_tok.strings.prefix = this.state.opt.citeAffixes.institutions[slot.secondary].prefix; 13632 secondary_tok.strings.suffix = this.state.opt.citeAffixes.institutions[slot.secondary].suffix; 13633 // Add a space if empty 13634 if (!secondary_tok.strings.prefix) { 13635 secondary_tok.strings.prefix = " "; 13636 } 13637 } 13638 var secondary_outer = new CSL.Token(); 13639 secondary_outer.decorations.push(["@font-style", "normal"]); 13640 secondary_outer.decorations.push(["@font-weight", "normal"]); 13641 this.state.output.openLevel(secondary_outer); 13642 this.state.output.append(secondary, secondary_tok); 13643 this.state.output.closeLevel(); 13644 13645 tertiary_tok = CSL.Util.cloneToken(style); 13646 if (slot.tertiary) { 13647 tertiary_tok.strings.prefix = this.state.opt.citeAffixes.institutions[slot.tertiary].prefix; 13648 tertiary_tok.strings.suffix = this.state.opt.citeAffixes.institutions[slot.tertiary].suffix; 13649 // Add a space if empty 13650 if (!tertiary_tok.strings.prefix) { 13651 tertiary_tok.strings.prefix = " "; 13652 } 13653 } 13654 var tertiary_outer = new CSL.Token(); 13655 tertiary_outer.decorations.push(["@font-style", "normal"]); 13656 tertiary_outer.decorations.push(["@font-weight", "normal"]); 13657 this.state.output.openLevel(tertiary_outer); 13658 this.state.output.append(tertiary, tertiary_tok); 13659 this.state.output.closeLevel(); 13660 13661 this.state.output.closeLevel(); 13662 13663 institutionblob = this.state.output.pop(); 13664 } else { 13665 institutionblob = primary; 13666 } 13667 return institutionblob; 13668}; 13669 13670CSL.NameOutput.prototype._renderOneInstitutionPart = function (blobs, style) { 13671 for (var i = 0, ilen = blobs.length; i < ilen; i += 1) { 13672 if (blobs[i]) { 13673 var str = blobs[i]; 13674 // XXXXX Cut-and-paste code in multiple locations. This code block should be 13675 // collected in a function. 13676 // Tag: strip-periods-block 13677 if (this.state.tmp.strip_periods) { 13678 str = str.replace(/\./g, ""); 13679 } else { 13680 for (var j = 0, jlen = style.decorations.length; j < jlen; j += 1) { 13681 if ("@strip-periods" === style.decorations[j][0] && "true" === style.decorations[j][1]) { 13682 str = str.replace(/\./g, ""); 13683 break; 13684 } 13685 } 13686 } 13687 //this.state.output.append(blobs[i], style, true); 13688 this.state.tmp.group_context.tip.variable_success = true; 13689 this.state.tmp.can_substitute.replace(false, CSL.LITERAL); 13690 if (str === "!here>>>") { 13691 blobs[i] = false; 13692 } else { 13693 this.state.output.append(str, style, true); 13694 blobs[i] = this.state.output.pop(); 13695 } 13696 } 13697 } 13698 if ("undefined" === typeof this.institution.strings["part-separator"]) { 13699 this.institution.strings["part-separator"] = this.state.tmp.name_delimiter; 13700 } 13701 return this._join(blobs, this.institution.strings["part-separator"]); 13702}; 13703 13704CSL.NameOutput.prototype._renderNames = function (v, values, pos, j) { 13705 // 13706 var ret = false; 13707 if (values.length) { 13708 var names = []; 13709 for (var i = 0, ilen = values.length; i < ilen; i += 1) { 13710 var name = values[i]; 13711 13712 // XXX We'll start here with attempts. 13713 // Figure out the three segments: primary, secondary, tertiary 13714 var ret, localesets; 13715 13716 if (this.state.tmp.extension) { 13717 localesets = ["sort"]; 13718 } else if (name.isInstitution || name.literal) { 13719 // Will never hit this in this function, but preserving 13720 // in case we factor this out. 13721 localesets = this.state.opt['cite-lang-prefs'].institutions; 13722 } else { 13723 localesets = this.state.opt['cite-lang-prefs'].persons; 13724 } 13725 var slot = {primary:'locale-orig',secondary:false,tertiary:false}; 13726 if (localesets) { 13727 var slotnames = ["primary", "secondary", "tertiary"]; 13728 for (var k = 0, klen = slotnames.length; k < klen; k += 1) { 13729 if (localesets.length - 1 < k) { 13730 break; 13731 } 13732 slot[slotnames[k]] = 'locale-' + localesets[k]; 13733 } 13734 } else { 13735 slot.primary = 'locale-translat'; 13736 } 13737 if (this.state.tmp.sort_key_flag || (this.state.tmp.area !== "bibliography" 13738 && !(this.state.tmp.area === "citation" 13739 && this.state.opt.xclass === "note" 13740 && this.item && !this.item.position))) { 13741 13742 slot.secondary = false; 13743 slot.tertiary = false; 13744 } 13745 13746 // primary 13747 // true is for fallback 13748 this.setRenderedName(name); 13749 13750 if (!name.literal && !name.isInstitution) { 13751 var nameBlob = this._renderPersonalName(v, name, slot, pos, i, j); 13752 var nameToken = CSL.Util.cloneToken(this.name); 13753 this.state.output.append(nameBlob, nameToken, true); 13754 names.push(this.state.output.pop()); 13755 } else { 13756 names.push(this._renderInstitutionName(v, name, slot, j)); 13757 } 13758 } 13759 //ret = this._join(names, ""); 13760 ret = this.joinPersons(names, pos, j); 13761 } 13762 return ret; 13763}; 13764 13765 13766CSL.NameOutput.prototype._renderPersonalName = function (v, name, slot, pos, i, j) { 13767 // XXXX FROM HERE (persons) 13768 13769 var res = this.getName(name, slot.primary, true); 13770 var primary = this._renderOnePersonalName(res.name, pos, i, j); 13771 var secondary = false; 13772 if (slot.secondary) { 13773 res = this.getName(name, slot.secondary, false, res.usedOrig); 13774 if (res.name) { 13775 secondary = this._renderOnePersonalName(res.name, pos, i, j); 13776 } 13777 } 13778 var tertiary = false; 13779 if (slot.tertiary) { 13780 res = this.getName(name, slot.tertiary, false, res.usedOrig); 13781 if (res.name) { 13782 tertiary = this._renderOnePersonalName(res.name, pos, i, j); 13783 } 13784 } 13785 // Now compose them to a unit 13786 var personblob; 13787 if (secondary || tertiary) { 13788 13789 this.state.output.openLevel("empty"); 13790 13791 this.state.output.append(primary); 13792 13793 var secondary_tok = new CSL.Token(); 13794 if (slot.secondary) { 13795 secondary_tok.strings.prefix = this.state.opt.citeAffixes.persons[slot.secondary].prefix; 13796 secondary_tok.strings.suffix = this.state.opt.citeAffixes.persons[slot.secondary].suffix; 13797 // Add a space if empty 13798 if (!secondary_tok.strings.prefix) { 13799 secondary_tok.strings.prefix = " "; 13800 } 13801 } 13802 this.state.output.append(secondary, secondary_tok); 13803 13804 var tertiary_tok = new CSL.Token(); 13805 if (slot.tertiary) { 13806 tertiary_tok.strings.prefix = this.state.opt.citeAffixes.persons[slot.tertiary].prefix; 13807 tertiary_tok.strings.suffix = this.state.opt.citeAffixes.persons[slot.tertiary].suffix; 13808 // Add a space if empty 13809 if (!tertiary_tok.strings.prefix) { 13810 tertiary_tok.strings.prefix = " "; 13811 } 13812 } 13813 this.state.output.append(tertiary, tertiary_tok); 13814 13815 this.state.output.closeLevel(); 13816 13817 personblob = this.state.output.pop(); 13818 } else { 13819 personblob = primary; 13820 } 13821 return personblob; 13822}; 13823 13824CSL.NameOutput.prototype._isRomanesque = function (name) { 13825 // 0 = entirely non-romanesque 13826 // 1 = mixed content 13827 // 2 = pure romanesque 13828 var ret = 2; 13829 if (!name.family.replace(/\"/g, '').match(CSL.ROMANESQUE_REGEXP)) { 13830 ret = 0; 13831 } 13832 if (!ret && name.given && name.given.match(CSL.STARTSWITH_ROMANESQUE_REGEXP)) { 13833 ret = 1; 13834 } 13835 var top_locale; 13836 if (ret == 2) { 13837 if (name.multi && name.multi.main) { 13838 top_locale = name.multi.main.slice(0, 2); 13839 } else if (this.Item.language) { 13840 top_locale = this.Item.language.slice(0, 2); 13841 } 13842 if (["ja", "zh"].indexOf(top_locale) > -1) { 13843 ret = 1; 13844 } 13845 } 13846 //print("name: "+name.given+", multi: "+name.multi+", ret: "+ret); 13847 return ret; 13848}; 13849 13850CSL.NameOutput.prototype._renderOnePersonalName = function (value, pos, i, j) { 13851 var name = value; 13852 var dropping_particle = this._droppingParticle(name, pos, j); 13853 var family = this._familyName(name); 13854 var non_dropping_particle = this._nonDroppingParticle(name); 13855 var givenInfo = this._givenName(name, pos, i); 13856 var given = givenInfo.blob; 13857 var suffix = this._nameSuffix(name); 13858 if (given === false) { 13859 dropping_particle = false; 13860 suffix = false; 13861 } 13862 var sort_sep = this.state.inheritOpt(this.name, "sort-separator"); 13863 if (!sort_sep) { 13864 sort_sep = ""; 13865 } 13866 var suffix_sep; 13867 if (name["comma-suffix"]) { 13868 suffix_sep = ", "; 13869 } else { 13870 suffix_sep = " "; 13871 } 13872 var romanesque = this._isRomanesque(name); 13873 function hasJoiningPunctuation(blob) { 13874 if (!blob) { 13875 return false; 13876 } else if ("string" === typeof blob.blobs) { 13877 if (["\u2019", "\'", "-", " "].indexOf(blob.blobs.slice(-1)) > -1) { 13878 return true; 13879 } else { 13880 return false; 13881 } 13882 } else { 13883 return hasJoiningPunctuation(blob.blobs[blob.blobs.length-1]); 13884 } 13885 } 13886 13887 var has_hyphenated_non_dropping_particle = hasJoiningPunctuation(non_dropping_particle); 13888 13889 var nbspace; 13890 if (["fr", "ru", "cs"].indexOf(this.state.opt["default-locale"][0].slice(0, 2)) > -1) { 13891 nbspace = "\u00a0"; 13892 } else { 13893 nbspace = " "; 13894 } 13895 13896 var blob, merged, first, second; 13897 if (romanesque === 0) { 13898 // XXX handle affixes for given and family 13899 blob = this._join([non_dropping_particle, family, given], ""); 13900 } else if (romanesque === 1 || name["static-ordering"]) { // entry likes sort order 13901 merged = this._join([non_dropping_particle, family], nbspace); 13902 blob = this._join([merged, given], " "); 13903 } else if (name["reverse-ordering"]) { // entry likes reverse order 13904 merged = this._join([non_dropping_particle, family], nbspace); 13905 blob = this._join([given, merged], " "); 13906 } else if (this.state.tmp.sort_key_flag) { 13907 // ok with no affixes here 13908 if (this.state.opt["demote-non-dropping-particle"] === "never") { 13909 merged = this._join([non_dropping_particle, family], nbspace); 13910 merged = this._join([merged, dropping_particle], " "); 13911 merged = this._join([merged, given], this.state.opt.sort_sep); 13912 blob = this._join([merged, suffix], " "); 13913 } else { 13914 second = this._join([given, dropping_particle, non_dropping_particle], " "); 13915 merged = this._join([family, second], this.state.opt.sort_sep); 13916 blob = this._join([merged, suffix], " "); 13917 } 13918 } else if (this.state.inheritOpt(this.name, "name-as-sort-order") === "all" || (this.state.inheritOpt(this.name, "name-as-sort-order") === "first" && i === 0 && (j === 0 || "undefined" === typeof j))) { 13919 // 13920 // Discretionary sort ordering and inversions 13921 // 13922 if (["Lord", "Lady"].indexOf(name.given) > -1) { 13923 sort_sep = ", "; 13924 } 13925 13926 // XXX Needs a more robust solution than this 13927 // XXX See https://forums.zotero.org/discussion/30974/any-idea-why-an-a-author-comes-last-in-the-bibliography/#Item_30 13928 13929 //if (["always", "display-and-sort"].indexOf(this.state.opt["demote-non-dropping-particle"]) > -1 && !has_hyphenated_non_dropping_particle) { 13930 if (["always", "display-and-sort"].indexOf(this.state.opt["demote-non-dropping-particle"]) > -1) { 13931 // Drop non-dropping particle 13932 //second = this._join([given, dropping_particle, non_dropping_particle], " "); 13933 second = this._join([given, dropping_particle], (name["comma-dropping-particle"] + " ")); 13934 13935 // This would be a problem with al-Ghazali. Avoided by has_hyphenated_non_dropping_particle check above. 13936 second = this._join([second, non_dropping_particle], " "); 13937 if (second && this.given) { 13938 second.strings.prefix = this.given.strings.prefix; 13939 second.strings.suffix = this.given.strings.suffix; 13940 } 13941 if (family && this.family) { 13942 family.strings.prefix = this.family.strings.prefix; 13943 family.strings.suffix = this.family.strings.suffix; 13944 } 13945 merged = this._join([family, second], sort_sep); 13946 blob = this._join([merged, suffix], sort_sep); 13947 } else { 13948 // Don't drop particle. 13949 // Don't do this 13950 //if (this.state.tmp.area === "bibliography" && !this.state.tmp.term_predecessor && non_dropping_particle) { 13951 // if (!has_hyphenated_non_dropping_particle) { 13952 // non_dropping_particle.blobs = CSL.Output.Formatters["capitalize-first"](this.state, non_dropping_particle.blobs) 13953 // } 13954 //} 13955 if (has_hyphenated_non_dropping_particle) { 13956 first = this._join([non_dropping_particle, family], ""); 13957 } else { 13958 first = this._join([non_dropping_particle, family], nbspace); 13959 } 13960 if (first && this.family) { 13961 first.strings.prefix = this.family.strings.prefix; 13962 first.strings.suffix = this.family.strings.suffix; 13963 } 13964 13965 second = this._join([given, dropping_particle], (name["comma-dropping-particle"] + " ")); 13966 //second = this._join([given, dropping_particle], " "); 13967 if (second && this.given) { 13968 second.strings.prefix = this.given.strings.prefix; 13969 second.strings.suffix = this.given.strings.suffix; 13970 } 13971 13972 merged = this._join([first, second], sort_sep); 13973 blob = this._join([merged, suffix], sort_sep); 13974 } 13975 blob.isInverted = true; 13976 } else { // plain vanilla 13977 if (name["dropping-particle"] && name.family && !name["non-dropping-particle"]) { 13978 var dp = name["dropping-particle"]; 13979 var apostrophes = ["'","\u02bc","\u2019","-"]; 13980 if (apostrophes.indexOf(dp.slice(-1)) > -1 && dp.slice(0, -1) !== "de") { 13981 family = this._join([dropping_particle, family], ""); 13982 dropping_particle = false; 13983 } 13984 } 13985 13986 if (has_hyphenated_non_dropping_particle) { 13987 second = this._join([non_dropping_particle, family], ""); 13988 second = this._join([dropping_particle, second], nbspace); 13989 } else { 13990 second = this._join([dropping_particle, non_dropping_particle, family], nbspace); 13991 } 13992 second = this._join([second, suffix], suffix_sep); 13993 if (second && this.family) { 13994 second.strings.prefix = this.family.strings.prefix; 13995 second.strings.suffix = this.family.strings.suffix; 13996 } 13997 if (given && this.given) { 13998 given.strings.prefix = this.given.strings.prefix; 13999 given.strings.suffix = this.given.strings.suffix; 14000 } 14001 if (second.strings.prefix) { 14002 name["comma-dropping-particle"] = ""; 14003 } 14004 14005 var space; 14006 if (this.state.inheritOpt(this.name, "initialize-with") 14007 && this.state.inheritOpt(this.name, "initialize-with").match(/[\u00a0\ufeff]/) 14008 && givenInfo.initializationLevel === 1) { 14009 14010 space = nbspace; 14011 } else { 14012 space = " "; 14013 } 14014 blob = this._join([given, second], (name["comma-dropping-particle"] + space)); 14015 } 14016 // XXX Just generally assume for the present that personal names render something 14017 this.state.tmp.group_context.tip.variable_success = true; 14018 this.state.tmp.can_substitute.replace(false, CSL.LITERAL); 14019 this.state.tmp.term_predecessor = true; 14020 // notSerious 14021 //this.state.output.append(blob, "literal", true); 14022 //var ret = this.state.output.pop(); 14023 this.state.tmp.name_node.children.push(blob); 14024 return blob; 14025}; 14026 14027/* 14028 // Do not include given name, dropping particle or suffix in strict short form of name 14029 14030 // initialize if appropriate 14031*/ 14032 14033// Input names should be touched by _normalizeNameInput() 14034// exactly once: this is not idempotent. 14035CSL.NameOutput.prototype._normalizeNameInput = function (value) { 14036 var name = { 14037 literal:value.literal, 14038 family:value.family, 14039 isInstitution:value.isInstitution, 14040 given:value.given, 14041 suffix:value.suffix, 14042 "comma-suffix":value["comma-suffix"], 14043 "non-dropping-particle":value["non-dropping-particle"], 14044 "dropping-particle":value["dropping-particle"], 14045 "static-ordering":value["static-ordering"], 14046 "static-particles":value["static-particles"], 14047 "reverse-ordering":value["reverse-ordering"], 14048 "full-form-always": value["full-form-always"], 14049 "parse-names":value["parse-names"], 14050 "comma-dropping-particle": "", 14051 block_initialize:value.block_initialize, 14052 multi:value.multi 14053 }; 14054 this._parseName(name); 14055 return name; 14056}; 14057 14058// _transformNameset() replaced with enhanced transform.name(). 14059 14060CSL.NameOutput.prototype._stripPeriods = function (tokname, str) { 14061 var decor_tok = this[tokname + "_decor"]; 14062 if (str) { 14063 if (this.state.tmp.strip_periods) { 14064 str = str.replace(/\./g, ""); 14065 } else if (decor_tok) { 14066 for (var i = 0, ilen = decor_tok.decorations.length; i < ilen; i += 1) { 14067 if ("@strip-periods" === decor_tok.decorations[i][0] && "true" === decor_tok.decorations[i][1]) { 14068 str = str.replace(/\./g, ""); 14069 break; 14070 } 14071 } 14072 } 14073 } 14074 return str; 14075}; 14076 14077CSL.NameOutput.prototype._nonDroppingParticle = function (name) { 14078 var ndp = name["non-dropping-particle"]; 14079 if (ndp && this.state.tmp.sort_key_flag) { 14080 ndp = ndp.replace(/[\'\u2019]/, ""); 14081 } 14082 var str = this._stripPeriods("family", ndp); 14083 if (this.state.output.append(str, this.family_decor, true)) { 14084 return this.state.output.pop(); 14085 } 14086 return false; 14087}; 14088 14089CSL.NameOutput.prototype._droppingParticle = function (name, pos, j) { 14090 var dp = name["dropping-particle"]; 14091 if (dp && this.state.tmp.sort_key_flag) { 14092 dp = dp.replace(/[\'\u2019]/, ""); 14093 } 14094 var str = this._stripPeriods("given", dp); 14095 if (name["dropping-particle"] && name["dropping-particle"].match(/^et.?al[^a-z]$/)) { 14096 if (this.state.inheritOpt(this.name, "et-al-use-last")) { 14097 if ("undefined" === typeof j) { 14098 this.etal_spec[pos].freeters = 2; 14099 } else { 14100 this.etal_spec[pos].persons = 2; 14101 } 14102 } else { 14103 if ("undefined" === typeof j) { 14104 this.etal_spec[pos].freeters = 1; 14105 } else { 14106 this.etal_spec[pos].persons = 1; 14107 } 14108 } 14109 name["comma-dropping-particle"] = ""; 14110 } else if (this.state.output.append(str, this.given_decor, true)) { 14111 return this.state.output.pop(); 14112 } 14113 return false; 14114}; 14115 14116CSL.NameOutput.prototype._familyName = function (name) { 14117 var str = this._stripPeriods("family", name.family); 14118 if (this.state.output.append(str, this.family_decor, true)) { 14119 return this.state.output.pop(); 14120 } 14121 return false; 14122}; 14123 14124CSL.NameOutput.prototype._givenName = function (name, pos, i) { 14125 var ret; 14126 // citation 14127 // use disambig as-is 14128 // biblography 14129 // use disambig only if it boosts over the default 14130 // SO WHAT IS THE DEFAULT? 14131 // A: If "form" is short, it's 0. 14132 // If "form" is long, initialize-with exists (and initialize is not false) it's 1 14133 // If "form" is long, and initialize_with does not exist, it's 2. 14134 var formIsShort = this.state.inheritOpt(this.name, "form", "name-form", "long") !== "long"; 14135 var initializeIsTurnedOn = this.state.inheritOpt(this.name, "initialize") !== false; 14136 var hasInitializeWith = "string" === typeof this.state.inheritOpt(this.name, "initialize-with") && !name.block_initialize; 14137 var defaultLevel; 14138 var useLevel; 14139 if (name["full-form-always"]) { 14140 useLevel = 2; 14141 } else { 14142 if (formIsShort) { 14143 defaultLevel = 0; 14144 } else if (hasInitializeWith) { 14145 defaultLevel = 1; 14146 } else { 14147 defaultLevel = 2; 14148 } 14149 var requestedLevel = this.state.tmp.disambig_settings.givens[pos][i]; 14150 if (requestedLevel > defaultLevel) { 14151 useLevel = requestedLevel; 14152 } else { 14153 useLevel = defaultLevel; 14154 } 14155 } 14156 var gdropt = this.state.citation.opt["givenname-disambiguation-rule"]; 14157 if (gdropt && gdropt.slice(-14) === "-with-initials") { 14158 hasInitializeWith = true; 14159 } 14160 if (name.family && useLevel === 1) { 14161 if (hasInitializeWith) { 14162 var initialize_with = this.state.inheritOpt(this.name, "initialize-with", false, ""); 14163 name.given = CSL.Util.Names.initializeWith(this.state, name.given, initialize_with, !initializeIsTurnedOn); 14164 } else { 14165 name.given = CSL.Util.Names.unInitialize(this.state, name.given); 14166 } 14167 } else if (useLevel === 0) { 14168 return { 14169 blob: false 14170 } 14171 } else if (useLevel === 2) { 14172 name.given = CSL.Util.Names.unInitialize(this.state, name.given); 14173 } 14174 14175 var str = this._stripPeriods("given", name.given); 14176 var rendered = this.state.output.append(str, this.given_decor, true); 14177 if (rendered) { 14178 ret = this.state.output.pop(); 14179 return { 14180 blob: ret, 14181 initializationLevel: useLevel 14182 }; 14183 } 14184 return { 14185 blob: false 14186 }; 14187}; 14188 14189CSL.NameOutput.prototype._nameSuffix = function (name) { 14190 14191 var str = name.suffix, ret; 14192 14193 if (str && "string" === typeof this.state.inheritOpt(this.name, "initialize-with")) { 14194 str = CSL.Util.Names.initializeWith(this.state, str, this.state.inheritOpt(this.name, "initialize-with"), true); 14195 } 14196 14197 str = this._stripPeriods("family", str); 14198 var toSuffix = ''; 14199 if (str && str.slice(-1) === '.') { 14200 str = str.slice(0, -1); 14201 toSuffix = '.'; 14202 } 14203 var rendered = this.state.output.append(str, "empty", true); 14204 if (rendered) { 14205 ret = this.state.output.pop(); 14206 ret.strings.suffix = toSuffix + ret.strings.suffix; 14207 return ret; 14208 } 14209 return false; 14210}; 14211 14212CSL.NameOutput.prototype._getLongStyle = function (name) { 14213 var long_style; 14214 if (name["short"].length) { 14215 if (this.institutionpart["long-with-short"]) { 14216 long_style = this.institutionpart["long-with-short"]; 14217 } else { 14218 long_style = this.institutionpart["long"]; 14219 } 14220 } else { 14221 long_style = this.institutionpart["long"]; 14222 } 14223 if (!long_style) { 14224 long_style = new CSL.Token(); 14225 } 14226 return long_style; 14227}; 14228 14229CSL.NameOutput.prototype._getShortStyle = function () { 14230 var short_style; 14231 if (this.institutionpart["short"]) { 14232 short_style = this.institutionpart["short"]; 14233 } else { 14234 short_style = new CSL.Token(); 14235 } 14236 return short_style; 14237}; 14238 14239CSL.NameOutput.prototype._parseName = function (name) { 14240 if (!name["parse-names"] && "undefined" !== typeof name["parse-names"]) { 14241 return name; 14242 } 14243 if (name.family && !name.given && name.isInstitution) { 14244 name.literal = name.family; 14245 name.family = undefined; 14246 name.isInstitution = undefined; 14247 } 14248 var noparse; 14249 if (name.family 14250 && (name.family.slice(0, 1) === '"' && name.family.slice(-1) === '"') 14251 || (!name["parse-names"] && "undefined" !== typeof name["parse-names"])) { 14252 14253 name.family = name.family.slice(1, -1); 14254 noparse = true; 14255 name["parse-names"] = 0; 14256 } else { 14257 noparse = false; 14258 } 14259 if (this.state.opt.development_extensions.parse_names) { 14260 if (!name["non-dropping-particle"] && name.family && !noparse && name.given) { 14261 if (!name["static-particles"]) { 14262 CSL.parseParticles(name, true); 14263 } 14264 } 14265 } 14266}; 14267 14268/* 14269 * Return a single name object 14270 */ 14271 14272// The interface is a mess, but this should serve. 14273 14274CSL.NameOutput.prototype.getName = function (name, slotLocaleset, fallback, stopOrig) { 14275 14276 // Needs to tell us whether we used orig or not. 14277 14278 if (stopOrig && slotLocaleset === 'locale-orig') { 14279 return {name:false,usedOrig:stopOrig}; 14280 } 14281 14282 // Normalize to string 14283 if (!name.family) { 14284 name.family = ""; 14285 } 14286 if (!name.given) { 14287 name.given = ""; 14288 } 14289 14290 // Recognized params are: 14291 // block-initialize 14292 // transliterated 14293 // static-ordering 14294 // full-form-always 14295 // All default to false, except for static-ordering, which is initialized 14296 // with a sniff. 14297 var name_params = {}; 14298 // Determines the default static-order setting based on the characters 14299 // used in the headline field. Will be overridden by locale-based 14300 // parameters evaluated against explicit lang tags set on the (sub)field. 14301 name_params["static-ordering"] = this.getStaticOrder(name); 14302 14303 var foundTag = true; 14304 var langTag; 14305 if (slotLocaleset !== 'locale-orig') { 14306 foundTag = false; 14307 if (name.multi) { 14308 var langTags = this.state.opt[slotLocaleset]; 14309 for (var i = 0, ilen = langTags.length; i < ilen; i += 1) { 14310 langTag = langTags[i]; 14311 if (name.multi._key[langTag]) { 14312 foundTag = true; 14313 var isInstitution = name.isInstitution; 14314 name = name.multi._key[langTag]; 14315 name.isInstitution = isInstitution; 14316 // Set name formatting params 14317 name_params = this.getNameParams(langTag); 14318 name_params.transliterated = true; 14319 break; 14320 } 14321 } 14322 } 14323 } 14324 14325 if (!foundTag) { 14326 langTag = false; 14327 if (name.multi && name.multi.main) { 14328 langTag = name.multi.main; 14329 } else if (this.Item.language) { 14330 langTag = this.Item.language; 14331 } 14332 if (langTag) { 14333 name_params = this.getNameParams(langTag); 14334 } 14335 } 14336 14337 if (!fallback && !foundTag) { 14338 return {name:false,usedOrig:stopOrig}; 14339 } 14340 14341 // Normalize to string (again) 14342 if (!name.family) { 14343 name.family = ""; 14344 } 14345 if (!name.given) { 14346 name.given = ""; 14347 } 14348 if (name.literal) { 14349 delete name.family; 14350 delete name.given; 14351 } 14352 // var clone the item before writing into it 14353 name = { 14354 family:name.family, 14355 given:name.given, 14356 "non-dropping-particle":name["non-dropping-particle"], 14357 "dropping-particle":name["dropping-particle"], 14358 suffix:name.suffix, 14359 "static-ordering":name_params["static-ordering"], 14360 "static-particles":name["static-particles"], 14361 "reverse-ordering":name_params["reverse-ordering"], 14362 "full-form-always": name_params["full-form-always"], 14363 "parse-names":name["parse-names"], 14364 "comma-suffix":name["comma-suffix"], 14365 "comma-dropping-particle":name["comma-dropping-particle"], 14366 transliterated: name_params.transliterated, 14367 block_initialize: name_params["block-initialize"], 14368 literal:name.literal, 14369 isInstitution:name.isInstitution, 14370 multi:name.multi 14371 }; 14372 14373 if (!name.literal && (!name.given && name.family && name.isInstitution)) { 14374 name.literal = name.family; 14375 } 14376 if (name.literal) { 14377 delete name.family; 14378 delete name.given; 14379 } 14380 name = this._normalizeNameInput(name); 14381 var usedOrig; 14382 if (stopOrig) { 14383 usedOrig = stopOrig; 14384 } else { 14385 usedOrig = !foundTag; 14386 } 14387 return {name:name,usedOrig:usedOrig}; 14388}; 14389 14390CSL.NameOutput.prototype.getNameParams = function (langTag) { 14391 var ret = {}; 14392 var langspec = CSL.localeResolve(this.Item.language, this.state.opt["default-locale"][0]); 14393 var try_locale = this.state.locale[langspec.best] ? langspec.best : this.state.opt["default-locale"][0]; 14394 var name_as_sort_order = this.state.locale[try_locale].opts["name-as-sort-order"]; 14395 var name_as_reverse_order = this.state.locale[try_locale].opts["name-as-reverse-order"]; 14396 var name_never_short = this.state.locale[try_locale].opts["name-never-short"]; 14397 var field_lang_bare = langTag.split("-")[0]; 14398 if (name_as_sort_order && name_as_sort_order[field_lang_bare]) { 14399 ret["static-ordering"] = true; 14400 ret["reverse-ordering"] = false; 14401 } 14402 if (name_as_reverse_order && name_as_reverse_order[field_lang_bare]) { 14403 ret["reverse-ordering"] = true; 14404 ret["static-ordering"] = false; 14405 } 14406 if (name_never_short && name_never_short[field_lang_bare]) { 14407 ret["full-form-always"] = true; 14408 } 14409 14410 if (ret["static-ordering"]) { 14411 ret["block-initialize"] = true; 14412 } 14413 return ret; 14414}; 14415 14416CSL.NameOutput.prototype.setRenderedName = function (name) { 14417 if (this.state.tmp.area === "bibliography") { 14418 var strname = ""; 14419 for (var j=0,jlen=CSL.NAME_PARTS.length;j<jlen;j+=1) { 14420 if (name[CSL.NAME_PARTS[j]]) { 14421 strname += name[CSL.NAME_PARTS[j]]; 14422 } 14423 } 14424 this.state.tmp.rendered_name.push(strname); 14425 } 14426}; 14427 14428CSL.NameOutput.prototype.fixupInstitution = function (name, varname, listpos) { 14429 if (!name.literal && name.family) { 14430 name.literal = name.family; 14431 delete name.family; 14432 } 14433 var longNameStr = name.literal; 14434 var shortNameStr = longNameStr; 14435 var ret = { 14436 "long": longNameStr.split(/\s*\|\s*/), 14437 "short": shortNameStr.split(/\s*\|\s*/), 14438 }; 14439 if (this.state.sys.getAbbreviation) { 14440 // Normalize longNameStr and shortNameStr 14441 if (this.institution.strings.form === "short") { 14442 let jurisdiction = this.Item.jurisdiction; 14443 jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-entire", longNameStr, this.Item.language); 14444 if (this.state.transform.abbrevs[jurisdiction]["institution-entire"][longNameStr]) { 14445 longNameStr = this.state.transform.abbrevs[jurisdiction]["institution-entire"][longNameStr]; 14446 } else { 14447 jurisdiction = this.Item.jurisdiction; 14448 jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", longNameStr, this.Item.language); 14449 if (this.state.transform.abbrevs[jurisdiction]["institution-part"][longNameStr]) { 14450 longNameStr = this.state.transform.abbrevs[jurisdiction]["institution-part"][longNameStr]; 14451 } 14452 } 14453 longNameStr = this._quashChecks(jurisdiction, longNameStr); 14454 } 14455 if (["short", "short-long", "long-short"].indexOf(this.institution.strings["institution-parts"]) > -1) { 14456 let jurisdiction = this.Item.jurisdiction; 14457 jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", shortNameStr, this.Item.language); 14458 if (this.state.transform.abbrevs[jurisdiction]["institution-part"][shortNameStr]) { 14459 shortNameStr = this.state.transform.abbrevs[jurisdiction]["institution-part"][shortNameStr]; 14460 } 14461 shortNameStr = this._quashChecks(jurisdiction, shortNameStr); 14462 if (["short-long", "long-short"].indexOf(this.institution.strings["institution-parts"]) > -1) { 14463 if (shortNameStr === longNameStr) { 14464 shortNameStr = ""; 14465 } 14466 } 14467 } 14468 // Split abbreviated strings 14469 // For pure long, split and we're done. 14470 ret["long"] = longNameStr.split(/\s*\|\s*/); 14471 // For short, split and then try abbrev with institution-part on each element 14472 ret["short"] = shortNameStr.split(/\s*\|\s*/); 14473 if (["short", "short-long", "long-short"].indexOf(this.institution.strings["institution-parts"]) > -1) { 14474 for (var j=ret["short"].length-1; j>-1; j--) { 14475 let jurisdiction = this.Item.jurisdiction; 14476 var abbrevKey = ret["short"][j]; 14477 jurisdiction = this.state.transform.loadAbbreviation(jurisdiction, "institution-part", abbrevKey, this.Item.language); 14478 if (this.state.transform.abbrevs[jurisdiction]["institution-part"][abbrevKey]) { 14479 ret["short"][j] = this.state.transform.abbrevs[jurisdiction]["institution-part"][abbrevKey]; 14480 } 14481 if (ret["short"][j].indexOf("|") > -1) { 14482 let retShort = ret["short"]; 14483 let splitShort = retShort[j].split(/\s*\|\s*/); 14484 ret["short"] = retShort.slice(0, j).concat(splitShort).concat(retShort.slice(j+1)); 14485 } 14486 } 14487 } 14488 if (this.state.opt.development_extensions.legacy_institution_name_ordering) { 14489 ret["short"].reverse(); 14490 } 14491 ret["short"] = this._trimInstitution(ret["short"]); 14492 if (this.institution.strings["reverse-order"]) { 14493 ret["short"].reverse(); 14494 } 14495 // trimmer is not available in getAmbiguousCite 14496 if (!this.state.tmp.just_looking) { 14497 if (this.Item.jurisdiction) { 14498 let jurisdiction = this.Item.jurisdiction; 14499 var trimmer = this.state.tmp.abbrev_trimmer; 14500 if (trimmer && trimmer[jurisdiction] && trimmer[jurisdiction][varname]) { 14501 for (var i=0,ilen=ret["short"].length;i<ilen;i++) { 14502 var frag = ret["short"][i]; 14503 ret["short"][i] = frag.replace(trimmer[jurisdiction][varname], "").trim(); 14504 } 14505 } 14506 } 14507 } 14508 } 14509 if (this.state.opt.development_extensions.legacy_institution_name_ordering) { 14510 ret["long"].reverse(); 14511 } 14512 ret["long"] = this._trimInstitution(ret["long"]); 14513 if (this.institution.strings["reverse-order"]) { 14514 ret["long"].reverse(); 14515 } 14516 14517 return ret; 14518}; 14519 14520 14521CSL.NameOutput.prototype.getStaticOrder = function (name, refresh) { 14522 var static_ordering_val = false; 14523 if (!refresh && name["static-ordering"]) { 14524 static_ordering_val = true; 14525 } else if (this._isRomanesque(name) === 0) { 14526 static_ordering_val = true; 14527 } else if ((!name.multi || !name.multi.main) && this.Item.language && ['vi', 'hu'].indexOf(this.Item.language) > -1) { 14528 static_ordering_val = true; 14529 } else if (name.multi && name.multi.main && ['vi', 'hu'].indexOf(name.multi.main.slice(0,2)) > -1) { 14530 static_ordering_val = true; 14531 } else { 14532 if (this.state.opt['auto-vietnamese-names'] 14533 && (CSL.VIETNAMESE_NAMES.exec(name.family + " " + name.given) 14534 && CSL.VIETNAMESE_SPECIALS.exec(name.family + name.given))) { 14535 14536 static_ordering_val = true; 14537 } 14538 } 14539 return static_ordering_val; 14540}; 14541 14542CSL.NameOutput.prototype._quashChecks = function (jurisdiction, str) { 14543 var str = this.state.transform.quashCheck(jurisdiction, str); 14544 // If the abbreviation has date cut-offs, find the most recent 14545 // abbreviation within scope. 14546 var lst = str.split(/>>[0-9]{4}>>/); 14547 var m = str.match(/>>([0-9]{4})>>/); 14548 str = lst.pop(); 14549 var date = this.Item["original-date"] ? this.Item["original-date"] : this.Item["issued"]; 14550 if (date) { 14551 date = parseInt(date.year, 10); 14552 date = isNaN(date) ? false : date; 14553 } 14554 if (date) { 14555 if (lst.length > 0) { 14556 for (var k=m.length-1; k>0; k--) { 14557 if (date >= parseInt(m[k], 10)) { 14558 break; 14559 } 14560 str = lst.pop(); 14561 } 14562 } 14563 str = str.replace(/\s*\|\s*/g, "|"); 14564 } 14565 return str; 14566} 14567 14568CSL.NameOutput.prototype._trimInstitution = function (subunits) { 14569 // Oh! Good catch in the tests. This happens before abbrevs. 14570 // Won't work that way. Need to do abbrev substitute first, 14571 // and apply this separately to long and to short. 14572 var use_first = false; 14573 var stop_last = false; 14574 var use_last = false; 14575 var stop_first = false; 14576 var s = subunits.slice(); 14577 if (this.institution) { 14578 // If use_first, apply stop_last, then apply use_first; 14579 // If use_last, apply stop_first, then apply use_last; 14580 if ("undefined" !== typeof this.institution.strings["use-first"]) { 14581 // this.state.sys.print("use-first OK"); 14582 use_first = this.institution.strings["use-first"]; 14583 } 14584 if ("undefined" !== typeof this.institution.strings["use-last"]) { 14585 // this.state.sys.print("use-last OK"); 14586 use_last = this.institution.strings["use-last"]; 14587 } 14588 if ("undefined" !== typeof this.institution.strings["stop-first"]) { 14589 // this.state.sys.print("stop-first OK"); 14590 stop_first = this.institution.strings["stop-first"]; 14591 } 14592 if ("undefined" !== typeof this.institution.strings["stop-last"]) { 14593 stop_last = this.institution.strings["stop-last"]; 14594 } 14595 14596 if (use_first) { 14597 if (stop_last) { 14598 s = s.slice(0, stop_last * -1); 14599 } 14600 s = s.slice(0, use_first); 14601 } 14602 14603 if (use_last) { 14604 var ss = subunits.slice(); 14605 if (use_first) { 14606 stop_first = use_first; 14607 } else { 14608 s = []; 14609 } 14610 if (stop_first) { 14611 ss = ss.slice(stop_first); 14612 } 14613 ss = ss.slice(use_last * -1); 14614 s = s.concat(ss); 14615 } 14616 subunits = s; 14617 } 14618 return subunits; 14619}; 14620 14621/*global CSL: true */ 14622 14623CSL.PublisherOutput = function (state, group_tok) { 14624 this.state = state; 14625 this.group_tok = group_tok; 14626 this.varlist = []; 14627}; 14628 14629CSL.PublisherOutput.prototype.render = function () { 14630 this.clearVars(); 14631 this.composeAndBlob(); 14632 this.composeElements(); 14633 this.composePublishers(); 14634 this.joinPublishers(); 14635}; 14636 14637 14638// XXX Figure out how to adapt this to the House of Lords / House of Commons 14639// joint committee case 14640 14641// name_delimiter 14642// delimiter_precedes_last 14643// and 14644 14645CSL.PublisherOutput.prototype.composeAndBlob = function () { 14646 this.and_blob = {}; 14647 var and_term = false; 14648 if (this.group_tok.strings.and === "text") { 14649 and_term = this.state.getTerm("and"); 14650 } else if (this.group_tok.strings.and === "symbol") { 14651 and_term = "&"; 14652 } 14653 var tok = new CSL.Token(); 14654 tok.strings.suffix = " "; 14655 tok.strings.prefix = " "; 14656 this.state.output.append(and_term, tok, true); 14657 var no_delim = this.state.output.pop(); 14658 14659 tok.strings.prefix = this.group_tok.strings["subgroup-delimiter"]; 14660 this.state.output.append(and_term, tok, true); 14661 var with_delim = this.state.output.pop(); 14662 14663 this.and_blob.single = false; 14664 this.and_blob.multiple = false; 14665 if (and_term) { 14666 if (this.group_tok.strings["subgroup-delimiter-precedes-last"] === "always") { 14667 this.and_blob.single = with_delim; 14668 } else if (this.group_tok.strings["subgroup-delimiter-precedes-last"] === "never") { 14669 this.and_blob.single = no_delim; 14670 this.and_blob.multiple = no_delim; 14671 } else { 14672 this.and_blob.single = no_delim; 14673 this.and_blob.multiple = with_delim; 14674 } 14675 } 14676}; 14677 14678 14679CSL.PublisherOutput.prototype.composeElements = function () { 14680 for (var i = 0, ilen = 2; i < ilen; i += 1) { 14681 var varname = ["publisher", "publisher-place"][i]; 14682 for (var j = 0, jlen = this["publisher-list"].length; j < jlen; j += 1) { 14683 var str = this[varname + "-list"][j]; 14684 var tok = this[varname + "-token"]; 14685 // notSerious 14686 this.state.output.append(str, tok, true); 14687 this[varname + "-list"][j] = this.state.output.pop(); 14688 } 14689 } 14690}; 14691 14692 14693CSL.PublisherOutput.prototype.composePublishers = function () { 14694 var blobs; 14695 for (var i = 0, ilen = this["publisher-list"].length; i < ilen; i += 1) { 14696 blobs = [this[this.varlist[0] + "-list"][i], this[this.varlist[1] + "-list"][i]]; 14697 this["publisher-list"][i] = this._join(blobs, this.group_tok.strings.delimiter); 14698 } 14699}; 14700 14701 14702CSL.PublisherOutput.prototype.joinPublishers = function () { 14703 var blobs = this["publisher-list"]; 14704 var publishers = this._join(blobs, this.group_tok.strings["subgroup-delimiter"], this.and_blob.single, this.and_blob.multiple, this.group_tok); 14705 this.state.output.append(publishers, "literal"); 14706}; 14707 14708 14709// blobs, delimiter, single, multiple, tokenname 14710// Tokenname is a key at top level of this object. 14711CSL.PublisherOutput.prototype._join = CSL.NameOutput.prototype._join; 14712CSL.PublisherOutput.prototype._getToken = CSL.NameOutput.prototype._getToken; 14713 14714 14715CSL.PublisherOutput.prototype.clearVars = function () { 14716 this.state.tmp["publisher-list"] = false; 14717 this.state.tmp["publisher-place-list"] = false; 14718 this.state.tmp["publisher-group-token"] = false; 14719 this.state.tmp["publisher-token"] = false; 14720 this.state.tmp["publisher-place-token"] = false; 14721}; 14722 14723/*global CSL: true */ 14724 14725CSL.evaluateLabel = function (node, state, Item, item) { 14726 var myterm; 14727 if ("locator" === node.strings.term) { 14728 if (item && item.label) { 14729 if (item.label === "sub verbo") { 14730 myterm = "sub-verbo"; 14731 } else { 14732 myterm = item.label; 14733 } 14734 } 14735 if (!myterm) { 14736 myterm = "page"; 14737 } 14738 } else { 14739 myterm = node.strings.term; 14740 } 14741 14742 // Plurals detection. 14743 var plural = node.strings.plural; 14744 if ("number" !== typeof plural) { 14745 // (node, ItemObject, variable, type) 14746 var theItem = (item && node.strings.term === "locator") ? item : Item; 14747 if (theItem[node.strings.term]) { 14748 state.processNumber(false, theItem, node.strings.term, Item.type); 14749 plural = state.tmp.shadow_numbers[node.strings.term].plural; 14750 if (!state.tmp.shadow_numbers[node.strings.term].labelForm 14751 && !state.tmp.shadow_numbers[node.strings.term].labelDecorations) { 14752 if (node.strings.form) { 14753 state.tmp.shadow_numbers[node.strings.term].labelForm = node.strings.form; 14754 } else if (state.tmp.group_context.tip.label_form) { 14755 state.tmp.shadow_numbers[node.strings.term].labelForm = state.tmp.group_context.tip.label_form; 14756 } 14757 state.tmp.shadow_numbers[node.strings.term].labelCapitalizeIfFirst = node.strings.capitalize_if_first; 14758 state.tmp.shadow_numbers[node.strings.term].labelDecorations = node.decorations.slice(); 14759 } 14760 14761 if (["locator", "number", "page"].indexOf(node.strings.term) > -1 && state.tmp.shadow_numbers[node.strings.term].label) { 14762 myterm = state.tmp.shadow_numbers[node.strings.term].label; 14763 } 14764 if (node.decorations && state.opt.development_extensions.csl_reverse_lookup_support) { 14765 node.decorations.reverse(); 14766 node.decorations.push(["@showid","true", node.cslid]); 14767 node.decorations.reverse(); 14768 } 14769 } 14770 } 14771 return CSL.castLabel(state, node, myterm, plural, CSL.TOLERANT); 14772}; 14773 14774CSL.castLabel = function (state, node, term, plural, mode) { 14775 var label_form = node.strings.form; 14776 var label_capitalize_if_first = node.strings.capitalize_if_first; 14777 if (state.tmp.group_context.tip.label_form) { 14778 if (label_form === "static") { 14779 state.tmp.group_context.tip.label_static = true; 14780 } else { 14781 label_form = state.tmp.group_context.tip.label_form; 14782 } 14783 } 14784 14785 if (state.tmp.group_context.tip.label_capitalize_if_first) { 14786 label_capitalize_if_first = state.tmp.group_context.tip.label_capitalize_if_first; 14787 } 14788 var ret = state.getTerm(term, label_form, plural, false, mode, node.default_locale); 14789 if (label_capitalize_if_first) { 14790 ret = CSL.Output.Formatters["capitalize-first"](state, ret); 14791 } 14792 // XXXXX Cut-and-paste code in multiple locations. This code block should be 14793 // collected in a function. 14794 // Tag: strip-periods-block 14795 if (state.tmp.strip_periods) { 14796 ret = ret.replace(/\./g, ""); 14797 } else { 14798 for (var i = 0, ilen = node.decorations.length; i < ilen; i += 1) { 14799 if ("@strip-periods" === node.decorations[i][0] && "true" === node.decorations[i][1]) { 14800 ret = ret.replace(/\./g, ""); 14801 break; 14802 } 14803 } 14804 } 14805 return ret; 14806}; 14807 14808/*global CSL: true */ 14809 14810CSL.Node.name = { 14811 build: function (state, target) { 14812 var func; 14813 if ([CSL.SINGLETON, CSL.START].indexOf(this.tokentype) > -1) { 14814 var oldTmpRoot; 14815 if ("undefined" === typeof state.tmp.root) { 14816 oldTmpRoot = undefined; 14817 state.tmp.root = "citation"; 14818 } else { 14819 oldTmpRoot = state.tmp.root; 14820 } 14821 // Many CSL styles set et-al-[min|use-first] 14822 // and et-al-subsequent-[min|use-first] to the same 14823 // value. 14824 // Set state.opt.update_mode = CSL.POSITION if 14825 // et-al-subsequent-min or et-al-subsequent-use-first 14826 // are set AND their value differs from their plain 14827 // counterparts. 14828 if (state.inheritOpt(this, "et-al-subsequent-min") 14829 && (state.inheritOpt(this, "et-al-subsequent-min") !== state.inheritOpt(this, "et-al-min"))) { 14830 14831 state.opt.update_mode = CSL.POSITION; 14832 } 14833 if (state.inheritOpt(this, "et-al-subsequent-use-first") 14834 && (state.inheritOpt(this, "et-al-subsequent-use-first") !== state.inheritOpt(this, "et-al-use-first"))) { 14835 14836 state.opt.update_mode = CSL.POSITION; 14837 } 14838 14839 state.tmp.root = oldTmpRoot; 14840 14841 func = function (state) { 14842 // Et-al (onward processing in node_etal.js and node_names.js) 14843 // XXXXX Why is this necessary? This is available on this.name, right? 14844 state.tmp.etal_term = "et-al"; 14845 14846 // Use default delimiter as fallback, in a way that allows explicit 14847 // empty strings. 14848 state.tmp.name_delimiter = state.inheritOpt(this, "delimiter", "name-delimiter", ", "); 14849 state.tmp["delimiter-precedes-et-al"] = state.inheritOpt(this, "delimiter-precedes-et-al"); 14850 14851 // And 14852 if ("text" === state.inheritOpt(this, "and")) { 14853 this.and_term = state.getTerm("and", "long", 0); 14854 } else if ("symbol" === state.inheritOpt(this, "and")) { 14855 if (state.opt.development_extensions.expect_and_symbol_form) { 14856 this.and_term = state.getTerm("and", "symbol", 0); 14857 } else { 14858 this.and_term = "&"; 14859 } 14860 } 14861 state.tmp.and_term = this.and_term; 14862 if (CSL.STARTSWITH_ROMANESQUE_REGEXP.test(this.and_term)) { 14863 this.and_prefix_single = " "; 14864 this.and_prefix_multiple = ", "; 14865 // Workaround to allow explicit empty string 14866 // on cs:name delimiter. 14867 if ("string" === typeof state.tmp.name_delimiter) { 14868 this.and_prefix_multiple = state.tmp.name_delimiter; 14869 } 14870 this.and_suffix = " "; 14871 14872 // Really can't inspect these values in the build phase. Sorry. 14873 //state.build.name_delimiter = this.strings.delimiter; 14874 14875 } else { 14876 this.and_prefix_single = ""; 14877 this.and_prefix_multiple = ""; 14878 this.and_suffix = ""; 14879 } 14880 if (state.inheritOpt(this, "delimiter-precedes-last") === "always") { 14881 this.and_prefix_single = state.tmp.name_delimiter; 14882 } else if (state.inheritOpt(this, "delimiter-precedes-last") === "never") { 14883 // Slightly fragile: could test for charset here to make 14884 // this more certain. 14885 if (this.and_prefix_multiple) { 14886 this.and_prefix_multiple = " "; 14887 } 14888 } else if (state.inheritOpt(this, "delimiter-precedes-last") === "after-inverted-name") { 14889 if (this.and_prefix_single) { 14890 this.and_prefix_single = state.tmp.name_delimiter; 14891 } 14892 if (this.and_prefix_multiple) { 14893 this.and_prefix_multiple = " "; 14894 } 14895 } 14896 14897 this.and = {}; 14898 if (state.inheritOpt(this, "and")) { 14899 state.output.append(this.and_term, "empty", true); 14900 this.and.single = state.output.pop(); 14901 this.and.single.strings.prefix = this.and_prefix_single; 14902 this.and.single.strings.suffix = this.and_suffix; 14903 state.output.append(this.and_term, "empty", true); 14904 this.and.multiple = state.output.pop(); 14905 this.and.multiple.strings.prefix = this.and_prefix_multiple; 14906 this.and.multiple.strings.suffix = this.and_suffix; 14907 } else if (state.tmp.name_delimiter) { 14908 // This is a little weird, but it works. 14909 this.and.single = new CSL.Blob(state.tmp.name_delimiter); 14910 this.and.single.strings.prefix = ""; 14911 this.and.single.strings.suffix = ""; 14912 this.and.multiple = new CSL.Blob(state.tmp.name_delimiter); 14913 this.and.multiple.strings.prefix = ""; 14914 this.and.multiple.strings.suffix = ""; 14915 } 14916 14917 this.ellipsis = {}; 14918 if (state.inheritOpt(this, "et-al-use-last")) { 14919 // We use the dedicated Unicode ellipsis character because 14920 // it is recommended by some editors, and can be more easily 14921 // identified for find and replace operations. 14922 // Source: http://en.wikipedia.org/wiki/Ellipsis#Computer_representations 14923 // 14924 14925 // Eventually, this should be localized as a term in CSL, with some 14926 // mechanism for triggering appropriate punctuation handling around 14927 // the ellipsis placeholder (Polish is a particularly tough case for that). 14928 this.ellipsis_term = "\u2026"; 14929 // Similar treatment to "and", above, will be needed 14930 // here when this becomes a locale term. 14931 this.ellipsis_prefix_single = " "; 14932 this.ellipsis_prefix_multiple = state.inheritOpt(this, "delimiter", "name-delimiter", ", "); 14933 this.ellipsis_suffix = " "; 14934 this.ellipsis.single = new CSL.Blob(this.ellipsis_term); 14935 this.ellipsis.single.strings.prefix = this.ellipsis_prefix_single; 14936 this.ellipsis.single.strings.suffix = this.ellipsis_suffix; 14937 this.ellipsis.multiple = new CSL.Blob(this.ellipsis_term); 14938 this.ellipsis.multiple.strings.prefix = this.ellipsis_prefix_multiple; 14939 this.ellipsis.multiple.strings.suffix = this.ellipsis_suffix; 14940 } 14941 14942 // et-al parameters are annoyingly incomprehensible 14943 // again. 14944 // 14945 // Explanation probably just adds a further layer of 14946 // irritation, but what's INTENDED here is that 14947 // the state.tmp et-al variables are set from the 14948 // cs:key element when composing sort keys, and a 14949 // macro containing a name can be called from cs:key. 14950 // So when cs:key sets et-al attributes, they are 14951 // set on state.tmp, and when the key is finished 14952 // processing, the state.tmp variables are reset to 14953 // undefined. IN THEORY the state.tmp et-al variables 14954 // will not be used in other contexts. I hope. 14955 // 14956 // Anyway, the current tests now seem to pass. 14957 if ("undefined" === typeof state.tmp["et-al-min"]) { 14958 state.tmp["et-al-min"] = state.inheritOpt(this, "et-al-min"); 14959 } 14960 if ("undefined" === typeof state.tmp["et-al-use-first"]) { 14961 state.tmp["et-al-use-first"] = state.inheritOpt(this, "et-al-use-first"); 14962 } 14963 if ("undefined" === typeof state.tmp["et-al-use-last"]) { 14964 //print(" setting et-al-use-last from name: "+this.strings["et-al-use-last"]); 14965 state.tmp["et-al-use-last"] = state.inheritOpt(this, "et-al-use-last"); 14966 } 14967 14968 state.nameOutput.name = this; 14969 }; 14970 14971 state.build.name_flag = true; 14972 14973 this.execs.push(func); 14974 } 14975 target.push(this); 14976 } 14977}; 14978 14979 14980 14981/*global CSL: true */ 14982 14983CSL.Node["name-part"] = { 14984 build: function (state) { 14985 state.build[this.strings.name] = this; 14986 } 14987}; 14988 14989/*global CSL: true */ 14990 14991CSL.Node.names = { 14992 build: function (state, target) { 14993 var func; 14994 // CSL.debug = print; 14995 14996 if (this.tokentype === CSL.START || this.tokentype === CSL.SINGLETON) { 14997 CSL.Util.substituteStart.call(this, state, target); 14998 state.build.substitute_level.push(1); 14999 } 15000 15001 if (this.tokentype === CSL.SINGLETON) { 15002 state.build.names_variables[state.build.names_variables.length-1].concat(this.variables); 15003 for (var i in this.variables) { 15004 var variable = this.variables[i]; 15005 var name_labels = state.build.name_label[state.build.name_label.length-1]; 15006 if (Object.keys(name_labels).length) { 15007 name_labels[variable] = name_labels[Object.keys(name_labels)[0]]; 15008 } 15009 } 15010 func = function (state) { 15011 state.nameOutput.reinit(this, this.variables_real[0]); 15012 }; 15013 this.execs.push(func); 15014 } 15015 15016 if (this.tokentype === CSL.START) { 15017 15018 state.build.names_flag = true; 15019 state.build.name_flag = false; 15020 state.build.names_level += 1; 15021 state.build.names_variables.push(this.variables); 15022 state.build.name_label.push({}); 15023 // init can substitute 15024 // init names 15025 func = function (state) { 15026 state.tmp.can_substitute.push(true); 15027 state.tmp.name_node = {}; 15028 state.tmp.name_node.children = []; 15029 state.nameOutput.init(this); 15030 }; 15031 this.execs.push(func); 15032 15033 } 15034 15035 if (this.tokentype === CSL.END) { 15036 15037 // Set/reset name blobs if they exist, for processing 15038 // by namesOutput() 15039 for (var i = 0, ilen = 3; i < ilen; i += 1) { 15040 var key = ["family", "given", "et-al"][i]; 15041 this[key] = state.build[key]; 15042 if (state.build.names_level === 1) { 15043 state.build[key] = undefined; 15044 } 15045 } 15046 // Labels, if any 15047 this.label = state.build.name_label[state.build.name_label.length-1]; 15048 state.build.names_level += -1; 15049 state.build.names_variables.pop(); 15050 state.build.name_label.pop(); 15051 15052 // The with term. This isn't the right place 15053 // for this, but it's all hard-wired at the 15054 // moment. 15055 15056 // "and" and "ellipsis" are set in node_name.js 15057 func = function (state) { 15058 // Et-al (strings only) 15059 // Blob production has to happen inside nameOutput() 15060 // since proper escaping requires access to the output 15061 // queue. 15062 if (state.tmp.etal_node) { 15063 this.etal_style = state.tmp.etal_node; 15064 } else { 15065 this.etal_style = "empty"; 15066 } 15067 15068 this.etal_term = state.getTerm(state.tmp.etal_term, "long", 0); 15069 this.etal_prefix_single = " "; 15070 // Should be name delimiter, not hard-wired. 15071 this.etal_prefix_multiple = state.tmp.name_delimiter; 15072 if (state.tmp["delimiter-precedes-et-al"] === "always") { 15073 this.etal_prefix_single = state.tmp.name_delimiter; 15074 } else if (state.tmp["delimiter-precedes-et-al"] === "never") { 15075 this.etal_prefix_multiple = " "; 15076 } else if (state.tmp["delimiter-precedes-et-al"] === "after-inverted-name") { 15077 this.etal_prefix_single = state.tmp.name_delimiter; 15078 this.etal_prefix_multiple = " "; 15079 } 15080 this.etal_suffix = ""; 15081 if (!CSL.STARTSWITH_ROMANESQUE_REGEXP.test(this.etal_term)) { 15082 // Not sure what the correct treatment is here, but we should not suppress 15083 // a comma-space. 15084 // https://forums.zotero.org/discussion/76679/delimiter-precedes-et-al-always-dose-not-work-in-locale-zh-cn 15085 if (this.etal_prefix_single === " ") { 15086 this.etal_prefix_single = ""; 15087 } 15088 if (this.etal_prefix_multiple === " ") { 15089 this.etal_prefix_multiple = ""; 15090 } 15091 if (this.etal_suffix === " ") { 15092 this.etal_suffix = ""; 15093 } 15094 } 15095 // et-al affixes are further adjusted in nameOutput(), 15096 // after the term (possibly changed in cs:et-al) is known. 15097 15098 15099 for (var i = 0, ilen = 3; i < ilen; i += 1) { 15100 var key = ["family", "given"][i]; 15101 state.nameOutput[key] = this[key]; 15102 } 15103 state.nameOutput["with"] = this["with"]; 15104 15105 // REMOVE THIS 15106 var mywith = "with"; 15107 var with_default_prefix = ""; 15108 var with_suffix = ""; 15109 if (CSL.STARTSWITH_ROMANESQUE_REGEXP.test(mywith)) { 15110 with_default_prefix = " "; 15111 with_suffix = " "; 15112 } 15113 var thewith = {}; 15114 thewith.single = new CSL.Blob(mywith); 15115 thewith.single.strings.suffix = with_suffix; 15116 thewith.multiple = new CSL.Blob(mywith); 15117 thewith.multiple.strings.suffix = with_suffix; 15118 if (state.inheritOpt(state.nameOutput.name, "delimiter-precedes-last") === "always") { 15119 thewith.single.strings.prefix = state.inheritOpt(this, "delimiter", "names-delimiter"); 15120 thewith.multiple.strings.prefix = state.inheritOpt(this, "delimiter", "names-delimiter"); 15121 } else if (state.inheritOpt(state.nameOutput.name, "delimiter-precedes-last") === "contextual") { 15122 thewith.single.strings.prefix = with_default_prefix; 15123 thewith.multiple.strings.prefix = state.inheritOpt(this, "delimiter", "names-delimiter"); 15124 } else if (state.inheritOpt(state.nameOutput.name, "delimiter-precedes-last") === "after-inverted-name") { 15125 thewith.single.strings.prefix = state.inheritOpt(this, "delimiter", "names-delimiter"); 15126 thewith.multiple.strings.prefix = with_default_prefix; 15127 } else { 15128 thewith.single.strings.prefix = with_default_prefix; 15129 thewith.multiple.strings.prefix = with_default_prefix; 15130 } 15131 state.nameOutput["with"] = thewith; 15132 15133 15134 // XXX label style should be set per variable, since they may differ 15135 // XXX with full-form nested names constructs 15136 state.nameOutput.label = this.label; 15137 15138 state.nameOutput.etal_style = this.etal_style; 15139 state.nameOutput.etal_term = this.etal_term; 15140 state.nameOutput.etal_prefix_single = this.etal_prefix_single; 15141 state.nameOutput.etal_prefix_multiple = this.etal_prefix_multiple; 15142 state.nameOutput.etal_suffix = this.etal_suffix; 15143 state.nameOutput.outputNames(); 15144 state.tmp["et-al-use-first"] = undefined; 15145 state.tmp["et-al-min"] = undefined; 15146 state.tmp["et-al-use-last"] = undefined; 15147 }; 15148 this.execs.push(func); 15149 15150 // unsets 15151 func = function (state) { 15152 if (!state.tmp.can_substitute.pop()) { 15153 state.tmp.can_substitute.replace(false, CSL.LITERAL); 15154 } 15155 15156 // For posterity ... 15157 // 15158 // This was enough to fix the issue reported here: 15159 // 15160 // http://forums.zotero.org/discussion/25223/citeproc-bug-substitute-doesnt-work-correctly-for-title-macro/ 15161 // 15162 // The remainder of the changes applied in the same patch 15163 // relate to a label assignments, which were found to be 15164 // buggy while working on the issue. The test covering 15165 // both problems is here: 15166 // 15167 // https://bitbucket.org/bdarcus/citeproc-test/src/ab136a6aa8f2/processor-tests/humans/substitute_SuppressOrdinaryVariable.txt 15168 if (state.tmp.can_substitute.mystack.length === 1) { 15169 state.tmp.can_block_substitute = false; 15170 } 15171 }; 15172 this.execs.push(func); 15173 15174 state.build.name_flag = false; 15175 } 15176 target.push(this); 15177 15178 if (this.tokentype === CSL.END || this.tokentype === CSL.SINGLETON) { 15179 state.build.substitute_level.pop(); 15180 CSL.Util.substituteEnd.call(this, state, target); 15181 } 15182 } 15183}; 15184 15185/*global CSL: true */ 15186 15187CSL.Node.number = { 15188 build: function (state, target) { 15189 var func; 15190 CSL.Util.substituteStart.call(this, state, target); 15191 // 15192 // This should push a rangeable object to the queue. 15193 // 15194 if (this.strings.form === "roman") { 15195 this.formatter = state.fun.romanizer; 15196 } else if (this.strings.form === "ordinal") { 15197 this.formatter = state.fun.ordinalizer; 15198 } else if (this.strings.form === "long-ordinal") { 15199 this.formatter = state.fun.long_ordinalizer; 15200 } 15201 if ("undefined" === typeof this.successor_prefix) { 15202 this.successor_prefix = state[state.build.area].opt.layout_delimiter; 15203 } 15204 if ("undefined" === typeof this.splice_prefix) { 15205 this.splice_prefix = state[state.build.area].opt.layout_delimiter; 15206 } 15207 // is this needed? 15208 //if ("undefined" === typeof this.splice_prefix){ 15209 // this.splice_prefix = state[state.tmp.area].opt.layout_delimiter; 15210 //} 15211 // 15212 // Whether we actually stick a number object on 15213 // the output queue depends on whether the field 15214 // contains a pure number. 15215 // 15216 // push number or text 15217 func = function (state, Item, item) { 15218 // NOTE: this works because this is the ONLY function in this node. 15219 // If further functions are added, they need to start with the same 15220 // abort condition. 15221 if (this.variables.length === 0) { 15222 return; 15223 } 15224 var varname; 15225 varname = this.variables[0]; 15226 if ("undefined" === typeof item) { 15227 var item = {}; 15228 } 15229 if (["locator", "locator-extra"].indexOf(varname) > -1) { 15230 if (state.tmp.just_looking) { 15231 return; 15232 } 15233 if (!item[varname]) { 15234 return; 15235 } 15236 } else { 15237 if (!Item[varname]) { 15238 return; 15239 } 15240 } 15241 15242 if (varname === 'collection-number' && Item.type === 'legal_case') { 15243 state.tmp.renders_collection_number = true; 15244 } 15245 15246 // For bill or legislation items that have a label-form 15247 // attribute set on the cs:number node rendering the locator, 15248 // the form and pluralism of locator terms are controlled 15249 // separately from those of the initial label. Form is 15250 // straightforward: the label uses the value set on 15251 // the cs:label node that renders it, and the embedded 15252 // labels use the value of label-form set on the cs:number 15253 // node. Both default to "long". 15254 // 15255 // Pluralism is more complicated. For embedded labels, 15256 // pluralism is evaluated using a simple heuristic that 15257 // can be found below (it just looks for comma, ampersand etc). 15258 // The item.label rendered independently via cs:label 15259 // defaults to singular. It is always singular if embedded 15260 // labels exist that (when expanded to their valid CSL 15261 // value) do not match the value of item.label. Otherwise, 15262 // if one or more matching embedded labels exist, the 15263 // cs:label is set to plural. 15264 // 15265 // The code that does all this is divided between this module, 15266 // util_static_locator.js, and util_label.js. It's not easy 15267 // to follow, but seems to do the job. Let's home for good 15268 // luck out there in the wild. 15269 15270 var node = this; 15271 15272 if (state.tmp.group_context.tip.force_suppress) { 15273 return false; 15274 } 15275 15276 if (["locator", "locator-extra"].indexOf(varname) > -1) { 15277 // amazing that we reach this. should abort sooner if no content? 15278 state.processNumber.call(state, node, item, varname, Item.type); 15279 } else { 15280 if (!state.tmp.group_context.tip.condition && Item[varname]) { 15281 state.tmp.just_did_number = ("" + Item[varname]).match(/[0-9]$/); 15282 } 15283 // UPDATE_GROUP_CONTEXT_CONDITION is run by processNumber 15284 state.processNumber.call(state, node, Item, varname, Item.type); 15285 } 15286 15287 if (this.substring) { 15288 var val = Item[varname].slice(this.substring); 15289 state.output.append(val, node); 15290 } else { 15291 CSL.Util.outputNumericField(state, varname, Item.id); 15292 } 15293 15294 if (["locator", "locator-extra"].indexOf(this.variables_real[0]) > -1 15295 && !state.tmp.just_looking) { 15296 state.tmp.done_vars.push(this.variables_real[0]); 15297 state.tmp.group_context.tip.done_vars.push(this.variables_real[0]); 15298 } 15299 }; 15300 this.execs.push(func); 15301 target.push(this); 15302 15303 CSL.Util.substituteEnd.call(this, state, target); 15304 } 15305}; 15306 15307/*global CSL: true */ 15308 15309/* 15310 * Yikes, these functions were running out of scope for yonks. 15311 * now that they are set in the correct token list, 15312 * they might be useful for things. 15313 * FB 2013.11.09 15314*/ 15315 15316CSL.Node.sort = { 15317 build: function (state, target) { 15318 target = state[state.build.root + "_sort"].tokens; 15319 if (this.tokentype === CSL.START) { 15320 if (state.build.area === "citation") { 15321 state.opt.sort_citations = true; 15322 } 15323 state.build.area = state.build.root + "_sort"; 15324 state.build.extension = "_sort"; 15325 15326 var func = function (state, Item) { 15327 //state.tmp.area = state.tmp.root + "_sort"; 15328 //state.tmp.extension = "_sort"; 15329 if (state.opt.has_layout_locale) { 15330 var langspec = CSL.localeResolve(Item.language, state.opt["default-locale"][0]); 15331 var sort_locales = state[state.tmp.area.slice(0,-5)].opt.sort_locales; 15332 var langForItem; 15333 for (var i=0,ilen=sort_locales.length;i<ilen;i+=1) { 15334 langForItem = sort_locales[i][langspec.bare]; 15335 if (!langForItem) { 15336 langForItem = sort_locales[i][langspec.best]; 15337 } 15338 if (langForItem) { 15339 break; 15340 } 15341 } 15342 if (!langForItem) { 15343 langForItem = state.opt["default-locale"][0]; 15344 } 15345 state.tmp.lang_sort_hold = state.opt.lang; 15346 state.opt.lang = langForItem; 15347 } 15348 }; 15349 this.execs.push(func); 15350 15351 } 15352 if (this.tokentype === CSL.END) { 15353 state.build.area = state.build.root; 15354 state.build.extension = ""; 15355 var func = function (state) { 15356 if (state.opt.has_layout_locale) { 15357 state.opt.lang = state.tmp.lang_sort_hold; 15358 delete state.tmp.lang_sort_hold; 15359 } 15360 //state.tmp.area = state.tmp.root; 15361 //state.tmp.extension = ""; 15362 }; 15363 this.execs.push(func); 15364 /* 15365 var func = function (state, Item) { 15366 state.tmp.area = state.tmp.root; 15367 state.tmp.extension = ""; 15368 } 15369 this.execs.push(func); 15370 */ 15371 } 15372 target.push(this); 15373 } 15374}; 15375 15376 15377 15378/*global CSL: true */ 15379 15380CSL.Node.substitute = { 15381 build: function (state, target) { 15382 var func; 15383 if (this.tokentype === CSL.START) { 15384 /* */ 15385 // set conditional 15386 var choose_start = new CSL.Token("choose", CSL.START); 15387 CSL.Node.choose.build.call(choose_start, state, target); 15388 var if_singleton = new CSL.Token("if", CSL.SINGLETON); 15389 func = function() { 15390 if (state.tmp.value.length && !state.tmp.common_term_match_fail) { 15391 return true; 15392 } 15393 return false; 15394 } 15395 if_singleton.tests = [func]; 15396 if_singleton.test = state.fun.match.any(if_singleton, state, if_singleton.tests); 15397 target.push(if_singleton); 15398 15399 func = function (state) { 15400 state.tmp.can_block_substitute = true; 15401 if (state.tmp.value.length && !state.tmp.common_term_match_fail) { 15402 state.tmp.can_substitute.replace(false, CSL.LITERAL); 15403 } 15404 state.tmp.common_term_match_fail = false; 15405 }; 15406 this.execs.push(func); 15407 target.push(this); 15408 /* */ 15409 } 15410 if (this.tokentype === CSL.END) { 15411 //var if_end = new CSL.Token("if", CSL.END); 15412 //CSL.Node["if"].build.call(if_end, state, target); 15413 /* */ 15414 target.push(this); 15415 var choose_end = new CSL.Token("choose", CSL.END); 15416 CSL.Node.choose.build.call(choose_end, state, target); 15417 /* */ 15418 } 15419 } 15420}; 15421 15422 15423 15424/*global CSL: true */ 15425 15426CSL.Node.text = { 15427 build: function (state, target) { 15428 var func, form, plural, id, num, number, formatter, firstoutput, specialdelimiter, label, suffix, term; 15429 if (this.postponed_macro) { 15430 var group_start = CSL.Util.cloneToken(this); 15431 group_start.name = "group"; 15432 group_start.tokentype = CSL.START; 15433 CSL.Node.group.build.call(group_start, state, target); 15434 15435 CSL.expandMacro.call(state, this, target); 15436 15437 var group_end = CSL.Util.cloneToken(this); 15438 group_end.name = "group"; 15439 group_end.tokentype = CSL.END; 15440 if (this.postponed_macro === 'juris-locator-label') { 15441 group_end.isJurisLocatorLabel = true; 15442 } 15443 CSL.Node.group.build.call(group_end, state, target); 15444 15445 } else { 15446 CSL.Util.substituteStart.call(this, state, target); 15447 // ... 15448 // 15449 // Do non-macro stuff 15450 15451 // Guess again. this.variables is ephemeral, adjusted by an initial 15452 // function set on the node via @variable attribute setup. 15453 //variable = this.variables[0]; 15454 15455 if (!this.variables_real) { 15456 this.variables_real = []; 15457 } 15458 if (!this.variables) { 15459 this.variables = []; 15460 } 15461 15462 form = "long"; 15463 plural = 0; 15464 if (this.strings.form) { 15465 form = this.strings.form; 15466 } 15467 if (this.strings.plural) { 15468 plural = this.strings.plural; 15469 } 15470 if ("citation-number" === this.variables_real[0] || "year-suffix" === this.variables_real[0] || "citation-label" === this.variables_real[0]) { 15471 // 15472 // citation-number and year-suffix are super special, 15473 // because they are rangeables, and require a completely 15474 // different set of formatting parameters on the output 15475 // queue. 15476 if (this.variables_real[0] === "citation-number") { 15477 15478 if (state.build.root === "citation") { 15479 state.opt.update_mode = CSL.NUMERIC; 15480 } 15481 if (state.build.root === "bibliography") { 15482 state.opt.bib_mode = CSL.NUMERIC; 15483 } 15484 //this.strings.is_rangeable = true; 15485 if ("citation-number" === state[state.tmp.area].opt.collapse) { 15486 this.range_prefix = state.getTerm("citation-range-delimiter"); 15487 } 15488 this.successor_prefix = state[state.build.area].opt.layout_delimiter; 15489 this.splice_prefix = state[state.build.area].opt.layout_delimiter; 15490 func = function (state, Item, item) { 15491 15492 id = "" + Item.id; 15493 if (!state.tmp.just_looking) { 15494 if (state.tmp.area.slice(-5) === "_sort" && this.variables[0] === "citation-number") { 15495 if (state.tmp.area === "bibliography_sort") { 15496 state.tmp.group_context.tip.done_vars.push("citation-number"); 15497 } 15498 if (state.tmp.area === "citation_sort" && state.bibliography_sort.tmp.citation_number_map) { 15499 var num = state.bibliography_sort.tmp.citation_number_map[state.registry.registry[Item.id].seq]; 15500 } else { 15501 var num = state.registry.registry[Item.id].seq; 15502 } 15503 if (num) { 15504 // Code currently in util_number.js 15505 num = CSL.Util.padding("" + num); 15506 } 15507 state.output.append(num, this); 15508 return; 15509 } 15510 if (item && item["author-only"]) { 15511 state.tmp.element_trace.replace("suppress-me"); 15512 } 15513 if (state.tmp.area !== "bibliography_sort" && state.bibliography_sort.tmp.citation_number_map && state.bibliography_sort.opt.citation_number_sort_direction === CSL.DESCENDING) { 15514 num = state.bibliography_sort.tmp.citation_number_map[state.registry.registry[id].seq]; 15515 } else { 15516 num = state.registry.registry[id].seq; 15517 } 15518 if (state.opt.citation_number_slug) { 15519 state.output.append(state.opt.citation_number_slug, this); 15520 } else { 15521 number = new CSL.NumericBlob(state, false, num, this, Item.id); 15522 if (state.tmp.in_cite_predecessor) { 15523 number.suppress_splice_prefix = true; 15524 } 15525 state.output.append(number, "literal"); 15526 } 15527 } 15528 }; 15529 this.execs.push(func); 15530 } else if (this.variables_real[0] === "year-suffix") { 15531 15532 state.opt.has_year_suffix = true; 15533 15534 if (state[state.tmp.area].opt.collapse === "year-suffix-ranged") { 15535 //this.range_prefix = "-"; 15536 this.range_prefix = state.getTerm("citation-range-delimiter"); 15537 } 15538 this.successor_prefix = state[state.build.area].opt.layout_delimiter; 15539 if (state[state.tmp.area].opt["year-suffix-delimiter"]) { 15540 this.successor_prefix = state[state.build.area].opt["year-suffix-delimiter"]; 15541 } 15542 func = function (state, Item) { 15543 if (state.registry.registry[Item.id] && state.registry.registry[Item.id].disambig.year_suffix !== false && !state.tmp.just_looking) { 15544 //state.output.append(state.registry.registry[Item.id].disambig[2],this); 15545 num = parseInt(state.registry.registry[Item.id].disambig.year_suffix, 10); 15546 15547 //if (state[state.tmp.area].opt.collapse === "year-suffix-ranged") { 15548 // //this.range_prefix = "-"; 15549 // this.range_prefix = state.getTerm("citation-range-delimiter"); 15550 //} 15551 //this.successor_prefix = state[state.tmp.area].opt.layout_delimiter; 15552 if (state[state.tmp.area].opt.cite_group_delimiter) { 15553 this.successor_prefix = state[state.tmp.area].opt.cite_group_delimiter; 15554 } 15555 number = new CSL.NumericBlob(state, false, num, this, Item.id); 15556 formatter = new CSL.Util.Suffixator(CSL.SUFFIX_CHARS); 15557 number.setFormatter(formatter); 15558 state.output.append(number, "literal"); 15559 firstoutput = false; 15560 // XXX Can we do something better for length here? 15561 for (var i=0,ilen=state.tmp.group_context.mystack.length; i<ilen; i++) { 15562 var flags = state.tmp.group_context.mystack[i]; 15563 if (!flags.variable_success && (flags.variable_attempt || (!flags.variable_attempt && !flags.term_intended))) { 15564 firstoutput = true; 15565 break; 15566 } 15567 } 15568 specialdelimiter = state[state.tmp.area].opt["year-suffix-delimiter"]; 15569 if (firstoutput && specialdelimiter && !state.tmp.sort_key_flag) { 15570 state.tmp.splice_delimiter = state[state.tmp.area].opt["year-suffix-delimiter"]; 15571 } 15572 } 15573 }; 15574 this.execs.push(func); 15575 } else if (this.variables_real[0] === "citation-label") { 15576 if (state.build.root === "bibliography") { 15577 state.opt.bib_mode = CSL.TRIGRAPH; 15578 } 15579 state.opt.has_year_suffix = true; 15580 func = function (state, Item) { 15581 label = Item["citation-label"]; 15582 if (!label) { 15583 label = state.getCitationLabel(Item); 15584 } 15585 if (!state.tmp.just_looking) { 15586 suffix = ""; 15587 if (state.registry.registry[Item.id] && state.registry.registry[Item.id].disambig.year_suffix !== false) { 15588 num = parseInt(state.registry.registry[Item.id].disambig.year_suffix, 10); 15589 suffix = state.fun.suffixator.format(num); 15590 } 15591 label += suffix; 15592 } 15593 state.output.append(label, this); 15594 }; 15595 this.execs.push(func); 15596 } 15597 } else { 15598 if (this.strings.term) { 15599 15600 // printterm 15601 func = function (state, Item) { 15602 var gender = state.opt.gender[Item.type]; 15603 var term = this.strings.term; 15604 term = state.getTerm(term, form, plural, gender, CSL.TOLERANT, this.default_locale); 15605 var myterm; 15606 // if the term is not an empty string, say 15607 // that we rendered a term 15608 if (term !== "") { 15609 state.tmp.group_context.tip.term_intended = true; 15610 } 15611 CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, term, null, this); 15612 15613 // capitalize the first letter of a term, if it is the 15614 // first thing rendered in a citation (or if it is 15615 // being rendered immediately after terminal punctuation, 15616 // I guess, actually). 15617 if (!state.tmp.term_predecessor && !(state.opt["class"] === "in-text" && state.tmp.area === "citation")) { 15618 myterm = CSL.Output.Formatters["capitalize-first"](state, term); 15619 //CSL.debug("Capitalize"); 15620 } else { 15621 myterm = term; 15622 } 15623 15624 // XXXXX Cut-and-paste code in multiple locations. This code block should be 15625 // collected in a function. 15626 // Tag: strip-periods-block 15627 if (state.tmp.strip_periods) { 15628 myterm = myterm.replace(/\./g, ""); 15629 } else { 15630 for (var i = 0, ilen = this.decorations.length; i < ilen; i += 1) { 15631 if ("@strip-periods" === this.decorations[i][0] && "true" === this.decorations[i][1]) { 15632 myterm = myterm.replace(/\./g, ""); 15633 break; 15634 } 15635 } 15636 } 15637 state.output.append(myterm, this); 15638 if (state.tmp.can_block_substitute) { 15639 // Black magic here. This causes the cs:substitution condition to pass, 15640 // blocking further rendering within its scope. 15641 state.tmp.can_substitute.replace(false, CSL.LITERAL); 15642 } 15643 }; 15644 this.execs.push(func); 15645 state.build.term = false; 15646 state.build.form = false; 15647 state.build.plural = false; 15648 } else if (this.variables_real.length) { 15649 func = function (state, Item) { 15650 15651 // If some text variable is rendered, we're not collapsing. 15652 if (this.variables_real[0] !== "locator") { 15653 state.tmp.have_collapsed = false; 15654 } 15655 15656 if (!state.tmp.group_context.tip.condition && Item[this.variables[0]]) { 15657 state.tmp.just_did_number = false; 15658 } 15659 var val = Item[this.variables[0]]; 15660 if (val && !state.tmp.group_context.tip.condition) { 15661 if (("" + val).slice(-1).match(/[0-9]/)) { 15662 state.tmp.just_did_number = true; 15663 } else { 15664 state.tmp.just_did_number = false; 15665 } 15666 } 15667 }; 15668 this.execs.push(func); 15669 15670 // plain string fields 15671 15672 // Deal with multi-fields and ordinary fields separately. 15673 if (CSL.MULTI_FIELDS.indexOf(this.variables_real[0]) > -1 15674 || this.variables_real[0].indexOf("-main") > -1 15675 || this.variables_real[0].indexOf("-sub") > -1 15676 || ["language-name", "language-name-original"].indexOf(this.variables_real[0]) > -1 15677 ) { 15678 15679 // multi-fields 15680 // Initialize transform factory according to whether 15681 // abbreviation is desired. 15682 var abbrevfam = this.variables[0]; 15683 var abbrfall = false; 15684 var altvar = false; 15685 var transfall = false; 15686 if (form === "short") { 15687 if (this.variables_real[0].slice(-6) !== "-short") { 15688 altvar = this.variables_real[0] + "-short"; 15689 } 15690 } else { 15691 abbrevfam = false; 15692 } 15693 if (state.build.extension) { 15694 // multi-fields for sorting get a sort transform, 15695 // (abbreviated if the short form was selected) 15696 transfall = true; 15697 } else { 15698 transfall = true; 15699 abbrfall = true; 15700 } 15701 15702 func = state.transform.getOutputFunction(this.variables, abbrevfam, abbrfall, altvar, transfall); 15703 } else { 15704 // ordinary fields 15705 if (CSL.CITE_FIELDS.indexOf(this.variables_real[0]) > -1) { 15706 // per-cite fields are read from item, rather than Item 15707 func = function (state, Item, item) { 15708 if (item && item[this.variables[0]]) { 15709 // Code copied to page variable as well; both 15710 // become cs:number in MLZ extended schema 15711 15712 // If locator, use cs:number. Otherwise, render 15713 // normally. 15714 15715 // XXX The code below is pretty-much copied from 15716 // XXX node_number.js. Should be a common function. 15717 // XXX BEGIN 15718 state.processNumber(this, item, this.variables[0], Item.type); 15719 CSL.Util.outputNumericField(state, this.variables[0], Item.id); 15720 // XXX END 15721 15722 if (["locator", "locator-extra"].indexOf(this.variables_real[0]) > -1 15723 && !state.tmp.just_looking) { 15724 state.tmp.done_vars.push(this.variables_real[0]); 15725 } 15726 } 15727 }; 15728 } else if (["page", "page-first", "chapter-number", "collection-number", "edition", "issue", "number", "number-of-pages", "number-of-volumes", "volume"].indexOf(this.variables_real[0]) > -1) { 15729 // page gets mangled with the correct collapsing 15730 // algorithm 15731 func = function(state, Item) { 15732 state.processNumber(this, Item, this.variables[0], Item.type); 15733 CSL.Util.outputNumericField(state, this.variables[0], Item.id); 15734 }; 15735 } else if (["URL", "DOI"].indexOf(this.variables_real[0]) > -1) { 15736 func = function (state, Item) { 15737 var value; 15738 if (this.variables[0]) { 15739 value = state.getVariable(Item, this.variables[0], form); 15740 if (value) { 15741 if (this.variables[0] === "URL" && form === "short") { 15742 value = value.replace(/(.*\.[^\/]+)\/.*/, "$1"); 15743 if (value.match(/\/\/www\./)) { 15744 value = value.replace(/https?:\/\//, ""); 15745 } 15746 } 15747 // true is for non-suppression of periods 15748 if (state.opt.development_extensions.wrap_url_and_doi) { 15749 if (!this.decorations.length || this.decorations[0][0] !== "@" + this.variables[0]) { 15750 // Special-casing to fix https://github.com/Juris-M/citeproc-js/issues/57 15751 // clone current token, to avoid collateral damage 15752 var clonetoken = CSL.Util.cloneToken(this); 15753 // cast a group blob 15754 var groupblob = new CSL.Blob(null, null, "url-wrapper"); 15755 // set the DOI decoration on the blob 15756 groupblob.decorations.push(["@DOI", "true"]); 15757 if (this.variables_real[0] === "DOI") { 15758 // strip a proper DOI prefix 15759 var prefix; 15760 if (this.strings.prefix && this.strings.prefix.match(/^.*https:\/\/doi\.org\/$/)) { 15761 value = value.replace(/^https?:\/\/doi\.org\//, ""); 15762 if (value.match(/^https?:\/\//)) { 15763 // Do not tamper with another protocol + domain if already set in field value 15764 prefix = ""; 15765 } else { 15766 // Otherwise https + domain 15767 prefix = "https://doi.org/"; 15768 } 15769 // set any string prefix on the clone 15770 clonetoken.strings.prefix = this.strings.prefix.slice(0, clonetoken.strings.prefix.length-16); 15771 } 15772 // cast a text blob 15773 // set the prefix as the content of the blob 15774 var prefixblob = new CSL.Blob(prefix); 15775 // cast another text blob 15776 // set the value as the content of the second blob 15777 var valueblob = new CSL.Blob(value); 15778 // append new text token and clone to group token 15779 groupblob.push(prefixblob); 15780 groupblob.push(valueblob); 15781 // append group token to output 15782 state.output.append(groupblob, clonetoken, false, false, true); 15783 } else { 15784 var valueblob = new CSL.Blob(value); 15785 // append new text token and clone to group token 15786 groupblob.push(valueblob); 15787 // append group token to output 15788 //this.decorations = [["@" + this.variables[0], "true"]].concat(this.decorations); 15789 state.output.append(groupblob, clonetoken, false, false, true); 15790 } 15791 } else { 15792 state.output.append(value, this, false, false, true); 15793 } 15794 } else { 15795 // This is totally unnecessary, isn't it? 15796 if (this.decorations.length) { 15797 for (var i=this.decorations.length-1; i>-1; i--) { 15798 if (this.decorations[i][0] === "@" + this.variables[0]) { 15799 this.decorations = this.decorations.slice(0, i).concat(this.decorations.slice(i+1)); 15800 } 15801 } 15802 } 15803 state.output.append(value, this, false, false, true); 15804 } 15805 } 15806 } 15807 }; 15808 } else if (this.variables_real[0] === "section") { 15809 // Sections for statutes are special. This is an uncommon 15810 // variable, so we save the cost of the runtime check 15811 // unless it's being used. 15812 func = function (state, Item) { 15813 var value; 15814 value = state.getVariable(Item, this.variables[0], form); 15815 if (value) { 15816 state.output.append(value, this); 15817 } 15818 }; 15819 } else if (this.variables_real[0] === "hereinafter") { 15820 func = function (state, Item) { 15821 var value = state.transform.abbrevs["default"]["hereinafter"][Item.id]; 15822 if (value) { 15823 state.output.append(value, this); 15824 state.tmp.group_context.tip.variable_success = true; 15825 } 15826 }; 15827 } else { 15828 // anything left over just gets output in the normal way. 15829 func = function (state, Item) { 15830 var value; 15831 if (this.variables[0]) { 15832 value = state.getVariable(Item, this.variables[0], form); 15833 if (value) { 15834 value = "" + value; 15835 value = value.split("\\").join(""); 15836 state.output.append(value, this); 15837 } 15838 } 15839 }; 15840 } 15841 } 15842 this.execs.push(func); 15843 } else if (this.strings.value) { 15844 // for the text value attribute. 15845 func = function (state) { 15846 state.tmp.group_context.tip.term_intended = true; 15847 // true flags that this is a literal-value term 15848 CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, this.strings.value, true, this); 15849 state.output.append(this.strings.value, this); 15850 if (state.tmp.can_block_substitute) { 15851 // Black magic here. This causes the cs:substitution condition to pass, 15852 // blocking further rendering within its scope. 15853 state.tmp.can_substitute.replace(false, CSL.LITERAL); 15854 } 15855 }; 15856 this.execs.push(func); 15857 // otherwise no output 15858 } 15859 } 15860 target.push(this); 15861 CSL.Util.substituteEnd.call(this, state, target); 15862 } 15863 } 15864}; 15865 15866 15867 15868/*global CSL: true */ 15869 15870CSL.Node.intext = { 15871 build: function (state, target) { 15872 if (this.tokentype === CSL.START) { 15873 15874 state.build.area = "intext"; 15875 state.build.root = "intext"; 15876 state.build.extension = ""; 15877 15878 var func = function(state, Item) { 15879 state.tmp.area = "intext"; 15880 state.tmp.root = "intext"; 15881 state.tmp.extension = ""; 15882 } 15883 this.execs.push(func); 15884 } 15885 if (this.tokentype === CSL.END) { 15886 15887 // Do whatever cs:citation does with sorting. 15888 state.intext_sort = { 15889 opt: { 15890 sort_directions: state.citation_sort.opt.sort_directions 15891 } 15892 } 15893 state.intext.srt = state.citation.srt; 15894 } 15895 target.push(this); 15896 } 15897}; 15898 15899 15900/*global CSL: true */ 15901 15902CSL.Attributes = {}; 15903 15904CSL.Attributes["@disambiguate"] = function (state, arg) { 15905 if (!this.tests) {this.tests = []; }; 15906 if (arg === "true") { 15907 state.opt.has_disambiguate = true; 15908 var func = function (Item) { 15909 if (state.tmp.area === "bibliography") { 15910 if (state.tmp.disambiguate_count < state.registry.registry[Item.id].disambig.disambiguate) { 15911 state.tmp.disambiguate_count += 1; 15912 return true; 15913 } 15914 } else { 15915 state.tmp.disambiguate_maxMax += 1; 15916 if (state.tmp.disambig_settings.disambiguate 15917 && state.tmp.disambiguate_count < state.tmp.disambig_settings.disambiguate) { 15918 state.tmp.disambiguate_count += 1; 15919 return true; 15920 } 15921 } 15922 return false; 15923 }; 15924 this.tests.push(func); 15925 } else if (arg === "check-ambiguity-and-backreference") { 15926 var func = function (Item) { 15927 if (state.registry.registry[Item.id].disambig.disambiguate && state.registry.registry[Item.id]["citation-count"] > 1) { 15928 return true; 15929 } 15930 return false; 15931 }; 15932 this.tests.push(func); 15933 } 15934}; 15935 15936CSL.Attributes["@is-numeric"] = function (state, arg) { 15937 if (!this.tests) {this.tests = []; }; 15938 var variables = arg.split(/\s+/); 15939 var maketest = function(variable) { 15940 return function (Item, item) { 15941 var myitem = Item; 15942 if (item && ["locator","locator-extra"].indexOf(variable) > -1) { 15943 myitem = item; 15944 } 15945 if (!myitem[variable]) { 15946 return false; 15947 } 15948 if (CSL.NUMERIC_VARIABLES.indexOf(variable) > -1) { 15949 if (!state.tmp.shadow_numbers[variable]) { 15950 state.processNumber(false, myitem, variable, Item.type); 15951 } 15952 if (state.tmp.shadow_numbers[variable].numeric) { 15953 return true; 15954 } 15955 } else if (["title","version"].indexOf(variable) > -1) { 15956 if (myitem[variable].slice(-1) === "" + parseInt(myitem[variable].slice(-1), 10)) { 15957 return true; 15958 } 15959 } 15960 return false; 15961 }; 15962 }; 15963 for (var i=0; i<variables.length; i+=1) { 15964 this.tests.push(maketest(variables[i])); 15965 } 15966}; 15967 15968 15969CSL.Attributes["@is-uncertain-date"] = function (state, arg) { 15970 if (!this.tests) {this.tests = []; }; 15971 var variables = arg.split(/\s+/); 15972 // Strip off any boolean prefix. 15973 var maketest = function (myvariable) { 15974 return function(Item) { 15975 if (Item[myvariable] && Item[myvariable].circa) { 15976 return true; 15977 } else { 15978 return false; 15979 } 15980 }; 15981 }; 15982 for (var i=0,ilen=variables.length;i<ilen;i+=1) { 15983 this.tests.push(maketest(variables[i])); 15984 } 15985}; 15986 15987 15988CSL.Attributes["@locator"] = function (state, arg) { 15989 if (!this.tests) {this.tests = []; }; 15990 var trylabels = arg.replace("sub verbo", "sub-verbo"); 15991 trylabels = trylabels.split(/\s+/); 15992 // Strip off any boolean prefix. 15993 var maketest = function (trylabel) { 15994 return function(Item, item) { 15995 var label; 15996 state.processNumber(false, item, "locator"); 15997 label = state.tmp.shadow_numbers.locator.label; 15998 if (label && trylabel === label) { 15999 return true; 16000 } else { 16001 return false; 16002 } 16003 }; 16004 }; 16005 for (var i=0,ilen=trylabels.length;i<ilen;i+=1) { 16006 this.tests.push(maketest(trylabels[i])); 16007 } 16008}; 16009 16010 16011CSL.Attributes["@position"] = function (state, arg) { 16012 if (!this.tests) {this.tests = []; }; 16013 var tryposition; 16014 state.opt.update_mode = CSL.POSITION; 16015 var trypositions = arg.split(/\s+/); 16016 var testSubsequentNear = function (Item, item) { 16017 if (item && CSL.POSITION_MAP[item.position] >= CSL.POSITION_MAP[CSL.POSITION_SUBSEQUENT] && item["near-note"]) { 16018 return true; 16019 } 16020 return false; 16021 }; 16022 var testSubsequentNotNear = function (Item, item) { 16023 if (item && CSL.POSITION_MAP[item.position] == CSL.POSITION_MAP[CSL.POSITION_SUBSEQUENT] && !item["near-note"]) { 16024 return true; 16025 } 16026 return false; 16027 }; 16028 var maketest = function(tryposition) { 16029 return function (Item, item) { 16030 if (state.tmp.area === "bibliography") { 16031 return false; 16032 } 16033 if (item && "undefined" === typeof item.position) { 16034 item.position = 0; 16035 } 16036 if (item && typeof item.position === "number") { 16037 if (item.position === 0 && tryposition === 0) { 16038 return true; 16039 } else if (tryposition > 0 && CSL.POSITION_MAP[item.position] >= CSL.POSITION_MAP[tryposition]) { 16040 return true; 16041 } 16042 } else if (tryposition === 0) { 16043 return true; 16044 } 16045 return false; 16046 }; 16047 }; 16048 for (var i=0,ilen=trypositions.length;i<ilen;i+=1) { 16049 var tryposition = trypositions[i]; 16050 if (tryposition === "first") { 16051 tryposition = CSL.POSITION_FIRST; 16052 } else if (tryposition === "container-subsequent") { 16053 tryposition = CSL.POSITION_CONTAINER_SUBSEQUENT; 16054 } else if (tryposition === "subsequent") { 16055 tryposition = CSL.POSITION_SUBSEQUENT; 16056 } else if (tryposition === "ibid") { 16057 tryposition = CSL.POSITION_IBID; 16058 } else if (tryposition === "ibid-with-locator") { 16059 tryposition = CSL.POSITION_IBID_WITH_LOCATOR; 16060 } 16061 if ("near-note" === tryposition) { 16062 this.tests.push(testSubsequentNear); 16063 } else if ("far-note" === tryposition) { 16064 this.tests.push(testSubsequentNotNear); 16065 } else { 16066 this.tests.push(maketest(tryposition)); 16067 } 16068 } 16069}; 16070 16071CSL.Attributes["@type"] = function (state, arg) { 16072 if (!this.tests) {this.tests = []; }; 16073 // XXX This is ALWAYS composed as an "any" match 16074 var types = arg.split(/\s+/); 16075 // Strip off any boolean prefix. 16076 var maketest = function (mytype) { 16077 return function(Item) { 16078 var ret = (Item.type === mytype); 16079 if (ret) { 16080 return true; 16081 } else { 16082 return false; 16083 } 16084 }; 16085 }; 16086 var tests = []; 16087 for (var i=0,ilen=types.length;i<ilen;i+=1) { 16088 tests.push(maketest(types[i])); 16089 } 16090 this.tests.push(state.fun.match.any(this, state, tests)); 16091}; 16092 16093CSL.Attributes["@variable"] = function (state, arg) { 16094 if (!this.tests) {this.tests = []; }; 16095 var func; 16096 this.variables = arg.split(/\s+/); 16097 this.variables_real = this.variables.slice(); 16098 16099 // First the non-conditional code. 16100 if ("label" === this.name && this.variables[0]) { 16101 this.strings.term = this.variables[0]; 16102 } else if (["names", "date", "text", "number"].indexOf(this.name) > -1) { 16103 // 16104 // An oddity of variable handling is that this.variables 16105 // is actually ephemeral; the full list of variables is 16106 // held in the variables_real var, and pushed into this.variables 16107 // conditionally in order to suppress repeat renderings of 16108 // the same item variable. [STILL FUNCTIONAL? 2010.01.15] 16109 // 16110 // set variable names 16111 func = function (state, Item, item) { 16112 // Clear this.variables in place 16113 for (var i = this.variables.length - 1; i > -1; i += -1) { 16114 this.variables.pop(); 16115 } 16116 for (var i=0,ilen=this.variables_real.length;i<ilen;i++) { 16117 // set variable name if not quashed, and if not the title of a legal case w/suppress-author 16118 if (state.tmp.done_vars.indexOf(this.variables_real[i]) === -1 16119// This looks nuts. Why suppress a case name if not required by context? 16120// && !(item && Item.type === "legal_case" && item["suppress-author"] && this.variables_real[i] === "title") 16121 ) { 16122 this.variables.push(this.variables_real[i]); 16123 } 16124 if (state.tmp.can_block_substitute) { 16125 state.tmp.done_vars.push(this.variables_real[i]); 16126 } 16127 } 16128 }; 16129 this.execs.push(func); 16130 16131 // check for output 16132 func = function (state, Item, item) { 16133 var output = false; 16134 for (var i=0,ilen=this.variables.length;i<ilen;i++) { 16135 var variable = this.variables[i]; 16136 if (["authority", "committee"].indexOf(variable) > -1 16137 && "string" === typeof Item[variable] 16138 && "names" === this.name) { 16139 16140 // Great! So for each of these, we split. 16141 // And we only recombine everything if the length 16142 // of all the splits matches. 16143 16144 // Preflight 16145 var isValid = true; 16146 var rawNames = Item[variable].split(/\s*;\s*/); 16147 var rawMultiNames = {}; 16148 if (Item.multi && Item.multi._keys[variable]) { 16149 for (var langTag in Item.multi._keys[variable]) { 16150 rawMultiNames[langTag] = Item.multi._keys[variable][langTag].split(/\s*;\s*/); 16151 if (rawMultiNames[langTag].length !== rawNames.length) { 16152 isValid = false; 16153 break; 16154 } 16155 } 16156 } 16157 if (!isValid) { 16158 rawNames = [Item[variable]]; 16159 rawMultiNames = Item.multi._keys[variable]; 16160 } 16161 for (var j = 0, jlen = rawNames.length; j < jlen; j++) { 16162 var creatorParent = { 16163 literal:rawNames[j], 16164 multi:{ 16165 _key:{} 16166 } 16167 }; 16168 for (var langTag in rawMultiNames) { 16169 var creatorChild = { 16170 literal:rawMultiNames[langTag][j] 16171 }; 16172 creatorParent.multi._key[langTag] = creatorChild; 16173 } 16174 rawNames[j] = creatorParent; 16175 } 16176 Item[variable] = rawNames; 16177 } 16178 if (this.strings.form === "short" && !Item[variable]) { 16179 if (variable === "title") { 16180 variable = "title-short"; 16181 } else if (variable === "container-title") { 16182 variable = "container-title-short"; 16183 } 16184 } 16185 if (variable === "year-suffix") { 16186 // year-suffix always signals that it produces output, 16187 // even when it doesn't. This permits it to be used with 16188 // the "no date" term inside a group used exclusively 16189 // to control formatting. 16190 output = true; 16191 break; 16192 } else if (CSL.DATE_VARIABLES.indexOf(variable) > -1) { 16193 if (state.opt.development_extensions.locator_date_and_revision && "locator-date" === variable) { 16194 // If locator-date is set, it's valid. 16195 output = true; 16196 break; 16197 } 16198 if (Item[variable]) { 16199 for (var key in Item[variable]) { 16200 if (this.dateparts.indexOf(key) === -1 && "literal" !== key) { 16201 continue; 16202 } 16203 if (Item[variable][key]) { 16204 output = true; 16205 break; 16206 } 16207 } 16208 if (output) { 16209 break; 16210 } 16211 } 16212 } else if ("locator" === variable) { 16213 if (item && item.locator) { 16214 output = true; 16215 } 16216 break; 16217 } else if ("locator-extra" === variable) { 16218 if (item && item["locator-extra"]) { 16219 output = true; 16220 } 16221 break; 16222 } else if (["citation-number","citation-label"].indexOf(variable) > -1) { 16223 output = true; 16224 break; 16225 } else if ("first-reference-note-number" === variable) { 16226 if (item && item["first-reference-note-number"]) { 16227 output = true; 16228 } 16229 break; 16230 } else if ("first-container-reference-note-number" === variable) { 16231 if (item && item["first-container-reference-note-number"]) { 16232 output = true; 16233 } 16234 break; 16235 } else if ("hereinafter" === variable) { 16236 if (state.transform.abbrevs["default"].hereinafter[Item.id] 16237 && state.sys.getAbbreviation 16238 && Item.id) { 16239 16240 output = true; 16241 } 16242 break; 16243 } else if ("object" === typeof Item[variable]) { 16244 break; 16245 } else if ("string" === typeof Item[variable] && Item[variable]) { 16246 output = true; 16247 break; 16248 } else if ("number" === typeof Item[variable]) { 16249 output = true; 16250 break; 16251 } 16252 if (output) { 16253 break; 16254 } 16255 } 16256 //print("-- VAR: "+variable); 16257 //flag = state.tmp.group_context.tip; 16258 if (output) { 16259 for (var i=0,ilen=this.variables_real.length;i<ilen;i++) { 16260 var variable = this.variables_real[i]; 16261 if (variable !== "citation-number" || state.tmp.area !== "bibliography") { 16262 state.tmp.cite_renders_content = true; 16263 } 16264 //print(" setting [2] to true based on: " + arg); 16265 state.tmp.group_context.tip.variable_success = true; 16266 // For util_substitute.js, subsequent-author-substitute 16267 if (state.tmp.can_substitute.value() 16268 && state.tmp.area === "bibliography" 16269 && "string" === typeof Item[variable]) { 16270 16271 state.tmp.name_node.top = state.output.current.value(); 16272 state.tmp.rendered_name.push(Item[variable]); 16273 } 16274 } 16275 state.tmp.can_substitute.replace(false, CSL.LITERAL); 16276 } else { 16277 //print(" setting [1] to true based on: " + arg); 16278 state.tmp.group_context.tip.variable_attempt = true; 16279 } 16280 //state.tmp.group_context.replace(flag); 16281 }; 16282 this.execs.push(func); 16283 } else if (["if", "else-if", "condition"].indexOf(this.name) > -1) { 16284 // Strip off any boolean prefix. 16285 // Now the conditionals. 16286 var maketest = function (variable) { 16287 return function(Item,item){ 16288 var myitem = Item; 16289 if (item && ["locator", "locator-extra", "first-reference-note-number", "first-container-reference-note-number", "locator-date"].indexOf(variable) > -1) { 16290 myitem = item; 16291 } 16292 // We don't run loadAbbreviation() here; it is run by the application-supplied 16293 // retrieveItem() if hereinafter functionality is to be used, so this key will 16294 // always exist in memory, possibly with a nil value. 16295 if (variable === "hereinafter" && state.sys.getAbbreviation && myitem.id) { 16296 if (state.transform.abbrevs["default"].hereinafter[myitem.id]) { 16297 return true; 16298 } 16299 } else if (myitem[variable]) { 16300 if ("number" === typeof myitem[variable] || "string" === typeof myitem[variable]) { 16301 return true; 16302 } else if ("object" === typeof myitem[variable]) { 16303 // 16304 // this will turn true only for hash objects 16305 // that have at least one attribute, or for a 16306 // non-zero-length list 16307 // 16308 for (var key in myitem[variable]) { 16309 if (myitem[variable][key]) { 16310 return true; 16311 } 16312 } 16313 } 16314 } 16315 return false; 16316 }; 16317 }; 16318 for (var i=0,ilen=this.variables.length;i<ilen;i+=1) { 16319 this.tests.push(maketest(this.variables[i])); 16320 } 16321 } 16322}; 16323 16324 16325CSL.Attributes["@page"] = function (state, arg) { 16326 if (!this.tests) {this.tests = []; }; 16327 var trylabels = arg.replace("sub verbo", "sub-verbo"); 16328 trylabels = trylabels.split(/\s+/); 16329 // Strip off any boolean prefix. 16330 var maketest = function (trylabel) { 16331 return function(Item) { 16332 var label; 16333 state.processNumber(false, Item, "page", Item.type); 16334 if (!state.tmp.shadow_numbers.page.label) { 16335 label = "page"; 16336 } else if (state.tmp.shadow_numbers.page.label === "sub verbo") { 16337 label = "sub-verbo"; 16338 } else { 16339 label = state.tmp.shadow_numbers.page.label; 16340 } 16341 if (state.tmp.shadow_numbers.page.values.length > 0) { 16342 if (state.tmp.shadow_numbers.page.values[0].gotosleepability) { 16343 state.tmp.shadow_numbers.page.values[0].labelVisibility = false; 16344 } 16345 } 16346 if (trylabel === label) { 16347 return true; 16348 } else { 16349 return false; 16350 } 16351 }; 16352 }; 16353 for (var i=0,ilen=trylabels.length;i<ilen;i+=1) { 16354 this.tests.push(maketest(trylabels[i])); 16355 } 16356}; 16357 16358 16359// a near duplicate of code above 16360CSL.Attributes["@number"] = function (state, arg) { 16361 if (!this.tests) {this.tests = []; }; 16362 var trylabels = arg.split(/\s+/); 16363 var maketest = function(trylabel) { 16364 return function (Item) { 16365 var label; 16366 state.processNumber(false, Item, "number", Item.type); 16367 if (!state.tmp.shadow_numbers.number.label) { 16368 label = "number"; 16369 } else { 16370 label = state.tmp.shadow_numbers.number.label; 16371 } 16372 if (trylabel === label) { 16373 return true; 16374 } else { 16375 return false; 16376 } 16377 }; 16378 }; 16379 for (var i=0,ilen=trylabels.length;i<ilen;i+=1) { 16380 this.tests.push(maketest(trylabels[i])); 16381 } 16382}; 16383 16384CSL.Attributes["@jurisdiction"] = function (state, arg) { 16385 if (!this.tests) {this.tests = []; }; 16386 var tryjurisdictions = arg.split(/\s+/); 16387 16388 // This forces a match=any method, similar to @type 16389 var maketests = function (tryjurisdictions) { 16390 return function(Item) { 16391 if (!Item.jurisdiction) { 16392 return false; 16393 } 16394 var jurisdiction = Item.jurisdiction; 16395 for (var i=0,ilen=tryjurisdictions.length;i<ilen;i++) { 16396 if (jurisdiction === tryjurisdictions[i]) { 16397 return true; 16398 } 16399 } 16400 return false; 16401 }; 16402 }; 16403 this.tests.push(maketests(tryjurisdictions)); 16404}; 16405 16406CSL.Attributes["@country"] = function (state, arg) { 16407 if (!this.tests) {this.tests = []; }; 16408 var trycountries = arg.split(/\s+/); 16409 16410 // This forces a match=any method, similar to @type 16411 var maketests = function (trycountries) { 16412 return function(Item) { 16413 if (!Item.country) { 16414 return false; 16415 } 16416 var country = Item.country; 16417 for (var i=0,ilen=trycountries.length;i<ilen;i++) { 16418 if (country === trycountries[i]) { 16419 return true; 16420 } 16421 } 16422 return false; 16423 }; 16424 }; 16425 this.tests.push(maketests(trycountries)); 16426}; 16427 16428CSL.Attributes["@context"] = function (state, arg) { 16429 if (!this.tests) {this.tests = []; }; 16430 var func = function () { 16431 if (["bibliography", "citation"].indexOf(arg) > -1) { 16432 var area = state.tmp.area.slice(0, arg.length); 16433 if (area === arg) { 16434 return true; 16435 } 16436 return false; 16437 } else if ("alternative" === arg) { 16438 return !!state.tmp.abort_alternative; 16439 } 16440 }; 16441 this.tests.push(func); 16442}; 16443 16444CSL.Attributes["@has-year-only"] = function (state, arg) { 16445 if (!this.tests) {this.tests = []; }; 16446 var trydates = arg.split(/\s+/); 16447 var maketest = function (trydate) { 16448 return function(Item) { 16449 var date = Item[trydate]; 16450 if (!date || date.month || date.season) { 16451 return false; 16452 } else { 16453 return true; 16454 } 16455 }; 16456 }; 16457 for (var i=0,ilen=trydates.length;i<ilen;i+=1) { 16458 this.tests.push(maketest(trydates[i])); 16459 } 16460}; 16461 16462CSL.Attributes["@has-to-month-or-season"] = function (state, arg) { 16463 if (!this.tests) {this.tests = []; }; 16464 var trydates = arg.split(/\s+/); 16465 var maketest = function (trydate) { 16466 return function(Item) { 16467 var date = Item[trydate]; 16468 if (!date || (!date.month && !date.season) || date.day) { 16469 return false; 16470 } else { 16471 return true; 16472 } 16473 }; 16474 }; 16475 for (var i=0,ilen=trydates.length;i<ilen;i+=1) { 16476 this.tests.push(maketest(trydates[i])); 16477 } 16478}; 16479 16480CSL.Attributes["@has-day"] = function (state, arg) { 16481 if (!this.tests) {this.tests = []; }; 16482 var trydates = arg.split(/\s+/); 16483 var maketest = function (trydate) { 16484 return function(Item) { 16485 var date = Item[trydate]; 16486 if (!date || !date.day) { 16487 return false; 16488 } else { 16489 return true; 16490 } 16491 }; 16492 }; 16493 for (var i=0,ilen=trydates.length;i<ilen;i+=1) { 16494 this.tests.push(maketest(trydates[i])); 16495 } 16496}; 16497 16498CSL.Attributes["@is-plural"] = function (state, arg) { 16499 if (!this.tests) {this.tests = []; }; 16500 var func = function (Item) { 16501 var nameList = Item[arg]; 16502 if (nameList && nameList.length) { 16503 var persons = 0; 16504 var institutions = 0; 16505 var last_is_person = false; 16506 for (var i = 0, ilen = nameList.length; i < ilen; i += 1) { 16507 if (state.opt.development_extensions.spoof_institutional_affiliations 16508 && (nameList[i].literal || (nameList[i].isInstitution && nameList[i].family && !nameList[i].given))) { 16509 institutions += 1; 16510 last_is_person = false; 16511 } else { 16512 persons += 1; 16513 last_is_person = true; 16514 } 16515 } 16516 if (persons > 1) { 16517 return true; 16518 } else if (institutions > 1) { 16519 return true; 16520 } else if (institutions && last_is_person) { 16521 return true; 16522 } 16523 } 16524 return false; 16525 }; 16526 this.tests.push(func); 16527}; 16528 16529CSL.Attributes["@is-multiple"] = function (state, arg) { 16530 if (!this.tests) {this.tests = []; }; 16531 var func = function (Item) { 16532 var val = ("" + Item[arg]); 16533 var lst = val.split(/(?:,\s|\s(?:tot\sen\smet|lÄ«dz|oraz|and|bis|ÎÏÏ|και|och|až|do|en|et|in|ir|ja|og|sa|to|un|und|és|Èi|i|u|y|à |e|a|и|-|â)\s|â|\&)/); 16534 if (lst.length > 1) { 16535 return true; 16536 } 16537 return false; 16538 }; 16539 this.tests.push(func); 16540}; 16541 16542 16543 16544 16545CSL.Attributes["@locale"] = function (state, arg) { 16546 if (!this.tests) {this.tests = []; }; 16547 var ret, langspec, lang, lst, i, ilen; 16548 // Style default 16549 var locale_default = state.opt["default-locale"][0]; 16550 16551 if (this.name === "layout") { 16552 // For layout 16553 this.locale_raw = arg; 16554 if (this.tokentype === CSL.START) { 16555 if (!state.opt.multi_layout) { 16556 state.opt.multi_layout = []; 16557 } 16558 var locale_data = []; 16559 // Register the primary locale in the set, and others that "map" to it, 16560 // so that they can be used when generating sort keys. See node_sort.js. 16561 // Not idempotent. Only do this once. 16562 var locales = arg.split(/\s+/); 16563 var sort_locale = {}; 16564 var localeMaster = CSL.localeResolve(locales[0], locale_default); 16565 locale_data.push(localeMaster); 16566 if (localeMaster.generic) { 16567 sort_locale[localeMaster.generic] = localeMaster.best; 16568 } else { 16569 sort_locale[localeMaster.best] = localeMaster.best; 16570 } 16571 for (var i=1,ilen=locales.length;i<ilen;i+=1) { 16572 var localeServant = CSL.localeResolve(locales[i], locale_default); 16573 locale_data.push(localeServant); 16574 if (localeServant.generic) { 16575 sort_locale[localeServant.generic] = localeMaster.best; 16576 } else { 16577 sort_locale[localeServant.best] = localeMaster.best; 16578 } 16579 16580 } 16581 state[state.build.area].opt.sort_locales.push(sort_locale); 16582 state.opt.multi_layout.push(locale_data); 16583 } 16584 state.opt.has_layout_locale = true; 16585 } else { 16586 // For if and if-else 16587 16588 // Split argument 16589 lst = arg.split(/\s+/); 16590 16591 // Expand each list element 16592 var locale_bares = []; 16593 for (i = 0, ilen = lst.length; i < ilen; i += 1) { 16594 // Parse out language string 16595 lang = lst[i]; 16596 16597 // Analyze the locale 16598 langspec = CSL.localeResolve(lang, locale_default); 16599 if (lst[i].length === 2) { 16600 // For fallback 16601 locale_bares.push(langspec.bare); 16602 } 16603 // Load the locale terms etc. 16604 // (second argument causes immediate return if locale already exists) 16605 state.localeConfigure(langspec, true); 16606 16607 // Replace string with locale spec object 16608 lst[i] = langspec; 16609 } 16610 // Locales to test 16611 var locale_list = lst.slice(); 16612 16613 // check for variable value 16614 // Closure probably not necessary here. 16615 var maketest = function (locale_list, locale_default,locale_bares) { 16616 return function (Item) { 16617 var res; 16618 ret = []; 16619 res = false; 16620 var langspec = false; 16621 16622 var lang; 16623 if (!Item.language) { 16624 lang = locale_default; 16625 } else { 16626 lang = Item.language; 16627 } 16628 langspec = CSL.localeResolve(lang, locale_default); 16629 for (i = 0, ilen = locale_list.length; i < ilen; i += 1) { 16630 if (langspec.best === locale_list[i].best) { 16631 state.tmp.condition_lang_counter_arr.push(state.tmp.condition_counter); 16632 state.tmp.condition_lang_val_arr.push(state.opt.lang); 16633 state.opt.lang = locale_list[0].best; 16634 res = true; 16635 break; 16636 } 16637 } 16638 if (!res && locale_bares.indexOf(langspec.bare) > -1) { 16639 state.tmp.condition_lang_counter_arr.push(state.tmp.condition_counter); 16640 state.tmp.condition_lang_val_arr.push(state.opt.lang); 16641 state.opt.lang = locale_list[0].best; 16642 res = true; 16643 } 16644 return res; 16645 }; 16646 }; 16647 this.tests.push(maketest(locale_list,locale_default,locale_bares)); 16648 } 16649}; 16650 16651CSL.Attributes["@alternative-node-internal"] = function (state) { 16652 if (!this.tests) {this.tests = []; }; 16653 var maketest = function () { 16654 return function() { 16655 return !state.tmp.abort_alternative; 16656 }; 16657 }; 16658 var me = this; 16659 this.tests.push(maketest(me)); 16660}; 16661 16662CSL.Attributes["@locale-internal"] = function (state, arg) { 16663 if (!this.tests) {this.tests = []; }; 16664 var langspec, lang, lst, i, ilen; 16665 // For if and if-else 16666 16667 // Split argument 16668 lst = arg.split(/\s+/); 16669 16670 // Expand each list element 16671 this.locale_bares = []; 16672 for (i = 0, ilen = lst.length; i < ilen; i += 1) { 16673 // Parse out language string 16674 lang = lst[i]; 16675 16676 // Analyze the locale 16677 langspec = CSL.localeResolve(lang, state.opt["default-locale"][0]); 16678 if (lst[i].length === 2) { 16679 // For fallback 16680 this.locale_bares.push(langspec.bare); 16681 } 16682 // Load the locale terms etc. 16683 state.localeConfigure(langspec); 16684 16685 // Replace string with locale spec object 16686 lst[i] = langspec; 16687 } 16688 // Set locale tag on node 16689 this.locale_default = state.opt["default-locale"][0]; 16690 // The locale to set on node children if match is successful 16691 this.locale = lst[0].best; 16692 // Locales to test 16693 this.locale_list = lst.slice(); 16694 16695 // check for variable value 16696 // Closure probably not necessary here. 16697 var maketest = function (me) { 16698 return function (Item) { 16699 var ret, res; 16700 ret = []; 16701 res = false; 16702 var langspec = false; 16703 if (Item.language) { 16704 lang = Item.language; 16705 langspec = CSL.localeResolve(lang, state.opt["default-locale"][0]); 16706 if (langspec.best === state.opt["default-locale"][0]) { 16707 langspec = false; 16708 } 16709 } 16710 if (langspec) { 16711 // We attempt to match a specific locale from the 16712 // list of parameters. If that fails, we fall back 16713 // to the base locale of the first element. The 16714 // locale applied is always the first local 16715 // in the list of parameters (or base locale, for a 16716 // single two-character language code) 16717 for (i = 0, ilen = me.locale_list.length; i < ilen; i += 1) { 16718 if (langspec.best === me.locale_list[i].best) { 16719 state.opt.lang = me.locale; 16720 state.tmp.last_cite_locale = me.locale; 16721 // Set empty group open tag with locale set marker 16722 state.output.openLevel("empty"); 16723 state.output.current.value().new_locale = me.locale; 16724 res = true; 16725 break; 16726 } 16727 } 16728 if (!res && me.locale_bares.indexOf(langspec.bare) > -1) { 16729 state.opt.lang = me.locale; 16730 state.tmp.last_cite_locale = me.locale; 16731 // Set empty group open tag with locale set marker 16732 state.output.openLevel("empty"); 16733 state.output.current.value().new_locale = me.locale; 16734 res = true; 16735 } 16736 } 16737 return res; 16738 }; 16739 }; 16740 var me = this; 16741 this.tests.push(maketest(me)); 16742}; 16743 16744 16745CSL.Attributes["@court-class"] = function (state, arg) { 16746 if (!this.tests) {this.tests = []; }; 16747 var tryclasses = arg.split(/\s+/); 16748 var maketest = function (tryclass) { 16749 return function(Item) { 16750 var cls = CSL.GET_COURT_CLASS(state, Item); 16751 if (cls === tryclass) { 16752 return true; 16753 } else { 16754 return false; 16755 } 16756 }; 16757 }; 16758 for (var i=0,ilen=tryclasses.length; i<ilen; i++) { 16759 this.tests.push(maketest(tryclasses[i])); 16760 } 16761}; 16762 16763CSL.Attributes["@container-multiple"] = function (state, arg) { 16764 if (!this.tests) {this.tests = []; }; 16765 var retval = "true" === arg ? true : false; 16766 var maketest = function (retval) { 16767 return function(Item) { 16768 if (!state.tmp.container_item_count[Item.container_id]) { 16769 return !retval; 16770 } else if (state.tmp.container_item_count[Item.container_id] > 1) { 16771 return retval; 16772 } 16773 return !retval; 16774 }; 16775 }; 16776 this.tests.push(maketest(retval)); 16777}; 16778 16779CSL.Attributes["@container-subsequent"] = function (state, arg) { 16780 if (!this.tests) {this.tests = []; }; 16781 var retval = "true" === arg ? true : false; 16782 var maketest = function (retval) { 16783 return function(Item) { 16784 if (state.tmp.container_item_pos[Item.container_id] > 1) { 16785 return retval; 16786 } 16787 return !retval; 16788 }; 16789 }; 16790 this.tests.push(maketest(retval)); 16791}; 16792 16793CSL.Attributes["@has-subunit"] = function (state, arg) { 16794 if (!this.tests) {this.tests = []; }; 16795 var maketest = function(namevar) { 16796 return function (Item) { 16797 var subunit_count = 0; 16798 for (var i in Item[namevar]) { 16799 var name = Item[namevar][i]; 16800 if (!name.given) { 16801 var institution = name.literal ? name.literal : name.family; 16802 var length = institution.split("|").length; 16803 if (subunit_count === 0 || length < subunit_count) { 16804 subunit_count = length; 16805 } 16806 } 16807 } 16808 return (subunit_count > 1); 16809 }; 16810 }; 16811 this.tests.push(maketest(arg)); 16812} 16813 16814CSL.Attributes["@cite-form"] = function (state, arg) { 16815 if (!this.tests) {this.tests = []; }; 16816 var maketest = function(citeForm) { 16817 return function (Item) { 16818 if (Item["cite-form"] === citeForm) { 16819 return true; 16820 } 16821 return false; 16822 }; 16823 }; 16824 this.tests.push(maketest(arg)); 16825} 16826 16827CSL.Attributes["@disable-duplicate-year-suppression"] = function (state, arg) { 16828 state.opt.disable_duplicate_year_suppression = arg.split(/\s+/); 16829} 16830 16831CSL.Attributes["@consolidate-containers"] = function (state, arg) { 16832 CSL.Attributes["@track-containers"](state, arg); 16833 var args = arg.split(/\s+/); 16834 state.bibliography.opt.consolidate_containers = args; 16835} 16836 16837CSL.Attributes["@track-containers"] = function (state, arg) { 16838 var args = arg.split(/\s+/); 16839 if (!state.bibliography.opt.track_container_items) { 16840 state.bibliography.opt.track_container_items = []; 16841 } 16842 if (!state.bibliography.opt.consolidate_containers) { 16843 state.bibliography.opt.consolidate_containers = []; 16844 } 16845 state.bibliography.opt.track_container_items = state.bibliography.opt.track_container_items.concat(args); 16846} 16847 16848// These are not evaluated as conditions immediately: they only 16849// set parameters that are picked up during processing. 16850CSL.Attributes["@parallel-first"] = function (state, arg) { 16851 state.opt.parallel.enable = true; 16852 var vars = arg.split(/\s+/); 16853 if (!state.opt.track_repeat) { 16854 state.opt.track_repeat = {}; 16855 } 16856 this.parallel_first = {}; 16857 for (var i in vars) { 16858 var v = vars[i]; 16859 this.parallel_first[v] = true; 16860 state.opt.track_repeat[v] = true; 16861 } 16862}; 16863CSL.Attributes["@parallel-last"] = function (state, arg) { 16864 state.opt.parallel.enable = true; 16865 var vars = arg.split(/\s+/); 16866 if (!state.opt.track_repeat) { 16867 state.opt.track_repeat = {}; 16868 } 16869 this.parallel_last = {}; 16870 for (var i in vars) { 16871 var v = vars[i]; 16872 this.parallel_last[v] = true; 16873 state.opt.track_repeat[v] = true; 16874 } 16875}; 16876CSL.Attributes["@parallel-last-to-first"] = function (state, arg) { 16877 state.opt.parallel.enable = true; 16878 var vars = arg.split(/\s+/); 16879 this.parallel_last_to_first = {}; 16880 for (var i=0,ilen=vars.length;i<ilen;i++) { 16881 this.parallel_last_to_first[vars[i]] = true; 16882 } 16883}; 16884CSL.Attributes["@parallel-delimiter-override"] = function (state, arg) { 16885 state.opt.parallel.enable = true; 16886 this.strings.set_parallel_delimiter_override = arg; 16887}; 16888CSL.Attributes["@parallel-delimiter-override-on-suppress"] = function (state, arg) { 16889 state.opt.parallel.enable = true; 16890 this.strings.set_parallel_delimiter_override_on_suppress = arg; 16891}; 16892CSL.Attributes["@no-repeat"] = function (state, arg) { 16893 state.opt.parallel.enable = true; 16894 var vars = arg.split(/\s+/); 16895 if (!state.opt.track_repeat) { 16896 state.opt.track_repeat = {}; 16897 } 16898 this.non_parallel = {}; 16899 for (var i in vars) { 16900 var v = vars[i]; 16901 this.non_parallel[v] = true; 16902 state.opt.track_repeat[v] = true; 16903 } 16904}; 16905 16906CSL.Attributes["@require"] = function (state, arg) { 16907 state.opt.use_context_condition = true; 16908 this.strings.require = arg; 16909 16910 // Introduced to constrain rendering of the group with a 16911 // requirement that it either render an alpha term via cs:label or 16912 // cs:text at least once, or render without any label. That 16913 // behaviour is invoked with "label-empty-or-alpha" as arg. 16914 16915 // This attribute is a complement to @label-form and modular 16916 // jurisdiction support, as it makes macros that adapt to shifting 16917 // local term definitions possible. 16918}; 16919 16920CSL.Attributes["@reject"] = function (state, arg) { 16921 state.opt.use_context_condition = true; 16922 this.strings.reject = arg; 16923 16924 // Introduced to constrain rendering of the group with a 16925 // requirement that it render some label via cs:label or cs:text, 16926 // and that it NOT be alpha. That behaviour is invoked with 16927 // "label-empty-or-alpha" as arg. 16928 16929 // This attribute is a complement to @label-form and modular 16930 // jurisdiction support, as it makes macros that adapt to shifting 16931 // local term definitions possible. 16932}; 16933 16934CSL.Attributes["@require-comma-on-symbol"] = function (state, arg) { 16935 state.opt.require_comma_on_symbol = arg; 16936} 16937 16938CSL.Attributes["@gender"] = function (state, arg) { 16939 this.gender = arg; 16940}; 16941 16942CSL.Attributes["@cslid"] = function (state, arg) { 16943 // @cslid is a noop 16944 // The value set on this attribute is used to 16945 // generate reverse lookup wrappers on output when 16946 // this.development_extensions.csl_reverse_lookup_support is 16947 // set to true in state.js (there is no runtime option, 16948 // it must be set in state.js) 16949 // 16950 // See the @showid method in the html output 16951 // section of formats.js for the function that 16952 // renders the wrappers. 16953 this.cslid = parseInt(arg, 10); 16954}; 16955 16956CSL.Attributes["@capitalize-if-first"] = function (state, arg) { 16957 this.strings.capitalize_if_first_override = arg; 16958}; 16959 16960CSL.Attributes["@label-capitalize-if-first"] = function (state, arg) { 16961 this.strings.label_capitalize_if_first_override = arg; 16962}; 16963 16964CSL.Attributes["@label-form"] = function (state, arg) { 16965 this.strings.label_form_override = arg; 16966}; 16967 16968CSL.Attributes["@part-separator"] = function (state, arg) { 16969 this.strings["part-separator"] = arg; 16970}; 16971 16972CSL.Attributes["@leading-noise-words"] = function (state, arg) { 16973 this["leading-noise-words"] = arg; 16974}; 16975 16976CSL.Attributes["@name-never-short"] = function (state, arg) { 16977 this["name-never-short"] = arg; 16978}; 16979 16980CSL.Attributes["@class"] = function (state, arg) { 16981 state.opt["class"] = arg; 16982}; 16983 16984CSL.Attributes["@version"] = function (state, arg) { 16985 state.opt.version = arg; 16986}; 16987 16988/** 16989 * Store the value attribute on the token. 16990 * @name CSL.Attributes.@value 16991 * @function 16992 */ 16993CSL.Attributes["@value"] = function (state, arg) { 16994 this.strings.value = arg; 16995}; 16996 16997 16998/** 16999 * Store the name attribute (of a macro or term node) 17000 * on the state object. 17001 * <p>For reference when the closing node of a macro 17002 * or locale definition is encountered.</p> 17003 * @name CSL.Attributes.@name 17004 * @function 17005 */ 17006CSL.Attributes["@name"] = function (state, arg) { 17007 this.strings.name = arg; 17008}; 17009 17010/** 17011 * Store the form attribute (of a term node) on the state object. 17012 * <p>For reference when the closing node of a macro 17013 * or locale definition is encountered.</p> 17014 * @name CSL.Attributes.@form 17015 * @function 17016 */ 17017CSL.Attributes["@form"] = function (state, arg) { 17018 this.strings.form = arg; 17019}; 17020 17021CSL.Attributes["@date-parts"] = function (state, arg) { 17022 this.strings["date-parts"] = arg; 17023}; 17024 17025CSL.Attributes["@range-delimiter"] = function (state, arg) { 17026 this.strings["range-delimiter"] = arg; 17027}; 17028 17029/** 17030 * Store macro tokens in a buffer on the state object. 17031 * <p>For reference when the enclosing text token is 17032 * processed.</p> 17033 * @name CSL.Attributes.@macro 17034 * @function 17035 */ 17036CSL.Attributes["@macro"] = function (state, arg) { 17037 this.postponed_macro = arg; 17038}; 17039 17040/* 17041 * CSL.Attributes["@prefer-jurisdiction"] = function (state, arg) { 17042 * this.prefer_jurisdiction = true; 17043 * }; 17044 */ 17045 17046CSL.Attributes["@term"] = function (state, arg) { 17047 if (arg === "sub verbo") { 17048 this.strings.term = "sub-verbo"; 17049 } else { 17050 this.strings.term = arg; 17051 } 17052}; 17053 17054 17055/* 17056 * Ignore xmlns attribute. 17057 * <p>This should always be <p>http://purl.org/net/xbiblio/csl</code> 17058 * anyway. At least for the present we will blindly assume 17059 * that it is.</p> 17060 * @name CSL.Attributes.@xmlns 17061 * @function 17062 */ 17063CSL.Attributes["@xmlns"] = function () {}; 17064 17065 17066/* 17067 * Store language attribute to a buffer field. 17068 * <p>Will be placed in the appropriate location 17069 * when the element is processed.</p> 17070 * @name CSL.Attributes.@lang 17071 * @function 17072 */ 17073CSL.Attributes["@lang"] = function (state, arg) { 17074 if (arg) { 17075 state.build.lang = arg; 17076 } 17077}; 17078 17079 17080// Used as a flag during dates processing 17081CSL.Attributes["@lingo"] = function () {}; 17082 17083// Used as a flag during dates processing 17084CSL.Attributes["@macro-has-date"] = function () { 17085 this["macro-has-date"] = true; 17086}; 17087 17088/* 17089 * Store suffix string on token. 17090 * @name CSL.Attributes.@suffix 17091 * @function 17092 */ 17093CSL.Attributes["@suffix"] = function (state, arg) { 17094 this.strings.suffix = arg; 17095}; 17096 17097 17098/* 17099 * Store prefix string on token. 17100 * @name CSL.Attributes.@prefix 17101 * @function 17102 */ 17103CSL.Attributes["@prefix"] = function (state, arg) { 17104 this.strings.prefix = arg; 17105}; 17106 17107 17108/* 17109 * Store delimiter string on token. 17110 * @name CSL.Attributes.@delimiter 17111 * @function 17112 */ 17113CSL.Attributes["@delimiter"] = function (state, arg) { 17114 this.strings.delimiter = arg; 17115}; 17116 17117 17118/* 17119 * Store match evaluator on token. 17120 */ 17121CSL.Attributes["@match"] = function (state, arg) { 17122 this.match = arg; 17123}; 17124 17125 17126CSL.Attributes["@names-min"] = function (state, arg) { 17127 var val = parseInt(arg, 10); 17128 if (state[state.build.area].opt.max_number_of_names < val) { 17129 state[state.build.area].opt.max_number_of_names = val; 17130 } 17131 this.strings["et-al-min"] = val; 17132}; 17133 17134CSL.Attributes["@names-use-first"] = function (state, arg) { 17135 this.strings["et-al-use-first"] = parseInt(arg, 10); 17136}; 17137 17138CSL.Attributes["@names-use-last"] = function (state, arg) { 17139 if (arg === "true") { 17140 this.strings["et-al-use-last"] = true; 17141 } else { 17142 this.strings["et-al-use-last"] = false; 17143 } 17144}; 17145 17146CSL.Attributes["@sort"] = function (state, arg) { 17147 if (arg === "descending") { 17148 this.strings.sort_direction = CSL.DESCENDING; 17149 } 17150}; 17151 17152CSL.Attributes["@plural"] = function (state, arg) { 17153 // Accepted values of plural attribute differ on cs:text 17154 // and cs:label nodes. 17155 if ("always" === arg || "true" === arg) { 17156 this.strings.plural = 1; 17157 } else if ("never" === arg || "false" === arg) { 17158 this.strings.plural = 0; 17159 } else if ("contextual" === arg) { 17160 this.strings.plural = false; 17161 } 17162}; 17163 17164CSL.Attributes["@has-publisher-and-publisher-place"] = function () { 17165 this.strings["has-publisher-and-publisher-place"] = true; 17166}; 17167 17168CSL.Attributes["@publisher-delimiter-precedes-last"] = function (state, arg) { 17169 this.strings["publisher-delimiter-precedes-last"] = arg; 17170}; 17171 17172CSL.Attributes["@publisher-delimiter"] = function (state, arg) { 17173 this.strings["publisher-delimiter"] = arg; 17174}; 17175 17176CSL.Attributes["@publisher-and"] = function (state, arg) { 17177 this.strings["publisher-and"] = arg; 17178}; 17179 17180CSL.Attributes["@givenname-disambiguation-rule"] = function (state, arg) { 17181 if (CSL.GIVENNAME_DISAMBIGUATION_RULES.indexOf(arg) > -1) { 17182 state.citation.opt["givenname-disambiguation-rule"] = arg; 17183 } 17184}; 17185 17186CSL.Attributes["@collapse"] = function (state, arg) { 17187 // only one collapse value will be honoured. 17188 if (arg) { 17189 state[this.name].opt.collapse = arg; 17190 } 17191}; 17192 17193CSL.Attributes["@cite-group-delimiter"] = function (state, arg) { 17194 if (arg) { 17195 state[state.tmp.area].opt.cite_group_delimiter = arg; 17196 } 17197}; 17198 17199 17200 17201CSL.Attributes["@names-delimiter"] = function (state, arg) { 17202 state.setOpt(this, "names-delimiter", arg); 17203}; 17204 17205CSL.Attributes["@name-form"] = function (state, arg) { 17206 state.setOpt(this, "name-form", arg); 17207}; 17208 17209CSL.Attributes["@subgroup-delimiter"] = function (state, arg) { 17210 this.strings["subgroup-delimiter"] = arg; 17211}; 17212 17213CSL.Attributes["@subgroup-delimiter-precedes-last"] = function (state, arg) { 17214 this.strings["subgroup-delimiter-precedes-last"] = arg; 17215}; 17216 17217 17218CSL.Attributes["@name-delimiter"] = function (state, arg) { 17219 state.setOpt(this, "name-delimiter", arg); 17220}; 17221 17222CSL.Attributes["@et-al-min"] = function (state, arg) { 17223 var val = parseInt(arg, 10); 17224 if (state[state.build.area].opt.max_number_of_names < val) { 17225 state[state.build.area].opt.max_number_of_names = val; 17226 } 17227 state.setOpt(this, "et-al-min", val); 17228}; 17229 17230CSL.Attributes["@et-al-use-first"] = function (state, arg) { 17231 state.setOpt(this, "et-al-use-first", parseInt(arg, 10)); 17232}; 17233 17234CSL.Attributes["@et-al-use-last"] = function (state, arg) { 17235 if (arg === "true") { 17236 state.setOpt(this, "et-al-use-last", true); 17237 } else { 17238 state.setOpt(this, "et-al-use-last", false); 17239 } 17240}; 17241 17242CSL.Attributes["@et-al-subsequent-min"] = function (state, arg) { 17243 var val = parseInt(arg, 10); 17244 if (state[state.build.area].opt.max_number_of_names < val) { 17245 state[state.build.area].opt.max_number_of_names = val; 17246 } 17247 state.setOpt(this, "et-al-subsequent-min", val); 17248}; 17249 17250CSL.Attributes["@et-al-subsequent-use-first"] = function (state, arg) { 17251 state.setOpt(this, "et-al-subsequent-use-first", parseInt(arg, 10)); 17252}; 17253 17254CSL.Attributes["@suppress-min"] = function (state, arg) { 17255 this.strings["suppress-min"] = parseInt(arg, 10); 17256}; 17257 17258CSL.Attributes["@suppress-max"] = function (state, arg) { 17259 this.strings["suppress-max"] = parseInt(arg, 10); 17260}; 17261 17262 17263CSL.Attributes["@and"] = function (state, arg) { 17264 state.setOpt(this, "and", arg); 17265}; 17266 17267CSL.Attributes["@delimiter-precedes-last"] = function (state, arg) { 17268 state.setOpt(this, "delimiter-precedes-last", arg); 17269}; 17270 17271CSL.Attributes["@delimiter-precedes-et-al"] = function (state, arg) { 17272 state.setOpt(this, "delimiter-precedes-et-al", arg); 17273}; 17274 17275CSL.Attributes["@initialize-with"] = function (state, arg) { 17276 state.setOpt(this, "initialize-with", arg); 17277}; 17278 17279CSL.Attributes["@initialize"] = function (state, arg) { 17280 if (arg === "false") { 17281 state.setOpt(this, "initialize", false); 17282 } 17283}; 17284 17285CSL.Attributes["@name-as-reverse-order"] = function (state, arg) { 17286 this["name-as-reverse-order"] = arg; 17287}; 17288 17289CSL.Attributes["@name-as-sort-order"] = function (state, arg) { 17290 if (this.name === "style-options") { 17291 this["name-as-sort-order"] = arg; 17292 } else { 17293 state.setOpt(this, "name-as-sort-order", arg); 17294 } 17295}; 17296 17297CSL.Attributes["@sort-separator"] = function (state, arg) { 17298 state.setOpt(this, "sort-separator", arg); 17299}; 17300 17301CSL.Attributes["@require-match"] = function (state, arg) { 17302 if (arg === "true") { 17303 this.requireMatch = true; 17304 } 17305}; 17306 17307CSL.Attributes["@exclude-types"] = function (state, arg) { 17308 state.bibliography.opt.exclude_types = arg.split(/\s+/); 17309}; 17310 17311CSL.Attributes["@exclude-with-fields"] = function (state, arg) { 17312 state.bibliography.opt.exclude_with_fields = arg.split(/\s+/); 17313}; 17314 17315 17316CSL.Attributes["@year-suffix-delimiter"] = function (state, arg) { 17317 state[this.name].opt["year-suffix-delimiter"] = arg; 17318}; 17319 17320CSL.Attributes["@after-collapse-delimiter"] = function (state, arg) { 17321 state[this.name].opt["after-collapse-delimiter"] = arg; 17322}; 17323 17324CSL.Attributes["@subsequent-author-substitute"] = function (state, arg) { 17325 state[this.name].opt["subsequent-author-substitute"] = arg; 17326}; 17327 17328CSL.Attributes["@subsequent-author-substitute-rule"] = function (state, arg) { 17329 state[this.name].opt["subsequent-author-substitute-rule"] = arg; 17330}; 17331 17332CSL.Attributes["@disambiguate-add-names"] = function (state, arg) { 17333 if (arg === "true") { 17334 state.opt["disambiguate-add-names"] = true; 17335 } 17336}; 17337 17338CSL.Attributes["@disambiguate-add-givenname"] = function (state, arg) { 17339 if (arg === "true") { 17340 state.opt["disambiguate-add-givenname"] = true; 17341 } 17342}; 17343 17344CSL.Attributes["@disambiguate-add-year-suffix"] = function (state, arg) { 17345 if (arg === "true" && state.opt.xclass !== "numeric") { 17346 state.opt["disambiguate-add-year-suffix"] = true; 17347 } 17348}; 17349 17350 17351CSL.Attributes["@second-field-align"] = function (state, arg) { 17352 if (arg === "flush" || arg === "margin") { 17353 state[this.name].opt["second-field-align"] = arg; 17354 } 17355}; 17356 17357 17358CSL.Attributes["@hanging-indent"] = function (state, arg) { 17359 if (arg === "true") { 17360 if (state.opt.development_extensions.hanging_indent_legacy_number) { 17361 state[this.name].opt.hangingindent = 2; 17362 } else { 17363 state[this.name].opt.hangingindent = true; 17364 } 17365 } 17366}; 17367 17368 17369CSL.Attributes["@line-spacing"] = function (state, arg) { 17370 if (arg && arg.match(/^[.0-9]+$/)) { 17371 state[this.name].opt["line-spacing"] = parseFloat(arg, 10); 17372 } 17373}; 17374 17375 17376CSL.Attributes["@entry-spacing"] = function (state, arg) { 17377 if (arg && arg.match(/^[.0-9]+$/)) { 17378 state[this.name].opt["entry-spacing"] = parseFloat(arg, 10); 17379 } 17380}; 17381 17382 17383CSL.Attributes["@near-note-distance"] = function (state, arg) { 17384 state[this.name].opt["near-note-distance"] = parseInt(arg, 10); 17385}; 17386 17387CSL.Attributes["@substring"] = function (state, arg) { 17388 this.substring = parseInt(arg, 10); 17389}; 17390 17391CSL.Attributes["@text-case"] = function (state, arg) { 17392 var func = function (state, Item) { 17393 if (arg === "normal") { 17394 this.text_case_normal = true; 17395 } else { 17396 this.strings["text-case"] = arg; 17397 if (arg === "title") { 17398 if (Item.jurisdiction) { 17399 this.strings["text-case"] = "passthrough"; 17400 } 17401 } 17402 } 17403 }; 17404 this.execs.push(func); 17405}; 17406 17407 17408CSL.Attributes["@page-range-format"] = function (state, arg) { 17409 state.opt["page-range-format"] = arg; 17410}; 17411 17412 17413CSL.Attributes["@year-range-format"] = function (state, arg) { 17414 state.opt["year-range-format"] = arg; 17415}; 17416 17417 17418CSL.Attributes["@default-locale"] = function (state, arg) { 17419 if (this.name === 'style') { 17420 var lst, len, pos, m, ret; 17421 // 17422 // Workaround for Internet Exploder 6 (doesn't recognize 17423 // groups in str.split(/something(braced-group)something/) 17424 // 17425 var m = arg.match(/-x-(sort|translit|translat)-/g); 17426 if (m) { 17427 for (pos = 0, len = m.length; pos < len; pos += 1) { 17428 m[pos] = m[pos].replace(/^-x-/, "").replace(/-$/, ""); 17429 } 17430 } 17431 lst = arg.split(/-x-(?:sort|translit|translat)-/); 17432 ret = [lst[0]]; 17433 for (pos = 1, len = lst.length; pos < len; pos += 1) { 17434 ret.push(m[pos - 1]); 17435 ret.push(lst[pos]); 17436 } 17437 lst = ret.slice(); 17438 len = lst.length; 17439 for (pos = 1; pos < len; pos += 2) { 17440 state.opt[("locale-" + lst[pos])].push(lst[(pos + 1)].replace(/^\s*/g, "").replace(/\s*$/g, "")); 17441 } 17442 if (lst.length) { 17443 state.opt["default-locale"] = lst.slice(0, 1); 17444 } else { 17445 state.opt["default-locale"] = ["en"]; 17446 } 17447 } else if (arg === "true") { 17448 this.default_locale = true; 17449 } 17450}; 17451 17452CSL.Attributes["@default-locale-sort"] = function (state, arg) { 17453 state.opt["default-locale-sort"] = arg; 17454}; 17455 17456CSL.Attributes["@demote-non-dropping-particle"] = function (state, arg) { 17457 state.opt["demote-non-dropping-particle"] = arg; 17458}; 17459 17460CSL.Attributes["@initialize-with-hyphen"] = function (state, arg) { 17461 if (arg === "false") { 17462 state.opt["initialize-with-hyphen"] = false; 17463 } 17464}; 17465 17466CSL.Attributes["@institution-parts"] = function (state, arg) { 17467 this.strings["institution-parts"] = arg; 17468}; 17469 17470CSL.Attributes["@if-short"] = function (state, arg) { 17471 if (arg === "true") { 17472 this.strings["if-short"] = true; 17473 } 17474}; 17475 17476CSL.Attributes["@substitute-use-first"] = function (state, arg) { 17477 this.strings["substitute-use-first"] = parseInt(arg, 10); 17478}; 17479 17480CSL.Attributes["@use-first"] = function (state, arg) { 17481 this.strings["use-first"] = parseInt(arg, 10); 17482}; 17483 17484CSL.Attributes["@use-last"] = function (state, arg) { 17485 this.strings["use-last"] = parseInt(arg, 10); 17486}; 17487 17488CSL.Attributes["@stop-first"] = function (state, arg) { 17489 this.strings["stop-first"] = parseInt(arg, 10); 17490}; 17491 17492CSL.Attributes["@stop-last"] = function (state, arg) { 17493 this.strings["stop-last"] = parseInt(arg, 10) * -1; 17494}; 17495 17496 17497CSL.Attributes["@reverse-order"] = function (state, arg) { 17498 if ("true" === arg) { 17499 this.strings["reverse-order"] = true; 17500 } 17501}; 17502 17503CSL.Attributes["@display"] = function (state, arg) { 17504 if (state.bibliography.tokens.length === 2) { 17505 state.opt.using_display = true; 17506 } 17507 this.strings.cls = arg; 17508}; 17509 17510 17511/*global CSL: true */ 17512 17513 17514/** 17515 * String stack object. 17516 * <p>Numerous string stacks are used to track nested 17517 * parameters at runtime. This class provides methods 17518 * that remove some of the aggravation of managing 17519 * them.</p> 17520 * @class 17521 */ 17522CSL.Stack = function (val, literal) { 17523 this.mystack = []; 17524 if (literal || val) { 17525 this.mystack.push(val); 17526 } 17527 this.tip = this.mystack[0]; 17528}; 17529 17530/** 17531 * Push a value onto the stack. 17532 * <p>This just does what it says.</p> 17533 */ 17534CSL.Stack.prototype.push = function (val, literal) { 17535 if (literal || val) { 17536 this.mystack.push(val); 17537 } else { 17538 this.mystack.push(""); 17539 } 17540 this.tip = this.mystack[this.mystack.length - 1]; 17541}; 17542 17543/** 17544 * Clear the stack 17545 */ 17546CSL.Stack.prototype.clear = function () { 17547 this.mystack = []; 17548 this.tip = {}; 17549}; 17550 17551/** 17552 * Replace the top value on the stack. 17553 * <p>This removes some ugly syntax from the 17554 * main code.</p> 17555 */ 17556CSL.Stack.prototype.replace = function (val, literal) { 17557 // 17558 // safety fix after a bug was chased down. Rhino 17559 // JS will process a negative index without error (!). 17560 if (this.mystack.length === 0) { 17561 CSL.error("Internal CSL processor error: attempt to replace nonexistent stack item with " + val); 17562 } 17563 if (literal || val) { 17564 this.mystack[(this.mystack.length - 1)] = val; 17565 } else { 17566 this.mystack[(this.mystack.length - 1)] = ""; 17567 } 17568 this.tip = this.mystack[this.mystack.length - 1]; 17569}; 17570 17571 17572/** 17573 * Remove the top value from the stack. 17574 * <p>Just does what it says.</p> 17575 */ 17576CSL.Stack.prototype.pop = function () { 17577 var ret = this.mystack.pop(); 17578 if (this.mystack.length) { 17579 this.tip = this.mystack[this.mystack.length - 1]; 17580 } else { 17581 this.tip = {}; 17582 } 17583 return ret; 17584}; 17585 17586 17587/** 17588 * Return the top value on the stack. 17589 * <p>Removes a little hideous complication from 17590 * the main code.</p> 17591 */ 17592CSL.Stack.prototype.value = function () { 17593 return this.mystack.slice(-1)[0]; 17594}; 17595 17596 17597/** 17598 * Return length (depth) of stack. 17599 * <p>Used to identify if there is content to 17600 * be handled on the stack</p> 17601 */ 17602CSL.Stack.prototype.length = function () { 17603 return this.mystack.length; 17604}; 17605 17606/*global CSL: true */ 17607 17608/** 17609 * Initializes the parallel cite tracking arrays 17610 */ 17611CSL.Parallel = function (state) { 17612 this.state = state; 17613}; 17614 17615CSL.Parallel.prototype.StartCitation = function (sortedItems, out) { 17616 // This array carries the repeat markers used in rendering the cite. 17617 this.state.tmp.suppress_repeats = []; 17618 if (sortedItems.length < 2) return; 17619 var idxEnd = 0; 17620 var parallelMatchList = false; 17621 var siblingRanges = []; 17622 17623 for (var i=0,ilen=sortedItems.length-1;i<ilen;i++) { 17624 var currItem = sortedItems[i][0]; 17625 var nextItem = sortedItems[i+1][0]; 17626 var freshMatchList = false; 17627 var info = {}; 17628 if (sortedItems[i][0].seeAlso && sortedItems[i][0].seeAlso.length > 0 && !parallelMatchList) { 17629 freshMatchList = true; 17630 parallelMatchList = [sortedItems[i][0].id].concat(sortedItems[i][0].seeAlso); 17631 var tempMatchList = parallelMatchList.slice(); 17632 var remainder = sortedItems.slice(i); 17633 remainder[0][1].parallel = "first"; 17634 for (var j=0,jlen=remainder.length;j<jlen;j++) { 17635 var itemID = remainder[j][0].id; 17636 var ididx = tempMatchList.indexOf(itemID); 17637 idxEnd = false; 17638 if (ididx === -1) { 17639 idxEnd = (i+j-1); 17640 } else if ((i+j) === (sortedItems.length-1)) { 17641 idxEnd = (i+j); 17642 } 17643 if (idxEnd) { 17644 siblingRanges.push([i, idxEnd]); 17645 break; 17646 } else { 17647 tempMatchList = tempMatchList.slice(0, ididx).concat(tempMatchList.slice(ididx+1)); 17648 } 17649 } 17650 } 17651 // parallelMatchList/freshMatchList relate only to parallels. 17652 // no-repeat non-parallels are handled in a separate block. 17653 if (i > 0 && freshMatchList) { 17654 this.state.tmp.suppress_repeats[i-1].START = true; 17655 freshMatchList = false; 17656 } 17657 for (var varname in this.state.opt.track_repeat) { 17658 if (!currItem[varname] || !nextItem[varname]) { 17659 // Go ahead and render any value with an empty partner 17660 info[varname] = false; 17661 } else if ("string" === typeof nextItem[varname] || "number" === typeof nextItem[varname]) { 17662 // Simple comparison of string values 17663 if (varname === "title" && currItem["title-short"] && nextItem["title-short"]) { 17664 var currVal = currItem["title-short"]; 17665 var nextVal = nextItem["title-short"]; 17666 } else { 17667 var currVal = currItem[varname]; 17668 var nextVal = nextItem[varname]; 17669 } 17670 if (currVal == nextVal) { 17671 info[varname] = true; 17672 } else { 17673 info[varname] = false; 17674 } 17675 } else if ("undefined" === typeof currItem[varname].length) { 17676 // If a date, use only the year 17677 info[varname] = false; 17678 var currYear = currItem[varname].year; 17679 var nextYear = nextItem[varname].year; 17680 if (currYear && nextYear) { 17681 if (currYear == nextYear) { 17682 info[varname] = true; 17683 } 17684 } 17685 } else { 17686 // If a creator value, kludge it 17687 var currVal = JSON.stringify(currItem[varname]); 17688 var nextVal = JSON.stringify(nextItem[varname]); 17689 if (currVal === nextVal) { 17690 info[varname] = true; 17691 } else { 17692 info[varname] = false; 17693 } 17694 } 17695 } 17696 if (!parallelMatchList) { 17697 info.ORPHAN = true; 17698 } 17699 if (idxEnd === i) { 17700 info.END = true; 17701 parallelMatchList = false; 17702 } 17703 this.state.tmp.suppress_repeats.push(info); 17704 } 17705 17706 // if (!this.state.tmp.just_looking) { 17707 // this.state.sys.print(`${JSON.stringify(this.state.tmp.suppress_repeats, null, 2)}`); 17708 // } 17709 17710 // Set no-repeat info here? 17711 for (var j=0,jlen=siblingRanges.length;j<jlen;j++) { 17712 var masterID = sortedItems[siblingRanges[j][0]][0].id; 17713 this.state.registry.registry[masterID].master = true; 17714 this.state.registry.registry[masterID].siblings = []; 17715 var start = siblingRanges[j][0]; 17716 var end = siblingRanges[j][1]; 17717 for (var k=start; k<end; k++) { 17718 this.state.tmp.suppress_repeats[k].SIBLING = true; 17719 var siblingID = sortedItems[k+1][0].id; 17720 sortedItems[k+1][1].parallel = "other"; 17721 this.state.registry.registry[masterID].siblings.push(siblingID); 17722 } 17723 } 17724 // this.state.sys.print(JSON.stringify(this.state.tmp.suppress_repeats, null, 2)); 17725}; 17726 17727CSL.Parallel.prototype.checkRepeats = function(params) { 17728 var idx = this.state.tmp.cite_index; 17729 if (this.state.tmp.suppress_repeats) { 17730 if (params.parallel_first && Object.keys(params.parallel_first).length > 0) { 17731 var arr = [{}].concat(this.state.tmp.suppress_repeats); 17732 var ret = true; 17733 for (var varname in params.parallel_first) { 17734 if (!arr[idx][varname] || arr[idx].START) { 17735 // true --> suppress the entry 17736 // Test here evaluates as "all", not "any" 17737 ret = false; 17738 } 17739 } 17740 return ret; 17741 } 17742 if (params.parallel_last && Object.keys(params.parallel_last).length > 0) { 17743 var arr = this.state.tmp.suppress_repeats.concat([{}]); 17744 var ret = Object.keys(params.parallel_last).length > 0 ? true : false; 17745 for (var varname in params.parallel_last) { 17746 if (!arr[idx][varname] || arr[idx].END) { 17747 // "all" match, as above. 17748 ret = false; 17749 } 17750 } 17751 return ret; 17752 } 17753 if (params.non_parallel && Object.keys(params.non_parallel).length > 0) { 17754 var arr = [{}].concat(this.state.tmp.suppress_repeats); 17755 var ret = true; 17756 for (var varname in params.non_parallel) { 17757 if (!arr[idx][varname]) { 17758 ret = false; 17759 } 17760 } 17761 return ret; 17762 } 17763 } 17764 return false; 17765}; 17766 17767/*global CSL: true */ 17768 17769 17770CSL.Util = {}; 17771 17772CSL.Util.Match = function () { 17773 17774 this.any = function (token, state, tests) { 17775 return function (Item, item) { 17776 for (var i=0, ilen=tests.length; i < ilen; i += 1) { 17777 var result = tests[i](Item, item); 17778 if (result) { 17779 return true; 17780 } 17781 } 17782 return false; 17783 }; 17784 }; 17785 17786 this.none = function (token, state, tests) { 17787 return function (Item, item) { 17788 for (var i=0,ilen=tests.length;i<ilen;i+=1) { 17789 var result = tests[i](Item,item); 17790 if (result) { 17791 return false; 17792 } 17793 } 17794 return true; 17795 }; 17796 }; 17797 17798 this.all = function (token, state, tests) { 17799 return function (Item, item) { 17800 for (var i=0,ilen=tests.length;i<ilen;i+=1) { 17801 var result = tests[i](Item,item); 17802 if (!result) { 17803 return false; 17804 } 17805 } 17806 return true; 17807 }; 17808 }; 17809 17810 this[undefined] = this.all; 17811 17812 this.nand = function (token, state, tests) { 17813 return function (Item, item) { 17814 for (var i=0,ilen=tests.length;i<ilen;i+=1) { 17815 var result = tests[i](Item,item); 17816 if (!result) { 17817 return true; 17818 } 17819 } 17820 return false; 17821 }; 17822 }; 17823 17824}; 17825 17826/*global CSL: true */ 17827 17828/* 17829 * Fields can be transformed by translation/transliteration, or by 17830 * abbreviation. Transformations are performed in that order. 17831 * 17832 * Renderings of original, translated or transliterated content 17833 * (followed by abbreviation if requested) are placed in the primary 17834 * output slot or the (implicitly punctuated) secondary and tertiary 17835 * output slots according to the settings registered in the 17836 * state.opt['cite-lang-prefs'] arrays. The array has six segments: 17837 * 'persons', 'institutions', 'titles', 'journals', 'publishers', and 17838 * 'places'. Each segment always contains at least one item, and may 17839 * hold values 'orig', 'translit' or 'translat'. The array defaults to 17840 * a single item 'orig'. 17841 * 17842 * All multilingual variables are associated with segments, 17843 * with the exception of 'edition' and 'genre'. These two 17844 * exceptions are always rendered with the first matching 17845 * language form found in state.opt['locale-translit'] or, if 17846 * composing a sort key, state.opt['locale-sort']. No secondary 17847 * slot rendering is performed for this two variables. 17848 * 17849 * The balance of multilingual variables are rendered with 17850 * the first matching value in the transform locales spec 17851 * (no transform, state.opt['locale-translit'], or 17852 * state.opt['locale-translat']) mapped to the target 17853 * slot. 17854 * 17855 * Full primary+secondary+tertiary rendering is performed only in 17856 * note-style citations and the bibliography. In-text citations are 17857 * rendered in the primary output slot only, following the same spec 17858 * parameters. 17859 * 17860 * Optional setters: 17861 * .setAbbreviationFallback(); fallback flag 17862 * (if true, a failed abbreviation will fallback to long) 17863 * 17864 * .setAlternativeVariableName(): alternative variable name in Item, 17865 * for use as a fallback abbreviation source 17866 * 17867 * Translation/transliteration 17868 * 17869 * Optional setter: 17870 * .setTransformFallback(): 17871 * default flag (if true, the original field value will be used as a fallback) 17872 * 17873 * The getTextSubField() method may be used to obtain a string transform 17874 * of a field, without abbreviation, as needed for setting sort keys 17875 * (for example). 17876 * 17877 */ 17878 17879CSL.Transform = function (state) { 17880 // Abbreviation families 17881 this.abbrevs = {}; 17882 this.abbrevs["default"] = new state.sys.AbbreviationSegments(); 17883 17884 function getCountryOrJurisdiction(variable, normalizedKey, quashCountry) { 17885 var value = ""; 17886 if (state.sys.getHumanForm) { 17887 if (variable === "country") { 17888 value = state.sys.getHumanForm(normalizedKey.toLowerCase(), false, true); 17889 value = value.split("|")[0]; 17890 } else if (variable === "jurisdiction") { 17891 value = state.sys.getHumanForm(normalizedKey.toLowerCase(), false, true); 17892 if (!quashCountry) { 17893 value = value.split("|").slice(1).join(", "); 17894 } else { 17895 // Bare country name is rendered by "country", not "jurisdiction" 17896 value = ""; 17897 } 17898 } 17899 } 17900 return value; 17901 } 17902 17903 // Internal function 17904 function abbreviate(state, tok, Item, altvar, basevalue, family_var, use_field) { 17905 17906 var value = ""; 17907 var myabbrev_family = CSL.FIELD_CATEGORY_REMAP[family_var]; 17908 var preferredJurisdiction; 17909 if (!myabbrev_family) { 17910 return basevalue; 17911 } 17912 17913 var variable = family_var; 17914 var normalizedKey = basevalue; 17915 17916 if (state.sys.normalizeAbbrevsKey) { 17917 normalizedKey = state.sys.normalizeAbbrevsKey(family_var, basevalue); 17918 } 17919 var quashCountry = false; 17920 if (variable === "jurisdiction" && normalizedKey) { 17921 quashCountry = normalizedKey.indexOf(":") === -1; 17922 } 17923 // Fix up jurisdiction codes 17924 if (["jurisdiction", "country"].indexOf(family_var) > -1 && basevalue === basevalue.toLowerCase()) { 17925 normalizedKey = basevalue.toUpperCase(); 17926 } 17927 17928 17929 // Lazy retrieval of abbreviations. 17930 if (state.sys.getAbbreviation) { 17931 17932 if (["jurisdiction", "country", "language-name", "language-name-original"].indexOf(variable) > -1) { 17933 preferredJurisdiction = "default"; 17934 } else if (Item.jurisdiction) { 17935 preferredJurisdiction = Item.jurisdiction; 17936 } else { 17937 preferredJurisdiction = "default"; 17938 } 17939 var jurisdiction = state.transform.loadAbbreviation(preferredJurisdiction, myabbrev_family, normalizedKey, Item.language); 17940 17941 // Some rules: 17942 // # variable === "country" 17943 // (1) If an abbreviation is associated with the code, then: 17944 // (a) return the abbreviated form if form="short" 17945 // (2) Otherwise: 17946 // (a) return the human-readable country name, or whatever is there if it's not a code 17947 // # variable === "jurisdiction" 17948 // (1) If !!getHumanForm(jurisdictionID, false, false): 17949 // (a) If the code is top-level (i.e. a country): 17950 // (i) return nothing -- this is what the "country" variable is for. 17951 // (b) otherwise: 17952 // (i) If an abbreviation is associated with the code, then: 17953 // (A) return the abbreviated form 17954 // (ii) Otherwise 17955 // (A) return the human-readable form, with the country name & code removed from the front 17956 // (2) Otherwise: 17957 // (a) abbreviate as per normal. 17958 // # other variables 17959 // (1) Abbreviate as per normal. 17960 17961 if (state.transform.abbrevs[jurisdiction][myabbrev_family] && normalizedKey) { 17962 // Safe to test presence of abbrev against raw object in this block 17963 var abbrev = state.transform.abbrevs[jurisdiction][myabbrev_family][normalizedKey]; 17964 if (tok.strings.form === "short" && abbrev) { 17965 if (quashCountry) { 17966 value = ""; 17967 } else { 17968 value = abbrev; 17969 } 17970 } else { 17971 value = getCountryOrJurisdiction(variable, normalizedKey, quashCountry); 17972 } 17973 } 17974 } 17975 17976 // Was for: 17977 if (!value 17978 && (!state.opt.development_extensions.require_explicit_legal_case_title_short || Item.type !== 'legal_case') 17979 && altvar && Item[altvar] && use_field) { 17980 value = Item[altvar]; 17981 } 17982 if (!value && !state.sys.getAbbreviation && state.sys.getHumanForm) { 17983 value = getCountryOrJurisdiction(variable, normalizedKey, quashCountry); 17984 } 17985 if (!value && !quashCountry && (!state.sys.getHumanForm || variable !== "jurisdiction")) { 17986 value = basevalue; 17987 } 17988 if (state.opt.development_extensions.force_title_abbrev_fallback) { 17989 if (variable === "title" && value === basevalue && Item["title-short"]) { 17990 value = Item["title-short"]; 17991 } 17992 } 17993 return value; 17994 } 17995 17996 function getFieldLocale(Item,field) { 17997 var ret = state.opt["default-locale"][0].slice(0, 2); 17998 var localeRex; 17999 if (state.opt.development_extensions.strict_text_case_locales) { 18000 localeRex = new RegExp("^([a-zA-Z]{2})(?:$|-.*| .*)"); 18001 } else { 18002 localeRex = new RegExp("^([a-zA-Z]{2})(?:$|-.*|.*)"); 18003 } 18004 if (Item.language) { 18005 var m = ("" + Item.language).match(localeRex); 18006 if (m) { 18007 ret = m[1]; 18008 } else { 18009 // Set garbage to "Klingon". 18010 ret = "tlh"; 18011 } 18012 } 18013 if (Item.multi && Item.multi && Item.multi.main && Item.multi.main[field]) { 18014 ret = Item.multi.main[field]; 18015 }
18016 if (!state.opt.development_extensions.strict_text_case_locales 18017 || state.opt.development_extensions.normalize_lang_keys_to_lowercase) { 18018 18019 ret = ret.toLowerCase(); 18020 } 18021 return ret; 18022 } 18023 18024 // Internal functions 18025 function getTextSubField (Item, field, locale_type, use_default, stopOrig, family_var) { 18026 var opt, o, ret, opts; 18027 var usedOrig = stopOrig; 18028 var usingOrig = false; 18029 18030 if (!Item[field]) { 18031 return { 18032 name:"", 18033 usedOrig:stopOrig, 18034 token: CSL.Util.cloneToken(this) 18035 }; 18036 } 18037 // If form="short" is selected ("family_var" is a misnomer 18038 // here, it means short-form requested), and the variable 18039 // has a short-form partner (i.e. it is in array 18040 // VARIABLES_WITH_SHORT_FORM), then it is run here as *-short". 18041 var stickyLongForm = false; 18042 if (CSL.VARIABLES_WITH_SHORT_FORM.indexOf(field) > -1 18043 && family_var) { 18044 18045 field = field + "-short"; 18046 stickyLongForm = true; 18047 } 18048 var breakMe = false;
vendor: 182,392 bytes, lines 18049-22832
18049 var firstValue = null; 18050 var fieldsToTry = []; 18051 if (field.slice(-6) === "-short") { 18052 fieldsToTry.push(field); 18053 fieldsToTry.push(field.slice(0, -6)) 18054 } else { 18055 fieldsToTry.push(field); 18056 } 18057 18058 for (var h=0,hlen=fieldsToTry.length; h<hlen; h++) { 18059 var variantMatch = false; 18060 var field = fieldsToTry[h]; 18061 18062 ret = {name:"", usedOrig:stopOrig,locale:getFieldLocale(Item,field)}; 18063 18064 opts = state.opt[locale_type] ? state.opt[locale_type].slice() : []; 18065 var hasVal = false; 18066 18067 if (locale_type === 'locale-orig') { 18068 if (!stopOrig) { 18069 ret.name = Item[field]; 18070 ret.usedOrig = false; 18071 } 18072 hasVal = true; 18073 usingOrig = true; 18074 } else if (use_default && ("undefined" === typeof opts || opts.length === 0)) { 18075 // If we want the original, or if we don't have any specific guidance and we 18076 // definitely want output, just return the original value. 18077 ret.name = Item[field]; 18078 ret.usedOrig = true; 18079 hasVal = true; 18080 usingOrig = true; 18081 } 18082 18083 if (!hasVal) { 18084 for (var i = 0, ilen = opts.length; i < ilen; i += 1) { 18085 opt = opts[i]; 18086 o = opt.split(/[\-_]/)[0]; 18087 if (opt && Item.multi && Item.multi._keys[field] && Item.multi._keys[field][opt]) { 18088 ret.name = Item.multi._keys[field][opt]; 18089 ret.locale = opt; 18090 hasVal = true; 18091 variantMatch = true; 18092 usingOrig = false; 18093 break; 18094 } else if (o && Item.multi && Item.multi._keys[field] && Item.multi._keys[field][o]) { 18095 ret.name = Item.multi._keys[field][o]; 18096 ret.locale = o; 18097 hasVal = true; 18098 variantMatch = true; 18099 usingOrig = false; 18100 break; 18101 } 18102 } 18103 if (!ret.name && use_default) { 18104 ret = {name:Item[field], usedOrig:true, locale:getFieldLocale(Item,field)}; 18105 usingOrig = true; 18106 } 18107 } 18108 ret.token = CSL.Util.cloneToken(this); 18109 if (h === 0) { 18110 if (variantMatch) { 18111 ret.found_variant_ok = true; 18112 } 18113 firstValue = ret; 18114 if (!stickyLongForm && ("undefined" === typeof opts || opts.length === 0)) { 18115 breakMe = true; 18116 } 18117 if (variantMatch) { 18118 breakMe = true; 18119 } 18120 } else { 18121 if (!stickyLongForm && !variantMatch && firstValue) { 18122 ret = firstValue; 18123 field = fieldsToTry[0]; 18124 } else if (variantMatch) { 18125 ret.found_variant_ok = true; 18126 } 18127 } 18128 if (["title", "container-title"].indexOf(field) > -1) { 18129 if (!usedOrig 18130 && (!ret.token.strings["text-case"] 18131 || ret.token.strings["text-case"] === "sentence" 18132 || ret.token.strings["text-case"] === "normal")) { 18133 var locale = state.opt.lang; 18134 var lang; 18135 if (usingOrig) { 18136 lang = false; 18137 } else { 18138 lang = ret.locale; 18139 } 18140 var seg = field.slice(0,-5); 18141 var sentenceCase = ret.token.strings["text-case"] === "sentence" ? true : false; 18142 ret.name = CSL.titlecaseSentenceOrNormal(state, Item, seg, lang, sentenceCase); 18143 delete ret.token.strings["text-case"]; 18144 } 18145 } 18146 if (breakMe) { 18147 break; 18148 } 18149 } 18150 return ret; 18151 } 18152 this.getTextSubField = getTextSubField; 18153 18154 // Setter for abbreviation lists 18155 // This initializes a single abbreviation based on known 18156 // data. 18157 function loadAbbreviation(jurisdiction, category, orig, lang) { 18158 if (!jurisdiction) { 18159 jurisdiction = "default"; 18160 } 18161 var country = jurisdiction.split(":")[0]; 18162 var domain = CSL.getAbbrevsDomain(state, country, lang); 18163 if (domain) { 18164 jurisdiction += ("@" + domain); 18165 } 18166 if (!orig) { 18167 if (!state.transform.abbrevs[jurisdiction]) { 18168 state.transform.abbrevs[jurisdiction] = new state.sys.AbbreviationSegments(); 18169 } 18170 if (!state.transform.abbrevs[jurisdiction][category]) { 18171 state.transform.abbrevs[jurisdiction][category] = {}; 18172 } 18173 return jurisdiction; 18174 } 18175 // The getAbbreviation() function should check the 18176 // external DB for the content key. If a value exists 18177 // in this[category] and no value exists in DB, the entry 18178 // in memory is left untouched. If a value does exist in 18179 // DB, the memory value is created. 18180 // 18181 // See testrunner_stdrhino.js for an example. 18182 if (state.sys.getAbbreviation) { 18183 jurisdiction = state.sys.getAbbreviation(state.opt.styleID, state.transform.abbrevs, jurisdiction, category, orig); 18184 if (!jurisdiction) { 18185 jurisdiction = "default"; 18186 if (domain) { 18187 jurisdiction += ("@" + domain); 18188 } 18189 } 18190 } 18191 return jurisdiction; 18192 } 18193 this.loadAbbreviation = loadAbbreviation; 18194 18195 function publisherCheck (tok, Item, primary, family_var) { 18196 var varname = tok.variables[0]; 18197 if (state.publisherOutput && primary) { 18198 if (["publisher","publisher-place"].indexOf(varname) === -1) { 18199 return false; 18200 } else { 18201 // In this case, the publisher bundle will be rendered 18202 // at the close of the group, by the closing group node. 18203 state.publisherOutput[varname + "-token"] = tok; 18204 state.publisherOutput.varlist.push(varname); 18205 var lst = primary.split(/;\s*/); 18206 if (lst.length === state.publisherOutput[varname + "-list"].length) { 18207 state.publisherOutput[varname + "-list"] = lst; 18208 } 18209 // Abbreviate each of the items in the list here! 18210 for (var i = 0, ilen = lst.length; i < ilen; i += 1) { 18211 lst[i] = abbreviate(state, tok, Item, false, lst[i], family_var, true); 18212 } 18213 state.tmp[varname + "-token"] = tok; 18214 return true; 18215 } 18216 } 18217 return false; 18218 } 18219 18220 function citeFormCheck(Item, value) { 18221 var m = value.match(/^#([0-9]+).*>>>/); 18222 if (m && m[1]) { 18223 Item["cite-form"] = m[1]; 18224 } 18225 } 18226 18227 // The name transform code is placed here to keep similar things 18228 // in one place. Obviously this module could do with a little 18229 // tidying up. 18230 function quashCheck(jurisdiction, value) { 18231 var m = value.match(/^(?:#[0-9]+)*(?:!((?:[-_a-z]+(?:(?:.*)))(?:,(?:[-_a-z]+(?:(?:.*))))*))*>>>/); 18232 if (m) { 18233 value = value.slice(m[0].length); 18234 if (m[1]) { 18235 var fields = m[1].split(","); 18236 for (var i = 0, ilen = fields.length; i < ilen; i += 1) { 18237 var rawField = fields[i]; 18238 var mm = rawField.match(/^([-_a-z]+)(?:\:(.*))*$/); 18239 var field = mm[1]; 18240 // trimmer is not available in getAmbiguousCite 18241 var trimmer = state.tmp.abbrev_trimmer; 18242 if (mm[2]) { 18243 if (trimmer && jurisdiction) { 18244 if (!trimmer[jurisdiction]) { 18245 trimmer[jurisdiction] = {}; 18246 } 18247 trimmer[jurisdiction][field] = mm[2]; 18248 } 18249 } else if (state.tmp.done_vars.indexOf(field) === -1) { 18250 if (trimmer && jurisdiction) { 18251 if (!trimmer.QUASHES[jurisdiction]) { 18252 trimmer.QUASHES[jurisdiction] = {}; 18253 } 18254 trimmer.QUASHES[jurisdiction][field] = true; 18255 } 18256 state.tmp.done_vars.push(field); 18257 } 18258 } 18259 } 18260 } 18261 return value; 18262 } 18263 this.quashCheck = quashCheck; 18264 18265 // Return function appropriate to selected options 18266 function getOutputFunction(variables, family_var, abbreviation_fallback, alternative_varname) { 18267 // var mytoken; 18268 18269 // Set the primary_locale and secondary_locale lists appropriately. 18270 // No instance helper function for this; everything can be derived 18271 // from processor settings and rendering context. 18272 18273 var localesets; 18274 var langPrefs = CSL.LangPrefsMap[variables[0]]; 18275 if (!langPrefs) { 18276 localesets = false; 18277 } else { 18278 localesets = state.opt['cite-lang-prefs'][langPrefs]; 18279 } 18280 18281 return function (state, Item, item) { 18282 var primary, primary_locale, secondary, secondary_locale, tertiary, tertiary_locale, primary_tok; 18283 if (!variables[0] || (!Item[variables[0]] && !Item[alternative_varname])) { 18284 return null; 18285 } 18286 // 18287 // Exploring the edges here. 18288 // "suppress-author" for string variables (mostly titles). 18289 // 18290 if (!state.tmp.just_looking && item && item["suppress-author"]) { 18291 if (!state.tmp.probably_rendered_something && state.tmp.can_substitute.length() > 1) { 18292 return null; 18293 } 18294 } 18295 var slot = {primary:false, secondary:false, tertiary:false}; 18296 if (state.tmp.area.slice(-5) === "_sort") { 18297 slot.primary = 'locale-sort'; 18298 } else { 18299 if (localesets && localesets.length === 1 && localesets[0] === "locale-orig") { 18300 slot.primary = "locale-orig"; 18301 localesets = false; 18302 } else if (localesets && !state.tmp.multi_layout) { 18303 var slotnames = ["primary", "secondary", "tertiary"]; 18304 for (var i = 0, ilen = slotnames.length; i < ilen; i += 1) { 18305 if (localesets.length - 1 < i) { 18306 break; 18307 } 18308 if (localesets[i]) { 18309 slot[slotnames[i]] = 'locale-' + localesets[i]; 18310 } 18311 } 18312 } else { 18313 slot.primary = 'locale-orig'; 18314 } 18315 } 18316 18317 if (variables[0] === "title-short" 18318 || (state.tmp.area !== "bibliography" 18319 && !(state.tmp.area === "citation" 18320 && state.opt.xclass === "note" 18321 && item && !item.position))) { 18322 18323 slot.secondary = false; 18324 slot.tertiary = false; 18325 } 18326 18327 if (state.tmp.multi_layout) { 18328 slot.secondary = false; 18329 slot.tertiary = false; 18330 } 18331 18332 // Problem for multilingual: we really should be 18333 // checking for sanity on the basis of the output 18334 // strings to be actually used. (also below) 18335 if (state.tmp["publisher-list"]) { 18336 if (variables[0] === "publisher") { 18337 state.tmp["publisher-token"] = this; 18338 } else if (variables[0] === "publisher-place") { 18339 state.tmp["publisher-place-token"] = this; 18340 } 18341 return null; 18342 } 18343 18344 // tmp.lang_array carries the current locale IDs of the style 18345 // and the item. Field-level locale IDs are added here, so 18346 // we clone it to allow reset. 18347 var oldLangArray = state.tmp.lang_array.slice(); 18348 18349 // True is for transform fallback 18350 var res = getTextSubField.call(this, Item, variables[0], slot.primary, true, null, family_var); 18351 primary = res.name; 18352 primary_locale = res.locale; 18353 var primary_tok = res.token; 18354 var primaryUsedOrig = res.usedOrig; 18355 if (family_var && !res.found_variant_ok) { 18356 primary = abbreviate(state, primary_tok, Item, alternative_varname, primary, family_var, true); 18357 // Suppress subsequent use of another variable if requested by 18358 // hack syntax in this abbreviation short form. 18359 if (primary) { 18360 // We run quash-check in getAmbiguousCite, to possibly pick up a cite-form value. 18361 citeFormCheck(Item, primary); 18362 if (!state.tmp.just_looking) { 18363 primary = quashCheck(Item.jurisdiction, primary); 18364 } 18365 } 18366 } 18367 if (publisherCheck(this, Item, primary, family_var)) { 18368 state.tmp.lang_array = oldLangArray; 18369 return null; 18370 } 18371 18372 // No fallback for secondary and tertiary 18373 secondary = false; 18374 tertiary = false; 18375 var secondary_tok; 18376 var tertiary_tok; 18377 if (slot.secondary) { 18378 res = getTextSubField.call(this, Item, variables[0], slot.secondary, false, res.usedOrig, null, family_var); 18379 secondary = res.name; 18380 secondary_locale = res.locale; 18381 secondary_tok = res.token; 18382 if (family_var && !res.found_variant_ok) { 18383 if (secondary) { 18384 // The abbreviate() function could use a cleanup, after Zotero correct to use title-short 18385 secondary = abbreviate(state, secondary_tok, Item, false, secondary, family_var, true); 18386 } 18387 } 18388 //print("XXX secondary_locale: "+secondary_locale); 18389 } 18390 if (slot.tertiary) { 18391 res = getTextSubField.call(this, Item, variables[0], slot.tertiary, false, res.usedOrig, null, family_var); 18392 tertiary = res.name; 18393 tertiary_locale = res.locale; 18394 tertiary_tok = res.token; 18395 if (family_var && !res.found_variant_ok) { 18396 if (tertiary) { 18397 // The abbreviate() function could use a cleanup, after Zotero correct to use title-short 18398 tertiary = abbreviate(state, tertiary_tok, Item, false, tertiary, family_var, true); 18399 } 18400 } 18401 //print("XXX tertiary_locale: "+tertiary_locale); 18402 } 18403 18404 // Decoration of primary (currently translit only) goes here 18405 var primaryPrefix; 18406 if (slot.primary === "locale-translit") { 18407 primaryPrefix = state.opt.citeAffixes[langPrefs][slot.primary].prefix; 18408 } 18409 // XXX This should probably protect against italics at higher 18410 // levels. 18411 18412 if (primaryPrefix === "<i>" && variables[0] === 'title' && !primaryUsedOrig) { 18413 var hasItalic = false; 18414 for (var i = 0, ilen = primary_tok.decorations.length; i < ilen; i += 1) { 18415 if (primary_tok.decorations[i][0] === "@font-style" 18416 && primary_tok.decorations[i][1] === "italic") { 18417 18418 hasItalic = true; 18419 } 18420 } 18421 if (!hasItalic) { 18422 primary_tok.decorations.push(["@font-style", "italic"]); 18423 } 18424 } 18425 18426 //print("XXX "+primary_tok.strings["text-case"]); 18427 if (primary_locale !== "en" && primary_tok.strings["text-case"] === "title") { 18428 primary_tok.strings["text-case"] = "passthrough"; 18429 } 18430 18431 if ("title" === variables[0]) { 18432 primary = CSL.demoteNoiseWords(state, primary, this["leading-noise-words"]); 18433 } 18434 if (secondary || tertiary) { 18435 18436 state.output.openLevel("empty"); 18437 18438 // A little too aggressive maybe. 18439 primary_tok.strings.suffix = primary_tok.strings.suffix.replace(/[ .,]+$/,""); 18440 if (primary_locale) { 18441 state.tmp.lang_array = [primary_locale].concat(oldLangArray); 18442 } 18443 CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, null, null, primary_tok, primary_tok.strings.prefix + primary); 18444 state.output.append(primary, primary_tok); 18445 state.tmp.probably_rendered_something = true; 18446 18447 if (primary === secondary) { 18448 secondary = false; 18449 } 18450 if (secondary) { 18451 secondary_tok.strings.prefix = state.opt.citeAffixes[langPrefs][slot.secondary].prefix; 18452 secondary_tok.strings.suffix = state.opt.citeAffixes[langPrefs][slot.secondary].suffix; 18453 // Add a space if empty 18454 if (!secondary_tok.strings.prefix) { 18455 secondary_tok.strings.prefix = " "; 18456 } 18457 // Remove quotes 18458 for (var i = secondary_tok.decorations.length - 1; i > -1; i += -1) { 18459 if (['@quotes/true', '@font-style/italic', '@font-style/oblique', '@font-weight/bold'].indexOf(secondary_tok.decorations[i].join('/')) > -1) { 18460 secondary_tok.decorations = secondary_tok.decorations.slice(0, i).concat(secondary_tok.decorations.slice(i + 1)); 18461 } 18462 } 18463 if (secondary_locale !== "en" && secondary_tok.strings["text-case"] === "title") { 18464 secondary_tok.strings["text-case"] = "passthrough"; 18465 } 18466 if (secondary_locale) { 18467 state.tmp.lang_array = [secondary_locale].concat(oldLangArray); 18468 } 18469 var secondary_outer = new CSL.Token(); 18470 secondary_outer.decorations.push(["@font-style", "normal"]); 18471 secondary_outer.decorations.push(["@font-weight", "normal"]); 18472 state.output.openLevel(secondary_outer); 18473 state.output.append(secondary, secondary_tok); 18474 state.output.closeLevel(); 18475 18476 var blob_obj = state.output.current.value(); 18477 var blobs_pos = state.output.current.value().blobs.length - 1; 18478 // Suppress supplementary multilingual info on subsequent 18479 // partners of a parallel cite? 18480 } 18481 if (primary === tertiary) { 18482 tertiary = false; 18483 } 18484 18485 if (tertiary) { 18486 tertiary_tok.strings.prefix = state.opt.citeAffixes[langPrefs][slot.tertiary].prefix; 18487 tertiary_tok.strings.suffix = state.opt.citeAffixes[langPrefs][slot.tertiary].suffix; 18488 // Add a space if empty 18489 if (!tertiary_tok.strings.prefix) { 18490 tertiary_tok.strings.prefix = " "; 18491 } 18492 // Remove quotes 18493 for (var i = tertiary_tok.decorations.length - 1; i > -1; i += -1) { 18494 if (['@quotes/true', '@font-style/italic', '@font-style/oblique', '@font-weight/bold'].indexOf(tertiary_tok.decorations[i].join('/')) > -1) { 18495 tertiary_tok.decorations = tertiary_tok.decorations.slice(0, i).concat(tertiary_tok.decorations.slice(i + 1)); 18496 } 18497 } 18498 if (tertiary_locale !== "en" && tertiary_tok.strings["text-case"] === "title") { 18499 tertiary_tok.strings["text-case"] = "passthrough"; 18500 } 18501 if (tertiary_locale) { 18502 state.tmp.lang_array = [tertiary_locale].concat(oldLangArray); 18503 } 18504 var tertiary_outer = new CSL.Token(); 18505 tertiary_outer.decorations.push(["@font-style", "normal"]); 18506 tertiary_outer.decorations.push(["@font-weight", "normal"]); 18507 state.output.openLevel(tertiary_outer); 18508 state.output.append(tertiary, tertiary_tok); 18509 state.output.closeLevel(); 18510 18511 var blob_obj = state.output.current.value(); 18512 var blobs_pos = state.output.current.value().blobs.length - 1; 18513 // Suppress supplementary multilingual info on subsequent 18514 // partners of a parallel cite? 18515 // See note above. 18516 } 18517 18518 state.output.closeLevel(); 18519 } else { 18520 if (primary_locale) { 18521 state.tmp.lang_array = [primary_locale].concat(oldLangArray); 18522 } 18523 CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, null, null, primary_tok, primary_tok.strings.prefix + primary); 18524 state.output.append(primary, primary_tok); 18525 state.tmp.probably_rendered_something = true; 18526 } 18527 18528 state.tmp.lang_array = oldLangArray; 18529 18530 if (state.tmp.can_block_substitute) { 18531 state.tmp.name_node.children.push(state.output.current.value()); 18532 } 18533 return null; 18534 }; 18535 } 18536 this.getOutputFunction = getOutputFunction; 18537}; 18538 18539/*global CSL: true */ 18540 18541/** 18542 * Style token. 18543 * <p>This class provides the tokens that define 18544 * the runtime version of the style. The tokens are 18545 * instantiated by {@link CSL.Core.Build}, but the token list 18546 * must be post-processed with 18547 * {@link CSL.Core.Configure} before it can be used to generate 18548 * citations.</p> 18549 * @param {String} name The node name represented by this token. 18550 * @param {Int} tokentype A flag indicating whether this token 18551 * marks the start of a node, the end of a node, or is a singleton. 18552 * @class 18553 */ 18554CSL.Token = function (name, tokentype, conditional) { 18555 /** 18556 * Name of the element. 18557 * <p>This corresponds to the element name of the 18558 * relevant tag in the CSL file. 18559 */ 18560 this.name = name; 18561 /** 18562 * Strings and other static content specific to the element. 18563 */ 18564 this.strings = {}; 18565 this.strings.delimiter = undefined; 18566 this.strings.prefix = ""; 18567 this.strings.suffix = ""; 18568 /** 18569 * Formatting parameters. 18570 * <p>This is a placeholder at instantiation. It is 18571 * replaced by the result of {@link CSL.setDecorations} 18572 * when the tag is created and configured during {@link CSL.Core.Build} 18573 * by {@link CSL.XmlToToken}. The parameters for particular 18574 * formatting attributes are stored as string arrays, which 18575 * map to formatting functions at runtime, 18576 * when the output format is known. Note that the order in which 18577 * parameters are registered is fixed by the constant 18578 * {@link CSL.FORMAT_KEY_SEQUENCE}. 18579 */ 18580 this.decorations = []; 18581 this.variables = []; 18582 /** 18583 * Element functions. 18584 * <p>Functions implementing the styling behaviour of the element 18585 * are pushed into this array in the {@link CSL.Core.Build} phase. 18586 */ 18587 this.execs = []; 18588 /** 18589 * Token type. 18590 * <p>This is a flag constant indicating whether the token represents 18591 * a start tag, an end tag, or is a singleton.</p> 18592 */ 18593 this.tokentype = tokentype; 18594 18595 // Conditional attributes added to bare tokens at runtime 18596 18597 /** 18598 * Condition evaluator. 18599 * <p>This is a placeholder that receives a single function, and is 18600 * only relevant for a conditional branching tag (<code>if</code> or 18601 * <code>else-if</code>). The function implements the argument to 18602 * the <code>match=</code> attribute (<code>any</code>, <code>all</code> 18603 * or <code>none</code>), by executing the functions registered in the 18604 * <code>tests</code> array (see below), and reacting accordingly. This 18605 * function is invoked by the execution wrappers found in 18606 * {@link CSL.Engine}.</p> 18607 */ 18608 // this.evaluator = false; 18609 /** 18610 * Conditions. 18611 * <p>Functions that evaluate to true or false, implementing 18612 * various posisble attributes to the conditional branching tags, 18613 * are registered here during {@link CSL.Core.Build}. 18614 * </p> 18615 */ 18616 // this.tests = []; 18617 /** 18618 * Jump point on success. 18619 * <p>This holds the list jump point to be used when the 18620 * <code>evaluator</code> function of a conditional tag 18621 * returns true (success). The jump index value is set during the 18622 * back-to-front token pass performed during {@link CSL.Core.Configure}. 18623 * </p> 18624 */ 18625 // this.succeed = false; 18626 /** 18627 * Jump point on failure. 18628 * <p>This holds the list jump point to be used when the 18629 * <code>evaluator</code> function of a conditional tag 18630 * returns false (failure). Jump index values are set during the 18631 * back-to-front token pass performed during {@link CSL.Core.Configure}. 18632 * </p> 18633 */ 18634 // this.fail = false; 18635 /** 18636 * Index of next token. 18637 * <p>This holds the index of the next token in the 18638 * token list, which is the default "jump-point" for ordinary 18639 * processing. Jump index values are set during the 18640 * back-to-front token pass performed during {@link CSL.Core.Configure}. 18641 * </p> 18642 */ 18643 // this.next = false; 18644}; 18645 18646// Have needed this for yonks 18647CSL.Util.cloneToken = function (token) { 18648 var newtok, key, pos, len; 18649 if ("string" === typeof token) { 18650 return token; 18651 } 18652 newtok = new CSL.Token(token.name, token.tokentype); 18653 for (var key in token.strings) { 18654 if (token.strings.hasOwnProperty(key)) { 18655 newtok.strings[key] = token.strings[key]; 18656 } 18657 } 18658 if (token.decorations) { 18659 newtok.decorations = []; 18660 for (pos = 0, len = token.decorations.length; pos < len; pos += 1) { 18661 newtok.decorations.push(token.decorations[pos].slice()); 18662 } 18663 } 18664 if (token.variables) { 18665 newtok.variables = token.variables.slice(); 18666 } 18667 // Probably overkill; this is only used for cloning formatting 18668 // tokens. 18669 if (token.execs) { 18670 newtok.execs = token.execs.slice(); 18671 if (token.tests) { 18672 newtok.tests = token.tests.slice(); 18673 } 18674 } 18675 return newtok; 18676}; 18677 18678/*global CSL: true */ 18679 18680/** 18681 * Ambiguous Cite Configuration Object 18682 * @class 18683 */ 18684CSL.AmbigConfig = function () { 18685 this.maxvals = []; 18686 this.minval = 1; 18687 this.names = []; 18688 this.givens = []; 18689 this.year_suffix = false; 18690 this.disambiguate = 0; 18691}; 18692 18693/*global CSL: true */ 18694 18695CSL.Blob = function (str, token, levelname) { 18696 var len, pos, key; 18697 this.levelname = levelname; 18698 //print(levelname); 18699 if (token) { 18700 this.strings = {"prefix":"","suffix":""}; 18701 for (var key in token.strings) { 18702 if (token.strings.hasOwnProperty(key)) { 18703 this.strings[key] = token.strings[key]; 18704 } 18705 } 18706 this.decorations = []; 18707 if (token.decorations === undefined) { 18708 len = 0; 18709 } else { 18710 len = token.decorations.length; 18711 } 18712 for (pos = 0; pos < len; pos += 1) { 18713 this.decorations.push(token.decorations[pos].slice()); 18714 } 18715 } else { 18716 this.strings = {}; 18717 this.strings.prefix = ""; 18718 this.strings.suffix = ""; 18719 this.strings.delimiter = ""; 18720 this.decorations = []; 18721 } 18722 if ("string" === typeof str) { 18723 this.blobs = str; 18724 } else if (str) { 18725 this.blobs = [str]; 18726 } else { 18727 this.blobs = []; 18728 } 18729 this.alldecor = [this.decorations]; 18730}; 18731 18732 18733CSL.Blob.prototype.push = function (blob) { 18734 if ("string" === typeof this.blobs) { 18735 CSL.error("Attempt to push blob onto string object"); 18736 } else if (false !== blob) { 18737 blob.alldecor = blob.alldecor.concat(this.alldecor); 18738 this.blobs.push(blob); 18739 } 18740}; 18741 18742/*global CSL: true */ 18743 18744/** 18745 * An output instance object representing a number or a range 18746 * 18747 * with attributes next and start, and 18748 * methods isRange(), renderStart(), renderEnd() and renderRange(). 18749 * At render time, the output queue will perform optional 18750 * collapsing of these objects in the queue, according to 18751 * configurable options, and apply any decorations registered 18752 * in the object to the output elements. 18753 * @namespace Range object and friends. 18754 */ 18755 18756CSL.NumericBlob = function (state, particle, num, mother_token, id) { 18757 // item id is used to assure that prefix delimiter is invoked only 18758 // when joining blobs across items 18759 this.id = id; 18760 this.alldecor = []; 18761 this.num = num; 18762 this.particle = particle; 18763 this.blobs = num.toString(); 18764 this.status = CSL.START; 18765 this.strings = {}; 18766 if (mother_token) { 18767 if (mother_token.strings["text-case"]) { 18768 var textCase = mother_token.strings["text-case"]; 18769 this.particle = CSL.Output.Formatters[textCase](state, this.particle); 18770 this.blobs = CSL.Output.Formatters[textCase](state, this.blobs); 18771 } 18772 this.gender = mother_token.gender; 18773 this.decorations = mother_token.decorations; 18774 this.strings.prefix = mother_token.strings.prefix; 18775 this.strings.suffix = mother_token.strings.suffix; 18776 this.strings["text-case"] = mother_token.strings["text-case"]; 18777 this.successor_prefix = mother_token.successor_prefix; 18778 this.range_prefix = mother_token.range_prefix; 18779 this.splice_prefix = mother_token.splice_prefix; 18780 this.formatter = mother_token.formatter; 18781 if (!this.formatter) { 18782 this.formatter = new CSL.Output.DefaultFormatter(); 18783 } 18784 if (this.formatter) { 18785 this.type = this.formatter.format(1); 18786 } 18787 } else { 18788 this.decorations = []; 18789 this.strings.prefix = ""; 18790 this.strings.suffix = ""; 18791 this.successor_prefix = ""; 18792 this.range_prefix = ""; 18793 this.splice_prefix = ""; 18794 this.formatter = new CSL.Output.DefaultFormatter(); 18795 } 18796}; 18797 18798 18799CSL.NumericBlob.prototype.setFormatter = function (formatter) { 18800 this.formatter = formatter; 18801 this.type = this.formatter.format(1); 18802}; 18803 18804 18805CSL.Output.DefaultFormatter = function () {}; 18806 18807CSL.Output.DefaultFormatter.prototype.format = function (num) { 18808 return num.toString(); 18809}; 18810 18811CSL.NumericBlob.prototype.checkNext = function (next,start) { 18812 if (start) { 18813 this.status = CSL.START; 18814 if ("object" === typeof next) { 18815 if (next.num === (this.num + 1)) { 18816 next.status = CSL.SUCCESSOR; 18817 } else { 18818 next.status = CSL.SEEN; 18819 } 18820 } 18821 } else if (! next || !next.num || this.type !== next.type || next.num !== (this.num + 1)) { 18822 if (this.status === CSL.SUCCESSOR_OF_SUCCESSOR) { 18823 this.status = CSL.END; 18824 } 18825 if ("object" === typeof next) { 18826 next.status = CSL.SEEN; 18827 } 18828 } else { // next number is in the sequence 18829 if (this.status === CSL.START || this.status === CSL.SEEN) { 18830 next.status = CSL.SUCCESSOR; 18831 } else if (this.status === CSL.SUCCESSOR || this.status === CSL.SUCCESSOR_OF_SUCCESSOR) { 18832 if (this.range_prefix) { 18833 next.status = CSL.SUCCESSOR_OF_SUCCESSOR; 18834 this.status = CSL.SUPPRESS; 18835 } else { 18836 next.status = CSL.SUCCESSOR; 18837 } 18838 } 18839 // wakes up the correct delimiter. 18840 //if (this.status === CSL.SEEN) { 18841 // this.status = CSL.SUCCESSOR; 18842 //} 18843 } 18844}; 18845 18846 18847CSL.NumericBlob.prototype.checkLast = function (last) { 18848 // Used to adjust final non-range join 18849 if (this.status === CSL.SEEN 18850 || (last.num !== (this.num - 1) && this.status === CSL.SUCCESSOR)) { 18851 this.status = CSL.SUCCESSOR; 18852 return true; 18853 } 18854 return false; 18855}; 18856 18857/*global CSL: true */ 18858 18859CSL.Util.fixDateNode = function (parent, pos, node) { 18860 var form, variable, datexml, subnode, partname, attr, val, prefix, suffix, children, subchildren, display, cslid; 18861 18862 var lingo = this.cslXml.getAttributeValue(node, "lingo"); 18863 18864 var default_locale = this.cslXml.getAttributeValue(node, "default-locale"); 18865 18866 // Raise date flag, used to control inclusion of year-suffix key in sorts 18867 // This may be a little reckless: not sure what happens on no-date conditions 18868 this.build.date_key = true; 18869 18870 form = this.cslXml.getAttributeValue(node, "form"); 18871 var lingo; 18872 if (default_locale) { 18873 lingo = this.opt["default-locale"][0]; 18874 } else { 18875 lingo = this.cslXml.getAttributeValue(node, "lingo"); 18876 } 18877 18878 if (!this.getDate(form, default_locale)) { 18879 return parent; 18880 } 18881 18882 var dateparts = this.cslXml.getAttributeValue(node, "date-parts"); 18883 18884 variable = this.cslXml.getAttributeValue(node, "variable"); 18885 prefix = this.cslXml.getAttributeValue(node, "prefix"); 18886 suffix = this.cslXml.getAttributeValue(node, "suffix"); 18887 display = this.cslXml.getAttributeValue(node, "display"); 18888 cslid = this.cslXml.getAttributeValue(node, "cslid"); 18889 18890 // 18891 // Xml: Copy a node 18892 // 18893 datexml = this.cslXml.nodeCopy(this.getDate(form, default_locale)); 18894 this.cslXml.setAttribute(datexml, 'lingo', this.opt.lang); 18895 this.cslXml.setAttribute(datexml, 'form', form); 18896 this.cslXml.setAttribute(datexml, 'date-parts', dateparts); 18897 this.cslXml.setAttribute(datexml, "cslid", cslid); 18898 // 18899 // Xml: Set attribute 18900 // 18901 this.cslXml.setAttribute(datexml, 'variable', variable); 18902 this.cslXml.setAttribute(datexml, 'default-locale', default_locale); 18903 // 18904 // Xml: Set flag 18905 // 18906 if (prefix) { 18907 // 18908 // Xml: Set attribute 18909 // 18910 this.cslXml.setAttribute(datexml, "prefix", prefix); 18911 } 18912 if (suffix) { 18913 // 18914 // Xml: Set attribute 18915 // 18916 this.cslXml.setAttribute(datexml, "suffix", suffix); 18917 } 18918 if (display) { 18919 // 18920 // Xml: Set attribute 18921 // 18922 this.cslXml.setAttribute(datexml, "display", display); 18923 } 18924 // 18925 // Step through any date-part children of the layout date node, 18926 // and lay their attributes onto the corresponding node in the 18927 // locale template node copy. 18928 // 18929 // tests: language_BaseLocale 18930 // tests: date_LocalizedTextInStyleLocaleWithTextCase 18931 // 18932 children = this.cslXml.children(datexml); 18933 for (var key in children) { 18934 subnode = children[key]; 18935 if ("date-part" === this.cslXml.nodename(subnode)) { 18936 partname = this.cslXml.getAttributeValue(subnode, "name"); 18937 if (default_locale) { 18938 this.cslXml.setAttributeOnNodeIdentifiedByNameAttribute(datexml, "date-part", partname, "@default-locale", "true"); 18939 } 18940 } 18941 } 18942 18943 children = this.cslXml.children(node); 18944 for (var key in children) { 18945 subnode = children[key]; 18946 if ("date-part" === this.cslXml.nodename(subnode)) { 18947 partname = this.cslXml.getAttributeValue(subnode, "name"); 18948 subchildren = this.cslXml.attributes(subnode); 18949 for (attr in subchildren) { 18950 if ("@name" === attr) { 18951 continue; 18952 } 18953 if (lingo && lingo !== this.opt.lang) { 18954 if (["@suffix", "@prefix", "@form"].indexOf(attr) > -1) { 18955 continue; 18956 } 18957 } 18958 val = subchildren[attr]; 18959 this.cslXml.setAttributeOnNodeIdentifiedByNameAttribute(datexml, "date-part", partname, attr, val); 18960 } 18961 } 18962 } 18963 18964 if ("year" === this.cslXml.getAttributeValue(node, "date-parts")) { 18965 18966 // 18967 // Xml: Find one node by attribute and delete 18968 // 18969 this.cslXml.deleteNodeByNameAttribute(datexml, 'month'); 18970 // 18971 // Xml: Find one node by attribute and delete 18972 // 18973 this.cslXml.deleteNodeByNameAttribute(datexml, 'day'); 18974 18975 } else if ("year-month" === this.cslXml.getAttributeValue(node, "date-parts")) { 18976 // 18977 // Xml: Find one node by attribute and delete 18978 // 18979 this.cslXml.deleteNodeByNameAttribute(datexml, 'day'); 18980 } else if ("month-day" === this.cslXml.getAttributeValue(node, "date-parts")) { 18981 // 18982 // Xml: Get child nodes 18983 // 18984 var childNodes = this.cslXml.children(datexml); 18985 for (var i=1,ilen=this.cslXml.numberofnodes(childNodes);i<ilen;i++) { 18986 // 18987 // Xml: Get attribute value (for string comparison) 18988 // 18989 if (this.cslXml.getAttributeValue(childNodes[i], 'name') === "year") { 18990 // 18991 // Xml: Set attribute value 18992 // 18993 this.cslXml.setAttribute(childNodes[i-1], "suffix", ""); 18994 break; 18995 } 18996 } 18997 // 18998 // Xml: Find one node by attribute and delete 18999 // 19000 this.cslXml.deleteNodeByNameAttribute(datexml, 'year'); 19001 } 19002 return this.cslXml.insertChildNodeAfter(parent, node, pos, datexml); 19003}; 19004 19005/*global CSL: true */ 19006 19007CSL.dateMacroAsSortKey = function (state, Item) { 19008 CSL.dateAsSortKey.call(this, state, Item, true); 19009}; 19010 19011 19012CSL.dateAsSortKey = function (state, Item, isMacro) { 19013 var dp, elem, value, e, yr, prefix, i, ilen; 19014 var variable = this.variables[0]; 19015 var macroFlag = "empty"; 19016 if (isMacro && state.tmp.extension) { 19017 macroFlag = "macro-with-date"; 19018 } 19019 dp = Item[variable]; 19020 if ("undefined" === typeof dp) { 19021 dp = {"date-parts": [[0]] }; 19022 } 19023 if ("undefined" === typeof this.dateparts) { 19024 this.dateparts = ["year", "month", "day"]; 19025 } 19026 if (dp.raw) { 19027 dp = state.fun.dateparser.parseDateToArray(dp.raw); 19028 } else if (dp["date-parts"]) { 19029 dp = state.dateParseArray(dp); 19030 } 19031 if ("undefined" === typeof dp) { 19032 dp = {}; 19033 } 19034 if (dp.year) { 19035 for (i = 0, ilen = CSL.DATE_PARTS_INTERNAL.length; i < ilen; i += 1) { 19036 elem = CSL.DATE_PARTS_INTERNAL[i]; 19037 value = 0; 19038 e = elem; 19039 if (e.slice(-4) === "_end") { 19040 e = e.slice(0, -4); 19041 } 19042 if (dp[elem] && this.dateparts.indexOf(e) > -1) { 19043 value = dp[elem]; 19044 } 19045 if (elem.slice(0, 4) === "year") { 19046 yr = CSL.Util.Dates[e].numeric(state, value); 19047 var prefix = "1"; 19048 if (yr[0] === "-") { 19049 prefix = "0"; 19050 yr = yr.slice(1); 19051 yr = 9999 - parseInt(yr, 10); 19052 } 19053 state.output.append(CSL.Util.Dates[elem.slice(0, 4)].numeric(state, (prefix + yr)), macroFlag); 19054 } else { 19055 value = CSL.Util.Dates[e]["numeric-leading-zeros"](state, value); 19056 // Ugh. 19057 if (!value) { 19058 value = "00"; 19059 } 19060 state.output.append(value, macroFlag); 19061 } 19062 } 19063 } 19064}; 19065 19066CSL.Engine.prototype.dateParseArray = function (date_obj) { 19067 var ret, field, dp, exts; 19068 ret = {}; 19069 for (field in date_obj) { 19070 if (field === "date-parts") { 19071 dp = date_obj["date-parts"]; 19072 if (dp.length > 1) { 19073 if (dp[0].length !== dp[1].length) { 19074 CSL.error("CSL data error: element mismatch in date range input."); 19075 } 19076 } 19077 exts = ["", "_end"]; 19078 for (var i = 0, ilen = dp.length; i < ilen; i += 1) { 19079 for (var j = 0, jlen = CSL.DATE_PARTS.length; j < jlen; j += 1) { 19080 if (isNaN(parseInt(dp[i][j], 10))) { 19081 ret[(CSL.DATE_PARTS[j] + exts[i])] = undefined; 19082 } else { 19083 ret[(CSL.DATE_PARTS[j] + exts[i])] = parseInt(dp[i][j], 10); 19084 } 19085 } 19086 } 19087 } else if (date_obj.hasOwnProperty(field)) { 19088 19089 // XXXX: temporary workaround 19090 19091 if (field === "literal" && "object" === typeof date_obj.literal && "string" === typeof date_obj.literal.part) { 19092 CSL.debug("Warning: fixing up weird literal date value"); 19093 ret.literal = date_obj.literal.part; 19094 } else { 19095 ret[field] = date_obj[field]; 19096 } 19097 } 19098 } 19099 return ret; 19100}; 19101 19102/*global CSL: true */ 19103 19104CSL.Util.Names = {}; 19105 19106CSL.Util.Names.compareNamesets = CSL.NameOutput.prototype._compareNamesets; 19107 19108/** 19109 * Un-initialize a name (quash caps after first character) 19110 */ 19111CSL.Util.Names.unInitialize = function (state, name) { 19112 var i, ilen, namelist, punctlist, ret; 19113 if (!name) { 19114 return ""; 19115 } 19116 namelist = name.split(/(?:\-|\s+)/); 19117 punctlist = name.match(/(\-|\s+)/g); 19118 ret = ""; 19119 for (i = 0, ilen = namelist.length; i < ilen; i += 1) { 19120 // if (CSL.ALL_ROMANESQUE_REGEXP.exec(namelist[i].slice(0,-1)) 19121 // && namelist[i] 19122 // && namelist[i] !== namelist[i].toUpperCase()) { 19123 19124 // More or less like this, to address the following fault report: 19125 // http://forums.zotero.org/discussion/17610/apsa-problems-with-capitalization-of-mc-mac-etc/ 19126 19127 // Leaving the name string untouched because name capitalization is varied and wonderful. 19128 // https://github.com/Juris-M/citeproc-js/issues/43 19129 19130 //namelist[i] = namelist[i].slice(0, 1) + namelist[i].slice(1, 2).toLowerCase() + namelist[i].slice(2); 19131 // } 19132 ret += namelist[i]; 19133 if (i < ilen - 1) { 19134 ret += punctlist[i]; 19135 } 19136 } 19137 return ret; 19138}; 19139 19140/** 19141 * Initialize a name. 19142 */ 19143CSL.Util.Names.initializeWith = function (state, name, terminator, normalizeOnly) { 19144 var i, ilen, mm, lst, ret; 19145 if (!name) { 19146 return ""; 19147 } 19148 if (!terminator) { 19149 terminator = ""; 19150 } 19151 if (["Lord", "Lady"].indexOf(name) > -1 19152 || (!name.replace(/^(?:<[^>]+>)*/, "").match(CSL.STARTSWITH_ROMANESQUE_REGEXP) 19153 && !terminator.match("%s"))) { 19154 return name; 19155 } 19156 19157 if (state.opt["initialize-with-hyphen"] === false) { 19158 name = name.replace(/\-/g, " "); 19159 } 19160 19161 // We need to suss out what is a set of initials or abbreviation, 19162 // so that they can be selectively normalized. Steps might be: 19163 // (1) Split the string 19164 // (2) Step through the string, deleting periods and, if initalize="false", then 19165 // (a) note abbreviations and initials (separately). 19166 // (3) If initialize="false" then: 19167 // (a) Do the thing below, but only pushing terminator; or else 19168 // (b) Do the thing below 19169 19170 name = name.replace(/\s*\-\s*/g, "-").replace(/\s+/g, " "); 19171 name = name.replace(/-([a-z])/g, "\u2013$1"); 19172 19173 for (var i=name.length-2; i>-1; i += -1) { 19174 if (name.slice(i, i+1) === "." && name.slice(i+1, i+2) !== " ") { 19175 name = name.slice(0, i) + ". " + name.slice(i+1); 19176 } 19177 } 19178 19179 // (1) Split the string 19180 var nameSplits = CSL.Output.Formatters.nameDoppel.split(name); 19181 var namelist = []; 19182 namelist = [nameSplits.strings[0]]; 19183 19184 if (nameSplits.tags.length === 0) { 19185 var mmm = namelist[0].match(/[^\.]+$/); 19186 if (mmm && mmm[0].length === 1 && mmm[0] !== mmm[0].toLowerCase()) { 19187 namelist[0] += "."; 19188 } 19189 } 19190 19191 for (i = 1, ilen = nameSplits.strings.length; i < ilen; i += 1) { 19192 namelist.push(nameSplits.tags[i - 1]); 19193 namelist.push(nameSplits.strings[i]); 19194 } 19195 19196 // Use doInitializeName or doNormalizeName, depending on requirements. 19197 if (normalizeOnly) { 19198 ret = this.doNormalize(state, namelist, terminator); 19199 } else { 19200 ret = this.doInitialize(state, namelist, terminator); 19201 } 19202 ret = ret.replace(/\u2013([a-z])/g, "-$1"); 19203 return ret; 19204}; 19205 19206CSL.Util.Names.notag = function(str) { 19207 return str.replace(/^(?:<[^>]+>)*/, ""); 19208}; 19209 19210CSL.Util.Names.mergetag = function(state, tagstr, newstr) { 19211 var m = tagstr.match(/(?:-*<[^>]+>-*)/g); 19212 if (!m) { 19213 return newstr; 19214 } else { 19215 tagstr = m.join(""); 19216 } 19217 m = newstr.match(/^(.*[^\s])*(\s+)$/); 19218 if (m) { 19219 m[1] = m[1] ? m[1] : ""; 19220 newstr = m[1] + tagstr + m[2]; 19221 } else { 19222 newstr = newstr + tagstr; 19223 } 19224 return newstr; 19225}; 19226 19227CSL.Util.Names.tagonly = function(state, str) { 19228 var m = str.match(/(?:<[^>]+>)+/); 19229 if (!m) { 19230 return str; 19231 } else { 19232 return m.join(""); 19233 } 19234}; 19235 19236CSL.Util.Names.doNormalize = function (state, namelist, terminator) { 19237 var i, ilen; 19238 // namelist is a flat list of given-name elements and space-like separators between them 19239 terminator = terminator ? terminator : ""; 19240 // Flag elements that look like abbreviations 19241 var isAbbrev = []; 19242 for (i = 0, ilen = namelist.length; i < ilen; i += 1) { 19243 if (this.notag(namelist[i]).length > 1 && this.notag(namelist[i]).slice(-1) === ".") { 19244 // namelist[i] = namelist[i].slice(0, -1); 19245 namelist[i] = namelist[i].replace(/^(.*)\.(.*)$/, "$1$2"); 19246 isAbbrev.push(true); 19247 } else if (namelist[i].length === 1 && namelist[i].toUpperCase() === namelist[i]) { 19248 isAbbrev.push(true); 19249 } else { 19250 isAbbrev.push(false); 19251 } 19252 } 19253 // Step through the elements of the givenname array 19254 for (i = 0, ilen = namelist.length; i < ilen; i += 2) { 19255 // If the element is not an abbreviation, leave it and its trailing spaces alone 19256 if (isAbbrev[i]) { 19257 // For all elements but the last 19258 if (i < namelist.length - 2) { 19259 // Start from scratch on space-like things following an abbreviation 19260 namelist[i + 1] = this.tagonly(state, namelist[i+1]); 19261 if (!isAbbrev[i+2]) { 19262 namelist[i + 1] = this.tagonly(state, namelist[i+1]) + " "; 19263 } 19264 19265 // Add the terminator to the element 19266 // If the following element is not a single-character abbreviation, remove a trailing zero-width non-break space, if present 19267 // These ops may leave some duplicate cruft in the elements and separators. This will be cleaned at the end of the function. 19268 if (namelist[i + 2].length > 1) { 19269 namelist[i+1] = terminator.replace(/\ufeff$/, "") + namelist[i+1]; 19270 } else { 19271 namelist[i+1] = this.mergetag(state, namelist[i+1], terminator); 19272 } 19273 } 19274 // For the last element (if it is an abbreviation), just append the terminator 19275 if (i === namelist.length - 1) { 19276 namelist[i] = namelist[i] + terminator; 19277 } 19278 } 19279 } 19280 // Remove trailing cruft and duplicate spaces, and return 19281 return namelist.join("").replace(/[\u0009\u000a\u000b\u000c\u000d\u0020\ufeff\u00a0]+$/,"").replace(/\s*\-\s*/g, "-").replace(/[\u0009\u000a\u000b\u000c\u000d\u0020]+/g, " "); 19282}; 19283 19284CSL.Util.Names.doInitialize = function (state, namelist, terminator) { 19285 var i, ilen, m, j, jlen, lst, n; 19286 for (i = 0, ilen = namelist.length; i < ilen; i += 2) { 19287 n = namelist[i]; 19288 if (!n) { 19289 continue; 19290 } 19291 m = n.match(CSL.NAME_INITIAL_REGEXP); 19292 if (!m && (!n.match(CSL.STARTSWITH_ROMANESQUE_REGEXP) && n.length > 1 && terminator.match("%s"))) { 19293 m = n.match(/(.)(.*)/); 19294 } 19295 if (m && m[2] && m[3]) { 19296 m[1] = m[1] + m[2]; 19297 m[2] = ""; 19298 } 19299 if (m && m[1].slice(0, 1) === m[1].slice(0, 1).toUpperCase()) { 19300 var extra = ""; 19301 if (m[2]) { 19302 var s = ""; 19303 lst = m[2].split(""); 19304 for (j = 0, jlen = lst.length; j < jlen; j += 1) { 19305 var c = lst[j]; 19306 if (c === c.toUpperCase()) { 19307 s += c; 19308 } else { 19309 break; 19310 } 19311 } 19312 if (s.length < m[2].length) { 19313 extra = CSL.toLocaleLowerCase.call(state, s); 19314 } 19315 } 19316 namelist[i] = m[1] + extra; 19317 if (i < (ilen - 1)) { 19318 if (terminator.match("%s")) { 19319 namelist[i] = terminator.replace("%s", namelist[i]); 19320 } else { 19321 if (namelist[i + 1].indexOf("-") > -1) { 19322 namelist[i + 1] = this.mergetag(state, namelist[i+1].replace("-", ""), terminator) + "-"; 19323 } else { 19324 namelist[i + 1] = this.mergetag(state, namelist[i+1], terminator); 19325 } 19326 } 19327 } else { 19328 if (terminator.match("%s")) { 19329 namelist[i] = terminator.replace("%s", namelist[i]); 19330 } else { 19331 namelist.push(terminator); 19332 } 19333 } 19334 } else if (n.match(CSL.ROMANESQUE_REGEXP) && (!m || !m[3])) { 19335 namelist[i] = " " + n; 19336 } 19337 } 19338 var ret = namelist.join(""); 19339 ret = ret.replace(/[\u0009\u000a\u000b\u000c\u000d\u0020\ufeff\u00a0]+$/,"").replace(/\s*\-\s*/g, "-").replace(/[\u0009\u000a\u000b\u000c\u000d\u0020]+/g, " "); 19340 return ret; 19341}; 19342 19343CSL.Util.Names.getRawName = function (name) { 19344 var ret = []; 19345 if (name.literal) { 19346 ret.push(name.literal); 19347 } else { 19348 if (name.given) { 19349 ret.push(name.given); 19350 } 19351 if (name.family) { 19352 ret.push(name.family); 19353 } 19354 } 19355 return ret.join(" "); 19356}; 19357 19358/*global CSL: true */ 19359 19360/** 19361 * Date mangling functions. 19362 * @namespace Date construction utilities 19363 */ 19364CSL.Util.Dates = {}; 19365 19366/** 19367 * Year manglers 19368 * <p>short, long</p> 19369 */ 19370CSL.Util.Dates.year = {}; 19371 19372/** 19373 * Convert year to long form 19374 * <p>This just passes the number back as a string.</p> 19375 */ 19376CSL.Util.Dates.year["long"] = function (state, num) { 19377 if (!num) { 19378 if ("boolean" === typeof num) { 19379 num = ""; 19380 } else { 19381 num = 0; 19382 } 19383 } 19384 return num.toString(); 19385}; 19386 19387/** 19388 * Crudely convert to Japanese Imperial form. 19389 * <p>Returns the result as a string.</p> 19390 */ 19391CSL.Util.Dates.year.imperial = function (state, num, end) { 19392 var year = ""; 19393 if (!num) { 19394 if ("boolean" === typeof num) { 19395 num = ""; 19396 } else { 19397 num = 0; 19398 } 19399 } 19400 end = end ? "_end" : ""; 19401 var month = state.tmp.date_object["month" + end]; 19402 month = month ? ""+month : "1"; 19403 while (month.length < 2) { 19404 month = "0" + month; 19405 } 19406 var day = state.tmp.date_object["day" + end]; 19407 day = day ? ""+day : "1"; 19408 while (day.length < 2) { 19409 day = "0" + day; 19410 } 19411 var date = parseInt(num + month + day, 10); 19412 var label; 19413 var offset; 19414 if (date >= 18680908 && date < 19120730) { 19415 label = '\u660e\u6cbb'; 19416 offset = 1867; 19417 } else if (date >= 19120730 && date < 19261225) { 19418 label = '\u5927\u6b63'; 19419 offset = 1911; 19420 } else if (date >= 19261225 && date < 19890108) { 19421 label = '\u662d\u548c'; 19422 offset = 1925; 19423 } else if (date >= 19890108) { 19424 label = '\u5e73\u6210'; 19425 offset = 1988; 19426 } 19427 // This entire "imperial" code block should be cut. Scraped input 19428 // for this will be too ratty to be useful anyway. 19429 if (label && offset) { 19430 var normalizedKey = label; 19431 if (state.sys.normalizeAbbrevsKey) { 19432 // The first argument does not need to specify the exact variable 19433 // name. 19434 normalizedKey = state.sys.normalizeAbbrevsKey("number", label); 19435 } 19436 if (!state.transform.abbrevs['default']['number'][normalizedKey]) { 19437 // loadAbbreviation normally takes an item as fourth argument. 19438 // It is not available here, 19439 state.transform.loadAbbreviation('default', "number", normalizedKey, null); 19440 } 19441 if (state.transform.abbrevs['default']['number'][normalizedKey]) { 19442 label = state.transform.abbrevs['default']['number'][normalizedKey]; 19443 } 19444 year = label + (num - offset); 19445 } 19446 return year; 19447}; 19448 19449/** 19450 * Convert year to short form 19451 * <p>Just crops any 4-digit year to the last two digits.</p> 19452 */ 19453CSL.Util.Dates.year["short"] = function (state, num) { 19454 num = num.toString(); 19455 if (num && num.length === 4) { 19456 return num.substr(2); 19457 } 19458}; 19459 19460 19461/** 19462 * Convert year to short form 19463 * <p>Just crops any 4-digit year to the last two digits.</p> 19464 */ 19465CSL.Util.Dates.year.numeric = function (state, num) { 19466 var m, pre; 19467 num = "" + num; 19468 var m = num.match(/([0-9]*)$/); 19469 if (m) { 19470 pre = num.slice(0, m[1].length * -1); 19471 num = m[1]; 19472 } else { 19473 pre = num; 19474 num = ""; 19475 } 19476 while (num.length < 4) { 19477 num = "0" + num; 19478 } 19479 return (pre + num); 19480}; 19481 19482 19483/* 19484 * MONTH manglers 19485 * normalize 19486 * long, short, numeric, numeric-leading-zeros 19487 */ 19488CSL.Util.Dates.normalizeMonth = function (num, useSeason) { 19489 var ret; 19490 if (!num) { 19491 num = 0; 19492 } 19493 num = "" + num; 19494 if (!num.match(/^[0-9]+$/)) { 19495 num = 0; 19496 } 19497 num = parseInt(num, 10); 19498 if (useSeason) { 19499 var res = {stub: "month-", num: num}; 19500 if (res.num < 1 || res.num > 24) { 19501 res.num = 0; 19502 } else { 19503 while (res.num > 16) { 19504 res.num = res.num - 4; 19505 } 19506 if (res.num > 12) { 19507 res.stub = "season-"; 19508 res.num = res.num - 12; 19509 } 19510 } 19511 ret = res; 19512 } else { 19513 if (num < 1 || num > 12) { 19514 num = 0; 19515 } 19516 ret = num; 19517 } 19518 return ret; 19519}; 19520 19521CSL.Util.Dates.month = {}; 19522 19523/** 19524 * Convert month to numeric form 19525 * <p>This just passes the number back as a string.</p> 19526 */ 19527CSL.Util.Dates.month.numeric = function (state, num) { 19528 var num = CSL.Util.Dates.normalizeMonth(num); 19529 if (!num) { 19530 num = ""; 19531 } 19532 return num; 19533}; 19534 19535/** 19536 * Convert month to numeric-leading-zeros form 19537 * <p>This just passes the number back as string padded with zeros.</p> 19538 */ 19539CSL.Util.Dates.month["numeric-leading-zeros"] = function (state, num) { 19540 var num = CSL.Util.Dates.normalizeMonth(num); 19541 if (!num) { 19542 num = ""; 19543 } else { 19544 num = "" + num; 19545 while (num.length < 2) { 19546 num = "0" + num; 19547 } 19548 } 19549 return num; 19550}; 19551 19552/** 19553 * Convert month to long form 19554 * <p>This passes back the month of the locale in long form.</p> 19555 */ 19556 19557// Gender is not currently used. Is it needed? 19558 19559CSL.Util.Dates.month["long"] = function (state, num, gender, forceDefaultLocale) { 19560 var res = CSL.Util.Dates.normalizeMonth(num, true); 19561 var num = res.num; 19562 if (!num) { 19563 num = ""; 19564 } else { 19565 num = "" + num; 19566 while (num.length < 2) { 19567 num = "0" + num; 19568 } 19569 num = state.getTerm(res.stub + num, "long", 0, 0, false, forceDefaultLocale); 19570 } 19571 return num; 19572}; 19573 19574/** 19575 * Convert month to long form 19576 * <p>This passes back the month of the locale in short form.</p> 19577 */ 19578 19579// See above. 19580 19581CSL.Util.Dates.month["short"] = function (state, num, gender, forceDefaultLocale) { 19582 var res = CSL.Util.Dates.normalizeMonth(num, true); 19583 var num = res.num; 19584 if (!num) { 19585 num = ""; 19586 } else { 19587 num = "" + num; 19588 while (num.length < 2) { 19589 num = "0" + num; 19590 } 19591 num = state.getTerm(res.stub + num, "short", 0, 0, false, forceDefaultLocale); 19592 } 19593 return num; 19594}; 19595 19596/* 19597 * DAY manglers 19598 * numeric, numeric-leading-zeros, ordinal 19599 */ 19600CSL.Util.Dates.day = {}; 19601 19602/** 19603 * Convert day to numeric form 19604 * <p>This just passes the number back as a string.</p> 19605 */ 19606CSL.Util.Dates.day.numeric = function (state, num) { 19607 return num.toString(); 19608}; 19609 19610CSL.Util.Dates.day["long"] = CSL.Util.Dates.day.numeric; 19611 19612/** 19613 * Convert day to numeric-leading-zeros form 19614 * <p>This just passes the number back as a string padded with zeros.</p> 19615 */ 19616CSL.Util.Dates.day["numeric-leading-zeros"] = function (state, num) { 19617 if (!num) { 19618 num = 0; 19619 } 19620 num = num.toString(); 19621 while (num.length < 2) { 19622 num = "0" + num; 19623 } 19624 return num.toString(); 19625}; 19626 19627/** 19628 * Convert day to ordinal form 19629 * <p>This will one day pass back the number as a string with the 19630 * ordinal suffix appropriate to the locale. For the present, 19631 * it just does what is most of the time right for English.</p> 19632 */ 19633CSL.Util.Dates.day.ordinal = function (state, num, gender) { 19634 return state.fun.ordinalizer.format(num, gender); 19635}; 19636 19637/*global CSL: true */ 19638 19639/** 19640 * Helper functions for constructing sort keys. 19641 * @namespace Sort key utilities 19642 */ 19643CSL.Util.Sort = {}; 19644 19645/** 19646 * Strip prepositions from a string 19647 * <p>Used when generating sort keys.</p> 19648 */ 19649CSL.Util.Sort.strip_prepositions = function (str) { 19650 var m; 19651 if ("string" === typeof str) { 19652 m = str.match(/^(([aA]|[aA][nN]|[tT][hH][eE])\s+)/); 19653 } 19654 if (m) { 19655 str = str.substr(m[1].length); 19656 } 19657 return str; 19658}; 19659 19660/*global CSL: true */ 19661 19662CSL.Util.substituteStart = function (state, target) { 19663 var element_trace, display, bib_first, func, choose_start, if_start, nodetypes; 19664 func = function (state, Item, item) { 19665 for (var i = 0, ilen = this.decorations.length; i < ilen; i += 1) { 19666 if ("@strip-periods" === this.decorations[i][0] && "true" === this.decorations[i][1]) { 19667 state.tmp.strip_periods += 1; 19668 break; 19669 } 19670 } 19671 }; 19672 this.execs.push(func); 19673 if (this.decorations && state.opt.development_extensions.csl_reverse_lookup_support) { 19674 this.decorations.reverse(); 19675 this.decorations.push(["@showid","true", this.cslid]); 19676 this.decorations.reverse(); 19677 } 19678 // 19679 // Contains body code for both substitute and first-field/remaining-fields 19680 // formatting. 19681 // 19682 19683 nodetypes = ["number", "date", "names"]; 19684 if (("text" === this.name && !this.postponed_macro) || nodetypes.indexOf(this.name) > -1) { 19685 element_trace = function (state, Item, item) { 19686 if (state.tmp.element_trace.value() === "author" || "names" === this.name) { 19687 if (!state.tmp.just_looking && item && item["author-only"] && state.tmp.area !== "intext") { 19688 if (!state.tmp.probably_rendered_something) { 19689 } else { 19690 state.tmp.element_trace.push("suppress-me"); 19691 } 19692 } 19693 if (!state.tmp.just_looking && item && item["suppress-author"]) { 19694 if (!state.tmp.probably_rendered_something) { 19695 state.tmp.element_trace.push("suppress-me"); 19696 } 19697 } 19698 } 19699 else if ("date" === this.name) { 19700 if (!state.tmp.just_looking && item && item["author-only"] && state.tmp.area !== "intext") { 19701 if (state.tmp.probably_rendered_something) { 19702 state.tmp.element_trace.push("suppress-me"); 19703 } 19704 } 19705 /* 19706 if (!state.tmp.just_looking && item && item["suppress-author"]) { 19707 if (state.tmp.probably_rendered_something) { 19708 //state.tmp.element_trace.push("suppress-me"); 19709 } 19710 } 19711 */ 19712 } else { 19713 if (!state.tmp.just_looking && item && item["author-only"] && state.tmp.area !== "intext") { 19714 // XXX can_block_substitute probably is doing nothing here. The value is always true. 19715 if (!state.tmp.probably_rendered_something && state.tmp.can_block_substitute) { 19716 } else { 19717 state.tmp.element_trace.push("suppress-me"); 19718 } 19719 } else if (item && item["suppress-author"]) { 19720 state.tmp.element_trace.push("do-not-suppress-me"); 19721 } 19722 } 19723 }; 19724 this.execs.push(element_trace); 19725 } 19726 display = this.strings.cls; 19727 this.strings.cls = false; 19728 if (state.build.render_nesting_level === 0) { 19729 // 19730 // The markup formerly known as @bibliography/first 19731 // 19732 // Separate second-field-align from the generic display logic. 19733 // There will be some code replication, but not in the 19734 // assembled style. 19735 // 19736 if (state.build.area === "bibliography" && state.bibliography.opt["second-field-align"]) { 19737 bib_first = new CSL.Token("group", CSL.START); 19738 bib_first.decorations = [["@display", "left-margin"]]; 19739 func = function (state, Item) { 19740 if (!state.tmp.render_seen) { 19741 bib_first.strings.first_blob = Item.id; 19742 state.output.startTag("bib_first", bib_first); 19743 } 19744 }; 19745 bib_first.execs.push(func); 19746 target.push(bib_first); 19747 } else if (CSL.DISPLAY_CLASSES.indexOf(display) > -1) { 19748 bib_first = new CSL.Token("group", CSL.START); 19749 bib_first.decorations = [["@display", display]]; 19750 func = function (state, Item) { 19751 bib_first.strings.first_blob = Item.id; 19752 state.output.startTag("bib_first", bib_first); 19753 }; 19754 bib_first.execs.push(func); 19755 target.push(bib_first); 19756 } 19757 state.build.cls = display; 19758 } 19759 state.build.render_nesting_level += 1; 19760 // Should this be render_nesting_level, with the increment 19761 // below? ... ? 19762 if (state.build.substitute_level.value() === 1) { 19763 // 19764 // All top-level elements in a substitute environment get 19765 // wrapped in conditionals. The substitute_level variable 19766 // is a stack, because spanned names elements (with their 19767 // own substitute environments) can be nested inside 19768 // a substitute environment. 19769 // 19770 // (okay, we use conditionals a lot more than that. 19771 // we slot them in for author-only as well...) 19772 choose_start = new CSL.Token("choose", CSL.START); 19773 CSL.Node.choose.build.call(choose_start, state, target); 19774 if_start = new CSL.Token("if", CSL.START); 19775 // 19776 // Set a test of the shadow if token to skip this 19777 // macro if we have acquired a name value. 19778 19779 // check for variable 19780 func = function () { 19781 if (state.tmp.can_substitute.value()) { 19782 return true; 19783 } 19784 return false; 19785 }; 19786 if_start.tests ? {} : if_start.tests = []; 19787 if_start.tests.push(func); 19788 if_start.test = state.fun.match.any(this, state, if_start.tests); 19789 target.push(if_start); 19790 } 19791 19792 if (state.sys.variableWrapper 19793 && this.variables_real 19794 && this.variables_real.length) { 19795 19796 func = function (state, Item, item) { 19797 if (!state.tmp.just_looking && !state.tmp.suppress_decorations) { 19798 // Attach item data and variable names. 19799 // Do with them what you will. 19800 var variable_entry = new CSL.Token("text", CSL.START); 19801 variable_entry.decorations = [["@showid", "true"]]; 19802 state.output.startTag("variable_entry", variable_entry); 19803 var position = null; 19804 if (item) { 19805 position = item.position; 19806 } 19807 if (!position) { 19808 position = 0; 19809 } 19810 var positionMap = [ 19811 "first", 19812 "container-subsequent", 19813 "subsequent", 19814 "ibid", 19815 "ibid-with-locator" 19816 ]; 19817 var noteNumber = 0; 19818 if (item && item.noteIndex) { 19819 noteNumber = item.noteIndex; 19820 } 19821 var firstReferenceNoteNumber = 0; 19822 if (item && item['first-reference-note-number']) { 19823 firstReferenceNoteNumber = item['first-reference-note-number']; 19824 } 19825 var firstContainerReferenceNoteNumber = 0; 19826 if (item && item['first-container-reference-note-number']) { 19827 firstContainerReferenceNoteNumber = item['first-container-reference-note-number']; 19828 } 19829 var citationNumber = 0; 19830 // XXX Will this EVER happen? 19831 if (item && item['citation-number']) { 19832 citationNumber = item['citation-number']; 19833 } 19834 var index = 0; 19835 if (item && item.index) { 19836 index = item.index; 19837 } 19838 var params = { 19839 itemData: Item, 19840 variableNames: this.variables, 19841 context: state.tmp.area, 19842 xclass: state.opt.xclass, 19843 position: positionMap[position], 19844 "note-number": noteNumber, 19845 "first-reference-note-number": firstReferenceNoteNumber, 19846 "first-container-reference-note-number": firstContainerReferenceNoteNumber, 19847 "citation-number": citationNumber, 19848 "index": index, 19849 "mode": state.opt.mode 19850 }; 19851 state.output.current.value().params = params; 19852 } 19853 }; 19854 this.execs.push(func); 19855 } 19856}; 19857 19858 19859CSL.Util.substituteEnd = function (state, target) { 19860 var func, bib_first_end, bib_other, if_end, choose_end, author_substitute, str; 19861 19862 if (state.sys.variableWrapper 19863 && (this.hasVariable || (this.variables_real && this.variables_real.length))) { 19864 19865 func = function (state) { 19866 if (!state.tmp.just_looking && !state.tmp.suppress_decorations) { 19867 state.output.endTag("variable_entry"); 19868 } 19869 }; 19870 this.execs.push(func); 19871 } 19872 19873 func = function (state) { 19874 for (var i = 0, ilen = this.decorations.length; i < ilen; i += 1) { 19875 if ("@strip-periods" === this.decorations[i][0] && "true" === this.decorations[i][1]) { 19876 state.tmp.strip_periods += -1; 19877 break; 19878 } 19879 } 19880 }; 19881 this.execs.push(func); 19882 19883 state.build.render_nesting_level += -1; 19884 if (state.build.render_nesting_level === 0) { 19885 if (state.build.cls) { 19886 func = function (state) { 19887 state.output.endTag("bib_first"); 19888 }; 19889 this.execs.push(func); 19890 state.build.cls = false; 19891 } else if (state.build.area === "bibliography" && state.bibliography.opt["second-field-align"]) { 19892 bib_first_end = new CSL.Token("group", CSL.END); 19893 // first func end 19894 func = function (state) { 19895 if (!state.tmp.render_seen) { 19896 state.output.endTag("bib_first"); // closes bib_first 19897 } 19898 }; 19899 bib_first_end.execs.push(func); 19900 target.push(bib_first_end); 19901 bib_other = new CSL.Token("group", CSL.START); 19902 bib_other.decorations = [["@display", "right-inline"]]; 19903 func = function (state) { 19904 if (!state.tmp.render_seen) { 19905 state.tmp.render_seen = true; 19906 state.output.startTag("bib_other", bib_other); 19907 } 19908 }; 19909 bib_other.execs.push(func); 19910 target.push(bib_other); 19911 } 19912 } 19913 if (state.build.substitute_level.value() === 1) { 19914 if_end = new CSL.Token("if", CSL.END); 19915 target.push(if_end); 19916 choose_end = new CSL.Token("choose", CSL.END); 19917 CSL.Node.choose.build.call(choose_end, state, target); 19918 } 19919 19920 if ("names" === this.name || ("text" === this.name && this.variables_real !== "title")) { 19921 author_substitute = new CSL.Token("text", CSL.SINGLETON); 19922 var substitution_name = this.name; 19923 func = function (state, Item) { 19924 if (state.tmp.area !== "bibliography") { 19925 return; 19926 } 19927 if ("string" !== typeof state.bibliography.opt["subsequent-author-substitute"]) { 19928 return; 19929 } 19930 if (this.variables_real && !Item[this.variables_real]) { 19931 return; 19932 } 19933 // The logic of these two is not obvious. The effect is to enable placeholder substitution 19934 // on a text macro name substitution, without printing both the text macro AND the placeholder. 19935 // See https://forums.zotero.org/discussion/comment/350407 19936 if (this.variables_real && substitution_name === "names") { 19937 return; 19938 } 19939 19940 var subrule = state.bibliography.opt["subsequent-author-substitute-rule"]; 19941 var i, ilen; 19942 //var text_esc = CSL.getSafeEscape(state); 19943 var printing = !state.tmp.suppress_decorations; 19944 if (printing && state.tmp.subsequent_author_substitute_ok) { 19945 if (state.tmp.rendered_name) { 19946 if ("partial-each" === subrule || "partial-first" === subrule) { 19947 var dosub = true; 19948 var rendered_name = []; 19949 // This is a wee bit risky, as we're assuming that the name 19950 // children and the list of stringified names are congruent. 19951 // That *should* always be true, but you never know. 19952 for (i = 0, ilen = state.tmp.name_node.children.length; i < ilen; i += 1) { 19953 var name = state.tmp.rendered_name[i]; 19954 if (dosub 19955 && state.tmp.last_rendered_name && state.tmp.last_rendered_name.length > (i - 1) 19956 && name && !name.localeCompare(state.tmp.last_rendered_name[i])) { 19957 str = new CSL.Blob(state[state.tmp.area].opt["subsequent-author-substitute"]); 19958 state.tmp.name_node.children[i].blobs = [str]; 19959 if ("partial-first" === subrule) { 19960 dosub = false; 19961 } 19962 } else { 19963 dosub = false; 19964 } 19965 rendered_name.push(name); 19966 } 19967 // might want to slice this? 19968 state.tmp.last_rendered_name = rendered_name; 19969 } else if ("complete-each" === subrule) { 19970 var rendered_name = state.tmp.rendered_name.join(","); 19971 if (rendered_name) { 19972 if (state.tmp.last_rendered_name && !rendered_name.localeCompare(state.tmp.last_rendered_name)) { 19973 for (i = 0, ilen = state.tmp.name_node.children.length; i < ilen; i += 1) { 19974 str = new CSL.Blob(state[state.tmp.area].opt["subsequent-author-substitute"]); 19975 state.tmp.name_node.children[i].blobs = [str]; 19976 } 19977 } 19978 state.tmp.last_rendered_name = rendered_name; 19979 } 19980 } else { 19981 var rendered_name = state.tmp.rendered_name.join(","); 19982 if (rendered_name) { 19983 if (state.tmp.last_rendered_name && !rendered_name.localeCompare(state.tmp.last_rendered_name)) { 19984 str = new CSL.Blob(state[state.tmp.area].opt["subsequent-author-substitute"]); 19985 if (state.tmp.label_blob) { 19986 state.tmp.name_node.top.blobs = [str,state.tmp.label_blob]; 19987 } else if (state.tmp.name_node.top.blobs.length) { 19988 state.tmp.name_node.top.blobs[0].blobs = [str]; 19989 } else { 19990 state.tmp.name_node.top.blobs = [str]; 19991 } 19992 state.tmp.substituted_variable = substitution_name; 19993 } 19994 state.tmp.last_rendered_name = rendered_name; 19995 } 19996 } 19997 state.tmp.subsequent_author_substitute_ok = false; 19998 } 19999 } 20000 }; 20001 this.execs.push(func); 20002 } 20003 20004 if (("text" === this.name && !this.postponed_macro) || ["number", "date", "names"].indexOf(this.name) > -1) { 20005 // element trace 20006 func = function (state, Item) { 20007 // element_trace is a mess, but it's trying to do something simple. 20008 // A queue append is done, and element_trace.value() returns "suppress-me" 20009 // the append is aborted. That's it. 20010 // It seems only to be used on numeric elements of numeric styles ATM. 20011 // If used only for that purpose, it could be greatly simplified. 20012 // If cleaned up, it could do more interesting things, like control 20013 // the suppression of names set later than first position. 20014 if (state.tmp.element_trace.mystack.length>1) { 20015 state.tmp.element_trace.pop(); 20016 } 20017 }; 20018 this.execs.push(func); 20019 } 20020}; 20021 20022/*global CSL: true */ 20023 20024CSL.Util.padding = function (num) { 20025 var m = num.match(/\s*(-{0,1}[0-9]+)/); 20026 if (m) { 20027 num = parseInt(m[1], 10); 20028 if (num < 0) { 20029 num = 99999999999999999999 + num; 20030 } 20031 num = "" + num; 20032 while (num.length < 20) { 20033 num = "0" + num; 20034 } 20035 } 20036 return num; 20037}; 20038 20039CSL.Util.LongOrdinalizer = function () {}; 20040 20041CSL.Util.LongOrdinalizer.prototype.init = function (state) { 20042 this.state = state; 20043}; 20044 20045CSL.Util.LongOrdinalizer.prototype.format = function (num, gender) { 20046 if (num < 10) { 20047 num = "0" + num; 20048 } 20049 // Argument true means "loose". 20050 var ret = CSL.Engine.getField( 20051 CSL.LOOSE, 20052 this.state.locale[this.state.opt.lang].terms, 20053 "long-ordinal-" + num, 20054 "long", 20055 0, 20056 gender 20057 ); 20058 if (!ret) { 20059 ret = this.state.fun.ordinalizer.format(num, gender); 20060 } 20061 // Probably too optimistic -- what if only renders in _sort? 20062 this.state.tmp.cite_renders_content = true; 20063 return ret; 20064}; 20065 20066 20067CSL.Util.Ordinalizer = function (state) { 20068 this.state = state; 20069 this.suffixes = {}; 20070}; 20071 20072CSL.Util.Ordinalizer.prototype.init = function () { 20073 if (!this.suffixes[this.state.opt.lang]) { 20074 this.suffixes[this.state.opt.lang] = {}; 20075 for (var i = 0, ilen = 3; i < ilen; i += 1) { 20076 var gender = [undefined, "masculine", "feminine"][i]; 20077 this.suffixes[this.state.opt.lang][gender] = []; 20078 for (var j = 1; j < 5; j += 1) { 20079 var ordinal = this.state.getTerm("ordinal-0" + j, "long", false, gender); 20080 if ("undefined" === typeof ordinal) { 20081 delete this.suffixes[this.state.opt.lang][gender]; 20082 break; 20083 } 20084 this.suffixes[this.state.opt.lang][gender].push(ordinal); 20085 } 20086 } 20087 } 20088}; 20089 20090CSL.Util.Ordinalizer.prototype.format = function (num, gender) { 20091 var str; 20092 num = parseInt(num, 10); 20093 str = "" + num; 20094 var suffix = ""; 20095 var trygenders = []; 20096 if (gender) { 20097 trygenders.push(gender); 20098 } 20099 trygenders.push("neuter"); 20100 if (this.state.locale[this.state.opt.lang].ord["1.0.1"]) { 20101 suffix = this.state.getTerm("ordinal",false,0,gender); 20102 var trygender; 20103 for (var i = 0, ilen = trygenders.length; i < ilen; i += 1) { 20104 trygender = trygenders[i]; 20105 var ordinfo = this.state.locale[this.state.opt.lang].ord["1.0.1"]; 20106 if (ordinfo["whole-number"][str] && ordinfo["whole-number"][str][trygender]) { 20107 suffix = this.state.getTerm(this.state.locale[this.state.opt.lang].ord["1.0.1"]["whole-number"][str][trygender],false,0,gender); 20108 } else if (ordinfo["last-two-digits"][str.slice(str.length - 2)] && ordinfo["last-two-digits"][str.slice(str.length - 2)][trygender]) { 20109 suffix = this.state.getTerm(this.state.locale[this.state.opt.lang].ord["1.0.1"]["last-two-digits"][str.slice(str.length - 2)][trygender],false,0,gender); 20110 } else if (ordinfo["last-digit"][str.slice(str.length - 1)] && ordinfo["last-digit"][str.slice(str.length - 1)][trygender]) { 20111 suffix = this.state.getTerm(this.state.locale[this.state.opt.lang].ord["1.0.1"]["last-digit"][str.slice(str.length - 1)][trygender],false,0,gender); 20112 } 20113 if (suffix) { 20114 break; 20115 } 20116 } 20117 } else { 20118 if (!gender) { 20119 // XXX hack to prevent crash on CSL 1.0 styles. 20120 // Reported by Carles. 20121 gender = undefined; 20122 } 20123 this.state.fun.ordinalizer.init(); 20124 if ((num / 10) % 10 === 1 || (num > 10 && num < 20)) { 20125 suffix = this.suffixes[this.state.opt.lang][gender][3]; 20126 } else if (num % 10 === 1 && num % 100 !== 11) { 20127 suffix = this.suffixes[this.state.opt.lang][gender][0]; 20128 } else if (num % 10 === 2 && num % 100 !== 12) { 20129 suffix = this.suffixes[this.state.opt.lang][gender][1]; 20130 } else if (num % 10 === 3 && num % 100 !== 13) { 20131 suffix = this.suffixes[this.state.opt.lang][gender][2]; 20132 } else { 20133 suffix = this.suffixes[this.state.opt.lang][gender][3]; 20134 } 20135 } 20136 str = str += suffix; 20137 return str; 20138}; 20139 20140CSL.Util.Romanizer = function () {}; 20141 20142CSL.Util.Romanizer.prototype.format = function (num) { 20143 var ret, pos, n, numstr, len; 20144 ret = ""; 20145 if (num < 6000) { 20146 numstr = num.toString().split(""); 20147 numstr.reverse(); 20148 pos = 0; 20149 n = 0; 20150 len = numstr.length; 20151 for (pos = 0; pos < len; pos += 1) { 20152 n = parseInt(numstr[pos], 10); 20153 ret = CSL.ROMAN_NUMERALS[pos][n] + ret; 20154 } 20155 } 20156 return ret; 20157}; 20158 20159 20160/** 20161 * Create a suffix formed from a list of arbitrary characters of arbitrary length. 20162 * <p>This is a <i>lot</i> harder than it seems.</p> 20163 */ 20164CSL.Util.Suffixator = function (slist) { 20165 if (!slist) { 20166 slist = CSL.SUFFIX_CHARS; 20167 } 20168 this.slist = slist.split(","); 20169}; 20170 20171/** 20172 * The format method. 20173 * <p>This method is used in generating ranges. Every numeric 20174 * formatter (of which Suffixator is one) must be an instantiated 20175 * object with such a "format" method.</p> 20176 */ 20177 20178CSL.Util.Suffixator.prototype.format = function (N) { 20179 // Many thanks to Avram Lyon for this code, and good 20180 // riddance to the several functions that it replaces. 20181 var X; 20182 N += 1; 20183 var key = ""; 20184 do { 20185 X = ((N % 26) === 0) ? 26 : (N % 26); 20186 var key = this.slist[X-1] + key; 20187 N = (N - X) / 26; 20188 } while ( N !== 0 ); 20189 return key; 20190}; 20191 20192 20193CSL.Engine.prototype.processNumber = function (node, ItemObject, variable) { 20194 //print("** processNumber() ItemObject[variable]="+ItemObject[variable]); 20195 var val; 20196 20197 var me = this; 20198 20199 var realVariable = variable; 20200 variable = (variable === "page-first") ? "page" : variable; 20201 20202 var fullformAnd = ",\\s+and\\s+|\\s+and\\s+"; 20203 if (this.opt.lang.slice(0, 2) !== "en") { 20204 fullformAnd += "|,\\s+" + this.getTerm("and") + "\\s+|\\s+" + this.getTerm("and") + "\\s+"; 20205 } 20206 var symbolAnd = "\\s*&\\s*"; 20207 var andRex = new RegExp("^" + symbolAnd+ "$"); 20208 var joinerMatchRex = new RegExp("(" + symbolAnd + "|" + fullformAnd + "|;\\s+|,\\s+|\\s*\\\\*[\\-\\u2013]+\\s*)", "g"); 20209 var joinerSplitRex = new RegExp("(?:" + symbolAnd + "|" + fullformAnd + "|;\\s+|,\\s+|\\s*\\\\*[\\-\\u2013]+\\s*)"); 20210 20211 // This guesses whether the symbol form is defined or not. 20212 // It's the best we can do, because when locales are built, all of the 20213 // holes are filled explictly with fallback values: the symbol form is never undefined. 20214 var localeAnd = this.getTerm("and"); 20215 var localeAmpersand = this.getTerm("and", "symbol"); 20216 if (localeAnd === localeAmpersand) { 20217 localeAmpersand = "&"; 20218 } 20219 20220 // XXXX shadow_numbers should carry an array of objects with 20221 // XXXX full data for each. The test of a number should be 20222 // XXXX a separate function, possibly supported by a splitter 20223 // XXXX method also used here. Keep code for each action in one place, 20224 // XXXX to prevent debugging from becoming a nightmare. 20225 20226 // The capture pattern below would apply affixes to all sub-elements, 20227 // which is not what we want. Sub-elements should nest within, or 20228 // affixes should be edited. The latter is probably easier to handle. 20229 20230 // values = [ 20231 // { 20232 // label: "sec.", 20233 // label-form: "plural", 20234 // value: 100, 20235 // styling: [object], 20236 // numeric: true 20237 // joiningSuffix: " & ", 20238 // }, 20239 // { 20240 // label: "sec.", 20241 // label-form: "none", 20242 // value: 103, 20243 // styling: [object], 20244 // numeric: true, 20245 // joiningSuffix: "" 20246 // } 20247 // ] 20248 20249 function normalizeFieldValue(str) { 20250 str = str.trim(); 20251 var m = str.match(/^([^ ]+)/); 20252 if (m && !CSL.STATUTE_SUBDIV_STRINGS[m[1]]) { 20253 var embeddedLabel = null; 20254 if (["locator", "locator-extra", "page"].indexOf(variable) > -1) { 20255 if (ItemObject.label) { 20256 embeddedLabel = CSL.STATUTE_SUBDIV_STRINGS_REVERSE[ItemObject.label]; 20257 } else { 20258 embeddedLabel = "p."; 20259 } 20260 } else { 20261 embeddedLabel = CSL.STATUTE_SUBDIV_STRINGS_REVERSE[variable]; 20262 } 20263 if (embeddedLabel) { 20264 str = embeddedLabel + " " + str; 20265 } 20266 } 20267 return str; 20268 } 20269 20270 20271 function composeNumberInfo(origLabel, label, val, joiningSuffix, parsePosition) { 20272 joiningSuffix = joiningSuffix ? joiningSuffix : ""; 20273 var info = {}; 20274 20275 if (!label && !CSL.STATUTE_SUBDIV_STRINGS_REVERSE[variable]) { 20276 label = "var:"+ variable; 20277 } 20278 20279 if (label) { 20280 var m = label.match(/(\s*)([^\s]+)(\s*)/); 20281 if (realVariable === "page" && parsePosition === 0 && ["p.", "pp."].indexOf(m[2]) === -1) { 20282 info.gotosleepability = true; 20283 info.labelVisibility = true; 20284 } else { 20285 info.labelVisibility = false; 20286 } 20287 info.label = m[2]; 20288 info.origLabel = origLabel; 20289 info.labelSuffix = m[3] ? m[3] : ""; 20290 info.plural = 0; 20291 } 20292 20293 var m = val.match(/^([0-9]*[a-zA-Z]+0*)?([0-9]+(?:[a-zA-Z]*|[-,a-zA-Z]+))$/); 20294 //var m = val.match(/^([0-9]*[a-zA-Z]0*)([0-9]+(?:[a-zA-Z]*|[-,a-zA-Z]+))$/); 20295 if (m) { 20296 info.particle = m[1] ? m[1] : ""; 20297 info.value = m[2]; 20298 } else { 20299 info.particle = ""; 20300 info.value = val; 20301 } 20302 info.joiningSuffix = joiningSuffix.replace(/\s*-\s*/, "-"); 20303 return info; 20304 } 20305 20306 function fixupSubsections(elems) { 20307 // This catches things like p. 12a-c, recombining content to yield 20308 // numeric true despite the hyphen. 20309 for (var i=elems.length-2;i>-1;i-=2) { 20310 if (elems[i] === "-" 20311 && elems[i-1].match(/^(?:(?:[a-z]|[a-z][a-z]|[a-z][a-z][a-z]|[a-z][a-z][a-z][a-z])\. *)*[0-9]+[,a-zA-Z]+$/) 20312 && elems[i+1].match(/^[,a-zA-Z]+$/)) { 20313 elems[i-1] = elems.slice(i-1,i+2).join(""); 20314 elems = elems.slice(0,i).concat(elems.slice(i+2)); 20315 } 20316 } 20317 return elems; 20318 } 20319 20320 function parseString(str, defaultLabel) { 20321 defaultLabel = defaultLabel ? defaultLabel : ""; 20322 20323 str = normalizeFieldValue(str, defaultLabel); 20324 20325 var jmrex, jsrex, mystr; 20326 if ("page" === variable) { 20327 if (str.indexOf("\u2013") > -1) { 20328 str = str.replace(/\u2013/g, "-"); 20329 } 20330 } 20331 if (str.indexOf("\\-") > -1) { 20332 jmrex = new RegExp(joinerMatchRex.source.replace("\\-", "")); 20333 jsrex = new RegExp(joinerSplitRex.source.replace("\\-", "")); 20334 var lst = str.split("\\-"); 20335 for (var i=0,ilen=lst.length;i<ilen;i++) { 20336 lst[i] = lst[i].replace(/\-/g, "\u2013"); 20337 } 20338 mystr = lst.join("\\-"); 20339 mystr = mystr.replace(/\\/g, ""); 20340 } else { 20341 jmrex = joinerMatchRex; 20342 jsrex = joinerSplitRex; 20343 mystr = str; 20344 } 20345 // jmrex = joinerMatchRex; 20346 // jsrex = joinerSplitRex; 20347 20348 // Split chunks and collate delimiters. 20349 var elems = []; 20350 var m = mystr.match(jmrex); 20351 if (m) { 20352 var lst = mystr.split(jsrex); 20353 for (var i=0, ilen=m.length; i<ilen; i++) { 20354 if (m[i].match(andRex)) { 20355 if (lst[i].match(/[a-zA-Z]$/) && lst[i].match(/^[a-zA-Z]/)) { 20356 m[i] = localeAmpersand; 20357 } else { 20358 m[i] = " " + localeAmpersand + " "; 20359 } 20360 } 20361 } 20362 var recombine = false; 20363 for (var i in lst) { 20364 if (("" + lst[i]).replace(/^[a-z]\.\s+/, "").match(/[^\s0-9ivxlcmIVXLCM]/)) { 20365 //recombine = true; 20366 break; 20367 } 20368 } 20369 if (recombine) { 20370 elems = [mystr]; 20371 } else { 20372 for (var i=0,ilen=lst.length-1; i<ilen; i++) { 20373 elems.push(lst[i]); 20374 elems.push(m[i]); 20375 } 20376 elems.push(lst[lst.length-1]); 20377 //print("ELEMS: "+elems); 20378 elems = fixupSubsections(elems); 20379 //print(" fixup: "+elems); 20380 } 20381 } else { 20382 var elems = [mystr]; 20383 } 20384 // Split elements within each chunk build list of value objects. 20385 var values = []; 20386 var label = defaultLabel; 20387 var origLabel = ""; 20388 for (var i=0,ilen=elems.length;i<ilen;i += 2) { 20389 20390 // AHA! HERE'S THE CULPRIT!!! 20391 // Words up to four characters are treated as honorary short-form labels. 20392 // Some valid labels are longer than four chars, so we stir those in explicitly 20393 20394 var m = elems[i].match(/((?:^| )(?:[a-z]|[a-z][a-z]|[a-z][a-z][a-z]|[a-z][a-z][a-z][a-z]|subpara|subch|amend|bibliog|annot|illus|princ|intro|sched|subdiv|subsec)(?:\.| ) *)/g); 20395 if (m) { 20396 var lst = elems[i].split(/(?:(?:^| )(?:[a-z]|[a-z][a-z]|[a-z][a-z][a-z]|[a-z][a-z][a-z][a-z]|subpara|subch|amend|bibliog|annot|illus|princ|intro|sched|subdiv|subsec)(?:\.| ) *)/); 20397 // Head off disaster by merging parsed labels on non-numeric values into content 20398 for (var j=lst.length-1;j>0;j--) { 20399 if (lst[j-1] && (!lst[j].match(/^[0-9]+([-;,:a-zA-Z]*)$/) || !lst[j-1].match(/^[0-9]+([-;,:a-zA-Z]*)$/))) { 20400 lst[j-1] = lst[j-1] + m[j-1] + lst[j]; 20401 lst = lst.slice(0,j).concat(lst.slice(j+1)); 20402 m = m.slice(0,j-1).concat(m.slice(j)); 20403 } 20404 } 20405 // merge bad leading label into content 20406 if (m.length > 0) { 20407 var slug = m[0].trim(); 20408 var notAlabel = !CSL.STATUTE_SUBDIV_STRINGS[slug] 20409 || "undefined" === typeof me.getTerm(CSL.STATUTE_SUBDIV_STRINGS[slug]) 20410 || (["locator", "number", "locator-extra", "page"].indexOf(variable) === -1 && CSL.STATUTE_SUBDIV_STRINGS[slug] !== variable); 20411 if (notAlabel) { 20412 if (i === 0) { 20413 m = m.slice(1); 20414 lst[0] = lst[0] + " " + slug + " " + lst[1]; 20415 lst = lst.slice(0,1).concat(lst.slice(2)); 20416 } 20417 } else { 20418 origLabel = slug; 20419 } 20420 } 20421 20422 for (var j=0,jlen=lst.length; j<jlen; j++) { 20423 if (lst[j] || j === (lst.length-1)) { 20424 var filteredOrigLabel; 20425 label = m[j-1] ? m[j-1] : label; 20426 if (origLabel === label.trim()) { 20427 filteredOrigLabel = ""; 20428 } else { 20429 filteredOrigLabel = origLabel; 20430 } 20431 //var origLabel = j > 1 ? m[j-1] : ""; 20432 mystr = lst[j] ? lst[j].trim() : ""; 20433 if (j === (lst.length-1)) { 20434 values.push(composeNumberInfo(filteredOrigLabel, label, mystr, elems[i+1], i)); 20435 } else { 20436 values.push(composeNumberInfo(filteredOrigLabel, label, mystr, null, i)); 20437 } 20438 } 20439 } 20440 } else { 20441 var filteredOrigLabel; 20442 if (origLabel === label.trim()) { 20443 filteredOrigLabel = ""; 20444 } else { 20445 filteredOrigLabel = origLabel; 20446 } 20447 values.push(composeNumberInfo(filteredOrigLabel, label, elems[i], elems[i+1])); 20448 } 20449 } 20450 return values; 20451 } 20452 20453 function setSpaces(values) { 20454 // Add space joins (is this really right?) 20455 for (var i=0,ilen=values.length-1;i<ilen;i++) { 20456 if (!values[i].joiningSuffix && values[i+1].label) { 20457 values[i].joiningSuffix = " "; 20458 } 20459 } 20460 } 20461 20462 function fixNumericAndCount(values, i, currentLabelInfo) { 20463 var master = values[currentLabelInfo.pos]; 20464 var val = values[i].value; 20465 var isEscapedHyphen = master.joiningSuffix === "\\-"; 20466 if (val.particle && val.particle !== master.particle) { 20467 currentLabelInfo.collapsible = false; 20468 } 20469 var mVal = val.match(/^[0-9]+([-,:a-zA-Z]*)$/); 20470 var mCurrentLabel = master.value.match(/^(?:[0-9]+|[ixv]+)([-,:a-zA-Z]*|\-[\-0-9]+)$/); 20471 if (!val || !mVal || !mCurrentLabel || isEscapedHyphen) { 20472 currentLabelInfo.collapsible = false; 20473 if (!val || !mCurrentLabel) { 20474 currentLabelInfo.numeric = false; 20475 } 20476 if (isEscapedHyphen) { 20477 currentLabelInfo.count--; 20478 } 20479 } 20480 if ((mVal && mVal[1]) || (mCurrentLabel && mCurrentLabel[1])) { 20481 currentLabelInfo.collapsible = false; 20482 } 20483 if (undefined === values[i].collapsible) { 20484 for (var j=i,jlen=i+currentLabelInfo.count;j<jlen;j++) { 20485 if (isNaN(parseInt(values[j].value)) && !values[j].value.match(/^[ivxlcmIVXLCM]+$/)) { 20486 values[j].collapsible = false; 20487 } else { 20488 values[j].collapsible = true; 20489 } 20490 } 20491 currentLabelInfo.collapsible = values[i].collapsible; 20492 } 20493 var isCollapsible = currentLabelInfo.collapsible; 20494 for (var j=currentLabelInfo.pos,jlen=(currentLabelInfo.pos + currentLabelInfo.count); j<jlen; j++) { 20495 if (currentLabelInfo.count > 1 && isCollapsible) { 20496 values[j].plural = 1; 20497 } 20498 values[j].numeric = currentLabelInfo.numeric; 20499 values[j].collapsible = currentLabelInfo.collapsible; 20500 } 20501 } 20502 20503 function fixLabelVisibility(values, groupStartPos, currentLabelInfo) { 20504 if (currentLabelInfo.label.slice(0, 4) !== "var:") { 20505 if (currentLabelInfo.pos === 0) { 20506 if (["locator", "number", "locator-extra", "page"].indexOf(variable) > -1) { 20507 // Actually, shouldn't we do this always? 20508 if ("undefined" === typeof me.getTerm(CSL.STATUTE_SUBDIV_STRINGS[currentLabelInfo.label])) { 20509 values[currentLabelInfo.pos].labelVisibility = true; 20510 } 20511 } 20512 // If there is an explicit 20513 // label embedded at the start of a field that 20514 // does not match the context, it should be 20515 // marked for rendering. 20516 if (["locator", "number", "locator-extra", "page"].indexOf(variable) === -1) { 20517 if (CSL.STATUTE_SUBDIV_STRINGS[currentLabelInfo.label] !== variable) { 20518 values[0].labelVisibility = true; 20519 } 20520 } 20521 } else { 20522 // Also, mark initial mid-field labels for 20523 // rendering. 20524 //if (values[i-1].label !== values[i].label && currentLabelInfo.label.slice(0, 4) !== "var:") { 20525 values[currentLabelInfo.pos].labelVisibility = true; 20526 //} 20527 } 20528 } 20529 } 20530 20531 function setPluralsAndNumerics(values) { 20532 if (values.length === 0) { 20533 return; 20534 } 20535 var groupStartPos = 0; 20536 var groupCount = 1; 20537 20538 for (var i=1,ilen=values.length;i<ilen;i++) { 20539 var lastVal = values[i-1]; 20540 var thisVal = values[i]; 20541 if (lastVal.label === thisVal.label && lastVal.particle === lastVal.particle) { 20542 groupCount++; 20543 } else { 20544 var currentLabelInfo = JSON.parse(JSON.stringify(values[groupStartPos])); 20545 currentLabelInfo.pos = groupStartPos; 20546 currentLabelInfo.count = groupCount; 20547 currentLabelInfo.numeric = true; 20548 fixNumericAndCount(values, groupStartPos, currentLabelInfo); 20549 if (lastVal.label !== thisVal.label) { 20550 fixLabelVisibility(values, groupStartPos, currentLabelInfo); 20551 } 20552 groupStartPos = i; 20553 groupCount = 1; 20554 } 20555 } 20556 // Not sure why this repetition is necessary? 20557 var currentLabelInfo = JSON.parse(JSON.stringify(values[groupStartPos])); 20558 currentLabelInfo.pos = groupStartPos; 20559 currentLabelInfo.count = groupCount; 20560 currentLabelInfo.numeric = true; 20561 fixNumericAndCount(values, groupStartPos, currentLabelInfo); 20562 fixLabelVisibility(values, groupStartPos, currentLabelInfo); 20563 if (values.length && values[0].numeric && variable.slice(0, 10) === "number-of-") { 20564 if (parseInt(ItemObject[realVariable], 10) > 1) { 20565 values[0].plural = 1; 20566 } 20567 } 20568 } 20569 20570 function stripHyphenBackslash(joiningSuffix) { 20571 return joiningSuffix.replace("\\-", "-"); 20572 } 20573 20574 function setStyling(values) { 20575 var masterNode = CSL.Util.cloneToken(node); 20576 var masterStyling = new CSL.Token(); 20577 if (!me.tmp.just_looking) { 20578 // Per discussion @ https://discourse.citationstyles.org/t/formatting-attributes-and-hyphen/1518 20579 masterStyling.decorations = masterNode.decorations; 20580 masterNode.decorations = []; 20581 //for (var j=masterNode.decorations.length-1;j>-1;j--) { 20582 // if (masterNode.decorations[j][0] === "@quotes") { 20583 // // Add to styling 20584 // masterStyling.decorations = masterStyling.decorations.concat(masterNode.decorations.slice(j, j+1)); 20585 // // Remove from node 20586 // masterNode.decorations = masterNode.decorations.slice(0, j).concat(masterNode.decorations.slice(j+1)); 20587 // } 20588 //} 20589 masterStyling.strings.prefix = masterNode.strings.prefix; 20590 masterNode.strings.prefix = ""; 20591 masterStyling.strings.suffix = masterNode.strings.suffix; 20592 masterNode.strings.suffix = ""; 20593 } 20594 var masterLabel = values.length ? values[0].label : null; 20595 if (values.length) { 20596 for (var i=0,ilen=values.length; i<ilen; i++) { 20597 var val = values[i]; 20598 // Clone node, make styling parameters on each instance sane. 20599 var newnode = CSL.Util.cloneToken(masterNode); 20600 newnode.gender = node.gender; 20601 if (masterLabel === val.label) { 20602 newnode.formatter = node.formatter; 20603 } 20604 if (val.numeric) { 20605 newnode.successor_prefix = val.successor_prefix; 20606 } 20607 newnode.strings.suffix = newnode.strings.suffix + stripHyphenBackslash(val.joiningSuffix); 20608 val.styling = newnode; 20609 } 20610 if (!me.tmp.just_looking) { 20611 if (values[0].value.slice(0,1) === "\"" && values[values.length-1].value.slice(-1) === "\"") { 20612 values[0].value = values[0].value.slice(1); 20613 values[values.length-1].value = values[values.length-1].value.slice(0,-1); 20614 masterStyling.decorations.push(["@quotes", true]); 20615 } 20616 } 20617 } 20618 return masterStyling; 20619 } 20620 20621 function checkTerm(variable, val) { 20622 var ret = true; 20623 if (["locator", "locator-extra", "page"].indexOf(variable) > -1) { 20624 var label; 20625 if (val.origLabel) { 20626 label = val.origLabel; 20627 } else { 20628 label = val.label; 20629 } 20630 ret = !!me.getTerm(CSL.STATUTE_SUBDIV_STRINGS[label]); 20631 } 20632 return ret; 20633 } 20634 20635 function checkPage(variable, val) { 20636 return "page" === variable 20637 || (["locator", "locator-extra"].indexOf(variable) > -1 && (["p."].indexOf(val.label) > -1 || ["p."].indexOf(val.origLabel) > -1)); 20638 } 20639 20640 function fixupRangeDelimiter(variable, val, rangeDelimiter, isNumeric) { 20641 var isPage = checkPage(variable, val); 20642 var hasTerm = checkTerm(variable, val); 20643 if (hasTerm && rangeDelimiter === "-") { 20644 if (isNumeric) { 20645 if (isPage || ["locator", "locator-extra", "issue", "volume", "edition", "number"].indexOf(variable) > -1) { 20646 rangeDelimiter = me.getTerm("page-range-delimiter"); 20647 if (!rangeDelimiter) { 20648 rangeDelimiter = "\u2013"; 20649 } 20650 } 20651 if (variable === "collection-number") { 20652 rangeDelimiter = me.getTerm("year-range-delimiter"); 20653 if (!rangeDelimiter) { 20654 rangeDelimiter = "\u2013"; 20655 } 20656 } 20657 } 20658 } 20659 //if (rangeDelimiter === "\\-") { 20660 // rangeDelimiter = "-"; 20661 //} 20662 return rangeDelimiter; 20663 } 20664 20665 function manglePageNumbers(values, i, currentInfo) { 20666 if (i<1) { 20667 return; 20668 } 20669 if (currentInfo.count !== 2) { 20670 return; 20671 } 20672 if (values[i-1].particle !== values[i].particle) { 20673 return; 20674 } 20675 if (values[i-1].joiningSuffix !== "-") { 20676 currentInfo.count = 1; 20677 return; 20678 } 20679 if (!me.opt["page-range-format"] && (parseInt(values[i-1].value, 10) > parseInt(values[i].value, 10))) { 20680 values[i-1].joiningSuffix = fixupRangeDelimiter(variable, values[i], values[i-1].joiningSuffix, true); 20681 return; 20682 } 20683 var val = values[i]; 20684 20685 var isPage = checkPage(variable, val); 20686 var str; 20687 if (isPage && !isNaN(parseInt(values[i-1].value)) && !isNaN(parseInt(values[i].value))) { 20688 str = values[i-1].particle + values[i-1].value + " - " + values[i].particle + values[i].value; 20689 str = me.fun.page_mangler(str); 20690 } else { 20691 // if (("" + values[i-1].value).match(/[0-9]$/) && ("" + values[i].value).match(/^[0-9]/)) { 20692 if (("" + values[i-1].value).match(/^([0-9]+|[ivxlcmIVXLCM]+)$/) && ("" + values[i].value).match(/^([0-9]+|[ivxlcmIVXLCM]+)$/)) { 20693 values[i-1].joiningSuffix = me.getTerm("page-range-delimiter"); 20694 } 20695 str = values[i-1].value + stripHyphenBackslash(values[i-1].joiningSuffix) + values[i].value; 20696 } 20697 var m = str.match(/^((?:[0-9]*[a-zA-Z]+0*))?([0-9]+[a-z]*)(\s*[^0-9]+\s*)([-,a-zA-Z]?0*)([0-9]+[a-z]*)$/); 20698 // var m = str.match(/^((?:[0-9]*[a-zA-Z]+0*))?([0-9]+[a-z]*)(\s*[^0-9]+\s*)([-,a-zA-Z]?0*)([0-9]+[a-z]*)$/); 20699 if (m) { 20700 var rangeDelimiter = m[3]; 20701 rangeDelimiter = fixupRangeDelimiter(variable, val, rangeDelimiter, values[i].numeric); 20702 values[i-1].particle = m[1]; 20703 values[i-1].value = m[2]; 20704 values[i-1].joiningSuffix = rangeDelimiter; 20705 values[i].particle = m[4]; 20706 values[i].value = m[5]; 20707 } 20708 currentInfo.count = 0; 20709 } 20710 20711 function fixRanges(values) { 20712 20713 if (!node) { 20714 return; 20715 } 20716 if (["page", "chapter-number", "collection-number", "edition", "issue", "number", "number-of-pages", "number-of-volumes", "volume", "locator", "locator-extra"].indexOf(variable) === -1) { 20717 return; 20718 } 20719 20720 var currentInfo = { 20721 count: 0, 20722 label: null, 20723 lastHadRangeDelimiter: false 20724 }; 20725 20726 for (var i=0,ilen=values.length; i<ilen; i++) { 20727 var val = values[i]; 20728 if (!val.collapsible) { 20729 currentInfo.count = 0; 20730 currentInfo.label = null; 20731 var isNumeric = val.numeric; 20732 val.joiningSuffix = fixupRangeDelimiter(variable, val, val.joiningSuffix, isNumeric); 20733 } else if (currentInfo.label === val.label && val.joiningSuffix === "-") { 20734 // So if there is a hyphen here, and none previous, reset to 1 20735 currentInfo.count = 1; 20736 } else if (currentInfo.label === val.label && val.joiningSuffix !== "-") { 20737 // If there is NO hyphen here, count up 20738 currentInfo.count++; 20739 if (currentInfo.count === 2) { 20740 manglePageNumbers(values, i, currentInfo); 20741 } 20742 } else if (currentInfo.label !== val.label) { 20743 // If the label doesn't match and count is 2, process 20744 currentInfo.label = val.label; 20745 currentInfo.count = 1; 20746 } else { 20747 // Safety belt: label doesn't match and count is some other value, so reset to 1 20748 // This never happens, though. 20749 currentInfo.count = 1; 20750 currentInfo.label = val.label; 20751 } 20752 } 20753 // Finally clear, if needed 20754 if (currentInfo.count === 2) { 20755 manglePageNumbers(values, values.length-1, currentInfo); 20756 } 20757 } 20758 20759 function setVariableParams(shadow_numbers, realVariable, values) { 20760 var obj = shadow_numbers[realVariable]; 20761 if (values.length) { 20762 obj.numeric = values[0].numeric; 20763 obj.collapsible = values[0].collapsible; 20764 obj.plural = values[0].plural; 20765 obj.label = CSL.STATUTE_SUBDIV_STRINGS[values[0].label]; 20766 if (variable === "number" && obj.label === "issue" && me.getTerm("number")) { 20767 obj.label = "number"; 20768 } 20769 } 20770 } 20771 20772 // Split out the labels and values. 20773 20774 // short-circuit if object exists: if numeric, set styling, no other action 20775 if (node && this.tmp.shadow_numbers[realVariable] && this.tmp.shadow_numbers[realVariable].values.length) { 20776 var values = this.tmp.shadow_numbers[realVariable].values; 20777 fixRanges(values); 20778 //if (!this.tmp.shadow_numbers[variable].masterStyling && !this.tmp.just_looking) { 20779 this.tmp.shadow_numbers[realVariable].masterStyling = setStyling(values); 20780 //} 20781 return; 20782 } 20783 20784 // info.styling = node; 20785 20786 // This carries value, pluralization and numeric info for use in other contexts. 20787 // XXX We used to use one set of params for the entire variable value. 20788 // XXX Now params are set on individual objects, of which there may be several after parsing. 20789 if (!this.tmp.shadow_numbers[realVariable]) { 20790 this.tmp.shadow_numbers[realVariable] = { 20791 values:[] 20792 }; 20793 } 20794 //this.tmp.shadow_numbers[variable].values = []; 20795 //this.tmp.shadow_numbers[variable].plural = 0; 20796 //this.tmp.shadow_numbers[variable].numeric = false; 20797 //this.tmp.shadow_numbers[variable].label = false; 20798 20799 if (!ItemObject) { 20800 return; 20801 } 20802 20803 // Possibly apply multilingual transform 20804 var languageRole = CSL.LangPrefsMap[variable]; 20805 if (languageRole) { 20806 var localeType = this.opt["cite-lang-prefs"][languageRole][0]; 20807 val = this.transform.getTextSubField(ItemObject, realVariable, "locale-"+localeType, true); 20808 val = val.name; 20809 } else { 20810 val = ItemObject[realVariable]; 20811 } 20812 20813 if (val && realVariable === "number" && ItemObject.type === "legal_case") { 20814 val = val.replace(/[\\]*-/g, "\\-"); 20815 } 20816 20817 // XXX HOLDING THIS 20818 // Apply short form ONLY if first element tests is-numeric=false 20819 if (val && this.sys.getAbbreviation) { 20820 // RefMe bug report: print("XX D'oh! (3): "+num); 20821 // true as the fourth argument suppresses update of the UI 20822 20823 // No need for this. 20824 //val = ("" + val).replace(/^\"/, "").replace(/\"$/, ""); 20825 if (this.sys.normalizeAbbrevsKey) { 20826 var normval = this.sys.normalizeAbbrevsKey(realVariable, val); 20827 } else { 20828 var normval = val; 20829 } 20830 var jurisdiction = this.transform.loadAbbreviation(ItemObject.jurisdiction, "number", normval, ItemObject.language); 20831 if (this.transform.abbrevs[jurisdiction].number) { 20832 if (this.transform.abbrevs[jurisdiction].number[normval]) { 20833 val = this.transform.abbrevs[jurisdiction].number[normval]; 20834 } else { 20835 20836 // *** This is terrible *** 20837 20838 // Strings rendered via cs:number should not be added to the abbreviations 20839 // UI unless they test non-numeric. The test happens below. 20840 if ("undefined" !== typeof this.transform.abbrevs[jurisdiction].number[normval]) { 20841 delete this.transform.abbrevs[jurisdiction].number[normval]; 20842 } 20843 } 20844 } 20845 } 20846 20847 // { 20848 // label: "sec.", 20849 // labelForm: "plural", 20850 // labelVisibility: true, 20851 // value: 100, 20852 // styling: [object], 20853 // numeric: true 20854 // joiningSuffix: " & ", 20855 // }, 20856 20857 // Process only if there is a value. 20858 if ("undefined" !== typeof val && ("string" === typeof val || "number" === typeof val)) { 20859 if ("number" === typeof val) { 20860 val = "" + val; 20861 } 20862 var defaultLabel = CSL.STATUTE_SUBDIV_STRINGS_REVERSE[variable]; 20863 20864 if (this.tmp.shadow_numbers[realVariable].values.length === 0) { 20865 // XXX 20866 var values = parseString(val, defaultLabel); 20867 20868 setSpaces(values); 20869 //print("setSpaces(): "+JSON.stringify(values, null, 2)); 20870 20871 setPluralsAndNumerics(values); 20872 //print("setPluralsAndNumerics(): "+JSON.stringify(values, null, 2)); 20873 20874 for (var obj of values) { 20875 if (!obj.numeric) obj.plural = 0; 20876 } 20877 this.tmp.shadow_numbers[realVariable].values = values; 20878 // me.sys.print(JSON.stringify(values)) 20879 20880 if (node) { 20881 fixRanges(values); 20882 20883 this.tmp.shadow_numbers[realVariable].masterStyling = setStyling(values); 20884 // me.sys.print("setStyling(): "+JSON.stringify(values, null, 2)); 20885 } 20886 setVariableParams(this.tmp.shadow_numbers, realVariable, values); 20887 } 20888 20889 // hack in support for non-numeric numerics like "91 Civ. 5442 (RPP)|91 Civ. 5471" 20890 var info = this.tmp.shadow_numbers[realVariable]; 20891 if (variable === "number") { 20892 if (info.values.length === 1 && info.values[0].value.indexOf("|") > -1) { 20893 info.values[0].value = info.values[0].value.replace(/\|/g, ", "); 20894 info.values[0].numeric = true; 20895 info.values[0].plural = 1; 20896 info.values[0].collapsible = false; 20897 info.numeric = true; 20898 info.plural = 1; 20899 info.collapsible = false; 20900 } 20901 } 20902 if (info.values.length === 1) { 20903 if (info.values[0].value.match(/^[0-9]+(?:\/[0-9]+)+$/)) { 20904 info.values[0].numeric = true; 20905 info.values[0].plural = 0; 20906 info.values[0].collapsible = false; 20907 info.numeric = true; 20908 info.plural = 0; 20909 info.collapsible = false; 20910 } 20911 } 20912 if (variable === "page") { 20913 if (info.values.length > 0) { 20914 if (info.values[0].gotosleepability) { 20915 info.labelForm = "short"; 20916 } 20917 } 20918 } 20919 //this.sys.print("OK "+JSON.stringify(values, ["label", "origLabel", "labelSuffix", "particle", "collapsible", "value", "numeric", "joiningSuffix", "labelVisibility", "plural"], 2)); 20920 } 20921}; 20922 20923CSL.Util.outputNumericField = function(state, varname, itemID) { 20924 20925 state.output.openLevel(state.tmp.shadow_numbers[varname].masterStyling); 20926 var masterStyling = state.tmp.shadow_numbers[varname].masterStyling; 20927 var nums = state.tmp.shadow_numbers[varname].values; 20928 var masterLabel = nums.length ? nums[0].label : null; 20929 var labelForm = state.tmp.shadow_numbers[varname].labelForm; 20930 var tryStatic = state.tmp.group_context.tip.label_static; 20931 var embeddedLabelForm; 20932 if (labelForm) { 20933 embeddedLabelForm = labelForm; 20934 } else { 20935 embeddedLabelForm = "short"; 20936 //labelForm = "short"; 20937 } 20938 var labelCapitalizeIfFirst = state.tmp.shadow_numbers[varname].labelCapitalizeIfFirst; 20939 var labelDecorations = state.tmp.shadow_numbers[varname].labelDecorations; 20940 var lastLabelName = null; 20941 20942 for (var i=0,ilen=nums.length;i<ilen;i++) { 20943 var num = nums[i]; 20944 var label = ""; 20945 var labelName; 20946 if (num.label) { 20947 if ('var:' === num.label.slice(0,4)) { 20948 labelName = num.label.slice(4); 20949 } else { 20950 labelName = CSL.STATUTE_SUBDIV_STRINGS[num.label]; 20951 } 20952 if (labelName) { 20953 // Simplify this some day. 20954 if (num.label === masterLabel) { 20955 if (tryStatic) { 20956 label = state.getTerm(labelName, "static", num.plural); 20957 if (label.indexOf("%s") === -1) { 20958 label = ""; 20959 } 20960 } 20961 if (!label) { 20962 label = state.getTerm(labelName, labelForm, num.plural); 20963 } 20964 } else { 20965 if (tryStatic) { 20966 label = state.getTerm(labelName, "static", num.plural); 20967 if (label.indexOf("%s") === -1) { 20968 label = ""; 20969 } 20970 } 20971 if (!label) { 20972 label = state.getTerm(labelName, embeddedLabelForm, num.plural); 20973 } 20974 } 20975 if (labelCapitalizeIfFirst) { 20976 label = CSL.Output.Formatters["capitalize-first"](state, label); 20977 } 20978 } 20979 } 20980 var labelPlaceholderPos = -1; 20981 if (label) { 20982 labelPlaceholderPos = label.indexOf("%s"); 20983 } 20984 var numStyling = CSL.Util.cloneToken(num.styling); 20985 numStyling.formatter = num.styling.formatter; 20986 numStyling.type = num.styling.type; 20987 numStyling.num = num.styling.num; 20988 numStyling.gender = num.styling.gender; 20989 20990 if (labelPlaceholderPos > 0 && labelPlaceholderPos < (label.length-2)) { 20991 numStyling.strings.prefix += label.slice(0,labelPlaceholderPos); 20992 numStyling.strings.suffix = label.slice(labelPlaceholderPos+2) + numStyling.strings.suffix; 20993 } else if (num.labelVisibility) { 20994 if (!label) { 20995 label = num.label; 20996 labelName = num.label; 20997 } 20998 if (labelPlaceholderPos > 0) { 20999 var prefixLabelStyling = new CSL.Token(); 21000 prefixLabelStyling.decorations = labelDecorations; 21001 state.output.append(label.slice(0,labelPlaceholderPos), prefixLabelStyling); 21002 } else if (labelPlaceholderPos === (label.length-2) || labelPlaceholderPos === -1) { 21003 // And add a trailing delimiter. 21004 state.output.append(label+num.labelSuffix, "empty"); 21005 } 21006 } 21007 CSL.UPDATE_GROUP_CONTEXT_CONDITION(state, masterStyling.strings.prefix, null, masterStyling, `${num.particle}${num.value}`); 21008 if (num.collapsible) { 21009 var blob; 21010 if (num.value.match(/^[1-9][0-9]*$/) && Number.isSafeInteger(parseInt(num.value, 10))) { 21011 blob = new CSL.NumericBlob(state, num.particle, parseInt(num.value, 10), numStyling, itemID); 21012 } else { 21013 blob = new CSL.NumericBlob(state, num.particle, num.value, numStyling, itemID); 21014 } 21015 if ("undefined" === typeof blob.gender) { 21016 blob.gender = state.locale[state.opt.lang]["noun-genders"][varname]; 21017 } 21018 state.output.append(blob, "literal"); 21019 } else { 21020 state.output.append(num.particle + num.value, numStyling); 21021 } 21022 if (labelPlaceholderPos === 0 && labelPlaceholderPos < (label.length-2)) { 21023 // Only and always if this is the last entry of this label 21024 if (lastLabelName === null) { 21025 lastLabelName = labelName; 21026 } 21027 if (labelName !== lastLabelName || i === (nums.length-1)) { 21028 var suffixLabelStyling = new CSL.Token(); 21029 suffixLabelStyling.decorations = labelDecorations; 21030 state.output.append(label.slice(labelPlaceholderPos+2), suffixLabelStyling); 21031 } 21032 } 21033 lastLabelName = labelName; 21034 state.tmp.term_predecessor = true; 21035 } 21036 state.output.closeLevel(); 21037}; 21038 21039/*global CSL: true */ 21040 21041CSL.Util.PageRangeMangler = {}; 21042 21043CSL.Util.PageRangeMangler.getFunction = function (state, rangeType) { 21044 var rangerex, pos, len, stringify, listify, expand, minimize, minimize_internal, chicago15, chicago16, lst, m, b, e, ret, begin, end, ret_func; 21045 21046 var range_delimiter = state.getTerm(rangeType + "-range-delimiter"); 21047 21048 rangerex = /([0-9]*[a-zA-Z]+0*)?([0-9]+[a-z]*)\s*(?:\u2013|-)\s*([0-9]*[a-zA-Z]+0*)?([0-9]+[a-z]*)/; 21049 21050 stringify = function (lst) { 21051 len = lst.length; 21052 for (pos = 1; pos < len; pos += 2) { 21053 if ("object" === typeof lst[pos]) { 21054 lst[pos] = lst[pos].join(""); 21055 } 21056 } 21057 var ret = lst.join(""); 21058 ret = ret.replace(/([^\\])\-/g, "$1"+state.getTerm(rangeType + "-range-delimiter")); 21059 return ret; 21060 }; 21061 21062 listify = function (str) { 21063 var m, lst, ret; 21064 // Normalized delimiter form, for use in regexps 21065 var hyphens = "\\s+\\-\\s+"; 21066 // Normalize delimiters to hyphen wrapped in single spaces 21067 var this_range_delimiter = range_delimiter === "-" ? "" : range_delimiter; 21068 var delimRex = new RegExp("([^\\\\])[-" + this_range_delimiter + "\\u2013]", "g"); 21069 str = str.replace(delimRex, "$1 - ").replace(/\s+-\s+/g, " - "); 21070 // Workaround for Internet Explorer 21071 //var rexm = new RegExp("((?:[0-9]*[a-zA-Z]+)?[0-9]+" + hyphens + "(?:[0-9]*[a-zA-Z]+)?[0-9]+)", "g"); 21072 //var rexlst = new RegExp("(?:[0-9]*[a-zA-Z]+)?[0-9]+" + hyphens + "(?:[0-9]*[a-zA-Z]+)?[0-9]+"); 21073 var rexm = new RegExp("((?:[0-9]*[a-zA-Z]+0*)?[0-9]+[a-z]*" + hyphens + "(?:[0-9]*[a-zA-Z]+0*)?[0-9]+[a-z]*)", "g"); 21074 var rexlst = new RegExp("(?:[0-9]*[a-zA-Z]+0*)?[0-9]+[a-z]*" + hyphens + "(?:[0-9]*[a-zA-Z]+0*)?[0-9]+[a-z]*"); 21075 m = str.match(rexm); 21076 lst = str.split(rexlst); 21077 if (lst.length === 0) { 21078 ret = m; 21079 } else { 21080 ret = [lst[0]]; 21081 for (pos = 1, len = lst.length; pos < len; pos += 1) { 21082 ret.push(m[pos - 1].replace(/\s*\-\s*/g, "-")); 21083 ret.push(lst[pos]); 21084 } 21085 } 21086 return ret; 21087 }; 21088 21089 expand = function (str) { 21090 str = "" + str; 21091 lst = listify(str); 21092 len = lst.length; 21093 for (pos = 1; pos < len; pos += 2) { 21094 m = lst[pos].match(rangerex); 21095 if (m) { 21096 if (!m[3] || m[1] === m[3]) { 21097 if (m[4].length < m[2].length) { 21098 m[4] = m[2].slice(0, (m[2].length - m[4].length)) + m[4]; 21099 } 21100 if (parseInt(m[2], 10) < parseInt(m[4], 10)) { 21101 m[3] = range_delimiter + (m[1] ? m[1] : ""); 21102 lst[pos] = m.slice(1); 21103 } 21104 } 21105 } 21106 if ("string" === typeof lst[pos]) { 21107 lst[pos] = lst[pos].replace(/\-/g, range_delimiter); 21108 } 21109 } 21110 return lst; 21111 }; 21112 21113 minimize = function (lst, minchars, isyear) { 21114 len = lst.length; 21115 for (var i = 1, ilen = lst.length; i < ilen; i += 2) { 21116 if ("object" === typeof lst[i]) { 21117 lst[i][3] = minimize_internal(lst[i][1], lst[i][3], minchars, isyear); 21118 if (lst[i][2].slice(1) === lst[i][0]) { 21119 lst[i][2] = range_delimiter; 21120 } 21121 } 21122 } 21123 return stringify(lst); 21124 }; 21125 21126 minimize_internal = function (begin, end, minchars, isyear) { 21127 if (!minchars) { 21128 minchars = 0; 21129 } 21130 b = ("" + begin).split(""); 21131 e = ("" + end).split(""); 21132 ret = e.slice(); 21133 ret.reverse(); 21134 if (b.length === e.length) { 21135 for (var i = 0, ilen = b.length; i < ilen; i += 1) { 21136 if (b[i] === e[i] && ret.length > minchars) { 21137 ret.pop(); 21138 } else { 21139 if (minchars && isyear && ret.length === 3) { 21140 var front = b.slice(0, i); 21141 front.reverse(); 21142 ret = ret.concat(front); 21143 } 21144 break; 21145 } 21146 } 21147 } 21148 ret.reverse(); 21149 return ret.join(""); 21150 }; 21151 21152 chicago15 = function (lst) { 21153 len = lst.length; 21154 for (pos = 1; pos < len; pos += 2) { 21155 if ("object" === typeof lst[pos]) { 21156 m = lst[pos]; 21157 begin = parseInt(m[1], 10); 21158 end = parseInt(m[3], 10); 21159 if (begin > 100 && begin % 100 && parseInt((begin / 100), 10) === parseInt((end / 100), 10)) { 21160 m[3] = "" + (end % 100); 21161 } else if (begin >= 10000) { 21162 m[3] = "" + (end % 1000); 21163 } 21164 } 21165 if (m[2].slice(1) === m[0]) { 21166 m[2] = range_delimiter; 21167 } 21168 } 21169 return stringify(lst); 21170 }; 21171 21172 chicago16 = function (lst) { 21173 len = lst.length; 21174 for (pos = 1; pos < len; pos += 2) { 21175 if ("object" === typeof lst[pos]) { 21176 m = lst[pos]; 21177 begin = parseInt(m[1], 10); 21178 end = parseInt(m[3], 10); 21179 e = "" + end; 21180 if (begin > 100 && begin % 100) { 21181 for (var i = 2; i < e.length; i++) { 21182 var divisor = Math.pow(10, i); 21183 if (Math.floor(begin / divisor) === Math.floor(end / divisor)) { 21184 m[3] = "" + (end % divisor); 21185 break; 21186 } 21187 } 21188 } 21189 } 21190 if (m[2].slice(1) === m[0]) { 21191 m[2] = range_delimiter; 21192 } 21193 } 21194 return stringify(lst); 21195 }; 21196 21197 // 21198 // The top-level option handlers. 21199 // 21200 var sniff = function (str, func, minchars, isyear) { 21201 var ret; 21202 str = "" + str; 21203 var lst = expand(str); 21204 var ret = func(lst, minchars, isyear); 21205 return ret; 21206 }; 21207 if (!state.opt[rangeType + "-range-format"]) { 21208 ret_func = function (str) { 21209 //return str.replace("-", "\u2013", "g"); 21210 return sniff(str, stringify); 21211 }; 21212 } else if (state.opt[rangeType + "-range-format"] === "expanded") { 21213 ret_func = function (str) { 21214 return sniff(str, stringify); 21215 }; 21216 } else if (state.opt[rangeType + "-range-format"] === "minimal") { 21217 ret_func = function (str) { 21218 return sniff(str, minimize); 21219 }; 21220 } else if (state.opt[rangeType + "-range-format"] === "minimal-two") { 21221 ret_func = function (str, isyear) { 21222 return sniff(str, minimize, 2, isyear); 21223 }; 21224 } else if (state.opt[rangeType + "-range-format"] === "chicago") { 21225 ret_func = function (str) { 21226 return sniff(str, chicago15); 21227 }; 21228 } else if (state.opt[rangeType + "-range-format"] === "chicago-15") { 21229 ret_func = function (str) { 21230 return sniff(str, chicago15); 21231 }; 21232 } else if (state.opt[rangeType + "-range-format"] === "chicago-16") { 21233 ret_func = function (str) { 21234 return sniff(str, chicago16); 21235 }; 21236 } 21237 21238 return ret_func; 21239}; 21240 21241 21242/*global CSL: true */ 21243 21244// Use a state machine 21245 21246// Okay, good! 21247// Needs some tweaks: 21248// 1. First pass: quotes only 21249// Special: Convert all sandwiched single-quote markup to apostrophe 21250// 2. Second pass: tags 21251 21252CSL.Util.FlipFlopper = function(state) { 21253 21254 /** 21255 * INTERNAL 21256 */ 21257 21258 var _nestingState = []; 21259 21260 var _nestingData = { 21261 "<span class=\"nocase\">": { 21262 type: "nocase", 21263 opener: "<span class=\"nocase\">", 21264 closer: "</span>", 21265 attr: null, 21266 outer: null, 21267 flipflop: null 21268 }, 21269 "<span class=\"nodecor\">": { 21270 type: "nodecor", 21271 opener: "<span class=\"nodecor\">", 21272 closer: "</span>", 21273 attr: "@class", 21274 outer: "nodecor", 21275 flipflop: { 21276 "nodecor": "nodecor" 21277 } 21278 }, 21279 "<span style=\"font-variant:small-caps;\">": { 21280 type: "tag", 21281 opener: "<span style=\"font-variant:small-caps;\">", 21282 closer: "</span>", 21283 attr: "@font-variant", 21284 outer: "small-caps", 21285 flipflop: { 21286 "small-caps": "normal", 21287 "normal": "small-caps" 21288 } 21289 }, 21290 "<sc>": { 21291 type: "tag", 21292 opener: "<sc>", 21293 closer: "</sc>", 21294 attr: "@font-variant", 21295 outer: "small-caps", 21296 flipflop: { 21297 "small-caps": "normal", 21298 "normal": "small-caps" 21299 } 21300 }, 21301 "<i>": { 21302 type: "tag", 21303 opener: "<i>", 21304 closer: "</i>", 21305 attr: "@font-style", 21306 outer: "italic", 21307 flipflop: { 21308 "italic": "normal", 21309 "normal": "italic" 21310 } 21311 }, 21312 "<b>": { 21313 type: "tag", 21314 opener: "<b>", 21315 closer: "</b>", 21316 attr: "@font-weight", 21317 outer: "bold", 21318 flipflop: { 21319 "bold": "normal", 21320 "normal": "bold" 21321 } 21322 }, 21323 "<sup>": { 21324 type: "tag", 21325 opener: "<sup>", 21326 closer: "</sup>", 21327 attr: "@vertical-align", 21328 outer: "sup", 21329 flipflop: { 21330 "sub": "sup", 21331 "sup": "sup" 21332 } 21333 }, 21334 "<sub>": { 21335 type: "tag", 21336 opener: "<sub>", 21337 closer: "</sub>", 21338 attr: "@vertical-align", 21339 outer: "sub", 21340 flipflop: { 21341 "sup": "sub", 21342 "sub": "sub" 21343 } 21344 }, 21345 " \"": { 21346 type: "quote", 21347 opener: " \"", 21348 closer: "\"", 21349 attr: "@quotes", 21350 outer: "true", 21351 flipflop: { 21352 "true": "inner", 21353 "inner": "true", 21354 "false": "true" 21355 } 21356 }, 21357 " \'": { 21358 type: "quote", 21359 opener: " \'", 21360 closer: "\'", 21361 attr: "@quotes", 21362 outer: "inner", 21363 flipflop: { 21364 "true": "inner", 21365 "inner": "true", 21366 "false": "true" 21367 } 21368 } 21369 }; 21370 21371 _nestingData["(\""] = _nestingData[" \""]; 21372 _nestingData["(\'"] = _nestingData[" \'"]; 21373 21374 var localeOpenQuote = state.getTerm("open-quote"); 21375 var localeCloseQuote = state.getTerm("close-quote"); 21376 var localeOpenInnerQuote = state.getTerm("open-inner-quote"); 21377 var localeCloseInnerQuote = state.getTerm("close-inner-quote"); 21378 21379 // If locale uses straight quotes, do not register them. All will be well. 21380 // Otherwise, clone straight-quote data, and adjust. 21381 if (localeOpenQuote && localeCloseQuote && [" \""," \'","\"","\'"].indexOf(localeOpenQuote) === -1) { 21382 _nestingData[localeOpenQuote] = JSON.parse(JSON.stringify(_nestingData[" \""])); 21383 _nestingData[localeOpenQuote].opener = localeOpenQuote; 21384 _nestingData[localeOpenQuote].closer = localeCloseQuote; 21385 } 21386 21387 if (localeOpenInnerQuote && localeCloseInnerQuote && [" \""," \'","\"","\'"].indexOf(localeOpenInnerQuote) === -1) { 21388 _nestingData[localeOpenInnerQuote] = JSON.parse(JSON.stringify(_nestingData[" \'"])); 21389 _nestingData[localeOpenInnerQuote].opener = localeOpenInnerQuote; 21390 _nestingData[localeOpenInnerQuote].closer = localeCloseInnerQuote; 21391 } 21392 21393 function _setOuterQuoteForm(quot) { 21394 var flip = { 21395 " \'": " \"", 21396 " \"": " \'", 21397 "(\"": "(\'", 21398 "(\'": "(\"" 21399 }; 21400 _nestingData[quot].outer = "true"; 21401 _nestingData[flip[quot]].outer = "inner"; 21402 } 21403 21404 function _getNestingOpenerParams(opener) { 21405 var openers = []; 21406 var keys = Object.keys(_nestingData); 21407 for (var i = 0, l = keys.length; i < l; i++) { 21408 var key = keys[i]; 21409 if (_nestingData[opener].type !== "quote" || !_nestingData[opener]) { 21410 openers.push(key); 21411 } 21412 } 21413 var ret = _nestingData[opener]; 21414 ret.opener = new RegExp("^(?:" + openers.map(function(str){ 21415 return str.replace("(", "\\("); 21416 }).join("|") + ")"); 21417 return ret; 21418 } 21419 21420 var _nestingParams = (function() { 21421 var ret = {}; 21422 var keys = Object.keys(_nestingData); 21423 for (var i = 0, l = keys.length; i < l; i++) { 21424 var key = keys[i]; 21425 ret[key] = _getNestingOpenerParams(key); 21426 } 21427 return ret; 21428 }()); 21429 21430 var _tagRex = (function() { 21431 var openers = []; 21432 var closers = []; 21433 var vals = {}; 21434 for (var opener in _nestingParams) { 21435 openers.push(opener); 21436 vals[_nestingParams[opener].closer] = true; 21437 } 21438 var keys = Object.keys(vals); 21439 for (var i = 0, l = keys.length; i < l; i++) { 21440 var closer = keys[i]; 21441 closers.push(closer); 21442 } 21443 21444 var all = openers.concat(closers).map(function(str){ 21445 return str.replace("(", "\\("); 21446 }).join("|"); 21447 21448 return { 21449 matchAll: new RegExp("((?:" + all + "))", "g"), 21450 splitAll: new RegExp("(?:" + all + ")", "g"), 21451 open: new RegExp("(^(?:" + openers.map(function(str){ 21452 return str.replace("(", "\\("); 21453 }).join("|") + ")$)"), 21454 close: new RegExp("(^(?:" + closers.join("|") + ")$)"), 21455 }; 21456 }()); 21457 21458 function _tryOpen(tag, pos) { 21459 var params = _nestingState[_nestingState.length - 1]; 21460 if (!params || tag.match(params.opener)) { 21461 _nestingState.push({ 21462 type: _nestingParams[tag].type, 21463 opener: _nestingParams[tag].opener, 21464 closer: _nestingParams[tag].closer, 21465 pos: pos 21466 }); 21467 return false; 21468 } else { 21469 _nestingState.pop(); 21470 _nestingState.push({ 21471 type: _nestingParams[tag].type, 21472 opener: _nestingParams[tag].opener, 21473 closer: _nestingParams[tag].closer, 21474 pos: pos 21475 }); 21476 return { 21477 fixtag: params.pos 21478 }; 21479 } 21480 } 21481 21482 function _tryClose(tag, pos) { 21483 var params = _nestingState[_nestingState.length - 1]; 21484 if (params && tag === params.closer) { 21485 _nestingState.pop(); 21486 if (params.type === "nocase") { 21487 return { 21488 nocase: { 21489 open: params.pos, 21490 close: pos 21491 } 21492 }; 21493 } else { 21494 return false; 21495 } 21496 } else { 21497 if (params) { 21498 return { 21499 fixtag: params.pos 21500 }; 21501 } else { 21502 return { 21503 fixtag: pos 21504 }; 21505 } 21506 } 21507 } 21508 21509 function _pushNestingState(tag, pos) { 21510 if (tag.match(_tagRex.open)) { 21511 return _tryOpen(tag, pos); 21512 } else { 21513 return _tryClose(tag, pos); 21514 } 21515 } 21516 21517 function _doppelString(str) { 21518 var forcedSpaces = []; 21519 // Normalize markup 21520 str = str.replace(/(<span)\s+(style=\"font-variant:)\s*(small-caps);?\"[^>]*(>)/g, "$1 $2$3;\"$4"); 21521 str = str.replace(/(<span)\s+(class=\"no(?:case|decor)\")[^>]*(>)/g, "$1 $2$3"); 21522 21523 var match = str.match(_tagRex.matchAll); 21524 if (!match) { 21525 return { 21526 tags: [], 21527 strings: [str], 21528 forcedSpaces: [] 21529 }; 21530 } 21531 var split = str.split(_tagRex.splitAll); 21532 21533 for (var i=0,ilen=match.length-1;i<ilen;i++) { 21534 if (_nestingData[match[i]]) { 21535 if (split[i+1] === "" && ["\"", "'"].indexOf(match[i+1]) > -1) { 21536 match[i+1] = " " + match[i+1]; 21537 forcedSpaces.push(true); 21538 } else { 21539 forcedSpaces.push(false); 21540 } 21541 } 21542 } 21543 return { 21544 tags: match, 21545 strings: split, 21546 forcedSpaces: forcedSpaces 21547 }; 21548 } 21549 21550 var TagReg = function(blob) { 21551 var _stack = []; 21552 this.set = function (tag) { 21553 var attr = _nestingData[tag].attr; 21554 var decor = null; 21555 for (var i=_stack.length-1;i>-1;i--) { 21556 var _decor = _stack[i]; 21557 if (_decor[0] === attr) { 21558 decor = _decor; 21559 break; 21560 } 21561 } 21562 if (!decor) { 21563 var allTheDecor = [state[state.tmp.area].opt.layout_decorations].concat(blob.alldecor); 21564 outer: 21565 for (var i=allTheDecor.length-1;i>-1;i--) { 21566 var decorset = allTheDecor[i]; 21567 if (!decorset) { 21568 continue; 21569 } 21570 for (var j=decorset.length-1;j>-1;j--) { 21571 var _decor = decorset[j]; 21572 if (_decor[0] === attr) { 21573 decor = _decor; 21574 break outer; 21575 } 21576 } 21577 } 21578 } 21579 if (!decor) { 21580 decor = [attr, _nestingData[tag].outer]; 21581 } else { 21582 decor = [attr, _nestingData[tag].flipflop[decor[1]]]; 21583 } 21584 _stack.push(decor); 21585 }; 21586 this.pair = function () { 21587 return _stack[_stack.length-1]; 21588 }; 21589 this.pop = function () { 21590 _stack.pop(); 21591 }; 21592 }; 21593 21594 function _apostropheForce(tag, str) { 21595 if (tag === "\'") { 21596 if (str && str.match(/^[^\,\.\?\:\;\ ]/)) { 21597 return "\u2019"; 21598 } 21599 } else if (tag === " \'" && str && str.match(/^[\ ]/)) { 21600 return " \u2019"; 21601 } 21602 return false; 21603 } 21604 21605 function _undoppelToQueue(blob, doppel, leadingSpace) { 21606 var firstString = true; 21607 var tagReg = new TagReg(blob); 21608 blob.blobs = []; 21609 function Stack (blob) { 21610 this.stack = [blob]; 21611 this.latest = blob; 21612 this.addStyling = function(str, decor) { 21613 if (firstString) { 21614 if (str.slice(0, 1) === " ") { 21615 str = str.slice(1); 21616 } 21617 if (str.slice(0, 1) === " ") { 21618 str = str.slice(1); 21619 } 21620 firstString = false; 21621 } 21622 this.latest = this.stack[this.stack.length-1]; 21623 if (decor) { 21624 if ("string" === typeof this.latest.blobs) { 21625 var child = new CSL.Blob(); 21626 child.blobs = this.latest.blobs; 21627 child.alldecor = this.latest.alldecor.slice(); 21628 this.latest.blobs = [child]; 21629 } 21630 var tok = new CSL.Token(); 21631 var newblob = new CSL.Blob(null, tok); 21632 newblob.alldecor = this.latest.alldecor.slice(); 21633 21634 // AHA! Bad naming. There is _decorset from the list, and 21635 // there WAS decorset that we are building. Dumb. Fix the 21636 // names and fix it up. 21637 21638 if (decor[0] === "@class" && decor[1] === "nodecor") { 21639 var newdecorset = []; 21640 var seen = {}; 21641 var allTheDecor = [state[state.tmp.area].opt.layout_decorations].concat(newblob.alldecor); 21642 for (var i=allTheDecor.length-1;i>-1;i--) { 21643 var _decorset = allTheDecor[i]; 21644 if (!_decorset) { 21645 continue; 21646 } 21647 for (var j=_decorset.length-1;j>-1;j--) { 21648 var _olddecor = _decorset[j]; 21649 if (["@font-weight", "@font-style", "@font-variant"].indexOf(_olddecor[0]) > -1 21650 && !seen[_olddecor[0]]) { 21651 21652 if (decor[1] !== "normal") { 21653 newblob.decorations.push([_olddecor[0], "normal"]); 21654 newdecorset.push([_olddecor[0], "normal"]); 21655 } 21656 seen[_olddecor[0]] = true; 21657 } 21658 } 21659 } 21660 newblob.alldecor.push(newdecorset); 21661 21662 } else { 21663 newblob.decorations.push(decor); 21664 newblob.alldecor.push([decor]); 21665 } 21666 this.latest.blobs.push(newblob); 21667 this.stack.push(newblob); 21668 this.latest = newblob; 21669 if (str) { 21670 var tok = new CSL.Token(); 21671 var newblob = new CSL.Blob(null, tok); 21672 newblob.blobs = str; 21673 newblob.alldecor = this.latest.alldecor.slice(); 21674 this.latest.blobs.push(newblob); 21675 } 21676 } else { 21677 if (str) { 21678 var child = new CSL.Blob(); 21679 child.blobs = str; 21680 child.alldecor = this.latest.alldecor.slice(); 21681 this.latest.blobs.push(child); 21682 } 21683 } 21684 }; 21685 this.popStyling = function() { 21686 this.stack.pop(); 21687 }; 21688 } 21689 var stack = new Stack(blob); 21690 if (doppel.strings.length) { 21691 var str = doppel.strings[0]; 21692 if (leadingSpace) { 21693 str = " " + str; 21694 } 21695 stack.addStyling(str); 21696 } 21697 for (var i=0,ilen=doppel.tags.length;i<ilen;i++) { 21698 var tag = doppel.tags[i]; 21699 var str = doppel.strings[i+1]; 21700 if (tag.match(_tagRex.open)) { 21701 tagReg.set(tag); 21702 stack.addStyling(str, tagReg.pair()); 21703 } else { 21704 tagReg.pop(); 21705 stack.popStyling(); 21706 stack.addStyling(str); 21707 } 21708 } 21709 } 21710 21711 /** 21712 * PUBLIC 21713 */ 21714 21715 this.processTags = function (blob) { 21716 var str = blob.blobs; 21717 var leadingSpace = false; 21718 if (str.slice(0, 1) === " " && !str.match(/^\s+[\'\"]/)) { 21719 leadingSpace = true; 21720 } 21721 var rex = new RegExp("(" + CSL.ROMANESQUE_REGEXP.source + ")\u2019(" + CSL.ROMANESQUE_REGEXP.source + ")", "g"); 21722 var str = " " + str.replace(rex, "$1\'$2"); 21723 var doppel = _doppelString(str); 21724 if (doppel.tags.length === 0) { 21725 return; 21726 } 21727 var quoteFormSeen = false; 21728 21729 for (var i=0,ilen=doppel.tags.length;i<ilen;i++) { 21730 var tag = doppel.tags[i]; 21731 var str = doppel.strings[i+1]; 21732 var apostrophe = _apostropheForce(tag, str); 21733 if (apostrophe) { 21734 doppel.strings[i+1] = apostrophe + doppel.strings[i+1]; 21735 doppel.tags[i] = ""; 21736 } else { 21737 var tagInfo; 21738 while (true) { 21739 tagInfo = _pushNestingState(tag, i); 21740 if (tagInfo) { 21741 if (Object.keys(tagInfo).indexOf("fixtag") > -1) { 21742 if (tag.match(_tagRex.close) 21743 && tag === "\'") { 21744 21745 doppel.strings[i+1] = "\u2019" + doppel.strings[i+1]; 21746 doppel.tags[i] = ""; 21747 } else { 21748 var failedTag = doppel.tags[tagInfo.fixtag]; 21749 if (doppel.forcedSpaces[tagInfo.fixtag-1]) { 21750 failedTag = failedTag.slice(1); 21751 } 21752 doppel.strings[tagInfo.fixtag+1] = failedTag + doppel.strings[tagInfo.fixtag+1]; 21753 doppel.tags[tagInfo.fixtag] = ""; 21754 } 21755 if (_nestingState.length > 0) { 21756 if (tag !== "\'") { 21757 _nestingState.pop(); 21758 } else { 21759 break; 21760 } 21761 } else { 21762 break; 21763 } 21764 } else if (tagInfo.nocase) { 21765 doppel.tags[tagInfo.nocase.open] = ""; 21766 doppel.tags[tagInfo.nocase.close] = ""; 21767 break; 21768 } else { 21769 break; 21770 } 21771 } else { 21772 break; 21773 } 21774 } 21775 if (tagInfo && (tagInfo.fixtag|| tagInfo.fixtag === 0)) { 21776 doppel.strings[i+1] = doppel.tags[i] + doppel.strings[i+1]; 21777 doppel.tags[i] = ""; 21778 } 21779 } 21780 } 21781 // Stray tags are neutralized here 21782 for (var i=_nestingState.length-1;i>-1;i--) { 21783 var tagPos = _nestingState[i].pos; 21784 var tag = doppel.tags[tagPos]; 21785 if (tag === " \'" || tag === "\'") { 21786 21787 doppel.strings[tagPos+1] = " \u2019" + doppel.strings[tagPos+1]; 21788 } else { 21789 doppel.strings[tagPos+1] = doppel.tags[tagPos] + doppel.strings[tagPos+1]; 21790 } 21791 doppel.tags[tagPos] = ""; 21792 _nestingState.pop(); 21793 } 21794 for (var i=doppel.tags.length-1;i>-1;i--) { 21795 if (!doppel.tags[i]) { 21796 doppel.tags = doppel.tags.slice(0,i).concat(doppel.tags.slice(i+1)); 21797 doppel.strings[i] = doppel.strings[i] + doppel.strings[i+1]; 21798 doppel.strings = doppel.strings.slice(0,i+1).concat(doppel.strings.slice(i+2)); 21799 } 21800 } 21801 // Sniff initial (outer) quote form (single or double) and configure parser 21802 // Also add leading spaces. 21803 for (var i=0,ilen=doppel.tags.length;i<ilen;i++) { 21804 var tag = doppel.tags[i]; 21805 var forcedSpace = doppel.forcedSpaces[i-1]; 21806 if ([" \"", " \'", "(\"", "(\'"].indexOf(tag) > -1) { 21807 if (!quoteFormSeen) { 21808 _setOuterQuoteForm(tag); 21809 quoteFormSeen = true; 21810 } 21811 if (!forcedSpace) { 21812 doppel.strings[i] += tag.slice(0, 1); 21813 } 21814 } 21815 } 21816 //print(JSON.stringify(doppel, null, 2)) 21817 //print(_undoppelString(doppel)); 21818 _undoppelToQueue(blob, doppel, leadingSpace); 21819 }; 21820}; 21821 21822/*global CSL: true */ 21823 21824CSL.Output.Formatters = (function () { 21825 var rexStr = "(?:\u2018|\u2019|\u201C|\u201D| \"| \'|\"|\'|[-\u2013\u2014\/.,;?!:]|\\[|\\]|\\(|\\)|<span style=\"font-variant: small-caps;\">|<span class=\"no(?:case|decor)\">|<\/span>|<\/?(?:i|sc|b|sub|sup)>)"; 21826 var tagDoppel = new CSL.Doppeler(rexStr, function(str) { 21827 return str.replace(/(<span)\s+(class=\"no(?:case|decor)\")[^>]*(>)/g, "$1 $2$3").replace(/(<span)\s+(style=\"font-variant:)\s*(small-caps);?(\")[^>]*(>)/g, "$1 $2 $3;$4$5"); 21828 }); 21829 var rexNameStr = "(?:[-\\s]*<\\/*(?:span\s+class=\"no(?:case|decor)\"|i|sc|b|sub|sup)>[-\\s]*|[-\\s]+)"; 21830 var nameDoppel = new CSL.Doppeler(rexNameStr); 21831 21832 var wordDoppel = new CSL.Doppeler("(?:[\u00A0\u0020\u00A0\u2000-\u200B\u205F\u3000]+)"); 21833 21834 /** 21835 * INTERNAL 21836 */ 21837 21838 var _tagParams = { 21839 "<span style=\"font-variant: small-caps;\">": "</span>", 21840 "<span class=\"nocase\">": "</span>", 21841 "<span class=\"nodecor\">": "</span>", 21842 "<sc>": "</sc>", 21843 "<sub>": "</sub>", 21844 "<sup>": "</sup>" 21845 }; 21846 21847 function _capitalise (word) { 21848 // Weird stuff is (.) transpiled with regexpu 21849 // https://github.com/mathiasbynens/regexpu 21850 var m = word.match(/(^\s*)((?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))(.*)/); 21851 // Do not uppercase lone Greek letters 21852 // (No case transforms in Greek citations, but chars used in titles to science papers) 21853 if (m && !(m[2].match(/^[\u0370-\u03FF]$/) && !m[3])) { 21854 return m[1] + CSL.toLocaleUpperCase.call(this, m[2]) + m[3]; 21855 } 21856 return word; 21857 } 21858 21859 function _textcaseEngine(config, string) { 21860 if (!string) { 21861 return ""; 21862 } 21863 config.doppel = tagDoppel.split(string); 21864 var quoteParams = { 21865 " \"": { 21866 opener: " \'", 21867 closer: "\"" 21868 }, 21869 " \'": { 21870 opener: " \"", 21871 closer: "\'" 21872 }, 21873 "\u2018": { 21874 opener: "\u2018", 21875 closer: "\u2019" 21876 }, 21877 "\u201C": { 21878 opener: "\u201C", 21879 closer: "\u201D" 21880 }, 21881 }; 21882 function tryOpen(tag, pos) { 21883 if (config.quoteState.length === 0 || tag === config.quoteState[config.quoteState.length - 1].opener) { 21884 config.quoteState.push({ 21885 opener: quoteParams[tag].opener, 21886 closer: quoteParams[tag].closer, 21887 pos: pos 21888 }); 21889 return false; 21890 } else { 21891 var prevPos = config.quoteState[config.quoteState.length-1].pos; 21892 config.quoteState.pop(); 21893 config.quoteState.push({ 21894 opener: quoteParams[tag].opener, 21895 closer: quoteParams[tag].closer, 21896 positions: pos 21897 }); 21898 return prevPos; 21899 } 21900 } 21901 function tryClose(tag, pos) { 21902 if (config.quoteState.length > 0 && tag === config.quoteState[config.quoteState.length - 1].closer) { 21903 config.quoteState.pop(); 21904 } else { 21905 return pos; 21906 } 21907 } 21908 function pushQuoteState(tag, pos) { 21909 var isOpener = ["\u201C", "\u2018", " \"", " \'"].indexOf(tag) > -1 ? true : false; 21910 if (isOpener) { 21911 return tryOpen(tag, pos); 21912 } else { 21913 return tryClose(tag, pos); 21914 } 21915 } 21916 function quoteFix (tag, positions) { 21917 var m = tag.match(/(^(?:\u2018|\u2019|\u201C|\u201D|\"|\')|(?: \"| \')$)/); 21918 if (m) { 21919 return pushQuoteState(m[1], positions); 21920 } 21921 } 21922 // Run state machine 21923 if (config.doppel.strings.length && config.doppel.strings[0].trim()) { 21924 config.doppel.strings[0] = config.capitaliseWords(config.doppel.strings[0], 0, config.doppel.tags[0]); 21925 } 21926 21927 for (var i=0,ilen=config.doppel.tags.length;i<ilen;i++) { 21928 var tag = config.doppel.tags[i]; 21929 var str = config.doppel.strings[i+1]; 21930 21931 if (config.tagState !== null) { 21932 // Evaluate tag state for current string 21933 if (_tagParams[tag]) { 21934 config.tagState.push(_tagParams[tag]); 21935 } else if (config.tagState.length && tag === config.tagState[config.tagState.length - 1]) { 21936 config.tagState.pop(); 21937 } 21938 } 21939 21940 if (config.afterPunct !== null) { 21941 // Evaluate punctuation state of current string 21942 if (tag.match(/[\!\?\:]$/)) { 21943 config.afterPunct = true; 21944 } 21945 } 21946 21947 // Process if outside tag scope, else noop for upper-casing 21948 if (config.tagState.length === 0) { 21949 config.doppel.strings[i+1] = config.capitaliseWords(str, i+1, config.doppel,config.doppel.tags[i+1]); 21950 21951 } else if (config.doppel.strings[i+1].trim()) { 21952 config.lastWordPos = null; 21953 } 21954 21955 if (config.quoteState !== null) { 21956 // Evaluate quote state of current string and fix chars that have flown 21957 var quotePos = quoteFix(tag, i); 21958 if (quotePos || quotePos === 0) { 21959 var origChar = config.doppel.origStrings[quotePos+1].slice(0, 1); 21960 config.doppel.strings[quotePos+1] = origChar + config.doppel.strings[quotePos+1].slice(1); 21961 config.lastWordPos = null; 21962 } 21963 } 21964 21965 // If there was a printable string, unset first-word and after-punctuation 21966 if (config.isFirst) { 21967 if (str.trim()) { 21968 config.isFirst = false; 21969 } 21970 } 21971 if (config.afterPunct) { 21972 if (str.trim()) { 21973 config.afterPunct = false; 21974 } 21975 } 21976 } 21977 if (config.quoteState) { 21978 for (var i=0,ilen=config.quoteState.length;i<ilen;i++) { 21979 var quotePos = config.quoteState[i].pos; 21980 // Test for quotePos avoids a crashing error: 21981 // https://github.com/citation-style-language/test-suite/blob/master/processor-tests/humans/flipflop_OrphanQuote.txt 21982 if (typeof quotePos !== 'undefined') { 21983 var origChar = config.doppel.origStrings[quotePos+1].slice(0, 1); 21984 config.doppel.strings[quotePos+1] = origChar + config.doppel.strings[quotePos+1].slice(1); 21985 } 21986 } 21987 } 21988 // Specially capitalize the last word if necessary (invert stop-word list) 21989 if (config.lastWordPos) { 21990 var lastWords = wordDoppel.split(config.doppel.strings[config.lastWordPos.strings]); 21991 var lastWord = lastWords.strings[config.lastWordPos.words]; 21992 if (lastWord.length > 1 && CSL.toLocaleLowerCase.call(this, lastWord).match(config.skipWordsRex)) { 21993 lastWord = _capitalise.call(this, lastWord); 21994 lastWords.strings[config.lastWordPos.words] = lastWord; 21995 } 21996 config.doppel.strings[config.lastWordPos.strings] = wordDoppel.join(lastWords); 21997 } 21998 21999 // Recombine the string 22000 return tagDoppel.join(config.doppel); 22001 } 22002 22003 /** 22004 * PUBLIC 22005 */ 22006 22007 /** 22008 * A noop that just delivers the string. 22009 */ 22010 function passthrough (state, str) { 22011 return str; 22012 } 22013 22014 /** 22015 * Force all letters in the string to lowercase, skipping nocase spans 22016 */ 22017 function lowercase(state, string) { 22018 var config = { 22019 quoteState: null, 22020 capitaliseWords: function(str) { 22021 var words = str.split(" "); 22022 for (var i=0,ilen=words.length;i<ilen;i++) { 22023 var word = words[i]; 22024 if (word) { 22025 words[i] = CSL.toLocaleLowerCase.call(state, word); 22026 } 22027 } 22028 return words.join(" "); 22029 }, 22030 skipWordsRex: null, 22031 tagState: [], 22032 afterPunct: null, 22033 isFirst: null 22034 }; 22035 return _textcaseEngine.call(state, config, string); 22036 } 22037 22038 /** 22039 * Force all letters in the string to uppercase. 22040 */ 22041 function uppercase(state, string) { 22042 var config = { 22043 quoteState: null, 22044 capitaliseWords: function(str) { 22045 var words = str.split(" "); 22046 for (var i=0,ilen=words.length;i<ilen;i++) { 22047 var word = words[i]; 22048 if (word) { 22049 // Okay. 22050 // So we need to pick up an array of locales from state.tmp. 22051 // This function is invoked in the context of queue.js, so 22052 // the item is not available here. Three levels to be included 22053 // in the array: 22054 // 1. Field language tag, if any 22055 // 2. Item language tag, if any 22056 // 3. Value of state.opt.lang 22057 words[i] = CSL.toLocaleUpperCase.call(state, word); 22058 } 22059 } 22060 return words.join(" "); 22061 }, 22062 skipWordsRex: null, 22063 tagState: [], 22064 afterPunct: null, 22065 isFirst: null 22066 }; 22067 return _textcaseEngine.call(state, config, string); 22068 } 22069 22070 /** 22071 * Similar to <b>capitalize_first</b>, but force the 22072 * subsequent characters to lowercase. 22073 */ 22074 function sentence(state, string) { 22075 var config = { 22076 quoteState: [], 22077 capitaliseWords: function(str) { 22078 var words = str.split(" "); 22079 for (var i=0,ilen=words.length;i<ilen;i++) { 22080 var word = words[i]; 22081 if (word) { 22082 if (config.isFirst) { 22083 words[i] = _capitalise.call(state, word); 22084 config.isFirst = false; 22085 } else { 22086 words[i] = CSL.toLocaleLowerCase.call(state, word); 22087 } 22088 } 22089 } 22090 return words.join(" "); 22091 }, 22092 skipWordsRex: null, 22093 tagState: [], 22094 afterPunct: null, 22095 isFirst: true 22096 }; 22097 return _textcaseEngine.call(state, config, string); 22098 } 22099 22100 function title(state, string) { 22101 var config = { 22102 quoteState: [], 22103 capitaliseWords: function(str, i, followingTag) { 22104 if (str.trim()) { 22105 var wordle = wordDoppel.split(str); 22106 var words = wordle.strings; 22107 for (var j=0,jlen=words.length;j<jlen;j++) { 22108 var word = words[j]; 22109 if (!word) { 22110 continue; 22111 } 22112 let lcase = CSL.toLocaleLowerCase.call(state, word); 22113 let capitalize = false; 22114 if (word.length > 1 && !lcase.match(config.skipWordsRex)) { 22115 // Capitalize every word that is not a stop-word 22116 capitalize = true; 22117 } else if (j === (words.length - 1) && followingTag === "-") { 22118 capitalize = true; 22119 } else if (config.isFirst) { 22120 // Capitalize first word, even if a stop-word 22121 capitalize = true; 22122 } else if (config.afterPunct) { 22123 // Capitalize after punctuation 22124 capitalize = true; 22125 } 22126 // Don't capitalize if word already contains capitalization 22127 if (capitalize && word === lcase) { 22128 words[j] = _capitalise.call(state, word); 22129 } 22130 config.afterPunct = false; 22131 config.isFirst = false; 22132 config.lastWordPos = { 22133 strings: i, 22134 words: j 22135 }; 22136 } 22137 str = wordDoppel.join(wordle); 22138 } 22139 return str; 22140 }, 22141 skipWordsRex: state.locale[state.opt.lang].opts["skip-words-regexp"], 22142 tagState: [], 22143 afterPunct: false, 22144 isFirst: true 22145 }; 22146 return _textcaseEngine.call(state, config, string); 22147 } 22148 22149 22150 /** 22151 * Force capitalization of the first letter in the string, leave 22152 * the rest of the characters untouched. 22153 */ 22154 function capitalizeFirst(state, string) { 22155 var config = { 22156 quoteState: [], 22157 capitaliseWords: function(str) { 22158 var wordle = wordDoppel.split(str); 22159 var words = wordle.strings; 22160 for (var i=0,ilen=words.length;i<ilen;i++) { 22161 var word = words[i]; 22162 if (word) { 22163 if (config.isFirst) { 22164 // Don't capitalize if word already contains capitalization 22165 if (word === CSL.toLocaleLowerCase.call(state, word)) { 22166 words[i] = _capitalise.call(state, word); 22167 } 22168 config.isFirst = false; 22169 break; 22170 } 22171 } 22172 } 22173 return wordDoppel.join(wordle); 22174 }, 22175 skipWordsRex: null, 22176 tagState: [], 22177 afterPunct: null, 22178 isFirst: true 22179 }; 22180 return _textcaseEngine.call(state, config, string); 22181 } 22182 22183 /** 22184 * Force the first letter of each space-delimited 22185 * word in the string to uppercase, and leave the remainder 22186 * of the string untouched. Single characters are forced 22187 * to uppercase. 22188 */ 22189 function capitalizeAll (state, string) { 22190 var config = { 22191 quoteState: [], 22192 capitaliseWords: function(str) { 22193 var wordle = wordDoppel.split(str); 22194 var words = wordle.strings; 22195 for (var i=0,ilen=words.length;i<ilen;i++) { 22196 var word = words[i]; 22197 if (word) { 22198 // Don't capitalize if word already contains capitalization 22199 if (word === CSL.toLocaleLowerCase.call(state, word)) { 22200 words[i] = _capitalise.call(state, word); 22201 } 22202 } 22203 } 22204 return wordDoppel.join(wordle); 22205 }, 22206 skipWordsRex: null, 22207 tagState: [], 22208 afterPunct: null, 22209 isFirst: null 22210 }; 22211 return _textcaseEngine.call(state, config, string); 22212 } 22213 return { 22214 nameDoppel: nameDoppel, 22215 passthrough: passthrough, 22216 lowercase: lowercase, 22217 uppercase: uppercase, 22218 sentence: sentence, 22219 title: title, 22220 "capitalize-first": capitalizeFirst, 22221 "capitalize-all": capitalizeAll 22222 }; 22223}()); 22224 22225/*global CSL: true */ 22226 22227 22228/** 22229 * Output specifications. 22230 * @class 22231 */ 22232CSL.Output.Formats = function () {}; 22233 22234/** 22235 * HTML output format specification. 22236 * <p>The headline says it all. The source code for this 22237 * object can be used as a template for producing other 22238 * output modes.</p> 22239 */ 22240CSL.Output.Formats.prototype.html = { 22241 // 22242 // text_escape: Format-specific function for escaping text destined 22243 // for output. Takes the text to be escaped as sole argument. Function 22244 // will be run only once across each portion of text to be escaped, it 22245 // need not be idempotent. 22246 // 22247 "text_escape": function (text) { 22248 // Numeric entities, in case the output is processed as 22249 // xml in an environment in which HTML named entities are 22250 // not declared. 22251 if (!text) { 22252 text = ""; 22253 } 22254 return text.replace(/&/g, "&") 22255 .replace(/</g, "<") 22256 .replace(/>/g, ">") 22257 .replace(/\s\s/g, "\u00A0 ") 22258 .replace(CSL.SUPERSCRIPTS_REGEXP, 22259 function(aChar) { 22260 // return "<sup>" + CSL.SUPERSCRIPTS[aChar] + "</sup>"; 22261 return "<sup>" + CSL.SUPERSCRIPTS[aChar] + "</sup>"; 22262 }); 22263 }, 22264 "bibstart": "<div class=\"csl-bib-body\">\n", 22265 "bibend": "</div>", 22266 "@font-style/italic": "<i>%%STRING%%</i>", 22267 "@font-style/oblique": "<em>%%STRING%%</em>", 22268 "@font-style/normal": "<span style=\"font-style:normal;\">%%STRING%%</span>", 22269 "@font-variant/small-caps": "<span style=\"font-variant:small-caps;\">%%STRING%%</span>", 22270 "@passthrough/true": CSL.Output.Formatters.passthrough, 22271 "@font-variant/normal": "<span style=\"font-variant:normal;\">%%STRING%%</span>", 22272 "@font-weight/bold": "<b>%%STRING%%</b>", 22273 "@font-weight/normal": "<span style=\"font-weight:normal;\">%%STRING%%</span>", 22274 "@font-weight/light": false, 22275 "@text-decoration/none": "<span style=\"text-decoration:none;\">%%STRING%%</span>", 22276 "@text-decoration/underline": "<span style=\"text-decoration:underline;\">%%STRING%%</span>", 22277 "@vertical-align/sup": "<sup>%%STRING%%</sup>", 22278 "@vertical-align/sub": "<sub>%%STRING%%</sub>", 22279 "@vertical-align/baseline": "<span style=\"baseline\">%%STRING%%</span>", 22280 "@strip-periods/true": CSL.Output.Formatters.passthrough, 22281 "@strip-periods/false": CSL.Output.Formatters.passthrough, 22282 "@quotes/true": function (state, str) { 22283 if ("undefined" === typeof str) { 22284 return state.getTerm("open-quote"); 22285 } 22286 return state.getTerm("open-quote") + str + state.getTerm("close-quote"); 22287 }, 22288 "@quotes/inner": function (state, str) { 22289 if ("undefined" === typeof str) { 22290 // 22291 // Mostly right by being wrong (for apostrophes) 22292 // 22293 return "\u2019"; 22294 } 22295 return state.getTerm("open-inner-quote") + str + state.getTerm("close-inner-quote"); 22296 }, 22297 "@quotes/false": false, 22298 //"@bibliography/body": function (state,str){ 22299 // return "<div class=\"csl-bib-body\">\n"+str+"</div>"; 22300 //}, 22301 "@cite/entry": function (state, str) { 22302 return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt); 22303 }, 22304 "@bibliography/entry": function (state, str) { 22305 // Test for this.item_id to add decorations to 22306 // bibliography output of individual entries. 22307 // 22308 // Full item content can be obtained from 22309 // state.registry.registry[id].ref, using 22310 // CSL variable keys. 22311 // 22312 // Example: 22313 // 22314 // print(state.registry.registry[this.item_id].ref["title"]); 22315 // 22316 // At present, for parallel citations, only the 22317 // id of the master item is supplied on this.item_id. 22318 var insert = ""; 22319 if (state.sys.embedBibliographyEntry) { 22320 insert = state.sys.embedBibliographyEntry(this.item_id) + "\n"; 22321 } 22322 return " <div class=\"csl-entry\">" + str + "</div>\n" + insert; 22323 }, 22324 "@display/block": function (state, str) { 22325 return "\n\n <div class=\"csl-block\">" + str + "</div>\n"; 22326 }, 22327 "@display/left-margin": function (state, str) { 22328 return "\n <div class=\"csl-left-margin\">" + str + "</div>"; 22329 }, 22330 "@display/right-inline": function (state, str) { 22331 return "<div class=\"csl-right-inline\">" + str + "</div>\n "; 22332 }, 22333 "@display/indent": function (state, str) { 22334 return "<div class=\"csl-indent\">" + str + "</div>\n "; 22335 }, 22336 "@showid/true": function (state, str, cslid) { 22337 if (!state.tmp.just_looking && ! state.tmp.suppress_decorations) { 22338 if (cslid) { 22339 return "<span class=\"" + state.opt.nodenames[cslid] + "\" cslid=\"" + cslid + "\">" + str + "</span>"; 22340 } else if (this.params && "string" === typeof str) { 22341 var prePunct = ""; 22342 if (str) { 22343 var m = str.match(CSL.VARIABLE_WRAPPER_PREPUNCT_REX); 22344 prePunct = m[1]; 22345 str = m[2]; 22346 } 22347 var postPunct = ""; 22348 if (str && CSL.SWAPPING_PUNCTUATION.indexOf(str.slice(-1)) > -1) { 22349 postPunct = str.slice(-1); 22350 str = str.slice(0,-1); 22351 } 22352 return state.sys.variableWrapper(this.params, prePunct, str, postPunct); 22353 } else { 22354 return str; 22355 } 22356 } else { 22357 return str; 22358 } 22359 }, 22360 "@URL/true": function (state, str) { 22361 return "<a href=\"" + str + "\">" + str + "</a>"; 22362 }, 22363 "@DOI/true": function (state, str) { 22364 var doiurl = str; 22365 if (!str.match(/^https?:\/\//)) { 22366 doiurl = "https://doi.org/" + str; 22367 } 22368 return "<a href=\"" + doiurl + "\">" + str + "</a>"; 22369 } 22370}; 22371 22372/** 22373 * Plain text output specification. 22374 * 22375 * (Code contributed by Simon Kornblith, Center for History and New Media, 22376 * George Mason University.) 22377 */ 22378CSL.Output.Formats.prototype.text = { 22379 // 22380 // text_escape: Format-specific function for escaping text destined 22381 // for output. Takes the text to be escaped as sole argument. Function 22382 // will be run only once across each portion of text to be escaped, it 22383 // need not be idempotent. 22384 // 22385 "text_escape": function (text) { 22386 if (!text) { 22387 text = ""; 22388 } 22389 return text; 22390 }, 22391 "bibstart": "", 22392 "bibend": "", 22393 "@font-style/italic": false, 22394 "@font-style/oblique": false, 22395 "@font-style/normal": false, 22396 "@font-variant/small-caps": false, 22397 "@passthrough/true": CSL.Output.Formatters.passthrough, 22398 "@font-variant/normal": false, 22399 "@font-weight/bold": false, 22400 "@font-weight/normal": false, 22401 "@font-weight/light": false, 22402 "@text-decoration/none": false, 22403 "@text-decoration/underline": false, 22404 "@vertical-align/baseline": false, 22405 "@vertical-align/sup": false, 22406 "@vertical-align/sub": false, 22407 "@strip-periods/true": CSL.Output.Formatters.passthrough, 22408 "@strip-periods/false": CSL.Output.Formatters.passthrough, 22409 "@quotes/true": function (state, str) { 22410 if ("undefined" === typeof str) { 22411 return state.getTerm("open-quote"); 22412 } 22413 return state.getTerm("open-quote") + str + state.getTerm("close-quote"); 22414 }, 22415 "@quotes/inner": function (state, str) { 22416 if ("undefined" === typeof str) { 22417 // 22418 // Mostly right by being wrong (for apostrophes) 22419 // 22420 return "\u2019"; 22421 } 22422 return state.getTerm("open-inner-quote") + str + state.getTerm("close-inner-quote"); 22423 }, 22424 "@quotes/false": false, 22425 //"@bibliography/body": function (state,str){ 22426 // return "<div class=\"csl-bib-body\">\n"+str+"</div>"; 22427 //}, 22428 "@cite/entry": function (state, str) { 22429 return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt); 22430 }, 22431 "@bibliography/entry": function (state, str) { 22432 return str+"\n"; 22433 }, 22434 "@display/block": function (state, str) { 22435 return "\n"+str; 22436 }, 22437 "@display/left-margin": function (state, str) { 22438 return str + " "; 22439 }, 22440 "@display/right-inline": function (state, str) { 22441 return str; 22442 }, 22443 "@display/indent": function (state, str) { 22444 return "\n "+str; 22445 }, 22446 "@showid/true": function (state, str) { 22447 return str; 22448 }, 22449 "@URL/true": function (state, str) { 22450 return str; 22451 }, 22452 "@DOI/true": function (state, str) { 22453 return str; 22454 } 22455}; 22456 22457/** 22458 * Plain text output specification. 22459 * 22460 * (Code contributed by Simon Kornblith, Center for History and New Media, 22461 * George Mason University.) 22462 */ 22463CSL.Output.Formats.prototype.rtf = { 22464 // 22465 // text_escape: Format-specific function for escaping text destined 22466 // for output. Takes the text to be escaped as sole argument. Function 22467 // will be run only once across each portion of text to be escaped, it 22468 // need not be idempotent. 22469 // 22470 "text_escape": function (text) { 22471 if (!text) { 22472 text = ""; 22473 } 22474 return text 22475 .replace(/([\\{}])/g, "\\$1") 22476 .replace(CSL.SUPERSCRIPTS_REGEXP, 22477 function(aChar) { 22478 return "\\super " + CSL.SUPERSCRIPTS[aChar] + "\\nosupersub{}"; 22479 }) 22480 .replace(/[\u007F-\uFFFF]/g, 22481 function(aChar) { return "\\uc0\\u"+aChar.charCodeAt(0).toString()+"{}"; }) 22482 .split("\t").join("\\tab{}"); 22483 }, 22484 "@passthrough/true": CSL.Output.Formatters.passthrough, 22485 "@font-style/italic":"{\\i{}%%STRING%%}", 22486 "@font-style/normal":"{\\i0{}%%STRING%%}", 22487 "@font-style/oblique":"{\\i{}%%STRING%%}", 22488 "@font-variant/small-caps":"{\\scaps %%STRING%%}", 22489 "@font-variant/normal":"{\\scaps0{}%%STRING%%}", 22490 "@font-weight/bold":"{\\b{}%%STRING%%}", 22491 "@font-weight/normal":"{\\b0{}%%STRING%%}", 22492 "@font-weight/light":false, 22493 "@text-decoration/none":false, 22494 "@text-decoration/underline":"{\\ul{}%%STRING%%}", 22495 "@vertical-align/baseline":false, 22496 "@vertical-align/sup":"\\super %%STRING%%\\nosupersub{}", 22497 "@vertical-align/sub":"\\sub %%STRING%%\\nosupersub{}", 22498 "@strip-periods/true": CSL.Output.Formatters.passthrough, 22499 "@strip-periods/false": CSL.Output.Formatters.passthrough, 22500 "@quotes/true": function (state, str) { 22501 if ("undefined" === typeof str) { 22502 return CSL.Output.Formats.rtf.text_escape(state.getTerm("open-quote")); 22503 } 22504 return CSL.Output.Formats.rtf.text_escape(state.getTerm("open-quote")) + str + CSL.Output.Formats.rtf.text_escape(state.getTerm("close-quote")); 22505 }, 22506 "@quotes/inner": function (state, str) { 22507 if ("undefined" === typeof str) { 22508 return CSL.Output.Formats.rtf.text_escape("\u2019"); 22509 } 22510 return CSL.Output.Formats.rtf.text_escape(state.getTerm("open-inner-quote")) + str + CSL.Output.Formats.rtf.text_escape(state.getTerm("close-inner-quote")); 22511 }, 22512 "@quotes/false": false, 22513 "bibstart":"{\\rtf ", 22514 "bibend":"}", 22515 "@display/block": "\\line{}%%STRING%%\\line\r\n", 22516 "@cite/entry": function (state, str) { 22517 // If wrapCitationEntry does not exist, cite/entry 22518 // is not applied. 22519 return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt); 22520 }, 22521 "@bibliography/entry": function(state,str){ 22522 return str; 22523 }, 22524 "@display/left-margin": function(state,str){ 22525 return str+"\\tab "; 22526 }, 22527 "@display/right-inline": function (state, str) { 22528 return str+"\r\n"; 22529 }, 22530 "@display/indent": function (state, str) { 22531 return "\n\\tab "+str+"\\line\r\n"; 22532 }, 22533 "@showid/true": function (state, str) { 22534 if (!state.tmp.just_looking && ! state.tmp.suppress_decorations) { 22535 var prePunct = ""; 22536 if (str) { 22537 var m = str.match(CSL.VARIABLE_WRAPPER_PREPUNCT_REX); 22538 prePunct = m[1]; 22539 str = m[2]; 22540 } 22541 var postPunct = ""; 22542 if (str && CSL.SWAPPING_PUNCTUATION.indexOf(str.slice(-1)) > -1) { 22543 postPunct = str.slice(-1); 22544 str = str.slice(0,-1); 22545 } 22546 return state.sys.variableWrapper(this.params, prePunct, str, postPunct); 22547 } else { 22548 return str; 22549 } 22550 }, 22551 "@URL/true": function (state, str) { 22552 return str; 22553 }, 22554 "@DOI/true": function (state, str) { 22555 return str; 22556 } 22557}; 22558 22559/* 22560 22561 This does not seem to work in Zotero plugins. For some reason the scope of the link does not 22562 close when interpreted by the LibreOffice. Perhaps this creates a field within a field, 22563 and that is not allowed? 22564 22565 "@URL/true": function (state, str) { 22566 return "\\field{\\*\\fldinst{HYPERLINK \"" + str + "\"}}{\\fldrslt{"+ str +"}}"; 22567 }, 22568 "@DOI/true": function (state, str) { 22569 return "\\field{\\*\\fldinst{HYPERLINK \"https://doi.org/" + str + "\"}}{\\fldrslt{"+ str +"}}"; 22570 } 22571*/ 22572 22573/** 22574 * AsciiDoc output specification. 22575 * 22576 * See http://asciidoc.org/ or https://asciidoctor.org/ 22577 */ 22578CSL.Output.Formats.prototype.asciidoc = { 22579 "text_escape": function (text) { 22580 if (!text) { 22581 text = ""; 22582 } 22583 return text.replace("*", "pass:[*]", "g") 22584 .replace("_", "pass:[_]", "g") 22585 .replace("#", "pass:[#]", "g") 22586 .replace("^", "pass:[^]", "g") 22587 .replace("~", "pass:[~]", "g") 22588 .replace("[[", "pass:[[[]", "g") 22589 .replace(" ", "  ", "g") 22590 .replace(CSL.SUPERSCRIPTS_REGEXP, function(aChar) { 22591 return "^" + CSL.SUPERSCRIPTS[aChar] + "^"; 22592 }); 22593 }, 22594 "bibstart": "", 22595 "bibend": "", 22596 "@passthrough/true": CSL.Output.Formatters.passthrough, 22597 "@font-style/italic": "__%%STRING%%__", 22598 "@font-style/oblique": "__%%STRING%%__", 22599 "@font-style/normal": false, 22600 "@font-variant/small-caps": "[small-caps]#%%STRING%%#", 22601 "@font-variant/normal": false, 22602 "@font-weight/bold": "**%%STRING%%**", 22603 "@font-weight/normal": false, 22604 "@font-weight/light": false, 22605 "@text-decoration/none": false, 22606 "@text-decoration/underline": "[underline]##%%STRING%%##", 22607 "@vertical-align/sup": "^^%%STRING%%^^", 22608 "@vertical-align/sub": "~~%%STRING%%~~", 22609 "@vertical-align/baseline": false, 22610 "@strip-periods/true": CSL.Output.Formatters.passthrough, 22611 "@strip-periods/false": CSL.Output.Formatters.passthrough, 22612 "@quotes/true": function (state, str) { 22613 if ("undefined" === typeof str) { 22614 return "``"; 22615 } 22616 return "``" + str + "''"; 22617 }, 22618 "@quotes/inner": function (state, str) { 22619 if ("undefined" === typeof str) { 22620 return "`"; 22621 } 22622 return "`" + str + "'"; 22623 }, 22624 "@quotes/false": false, 22625 "@cite/entry": function (state, str) { 22626 // if wrapCitationEntry does not exist, cite/entry is not applied 22627 return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt); 22628 }, 22629 "@bibliography/entry": function (state, str) { 22630 return str + "\n"; 22631 }, 22632 "@display/block": function (state, str) { 22633 return str; 22634 }, 22635 "@display/left-margin": function (state, str) { 22636 return str; 22637 }, 22638 "@display/right-inline": function (state, str) { 22639 return " " + str; 22640 }, 22641 "@display/indent": function (state, str) { 22642 return " " + str; 22643 }, 22644 "@showid/true": function (state, str) { 22645 if (!state.tmp.just_looking && !state.tmp.suppress_decorations && this.params && "string" === typeof str) { 22646 var prePunct = ""; 22647 if (str) { 22648 var m = str.match(CSL.VARIABLE_WRAPPER_PREPUNCT_REX); 22649 prePunct = m[1]; 22650 str = m[2]; 22651 } 22652 var postPunct = ""; 22653 if (str && CSL.SWAPPING_PUNCTUATION.indexOf(str.slice(-1)) > -1) { 22654 postPunct = str.slice(-1); 22655 str = str.slice(0,-1); 22656 } 22657 return state.sys.variableWrapper(this.params, prePunct, str, postPunct); 22658 } else { 22659 return str; 22660 } 22661 }, 22662 "@URL/true": function (state, str) { 22663 // AsciiDoc renders URLs automatically as links 22664 return str; 22665 }, 22666 "@DOI/true": function (state, str) { 22667 var doiurl = str; 22668 if (!str.match(/^https?:\/\//)) { 22669 doiurl = "https://doi.org/" + str; 22670 } 22671 return doiurl + "[" + str + "]"; 22672 } 22673}; 22674 22675/** 22676 * Output specification for XSL-FO (Extensible Stylesheet 22677 * Language - Formatting Objects) 22678 * 22679 * See https://www.w3.org/TR/xsl11/#fo-section 22680 */ 22681CSL.Output.Formats.prototype.fo = { 22682 "text_escape": function (text) { 22683 if (!text) { 22684 text = ""; 22685 } 22686 return text.replace(/&/g, "&") 22687 .replace(/</g, "<") 22688 .replace(/>/g, ">") 22689 .replace(" ", "  ", "g") 22690 .replace(CSL.SUPERSCRIPTS_REGEXP, function(aChar) { 22691 return "<fo:inline vertical-align=\"super\">" + CSL.SUPERSCRIPTS[aChar] + "</fo:inline>"; 22692 }); 22693 }, 22694 "bibstart": "", 22695 "bibend": "", 22696 "@passthrough/true": CSL.Output.Formatters.passthrough, 22697 "@font-style/italic": "<fo:inline font-style=\"italic\">%%STRING%%</fo:inline>", 22698 "@font-style/oblique": "<fo:inline font-style=\"oblique\">%%STRING%%</fo:inline>", 22699 "@font-style/normal": "<fo:inline font-style=\"normal\">%%STRING%%</fo:inline>", 22700 "@font-variant/small-caps": "<fo:inline font-variant=\"small-caps\">%%STRING%%</fo:inline>", 22701 "@font-variant/normal": "<fo:inline font-variant=\"normal\">%%STRING%%</fo:inline>", 22702 "@font-weight/bold": "<fo:inline font-weight=\"bold\">%%STRING%%</fo:inline>", 22703 "@font-weight/normal": "<fo:inline font-weight=\"normal\">%%STRING%%</fo:inline>", 22704 "@font-weight/light": "<fo:inline font-weight=\"lighter\">%%STRING%%</fo:inline>", 22705 "@text-decoration/none": "<fo:inline text-decoration=\"none\">%%STRING%%</fo:inline>", 22706 "@text-decoration/underline": "<fo:inline text-decoration=\"underline\">%%STRING%%</fo:inline>", 22707 "@vertical-align/sup": "<fo:inline vertical-align=\"super\">%%STRING%%</fo:inline>", 22708 "@vertical-align/sub": "<fo:inline vertical-align=\"sub\">%%STRING%%</fo:inline>", 22709 "@vertical-align/baseline": "<fo:inline vertical-align=\"baseline\">%%STRING%%</fo:inline>", 22710 "@strip-periods/true": CSL.Output.Formatters.passthrough, 22711 "@strip-periods/false": CSL.Output.Formatters.passthrough, 22712 "@quotes/true": function (state, str) { 22713 if ("undefined" === typeof str) { 22714 return state.getTerm("open-quote"); 22715 } 22716 return state.getTerm("open-quote") + str + state.getTerm("close-quote"); 22717 }, 22718 "@quotes/inner": function (state, str) { 22719 if ("undefined" === typeof str) { 22720 return "\u2019"; 22721 } 22722 return state.getTerm("open-inner-quote") + str + state.getTerm("close-inner-quote"); 22723 }, 22724 "@quotes/false": false, 22725 "@cite/entry": function (state, str) { 22726 return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt); 22727 }, 22728 "@bibliography/entry": function (state, str) { 22729 var indent = ""; 22730 if (state.bibliography && state.bibliography.opt && state.bibliography.opt.hangingindent) { 22731 var hi = state.bibliography.opt.hangingindent; 22732 indent = " start-indent=\"" + hi +"em\" text-indent=\"-" + hi + "em\""; 22733 } 22734 var insert = ""; 22735 if (state.sys.embedBibliographyEntry) { 22736 insert = state.sys.embedBibliographyEntry(this.item_id) + "\n"; 22737 } 22738 return "<fo:block id=\"" + this.system_id + "\"" + indent + ">" + str + "</fo:block>\n" + insert; 22739 }, 22740 "@display/block": function (state, str) { 22741 return "\n <fo:block>" + str + "</fo:block>\n"; 22742 }, 22743 "@display/left-margin": function (state, str) { 22744 return "\n <fo:table table-layout=\"fixed\" width=\"100%\">\n " + 22745 "<fo:table-column column-number=\"1\" column-width=\"$$$__COLUMN_WIDTH_1__$$$\"/>\n " + 22746 "<fo:table-column column-number=\"2\" column-width=\"proportional-column-width(1)\"/>\n " + 22747 "<fo:table-body>\n " + 22748 "<fo:table-row>\n " + 22749 "<fo:table-cell>\n " + 22750 "<fo:block>" + str + "</fo:block>\n " + 22751 "</fo:table-cell>\n "; 22752 }, 22753 "@display/right-inline": function (state, str) { 22754 return "<fo:table-cell>\n " + 22755 "<fo:block>" + str + "</fo:block>\n " + 22756 "</fo:table-cell>\n " + 22757 "</fo:table-row>\n " + 22758 "</fo:table-body>\n " + 22759 "</fo:table>\n"; 22760 }, 22761 "@display/indent": function (state, str) { 22762 return "<fo:block margin-left=\"2em\">" + str + "</fo:block>\n"; 22763 }, 22764 "@showid/true": function (state, str) { 22765 if (!state.tmp.just_looking && !state.tmp.suppress_decorations && this.params && "string" === typeof str) { 22766 var prePunct = ""; 22767 if (str) { 22768 var m = str.match(CSL.VARIABLE_WRAPPER_PREPUNCT_REX); 22769 prePunct = m[1]; 22770 str = m[2]; 22771 } 22772 var postPunct = ""; 22773 if (str && CSL.SWAPPING_PUNCTUATION.indexOf(str.slice(-1)) > -1) { 22774 postPunct = str.slice(-1); 22775 str = str.slice(0,-1); 22776 } 22777 return state.sys.variableWrapper(this.params, prePunct, str, postPunct); 22778 } else { 22779 return str; 22780 } 22781 }, 22782 "@URL/true": function (state, str) { 22783 return "<fo:basic-link external-destination=\"url('" + str + "')\">" + str + "</fo:basic-link>"; 22784 }, 22785 "@DOI/true": function (state, str) { 22786 var doiurl = str; 22787 if (!str.match(/^https?:\/\//)) { 22788 doiurl = "https://doi.org/" + str; 22789 } 22790 return "<fo:basic-link external-destination=\"url('" + doiurl + "')\">" + str + "</fo:basic-link>"; 22791 } 22792}; 22793 22794/** 22795 * LaTeX .bbl output. 22796 * 22797 * (Code contributed by Egon Willighagen, based on the prototype.text code.) 22798 */ 22799CSL.Output.Formats.prototype.latex = { 22800 "text_escape": function (text) { 22801 if (!text) { 22802 text = ""; 22803 } 22804 return text; 22805 }, 22806 "bibstart": "\\begin{thebibliography}{4}", 22807 "bibend": "\\end{thebibliography}", 22808 "@font-style/italic": "{\\em %%STRING%%}", 22809 "@font-style/oblique": false, 22810 "@font-style/normal": false, 22811 "@font-variant/small-caps": false, 22812 "@passthrough/true": CSL.Output.Formatters.passthrough, 22813 "@font-variant/normal": false, 22814 "@font-weight/bold": "{\\bf %%STRING%%}", 22815 "@font-weight/normal": false, 22816 "@font-weight/light": false, 22817 "@text-decoration/none": false, 22818 "@text-decoration/underline": false, 22819 "@vertical-align/baseline": false, 22820 "@vertical-align/sup": false, 22821 "@vertical-align/sub": false, 22822 "@strip-periods/true": CSL.Output.Formatters.passthrough, 22823 "@strip-periods/false": CSL.Output.Formatters.passthrough, 22824 "@quotes/true": function (state, str) { 22825 if ("undefined" === typeof str) { 22826 return state.getTerm("open-quote"); 22827 } 22828 return state.getTerm("open-quote") + str + state.getTerm("close-quote"); 22829 }, 22830 "@quotes/inner": function (state, str) { 22831 if ("undefined" === typeof str) { 22832 //
22833 // Mostly right by being wrong (for apostrophes) 22834 // 22835 return "\u2019"; 22836 } 22837 return state.getTerm("open-inner-quote") + str + state.getTerm("close-inner-quote"); 22838 }, 22839 "@quotes/false": false, 22840 //"@bibliography/body": function (state,str){ 22841 // return "<div class=\"csl-bib-body\">\n"+str+"</div>"; 22842 //}, 22843 "@cite/entry": function (state, str) { 22844 return state.sys.wrapCitationEntry(str, this.item_id, this.locator_txt, this.suffix_txt); 22845 }, 22846 "@bibliography/entry": function (state, str) { 22847 return "\\bibitem{" + state.sys.embedBibliographyEntry(this.item_id) + "}\n"; 22848 }, 22849 "@display/block": function (state, str) { 22850 return "\n"+str; 22851 }, 22852 "@display/left-margin": function (state, str) { 22853 return str; 22854 }, 22855 "@display/right-inline": function (state, str) { 22856 return str; 22857 }, 22858 "@display/indent": function (state, str) { 22859 return "\n "+str; 22860 }, 22861 "@showid/true": function (state, str, cslid) { 22862 return str; 22863 }, 22864 "@URL/true": function (state, str) { 22865 return str; 22866 }, 22867 "@DOI/true": function (state, str) { 22868 return str; 22869 } 22870}; 22871 22872CSL.Output.Formats = new CSL.Output.Formats(); 22873 22874/*global CSL: true */ 22875 22876 22877// 22878// Time for a rewrite of this module. 22879// 22880// Simon has pointed out that list and hash behavior can 22881// be obtained by ... just using a list and a hash. This 22882// is faster for batched operations, because sorting is 22883// greatly optimized. Since most of the interaction 22884// with plugins at runtime will involve batches of 22885// references, there will be solid gains if the current, 22886// one-reference-at-a-time approach implemented here 22887// can be replaced with something that leverages the native 22888// sort method of the Array() type. 22889// 22890// That's going to take some redesign, but it will simplify 22891// things in the long run, so it might as well happen now. 22892// 22893// We'll keep makeCitationCluster and makeBibliography as 22894// simple methods that return a string. Neither should 22895// have any effect on internal state. This will be a change 22896// in behavior for makeCitationCluster. 22897// 22898// A new updateItems command will be introduced, to replace 22899// insertItems. It will be a simple list of IDs, in the 22900// sequence of first reference in the document. 22901// 22902// The calling application should always invoke updateItems 22903// before makeCitationCluster. 22904// 22905 22906// 22907// should allow batched registration of items by 22908// key. should behave as an update, with deletion 22909// of items and the tainting of disambiguation 22910// partner sets affected by a deletes and additions. 22911// 22912// 22913// we'll need a reset method, to clear the decks 22914// in the citation area and start over. 22915 22916/** 22917 * Registry of cited items. 22918 * <p>This is a persistent store of disambiguation and 22919 * sort order information relating to individual items 22920 * for which rendering is requested. Item data is stored 22921 * in a hash, with the item key as hash key, for quick 22922 * retrieval. A virtual sequence within the hashed store 22923 * is maintained on the fly as items are added to the 22924 * store, using <code>*_next</code> and <code>*_prev</code> 22925 * attributes on each item. A separate hash of items 22926 * based on their undisambiguated cite form is 22927 * maintained, and the item id list and disambiguation 22928 * level for each set of disambiguation partners is shared 22929 * through the registry item.</p> 22930 * @class 22931 */ 22932CSL.Registry = function (state) { 22933 this.debug = false; 22934 this.state = state; 22935 this.registry = {}; 22936 this.reflist = []; 22937 this.refhash = {}; 22938 this.namereg = new CSL.Registry.NameReg(state); 22939 this.citationreg = new CSL.Registry.CitationReg(state); 22940 // See CSL.NameOutput.prototype.outputNames 22941 // and CSL.Registry.prototype.doinserts 22942 this.authorstrings = {}; 22943 22944 // for parallel delimiter support 22945 this.masterMap = {}; 22946 22947 // 22948 // shared scratch vars 22949 this.mylist = []; 22950 this.myhash = {}; 22951 this.deletes = []; 22952 this.inserts = []; 22953 this.uncited = {}; 22954 this.refreshes = {}; 22955 this.akeys = {}; 22956 this.oldseq = {}; 22957 this.return_data = {}; 22958 // 22959 // each ambig is a list of the ids of other objects 22960 // that have the same base-level rendering 22961 this.ambigcites = {}; 22962 this.ambigresets = {}; 22963 this.sorter = new CSL.Registry.Comparifier(state, "bibliography_sort"); 22964 //this.modes = CSL.getModes.call(this.state); 22965 //this.checkerator = new CSL.Checkerator(); 22966
vendor: 81,607 bytes, lines 22967-25088
22967 this.getSortedIds = function () { 22968 var ret = []; 22969 for (var i = 0, ilen = this.reflist.length; i < ilen; i += 1) { 22970 ret.push("" + this.reflist[i].id); 22971 } 22972 return ret; 22973 }; 22974 22975 this.getSortedRegistryItems = function () { 22976 var ret = []; 22977 for (var i = 0, ilen = this.reflist.length; i < ilen; i += 1) { 22978 ret.push(this.reflist[i]); 22979 } 22980 return ret; 22981 }; 22982}; 22983 22984// 22985// Here's the sequence of operations to be performed on 22986// update: 22987// 22988// 1. (o) [init] Receive list as function argument, store as hash and as list. 22989// 2. (o) [init] Initialize refresh list. Never needs sorting, only hash required. 22990 22991// 3. (o) [dodeletes] Delete loop. 22992// 3a. (o) [dodeletes] Delete names in items to be deleted from names reg. 22993// 3b. (o) [dodeletes] Complement refreshes list with items affected by 22994// possible name changes. We'll actually perform the refresh once 22995// all of the necessary data and parameters have been established 22996// in the registry. 22997// 3c. (o) [dodeletes] Delete all items to be deleted from their disambig pools. 22998// 3d. (o) [dodeletes] Delete all items in deletion list from hash. 22999 23000// 4. (o) [doinserts] Insert loop. 23001// 4a. (o) [doinserts] Retrieve entries for items to insert. 23002// 4b. (o) [doinserts] Generate ambig key. 23003// 4c. (o) [doinserts] Add names in items to be inserted to names reg 23004// (implicit in getAmbiguousCite). 23005// 4d. (o) [doinserts] Record ambig pool key on akey list (used for updating further 23006// down the chain). 23007// 4e. (o) [doinserts] Create registry token. 23008// 4f. (o) [doinserts] Add item ID to hash. 23009// 4g. (o) [doinserts] Set and record the base token to hold disambiguation 23010// results ("disambig" in the object above). 23011// 5. (o) [rebuildlist] Create "new" list of hash pointers, in the order given 23012// in the argument to the update function. 23013// 6. (o) [rebuildlist] Apply citation numbers to new list. 23014// 7. (o) [dorefreshes] Refresh items requiring update. 23015 23016 23017 23018// 5. (o) [delnames] Delete names in items to be deleted from names reg, and obtain IDs 23019// of other items that would be affected by changes around that surname. 23020// 6. (o) [delnames] Complement delete and insert lists with items affected by 23021// possible name changes. 23022// 7. (o) [delambigs] Delete all items to be deleted from their disambig pools. 23023// 8. (o) [delhash] Delete all items in deletion list from hash. 23024 23025// 9. (o) [addtohash] Retrieve entries for items to insert. 23026// 10. (o) [addtohash] Add items to be inserted to their disambig pools. 23027// 11. (o) [addtohash] Add names in items to be inserted to names reg 23028// (implicit in getAmbiguousCite). 23029// 12. (o) [addtohash] Create registry token for each item to be inserted. 23030// 13. (o) [addtohash] Add items for insert to hash. 23031 23032// 14. (o) [buildlist] Create "new" list of hash pointers, in the order given in the argument 23033// to the update function. 23034// 15. (o) [renumber] Apply citation numbers to new list. 23035// 16. (o) [setdisambigs] Set disambiguation parameters on each inserted item token. 23036// 17. (o) [setsortkeys] Set sort keys on each item token. 23037// 18. (o) [sorttokens] Resort token list 23038// 19. (o) [renumber] Reset citation numbers on list items 23039// 23040 23041CSL.Registry.prototype.init = function (itemIDs, uncited_flag) { 23042 var i, ilen; 23043 this.oldseq = {}; 23044 // 1. Receive list as function argument, store as hash and as list. 23045 // 23046 // Result: 23047 // this.mylist: a list of all itemIDs of referenced items, cited and uncited. 23048 // this.myhash: a hash of index positions in this.mylist. 23049 // this.uncited: hash of uncited itemIDs. 23050 // 23051 // Proceed as follows. 23052 // 23053 if (uncited_flag) { 23054 // If uncited_flag is non-nil, add any missing itemIDs to this.mylist 23055 // from itemIDs input list, and set the itemIDs in itemIDs on this.uncited. 23056 this.uncited = {}; 23057 for (var i=0,ilen=itemIDs.length;i<ilen; i += 1) { 23058 if (!this.myhash[itemIDs[i]]) { 23059 this.mylist.push("" + itemIDs[i]); 23060 } 23061 this.uncited[itemIDs[i]] = true; 23062 this.myhash[itemIDs[i]] = true; 23063 } 23064 } else { 23065 // If uncited_flag is nil, remove duplicate itemIDs from itemIDs input 23066 // list, set the result on this.mylist, and add missing itemIDs to 23067 // this.mylist from itemIDs input list. 23068 for (var key in this.uncited) { 23069 itemIDs.push(key); 23070 } 23071 var myhash = {}; 23072 for (i=itemIDs.length-1;i>-1; i += -1) { 23073 if (myhash[itemIDs[i]]) { 23074 itemIDs = itemIDs.slice(0, i).concat(itemIDs.slice(i + 1)); 23075 } else { 23076 myhash[itemIDs[i]] = true; 23077 } 23078 } 23079 this.mylist = itemIDs; 23080 this.myhash = myhash; 23081 } 23082 // 23083 // 2. Initialize refresh list. Never needs sorting, only hash required. 23084 // 23085 this.refreshes = {}; 23086 this.touched = {}; 23087 this.ambigsTouched = {}; 23088 this.ambigresets = {}; 23089}; 23090 23091CSL.Registry.prototype.dopurge = function (myhash) { 23092 // Remove any uncited items not in myhash 23093 for (var i=this.mylist.length-1;i>-1;i+=-1) { 23094 // Might not want to be quite this restrictive. 23095 if (this.citationreg.citationsByItemId) { 23096 if ((!this.citationreg.citationsByItemId || !this.citationreg.citationsByItemId[this.mylist[i]]) && !myhash[this.mylist[i]]) { 23097 delete this.myhash[this.mylist[i]]; 23098 delete this.uncited[this.mylist[i]]; 23099 this.mylist = this.mylist.slice(0,i).concat(this.mylist.slice(i+1)); 23100 } 23101 } 23102 } 23103 this.dodeletes(this.myhash); 23104}; 23105 23106CSL.Registry.prototype.dodeletes = function (myhash) { 23107 var otheritems, key, ambig, pos, len, items, kkey, mypos, id; 23108 if ("string" === typeof myhash) { 23109 var key = myhash; 23110 myhash = {}; 23111 myhash[key] = true; 23112 } 23113 // 23114 // 3. Delete loop. 23115 // 23116 for (var key in this.registry) { 23117 if (!myhash[key]) { 23118 // skip items explicitly marked as uncited 23119 if (this.uncited[key]) { 23120 continue; 23121 } 23122 // 23123 // 3a. Delete names in items to be deleted from names reg. 23124 // 23125 otheritems = this.namereg.delitems(key); 23126 // 23127 // 3b. Complement refreshes list with items affected by 23128 // possible name changes. We'll actually perform the refresh once 23129 // all of the necessary data and parameters have been established 23130 // in the registry. 23131 // 23132 for (kkey in otheritems) { 23133 this.refreshes[kkey] = true; 23134 } 23135 // 23136 // 3c. Delete all items to be deleted from their disambig pools. 23137 // 23138 ambig = this.registry[key].ambig; 23139 mypos = this.ambigcites[ambig].indexOf(key); 23140 if (mypos > -1) { 23141 items = this.ambigcites[ambig].slice(); 23142 this.ambigcites[ambig] = items.slice(0, mypos).concat(items.slice(mypos+1, items.length)); 23143 this.ambigresets[ambig] = this.ambigcites[ambig].length; 23144 } 23145 // 23146 // XX. What we've missed is to provide an update of all 23147 // items sharing the same ambig += -1 the remaining items in 23148 // ambigcites. So let's do that here, just in case the 23149 // names update above doesn't catch them all. 23150 // 23151 len = this.ambigcites[ambig].length; 23152 for (pos = 0; pos < len; pos += 1) { 23153 id = "" + this.ambigcites[ambig][pos]; 23154 this.refreshes[id] = true; 23155 } 23156 // 23157 // 3d-0. Remove parallel id references and realign 23158 // parallel ID refs. 23159 // 23160 if (this.registry[key].siblings) { 23161 if (this.registry[key].siblings.length == 1) { 23162 var loneSiblingID = this.registry[key].siblings[0]; 23163 if (this.registry[loneSiblingID].siblings) { 23164 this.registry[loneSiblingID].siblings.pop(); 23165 this.registry[loneSiblingID].master = true; 23166 // this.registry[loneSiblingID].parallel = false; 23167 } 23168 } else if (this.registry[key].siblings.length > 1) { 23169 var removeIDs = [key]; 23170 if (this.registry[key].master) { 23171 var newmasterID = this.registry[key].siblings[0]; 23172 var newmaster = this.registry[newmasterID]; 23173 newmaster.master = true; 23174 // newmaster.parallel_delimiter is set externally, if at all 23175 // newmaster.parallel = false; 23176 removeIDs.push(newmasterID); 23177 // for (var k = 0, klen = this.registry[key].siblings.length; k < klen; k += 1) { 23178 // this.registry[this.registry[key].siblings[k]].parallel = newmasterID; 23179 // } 23180 } 23181 var buffer = []; 23182 for (var k = this.registry[key].siblings.length - 1; k > -1; k += -1) { 23183 var siblingID = this.registry[key].siblings.pop(); 23184 if (removeIDs.indexOf(siblingID) === -1) { 23185 buffer.push(siblingID); 23186 } 23187 } 23188 for (var k = buffer.length - 1; k > -1; k += -1) { 23189 this.registry[key].siblings.push(buffer[k]); 23190 } 23191 } 23192 } 23193 // 23194 // 3d-1. Remove item from reflist 23195 for (var i=this.reflist.length-1;i>-1;i--) { 23196 if (this.reflist[i].id === key) { 23197 this.reflist = this.reflist.slice(0, i).concat(this.reflist.slice(i+1)); 23198 } 23199 } 23200 // 23201 // 3d. Delete all items in deletion list from hash. 23202 // 23203 delete this.registry[key]; 23204 delete this.refhash[key]; 23205 23206 // For processCitationCluster() 23207 this.return_data.bibchange = true; 23208 } 23209 } 23210 // Disabled. See formats.js for code. 23211 // this.state.fun.decorate.items_delete( this.state.output[this.state.opt.mode].tmp, myhash ); 23212}; 23213 23214CSL.Registry.prototype.doinserts = function (mylist) { 23215 var item, Item, akey, newitem, abase, i, ilen; 23216 if ("string" === typeof mylist) { 23217 mylist = [mylist]; 23218 } 23219 // 23220 // 4. Insert loop. 23221 // 23222 for (var i = 0, ilen = mylist.length; i < ilen; i += 1) { 23223 item = mylist[i]; 23224 if (!this.registry[item]) { 23225 // 23226 // 4a. Retrieve entries for items to insert. 23227 // 23228 Item = this.state.retrieveItem(item); 23229 23230 // 23231 // 4b. Generate ambig key. 23232 // 23233 // AND 23234 // 23235 // 4c. Add names in items to be inserted to names reg 23236 // (implicit in getAmbiguousCite). 23237 // 23238 akey = CSL.getAmbiguousCite.call(this.state, Item); 23239 this.ambigsTouched[akey] = true; 23240 // 23241 // 4d. Record ambig pool key on akey list (used for updating further 23242 // down the chain). 23243 // 23244 if (!Item.legislation_id) { 23245 this.akeys[akey] = true; 23246 } 23247 // 23248 // 4e. Create registry token. 23249 // 23250 newitem = { 23251 "id": "" + item, 23252 "seq": 0, 23253 "offset": 0, 23254 "sortkeys": false, 23255 "ambig": false, 23256 "rendered": false, 23257 "disambig": false, 23258 "ref": Item, 23259 "newItem": true 23260 }; 23261 // 23262 // 23263 // 4f. Add item ID to hash. 23264 // 23265 this.registry[item] = newitem; 23266 // 23267 // 4f(a). Add first reference note number 23268 // (this may be redundant) 23269 if (this.citationreg.citationsByItemId && this.citationreg.citationsByItemId[item]) { 23270 this.registry[item]["first-reference-note-number"] = this.citationreg.citationsByItemId[item][0].properties.noteIndex; 23271 } 23272 23273 // 23274 // 4g. Set and record the base token to hold disambiguation 23275 // results ("disambig" in the object above). 23276 // 23277 abase = CSL.getAmbigConfig.call(this.state); 23278 this.registerAmbigToken(akey, item, abase); 23279 23280 //if (!this.ambigcites[akey]){ 23281 // this.ambigcites[akey] = []; 23282 //} 23283 //CSL.debug("Run: "+item+"("+this.ambigcites[akey]+")"); 23284 //if (this.ambigcites[akey].indexOf(item) === -1){ 23285 // CSL.debug(" Add: "+item); 23286 // this.ambigcites[akey].push(item); 23287 //} 23288 // 23289 // 4h. Make a note that this item needs its sort keys refreshed. 23290 // 23291 this.touched[item] = true; 23292 // For processCitationCluster() 23293 this.return_data.bibchange = true; 23294 } 23295 } 23296 // Disabled. See formats.js for code. 23297 // this.state.fun.decorate.items_add( this.state.output[this.state.opt.mode].tmp, mylist ); 23298}; 23299 23300/* 23301// No longer required. 23302CSL.Registry.prototype.douncited = function () { 23303 var pos, len; 23304 var cited_len = this.mylist.length - this.uncited.length; 23305 for (pos = 0, len = cited_len; pos < len; pos += 1) { 23306 this.registry[this.mylist[pos]].uncited = false; 23307 } 23308 for (pos = cited_len, len = this.mylist.length; pos < len; pos += 1) { 23309 this.registry[this.mylist[pos]].uncited = true; 23310 } 23311}; 23312*/ 23313 23314CSL.Registry.prototype.rebuildlist = function (nosort) { 23315 var len, pos, item, Item; 23316 // 23317 // 5. Create "new" list of hash pointers, in the order given in the argument 23318 // to the update function. 23319 // 23320 // 23321 // XXX Keep reflist in place. 23322 // 23323 if (!nosort) { 23324 this.reflist_inserts = []; 23325 // 23326 // 6. Apply citation numbers to new list, 23327 // saving off old sequence numbers as we go. 23328 // 23329 // XXX Just memo inserts -- actual insert happens below, at last "sort" 23330 // 23331 len = this.mylist.length; 23332 for (pos = 0; pos < len; pos += 1) { 23333 item = this.mylist[pos]; 23334 Item = this.registry[item]; 23335 if (Item.newItem) { 23336 this.reflist_inserts.push(Item); 23337 } 23338 this.oldseq[item] = this.registry[item].seq; 23339 this.registry[item].seq = (pos + 1); 23340 } 23341 } else { 23342 this.reflist = []; 23343 len = this.mylist.length; 23344 for (pos = 0; pos < len; pos += 1) { 23345 item = this.mylist[pos]; 23346 Item = this.registry[item]; 23347 this.reflist.push(Item); 23348 this.oldseq[item] = this.registry[item].seq; 23349 this.registry[item].seq = (pos + 1); 23350 } 23351 } 23352}; 23353 23354/* 23355 * Okay, at this point we should have a numbered list 23356 * of registry tokens in the notional order requested, 23357 * with sequence numbers to reconstruct the ordering 23358 * if the list is remangled. So far so good. 23359 */ 23360 23361CSL.Registry.prototype.dorefreshes = function () { 23362 var key, regtoken, Item, akey, abase; 23363 // 23364 // 7. Refresh items requiring update. 23365 // 23366 // It looks like we need to do four things on each cite for refresh: 23367 // (1) Generate the akey for the cite. 23368 // (2) Register it on the ambig token. 23369 // (3) Register the akey in this.akeys 23370 // (4) Register the item ID in this.touched 23371 // 23372 for (var key in this.refreshes) { 23373 regtoken = this.registry[key]; 23374 if (!regtoken) { 23375 continue; 23376 } 23377 regtoken.sortkeys = undefined; 23378 Item = this.state.refetchItem(key); 23379 var akey = regtoken.ambig; 23380 23381 if ("undefined" === typeof akey) { 23382 this.state.tmp.disambig_settings = false; 23383 akey = CSL.getAmbiguousCite.call(this.state, Item); 23384 abase = CSL.getAmbigConfig.call(this.state); 23385 this.registerAmbigToken(akey, key, abase); 23386 } 23387 for (var akkey in this.ambigresets) { 23388 if (this.ambigresets[akkey] === 1) { 23389 var loneKey = this.ambigcites[akey][0]; 23390 var Item = this.state.refetchItem(loneKey); 23391 this.registry[loneKey].disambig = new CSL.AmbigConfig(); 23392 this.state.tmp.disambig_settings = false; 23393 var akey = CSL.getAmbiguousCite.call(this.state, Item); 23394 var abase = CSL.getAmbigConfig.call(this.state); 23395 this.registerAmbigToken(akey, loneKey, abase); 23396 } 23397 } 23398 this.state.tmp.taintedItemIDs[key] = true; 23399 this.ambigsTouched[akey] = true; 23400 if (!Item.legislation_id) { 23401 this.akeys[akey] = true; 23402 } 23403 this.touched[key] = true; 23404 } 23405}; 23406 23407/* 23408 * Main disambiguation += -1 can everything for disambiguation be 23409 * crunched into this function? 23410 */ 23411CSL.Registry.prototype.setdisambigs = function () { 23412 // 23413 // Okay, more changes. Here is where we resolve all disambiguation 23414 // issues for cites touched by the update. The this.ambigcites set is 23415 // based on the complete short form of citations, and is the basis on 23416 // which names are added and minimal adding of initials or given names 23417 // is performed. 23418 // 23419 23420 // 23421 // 8. Set disambiguation parameters on each inserted item token. 23422 // 23423 for (var akey in this.ambigsTouched) { 23424 // 23425 // Disambiguation is fully encapsulated. 23426 // Disambiguator will run only if there are multiple 23427 // items, and at least one disambiguation mode is 23428 // in effect. 23429 this.state.disambiguate.run(akey); 23430 } 23431 this.ambigsTouched = {}; 23432 this.akeys = {}; 23433}; 23434 23435 23436 23437CSL.Registry.prototype.renumber = function () { 23438 var len, pos, item; 23439 // 23440 // 19. Reset citation numbers on list items 23441 // 23442 if (this.state.bibliography_sort.opt.citation_number_sort_direction === CSL.DESCENDING) { 23443 this.state.bibliography_sort.tmp.citation_number_map = {}; 23444 } 23445 len = this.reflist.length; 23446 for (pos = 0; pos < len; pos += 1) { 23447 item = this.reflist[pos]; 23448 // save the overhead of rerenderings if citation-number is not 23449 // used in the style. 23450 item.seq = (pos + 1); 23451 if (this.state.bibliography_sort.opt.citation_number_sort_direction === CSL.DESCENDING) { 23452 this.state.bibliography_sort.tmp.citation_number_map[item.seq] = (this.reflist.length - item.seq + 1); 23453 } 23454 // update_mode is set to CSL.NUMERIC if citation-number is rendered 23455 // in citations. 23456 if (this.state.opt.update_mode === CSL.NUMERIC && item.seq != this.oldseq[item.id]) { 23457 this.state.tmp.taintedItemIDs[item.id] = true; 23458 } 23459 if (item.seq != this.oldseq[item.id]) { 23460 this.return_data.bibchange = true; 23461 } 23462 } 23463}; 23464 23465CSL.Registry.prototype.setsortkeys = function () { 23466 var key; 23467 // 23468 // 17. Set sort keys on each item token. 23469 // 23470 for (var i = 0, ilen = this.mylist.length; i < ilen; i += 1) { 23471 var key = this.mylist[i]; 23472 // The last of these conditions may create some thrashing on styles that do not require sorting. 23473 if (this.touched[key] || this.state.tmp.taintedItemIDs[key] || !this.registry[key].sortkeys) { 23474 this.registry[key].sortkeys = CSL.getSortKeys.call(this.state, this.state.retrieveItem(key), "bibliography_sort"); 23475 } 23476 } 23477}; 23478 23479CSL.Registry.prototype._insertItem = function(element, array) { 23480 array.splice(this._locationOf(element, array) + 1, 0, element); 23481 return array; 23482}; 23483 23484CSL.Registry.prototype._locationOf = function(element, array, start, end) { 23485 if (array.length === 0) { 23486 return -1; 23487 } 23488 start = start || 0; 23489 end = end || array.length; 23490 var pivot = (start + end) >> 1; // should be faster than dividing by 2 23491 23492 var c = this.sorter.compareKeys(element, array[pivot]); 23493 if (end - start <= 1) { 23494 return c == -1 ? pivot - 1 : pivot; 23495 } 23496 switch (c) { 23497 case -1: return this._locationOf(element, array, start, pivot); 23498 case 0: return pivot; 23499 case 1: return this._locationOf(element, array, pivot, end); 23500 } 23501}; 23502 23503CSL.Registry.prototype.sorttokens = function (nosort) { 23504 var len, item, Item, pos; 23505 // 23506 // 18. Resort token list. 23507 // 23508 if (!nosort) { 23509 this.reflist_inserts = []; 23510 len = this.mylist.length; 23511 for (pos = 0; pos < len; pos += 1) { 23512 item = this.mylist[pos]; 23513 Item = this.registry[item]; 23514 if (Item.newItem) { 23515 this.reflist_inserts.push(Item); 23516 } 23517 } 23518 // There is a thin possibility that tainted items in a sorted list 23519 // will change position due to disambiguation. We cover for that here. 23520 for (var key in this.state.tmp.taintedItemIDs) { 23521 if (this.registry[key] && !this.registry[key].newItem) { 23522 // Move tainted items from reflist to reflist_inserts 23523 for (var i=this.reflist.length-1;i>-1;i--) { 23524 if (this.reflist[i].id === key) { 23525 this.reflist_inserts.push(this.reflist[i]); 23526 this.reflist = this.reflist.slice(0, i).concat(this.reflist.slice(i+1)); 23527 } 23528 } 23529 } 23530 } 23531 for (var i=0,ilen=this.reflist_inserts.length;i<ilen;i++) { 23532 var Item = this.reflist_inserts[i]; 23533 delete Item.newItem; 23534 this.reflist = this._insertItem(Item, this.reflist); 23535 } 23536 for (pos = 0; pos < len; pos += 1) { 23537 item = this.mylist[pos]; 23538 Item = this.registry[item]; 23539 this.registry[item].seq = (pos + 1); 23540 } 23541 } 23542}; 23543 23544/** 23545 * Compare two sort keys 23546 * <p>Nested, because keys are an array.</p> 23547 */ 23548CSL.Registry.Comparifier = function (state, keyset) { 23549 var sort_directions, len, pos, compareKeys; 23550 var sortCompare = CSL.getSortCompare.call(state, state.opt["default-locale-sort"]); 23551 sort_directions = state[keyset].opt.sort_directions; 23552 this.compareKeys = function (a, b) { 23553 len = a.sortkeys ? a.sortkeys.length : 0; 23554 for (pos = 0; pos < len; pos += 1) { 23555 // 23556 // for ascending sort 1 uses 1, -1 uses -1. 23557 // For descending sort, the values are reversed. 23558 // 23559 // Need to handle undefined values. No way around it. 23560 // So have to screen .localeCompare (which is also 23561 // needed) from undefined values. Everywhere, in all 23562 // compares. 23563 // 23564 var cmp = 0; 23565 if (a.sortkeys[pos] === b.sortkeys[pos]) { 23566 cmp = 0; 23567 } else if ("undefined" === typeof a.sortkeys[pos]) { 23568 cmp = sort_directions[pos][1]; 23569 } else if ("undefined" === typeof b.sortkeys[pos]) { 23570 cmp = sort_directions[pos][0]; 23571 } else { 23572 // cmp = a.sortkeys[pos].localeCompare(b.sortkeys[pos]); 23573 cmp = sortCompare(a.sortkeys[pos], b.sortkeys[pos]); 23574 } 23575 if (0 < cmp) { 23576 return sort_directions[pos][1]; 23577 } else if (0 > cmp) { 23578 return sort_directions[pos][0]; 23579 } 23580 } 23581 if (a.seq > b.seq) { 23582 return 1; 23583 } else if (a.seq < b.seq) { 23584 return -1; 23585 } 23586 return 0; 23587 }; 23588 compareKeys = this.compareKeys; 23589 this.compareCompositeKeys = function (a, b) { 23590 return compareKeys(a[1], b[1]); 23591 }; 23592}; 23593 23594 23595/** 23596 * Compare two disambiguation tokens by their registry sort order 23597 * <p>Disambiguation lists need to be sorted this way, to 23598 * obtain the correct year-suffix when that's used.</p> 23599 */ 23600CSL.Registry.prototype.compareRegistryTokens = function (a, b) { 23601 if (a.seq > b.seq) { 23602 return 1; 23603 } else if (a.seq < b.seq) { 23604 return -1; 23605 } 23606 return 0; 23607}; 23608 23609CSL.Registry.prototype.registerAmbigToken = function (akey, id, ambig_config) { 23610 //SNIP-START 23611 if (!this.registry[id]) { 23612 CSL.debug("Warning: unregistered item: itemID=("+id+"), akey=("+akey+")"); 23613 } 23614 //SNIP-END 23615 // Taint if number of names to be included has changed 23616 if (this.registry[id] && this.registry[id].disambig && this.registry[id].disambig.names) { 23617 for (var i = 0, ilen = ambig_config.names.length; i < ilen; i += 1) { 23618 var new_names_params = ambig_config.names[i]; 23619 var old_names_params = this.registry[id].disambig.names[i]; 23620 if (new_names_params !== old_names_params) { 23621 this.state.tmp.taintedItemIDs[id] = true; 23622 } else if (ambig_config.givens[i]) { 23623 // Compare givenses only if the number of names is aligned. 23624 for (var j=0,jlen=ambig_config.givens[i].length;j<jlen;j+=1) { 23625 var new_gnames_params = ambig_config.givens[i][j]; 23626 var old_gnames_params = this.registry[id].disambig.givens[i][j]; 23627 if (new_gnames_params !== old_gnames_params) { 23628 this.state.tmp.taintedItemIDs[id] = true; 23629 } 23630 } 23631 } 23632 } 23633 } 23634 23635 if (!this.ambigcites[akey]) { 23636 this.ambigcites[akey] = []; 23637 } 23638 if (this.ambigcites[akey].indexOf("" + id) === -1) { 23639 this.ambigcites[akey].push("" + id); 23640 } 23641 this.registry[id].ambig = akey; 23642 this.registry[id].disambig = CSL.cloneAmbigConfig(ambig_config); 23643}; 23644 23645 23646/** 23647 * Get the sort key of an item, without decorations 23648 * <p>This is used internally by the Registry.</p> 23649 */ 23650CSL.getSortKeys = function (Item, key_type) { 23651 var area, root, extension, strip_prepositions, len, pos; 23652 //SNIP-START 23653 if (false) { 23654 CSL.debug("KEY TYPE: " + key_type); 23655 } 23656 //SNIP-END 23657 area = this.tmp.area; 23658 root = this.tmp.root; 23659 extension = this.tmp.extension; 23660 strip_prepositions = CSL.Util.Sort.strip_prepositions; 23661 this.tmp.area = key_type; 23662 // Gawdawful, this. 23663 this.tmp.root = key_type.indexOf("_") > -1 ? key_type.slice(0,-5) : key_type; 23664 this.tmp.extension = "_sort"; 23665 this.tmp.disambig_override = true; 23666 this.tmp.disambig_request = false; 23667 this.tmp.suppress_decorations = true; 23668 CSL.getCite.call(this, Item); 23669 this.tmp.suppress_decorations = false; 23670 this.tmp.disambig_override = false; 23671 len = this[key_type].keys.length; 23672 for (pos = 0; pos < len; pos += 1) { 23673 this[key_type].keys[pos] = strip_prepositions(this[key_type].keys[pos]); 23674 } 23675 //SNIP-START 23676 if (false) { 23677 CSL.debug("sort keys (" + key_type + "): " + this[key_type].keys); 23678 } 23679 //SNIP-END 23680 23681 this.tmp.area = area; 23682 this.tmp.root = root; 23683 this.tmp.extension = extension; 23684 return this[key_type].keys; 23685}; 23686 23687 23688/*global CSL: true */ 23689 23690CSL.Registry.NameReg = function (state) { 23691 var pkey, ikey, skey, dagopt, gdropt, items, strip_periods, set_keys, evalname, delitems, addname, myitems; 23692 this.state = state; 23693 this.namereg = {}; 23694 this.nameind = {}; 23695 // used for restoring state following preview 23696 this.nameindpkeys = {}; 23697 // 23698 // family, initials form, fullname (with given stripped of periods) 23699 // 23700 // keys registered, indexed by ID 23701 this.itemkeyreg = {}; 23702 23703 strip_periods = function (str) { 23704 if (!str) { 23705 str = ""; 23706 } 23707 return str.replace(/\./g, " ").replace(/\s+/g, " ").replace(/\s+$/,""); 23708 }; 23709 23710 set_keys = function (state, itemid, nameobj) { 23711 pkey = strip_periods(nameobj.family); 23712 23713 if (state.opt["demote-non-dropping-particle"] === "never" && nameobj["non-dropping-particle"] && nameobj["family"]) { 23714 pkey = `${pkey} ${nameobj["non-dropping-particle"]}`; 23715 } 23716 23717 skey = strip_periods(nameobj.given); 23718 // Drop lowercase suffixes (such as et al.) from given name field 23719 // for disambiguation purposes. 23720 var m = skey.match(/[,\!]* ([^,]+)$/); 23721 if (m && m[1] === m[1].toLowerCase()) { 23722 skey = skey.replace(/[,\!]* [^,]+$/, ""); 23723 } 23724 // The %s terminator enables normal initialization behavior 23725 // with non-Byzantine names. 23726 ikey = CSL.Util.Names.initializeWith(state, skey, "%s"); 23727 if (state.citation.opt["givenname-disambiguation-rule"] === "by-cite") { 23728 pkey = "" + itemid + pkey; 23729 } 23730 }; 23731 23732 evalname = function (item_id, nameobj, namenum, request_base, form, initials) { 23733 var param; 23734 // XXX THIS CAN NO LONGER HAPPEN 23735 if (state.tmp.area.slice(0, 12) === "bibliography" && !form) { 23736 if ("string" === typeof initials) { 23737 return 1; 23738 } else { 23739 return 2; 23740 } 23741 } 23742 var res = state.nameOutput.getName(nameobj, "locale-translit", true); 23743 nameobj = res.name; 23744 set_keys(this.state, "" + item_id, nameobj); 23745 // 23746 // possible options are: 23747 // 23748 // <option disambiguate-add-givenname value="true"/> (a) 23749 // <option disambiguate-add-givenname value="all-names"/> (a) 23750 // <option disambiguate-add-givenname value="all-names-with-initials"/> (b) 23751 // <option disambiguate-add-givenname value="primary-name"/> (d) 23752 // <option disambiguate-add-givenname value="primary-name-with-initials"/> (e) 23753 // <option disambiguate-add-givenname value="by-cite"/> (g) 23754 // 23755 param = 2; 23756 dagopt = state.opt["disambiguate-add-givenname"]; 23757 gdropt = state.citation.opt["givenname-disambiguation-rule"]; 23758 var gdropt_orig = gdropt; 23759 if (gdropt === "by-cite") { 23760 gdropt = "all-names"; 23761 } 23762 // 23763 // set initial value 23764 // 23765 if ("short" === form) { 23766 param = 0; 23767 } else if ("string" === typeof initials) { 23768 param = 1; 23769 } 23770 // 23771 // give literals a pass 23772 if ("undefined" === typeof this.namereg[pkey] || "undefined" === typeof this.namereg[pkey].ikey[ikey]) { 23773 return param; 23774 } 23775 // 23776 // adjust value upward if appropriate -- only if running 23777 // a non-names-global disambiguation strategy 23778 // 23779 if (gdropt_orig === "by-cite" && param <= request_base) { 23780 //param = request_base; 23781 return request_base; 23782 } 23783 if (!dagopt) { 23784 return param; 23785 } 23786 if ("string" === typeof gdropt && gdropt.slice(0, 12) === "primary-name" && namenum > 0) { 23787 return param; 23788 } 23789 // 23790 // the last composite condition is for backward compatibility 23791 // 23792 if (!gdropt || gdropt === "all-names" || gdropt === "primary-name") { 23793 if (this.namereg[pkey].count > 1) { 23794 param = 1; 23795 } 23796 if ((this.namereg[pkey].ikey 23797 && this.namereg[pkey].ikey[ikey].count > 1) 23798 || (this.namereg[pkey].count > 1 23799 && "string" !== typeof initials)) { 23800 23801 param = 2; 23802 } 23803 } else if (gdropt === "all-names-with-initials" || gdropt === "primary-name-with-initials") { 23804 if (this.namereg[pkey].count > 1) { 23805 param = 1; 23806 } else { 23807 param = 0; 23808 } 23809 } 23810 if (!state.registry.registry[item_id]) { 23811 if (form == "short") { 23812 return 0; 23813 } else if ("string" == typeof initials) { 23814 return 1; 23815 } 23816 } else { 23817 return param; 23818 } 23819 }; 23820 23821 // 23822 // The operation of this function does not show up in the 23823 // standard test suite, but it has been hand-tested with 23824 // a print trace, and seems to work okay. 23825 // 23826 delitems = function (ids) { 23827 var pos, len, posB, id, fullkey; 23828 if ("string" === typeof ids || "number" === typeof ids) { 23829 ids = ["" + ids]; 23830 } 23831 // ret carries the IDs of other items using this name. 23832 var ret = {}; 23833 len = ids.length; 23834 for (pos = 0; pos < len; pos += 1) { 23835 id = "" + ids[pos]; 23836 if (!this.nameind[id]) { 23837 continue; 23838 } 23839 for (fullkey in this.nameind[id]) { 23840 if (this.nameind[id].hasOwnProperty(fullkey)) { 23841 var key = fullkey.split("::"); 23842 pkey = key[0]; 23843 ikey = key[1]; 23844 skey = key[2]; 23845 // Skip names that have been deleted already. 23846 // Needed to clear integration DisambiguateAddGivenname1.txt 23847 // and integration DisambiguateAddGivenname2.txt 23848 if ("undefined" === typeof this.namereg[pkey]) { 23849 continue; 23850 } 23851 23852 // ???? 23853 //posA = this.namereg[pkey].items.indexOf(posA); 23854 23855 items = this.namereg[pkey].items; 23856 // This was really, really unperceptive. They key elements 23857 // have absolutely nothing to do with whether there was ever 23858 // a registration at each key level. 23859 if (skey && this.namereg[pkey].ikey[ikey] && this.namereg[pkey].ikey[ikey].skey[skey]) { 23860 myitems = this.namereg[pkey].ikey[ikey].skey[skey].items; 23861 posB = myitems.indexOf("" + id); 23862 if (posB > -1) { 23863 this.namereg[pkey].ikey[ikey].skey[skey].items = myitems.slice(0, posB).concat(myitems.slice([(posB + 1)])); 23864 } 23865 if (this.namereg[pkey].ikey[ikey].skey[skey].items.length === 0) { 23866 delete this.namereg[pkey].ikey[ikey].skey[skey]; 23867 this.namereg[pkey].ikey[ikey].count += -1; 23868 if (this.namereg[pkey].ikey[ikey].count < 2) { 23869 for (var i = 0, ilen = this.namereg[pkey].ikey[ikey].items.length; i < ilen; i += 1) { 23870 state.tmp.taintedItemIDs[this.namereg[pkey].ikey[ikey].items[i]] = true; 23871 } 23872 } 23873 } 23874 } 23875 if (ikey && this.namereg[pkey].ikey[ikey]) { 23876 posB = this.namereg[pkey].ikey[ikey].items.indexOf("" + id); 23877 if (posB > -1) { 23878 items = this.namereg[pkey].ikey[ikey].items.slice(); 23879 this.namereg[pkey].ikey[ikey].items = items.slice(0, posB).concat(items.slice([posB + 1])); 23880 } 23881 if (this.namereg[pkey].ikey[ikey].items.length === 0) { 23882 delete this.namereg[pkey].ikey[ikey]; 23883 this.namereg[pkey].count += -1; 23884 if (this.namereg[pkey].count < 2) { 23885 for (var i = 0, ilen = this.namereg[pkey].items.length; i < ilen; i += 1) { 23886 state.tmp.taintedItemIDs[this.namereg[pkey].items[i]] = true; 23887 } 23888 } 23889 } 23890 } 23891 if (pkey) { 23892 posB = this.namereg[pkey].items.indexOf("" + id); 23893 if (posB > -1) { 23894 items = this.namereg[pkey].items.slice(); 23895 this.namereg[pkey].items = items.slice(0, posB).concat(items.slice([posB + 1], items.length)); 23896 } 23897 if (this.namereg[pkey].items.length < 2) { 23898 delete this.namereg[pkey]; 23899 } 23900 } 23901 delete this.nameind[id][fullkey]; 23902 } 23903 } 23904 delete this.nameind[id]; 23905 delete this.nameindpkeys[id]; 23906 } 23907 return ret; 23908 }; 23909 // 23910 // Run ALL 23911 // renderings with disambiguate-add-givenname set to a value 23912 // with the by-cite behaviour, and then set the names-based 23913 // expanded form when the final makeCitationCluster rendering 23914 // is output. This could be done with a single var set on 23915 // the state object in the execution wrappers that run the 23916 // style. 23917 // 23918 addname = function (item_id, nameobj, pos) { 23919 var i, ilen; 23920 var res = state.nameOutput.getName(nameobj, "locale-translit", true); 23921 nameobj = res.name; 23922 23923 if (state.citation.opt["givenname-disambiguation-rule"] 23924 && state.citation.opt["givenname-disambiguation-rule"].slice(0, 8) === "primary-" 23925 && pos !== 0) { 23926 return; 23927 } 23928 23929 // A hack. Safe if the name object is used only here, for disambiguation purposes. 23930 /* 23931 */ 23932 23933 //CSL.debug("INS"); 23934 set_keys(this.state, "" + item_id, nameobj); 23935 // pkey, ikey and skey should be stored in separate cascading objects. 23936 // there should also be a kkey, on each, which holds the item ids using 23937 // that form of the name. 23938 // 23939 // (later note: well, we seem to have slipped a notch here. 23940 // Adding lists of IDs all over the place here makes no sense; 23941 // the lists need to include _only_ the items currently rendered 23942 // at the given level, and the place to do that is in evalname, 23943 // and in delnames, not here.) 23944 if (pkey) { 23945 if ("undefined" === typeof this.namereg[pkey]) { 23946 this.namereg[pkey] = {}; 23947 this.namereg[pkey].count = 0; 23948 this.namereg[pkey].ikey = {}; 23949 this.namereg[pkey].items = [item_id]; 23950 } else if (this.namereg[pkey].items.indexOf(item_id) === -1) { 23951 this.namereg[pkey].items.push(item_id); 23952 } 23953// if (this.namereg[pkey].items.indexOf(item_id) === -1) { 23954// this.namereg[pkey].items.push(item_id); 23955// } 23956 } 23957 if (pkey && ikey) { 23958 if ("undefined" === typeof this.namereg[pkey].ikey[ikey]) { 23959 this.namereg[pkey].ikey[ikey] = {}; 23960 this.namereg[pkey].ikey[ikey].count = 0; 23961 this.namereg[pkey].ikey[ikey].skey = {}; 23962 this.namereg[pkey].ikey[ikey].items = [item_id]; 23963 this.namereg[pkey].count += 1; 23964 if (this.namereg[pkey].count === 2) { 23965 for (var i = 0, ilen = this.namereg[pkey].items.length; i < ilen; i += 1) { 23966 state.tmp.taintedItemIDs[this.namereg[pkey].items[i]] = true; 23967 } 23968 } 23969 } else if (this.namereg[pkey].ikey[ikey].items.indexOf(item_id) === -1) { 23970 this.namereg[pkey].ikey[ikey].items.push(item_id); 23971 } 23972// if (this.namereg[pkey].ikey[ikey].items.indexOf(item_id) === -1) { 23973// this.namereg[pkey].ikey[ikey].items.push(item_id); 23974// } 23975 } 23976 if (pkey && ikey && skey) { 23977 if ("undefined" === typeof this.namereg[pkey].ikey[ikey].skey[skey]) { 23978 this.namereg[pkey].ikey[ikey].skey[skey] = {}; 23979 this.namereg[pkey].ikey[ikey].skey[skey].items = [item_id]; 23980 this.namereg[pkey].ikey[ikey].count += 1; 23981 if (this.namereg[pkey].ikey[ikey].count === 2) { 23982 for (var i = 0, ilen = this.namereg[pkey].ikey[ikey].items.length; i < ilen; i += 1) { 23983 state.tmp.taintedItemIDs[this.namereg[pkey].ikey[ikey].items[i]] = true; 23984 } 23985 } 23986 } else if (this.namereg[pkey].ikey[ikey].skey[skey].items.indexOf(item_id) === -1) { 23987 this.namereg[pkey].ikey[ikey].skey[skey].items.push(item_id); 23988 } 23989// if (this.namereg[pkey].ikey[ikey].skey[skey].items.indexOf(item_id) === -1) { 23990// this.namereg[pkey].ikey[ikey].skey[skey].items.push(item_id); 23991// } 23992 } 23993 if ("undefined" === typeof this.nameind[item_id]) { 23994 this.nameind[item_id] = {}; 23995 this.nameindpkeys[item_id] = {}; 23996 } 23997 //CSL.debug("INS-A: [" + pkey + "] [" + ikey + "] [" + skey + "]"); 23998 if (pkey) { 23999 this.nameind[item_id][pkey + "::" + ikey + "::" + skey] = true; 24000 this.nameindpkeys[item_id][pkey] = this.namereg[pkey]; 24001 } 24002 //CSL.debug("INS-B"); 24003 }; 24004 this.addname = addname; 24005 this.delitems = delitems; 24006 this.evalname = evalname; 24007}; 24008 24009/*global CSL: true */ 24010 24011CSL.Registry.CitationReg = function () { 24012 this.citationById = {}; 24013 this.citationByIndex = []; 24014}; 24015 24016/*global CSL: true */ 24017 24018CSL.Disambiguation = function (state) { 24019 this.state = state; 24020 this.sys = this.state.sys; 24021 this.registry = state.registry.registry; 24022 this.ambigcites = state.registry.ambigcites; 24023 this.configModes(); 24024 this.debug = false; 24025}; 24026 24027CSL.Disambiguation.prototype.run = function(akey) { 24028 if (!this.modes.length) { 24029 return; 24030 } 24031 //SNIP-START 24032 if (this.debug) { 24033 this.state.sys.print("[A] === RUN ==="); 24034 } 24035 //SNIP-END 24036 this.akey = akey; 24037 if (this.initVars(akey)) { 24038 this.runDisambig(); 24039 } 24040 24041}; 24042 24043CSL.Disambiguation.prototype.runDisambig = function () { 24044 var ismax; 24045 //SNIP-START 24046 if (this.debug) { 24047 this.state.sys.print("[C] === runDisambig() ==="); 24048 } 24049 //SNIP-END 24050 this.initGivens = true; 24051 // 24052 // Length of list may change during processing 24053 while (this.lists.length) { 24054 this.gnameset = 0; 24055 this.gname = 0; 24056 this.clashes = [1, 0]; 24057 // each list is scanned repeatedly until all 24058 // items either succeed or ultimately fail. 24059 while(this.lists[0][1].length) { 24060 this.listpos = 0; 24061 if (!this.base) { 24062 this.base = this.lists[0][0]; 24063 } 24064 ismax = this.incrementDisambig(); 24065 this.scanItems(this.lists[0]); 24066 this.evalScan(ismax); 24067 } 24068 this.lists = this.lists.slice(1); 24069 } 24070}; 24071 24072CSL.Disambiguation.prototype.scanItems = function (list) { 24073 var pos, len, otherItem; 24074 //SNIP-START 24075 if (this.debug) { 24076 this.state.sys.print("[2] === scanItems() ==="); 24077 } 24078 //SNIP-END 24079 24080 this.Item = list[1][0]; 24081 this.ItemCite = CSL.getAmbiguousCite.call(this.state, this.Item, this.base, true); 24082 24083 this.scanlist = list[1]; 24084 this.partners = []; 24085 this.partners.push(this.Item); 24086 this.nonpartners = []; 24087 var clashes = 0; 24088 24089 for (var pos = 1, len = list[1].length; pos < len; pos += 1) { 24090 otherItem = list[1][pos]; 24091 var otherItemCite = CSL.getAmbiguousCite.call(this.state, otherItem, this.base, true); 24092 //SNIP-START 24093 if (this.debug) { 24094 if (pos > 1) { 24095 this.state.sys.print(" -----------"); 24096 } 24097 } 24098 //SNIP-END 24099 if (this.ItemCite === otherItemCite) { 24100 //SNIP-START 24101 if (this.debug) { 24102 this.state.sys.print(" [CLASH]--> "+this.Item.id+": "+this.ItemCite); 24103 this.state.sys.print(" "+otherItem.id+": "+otherItemCite); 24104 } 24105 //SNIP-END 24106 clashes += 1; 24107 this.partners.push(otherItem); 24108 } else { 24109 //SNIP-START 24110 if (this.debug) { 24111 this.state.sys.print(" [clear]--> "+this.Item.id+": "+this.ItemCite); 24112 this.state.sys.print(" "+otherItem.id+": "+otherItemCite); 24113 } 24114 //SNIP-END 24115 this.nonpartners.push(otherItem); 24116 } 24117 } 24118 this.clashes[0] = this.clashes[1]; 24119 this.clashes[1] = clashes; 24120}; 24121 24122CSL.Disambiguation.prototype.evalScan = function (maxed) { 24123 this[this.modes[this.modeindex]](maxed); 24124 if (maxed) { 24125 if (this.modeindex < this.modes.length - 1) { 24126 this.modeindex += 1; 24127 } else { 24128 this.lists[this.listpos + 1] = [this.base, []]; 24129 } 24130 } 24131}; 24132 24133CSL.Disambiguation.prototype.disNames = function (ismax) { 24134 var i, ilen; 24135 24136 //SNIP-START 24137 if (this.debug) { 24138 this.state.sys.print("[3] == disNames() =="); 24139 //this.state.sys.print(" partners: "+[this.partners[i].id for (i in this.partners)].join(", ")); 24140 //this.state.sys.print(" nonpartners: "+[this.nonpartners[i].id for (i in this.nonpartners)].join(", ")); 24141 } 24142 //SNIP-END 24143 24144 // New design 24145 // this.base is a forward-only counter. Values are never 24146 // reduced, and the counter object is never overwritten. 24147 // It is methodically pushed forward in single-unit increments 24148 // in incrementDisambig() until disNames() wipes out the list. 24149 24150 // this.betterbase is cloned from this.base exactly once, 24151 // at the start of a disambiguation run. Whenever an operation 24152 // results in improvement, the just-incremented elements 24153 // identified as this.base.names[this.gnameset] (number of 24154 // names)and as this.base.givens[this.gnameset][this.gname] 24155 // (level of given name) are copied from this.base. 24156 24157 // The this.base object is used to control disambiguation 24158 // renderings. These will be more fully expanded than the final 24159 // text, but the flip side of the fact that the extra data does 24160 // not contribute anything to disambiguation is that leaving 24161 // it in does no harm -- think of it as the Cold Dark Matter of 24162 // disambiguation. 24163 24164 if (this.clashes[1] === 0 && this.nonpartners.length === 1) { 24165 this.captureStepToBase(); 24166 //SNIP-START 24167 if (this.debug) { 24168 this.state.sys.print(" ** RESOLUTION [a]: lone partner, one nonpartner"); 24169 this.state.sys.print(" registering "+this.partners[0].id+" and "+this.nonpartners[0].id); 24170 } 24171 //SNIP-END 24172 this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.betterbase); 24173 this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, this.betterbase); 24174 this.lists[this.listpos] = [this.betterbase, []]; 24175 } else if (this.clashes[1] === 0) { 24176 this.captureStepToBase(); 24177 //SNIP-START 24178 if (this.debug) { 24179 this.state.sys.print(" ** RESOLUTION [b]: lone partner, unknown number of remaining nonpartners"); 24180 this.state.sys.print(" registering "+this.partners[0].id); 24181 } 24182 //SNIP-END 24183 this.state.registry.registerAmbigToken(this.akey, "" + this.partners[0].id, this.betterbase); 24184 this.lists[this.listpos] = [this.betterbase, this.nonpartners]; 24185 if (this.nonpartners.length) { 24186 this.initGivens = true; 24187 } 24188 } else if (this.nonpartners.length === 1) { 24189 this.captureStepToBase(); 24190 //SNIP-START 24191 if (this.debug) { 24192 this.state.sys.print(" ** RESOLUTION [c]: lone nonpartner, unknown number of partners remaining"); 24193 this.state.sys.print(" registering "+this.nonpartners[0].id); 24194 } 24195 //SNIP-END 24196 this.state.registry.registerAmbigToken(this.akey, "" + this.nonpartners[0].id, this.betterbase); 24197 //this.lists[this.listpos] = [this.betterbase, this.partners]; 24198 this.lists[this.listpos] = [this.betterbase, this.partners]; 24199 } else if (this.clashes[1] < this.clashes[0]) { 24200 this.captureStepToBase(); 24201 //SNIP-START 24202 if (this.debug) { 24203 this.state.sys.print(" ** RESOLUTION [d]: better result, but no entries safe to register"); 24204 } 24205 //SNIP-END 24206 this.lists[this.listpos] = [this.betterbase, this.partners]; 24207 this.lists.push([this.betterbase, this.nonpartners]); 24208 } else { 24209 //SNIP-START 24210 if (this.debug) { 24211 this.state.sys.print(" ** RESOLUTION [e]: no improvement, and clashes remain"); 24212 } 24213 //SNIP-END 24214 if (ismax) { 24215 this.lists[this.listpos] = [this.betterbase, this.nonpartners]; 24216 this.lists.push([this.betterbase, this.partners]); 24217 if (this.modeindex === this.modes.length - 1) { 24218 //SNIP-START 24219 if (this.debug) { 24220 this.state.sys.print(" (registering clashing entries because we've run out of options)"); 24221 } 24222 //SNIP-END 24223 for (var i = 0, ilen = this.partners.length; i < ilen; i += 1) { 24224 this.state.registry.registerAmbigToken(this.akey, "" + this.partners[i].id, this.betterbase); 24225 } 24226 this.lists[this.listpos] = [this.betterbase, []]; 24227 } 24228 } 24229 } 24230}; 24231 24232CSL.Disambiguation.prototype.disExtraText = function () { 24233 //SNIP-START 24234 if (this.debug) { 24235 this.state.sys.print("[3] === disExtraText =="); 24236 } 24237 //SNIP-END 24238 24239 var done = false; 24240 24241 if (this.clashes[1] === 0 && this.nonpartners.length < 2) { 24242 done = true; 24243 } 24244 24245 // If first encounter in this cycle and multiple modes are 24246 // available, decrement mode and reset base 24247 if (!done && (!this.base.disambiguate || this.state.tmp.disambiguate_count !== this.state.tmp.disambiguate_maxMax)) { 24248 // Rerun everything on each subcycle? This doesn't work currently. 24249 //this.initVars(this.akey) 24250 this.modeindex = 0; 24251 this.base.disambiguate = this.state.tmp.disambiguate_count; 24252 this.betterbase.disambiguate = this.state.tmp.disambiguate_count; 24253 if (!this.base.disambiguate) { 24254 // Evaluate here? 24255 this.initGivens = true; 24256 // If disambiguate is false set to true 24257 this.base.disambiguate = 1; 24258 // There may be changes 24259 for (var i = 0, ilen = this.lists[this.listpos][1].length; i < ilen; i += 1) { 24260 this.state.tmp.taintedItemIDs[this.lists[this.listpos][1][i].id] = true; 24261 } 24262 } else { 24263 this.disNames(); 24264 } 24265 } else if (done || this.state.tmp.disambiguate_count === this.state.tmp.disambiguate_maxMax) { 24266 if (done || this.modeindex === this.modes.length - 1) { 24267 // If this is the end, disambiguation failed. 24268 // Discard disambiguate=true (?) and set parameters 24269 var base = this.lists[this.listpos][0]; 24270 for (var i = 0, ilen = this.lists[this.listpos][1].length; i < ilen; i += 1) { 24271 this.state.tmp.taintedItemIDs[this.lists[this.listpos][1][i].id] = true; 24272 this.state.registry.registerAmbigToken(this.akey, "" + this.lists[this.listpos][1][i].id, base); 24273 } 24274 this.lists[this.listpos] = [this.betterbase, []]; 24275 } else { 24276 // If this is followed by year-suffix, keep 24277 // parameters and set disambiguate=true since it MIGHT 24278 // include the date, needed for year-suffix. 24279 // This may be a bit over-aggressive for cases in which the 24280 // disambiguate condition does not add the date 24281 this.modeindex = this.modes.length - 1; 24282 var base = this.lists[this.listpos][0]; 24283 base.disambiguate = true; 24284 for (var i = 0, ilen = this.lists[this.listpos][1].length; i < ilen; i += 1) { 24285 // Always tainting here might be a little over-aggressive, but a taint may be required. 24286 this.state.tmp.taintedItemIDs[this.lists[this.listpos][1][i].id] = true; 24287 this.state.registry.registerAmbigToken(this.akey, "" + this.lists[this.listpos][1][i].id, base); 24288 } 24289 } 24290 } 24291}; 24292 24293CSL.Disambiguation.prototype.disYears = function () { 24294 var pos, len, tokens, token; 24295 //SNIP-START 24296 if (this.debug) { 24297 this.state.sys.print("[3] === disYears =="); 24298 } 24299 //SNIP-END 24300 tokens = []; 24301 var base = this.lists[this.listpos][0]; 24302 if (this.clashes[1]) { 24303 // That is, if the initial increment on the ambigs group returns no 24304 // clashes, don't apply suffix. The condition is a necessary failsafe. 24305 // In original submission order 24306 for (var i = 0, ilen = this.state.registry.mylist.length; i < ilen; i += 1) { 24307 var origid = this.state.registry.mylist[i]; 24308 for (var j = 0, jlen = this.lists[this.listpos][1].length; j < jlen; j += 1) { 24309 var token = this.lists[this.listpos][1][j]; 24310 // Warning: token.id can be number. This should be fixed at a higher level in citeproc-js if poss. 24311 if (token.id == origid) { 24312 tokens.push(this.registry[token.id]); 24313 break; 24314 } 24315 } 24316 } 24317 } 24318 tokens.sort(this.state.registry.sorter.compareKeys); 24319 for (var pos = 0, len = tokens.length; pos < len; pos += 1) { 24320 base.year_suffix = ""+pos; 24321 var oldBase = this.state.registry.registry[tokens[pos].id].disambig; 24322 this.state.registry.registerAmbigToken(this.akey, "" + tokens[pos].id, base); 24323 if (CSL.ambigConfigDiff(oldBase,base)) { 24324 this.state.tmp.taintedItemIDs[tokens[pos].id] = true; 24325 } 24326 } 24327 this.lists[this.listpos] = [this.betterbase, []]; 24328}; 24329 24330CSL.Disambiguation.prototype.incrementDisambig = function () { 24331 //SNIP-START 24332 if (this.debug) { 24333 this.state.sys.print("\n[1] === incrementDisambig() ==="); 24334 } 24335 //SNIP-END 24336 if (this.initGivens) { 24337 this.initGivens = false; 24338 return false; 24339 } 24340 var maxed = false; 24341 var increment_names = true; 24342 if ("disNames" === this.modes[this.modeindex]) { 24343 // this.gnameset: the index pos of the current nameset 24344 // this.gname: the index pos of the current name w/in the current nameset 24345 24346 // Stages: 24347 // - Increment givenname (optional) 24348 // - Add a name (optional) 24349 // - Move to next nameset 24350 24351 // Incrementing is done forward-only on this.base. Values 24352 // that improve disambiguation results are copied to 24353 // this.betterbase, which is used to set the disambig parameters 24354 // in the processor registry. 24355 24356 24357 // Increment 24358 // Max val is always true if a level is inactive. 24359 increment_names = false; 24360 if ("number" !== typeof this.givensMax) { 24361 increment_names = true; 24362 } 24363 var increment_namesets = false; 24364 if ("number" !== typeof this.namesMax) { 24365 increment_namesets = true; 24366 } 24367 if ("number" === typeof this.givensMax) { 24368 if (this.base.givens.length && this.base.givens[this.gnameset][this.gname] < this.givensMax) { 24369 this.base.givens[this.gnameset][this.gname] += 1; 24370 } else { 24371 increment_names = true; 24372 } 24373 } 24374 if ("number" === typeof this.namesMax 24375 && increment_names) { 24376 if (this.state.opt["disambiguate-add-names"]) { 24377 increment_namesets = false; 24378 if (this.gname < this.namesMax) { 24379 this.base.names[this.gnameset] += 1; 24380 this.gname += 1; 24381 } else { 24382 increment_namesets = true; 24383 } 24384 } else { 24385 increment_namesets = true; 24386 } 24387 } 24388 if ("number" === typeof this.namesetsMax && increment_namesets) { 24389 if (this.gnameset < this.namesetsMax) { 24390 this.gnameset += 1; 24391 this.base.names[this.gnameset] = 1; 24392 this.gname = 0; 24393 } 24394 } 24395 //SNIP-START 24396 if (this.debug) { 24397 this.state.sys.print(" ------------------"); 24398 this.state.sys.print(" incremented values"); 24399 this.state.sys.print(" ------------------"); 24400 this.state.sys.print(" | gnameset: "+this.gnameset); 24401 this.state.sys.print(" | gname: "+this.gname); 24402 this.state.sys.print(" | names value: "+this.base.names[this.gnameset]); 24403 if (this.base.givens.length) { 24404 this.state.sys.print(" | givens value: "+this.base.givens[this.gnameset][this.gname]); 24405 } else { 24406 this.state.sys.print(" | givens value: nil"); 24407 } 24408 this.state.sys.print(" | namesetsMax: "+this.namesetsMax); 24409 this.state.sys.print(" | namesMax: "+this.namesMax); 24410 this.state.sys.print(" | givensMax: "+this.givensMax); 24411 } 24412 //SNIP-END 24413 if (("number" !== typeof this.namesetsMax || this.namesetsMax === -1 || this.gnameset === this.namesetsMax) 24414 && (!this.state.opt["disambiguate-add-names"] || "number" !== typeof this.namesMax || this.gname === this.namesMax) 24415 && ("number" != typeof this.givensMax || "undefined" === typeof this.base.givens[this.gnameset] || "undefined" === typeof this.base.givens[this.gnameset][this.gname] || this.base.givens[this.gnameset][this.gname] === this.givensMax)) { 24416 24417 24418 maxed = true; 24419 //SNIP-START 24420 if (this.debug) { 24421 this.state.sys.print(" MAXED"); 24422 } 24423 //SNIP-END 24424 } 24425 } else if ("disExtraText" === this.modes[this.modeindex]) { 24426 this.base.disambiguate += 1; 24427 this.betterbase.disambiguate += 1; 24428 } 24429 return maxed; 24430}; 24431 24432CSL.Disambiguation.prototype.initVars = function (akey) { 24433 var i, ilen, myIds, myItemBundles, myItems; 24434 //SNIP-START 24435 if (this.debug) { 24436 this.state.sys.print("[B] === initVars() ==="); 24437 } 24438 //SNIP-END 24439 this.lists = []; 24440 this.base = false; 24441 this.betterbase = false; 24442 this.akey = akey; 24443 24444 this.maxNamesByItemId = {}; 24445 24446 24447 myItemBundles = []; 24448 myIds = this.ambigcites[akey]; 24449 if (!myIds || !myIds.length) { 24450 return false; 24451 } 24452 var myItem = this.state.refetchItem("" + myIds[0]); 24453 this.getCiteData(myItem); 24454 this.base = CSL.getAmbigConfig.call(this.state); 24455 if (myIds && myIds.length > 1) { 24456 myItemBundles.push([this.maxNamesByItemId[myItem.id], myItem]); 24457 // Build a composite list of Items and associated 24458 // max names. This is messy, but it's the only 24459 // way to get the items sorted by the number of names 24460 // to be disambiguated. If they are in descending order 24461 // with name expansions, the processor will hang. 24462 for (var i = 1, ilen = myIds.length; i < ilen; i += 1) { 24463 myItem = this.state.refetchItem("" + myIds[i]); 24464 this.getCiteData(myItem, this.base); 24465 myItemBundles.push([this.maxNamesByItemId[myItem.id], myItem]); 24466 } 24467 myItemBundles.sort( 24468 function (a, b) { 24469 if (a[0] > b[0]) { 24470 return 1; 24471 } else if (a[0] < b[0]) { 24472 return -1; 24473 } else { 24474 if (a[1].id > b[1].id) { 24475 return 1; 24476 } else if (a[1].id < b[1].id) { 24477 return -1; 24478 } else { 24479 return 0; 24480 } 24481 } 24482 } 24483 ); 24484 myItems = []; 24485 for (var i = 0, ilen = myItemBundles.length; i < ilen; i += 1) { 24486 myItems.push(myItemBundles[i][1]); 24487 } 24488 this.lists.push([this.base, myItems]); 24489 this.Item = this.lists[0][1][0]; 24490 } else { 24491 this.Item = this.state.refetchItem("" + myIds[0]); 24492 } 24493 24494 this.modeindex = 0; 24495 if (this.state.citation.opt["disambiguate-add-names"] || true) { 24496 this.namesMax = this.maxNamesByItemId[this.Item.id][0]; 24497 } else { 24498 var namesMax = this.base.names[0]; 24499 for (var i=1,ilen=this.base.names.length;i<ilen;i+=1){ 24500 namesMax = Math.max(namesMax,this.base.names.names[i]); 24501 } 24502 } 24503 24504 24505 this.padBase(this.base); 24506 this.padBase(this.betterbase); 24507 this.base.year_suffix = false; 24508 this.base.disambiguate = false; 24509 this.betterbase.year_suffix = false; 24510 this.betterbase.disambiguate = false; 24511 if (this.state.citation.opt["givenname-disambiguation-rule"] === "by-cite" 24512 && this.state.opt["disambiguate-add-givenname"]) { 24513 this.givensMax = 2; 24514 } 24515 return true; 24516}; 24517 24518 24519CSL.Disambiguation.prototype.padBase = function (base) { 24520 for (var i = 0, ilen = base.names.length; i < ilen; i += 1) { 24521 if (!base.givens[i]) { 24522 base.givens[i] = []; 24523 } 24524 for (var j=0,jlen=base.names[i];j<jlen;j+=1) { 24525 if (!base.givens[i][j]) { 24526 base.givens[i][j] = 0; 24527 } 24528 } 24529 } 24530}; 24531 24532/** 24533 * Set available modes for disambiguation 24534 */ 24535CSL.Disambiguation.prototype.configModes = function () { 24536 var dagopt, gdropt; 24537 // Modes are function names prototyped to this instance. 24538 this.modes = []; 24539 dagopt = this.state.opt["disambiguate-add-givenname"]; 24540 gdropt = this.state.citation.opt["givenname-disambiguation-rule"]; 24541 if (this.state.opt['disambiguate-add-names'] || (dagopt && gdropt === "by-cite")) { 24542 this.modes.push("disNames"); 24543 } 24544 24545 if (this.state.opt.development_extensions.prioritize_disambiguate_condition) { 24546 if (this.state.opt.has_disambiguate) { 24547 this.modes.push("disExtraText"); 24548 } 24549 if (this.state.opt["disambiguate-add-year-suffix"]) { 24550 this.modes.push("disYears"); 24551 } 24552 } else { 24553 if (this.state.opt["disambiguate-add-year-suffix"]) { 24554 this.modes.push("disYears"); 24555 } 24556 if (this.state.opt.has_disambiguate) { 24557 this.modes.push("disExtraText"); 24558 } 24559 } 24560}; 24561 24562CSL.Disambiguation.prototype.getCiteData = function(Item, base) { 24563 // Initialize base if first set item seen 24564 if (!this.maxNamesByItemId[Item.id]) { 24565 CSL.getAmbiguousCite.call(this.state, Item, base); 24566 base = CSL.getAmbigConfig.call(this.state); 24567 this.maxNamesByItemId[Item.id] = CSL.getMaxVals.call(this.state); 24568 this.state.registry.registry[Item.id].disambig.givens = this.state.tmp.disambig_settings.givens.slice(); 24569 // Slice the nested lists as well. Without this, disambiguate_YearSuffixFiftyTwoEntriesByCite fails. 24570 for (var i=0,ilen=this.state.registry.registry[Item.id].disambig.givens.length;i<ilen;i+=1) { 24571 this.state.registry.registry[Item.id].disambig.givens[i] = this.state.tmp.disambig_settings.givens[i].slice(); 24572 } 24573 this.namesetsMax = this.state.registry.registry[Item.id].disambig.names.length - 1; 24574 if (!this.base) { 24575 this.base = base; 24576 this.betterbase = CSL.cloneAmbigConfig(base); 24577 } 24578 if (base.names.length < this.base.names.length) { 24579 // I don't know what would happen with discrepancies in the number 24580 // of namesets rendered on items, so we use the fewer of the two 24581 // and limit the other to that size. 24582 this.base = base; 24583 } 24584 // Padding. Within namesets, we use the longer of the two throughout. 24585 for (var i = 0, ilen = base.names.length; i < ilen; i += 1) { 24586 if (base.names[i] > this.base.names[i]) { 24587 // XXX The old must have been wrong surely. The new, I'm not sure. 24588 //this.base.givens[i] = this.base.givens[i].concat(this.base.givens[i].slice(this.base.names[i])); 24589 this.base.givens[i] = base.givens[i].slice(); 24590 this.base.names[i] = base.names[i]; 24591 this.betterbase.names = this.base.names.slice(); 24592 this.betterbase.givens = this.base.givens.slice(); 24593 this.padBase(this.base); 24594 this.padBase(this.betterbase); 24595 } 24596 } 24597 // This shouldn't be necessary 24598 // getAmbiguousCite() should return a valid and complete 24599 // givens segment under all conditions, but it does not 24600 // do so for institution authors, so we clean up after it 24601 // here. 24602 // Relevant test: sort_ChicagoYearSuffix2 24603 this.betterbase.givens = this.base.givens.slice(); 24604 for (var j = 0, jlen = this.base.givens.length; j < jlen; j += 1) { 24605 this.betterbase.givens[j] = this.base.givens[j].slice(); 24606 } 24607 } 24608}; 24609 24610CSL.Disambiguation.prototype.captureStepToBase = function() { 24611 // Be paranoid about the presence of givens 24612 if (this.state.citation.opt["givenname-disambiguation-rule"] === "by-cite" 24613 && this.base.givens && this.base.givens.length) { 24614 if ("undefined" !== typeof this.base.givens[this.gnameset][this.gname]) { 24615 if (this.betterbase.givens.length < this.base.givens.length) { 24616 this.betterbase.givens = JSON.parse(JSON.stringify(this.base.givens)); 24617 } 24618 this.betterbase.givens[this.gnameset][this.gname] = this.base.givens[this.gnameset][this.gname]; 24619 } 24620 } 24621 this.betterbase.names[this.gnameset] = this.base.names[this.gnameset]; 24622}; 24623 24624CSL.Engine.prototype.getJurisdictionList = function (jurisdiction) { 24625 var jurisdictionList = []; 24626 var jurisdictionElems = jurisdiction.split(":"); 24627 for (var j=jurisdictionElems.length;j>0;j--) { 24628 var composedID = jurisdictionElems.slice(0,j).join(":"); 24629 jurisdictionList.push(composedID); 24630 if (this.opt.jurisdiction_fallbacks[composedID]) { 24631 var fallback = this.opt.jurisdiction_fallbacks[composedID]; 24632 jurisdictionList.push(fallback); 24633 } 24634 } 24635 if (jurisdictionList.indexOf("us") === -1) { 24636 jurisdictionList.push("us"); 24637 } 24638 return jurisdictionList; 24639}; 24640 24641CSL.Engine.prototype.loadStyleModule = function (jurisdiction, xmlSource, skipFallback) { 24642 var myFallback = null; 24643 var macroCount = 0; 24644 this.juris[jurisdiction] = {}; 24645 var myXml = CSL.setupXml(xmlSource); 24646 myXml.addMissingNameNodes(myXml.dataObj); 24647 myXml.addInstitutionNodes(myXml.dataObj); 24648 myXml.insertPublisherAndPlace(myXml.dataObj); 24649 myXml.flagDateMacros(myXml.dataObj); 24650 var myNodes = myXml.getNodesByName(myXml.dataObj, "law-module"); 24651 for (var i=0,ilen=myNodes.length;i<ilen;i++) { 24652 var myTypes = myXml.getAttributeValue(myNodes[i],"types"); 24653 if (myTypes) { 24654 this.juris[jurisdiction].types = {}; 24655 myTypes = myTypes.split(/\s+/); 24656 for (var j=0,jlen=myTypes.length;j<jlen;j++) { 24657 this.juris[jurisdiction].types[myTypes[j]] = true; 24658 } 24659 } 24660 if (!skipFallback) { 24661 myFallback = myXml.getAttributeValue(myNodes[i],"fallback"); 24662 if (myFallback) { 24663 if (jurisdiction !== "us") { 24664 this.opt.jurisdiction_fallbacks[jurisdiction] = myFallback; 24665 } 24666 } 24667 } 24668 } 24669 var lang = this.opt.lang ? this.opt.lang : this.opt["default-locale"][0]; 24670 CSL.SET_COURT_CLASSES(this, lang, myXml, myXml.dataObj); 24671 24672 if (!this.juris[jurisdiction].types) { 24673 this.juris[jurisdiction].types = CSL.MODULE_TYPES; 24674 } 24675 var myNodes = myXml.getNodesByName(myXml.dataObj, "macro"); 24676 for (var i=0,ilen=myNodes.length;i<ilen;i++) { 24677 var myName = myXml.getAttributeValue(myNodes[i], "name"); 24678 if (!CSL.MODULE_MACROS[myName]) { 24679 CSL.debug("CSL: skipping non-modular macro name \"" + myName + "\" in module context"); 24680 continue; 24681 } 24682 macroCount++; 24683 this.juris[jurisdiction][myName] = []; 24684 // Must use the same XML parser for style and modules. 24685 this.buildTokenLists(myNodes[i], this.juris[jurisdiction][myName]); 24686 this.configureTokenList(this.juris[jurisdiction][myName]); 24687 } 24688 //if (macroCount < Object.keys(CSL.MODULE_MACROS).length) { 24689 // var missing = []; 24690 // throw "CSL ERROR: Incomplete jurisdiction style module for: " + jurisdiction; 24691 //} 24692 return myFallback; 24693}; 24694 24695CSL.Engine.prototype.retrieveAllStyleModules = function (jurisdictionList) { 24696 var ret = {}; 24697 var preferences = this.locale[this.opt.lang].opts["jurisdiction-preference"]; 24698 preferences = preferences ? preferences : []; 24699 preferences = [""].concat(preferences); 24700 for (var i=preferences.length-1;i>-1;i--) { 24701 var preference = preferences[i]; 24702 for (var j=0,jlen=jurisdictionList.length;j<jlen;j++) { 24703 var jurisdiction = jurisdictionList[j]; 24704 // If we've "seen" it, we have it already, or we're not going to get it. 24705 if (this.opt.jurisdictions_seen[jurisdiction]) { 24706 continue; 24707 } 24708 // Try to get the module 24709 var res = this.sys.retrieveStyleModule(jurisdiction, preference); 24710 // If we fail and we've run out of preferences, mark as "seen" 24711 // Otherwise mark as "seen" if we get something. 24712 if ((!res && !preference) || res) { 24713 this.opt.jurisdictions_seen[jurisdiction] = true; 24714 } 24715 // Don't memo unless get got style code. 24716 if (!res) { 24717 continue; 24718 } 24719 ret[jurisdiction] = res; 24720 } 24721 } 24722 // Give 'em what we got. 24723 return ret; 24724}; 24725 24726CSL.ParticleList = (function() { 24727 var always_dropping_1 = [[[0,1], null]]; 24728 var always_dropping_3 = [[[0,3], null]]; 24729 var always_non_dropping_1 = [[null, [0,1]]]; 24730 var always_non_dropping_2 = [[null, [0,2]]]; 24731 var always_non_dropping_3 = [[null, [0,3]]]; 24732 var either_1 = [[null, [0,1]],[[0,1],null]]; 24733 var either_2 = [[null, [0,2]],[[0,2],null]]; 24734 var either_1_dropping_best = [[[0,1],null],[null, [0,1]]]; 24735 var either_2_dropping_best = [[[0,2],null],[null, [0,2]]]; 24736 var either_3_dropping_best = [[[0,3],null],[null, [0,3]]]; 24737 var non_dropping_2_alt_dropping_1_non_dropping_1 = [[null, [0,2]], [[0,1], [1,2]]]; 24738 var PARTICLES = [ 24739 ["'s", always_non_dropping_1], 24740 ["'s-", always_non_dropping_1], 24741 ["'t", always_non_dropping_1], 24742 ["a", always_non_dropping_1], 24743 ["aan 't", always_non_dropping_2], 24744 ["aan de", always_non_dropping_2], 24745 ["aan den", always_non_dropping_2], 24746 ["aan der", always_non_dropping_2], 24747 ["aan het", always_non_dropping_2], 24748 ["aan t", always_non_dropping_2], 24749 ["aan", always_non_dropping_1], 24750 ["ad-", either_1], 24751 ["adh-", either_1], 24752 ["af", either_1], 24753 ["al", either_1], 24754 ["al-", either_1], 24755 ["am de", always_non_dropping_2], 24756 ["am", always_non_dropping_1], 24757 ["an-", either_1], 24758 ["ar-", either_1], 24759 ["as-", either_1], 24760 ["ash-", either_1], 24761 ["at-", either_1], 24762 ["ath-", either_1], 24763 ["auf dem", either_2_dropping_best], 24764 ["auf den", either_2_dropping_best], 24765 ["auf der", either_2_dropping_best], 24766 ["auf ter", always_non_dropping_2], 24767 ["auf", either_1_dropping_best], 24768 ["aus 'm", either_2_dropping_best], 24769 ["aus dem", either_2_dropping_best], 24770 ["aus den", either_2_dropping_best], 24771 ["aus der", either_2_dropping_best], 24772 ["aus m", either_2_dropping_best], 24773 ["aus", either_1_dropping_best], 24774 ["aus'm", either_2_dropping_best], 24775 ["az-", either_1], 24776 ["aÅ¡-", either_1], 24777 ["aá¸-", either_1], 24778 ["aá¸-", either_1], 24779 ["aá¹£-", either_1], 24780 ["aá¹-", either_1], 24781 ["aṯ-", either_1], 24782 ["aáº-", either_1], 24783 ["ben", always_non_dropping_1], 24784 ["bij 't", always_non_dropping_2], 24785 ["bij de", always_non_dropping_2], 24786 ["bij den", always_non_dropping_2], 24787 ["bij het", always_non_dropping_2], 24788 ["bij t", always_non_dropping_2], 24789 ["bij", always_non_dropping_1], 24790 ["bin", always_non_dropping_1], 24791 ["boven d", always_non_dropping_2], 24792 ["boven d'", always_non_dropping_2], 24793 ["d", always_non_dropping_1], 24794 ["d'", either_1], 24795 ["da", either_1], 24796 ["dal", always_non_dropping_1], 24797 ["dal'", always_non_dropping_1], 24798 ["dall'", always_non_dropping_1], 24799 ["dalla", always_non_dropping_1], 24800 ["das", either_1], 24801 ["de die le", always_non_dropping_3], 24802 ["de die", always_non_dropping_2], 24803 ["de l", always_non_dropping_2], 24804 ["de l'", always_non_dropping_2], 24805 ["de la", non_dropping_2_alt_dropping_1_non_dropping_1], 24806 ["de las", non_dropping_2_alt_dropping_1_non_dropping_1], 24807 ["de le", always_non_dropping_2], 24808 ["de li", either_2], 24809 ["de van der", always_non_dropping_3], 24810 ["de", either_1], 24811 ["de'", either_1], 24812 ["deca", always_non_dropping_1], 24813 ["degli", either_1], 24814 ["dei", either_1], 24815 ["del", either_1], 24816 ["dela", always_dropping_1], 24817 ["dell'", either_1], 24818 ["della", either_1], 24819 ["delle", either_1], 24820 ["dello", either_1], 24821 ["den", either_1], 24822 ["der", either_1], 24823 ["des", either_1], 24824 ["di", either_1], 24825 ["die le", always_non_dropping_2], 24826 ["do", always_non_dropping_1], 24827 ["don", always_non_dropping_1], 24828 ["dos", either_1], 24829 ["du", either_1], 24830 ["ed-", either_1], 24831 ["edh-", either_1], 24832 ["el", either_1], 24833 ["el-", either_1], 24834 ["en-", either_1], 24835 ["er-", either_1], 24836 ["es-", either_1], 24837 ["esh-", either_1], 24838 ["et-", either_1], 24839 ["eth-", either_1], 24840 ["ez-", either_1], 24841 ["eÅ¡-", either_1], 24842 ["eá¸-", either_1], 24843 ["eá¸-", either_1], 24844 ["eá¹£-", either_1], 24845 ["eá¹-", either_1], 24846 ["eṯ-", either_1], 24847 ["eáº-", either_1], 24848 ["het", always_non_dropping_1], 24849 ["i", always_non_dropping_1], 24850 ["il", always_dropping_1], 24851 ["im", always_non_dropping_1], 24852 ["in 't", always_non_dropping_2], 24853 ["in de", always_non_dropping_2], 24854 ["in den", always_non_dropping_2], 24855 ["in der", either_2], 24856 ["in het", always_non_dropping_2], 24857 ["in t", always_non_dropping_2], 24858 ["in", always_non_dropping_1], 24859 ["l", always_non_dropping_1], 24860 ["l'", always_non_dropping_1], 24861 ["la", always_non_dropping_1], 24862 ["las", always_non_dropping_1], 24863 ["le", always_non_dropping_1], 24864 ["les", either_1], 24865 ["lo", either_1], 24866 ["los", always_non_dropping_1], 24867 ["lou", always_non_dropping_1], 24868 ["of", always_non_dropping_1], 24869 ["onder 't", always_non_dropping_2], 24870 ["onder de", always_non_dropping_2], 24871 ["onder den", always_non_dropping_2], 24872 ["onder het", always_non_dropping_2], 24873 ["onder t", always_non_dropping_2], 24874 ["onder", always_non_dropping_1], 24875 ["op 't", always_non_dropping_2], 24876 ["op de", either_2], 24877 ["op den", always_non_dropping_2], 24878 ["op der", always_non_dropping_2], 24879 ["op gen", always_non_dropping_2], 24880 ["op het", always_non_dropping_2], 24881 ["op t", always_non_dropping_2], 24882 ["op ten", always_non_dropping_2], 24883 ["op", always_non_dropping_1], 24884 ["over 't", always_non_dropping_2], 24885 ["over de", always_non_dropping_2], 24886 ["over den", always_non_dropping_2], 24887 ["over het", always_non_dropping_2], 24888 ["over t", always_non_dropping_2], 24889 ["over", always_non_dropping_1], 24890 ["s", always_non_dropping_1], 24891 ["s'", always_non_dropping_1], 24892 ["sen", always_dropping_1], 24893 ["t", always_non_dropping_1], 24894 ["te", always_non_dropping_1], 24895 ["ten", always_non_dropping_1], 24896 ["ter", always_non_dropping_1], 24897 ["tho", always_non_dropping_1], 24898 ["thoe", always_non_dropping_1], 24899 ["thor", always_non_dropping_1], 24900 ["to", always_non_dropping_1], 24901 ["toe", always_non_dropping_1], 24902 ["tot", always_non_dropping_1], 24903 ["uijt 't", always_non_dropping_2], 24904 ["uijt de", always_non_dropping_2], 24905 ["uijt den", always_non_dropping_2], 24906 ["uijt te de", always_non_dropping_3], 24907 ["uijt ten", always_non_dropping_2], 24908 ["uijt", always_non_dropping_1], 24909 ["uit 't", always_non_dropping_2], 24910 ["uit de", always_non_dropping_2], 24911 ["uit den", always_non_dropping_2], 24912 ["uit het", always_non_dropping_2], 24913 ["uit t", always_non_dropping_2], 24914 ["uit te de", always_non_dropping_3], 24915 ["uit ten", always_non_dropping_2], 24916 ["uit", always_non_dropping_1], 24917 ["unter", always_non_dropping_1], 24918 ["v", always_non_dropping_1], 24919 ["v.", always_non_dropping_1], 24920 ["v.d.", always_non_dropping_1], 24921 ["van 't", always_non_dropping_2], 24922 ["van de l", always_non_dropping_3], 24923 ["van de l'", always_non_dropping_3], 24924 ["van de", always_non_dropping_2], 24925 ["van de", always_non_dropping_2], 24926 ["van den", always_non_dropping_2], 24927 ["van der", always_non_dropping_2], 24928 ["van gen", always_non_dropping_2], 24929 ["van het", always_non_dropping_2], 24930 ["van la", always_non_dropping_2], 24931 ["van t", always_non_dropping_2], 24932 ["van ter", always_non_dropping_2], 24933 ["van van de", always_non_dropping_3], 24934 ["van", either_1], 24935 ["vander", always_non_dropping_1], 24936 ["vd", always_non_dropping_1], 24937 ["ver", always_non_dropping_1], 24938 ["vom und zum", always_dropping_3], 24939 ["vom", either_1], 24940 ["von 't", always_non_dropping_2], 24941 ["von dem", either_2_dropping_best], 24942 ["von den", either_2_dropping_best], 24943 ["von der", either_2_dropping_best], 24944 ["von t", always_non_dropping_2], 24945 ["von und zu", either_3_dropping_best], 24946 ["von zu", either_2_dropping_best], 24947 ["von", either_1_dropping_best], 24948 ["voor 't", always_non_dropping_2], 24949 ["voor de", always_non_dropping_2], 24950 ["voor den", always_non_dropping_2], 24951 ["voor in 't", always_non_dropping_3], 24952 ["voor in t", always_non_dropping_3], 24953 ["voor", always_non_dropping_1], 24954 ["vor der", either_2_dropping_best], 24955 ["vor", either_1_dropping_best], 24956 ["z", always_dropping_1], 24957 ["ze", always_dropping_1], 24958 ["zu", either_1_dropping_best], 24959 ["zum", either_1], 24960 ["zur", either_1] 24961 ]; 24962 return PARTICLES; 24963}()); 24964 24965CSL.parseParticles = (function(){ 24966 function splitParticles(nameValue, firstNameFlag, caseOverride) { 24967 // Parse particles out from name fields. 24968 // * nameValue (string) is the field content to be parsed. 24969 // * firstNameFlag (boolean) parse trailing particles 24970 // (default is to parse leading particles) 24971 // * caseOverride (boolean) include all but one word in particle set 24972 // (default is to include only words with lowercase first char) 24973 // [caseOverride is not used in this application] 24974 // Returns an array with: 24975 // * (boolean) flag indicating whether a particle was found 24976 // * (string) the name after removal of particles 24977 // * (array) the list of particles found 24978 var origNameValue = nameValue; 24979 nameValue = caseOverride ? nameValue.toLowerCase() : nameValue; 24980 var particleList = []; 24981 var rex; 24982 var hasParticle; 24983 if (firstNameFlag) { 24984 nameValue = nameValue.split("").reverse().join(""); 24985 rex = CSL.PARTICLE_GIVEN_REGEXP; 24986 } else { 24987 rex = CSL.PARTICLE_FAMILY_REGEXP; 24988 } 24989 var m = nameValue.match(rex); 24990 while (m) { 24991 var m1 = firstNameFlag ? m[1].split("").reverse().join("") : m[1]; 24992 var firstChar = m ? m1 : false; 24993 var firstChar = firstChar ? m1.replace(/^[-\'\u02bb\u2019\s]*(.).*$/, "$1") : false; 24994 hasParticle = firstChar ? firstChar.toUpperCase() !== firstChar : false; 24995 if (!hasParticle) { 24996 break; 24997 } 24998 if (firstNameFlag) { 24999 particleList.push(origNameValue.slice(m1.length * -1)); 25000 origNameValue = origNameValue.slice(0,m1.length * -1); 25001 } else { 25002 particleList.push(origNameValue.slice(0,m1.length)); 25003 origNameValue = origNameValue.slice(m1.length); 25004 } 25005 //particleList.push(m1); 25006 nameValue = m[2]; 25007 m = nameValue.match(rex); 25008 } 25009 if (firstNameFlag) { 25010 nameValue = nameValue.split("").reverse().join(""); 25011 particleList.reverse(); 25012 for (var i=1,ilen=particleList.length;i<ilen;i++) { 25013 if (particleList[i].slice(0, 1) == " ") { 25014 particleList[i-1] += " "; 25015 } 25016 } 25017 for (var i=0,ilen=particleList.length;i<ilen;i++) { 25018 if (particleList[i].slice(0, 1) == " ") { 25019 particleList[i] = particleList[i].slice(1); 25020 } 25021 } 25022 nameValue = origNameValue.slice(0, nameValue.length); 25023 } else { 25024 nameValue = origNameValue.slice(nameValue.length * -1); 25025 } 25026 return [hasParticle, nameValue, particleList]; 25027 } 25028 function trimLast(str) { 25029 var lastChar = str.slice(-1); 25030 str = str.trim(); 25031 if (lastChar === " " && ["\'", "\u2019"].indexOf(str.slice(-1)) > -1) { 25032 str += " "; 25033 } 25034 return str; 25035 } 25036 function parseSuffix(nameObj) { 25037 if (!nameObj.suffix && nameObj.given) { 25038 var m = nameObj.given.match(/(\s*,!*\s*)/); 25039 if (m) { 25040 var idx = nameObj.given.indexOf(m[1]); 25041 var possible_suffix = nameObj.given.slice(idx + m[1].length); 25042 var possible_comma = nameObj.given.slice(idx, idx + m[1].length).replace(/\s*/g, ""); 25043 if (possible_suffix.replace(/\./g, "") === 'et al' && !nameObj["dropping-particle"]) { 25044 // This hack covers the case where "et al." is explicitly used in the 25045 // authorship information of the work. 25046 nameObj["dropping-particle"] = possible_suffix; 25047 nameObj["comma-dropping-particle"] = ","; 25048 } else { 25049 if (possible_comma.length === 2) { 25050 nameObj["comma-suffix"] = true; 25051 } 25052 nameObj.suffix = possible_suffix; 25053 } 25054 nameObj.given = nameObj.given.slice(0, idx); 25055 } 25056 } 25057 } 25058 return function(nameObj) { 25059 // Extract and set non-dropping particle(s) from family name field 25060 var res = splitParticles(nameObj.family); 25061 var lastNameValue = res[1]; 25062 var lastParticleList = res[2]; 25063 nameObj.family = lastNameValue; 25064 var nonDroppingParticle = trimLast(lastParticleList.join("")); 25065 if (nonDroppingParticle) { 25066 nameObj['non-dropping-particle'] = nonDroppingParticle; 25067 } 25068 // Split off suffix first of all 25069 parseSuffix(nameObj); 25070 // Extract and set dropping particle(s) from given name field 25071 var res = splitParticles(nameObj.given, true); 25072 var firstNameValue = res[1]; 25073 var firstParticleList = res[2]; 25074 nameObj.given = firstNameValue; 25075 var droppingParticle = firstParticleList.join("").trim(); 25076 if (droppingParticle) { 25077 nameObj['dropping-particle'] = droppingParticle; 25078 } 25079 }; 25080}()); 25081 25082 25083module.exports = CSL 25084 25085/***/ }) 25086/******/ ]); 25087}); 25088//# sourceMappingURL=citeproc.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.