1<?php 2 3/** 4 * e107 website system 5 * 6 * Copyright (C) 2008-2017 e107 Inc (e107.org) 7 * Released under the terms and conditions of the 8 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) 9 * 10 * @file 11 * JS Manager. 12 */ 13 14 15/** 16 * Class e_jsmanager. 17 */ 18class e_jsmanager 19{ 20 /** 21 * Supported Libraries (Front-End) - loaded on demand. 22 */ 23 protected $_libraries = array( 24 'prototype' => array(), // TODO remove prototype completely. 25 'jquery' => array(), 26 ); 27 28 /** 29 * Dynamic List of files to be cached. 30 * @var array 31 */ 32 protected $_cache_list = array(); 33 34 35 36 protected $_core_prefs = array(); 37 38 /** 39 * Array to store JavaScript options will be rendered in footer as JSON object. 40 * 41 * @var array 42 */ 43 protected $_e_js_settings = array( 44 'basePath' => e_HTTP, 45 ); 46 47 /** 48 * Core JS library files, loaded via e_jslib.php 49 * 50 * @var array 51 */ 52 protected $_e_jslib_core = array(); 53 54 /** 55 * Plugin JS library files, loaded via e_jslib.php 56 * 57 * @var array 58 */ 59 protected $_e_jslib_plugin = array(); 60 61 /** 62 * Theme JS library files, loaded via e_jslib.php 63 * 64 * @var array 65 */ 66 protected $_e_jslib_theme = array(); 67 68 /** 69 * JS files array - loaded in page header 70 * 71 * @var array 72 */ 73 protected $_runtime_header = array(); 74 75 /** 76 * JS code array - loaded in page header 77 * after all registered JS header files 78 * 79 * @var array 80 */ 81 protected $_runtime_header_src = array(); 82 83 /** 84 * Current Header zone (under development) 85 * 86 * @var array 87 */ 88 protected $_zone_header = 0; 89 90 /** 91 * Current Footer zone (under development) 92 * 93 * @var array 94 */ 95 protected $_zone_footer = 0; 96 97 /** 98 * JS files array - loaded in page footer 99 * 100 * @var array 101 */ 102 protected $_runtime_footer = array(); 103 104 /** 105 * JS code array - loaded in page footer 106 * 107 * @var array 108 */ 109 protected $_runtime_footer_src = array(); 110 111 /** 112 * Index of all registered JS/CSS files - for faster searching 113 * 114 * @var array 115 */ 116 protected $_index_all = array(); 117 118 /** 119 * Registered link tags files by type (core|theme|plugin|other) 120 * 121 * @var array 122 */ 123 protected $_e_link = array(); 124 125 126 /** 127 * Registered CSS files by type (core|theme|plugin|other) 128 * 129 * @var array 130 */ 131 protected $_e_css = array(); 132 133 /** 134 * Inline CSS 135 * 136 * @var array 137 */ 138 protected $_e_css_src = array(); 139 140 141 /** 142 * Runtime location 143 * 144 * @var boolean 145 */ 146 protected $_in_admin = false; 147 148 /** 149 * Browser cache id 150 * 151 * @var integer 152 */ 153 protected $_browser_cache_id = 0; 154 155 /** 156 * @var array 157 */ 158 protected $_lastModified = array(); 159 160 /** 161 * Singleton instance 162 * Allow class extends - override {@link getInstance()} 163 * 164 * @var e_jsmanager 165 */ 166 protected static $_instance = null; 167 168 /** 169 * Current Framework Dependency 170 * 171 * @var string null | prototype | jquery 172 */ 173 protected $_dependence = null; 174 175 /** 176 * Loaded Framework Dependency 177 * 178 * @var array 179 */ 180 protected $_dependenceLoaded = array(); 181 182 183 protected $_cache_enabled = false; 184 185 186 protected $_sep = '#|#'; 187 188 /** 189 * Constructor 190 * 191 * Use {@link getInstance()}, direct instantiating 192 * is not possible for signleton objects 193 */ 194 protected function __construct() 195 { 196 } 197 198 /** 199 * Cloning is not allowed 200 * 201 */ 202 private function __clone() 203 { 204 } 205 206 /** 207 * Get singleton instance 208 * 209 * @return e_jsmanager 210 */ 211 public static function getInstance() 212 { 213 if(null === self::$_instance) 214 { 215 self::$_instance = new self(); 216 self::$_instance->_init(); 217 } 218 return self::$_instance; 219 } 220 221 /** 222 * Get and parse core preference values (if available) 223 * 224 * @return void 225 */ 226 protected function _init() 227 { 228 // Try to auto-detect runtime location 229 $this->setInAdmin(defset('e_ADMIN_AREA', false)); 230 231 if($this->isInAdmin()) // Admin Area. 232 { 233 e107::library('load', 'jquery'); 234 // jQuery Once is used in e107.behaviors. 235 e107::library('load', 'jquery.once'); 236 e107::library('load', 'jquery.ui'); 237 } 238 else // Front-End. 239 { 240 e107::library('load', 'jquery'); 241 // jQuery Once is used in e107.behaviors. 242 e107::library('load', 'jquery.once'); 243 } 244 245 // TODO 246 // jQuery is the only JS framework, and it is always loaded. So remove 247 // unnecessary code here below. 248 249 $customJqueryUrls = e107::getPref('library-jquery-urls'); 250 $this->_cache_enabled = e107::getPref('jscsscachestatus',false); 251 252 if(vartrue($customJqueryUrls) && $this->_in_admin === false) 253 { 254 $this->_libraries['jquery'] = explode("\n", $customJqueryUrls); 255 } 256 257 // Try to load browser cache id from core preferences 258 $this->setCacheId(e107::getPref('e_jslib_browser_cache', 0)); 259 260 // Load stored in preferences core lib paths ASAP 261 $this->_core_prefs = e107::getPref('e_jslib_core'); 262 $core = array(); 263 264 if(is_array($this->_core_prefs)) 265 { 266 foreach($this->_core_prefs as $id=>$vis) 267 { 268 $this->_dependence = $id; 269 270 if(!$this->libDisabled($id,$vis)) 271 { 272 if(vartrue($this->_libraries[$id])) 273 { 274 foreach($this->_libraries[$id] as $path) 275 { 276 $core[$path] = $vis; 277 } 278 } 279 280 } 281 282 } 283 } 284 $this->_dependence = null; 285 286 if($vis != 'auto') 287 { 288 $this->checkLibDependence(null, $core); 289 } 290 291 // Load stored in preferences plugin lib paths ASAP 292 $plug_libs = e107::getPref('e_jslib_plugin'); 293 if(!$plug_libs) 294 { 295 $plug_libs = array(); 296 } 297 foreach ($plug_libs as $plugname => $lib_paths) 298 { 299 $this->pluginLib($plugname, $lib_paths); 300 } 301 302 // Load stored in preferences theme lib paths ASAP 303 // TODO - decide if THEME should directly use themeLib() or 304 // we store paths in 'e_jslib_theme' on theme installation only (theme.xml)! 305 $theme_libs = e107::getPref('e_jslib_theme'); 306 if(!$theme_libs) 307 { 308 $theme_libs = array(); 309 } 310 $this->themeLib($theme_libs); 311 } 312 313 /** 314 * Add Core CSS file for inclusion in site header, shorthand of headerFile() method 315 * 316 * @param string|array $file_path relative to {e_JS} folder 317 * @param string $media any valid media attribute string - http://www.w3schools.com/TAGS/att_link_media.asp 318 * @return e_jsmanager 319 */ 320 public function coreCSS($file_path, $media = 'all', $preComment = '', $postComment = '') 321 { 322 $this->addJs('core_css', $file_path, $media); 323 return $this; 324 } 325 326 /** 327 * Add Plugin CSS file(s) for inclusion in site header 328 * 329 * @param string $plugname 330 * @param string|array $file_path relative to e107_plugins/myplug/ folder or array in format 'path - media' 331 * @param string $media any valid media attribute string - http://www.w3schools.com/TAGS/att_link_media.asp 332 * @return e_jsmanager 333 */ 334 public function pluginCSS($plugname, $file_path, $media = 'all', $preComment = '', $postComment = '') 335 { 336 if(is_array($file_path)) 337 { 338 foreach ($file_path as $fpath => $media_attr) 339 { 340 $this->addJs('plugin_css', $plugname.':'.$fpath, $media_attr, $preComment, $postComment); 341 } 342 return $this; 343 } 344 $this->addJs('plugin_css', $plugname.':'.$file_path, $media, $preComment, $postComment); 345 return $this; 346 } 347 348 /** 349 * Add Theme CSS file(s) for inclusion in site header 350 * 351 * @param string|array $file_path relative to e107_themes/current_theme/ folder 352 * @param string $media any valid media attribute string - http://www.w3schools.com/TAGS/att_link_media.asp 353 * @return e_jsmanager 354 */ 355 public function themeCSS($file_path, $media = 'all', $preComment = '', $postComment = '') 356 { 357 $this->addJs('theme_css', $file_path, $media, $preComment, $postComment); 358 return $this; 359 } 360 361 /** 362 * Add CSS file(s) for inclusion in site header in the 'library' category. 363 * 364 * @param string|array $file_path path, shortcodes usage is prefered 365 * @param string $media any valid media attribute string - http://www.w3schools.com/TAGS/att_link_media.asp 366 * @return e_jsmanager 367 */ 368 public function libraryCSS($file_path, $media = 'all', $preComment = '', $postComment = '') 369 { 370 $this->addJs('library_css', $file_path, $media, $preComment, $postComment); 371 return $this; 372 } 373 374 375 376 /** 377 * Add CSS file(s) for inclusion in site header 378 * 379 * @param string|array $file_path path, shortcodes usage is prefered 380 * @param string $media any valid media attribute string - http://www.w3schools.com/TAGS/att_link_media.asp 381 * @return e_jsmanager 382 */ 383 public function otherCSS($file_path, $media = 'all', $preComment = '', $postComment = '') 384 { 385 $this->addJs('other_css', $file_path, $media, $preComment, $postComment); 386 return $this; 387 } 388 389 /** 390 * Add CSS code to site header 391 * 392 * @param string|array $js_content 393 * @param string $media (not implemented yet) any valid media attribute string - http://www.w3schools.com/TAGS/att_link_media.asp 394 * @return e_jsmanager 395 */ 396 public function inlineCSS($css_content, $media = 'all') 397 { 398 $this->addJs('inline_css', $css_content, $media); 399 return $this; 400 } 401 402 403 /** 404 * Add Core JS library file(s) for inclusion from e_jslib routine 405 * 406 * @param string|array $file_path relative to e107_files/jslib/ folder or array in format 'path - runtime location' 407 * @param string $runtime_location admin|front|all - where should be JS used 408 * @return e_jsmanager 409 */ 410 protected function coreLib($file_path, $runtime_location = 'front') 411 { 412 $this->addJs('core', $file_path, $runtime_location); 413 return $this; 414 } 415 416 /** 417 * Add Plugin JS library file(s) for inclusion from e_jslib routine 418 * 419 * @param string $plugname 420 * @param string|array $file_path relative to e107_plugins/myplug/ folder or array in format 'path - runtime location' 421 * @param string $runtime_location admin|front|all - where should be JS used 422 * @return e_jsmanager 423 */ 424 protected function pluginLib($plugname, $file_path, $runtime_location = 'front') 425 { 426 if(is_array($file_path)) 427 { 428 foreach ($file_path as $fpath => $rlocation) 429 { 430 $this->addJs('plugin', $plugname.':'.$fpath, $rlocation); 431 } 432 return $this; 433 } 434 $this->addJs('plugin', $plugname.':'.$file_path, $runtime_location); 435 return $this; 436 } 437 438 /** 439 * Add Theme JS library file(s) for inclusion from e_jslib routine 440 * 441 * @param string|array $file_path relative to e107_themes/current_theme/ folder or array in format 'path - runtime location' 442 * @param string $runtime_location admin|front|all - where should be JS used 443 * @return e_jsmanager 444 */ 445 public function themeLib($file_path, $runtime_location = 'front') 446 { 447 $this->addJs('theme', $file_path, $runtime_location); 448 return $this; 449 } 450 451 /** 452 * Add Core JS library file(s) for inclusion in site header or site footer (in this order) if not 453 * already loaded by e_jslib routine. This should avoid dependency problems. 454 * Extremely useful for shortcodes and menus. 455 * 456 * @param string $file_path relative to e107_files/jslib/ folder 457 * @param integer $zone 1-5 (see header.php) 458 * @return e_jsmanager 459 */ 460 public function requireCoreLib($file_path, $zone = 2) 461 { 462 if(is_array($file_path)) 463 { 464 foreach ($file_path as $fpath => $z) 465 { 466 $this->tryHeaderFile('{e_WEB_JS}'.trim($fpath, '/'), $z); 467 } 468 return $this; 469 } 470 $this->tryHeaderFile('{e_WEB_JS}'.trim($file_path, '/'), $zone); 471 return $this; 472 } 473 474 /** 475 * Add Plugin JS library file(s) for inclusion in site header if not 476 * already loaded by e_jslib routine. This should avoid dependency problems. 477 * 478 * @param string $plugname 479 * @param string $file_path relative to e107_plugins/myplug/ folder 480 * @param integer $zone 1-5 (see header.php) 481 * @return e_jsmanager 482 */ 483 public function requirePluginLib($plugname, $file_path, $zone = 5) 484 { 485 if(is_array($file_path)) 486 { 487 foreach ($file_path as $fpath => $z) 488 { 489 $this->tryHeaderFile('{e_PLUGIN}'.$plugname.'/'.trim($fpath, '/'), $z); 490 } 491 return $this; 492 } 493 $this->tryHeaderFile('{e_PLUGIN}'.$plugname.'/'.trim($file_path, '/'), $zone); 494 return $this; 495 } 496 497 /** 498 * Add JS file(s) for inclusion in site header 499 * 500 * @param string|array $file_path path shortcodes usage is prefered 501 * @param integer $zone 1-5 (see header.php) 502 * @return e_jsmanager 503 */ 504 public function headerFile($file_path, $zone = 5, $pre = '', $post = '') 505 { 506 $this->addJs('header', $file_path, $zone, $pre, $post); 507 return $this; 508 } 509 510 /** 511 * Add Core JS file for inclusion in site header, shorthand of headerFile() method 512 * 513 * @param string $file_path relative to {e_JS} folder 514 * @param integer $zone 1-5 (see header.php) 515 * @return e_jsmanager 516 */ 517 public function headerCore($file_path, $zone = 2, $pre = '', $post = '') 518 { 519 $this->headerFile('{e_WEB_JS}'.trim($file_path, '/'), $zone, $pre, $post); 520 return $this; 521 } 522 523 /** 524 * Add Theme JS file for inclusion in site header, shorthand of headerFile() method 525 * 526 * @param string $file_path relative to theme root folder 527 * @param integer $zone 1-5 (see header.php) 528 * @return e_jsmanager 529 */ 530 public function headerTheme($file_path, $zone = 5, $pre = '', $post = '') 531 { 532 $this->headerFile(THEME.trim($file_path, '/'), $zone, $pre, $post); 533 return $this; 534 } 535 536 537 /** 538 * Add Theme JS file for inclusion in site footer (preferred), shorthand of footerFile() method 539 * 540 * @param string $file_path relative to theme root folder 541 * @param integer $zone 1-5 (see header.php) 542 * @return e_jsmanager 543 */ 544 public function footerTheme($file_path, $zone = 5, $pre = '', $post = '') 545 { 546 $this->footerFile(THEME.trim($file_path, '/'), $zone, $pre, $post); 547 return $this; 548 } 549 550 /** 551 * Add Plugin JS file for inclusion in site header, shorthand of headerFile() method 552 * 553 * @param string $plugname 554 * @param string $file_path relative to plugin root folder 555 * @param integer $zone 1-5 (see header.php) - REMOVED, actually we need to prevent zone change 556 * @return e_jsmanager 557 */ 558 public function headerPlugin($plugname, $file_path, $pre, $post) 559 { 560 $this->headerFile('{e_PLUGIN}'.$plugname.'/'.trim($file_path, '/'), 2, $pre, $post); // Zone 2 - after libraries 561 return $this; 562 } 563 564 /** 565 * Add JS file(s) for inclusion in site header if possible, else 566 * use {@link footerFile()} 567 * 568 * @param string|array $file_path path shortcodes usage is prefered 569 * @param integer $zone 1-5 (see header.php and footer.php) 570 * @return e_jsmanager 571 */ 572 public function tryHeaderFile($file_path, $zone = 5) 573 { 574 if(!defined('HEADER_INIT')) 575 { 576 $this->headerFile($file_path, $zone); 577 return $this; 578 } 579 580 $this->footerFile($file_path, $zone); 581 return $this; 582 } 583 584 /** 585 * Add JS file(s) for inclusion in site footer 586 * 587 * @param string|array $file_path path shortcodes usage is prefered 588 * @param integer $priority 1-5 (see footer.php) 589 * @return e_jsmanager 590 */ 591 public function footerFile($file_path, $priority = 5, $pre = '', $post = '') 592 { 593 $this->addJs('footer', $file_path, $priority, $pre, $post); 594 return $this; 595 } 596 597 /** 598 * Add JS code to site header 599 * 600 * @param string|array $js_content 601 * @param integer $zone 1-5 (see header.php) 602 * @return e_jsmanager 603 */ 604 public function headerInline($js_content, $zone = 5) 605 { 606 $this->addJs('header_inline', $js_content, $zone); 607 return $this; 608 } 609 610 /** 611 * Add JS code to site site header if possible, else 612 * use {@link footerInline()} 613 * 614 * @param string $js_content 615 * @param integer $zone 1-5 (see header.php and footer.php) 616 * @return e_jsmanager 617 */ 618 public function tryHeaderInline($js_content, $zone = 5) 619 { 620 if(!defined('HEADER_INIT')) 621 { 622 $this->headerInline($js_content, $zone); 623 return $this; 624 } 625 626 $this->footerInline($js_content, $zone); 627 return $this; 628 } 629 630 /** 631 * Add JS file(s) for inclusion in site footer 632 * 633 * @param string|array $js_content path shortcodes usage is prefered 634 * @param integer $priority 1-5 (see footer.php) 635 * @return e_jsmanager 636 */ 637 public function footerInline($js_content, $priority = 5) 638 { 639 $this->addJs('footer_inline', $js_content, $priority); 640 return $this; 641 } 642 643 /** 644 * Add JS settings to site footer 645 * 646 * @param array $js_settings 647 * @return e_jsmanager 648 */ 649 public function jsSettings(array $js_settings) 650 { 651 $this->addJs('settings', $js_settings); 652 return $this; 653 } 654 655 656 function setDependency($dep) 657 { 658 $this->_dependence = $dep; 659 } 660 661 public function resetDependency() 662 { 663 $this->_dependence = null; 664 } 665 666 /** 667 * Return TRUE if the library is disabled. ie. prototype or jquery. 668 * FIXME - remove $type & $loc 669 */ 670 public function libDisabled($type = null, $loc = null) 671 { 672 if($type == 'core' && ($loc == 'none')) 673 { 674 return true; 675 } 676 677 if($this->_dependence != null && isset($this->_libraries[$this->_dependence])) 678 { 679 $status = $this->_core_prefs[$this->_dependence]; 680 681 switch ($status) 682 { 683 case 'auto': 684 case 'all': 685 return false; 686 break; 687 688 case 'admin': 689 return ($this->isInAdmin()) ? false : true; 690 break; 691 692 case 'front': 693 return ($this->isInAdmin()) ? true : false; 694 break; 695 696 case 'none': 697 return true; 698 break; 699 700 default: 701 return true; 702 break; 703 } 704 } 705 706 return false; 707 708 } 709 710 public function checkLibDependence($rlocation, $libs = null) 711 { 712 // Load Required Library (prototype | jquery) 713 // called from addJs(), make isDisabled checks for smart runtime library detection 714 if($rlocation && $libs === null && $this->_dependence != null && isset($this->_libraries[$this->_dependence]) && !isset($this->_dependenceLoaded[$this->_dependence][$rlocation])) // load framework 715 { 716 if($this->libDisabled()) 717 { 718 $this->_dependenceLoaded[$this->_dependence][$rlocation] = array(); 719 return; 720 } 721 722 foreach($this->_libraries[$this->_dependence] as $inc) 723 { 724 if(strpos($inc,".css")!==false) 725 { 726 if(strpos($inc,"://")!==false) // cdn 727 { 728 $this->addJs('other_css', $inc, 'all', '<!-- AutoLoad -->'); 729 } 730 else 731 { 732 $this->addJs('core_css', $inc, 'all', '<!-- AutoLoad -->'); 733 } 734 } 735 else 736 { 737 $this->addJs('core', $inc, $rlocation, '<!-- AutoLoad -->'); 738 } 739 $this->_dependenceLoaded[$this->_dependence][$rlocation][] = $inc; 740 } 741 return $this; 742 } 743 // called on init time, isDisabled checks already done, just add stuff 744 if($rlocation === null && is_array($libs)) 745 { 746 foreach ($libs as $inc => $rlocation) 747 { 748 if(isset($this->_dependenceLoaded[$this->_dependence][$rlocation]) && in_array($inc, $this->_dependenceLoaded[$this->_dependence][$rlocation])) 749 { 750 continue; 751 } 752 if(strpos($inc,".css")!==false) 753 { 754 if(strpos($inc,"://")!==false) // cdn 755 { 756 $this->addJs('other_css', $inc, 'all', '<!-- AutoLoad -->'); 757 } 758 else 759 { 760 $this->addJs('core_css', $inc, 'all', '<!-- AutoLoad -->'); 761 } 762 } 763 else 764 { 765 $this->addJs('core', $inc, $rlocation, '<!-- AutoLoad -->'); 766 } 767 $this->_dependenceLoaded[$this->_dependence][$rlocation][] = $inc; 768 } 769 } 770 } 771 772 773 /** 774 * Add a <link> tag to the head. 775 * @param array $attributes key>value pairs 776 * @example addLink(array('rel'=>'prefetch', 'href'=>THEME.'images/browsers.png')); 777 */ 778 public function addLink($attributes=array()) 779 { 780 if(!empty($attributes)) 781 { 782 $this->_e_link[] = $attributes; 783 } 784 } 785 786 787 /** 788 * Render all link tags. (other than css) 789 * @return null 790 */ 791 public function renderLinks() 792 { 793 794 if(empty($this->_e_link)) 795 { 796 return null; 797 } 798 799 $text = ''; 800 801 foreach($this->_e_link as $v) 802 { 803 if(!empty($v['type'])) 804 { 805 if($v['type'] == 'text/css' || $v['rel'] == 'stylesheet') // not for this purpose. use e107::css(); 806 { 807 continue; 808 } 809 } 810 811 812 $text .= "\n<link"; 813 foreach($v as $key=>$val) 814 { 815 if(!empty($val)) 816 { 817 $text .= " ".$key."=\"".$val."\""; 818 } 819 } 820 $text .= " />"; 821 822 } 823 824 echo $text; 825 } 826 827 828 /** 829 * Require JS file(s). Used by corresponding public proxy methods. 830 * 831 * @see themeLib() 832 * @see pluginLib() 833 * @see coreLib() 834 * @see headerFile() 835 * @see footerFile() 836 * @see headerInline() 837 * @see footerInline() 838 * @param string $type core|plugin - jslib.php, header|footer|header_inline|footer_inline|core_css|plugin_css|theme_css|other_css|inline_css - runtime 839 * @param string|array $file_path 840 * @param string|integer $runtime_location admin|front|all|none (jslib), 0-5 (runtime inclusion), 'media' attribute (CSS) 841 * @return object $this 842 */ 843 protected function addJs($type, $file_path, $runtime_location = '', $pre = '', $post = '') 844 { 845 // TODO FIXME - remove JS framework dependency from front-end and backend. 846 // ie. no JS errors when prototype.js is completely disabled. 847 // no JS error with only 'e107 Core Minimum' is enabled. 848 // e107 Core Minimum should function independently of framework. 849 // ie. e107 Core Minimum: JS similar to e107 v1.0 should be loaded "e_js.php" (no framwork dependency) 850 // with basic functions like SyncWithServerTime() and expandit(), externalLinks() etc. 851 852 if(empty($file_path)) 853 { 854 return $this; 855 } 856 857 // prevent loop of death 858 if($pre != '<!-- AutoLoad -->') 859 { 860 $rlocation = $runtime_location; 861 if(is_numeric($runtime_location)) $rlocation = $this->isInAdmin() ? 'admin' : 'front'; 862 863 $this->checkLibDependence($rlocation); 864 865 866 // FIXME - better performance - executed on every addJs call - BAD 867 //libraries handled only by checkLibDependence() 868 if(!is_array($file_path)) 869 { 870 foreach ($this->_libraries as $l) 871 { 872 if(in_array($file_path, $l)) 873 { 874 return $this; 875 } 876 } 877 } 878 } 879 880 // if($type == 'core' && !is_array($file_path) && substr($file_path,0,4)=='http' ) // Core using CDN. 881 // { 882 // $type = 'header'; 883 // $runtime_location = 1; 884 // } 885 886 // Possibly no longer needed. 887 // FIXME - this could break something after CSS support was added, move it to separate method(s), recursion by type! 888 // Causes the css error on jquery-ui as a css file is loaded as a js. 889 890 if(is_array($file_path) && $type != 'settings') 891 { 892 // print_a($file_path); 893 foreach ($file_path as $fp => $loc) 894 { 895 if(is_numeric($fp)) 896 { 897 $fp = $loc; 898 $loc = $runtime_location; 899 } 900 901 $type = (strpos($fp,".css")!==false && $type == 'core') ? "core_css" : $type; 902 903 904 $this->addJs($type, $fp, $loc); 905 } 906 return $this; 907 } 908 909 if($this->libDisabled($type,$runtime_location)) 910 { 911 //echo $this->_dependence." :: DISABLED<br />"; 912 // echo $this->_dependence."::".$file_path." : DISABLED<br />"; 913 return $this; 914 915 } 916 else 917 { 918 // echo $this->_dependence." :: ENABLED<br />"; 919 // echo $this->_dependence."::".$file_path." : DISABLED<br />"; 920 } 921 922 923 924 $tp = e107::getParser(); 925 $runtime = false; 926 switch($type) 927 { 928 case 'core': 929 // added direct CDN support 930 $file_path = (strpos($file_path, 'http') !== 0 && strpos($file_path, '//') !== 0 ? '{e_WEB_JS}'.trim($file_path, '/') : $file_path).$this->_sep.$pre.$this->_sep.$post; 931 $registry = &$this->_e_jslib_core; 932 break; 933 934 case 'plugin': 935 $file_path = explode(':', $file_path); 936 $file_path = '{e_PLUGIN}'.$file_path[0].'/'.trim($file_path[1], '/').$this->_sep.$pre.$this->_sep.$post; 937 $registry = &$this->_e_jslib_plugin; 938 break; 939 940 case 'theme': 941 $file_path = '{e_THEME}'.$this->getCurrentTheme().'/'.trim($file_path, '/').$this->_sep.$pre.$this->_sep.$post; 942 //echo "file-Path = ".$file_path; 943 $registry = &$this->_e_jslib_theme; 944 break; 945 946 case 'core_css': //FIXME - core CSS should point to new e_WEB/css; add one more case - js_css -> e_WEB/jslib/ 947 // added direct CDN support 948 $file_path = $runtime_location.$this->_sep.(strpos($file_path, 'http') !== 0 && strpos($file_path, '//') !== 0 ? '{e_WEB_JS}'.trim($file_path, '/') : $file_path).$this->_sep.$pre.$this->_sep.$post; 949 if(!isset($this->_e_css['core'])) $this->_e_css['core'] = array(); 950 $registry = &$this->_e_css['core']; 951 $runtime = true; 952 break; 953 954 case 'plugin_css': 955 $pfile_path = explode(':', $file_path,2); 956 $plugfile_path = $runtime_location.$this->_sep.'{e_PLUGIN}'.$pfile_path[0].'/'.trim($pfile_path[1], '/').$this->_sep.$pre.$this->_sep.$post; 957 958 // allow for URLs to be attributed to plugins. (loads after theme css in admin area header) 959 $file_path = ((strpos($pfile_path[1], 'http') !== 0 && strpos($pfile_path[1], '//') !== 0)) ? $plugfile_path : 'all'.$this->_sep.$pfile_path[1].$this->_sep.$pre.$this->_sep.$post;; 960 if(!isset($this->_e_css['plugin'])) $this->_e_css['plugin'] = array(); 961 $registry = &$this->_e_css['plugin']; 962 $runtime = true; 963 964 break; 965 966 case 'theme_css': 967 $file_path = $runtime_location.$this->_sep.'{e_THEME}'.$this->getCurrentTheme().'/'.trim($file_path, '/').$this->_sep.$pre.$this->_sep.$post; 968 if(!isset($this->_e_css['theme'])) $this->_e_css['theme'] = array(); 969 $registry = &$this->_e_css['theme']; 970 $runtime = true; 971 break; 972 973 case 'other_css': 974 $file_path = $runtime_location.$this->_sep.$tp->createConstants($file_path, 'mix').$this->_sep.$pre.$this->_sep.$post; 975 if(!isset($this->_e_css['other'])) $this->_e_css['other'] = array(); 976 $registry = &$this->_e_css['other']; 977 $runtime = true; 978 break; 979 980 case 'library_css': 981 $file_path = $runtime_location.$this->_sep.$tp->createConstants($file_path, 'mix').$this->_sep.$pre.$this->_sep.$post; 982 // e107::getDebug()->log($file_path); 983 if(!isset($this->_e_css['library'])) $this->_e_css['library'] = array(); 984 $registry = &$this->_e_css['library']; 985 $runtime = true; 986 break; 987 988 case 'inline_css': // no zones, TODO - media? 989 $this->_e_css_src[] = $file_path; 990 return $this; 991 break; 992 break; 993 994 995 case 'header': 996 $file_path = $tp->createConstants($file_path, 'mix').$this->_sep.$pre.$this->_sep.$post; 997 $zone = intval($runtime_location); 998 if($zone > 5 || $zone < 1) 999 { 1000 $zone = 5; 1001 } 1002 if(!isset($this->_runtime_header[$zone])) 1003 { 1004 $this->_runtime_header[$zone] = array(); 1005 } 1006 $registry = &$this->_runtime_header[$zone]; 1007 $runtime = true; 1008 break; 1009 1010 case 'footer': 1011 $file_path = $tp->createConstants($file_path, 'mix').$this->_sep.$pre.$this->_sep.$post; 1012 $zone = intval($runtime_location); 1013 if($zone > 5 || $zone < 1) 1014 { 1015 $zone = 5; 1016 } 1017 if(!isset($this->_runtime_footer[$zone])) 1018 { 1019 $this->_runtime_footer[$zone] = array(); 1020 } 1021 $registry = &$this->_runtime_footer[$zone]; 1022 $runtime = true; 1023 break; 1024 1025 case 'header_inline': 1026 $zone = intval($runtime_location); 1027 if($zone > 5 || $zone < 1) 1028 { 1029 $zone = 5; 1030 } 1031 $this->_runtime_header_src[$zone][] = $file_path; 1032 return $this; 1033 break; 1034 break; 1035 1036 case 'footer_inline': 1037 $zone = intval($runtime_location); 1038 if($zone > 5 || $zone < 1) 1039 { 1040 $zone = 5; 1041 } 1042 $this->_runtime_footer_src[$zone][] = $file_path; 1043 return $this; 1044 break; 1045 1046 case 'settings': 1047 $this->_e_js_settings = $this->arrayMergeDeepArray(array($this->_e_js_settings, $file_path)); 1048 return $this; 1049 break; 1050 1051 default: 1052 return $this; 1053 break; 1054 } 1055 1056 if(in_array($file_path, $this->_index_all) || (!$runtime && $runtime_location != 'all' && $runtime_location != $this->getCurrentLocation())) 1057 { 1058 return $this; 1059 } 1060 1061 $this->_index_all[] = $file_path; 1062 $registry[] = $file_path; 1063 1064 return $this; 1065 } 1066 1067 /** 1068 * Render registered JS 1069 * 1070 * @param string $mod core|plugin|theme|header|footer|header_inline|footer_inline|core_css|plugin_css|theme_css|other_css|inline_css 1071 * @param integer $zone 1-5 - only used when in 'header','footer','header_inline' and 'footer_inline' render mod 1072 * @param boolean $external external file calls, only used when NOT in 'header_inline' and 'footer_inline' render mod 1073 * @param boolean $return 1074 * @return string JS content - only if $return is true 1075 */ 1076 public function renderJs($mod, $zone = null, $external = true, $return = false) 1077 { 1078 if($return) 1079 { 1080 ob_start(); 1081 } 1082 1083 switch($mod) 1084 { 1085 case 'settings': 1086 $tp = e107::getParser(); 1087 $json = $tp->toJSON($this->_e_js_settings); 1088 echo "<script>\n"; 1089 echo "var e107 = e107 || {'settings': {}, 'behaviors': {}};\n"; 1090 echo "jQuery.extend(e107.settings, " . $json . ");\n"; 1091 echo "</script>\n"; 1092 break; 1093 1094 case 'framework': // CDN frameworks - rendered before consolidation script (if enabled) 1095 $fw = array(); 1096 foreach ($this->_libraries as $lib) 1097 { 1098 foreach ($lib as $path) 1099 { 1100 $erase = array_search($path, $this->_e_jslib_core); 1101 if($erase !== false && strpos($path, 'http') === 0) 1102 { 1103 unset($this->_e_jslib_core[$erase]); 1104 $fw[] = $path; 1105 } 1106 } 1107 } 1108 $this->renderFile($fw, $external, 'CDN Framework', $mod, false); 1109 break; 1110 1111 case 'core': //e_jslib 1112 $this->setLastModfied($mod, $this->renderFile($this->_e_jslib_core, $external, 'Core libraries', $mod)); 1113 $this->_e_jslib_core = array(); 1114 break; 1115 1116 case 'plugin': //e_jslib 1117 /*foreach($this->_e_jslib_plugin as $plugname => $paths) 1118 { 1119 $this->setLastModfied($mod, $this->renderFile($paths, $external, $plugname.' libraries')); 1120 }*/ 1121 $this->setLastModfied($mod, $this->renderFile($this->_e_jslib_plugin, $external, 'Plugin libraries', $mod)); 1122 $this->_e_jslib_plugin = array(); 1123 break; 1124 1125 case 'theme': //e_jslib 1126 $this->setLastModfied($mod, $this->renderFile($this->_e_jslib_theme, $external, 'Theme libraries', $mod)); 1127 $this->_e_jslib_theme = array(); 1128 break; 1129 1130 case 'header': 1131 $this->renderFile(vartrue($this->_runtime_header[$zone], array()), $external, 'Header JS include - zone #'.$zone, $mod); 1132 unset($this->_runtime_header[$zone]); 1133 break; 1134 1135 case 'core_css': //e_jslib 1136 $this->renderFile(varset($this->_e_css['core'], array()), $external, 'Core CSS', $mod, false); 1137 unset($this->_e_css['core']); 1138 break; 1139 1140 case 'plugin_css': //e_jslib 1141 $this->renderFile(varset($this->_e_css['plugin'], array()), $external, 'Plugin CSS', $mod, false); 1142 unset($this->_e_css['plugin']); 1143 break; 1144 1145 case 'theme_css': //e_jslib 1146 $this->renderFile(varset($this->_e_css['theme'], array()), $external, 'Theme CSS', $mod, false); 1147 unset($this->_e_css['theme']); 1148 break; 1149 1150 case 'other_css': 1151 $this->renderFile(varset($this->_e_css['other'], array()), $external, 'Other CSS', $mod, false); 1152 unset($this->_e_css['other']); 1153 break; 1154 1155 case 'library_css': 1156 $this->renderFile(varset($this->_e_css['library'], array()), $external, 'Library CSS', $mod, false); 1157 unset($this->_e_css['library']); 1158 break; 1159 1160 case 'inline_css': 1161 $this->renderInline($this->_e_css_src, 'Inline CSS', 'css'); 1162 $this->_e_css_src = array(); 1163 break; 1164 1165 1166 case 'footer': 1167 if(true === $zone) 1168 { 1169 ksort($this->_runtime_footer, SORT_NUMERIC); 1170 foreach ($this->_runtime_footer as $priority => $path_array) 1171 { 1172 $this->renderFile($path_array, $external, 'Footer JS include - priority #'.$priority, $mod); 1173 } 1174 $this->_runtime_footer = array(); 1175 } 1176 else 1177 { 1178 $this->renderFile(vartrue($this->_runtime_footer[$zone], array()), $external, 'Footer JS include - priority #'.$zone, $mod); 1179 unset($this->_runtime_footer[$zone]); 1180 } 1181 break; 1182 1183 case 'header_inline': 1184 $this->renderInline(vartrue($this->_runtime_header_src[$zone], array()), 'Header JS - zone #'.$zone); 1185 unset($this->_runtime_header_src[$zone]); 1186 break; 1187 1188 case 'footer_inline': 1189 if(true === $zone) 1190 { 1191 ksort($this->_runtime_footer_src, SORT_NUMERIC); 1192 foreach ($this->_runtime_footer_src as $priority => $src_array) 1193 { 1194 $this->renderInline($src_array, 'Footer JS - priority #'.$priority); 1195 } 1196 $this->_runtime_footer_src = array(); 1197 } 1198 else 1199 { 1200 $this->renderInline(vartrue($this->_runtime_footer_src[$zone], array()), 'Footer JS - priority #'.$zone); 1201 unset($this->_runtime_footer_src[$zone]); 1202 } 1203 break; 1204 } 1205 1206 if($return) 1207 { 1208 $ret = ob_get_contents(); 1209 ob_end_clean(); 1210 return $ret; 1211 } 1212 } 1213 1214 1215 /** 1216 * Merges multiple arrays, recursively, and returns the merged array. 1217 */ 1218 public function arrayMergeDeepArray($arrays) { 1219 $result = array(); 1220 1221 foreach ($arrays as $array) { 1222 foreach ($array as $key => $value) { 1223 // Renumber integer keys as array_merge_recursive() does. Note that PHP 1224 // automatically converts array keys that are integer strings (e.g., '1') 1225 // to integers. 1226 if (is_integer($key)) { 1227 $result[] = $value; 1228 } 1229 // Recurse when both values are arrays. 1230 elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) { 1231 $result[$key] = $this->arrayMergeDeepArray(array($result[$key], $value)); 1232 } 1233 // Otherwise, use the latter value, overriding any previous value. 1234 else { 1235 $result[$key] = $value; 1236 } 1237 } 1238 } 1239 1240 return $result; 1241 } 1242 1243 1244 /** 1245 * Render JS/CSS file array 1246 * 1247 * @param array $file_path_array 1248 * @param string|boolean $external if true - external js file calls, if js|css - external js|css file calls, else output file contents 1249 * @param string $label added as comment if non-empty 1250 * @return void 1251 */ 1252 public function renderFile($file_path_array, $external = false, $label = '', $mod = null, $checkModified = true) 1253 { 1254 if(empty($file_path_array)) 1255 { 1256 return ''; 1257 } 1258 1259 1260 $tp = e107::getParser(); 1261 echo "\n"; 1262 if($label && E107_DEBUG_LEVEL > 0) 1263 { 1264 echo $external ? "<!-- [JSManager] ".$label." -->\n" : "/* [JSManager] ".$label." */\n\n"; 1265 e107::getDebug()->logTime("Load JS/CSS: ".$label); 1266 } 1267 1268 1269 1270 1271 $lmodified = 0; 1272 foreach ($file_path_array as $path) 1273 { 1274 if (substr($path, - 4) == '.php') 1275 { 1276 if('css' === $external) 1277 { 1278 $path = explode($this->_sep, $path, 4); 1279 $media = $path[0] ? $path[0] : 'all'; 1280 // support of IE checks 1281 $pre = varset($path[2]) ? $path[2]."\n" : ''; 1282 $post = varset($path[3]) ? "\n".$path[3] : ''; 1283 $path = $path[1]; 1284 if(strpos($path, 'http') !== 0) 1285 { 1286 $path = $tp->replaceConstants($path, 'abs').'?external=1'; // &'.$this->getCacheId(); 1287 $path = $this->url($path); 1288 } 1289 1290 echo $pre.'<link rel="stylesheet" media="'.$media.'" type="text/css" href="'.$path.'" />'.$post; 1291 echo "\n"; 1292 // $this->cacheList['css'][] = $path; 1293 continue; 1294 } 1295 elseif($external) //true or 'js' 1296 { 1297 if(strpos($path, 'http') === 0 || strpos($path, '//') === 0) continue; // not allowed 1298 1299 $path = explode($this->_sep, $path, 3); 1300 $pre = varset($path[1], ''); 1301 if($pre) $pre .= "\n"; 1302 $post = varset($path[2], ''); 1303 if($post) $post = "\n".$post; 1304 $path = $path[0]; 1305 1306 $path = $tp->replaceConstants($path, 'abs').'?external=1'; // &'.$this->getCacheId(); 1307 $path = $this->url($path); 1308 echo $pre.'<script type="text/javascript" src="'.$path.'"></script>'.$post; 1309 echo "\n"; 1310 continue; 1311 } 1312 1313 $path = $tp->replaceConstants($path, ''); 1314 if($checkModified) $lmodified = max($lmodified, filemtime($path)); 1315 include_once($path); 1316 echo "\n"; 1317 } 1318 else 1319 { 1320 // CDN fix, ignore URLs inside consolidation script, render as external scripts 1321 $isExternal = false; 1322 if(strpos($path, 'http') === 0 || strpos($path, '//') === 0) 1323 { 1324 if($external !== 'css') $isExternal = true; 1325 } 1326 1327 1328 1329 1330 1331 1332 if('css' === $external) 1333 { 1334 $path = explode($this->_sep, $path, 4); 1335 1336 1337 1338 $media = $path[0]; 1339 // support of IE checks 1340 $pre = varset($path[2]) ? $path[2]."\n" : ''; 1341 $post = varset($path[3]) ? "\n".$path[3] : ''; 1342 $path = $path[1]; 1343 1344 $insertID =''; 1345 1346 // issue #3390 Fix for protocol-less path 1347 if(strpos($path, 'http') !== 0 && strpos($path, '//') !== 0) // local file. 1348 { 1349 1350 if($label === 'Theme CSS') // add an id for local theme stylesheets. 1351 { 1352 $insertID = 'id="stylesheet-'. eHelper::secureIdAttr(str_replace(array('{e_THEME}','.css'),'',$path)).'"' ; 1353 } 1354 1355 if($this->addCache($external,$path) === true) // if cache enabled, then skip and continue. 1356 { 1357 continue; 1358 } 1359 $path = $tp->replaceConstants($path, 'abs'); // .'?'.$this->getCacheId(); 1360 $path = $this->url($path); 1361 } 1362 elseif($this->isValidUrl($path) === false) 1363 { 1364 continue; 1365 } 1366 1367 1368 echo $pre.'<link '.$insertID.' rel="stylesheet" media="'.$media.'" property="stylesheet" type="text/css" href="'.$path.'" />'.$post; 1369 echo "\n"; 1370 1371 continue; 1372 } 1373 1374 $path = explode($this->_sep, $path, 4); 1375 $pre = varset($path[1], ''); 1376 if($pre) $pre .= "\n"; 1377 $post = varset($path[2], ''); 1378 if($post) $post = "\n".$post; 1379 $inline = isset($path[3]) ? $path[3] : ''; 1380 if($inline) $inline = " ".$inline; 1381 $path = $path[0]; 1382 1383 if(!$isExternal && $this->addCache('js',$path)===true) 1384 { 1385 continue; 1386 } 1387 1388 1389 if($external) 1390 { 1391 // Never use CacheID on a CDN script, always render if it's CDN 1392 if(!$isExternal) 1393 { 1394 // don't render non CDN libs as external script calls when script consolidation is enabled 1395 if($mod === 'core' || $mod === 'plugin' || $mod === 'theme') 1396 { 1397 if(!e107::getPref('e_jslib_nocombine')) continue; 1398 } 1399 1400 $path = $tp->replaceConstants($path, 'abs'); //.'?'.$this->getCacheId(); 1401 $path = $this->url($path, 'js'); 1402 } 1403 1404 if($isExternal === true && $this->isValidUrl($path) == false) 1405 { 1406 continue; 1407 } 1408 1409 echo $pre.'<script type="text/javascript" src="'.$path.'"'.$inline.'></script>'.$post; 1410 echo "\n"; 1411 continue; 1412 } 1413 1414 1415 1416 1417 1418 // never try to consolidate external scripts 1419 if($isExternal) continue; 1420 $path = $tp->replaceConstants($path, ''); 1421 if($checkModified) $lmodified = max($lmodified, filemtime($path)); 1422 echo file_get_contents($path); 1423 echo "\n"; 1424 } 1425 } 1426 1427 1428 return $lmodified; 1429 } 1430 1431 1432 /** 1433 * Return the URL while checking for staticUrl configuration. 1434 * @param $path 1435 * @param bool $cacheId 1436 * @return mixed|string 1437 */ 1438 private function url($path, $cacheId = true) 1439 { 1440 1441 if(/*(e_MOD_REWRITE_STATIC === true || defined('e_HTTP_STATIC')) &&*/ $this->isInAdmin() !== true) 1442 { 1443 1444 /* $srch = array( 1445 e_PLUGIN_ABS, 1446 e_THEME_ABS, 1447 e_WEB_ABS 1448 ); 1449 1450 1451 $http = deftrue('e_HTTP_STATIC', e_HTTP); 1452 1453 $base = (e_MOD_REWRITE_STATIC === true) ? 'static/'.$this->getCacheId().'/' : ''; 1454 1455 $repl = array( 1456 $http.$base.e107::getFolder('plugins'), 1457 $http.$base.e107::getFolder('themes'), 1458 $http.$base.e107::getFolder('web') 1459 ); 1460 1461 $folder = str_replace($srch,$repl,$path); 1462 1463 if(e_MOD_REWRITE_STATIC === true) 1464 { 1465 return trim($folder); 1466 } 1467 1468 $path = $folder;*/ 1469 1470 $path = e107::getParser()->staticUrl($path); 1471 } 1472 1473 1474 if(!defined('e_HTTP_STATIC')) 1475 { 1476 if(strpos($path,'?')!==false) 1477 { 1478 $path .= "&".$this->getCacheId(); 1479 } 1480 else 1481 { 1482 $path .= "?".$this->getCacheId(); 1483 } 1484 } 1485 1486 return $path; 1487 1488 } 1489 1490 1491 1492 /** 1493 * Check CDN Url is valid. 1494 * Experimental. 1495 * @param $url 1496 * @return resource 1497 */ 1498 private function isValidUrl($url) 1499 { 1500 return true; 1501 /* 1502 1503 1504 $connected = e107::getFile()->isValidURL($url); 1505 1506 if($connected == false) 1507 { 1508 // echo "<br />Skipping: ".$url ." : ".$port; 1509 e107::getDebug()->log("Couldn't reach ".$url); 1510 } 1511 1512 return $connected; 1513 */ 1514 } 1515 1516 1517 /** 1518 * @param $type string css|js 1519 * @param $path 1520 * @return bool 1521 */ 1522 private function addCache($type,$path) 1523 { 1524 if($this->_cache_enabled != true || $this->isInAdmin() || substr($path,0,2) == '//' || strpos($path, 'wysiwyg.php')!==false ) 1525 { 1526 return false; 1527 } 1528 1529 if(e_REQUEST_HTTP == e_ADMIN_ABS."menus.php") // disabled in menu-manager. 1530 { 1531 return false; 1532 } 1533 1534 1535 $localPath = e107::getParser()->replaceConstants($path); 1536 $this->_cache_list[$type][] = $localPath; 1537 1538 return true; 1539 } 1540 1541 1542 1543 1544 /** 1545 * Render Cached JS or CSS file. 1546 * @param $type 1547 */ 1548 public function renderCached($type) 1549 { 1550 if($this->_cache_enabled != true || $this->isInAdmin() || deftrue('e_MENUMANAGER_ACTIVE')) 1551 { 1552 return false; 1553 } 1554 1555 if(!empty($this->_cache_list[$type])) 1556 { 1557 $content = ''; 1558 $cacheId = $this->getCacheFileId($this->_cache_list[$type]); 1559 1560 $fileName = $cacheId.".".$type; 1561 $saveFilePath = e_WEB.'cache/'.$fileName; 1562 1563 if(!is_readable($saveFilePath)) 1564 { 1565 1566 foreach($this->_cache_list[$type] as $k=>$path) 1567 { 1568 $content .= "/* File: ".str_replace("../",'',$path)." */\n"; 1569 $content .= $this->getCacheFileContent($path, $type); 1570 $content .= "\n\n"; 1571 } 1572 1573 if(!@file_put_contents($saveFilePath, $content)) 1574 { 1575 e107::getMessage()->addDebug("Couldn't save js/css cache file: ".$saveFilePath); 1576 } 1577 1578 } 1579 1580 echo "\n\n<!-- Cached ".$type." -->\n"; 1581 1582 if($type == 'js') 1583 { 1584 echo "<script type='text/javascript' src='".$this->url(e_WEB_ABS."cache/".$fileName,'js','cache')."'></script>\n\n"; 1585 } 1586 else 1587 { 1588 echo "<link type='text/css' href='".$this->url(e_WEB_ABS."cache/".$fileName,'cache')."' rel='stylesheet' property='stylesheet' />\n\n"; 1589 if(!empty($this->_cache_list['css_inline'])) 1590 { 1591 echo $this->_cache_list['css_inline']; 1592 unset($this->_cache_list['css_inline']); 1593 } 1594 } 1595 1596 // Remove from list, anything we have added. 1597 foreach($this->_cache_list[$type] as $k=>$path) 1598 { 1599 unset($this->_cache_list[$type][$k]); 1600 } 1601 1602 1603 } 1604 1605 1606 1607 } 1608 1609 1610 /** 1611 * Get js/css file to be cached and update url links. 1612 * @param $path string 1613 * @param $type string (js|css) 1614 * @return mixed|string 1615 */ 1616 private function getCacheFileContent($path, $type) 1617 { 1618 $content = @file_get_contents($path); 1619 1620 if($type == 'js') 1621 { 1622 return $this->compress($content, 'js'); 1623 } 1624 1625 // Correct relative paths in css files. 1626 preg_match_all('/url\([\'"]?([^\'"\) ]*)[\'"]?\)/',$content, $match); 1627 $newpath = array(); 1628 1629 if(empty($match[0])) 1630 { 1631 return $this->compress($content, 'css'); 1632 } 1633 1634 $path = str_replace("../",'',$path); 1635 1636 $basePath = dirname($path)."/"; 1637 1638 $tp = e107::getParser(); 1639 1640 foreach($match[1] as $k=>$v) 1641 { 1642 if(strpos($v,'data:') === 0 || strpos($v,'http') === 0) 1643 { 1644 unset($match[0][$k]); 1645 continue; 1646 } 1647 1648 $http = $tp->staticUrl(null, array('full'=>1)); // returns SITEURL or Static URL if enabled. 1649 $path = $this->normalizePath($basePath.$v); 1650 $dir = "url(".$http.$path.")"; 1651 1652 $newpath[$k] = $dir; 1653 } 1654 1655 $result = str_replace($match[0], $newpath, $content); 1656 1657 return $this->compress($result, 'css'); 1658 } 1659 1660 1661 /** 1662 * Normalize a path. 1663 * Replacement for realpath (move to core functions?) 1664 * It will _only_ normalize the path and resolve indirections (.. and .) 1665 * Normalization includes: 1666 * - directiory separator is always / 1667 * - there is never a trailing directory separator 1668 * @param $path 1669 * @return String 1670 */ 1671 private function normalizePath($path) 1672 { 1673 $parts = preg_split(":[\\\/]:", $path); // split on known directory separators 1674 1675 // resolve relative paths 1676 for ($i = 0; $i < count($parts); $i +=1) 1677 { 1678 if ($parts[$i] === "..") // resolve .. 1679 { 1680 if ($i === 0) 1681 { 1682 throw new Exception("Cannot resolve path, path seems invalid: `" . $path . "`"); 1683 } 1684 1685 unset($parts[$i - 1]); 1686 unset($parts[$i]); 1687 $parts = array_values($parts); 1688 $i -= 2; 1689 } 1690 elseif ($parts[$i] === ".") // resolve . 1691 { 1692 unset($parts[$i]); 1693 $parts = array_values($parts); 1694 $i -= 1; 1695 } 1696 1697 if ($i > 0 && $parts[$i] === "") // remove empty parts 1698 { 1699 unset($parts[$i]); 1700 $parts = array_values($parts); 1701 } 1702 } 1703 1704 return implode("/", $parts); 1705 } 1706 1707 1708 1709 /** 1710 * Minify JS/CSS for output 1711 * @param string $minify 1712 * @param string $type (js|css) 1713 * @return string 1714 */ 1715 private function compress($minify, $type = 'js' ) 1716 { 1717 1718 if($type == 'js') 1719 { 1720 return e107::minify($minify); 1721 } 1722 1723 // css 1724 1725 /* remove comments */ 1726 $minify = preg_replace( '!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $minify ); 1727 1728 /* remove tabs, spaces, newlines, etc. */ 1729 $minify = str_replace( array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $minify ); 1730 $minify = str_replace("}","} ",$minify); 1731 1732 return $minify; 1733 } 1734 1735 1736 /** 1737 * Generate a Cache-File ID from path list. 1738 * @param array $paths 1739 * @return string 1740 */ 1741 private function getCacheFileId($paths) 1742 { 1743 $id = ''; 1744 foreach($paths as $p) 1745 { 1746 $id .= str_replace("../","",$p); 1747 } 1748 1749 return hash('crc32', $id) ; 1750 1751 } 1752 1753 /** 1754 * Render JS/CSS source array 1755 * 1756 * @param array $js_content_array 1757 * @param string $label added as comment if non-empty 1758 * @return void 1759 */ 1760 function renderInline($content_array, $label = '', $type = 'js') 1761 { 1762 if(empty($content_array)) 1763 { 1764 return ''; 1765 } 1766 1767 $content_array = array_unique($content_array); //TODO quick fix, we need better control! 1768 echo "\n"; 1769 1770 $raw = array(); 1771 1772 if($type == 'js') // support for raw html as inline code. (eg. google/bing/yahoo analytics) 1773 { 1774 $script = array(); 1775 foreach($content_array as $code) 1776 { 1777 $start = substr($code,0,7); 1778 if($start == '<script' || $start == '<iframe') 1779 { 1780 $raw[] = $code; 1781 } 1782 else 1783 { 1784 $script[] = $code; 1785 } 1786 } 1787 1788 $content_array = $script; 1789 } 1790 1791 1792 switch ($type) 1793 { 1794 case 'js': 1795 if($label && E107_DEBUG_LEVEL > 0) 1796 { 1797 echo "<!-- [JSManager] ".$label." -->\n"; 1798 } 1799 echo '<script type="text/javascript">'; 1800 echo "\n//<![CDATA[\n"; 1801 echo implode("\n\n", $content_array); 1802 echo "\n//]]>\n"; 1803 echo '</script>'; 1804 echo "\n"; 1805 1806 if(!empty($raw)) 1807 { 1808 if($label) //TODO - print comments only if site debug is on 1809 { 1810 echo "\n<!-- [JSManager] (Raw) ".$label." -->\n"; 1811 } 1812 echo implode("\n\n", $raw); 1813 echo "\n\n"; 1814 } 1815 break; 1816 1817 case 'css': 1818 $text = ''; 1819 if($label && E107_DEBUG_LEVEL > 0) 1820 { 1821 $text .= "<!-- [CSSManager] ".$label." -->\n"; 1822 } 1823 $text .= '<style rel="stylesheet" type="text/css" property="stylesheet">'; 1824 $text .= implode("\n\n", $content_array); 1825 $text .= '</style>'; 1826 $text .= "\n"; 1827 1828 if($this->_cache_enabled != true || $this->isInAdmin() || deftrue('e_MENUMANAGER_ACTIVE')) 1829 { 1830 echo $text; 1831 } 1832 else 1833 { 1834 $this->_cache_list['css_inline'] = $text; 1835 } 1836 1837 1838 break; 1839 } 1840 } 1841 1842 /** 1843 * Returns true if currently running in 1844 * administration area. 1845 * 1846 * @return boolean 1847 */ 1848 public function isInAdmin() 1849 { 1850 return $this->_in_admin; 1851 } 1852 1853 /** 1854 * Set current script location 1855 * 1856 * @param object $is true - back-end, false - front-end 1857 * @return e_jsmanager 1858 */ 1859 public function setInAdmin($is) 1860 { 1861 $this->_in_admin = (boolean) $is; 1862 return $this; 1863 } 1864 1865 /** 1866 * Get current location as a string (admin|front) 1867 * 1868 * @return string 1869 */ 1870 public function getCurrentLocation() 1871 { 1872 return ($this->isInAdmin() ? 'admin' : 'front'); 1873 } 1874 1875 /** 1876 * Get current theme name 1877 * 1878 * @return string 1879 */ 1880 public function getCurrentTheme() 1881 { 1882 // XXX - USERTHEME is defined only on user session init 1883 return ($this->isInAdmin() ? e107::getPref('admintheme') : deftrue('USERTHEME', e107::getPref('sitetheme'))); 1884 } 1885 1886 /** 1887 * Get browser cache id 1888 * 1889 * @return integer 1890 */ 1891 public function getCacheId() 1892 { 1893 return $this->_browser_cache_id; 1894 } 1895 1896 /** 1897 * Set browser cache id 1898 * 1899 * @return e_jsmanager 1900 */ 1901 public function setCacheId($cacheid) 1902 { 1903 $this->_browser_cache_id = intval($cacheid); 1904 return $this; 1905 } 1906 1907 /** 1908 * Set last modification timestamp for given namespace 1909 * 1910 * @param string $what 1911 * @param integer $when [optional] 1912 * @return e_jsmanager 1913 */ 1914 public function setLastModfied($what, $when = 0) 1915 { 1916 $this->_lastModified[$what] = $when; 1917 return $this; 1918 } 1919 1920 /** 1921 * Get last modification timestamp for given namespace 1922 * 1923 * @param string $what 1924 * @return integer 1925 */ 1926 public function getLastModfied($what) 1927 { 1928 return (isset($this->_lastModified[$what]) ? $this->_lastModified[$what] : 0); 1929 } 1930 1931 public function addLibPref($mod, $array_newlib) 1932 { 1933 1934 if(!$array_newlib || !is_array($array_newlib)) 1935 { 1936 return $this; 1937 } 1938 $core = e107::getConfig(); 1939 $plugname = ''; 1940 if(strpos($mod, 'plugin:') === 0) 1941 { 1942 $plugname = str_replace('plugin:', '', $mod); 1943 $mod = 'plugin'; 1944 } 1945 1946 switch($mod) 1947 { 1948 case 'core': 1949 case 'theme': 1950 $key = 'e_jslib_'.$mod; 1951 break; 1952 1953 case 'plugin': 1954 $key = 'e_jslib_plugin/'.$plugname; 1955 break; 1956 1957 default: 1958 return $this; 1959 break; 1960 } 1961 1962 1963 $libs = $core->getPref($key); 1964 if(!$libs) $libs = array(); 1965 foreach ($array_newlib as $path => $location) 1966 { 1967 $path = trim($path, '/'); 1968 1969 if(!$path) continue; 1970 1971 $newlocation = $location == 'all' || (varset($libs[$path]) && $libs[$path] != $location) ? 'all' : $location; 1972 $libs[$path] = $newlocation; 1973 } 1974 1975 $core->setPref($key, $libs); 1976 return $this; 1977 } 1978 1979 public function removeLibPref($mod, $array_removelib) 1980 { 1981 1982 if(!$array_removelib || !is_array($array_removelib)) 1983 { 1984 return $this; 1985 } 1986 $core = e107::getConfig(); 1987 $plugname = ''; 1988 if(strpos($mod, 'plugin:') === 0) 1989 { 1990 $plugname = str_replace('plugin:', '', $mod); 1991 $mod = 'plugin'; 1992 } 1993 1994 switch($mod) 1995 { 1996 case 'core': 1997 case 'theme': 1998 $key = 'e_jslib_'.$mod; 1999 break; 2000 2001 case 'plugin': 2002 $key = 'e_jslib_plugin/'.$plugname; 2003 break; 2004 2005 default: 2006 return $this; 2007 break; 2008 } 2009 2010 2011 $libs = $core->getPref($key); 2012 if(!$libs) $libs = array(); 2013 foreach ($array_removelib as $path => $location) 2014 { 2015 $path = trim($path, '/'); 2016 if(!$path) continue; 2017 2018 unset($libs[$path]); 2019 } 2020 2021 $core->setPref($key, $libs); 2022 return $this; 2023 } 2024 2025 /** 2026 * Get current object data 2027 * @return array 2028 */ 2029 public function getData() 2030 { 2031 $data = get_class_vars(__CLASS__); 2032 unset($data['_instance'], $data['_in_admin']); 2033 $kdata = array_keys($data); 2034 $instance = self::getInstance(); 2035 $data = array(); 2036 foreach ($kdata as $prop) 2037 { 2038 $data[$prop] = $this->$prop; 2039 } 2040 return $data; 2041 } 2042 2043 /** 2044 * Set all current object data 2045 * @param $data 2046 * @return e_jsmanager 2047 */ 2048 public function setData($data) 2049 { 2050 if(!is_array($data)) return $this; 2051 foreach ($data as $prop => $val) 2052 { 2053 if('_instance' == $prop || '_in_admin' == $prop) continue; 2054 $this->$prop = $val; 2055 } 2056 return $this; 2057 } 2058 2059} 2060