2015-09-15 14:34:36 +02:00
|
|
|
/*
|
2016-01-07 05:06:39 +01:00
|
|
|
Copyright 2015, 2016 OpenMarket Ltd
|
2015-09-15 14:34:36 +02:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
class Skinner {
|
|
|
|
constructor() {
|
|
|
|
this.components = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
getComponent(name) {
|
|
|
|
if (this.components === null) {
|
|
|
|
throw new Error(
|
|
|
|
"Attempted to get a component before a skin has been loaded."+
|
2017-06-01 10:26:35 +02:00
|
|
|
" This is probably because either:"+
|
2015-09-15 14:34:36 +02:00
|
|
|
" a) Your app has not called sdk.loadSkin(), or"+
|
2017-06-01 10:26:35 +02:00
|
|
|
" b) A component has called getComponent at the root level",
|
2015-09-15 14:34:36 +02:00
|
|
|
);
|
|
|
|
}
|
2019-12-13 03:31:52 +01:00
|
|
|
|
|
|
|
const doLookup = (components) => {
|
|
|
|
if (!components) return null;
|
|
|
|
let comp = components[name];
|
|
|
|
// XXX: Temporarily also try 'views.' as we're currently
|
|
|
|
// leaving the 'views.' off views.
|
|
|
|
if (!comp) {
|
|
|
|
comp = components['views.' + name];
|
|
|
|
}
|
|
|
|
return comp;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check the skin first
|
|
|
|
let comp = doLookup(this.components);
|
|
|
|
|
|
|
|
// If that failed, check against our own components
|
2017-06-01 10:26:35 +02:00
|
|
|
if (!comp) {
|
2019-12-13 03:31:52 +01:00
|
|
|
// Lazily load our own components because they might end up calling .getComponent()
|
|
|
|
comp = doLookup(require("./component-index").components);
|
2017-06-01 10:26:35 +02:00
|
|
|
}
|
|
|
|
|
2019-12-13 03:31:52 +01:00
|
|
|
// Just return nothing instead of erroring - the consumer should be smart enough to
|
|
|
|
// handle this at this point.
|
2017-06-01 10:26:35 +02:00
|
|
|
if (!comp) {
|
2019-12-13 03:31:52 +01:00
|
|
|
return null;
|
2017-06-01 10:26:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// components have to be functions.
|
|
|
|
const validType = typeof comp === 'function';
|
|
|
|
if (!validType) {
|
|
|
|
throw new Error(`Not a valid component: ${name}.`);
|
2015-11-30 18:33:04 +01:00
|
|
|
}
|
2017-06-01 10:26:35 +02:00
|
|
|
return comp;
|
2015-09-15 14:34:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
load(skinObject) {
|
|
|
|
if (this.components !== null) {
|
|
|
|
throw new Error(
|
|
|
|
"Attempted to load a skin while a skin is already loaded"+
|
2017-07-01 15:28:12 +02:00
|
|
|
"If you want to change the active skin, call resetSkin first");
|
2015-09-15 14:34:36 +02:00
|
|
|
}
|
2015-11-30 18:33:04 +01:00
|
|
|
this.components = {};
|
2017-07-01 15:28:12 +02:00
|
|
|
const compKeys = Object.keys(skinObject.components);
|
|
|
|
for (let i = 0; i < compKeys.length; ++i) {
|
|
|
|
const comp = skinObject.components[compKeys[i]];
|
2015-11-30 18:33:04 +01:00
|
|
|
this.addComponent(compKeys[i], comp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addComponent(name, comp) {
|
2017-07-01 15:28:12 +02:00
|
|
|
let slot = name;
|
2015-11-30 18:33:04 +01:00
|
|
|
if (comp.replaces !== undefined) {
|
|
|
|
if (comp.replaces.indexOf('.') > -1) {
|
|
|
|
slot = comp.replaces;
|
|
|
|
} else {
|
|
|
|
slot = name.substr(0, name.lastIndexOf('.') + 1) + comp.replaces.split('.').pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.components[slot] = comp;
|
2015-09-15 14:34:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
reset() {
|
|
|
|
this.components = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We define one Skinner globally, because the intention is
|
|
|
|
// very much that it is a singleton. Relying on there only being one
|
|
|
|
// copy of the module can be dicey and not work as browserify's
|
|
|
|
// behaviour with multiple copies of files etc. is erratic at best.
|
|
|
|
// XXX: We can still end up with the same file twice in the resulting
|
|
|
|
// JS bundle which is nonideal.
|
2017-09-08 18:43:41 +02:00
|
|
|
// See https://derickbailey.com/2016/03/09/creating-a-true-singleton-in-node-js-with-es6-symbols/
|
|
|
|
// or https://nodejs.org/api/modules.html#modules_module_caching_caveats
|
|
|
|
// ("Modules are cached based on their resolved filename")
|
2015-09-15 14:34:36 +02:00
|
|
|
if (global.mxSkinner === undefined) {
|
|
|
|
global.mxSkinner = new Skinner();
|
|
|
|
}
|
|
|
|
module.exports = global.mxSkinner;
|
|
|
|
|