/** * EGroupware clientside API object * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package etemplate * @subpackage api * @link http://www.egroupware.org * @author Andreas Stöckel (as AT stylite.de) * @author Ralf Becker * @version $Id$ */ /*egw:uses egw_core; egw_ready; egw_debug; */ import './egw_core.js'; /** * @augments Class * @param {string} _app application name object is instanciated for * @param {object} _wnd window object is instanciated for */ egw.extend('files', egw.MODULE_WND_LOCAL, function(_app, _wnd) { "use strict"; var egw = this; /** * Remove optional timestamp attached as query parameter, eg. /path/name.js?12345678[&other=val] * * Examples: * /path/file.js --> /path/file.js * /path/file.js?123456 --> /path/file.js * /path/file.php?123456¶m=value --> /path/file.php?param=value * /path/file.php?param=value&123456 --> /path/file.php?param=value * * @param _src url * @return url with timestamp stripped off */ function removeTS(_src) { return _src.replace(/[?&][0-9]+&?/, '?').replace(/\?$/, ''); } /** * RegExp to extract string with comma-separated files from a bundle-url * * @type RegExp */ var bundle2files_regexp = /phpgwapi\/inc\/min\/\?b=[^&]+&f=([^&]+)/; /** * Regexp to detect and remove .min.js extension * * @type RegExp */ var min_js_regexp = /\.min\.js$/; /** * Return array of files-sources from bundle(s) incl. bundle-src itself * * @param {string|Array} _srcs all url's have to be egw releativ! * @returns {Array} */ function files_from_bundles(_srcs) { var files = []; if (typeof _srcs == 'string') _srcs = [_srcs]; for(var n=0; n < _srcs.length; ++n) { var file = _srcs[n]; files.push(file.replace(min_js_regexp, '.js')); var contains = file.match(bundle2files_regexp); if (contains && contains.length > 1) { var bundle = contains[1].split(','); for(var i=0; i < bundle.length; ++i) { files.push(bundle[i].replace(min_js_regexp, '.js')); } } } return files; } /** * Strip of egw_url from given urls (if containing it) * * @param {array} _urls absolute urls * @returns {array} relativ urls */ function strip_egw_url(_urls) { var egw_url = egw.webserverUrl; if (egw_url.charAt(egw_url.length-1) != '/') egw_url += '/'; for(var i=0; i < _urls.length; ++i) { var file = _urls[i]; // check if egw_url is only path and urls contains full url incl. protocol // --> prefix it with our protocol and host, as eg. splitting by just '/' will fail! var need_full_url = egw_url[0] == '/' && file.substr(0,4) == 'http' ? window.location.protocol+'//'+window.location.host : ''; var parts = file.split(need_full_url+egw_url); if (parts.length > 1) { // discard protocol and host parts.shift(); _urls[i] = parts.join(need_full_url+egw_url); } } return _urls; } /** * Array which contains all currently bound in javascript and css files. */ var files = []; // add already included scripts var tags = jQuery('script', _wnd.document); for(var i=0; i < tags.length; ++i) { files.push(removeTS(tags[i].src)); } // add already included css tags = jQuery('link[type="text/css"]', _wnd.document); for(var i=0; i < tags.length; ++i) { files.push(removeTS(tags[i].href)); } // make urls egw-relative files = strip_egw_url(files); // resolve bundles and replace .min.js with .js files = files_from_bundles(files); return { /** * Load and execute javascript file(s) in order * * @memberOf egw * @param {string|array} _jsFiles (array of) urls to include * @param {function} _callback called after JS files are loaded and executed * @param {object} _context * @param {string} _prefix prefix for _jsFiles * @return Promise */ includeJS: function(_jsFiles, _callback, _context, _prefix) { // Also allow including a single javascript file if (typeof _jsFiles === 'string') { _jsFiles = [_jsFiles]; } // filter out files included by script-tag via egw.js _jsFiles = _jsFiles.filter((src) => src.match(egw.legacy_js_regexp) === null); let promise; if (_jsFiles.length === 1) // running this in below case fails when loading app.js from etemplate.load() { const src = _jsFiles[0]; promise = import(_prefix ? _prefix+src : src) .catch((err) => { console.log(src+":\n\n"+err.message); }); } else { promise = Promise.all(_jsFiles.map((src) => { import(_prefix ? _prefix+src : src) .catch((err) => { console.log(src+":\n\n"+err.message); }) })); } return typeof _callback === 'undefined' ? promise : promise.then(_callback.call(_context)); }, /** * Check if file is already included and optional mark it as included if not yet included * * Check does NOT differenciate between file.min.js and file.js. * Only .js get's recored in files for further checking, if _add_if_not set. * * @param {string} _file * @param {boolean} _add_if_not if true mark file as included * @return boolean true if file already included, false if not */ included: function(_file, _add_if_not) { var file = removeTS(_file).replace(min_js_regexp, '.js'); var not_inc = files.indexOf(file) == -1; if (not_inc && _add_if_not) { files = files.concat(files_from_bundles(file)); } return !not_inc; }, /** * Include a CSS file * * @param {string|array} _cssFiles full url of file to include */ includeCSS: function(_cssFiles) { if (typeof _cssFiles == 'string') _cssFiles = [_cssFiles]; _cssFiles = strip_egw_url(_cssFiles); for(var n=0; n < _cssFiles.length; ++n) { var file = _cssFiles[n]; if (!this.included(file, true)) // check if included and marks as such if not { // Create the node which is used to include the css file var cssnode = _wnd.document.createElement('link'); cssnode.type = "text/css"; cssnode.rel = "stylesheet"; cssnode.href = egw.webserverUrl+'/'+file; // Get the head node and append the newly created "link" nod to it var head = _wnd.document.getElementsByTagName('head')[0]; head.appendChild(cssnode); } } } }; });