use Grunt&cssmin to minify CSS files, moved diverse conditional includes into etemplate2.css, as we anyway include all widgets from api

This commit is contained in:
Ralf Becker 2016-04-30 09:29:54 +00:00
parent 40fbf3e2c9
commit 9aac672e8f
11 changed files with 247 additions and 54 deletions

View File

@ -15,12 +15,15 @@
* npm install grunt --save-dev
* npm install grunt-contrib-uglify --save-dev
* npm install grunt-newer --save-dev
* npm install grunt-contrib-cssmin --save-dev
*
* Building happens by running in your EGroupware directory:
*
* grunt # runs uglify for all targets with changed files
* grunt # runs uglify and cssmin for all targets with changed files
* or
* grunt [newer:]uglify:<target> # targets: api, et2, pixelegg, mobile, mail, calendar, ...
* or
* grunt [newer:]cssmin:<target> # targets: pixelegg, jdots
*
* app.js files can be added like mail target or, if you want automatic dependencies,
* you need to add them in egw_framework::$bundle2minurl and egw_framework::get_bundles().
@ -258,14 +261,136 @@ module.exports = function (grunt) {
]
}
}
},
cssmin: {
options: {
shorthandCompacting: false,
sourceMap: true,
relativeTo: "pixelegg\/css\/"
},
pixelegg: {
files: {
"pixelegg\/css\/pixelegg.min.css": [
"api\/js\/jquery\/chosen\/chosen.css",
"api\/js\/jquery\/jquery-ui\/redmond\/jquery-ui.css",
"api\/js\/jquery\/magicsuggest\/magicsuggest.css",
"api\/js\/jquery\/jpicker\/css\/jPicker-1.1.6.min.css",
"api\/js\/jquery\/jquery-ui-timepicker-addon.css",
"api\/js\/jquery\/blueimp\/css\/blueimp-gallery.min.css",
"api\/js\/dhtmlxtree\/codebase\/dhtmlXTree.css",
"api\/js\/egw_action\/test\/skins\/dhtmlxmenu_egw.css",
"api\/js\/etemplate\/lib\/jsdifflib\/diffview.css",
"api\/templates\/default\/etemplate2.css",
"pixelegg\/css\/pixelegg.css",
"phpgwapi\/templates\/idots\/print.css",
"jdots\/print.css",
"pixelegg\/print.css"
],
"pixelegg\/css\/mobile.min.css": [
"api\/js\/jquery\/chosen\/chosen.css",
"api\/js\/jquery\/jquery-ui\/redmond\/jquery-ui.css",
"api\/js\/jquery\/magicsuggest\/magicsuggest.css",
"api\/js\/jquery\/jpicker\/css\/jPicker-1.1.6.min.css",
"api\/js\/jquery\/jquery-ui-timepicker-addon.css",
"api\/js\/jquery\/blueimp\/css\/blueimp-gallery.min.css",
"api\/js\/dhtmlxtree\/codebase\/dhtmlXTree.css",
"api\/js\/egw_action\/test\/skins\/dhtmlxmenu_egw.css",
"api\/js\/etemplate\/lib\/jsdifflib\/diffview.css",
"api\/templates\/default\/etemplate2.css",
"pixelegg\/css\/mobile.css",
"phpgwapi\/templates\/idots\/print.css",
"jdots\/print.css",
"pixelegg\/print.css"
],
"pixelegg\/mobile\/fw_mobile.min.css": [
"api\/js\/jquery\/chosen\/chosen.css",
"api\/js\/jquery\/jquery-ui\/redmond\/jquery-ui.css",
"api\/js\/jquery\/magicsuggest\/magicsuggest.css",
"api\/js\/jquery\/jpicker\/css\/jPicker-1.1.6.min.css",
"api\/js\/jquery\/jquery-ui-timepicker-addon.css",
"api\/js\/jquery\/blueimp\/css\/blueimp-gallery.min.css",
"api\/js\/dhtmlxtree\/codebase\/dhtmlXTree.css",
"api\/js\/egw_action\/test\/skins\/dhtmlxmenu_egw.css",
"api\/js\/etemplate\/lib\/jsdifflib\/diffview.css",
"api\/templates\/default\/etemplate2.css",
"pixelegg\/mobile\/fw_mobile.css",
"phpgwapi\/templates\/idots\/print.css",
"jdots\/print.css",
"pixelegg\/print.css"
]
}
},
jdots: {
files: {
"jdots\/css\/high-contrast.min.css": [
"api\/js\/jquery\/chosen\/chosen.css",
"api\/js\/jquery\/jquery-ui\/redmond\/jquery-ui.css",
"api\/js\/jquery\/magicsuggest\/magicsuggest.css",
"api\/js\/jquery\/jpicker\/css\/jPicker-1.1.6.min.css",
"api\/js\/jquery\/jquery-ui-timepicker-addon.css",
"api\/js\/jquery\/blueimp\/css\/blueimp-gallery.min.css",
"api\/js\/dhtmlxtree\/codebase\/dhtmlXTree.css",
"api\/js\/egw_action\/test\/skins\/dhtmlxmenu_egw.css",
"api\/js\/etemplate\/lib\/jsdifflib\/diffview.css",
"api\/templates\/default\/etemplate2.css",
"phpgwapi\/templates\/default\/def_tutorials.css",
"phpgwapi\/templates\/idots\/css\/traditional.css",
"jdots\/egw_fw.css",
"jdots\/css\/jdots.css",
"jdots\/css\/high-contrast.css",
"phpgwapi\/templates\/idots\/print.css",
"jdots\/print.css"
],
"jdots\/css\/jdots.min.css": [
"api\/js\/jquery\/chosen\/chosen.css",
"api\/js\/jquery\/jquery-ui\/redmond\/jquery-ui.css",
"api\/js\/jquery\/magicsuggest\/magicsuggest.css",
"api\/js\/jquery\/jpicker\/css\/jPicker-1.1.6.min.css",
"api\/js\/jquery\/jquery-ui-timepicker-addon.css",
"api\/js\/jquery\/blueimp\/css\/blueimp-gallery.min.css",
"api\/js\/dhtmlxtree\/codebase\/dhtmlXTree.css",
"api\/js\/egw_action\/test\/skins\/dhtmlxmenu_egw.css",
"api\/js\/etemplate\/lib\/jsdifflib\/diffview.css",
"api\/templates\/default\/etemplate2.css",
"phpgwapi\/templates\/default\/def_tutorials.css",
"phpgwapi\/templates\/idots\/css\/traditional.css",
"jdots\/egw_fw.css",
"jdots\/css\/jdots.css",
"phpgwapi\/templates\/idots\/print.css",
"jdots\/print.css"
],
"jdots\/css\/orange-green.min.css": [
"api\/js\/jquery\/chosen\/chosen.css",
"api\/js\/jquery\/jquery-ui\/redmond\/jquery-ui.css",
"api\/js\/jquery\/magicsuggest\/magicsuggest.css",
"api\/js\/jquery\/jpicker\/css\/jPicker-1.1.6.min.css",
"api\/js\/jquery\/jquery-ui-timepicker-addon.css",
"api\/js\/jquery\/blueimp\/css\/blueimp-gallery.min.css",
"api\/js\/dhtmlxtree\/codebase\/dhtmlXTree.css",
"api\/js\/egw_action\/test\/skins\/dhtmlxmenu_egw.css",
"api\/js\/etemplate\/lib\/jsdifflib\/diffview.css",
"api\/templates\/default\/etemplate2.css",
"phpgwapi\/templates\/default\/def_tutorials.css",
"phpgwapi\/templates\/idots\/css\/traditional.css",
"jdots\/egw_fw.css",
"jdots\/css\/jdots.css",
"jdots\/css\/orange-green.css",
"phpgwapi\/templates\/idots\/print.css",
"jdots\/print.css"
]
}
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
// Load plugin for css minificaton
grunt.loadNpmTasks('grunt-contrib-cssmin');
// Load the plugin that runs tasks only on modified files
grunt.loadNpmTasks('grunt-newer');
// Default task(s).
grunt.registerTask('default', ['newer:uglify']);
grunt.registerTask('default', ['newer:uglify', 'newer:cssmin']);
};

View File

@ -17,7 +17,12 @@
*/
// Need CSS, or it doesn't really work
if(typeof egw == 'function') egw(window).includeCSS(egw.webserverUrl + "/api/js/egw_action/test/skins/dhtmlxmenu_egw.css");
//if(typeof egw == 'function') egw(window).includeCSS(egw.webserverUrl + "/api/js/egw_action/test/skins/dhtmlxmenu_egw.css");
/**
*
* @param {type} _structure
*/
function egwMenuImpl(_structure)
{
//Create a new dhtmlxmenu object
@ -160,7 +165,7 @@ egwMenuImpl.prototype._translateStructure = function(_structure, _parentId, _idC
}
return counter;
}
};
egwMenuImpl.prototype.showAt = function(_x, _y, _onHide)
@ -182,11 +187,9 @@ egwMenuImpl.prototype.showAt = function(_x, _y, _onHide)
self.dhtmlxmenu.showContextMenu(_x, _y);
// TODO: Get keybard focus
}, 0);
}
};
egwMenuImpl.prototype.hide = function()
{
this.dhtmlxmenu.hide();
}
};

