Changeset 7975 for trunk/include
- Timestamp:
- Dec 2, 2010, 8:46:30 PM (13 years ago)
- File:
-
- 1 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 ?>
Note: See TracChangeset
for help on using the changeset viewer.