- switching minifying of javascript to use Grunt and Uglify (instead of doing it on the fly with php on every host)

- generating now a couple of bundles: api, et2, mail, calendar, jdots, pixelegg and mobile
- developers need to update minified files whenever a javascript file changes: grunt uglify[:(api|et2|mail|calendar|jdots|pixelegg|mobile)]
- minified files also contains a map file allowing to debug with them
This commit is contained in:
Ralf Becker 2016-03-01 20:45:31 +00:00
parent 96ba38dbf8
commit f25070a9c2
13 changed files with 320 additions and 40 deletions

View File

@ -68,7 +68,6 @@ module.exports = function (grunt) {
"phpgwapi\/js\/jsapi\/egw_message.js",
"phpgwapi\/js\/es6-promise.min.js",
"phpgwapi\/js\/jsapi\/app_base.js",
"phpgwapi\/js\/jsapi\/egw.js",
"phpgwapi\/js\/dhtmlxtree\/codebase\/dhtmlxcommon.js",
"phpgwapi\/js\/dhtmlxtree\/sources\/dhtmlxtree.js",
"phpgwapi\/js\/dhtmlxtree\/sources\/ext\/dhtmlxtree_json.js",
@ -188,12 +187,63 @@ module.exports = function (grunt) {
},
mail: {
files: {
"mail\/js\/app.min.js": "mail\/js\/app.js"
"mail\/js\/app.min.js": [
"mail\/js\/app.js"
]
}
},
calendar: {
files: {
"calendar\/js\/app.min.js": "calendar\/js\/app.js"
"calendar\/js\/app.min.js": [
"calendar\/js\/et2_widget_owner.js",
"calendar\/js\/et2_widget_view.js",
"calendar\/js\/et2_widget_timegrid.js",
"calendar\/js\/et2_widget_event.js",
"calendar\/js\/et2_widget_daycol.js",
"calendar\/js\/et2_widget_planner_row.js",
"calendar\/js\/et2_widget_planner.js",
"calendar\/js\/app.js"
]
}
},
jdots: {
files: {
"jdots\/js\/fw_jdots.min.js": [
"phpgwapi\/js\/framework\/fw_base.js",
"phpgwapi\/js\/framework\/fw_browser.js",
"phpgwapi\/js\/jquery\/mousewheel\/mousewheel.js",
"phpgwapi\/js\/framework\/fw_ui.js",
"phpgwapi\/js\/framework\/fw_classes.js",
"phpgwapi\/js\/framework\/fw_desktop.js",
"jdots\/js\/fw_jdots.js"
]
}
},
mobile: {
files: {
"jdots\/js\/fw_mobile.min.js": [
"phpgwapi\/js\/jquery\/fastclick\/lib\/fastclick.js",
"phpgwapi\/js\/framework\/fw_base.js",
"phpgwapi\/js\/framework\/fw_browser.js",
"phpgwapi\/js\/jquery\/mousewheel\/mousewheel.js",
"phpgwapi\/js\/framework\/fw_ui.js",
"phpgwapi\/js\/framework\/fw_classes.js",
"jdots\/js\/fw_mobile.js"
]
}
},
pixelegg: {
files: {
"pixelegg\/js\/fw_pixelegg.min.js": [
"phpgwapi\/js\/framework\/fw_base.js",
"phpgwapi\/js\/framework\/fw_browser.js",
"phpgwapi\/js\/jquery\/mousewheel\/mousewheel.js",
"phpgwapi\/js\/framework\/fw_ui.js",
"phpgwapi\/js\/framework\/fw_classes.js",
"phpgwapi\/js\/framework\/fw_desktop.js",
"pixelegg\/js\/slider.js",
"pixelegg\/js\/fw_pixelegg.js"
]
}
}
}

View File

