/*
* CalendarFactory.js - Constructs new instances of the right subclass of Calendar
*
* Copyright © 2015, 2018, JEDLSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var ilib = require("../index.js");
var Locale = require("./Locale.js");
var LocaleInfo = require("./LocaleInfo.js");
var Calendar = require("./Calendar.js");
/**
* Factory method to create a new instance of a calendar subclass.<p>
*
* The options parameter can be an object that contains the following
* properties:
*
* <ul>
* <li><i>type</i> - specify the type of the calendar desired. The
* list of valid values changes depending on which calendars are
* defined. When assembling your iliball.js, include those calendars
* you wish to use in your program or web page, and they will register
* themselves with this factory method. The "official", "gregorian",
* and "julian" calendars are all included by default, as they are the
* standard calendars for much of the world.
* <li><i>locale</i> - some calendars vary depending on the locale.
* For example, the "official" calendar transitions from a Julian-style
* calendar to a Gregorian-style calendar on a different date for
* each country, as the governments of those countries decided to
* adopt the Gregorian calendar at different times.
*
* <li><i>onLoad</i> - a callback function to call when the calendar object is fully
* loaded. When the onLoad option is given, the calendar factory will attempt to
* load any missing locale data using the ilib loader callback.
* When the constructor is done (even if the data is already preassembled), the
* onLoad function is called with the current instance as a parameter, so this
* callback can be used with preassembled or dynamic loading or a mix of the two.
*
* <li><i>sync</i> - tell whether to load any missing locale data synchronously or
* asynchronously. If this option is given as "false", then the "onLoad"
* callback must be given, as the instance returned from this constructor will
* not be usable for a while.
*
* <li><i>loadParams</i> - an object containing parameters to pass to the
* loader callback function when locale data is missing. The parameters are not
* interpretted or modified in any way. They are simply passed along. The object
* may contain any property/value pairs as long as the calling code is in
* agreement with the loader callback function as to what those parameters mean.
* </ul>
*
* If a locale is specified, but no type, then the calendar that is default for
* the locale will be instantiated and returned. If neither the type nor
* the locale are specified, then the calendar for the default locale will
* be used.
*
* @static
* @param {Object=} options options controlling the construction of this instance, or
* undefined to use the default options
* @return {Calendar} an instance of a calendar object of the appropriate type
*/
var CalendarFactory = function (options) {
var locale,
type,
sync = true,
instance;
if (options) {
if (options.locale) {
locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
}
type = options.type || options.calendar;
if (typeof(options.sync) === 'boolean') {
sync = options.sync;
}
}
if (!locale) {
locale = new Locale(); // default locale
}
if (!type) {
new LocaleInfo(locale, {
sync: sync,
loadParams: options && options.loadParams,
onLoad: function(info) {
type = info.getCalendar();
instance = CalendarFactory._init(type, options);
}
});
} else {
instance = CalendarFactory._init(type, options);
}
return instance;
};
/**
* Map calendar names to classes to initialize in the dynamic code model.
* TODO: Need to figure out some way that this doesn't have to be updated by hand.
* @private
*/
CalendarFactory._dynMap = {
"coptic": "Coptic",
"ethiopic": "Ethiopic",
"gregorian": "Gregorian",
"han": "Han",
"hebrew": "Hebrew",
"islamic": "Islamic",
"julian": "Julian",
"persian": "Persian",
"persian-algo": "PersianAlgo",
"thaisolar": "ThaiSolar"
};
function circumventWebPack(x) {
return "./" + x + "Cal.js";
}
/**
* Dynamically load the code for a calendar and calendar class if necessary.
* @protected
*/
CalendarFactory._dynLoadCalendar = function (name, fnc) {
if (!Calendar._constructors[name]) {
var entry = CalendarFactory._dynMap[name];
if (entry) {
// eslint-disable-next-line
Calendar._constructors[name] = require(fnc(entry));
}
}
return Calendar._constructors[name];
};
/** @private */
CalendarFactory._init = function(type, options) {
var cons;
if (ilib.isDynCode()) {
CalendarFactory._dynLoadCalendar(type, circumventWebPack);
}
cons = Calendar._constructors[type];
// pass the same options through to the constructor so the subclass
// has the ability to do something with if it needs to
if (!cons && typeof(options.onLoad) === "function") {
options.onLoad(undefined);
}
return cons && new cons(options);
};
/**
* Return an array of known calendar types that the factory method can instantiate.
*
* @return {Array.<string>} an array of calendar types
*/
CalendarFactory.getCalendars = function () {
var arr = [],
c;
if (ilib.isDynCode()) {
for (c in CalendarFactory._dynMap) {
CalendarFactory._dynLoadCalendar(c, circumventWebPack);
}
}
for (c in Calendar._constructors) {
if (c && Calendar._constructors[c]) {
arr.push(c); // code like a pirate
}
}
return arr;
};
module.exports = CalendarFactory;
Source