mirror of
https://bitbucket.org/bellord13/kit.git
synced 2026-11-13 19:06:11 +03:00
656 lines
23 KiB
JavaScript
656 lines
23 KiB
JavaScript
|
|
var kit;
|
|
kit = window.kit = window.kit || {};
|
|
kit.service = kit.service || {};
|
|
kit.config = kit.config || {};
|
|
(function(global){
|
|
var stateList = {
|
|
undefined: 0,
|
|
registered: 1,
|
|
loaded: 3,
|
|
installed: 4,
|
|
};
|
|
var moduleTypeList = {
|
|
loader: 'loader',
|
|
import: 'import',
|
|
};
|
|
|
|
/**
|
|
* @class PluginManager
|
|
* @param global
|
|
* @param plugins
|
|
* @memberOf kit
|
|
*/
|
|
var PluginManager = function(global, plugins = {}){
|
|
this.plugins = {};
|
|
this.global = global;
|
|
this.registerPlugins(plugins);
|
|
};
|
|
|
|
var __proto = PluginManager.prototype;
|
|
|
|
__proto.__checkState = function(pluginId, state){
|
|
return this.plugins.hasOwnProperty(pluginId) && this.plugins[pluginId].state >= state;
|
|
};
|
|
|
|
/**
|
|
* @param plugins
|
|
* @return {PluginManager}
|
|
*/
|
|
__proto.registerPlugins = function (plugins) {
|
|
if(Array.isArray(plugins)) {
|
|
plugins = Object.assign({}, plugins);
|
|
}
|
|
for (var pluginId in plugins) {
|
|
if(plugins.hasOwnProperty(pluginId)) {
|
|
this.register(pluginId, plugins[pluginId]);
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
*
|
|
* @param pluginId
|
|
* @param {kitModuleTypeLoader|{}} config
|
|
* @return {boolean}
|
|
*/
|
|
__proto.register = function (pluginId, config = {}) {
|
|
//console.warn('PluginManager.register: plugin ' + pluginId + ' ('+(this.plugins[pluginId]?this.plugins[pluginId].state:'0')+')');
|
|
var defaultPlugin = {
|
|
module: {
|
|
exports: {},
|
|
},
|
|
config: {
|
|
},
|
|
promise: new Promise(function(resolve, reject){resolve()}),
|
|
id: pluginId,
|
|
state: stateList.undefined,
|
|
stateInit: {
|
|
loading: false,
|
|
installing: false,
|
|
},
|
|
};
|
|
var plugin = this.__getPlugin(pluginId);
|
|
if(plugin) {
|
|
pluginId = plugin.id;
|
|
} else {
|
|
plugin = this.plugins[pluginId] = defaultPlugin;
|
|
}
|
|
|
|
if(
|
|
this.__checkState(pluginId, stateList.installed) ||
|
|
(
|
|
(plugin.stateInit.loading || this.__checkState(pluginId, stateList.loaded)) && typeof config === 'object'
|
|
)
|
|
) {
|
|
console.warn('PluginManager.register: plugin ' + pluginId + ' уже невозможно конфигурировать ('+plugin.state+')');
|
|
return false;
|
|
}
|
|
|
|
var loader;
|
|
if(typeof config === 'function') {
|
|
loader = config;
|
|
config = plugin.config;
|
|
config.loader = loader;
|
|
config.type = 'loader';
|
|
}
|
|
|
|
if(typeof config === 'object') {
|
|
if(typeof config.onLoad !== 'function') {
|
|
config.onLoad = function(){};
|
|
}
|
|
if(typeof config.onInstall !== 'function') {
|
|
config.onInstall = function(){};
|
|
}
|
|
if(typeof config.onRegister !== 'function') {
|
|
config.onRegister = function(){};
|
|
}
|
|
if(typeof config.path !== 'string') {
|
|
config.path = '';
|
|
}
|
|
if(!config.hasOwnProperty('modulePath')) {
|
|
config.modulePath = config.path;
|
|
} else if(typeof config.modulePath !== 'string') {
|
|
config.modulePath = config.path;
|
|
}
|
|
if(typeof config.js === 'string') {
|
|
config.js = [config.js];
|
|
} else if(!Array.isArray(config.js)) {
|
|
config.js = [];
|
|
}
|
|
if(typeof config.css === 'string') {
|
|
config.css = [config.css];
|
|
} else if(!Array.isArray(config.css)) {
|
|
config.css = [];
|
|
}
|
|
|
|
if(typeof config.alias === 'string') {
|
|
config.alias = [config.alias];
|
|
} else if(!Array.isArray(config.alias)) {
|
|
config.alias = [];
|
|
}
|
|
|
|
config.loadJsAfterModule = !!config.loadJsAfterModule;
|
|
config.type = config.type || 'import'; //loader
|
|
|
|
if(typeof config.deps === 'string') {
|
|
config.deps = [config.deps];
|
|
} else if(!Array.isArray(config.deps)) {
|
|
config.deps = [];
|
|
}
|
|
|
|
} else {
|
|
console.warn('PluginManager.register: plugin ' + pluginId + ' is not a valid format');
|
|
return false;
|
|
}
|
|
|
|
Object.assign(plugin.config, config);
|
|
|
|
if(!this.__checkState(pluginId, stateList.registered)) {
|
|
plugin.state = stateList.registered;
|
|
plugin.config.onRegister(plugin);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* @param pluginId
|
|
* @param callback
|
|
* @return {PluginManager}
|
|
*/
|
|
__proto.load = function (pluginId, callback) {
|
|
var self = this;
|
|
//console.log('PluginManager.load: plugin ' + pluginId + ' ('+(this.plugins[pluginId]?this.plugins[pluginId].state:'0')+')');
|
|
if(typeof callback !== 'function') {
|
|
callback = function(){};
|
|
}
|
|
|
|
var plugin = this.__getPlugin(pluginId);
|
|
if(plugin) {
|
|
pluginId = plugin.id;
|
|
}
|
|
|
|
if(!this.__checkState(pluginId, stateList.registered)) {
|
|
console.warn('kit.load: plugin ' + pluginId + ' is not register');
|
|
return this;
|
|
}
|
|
if(this.__checkState(pluginId, stateList.loaded)) {
|
|
//console.warn('PluginManager.load: plugin ' + pluginId + ' уже загружен');
|
|
callback();
|
|
return this;
|
|
}
|
|
|
|
|
|
if(plugin.stateInit.loading) {
|
|
//console.log('PluginManager.install: plugin ' + pluginId + ' уже грузится');
|
|
this.__exec(pluginId, callback);
|
|
return this;
|
|
}
|
|
plugin.stateInit.loading = true;
|
|
|
|
var configModule = plugin.config;
|
|
|
|
var stackPush = function (callbackPromise, argArr = []){
|
|
if(typeof plugin.promise === 'undefined') {
|
|
plugin.promise = callbackPromise.apply(this, argArr);
|
|
} else {
|
|
plugin.promise = plugin.promise.then(function(m){
|
|
return callbackPromise.apply(this, argArr);
|
|
});
|
|
}
|
|
}.bind(this);
|
|
|
|
var getModulePromise = (function () {
|
|
|
|
function getFileLoadNode(src) {
|
|
src = src.match(/^[^#?]*/)[0];
|
|
return document.querySelector('script[src^="'+src+'"]') ||
|
|
document.querySelector('link[href^="'+src+'"]') ||
|
|
document.querySelector('meta[property="kit:file-load"][name="'+src+'"]');
|
|
}
|
|
|
|
function loadScript(src, resolveCallback, rejectCallback) {
|
|
src = String(src).split('?')[0];
|
|
if(typeof resolveCallback !== 'function'){
|
|
resolveCallback = function(){};
|
|
}
|
|
if(typeof rejectCallback !== 'function'){
|
|
rejectCallback = function(res){
|
|
console.warn(res.message);
|
|
};
|
|
}
|
|
return new Promise(function(resolve, reject) {
|
|
var loadNode = getFileLoadNode(src);
|
|
if (loadNode) {
|
|
global.config.debug && console.warn(`Скрипт ${src} уже загружен`);
|
|
self.__ready(resolve, [loadNode]);
|
|
} else {
|
|
script = document.createElement('script');
|
|
script.src = '' + src + '?__pm' + (new Date()).getTime();
|
|
script.onload = () => resolve(script);
|
|
script.onerror = () => reject(new Error(`Ошибка загрузки скрипта ${src}`));
|
|
document.head.append(script);
|
|
}
|
|
}).then(resolveCallback, rejectCallback);
|
|
}
|
|
function syncLoadScriptList(list) {
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
var i = 0;
|
|
var status = true;
|
|
var count = list.length;
|
|
var checkResolve = function(src, cond){
|
|
if(!cond) {
|
|
console.error(`Ошибка загрузки скрипта ${src}`)
|
|
}
|
|
status = status && cond;
|
|
i++;
|
|
if(i === count) {
|
|
resolve();
|
|
}
|
|
};
|
|
var syncPromise = new Promise(function(_resolve, _reject){_resolve();});
|
|
list.forEach(function(item, i, arr){
|
|
syncPromise.then(function(){
|
|
var src = configModule.path + item;
|
|
var loadNode = getFileLoadNode(src);
|
|
if(loadNode){
|
|
self.__ready(resolve, [src, true]);
|
|
} else {
|
|
script = document.createElement('script');
|
|
script.src = '' + src + '?__pm' + (new Date()).getTime();
|
|
script.onload = () => checkResolve(src, true);
|
|
script.onerror = () => checkResolve(src, false);
|
|
document.head.append(script);
|
|
}
|
|
});
|
|
});
|
|
})
|
|
}
|
|
function loadStyle(src) {
|
|
return new Promise(function(resolve, reject) {
|
|
var loadNode = getFileLoadNode(src);
|
|
if(loadNode) {
|
|
global.config.debug && console.warn(`Стиль ${src} уже загружен`);
|
|
self.__ready(resolve, [src]);
|
|
} else {
|
|
var link = document.createElement('link');
|
|
link.rel = 'stylesheet';
|
|
link.type = 'text/css';
|
|
link.href = src + '?__pm' + (new Date()).getTime();
|
|
link.media = 'all';
|
|
document.head.append(link);
|
|
resolve(link);
|
|
}
|
|
});
|
|
}
|
|
|
|
var getImportPromise = function (){
|
|
if(configModule.module && configModule.type === 'import') {
|
|
return import(configModule.modulePath + configModule.module).then(function(m) {
|
|
m.__esModule = true;
|
|
plugin.module.exports = m;
|
|
}.bind(this), function(r){
|
|
console.error(r);
|
|
});
|
|
} else if(configModule.module && configModule.type === 'loader' && !(configModule.hasOwnProperty('loader') && typeof configModule.loader === 'function')) {
|
|
return loadScript(configModule.modulePath + configModule.module).then(function() {
|
|
|
|
}.bind(this));
|
|
}
|
|
return new Promise(function(resolve, reject){resolve();});
|
|
}.bind(this);
|
|
var getResPromise = function(){
|
|
var p = new Promise(function(resolve, reject){resolve();});
|
|
configModule.js.forEach(function(item, i, array){
|
|
if(Array.isArray(item)) {
|
|
p = p.then(function () {
|
|
return syncLoadScriptList(item);
|
|
});
|
|
} else {
|
|
p = p.then(function () {
|
|
return loadScript(configModule.path + item);
|
|
});
|
|
}
|
|
//loadScript(configModule.path + item);
|
|
});
|
|
return p;
|
|
}.bind(this);
|
|
var setSkipLoadPromise = function (depsPromise) {
|
|
|
|
var noJsPromise, noImportPromise;
|
|
if(configModule.js.length === 0) {
|
|
noJsPromise = true;
|
|
}
|
|
if((!configModule.module && configModule.type === 'import') || (configModule.type === 'loader' && typeof configModule.loader === 'function')) {
|
|
noImportPromise = true;
|
|
}
|
|
plugin.skipLoadPromise = noImportPromise && noJsPromise && !depsPromise;
|
|
return plugin.skipLoadPromise;
|
|
}.bind(this);
|
|
|
|
configModule.css.forEach(function(item, i, array){
|
|
loadStyle(configModule.path + item);
|
|
});
|
|
|
|
var depsPromise = this.__getDepsPromise(this.depsCorrection(configModule.deps));
|
|
setSkipLoadPromise(depsPromise);
|
|
|
|
if(depsPromise) {
|
|
stackPush(function () {
|
|
return depsPromise;
|
|
});
|
|
}
|
|
|
|
if(configModule.loadJsAfterModule) {
|
|
stackPush(getImportPromise);
|
|
stackPush(getResPromise);
|
|
} else {
|
|
stackPush(getResPromise);
|
|
stackPush(getImportPromise);
|
|
}
|
|
|
|
this.__exec(pluginId, function(){
|
|
plugin.state = stateList.loaded;
|
|
plugin.skipLoadPromise = true;
|
|
configModule.onLoad.call(plugin.module.exports);
|
|
callback();
|
|
}.bind(this));
|
|
|
|
return plugin.promise;
|
|
|
|
}).bind(this);
|
|
|
|
getModulePromise();
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* @param pluginId
|
|
* @param callback
|
|
* @return {PluginManager}
|
|
*/
|
|
__proto.install = function(pluginId, callback){
|
|
//console.warn('PluginManager.install: plugin ' + pluginId + ' ('+(this.plugins[pluginId]?this.plugins[pluginId].state:'0')+')');
|
|
if(typeof callback !== 'function') {
|
|
callback = function(){};
|
|
}
|
|
|
|
var plugin = this.__getPlugin(pluginId);
|
|
if(plugin) {
|
|
pluginId = plugin.id;
|
|
}
|
|
var module = plugin.module;
|
|
|
|
if(!this.__checkState(pluginId, stateList.registered)) {
|
|
console.warn('PluginManager.install: plugin ' + pluginId + ' is not register');
|
|
return this;
|
|
}
|
|
if(this.__checkState(pluginId, stateList.installed)) {
|
|
callback();
|
|
return this;
|
|
}
|
|
|
|
if(plugin.stateInit.installing) {
|
|
//console.log('PluginManager.install: plugin ' + pluginId + ' уже устанавливается');
|
|
this.__exec(pluginId, callback);
|
|
return this;
|
|
}
|
|
plugin.stateInit.installing = true;
|
|
|
|
this.load(pluginId);
|
|
|
|
var installer = function(){
|
|
var setGlobalExtension = function (key, aliasList, data) {
|
|
if(data === undefined) {
|
|
return;
|
|
}
|
|
if(this.global.hasOwnProperty(key)) {
|
|
console.warn(`PluginManager.install(${pluginId}): key ${key} overwritten in kit (${plugin.state}).`);
|
|
}
|
|
this.global[key] = data;
|
|
if(Array.isArray(aliasList) && aliasList.length) {
|
|
aliasList.forEach(function (alias) {
|
|
setGlobalExtension(alias, [], data)
|
|
}.bind(this))
|
|
}
|
|
}.bind(this);
|
|
if(plugin.config.type === moduleTypeList.loader && typeof plugin.config.loader === 'function') {
|
|
var returnData = plugin.config.loader.bind(module.exports).call(module.exports, module, module.exports, this.require.bind(this), global);
|
|
setGlobalExtension(pluginId, plugin.config.alias, returnData);
|
|
|
|
} else if(plugin.config.type === moduleTypeList.import && typeof plugin.config.module === 'string') {
|
|
setGlobalExtension(pluginId, plugin.config.alias, (typeof module.exports === 'object' && module.exports.default !== undefined)?module.exports.default:module.exports);
|
|
}
|
|
console.log('%cPluginManager.install: plugin ' + pluginId + ' установлен', "color: green;");
|
|
plugin.state = stateList.installed;
|
|
}.bind(this);
|
|
|
|
this.__exec(pluginId, function(){
|
|
installer();
|
|
plugin.config.onInstall.call(module.exports, plugin);
|
|
callback();
|
|
}.bind(this));
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* @name kitPluginManagerRequire
|
|
* @param {string} pluginId
|
|
* @param {callback} callback
|
|
* @return {boolean}
|
|
*/
|
|
__proto.require = function (pluginId, callback) {
|
|
|
|
var plugin = this.__getPlugin(pluginId);
|
|
if(!plugin) {
|
|
console.warn('PluginManager.require: plugin ' + pluginId + ' not found ('+(this.plugins[pluginId]?this.plugins[pluginId].state:'0')+')');
|
|
return undefined;
|
|
}
|
|
pluginId = plugin.id;
|
|
var module = plugin.module;
|
|
|
|
if(!this.__checkState(pluginId, stateList.registered)) {
|
|
console.warn('PluginManager.require: plugin ' + pluginId + ' is not register');
|
|
return false;
|
|
}
|
|
if(typeof callback !== 'function') {
|
|
callback = function(){};
|
|
}
|
|
|
|
this.install(pluginId, callback);
|
|
|
|
return module.exports;
|
|
};
|
|
|
|
|
|
|
|
__proto.depsCorrection = function(deps){
|
|
if(!Array.isArray(deps) ) {
|
|
deps = [deps];
|
|
}
|
|
var correct = [];
|
|
deps.forEach(function(pluginId, i, arr) {
|
|
var realPlugin = this.__getPlugin(pluginId);
|
|
if(realPlugin === undefined) {
|
|
console.warn(`Plugin "${pluginId}" not registered!`);
|
|
return false;
|
|
}
|
|
correct.push(realPlugin.id);
|
|
}.bind(this));
|
|
return correct;
|
|
};
|
|
|
|
__proto.exec = function (deps, callback, argArr = [], context = undefined) {
|
|
|
|
if(typeof deps === 'function') {
|
|
context = argArr;
|
|
argArr = callback;
|
|
callback = deps;
|
|
deps = [];
|
|
}
|
|
if(context === undefined) {
|
|
context = this;
|
|
}
|
|
|
|
deps = this.depsCorrection(deps);
|
|
|
|
if(typeof argArr === 'undefined') {
|
|
argArr = [];
|
|
}
|
|
|
|
deps = Array.from(new Set(deps.concat(global.config.requiredDependencies)));
|
|
|
|
if(typeof callback !== 'function') {
|
|
return undefined;
|
|
}
|
|
var depsPromise = this.__getDepsPromise(deps);
|
|
|
|
var getArgArr = function () {
|
|
if(argArr.length === 0) {
|
|
deps.forEach(function (pluginId) {
|
|
argArr.push(this.require(pluginId));
|
|
}.bind(this));
|
|
return argArr;
|
|
}
|
|
return argArr;
|
|
}.bind(this);
|
|
|
|
if(depsPromise) {
|
|
depsPromise.then(function(){
|
|
callback.apply(context, getArgArr());
|
|
});
|
|
} else {
|
|
callback.apply(context, getArgArr())
|
|
}
|
|
};
|
|
|
|
__proto.__exec = function (pluginId, callback, argArr = []) {
|
|
|
|
var module = this.plugins[pluginId];
|
|
|
|
var exec = function(_callback, argArr){
|
|
if(module.skipLoadPromise) {
|
|
_callback.apply(this, argArr);
|
|
} else {
|
|
module.promise = module.promise.then(function(){
|
|
_callback.apply(this, argArr);
|
|
}.bind(this));
|
|
}
|
|
}.bind(this);
|
|
|
|
exec(callback, argArr);
|
|
};
|
|
|
|
__proto.__getDepsPromise = function (pluginList) {
|
|
var skipPromise = true;
|
|
var promise = new Promise((resolve, reject)=>{
|
|
var i = 0;
|
|
var checkModule = function(){
|
|
if(++i === pluginList.length + 1) {
|
|
resolve();
|
|
}
|
|
};
|
|
checkModule();
|
|
pluginList.forEach(function(pluginId, i, array){
|
|
if(!this.__checkState(pluginId, stateList.registered)) {
|
|
checkModule();
|
|
return false;
|
|
}
|
|
this.install(pluginId, checkModule);
|
|
skipPromise = skipPromise && this.plugins[pluginId].skipLoadPromise;
|
|
}.bind(this));
|
|
});
|
|
|
|
return !skipPromise?promise:undefined;
|
|
};
|
|
|
|
|
|
__proto.ready = function(deps, callback) {
|
|
if(typeof deps === 'function') {
|
|
callback = deps;
|
|
deps = [];
|
|
}
|
|
this.__ready(this.exec, [deps, callback], this);
|
|
};
|
|
__proto.__ready = function(fn, args = [], context = window) {
|
|
context = context || this;
|
|
if (/complete|interactive|loaded/.test(document.readyState)) {
|
|
fn.apply(context, args);
|
|
} else {
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
fn.apply(context, args);
|
|
}.bind(this));
|
|
}
|
|
};
|
|
|
|
__proto.__getPlugin = function (pluginId) {
|
|
if(this.plugins.hasOwnProperty(pluginId)) {
|
|
return this.plugins[pluginId];
|
|
}
|
|
var _pluginId, alias;
|
|
for (_pluginId in this.plugins) {
|
|
if(this.plugins.hasOwnProperty(_pluginId) && this.plugins[_pluginId].config.alias.indexOf(pluginId) !== -1) {
|
|
return this.plugins[_pluginId];
|
|
}
|
|
}
|
|
var pluginIdLowerCase = pluginId.toLowerCase();
|
|
for (_pluginId in this.plugins) {
|
|
if(!this.plugins.hasOwnProperty(_pluginId)) {
|
|
continue;
|
|
}
|
|
if(pluginIdLowerCase === _pluginId.toLowerCase()) {
|
|
return this.plugins[_pluginId];
|
|
}
|
|
for(alias of this.plugins[_pluginId].config.alias) {
|
|
if(pluginIdLowerCase === alias.toLowerCase()) {
|
|
return this.plugins[_pluginId];
|
|
}
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
/**
|
|
* @class kitConfig
|
|
*/
|
|
var defaultConfig = {
|
|
version: '1.10',
|
|
assetsPath: '',
|
|
requiredDependencies: [],
|
|
autoloadPluginList: [],
|
|
debug: false,
|
|
defaultPhoneMask: '+7 (999) 999-99-99',
|
|
};
|
|
var manager = new PluginManager(global);
|
|
manager.register('PluginManager', {
|
|
alias: 'pm',
|
|
type: moduleTypeList.loader,
|
|
loader: function(module, exports, require) {
|
|
return module.exports = manager;
|
|
}
|
|
});
|
|
manager.install('PluginManager');
|
|
manager.register('config', {
|
|
type: moduleTypeList.loader,
|
|
loader: function(module, exports, require, global) {
|
|
var config = global.config || {};
|
|
config = Object.assign({}, defaultConfig, config);
|
|
window.__pmConfig__ = global.config = config;
|
|
}
|
|
});
|
|
manager.install('config');
|
|
|
|
window.__PluginManager__ = manager;
|
|
global.ready = manager.ready.bind(manager);
|
|
global.exec = manager.exec.bind(manager);
|
|
global.require = manager.require.bind(manager);
|
|
/** @type {kitCore} */
|
|
window.__pmGlobal__ = global;
|
|
})(kit);
|
|
|