new template features: combine_script, footer_script and get_combined_scripts
migrated public templates only; need more code doc git-svn-id: http://piwigo.org/svn/trunk@7975 68402e56-0260-453c-a942-63ccdbb3a9ee
This commit is contained in:
parent
208a5acddc
commit
af1cbac19d
8 changed files with 367 additions and 25 deletions
|
|
@ -43,10 +43,14 @@ class Template {
|
|||
// used by html_head smarty block to add content before </head>
|
||||
var $html_head_elements = array();
|
||||
|
||||
var $scriptLoader;
|
||||
var $html_footer_raw_script = array();
|
||||
|
||||
function Template($root = ".", $theme= "", $path = "template")
|
||||
{
|
||||
global $conf, $lang_info;
|
||||
|
||||
$this->scriptLoader = new ScriptLoader;
|
||||
$this->smarty = new Smarty;
|
||||
$this->smarty->debugging = $conf['debug_template'];
|
||||
$this->smarty->compile_check = $conf['template_compile_check'];
|
||||
|
|
@ -82,6 +86,9 @@ class Template {
|
|||
$this->smarty->register_modifier( 'explode', array('Template', 'mod_explode') );
|
||||
$this->smarty->register_modifier( 'get_extent', array(&$this, 'get_extent') );
|
||||
$this->smarty->register_block('html_head', array(&$this, 'block_html_head') );
|
||||
$this->smarty->register_function('combine_script', array(&$this, 'func_combine_script') );
|
||||
$this->smarty->register_function('get_combined_scripts', array(&$this, 'func_get_combined_scripts') );
|
||||
$this->smarty->register_block('footer_script', array(&$this, 'block_footer_script') );
|
||||
$this->smarty->register_function('known_script', array(&$this, 'func_known_script') );
|
||||
$this->smarty->register_prefilter( array('Template', 'prefilter_white_space') );
|
||||
if ( $conf['compiled_template_cache_language'] )
|
||||
|
|
@ -376,6 +383,26 @@ class Template {
|
|||
|
||||
function flush()
|
||||
{
|
||||
if (!$this->scriptLoader->did_head())
|
||||
{
|
||||
$search = "\n</head>";
|
||||
$pos = strpos( $this->output, $search );
|
||||
if ($pos !== false)
|
||||
{
|
||||
$scripts = $this->scriptLoader->get_head_scripts();
|
||||
$content = array();
|
||||
foreach ($scripts as $id => $script)
|
||||
{
|
||||
$content[]=
|
||||
'<script type="text/javascript" src="'
|
||||
. Template::make_script_src($script)
|
||||
.'"></script>';
|
||||
}
|
||||
|
||||
$this->output = substr_replace( $this->output, "\n".implode( "\n", $content ), $pos, 0 );
|
||||
} //else maybe error or warning ?
|
||||
}
|
||||
|
||||
if ( count($this->html_head_elements) )
|
||||
{
|
||||
$search = "\n</head>";
|
||||
|
|
@ -473,6 +500,119 @@ class Template {
|
|||
}
|
||||
}
|
||||
|
||||
function func_combine_script($params, &$smarty)
|
||||
{
|
||||
if (!isset($params['id']))
|
||||
{
|
||||
$smarty->trigger_error("combine_script: missing 'id' parameter", E_USER_ERROR);
|
||||
}
|
||||
$load = 0;
|
||||
if (isset($params['load']))
|
||||
{
|
||||
switch ($params['load'])
|
||||
{
|
||||
case 'header': break;
|
||||
case 'footer': $load=1; break;
|
||||
case 'async': $load=2; break;
|
||||
default: $smarty->trigger_error("combine_script: invalid 'load' parameter", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
$this->scriptLoader->add( $params['id'], $load,
|
||||
empty($params['require']) ? array() : explode( ',', $params['require'] ),
|
||||
@$params['path'],
|
||||
isset($params['version']) ? $params['version'] : 0 );
|
||||
}
|
||||
|
||||
|
||||
function func_get_combined_scripts($params, &$smarty)
|
||||
{
|
||||
if (!isset($params['load']))
|
||||
{
|
||||
$smarty->trigger_error("get_combined_scripts: missing 'load' parameter", E_USER_ERROR);
|
||||
}
|
||||
$load = $params['load']=='header' ? 0 : 1;
|
||||
$content = array();
|
||||
|
||||
if ($load==0)
|
||||
{
|
||||
if ($this->scriptLoader->did_head())
|
||||
fatal_error('get_combined_scripts several times header');
|
||||
|
||||
$scripts = $this->scriptLoader->get_head_scripts();
|
||||
foreach ($scripts as $id => $script)
|
||||
{
|
||||
$content[]=
|
||||
'<script type="text/javascript" src="'
|
||||
. Template::make_script_src($script)
|
||||
.'"></script>';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!$this->scriptLoader->did_head())
|
||||
fatal_error('get_combined_scripts didn\'t call header');
|
||||
$scripts = $this->scriptLoader->get_footer_scripts();
|
||||
foreach ($scripts[0] as $id => $script)
|
||||
{
|
||||
$content[]=
|
||||
'<script type="text/javascript" src="'
|
||||
. Template::make_script_src($script)
|
||||
.'"></script>';
|
||||
}
|
||||
if (count($this->html_footer_raw_script))
|
||||
{
|
||||
$content[]= '<script type="text/javascript">';
|
||||
$content = array_merge($content, $this->html_footer_raw_script);
|
||||
$content[]= '</script>';
|
||||
}
|
||||
|
||||
if (count($scripts[1]))
|
||||
{
|
||||
$content[]= '<script type="text/javascript">';
|
||||
$content[]= '(function() {
|
||||
var after = document.getElementsByTagName(\'script\')[document.getElementsByTagName(\'script\').length-1];
|
||||
var s;';
|
||||
foreach ($scripts[1] as $id => $script)
|
||||
{
|
||||
$content[]=
|
||||
's=document.createElement(\'script\'); s.type = \'text/javascript\'; s.async = true; s.src = \''
|
||||
. Template::make_script_src($script)
|
||||
.'\';';
|
||||
$content[]= 'after = after.parentNode.insertBefore(s, after);';
|
||||
}
|
||||
$content[]= '})();';
|
||||
$content[]= '</script>';
|
||||
}
|
||||
}
|
||||
return implode("\n", $content);
|
||||
}
|
||||
|
||||
|
||||
private static function make_script_src( $script )
|
||||
{
|
||||
$ret = '';
|
||||
if ( url_is_remote($script->path) )
|
||||
$ret = $script->path;
|
||||
else
|
||||
{
|
||||
$ret = get_root_url().$script->path;
|
||||
if ($script->version!==false)
|
||||
{
|
||||
$ret.= '?v'. ($script->version ? $script->version : PHPWG_VERSION);
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function block_footer_script($params, $content, &$smarty, &$repeat)
|
||||
{
|
||||
$content = trim($content);
|
||||
if ( !empty($content) )
|
||||
{ // second call
|
||||
$this->html_footer_raw_script[] = $content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function allows to declare a Smarty prefilter from a plugin, thus allowing
|
||||
* it to modify template source before compilation and without changing core files
|
||||
|
|
@ -644,4 +784,199 @@ class PwgTemplateAdapter
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
final class Script
|
||||
{
|
||||
public $load_mode;
|
||||
public $precedents = array();
|
||||
public $path;
|
||||
public $version;
|
||||
public $extra = array();
|
||||
|
||||
function Script($load_mode, $precedents, $path, $version)
|
||||
{
|
||||
$this->load_mode = $load_mode;
|
||||
$this->precedents = $precedents;
|
||||
$this->path = $path;
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
function set_path($path)
|
||||
{
|
||||
if (!empty($path))
|
||||
$this->path = $path;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Manage a list of required scripts for a page, by optimizing their loading location (head, bottom, async)
|
||||
and later on by combining them in a unique file respecting at the same time dependencies.*/
|
||||
class ScriptLoader
|
||||
{
|
||||
private $registered_scripts;
|
||||
private $did_head;
|
||||
private static $known_paths = array(
|
||||
'core.scripts' => 'themes/default/js/scripts.js',
|
||||
'jquery' => 'themes/default/js/jquery.min.js',
|
||||
'jquery.ui' => 'themes/default/js/ui/packed/ui.core.packed.js'
|
||||
);
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->clear();
|
||||
}
|
||||
|
||||
function clear()
|
||||
{
|
||||
$this->registered_scripts = array();
|
||||
$this->did_head = false;
|
||||
}
|
||||
|
||||
function add($id, $load_mode, $require, $path, $version=0)
|
||||
{
|
||||
if ($this->did_head && $load_mode==0 )
|
||||
{
|
||||
trigger_error("Attempt to add a new script $id but the head has been written", E_USER_WARNING);
|
||||
}
|
||||
if (! isset( $this->registered_scripts[$id] ) )
|
||||
{
|
||||
$script = new Script($load_mode, $require, $path, $version);
|
||||
self::fill_well_known($id, $script);
|
||||
$this->registered_scripts[$id] = $script;
|
||||
}
|
||||
else
|
||||
{
|
||||
$script = & $this->registered_scripts[$id];
|
||||
if (count($require))
|
||||
{
|
||||
$script->precedents = array_unique( array_merge($script->precedents, $require) );
|
||||
}
|
||||
$script->set_path($path);
|
||||
if ($version && version_compare($script->version, $version)<0 )
|
||||
$script->version = $version;
|
||||
if ($load_mode < $script->load_mode)
|
||||
$script->load_mode = $load_mode;
|
||||
}
|
||||
}
|
||||
|
||||
function did_head()
|
||||
{
|
||||
return $this->did_head;
|
||||
}
|
||||
|
||||
private static function fill_well_known($id, $script)
|
||||
{
|
||||
if ( empty($script->path) && isset(self::$known_paths[$id]))
|
||||
{
|
||||
$script->path = self::$known_paths[$id];
|
||||
}
|
||||
if ( strncmp($id, 'jquery.', 7)==0 )
|
||||
{
|
||||
if ( !in_array('jquery', $script->precedents ) )
|
||||
$script->precedents[] = 'jquery';
|
||||
if ( strncmp($id, 'jquery.ui.', 10)==0 && !in_array('jquery.ui', $script->precedents ) )
|
||||
$script->precedents[] = 'jquery.ui';
|
||||
}
|
||||
}
|
||||
|
||||
function get_head_scripts()
|
||||
{
|
||||
do
|
||||
{
|
||||
$changed = false;
|
||||
foreach( $this->registered_scripts as $id => $script)
|
||||
{
|
||||
$load = $script->load_mode;
|
||||
if ($load==0)
|
||||
continue;
|
||||
if ($load==2)
|
||||
$load=1; // we are async -> a predecessor cannot be async because the script execution order is not guaranteed
|
||||
foreach( $script->precedents as $precedent)
|
||||
{
|
||||
if ( !isset($this->registered_scripts[$precedent] ) )
|
||||
{
|
||||
trigger_error("Script $id requires undefined script $precedent", E_USER_WARNING);
|
||||
continue;
|
||||
}
|
||||
if ( $this->registered_scripts[$precedent]->load_mode > $load )
|
||||
{
|
||||
$this->registered_scripts[$precedent]->load_mode = $load;
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while ($changed);
|
||||
|
||||
foreach( array_keys($this->registered_scripts) as $id )
|
||||
{
|
||||
$this->compute_script_topological_order($id);
|
||||
}
|
||||
|
||||
uasort($this->registered_scripts, array('ScriptLoader', 'cmp_by_mode_and_order'));
|
||||
|
||||
$result = array();
|
||||
foreach( $this->registered_scripts as $id => $script)
|
||||
{
|
||||
if ($script->load_mode > 0)
|
||||
break;
|
||||
if ( !empty($script->path) )
|
||||
$result[$id] = $script;
|
||||
else
|
||||
trigger_error("Script $id has an undefined path", E_USER_WARNING);
|
||||
}
|
||||
$this->did_head = true;
|
||||
return $result;
|
||||
}
|
||||
|
||||
function get_footer_scripts()
|
||||
{
|
||||
if (!$this->did_head)
|
||||
{
|
||||
trigger_error("Attempt to write footer scripts without header scripts", E_USER_WARNING);
|
||||
}
|
||||
$result = array( array(), array() );
|
||||
foreach( $this->registered_scripts as $id => $script)
|
||||
{
|
||||
if ($script->load_mode > 0)
|
||||
{
|
||||
if ( !empty( $script->path ) )
|
||||
{
|
||||
$result[$script->load_mode-1][$id] = $script;
|
||||
}
|
||||
else
|
||||
trigger_error("Script $id has an undefined path", E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function compute_script_topological_order($script_id)
|
||||
{
|
||||
if (!isset($this->registered_scripts[$script_id]))
|
||||
{
|
||||
trigger_error("Undefined script $script_id is required by someone", E_USER_WARNING);
|
||||
return 0;
|
||||
}
|
||||
$script = & $this->registered_scripts[$script_id];
|
||||
if (isset($script->extra['order']))
|
||||
return $script->extra['order'];
|
||||
if (count($script->precedents) == 0)
|
||||
return ($script->extra['order'] = 0);
|
||||
$max = 0;
|
||||
foreach( $script->precedents as $precedent)
|
||||
$max = max($max, $this->compute_script_topological_order($precedent) );
|
||||
$max++;
|
||||
return ($script->extra['order'] = $max);
|
||||
}
|
||||
|
||||
private static function cmp_by_mode_and_order($s1, $s2)
|
||||
{
|
||||
$ret = $s1->load_mode - $s2->load_mode;
|
||||
if (!$ret)
|
||||
$ret = $s1->extra['order'] - $s2->extra['order'];
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
<a href="mailto:{$CONTACT_MAIL}?subject={'A comment on your site'|@translate|@escape:url}">{'Webmaster'|@translate}</a>
|
||||
{/if}
|
||||
|
||||
{get_combined_scripts load='footer'}
|
||||
|
||||
{if isset($footer_elements)}
|
||||
{foreach from=$footer_elements item=v}
|
||||
|
|
|
|||
|
|
@ -40,12 +40,15 @@
|
|||
{if isset($U_PREFETCH) }<link rel="prefetch" href="{$U_PREFETCH}">{/if}
|
||||
|
||||
{if not empty($page_refresh) }<meta http-equiv="refresh" content="{$page_refresh.TIME};url={$page_refresh.U_REFRESH}">{/if}
|
||||
|
||||
{*
|
||||
<script type="text/javascript" src="{$ROOT_URL}themes/default/js/scripts.js"></script>
|
||||
*}
|
||||
<!--[if lt IE 7]>
|
||||
<script type="text/javascript" src="{$ROOT_URL}themes/default/js/pngfix.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
{get_combined_scripts load='header'}
|
||||
|
||||
{if not empty($head_elements)}
|
||||
{foreach from=$head_elements item=elt}{$elt}
|
||||
{/foreach}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
|
||||
{known_script id="jquery" src=$ROOT_URL|@cat:"themes/default/js/jquery.packed.js"}
|
||||
{known_script id="jquery.ui" src=$ROOT_URL|@cat:"themes/default/js/ui/packed/ui.core.packed.js"}
|
||||
{known_script id="jquery.ui.datepicker" src=$ROOT_URL|@cat:"themes/default/js/ui/packed/ui.datepicker.packed.js"}
|
||||
{known_script id="datepicker.js" src=$ROOT_URL|@cat:"themes/default/js/datepicker.js"}
|
||||
{combine_script id='jquery' load='footer' path='themes/default/js/jquery.packed.js'}
|
||||
{combine_script id='jquery.ui' load='footer' require='jquery' path='themes/default/js/ui/packed/ui.core.packed.js'}
|
||||
{combine_script id='jquery.ui.datepicker' load='footer' require='jquery.ui' path='themes/default/js/ui/packed/ui.datepicker.packed.js'}
|
||||
{combine_script id='datepicker.js' load='footer' require='jquery.ui.datepicker' path='themes/default/js/datepicker.js'}
|
||||
|
||||
{assign var="datepicker_language" value="themes/default/js/ui/i18n/ui.datepicker-"|@cat:$lang_info.code|@cat:".js"}
|
||||
|
||||
{if "PHPWG_ROOT_PATH"|@constant|@cat:$datepicker_language|@file_exists}
|
||||
{known_script id="jquery.ui.datepicker-$lang_info.code" src=$ROOT_URL|@cat:$datepicker_language}
|
||||
{combine_script id="jquery.ui.datepicker-$lang_info.code" path=$datepicker_language}
|
||||
{/if}
|
||||
|
||||
{html_head}
|
||||
<link rel="stylesheet" type="text/css" href="{$ROOT_URL}themes/default/js/ui/theme/ui.datepicker.css">
|
||||
{/html_head}
|
||||
|
||||
<script type="text/javascript">
|
||||
{footer_script}
|
||||
function pwg_initialization_datepicker(day, month, year, linked_date, checked_on_change, min_linked_date, max_linked_date)
|
||||
{ldelim}
|
||||
return pwg_common_initialization_datepicker(
|
||||
"{$ROOT_URL}{$themeconf.icon_dir}/datepicker.png",
|
||||
day, month, year, linked_date, checked_on_change, min_linked_date, max_linked_date);
|
||||
}
|
||||
</script>
|
||||
{/footer_script}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
{known_script id="jquery" src=$ROOT_URL|@cat:"themes/default/js/jquery.packed.js"}
|
||||
{known_script id="jquery.ui" src=$ROOT_URL|@cat:"themes/default/js/ui/packed/ui.core.packed.js"}
|
||||
{known_script id="jquery.ui.resizable" src=$ROOT_URL|@cat:"themes/default/js/ui/packed/ui.resizable.packed.js"}
|
||||
{combine_script id='jquery' load='footer' path='themes/default/js/jquery.packed.js'}
|
||||
{combine_script id='jquery.ui' load='footer' require='jquery' path='themes/default/js/ui/packed/ui.core.packed.js'}
|
||||
{combine_script id='jquery.ui.resizable' load='footer' require='jquery.ui' path='themes/default/js/ui/packed/ui.resizable.packed.js'}
|
||||
|
||||
{* Resize possible *}
|
||||
{literal}
|
||||
<script type="text/javascript">
|
||||
{footer_script}{literal}
|
||||
jQuery().ready(function(){
|
||||
// Resize possible for list
|
||||
jQuery(".categoryList").resizable({
|
||||
|
|
@ -18,5 +17,5 @@
|
|||
ghost: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{/literal}
|
||||
{/literal}{/footer_script}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
{/if}
|
||||
|
||||
{if isset($U_SEARCH_RULES) }
|
||||
{combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'}
|
||||
<li><a href="{$U_SEARCH_RULES}" onclick="popuphelp(this.href); return false;" title="{'Search rules'|@translate}" rel="nofollow"><img src="{$ROOT_URL}{$themeconf.icon_dir}/search_rules.png" class="button" alt="(?)"></a></li>
|
||||
{/if}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@
|
|||
<a href="{$U_ADMIN}" title="{'Modify information'|@translate}"><img src="{$ROOT_URL}{$themeconf.icon_dir}/preferences.png" class="button" alt="{'edit'|@translate}"></a>
|
||||
{/if}
|
||||
{if isset($U_CADDIE) }{*caddie management BEGIN*}
|
||||
{combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'}
|
||||
<script type="text/javascript">
|
||||
{literal}function addToCadie(aElement, rootUrl, id)
|
||||
{
|
||||
|
|
@ -208,15 +209,17 @@ y.callService(
|
|||
<input type="submit" name="rate" value="{$mark}" class="rateButton" title="{$mark}">
|
||||
{/if}
|
||||
{/foreach}
|
||||
{combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'}
|
||||
{combine_script id='rating' load='async' require='core.scripts' path='themes/default/js/rating.js'}
|
||||
<script type="text/javascript">
|
||||
var _pwgRatingAutoQueue = _pwgRatingAutoQueue || [];
|
||||
_pwgRatingAutoQueue.push( {ldelim}rootUrl: '{$ROOT_URL|@escape:"javascript"}', image_id: {$current.id},
|
||||
updateRateText: "{'Update your rating'|@translate|@escape:'javascript'}", updateRateElement: document.getElementById("updateRate"),
|
||||
ratingSummaryText: "{'%.2f (rated %d times)'|@translate|@escape:'javascript'}", ratingSummaryElement: document.getElementById("ratingSummary") {rdelim} );
|
||||
(function () {ldelim}
|
||||
/*(function () {ldelim}
|
||||
var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = '{$ROOT_URL}themes/default/js/rating.js';
|
||||
var s0 = document.getElementsByTagName('script')[0]; s0.parentNode.insertBefore(s, s0);
|
||||
})();
|
||||
})();*/
|
||||
</script>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -228,6 +231,7 @@ y.callService(
|
|||
<tr id="Privacy">
|
||||
<td class="label">{'Who can see this photo?'|@translate}</td>
|
||||
<td class="value">
|
||||
{combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'}
|
||||
<script type="text/javascript">
|
||||
{literal}function setPrivacyLevel(selectElement, rootUrl, id, level)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,26 +1,25 @@
|
|||
|
||||
{* Example of resizeable *}
|
||||
{*
|
||||
|
||||
{include file='include/resize.inc.tpl'}
|
||||
*}
|
||||
|
||||
|
||||
{* Example of datepicker *}
|
||||
{*
|
||||
|
||||
{include file='include/datepicker.inc.tpl'}
|
||||
|
||||
{literal}
|
||||
<script type="text/javascript">
|
||||
{footer_script}{literal}
|
||||
pwg_initialization_datepicker("#start_day", "#start_month", "#start_year", "#start_linked_date", null, null, "#end_linked_date");
|
||||
pwg_initialization_datepicker("#end_day", "#end_month", "#end_year", "#end_linked_date", null, "#start_linked_date", null);
|
||||
jQuery().ready(function(){ $(".date_today").hide(); });
|
||||
</script>
|
||||
{/literal}
|
||||
*}
|
||||
{/literal}{/footer_script}
|
||||
|
||||
|
||||
<div id="content" class="content">
|
||||
|
||||
<div class="titrePage">
|
||||
<ul class="categoryActions">
|
||||
{combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'}
|
||||
<li><a href="{$U_HELP}" onclick="popuphelp(this.href); return false;" title="{'Help'|@translate}" rel="nofollow"><img src="{$ROOT_URL}{$themeconf.icon_dir}/help.png" class="button" alt="(?)"></a></li>
|
||||
<li><a href="{$U_HOME}" title="{'Home'|@translate}" rel="Home"><img src="{$ROOT_URL}{$themeconf.icon_dir}/home.png" class="button" alt="{'Home'|@translate}"></a></li>
|
||||
</ul>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue