tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Collator.js (12168B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /* Portions Copyright Norbert Lindenberg 2011-2012. */
      6 
      7 /**
      8 * Compute an internal properties object from |lazyCollatorData|.
      9 */
     10 function resolveCollatorInternals(lazyCollatorData) {
     11  assert(IsObject(lazyCollatorData), "lazy data not an object?");
     12 
     13  var internalProps = std_Object_create(null);
     14 
     15  var Collator = collatorInternalProperties;
     16 
     17  // Step 5.
     18  internalProps.usage = lazyCollatorData.usage;
     19 
     20  // Steps 6-7.
     21  var collatorIsSorting = lazyCollatorData.usage === "sort";
     22  var localeData = collatorIsSorting
     23    ? Collator.sortLocaleData
     24    : Collator.searchLocaleData;
     25 
     26  // Compute effective locale.
     27  // Step 16.
     28  var relevantExtensionKeys = Collator.relevantExtensionKeys;
     29 
     30  // Step 17.
     31  var r = ResolveLocale(
     32    "Collator",
     33    lazyCollatorData.requestedLocales,
     34    lazyCollatorData.opt,
     35    relevantExtensionKeys,
     36    localeData
     37  );
     38 
     39  // Step 18.
     40  internalProps.locale = r.locale;
     41 
     42  // Step 19.
     43  var collation = r.co;
     44 
     45  // Step 20.
     46  if (collation === null) {
     47    collation = "default";
     48  }
     49 
     50  // Step 21.
     51  internalProps.collation = collation;
     52 
     53  // Step 22.
     54  internalProps.numeric = r.kn === "true";
     55 
     56  // Step 23.
     57  internalProps.caseFirst = r.kf;
     58 
     59  // Compute remaining collation options.
     60  // Step 25.
     61  var s = lazyCollatorData.rawSensitivity;
     62  if (s === undefined) {
     63    // In theory the default sensitivity for the "search" collator is
     64    // locale dependent; in reality the CLDR/ICU default strength is
     65    // always tertiary. Therefore use "variant" as the default value for
     66    // both collation modes.
     67    s = "variant";
     68  }
     69 
     70  // Step 26.
     71  internalProps.sensitivity = s;
     72 
     73  // Step 28.
     74  var ignorePunctuation = lazyCollatorData.ignorePunctuation;
     75  if (ignorePunctuation === undefined) {
     76    var actualLocale = collatorActualLocale(r.dataLocale);
     77    ignorePunctuation = intl_isIgnorePunctuation(actualLocale);
     78  }
     79  internalProps.ignorePunctuation = ignorePunctuation;
     80 
     81  // The caller is responsible for associating |internalProps| with the right
     82  // object using |setInternalProperties|.
     83  return internalProps;
     84 }
     85 
     86 /**
     87 * Returns an object containing the Collator internal properties of |obj|.
     88 */
     89 function getCollatorInternals(obj) {
     90  assert(IsObject(obj), "getCollatorInternals called with non-object");
     91  assert(
     92    intl_GuardToCollator(obj) !== null,
     93    "getCollatorInternals called with non-Collator"
     94  );
     95 
     96  var internals = getIntlObjectInternals(obj);
     97  assert(
     98    internals.type === "Collator",
     99    "bad type escaped getIntlObjectInternals"
    100  );
    101 
    102  // If internal properties have already been computed, use them.
    103  var internalProps = maybeInternalProperties(internals);
    104  if (internalProps) {
    105    return internalProps;
    106  }
    107 
    108  // Otherwise it's time to fully create them.
    109  internalProps = resolveCollatorInternals(internals.lazyData);
    110  setInternalProperties(internals, internalProps);
    111  return internalProps;
    112 }
    113 
    114 /**
    115 * Initializes an object as a Collator.
    116 *
    117 * This method is complicated a moderate bit by its implementing initialization
    118 * as a *lazy* concept.  Everything that must happen now, does -- but we defer
    119 * all the work we can until the object is actually used as a Collator.  This
    120 * later work occurs in |resolveCollatorInternals|; steps not noted here occur
    121 * there.
    122 *
    123 * Spec: ECMAScript Internationalization API Specification, 10.1.1.
    124 */
    125 function InitializeCollator(collator, locales, options) {
    126  assert(IsObject(collator), "InitializeCollator called with non-object");
    127  assert(
    128    intl_GuardToCollator(collator) !== null,
    129    "InitializeCollator called with non-Collator"
    130  );
    131 
    132  // Lazy Collator data has the following structure:
    133  //
    134  //   {
    135  //     requestedLocales: List of locales,
    136  //     usage: "sort" / "search",
    137  //     opt: // opt object computed in InitializeCollator
    138  //       {
    139  //         localeMatcher: "lookup" / "best fit",
    140  //         co: string matching a Unicode extension type / undefined
    141  //         kn: true / false / undefined,
    142  //         kf: "upper" / "lower" / "false" / undefined
    143  //       }
    144  //     rawSensitivity: "base" / "accent" / "case" / "variant" / undefined,
    145  //     ignorePunctuation: true / false / undefined
    146  //   }
    147  //
    148  // Note that lazy data is only installed as a final step of initialization,
    149  // so every Collator lazy data object has *all* these properties, never a
    150  // subset of them.
    151  var lazyCollatorData = std_Object_create(null);
    152 
    153  // Step 1.
    154  var requestedLocales = CanonicalizeLocaleList(locales);
    155  lazyCollatorData.requestedLocales = requestedLocales;
    156 
    157  // Steps 2-3.
    158  //
    159  // If we ever need more speed here at startup, we should try to detect the
    160  // case where |options === undefined| and then directly use the default
    161  // value for each option.  For now, just keep it simple.
    162  if (options === undefined) {
    163    options = std_Object_create(null);
    164  } else {
    165    options = ToObject(options);
    166  }
    167 
    168  // Compute options that impact interpretation of locale.
    169  // Step 4.
    170  var u = GetOption(options, "usage", "string", ["sort", "search"], "sort");
    171  lazyCollatorData.usage = u;
    172 
    173  // Step 8.
    174  var opt = NEW_RECORD();
    175  lazyCollatorData.opt = opt;
    176 
    177  // Steps 9-10.
    178  var matcher = GetOption(
    179    options,
    180    "localeMatcher",
    181    "string",
    182    ["lookup", "best fit"],
    183    "best fit"
    184  );
    185  opt.localeMatcher = matcher;
    186 
    187  // https://github.com/tc39/ecma402/pull/459
    188  var collation = GetOption(
    189    options,
    190    "collation",
    191    "string",
    192    undefined,
    193    undefined
    194  );
    195  if (collation !== undefined) {
    196    collation = intl_ValidateAndCanonicalizeUnicodeExtensionType(
    197      collation,
    198      "collation",
    199      "co"
    200    );
    201  }
    202  opt.co = collation;
    203 
    204  // Steps 11-13.
    205  var numericValue = GetOption(
    206    options,
    207    "numeric",
    208    "boolean",
    209    undefined,
    210    undefined
    211  );
    212  if (numericValue !== undefined) {
    213    numericValue = numericValue ? "true" : "false";
    214  }
    215  opt.kn = numericValue;
    216 
    217  // Steps 14-15.
    218  var caseFirstValue = GetOption(
    219    options,
    220    "caseFirst",
    221    "string",
    222    ["upper", "lower", "false"],
    223    undefined
    224  );
    225  opt.kf = caseFirstValue;
    226 
    227  // Compute remaining collation options.
    228  // Step 24.
    229  var s = GetOption(
    230    options,
    231    "sensitivity",
    232    "string",
    233    ["base", "accent", "case", "variant"],
    234    undefined
    235  );
    236  lazyCollatorData.rawSensitivity = s;
    237 
    238  // Step 27.
    239  var ip = GetOption(options, "ignorePunctuation", "boolean", undefined, undefined);
    240  lazyCollatorData.ignorePunctuation = ip;
    241 
    242  // Step 29.
    243  //
    244  // We've done everything that must be done now: mark the lazy data as fully
    245  // computed and install it.
    246  initializeIntlObject(collator, "Collator", lazyCollatorData);
    247 }
    248 
    249 /**
    250 * Collator internal properties.
    251 *
    252 * Spec: ECMAScript Internationalization API Specification, 9.1 and 10.2.3.
    253 */
    254 var collatorInternalProperties = {
    255  sortLocaleData: collatorSortLocaleData,
    256  searchLocaleData: collatorSearchLocaleData,
    257  relevantExtensionKeys: ["co", "kf", "kn"],
    258 };
    259 
    260 /**
    261 * Returns the actual locale used when a collator for |locale| is constructed.
    262 */
    263 function collatorActualLocale(locale) {
    264  assert(typeof locale === "string", "locale should be string");
    265 
    266  // If |locale| is the default locale (e.g. da-DK), but only supported
    267  // through a fallback (da), we need to get the actual locale before we
    268  // can call intl_isUpperCaseFirst. Also see intl_BestAvailableLocale.
    269  return BestAvailableLocaleIgnoringDefault("Collator", locale);
    270 }
    271 
    272 /**
    273 * Returns the default caseFirst values for the given locale. The first
    274 * element in the returned array denotes the default value per ES2017 Intl,
    275 * 9.1 Internal slots of Service Constructors.
    276 */
    277 function collatorSortCaseFirst(locale) {
    278  var actualLocale = collatorActualLocale(locale);
    279  if (intl_isUpperCaseFirst(actualLocale)) {
    280    return ["upper", "false", "lower"];
    281  }
    282 
    283  // Default caseFirst values for all other languages.
    284  return ["false", "lower", "upper"];
    285 }
    286 
    287 /**
    288 * Returns the default caseFirst value for the given locale.
    289 */
    290 function collatorSortCaseFirstDefault(locale) {
    291  var actualLocale = collatorActualLocale(locale);
    292  if (intl_isUpperCaseFirst(actualLocale)) {
    293    return "upper";
    294  }
    295 
    296  // Default caseFirst value for all other languages.
    297  return "false";
    298 }
    299 
    300 function collatorSortLocaleData() {
    301  /* eslint-disable object-shorthand */
    302  return {
    303    co: intl_availableCollations,
    304    kn: function() {
    305      return ["false", "true"];
    306    },
    307    kf: collatorSortCaseFirst,
    308    default: {
    309      co: function() {
    310        // The first element of the collations array must be |null|
    311        // per ES2017 Intl, 10.2.3 Internal Slots.
    312        return null;
    313      },
    314      kn: function() {
    315        return "false";
    316      },
    317      kf: collatorSortCaseFirstDefault,
    318    },
    319  };
    320  /* eslint-enable object-shorthand */
    321 }
    322 
    323 function collatorSearchLocaleData() {
    324  /* eslint-disable object-shorthand */
    325  return {
    326    co: function() {
    327      return [null];
    328    },
    329    kn: function() {
    330      return ["false", "true"];
    331    },
    332    kf: function() {
    333      return ["false", "lower", "upper"];
    334    },
    335    default: {
    336      co: function() {
    337        return null;
    338      },
    339      kn: function() {
    340        return "false";
    341      },
    342      kf: function() {
    343        return "false";
    344      },
    345    },
    346  };
    347  /* eslint-enable object-shorthand */
    348 }
    349 
    350 /**
    351 * Create function to be cached and returned by Intl.Collator.prototype.compare.
    352 *
    353 * Spec: ECMAScript Internationalization API Specification, 10.3.3.1.
    354 */
    355 function createCollatorCompare(collator) {
    356  // This function is not inlined in $Intl_Collator_compare_get to avoid
    357  // creating a call-object on each call to $Intl_Collator_compare_get.
    358  return function(x, y) {
    359    // Step 1 (implicit).
    360 
    361    // Step 2.
    362    assert(IsObject(collator), "collatorCompareToBind called with non-object");
    363    assert(
    364      intl_GuardToCollator(collator) !== null,
    365      "collatorCompareToBind called with non-Collator"
    366    );
    367 
    368    // Steps 3-6
    369    var X = ToString(x);
    370    var Y = ToString(y);
    371 
    372    // Step 7.
    373    return intl_CompareStrings(collator, X, Y);
    374  };
    375 }
    376 
    377 /**
    378 * Returns a function bound to this Collator that compares x (converted to a
    379 * String value) and y (converted to a String value),
    380 * and returns a number less than 0 if x < y, 0 if x = y, or a number greater
    381 * than 0 if x > y according to the sort order for the locale and collation
    382 * options of this Collator object.
    383 *
    384 * Spec: ECMAScript Internationalization API Specification, 10.3.3.
    385 */
    386 // Uncloned functions with `$` prefix are allocated as extended function
    387 // to store the original name in `SetCanonicalName`.
    388 function $Intl_Collator_compare_get() {
    389  // Step 1.
    390  var collator = this;
    391 
    392  // Steps 2-3.
    393  if (
    394    !IsObject(collator) ||
    395    (collator = intl_GuardToCollator(collator)) === null
    396  ) {
    397    return callFunction(
    398      intl_CallCollatorMethodIfWrapped,
    399      this,
    400      "$Intl_Collator_compare_get"
    401    );
    402  }
    403 
    404  var internals = getCollatorInternals(collator);
    405 
    406  // Step 4.
    407  if (internals.boundCompare === undefined) {
    408    // Steps 4.a-c.
    409    internals.boundCompare = createCollatorCompare(collator);
    410  }
    411 
    412  // Step 5.
    413  return internals.boundCompare;
    414 }
    415 SetCanonicalName($Intl_Collator_compare_get, "get compare");
    416 
    417 /**
    418 * Returns the resolved options for a Collator object.
    419 *
    420 * Spec: ECMAScript Internationalization API Specification, 10.3.4.
    421 */
    422 function Intl_Collator_resolvedOptions() {
    423  // Step 1.
    424  var collator = this;
    425 
    426  // Steps 2-3.
    427  if (
    428    !IsObject(collator) ||
    429    (collator = intl_GuardToCollator(collator)) === null
    430  ) {
    431    return callFunction(
    432      intl_CallCollatorMethodIfWrapped,
    433      this,
    434      "Intl_Collator_resolvedOptions"
    435    );
    436  }
    437 
    438  var internals = getCollatorInternals(collator);
    439 
    440  // Steps 4-5.
    441  var result = {
    442    locale: internals.locale,
    443    usage: internals.usage,
    444    sensitivity: internals.sensitivity,
    445    ignorePunctuation: internals.ignorePunctuation,
    446    collation: internals.collation,
    447    numeric: internals.numeric,
    448    caseFirst: internals.caseFirst,
    449  };
    450 
    451  // Step 6.
    452  return result;
    453 }