diff --git a/phpgwapi/inc/class.egw_framework.inc.php b/phpgwapi/inc/class.egw_framework.inc.php
index 4ac9141ef5..2bfcac924b 100644
--- a/phpgwapi/inc/class.egw_framework.inc.php
+++ b/phpgwapi/inc/class.egw_framework.inc.php
@@ -993,7 +993,7 @@ abstract class egw_framework
self::includeCSS('/phpgwapi/js/jquery/chosen/chosen.css');
// eTemplate2 uses jQueryUI, so load it first so et2 can override if needed
- egw_framework::includeCSS("/phpgwapi/js/jquery/jquery-ui/redmond/jquery-ui-1.10.3.custom.css");
+ self::includeCSS("/phpgwapi/js/jquery/jquery-ui/redmond/jquery-ui-1.10.3.custom.css");
// eTemplate2 - load in top so sidebox has styles too
self::includeCSS('/etemplate/templates/default/etemplate2.css');
@@ -1023,7 +1023,6 @@ abstract class egw_framework
$print_css = '/phpgwapi/templates/idots/print.css';
}
-
// search for app specific css file
self::includeCSS($GLOBALS['egw_info']['flags']['currentapp'], 'app');
@@ -1597,18 +1596,96 @@ abstract class egw_framework
*/
static public function get_script_links($return_pathes=false, $clear_files=false)
{
- // RB: disabled minifying (debug=true), 'til I found time to fix it
+ $to_include = self::bundle_js_includes(self::$js_include_mgr->get_included_files($clear_files));
+
+ if ($return_pathes)
+ {
+ return $to_include;
+ }
+ $start = '\n";
+ return "\n".$start.implode($end.$start, $to_include).$end;
+ }
+
+ /**
+ * Devide js-includes in bundles of javascript files to include eg. api or etemplate2, if minifying is enabled
+ *
+ * @param array $js_includes files to include with egw relative url
+ * @return array egw relative urls to include incl. bundels/minify urls, if enabled
+ */
+ public static function bundle_js_includes(array $js_includes)
+ {
+ if ($GLOBALS['egw_info']['server']['debug_minify'] === 'True')
+ {
+ return $js_includes; // nothing to do, just return all single files
+ }
+ // get used bundles and cache them on tree-level for 2h
+ $bundles = egw_cache::getTree(__CLASS__, 'bundles', array(__CLASS__, 'get_bundles'), array(), 7200);
+ $bundles_ts = $bundles['.ts'];
+ unset($bundles['.ts']);
+ $file2bundle = array();
+ foreach($bundles as $name => $files)
+ {
+ $file2bundle += array_combine($files, array_fill(0, count($files), $name));
+ }
+
+ $to_include = $included_bundles = array();
+ foreach($js_includes as $file)
+ {
+ if (!isset($to_include[$file]))
+ {
+ if (($bundle = $file2bundle[$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)
+ {
+ // force new bundle config by deleting cached one and call ourself again
+ egw_cache::unsetTree(__CLASS__, 'bundles');
+ return self::bundle_js_includes($js_includes);
+ }
+ }
+ }
+ else
+ {
+ $query = '';
+ list($path, $query) = explode('?', $file, 2);
+ $mod = filemtime(EGW_SERVER_ROOT.$path);
+
+ $to_include[$file] = $path.'?'.$mod.($query ? '&'.$query : '');
+ }
+ }
+ }
+ /*_debug_array($js_includes);
+ _debug_array(array_values($to_include));
+ die('STOP');*/
+
+ return array_values($to_include);
+ }
+
+ /**
+ * Generate bundle url(s) for given js files
+ *
+ * @param array $js_includes
+ * @param int& $max_modified=null on return maximum modification time of bundle
+ * @return array js-files (can be more then one, if one of given files can not be bundeled)
+ */
+ protected static function bundle_urls(array $js_includes, &$max_modified=null)
+ {
$debug_minify = $GLOBALS['egw_info']['server']['debug_minify'] === 'True';
- $files = '';
$to_include = $to_minify = array();
$max_modified = 0;
- foreach(self::$js_include_mgr->get_included_files($clear_files) as $path)
+ foreach($js_includes as $path)
{
if ($path == '/phpgwapi/js/jsapi/egw.js') continue; // loaded via own tag, and we must not load it twice!
$query = '';
list($path,$query) = explode('?',$path,2);
- if (($mod = filemtime(EGW_SERVER_ROOT.$path)) > $max_modified) $max_modified = $mod;
+ $mod = filemtime(EGW_SERVER_ROOT.$path);
// for now minify does NOT support query parameters, nor php files generating javascript
if ($debug_minify || $query || substr($path, -3) != '.js' || strpos($path,'ckeditor') !== false ||
@@ -1619,6 +1696,7 @@ abstract class egw_framework
}
else
{
+ if ($mod > $max_modified) $max_modified = $mod;
$to_minify[] = substr($path,1);
}
}
@@ -1633,23 +1711,48 @@ abstract class egw_framework
// need to include minified javascript before not minified stuff like jscalendar-setup, as it might depend on it
array_unshift($to_include, $path);
}
- if ($return_pathes)
- {
- return $to_include;
- }
- $start = '\n";
- return "\n".$start.implode($end.$start, $to_include).$end;
+ //error_log(__METHOD__."(".array2string($js_includes).") returning ".array2string($to_include));
+ return $to_include;
+ }
- // using LABjs to load all javascript would require all other script-tags to run in wait() of queue!
- /*
- return "\n".$start.'/phpgwapi/js/labjs/LAB.src.js'.$end."\n".
- '
-';
- */
+ /**
+ * Return typical bundes we use:
+ * - api stuff phpgwapi/js/jsapi/* and it's dependencies incl. jquery
+ * - etemplate2 stuff not including api bundle, but jquery-ui
+ *
+ * @return array bundle-url => array of contained files
+ */
+ public static function get_bundles()
+ {
+ $inc_mgr = new egw_include_mgr();
+ $bundles = array();
+
+ // generate api bundle
+ $inc_mgr->include_js_file('/phpgwapi/js/jquery/jquery.js');
+ $inc_mgr->include_js_file('/phpgwapi/js/jsapi/jsapi.js');
+ $inc_mgr->include_js_file('/phpgwapi/js/egw_json.js');
+ $inc_mgr->include_js_file('/phpgwapi/js/jsapi/egw.js');
+ $bundles['api'] = $inc_mgr->get_included_files();
+
+ // 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']);
+
+ // generate jdots bundle, if installed
+ if (file_exists(EGW_SERVER_ROOT.'/jdots'))
+ {
+ $inc_mgr->include_js_file('/jdots/js/egw_fw.js');
+ $inc_mgr->include_js_file('/jdots/js/egw_fw_ui.js');
+ $inc_mgr->include_js_file('/jdots/js/egw_fw_classes.js');
+ $bundles['jdots'] = array_diff($inc_mgr->get_included_files(), call_user_func_array('array_merge', $bundles));
+ }
+
+ // store timestamp of when bundle-config was created
+ $bundles['.ts'] = time();
+
+ error_log(__METHOD__."() returning ".array2string($bundles));
+ return $bundles;
}
/**
@@ -1725,11 +1828,9 @@ $LAB.setOptions({AlwaysPreserveOrder:true,BasePath:"'.$GLOBALS['egw_info']['serv
// add all js files from egw_framework::validate_file()
$files = self::$js_include_mgr->get_included_files();
+ $files = self::bundle_js_includes($files);
foreach($files as $path)
{
- $query = '';
- list($path,$query) = explode('?',$path,2);
- $path .= '?'. filemtime(EGW_SERVER_ROOT.$path).($query ? '&'.$query : '');
$response->includeScript($GLOBALS['egw_info']['server']['webserver_url'].$path);
}
}
diff --git a/phpgwapi/inc/class.egw_include_mgr.inc.php b/phpgwapi/inc/class.egw_include_mgr.inc.php
index 3027590bc0..17fa5fc615 100644
--- a/phpgwapi/inc/class.egw_include_mgr.inc.php
+++ b/phpgwapi/inc/class.egw_include_mgr.inc.php
@@ -284,6 +284,8 @@ class egw_include_mgr
is_readable(EGW_SERVER_ROOT.($path="/$app/js/$package/$file.js")) ||
$app != 'phpgwapi' && is_readable(EGW_SERVER_ROOT.($path="/phpgwapi/js/$package/$file.js")))
{
+ // normalise /./ to /
+ $path = str_replace('/./', '/', $path);
// Handle the special case, that the file is an url - in this case
// we will do no further processing but just include the file