View File

@ -56,7 +56,8 @@ var et2_color = (function(){ "use strict"; return et2_inputWidget.extend(
init: function() {
this._super.apply(this, arguments);
this.egw().includeCSS("phpgwapi/js/jquery/jpicker/css/jPicker-1.1.6.min.css");
// included via etemplate2.css
//this.egw().includeCSS("phpgwapi/js/jquery/jpicker/css/jPicker-1.1.6.min.css");
this.input = this.$node = jQuery(document.createElement("span"));
// Translations

View File

@ -40,7 +40,8 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe
this._super.apply(this, arguments);
this.mini = true;
this.egw().includeCSS('etemplate/js/lib/jsdifflib/diffview.css');
// included via etemplate2.css
//this.egw().includeCSS('etemplate/js/lib/jsdifflib/diffview.css');
this.div = document.createElement("div");
jQuery(this.div).addClass('diff');
},
@ -106,6 +107,8 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe
/**
* Make the diff into a mini-diff
*
* @param {DOMNode|String} view
*/
minify: function(view) {
view = jQuery(view)
@ -118,6 +121,11 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe
.prevAll().hide();
},
/**
* Expand mini-diff
*
* @param {DOMNode|String} view
*/
un_minify: function(view) {
jQuery(view).removeClass('mini').show();
jQuery('th',view).show();
@ -133,6 +141,8 @@ var et2_diff = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDe
* Build a list of attributes which can be set when working in the
* "detached" mode in the _attrs array which is provided
* by the calling code.
*
* @param {object} _attrs
*/
getDetachedAttributes: function(_attrs) {
_attrs.push("value", "label");

View File

@ -358,7 +358,7 @@ var et2_taglist = (function(){ "use strict"; return et2_selectbox.extend([et2_IR
$j('.ms-trigger',this.div).on('click', function(e) {
e.stopPropagation();
})
});
// Unbind change handler of widget's ancestor to stop it from bubbling
// taglist has its own onchange
$j(this.getDOMNode()).unbind('change.et2_inputWidget');
@ -458,7 +458,7 @@ var et2_taglist = (function(){ "use strict"; return et2_selectbox.extend([et2_IR
this._query_server = false;
// Turn on local filtering, or trust server to do it
cfg.mode = typeof return_value === 'string' ? 'remote' : 'local'
cfg.mode = typeof return_value === 'string' ? 'remote' : 'local';
return return_value;
},
@ -1251,5 +1251,5 @@ var et2_taglist_ro = (function(){ "use strict"; return et2_selectbox_ro.extend(
et2_register_widget(et2_taglist_ro, ["taglist_ro","taglist_email_ro", "taglist_account_ro" ]);
// Require css
// TODO: merge into etemplate2.css with all other widgets when done
if(typeof egw == 'function') egw(window).includeCSS(egw.webserverUrl + "/api/js/jquery/magicsuggest/magicsuggest.css");
// included via etemplate2.css
//if(typeof egw == 'function') egw(window).includeCSS(egw.webserverUrl + "/api/js/jquery/magicsuggest/magicsuggest.css");

View File

@ -211,15 +211,6 @@ class Etemplate extends Etemplate\Widget\Template
}
else // first call
{
// missing dependency, thought egw:uses jquery.jquery.tools does NOT work, maybe we should rename it to jquery-tools
// Framework::includeJS('jquery','jquery.tools.min');
// Include the jQuery-UI CSS - many more complex widgets use it
$theme = 'redmond';
Framework::includeCSS("/api/js/jquery/jquery-ui/$theme/jquery-ui-1.10.3.custom.css");
// Load our CSS after jQuery-UI, so we can override it
Framework::includeCSS('/api/templates/default/etemplate2.css');
// check if application of template has a app.js file --> load it
list($app) = explode('.',$this->name);
if (file_exists(EGW_SERVER_ROOT.'/'.$app.'/js/app.js'))

View File

@ -813,6 +813,26 @@ abstract class Framework extends Framework\Extra
}
if (self::$load_default_css)
{
// For mobile user-agent we prefer mobile theme over selected one with a final fallback to theme named as template
$themes_to_check = array();
if (Header\UserAgent::mobile() || $GLOBALS['egw_info']['user']['preferences']['common']['theme'] == 'fw_mobile')
{
$themes_to_check[] = $this->template_dir.'/mobile/fw_mobile.css';
}
$themes_to_check[] = $this->template_dir.'/css/'.$GLOBALS['egw_info']['user']['preferences']['common']['theme'].'.css';
$themes_to_check[] = $this->template_dir.'/css/'.$this->template.'.css';
foreach($themes_to_check as $theme_css)
{
if (file_exists(EGW_SERVER_ROOT.$theme_css)) break;
}
$debug_minify = $GLOBALS['egw_info']['server']['debug_minify'] === 'True';
if (!$debug_minify && file_exists(EGW_SERVER_ROOT.($theme_min_css = str_replace('.css', '.min.css', $theme_css))))
{
error_log(__METHOD__."() Framework\CssIncludes::get()=".array2string(Framework\CssIncludes::get()));
self::includeCSS($theme_min_css);
}
else
{
// Load these first
// Cascade should go:
@ -829,21 +849,8 @@ abstract class Framework extends Framework\Extra
// Category styles
Categories::css(Categories::GLOBAL_APPNAME);
// For mobile user-agent we prefer mobile theme over selected one with a final fallback to theme named as template
$themes_to_check = array();
if (Header\UserAgent::mobile()) $themes_to_check[] = $this->template_dir.'/mobile/fw_mobile.css';
$themes_to_check[] = $this->template_dir.'/css/'.$GLOBALS['egw_info']['user']['preferences']['common']['theme'].'.css';
$themes_to_check[] = $this->template_dir.'/css/'.$this->template.'.css';
foreach($themes_to_check as $theme_css)
{
if (file_exists(EGW_SERVER_ROOT.$theme_css)) break;
}
self::includeCSS($theme_css);
// search for app specific css file, so it can customize the theme
self::includeCSS($GLOBALS['egw_info']['flags']['currentapp'], 'app-'.$GLOBALS['egw_info']['user']['preferences']['common']['theme']) ||
self::includeCSS($GLOBALS['egw_info']['flags']['currentapp'], 'app');
// sending print css last, so it can overwrite anything
$print_css = $this->template_dir.'/print.css';
if(!file_exists(EGW_SERVER_ROOT.$print_css))
@ -852,6 +859,10 @@ abstract class Framework extends Framework\Extra
}
self::includeCSS($print_css);
}
// search for app specific css file, so it can customize the theme
self::includeCSS($GLOBALS['egw_info']['flags']['currentapp'], 'app-'.$GLOBALS['egw_info']['user']['preferences']['common']['theme']) ||
self::includeCSS($GLOBALS['egw_info']['flags']['currentapp'], 'app');
}
return array(
'app_css' => $app_css,
'css_file' => Framework\CssIncludes::tags(),
@ -950,7 +961,7 @@ abstract class Framework extends Framework\Extra
function list_themes()
{
$list = array();
if (($dh = @opendir(EGW_SERVER_ROOT.$this->template_dir . SEP . 'css')))
if (($dh = @opendir(EGW_SERVER_ROOT.$this->template_dir.'/css')))
{
while (($file = readdir($dh)))
{
@ -1293,6 +1304,7 @@ abstract class Framework extends Framework\Extra
{
self::$load_default_css = false;
}
error_log(__METHOD__."('$app', '$name', append=$append, no_default=$no_default_css) ".function_backtrace());
return Framework\CssIncludes::add($app, $name, $append, $no_default_css);
}
@ -1310,6 +1322,7 @@ abstract class Framework extends Framework\Extra
// add all css files from Framework::includeCSS()
$query = null;
error_log(__METHOD__."() Framework\CssIncludes::get()=".array2string(Framework\CssIncludes::get()));
foreach(Framework\CssIncludes::get() as $path)
{
unset($query);

View File

@ -79,10 +79,22 @@ class CssIncludes
*
* @return string
*/
public static function get()
public static function get($resolve=false)
{
if (!$resolve)
{
return self::$files;
}
$files = array();
foreach(self::$files as $path)
{
foreach(self::resolve_css_includes($path) as $path)
{
$files[] = $path;
}
}
return $files;
}
/**
* Return link tags for all included css files incl. minifying
@ -93,7 +105,7 @@ class CssIncludes
{
// add all css files from self::includeCSS
$max_modified = 0;
$debug_minify = $GLOBALS['egw_info']['server']['debug_minify'] === 'True';
$debug_minify = true; //no more dynamic minifying: $GLOBALS['egw_info']['server']['debug_minify'] === 'True';
$base_path = $GLOBALS['egw_info']['server']['webserver_url'];
if ($base_path[0] != '/') $base_path = parse_url($base_path, PHP_URL_PATH);
$css_files = '';
@ -109,12 +121,14 @@ class CssIncludes
{
$css_files .= '<link href="'.$GLOBALS['egw_info']['server']['webserver_url'].$path.($query ? '&' : '?').$mod.'" type="text/css" rel="StyleSheet" />'."\n";
}
/* no more dynamic minifying
else
{
$css_file .= ($css_file ? ',' : '').substr($path, 1);
}*/
}
}
}
/* no more dynamic minifying
if (!$debug_minify)
{
$css = $GLOBALS['egw_info']['server']['webserver_url'].'/phpgwapi/inc/min/?';
@ -123,7 +137,7 @@ class CssIncludes
($GLOBALS['egw_info']['server']['debug_minify'] === 'debug' ? '&debug' : '').
'&'.$max_modified;
$css_files = '<link href="'.$css.'" type="text/css" rel="StyleSheet" />'."\n".$css_files;
}
}*/
return $css_files;
}

View File

@ -13,6 +13,7 @@
/*@import url("../../js/jquery/blueimp/css/blueimp-gallery.min.css");*/
/*@import url("../../js/dhtmlxtree/codebase/dhtmlXTree.css");*/
/*@import url("../../js/egw_action/test/skins/dhtmlxmenu_egw.css");*/
/*@import url("../../js/etemplate/lib/jsdifflib/diffview.css");*/
/**

View File

@ -7,6 +7,7 @@
"repository": {},
"devDependencies": {
"grunt": "^0.4.5",
"grunt-contrib-cssmin": "^1.0.1",
"grunt-contrib-uglify": "^0.11.1",
"grunt-newer": "^1.1.2"
}

View File

@ -10,6 +10,7 @@
* @version $Id$
*/
use EGroupware\Api\Framework;
use EGroupware\Api\Framework\Bundle;
if (php_sapi_name() !== 'cli') die("This is a commandline ONLY tool!\n");
@ -77,6 +78,39 @@ foreach(Bundle::all() as $name => $files)
}
}
// add css for all templates and themes
$cssmin =& $config['cssmin'];
$GLOBALS['egw_info']['flags']['currentapp'] = '*grunt*'; // to no find any app.css files
$GLOBALS['egw_info']['server']['debug_minify'] = 'True'; // otherwise we would only get minified file
foreach(array('pixelegg','jdots')/*array_keys(Framework::list_templates())*/ as $template)
{
$GLOBALS['egw_info']['server']['template_set'] = $template;
$tpl = Framework::factory();
$themes = $tpl->list_themes();
if ($template == 'pixelegg') $themes[] = 'fw_mobile'; // this is for mobile devices
foreach($themes as $theme)
{
// skip not working cssmin of pixelegg/traditional: Broken @import declaration of "../../etemplate/templates/default/etemplate2.css"
if ($template == 'pixelegg' && $theme == 'traditional') continue;
$GLOBALS['egw_info']['user']['preferences']['common']['theme'] = $theme;
// empty include list by not-existing file plus last true
Framework\CssIncludes::add('*grunt*', null, true, true);
$tpl->_get_css();
$dest = substr($tpl->template_dir, 1).($theme == 'fw_mobile' ? '/mobile/' : '/css/').$theme.'.min.css';
$cssmin[$template]['files'][$dest] =
// remove leading slash from src path
array_map(function($path)
{
return substr($path, 1);
},
// filter out all dynamic css, like categories.php
array_values(array_filter(Framework\CssIncludes::get(true), function($path)
{
return strpos($path, '.php?') === false;
})));
}
}
$new_json = str_replace("\n", "\n\t",
preg_replace_callback('/^( *)/m', function($matches)
{