1 /*
  2  * ListFmt.js - Represent a list formatter.
  3  *
  4  * Copyright © 2017-2018, 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("./ilib.js");
 24 var Utils = require("./Utils.js");
 25 var Locale = require("./Locale.js");
 26 
 27 
 28 /**
 29  * @class
 30  * Create a new list formatter object that formats lists of items according to
 31  * the options.<p>
 32  *
 33  * The options object can contain zero or more of the following parameters:
 34  *
 35  * <ul>
 36  * <li><i>locale</i> locale to use to format this list, or undefined to use the
 37  * default locale
 38  *
 39  * <li><i>length</i> - Specify the length of the format to use. The length is the approximate size of the
 40  * formatted string.
 41  *
 42  * <ul>
 43  * <li><i>short</i>
 44  * <li><i>medium</i>
 45  * <li><i>long</i>
 46  * <li><i>full</i>
 47  * </ul>
 48  *
 49  * <li><i>style</i> the name of style to use to format the list, or undefined
 50  * to use the default "standard" style. another style option is "units".
 51  *
 52  * <li><i>onLoad</i> - a callback function to call when the locale data is fully loaded and the address has been
 53  * parsed. When the onLoad option is given, the address formatter object
 54  * will attempt to load any missing locale data using the ilib loader callback.
 55  * When the constructor is done (even if the data is already preassembled), the
 56  * onLoad function is called with the current instance as a parameter, so this
 57  * callback can be used with preassembled or dynamic loading or a mix of the two.
 58  *
 59  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or
 60  * asynchronously. If this option is given as "false", then the "onLoad"
 61  * callback must be given, as the instance returned from this constructor will
 62  * not be usable for a while.
 63  *
 64  * <li><i>loadParams</i> - an object containing parameters to pass to the
 65  * loader callback function when locale data is missing. The parameters are not
 66  * interpretted or modified in any way. They are simply passed along. The object
 67  * may contain any property/value pairs as long as the calling code is in
 68  * agreement with the loader callback function as to what those parameters mean.
 69  * </ul>
 70  *
 71  * @constructor
 72  * @param {Object} options properties that control how this formatter behaves
 73  */
 74 var ListFmt = function(options) {
 75     this.locale = new Locale();
 76     this.sync = true;
 77     this.style = "standard";
 78     this.length = "short";
 79     this.loadParams = {};
 80 
 81     if (options) {
 82         if (options.locale) {
 83             this.locale = options.locale;
 84         }
 85 
 86         if (typeof(options.sync) !== 'undefined') {
 87             this.sync = !!options.sync;
 88         }
 89 
 90         if (options.length) {
 91             this.length = options.length;
 92         }
 93 
 94         if (options.loadParams) {
 95             this.loadParams = options.loadParams;
 96         }
 97 
 98         if (options.style) {
 99             this.style = options.style;
100         }
101     }
102 
103     Utils.loadData({
104         name: "list.json",
105         object: "ListFmt",
106         locale: this.locale,
107         sync: this.sync,
108         loadParams: this.loadParams,
109         callback: ilib.bind(this, function (fmtdata) {
110             this.fmtdata = fmtdata;
111 
112             if (options && typeof(options.onLoad) === 'function') {
113                 options.onLoad(this);
114             }
115         })
116     });
117 };
118 
119 /**
120  * Format a list of strings as grammatical text that is appropriate
121  * for the locale of this formatter.
122  *
123  * @param {Array.<string>} items an array of strings to format in
124  * order that you would like them to appear
125  * @returns {string} a string containing the list of items that
126  * is grammatically correct for the locale of this formatter
127  */
128 
129 ListFmt.prototype.format = function(items) {
130     if (!items || (!ilib.isArray(items))) {
131         return "";
132     }
133 
134     var itemCount = items.length;
135     var fmtTemplate, formattedList;
136     var startFmt, middleFmt, endFmt;
137     var i;
138 
139     fmtTemplate = this.fmtdata[this.style][this.length] || this.fmtdata[this.style];
140     startFmt = fmtTemplate["start"];
141     middleFmt = fmtTemplate["middle"];
142     endFmt = fmtTemplate["end"];
143 
144     if (itemCount === 0) {
145         return "";
146     }
147     else if (itemCount === 1) {
148         formattedList =  items.toString();
149 
150     } else if ( itemCount === 2) {
151         fmtTemplate = fmtTemplate["2"];
152         formattedList = fmtTemplate.replace("{0}", items[0]).replace("{1}", items[1]);
153 
154     } else {
155         for(i = itemCount; i >= 0 ; i--){
156             if (i == itemCount) {
157                 formattedList = endFmt.replace("{0}", items[itemCount-2]).replace("{1}", items[itemCount-1]);
158                 i = i-2;
159             } else if (i == 0) {
160                 formattedList = startFmt.replace("{0}",items[i]).replace("{1}", formattedList);
161             }
162              else {
163                 formattedList = middleFmt.replace("{0}",items[i]).replace("{1}", formattedList);
164             }
165         }
166     }
167     return formattedList;
168 };
169 
170 /**
171  * Return the locale of this formatter.
172  *
173  * @returns {string} the locale of this formatter
174  */
175 ListFmt.prototype.getLocale = function() {
176     return this.locale.getSpec();
177 };
178 
179 /**
180  * Return the style of names returned by this formatter
181  * @return {string} the style of names returned by this formatter
182  */
183 ListFmt.prototype.getStyle = function() {
184     return this.style;
185 };
186 
187 module.exports = ListFmt;