cachedTypes.js (20644B)
1 /* 2 ***** BEGIN LICENSE BLOCK ***** 3 4 Copyright © 2009 Center for History and New Media 5 George Mason University, Fairfax, Virginia, USA 6 http://zotero.org 7 8 This file is part of Zotero. 9 10 Zotero is free software: you can redistribute it and/or modify 11 it under the terms of the GNU Affero General Public License as published by 12 the Free Software Foundation, either version 3 of the License, or 13 (at your option) any later version. 14 15 Zotero is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU Affero General Public License for more details. 19 20 You should have received a copy of the GNU Affero General Public License 21 along with Zotero. If not, see <http://www.gnu.org/licenses/>. 22 23 ***** END LICENSE BLOCK ***** 24 */ 25 26 27 /* 28 * Base function for retrieving ids and names of static types stored in the DB 29 * (e.g. creatorType, fileType, charset, itemType) 30 * 31 * Extend using the following code within a child constructor: 32 * 33 * Zotero.CachedTypes.apply(this, arguments); 34 * this.constructor.prototype = new Zotero.CachedTypes(); 35 * 36 * And the following properties: 37 * 38 * this._typeDesc = ''; 39 * this._typeDescPlural = ''; 40 * this._idCol = ''; 41 * this._nameCol = ''; 42 * this._table = ''; 43 * 44 * Optional properties: 45 * 46 * this._allowAdd: Allow new types to be added via .add(name) 47 * this._ignoreCase: Ignore case when looking for types, and add new types as lowercase 48 * 49 * And add .init() to zotero.js 50 */ 51 Zotero.CachedTypes = function() { 52 this._types = null; 53 this._typesArray = null; 54 var self = this; 55 56 // Override these variables in child classes 57 this._typeDesc = ''; 58 this._idCol = ''; 59 this._nameCol = ''; 60 this._table = ''; 61 this._allowAdd = false; 62 this._ignoreCase = false; 63 this._hasCustom = false; 64 65 66 this.init = Zotero.Promise.coroutine(function* () { 67 this._types = {}; 68 this._typesArray = []; 69 70 var types = yield this._getTypesFromDB(); 71 for (let i=0; i<types.length; i++) { 72 this._cacheTypeData(types[i]); 73 } 74 }); 75 76 77 this.getName = function (idOrName) { 78 if (!this._types) { 79 throw new Zotero.Exception.UnloadedDataException( 80 Zotero.Utilities.capitalize(this._typeDesc) + " data not yet loaded" 81 ); 82 } 83 84 if (this._ignoreCase) { 85 idOrName = idOrName + ''; 86 idOrName = idOrName.toLowerCase(); 87 } 88 89 if (!this._types['_' + idOrName]) { 90 Zotero.debug(`Unknown ${this._typeDesc} '${idOrName}'`, 1); 91 return ''; 92 } 93 94 return this._types['_' + idOrName]['name']; 95 } 96 97 98 this.getID = function (idOrName) { 99 if (!this._types) { 100 throw new Zotero.Exception.UnloadedDataException( 101 Zotero.Utilities.capitalize(this._typeDesc) + " data not yet loaded" 102 ); 103 } 104 105 if (this._ignoreCase) { 106 idOrName = idOrName + ''; 107 idOrName = idOrName.toLowerCase(); 108 } 109 110 if (!this._types['_' + idOrName]) { 111 Zotero.debug(`Unknown ${this._typeDesc} '${idOrName}'`, 1); 112 return false; 113 } 114 115 return this._types['_' + idOrName]['id']; 116 } 117 118 119 this.getAll = this.getTypes = function () { 120 if (!this._typesArray) { 121 throw new Zotero.Exception.UnloadedDataException( 122 Zotero.Utilities.capitalize(this._typeDesc) + " data not yet loaded" 123 ); 124 } 125 return this._typesArray; 126 } 127 128 129 // Currently used only for item types 130 this.isCustom = function (idOrName) { 131 return this._types['_' + idOrName] && this._types['_' + idOrName].custom ? this._types['_' + idOrName].custom : false; 132 } 133 134 135 /** 136 * Add a new type to the data and return its id. If the type already exists, return its id. 137 * 138 * @param {String} name - Type name to add 139 * @return {Integer|False} - The type id (new or existing), or false if invalid type name 140 */ 141 this.add = Zotero.Promise.coroutine(function* (name) { 142 if (!this._allowAdd) { 143 throw new Error("New " + this._typeDescPlural + " cannot be added"); 144 } 145 146 if (typeof name != 'string' || name === "") { 147 throw new Error("'name' must be a string"); 148 } 149 150 var id = this.getID(name); 151 if (id) { 152 return id; 153 } 154 155 if (this._ignoreCase) { 156 name = name.toLowerCase(); 157 } 158 159 var allow = this._valueCheck(name); 160 if (!allow) { 161 return false; 162 } 163 164 var sql = "INSERT INTO " + this._table + " (" + this._nameCol + ") VALUES (?)"; 165 yield Zotero.DB.queryAsync(sql, name); 166 167 sql = "SELECT " + this._idCol + " FROM " + this._table + " WHERE " + this._nameCol + "=?"; 168 var id = yield Zotero.DB.valueQueryAsync(sql, name); 169 170 this._cacheTypeData({ 171 id: id, 172 name: name 173 }); 174 175 return id; 176 }); 177 178 179 this._valueCheck = function (name) { 180 return true; 181 } 182 183 184 /** 185 * @return {Promise} 186 */ 187 this._getTypesFromDB = function (where, params) { 188 return Zotero.DB.queryAsync( 189 'SELECT ' + this._idCol + ' AS id, ' 190 + this._nameCol + ' AS name' 191 + (this._hasCustom ? ', custom' : '') 192 + ' FROM ' + this._table 193 + (where ? ' ' + where : ''), 194 params ? params : false 195 ); 196 }; 197 198 199 this._cacheTypeData = function (type) { 200 // Store as both id and name for access by either 201 var typeData = { 202 id: type.id, 203 name: type.name, 204 custom: this._hasCustom ? !!type.custom : false 205 } 206 this._types['_' + type.id] = typeData; 207 if (this._ignoreCase) { 208 this._types['_' + type.name.toLowerCase()] = this._types['_' + type.id]; 209 } 210 else { 211 this._types['_' + type.name] = this._types['_' + type.id]; 212 } 213 this._typesArray.push(typeData); 214 } 215 } 216 217 218 Zotero.CreatorTypes = new function() { 219 Zotero.CachedTypes.apply(this, arguments); 220 this.constructor.prototype = new Zotero.CachedTypes(); 221 222 this.isValidForItemType = isValidForItemType; 223 224 this._typeDesc = 'creator type'; 225 this._typeDescPlural = 'creator types'; 226 this._idCol = 'creatorTypeID'; 227 this._nameCol = 'creatorType'; 228 this._table = 'creatorTypes'; 229 230 var _primaryIDCache; 231 var _hasCreatorTypeCache = {}; 232 var _creatorTypesByItemType = {}; 233 var _isValidForItemType = {}; 234 235 236 this.init = Zotero.Promise.coroutine(function* () { 237 yield this.constructor.prototype.init.apply(this); 238 239 var sql = "SELECT itemTypeID, creatorTypeID AS id, creatorType AS name, primaryField " 240 + "FROM itemTypeCreatorTypes NATURAL JOIN creatorTypes"; 241 var rows = yield Zotero.DB.queryAsync(sql); 242 for (let i=0; i<rows.length; i++) { 243 let row = rows[i]; 244 let itemTypeID = row.itemTypeID; 245 if (!_creatorTypesByItemType[itemTypeID]) { 246 _creatorTypesByItemType[itemTypeID] = []; 247 } 248 _creatorTypesByItemType[itemTypeID].push({ 249 id: row.id, 250 name: row.name, 251 primaryField: row.primaryField, 252 localizedName: this.getLocalizedString(row.name) 253 }); 254 } 255 // Sort primary field first, then by localized name 256 for (let itemTypeID in _creatorTypesByItemType) { 257 _creatorTypesByItemType[itemTypeID].sort((a, b) => { 258 if (a.primaryField != b.primaryField) return b.primaryField - a.primaryField; 259 return Zotero.localeCompare(a.localizedName, b.localizedName); 260 }); 261 _creatorTypesByItemType[itemTypeID].forEach((x) => { 262 delete x.primaryField; 263 delete x.localizedName; 264 }); 265 } 266 267 // Load primary creator type ids 268 _primaryIDCache = {}; 269 var sql = "SELECT itemTypeID, creatorTypeID FROM itemTypeCreatorTypes " 270 + "WHERE primaryField=1"; 271 var rows = yield Zotero.DB.queryAsync(sql); 272 for (let i=0; i<rows.length; i++) { 273 let row = rows[i]; 274 _primaryIDCache[row.itemTypeID] = row.creatorTypeID; 275 } 276 }); 277 278 279 this.getTypesForItemType = function (itemTypeID) { 280 if (!_creatorTypesByItemType[itemTypeID]) { 281 return []; 282 } 283 return _creatorTypesByItemType[itemTypeID]; 284 } 285 286 287 function isValidForItemType(creatorTypeID, itemTypeID) { 288 if (_isValidForItemType[itemTypeID] && typeof _isValidForItemType[itemTypeID][creatorTypeID] != 'undefined') { 289 return _isValidForItemType[itemTypeID][creatorTypeID]; 290 } 291 292 var valid = false; 293 var types = this.getTypesForItemType(itemTypeID); 294 for (let type of types) { 295 if (type.id == creatorTypeID) { 296 valid = true; 297 break; 298 } 299 } 300 301 if (!_isValidForItemType[itemTypeID]) { 302 _isValidForItemType[itemTypeID] = {}; 303 } 304 _isValidForItemType[itemTypeID][creatorTypeID] = valid; 305 return valid; 306 } 307 308 309 this.getLocalizedString = function(idOrName) { 310 return Zotero.getString("creatorTypes."+this.getName(idOrName)); 311 } 312 313 314 this.itemTypeHasCreators = function (itemTypeID) { 315 if (typeof _hasCreatorTypeCache[itemTypeID] != 'undefined') { 316 return _hasCreatorTypeCache[itemTypeID]; 317 } 318 _hasCreatorTypeCache[itemTypeID] = !!this.getTypesForItemType(itemTypeID).length; 319 return _hasCreatorTypeCache[itemTypeID]; 320 } 321 322 323 this.getPrimaryIDForType = function (itemTypeID) { 324 if (!_primaryIDCache) { 325 throw new Zotero.Exception.UnloadedDataException( 326 "Primary creator types not yet loaded" 327 ); 328 } 329 330 if (_primaryIDCache[itemTypeID] === undefined) { 331 return false; 332 } 333 334 return _primaryIDCache[itemTypeID]; 335 } 336 } 337 338 339 Zotero.ItemTypes = new function() { 340 Zotero.CachedTypes.apply(this, arguments); 341 this.constructor.prototype = new Zotero.CachedTypes(); 342 343 this.customIDOffset = 10000; 344 345 this._typeDesc = 'item type'; 346 this._typeDescPlural = 'item types'; 347 this._idCol = 'itemTypeID'; 348 this._nameCol = 'typeName'; 349 this._table = 'itemTypesCombined'; 350 this._hasCustom = true; 351 352 var _primaryTypes; 353 var _secondaryTypes; 354 var _hiddenTypes; 355 356 var _numPrimary = 5; 357 358 var _customImages = {}; 359 var _customLabels = {}; 360 361 362 this.init = Zotero.Promise.coroutine(function* () { 363 yield this.constructor.prototype.init.apply(this); 364 365 // TODO: get rid of ' AND itemTypeID!=5' and just remove display=2 366 // from magazineArticle in system.sql 367 _primaryTypes = yield this._getTypesFromDB('WHERE (display=2 AND itemTypeID!=5) LIMIT ' + _numPrimary); 368 369 // Secondary types 370 _secondaryTypes = yield this._getTypesFromDB('WHERE display IN (1,2)'); 371 372 // Hidden types 373 _hiddenTypes = yield this._getTypesFromDB('WHERE display=0') 374 375 // Custom labels and icons 376 var sql = "SELECT customItemTypeID AS id, label, icon FROM customItemTypes"; 377 var rows = yield Zotero.DB.queryAsync(sql); 378 for (let i=0; i<rows.length; i++) { 379 let row = rows[i]; 380 let id = row.id; 381 _customLabels[id] = row.label; 382 _customImages[id] = row.icon; 383 } 384 }); 385 386 387 this.getPrimaryTypes = function () { 388 if (!_primaryTypes) { 389 throw new Zotero.Exception.UnloadedDataException("Primary item type data not yet loaded"); 390 } 391 392 var mru = Zotero.Prefs.get('newItemTypeMRU'); 393 if (mru && mru.length) { 394 // Get types from the MRU list 395 mru = new Set( 396 mru.split(',') 397 .slice(0, _numPrimary) 398 .map(id => parseInt(id)) 399 // Ignore 'webpage' item type 400 .filter(id => !isNaN(id) && id != 13) 401 ); 402 403 // Add types from defaults until we reach our limit 404 for (let i = 0; i < _primaryTypes.length && mru.size < _numPrimary; i++) { 405 mru.add(_primaryTypes[i].id); 406 } 407 408 return Array.from(mru).map(id => ({ id, name: this.getName(id) })); 409 } 410 411 return _primaryTypes; 412 } 413 414 this.getSecondaryTypes = function () { 415 if (!_secondaryTypes) { 416 throw new Zotero.Exception.UnloadedDataException("Secondary item type data not yet loaded"); 417 } 418 return _secondaryTypes; 419 } 420 421 this.getHiddenTypes = function () { 422 if (!_hiddenTypes) { 423 throw new Zotero.Exception.UnloadedDataException("Hidden item type data not yet loaded"); 424 } 425 return _hiddenTypes; 426 } 427 428 this.getLocalizedString = function (idOrName) { 429 var typeName = this.getName(idOrName); 430 431 // For custom types, use provided label 432 if (this.isCustom(idOrName)) { 433 var id = this.getID(idOrName) - this.customIDOffset; 434 if (!_customLabels[id]) { 435 throw new Error("Label not available for custom field " + idOrName); 436 } 437 return _customLabels[id]; 438 } 439 440 return Zotero.getString("itemTypes." + typeName); 441 } 442 443 this.getImageSrc = function (itemType) { 444 var suffix = Zotero.hiDPISuffix; 445 446 if (this.isCustom(itemType)) { 447 var id = this.getID(itemType) - this.customIDOffset; 448 if (!_customImages[id]) { 449 throw new Error("Image not available for custom field " + itemType); 450 } 451 return _customImages[id]; 452 } 453 454 switch (itemType) { 455 // Use treeitem.png 456 case 'attachment-file': 457 case 'document': 458 break; 459 460 // HiDPI images available 461 case 'attachment-link': 462 case 'attachment-pdf': 463 case 'attachment-web-link': 464 case 'artwork': 465 case 'audioRecording': 466 case 'bill': 467 case 'blogPost': 468 case 'book': 469 case 'bookSection': 470 case 'case': 471 case 'computerProgram': 472 case 'dictionaryEntry': 473 case 'email': 474 case 'encyclopediaArticle': 475 case 'film': 476 case 'forumPost': 477 case 'hearing': 478 case 'instantMessage': 479 case 'interview': 480 case 'journalArticle': 481 case 'letter': 482 case 'magazineArticle': 483 case 'manuscript': 484 case 'newspaperArticle': 485 case 'note': 486 case 'patent': 487 case 'presentation': 488 case 'report': 489 case 'statute': 490 case 'thesis': 491 case 'webpage': 492 return "chrome://zotero/skin/treeitem-" + itemType + suffix + ".png"; 493 494 // No HiDPI images available 495 case 'attachment-snapshot': 496 case 'conferencePaper': 497 case 'map': 498 case 'podcast': 499 case 'radioBroadcast': 500 case 'tvBroadcast': 501 case 'videoRecording': 502 return "chrome://zotero/skin/treeitem-" + itemType + ".png"; 503 } 504 505 return "chrome://zotero/skin/treeitem" + suffix + ".png"; 506 } 507 } 508 509 510 Zotero.FileTypes = new function() { 511 Zotero.CachedTypes.apply(this, arguments); 512 this.constructor.prototype = new Zotero.CachedTypes(); 513 514 this._typeDesc = 'file type'; 515 this._typeDescPlural = 'file types'; 516 this._idCol = 'fileTypeID'; 517 this._nameCol = 'fileType'; 518 this._table = 'fileTypes'; 519 520 /** 521 * @return {Promise<Integer>} fileTypeID 522 */ 523 this.getIDFromMIMEType = function (mimeType) { 524 var sql = "SELECT fileTypeID FROM fileTypeMIMETypes " 525 + "WHERE ? LIKE mimeType || '%'"; 526 return Zotero.DB.valueQueryAsync(sql, [mimeType]); 527 }; 528 } 529 530 531 Zotero.CharacterSets = new function() { 532 Zotero.CachedTypes.apply(this, arguments); 533 this.constructor.prototype = new Zotero.CachedTypes(); 534 535 this._typeDesc = 'character set'; 536 this._typeDescPlural = 'character sets'; 537 this._idCol = 'charsetID'; 538 this._nameCol = 'charset'; 539 this._table = 'charsets'; 540 this._ignoreCase = true; 541 542 543 // Converts charset label to charset name 544 // https://encoding.spec.whatwg.org/#names-and-labels 545 // @param {String} charset 546 // @return {String|Boolean} Normalized charset name or FALSE if not recognized 547 this.toCanonical = function (charset) { 548 let canonical = charsetMap[charset.trim().toLowerCase()]; 549 if (!canonical) { 550 Zotero.debug("Unrecognized charset: " + charset); 551 return false; 552 } 553 return canonical; 554 }; 555 556 // Normalizes charset label to conform to DOM standards 557 // https://dom.spec.whatwg.org/#dom-document-characterset 558 // @param {String} charset 559 // @param {Boolean} mozCompat Whether to return a Mozilla-compatible label 560 // for use in Gecko internal APIs. 561 // https://developer.mozilla.org/en-US/docs/Gecko/Character_sets_supported_by_Gecko 562 // @return {String|Boolean} Normalized label or FALSE is not recognized 563 this.toLabel = function (charset, mozCompat) { 564 charset = this.toCanonical(charset); 565 if (!charset) return false; 566 567 if (mozCompat && charset == 'gbk') return charset; // See https://developer.mozilla.org/en-US/docs/Gecko/Character_sets_supported_by_Gecko 568 569 return compatibilityNames[charset]; 570 } 571 572 // From https://encoding.spec.whatwg.org/#names-and-labels 573 // Don't use this, use charsetMap. See below 574 let charsetList = { 575 "utf-8": ["unicode-1-1-utf-8", "utf-8", "utf8"], 576 "ibm866": ["866", "cp866", "csibm866", "ibm866"], 577 "iso-8859-2": ["csisolatin2", "iso-8859-2", "iso-ir-101", "iso8859-2", "iso88592", "iso_8859-2", 578 "iso_8859-2:1987","l2", "latin2"], 579 "iso-8859-3": ["csisolatin3", "iso-8859-3", "iso-ir-109", "iso8859-3", "iso88593", "iso_8859-3", 580 "iso_8859-3:1988","l3", "latin3"], 581 "iso-8859-4": ["csisolatin4", "iso-8859-4", "iso-ir-110", "iso8859-4", "iso88594", "iso_8859-4", 582 "iso_8859-4:1988","l4", "latin4"], 583 "iso-8859-5": ["csisolatincyrillic", "cyrillic", "iso-8859-5", "iso-ir-144", "iso8859-5", "iso88595", "iso_8859-5", 584 "iso_8859-5:1988"], 585 "iso-8859-6": ["arabic", "asmo-708", "csiso88596e", "csiso88596i", "csisolatinarabic", "ecma-114", "iso-8859-6", "iso-8859-6-e", "iso-8859-6-i", "iso-ir-127", "iso8859-6", "iso88596", "iso_8859-6", 586 "iso_8859-6:1987"], 587 "iso-8859-7": ["csisolatingreek", "ecma-118", "elot_928", "greek", "greek8", "iso-8859-7", "iso-ir-126", "iso8859-7", "iso88597", "iso_8859-7", 588 "iso_8859-7:1987","sun_eu_greek"], 589 "iso-8859-8": ["csiso88598e", "csisolatinhebrew", "hebrew", "iso-8859-8", "iso-8859-8-e", "iso-ir-138", "iso8859-8", "iso88598", "iso_8859-8", 590 "iso_8859-8:1988","visual"], 591 "iso-8859-8-i": ["csiso88598i", "iso-8859-8-i", "logical"], 592 "iso-8859-10": ["csisolatin6", "iso-8859-10", "iso-ir-157", "iso8859-10", "iso885910", "l6", "latin6"], 593 "iso-8859-13": ["iso-8859-13", "iso8859-13", "iso885913"], 594 "iso-8859-14": ["iso-8859-14", "iso8859-14", "iso885914"], 595 "iso-8859-15": ["csisolatin9", "iso-8859-15", "iso8859-15", "iso885915", "iso_8859-15", "l9"], 596 "iso-8859-16": ["iso-8859-16"], 597 "koi8-r": ["cskoi8r", "koi", "koi8", "koi8-r", "koi8_r"], 598 "koi8-u": ["koi8-u"], 599 "macintosh": ["csmacintosh", "mac", "macintosh", "x-mac-roman"], 600 "windows-874": ["dos-874", "iso-8859-11", "iso8859-11", "iso885911", "tis-620", "windows-874"], 601 "windows-1250": ["cp1250", "windows-1250", "x-cp1250"], 602 "windows-1251": ["cp1251", "windows-1251", "x-cp1251"], 603 "windows-1252": [ 604 "ansi_x3.4-1968","ascii", "cp1252", "cp819", "csisolatin1", "ibm819", "iso-8859-1", "iso-ir-100", "iso8859-1", "iso88591", "iso_8859-1", 605 "iso_8859-1:1987","l1", "latin1", "us-ascii", "windows-1252", "x-cp1252"], 606 "windows-1253": ["cp1253", "windows-1253", "x-cp1253"], 607 "windows-1254": ["cp1254", "csisolatin5", "iso-8859-9", "iso-ir-148", "iso8859-9", "iso88599", "iso_8859-9", 608 "iso_8859-9:1989","l5", "latin5", "windows-1254", "x-cp1254"], 609 "windows-1255": ["cp1255", "windows-1255", "x-cp1255"], 610 "windows-1256": ["cp1256", "windows-1256", "x-cp1256"], 611 "windows-1257": ["cp1257", "windows-1257", "x-cp1257"], 612 "windows-1258": ["cp1258", "windows-1258", "x-cp1258"], 613 "x-mac-cyrillic": ["x-mac-cyrillic", "x-mac-ukrainian"], 614 "gbk": ["chinese", "csgb2312", "csiso58gb231280", "gb2312", "gb_2312", "gb_2312-80", "gbk", "iso-ir-58", "x-gbk"], 615 "gb18030": ["gb18030"], 616 "big5": ["big5", "cn-big5", "csbig5", "x-x-big5"], 617 "big5-hkscs": ["big5-hkscs"], // see https://bugzilla.mozilla.org/show_bug.cgi?id=912470 618 "euc-jp": ["cseucpkdfmtjapanese", "euc-jp", "x-euc-jp"], 619 "iso-2022-jp": ["csiso2022jp", "iso-2022-jp"], 620 "shift_jis": ["csshiftjis", "ms_kanji", "shift-jis", "shift_jis", "sjis", "windows-31j", "x-sjis"], 621 "euc-kr": ["cseuckr", "csksc56011987", "euc-kr", "iso-ir-149", "korean", "ks_c_5601-1987", "ks_c_5601-1989", "ksc5601", "ksc_5601", "windows-949"], 622 "replacement": ["csiso2022kr", "hz-gb-2312", "iso-2022-cn", "iso-2022-cn-ext", "iso-2022-kr"], 623 "utf-16be": ["utf-16be"], 624 "utf-16le": ["utf-16", "utf-16le"], 625 "x-user-defined": ["x-user-defined"] 626 } 627 628 // As per https://dom.spec.whatwg.org/#dom-document-characterset 629 let compatibilityNames = { 630 "utf-8": "UTF-8", 631 "ibm866": "IBM866", 632 "iso-8859-2": "ISO-8859-2", 633 "iso-8859-3": "ISO-8859-3", 634 "iso-8859-4": "ISO-8859-4", 635 "iso-8859-5": "ISO-8859-5", 636 "iso-8859-6": "ISO-8859-6", 637 "iso-8859-7": "ISO-8859-7", 638 "iso-8859-8": "ISO-8859-8", 639 "iso-8859-8-i": "ISO-8859-8-I", 640 "iso-8859-10": "ISO-8859-10", 641 "iso-8859-13": "ISO-8859-13", 642 "iso-8859-14": "ISO-8859-14", 643 "iso-8859-15": "ISO-8859-15", 644 "iso-8859-16": "ISO-8859-16", 645 "koi8-r": "KOI8-R", 646 "koi8-u": "KOI8-U", 647 "gbk": "GBK", 648 "big5": "Big5", 649 "euc-jp": "EUC-JP", 650 "iso-2022-jp": "ISO-2022-JP", 651 "shift_jis": "Shift_JIS", 652 "euc-kr": "EUC-KR", 653 "utf-16be": "UTF-16BE", 654 "utf-16le": "UTF-16LE" 655 }; 656 657 let charsetMap = {}; 658 for (let canonical in charsetList) { 659 charsetMap[canonical.toLowerCase()] = canonical; 660 charsetList[canonical].forEach((c) => charsetMap[c.toLowerCase()] = canonical); 661 662 if (!compatibilityNames[canonical]) { 663 compatibilityNames[canonical] = canonical; 664 } 665 } 666 667 // Clear charsetList 668 charsetList = null; 669 } 670 671 672 Zotero.RelationPredicates = new function () { 673 Zotero.CachedTypes.apply(this, arguments); 674 this.constructor.prototype = new Zotero.CachedTypes(); 675 676 this._typeDesc = 'relation predicate'; 677 this._typeDescPlural = 'relation predicates'; 678 this._idCol = 'predicateID'; 679 this._nameCol = 'predicate'; 680 this._table = 'relationPredicates'; 681 this._ignoreCase = false; 682 this._allowAdd = true; 683 }