1 /* 2 * CalendarFactory.js - Constructs new instances of the right subclass of Calendar 3 * 4 * Copyright © 2015, 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 var ilib = require("./ilib.js"); 21 var Locale = require("./Locale.js"); 22 var LocaleInfo = require("./LocaleInfo.js"); 23 var Calendar = require("./Calendar.js"); 24 25 /** 26 * Factory method to create a new instance of a calendar subclass.<p> 27 * 28 * The options parameter can be an object that contains the following 29 * properties: 30 * 31 * <ul> 32 * <li><i>type</i> - specify the type of the calendar desired. The 33 * list of valid values changes depending on which calendars are 34 * defined. When assembling your iliball.js, include those calendars 35 * you wish to use in your program or web page, and they will register 36 * themselves with this factory method. The "official", "gregorian", 37 * and "julian" calendars are all included by default, as they are the 38 * standard calendars for much of the world. 39 * <li><i>locale</i> - some calendars vary depending on the locale. 40 * For example, the "official" calendar transitions from a Julian-style 41 * calendar to a Gregorian-style calendar on a different date for 42 * each country, as the governments of those countries decided to 43 * adopt the Gregorian calendar at different times. 44 * 45 * <li><i>onLoad</i> - a callback function to call when the calendar object is fully 46 * loaded. When the onLoad option is given, the calendar factory will attempt to 47 * load any missing locale data using the ilib loader callback. 48 * When the constructor is done (even if the data is already preassembled), the 49 * onLoad function is called with the current instance as a parameter, so this 50 * callback can be used with preassembled or dynamic loading or a mix of the two. 51 * 52 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 53 * asynchronously. If this option is given as "false", then the "onLoad" 54 * callback must be given, as the instance returned from this constructor will 55 * not be usable for a while. 56 * 57 * <li><i>loadParams</i> - an object containing parameters to pass to the 58 * loader callback function when locale data is missing. The parameters are not 59 * interpretted or modified in any way. They are simply passed along. The object 60 * may contain any property/value pairs as long as the calling code is in 61 * agreement with the loader callback function as to what those parameters mean. 62 * </ul> 63 * 64 * If a locale is specified, but no type, then the calendar that is default for 65 * the locale will be instantiated and returned. If neither the type nor 66 * the locale are specified, then the calendar for the default locale will 67 * be used. 68 * 69 * @static 70 * @param {Object=} options options controlling the construction of this instance, or 71 * undefined to use the default options 72 * @return {Calendar} an instance of a calendar object of the appropriate type 73 */ 74 var CalendarFactory = function (options) { 75 var locale, 76 type, 77 sync = true, 78 instance; 79 80 if (options) { 81 if (options.locale) { 82 locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale; 83 } 84 85 type = options.type || options.calendar; 86 87 if (typeof(options.sync) === 'boolean') { 88 sync = options.sync; 89 } 90 } 91 92 if (!locale) { 93 locale = new Locale(); // default locale 94 } 95 96 if (!type) { 97 new LocaleInfo(locale, { 98 sync: sync, 99 loadParams: options && options.loadParams, 100 onLoad: function(info) { 101 type = info.getCalendar(); 102 103 instance = CalendarFactory._init(type, options); 104 } 105 }); 106 } else { 107 instance = CalendarFactory._init(type, options); 108 } 109 110 return instance; 111 }; 112 113 /** 114 * Map calendar names to classes to initialize in the dynamic code model. 115 * TODO: Need to figure out some way that this doesn't have to be updated by hand. 116 * @private 117 */ 118 CalendarFactory._dynMap = { 119 "coptic": "Coptic", 120 "ethiopic": "Ethiopic", 121 "gregorian": "Gregorian", 122 "han": "Han", 123 "hebrew": "Hebrew", 124 "islamic": "Islamic", 125 "julian": "Julian", 126 "persian": "Persian", 127 "persian-algo": "PersianAlgo", 128 "thaisolar": "ThaiSolar" 129 }; 130 131 function circumventWebPack(x) { 132 return "./" + x + "Cal.js"; 133 } 134 135 /** 136 * Dynamically load the code for a calendar and calendar class if necessary. 137 * @protected 138 */ 139 CalendarFactory._dynLoadCalendar = function (name, fnc) { 140 if (!Calendar._constructors[name]) { 141 var entry = CalendarFactory._dynMap[name]; 142 if (entry) { 143 Calendar._constructors[name] = require(fnc(entry)); 144 } 145 } 146 return Calendar._constructors[name]; 147 }; 148 149 /** @private */ 150 CalendarFactory._init = function(type, options) { 151 var cons; 152 153 if (ilib.isDynCode()) { 154 CalendarFactory._dynLoadCalendar(type, circumventWebPack); 155 } 156 157 cons = Calendar._constructors[type]; 158 159 // pass the same options through to the constructor so the subclass 160 // has the ability to do something with if it needs to 161 if (!cons && typeof(options.onLoad) === "function") { 162 options.onLoad(undefined); 163 } 164 return cons && new cons(options); 165 }; 166 167 /** 168 * Return an array of known calendar types that the factory method can instantiate. 169 * 170 * @return {Array.<string>} an array of calendar types 171 */ 172 CalendarFactory.getCalendars = function () { 173 var arr = [], 174 c; 175 176 if (ilib.isDynCode()) { 177 for (c in CalendarFactory._dynMap) { 178 CalendarFactory._dynLoadCalendar(c, circumventWebPack); 179 } 180 } 181 182 for (c in Calendar._constructors) { 183 if (c && Calendar._constructors[c]) { 184 arr.push(c); // code like a pirate 185 } 186 } 187 188 return arr; 189 }; 190 191 module.exports = CalendarFactory; 192