ListFormat.js (7636B)
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 /** 6 * ListFormat internal properties. 7 */ 8 function listFormatLocaleData() { 9 // ListFormat don't support any extension keys. 10 return {}; 11 } 12 var listFormatInternalProperties = { 13 localeData: listFormatLocaleData, 14 relevantExtensionKeys: [], 15 }; 16 17 /** 18 * Intl.ListFormat ( [ locales [ , options ] ] ) 19 * 20 * Compute an internal properties object from |lazyListFormatData|. 21 */ 22 function resolveListFormatInternals(lazyListFormatData) { 23 assert(IsObject(lazyListFormatData), "lazy data not an object?"); 24 25 var internalProps = std_Object_create(null); 26 27 var ListFormat = listFormatInternalProperties; 28 29 // Compute effective locale. 30 31 // Step 9. 32 var localeData = ListFormat.localeData; 33 34 // Step 10. 35 var r = ResolveLocale( 36 "ListFormat", 37 lazyListFormatData.requestedLocales, 38 lazyListFormatData.opt, 39 ListFormat.relevantExtensionKeys, 40 localeData 41 ); 42 43 // Step 11. 44 internalProps.locale = r.locale; 45 46 // Step 13. 47 internalProps.type = lazyListFormatData.type; 48 49 // Step 15. 50 internalProps.style = lazyListFormatData.style; 51 52 // Steps 16-23 (not applicable in our implementation). 53 54 // The caller is responsible for associating |internalProps| with the right 55 // object using |setInternalProperties|. 56 return internalProps; 57 } 58 59 /** 60 * Returns an object containing the ListFormat internal properties of |obj|. 61 */ 62 function getListFormatInternals(obj) { 63 assert(IsObject(obj), "getListFormatInternals called with non-object"); 64 assert( 65 intl_GuardToListFormat(obj) !== null, 66 "getListFormatInternals called with non-ListFormat" 67 ); 68 69 var internals = getIntlObjectInternals(obj); 70 assert( 71 internals.type === "ListFormat", 72 "bad type escaped getIntlObjectInternals" 73 ); 74 75 // If internal properties have already been computed, use them. 76 var internalProps = maybeInternalProperties(internals); 77 if (internalProps) { 78 return internalProps; 79 } 80 81 // Otherwise it's time to fully create them. 82 internalProps = resolveListFormatInternals(internals.lazyData); 83 setInternalProperties(internals, internalProps); 84 return internalProps; 85 } 86 87 /** 88 * Intl.ListFormat ( [ locales [ , options ] ] ) 89 * 90 * Initializes an object as a ListFormat. 91 * 92 * This method is complicated a moderate bit by its implementing initialization 93 * as a *lazy* concept. Everything that must happen now, does -- but we defer 94 * all the work we can until the object is actually used as a ListFormat. 95 * This later work occurs in |resolveListFormatInternals|; steps not noted 96 * here occur there. 97 */ 98 function InitializeListFormat(listFormat, locales, options) { 99 assert(IsObject(listFormat), "InitializeListFormat called with non-object"); 100 assert( 101 intl_GuardToListFormat(listFormat) !== null, 102 "InitializeListFormat called with non-ListFormat" 103 ); 104 105 // Lazy ListFormat data has the following structure: 106 // 107 // { 108 // requestedLocales: List of locales, 109 // type: "conjunction" / "disjunction" / "unit", 110 // style: "long" / "short" / "narrow", 111 // 112 // opt: // opt object computed in InitializeListFormat 113 // { 114 // localeMatcher: "lookup" / "best fit", 115 // } 116 // } 117 // 118 // Note that lazy data is only installed as a final step of initialization, 119 // so every ListFormat lazy data object has *all* these properties, never a 120 // subset of them. 121 var lazyListFormatData = std_Object_create(null); 122 123 // Step 3. 124 var requestedLocales = CanonicalizeLocaleList(locales); 125 lazyListFormatData.requestedLocales = requestedLocales; 126 127 // Steps 4-5. 128 if (options === undefined) { 129 options = std_Object_create(null); 130 } else if (!IsObject(options)) { 131 ThrowTypeError( 132 JSMSG_OBJECT_REQUIRED, 133 options === null ? "null" : typeof options 134 ); 135 } 136 137 // Step 6. 138 var opt = NEW_RECORD(); 139 lazyListFormatData.opt = opt; 140 141 // Steps 7-8. 142 var matcher = GetOption( 143 options, 144 "localeMatcher", 145 "string", 146 ["lookup", "best fit"], 147 "best fit" 148 ); 149 opt.localeMatcher = matcher; 150 151 // Compute formatting options. 152 153 // Steps 12-13. 154 var type = GetOption( 155 options, 156 "type", 157 "string", 158 ["conjunction", "disjunction", "unit"], 159 "conjunction" 160 ); 161 lazyListFormatData.type = type; 162 163 // Steps 14-15. 164 var style = GetOption( 165 options, 166 "style", 167 "string", 168 ["long", "short", "narrow"], 169 "long" 170 ); 171 lazyListFormatData.style = style; 172 173 // We've done everything that must be done now: mark the lazy data as fully 174 // computed and install it. 175 initializeIntlObject(listFormat, "ListFormat", lazyListFormatData); 176 } 177 178 /** 179 * StringListFromIterable ( iterable ) 180 */ 181 function StringListFromIterable(iterable, methodName) { 182 // Step 1. 183 if (iterable === undefined) { 184 return []; 185 } 186 187 // Step 3. 188 var list = []; 189 190 // Steps 2, 4-5. 191 for (var element of allowContentIter(iterable)) { 192 // Step 5.b.ii. 193 if (typeof element !== "string") { 194 ThrowTypeError( 195 JSMSG_NOT_EXPECTED_TYPE, 196 methodName, 197 "string", 198 typeof element 199 ); 200 } 201 202 // Step 5.b.iii. 203 DefineDataProperty(list, list.length, element); 204 } 205 206 // Step 6. 207 return list; 208 } 209 210 /** 211 * Intl.ListFormat.prototype.format ( list ) 212 */ 213 function Intl_ListFormat_format(list) { 214 // Step 1. 215 var listFormat = this; 216 217 // Steps 2-3. 218 if ( 219 !IsObject(listFormat) || 220 (listFormat = intl_GuardToListFormat(listFormat)) === null 221 ) { 222 return callFunction( 223 intl_CallListFormatMethodIfWrapped, 224 this, 225 list, 226 "Intl_ListFormat_format" 227 ); 228 } 229 230 // Step 4. 231 var stringList = StringListFromIterable(list, "format"); 232 233 // We can directly return if |stringList| contains less than two elements. 234 if (stringList.length < 2) { 235 return stringList.length === 0 ? "" : stringList[0]; 236 } 237 238 // Ensure the ListFormat internals are resolved. 239 getListFormatInternals(listFormat); 240 241 // Step 5. 242 return intl_FormatList(listFormat, stringList, /* formatToParts = */ false); 243 } 244 245 /** 246 * Intl.ListFormat.prototype.formatToParts ( list ) 247 */ 248 function Intl_ListFormat_formatToParts(list) { 249 // Step 1. 250 var listFormat = this; 251 252 // Steps 2-3. 253 if ( 254 !IsObject(listFormat) || 255 (listFormat = intl_GuardToListFormat(listFormat)) === null 256 ) { 257 return callFunction( 258 intl_CallListFormatMethodIfWrapped, 259 this, 260 list, 261 "Intl_ListFormat_formatToParts" 262 ); 263 } 264 265 // Step 4. 266 var stringList = StringListFromIterable(list, "formatToParts"); 267 268 // We can directly return if |stringList| contains less than two elements. 269 if (stringList.length < 2) { 270 return stringList.length === 0 271 ? [] 272 : [{ type: "element", value: stringList[0] }]; 273 } 274 275 // Ensure the ListFormat internals are resolved. 276 getListFormatInternals(listFormat); 277 278 // Step 5. 279 return intl_FormatList(listFormat, stringList, /* formatToParts = */ true); 280 } 281 282 /** 283 * Returns the resolved options for a ListFormat object. 284 */ 285 function Intl_ListFormat_resolvedOptions() { 286 // Step 1. 287 var listFormat = this; 288 289 // Steps 2-3. 290 if ( 291 !IsObject(listFormat) || 292 (listFormat = intl_GuardToListFormat(listFormat)) === null 293 ) { 294 return callFunction( 295 intl_CallListFormatMethodIfWrapped, 296 this, 297 "Intl_ListFormat_resolvedOptions" 298 ); 299 } 300 301 var internals = getListFormatInternals(listFormat); 302 303 // Steps 4-5. 304 var result = { 305 locale: internals.locale, 306 type: internals.type, 307 style: internals.style, 308 }; 309 310 // Step 6. 311 return result; 312 }