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

/plugins/system/jsntplframework/libraries/joomlashine/compress/js.php

https://bitbucket.org/rippleau/nrm-org-au
PHP | 467 lines | 328 code | 63 blank | 76 comment | 48 complexity | 431128405e1f11982936af032e153748 MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0, 0BSD, MIT, LGPL-2.1
  1. <?php
  2. /**
  3. * @version $Id$
  4. * @package JSNExtension
  5. * @subpackage TPLFramework
  6. * @author JoomlaShine Team <support@joomlashine.com>
  7. * @copyright Copyright (C) 2012 JoomlaShine.com. All Rights Reserved.
  8. * @license GNU/GPL v2 or later http://www.gnu.org/licenses/gpl-2.0.html
  9. *
  10. * Websites: http://www.joomlashine.com
  11. * Technical Support: Feedback - http://www.joomlashine.com/contact-us/get-support.html
  12. */
  13. // No direct access to this file
  14. defined('_JEXEC') or die('Restricted access');
  15. // Import necessary Joomla libraries
  16. jimport('joomla.filesystem.folder');
  17. jimport('joomla.filesystem.file');
  18. /**
  19. * Javascript Compression engine
  20. *
  21. * @package TPLFramework
  22. * @subpackage Plugin
  23. * @since 1.0.0
  24. */
  25. abstract class JSNTplCompressJs
  26. {
  27. /**
  28. * Method to parse all link to css files from the html markup
  29. * and compress it
  30. *
  31. * @param string $htmlMarkup HTML Content to response to browser
  32. *
  33. * @return void
  34. */
  35. public static function compress ($scripts)
  36. {
  37. static $compressedFiles;
  38. // Get object for working with URI
  39. $uri = JUri::getInstance();
  40. // Generate link prefix if current scheme is HTTPS
  41. $prefix = '';
  42. if ($uri->getScheme() == 'https')
  43. {
  44. $prefix = $uri->toString(array('scheme', 'host', 'port'));
  45. }
  46. // Initialize variables
  47. $groupIndex = 0;
  48. $groupType = 'default';
  49. $groupFiles = array();
  50. $compress = array();
  51. // Sometime, script file need to be stored in the original location and file name
  52. $document = JFactory::getDocument();
  53. $leaveAlone = preg_split('/[\r\n]+/', $document->params->get('compressionExclude'));
  54. // We already know some files must be excluded from compression
  55. $leaveAlone[] = 'modal.js';
  56. $leaveAlone[] = 'tiny_mce.js';
  57. $leaveAlone[] = 'tinymce.min.js';
  58. // Parse script tags
  59. foreach ($scripts as $key => $line)
  60. {
  61. // Set default group
  62. $attributes['group'] = 'default';
  63. $attributes['src'] = $key;
  64. if (!isset($attributes['src']))
  65. {
  66. continue;
  67. }
  68. if(isset($attributes['type']) && $attributes['type'] == "application/ld+json"){
  69. continue;
  70. }
  71. // Add to result list if this is external file
  72. if ( ! ($isInternal = JSNTplCompressHelper::isInternal($attributes['src'])) OR strpos($attributes['src'], '//') === 0)
  73. {
  74. // Add collected files to compress list
  75. if ( ! empty($groupFiles))
  76. {
  77. $compress[] = array(
  78. 'files' => $groupFiles[$groupIndex],
  79. 'group' => $groupType
  80. );
  81. $groupFiles = array();
  82. }
  83. $compress[] = array('src' => $attributes['src']);
  84. continue;
  85. }
  86. // Add to result list if this is dynamic generation content
  87. $questionPos = false;
  88. if (($questionPos = strpos($attributes['src'], '?')) !== false)
  89. {
  90. $isDynamic = (substr($attributes['src'], $questionPos - 4, 4) == '.php');
  91. $path = JSNTplCompressHelper::getFilePath(substr($attributes['src'], 0, $questionPos));
  92. // Check if this is a dynamic generation content
  93. if ( ! $isDynamic AND JSNTplCompressHelper::isInternal($attributes['src']))
  94. {
  95. $isDynamic = ! is_file($path);
  96. }
  97. if ($isDynamic)
  98. {
  99. // Add collected files to compress list
  100. if ( ! empty($groupFiles))
  101. {
  102. $compress[] = array(
  103. 'files' => $groupFiles[$groupIndex],
  104. 'group' => $groupType
  105. );
  106. $groupFiles = array();
  107. }
  108. $compress[] = array('src' => $attributes['src']);
  109. continue;
  110. }
  111. }
  112. // Check if reserving script file name is required
  113. $scriptName = basename($questionPos !== false ? $path : $attributes['src']);
  114. if (in_array($scriptName, $leaveAlone))
  115. {
  116. $attributes['group'] = 'reserve|' . $scriptName;
  117. }
  118. // Create new compression group if reserving script file name is required
  119. if ($attributes['group'] != $groupType)
  120. {
  121. // Add collected files to compress list
  122. if (isset($groupFiles[$groupIndex]) AND ! empty($groupFiles[$groupIndex]))
  123. {
  124. $compress[] = array(
  125. 'files' => $groupFiles[$groupIndex],
  126. 'group' => $groupType
  127. );
  128. }
  129. // Increase index number of the group
  130. $groupIndex++;
  131. $groupType = $attributes['group'];
  132. }
  133. // Initial group
  134. if ( ! isset($groupFiles[$groupIndex]))
  135. {
  136. $groupFiles[$groupIndex] = array();
  137. }
  138. $src = $attributes['src'];
  139. $queryStringIndex = strpos($src, '?');
  140. if ($queryStringIndex !== false)
  141. {
  142. $src = substr($src, 0, $queryStringIndex);
  143. }
  144. // Add file to the group
  145. $groupFiles[$groupIndex][] = preg_match('/^reserve\|(.+)$/', $groupType) ? $attributes['src'] : $src;
  146. }
  147. // Add collected files to result list
  148. if (isset($groupFiles[$groupIndex]) AND ! empty($groupFiles[$groupIndex]))
  149. {
  150. $compress[] = array(
  151. 'files' => $groupFiles[$groupIndex],
  152. 'group' => $groupType
  153. );
  154. }
  155. // Initial compress result
  156. $compressResult = array();
  157. $fileCompressed = array();
  158. // Get template details
  159. $templateName = JFactory::getApplication()->getTemplate();
  160. // Generate path to store compressed files
  161. if ( ! preg_match('#^(/|\\|[a-z]:)#i', $document->params->get('cacheDirectory')))
  162. {
  163. $compressPath = JPATH_ROOT . '/' . rtrim($document->params->get('cacheDirectory'), '\\/');
  164. }
  165. else
  166. {
  167. $compressPath = rtrim($document->params->get('cacheDirectory'), '\\/');
  168. }
  169. $compressPath = $compressPath . '/' . $templateName . '/';
  170. // Create directory if not exists
  171. if ( ! is_dir($compressPath))
  172. {
  173. JFolder::create($compressPath);
  174. }
  175. // Loop to each compress element to compress file
  176. $modifiedFlag = false;
  177. foreach ($compress AS $group)
  178. {
  179. // Ignore compress when group is a external file
  180. if (isset($group['src']))
  181. {
  182. $compressResult[] = sprintf('<script src="%s" type="text/javascript"></script>', $group['src']);
  183. $fileCompressed[] = $group['src'];
  184. continue;
  185. }
  186. // Check if reserving script file name is required
  187. if (isset($group['group']) AND preg_match('/^reserve\|(.+)$/', $group['group']))
  188. {
  189. foreach ($group['files'] as $gFile)
  190. {
  191. $compressResult[] = sprintf('<script src="%s" type="text/javascript"></script>', $gFile);
  192. $fileCompressed[] = $gFile;
  193. }
  194. continue;
  195. }
  196. // Generate compress file name
  197. $compressFile = md5(implode('', $group['files'])) . '.js';
  198. $lastModified = 0;
  199. $splittedFiles = array();
  200. // Check last modified time for each file in the group
  201. foreach ($group['files'] AS $file)
  202. {
  203. $path = JSNTplCompressHelper::getFilePath($file);
  204. $lastModified = (is_file($path) && @filemtime($path) > $lastModified) ? @filemtime($path) : $lastModified;
  205. }
  206. if (@filemtime($compressPath . $compressFile) < $lastModified)
  207. {
  208. $modifiedFlag = true;
  209. }
  210. // Compress group when expired
  211. if ( ! is_file($compressPath . $compressFile) OR @filemtime($compressPath . $compressFile) < $lastModified)
  212. {
  213. // Preset compression buffer
  214. $buffer = '';
  215. // Preset some variables to hold compression status
  216. $processedFiles = array();
  217. $maxFileSize = 1024 * (int) $document->params->get('maxCompressionSize');
  218. $currentSize = 0;
  219. // Read content of each file and write it to the cache file
  220. foreach ($group['files'] AS $file)
  221. {
  222. $filePath = JSNTplCompressHelper::getFilePath($file);
  223. // Skip when cannot access to file
  224. if ( ! is_file($filePath) OR ! is_readable($filePath))
  225. {
  226. continue;
  227. }
  228. // Prepend path to source file
  229. $source = ($currentSize == 0 ? '' : "\n\n")
  230. . '/* FILE: ' . str_replace(str_replace('\\', '/', JPATH_ROOT), '', str_replace('\\', '/', $filePath)) . ' */'
  231. . "\n" . file_get_contents($filePath);
  232. // Get length of processed content
  233. $length = strlen($source);
  234. if ($length > $maxFileSize OR ($currentSize + $length) > $maxFileSize)
  235. {
  236. // Write buffer to cache file
  237. JFile::write($compressPath . $compressFile, $buffer);
  238. // Rename created cache file
  239. if ($currentSize > 0)
  240. {
  241. $newFileName = md5(implode('', $processedFiles)) . '.js';
  242. JFile::move($compressPath . $compressFile, $compressPath . $newFileName);
  243. // Save every compressed file associated with this page for maintenance later
  244. $compressedFiles[] = str_replace('\\', '/', $compressPath) . $newFileName;
  245. // Store splitted file URL for later reference
  246. $splittedFiles[] = $prefix . str_replace(str_replace('\\', '/', JPATH_ROOT), JUri::root(true), str_replace('\\', '/', $compressPath)) . $newFileName;
  247. }
  248. // Reset compression buffer
  249. $buffer = '';
  250. // Reset current file size
  251. $currentSize = $length;
  252. $processedFiles = array($filePath);
  253. }
  254. else
  255. {
  256. // Update current file size
  257. $currentSize += $length;
  258. $processedFiles[] = $filePath;
  259. }
  260. // Append processed content to buffer
  261. $buffer .= $source . ";\n";
  262. }
  263. // Write buffer to cache file
  264. JFile::write($compressPath . $compressFile, $buffer);
  265. // Save every compressed file associated with this page for maintenance later
  266. $compressedFiles[] = str_replace('\\', '/', $compressPath) . $compressFile;
  267. // Prepend splitted compress files into trackable compress file
  268. if (count($splittedFiles))
  269. {
  270. for ($n = count($splittedFiles), $i = $n - 1; $i >= 0; $i--)
  271. {
  272. JSNTplCompressHelper::prependIntoFile("// Include: {$splittedFiles[$i]}" . ($i + 1 < $n ? "\n" : "\n\n"), $compressPath . $compressFile);
  273. }
  274. }
  275. }
  276. else
  277. {
  278. // Read compressed file for list of splitted file
  279. $include = file_get_contents($compressPath . $compressFile);
  280. $include = substr($include, 0, strpos($include, "\n\n"));
  281. // Parse splitted compress file
  282. foreach (explode("\n", $include) AS $line)
  283. {
  284. if (strpos($line, '// Include: ') === 0)
  285. {
  286. $splittedFiles[] = str_replace('// Include: ', '', $line);
  287. }
  288. }
  289. }
  290. // Load splitted compress file
  291. if (count($splittedFiles))
  292. {
  293. foreach ($splittedFiles AS $file)
  294. {
  295. $compressResult[] = sprintf('<script src="%s" type="text/javascript"></script>', $file);
  296. $fileCompressed[] = $file;
  297. }
  298. }
  299. // Add compressed file to the compress result list
  300. $compressUrl = str_replace(str_replace('\\', '/', JPATH_ROOT), JUri::root(true), str_replace('\\', '/', $compressPath)) . $compressFile;
  301. $compressResult[] = sprintf('<script src="%s" type="text/javascript"></script>', $prefix . $compressUrl);
  302. $fileCompressed[] = $prefix . $compressUrl;
  303. }
  304. // Verify if stylesheets associated with this page has been changed
  305. if (isset($compressedFiles))
  306. {
  307. $trackFile = $compressPath . 'tracking.php';
  308. $pageLink = JUri::current();
  309. $cleanUp = array();
  310. if (file_exists($trackFile))
  311. {
  312. if ( ! file_exists("{$trackFile}.lock"))
  313. {
  314. // Get tracking data
  315. include $trackFile;
  316. if (isset($tracking) && isset($tracking[$pageLink]) && isset($tracking[$pageLink]['js']))
  317. {
  318. foreach ($tracking[$pageLink]['js'] as $file)
  319. {
  320. if ( ! in_array($file, $compressedFiles))
  321. {
  322. // Store obsolete file to be removed
  323. $cleanUp[] = $file;
  324. }
  325. }
  326. // Remove obsolete file only if not used in another page
  327. foreach ($cleanUp as $file)
  328. {
  329. $removable = true;
  330. foreach ($tracking as $link => $assets)
  331. {
  332. if ($pageLink == $link)
  333. {
  334. continue;
  335. }
  336. if (in_array($file, $assets['js']))
  337. {
  338. $removable = false;
  339. break;
  340. }
  341. }
  342. if ($removable && !$modifiedFlag)
  343. {
  344. JFile::delete($file);
  345. }
  346. }
  347. }
  348. }
  349. }
  350. else
  351. {
  352. // Clean all unmaintained compressed files
  353. if ($files = glob($compressPath . '*.js'))
  354. {
  355. foreach ($files as $file)
  356. {
  357. $file = str_replace('\\', '/', $file);
  358. if ( ! in_array($file, $compressedFiles))
  359. {
  360. JFile::delete($file);
  361. }
  362. }
  363. }
  364. }
  365. // Update tracking file if not locked
  366. if ( ! file_exists("{$trackFile}.lock"))
  367. {
  368. // Create lock file
  369. $content = 'Updating';
  370. JFile::write("{$trackFile}.lock", $content);
  371. // Preset tracking array
  372. if ( ! isset($tracking))
  373. {
  374. $tracking = array($pageLink => array());
  375. }
  376. $tracking[$pageLink]['js'] = $compressedFiles;
  377. // Update tracking data
  378. $content = "<?php\n\$tracking = json_decode('" . json_encode($tracking) . "', true);\n?>";
  379. // Update tracking file
  380. JFile::write($trackFile, $content);
  381. // Remove lock file
  382. JFile::delete("{$trackFile}.lock");
  383. }
  384. }
  385. return $fileCompressed;
  386. }
  387. }