PageRenderTime 53ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/classes/Media.php

https://gitlab.com/mtellezgalindo/PrestaShop
PHP | 799 lines | 581 code | 90 blank | 128 comment | 129 complexity | 64801978c54594ff78e91e7c156424cd MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-3.0
  1. <?php
  2. /*
  3. * 2007-2014 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2014 PrestaShop SA
  23. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  24. * International Registered Trademark & Property of PrestaShop SA
  25. */
  26. class MediaCore
  27. {
  28. public static $jquery_ui_dependencies = array(
  29. 'ui.core' => array('fileName' => 'jquery.ui.core.min.js', 'dependencies' => array(), 'theme' => true),
  30. 'ui.widget' => array('fileName' => 'jquery.ui.widget.min.js', 'dependencies' => array(), 'theme' => false),
  31. 'ui.mouse' => array('fileName' => 'jquery.ui.mouse.min.js', 'dependencies' => array('ui.core', 'ui.widget'), 'theme' => false),
  32. 'ui.position' => array('fileName' => 'jquery.ui.position.min.js', 'dependencies' => array(), 'theme' => false),
  33. 'ui.draggable' => array('fileName' => 'jquery.ui.draggable.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse'), 'theme' => false),
  34. 'ui.droppable' => array('fileName' => 'jquery.ui.droppable.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse', 'ui.draggable'), 'theme' => false),
  35. 'ui.resizable' => array('fileName' => 'jquery.ui.resizable.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse'), 'theme' => true),
  36. 'ui.selectable' => array('fileName' => 'jquery.ui.selectable.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse'), 'theme' => true),
  37. 'ui.sortable' => array('fileName' => 'jquery.ui.sortable.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse'), 'theme' => true),
  38. 'ui.autocomplete' => array('fileName' => 'jquery.ui.autocomplete.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.position', 'ui.menu'), 'theme' => true),
  39. 'ui.button' => array('fileName' => 'jquery.ui.button.min.js', 'dependencies' => array('ui.core', 'ui.widget'), 'theme' => true),
  40. 'ui.dialog' => array('fileName' => 'jquery.ui.dialog.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.position','ui.button'), 'theme' => true),
  41. 'ui.menu' => array('fileName' => 'jquery.ui.menu.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.position'), 'theme' => true),
  42. 'ui.slider' => array('fileName' => 'jquery.ui.slider.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse'), 'theme' => true),
  43. 'ui.spinner' => array('fileName' => 'jquery.ui.spinner.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.button'), 'theme' => true),
  44. 'ui.tabs' => array('fileName' => 'jquery.ui.tabs.min.js', 'dependencies' => array('ui.core', 'ui.widget'), 'theme' => true),
  45. 'ui.datepicker' => array('fileName' => 'jquery.ui.datepicker.min.js', 'dependencies' => array('ui.core'), 'theme' => true),
  46. 'ui.progressbar' => array('fileName' => 'jquery.ui.progressbar.min.js', 'dependencies' => array('ui.core', 'ui.widget'), 'theme' => true),
  47. 'ui.tooltip' => array('fileName' => 'jquery.ui.tooltip.min.js', 'dependencies' => array('ui.core', 'ui.widget','ui.position','effects.core'), 'theme' => true),
  48. 'ui.accordion' => array('fileName' => 'jquery.ui.accordion.min.js', 'dependencies' => array('ui.core', 'ui.widget','effects.core'), 'theme' => true),
  49. 'effects.core' => array('fileName' => 'jquery.effects.core.min.js', 'dependencies' => array(), 'theme' => false),
  50. 'effects.blind' => array('fileName' => 'jquery.effects.blind.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  51. 'effects.bounce' => array('fileName' => 'jquery.effects.bounce.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  52. 'effects.clip' => array('fileName' => 'jquery.effects.clip.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  53. 'effects.drop' => array('fileName' => 'jquery.effects.drop.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  54. 'effects.explode' => array('fileName' => 'jquery.effects.explode.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  55. 'effects.fade' => array('fileName' => 'jquery.effects.fade.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  56. 'effects.fold' => array('fileName' => 'jquery.effects.fold.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  57. 'effects.highlight' => array('fileName' => 'jquery.effects.highlight.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  58. 'effects.pulsate' => array('fileName' => 'jquery.effects.pulsate.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  59. 'effects.scale' => array('fileName' => 'jquery.effects.scale.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  60. 'effects.shake' => array('fileName' => 'jquery.effects.shake.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  61. 'effects.slide' => array('fileName' => 'jquery.effects.slide.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
  62. 'effects.transfer' => array('fileName' => 'jquery.effects.transfer.min.js', 'dependencies' => array('effects.core'), 'theme' => false)
  63. );
  64. /**
  65. * @var array list of javascript definitions
  66. */
  67. protected static $js_def = array();
  68. /**
  69. * @var array list of javascript inline scripts
  70. */
  71. protected static $inline_script = array();
  72. /**
  73. * @var array list of javascript external scripts
  74. */
  75. protected static $inline_script_src = array();
  76. public static function minifyHTML($html_content)
  77. {
  78. if (strlen($html_content) > 0)
  79. {
  80. //set an alphabetical order for args
  81. // $html_content = preg_replace_callback(
  82. // '/(<[a-zA-Z0-9]+)((\s*[a-zA-Z0-9]+=[\"\\\'][^\"\\\']*[\"\\\']\s*)*)>/',
  83. // array('Media', 'minifyHTMLpregCallback'),
  84. // $html_content,
  85. // Media::getBackTrackLimit());
  86. require_once(_PS_TOOL_DIR_.'minify_html/minify_html.class.php');
  87. $html_content = str_replace(chr(194).chr(160), '&nbsp;', $html_content);
  88. if (trim($minified_content = Minify_HTML::minify($html_content, array('cssMinifier', 'jsMinifier'))) != '')
  89. $html_content = $minified_content;
  90. return $html_content;
  91. }
  92. return false;
  93. }
  94. public static function minifyHTMLpregCallback($preg_matches)
  95. {
  96. $args = array();
  97. preg_match_all('/[a-zA-Z0-9]+=[\"\\\'][^\"\\\']*[\"\\\']/is', $preg_matches[2], $args);
  98. $args = $args[0];
  99. sort($args);
  100. // if there is no args in the balise, we don't write a space (avoid previous : <title >, now : <title>)
  101. if (empty($args))
  102. $output = $preg_matches[1].'>';
  103. else
  104. $output = $preg_matches[1].' '.implode(' ', $args).'>';
  105. return $output;
  106. }
  107. public static function packJSinHTML($html_content)
  108. {
  109. if (strlen($html_content) > 0)
  110. {
  111. $html_content_copy = $html_content;
  112. $html_content = preg_replace_callback(
  113. '/\\s*(<script\\b[^>]*?>)([\\s\\S]*?)(<\\/script>)\\s*/i',
  114. array('Media', 'packJSinHTMLpregCallback'),
  115. $html_content,
  116. Media::getBackTrackLimit());
  117. // If the string is too big preg_replace return an error
  118. // In this case, we don't compress the content
  119. if (function_exists('preg_last_error') && preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR)
  120. {
  121. if (_PS_MODE_DEV_)
  122. error_log('ERROR: PREG_BACKTRACK_LIMIT_ERROR in function packJSinHTML');
  123. return $html_content_copy;
  124. }
  125. return $html_content;
  126. }
  127. return false;
  128. }
  129. public static function packJSinHTMLpregCallback($preg_matches)
  130. {
  131. if (!(trim($preg_matches[2])))
  132. return $preg_matches[0];
  133. $preg_matches[1] = $preg_matches[1].'/* <![CDATA[ */';
  134. $preg_matches[2] = Media::packJS($preg_matches[2]);
  135. $preg_matches[count($preg_matches) - 1] = '/* ]]> */'.$preg_matches[count($preg_matches) - 1];
  136. unset($preg_matches[0]);
  137. $output = implode('', $preg_matches);
  138. return $output;
  139. }
  140. public static function packJS($js_content)
  141. {
  142. if (!empty($js_content))
  143. {
  144. require_once(_PS_TOOL_DIR_.'js_minify/jsmin.php');
  145. try {
  146. $js_content = JSMin::minify($js_content);
  147. } catch (Exception $e) {
  148. if (_PS_MODE_DEV_)
  149. echo $e->getMessage();
  150. return ';'.trim($js_content, ';').';';
  151. }
  152. }
  153. return ';'.trim($js_content, ';').';';
  154. }
  155. public static function minifyCSS($css_content, $fileuri = false, &$import_url = array())
  156. {
  157. global $current_css_file;
  158. $current_css_file = $fileuri;
  159. if (strlen($css_content) > 0)
  160. {
  161. $limit = Media::getBackTrackLimit();
  162. $css_content = preg_replace('#/\*.*?\*/#s', '', $css_content, $limit);
  163. $css_content = preg_replace_callback('#(url\((?![\\\'"]?data:)(?!http://)(?!https://)(?:\'|")?)([^\)\'"]*(?:\'|")?\))#s', array('Tools', 'replaceByAbsoluteURL'), $css_content, $limit);
  164. $css_content = preg_replace('#\s+#', ' ', $css_content, $limit);
  165. $css_content = str_replace(array("\t", "\n", "\r"), '', $css_content);
  166. $css_content = str_replace(array('; ', ': '), array(';', ':'), $css_content);
  167. $css_content = str_replace(array(' {', '{ '), '{', $css_content);
  168. $css_content = str_replace(', ', ',', $css_content);
  169. $css_content = str_replace(array('} ', ' }', ';}'), '}', $css_content);
  170. $css_content = str_replace(array(':0px', ':0em', ':0pt', ':0%'), ':0', $css_content);
  171. $css_content = str_replace(array(' 0px', ' 0em', ' 0pt', ' 0%'), ' 0', $css_content);
  172. $css_content = str_replace('\'images_ie/', '\'images/', $css_content);
  173. $css_content = preg_replace_callback('#(AlphaImageLoader\(src=\')([^\']*\',)#s', array('Tools', 'replaceByAbsoluteURL'), $css_content);
  174. // Store all import url
  175. preg_match_all('#@(import|charset) .*?;#i', $css_content, $m);
  176. for ($i = 0, $total = count($m[0]); $i < $total; $i++)
  177. {
  178. if (isset($m[1][$i]) && $m[1][$i] == 'import')
  179. $import_url[] = $m[0][$i];
  180. $css_content = str_replace($m[0][$i], '', $css_content);
  181. }
  182. return trim($css_content);
  183. }
  184. return false;
  185. }
  186. /**
  187. * addJS return javascript path
  188. *
  189. * @param mixed $js_uri
  190. * @return string
  191. */
  192. public static function getJSPath($js_uri)
  193. {
  194. return Media::getMediaPath($js_uri);
  195. }
  196. /**
  197. * addCSS return stylesheet path.
  198. *
  199. * @param mixed $css_uri
  200. * @param string $css_media_type
  201. * @param bool $need_rtl
  202. * @return string
  203. */
  204. public static function getCSSPath($css_uri, $css_media_type = 'all', $need_rtl = true)
  205. {
  206. // RTL Ready: search and load rtl css file if it's not originally rtl
  207. if($need_rtl && Context::getContext()->language->is_rtl)
  208. {
  209. $css_uri_rtl = preg_replace('/(^[^.].*)(\.css)$/', '$1_rtl.css', $css_uri);
  210. $rtl_media = Media::getMediaPath($css_uri_rtl, $css_media_type);
  211. if($rtl_media != false)
  212. return $rtl_media;
  213. }
  214. // End RTL
  215. return Media::getMediaPath($css_uri, $css_media_type);
  216. }
  217. public static function getMediaPath($media_uri, $css_media_type = null)
  218. {
  219. if (is_array($media_uri) || $media_uri === null || empty($media_uri))
  220. return false;
  221. $url_data = parse_url($media_uri);
  222. if (!is_array($url_data))
  223. return false;
  224. $file_uri = '';
  225. if (!array_key_exists('host', $url_data))
  226. {
  227. $media_uri_host_mode = '/'.ltrim(str_replace(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, _PS_CORE_DIR_), __PS_BASE_URI__, $media_uri), '/\\');
  228. $media_uri = '/'.ltrim(str_replace(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, _PS_ROOT_DIR_), __PS_BASE_URI__, $media_uri), '/\\');
  229. $url_data['path'] = $media_uri;
  230. // remove PS_BASE_URI on _PS_ROOT_DIR_ for the following
  231. $file_uri = _PS_ROOT_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
  232. $file_uri_host_mode = _PS_CORE_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, Tools::str_replace_once(_PS_CORE_DIR_, '', $url_data['path']));
  233. }
  234. // check if css files exists
  235. if (!array_key_exists('host', $url_data))
  236. {
  237. if (!@filemtime($file_uri) || @filesize($file_uri) === 0)
  238. {
  239. if (!defined('_PS_HOST_MODE_'))
  240. return false;
  241. elseif(!@filemtime($file_uri_host_mode) || @filesize($file_uri_host_mode) === 0)
  242. return false;
  243. else
  244. {
  245. $media_uri = $media_uri_host_mode;
  246. $file_uri = $file_uri_host_mode;
  247. }
  248. }
  249. }
  250. if (!array_key_exists('host', $url_data))
  251. $media_uri = str_replace('//', '/', $media_uri);
  252. if ($css_media_type)
  253. return array($media_uri => $css_media_type);
  254. return $media_uri;
  255. }
  256. /**
  257. * return jquery path.
  258. *
  259. * @param mixed $version
  260. * @return string
  261. */
  262. public static function getJqueryPath($version = null, $folder = null, $minifier = true)
  263. {
  264. $add_no_conflict = false;
  265. if ($version === null)
  266. $version = _PS_JQUERY_VERSION_; //set default version
  267. elseif (preg_match('/^([0-9\.]+)$/Ui', $version))
  268. $add_no_conflict = true;
  269. else
  270. return false;
  271. if ($folder === null)
  272. $folder = _PS_JS_DIR_.'jquery/'; //set default folder
  273. //check if file exist
  274. $file = $folder.'jquery-'.$version.($minifier ? '.min.js' : '.js');
  275. // remove PS_BASE_URI on _PS_ROOT_DIR_ for the following
  276. $url_data = parse_url($file);
  277. $file_uri = _PS_ROOT_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
  278. $file_uri_host_mode = _PS_CORE_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
  279. // check if js files exists, if not try to load query from ajax.googleapis.com
  280. $return = array();
  281. if (@filemtime($file_uri) || (defined('_PS_HOST_MODE_') && @filemtime($file_uri_host_mode)))
  282. $return[] = Media::getJSPath($file);
  283. else
  284. $return[] = Media::getJSPath(Tools::getCurrentUrlProtocolPrefix().'ajax.googleapis.com/ajax/libs/jquery/'.$version.'/jquery'.($minifier ? '.min.js' : '.js'));
  285. if ($add_no_conflict)
  286. $return[] = Media::getJSPath(Context::getContext()->link->getMediaLink(_PS_JS_DIR_.'jquery/jquery.noConflict.php?version='.$version));
  287. //added query migrate for compatibility with new version of jquery will be removed in ps 1.6
  288. $return[] = Media::getJSPath(_PS_JS_DIR_.'jquery/jquery-migrate-1.2.1.min.js');
  289. return $return;
  290. }
  291. /**
  292. * return jqueryUI component path.
  293. *
  294. * @param mixed $component
  295. * @return string
  296. */
  297. public static function getJqueryUIPath($component, $theme, $check_dependencies)
  298. {
  299. $ui_path = array('js' => array(), 'css' => array());
  300. $folder = _PS_JS_DIR_.'jquery/ui/';
  301. $file = 'jquery.'.$component.'.min.js';
  302. $url_data = parse_url($folder.$file);
  303. $file_uri = _PS_ROOT_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
  304. $file_uri_host_mode = _PS_CORE_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
  305. $ui_tmp = array();
  306. if (isset(Media::$jquery_ui_dependencies[$component]) && Media::$jquery_ui_dependencies[$component]['theme'] && $check_dependencies)
  307. {
  308. $theme_css = Media::getCSSPath($folder.'themes/'.$theme.'/jquery.ui.theme.css');
  309. $comp_css = Media::getCSSPath($folder.'themes/'.$theme.'/jquery.'.$component.'.css');
  310. if (!empty($theme_css) || $theme_css)
  311. $ui_path['css'] = array_merge($ui_path['css'], $theme_css);
  312. if (!empty($comp_css) || $comp_css)
  313. $ui_path['css'] = array_merge($ui_path['css'], $comp_css);
  314. }
  315. if ($check_dependencies && array_key_exists($component, self::$jquery_ui_dependencies))
  316. {
  317. foreach (self::$jquery_ui_dependencies[$component]['dependencies'] as $dependency)
  318. {
  319. $ui_tmp[] = Media::getJqueryUIPath($dependency, $theme, false);
  320. if (self::$jquery_ui_dependencies[$dependency]['theme'])
  321. $dep_css = Media::getCSSPath($folder.'themes/'.$theme.'/jquery.'.$dependency.'.css');
  322. if (isset($dep_css) && (!empty($dep_css) || $dep_css))
  323. $ui_path['css'] = array_merge($ui_path['css'], $dep_css);
  324. }
  325. }
  326. if (@filemtime($file_uri) || (defined('_PS_HOST_MODE_') && @filemtime($file_uri_host_mode)))
  327. {
  328. if (!empty($ui_tmp))
  329. {
  330. foreach ($ui_tmp as $ui)
  331. {
  332. if (!empty($ui['js']))
  333. $ui_path['js'][] = $ui['js'];
  334. if (!empty($ui['css']))
  335. $ui_path['css'][] = $ui['css'];
  336. }
  337. $ui_path['js'][] = Media::getJSPath($folder.$file);
  338. }
  339. else
  340. $ui_path['js'] = Media::getJSPath($folder.$file);
  341. }
  342. //add i18n file for datepicker
  343. if ($component == 'ui.datepicker')
  344. {
  345. if (!is_array($ui_path['js']))
  346. $ui_path['js'] = array($ui_path['js']);
  347. $ui_path['js'][] = Media::getJSPath($folder.'i18n/jquery.ui.datepicker-'.Context::getContext()->language->iso_code.'.js');
  348. }
  349. return $ui_path;
  350. }
  351. /**
  352. * return jquery plugin path.
  353. *
  354. * @param mixed $name
  355. * @return string|boolean
  356. */
  357. public static function getJqueryPluginPath($name, $folder = null)
  358. {
  359. $plugin_path = array('js' => array(), 'css' => array());
  360. if ($folder === null)
  361. $folder = _PS_JS_DIR_.'jquery/plugins/'; //set default folder
  362. $file = 'jquery.'.$name.'.js';
  363. $url_data = parse_url($folder);
  364. $file_uri = _PS_ROOT_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
  365. $file_uri_host_mode = _PS_CORE_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
  366. if (@file_exists($file_uri.$file) || (defined('_PS_HOST_MODE_') && @file_exists($file_uri_host_mode.$file)))
  367. $plugin_path['js'] = Media::getJSPath($folder.$file);
  368. elseif (@file_exists($file_uri.$name.'/'.$file) || (defined('_PS_HOST_MODE_') && @file_exists($file_uri_host_mode.$name.'/'.$file)))
  369. $plugin_path['js'] = Media::getJSPath($folder.$name.'/'.$file);
  370. else
  371. return false;
  372. $plugin_path['css'] = Media::getJqueryPluginCSSPath($name, $folder);
  373. return $plugin_path;
  374. }
  375. /**
  376. * return jquery plugin css path if exist.
  377. *
  378. * @param mixed $name
  379. * @return string|boolean
  380. */
  381. public static function getJqueryPluginCSSPath($name, $folder = null)
  382. {
  383. if ($folder === null)
  384. $folder = _PS_JS_DIR_.'jquery/plugins/'; //set default folder
  385. $file = 'jquery.'.$name.'.css';
  386. $url_data = parse_url($folder);
  387. $file_uri = _PS_ROOT_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
  388. $file_uri_host_mode = _PS_CORE_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
  389. if (@file_exists($file_uri.$file) || (defined('_PS_HOST_MODE_') && @file_exists($file_uri_host_mode.$file)))
  390. return Media::getCSSPath($folder.$file);
  391. elseif (@file_exists($file_uri.$name.'/'.$file) || (defined('_PS_HOST_MODE_') && @file_exists($file_uri_host_mode.$name.'/'.$file)))
  392. return Media::getCSSPath($folder.$name.'/'.$file);
  393. else
  394. return false;
  395. }
  396. /**
  397. * Combine Compress and Cache CSS (ccc) calls
  398. *
  399. * @param array css_files
  400. * @return array processed css_files
  401. */
  402. public static function cccCss($css_files)
  403. {
  404. //inits
  405. $css_files_by_media = array();
  406. $external_css_files = array();
  407. $compressed_css_files = array();
  408. $compressed_css_files_not_found = array();
  409. $compressed_css_files_infos = array();
  410. $protocol_link = Tools::getCurrentUrlProtocolPrefix();
  411. $cache_path = _PS_THEME_DIR_.'cache/';
  412. // group css files by media
  413. foreach ($css_files as $filename => $media)
  414. {
  415. if (!array_key_exists($media, $css_files_by_media))
  416. $css_files_by_media[$media] = array();
  417. $infos = array();
  418. $infos['uri'] = $filename;
  419. $url_data = parse_url($filename);
  420. if(array_key_exists('host', $url_data))
  421. {
  422. $external_css_files[$filename] = $media;
  423. continue;
  424. }
  425. $infos['path'] = _PS_ROOT_DIR_.Tools::str_replace_once(__PS_BASE_URI__, '/', $url_data['path']);
  426. if (!@filemtime($infos['path']))
  427. $infos['path'] = _PS_CORE_DIR_.Tools::str_replace_once(__PS_BASE_URI__, '/', $url_data['path']);
  428. $css_files_by_media[$media]['files'][] = $infos;
  429. if (!array_key_exists('date', $css_files_by_media[$media]))
  430. $css_files_by_media[$media]['date'] = 0;
  431. $css_files_by_media[$media]['date'] = max(
  432. file_exists($infos['path']) ? @filemtime($infos['path']) : 0,
  433. $css_files_by_media[$media]['date']
  434. );
  435. if (!array_key_exists($media, $compressed_css_files_infos))
  436. $compressed_css_files_infos[$media] = array('key' => '');
  437. $compressed_css_files_infos[$media]['key'] .= $filename;
  438. }
  439. // get compressed css file infos
  440. $version = (int)Configuration::get('PS_CCCCSS_VERSION');
  441. foreach ($compressed_css_files_infos as $media => &$info)
  442. {
  443. $key = md5($info['key'].$protocol_link);
  444. $filename = $cache_path.'v_'.$version.'_'.$key.'_'.$media.'.css';
  445. $info = array(
  446. 'key' => $key,
  447. 'date' => file_exists($filename) ? @filemtime($filename) : 0
  448. );
  449. }
  450. foreach ($css_files_by_media as $media => $media_infos)
  451. {
  452. if ($media_infos['date'] > $compressed_css_files_infos[$media]['date'])
  453. {
  454. if ($compressed_css_files_infos[$media]['date'])
  455. {
  456. Configuration::updateValue('PS_CCCCSS_VERSION', ++$version);
  457. break;
  458. }
  459. }
  460. }
  461. // aggregate and compress css files content, write new caches files
  462. $import_url = array();
  463. foreach ($css_files_by_media as $media => $media_infos)
  464. {
  465. $cache_filename = $cache_path.'v_'.$version.'_'.$compressed_css_files_infos[$media]['key'].'_'.$media.'.css';
  466. if ($media_infos['date'] > $compressed_css_files_infos[$media]['date'])
  467. {
  468. $cache_filename = $cache_path.'v_'.$version.'_'.$compressed_css_files_infos[$media]['key'].'_'.$media.'.css';
  469. $compressed_css_files[$media] = '';
  470. foreach ($media_infos['files'] as $file_infos)
  471. {
  472. if (file_exists($file_infos['path']))
  473. $compressed_css_files[$media] .= Media::minifyCSS(file_get_contents($file_infos['path']), $file_infos['uri'], $import_url);
  474. else
  475. $compressed_css_files_not_found[] = $file_infos['path'];
  476. }
  477. if (!empty($compressed_css_files_not_found))
  478. $content = '/* WARNING ! file(s) not found : "'.
  479. implode(',', $compressed_css_files_not_found).
  480. '" */'."\n".$compressed_css_files[$media];
  481. else
  482. $content = $compressed_css_files[$media];
  483. $content = '@charset "UTF-8";'."\n".$content;
  484. $content = implode('', $import_url).$content;
  485. file_put_contents($cache_filename, $content);
  486. chmod($cache_filename, 0777);
  487. }
  488. $compressed_css_files[$media] = $cache_filename;
  489. }
  490. // rebuild the original css_files array
  491. $css_files = array();
  492. foreach ($compressed_css_files as $media => $filename)
  493. {
  494. $url = str_replace(_PS_THEME_DIR_, _THEMES_DIR_._THEME_NAME_.'/', $filename);
  495. $css_files[$protocol_link.Tools::getMediaServer($url).$url] = $media;
  496. }
  497. return array_merge($external_css_files, $css_files);
  498. }
  499. public static function getBackTrackLimit()
  500. {
  501. static $limit = null;
  502. if ($limit === null)
  503. {
  504. $limit = @ini_get('pcre.backtrack_limit');
  505. if (!$limit)
  506. $limit = -1;
  507. }
  508. return $limit;
  509. }
  510. /**
  511. * Combine Compress and Cache (ccc) JS calls
  512. *
  513. * @param array js_files
  514. * @return array processed js_files
  515. */
  516. public static function cccJS($js_files)
  517. {
  518. //inits
  519. $compressed_js_files_not_found = array();
  520. $js_files_infos = array();
  521. $js_files_date = 0;
  522. $compressed_js_filename = '';
  523. $js_external_files = array();
  524. $protocol_link = Tools::getCurrentUrlProtocolPrefix();
  525. $cache_path = _PS_THEME_DIR_.'cache/';
  526. // get js files infos
  527. foreach ($js_files as $filename)
  528. {
  529. if (Validate::isAbsoluteUrl($filename))
  530. $js_external_files[] = $filename;
  531. else
  532. {
  533. $infos = array();
  534. $infos['uri'] = $filename;
  535. $url_data = parse_url($filename);
  536. $infos['path'] = _PS_ROOT_DIR_.Tools::str_replace_once(__PS_BASE_URI__, '/', $url_data['path']);
  537. if (!@filemtime($infos['path']))
  538. $infos['path'] = _PS_CORE_DIR_.Tools::str_replace_once(__PS_BASE_URI__, '/', $url_data['path']);
  539. $js_files_infos[] = $infos;
  540. $js_files_date = max(
  541. file_exists($infos['path']) ? @filemtime($infos['path']) : 0,
  542. $js_files_date
  543. );
  544. $compressed_js_filename .= $filename;
  545. }
  546. }
  547. // get compressed js file infos
  548. $compressed_js_filename = md5($compressed_js_filename);
  549. $version = (int)Configuration::get('PS_CCCJS_VERSION');
  550. $compressed_js_path = $cache_path.'v_'.$version.'_'.$compressed_js_filename.'.js';
  551. $compressed_js_file_date = file_exists($compressed_js_path) ? @filemtime($compressed_js_path) : 0;
  552. // aggregate and compress js files content, write new caches files
  553. if ($js_files_date > $compressed_js_file_date)
  554. {
  555. if ($compressed_js_file_date)
  556. Configuration::updateValue('PS_CCCJS_VERSION', ++$version);
  557. $compressed_js_path = $cache_path.'v_'.$version.'_'.$compressed_js_filename.'.js';
  558. $content = '';
  559. foreach ($js_files_infos as $file_infos)
  560. {
  561. if (file_exists($file_infos['path']))
  562. {
  563. $tmp_content = file_get_contents($file_infos['path']);
  564. if (preg_match('@\.(min|pack)\.[^/]+$@', $file_infos['path'], $matches))
  565. $content .= preg_replace('/\/\/@\ssourceMappingURL\=[_a-zA-Z0-9-.]+\.'.$matches[1].'\.map\s+/', '', $tmp_content);
  566. else
  567. $content .= Media::packJS($tmp_content);
  568. }
  569. else
  570. $compressed_js_files_not_found[] = $file_infos['path'];
  571. }
  572. if (!empty($compressed_js_files_not_found))
  573. $content = '/* WARNING ! file(s) not found : "'.
  574. implode(',', $compressed_js_files_not_found).
  575. '" */'."\n".$content;
  576. file_put_contents($compressed_js_path, $content);
  577. chmod($compressed_js_path, 0777);
  578. }
  579. // rebuild the original js_files array
  580. if (strpos($compressed_js_path, _PS_ROOT_DIR_) !== false)
  581. $url = str_replace(_PS_ROOT_DIR_.'/', __PS_BASE_URI__, $compressed_js_path);
  582. if (strpos($compressed_js_path, _PS_CORE_DIR_) !== false)
  583. $url = str_replace(_PS_CORE_DIR_.'/', __PS_BASE_URI__, $compressed_js_path);
  584. return array_merge(array($protocol_link.Tools::getMediaServer($url).$url), $js_external_files);
  585. }
  586. public static function clearCache()
  587. {
  588. foreach (array(_PS_THEME_DIR_.'cache') as $dir)
  589. if (file_exists($dir))
  590. foreach (scandir($dir) as $file)
  591. if ($file[0] != '.' && $file != 'index.php')
  592. Tools::deleteFile($dir.DIRECTORY_SEPARATOR.$file, array('index.php'));
  593. $version = (int)Configuration::get('PS_CCCJS_VERSION');
  594. Configuration::updateValue('PS_CCCJS_VERSION', ++$version);
  595. $version = (int)Configuration::get('PS_CCCCSS_VERSION');
  596. Configuration::updateValue('PS_CCCCSS_VERSION', ++$version);
  597. }
  598. public static function getJsDef()
  599. {
  600. ksort(Media::$js_def);
  601. return Media::$js_def;
  602. }
  603. public static function getInlineScript()
  604. {
  605. return Media::$inline_script;
  606. }
  607. /**
  608. * Add a new javascript definition at bottom of page
  609. *
  610. * @param mixed $js_def
  611. * @return void
  612. */
  613. public static function addJsDef($js_def)
  614. {
  615. if (is_array($js_def))
  616. foreach ($js_def as $key => $js)
  617. Media::$js_def[$key] = $js;
  618. elseif ($js_def)
  619. Media::$js_def[] = $js_def;
  620. }
  621. /**
  622. * Add a new javascript definition from a capture at bottom of page
  623. *
  624. * @param mixed $js_def
  625. * @return void
  626. */
  627. public static function addJsDefL($params, $content, $smarty = null, &$repeat = false)
  628. {
  629. if (!$repeat && isset($params) && Tools::strlen($content))
  630. {
  631. if (!is_array($params))
  632. $params = (array)$params;
  633. foreach($params as $param)
  634. Media::$js_def[$param] = $content;
  635. }
  636. }
  637. public static function deferInlineScripts($output)
  638. {
  639. /* Try to enqueue in js_files inline scripts with src but without conditionnal comments */
  640. $dom = new DOMDocument();
  641. libxml_use_internal_errors(true);
  642. @$dom->loadHTML(($output));
  643. libxml_use_internal_errors(false);
  644. $scripts = $dom->getElementsByTagName('script');
  645. if (is_object($scripts) && $scripts->length)
  646. foreach ($scripts as $script)
  647. if ($src = $script->getAttribute('src'))
  648. {
  649. if (substr($src, 0, 2) == '//')
  650. $src = Tools::getCurrentUrlProtocolPrefix().substr($src, 2);
  651. $patterns = array(
  652. '#code\.jquery\.com/jquery-([0-9\.]+)(\.min)*\.js$#Ui',
  653. '#ajax\.googleapis\.com/ajax/libs/jquery/([0-9\.]+)/jquery(\.min)*\.js$#Ui',
  654. '#ajax\.aspnetcdn\.com/ajax/jquery/jquery-([0-9\.]+)(\.min)*\.js$#Ui',
  655. '#cdnjs\.cloudflare\.com/ajax/libs/jquery/([0-9\.]+)/jquery(\.min)*\.js$#Ui',
  656. '#/jquery-([0-9\.]+)(\.min)*\.js$#Ui'
  657. );
  658. foreach($patterns as $pattern)
  659. {
  660. $matches = array();
  661. if (preg_match($pattern, $src, $matches))
  662. {
  663. $minifier = $version = false;
  664. if (isset($matches[2]) && $matches[2])
  665. $minifier = (bool)$matches[2];
  666. if (isset($matches[1]) && $matches[1])
  667. $version = $matches[1];
  668. if ($version)
  669. {
  670. if ($version != _PS_JQUERY_VERSION_)
  671. Context::getContext()->controller->addJquery($version, null, $minifier);
  672. array_push(Media::$inline_script_src, $src);
  673. }
  674. }
  675. }
  676. if (!in_array($src, Media::$inline_script_src))
  677. Context::getContext()->controller->addJS($src);
  678. }
  679. $output = preg_replace_callback('/<script[^>]*>(.*)<\s*\/script\s*[^>]*>/Uims', array('Media', 'deferScript'), $output);
  680. return $output;
  681. }
  682. public static function deferScript($matches)
  683. {
  684. if (!is_array($matches))
  685. return false;
  686. $inline = '';
  687. if (isset($matches[0]))
  688. $original = trim($matches[0]);
  689. if (isset($matches[1]))
  690. $inline = trim($matches[1]);
  691. /* This is an inline script, add its content to inline scripts stack then remove it from content */
  692. if (!empty($inline) && preg_match('/<\s*[\/]script[^>]*>/ims', $original) !== false && Media::$inline_script[] = $inline)
  693. return '';
  694. /* This is an external script, if it already belongs to js_files then remove it from content */
  695. preg_match('/src\s*=\s*["\']?([^"\']*)[^>]/ims', $original, $results);
  696. if (array_key_exists(1, $results))
  697. {
  698. if (substr($results[1], 0, 2) == '//')
  699. {
  700. $protocol_link = Tools::getCurrentUrlProtocolPrefix();
  701. $results[1] = $protocol_link.ltrim($results[1], '/');
  702. }
  703. if (in_array($results[1], Context::getContext()->controller->js_files) || in_array($results[1], Media::$inline_script_src))
  704. return '';
  705. }
  706. /* return original string because no match was found */
  707. return $original;
  708. }
  709. }