Changeset 7975
- Timestamp:
- Dec 2, 2010, 8:46:30 PM (13 years ago)
- Location:
- trunk
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/template.class.php
r6363 r7975 44 44 var $html_head_elements = array(); 45 45 46 var $scriptLoader; 47 var $html_footer_raw_script = array(); 48 46 49 function Template($root = ".", $theme= "", $path = "template") 47 50 { 48 51 global $conf, $lang_info; 49 52 53 $this->scriptLoader = new ScriptLoader; 50 54 $this->smarty = new Smarty; 51 55 $this->smarty->debugging = $conf['debug_template']; … … 83 87 $this->smarty->register_modifier( 'get_extent', array(&$this, 'get_extent') ); 84 88 $this->smarty->register_block('html_head', array(&$this, 'block_html_head') ); 89 $this->smarty->register_function('combine_script', array(&$this, 'func_combine_script') ); 90 $this->smarty->register_function('get_combined_scripts', array(&$this, 'func_get_combined_scripts') ); 91 $this->smarty->register_block('footer_script', array(&$this, 'block_footer_script') ); 85 92 $this->smarty->register_function('known_script', array(&$this, 'func_known_script') ); 86 93 $this->smarty->register_prefilter( array('Template', 'prefilter_white_space') ); … … 377 384 function flush() 378 385 { 386 if (!$this->scriptLoader->did_head()) 387 { 388 $search = "\n</head>"; 389 $pos = strpos( $this->output, $search ); 390 if ($pos !== false) 391 { 392 $scripts = $this->scriptLoader->get_head_scripts(); 393 $content = array(); 394 foreach ($scripts as $id => $script) 395 { 396 $content[]= 397 '<script type="text/javascript" src="' 398 . Template::make_script_src($script) 399 .'"></script>'; 400 } 401 402 $this->output = substr_replace( $this->output, "\n".implode( "\n", $content ), $pos, 0 ); 403 } //else maybe error or warning ? 404 } 405 379 406 if ( count($this->html_head_elements) ) 380 407 { … … 473 500 } 474 501 } 502 503 function func_combine_script($params, &$smarty) 504 { 505 if (!isset($params['id'])) 506 { 507 $smarty->trigger_error("combine_script: missing 'id' parameter", E_USER_ERROR); 508 } 509 $load = 0; 510 if (isset($params['load'])) 511 { 512 switch ($params['load']) 513 { 514 case 'header': break; 515 case 'footer': $load=1; break; 516 case 'async': $load=2; break; 517 default: $smarty->trigger_error("combine_script: invalid 'load' parameter", E_USER_ERROR); 518 } 519 } 520 $this->scriptLoader->add( $params['id'], $load, 521 empty($params['require']) ? array() : explode( ',', $params['require'] ), 522 @$params['path'], 523 isset($params['version']) ? $params['version'] : 0 ); 524 } 525 526 527 function func_get_combined_scripts($params, &$smarty) 528 { 529 if (!isset($params['load'])) 530 { 531 $smarty->trigger_error("get_combined_scripts: missing 'load' parameter", E_USER_ERROR); 532 } 533 $load = $params['load']=='header' ? 0 : 1; 534 $content = array(); 535 536 if ($load==0) 537 { 538 if ($this->scriptLoader->did_head()) 539 fatal_error('get_combined_scripts several times header'); 540 541 $scripts = $this->scriptLoader->get_head_scripts(); 542 foreach ($scripts as $id => $script) 543 { 544 $content[]= 545 '<script type="text/javascript" src="' 546 . Template::make_script_src($script) 547 .'"></script>'; 548 } 549 } 550 else 551 { 552 if (!$this->scriptLoader->did_head()) 553 fatal_error('get_combined_scripts didn\'t call header'); 554 $scripts = $this->scriptLoader->get_footer_scripts(); 555 foreach ($scripts[0] as $id => $script) 556 { 557 $content[]= 558 '<script type="text/javascript" src="' 559 . Template::make_script_src($script) 560 .'"></script>'; 561 } 562 if (count($this->html_footer_raw_script)) 563 { 564 $content[]= '<script type="text/javascript">'; 565 $content = array_merge($content, $this->html_footer_raw_script); 566 $content[]= '</script>'; 567 } 568 569 if (count($scripts[1])) 570 { 571 $content[]= '<script type="text/javascript">'; 572 $content[]= '(function() { 573 var after = document.getElementsByTagName(\'script\')[document.getElementsByTagName(\'script\').length-1]; 574 var s;'; 575 foreach ($scripts[1] as $id => $script) 576 { 577 $content[]= 578 's=document.createElement(\'script\'); s.type = \'text/javascript\'; s.async = true; s.src = \'' 579 . Template::make_script_src($script) 580 .'\';'; 581 $content[]= 'after = after.parentNode.insertBefore(s, after);'; 582 } 583 $content[]= '})();'; 584 $content[]= '</script>'; 585 } 586 } 587 return implode("\n", $content); 588 } 589 590 591 private static function make_script_src( $script ) 592 { 593 $ret = ''; 594 if ( url_is_remote($script->path) ) 595 $ret = $script->path; 596 else 597 { 598 $ret = get_root_url().$script->path; 599 if ($script->version!==false) 600 { 601 $ret.= '?v'. ($script->version ? $script->version : PHPWG_VERSION); 602 } 603 } 604 return $ret; 605 } 606 607 function block_footer_script($params, $content, &$smarty, &$repeat) 608 { 609 $content = trim($content); 610 if ( !empty($content) ) 611 { // second call 612 $this->html_footer_raw_script[] = $content; 613 } 614 } 475 615 476 616 /** … … 645 785 } 646 786 787 788 final class Script 789 { 790 public $load_mode; 791 public $precedents = array(); 792 public $path; 793 public $version; 794 public $extra = array(); 795 796 function Script($load_mode, $precedents, $path, $version) 797 { 798 $this->load_mode = $load_mode; 799 $this->precedents = $precedents; 800 $this->path = $path; 801 $this->version = $version; 802 } 803 804 function set_path($path) 805 { 806 if (!empty($path)) 807 $this->path = $path; 808 } 809 } 810 811 812 /** Manage a list of required scripts for a page, by optimizing their loading location (head, bottom, async) 813 and later on by combining them in a unique file respecting at the same time dependencies.*/ 814 class ScriptLoader 815 { 816 private $registered_scripts; 817 private $did_head; 818 private static $known_paths = array( 819 'core.scripts' => 'themes/default/js/scripts.js', 820 'jquery' => 'themes/default/js/jquery.min.js', 821 'jquery.ui' => 'themes/default/js/ui/packed/ui.core.packed.js' 822 ); 823 824 function __construct() 825 { 826 $this->clear(); 827 } 828 829 function clear() 830 { 831 $this->registered_scripts = array(); 832 $this->did_head = false; 833 } 834 835 function add($id, $load_mode, $require, $path, $version=0) 836 { 837 if ($this->did_head && $load_mode==0 ) 838 { 839 trigger_error("Attempt to add a new script $id but the head has been written", E_USER_WARNING); 840 } 841 if (! isset( $this->registered_scripts[$id] ) ) 842 { 843 $script = new Script($load_mode, $require, $path, $version); 844 self::fill_well_known($id, $script); 845 $this->registered_scripts[$id] = $script; 846 } 847 else 848 { 849 $script = & $this->registered_scripts[$id]; 850 if (count($require)) 851 { 852 $script->precedents = array_unique( array_merge($script->precedents, $require) ); 853 } 854 $script->set_path($path); 855 if ($version && version_compare($script->version, $version)<0 ) 856 $script->version = $version; 857 if ($load_mode < $script->load_mode) 858 $script->load_mode = $load_mode; 859 } 860 } 861 862 function did_head() 863 { 864 return $this->did_head; 865 } 866 867 private static function fill_well_known($id, $script) 868 { 869 if ( empty($script->path) && isset(self::$known_paths[$id])) 870 { 871 $script->path = self::$known_paths[$id]; 872 } 873 if ( strncmp($id, 'jquery.', 7)==0 ) 874 { 875 if ( !in_array('jquery', $script->precedents ) ) 876 $script->precedents[] = 'jquery'; 877 if ( strncmp($id, 'jquery.ui.', 10)==0 && !in_array('jquery.ui', $script->precedents ) ) 878 $script->precedents[] = 'jquery.ui'; 879 } 880 } 881 882 function get_head_scripts() 883 { 884 do 885 { 886 $changed = false; 887 foreach( $this->registered_scripts as $id => $script) 888 { 889 $load = $script->load_mode; 890 if ($load==0) 891 continue; 892 if ($load==2) 893 $load=1; // we are async -> a predecessor cannot be async because the script execution order is not guaranteed 894 foreach( $script->precedents as $precedent) 895 { 896 if ( !isset($this->registered_scripts[$precedent] ) ) 897 { 898 trigger_error("Script $id requires undefined script $precedent", E_USER_WARNING); 899 continue; 900 } 901 if ( $this->registered_scripts[$precedent]->load_mode > $load ) 902 { 903 $this->registered_scripts[$precedent]->load_mode = $load; 904 $changed = true; 905 } 906 } 907 } 908 } 909 while ($changed); 910 911 foreach( array_keys($this->registered_scripts) as $id ) 912 { 913 $this->compute_script_topological_order($id); 914 } 915 916 uasort($this->registered_scripts, array('ScriptLoader', 'cmp_by_mode_and_order')); 917 918 $result = array(); 919 foreach( $this->registered_scripts as $id => $script) 920 { 921 if ($script->load_mode > 0) 922 break; 923 if ( !empty($script->path) ) 924 $result[$id] = $script; 925 else 926 trigger_error("Script $id has an undefined path", E_USER_WARNING); 927 } 928 $this->did_head = true; 929 return $result; 930 } 931 932 function get_footer_scripts() 933 { 934 if (!$this->did_head) 935 { 936 trigger_error("Attempt to write footer scripts without header scripts", E_USER_WARNING); 937 } 938 $result = array( array(), array() ); 939 foreach( $this->registered_scripts as $id => $script) 940 { 941 if ($script->load_mode > 0) 942 { 943 if ( !empty( $script->path ) ) 944 { 945 $result[$script->load_mode-1][$id] = $script; 946 } 947 else 948 trigger_error("Script $id has an undefined path", E_USER_WARNING); 949 } 950 } 951 return $result; 952 } 953 954 private function compute_script_topological_order($script_id) 955 { 956 if (!isset($this->registered_scripts[$script_id])) 957 { 958 trigger_error("Undefined script $script_id is required by someone", E_USER_WARNING); 959 return 0; 960 } 961 $script = & $this->registered_scripts[$script_id]; 962 if (isset($script->extra['order'])) 963 return $script->extra['order']; 964 if (count($script->precedents) == 0) 965 return ($script->extra['order'] = 0); 966 $max = 0; 967 foreach( $script->precedents as $precedent) 968 $max = max($max, $this->compute_script_topological_order($precedent) ); 969 $max++; 970 return ($script->extra['order'] = $max); 971 } 972 973 private static function cmp_by_mode_and_order($s1, $s2) 974 { 975 $ret = $s1->load_mode - $s2->load_mode; 976 if (!$ret) 977 $ret = $s1->extra['order'] - $s2->extra['order']; 978 return $ret; 979 } 980 } 981 647 982 ?> -
trunk/themes/default/template/footer.tpl
r5244 r7975 19 19 {/if} 20 20 21 {get_combined_scripts load='footer'} 21 22 22 23 {if isset($footer_elements)} -
trunk/themes/default/template/header.tpl
r7474 r7975 41 41 42 42 {if not empty($page_refresh) }<meta http-equiv="refresh" content="{$page_refresh.TIME};url={$page_refresh.U_REFRESH}">{/if} 43 43 {* 44 44 <script type="text/javascript" src="{$ROOT_URL}themes/default/js/scripts.js"></script> 45 *} 45 46 <!--[if lt IE 7]> 46 47 <script type="text/javascript" src="{$ROOT_URL}themes/default/js/pngfix.js"></script> 47 48 <![endif]--> 49 50 {get_combined_scripts load='header'} 48 51 49 52 {if not empty($head_elements)} -
trunk/themes/default/template/include/datepicker.inc.tpl
r7798 r7975 1 1 2 { known_script id="jquery" src=$ROOT_URL|@cat:"themes/default/js/jquery.packed.js"}3 { known_script id="jquery.ui" src=$ROOT_URL|@cat:"themes/default/js/ui/packed/ui.core.packed.js"}4 { known_script id="jquery.ui.datepicker" src=$ROOT_URL|@cat:"themes/default/js/ui/packed/ui.datepicker.packed.js"}5 { known_script id="datepicker.js" src=$ROOT_URL|@cat:"themes/default/js/datepicker.js"}2 {combine_script id='jquery' load='footer' path='themes/default/js/jquery.packed.js'} 3 {combine_script id='jquery.ui' load='footer' require='jquery' path='themes/default/js/ui/packed/ui.core.packed.js'} 4 {combine_script id='jquery.ui.datepicker' load='footer' require='jquery.ui' path='themes/default/js/ui/packed/ui.datepicker.packed.js'} 5 {combine_script id='datepicker.js' load='footer' require='jquery.ui.datepicker' path='themes/default/js/datepicker.js'} 6 6 7 7 {assign var="datepicker_language" value="themes/default/js/ui/i18n/ui.datepicker-"|@cat:$lang_info.code|@cat:".js"} 8 8 9 9 {if "PHPWG_ROOT_PATH"|@constant|@cat:$datepicker_language|@file_exists} 10 { known_script id="jquery.ui.datepicker-$lang_info.code" src=$ROOT_URL|@cat:$datepicker_language}10 {combine_script id="jquery.ui.datepicker-$lang_info.code" path=$datepicker_language} 11 11 {/if} 12 12 … … 15 15 {/html_head} 16 16 17 <script type="text/javascript"> 17 {footer_script} 18 18 function pwg_initialization_datepicker(day, month, year, linked_date, checked_on_change, min_linked_date, max_linked_date) 19 19 {ldelim} … … 22 22 day, month, year, linked_date, checked_on_change, min_linked_date, max_linked_date); 23 23 } 24 </script> 24 {/footer_script} -
trunk/themes/default/template/include/resize.inc.tpl
r5123 r7975 1 { known_script id="jquery" src=$ROOT_URL|@cat:"themes/default/js/jquery.packed.js"}2 { known_script id="jquery.ui" src=$ROOT_URL|@cat:"themes/default/js/ui/packed/ui.core.packed.js"}3 { known_script id="jquery.ui.resizable" src=$ROOT_URL|@cat:"themes/default/js/ui/packed/ui.resizable.packed.js"}1 {combine_script id='jquery' load='footer' path='themes/default/js/jquery.packed.js'} 2 {combine_script id='jquery.ui' load='footer' require='jquery' path='themes/default/js/ui/packed/ui.core.packed.js'} 3 {combine_script id='jquery.ui.resizable' load='footer' require='jquery.ui' path='themes/default/js/ui/packed/ui.resizable.packed.js'} 4 4 5 5 {* Resize possible *} 6 {literal} 7 <script type="text/javascript"> 6 {footer_script}{literal} 8 7 jQuery().ready(function(){ 9 8 // Resize possible for list … … 19 18 }); 20 19 }); 21 </script> 22 {/literal} 20 {/literal}{/footer_script} 21 -
trunk/themes/default/template/index.tpl
r6951 r7975 28 28 29 29 {if isset($U_SEARCH_RULES) } 30 {combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'} 30 31 <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> 31 32 {/if} -
trunk/themes/default/template/picture.tpl
r7957 r7975 60 60 {/if} 61 61 {if isset($U_CADDIE) }{*caddie management BEGIN*} 62 {combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'} 62 63 <script type="text/javascript"> 63 64 {literal}function addToCadie(aElement, rootUrl, id) … … 209 210 {/if} 210 211 {/foreach} 212 {combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'} 213 {combine_script id='rating' load='async' require='core.scripts' path='themes/default/js/rating.js'} 211 214 <script type="text/javascript"> 212 215 var _pwgRatingAutoQueue = _pwgRatingAutoQueue || []; … … 214 217 updateRateText: "{'Update your rating'|@translate|@escape:'javascript'}", updateRateElement: document.getElementById("updateRate"), 215 218 ratingSummaryText: "{'%.2f (rated %d times)'|@translate|@escape:'javascript'}", ratingSummaryElement: document.getElementById("ratingSummary") {rdelim} ); 216 (function () {ldelim}219 /*(function () {ldelim} 217 220 var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = '{$ROOT_URL}themes/default/js/rating.js'; 218 221 var s0 = document.getElementsByTagName('script')[0]; s0.parentNode.insertBefore(s, s0); 219 })(); 222 })();*/ 220 223 </script> 221 224 </div> … … 229 232 <td class="label">{'Who can see this photo?'|@translate}</td> 230 233 <td class="value"> 234 {combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'} 231 235 <script type="text/javascript"> 232 236 {literal}function setPrivacyLevel(selectElement, rootUrl, id, level) -
trunk/themes/default/template/search.tpl
r6951 r7975 1 1 2 2 {* Example of resizeable *} 3 {* 3 4 4 {include file='include/resize.inc.tpl'} 5 *} 5 6 6 7 7 {* Example of datepicker *} 8 {* 8 9 9 {include file='include/datepicker.inc.tpl'} 10 10 11 {literal} 12 <script type="text/javascript"> 11 {footer_script}{literal} 13 12 pwg_initialization_datepicker("#start_day", "#start_month", "#start_year", "#start_linked_date", null, null, "#end_linked_date"); 14 13 pwg_initialization_datepicker("#end_day", "#end_month", "#end_year", "#end_linked_date", null, "#start_linked_date", null); 15 14 jQuery().ready(function(){ $(".date_today").hide(); }); 16 </script> 17 {/literal} 18 *} 15 {/literal}{/footer_script} 16 19 17 20 18 <div id="content" class="content"> … … 22 20 <div class="titrePage"> 23 21 <ul class="categoryActions"> 22 {combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'} 24 23 <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> 25 24 <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>
Note: See TracChangeset
for help on using the changeset viewer.