@ -822,6 +822,9 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail,
ZLog::Write(LOGLEVEL_DEBUG,__METHOD__."() truncsize=$truncsize, mimeSupport=".array2string($mimesupport));
$bodypreference = $contentparameters->GetBodyPreference(); /* fmbiete's contribution r1528, ZP-320 */
// fix for z-push bug returning additional bodypreference type 4, even if only 1 is requested and mimessupport = 0
if (!$mimesupport && ($key = array_search('4', $bodypreference))) unset($bodypreference[$key]);
//$this->debugLevel=4;
if (!isset($this->mail)) $this->mail = mail_bo::getInstance(false,self::$profileID,true,false,true);
ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' FolderID:'.$folderid.' ID:'.$id.' TruncSize:'.$truncsize.' Bodypreference: '.array2string($bodypreference));

6
mail/js/app.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
mail/js/app.min.js.map Normal file

File diff suppressed because one or more lines are too long

View File

@ -2036,6 +2036,20 @@ abstract class egw_framework
return "\n".$start.implode($end.$start, $to_include).$end;
}
/**
* Url of minified version of bundle
*
* @var array
*/
static $bundle2minurl = array(
'api' => '/phpgwapi/js/jsapi.min.js',
'et2' => '/etemplate/js/etemplate2.min.js',
'et21'=> '/etemplate/js/etemplate2.min.js',
'pixelegg' => '/pixelegg/js/fw_pixelegg.min.js',
'jdots' => '/jdots/js/fw_jdots.min.js',
'mobile' => '/jdots/js/fw_mobile.min.js',
);
/**
* Devide js-includes in bundles of javascript files to include eg. api or etemplate2, if minifying is enabled
*
@ -2054,7 +2068,13 @@ abstract class egw_framework
unset($bundles['.ts']);
foreach($bundles as $name => $files)
{
$file2bundle += array_combine($files, array_fill(0, count($files), $name));
// ignore bundles of not used templates, as they can contain identical files
if (in_array($name, array('api', 'et2', 'et21')) ||
$name == (html::$ua_mobile ? 'mobile' : $GLOBALS['egw_info']['server']['template_set']) ||
isset($GLOBALS['egw_info']['apps'][$name]))
{
$file2bundle += array_combine($files, array_fill(0, count($files), $name));
}
}
}
$to_include = $included_bundles = array();
@ -2065,18 +2085,33 @@ abstract class egw_framework
{
if (($bundle = $file2bundle[$file]))
{
//error_log(__METHOD__."() requiring boundle $bundle for $file");
//error_log(__METHOD__."() requiring bundle $bundle for $file");
if (!in_array($bundle, $included_bundles))
{
$max_modified = 0;
$to_include = array_merge($to_include, self::bundle_urls($bundles[$bundle], $max_modified));
$included_bundles[] = $bundle;
// check if bundle-config is more recent then
if ($max_modified > $bundles_ts)
$minurl = self::$bundle2minurl[$bundle];
if (!isset($minurl) && isset($GLOBALS['egw_info']['apps'][$bundle]))
{
// force new bundle config by deleting cached one and call ourself again
egw_cache::unsetTree(__CLASS__, 'bundles');
return self::bundle_js_includes($js_includes);
$minurl = '/'.$bundle.'/js/app.min.js';
}
if (isset($minurl) && file_exists(EGW_SERVER_ROOT.$minurl))
{
if (!isset($to_include[$minurl]))
{
$to_include[$minurl] = $minurl.'?'.filemtime(EGW_SERVER_ROOT.$minurl);
}
}
else
{
$max_modified = 0;
$to_include = array_merge($to_include, self::bundle_urls($bundles[$bundle], $max_modified));
// check if bundle-config is more recent then
if ($max_modified > $bundles_ts)
{
// force new bundle config by deleting cached one and call ourself again
egw_cache::unsetTree(__CLASS__, 'bundles');
return self::bundle_js_includes($js_includes);
}
}
}
}
@ -2085,7 +2120,14 @@ abstract class egw_framework
unset($query);
list($path, $query) = explode('?', $file, 2);
$mod = filemtime(EGW_SERVER_ROOT.$path);
// check if we have a more recent minified version of the file and use it
if ($GLOBALS['egw_info']['server']['debug_minify'] !== 'True' &&
substr($path, -3) == '.js' && file_exists(EGW_SERVER_ROOT.($min_path = substr($path, 0, -3).'.min.js')) &&
(($min_mod = filemtime(EGW_SERVER_ROOT.$min_path)) >= $mod))
{
$path = $min_path;
$mod = $min_mod;
}
$to_include[$file] = $path.'?'.$mod.($query ? '&'.$query : '');
}
}
@ -2169,7 +2211,7 @@ abstract class egw_framework
$inc_mgr = new egw_include_mgr();
$bundles = array();
$api_max_mod = $et2_max_mod = $jdots_max_mod = 0;
$max_mod = array();
// generate api bundle
$inc_mgr->include_js_file('/phpgwapi/js/jquery/jquery.js');
@ -2195,22 +2237,33 @@ abstract class egw_framework
$inc_mgr->include_js_file('/phpgwapi/js/ckeditor/ckeditor.js');
$inc_mgr->include_js_file('/phpgwapi/js/ckeditor/config.js');
$bundles['api'] = $inc_mgr->get_included_files();
self::bundle_urls($bundles['api'], $api_max_mod);
self::bundle_urls($bundles['api'], $max_mod['api']);
// generate et2 bundle (excluding files in api bundle)
//$inc_mgr->include_js_file('/etemplate/js/lib/jsdifflib/difflib.js'); // it does not work with "use strict" therefore included in front
$inc_mgr->include_js_file('/etemplate/js/etemplate2.js');
$bundles['et2'] = array_diff($inc_mgr->get_included_files(), $bundles['api']);
self::bundle_urls($bundles['et2'], $et2_max_mod);
self::bundle_urls($bundles['et2'], $max_mod['et2']);
// generate jdots bundle, if installed
/* switching jdots bundle off, as fw_pixelegg will cause whole jdots bundle incl. fw_jdots to include
if (file_exists(EGW_SERVER_ROOT.'/jdots'))
$stock_files = call_user_func_array('array_merge', $bundles);
// generate template and app bundles, if installed
foreach(array(
'jdots' => '/jdots/js/fw_jdots.js',
'mobile' => '/jdots/js/fw_mobile.js',
'pixelegg' => '/pixelegg/js/fw_pixelegg.js',
'calendar' => '/calendar/js/app.js',
'mail' => '/mail/js/app.js',
) as $bundle => $file)
{
$inc_mgr->include_js_file('/jdots/js/fw_jdots.js');
$bundles['jdots'] = array_diff($inc_mgr->get_included_files(), call_user_func_array('array_merge', $bundles));
self::bundle_urls($bundles['jdots'], $jdots_max_mod);
}*/
if (@file_exists(EGW_SERVER_ROOT.$file))
{
$inc_mgr = new egw_include_mgr($stock_files); // reset loaded files to stock files
$inc_mgr->include_js_file($file);
$bundles[$bundle] = array_diff($inc_mgr->get_included_files(), $stock_files);
self::bundle_urls($bundles[$bundle], $max_mod[$bundle]);
}
}
// automatic split bundles with more then MAX_BUNDLE_FILES (=50) files
foreach($bundles as $name => $files)
@ -2224,7 +2277,7 @@ abstract class egw_framework
}
// store max modification time of all files in all bundles
$bundles['.ts'] = max(array($api_max_mod, $et2_max_mod, $jdots_max_mod));
$bundles['.ts'] = max($max_mod);
//error_log(__METHOD__."() returning ".array2string($bundles));
return $bundles;

View File

@ -270,8 +270,8 @@ class egw_include_mgr
* --> /phpgwapi/inc/calendar-setup.js?lang=de
*
* @param string $package package or complete path (relative to EGW_SERVER_ROOT) to be included
* @param string|array $file=null file to be included - no ".js" on the end or array with get params
* @param string $app='phpgwapi' application directory to search - default = phpgwapi
* @param string|array $file =null file to be included - no ".js" on the end or array with get params
* @param string $app ='phpgwapi' application directory to search - default = phpgwapi
*
* @returns the correct path on the server if the file is found or false, if the
* file is not found or no further processing is needed.
@ -332,8 +332,8 @@ class egw_include_mgr
* --> /phpgwapi/inc/calendar-setup.js?lang=de
*
* @param string $package package or complete path (relative to EGW_SERVER_ROOT) to be included
* @param string|array $file=null file to be included - no ".js" on the end or array with get params
* @param string $app='phpgwapi' application directory to search - default = phpgwapi
* @param string|array $file =null file to be included - no ".js" on the end or array with get params
* @param string $app ='phpgwapi' application directory to search - default = phpgwapi
*/
public function include_js_file($package, $file = null, $app = 'phpgwapi')
{
@ -361,11 +361,11 @@ class egw_include_mgr
* Include given files, optionally clear list of files to include
*
* @param array $files
* @param boolean $clear_files=false if true clear list of files, before including given ones
* @param boolean $clear_files =false if true clear list of files, before including given ones
*/
public function include_files(array $files, $clear_files=false)
{
if ($clear_files) self::$included_files = array();
if ($clear_files) $this->included_files = array();
foreach ($files as $file)
{
@ -376,7 +376,7 @@ class egw_include_mgr
/**
* Return all files
*
* @param boolean $clear_files=false if true clear list of files after returning them
* @param boolean $clear_files =false if true clear list of files after returning them
* @return array
*/
public function get_included_files($clear_files=false)
@ -389,7 +389,7 @@ class egw_include_mgr
/**
* Constructor
*
* @param array $files=null optional files to include as for include_files method
* @param array $files =null optional files to include as for include_files method
*/
public function __construct(array $files = null)
{

View File

@ -0,0 +1,135 @@
/**
* eGroupware JavaScript Framework - Non UI classes
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @author Andreas Stoeckel <as@stylite.de>
* @version $Id$
*/
/*----------------------------
Class egw_fw_class_application
----------------------------*/
/**
* application class constructor
*
* @param {type} _parentFw
* @param {type} _appName
* @param {type} _displayName
* @param {type} _icon
* @param {type} _indexUrl
* @param {type} _sideboxWidth
* @param {type} _baseUrl
* @param {type} _internalName
* @returns {egw_fw_class_application}
*/
function egw_fw_class_application(_parentFw, _appName, _displayName, _icon,
_indexUrl, _sideboxWidth, _baseUrl, _internalName)
{
//Copy the application properties
this.appName = _appName;
this.internalName = _internalName;
this.displayName = _displayName;
this.icon = _icon;
this.indexUrl = _indexUrl;
this.sidebox_md5 = '';
this.hasPrerequisites;
this.baseUrl = _baseUrl;
this.website_title = '';
this.app_header = '';
this.sideboxWidth = _sideboxWidth;
//Setup a link to the parent framework class
this.parentFw = _parentFw;
//Preset some variables
this.hasSideboxMenuContent = false;
this.sidemenuEntry = null;
this.tab = null;
this.browser = null;
}
/**
* Returns an menuaction inside the jdots_framework for this application.
* without a "this" context (by directly calling egw_fw_class_application.prototype.getAjaxUrl)
* or passing null to a "call" call "home" will be used as application name and
* the the base url will be omitted (default behaviour for all applications which)
* lie inside the default egw instance.
*
* @param {string} _fun is the function which shall be called on the server.
*/
egw_fw_class_application.prototype.getMenuaction = function(_fun)
{
var baseUrl = '';
var appName = 'home';
if (this)
{
baseUrl = this.getBaseUrl();
appName = this.internalName;
}
// Check whether the baseurl is actually set. If not, then this application
// resides inside the same egw instance as the jdots framework. We'll simply
// return a menu action and not a full featured url here.
if (baseUrl != '')
{
baseUrl = baseUrl + 'json.php?menuaction=';
}
// use template handler to call current framework, eg. pixelegg
return baseUrl + appName + '.jdots_framework.' + _fun + '.template';
};
/**
* Returns the base url for this application. If the application resides inside
* the default egw instance, '' will be returned unless the _force parameter is
* set to true.
*
* @param {boolean} _force Optional parameter. If set, getBaseUrl will return the
* webserverUrl instead of '' if the application resides inside the main
* egw instance.
*/
egw_fw_class_application.prototype.getBaseUrl = function(_force)
{
if (this.baseUrl)
{
return this.baseUrl;
}
else if ((typeof _force != 'undefined') && _force)
{
return egw_topWindow().egw_webserverUrl;
}
else
{
return '';
}
};
function egw_fw_getMenuaction(_fun)
{
return egw_fw_class_application.prototype.getMenuaction.call(null, _fun);
}
/*----------------------------
Class egw_fw_class_callback
----------------------------*/
function egw_fw_class_callback(_context, _proc)
{
this.context = _context;
this.proc = _proc;
}
egw_fw_class_callback.prototype.call = function()
{
return this.proc.apply(this.context, arguments);
};
array_remove = function(array, index)
{
array.splice(index, 1);
};

View File

@ -13,7 +13,7 @@
framework.fw_base;
framework.fw_browser;
framework.fw_ui;
egw_fw_classes;
framework.fw_classes;
egw_inheritance.js;
*/

View File

@ -50,6 +50,13 @@ egw.extend('files', egw.MODULE_WND_LOCAL, function(_app, _wnd)
*/
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
*
@ -65,12 +72,16 @@ egw.extend('files', egw.MODULE_WND_LOCAL, function(_app, _wnd)
for(var n=0; n < _srcs.length; ++n)
{
var file = _srcs[n];
files.push(file);
files.push(file.replace(min_js_regexp, '.js'));
var contains = file.match(bundle2files_regexp);
if (contains && contains.length > 1)
{
files = files.concat(contains[1].split(','));
var bundle = contains[1].split(',');
for(var i; i < bundle.length; ++i)
{
files.push(bundle[i].replace(min_js_regexp, '.js'));
}
}
}
return files;
@ -122,7 +133,7 @@ egw.extend('files', egw.MODULE_WND_LOCAL, function(_app, _wnd)
}
// make urls egw-relative
files = strip_egw_url(files);
// resolve bundles
// resolve bundles and replace .min.js with .js
files = files_from_bundles(files);
return {
@ -189,13 +200,16 @@ egw.extend('files', egw.MODULE_WND_LOCAL, function(_app, _wnd)
/**
* 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);
var file = removeTS(_file).replace(min_js_regexp, '.js');
var not_inc = files.indexOf(file) == -1;
if (not_inc && _add_if_not)

View File

@ -8,12 +8,14 @@
* @description Create jdots framework
*/
"use strict";
/*egw:uses
jquery.jquery;
framework.fw_desktop;
/pixelegg/js/slider.js;
*/
(function(window){
"use strict";
/**
* jdots framework object defenition
* here we can add framework methods and also override fw_desktop methods if it is neccessary

6
pixelegg/js/fw_pixelegg.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -45,6 +45,12 @@ foreach(egw_framework::get_bundles() as $name => $files)
if ($path[0] == '/') $path = substr($path, 1);
});
// phpgwapi/js/jsapi/egw.js loaded via own tag, and we must not load it twice!
if ($name == 'api' && ($key = array_search('phpgwapi/js/jsapi/egw.js', $files)))
{
unset($files[$key]);
}
//var_dump($name, $files);
if (isset($uglify[$name]))
{
@ -53,11 +59,14 @@ foreach(egw_framework::get_bundles() as $name => $files)
}
elseif (isset($uglify[$append = substr($name, 0, -1)]))
{
reset($uglify[$append]['files']);
list($target) = each($uglify[$append]['files']);
$uglify[$append]['files'][$target] = array_merge($uglify[$append]['files'][$target], array_values($files));
}
else
else // create new bundle using last file as target
{
error_log("Bundle $name ignored!\n");
$target = str_replace('.js', '.min.js', end($files));
$uglify[$name]['files'][$target] = array_values($files);
}
}