PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/include/loader.php

https://github.com/karpenoktem/punbb
PHP | 532 lines | 362 code | 111 blank | 59 comment | 76 complexity | b3ff42566059cc7686a413ce05a1e235 MD5 | raw file
  1. <?php
  2. /**
  3. * Loader class for inject CSS and JS files.
  4. *
  5. * @copyright (C) 2008-2012 PunBB, partially based on Drupal code
  6. * @license http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  7. * @package PunBB
  8. */
  9. // JS groups
  10. define('FORUM_JS_GROUP_SYSTEM', -100);
  11. define('FORUM_JS_GROUP_DEFAULT', 0);
  12. define('FORUM_JS_GROUP_COUNTER', 100);
  13. // CSS groups
  14. define('FORUM_CSS_GROUP_SYSTEM', -100);
  15. define('FORUM_CSS_GROUP_DEFAULT', 0);
  16. class Loader
  17. {
  18. private $libs;
  19. // Class instance
  20. private static $instance;
  21. // Start of life
  22. private function __construct() {
  23. $this->libs = array();
  24. $this->libs['js'] = array();
  25. $this->libs['css'] = array();
  26. }
  27. // The end
  28. public function __destruct() {
  29. unset($this->libs);
  30. }
  31. // Singleton
  32. public static function singleton() {
  33. if (!isset(self::$instance)) {
  34. $c = __CLASS__;
  35. self::$instance = new $c;
  36. }
  37. return self::$instance;
  38. }
  39. // Clone forbiden
  40. public function __clone() {
  41. trigger_error('Clone is forbiden.', E_USER_ERROR);
  42. }
  43. // Add JS url to load
  44. public function add_js($data = NULL, $options = NULL)
  45. {
  46. $return = ($hook = get_hook('ld_fn_add_js_start')) ? eval($hook) : null;
  47. if ($return != null)
  48. return $return;
  49. if (is_null($options) || !is_array($options))
  50. {
  51. $options = array();
  52. }
  53. // Default options
  54. $default_options = array(
  55. // url, inline
  56. 'type' => array(
  57. 'default' => 'url',
  58. ),
  59. //
  60. 'async' => array(
  61. 'default' => false,
  62. ),
  63. //
  64. 'weight' => array(
  65. 'default' => 100,
  66. ),
  67. //
  68. 'group' => array(
  69. 'default' => FORUM_JS_GROUP_DEFAULT,
  70. ),
  71. //
  72. 'every_page' => array(
  73. 'default' => false,
  74. ),
  75. //
  76. 'defer' => array(
  77. 'default' => false,
  78. ),
  79. //
  80. 'preprocess' => array(
  81. 'default' => true,
  82. )
  83. );
  84. $length = count($default_options);
  85. $keys = array_keys($default_options);
  86. for ($i = 0; $i < $length; $i++)
  87. {
  88. $key = $keys[$i];
  89. if (!isset($options[$key]))
  90. {
  91. $default_options[$keys[$i]] = $default_options[$keys[$i]]['default'];
  92. continue;
  93. }
  94. $default_options[$keys[$i]] = $options[$key];
  95. }
  96. // Check data — url or inline code
  97. $default_options['data'] = forum_trim($data);
  98. if (empty($default_options['data']) || utf8_strlen($default_options['data']) < 1)
  99. {
  100. return FALSE;
  101. }
  102. // Tweak weight
  103. $default_options['weight'] += count($this->libs['js']) / 1000;
  104. ($hook = get_hook('ld_fn_add_js_pre_merge')) ? eval($hook) : null;
  105. // Add to libs
  106. if ($default_options['type'] != 'inline')
  107. {
  108. $this->libs['js'][$default_options['data']] = $default_options;
  109. }
  110. else
  111. {
  112. $this->libs['js'][] = $default_options;
  113. }
  114. ($hook = get_hook('ld_fn_add_js_end')) ? eval($hook) : null;
  115. return $this->libs['js'];
  116. }
  117. //
  118. public function render_js()
  119. {
  120. $output = '';
  121. $return = ($hook = get_hook('ld_fn_render_js_start')) ? eval($hook) : null;
  122. if ($return != null)
  123. return $return;
  124. if (empty($this->libs['js']))
  125. return $output;
  126. // Sorts the scripts into correct order
  127. uasort($this->libs['js'], array('Loader', 'sort_libs'));
  128. if (defined('FORUM_DISABLE_ASYNC_JS_LOADER'))
  129. {
  130. return $this->render_js_simple();
  131. }
  132. return $this->render_js_labjs();
  133. }
  134. // Add CSS url to load
  135. public function add_css($data = NULL, $options = NULL)
  136. {
  137. $return = ($hook = get_hook('ld_fn_add_css_start')) ? eval($hook) : null;
  138. if ($return != null)
  139. return $return;
  140. if (is_null($options) || !is_array($options))
  141. {
  142. $options = array();
  143. }
  144. // Default options
  145. $default_options = array(
  146. //
  147. 'type' => array(
  148. 'default' => 'url',
  149. ),
  150. //
  151. 'weight' => array(
  152. 'default' => 100,
  153. ),
  154. //
  155. 'group' => array(
  156. 'default' => FORUM_CSS_GROUP_DEFAULT,
  157. ),
  158. // screen, all, print
  159. 'media' => array(
  160. 'default' => 'all',
  161. ),
  162. //
  163. 'every_page' => array(
  164. 'default' => false,
  165. ),
  166. //
  167. 'preprocess' => array(
  168. 'default' => true,
  169. ),
  170. 'browsers' => array(
  171. 'default' => array(),
  172. ),
  173. //
  174. 'noscript' => array(
  175. 'default' => false,
  176. )
  177. );
  178. $length = count($default_options);
  179. $keys = array_keys($default_options);
  180. for ($i = 0; $i < $length; $i++)
  181. {
  182. $key = $keys[$i];
  183. if (!isset($options[$key]))
  184. {
  185. $default_options[$keys[$i]] = $default_options[$keys[$i]]['default'];
  186. continue;
  187. }
  188. $default_options[$keys[$i]] = $options[$key];
  189. }
  190. // Check data — url or inline code
  191. $default_options['data'] = forum_trim($data);
  192. if (empty($default_options['data']) || utf8_strlen($default_options['data']) < 1)
  193. {
  194. return FALSE;
  195. }
  196. // Tweak weight
  197. $default_options['weight'] += count($this->libs['css']) / 1000;
  198. ($hook = get_hook('ld_fn_add_css_pre_merge')) ? eval($hook) : null;
  199. // Add to libs
  200. if ($default_options['type'] != 'inline')
  201. {
  202. $this->libs['css'][$default_options['data']] = $default_options;
  203. }
  204. else
  205. {
  206. $this->libs['css'][] = $default_options;
  207. }
  208. ($hook = get_hook('ld_fn_add_css_end')) ? eval($hook) : null;
  209. return $this->libs['css'];
  210. }
  211. // Render CSS libs
  212. public function render_css()
  213. {
  214. $output = '';
  215. $return = ($hook = get_hook('ld_fn_render_css_start')) ? eval($hook) : null;
  216. if ($return != null)
  217. return $return;
  218. if (empty($this->libs['css']))
  219. return $output;
  220. // Sorts the scripts into correct order
  221. uasort($this->libs['css'], array('Loader', 'sort_libs'));
  222. return $this->render_css_simple();
  223. }
  224. // Render for CSS — use link tags method
  225. private function render_css_simple()
  226. {
  227. $output = '';
  228. $libs = $this->libs['css'];
  229. $return = ($hook = get_hook('ld_fn_render_css_simple_start')) ? eval($hook) : null;
  230. if ($return != null)
  231. return $return;
  232. foreach ($libs as $key => $lib)
  233. {
  234. if ($lib['type'] == 'inline')
  235. {
  236. if ($lib['noscript'] === true)
  237. $output .= forum_trim($this->check_conditional_comments($lib, '<noscript><style>'.$lib['data'].'</style></noscript>'))."\n";
  238. else
  239. $output .= forum_trim($this->check_conditional_comments($lib, '<style>'.$lib['data'].'</style>'))."\n";
  240. unset($libs[$key]);
  241. continue;
  242. }
  243. else if ($lib['type'] == 'url')
  244. {
  245. if ($lib['noscript'] === true)
  246. $output .= forum_trim($this->check_conditional_comments($lib, '<noscript><link rel="stylesheet" type="text/css" media="'.$lib['media'].'" href="'.$lib['data'].'" /></noscript>'))."\n";
  247. else
  248. $output .= forum_trim($this->check_conditional_comments($lib, '<link rel="stylesheet" type="text/css" media="'.$lib['media'].'" href="'.$lib['data'].'" />'))."\n";
  249. unset($libs[$key]);
  250. continue;
  251. }
  252. }
  253. ($hook = get_hook('ld_fn_render_css_simple_end')) ? eval($hook) : null;
  254. return $output;
  255. }
  256. // Render for JS — use default script tags method
  257. private function render_js_simple()
  258. {
  259. $output = '';
  260. $libs = $this->libs['js'];
  261. $return = ($hook = get_hook('ld_fn_render_js_simple_start')) ? eval($hook) : null;
  262. if ($return != null)
  263. return $return;
  264. foreach ($libs as $key => $lib)
  265. {
  266. if ($lib['type'] == 'inline')
  267. {
  268. $output .= '<script>'.$lib['data'].'</script>'."\n";
  269. unset($libs[$key]);
  270. continue;
  271. }
  272. else if ($lib['type'] == 'url')
  273. {
  274. $output .= '<script'.(($lib['async']) ? " async" : "").(($lib['defer']) ? " defer=\"true\"" : "").' src="'.$lib['data'].'"></script>'."\n";
  275. unset($libs[$key]);
  276. continue;
  277. }
  278. }
  279. ($hook = get_hook('ld_fn_render_js_simple_end')) ? eval($hook) : null;
  280. return $output;
  281. }
  282. // Render for JS — use LABjs method
  283. private function render_js_labjs()
  284. {
  285. $output_system = $output_counter = $output_default = '';
  286. $libs = $this->libs['js'];
  287. $return = ($hook = get_hook('ld_fn_render_js_labjs_start')) ? eval($hook) : null;
  288. if ($return != null)
  289. return $return;
  290. foreach ($libs as $key => $lib)
  291. {
  292. if ($lib['data'] === FALSE)
  293. {
  294. continue;
  295. }
  296. if ($lib['type'] == 'inline')
  297. {
  298. if ($lib['group'] == FORUM_JS_GROUP_SYSTEM)
  299. {
  300. $output_system .= '<script>'.$lib['data'].'</script>'."\n";
  301. }
  302. else if ($lib['group'] == FORUM_JS_GROUP_COUNTER)
  303. {
  304. $output_counter .= '<script>'.$lib['data'].'</script>'."\n";
  305. }
  306. else
  307. {
  308. $output_default .= "\n\t".'.wait(function () { '.$lib['data'].' })';
  309. }
  310. unset($libs[$key]);
  311. continue;
  312. }
  313. else if ($lib['type'] == 'url')
  314. {
  315. if ($lib['group'] == FORUM_JS_GROUP_SYSTEM)
  316. {
  317. $output_system .= '<script src="'.$lib['data'].'"'.(($lib['async']) ? " async" : "").(($lib['defer']) ? " defer=\"true\"" : "").'></script>'."\n";
  318. }
  319. else if ($lib['group'] == FORUM_JS_GROUP_COUNTER)
  320. {
  321. $output_counter .= '<script src="'.$lib['data'].'"'.(($lib['async']) ? " async" : "").(($lib['defer']) ? " defer=\"true\"" : "").'></script>'."\n";
  322. }
  323. else
  324. {
  325. $output_default .= "\n\t".'.script("'.$lib['data'].'")'.(($lib['async']) ? "" : ".wait()");
  326. }
  327. unset($libs[$key]);
  328. continue;
  329. }
  330. }
  331. // Wrap default to LABjs parameters
  332. if ($output_default != '')
  333. {
  334. $output_default = '<script>'."\n\t".'$LAB.setOptions({AlwaysPreserveOrder:false})'.$output_default.';'."\n".'</script>';
  335. }
  336. ($hook = get_hook('ld_fn_render_js_labjs_end')) ? eval($hook) : null;
  337. return $output_system.$output_default.$output_counter;
  338. }
  339. // Sort libs
  340. private static function sort_libs($a, $b)
  341. {
  342. $return = ($hook = get_hook('ld_fn_sort_libs_start')) ? eval($hook) : null;
  343. if ($return != null)
  344. return $return;
  345. // 1. Sort by group — system first
  346. if ($a['group'] < $b['group'])
  347. {
  348. return -1;
  349. }
  350. elseif ($a['group'] > $b['group'])
  351. {
  352. return 1;
  353. }
  354. // 2. Within a group, order all infrequently needed, page-specific files after
  355. // common files needed throughout the website. Separating this way allows for
  356. // the aggregate file generated for all of the common files to be reused
  357. // across a site visit without being cut by a page using a less common file.
  358. elseif ($a['every_page'] && !$b['every_page'])
  359. {
  360. return -1;
  361. }
  362. elseif (!$a['every_page'] && $b['every_page'])
  363. {
  364. return 1;
  365. }
  366. // 3. Sort by weight
  367. elseif ($a['weight'] < $b['weight'])
  368. {
  369. return -1;
  370. }
  371. elseif ($a['weight'] > $b['weight'])
  372. {
  373. return 1;
  374. }
  375. else
  376. {
  377. return 0;
  378. }
  379. }
  380. // Helper func for render_* — wrap lib in IE-conditional comments
  381. private function check_conditional_comments($element, $data)
  382. {
  383. $return = ($hook = get_hook('ld_fn_check_conditional_comments_start')) ? eval($hook) : null;
  384. if ($return != null)
  385. return $return;
  386. $browsers = (isset($element['browsers']) && is_array($element['browsers'])) ? $element['browsers'] : array();
  387. $browsers += array('IE' => TRUE, '!IE' => TRUE);
  388. // If rendering in all browsers, no need for conditional comments.
  389. if ($browsers['IE'] === true && $browsers['!IE'])
  390. {
  391. return $data;
  392. }
  393. // Determine the conditional comment expression for Internet Explorer to evaluate.
  394. if ($browsers['IE'] === TRUE)
  395. {
  396. $expression = 'IE';
  397. }
  398. elseif ($browsers['IE'] === FALSE)
  399. {
  400. $expression = '!IE';
  401. }
  402. else
  403. {
  404. $expression = $browsers['IE'];
  405. }
  406. if (!$browsers['!IE'])
  407. {
  408. // "downlevel-hidden".
  409. $data = "\n<!--[if $expression]>".$data."<![endif]-->";
  410. }
  411. else
  412. {
  413. // "downlevel-revealed".
  414. $data = "\n<!--[if $expression]><!-->".$data."<!--<![endif]-->";
  415. }
  416. return $data;
  417. }
  418. }
  419. // Create the loader adapter object
  420. $forum_loader = Loader::singleton();
  421. ?>