1 /* 2 * ListFmt.js - Represent a list formatter. 3 * 4 * Copyright © 2017-2020, JEDLSoft 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 21 // !data list 22 23 var ilib = require("../index.js"); 24 var Utils = require("./Utils.js"); 25 var Locale = require("./Locale.js"); 26 27 // map our style parameter to the cldr style in the list.json files 28 var styleMap = { 29 "standard": "standard", 30 "conjunction": "standard", 31 "disjunction": "or", 32 "unit": "unit" 33 }; 34 35 /** 36 * @class 37 * Create a new list formatter object that formats lists of items according to 38 * the options.<p> 39 * 40 * The options object can contain zero or more of the following parameters: 41 * 42 * <ul> 43 * <li><i>locale</i> locale to use to format this list, or undefined to use the 44 * default locale 45 * 46 * <li><i>length</i> - Specify the length of the format to use. The length is the approximate size of the 47 * formatted string. 48 * 49 * <ul> 50 * <li><i>short</i> 51 * <li><i>medium</i> 52 * <li><i>long</i> 53 * <li><i>full</i> 54 * </ul> 55 * 56 * <li><i>style</i> the name of style to use to format the list, or undefined 57 * to use the default "conjunction" style. Valid values are: 58 * 59 * <ul> 60 * <ul><i>standard</i> create a standard list. 61 * <ul><i>conjunction</i> this list should be concatenated with a conjunction "and". 62 * This is the default style for "standard". 63 * <ul><i>disjunction</i> this list should be concatenated with a disjunction "or". 64 * <ul><i>unit</i> this is a list of measures like "5 minutes, 4 seconds". In 65 * some languages, these type of lists are concatenated without a conjunction. 66 * </ul> 67 * 68 * <li><i>onLoad</i> - a callback function to call when the locale data is fully loaded and the address has been 69 * parsed. When the onLoad option is given, the address formatter object 70 * will attempt to load any missing locale data using the ilib loader callback. 71 * When the constructor is done (even if the data is already preassembled), the 72 * onLoad function is called with the current instance as a parameter, so this 73 * callback can be used with preassembled or dynamic loading or a mix of the two. 74 * 75 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 76 * asynchronously. If this option is given as "false", then the "onLoad" 77 * callback must be given, as the instance returned from this constructor will 78 * not be usable for a while. 79 * 80 * <li><i>loadParams</i> - an object containing parameters to pass to the 81 * loader callback function when locale data is missing. The parameters are not 82 * interpretted or modified in any way. They are simply passed along. The object 83 * may contain any property/value pairs as long as the calling code is in 84 * agreement with the loader callback function as to what those parameters mean. 85 * </ul> 86 * 87 * @constructor 88 * @param {Object} options properties that control how this formatter behaves 89 */ 90 var ListFmt = function(options) { 91 this.locale = new Locale(); 92 this.sync = true; 93 this.style = "standard"; 94 this.length = "medium"; 95 this.loadParams = {}; 96 97 if (options) { 98 if (options.type) { 99 this.type = options.type; 100 } 101 102 if (options.locale) { 103 this.locale = options.locale; 104 } 105 106 if (typeof(options.sync) !== 'undefined') { 107 this.sync = !!options.sync; 108 } 109 110 if (options.length) { 111 this.length = options.length; 112 } 113 114 if (options.loadParams) { 115 this.loadParams = options.loadParams; 116 } 117 118 if (options.style) { 119 if (styleMap[options.style]) { 120 this.style = styleMap[options.style]; 121 } 122 } 123 } 124 125 Utils.loadData({ 126 name: "list.json", 127 object: "ListFmt", 128 locale: this.locale, 129 sync: this.sync, 130 loadParams: this.loadParams, 131 callback: ilib.bind(this, function (fmtdata) { 132 this.fmtdata = fmtdata; 133 134 // if the requested style is not available in this locale, fall back 135 // to the default "standard" style 136 if (!fmtdata[this.style]) { 137 this.style = "standard"; 138 } 139 if (options && typeof(options.onLoad) === 'function') { 140 options.onLoad(this); 141 } 142 }) 143 }); 144 }; 145 146 /** 147 * Format a list of strings as grammatical text that is appropriate 148 * for the locale of this formatter. 149 * 150 * @param {Array.<string>} items an array of strings to format in 151 * order that you would like them to appear 152 * @returns {string} a string containing the list of items that 153 * is grammatically correct for the locale of this formatter 154 */ 155 156 ListFmt.prototype.format = function(items) { 157 if (!items || (!ilib.isArray(items))) { 158 return ""; 159 } 160 161 var itemCount = items.length; 162 var fmtTemplate, formattedList; 163 var startFmt, middleFmt, endFmt; 164 var i; 165 166 fmtTemplate = this.fmtdata[this.style][this.length] || this.fmtdata[this.style]; 167 startFmt = fmtTemplate["start"]; 168 middleFmt = fmtTemplate["middle"]; 169 endFmt = fmtTemplate["end"]; 170 171 if (itemCount === 0) { 172 return ""; 173 } 174 else if (itemCount === 1) { 175 formattedList = items.toString(); 176 177 } else if ( itemCount === 2) { 178 fmtTemplate = fmtTemplate["2"]; 179 formattedList = fmtTemplate.replace("{0}", items[0]).replace("{1}", items[1]); 180 181 } else { 182 for(i = itemCount; i >= 0 ; i--){ 183 if (i === itemCount) { 184 formattedList = endFmt.replace("{0}", items[itemCount-2]).replace("{1}", items[itemCount-1]); 185 i = i-2; 186 } else if (i === 0) { 187 formattedList = startFmt.replace("{0}",items[i]).replace("{1}", formattedList); 188 } 189 else { 190 formattedList = middleFmt.replace("{0}",items[i]).replace("{1}", formattedList); 191 } 192 } 193 } 194 return formattedList; 195 }; 196 197 /** 198 * Return the locale of this formatter. 199 * 200 * @returns {string} the locale of this formatter 201 */ 202 ListFmt.prototype.getLocale = function() { 203 return this.locale.getSpec(); 204 }; 205 206 /** 207 * Return the style of names returned by this formatter 208 * @return {string} the style of names returned by this formatter 209 */ 210 ListFmt.prototype.getStyle = function() { 211 return this.style; 212 }; 213 214 module.exports = ListFmt; 215