/************************************************************************ * This file is part of EspoCRM. * * EspoCRM - Open Source CRM application. * Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko * Website: https://www.espocrm.com * * EspoCRM is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EspoCRM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with EspoCRM. If not, see http://www.gnu.org/licenses/. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU General Public License version 3. * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ var Espo = Espo || {classMap:{}}; (function (Espo, _, $) { var root = this; Espo.Loader = function (cache, cacheTimestamp) { this.basePath = $('script[data-base-path]').data('basePath') || ''; this.cache = cache || null; this._loadCallbacks = {}; this.pathsBeingLoaded = {}; this.libsConfig = {}; this.cacheTimestamp = cacheTimestamp || null; this.loadingSubject = null; } _.extend(Espo.Loader.prototype, { cache: null, data: null, classMap: Espo, _loadCallbacks: null, libsConfigUrl: 'client/cfg/libs.json', _getClass: function (name) { if (name in this.classMap) { return this.classMap[name]; } return false; }, _setClass: function (name, o) { this.classMap[name] = o; }, _nameToPath: function (name) { var path; if (name.indexOf(':') != -1) { var arr = name.split(':'); var namePart = arr[1]; var modulePart = arr[0]; if (modulePart == 'custom') { path = 'client/custom/src/' + namePart; } else { path = 'client/modules/' + modulePart + '/src/' + namePart; } } else { path = 'client/src/' + name; } path += '.js'; return path; }, _execute: function (script) { eval.call(root, script); }, _executeLoadCallback: function (subject, o) { if (subject in this._loadCallbacks) { this._loadCallbacks[subject].forEach(function (callback) { callback(o); }); delete this._loadCallbacks[subject]; } }, define: function (subject, dependency, callback) { if (subject) { subject = this.normalizeClassName(subject); } if (this.loadingSubject) { subject = subject || this.loadingSubject; this.loadingSubject = null; } var self = this; var proceed = function (relObj) { var o = callback.apply(this, arguments); if (!o) { if (self.cache) { self.cache.clear('a', subject); } throw new Error("Could not load '" + subject + "'"); } self._setClass(subject, o); self._executeLoadCallback(subject, o); }; if (!dependency) { proceed(); } else { this.require(dependency, function () { proceed.apply(this, arguments); }); } }, require: function (subject, callback, errorCallback) { var list; if (Object.prototype.toString.call(subject) === '[object Array]') { list = subject; list.forEach(function (item, i) { list[i] = this.normalizeClassName(list[i]); }, this); } else if (subject) { subject = this.normalizeClassName(subject); list = [subject]; } else { list = []; } var totalCount = list.length; if (totalCount === 1) { this.load(list[0], callback, errorCallback); return; } else if (totalCount) { var readyCount = 0; var loaded = {}; list.forEach(function (name) { this.load(name, function (c) { loaded[name] = c; readyCount++; if (readyCount == totalCount) { var args = []; for (var i in list) { args.push(loaded[list[i]]); } callback.apply(this, args); } }); }, this); } else { callback.apply(this); } }, convertCamelCaseToHyphen: function (string) { if (string == null) { return string; } return string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); }, normalizeClassName: function (name) { var normalizedName = name; if (~name.indexOf('.') && !~name.indexOf('!')) { console.warn(name + ': class name should use slashes for a directory separator and hyphen format.'); } if (!!/[A-Z]/.exec(name[0])) { if (name.indexOf(':') != -1) { var arr = name.split(':'); var modulePart = arr[0]; var namePart = arr[1]; normalizedName = this.convertCamelCaseToHyphen(modulePart) + ':' + this.convertCamelCaseToHyphen(namePart).split('.').join('/'); } else { normalizedName = this.convertCamelCaseToHyphen(name).split('.').join('/'); } } return normalizedName; }, _addLoadCallback: function (name, callback) { if (!(name in this._loadCallbacks)) { this._loadCallbacks[name] = []; } this._loadCallbacks[name].push(callback); }, dataLoaded: {}, load: function (name, callback, errorCallback) { var dataType, type, path, fetchObject; var realName = name; var noAppCache = false; if (name.indexOf('lib!') === 0) { dataType = 'script'; type = 'lib'; realName = name.substr(4); path = realName; var exportsTo = 'window'; var exportsAs = realName; if (realName in this.libsConfig) { var libData = this.libsConfig[realName] || {}; path = libData.path || path; exportsTo = libData.exportsTo || exportsTo; exportsAs = libData.exportsAs || exportsAs; noAppCache = libData.noAppCache || noAppCache; } fetchObject = function (name, d) { var from = root; if (exportsTo == 'window') { from = root; } else { exportsTo.split('.').forEach(function (item) { from = from[item]; }); } if (exportsAs in from) { return from[exportsAs]; } } } else if (name.indexOf('res!') === 0) { dataType = 'text'; type = 'res'; realName = name.substr(4); path = realName; } else { dataType = 'script'; type = 'class'; if (!name || name == '') { throw new Error("Can not load empty class name"); } var c = this._getClass(name); if (c) { callback(c); return; } path = this._nameToPath(name); } if (name in this.dataLoaded) { callback(this.dataLoaded[name]); return; } if (this.cache) { var cached = this.cache.get('a', name); if (cached) { if (type == 'class') { this.loadingSubject = name; } if (dataType == 'script') { this._execute(cached); } if (type == 'class') { var c = this._getClass(name); if (c) { callback(c); return; } this._addLoadCallback(name, callback); } else { var d = cached; if (typeof fetchObject == 'function') { d = fetchObject(realName, cached); } this.dataLoaded[name] = d; callback(d); } return; } } if (path in this.pathsBeingLoaded) { this._addLoadCallback(name, callback); return; } this.pathsBeingLoaded[path] = true; var useCache = false; if (this.cacheTimestamp) { useCache = true; var sep = (path.indexOf('?') > -1) ? '&' : '?'; path += sep + 'r=' + this.cacheTimestamp; } $.ajax({ type: 'GET', cache: useCache, dataType: 'text', mimeType: 'text/plain', local: true, url: this.basePath + path, success: function (response) { if (this.cache && !noAppCache) { this.cache.set('a', name, response); } this._addLoadCallback(name, callback); if (type == 'class') { this.loadingSubject = name; } if (dataType == 'script') { this._execute(response); } var data; if (type == 'class') { data = this._getClass(name); if (data && typeof data === 'function') { this._executeLoadCallback(name, data); } } else { data = response; if (typeof fetchObject == 'function') { data = fetchObject(realName, response); } this.dataLoaded[name] = data; this._executeLoadCallback(name, data); } return; }.bind(this), error: function (event, xhr, options) { if (typeof errorCallback == 'function') { errorCallback(); return; } throw new Error("Could not load file '" + path + "'"); } }); }, loadLib: function (url, callback) { if (this.cache) { var script = this.cache.get('a', url); if (script) { this._execute(script); if (typeof callback == 'function') { callback(); } return; } } $.ajax({ url: this.basePath + url, type: 'GET', dataType: 'script', local: true, success: function () { if (typeof callback == 'function') { callback(); } }, error: function () { throw new Error("Could not load file '" + url + "'"); }, }); }, loadLibsConfig: function (callback) { $.ajax({ url: this.basePath + this.libsConfigUrl, dataType: 'json', local: true, success: function (data) { this.libsConfig = data; callback(); }.bind(this) }); }, addLibsConfig: function (data) { this.libsConfig = _.extend(this.libsConfig, data); }, }); Espo.loader = new Espo.Loader(); root.require = Espo.require = function (subject, callback, context, errorCallback) { if (context) { callback = callback.bind(context); } Espo.loader.require(subject, callback, errorCallback); } root.define = Espo.define = function (arg1, arg2, arg3) { var subject = null; var dependency = null; var callback = null; if (typeof arg1 === 'function') { callback = arg1; } else if (typeof arg1 !== 'undefined' && typeof arg2 === 'function') { dependency = arg1; callback = arg2; } else { subject = arg1; dependency = arg2; callback = arg3; } Espo.loader.define(subject, dependency, callback); } Espo.loadLib = function (url, callback) { Espo.loader.loadLib(url, callback); } }).call(this, Espo, _, $);