egroupware/api/js/jsapi/egw_files.js
Ralf Becker a5ad691757 deprecated egw.includeJS() in favor of es6 import statement
with egw composition happening in main window the used import statement happens in that context and NOT in the window (eg. popup or iframe) this module is instantiated for!
2021-07-09 17:27:22 +02:00

240 lines
6.6 KiB
JavaScript

/**
* 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 <RalfBecker@outdoor-training.de>
*/
/*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&param=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
*
* Deprecated because with egw composition happening in main window the used import statement happens in that context
* and NOT in the window (eg. popup or iframe) this module is instantiated for!
*
* @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
* @deprecated use es6 import statement: Promise.all([].concat(_jsFiles).map((src)=>import(_prefix+src))).then(...)
* @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.error(src+": "+err.message);
return Promise.reject(err.message);
});
}
else
{
promise = Promise.all(_jsFiles.map((src) => {
import(_prefix ? _prefix+src : src)
.catch((err) => {
console.error(src+": "+err.message);
return Promise.reject(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);
}
}
}
};
});