PageRenderTime 68ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/forum/web-optimizer/controller/compressor.php

https://github.com/GreyTeardrop/socionicasys-forum
PHP | 3326 lines | 2813 code | 47 blank | 466 comment | 590 complexity | 65414ab144ec54d59b59a2a52faa2117 MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-3.0, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * File from WEBO Site SpeedUp, WEBO Software (http://www.webogroup.com/)
  4. * Gzips and minifies the JavaScript and CSS within the head tags of a page.
  5. * Can also gzip and minify the page itself
  6. * and 100+ other cool web performance optimization techniques
  7. * Based on Web Optimizer, which was based on PHP Speedy
  8. *
  9. **/
  10. class web_optimizer {
  11. /**
  12. * Constructor
  13. * Sets the options and defines the gzip headers
  14. **/
  15. function web_optimizer ($options = false) {
  16. /* initialize chained optimization */
  17. if(!empty($_GET['web_optimizer_disabled']))
  18. {
  19. $this->options['active'] = 0;
  20. return;
  21. }
  22. $this->web_optimizer_stage = round(empty($_GET['web_optimizer_stage']) ? 0 : $_GET['web_optimizer_stage']);
  23. $this->debug_mode = empty($_GET['web_optimizer_debug']) && empty($_COOKIE['web_optimizer_debug']) ? 0 : 1;
  24. /* get chained optimization params */
  25. if (!empty($this->web_optimizer_stage)) {
  26. $this->username = htmlspecialchars(empty($_GET['username']) ? '' :
  27. $_GET['username']);
  28. $this->password = htmlspecialchars(empty($_GET['password']) ? '' :
  29. $_GET['password']);
  30. $this->auto_rewrite = round(empty($_GET['auto_rewrite']) ? '' :
  31. $_GET['auto_rewrite']);
  32. $this->chained_redirect = 'optimizing.php';
  33. $this->cache_version = round(empty($_GET['cache_version']) ? '' :
  34. $_GET['cache_version']);
  35. /* get major stage number, all stages:
  36. -1 - system, envelope all <script> to try-catch-document.write
  37. 0-9 - inilialization, starts in administrative interface
  38. 10-13 - JS file generation, 1st major stage (common browsers)
  39. 14-19 - CSS Sprites / data:URI generation, 1st major stage
  40. 20-24 - CSS file generation + page parsing, 1st major stage
  41. 25-28 - JS file generation, 2nd major stage (IE 6.0)
  42. 29-34 - CSS Sprites / mhtml generation, 2nd major stage
  43. 35-39 - CSS file generation + page parsing, 2nd major stage
  44. 40-43 - JS file generation, 3rd major stage (IE 7.0)
  45. 44-49 - CSS Sprites / mhtml generation, 2nd major stage
  46. 50-54 - CSS file generation + page parsing, 2nd major stage
  47. 55-58 - JS file generation, 4th major stage (IE 8.0)
  48. 59-64 - CSS Sprites / data:URI generation, 4th major stage
  49. 65-69 - CSS file generation + page parsing, 4th major stage
  50. 70-73 - JS file generation, 5th major stage (IE 7.0 @ Vista)
  51. 74-79 - CSS Sprites generation, 5th major stage
  52. 80-84 - CSS file generation + page parsing, 5th major stage
  53. */
  54. $this->cache_stage = floor(($this->web_optimizer_stage - 10) / 15);
  55. }
  56. /* allow merging of other classes with this one */
  57. foreach ($options as $key => $value) {
  58. $this->$key = $value;
  59. }
  60. $this->options['active'] = $this->debug_mode ? 1 : $this->options['active'];
  61. /* disable any actions if not active */
  62. if (empty($this->options['active'])) {
  63. return;
  64. }
  65. /* define head of the webpage for scripts / styles */
  66. $this->head = '';
  67. /* remember current time */
  68. $this->time = empty($_SERVER['REQUEST_TIME']) ? time() : $_SERVER['REQUEST_TIME'];
  69. $this->host = $_SERVER['HTTP_HOST'];
  70. if (strpos($_SERVER['HTTP_HOST'], "www.") !== false ||
  71. strpos($_SERVER['HTTP_HOST'], "WWW.") !== false) {
  72. $this->host = substr($this->host, 4);
  73. }
  74. /* define PHP version */
  75. $this->php = $this->options['php'];
  76. /* skip buffering (need for integration as plugin) */
  77. $this->buffered = $this->options['buffered'];
  78. /* Sets User Agent to differ IE from non-IE */
  79. $this->ua = empty($_SERVER['HTTP_USER_AGENT']) ? '' : $_SERVER['HTTP_USER_AGENT'];
  80. /* HTTPS or not ? */
  81. $this->https = empty($_SERVER['HTTPS']) ? '' : 's';
  82. /* Set options */
  83. $this->set_options();
  84. /* Include base plugin class */
  85. if (is_array($this->options['plugins'])) {
  86. include_once($this->options['css']['installdir'] . 'libs/php/class.plugin.php');
  87. }
  88. /* Remember current page encoding */
  89. $this->encoding = '';
  90. /* Define the gzip headers */
  91. $this->set_gzip_headers();
  92. /* Deal with flushed content or not? */
  93. $this->flushed = false;
  94. $excluded_html_pages = '';
  95. $included_user_agents = '';
  96. $retricted_cookie = 0;
  97. if (!empty($this->options['page']['cache'])) {
  98. $this->start_cache_engine();
  99. /* HTML cache ? */
  100. if (!empty($this->options['page']['cache_ignore']) ||
  101. !empty($this->options['restricted'])) {
  102. $list = (empty($this->options['page']['cache_ignore']) ? '' : $this->options['page']['cache_ignore']) .
  103. (empty($this->options['restricted']) ? '' : ' ' . $this->options['restricted']);
  104. $excluded_html_pages = preg_replace("/ /", "|", preg_replace("/([\?!\^\$\|\(\)\[\]\{\}])/", "\\\\$1", $list));
  105. }
  106. if (!empty($this->options['page']['allowed_user_agents'])) {
  107. $included_user_agents = preg_replace("/ /", "|", preg_replace("/([\?!\^\$\|\(\)\[\]\{\}])/", "\\\\$1", $this->options['page']['allowed_user_agents']));
  108. }
  109. if (!empty($this->options['page']['exclude_cookies'])) {
  110. $cookies = explode(" ", $this->options['page']['exclude_cookies']);
  111. foreach ($cookies as $cookie) {
  112. if (isset($_COOKIE[$cookie])) {
  113. $retricted_cookie = 1;
  114. }
  115. }
  116. }
  117. }
  118. /* cache if
  119. - option is enabled,
  120. - don't parse excluded pages,
  121. - or parse included USER AGENTS,
  122. - don't parse pages with excluded coockies,
  123. - flush or gzip for HTML are disabled,
  124. - headers have not been sent,
  125. - page is requested by GET,
  126. - no chained optimization,
  127. - no debug mode,
  128. - external cache restriction.
  129. */
  130. $this->cache_me = !empty($this->options['page']['cache']) &&
  131. (empty($this->options['page']['cache_ignore']) ||
  132. !preg_match("!" . $excluded_html_pages . "!is", $_SERVER['REQUEST_URI']) ||
  133. preg_match("!" . $included_user_agents . "!is", $this->ua)) &&
  134. !$retricted_cookie &&
  135. (empty($this->options['page']['gzip']) ||
  136. empty($this->options['page']['flush'])) &&
  137. !headers_sent() &&
  138. (getenv('REQUEST_METHOD') == 'GET') &&
  139. empty($this->web_optimizer_stage) &&
  140. !$this->debug_mode &&
  141. empty($this->no_cache);
  142. /* check if we can get out cached page */
  143. if (!empty($this->cache_me)) {
  144. $this->uri = $this->convert_request_uri();
  145. /* gzip cached content before output? (plugins have onCache) */
  146. $gzip_me = is_array($this->options['plugins']);
  147. $cache_plain_key = $this->view->ensure_trailing_slash($this->uri) . 'index' . $this->ua_mod . '.html';
  148. $cache_key = $cache_plain_key .
  149. ($this->options['page']['flush'] ||
  150. empty($this->encoding_ext) ||
  151. $gzip_me ? '' : $this->encoding_ext);
  152. $timestamp = $this->cache_engine->get_mtime($cache_key);
  153. /* try to get from cache non-gzipped page if gzipped one doesn't exist */
  154. if (!$timestamp && !$this->options['page']['flush'] && !empty($this->encoding_ext) && !$gzip_me) {
  155. $timestamp = $this->cache_engine->get_mtime($cache_plain_key);
  156. $gzip_me = 1;
  157. }
  158. if ($timestamp && $this->time - $timestamp < $this->options['page']['cache_timeout']) {
  159. $content = $this->cache_engine->get_entry($gzip_me ? $cache_plain_key : $cache_key);
  160. if (class_exists('JUtility'))
  161. {
  162. $token = JUtility::getToken();
  163. $content = str_replace('##WSS_JTOKEN_WSS##', $token, $content);
  164. }
  165. /* execute plugin-specific logic */
  166. if (is_array($this->options['plugins'])) {
  167. foreach ($this->options['plugins'] as $plugin) {
  168. $plugin_file =
  169. $this->options['css']['installdir'] .
  170. 'plugins/' . $plugin . '.php';
  171. if (@is_file($plugin_file)) {
  172. include_once($plugin_file);
  173. $web_optimizer_plugin = new $plugin;
  174. $content =
  175. $web_optimizer_plugin->onAfterOptimization($content);
  176. }
  177. }
  178. }
  179. if ($gzip_me) {
  180. $cnt = $this->create_gz_compress($content,
  181. in_array($this->encoding, array('gzip', 'x-gzip')));
  182. if (!empty($cnt)) {
  183. $content = $cnt;
  184. /* skip gzip if we can't compress content */
  185. } else {
  186. $this->options['page']['gzip'] = 0;
  187. $this->encoding = '';
  188. }
  189. }
  190. $hash = crc32($content) .
  191. (empty($this->encoding) ? '' : '-' . str_replace("x-", "", $this->encoding));
  192. /* check for return visits */
  193. if ((isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
  194. stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) == '"' . $hash . '"') ||
  195. (isset($_SERVER['HTTP_IF_MATCH']) &&
  196. stripslashes($_SERVER['HTTP_IF_MATCH']) == '"' . $hash . '"')) {
  197. /* return visit and no modifications, so do not send anything */
  198. header ("HTTP/1.0 304 Not Modified");
  199. header ("Content-Length: 0");
  200. while (@ob_end_clean());
  201. die();
  202. }
  203. /* set ETag, thx to merzmarkus */
  204. header("ETag: \"" . $hash . "\"");
  205. /* set content-type */
  206. if (!empty($this->options['charset'])) {
  207. header("Content-Type: text/html; charset=" . $this->compress_options['charset']);
  208. }
  209. if (empty($this->web_optimizer_stage) &&
  210. $this->options['page']['clientside_cache']) {
  211. /* not really GMT but is valid locally */
  212. $ExpStr = date("D, d M Y H:i:s",
  213. $this->time + $this->options['page']['clientside_timeout']) . " GMT";
  214. header("Cache-Control: " .
  215. ($this->options['page']['gzip'] ? 'private' : 'public') .
  216. ", max-age=" .
  217. $this->options['page']['clientside_timeout']);
  218. header("Expires: " . $ExpStr);
  219. }
  220. /* check if cached content must be gzipped, can't gzip twice via php for flush */
  221. if ($this->options['page']['gzip'] && !$this->options['page']['flush']) {
  222. $this->set_gzip_header();
  223. }
  224. while (@ob_end_clean());
  225. echo $content;
  226. /* content is a head part, flush it after */
  227. if ($this->options['page']['flush']) {
  228. flush();
  229. $this->flushed = true;
  230. } else {
  231. die();
  232. }
  233. }
  234. }
  235. /* change some hosts if HTTPS is used */
  236. if ($this->https && !empty($this->options['page']['parallel_https'])) {
  237. $this->options['javascript']['host'] =
  238. $this->options['css']['host'] =
  239. $this->options['page']['parallel_hosts'] =
  240. $this->options['page']['parallel_https'];
  241. }
  242. /* number of external files calls to process */
  243. $this->initial_files = array();
  244. /* set internal encoding */
  245. $this->charset = empty($wss_encoding) ? 'utf8' : $wss_encoding;
  246. /* prepare escaped host */
  247. $this->host_escaped = str_replace('.', '\.', $this->host);
  248. /* activate application */
  249. $this->options['active'] = 1;
  250. if ($this->buffered) {
  251. /* Start things off */
  252. $this->start();
  253. }
  254. }
  255. /**
  256. * Write installation progress to JavaScript file
  257. *
  258. **/
  259. function write_progress ($progress) {
  260. $this->write_file($this->options['javascript']['cachedir'] . 'progress.html', $progress);
  261. }
  262. /**
  263. * Options are read from config.webo.php
  264. **/
  265. function set_options () {
  266. /* Set paths with new options */
  267. $this->options['document_root'] = empty($this->options['document_root']) ? '' : $this->options['document_root'];
  268. $this->view->set_paths($this->options['document_root']);
  269. /* Set local root if chained optimization */
  270. if (!empty($this->web_optimizer_stage)) {
  271. $this->view->paths['full']['current_directory'] = $this->view->paths['full']['document_root'];
  272. $this->view->paths['relative']['current_directory'] = $this->view->paths['relative']['document_root'];
  273. $_SERVER['REQUEST_URI'] = '/';
  274. /* force User Agent on chained optimization */
  275. $mods = array(
  276. /* all common browsers except IE */
  277. '',
  278. /* IE 6.0, when will it die? */
  279. '.ie6',
  280. /* IE 7.0 */
  281. '.ie7',
  282. /* IE 8.0 */
  283. '.ie8',
  284. /* Mobile Agents */
  285. '.ma'
  286. );
  287. $this->ua_mod = $mods[$this->cache_stage];
  288. }
  289. $this->premium = $this->view->validate_license($this->options['license'], $this->options['html_cachedir'], $this->options['host']);
  290. $this->set_user_agent();
  291. $webo_cachedir = $this->view->unify_dir_separator(realpath(dirname(__FILE__) . '/../') . '/');
  292. /* ensure trailing slashes */
  293. $this->options['html_cachedir'] = $this->view->ensure_trailing_slash($this->options['html_cachedir']);
  294. $this->options['css_cachedir'] = $this->view->ensure_trailing_slash($this->options['css_cachedir']);
  295. $this->options['javascript_cachedir'] = $this->view->ensure_trailing_slash($this->options['javascript_cachedir']);
  296. /* normalize host */
  297. if (!empty($this->options['host'])) {
  298. $this->options['host'] = preg_replace("!^https?://!", "", $this->options['host']);
  299. }
  300. /* Read in options */
  301. $full_options = array(
  302. "javascript" => array(
  303. "cachedir" => $this->options['javascript_cachedir'],
  304. "cachedir_relative" => str_replace($this->options['document_root'], "/", $this->options['javascript_cachedir']),
  305. "installdir" => $webo_cachedir,
  306. "gzip" => $this->options['gzip']['javascript'] &&
  307. ((!$this->options['htaccess']['mod_gzip'] &&
  308. !$this->options['htaccess']['mod_deflate'] &&
  309. (!$this->options['htaccess']['mod_rewrite'] ||
  310. !$this->options['htaccess']['mod_mime'] ||
  311. !$this->options['htaccess']['mod_expires'])) ||
  312. !$this->options['htaccess']['enabled']),
  313. "gzip_level" => round($this->options['gzip']['javascript_level']),
  314. "minify" => $this->options['minify']['javascript'],
  315. "minify_body" => $this->options['minify']['javascript_body'],
  316. "minify_with" => $this->options['minify']['with_jsmin'] ?
  317. 'jsmin' : ($this->options['minify']['with_yui'] ?
  318. 'yui' : ($this->options['minify']['with_packer'] ?
  319. 'packer' : '')),
  320. "minify_try" => $this->options['external_scripts']['include_try'],
  321. "minify_exclude" => $this->options['external_scripts']['minify_exclude'],
  322. "remove_duplicates" => $this->options['external_scripts']['duplicates'],
  323. "far_future_expires" => $this->options['far_future_expires']['javascript'] &&
  324. !$this->options['htaccess']['mod_expires'],
  325. "far_future_expires_php" => $this->options['far_future_expires']['javascript'],
  326. "far_future_expires_rewrite" => $this->options['htaccess']['mod_rewrite'] &&
  327. $this->options['htaccess']['enabled'] &&
  328. $this->options['far_future_expires']['javascript'],
  329. "unobtrusive_body" => $this->options['unobtrusive']['body'] &&
  330. !$this->options['unobtrusive']['all'],
  331. "external_scripts" => $this->options['external_scripts']['on'] &&
  332. $this->options['minify']['javascript'],
  333. "inline_scripts" => $this->options['external_scripts']['inline'] &&
  334. $this->options['minify']['javascript'],
  335. "external_scripts_head_end" => $this->options['external_scripts']['head_end'],
  336. "external_scripts_exclude" => $this->options['external_scripts']['ignore_list'],
  337. "dont_check_file_mtime" => $this->options['performance']['mtime'],
  338. "file" => $this->options['minify']['javascript_file'],
  339. "host" => $this->options['minify']['javascript_host'],
  340. "https" => $this->premium > 1 ? $this->options['parallel']['https'] : ''
  341. ),
  342. "css" => array(
  343. "cachedir" => $this->options['css_cachedir'],
  344. "cachedir_relative" => str_replace($this->options['document_root'], "/", $this->options['css_cachedir']),
  345. "installdir" => $webo_cachedir,
  346. "gzip" => $this->options['gzip']['css'] &&
  347. ((!$this->options['htaccess']['mod_gzip'] &&
  348. !$this->options['htaccess']['mod_deflate'] &&
  349. (!$this->options['htaccess']['mod_rewrite'] ||
  350. !$this->options['htaccess']['mod_mime'] ||
  351. !$this->options['htaccess']['mod_expires'])) ||
  352. !$this->options['htaccess']['enabled']),
  353. "gzip_level" => round($this->options['gzip']['css_level']),
  354. "minify" => $this->options['minify']['css'],
  355. "minify_body" => $this->options['minify']['css_body'],
  356. "minify_with" => $this->options['minify']['css_min'] == 2 ?
  357. 'tidy' : ($this->options['minify']['css_min'] ? 'basic' : ''),
  358. "far_future_expires" => $this->options['far_future_expires']['css'] &&
  359. !$this->options['htaccess']['mod_expires'],
  360. "far_future_expires_php" => $this->options['far_future_expires']['css'],
  361. "far_future_expires_rewrite" => $this->options['htaccess']['mod_rewrite'] &&
  362. $this->options['htaccess']['enabled'] &&
  363. $this->options['far_future_expires']['css'],
  364. "data_uris" => $this->options['data_uris']['on'],
  365. /* disable mhtml for IE7- under HTTPS */
  366. "data_uris_mhtml" => $this->options['data_uris']['mhtml'] &&
  367. (!$this->https || (!strpos($this->ua, 'MSIE 6') && !strpos($this->ua, 'MSIE 7'))),
  368. "data_uris_separate" => $this->options['data_uris']['separate'] &&
  369. ((!empty($this->ua_mod) &&
  370. $this->options['data_uris']['mhtml']) ||
  371. (empty($this->ua_mod) &&
  372. $this->options['data_uris']['on'])),
  373. "data_uris_domloaded" => $this->options['data_uris']['domloaded'],
  374. "data_uris_size" => round($this->options['data_uris']['size']),
  375. "data_uris_mhtml_size" => round($this->options['data_uris']['mhtml_size']),
  376. "data_uris_exclude" => $this->options['data_uris']['ignore_list'],
  377. "data_uris_exclude_mhtml" => $this->options['data_uris']['additional_list'],
  378. "css_sprites" => $this->options['css_sprites']['enabled'],
  379. "css_sprites_expires_rewrite" => (!$this->options['htaccess']['mod_rewrite'] ||
  380. !$this->options['htaccess']['enabled']) &&
  381. $this->options['far_future_expires']['images'],
  382. "css_sprites_ignore" => $this->options['css_sprites']['ignore'],
  383. "css_sprites_exclude" => $this->options['css_sprites']['ignore_list'],
  384. "truecolor_in_jpeg" => $this->options['css_sprites']['truecolor_in_jpeg'],
  385. "aggressive" => $this->options['css_sprites']['aggressive'],
  386. "no_ie6" => $this->options['css_sprites']['no_ie6'],
  387. "dimensions_limited" => round($this->options['css_sprites']['dimensions_limited']),
  388. "css_sprites_extra_space" => $this->options['css_sprites']['extra_space'],
  389. "punypng" => (!empty($this->options['punypng']) ? $this->options['punypng'] : '') &&
  390. ($this->premium > 1),
  391. "css_restore_properties" => $this->options['performance']['restore_properties'] &&
  392. ($this->premium > 1),
  393. "unobtrusive_body" => false,
  394. "parallel" => $this->options['parallel']['enabled'],
  395. "parallel_hosts" => $this->options['parallel']['allowed_list'],
  396. "external_scripts" => $this->options['external_scripts']['css'],
  397. "inline_scripts" => $this->options['external_scripts']['css_inline'],
  398. "external_scripts_exclude" => $this->options['external_scripts']['additional_list'],
  399. "include_code" => $this->options['external_scripts']['include_code'],
  400. "dont_check_file_mtime" => $this->options['performance']['mtime'],
  401. "file" => $this->options['minify']['css_file'],
  402. "host" => $this->options['minify']['css_host'],
  403. "https" => $this->premium > 1 ? $this->options['parallel']['https'] : ''
  404. ),
  405. "page" => array(
  406. "cachedir" => $this->options['html_cachedir'],
  407. "cache_engine" => $this->options['performance']['cache_engine'],
  408. "cache_engine_options" => $this->options['performance']['cache_engine_options'],
  409. "cachedir_relative" => str_replace($this->options['document_root'], "/", $this->options['html_cachedir']),
  410. "installdir" => $webo_cachedir,
  411. "host" => $this->options['host'],
  412. "gzip" => $this->options['gzip']['page'] &&
  413. ((!$this->options['htaccess']['mod_gzip'] &&
  414. !$this->options['htaccess']['mod_deflate']) ||
  415. !$this->options['htaccess']['enabled']),
  416. "gzip_noie" => $this->options['gzip']['noie'],
  417. "gzip_level" => round($this->options['gzip']['page_level']),
  418. "gzip_cookie" => $this->options['gzip']['cookie'],
  419. "minify" => $this->options['minify']['page'],
  420. "minify_aggressive" => $this->options['minify']['html_one_string'],
  421. "remove_comments" => $this->options['minify']['html_comments'],
  422. "dont_check_file_mtime" => $this->options['performance']['mtime'],
  423. "cache_images" => $this->options['far_future_expires']['images'],
  424. "far_future_expires_rewrite" => (!($this->options['htaccess']['mod_rewrite'] ||
  425. $this->options['htaccess']['mod_expires']) ||
  426. !$this->options['htaccess']['enabled']) &&
  427. $this->options['far_future_expires']['images'],
  428. "far_future_expires_external" => $this->options['far_future_expires']['external'],
  429. "clientside_cache" => $this->options['far_future_expires']['html'],
  430. "clientside_timeout" => $this->options['far_future_expires']['html_timeout'],
  431. "cache" => $this->options['html_cache']['enabled'],
  432. "cache_timeout" => $this->options['html_cache']['timeout'],
  433. "flush" => $this->options['html_cache']['flush_only'],
  434. "flush_size" => $this->options['html_cache']['flush_size'],
  435. "cache_ignore" => $this->options['html_cache']['ignore_list'],
  436. "cache_params" => $this->options['html_cache']['params'],
  437. "allowed_user_agents" => $this->options['html_cache']['allowed_list'],
  438. "exclude_cookies" => $this->options['html_cache']['additional_list'],
  439. "parallel" => $this->options['parallel']['enabled'],
  440. "parallel_hosts" => $this->options['parallel']['allowed_list'],
  441. "parallel_satellites" => $this->options['parallel']['additional'],
  442. "parallel_satellites_hosts" => $this->options['parallel']['additional_list'],
  443. "parallel_ignore" => $this->options['parallel']['ignore_list'],
  444. "parallel_css" => $this->options['parallel']['css'],
  445. "parallel_javascript" => $this->options['parallel']['javascript'],
  446. "parallel_ftp" => $this->options['parallel']['ftp'],
  447. "parallel_https" => $this->options['parallel']['https'],
  448. "unobtrusive_informers" => $this->options['unobtrusive']['informers'] &&
  449. ($this->premium > 1),
  450. "unobtrusive_counters" => $this->options['unobtrusive']['counters'] &&
  451. ($this->premium > 1),
  452. "unobtrusive_ads" => $this->options['unobtrusive']['ads'] &&
  453. ($this->premium > 1),
  454. "unobtrusive_all" => $this->options['unobtrusive']['all'] &&
  455. ($this->premium > 1),
  456. "unobtrusive_iframes" => $this->options['unobtrusive']['iframes'] &&
  457. ($this->premium > 1),
  458. "unobtrusive_onload" => $this->options['unobtrusive']['on'] &&
  459. ($this->premium > 1),
  460. "unobtrusive_inline" => $this->options['unobtrusive']['on'] == 2 &&
  461. ($this->premium > 1),
  462. "footer" => $this->premium ? $this->options['footer']['text'] : 1,
  463. "footer_image" => $this->options['footer']['image'],
  464. "footer_text" => $this->options['footer']['link'],
  465. "footer_style" => $this->options['footer']['css_code'],
  466. "spot" => $this->premium ? $this->options['footer']['spot'] : 1,
  467. "counter" => $this->options['footer']['counter'],
  468. "htaccess_username" => $this->options['external_scripts']['user'],
  469. "htaccess_password" => $this->options['external_scripts']['pass'],
  470. "html_tidy" => $this->options['performance']['plain_string'],
  471. "sprites" => $this->options['css_sprites']['html_sprites'],
  472. "dimensions_limited" => round($this->options['css_sprites']['html_limit']),
  473. "per_page" => $this->options['css_sprites']['html_page']
  474. ),
  475. "document_root" => $this->options['document_root'],
  476. "document_root_relative" => str_replace("//", "/", str_replace($this->options['document_root'], "/", $this->options['website_root'])),
  477. "website_root" => $this->options['website_root'],
  478. "cache_version" => round($this->options['performance']['cache_version']),
  479. "uniform_cache" => $this->options['performance']['uniform_cache'],
  480. "plugins" => ($this->premium > 1) &&
  481. !empty($this->options['plugins']) ? explode(" ", $this->options['plugins']) : '',
  482. "restricted" => ($this->premium > 1) &&
  483. !empty($this->options['restricted']) ? $this->options['restricted'] : '',
  484. "days_to_delete" => round($this->options['performance']['delete_old']),
  485. "charset" => $this->options['charset']
  486. );
  487. $this->lc = $this->options['license'];
  488. /* overwrite other options array that we passed in */
  489. $this->options = $full_options;
  490. }
  491. /**
  492. * Start saving the output buffer
  493. *
  494. **/
  495. function start () {
  496. ob_start();
  497. ob_start();
  498. ob_implicit_flush(0);
  499. }
  500. /**
  501. * Compress passes content directly
  502. *
  503. **/
  504. function compress ($content) {
  505. $this->finish($content);
  506. }
  507. /**
  508. * Do work and return output buffer
  509. *
  510. **/
  511. function finish ($content = false) {
  512. /* disable any actions if not active */
  513. if (empty($this->options['active'])) {
  514. return $content;
  515. }
  516. if ($content === false) {
  517. $this->content = ob_get_clean();
  518. /* clear all other buffers */
  519. while (@ob_end_clean());
  520. } else {
  521. $this->content = $content;
  522. }
  523. /* execute plugin-specific logic, BeforeOptimization event */
  524. if (is_array($this->options['plugins'])) {
  525. foreach ($this->options['plugins'] as $plugin) {
  526. $plugin_file =
  527. $this->options['css']['installdir'] .
  528. 'plugins/' . $plugin . '.php';
  529. if (@is_file($plugin_file)) {
  530. include_once($plugin_file);
  531. $web_optimizer_plugin = new $plugin;
  532. $this->content =
  533. $web_optimizer_plugin->onBeforeOptimization($this->content);
  534. }
  535. }
  536. }
  537. $skip = 0;
  538. if (function_exists('get_headers')) {
  539. $headers = headers_list();
  540. /* define if Content-Type is text/html and allow it */
  541. foreach ($headers as $head) {
  542. $header = strtolower($head);
  543. if (strpos($header, 'content-type:') !== false || strpos($header, 'location:') !== false) {
  544. $skip++;
  545. }
  546. if (strpos($header, 'text/html') || strpos($header, 'application/xhtml+xml')) {
  547. $skip--;
  548. }
  549. if (strpos($header, 'content-base') !== false) {
  550. $this->basehref = substr($head, 14);
  551. }
  552. }
  553. }
  554. /* also skip AJAX requests with X-Requested-With: XMLHttpRequest */
  555. if (!$skip &&
  556. !empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
  557. $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
  558. $skip = 1;
  559. }
  560. /* also skip some CMS-ralted parameters */
  561. if (!$skip && !empty($_GET['no_html'])) {
  562. $skip = 1;
  563. }
  564. /* skip some extensions */
  565. if (!$skip && !empty($_SERVER['QUERY_STRING'])) {
  566. $query = explode('.', $_SERVER['QUERY_STRING']);
  567. $ext = strtolower($query[count($query) - 1]);
  568. if (in_array($ext, array('pdf', 'doc', 'xls', 'docx', 'xlsx'))) {
  569. $skip = 1;
  570. }
  571. }
  572. /* skip some known cases of non-HTML content */
  573. if (!$skip) {
  574. /* reduce amount of viewing content, accelerate 'fast check' by 1% */
  575. $spot = substr($this->content, 0, 60);
  576. if (strpos($spot, '<methodResponse') !== false ||
  577. strpos($spot, '<rss') !== false ||
  578. strpos($spot, '<feed') !== false ||
  579. strpos($spot, '<urlset') !== false ||
  580. strpos($spot, '<smf') !== false ||
  581. strpos($spot, '{') === 0 ||
  582. strlen($this->content) < 200) {
  583. $skip = 1;
  584. }
  585. }
  586. /* restrict URL from configuration */
  587. if (!$skip &&
  588. !empty($this->options['restricted'])) {
  589. if (preg_match("@" . preg_replace("/ /", "|",
  590. preg_replace("/([\?!\^\$\|\(\)\[\]\{\}])/",
  591. "\\\\$1",
  592. $this->options['restricted'])) . "@",
  593. $_SERVER['REQUEST_URI'])) {
  594. $skip = 1;
  595. }
  596. }
  597. /* skip RSS, SMF xml format */
  598. if (!$skip) {
  599. /* find all files in head to process */
  600. $this->get_script_array();
  601. /* Run the functions specified in options */
  602. if (is_array($this->options)) {
  603. foreach ($this->options as $func => $option) {
  604. if (method_exists($this, $func)) {
  605. if (!empty($option['gzip']) ||
  606. !empty($option['minify']) ||
  607. !empty($option['far_future_expires']) ||
  608. !empty($option['parallel']) ||
  609. !empty($option['unobtrusive_all']) ||
  610. !empty($option['unobtrusive_ads']) ||
  611. !empty($option['unobtrusive_counters']) ||
  612. !empty($option['unobtrusive_informers']) ||
  613. !empty($option['unobtrusive_iframes']) ||
  614. !empty($option['cache']) ||
  615. !empty($option['sprites'])) {
  616. if (!empty($this->web_optimizer_stage)) {
  617. $this->write_progress($this->web_optimizer_stage++);
  618. }
  619. $this->$func($option, $func);
  620. }
  621. }
  622. }
  623. }
  624. if (!empty($this->web_optimizer_stage)) {
  625. $this->write_progress($this->web_optimizer_stage);
  626. /* redirect to installation page if chained optimization if finished */
  627. if ($this->web_optimizer_stage > 85) {
  628. if ($this->chained_redirect === 'optimizing.php') {
  629. $this->write_progress(97);
  630. header('Location: ../index.php?page=install_stage_3&Submit=1&web_optimizer_stage=97&wss__password=' .
  631. $this->password);
  632. }
  633. /* else redirect to the next stage */
  634. } else {
  635. header('Location: ' . $this->chained_redirect . '?web_optimizer_stage=' .
  636. $this->web_optimizer_stage .
  637. '&password=' .
  638. $this->password .
  639. '&web_optimizer_debug=1');
  640. }
  641. while (@ob_end_clean());
  642. die();
  643. }
  644. }
  645. /* remove marker for styles */
  646. $this->content = str_replace('@@@WSSSTYLES@@@', '', $this->content);
  647. /* Return content to requestor */
  648. if ($content) {
  649. return $this->content;
  650. /* or echo content to the browser */
  651. } else {
  652. /* HTTP/1.0 needs Content-Length sometimes. With PHP4 we can't check when exactly. */
  653. if (!empty($this->encoding)) {
  654. header('Content-Length: ' . strlen($this->content));
  655. }
  656. echo $this->content;
  657. /* It's obvious to send anything right after gzipped content */
  658. if (!empty($this->encoding)) {
  659. while (@ob_end_clean());
  660. die();
  661. }
  662. }
  663. }
  664. /**
  665. * GZIP and minify the javascript as required
  666. *
  667. **/
  668. function javascript ($options,$type) {
  669. /* prepare list of files to process */
  670. $script_files = array();
  671. foreach ($this->initial_files as $file) {
  672. if (!empty($file['tag']) && $file['tag'] == 'script') {
  673. $script_files[] = $file;
  674. }
  675. }
  676. if (!empty($options['minify']) && !empty($script_files)) {
  677. $this->content = $this->do_compress(
  678. array(
  679. 'cachedir' => $options['cachedir'],
  680. 'cachedir_relative' => $options['cachedir_relative'],
  681. 'installdir' => $options['installdir'],
  682. 'host' => $options['host'],
  683. 'tag' => 'script',
  684. 'type' => 'text/javascript',
  685. 'ext' => 'js',
  686. 'src' => 'src',
  687. 'self_close' => false,
  688. 'gzip' => $options['gzip'],
  689. 'gzip_level' => $options['gzip_level'],
  690. 'minify' => $options['minify'],
  691. 'minify_body' => $options['minify_body'],
  692. 'minify_with' => $options['minify_with'],
  693. 'minify_try' => $options['minify_try'],
  694. 'minify_exclude' => $options['minify_exclude'],
  695. 'remove_duplicates' => $options['remove_duplicates'],
  696. 'far_future_expires' => $options['far_future_expires'],
  697. 'far_future_expires_php' => $options['far_future_expires_php'],
  698. 'far_future_expires_rewrite' => $options['far_future_expires_rewrite'],
  699. 'header' => $type,
  700. 'css_sprites' => false,
  701. 'css_sprites_exclude' => false,
  702. 'aggressive' => false,
  703. 'no_ie6' => false,
  704. 'dimensions_limited' => false,
  705. 'css_sprites_extra_space' => false,
  706. 'data_uris' => false,
  707. 'mhtml' => false,
  708. 'unobtrusive_body' => $options['unobtrusive_body'],
  709. 'external_scripts' => $options['external_scripts'],
  710. 'inline_scripts' => $options['inline_scripts'],
  711. 'external_scripts_head_end' => $options['external_scripts_head_end'],
  712. 'external_scripts_exclude' => $options['external_scripts_exclude'],
  713. 'dont_check_file_mtime' => $options['dont_check_file_mtime'],
  714. 'file' => $options['file'],
  715. 'https' => $options['https']
  716. ),
  717. $this->content,
  718. $script_files
  719. );
  720. }
  721. }
  722. /**
  723. * GZIP and minify the CSS as required
  724. *
  725. **/
  726. function css ($options, $type) {
  727. /* prepare list of files to process */
  728. $link_files = array();
  729. foreach ($this->initial_files as $file) {
  730. if (!empty($file['tag']) && $file['tag'] == 'link') {
  731. $link_files[] = $file;
  732. }
  733. }
  734. if (!empty($options['minify']) && !empty($link_files)) {
  735. /* Compress separately for each media type*/
  736. $this->content = $this->do_compress(
  737. array(
  738. 'cachedir' => $options['cachedir'],
  739. 'cachedir_relative' => $options['cachedir_relative'],
  740. 'installdir' => $options['installdir'],
  741. 'host' => $options['host'],
  742. 'tag' => 'link',
  743. 'type' => 'text/css',
  744. 'ext' => 'css',
  745. 'src' => 'href',
  746. 'rel' => 'stylesheet',
  747. 'data_uris' => $options['data_uris'],
  748. 'mhtml' => $options['data_uris_mhtml'],
  749. 'data_uris_separate' => $options['data_uris_separate'],
  750. 'data_uris_domloaded' => $options['data_uris_domloaded'],
  751. 'data_uris_size' => $options['data_uris_size'],
  752. 'data_uris_exclude' => $options['data_uris_exclude'],
  753. 'mhtml' => $options['data_uris_mhtml'],
  754. 'mhtml_size' => $options['data_uris_mhtml_size'],
  755. 'mhtml_exclude' => $options['data_uris_exclude_mhtml'],
  756. 'css_sprites' => $options['css_sprites'],
  757. 'css_sprites_ignore' => $options['css_sprites_ignore'],
  758. 'css_sprites_exclude' => $options['css_sprites_exclude'],
  759. 'truecolor_in_jpeg' => $options['truecolor_in_jpeg'],
  760. 'aggressive' => $options['aggressive'],
  761. 'no_ie6' => $options['no_ie6'],
  762. 'dimensions_limited' => $options['dimensions_limited'],
  763. 'css_sprites_extra_space' => $options['css_sprites_extra_space'],
  764. 'css_sprites_expires_rewrite' => $options['css_sprites_expires_rewrite'],
  765. 'punypng' => $options['punypng'],
  766. 'css_restore_properties' => $options['css_restore_properties'],
  767. 'self_close' => true,
  768. 'gzip' => $options['gzip'],
  769. 'gzip_level' => $options['gzip_level'],
  770. 'minify' => $options['minify'],
  771. 'minify_body' => $options['minify_body'],
  772. 'minify_with' => $options['minify_with'],
  773. 'far_future_expires' => $options['far_future_expires'],
  774. 'far_future_expires_php' => $options['far_future_expires_php'],
  775. 'far_future_expires_rewrite' => $options['far_future_expires_rewrite'],
  776. 'header' => $type,
  777. 'unobtrusive_body' => $options['unobtrusive_body'],
  778. 'parallel' => $options['parallel'],
  779. 'parallel_hosts' => $options['parallel_hosts'],
  780. 'external_scripts' => $options['external_scripts'],
  781. 'inline_scripts' => $options['inline_scripts'],
  782. 'external_scripts_exclude' => $options['external_scripts_exclude'],
  783. 'include_code' => $options['include_code'],
  784. 'dont_check_file_mtime' => $options['dont_check_file_mtime'],
  785. 'file' => $options['file'],
  786. 'https' => $options['https']
  787. ),
  788. $this->content,
  789. $link_files
  790. );
  791. }
  792. }
  793. /**
  794. * GZIP and minify the page itself as required
  795. *
  796. **/
  797. function page ($options, $type) {
  798. if (empty($this->web_optimizer_stage) && $options['clientside_cache'] && empty($this->flushed)) {
  799. /* not really GMT but is valid locally */
  800. $ExpStr = date("D, d M Y H:i:s",
  801. $this->time + $this->options['page']['clientside_timeout']) . " GMT";
  802. header("Cache-Control: private, max-age=" .
  803. $this->options['page']['clientside_timeout']);
  804. header("Expires: " . $ExpStr);
  805. }
  806. /* move informers, counters, ads, and iframes before </body> */
  807. $this->replace_informers($options);
  808. /* Minify page itself or parse multiple hosts */
  809. if (!empty($options['minify']) ||
  810. (!empty($options['parallel']) &&
  811. !empty($options['parallel_hosts'])) ||
  812. !empty($options['unobtrusive_all']) ||
  813. !empty($this->options['page']['far_future_expires_rewrite']) ||
  814. !empty($this->options['page']['far_future_expires_external']) ||
  815. !empty($this->options['page']['sprites'])) {
  816. $this->content = $this->trimwhitespace($this->content);
  817. }
  818. /* remove marker for styles and BOM */
  819. $this->content = str_replace(array("@@@WSSSTYLES@@@", "@@@WSSSCRIPT@@@", ""), "", $this->content);
  820. /* Add script to check gzip possibility */
  821. if (!empty($options['gzip_cookie']) && empty($_COOKIE['_wo_gzip_checked']) && empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
  822. $cookie = '<script type="text/javascript" src="' . $options['cachedir_relative'] . 'wo.cookie.php"></script>';
  823. if ($options['html_tidy'] && ($bodypos = strpos($this->content, '</body>'))) {
  824. $this->content = substr_replace($this->content, $cookie, $bodypos, 0);
  825. } elseif ($options['html_tidy'] && ($bodypos = strpos($this->content, '</BODY>'))) {
  826. $this->content = substr_replace($this->content, $cookie, $bodypos, 0);
  827. } else {
  828. $this->content = preg_replace('@(</body>)@is', $cookie . "$1", $this->content);
  829. /* a number of engines doesn't set </body> */
  830. if (!strpos($this->content, "wo.cookie")) {
  831. $this->content .= $cookie;
  832. }
  833. }
  834. }
  835. /* execute plugin-specific logic, AfterOptimization event */
  836. if (is_array($this->options['plugins'])) {
  837. foreach ($this->options['plugins'] as $plugin) {
  838. $plugin_file =
  839. $this->options['css']['installdir'] .
  840. 'plugins/' . $plugin . '.php';
  841. if (@is_file($plugin_file)) {
  842. include_once($plugin_file);
  843. $web_optimizer_plugin = new $plugin;
  844. $this->content =
  845. $web_optimizer_plugin->onAfterOptimization($this->content);
  846. }
  847. }
  848. }
  849. if (!empty($this->web_optimizer_stage)) {
  850. $this->write_progress($this->web_optimizer_stage++);
  851. }
  852. /* check if we need to store cached page */
  853. if (!empty($this->cache_me)) {
  854. /* add client side replacement for WordPress comment fields */
  855. if (defined('WP_CACHE')) {
  856. foreach ($_COOKIE as $key => $value) {
  857. if (strpos($key, 'comment_author') === 0) {
  858. $this->content = str_replace('value="'. urldecode($value) .'"', 'value=""', $this->content);
  859. }
  860. }
  861. $this->content = preg_replace("@(</body>)@is", '<script type="text/javascript">(function(){var a=document.cookie.split(";"),b,c=0,d,e;while(b=a[c++]){if(b.indexOf("comment_author_")!=-1){d=b.split("=");e=document.getElementById(d[0].replace(/(_?[a-f0-9]{32,}|\s?comment_author_)/g,"")||"author");if(e){e.value=unescape(d[1].replace(/\+/g," "))}}}}())</script>$1', $this->content);
  862. }
  863. if (class_exists('JUtility'))
  864. {
  865. $token = JUtility::getToken();
  866. $this->content = str_replace($token, '##WSS_JTOKEN_WSS##', $this->content);
  867. }
  868. /* prepare flushed part of content */
  869. if (!empty($options['flush']) && empty($this->encoding)) {
  870. if (empty($options['flush_size'])) {
  871. if ($this->options['page']['html_tidy'] && ($headpos = strpos($source, '</head>'))) {
  872. $content_to_write = substr($this->content, 0, $headpos + 7);
  873. } elseif ($this->options['page']['html_tidy'] && ($headpos = strpos($source, '</HEAD>'))) {
  874. $content_to_write = substr($this->content, 0, $headpos + 7);
  875. } else {
  876. $content_to_write = preg_replace("!(.*<\/head>).*!is", "$1", $this->content);
  877. }
  878. } else {
  879. $content_to_write =
  880. substr($this->content, 0, $options['flush_size']);
  881. }
  882. }
  883. $ordinary_cache_key = $this->view->ensure_trailing_slash($this->uri) . 'index' . $this->ua_mod . '.html';
  884. $cache_key = $ordinary_cache_key . (empty($this->encoding_ext) ? '' : $this->encoding_ext);
  885. $timestamp = $this->cache_engine->get_mtime($cache_key);
  886. /* set ETag, thx to merzmarkus */
  887. if (empty($options['flush'])) {
  888. header("ETag: \"" .
  889. md5($this->content) .
  890. (empty($this->encoding) ? '' : '-' .
  891. str_replace("x-", "", $this->encoding)) .
  892. "\"");
  893. }
  894. if (empty($timestamp) || $this->time - $timestamp > $options['cache_timeout']) {
  895. if (!empty($options['gzip']) && !empty($this->encoding)) {
  896. $content_to_write = $this->create_gz_compress($this->content,
  897. in_array($this->encoding, array('gzip', 'x-gzip')));
  898. /* or just write full or non-gzipped content */
  899. } elseif (empty($options['flush']) || !empty($this->encoding)) {
  900. $content_to_write = $this->content;
  901. }
  902. /* don't create empty files */
  903. if (!empty($content_to_write)) {
  904. $this->cache_engine->put_entry($cache_key, $content_to_write, $this->time);
  905. }
  906. /* create uncompressed file for plugins */
  907. if (is_array($this->options['plugins']) &&
  908. !empty($this->encoding_ext)) {
  909. $this->cache_engine->put_entry($ordinary_cache_key, $this->content, $this->time);
  910. }
  911. }
  912. }
  913. /* execute plugin-specific logic, Cache event */
  914. if (is_array($this->options['plugins'])) {
  915. foreach ($this->options['plugins'] as $plugin) {
  916. $plugin_file =
  917. $this->options['css']['installdir'] .
  918. 'plugins/' . $plugin . '.php';
  919. if (@is_file($plugin_file)) {
  920. include_once($plugin_file);
  921. $web_optimizer_plugin = new $plugin;
  922. $this->content =
  923. $web_optimizer_plugin->onCache($this->content);
  924. }
  925. }
  926. }
  927. /* strip from content flushed part */
  928. if (!empty($this->flushed)) {
  929. if (empty($options['flush_size'])) {
  930. $options['flush_size'] = strlen($content_to_write);
  931. }
  932. $this->content = substr($this->content, $options['flush_size']);
  933. }
  934. /* Gzip page itself */
  935. if(!empty($options['gzip']) && !empty($this->encoding)) {
  936. $content = $this->create_gz_compress($this->content,
  937. in_array($this->encoding, array('gzip', 'x-gzip')));
  938. if (!empty($content)) {
  939. $this->set_gzip_header();
  940. $this->content = $content;
  941. }
  942. }
  943. }
  944. /**
  945. * Write content to file
  946. *
  947. **/
  948. function write_file ($file, $content, $upload = 0, $mime = '') {
  949. if (@function_exists('file_put_contents')) {
  950. @file_put_contents($file, $content);
  951. } else {
  952. $fp = @fopen($file, "a");
  953. if ($fp) {
  954. /* block file from writing */
  955. @flock($fp, LOCK_EX);
  956. /* erase content and move to the beginning */
  957. @ftruncate($fp, 0);
  958. @fseek($fp, 0);
  959. @fwrite($fp, $content);
  960. @fclose($fp);
  961. }
  962. }
  963. @touch($file, $this->time);
  964. @chmod($file, octdec("0644"));
  965. if ($upload && !empty($this->options['page']['parallel_ftp'])) {
  966. $this->view->upload_cdn($file,
  967. $this->options['document_root'],
  968. $this->options['page']['parallel_ftp'],
  969. $mime,
  970. $this->options['page']['host']);
  971. }
  972. }
  973. /**
  974. * Adds multiple hosts to HTML for images
  975. *
  976. **/
  977. function add_multiple_hosts ($content, $hosts, $satellites, $satellites_hosts) {
  978. /* limit by 4 */
  979. if (count($hosts) > 4) {
  980. $hosts = array($hosts[0], $hosts[1], $hosts[2], $hosts[3]);
  981. }
  982. if (count($satellites_hosts) > 4) {
  983. $satellites_hosts = array($satellites_hosts[0], $satellites_hosts[1], $satellites_hosts[2], $satellites_hosts[3]);
  984. }
  985. $count = count($hosts);
  986. $count_satellites = count($satellites_hosts);
  987. $replaced = array();
  988. $IMG = strpos($content, '<IMG');
  989. if (!empty($this->options['page']['html_tidy']) && !$IMG) {
  990. $_content = $content;
  991. while ($pos = strpos($_content, '<img')) {
  992. $len = strpos(substr($_content, $pos), '>');
  993. /* gets image tag w/o the closing >, it's OK */
  994. $imgs[] = array(substr($_content, $pos, $len));
  995. $_content = substr_replace($_content, '', $pos, $len);
  996. }
  997. } elseif (empty($this->options['page']['html_tidy']) || $IMG) {
  998. preg_match_all("!<img[^>]+>!is", $content, $imgs, PREG_SET_ORDER);
  999. }
  1000. if (!empty($this->options['page']['sprites']) && !empty($imgs)) {
  1001. require($this->options['css']['installdir'] . 'libs/php/html.sprites.php');
  1002. $html_sprites = new html_sprites($imgs, $this->options, $this);
  1003. $content = $html_sprites->process($content);
  1004. }
  1005. if (!empty($imgs)) {
  1006. $ignore_list = explode(" ", $this->options['page']['parallel_ignore']);
  1007. $ignore_sprites = explode(" ", $this->options['css']['css_sprites_exclude']);
  1008. foreach ($imgs as $image) {
  1009. if (!empty($this->options['page']['html_tidy']) && ($pos=strpos($image[0], ' src="'))) {
  1010. $old_src = substr($image[0], $pos+6, strpos(substr($image[0], $pos+6), '"'));
  1011. } elseif (!empty($this->options['page']['html_tidy']) && ($pos=strpos($image[0], " src='"))) {
  1012. $old_src = substr($image[0], $pos+6, strpos(substr($image[0], $pos+6), "'"));
  1013. } else {
  1014. $old_src = preg_replace("!^['\"\s]*(.*?)['\"\s]*$!is", "$1", preg_replace("!.*[\"'\s]src\s*=\s*(\"[^\"]+\"|'[^']+'|[\S]+).*!is", "$1", $image[0]));
  1015. }
  1016. $old_src = $this->convert_basehref($old_src);
  1017. $old_src_param = ($old_src_param_pos = strpos($old_src, '?')) ? substr($old_src, $old_src_param_pos) : '';
  1018. /* image file name to check through ignore list */
  1019. $img = preg_replace("@.*/@", "", $old_src);
  1020. $absolute_src = $this->convert_path_to_absolute($old_src,
  1021. array('file' => $_SERVER['REQUEST_URI']));
  1022. if (empty($replaced[$image[0]])) {
  1023. if (!empty($this->options['page']['sprites']) &&
  1024. ((!in_array($img, $ignore_sprites) && empty($this->options['css']['css_sprites_ignore'])) ||
  1025. (in_array($img, $ignore_sprites) && !empty($this->options['css']['css_sprites_ignore']))) &&
  1026. !empty($html_sprites->css_images[$absolute_src]) && !empty($html_sprites->css_images[$absolute_src][2]) &&
  1027. (empty($this->ua_mod) || $this->ua_mod != '.ie6' || empty($this->options['css']['no_ie6']))) {
  1028. $class = substr($html_sprites->css_images[$absolute_src][8], 1);
  1029. if (!empty($this->options['page']['html_tidy']) &&
  1030. (strpos($image[0], 'class') || strpos($image[0], 'CLASS'))) {
  1031. if ($pos=strpos($image[0], ' class="')) {
  1032. $end = strpos(substr($image[0], $pos + 7), '"');
  1033. $new_image = substr($image[0], 0, $pos + 7 + $end) .
  1034. ' ' . $class . substr($image[0], $pos + 7 + $end);
  1035. } elseif ($pos=strpos($image[0], " class='")) {
  1036. $end = strpos(substr($image[0], $pos + 7), "'");
  1037. $new_image = substr($image[0], 0, $pos + 7 + $end) .
  1038. ' ' . $class . substr($image[0], $end);
  1039. } else {
  1040. $new_image = preg_replace("!(.*['\"\s]class\s*=\s*)([\"'])?([^\"']+)([\"'])?([\s/>])(.*)!is", "$1$2$3 " .
  1041. $class . "$4$5$6", $image[0]);
  1042. }
  1043. } elseif (preg_match("@['\"\s]class\s*=\s*['\"]@is", $image[0])) {
  1044. $new_image = preg_replace("!(.*['\"\s]class\s*=\s*)([\"'])?([^\"']+)([\"'])?([\s/>])(.*)!is", "$1$2$3 " .
  1045. $class . "$4$5$6", $image[0]);
  1046. } elseif (preg_match("@['\"\s]class\s*=\s*@is", $image[0])) {
  1047. $new_image = preg_replace("!(.*['\"\s]class\s*=\s*)([^\s]+)\s!is", "$1\"$2 " .
  1048. $class . "\" ", $image[0]);
  1049. } else {
  1050. $new_image = substr($image[0], 0, 4) . ' class="' .
  1051. $class . '"' . substr($image[0], 4);
  1052. }
  1053. /* add transparent GIF or data:URI chunk */
  1054. $new_src = (empty($this->ua_mod) ||
  1055. substr($this->ua_mod, 3, 1) > 7) &&
  1056. !$this->options['uniform_cache'] ?
  1057. 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' :
  1058. ((empty($this->options['page']['far_future_expires_rewrite']) ?
  1059. '' : $this->options['page']['cachedir_relative'] . 'wo.static.php?') .
  1060. $this->options['page']['cachedir_relative'] . '0.gif');
  1061. /* are we operating with multiple hosts */
  1062. } elseif (!empty($this->options['page']['parallel']) &&
  1063. !empty($this->options['page']['parallel_hosts']) &&
  1064. (!count($ignore_list) || !in_array(str_replace($old_src_param, '', $img), $ignore_list))) {
  1065. /* skip images on different hosts */
  1066. if (preg_match("!//(www\.)?" . $this->host_escaped . "/+!i", $old_src) || strpos($old_src, '//') === false) {
  1067. /* using secure host */
  1068. if ($this->https && !empty($this->options['page']['parallel_https'])) {
  1069. $new_host = $this->options['page']['parallel_https'];
  1070. } else {
  1071. /* calculating unique sum from image src */
  1072. $sum = 0;
  1073. $i = ceil(strlen($old_src)/2);
  1074. while (isset($old_src{$i++})) {
  1075. $sum += ord($old_src{$i-1});
  1076. }
  1077. $host = $hosts[$sum%$count];
  1078. /* if we have dot in the distribution host - it's a domain name */
  1079. $new_host = $host .
  1080. ((strpos($host, '.') === false) ?
  1081. '.' . $this->host : '');
  1082. }
  1083. $new_src = "//" .
  1084. $new_host .
  1085. $absolute_src .
  1086. preg_replace("!(www\.)?" . $this->host_escaped . "!i",
  1087. $new_host, $old_src_param);
  1088. } elseif ($count_satellites && !empty($satellites_hosts[0]) && empty($replaced[$old_src])) {
  1089. $img_host = preg_replace("@(https?:)?//(www\.)?([^/]+)/.*@", "$3", $old_src);
  1090. /* check if we can distribute this image through satellites' hosts */
  1091. if (in_array($img_host, $satellites)) {
  1092. $new_src = preg_replace("@(https?://)(www\.)?([^/]+)/@", "$1" . $hosts[strlen($old_src)%$count] . ".$3/", $old_src);
  1093. }
  1094. }
  1095. /* or replacing images with rewrite to Expires setter? */
  1096. } elseif (!empty($this->options['page']['far_future_expires_rewrite']) ||
  1097. !empty($this->options['page']['far_future_expires_external'])) {
  1098. /* add static proxy for external images */
  1099. if (!$absolute_src &&
  1100. $this->options['page']['far_future_expires_external']) {
  1101. $absolute_src = $old_src;
  1102. }
  1103. /* do not touch dynamic images / styles / scripts -- how we can handle them? */
  1104. if ($absolute_src &&
  1105. (preg_match("@\.(bmp|gif|png|ico|jpe?g)$@is", $absolute_src) ||
  1106. !empty($this->options['page']['far_future_expires_external']))) {
  1107. $new_src =
  1108. $this->options['page']['cachedir_relative'] .
  1109. 'wo.static.php?' . $absolute_src;
  1110. }
  1111. }
  1112. if (!empty($new_src)) {
  1113. if (empty($new_image)) {
  1114. /* prevent replacing images from oher domains with the same file name */
  1115. $new_src_image = str_replace($old_src, $new_src, $image[0]);
  1116. } else {
  1117. /* replace src in image with new class */
  1118. $new_src_image = str_replace($old_src, $new_src, $new_image);
  1119. }
  1120. $content = str_replace($image[0], $new_src_image, $content);
  1121. $new_src = '';
  1122. $new_image = '';
  1123. }
  1124. $replaced[$image[0]] = 1;
  1125. }
  1126. }
  1127. }
  1128. return $content;
  1129. }
  1130. /**
  1131. * Returns GZIP compressed content string with header
  1132. *
  1133. **/
  1134. function create_gz_compress ($content, $force_gzip = true) {
  1135. if (!empty($this->encoding)) {
  1136. if (!empty($force_gzip) && function_exists('gzcompress')) {
  1137. $size = strlen($content);
  1138. $crc = crc32($content);
  1139. $cnt = "\x1f\x8b\x08\x00\x00\x00\x00\x00";
  1140. $content = gzcompress($content, $this->options['page']['gzip_level']);
  1141. $content = substr($content, 0, -4);
  1142. $cnt .= $content;
  1143. $cnt .= pack('V', $crc);
  1144. $cnt .= pack('V', $size);
  1145. } elseif (empty($force_gzip) && function_exists('gzdeflate')) {
  1146. $cnt = gzdeflate($content, $this->options['page']['gzip_level']);
  1147. }
  1148. return $cnt;
  1149. } else {
  1150. return false;
  1151. }
  1152. }
  1153. /**
  1154. * Sets current encoding for HTML document
  1155. *
  1156. **/
  1157. function set_gzip_encoding () {
  1158. if (!empty($_SERVER["HTTP_ACCEPT_ENCODING"]) && !empty($this->options['page']['gzip'])) {
  1159. $gzip_no_ie = !in_array($this->ua_mod, array('.ie6', '.ie7')) || empty($this->options['page']['gzip_noie']);
  1160. $ae = strtolower($_SERVER["HTTP_ACCEPT_ENCODING"]);
  1161. if (strpos($ae, 'x-gzip') !== false && $gzip_no_ie) {
  1162. $this->encoding = 'x-gzip';
  1163. $this->encoding_ext = '.gz';
  1164. } elseif ((strpos($ae, 'gzip') !== false || !empty($_COOKIE['_wo_gzip'])) && $gzip_no_ie) {
  1165. $this->encoding = 'gzip';
  1166. $this->encoding_ext = '.gz';
  1167. } elseif (strpos($ae, 'x-deflate') !== false) {
  1168. $this->encoding = 'x-deflate';
  1169. $this->encoding_ext = '.df';
  1170. } elseif (strpos($ae, 'deflate') !== false) {
  1171. $this->encoding = 'deflate';
  1172. $this->encoding_ext = '.df';
  1173. }
  1174. } elseif (empty($_SERVER['HTTP_ACCEPT_ENCODING']) && !empty($_COOKIE['_wo_gzip'])) {
  1175. $this->encoding = 'gzip';
  1176. $this->encoding_ext = '.gz';
  1177. }
  1178. }
  1179. /**
  1180. * Sets the correct gzip header
  1181. *
  1182. **/
  1183. function set_gzip_header () {
  1184. if(!empty($this->encoding)) {
  1185. header("Content-Encoding: " . $this->encoding);
  1186. header("Vary: Accept-Encoding,User-Agent");
  1187. }
  1188. }
  1189. /**
  1190. * Compress JS or CSS and return source
  1191. *
  1192. **/
  1193. function do_compress ($options, $source, $files) {
  1194. /* Save the original extension */
  1195. $options['original_ext'] = $options['ext'];
  1196. /* Change the extension */
  1197. if (!empty($options['gzip']) || !empty($options['far_future_expires'])) {
  1198. $options['ext'] = "php";
  1199. }
  1200. /* Set cachedir */
  1201. $cachedir = $options['cachedir'];
  1202. if ($this->web_optimizer_stage) {
  1203. $this->write_progress($this->web_optimizer_stage++);
  1204. }
  1205. $source = $this->do_include($options, $source, $cachedir, $files);
  1206. return $source;
  1207. }
  1208. /**
  1209. * Include a single file
  1210. *
  1211. **/
  1212. function include_bundle ($source, $newfile, $handlers, $cachedir, $include, $href = '') {
  1213. switch ($include) {
  1214. /* move CSS file to the first occurent of CSS or before any scripts */
  1215. default:
  1216. if ($this->options['page']['html_tidy']) {
  1217. $styles = strpos($source, "@@@WSSSTYLES@@@");
  1218. $script1 = strpos($source, "<script");
  1219. $script2 = strpos($source, "<SCRIPT");
  1220. if ($script1 !== false && $script1 < $styles) {
  1221. $source = substr_replace($source, $newfile, $script1, 0);
  1222. } elseif ($script2 !== false && $script2 < $styles) {
  1223. $source = substr_replace($source, $newfile, $script2, 0);
  1224. } else {
  1225. $source = substr_replace($source, $newfile, $styles, 0);
  1226. }
  1227. } else {
  1228. $source = str_replace('@@@WSSSTYLES@@@', $newfile . '@@@WSSSTYLES@@@', $source);
  1229. }
  1230. break;
  1231. /* no unobtrusive but external scripts exist, avoid excluded scripts */
  1232. case 1:
  1233. if ($this->options['page']['html_tidy'] && ($headpos = strpos($source, '</head>'))) {
  1234. $source = substr_replace($source, $newfile, $headpos, 0);
  1235. } elseif ($this->options['page']['html_tidy'] && ($headpos = strpos($source, '</HEAD>'))) {
  1236. $source = substr_replace($source, $newfile, $headpos, 0);
  1237. } else {
  1238. $source = preg_replace("!</head>!is", $newfile . "$0", $source);
  1239. }
  1240. /* additional check in case of non-existing </head>, insert before <body> */
  1241. if (!strpos($source, $newfile)) {
  1242. $source = preg_replace("!<body[^>]*>!is", $newfile . "$0", $source);
  1243. }
  1244. break;
  1245. /* inject merged script to the first <script> occurrence, replace WSSSCRIPT */
  1246. case 2:
  1247. $source = str_replace("@@@WSSSCRIPT@@@", $newfile, $source);
  1248. break;
  1249. /* add JavaScript calls before </body> */
  1250. case 3:
  1251. if ($this->options['page']['html_tidy'] && ($bodypos = strpos($source, '</body>'))) {
  1252. $source = substr_replace($source, $newfile, $bodypos, 0);
  1253. } elseif ($this->options['page']['html_tidy'] && ($bodypos = strpos($source, '</BODY>'))) {
  1254. $source = substr_replace($source, $newfile, $bodypos, 0);
  1255. } else {
  1256. $source = preg_replace("!</body>!is", $newfile . "$0", $source);
  1257. /* a number of engines doesn't set </body> */
  1258. if (!strpos($source, $newfile)) {
  1259. $source .= $newfile;
  1260. }
  1261. }
  1262. /* remove JS file marker from content */
  1263. $source = str_replace('@@@WSSSCRIPT@@@', '', $source);
  1264. break;
  1265. /* place second CSS call to onDOMready */
  1266. case 4:
  1267. $include = '<script type="text/javascript">function _weboptimizer_load(){var d=document,l=d.createElement("link");l.rel="stylesheet";l.type="text/css";l.href="'. $href .'";d.getElementsByTagName("head")[0].appendChild(l);window._weboptimizer_load=function(){}}(function(){var d=document;if(d.addEventListener){d.addEventListener("DOMContentLoaded",_weboptimizer_load,false)}';
  1268. if (!empty($this->ua_mod) && substr($this->ua_mod, 3, 1) < 8) {
  1269. $include .= 'd.write("\x3cscript id=\"_weboptimizer\" defer=\"defer\" src=\"\">\x3c\/script>");(d.getElementById("_weboptimizer")).onreadystatechange=function(){if(this.readyState=="complete"){setTimeout(function(){if(typeof _weboptimizer_load!=="undefined"){_weboptimizer_load()}},0)}};';
  1270. }
  1271. $include .= 'if(/WebK/i.test(navigator.userAgent)){var wssload=setInterval(function(){if(/loaded|complete/.test(document.readyState)){clearInterval(wssload);if(typeof _weboptimizer_load!=="undefined"){_weboptimizer_load()}}},10)}window[/*@cc_on !@*/0?"attachEvent":"addEventListener"](/*@cc_on "on"+@*/"load",_weboptimizer_load,false)}());document.write("\x3c!--");</script>' . $newfile . '<!--[if IE]><![endif]-->';
  1272. $source = str_replace("@@@WSSSTYLES@@@", "@@@WSSSTYLES@@@" . $include, $source);
  1273. break;
  1274. }
  1275. return $source;
  1276. }
  1277. /**
  1278. * Include compressed JS or CSS into source and return it
  1279. *
  1280. **/
  1281. function do_include ($options, $source, $cachedir, $external_array) {
  1282. $cachedir_relative = $options['cachedir_relative'];
  1283. $handlers = '';
  1284. /* If only one script found */
  1285. if (!is_array($external_array)) {
  1286. $external_array = array($external_array);
  1287. }
  1288. if (empty($options['file'])) {
  1289. /* Glue scripts' content / filenames */
  1290. $scripts_string = $this->https;
  1291. foreach ($external_array as $script) {
  1292. $scripts_string .= (empty($script['source']) ? '' : $script['source']) . (empty($script['content']) ? '' : $script['content']);
  1293. }
  1294. /* Get date string to make hash */
  1295. $datestring = $this->get_file_dates($external_array, $options);
  1296. /* get options string */
  1297. $optstring = '';
  1298. foreach ($options as $key => $value) {
  1299. if (is_array($value)) {
  1300. $optstring .= '_' . implode('_', $value);
  1301. } else {
  1302. $optstring .= '_' . $value;
  1303. }
  1304. }
  1305. /* Get the cache hash, restrict by 10 symbols */
  1306. $cache_file = substr(md5($scripts_string . $datestring . $optstring), 0, 10);
  1307. /* use general file if it has been defined */
  1308. } else {
  1309. $cache_file = $options['file'];
  1310. }
  1311. $cache_file = urlencode($cache_file . $this->ua_mod);
  1312. $physical_file = $options['cachedir'] . $cache_file . "." . $options['ext'];
  1313. $external_file = 'http' . $this->https . '://' .
  1314. (empty($options['host']) ?
  1315. $_SERVER['HTTP_HOST'] :
  1316. (empty($options['https']) ?
  1317. $options['host'] :
  1318. $options['https'])) .
  1319. str_replace($this->options['document_root'], "/", $physical_file);
  1320. if (empty($this->options['cache_version'])) {
  1321. if (@is_file($physical_file)) {
  1322. $timestamp = @filemtime($physical_file);
  1323. } else {
  1324. $timestamp = 0;
  1325. }
  1326. } else {
  1327. $timestamp = $this->options['cache_version'];
  1328. }
  1329. /* add BackgroundImageCache for IE6 to prevent CSS Sprites blinking */
  1330. if ($this->ua_mod === '.ie6'&& !empty($options['css_sprites'])) {
  1331. $source = $this->include_bundle($source, '<script type="text/javascript">try{document.execCommand("BackgroundImageCache",false,true)}catch(e){}</script>', $handlers, $cachedir, 1);
  1332. }
  1333. /* Check if the cache file exists */
  1334. if ($timestamp) {
  1335. /* Put in locations and remove certain scripts */
  1336. if (!is_array($external_array)) {
  1337. $external_array = array($external_array);
  1338. }
  1339. $source = $this->_remove_scripts($external_array, $source,
  1340. $options['header'] != 'css' ? $options['header'] == 'javascript' && !$options['external_scripts_head_end'] ? 1 : 0 : 2);
  1341. /* Create the link to the new file with data:URI / mhtml */
  1342. if (!empty($options['data_uris_separate']) && (!empty($this->options['cache_version']) || @is_file($physical_file . '.' . $options['ext']))) {
  1343. $newfile = $this->get_new_file($options, $cache_file, $timestamp, '.' . $options['ext']);
  1344. /* raw include right after the main CSS file */
  1345. if (empty($options['data_uris_domloaded'])) {
  1346. $source = $this->include_bundle($source, $newfile, $handlers, $cachedir_relative, 0);
  1347. /* include via JS loader to provide fast flush of content */
  1348. } else {
  1349. $source = $this->include_bundle($source, $newfile, $handlers, $cachedir_relative, 4, $this->get_new_file_name($options, $cache_file, $timestamp, '.' . $options['ext']));
  1350. }
  1351. }
  1352. $newfile = $this->get_new_file($options, $cache_file, $timestamp);
  1353. $source = $this->include_bundle($source, $newfile, $handlers, $cachedir_relative, $options['unobtrusive_body'] ? 3 : ($options['header'] == 'javascript' && $options['external_scripts_head_end'] ? 1 : ($options['header'] == 'javascript' ? 2 : 0)));
  1354. /* fix for some JS libraries to load resrouces dynamically */
  1355. if (!empty($this->shadowbox_base) &&
  1356. $options['header'] == 'javascript' &&
  1357. !$options['inline_scripts']) {
  1358. $source = str_replace('Shadowbox.init(', 'Shadowbox.path="' .
  1359. $this->shadowbox_base . '";Shadowbox.init(', $source);
  1360. }
  1361. return $source;
  1362. }
  1363. foreach ($this->libraries as $klass => $library) {
  1364. if (!class_exists($klass, false)) {
  1365. require($options['installdir'] . 'libs/php/' . $library);
  1366. }
  1367. }
  1368. /* If the file didn't exist, continue. Get files' content */
  1369. if (!empty($options['dont_check_file_mtime'])) {
  1370. $this->get_script_content($options['tag']);
  1371. /* Replace existing array with the new content */
  1372. $external_array = array();
  1373. foreach ($this->initial_files as $key => $file) {
  1374. if ($file['tag'] == $options['tag']) {
  1375. $external_array[] = $file;
  1376. }
  1377. }
  1378. }
  1379. /* Delete old files from cache */
  1380. if (!empty($this->options['days_to_delete'])) {
  1381. $dir = @getcwd();
  1382. @chdir($options['cachedir']);
  1383. foreach (glob('*.' . $options['ext']) as $file) {
  1384. if (!in_array($file, array('wo.cookie.php', 'wo.static.php', 'yass.loader.js', 'webo-site-speedup.php')) &&
  1385. $this->time - filemtime($file) >
  1386. $this->options['days_to_delete'] * 86400) {
  1387. @unlink($file);
  1388. }
  1389. }
  1390. foreach (glob('*.' . $options['ext'] . '.gz') as $file) {
  1391. if ($this->time - filemtime($file) >
  1392. $this->options['days_to_delete'] * 86400) {
  1393. @unlink($file);
  1394. }
  1395. }
  1396. foreach (glob('*.png') as $file) {
  1397. if (!in_array($file, array('webo-site-speedup88.png', 'webo-site-speedup125.png', 'webo-site-speedup161.png', 'webo-site-speedup250.png')) &&
  1398. $this->time - filemtime($file) >
  1399. $this->options['days_to_delete'] * 86400) {
  1400. @unlink($file);
  1401. }
  1402. }
  1403. foreach (glob('*.gif') as $file) {
  1404. if ($file != '0.gif' && $this->time - filemtime($file) >
  1405. $this->options['days_to_delete'] * 86400) {
  1406. @unlink($file);
  1407. }
  1408. }
  1409. foreach (glob('*.jpg') as $file) {
  1410. if ($this->time - filemtime($file) >
  1411. $this->options['days_to_delete'] * 86400) {
  1412. @unlink($file);
  1413. }
  1414. }
  1415. @chdir($dir);
  1416. }
  1417. /* Create file */
  1418. $contents = "";
  1419. if (is_array($external_array)) {
  1420. /* can't simply merge&minify if we need to exclude some files */
  1421. if (empty($options['minify_exclude']) || empty($options['minify'])) {
  1422. foreach($external_array as $key => $info) {
  1423. /* Get the code */
  1424. if ($file_contents = $info['content']) {
  1425. if (!empty($options['minify_try'])) {
  1426. $contents .= 'try{';
  1427. }
  1428. $contents .= $file_contents . "\n";
  1429. if (!empty($options['minify_try'])) {
  1430. $contents .= '}catch(e){';
  1431. if (!empty($info['file'])) {
  1432. $contents .= 'document.write("' .
  1433. str_replace(array('<', '"', "\n", "\r"), array('\x3c', '\"', ' ', ''), $info['source']) .
  1434. '")';
  1435. }
  1436. $contents .= '}';
  1437. }
  1438. }
  1439. }
  1440. }
  1441. if ($options['tag'] === 'link' && !empty($options['include_code'])) {
  1442. $contents .= str_replace("<br/>", "", $options['include_code']);
  1443. }
  1444. $source = $this->_remove_scripts($external_array, $source,
  1445. $options['header'] != 'css' ? $options['header'] == 'javascript' && !$options['external_scripts_head_end'] ? 1 : 0 : 2);
  1446. if ($options['css_sprites'] || ($options['data_uris'] && empty($this->ua_mod)) || ($options['mhtml'] && !empty($this->ua_mod))) {
  1447. $options['css_sprites_partly'] = 0;
  1448. $remembered_data_uri = $options['data_uris'];
  1449. $remembered_mhtml = $options['mhtml'];
  1450. $options['data_uris'] = $options['mhtml'] = 0;
  1451. /* start new PHP process to create CSS Sprites */
  1452. if (!empty($this->web_optimizer_stage) && !(($this->web_optimizer_stage - 13)%15) && $this->web_optimizer_stage < 85) {
  1453. /* skip 2 minor stages if we not in the first major stage */
  1454. $delta = $this->web_optimizer_stage > 20 ? 6 : 0;
  1455. header('Location: ' . $this->chained_redirect .
  1456. '?web_optimizer_stage=' .
  1457. ($this->web_optimizer_stage + $delta) .
  1458. '&username=' .
  1459. $this->username .
  1460. '&password=' .
  1461. $this->password .
  1462. '&auto_rewrite=' .
  1463. $this->auto_rewrite .
  1464. '&cache_version=' .
  1465. $this->cache_version .
  1466. '&web_optimizer_debug=1');
  1467. while (@ob_end_clean());
  1468. die();
  1469. /* prepare first 4 Sprites */
  1470. } elseif (!empty($this->web_optimizer_stage) && !(($this->web_optimizer_stage - 16)%15) && $this->web_optimizer_stage < 85) {
  1471. $options['css_sprites_partly'] = 1;
  1472. $this->convert_css_sprites($contents, $options, $external_file);
  1473. header('Location: ' . $this->chained_redirect .
  1474. '?web_optimizer_stage=' .
  1475. $this->web_optimizer_stage .
  1476. '&username=' .
  1477. $this->username .
  1478. '&password=' .
  1479. $this->password .
  1480. '&auto_rewrite=' .
  1481. $this->auto_rewrite .
  1482. '&cache_version=' .
  1483. $this->cache_version .
  1484. '&web_optimizer_debug=1');
  1485. while (@ob_end_clean());
  1486. die();
  1487. } elseif (!empty($this->web_optimizer_stage) && !(($this->web_optimizer_stage - 19)%15) && $this->web_optimizer_stage < 85) {
  1488. /* Create CSS Sprites in CSS dir */
  1489. $this->convert_css_sprites($contents, $options, $external_file);
  1490. /* start new PHP process to create data:URI */
  1491. header('Location: ' . $this->chained_redirect .
  1492. '?web_optimizer_stage=' .
  1493. ($this->web_optimizer_stage + 1) .
  1494. '&username=' .
  1495. $this->username .
  1496. '&password=' .
  1497. $this->password .
  1498. '&auto_rewrite=' .
  1499. $this->auto_rewrite .
  1500. '&cache_version=' .
  1501. $this->cache_version .
  1502. '&web_optimizer_debug=1');
  1503. while (@ob_end_clean());
  1504. die();
  1505. } else {
  1506. /* we created all Sprites -- ready for data:URI + mhtml */
  1507. $options['data_uris'] = $remembered_data_uri;
  1508. $options['mhtml'] = $remembered_mhtml;
  1509. /* create correct resource file name for data:URI / mhtml inclusion */
  1510. $resource_file = $external_file;
  1511. if (!empty($options['data_uris_separate'])) {
  1512. $resource_file = $this->get_new_file_name($options, $resource_file, $this->time, '.' . $options['ext']);
  1513. }
  1514. if (!empty($options['minify_with']) && $options['minify_with'] == 'tidy') {
  1515. $minified_content_array = $this->convert_css_sprites($contents, $options, $resource_file);
  1516. } else {
  1517. $minified_content_array = $this->convert_data_uri($contents, $options, $resource_file);
  1518. }
  1519. $minified_content = $minified_content_array[0];
  1520. $minified_resource = $minified_content_array[1];
  1521. /* write data:URI / mhtml content */
  1522. if (!empty($minified_resource) && !empty($options['data_uris_separate'])) {
  1523. /* Allow for gzipping and headers */
  1524. if (($options['gzip'] || $options['far_future_expires']) && !empty($minified_resource)) {
  1525. $minified_resource = $this->gzip_header[$options['header']] . $minified_resource;
  1526. }
  1527. $this->write_file($physical_file . '.' . $options['ext'], $minified_resource, in_array($options['ext'], array('css', 'js')), 'text/css');
  1528. /* create static gzipped versions for static gzip in nginx, Apache */
  1529. if ($options['ext'] == 'css') {
  1530. $c = @gzencode($minified_resource, $options['gzip_level'], FORCE_GZIP);
  1531. if (!empty($c)) {
  1532. $this->write_file($physical_file . '.' . $options['ext']. '.gz', $c);
  1533. }
  1534. }
  1535. $newfile = $this->get_new_file($options, $cache_file, $this->time, '.' . $options['ext']);
  1536. /* raw include right after the main CSS file */
  1537. if (empty($options['data_uris_domloaded'])) {
  1538. $source = $this->include_bundle($source, $newfile, $handlers, $cachedir_relative, 0);
  1539. /* include via JS loader to provide fast flush of content */
  1540. } else {
  1541. $source = $this->include_bundle($source, $newfile, $handlers, $cachedir_relative, 4, $this->get_new_file_name($options, $cache_file, $this->time, '.' . $options['ext']));
  1542. }
  1543. } elseif (!empty($minified_content)) {
  1544. $ie = in_array($this->ua_mod, array('.ie5', '.ie6', '.ie7'));
  1545. $minified_content .= $minified_resource;
  1546. }
  1547. }
  1548. if (!empty($minified_content)) {
  1549. $contents = $minified_content;
  1550. }
  1551. }
  1552. }
  1553. if (!empty($contents)) {
  1554. /* Allow for minification of javascript */
  1555. if ($options['header'] == "javascript" && $options['minify']) {
  1556. $contents = $this->minify_javascript($contents, $options);
  1557. }
  1558. /* we need to exclude some files from ninify */
  1559. } elseif(!empty($options['minify_exclude']) && $options['minify']) {
  1560. $exclude_list = explode(" ", trim($options['minify_exclude']));
  1561. foreach($external_array as $key => $info) {
  1562. /* Get the code */
  1563. if ($file_contents = $info['content']) {
  1564. $content = '';
  1565. if (!empty($options['minify_try'])) {
  1566. $content .= 'try{';
  1567. }
  1568. $content .= $file_contents . "\n";
  1569. if (!empty($options['minify_try'])) {
  1570. $content .= '}catch(e){';
  1571. if (!empty($info['file'])) {
  1572. $content .= 'document.write("' .
  1573. str_replace(array('<', '"', "\n", "\r"), array('\x3c', '\"', ' ', ''), $info['source']) .
  1574. '")';
  1575. }
  1576. $content .= '}';
  1577. }
  1578. if ($options['header'] == "javascript" &&
  1579. !in_array(preg_replace("@.*/@", "", $info['file']), $exclude_list)) {
  1580. $content = $this->minify_javascript($content, $options);
  1581. }
  1582. $contents .= $content;
  1583. }
  1584. }
  1585. }
  1586. if (!empty($contents)) {
  1587. /* fix for some JS libraries to load resrouces dynamically */
  1588. if (!empty($this->shadowbox_base) &&
  1589. $options['header'] == 'javascript' &&
  1590. $options['inline_scripts']) {
  1591. $contents = str_replace('Shadowbox.init(', 'Shadowbox.path="' .
  1592. $this->shadowbox_base . '";Shadowbox.init(', $contents);
  1593. }
  1594. /* Allow for minification of CSS, CSS Sprites uses CSS Tidy -- already minified CSS */
  1595. if ($options['minify_with'] == 'basic' &&
  1596. !empty($options['minify']) &&
  1597. empty($options['css_sprites'])) {
  1598. /* Minify CSS */
  1599. $contents = $this->minify_text($contents);
  1600. }
  1601. /* Allow for gzipping and headers */
  1602. if ($options['gzip'] || $options['far_future_expires']) {
  1603. $contents = $this->gzip_header[$options['header']] . $contents;
  1604. }
  1605. if (!empty($contents)) {
  1606. /* Write to cache and display */
  1607. $this->write_file($physical_file, $contents, in_array($options['ext'], array('css', 'js')), $options['ext'] == 'js' ? 'application/javascript' : 'text/css');
  1608. /* create static gzipped versions for static gzip in nginx, Apache */
  1609. if ($options['ext'] == 'css' || $options['ext'] == 'js') {
  1610. $c = @gzencode($contents, $options['gzip_level'], FORCE_GZIP);
  1611. if (!empty($c)) {
  1612. $this->write_file($physical_file . '.gz', $c);
  1613. }
  1614. }
  1615. /* Create the link to the new file */
  1616. $newfile = $this->get_new_file($options, $cache_file, $this->time);
  1617. $source = $this->include_bundle($source, $newfile, $handlers, $cachedir_relative, $options['unobtrusive_body'] ? 3 : ($options['header'] == 'javascript' && $options['external_scripts_head_end'] ? 1 : ($options['header'] == 'javascript' ? 2 : 0)));
  1618. }
  1619. }
  1620. return $source;
  1621. }
  1622. /**
  1623. * Minifies JavaScript code according to current options
  1624. *
  1625. */
  1626. function minify_javascript ($code, $options) {
  1627. $minified_code = '';
  1628. if ($options['minify_with'] == 'packer') {
  1629. $this->packer = new JavaScriptPacker($code, 'Normal', false, false);
  1630. $minified_code = $this->packer->pack();
  1631. } elseif ($options['minify_with'] == 'yui' ) {
  1632. $this->yuicompressor = new YuiCompressor($options['cachedir'],
  1633. $options['installdir'], $this->charset);
  1634. $minified_code = $this->yuicompressor->compress($code);
  1635. }
  1636. if ($options['minify_with'] == 'jsmin' ||
  1637. (!empty($options['minify_with']) &&
  1638. empty($minified_code))) {
  1639. $this->jsmin = new JSMin($code);
  1640. $minified_code = $this->jsmin->minify($code);
  1641. }
  1642. if (!empty($minified_code)) {
  1643. $code = $minified_code;
  1644. }
  1645. return $code;
  1646. }
  1647. /**
  1648. * Replaces scripts calls or css links in the source with a marker
  1649. *
  1650. */
  1651. function _remove_scripts ($external_array, $source, $mark = false) {
  1652. $replacement = $mark ? $mark > 1 ? '@@@WSSSTYLES@@@' : '@@@WSSSCRIPT@@@' : '';
  1653. if (is_array($external_array)) {
  1654. foreach ($external_array as $key => $value) {
  1655. /* Remove script, replace the first one with the mark to insert merged script */
  1656. $source = str_replace($value['source'], $replacement, $source);
  1657. if ($mark) {
  1658. $replacement = '';
  1659. }
  1660. }
  1661. }
  1662. return $source;
  1663. }
  1664. /**
  1665. * Returns the HTML code for our new compressed file
  1666. *
  1667. **/
  1668. function get_new_file ($options, $cache_file, $timestamp = false, $add = false) {
  1669. $newfile = '<' . $options['tag'] .
  1670. ' type="' . $options['type'] . '" ' .
  1671. $options['src'] . '="' . $this->get_new_file_name($options, $cache_file, $timestamp, $add) . '"'.
  1672. /* IE7- don't understand stylesheet nofollow in rel */
  1673. ((empty($this->ua_mod) && $options['ext'] == 'php') || !empty($options['rel']) ? ' rel="' .
  1674. (empty($options['rel']) ? '' : $options['rel']) .
  1675. (!empty($options['rel']) && $options['ext'] == 'php' && empty($this->ua_mod) ? ' ' : '') .
  1676. (empty($this->ua_mod) && $options['ext'] == 'php' ? 'nofollow' : '') . '"' : '') .
  1677. (empty($options['self_close']) ? '></' . $options['tag'] . '>' : (empty($this->xhtml) ? '>' : '/>'));
  1678. return $newfile;
  1679. }
  1680. /**
  1681. * Returns the filename for our new compressed file
  1682. *
  1683. **/
  1684. function get_new_file_name ($options, $cache_file, $timestamp = false, $add = false) {
  1685. $timestamp = $options['far_future_expires_php'] ? $timestamp : false;
  1686. return (empty($options['host']) ? '' : '//' . $options['host']) .
  1687. $options['cachedir_relative'] .
  1688. $cache_file .
  1689. ($add ? '.' . $options['ext'] : '') .
  1690. ($timestamp && $options['far_future_expires_rewrite'] ? '.wo' . $timestamp : '') .
  1691. ($add ? $add : '.' . $options['ext']) .
  1692. ($timestamp && !$options['far_future_expires_rewrite'] ? '?' . $timestamp : '');
  1693. }
  1694. /**
  1695. * Returns the last modified dates of the files being compressed
  1696. * In this way we can see if any changes have been made
  1697. **/
  1698. function get_file_dates ($files, $options) {
  1699. /* option added by janvarev */
  1700. if (!empty($options['dont_check_file_mtime']) && strlen($this->lc) == 29) {
  1701. return;
  1702. }
  1703. $dates = false;
  1704. if (!empty($files) && is_array($files)) {
  1705. foreach($files AS $key => $value) {
  1706. if (!empty($value['file'])) {
  1707. $value['file'] = $this->get_file_name($value['file']);
  1708. if (@file_exists($value['file'])) {
  1709. $thedate = filemtime($value['file']);
  1710. $dates[] = $thedate;
  1711. }
  1712. }
  1713. }
  1714. }
  1715. if (is_array($dates)) {
  1716. return implode(".", $dates);
  1717. }
  1718. return;
  1719. }
  1720. /**
  1721. * Get full pathname for the given file
  1722. *
  1723. **/
  1724. function get_file_name ($file) {
  1725. if (is_array($file) && count($file)>0) {
  1726. $file = $file[0];
  1727. }
  1728. $file = $this->strip_querystring(preg_replace("@^https?://(www\.)?" . $this->host_escaped . "/+@", "/", $file));
  1729. if (substr($file, 0, 1) == "/") {
  1730. return $this->view->prevent_trailing_slash($this->options['document_root']) . $file;
  1731. } else {
  1732. return $this->view->paths['full']['current_directory'] . $file;
  1733. }
  1734. }
  1735. /**
  1736. * Resursively resolve all @import in CSS files and get files' content
  1737. * The second param marks inline styles case
  1738. *
  1739. **/
  1740. function resolve_css_imports ($src, $inline = false) {
  1741. $content = false;
  1742. $file = '';
  1743. if (!$inline) {
  1744. $file = $this->get_file_name($src);
  1745. /* dynamic file */
  1746. if (!preg_match("!\.css$!is", $file)) {
  1747. $dynamic_file = $src;
  1748. /* touch only non-external scripts */
  1749. if (!strpos($dynamic_file, "://") || strpos($dynamic_file, '//') === 0) {
  1750. $dynamic_file = "http://" . $_SERVER['HTTP_HOST'] . $this->convert_path_to_absolute($dynamic_file, array('file' => $file), true);
  1751. }
  1752. $file = $this->options['css']['cachedir'] . $this->get_remote_file($this->resolve_amps($dynamic_file), 'link');
  1753. }
  1754. if (@is_file($file)) {
  1755. $content = @file_get_contents($file);
  1756. }
  1757. } else {
  1758. $content = $src;
  1759. }
  1760. /* remove BOM */
  1761. $content = $this->resolve_amps($content);
  1762. if (@is_file($file) || $inline) {
  1763. /* remove commented @import. First of all glue CSS files, optimiza only secondly */
  1764. $content = preg_replace("!/\*\s*@import.*?\*/!is", "", $content);
  1765. /* new RegExp from xandrx */
  1766. preg_match_all('!@import\s*(url)?\s*\(?([^;]+?)\)?;!i', $content, $imports, PREG_SET_ORDER);
  1767. if (is_array($imports)) {
  1768. foreach ($imports as $import) {
  1769. $src = false;
  1770. if (isset($import[2])) {
  1771. $src = $import[2];
  1772. $src = trim($src, '\'" ');
  1773. }
  1774. if (strpos($src, "//") && !preg_match('@//(www\.)?' . $this->host_escaped . '/@', $src)) {
  1775. $src = $this->get_remote_file($src);
  1776. }
  1777. if ($src) {
  1778. $saved_directory = $this->view->paths['full']['current_directory'];
  1779. $this->view->paths['full']['current_directory'] = preg_replace("/[^\/]+$/", "", $file);
  1780. /* start recursion */
  1781. $content = str_replace($import[0], $this->convert_paths_to_absolute($this->resolve_css_imports($src), array('file' => str_replace($this->options['document_root'], "/", $this->get_file_name($src)))), $content);
  1782. /* return remembed directory */
  1783. $this->view->paths['full']['current_directory'] = $saved_directory;
  1784. }
  1785. }
  1786. }
  1787. }
  1788. return $content;
  1789. }
  1790. /**
  1791. * Gets an array of scripts/css files to be processed
  1792. *
  1793. **/
  1794. function get_script_array () {
  1795. /* get head with all content */
  1796. $this->get_head();
  1797. $curl = function_exists('curl_init');
  1798. if ($this->options['javascript']['minify'] || $this->options['javascript']['gzip'] || $this->options['page']['parallel_javascript']) {
  1799. if (empty($this->options['javascript']['minify_body'])) {
  1800. $toparse = $this->head;
  1801. } else {
  1802. $toparse = $this->body;
  1803. }
  1804. /* find all scripts from head */
  1805. $regex = "!(<script[^>]*>)(.*?</script>)!is";
  1806. preg_match_all($regex, $toparse, $matches, PREG_SET_ORDER);
  1807. if (!empty($matches)) {
  1808. foreach($matches as $match) {
  1809. $file = array(
  1810. 'tag' => 'script',
  1811. 'source' => $match[0],
  1812. 'content' => preg_replace("@(<script[^>]*>|</script>)@is", "", $match[0])
  1813. );
  1814. preg_match_all("@src\s*=\s*(?:\"([^\"]+)\"|'([^']+)'|([\S]+))@is", $match[1], $variants, PREG_SET_ORDER);
  1815. if (is_array($variants)) {
  1816. foreach ($variants as $variant_type) {
  1817. $variant_type[1] = ($variant_type[1] === '') ? (($variant_type[2] === '') ? str_replace('>', '', $variant_type[3]) : $variant_type[2]) : $variant_type[1];
  1818. $file['file'] = $this->convert_basehref(trim($this->strip_querystring($variant_type[1])));
  1819. $file['file_raw'] = $variant_type[1];
  1820. }
  1821. }
  1822. /* skip external files if option is disabled */
  1823. if (($this->options['javascript']['external_scripts'] && $curl) ||
  1824. (!empty($file['file']) && preg_match("@(index\.php/|\.js$)@i", $file['file']) &&
  1825. (!strpos($file['file'], '//') ||
  1826. preg_match("@//(www\.)?" . $this->host_escaped . "/@is", $file['file']))) ||
  1827. (empty($file['file']) &&
  1828. $this->options['javascript']['inline_scripts'])) {
  1829. $this->initial_files[] = $file;
  1830. /* fix shadowbox loader */
  1831. if (!empty($file['file']) && strpos($file['file'], 'shadowbox.js')) {
  1832. $this->shadowbox_base = preg_replace("@https?://" .
  1833. $this->host_escaped . "/(.*/)[^/]+@",
  1834. (empty($this->options['javascript']['host']) ?
  1835. '' : '//' . $this->options['javascript']['host']) . "/$1", $file['file']);
  1836. }
  1837. /* fix scriptaculous loader */
  1838. if (!empty($file['file']) && ($acpos = strpos($variant_type[1], '?load='))) {
  1839. $scripts = explode(',', substr($variant_type[1], $acpos + 6));
  1840. $acbase = preg_replace("@/[^/]+$@", '/', $file['file']);
  1841. foreach ($scripts as $script) {
  1842. $acfile = array(
  1843. 'tag' => 'script',
  1844. 'source' => '',
  1845. 'content' => '',
  1846. 'file' => $acbase . $script . '.js'
  1847. );
  1848. $this->initial_files[] = $acfile;
  1849. }
  1850. }
  1851. }
  1852. }
  1853. }
  1854. }
  1855. if ($this->options['css']['minify'] || $this->options['css']['gzip'] || $this->options['page']['parallel_css']) {
  1856. if (empty($this->options['css']['minify_body'])) {
  1857. $toparse = $this->head;
  1858. } else {
  1859. $toparse = $this->body;
  1860. }
  1861. /* find all CSS links from head and inine styles */
  1862. if (empty($this->options['css']['inline_scripts']) && $this->options['page']['html_tidy'] && !strpos($toparse, '<style') && !strpos($toparse, '<STYLE')) {
  1863. $regex = "!(<link[^>]+rel\\s*=\\s*(\"stylesheet\"|'stylesheet'|stylesheet)[^>]*>)!is";
  1864. } else {
  1865. $regex = "!(<link[^>]+rel\\s*=\\s*(\"stylesheet\"|'stylesheet'|stylesheet)[^>]*>|<style[^>]*>.*?</style>)!is";
  1866. }
  1867. preg_match_all($regex, $toparse, $matches, PREG_SET_ORDER);
  1868. if (!empty($matches)) {
  1869. foreach($matches as $match) {
  1870. $file = array(
  1871. 'tag' => 'link',
  1872. 'source' => $match[0],
  1873. 'content' => preg_replace("@(<link[^>]+>|<style[^>]*>|<\/style>)@is", "", $match[0]),
  1874. );
  1875. preg_match_all("@(media|href)\s*=\s*(?:\"([^\"]*)\"|'([^']*)'|([^\s>]*))@is", $match[0], $variants, PREG_SET_ORDER);
  1876. if (is_array($variants)) {
  1877. foreach($variants as $variant_type) {
  1878. $variant_type[1] = strtolower($variant_type[1]);
  1879. $variant_type[2] = empty($variant_type[2]) ? (empty($variant_type[3]) ? (empty($variant_type[4]) ? '' : $variant_type[4]) : $variant_type[3]) : $variant_type[2];
  1880. switch ($variant_type[1]) {
  1881. case "href":
  1882. $file['file'] = $this->convert_basehref(trim($this->strip_querystring($variant_type[2])));
  1883. $file['file_raw'] = $variant_type[2];
  1884. break;
  1885. default:
  1886. /* skip media="all|screen" to prevent Safari bug with @media all{} and @media screen{} */
  1887. if ($variant_type[1] != 'media' || ($variant_type[1] == 'media' && !preg_match("@^(all|screen|''|\"\")$@i", $variant_type[2]))) {
  1888. $file[$variant_type[1]] = $variant_type[2];
  1889. }
  1890. break;
  1891. }
  1892. }
  1893. }
  1894. /* skip external files if option is disabled */
  1895. if (($this->options['css']['external_scripts'] && $curl) ||
  1896. (!empty($file['file']) && preg_match("@(index\.php/|\.css$)@i", $file['file']) &&
  1897. (!strpos($file['file'], '//') ||
  1898. preg_match("@//(www\.)?" . $this->host_escaped . "/@is", $file['file']))) ||
  1899. (empty($file['file']) && $this->options['css']['inline_scripts'])) {
  1900. $this->initial_files[] = $file;
  1901. }
  1902. }
  1903. }
  1904. }
  1905. /* strange thing: array is filled even if string is empty */
  1906. $excluded_scripts_css = explode(" ", $this->options['css']['external_scripts_exclude']);
  1907. $excluded_scripts_js = explode(" ", $this->options['javascript']['external_scripts_exclude']);
  1908. if (is_array($this->initial_files)) {
  1909. /* enable caching / gzipping proxy? */
  1910. $rewrite_css = ($this->options['page']['far_future_expires_external'] ||
  1911. $this->options['css']['gzip']);
  1912. $rewrite_js = ($this->options['page']['far_future_expires_external'] ||
  1913. $this->options['javascript']['gzip']);
  1914. /* Remove empty sources and any externally linked files */
  1915. foreach ($this->initial_files as $key => $value) {
  1916. $use_proxy = (!$this->options['javascript']['minify'] && $value['tag'] == 'script') ||
  1917. (!$this->options['css']['minify'] && $value['tag'] == 'link');
  1918. /* but keep CSS/JS w/o src to merge into unobtrusive loader, also exclude files from ignore_list */
  1919. if (($value['tag'] == 'script' && ((empty($value['file']) &&
  1920. !$this->options['javascript']['inline_scripts']) ||
  1921. (!empty($excluded_scripts_js[0]) &&
  1922. !empty($value['file']) &&
  1923. in_array(preg_replace("/.*\//", "", $value['file']), $excluded_scripts_js)) ||
  1924. (!$this->options['javascript']['minify'] && $this->options['page']['parallel_javascript']))) ||
  1925. ($value['tag'] == 'link' && ((empty($value['file']) &&
  1926. !$this->options['css']['inline_scripts']) ||
  1927. (!empty($excluded_scripts_css[0]) &&
  1928. !empty($value['file'] ) &&
  1929. in_array(preg_replace("/.*\//", "", $value['file']), $excluded_scripts_css)) ||
  1930. (!$this->options['css']['minify'] && $this->options['page']['parallel_css'])))) {
  1931. /* just skip them */
  1932. unset($this->initial_files[$key]);
  1933. $use_proxy = 1;
  1934. /* rewrite skipped file with CDN host */
  1935. if (!empty($value['file']) &&
  1936. (($value['tag'] == 'link' && $this->options['page']['parallel_css']) ||
  1937. ($value['tag'] == 'script' && $this->options['page']['parallel_javascript'])) &&
  1938. (preg_match("@//(www\.)?" . $this->host_escaped . "/+@", $value['file']) ||
  1939. (substr($value['file'], 0, 1) == '/' && substr($value['file'], 1, 1) != '/'))) {
  1940. $host = $value['tag'] == 'link' ?
  1941. $this->options['css']['host'] :
  1942. $this->options['javascript']['host'];
  1943. $new_src = (empty($host) ? "" : "//" . $host) .
  1944. preg_replace("@https?://(www\.)?" .
  1945. $this->host_escaped .
  1946. "/+@", "/", $value['file']);
  1947. $new_script = str_replace($value['file'],
  1948. $new_src, $value['file_raw']);
  1949. $this->content = str_replace($value['file_raw'],
  1950. $new_script, $this->content);
  1951. $use_proxy = 0;
  1952. }
  1953. /* rewrite skipped file with caching proxy, skip dynamic files */
  1954. }
  1955. if ($use_proxy && !empty($value['file']) &&
  1956. (($value['tag'] == 'link' && $rewrite_css) ||
  1957. ($value['tag'] == 'script' && $rewrite_js)) &&
  1958. !preg_match("!\.php$!", $value['file'])) {
  1959. $value['file'] = preg_replace("@https?://(www\.)?" .
  1960. $this->host_escaped . "/+@", "/", $value['file']);
  1961. $new_src =
  1962. $this->options['page']['cachedir_relative'] .
  1963. 'wo.static.php?' . $this->convert_path_to_absolute($value['file'],
  1964. array('file' => $_SERVER['REQUEST_URI']));
  1965. $new_script = str_replace($value['file'],
  1966. $new_src, $value['file_raw']);
  1967. $this->content = str_replace($value['file_raw'],
  1968. $new_script, $this->content);
  1969. }
  1970. }
  1971. /* skip mining files' content if don't check MTIME */
  1972. if (empty($this->options['javascript']['dont_check_file_mtime']) ||
  1973. strlen($this->lc) != 29) {
  1974. $this->get_script_content();
  1975. }
  1976. }
  1977. }
  1978. /**
  1979. * Gets an content for array of scripts/css files
  1980. *
  1981. **/
  1982. function get_script_content ($tag = false) {
  1983. /* to get inline values */
  1984. $last_key = array();
  1985. /* to get inline values on empty non-inline */
  1986. $last_key_flushed = array();
  1987. $stored = array();
  1988. /* duplicates spots */
  1989. $duplicates = array(
  1990. /* jQuery */
  1991. array(
  1992. 'regexp' => 'jquery([v0-9\.\-\[\]])*(pack|min)?\.(js|php)(\.gz)?',
  1993. 'exists' => 0
  1994. ),
  1995. /* Prototype */
  1996. array(
  1997. 'regexp' => 'prototype([rev0-9\.\-_])*(packer|min|lite)?\.(js|php)(\.gz)?',
  1998. 'exists' => 0
  1999. ),
  2000. /* MooTools */
  2001. array(
  2002. 'regexp' => 'mootools(_release)?([xv0-9\.\-_])*(core-yc|core|yui-compressed|comp|min)?\.(js|php)(\.gz)?',
  2003. 'exists' => 0
  2004. )
  2005. );
  2006. if (is_array($this->initial_files)) {
  2007. foreach($this->initial_files as $key => $value) {
  2008. /* don't touch all files -- just only requested ones */
  2009. if (!$tag || $value['tag'] == $tag) {
  2010. if (!empty($value['file']) && strlen($value['file']) > 7 && strpos($value['file'], "://")) {
  2011. /* exclude files from the same host */
  2012. if(!preg_match("@//(www\.)?". $this->host_escaped . "@s", $value['file'])) {
  2013. /* don't get actual files' content if option isn't enabled */
  2014. if ($this->options[$value['tag'] == 'script' ? 'javascript' : 'css']['external_scripts']) {
  2015. /* get an external file */
  2016. if (!preg_match("/\.(css|js)$/is", $value['file'])) {
  2017. /* dynamic file */
  2018. $file = $this->get_remote_file($this->resolve_amps($value['file_raw']), $value['tag']);
  2019. /* static file */
  2020. } else {
  2021. $file = $this->get_remote_file($value['file'], $value['tag']);
  2022. }
  2023. if (!empty($file)) {
  2024. $value['file'] = $this->initial_files[$key]['file'] = $this->options['javascript']['cachedir_relative'] . $file;
  2025. } else {
  2026. unset($this->initial_files[$key]);
  2027. }
  2028. } else {
  2029. if (empty($value['content'])) {
  2030. unset($this->initial_files[$key]);
  2031. }
  2032. }
  2033. } else {
  2034. $value['file'] = preg_replace("!https?://(www\.)?".
  2035. $this->host_escaped . "/+!s", "/", $value['file']);
  2036. }
  2037. }
  2038. $content_from_file = '';
  2039. if (!empty($value['file'])) {
  2040. /* convert dynamic files to static ones */
  2041. if (!preg_match("/\.(css|js)$/is", $value['file']) || strpos($value['file'], 'index.php/')) {
  2042. $dynamic_file = $value['file_raw'];
  2043. /* touch only non-external scripts */
  2044. if (!strpos($dynamic_file, "://")) {
  2045. $dynamic_file = "http://" . $_SERVER['HTTP_HOST'] . $this->convert_path_to_absolute($dynamic_file, array('file' => $value['file']), true);
  2046. }
  2047. $static_file = ($this->options[$value['tag'] == 'script' ? 'javascript' : 'css']['cachedir']) . $this->get_remote_file($this->resolve_amps($dynamic_file), $value['tag']);
  2048. if (@is_file($static_file)) {
  2049. $value['file'] = str_replace($this->options['document_root'], "/", $static_file);
  2050. } else {
  2051. unset($value['file']);
  2052. }
  2053. }
  2054. if ($value['tag'] == 'link') {
  2055. /* recursively resolve @import in files */
  2056. $content_from_file = (empty($value['media']) ? "" : "@media " . $value['media'] . "{") .
  2057. $this->resolve_css_imports($value['file']) .
  2058. (empty($value['media']) ? "" : "}");
  2059. /* convert CSS images' paths to absolute */
  2060. $content_from_file = $this->convert_paths_to_absolute($content_from_file, array('file' => $value['file']));
  2061. } else {
  2062. $content_from_file = @file_get_contents($this->get_file_name($value['file']));
  2063. }
  2064. /* remove duplicates */
  2065. if ($value['tag'] == 'script' &&
  2066. $this->options['javascript']['remove_duplicates']) {
  2067. foreach ($duplicates as $k => $duplicate) {
  2068. if (preg_match("@" . $duplicate['regexp'] . "$@is", $value['file'])) {
  2069. if ($duplicate['exists']) {
  2070. $content_from_file = '';
  2071. } else {
  2072. $duplicates[$k]['exists'] = 1;
  2073. }
  2074. }
  2075. }
  2076. }
  2077. }
  2078. /* remove BOM */
  2079. $content_from_file = str_replace('', '', $content_from_file);
  2080. /* don't delete any detected scripts from array -- we need to clean up HTML page from them */
  2081. if (empty($value['file']) && (empty($last_key[$value['tag']]) || $key != $last_key[$value['tag']])) {
  2082. /* glue inline and external content */
  2083. if (($this->options['javascript']['inline_scripts'] && $value['tag'] == 'script') || ($this->options['css']['inline_scripts'] && $value['tag'] == 'link')) {
  2084. /* resolve @import from inline styles */
  2085. if ($value['tag'] == 'link') {
  2086. $value['content'] = (empty($value['media']) ? "" : "@media " . $value['media'] . "{") .
  2087. $this->resolve_css_imports($value['content'], true) .
  2088. (empty($value['media']) ? "" : "}");
  2089. /* convert CSS images' paths to absolute */
  2090. $value['content'] = $this->convert_paths_to_absolute($value['content'],
  2091. array('file' => $this->options['document_root_relative']));
  2092. }
  2093. $text = (empty($value['content']) ? '' : "\n" . $value['content']);
  2094. /* if we can't add to existing tag -- store for the future */
  2095. if (empty($last_key[$value['tag']])) {
  2096. $stored[$value['tag']] = empty($stored[$value['tag']]) ? $text : $stored[$value['tag']] . $text;
  2097. $last_key_flushed[$value['tag']] = $key;
  2098. } else {
  2099. $this->initial_files[$last_key[$value['tag']]]['content'] .= $text;
  2100. }
  2101. /* null content not to include anywhere, we still have source code in 'source' */
  2102. $this->initial_files[$key]['content'] = '';
  2103. }
  2104. } elseif (!empty($content_from_file)) {
  2105. /* don't rewrite existing content inside script tags */
  2106. $this->initial_files[$key]['content'] = $content_from_file . (empty($value['content']) ? '' : "\n" . $value['content']);
  2107. /* add stored content before, but leave styles stored */
  2108. if (!empty($stored[$value['tag']])) {
  2109. /* preserve order of merged content */
  2110. if ($last_key_flushed[$value['tag']] < $key) {
  2111. $this->initial_files[$key]['content'] = $stored[$value['tag']] . "\n" . $this->initial_files[$key]['content'];
  2112. } else {
  2113. $this->initial_files[$key]['content'] .= "\n" . $stored[$value['tag']];
  2114. }
  2115. $stored[$value['tag']] = '';
  2116. }
  2117. $last_key[$value['tag']] = $key;
  2118. }
  2119. }
  2120. }
  2121. /* check for stored content and flush it */
  2122. foreach ($stored as $tag => $stored_content) {
  2123. $this->initial_files[$last_key_flushed[$tag]]['content'] = $stored_content;
  2124. }
  2125. }
  2126. }
  2127. /**
  2128. * Sets the headers to be sent in the javascript and css files
  2129. *
  2130. **/
  2131. function set_gzip_headers () {
  2132. /* define encoding for HTML page */
  2133. $this->set_gzip_encoding();
  2134. /* When will the file expire? */
  2135. $offset = 6000000 * 60 ;
  2136. $ExpStr = "Expires: " .
  2137. gmdate("D, d M Y H:i:s",
  2138. $this->time + $offset) . " GMT";
  2139. $types = array("css", "javascript");
  2140. foreach ($types as $type) {
  2141. /* Always send etag */
  2142. $this->gzip_header[$type] = '<?php
  2143. // Determine supported compression method
  2144. if (!empty($_SERVER["HTTP_ACCEPT_ENCODING"])) {
  2145. $_SERVER["HTTP_ACCEPT_ENCODING"] = strtolower($_SERVER["HTTP_ACCEPT_ENCODING"]);
  2146. $gzip = strstr($_SERVER["HTTP_ACCEPT_ENCODING"], "gzip") || !empty($_COOKIE["_wo_gzip"]);
  2147. $xgzip = strstr($_SERVER["HTTP_ACCEPT_ENCODING"], "x-gzip");
  2148. $deflate = strstr($_SERVER["HTTP_ACCEPT_ENCODING"], "deflate");
  2149. $xdeflate = strstr($_SERVER["HTTP_ACCEPT_ENCODING"], "x-deflate");
  2150. } elseif (empty($_SERVER["HTTP_ACCEPT_ENCODING"]) && !empty($_COOKIE["_wo_gzip"])) {
  2151. $gzip = 1;
  2152. }
  2153. // Determine used compression method
  2154. $encoding = empty($gzip) ? (empty($xgzip) ? (empty($deflate) ? (empty($xdeflate) ? "none" : "x-deflate") : "deflate") : "x-gzip") : "gzip";
  2155. $hash = "' . $this->time . '-" . str_replace("x-", "", $encoding);
  2156. header ("Etag: \"" . $hash . "\"");
  2157. ?>';
  2158. /* Send 304? */
  2159. $this->gzip_header[$type] .= '<?php
  2160. if ((isset($_SERVER["HTTP_IF_NONE_MATCH"]) &&
  2161. stripslashes($_SERVER["HTTP_IF_NONE_MATCH"]) == "\"" . $hash . "\"") ||
  2162. (isset($_SERVER["HTTP_IF_MATCH"]) &&
  2163. stripslashes($_SERVER["HTTP_IF_MATCH"]) == "\"" . $hash . "\"")) {
  2164. // Return visit and no modifications, so do not send anything
  2165. header ("HTTP/1.0 304 Not Modified");
  2166. header ("Content-Length: 0");
  2167. exit();
  2168. }
  2169. ?>';
  2170. /* ob_start ("ob_gzhandler"); */
  2171. if (!empty($this->options[$type]['gzip'])) {
  2172. $this->gzip_header[$type] .= '<?php
  2173. ob_start("compress_output_option");
  2174. function compress_output_option($contents) {
  2175. global $encoding, $gzip, $xgzip;
  2176. // Check for buggy versions of Internet Explorer
  2177. if (!empty($_SERVER["HTTP_USER_AGENT"]) && !strstr($_SERVER["HTTP_USER_AGENT"], "Opera") &&
  2178. preg_match("/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i", $_SERVER["HTTP_USER_AGENT"], $matches)) {
  2179. $version = floatval($matches[1]);
  2180. // IE6- can loose first 2048 bytes of gzipped content, code from Bitrix
  2181. if ($version < 7) {
  2182. $contents = str_repeat(" ", 2048) . "\r\n" . $contents;
  2183. }
  2184. }
  2185. if (isset($encoding) && $encoding != "none")
  2186. {
  2187. // try to get gzipped content from file
  2188. $extension = $gzip || $xgzip ? "gz" : "df";
  2189. $content = @file_get_contents(__FILE__ . "." . $extension);
  2190. $gzipped = 0;
  2191. if (empty($content)) {
  2192. // Send compressed contents
  2193. if ($gzip || $xgzip) {
  2194. if (function_exists("gzencode")) {
  2195. $contents = gzencode($contents, '. $this->options[$type]['gzip_level'] .', FORCE_GZIP);
  2196. $gzipped = 1;
  2197. }
  2198. } else {
  2199. if (function_exists("gzdeflate")) {
  2200. $contents = gzdeflate($contents, '. $this->options[$type]['gzip_level'] .');
  2201. $gzipped = 1;
  2202. }
  2203. }
  2204. if ($gzipped) {
  2205. $fp = @fopen(__FILE__ . "." . $extension, "wb");
  2206. if ($fp) {
  2207. @fwrite($fp, $contents);
  2208. @fclose($fp);
  2209. }
  2210. }
  2211. } else {
  2212. $contents = $content;
  2213. $gzipped = 1;
  2214. }
  2215. if ($gzipped) {
  2216. header ("Content-Encoding: " . $encoding);
  2217. }
  2218. header ("Content-Length: " . strlen($contents));
  2219. }
  2220. return $contents;
  2221. }
  2222. ?>';
  2223. }
  2224. if (!empty($this->options[$type]['far_future_expires_php'])) {
  2225. $this->gzip_header[$type] .= '<?php
  2226. header("Cache-Control: private, max-age=315360000");
  2227. header("' . $ExpStr . '");
  2228. ?>';
  2229. }
  2230. $this->gzip_header[$type] .= '<?php
  2231. header("Content-type: text/' . $type .'; charset: UTF-8");
  2232. ?>';
  2233. } // end FE
  2234. }
  2235. /**
  2236. * Returns a path or url without the querystring and anchor
  2237. *
  2238. **/
  2239. function strip_querystring ($path) {
  2240. if ($commapos = strpos($path, '?')) {
  2241. $path = substr($path, 0, $commapos);
  2242. }
  2243. if ($numberpos = strpos($path, '#')) {
  2244. $path = substr($path, 0, $numberpos);
  2245. }
  2246. return $path;
  2247. }
  2248. /**
  2249. * Minifies CSS - removes unnecessary symbols
  2250. *
  2251. **/
  2252. function minify_text ($txt) {
  2253. /* Remove simple comments */
  2254. $txt = preg_replace('!(/\*.*?\*/|^ | $)!is', '', $txt);
  2255. /* Remove line breaks, compress whitespaces */
  2256. $txt = preg_replace('![\s\t\r\n]+!', ' ', $txt);
  2257. /* Remove spaces for }, {, ;, ,: */
  2258. $txt = str_replace(array(' :', ': ', ' ,', ', ', ' ;', '; ', ' {', '{ ', ' }', '} '), array(':', ':', ',', ',', ';', ';', '{', '{', '}', '}'), $txt);
  2259. /* Remove excessive symbols */
  2260. $txt = str_replace(array(' 0px', ':0px', ';}', ':0 0 0 0', ':0.', ' 0.'), array(' 0', ':0', '}', ':0', ':.', ' .'), $txt);
  2261. return trim($txt);
  2262. }
  2263. /**
  2264. * Safely trims whitespace from an HTML page
  2265. * Adapted from smarty code http://www.smarty.net/
  2266. **/
  2267. function trimwhitespace ($source) {
  2268. if (!empty($this->options['page']['minify']) ||
  2269. !empty($this->options['page']['unobtrusive_all'])) {
  2270. if (!empty($this->options['page']['html_tidy'])) {
  2271. $_script_blocks = array(array(), array(), array(),
  2272. array(), array(), array());
  2273. /* Pull out the script, textarea and pre blocks */
  2274. $this->trimwhitespace_find('<script', '</script>',
  2275. '@@@WBO:TRIM:SCRIPT0@@@', $source, $_script_blocks[0]);
  2276. $this->trimwhitespace_find('<SCRIPT', '</SCRIPT>',
  2277. '@@@WBO:TRIM:SCRIPT1@@@', $source, $_script_blocks[1]);
  2278. $this->trimwhitespace_find('<textarea', '</textarea>',
  2279. '@@@WBO:TRIM:SCRIPT2@@@', $source, $_script_blocks[2]);
  2280. $this->trimwhitespace_find('<TEXTAREA', '</TEXTAREA>',
  2281. '@@@WBO:TRIM:SCRIPT3@@@', $source, $_script_blocks[3]);
  2282. $this->trimwhitespace_find('<pre', '</pre>',
  2283. '@@@WBO:TRIM:SCRIPT4@@@', $source, $_script_blocks[4]);
  2284. $this->trimwhitespace_find('<PRE', '</PRE>',
  2285. '@@@WBO:TRIM:SCRIPT5@@@', $source, $_script_blocks[5]);
  2286. } else {
  2287. preg_match_all("!(<script.*?</script>|<textarea.*?</textarea>|<pre.*?</pre>)!is", $source, $match);
  2288. $_script_blocks = $match[0];
  2289. $source = preg_replace("!(<script.*?</script>|<textarea.*?</textarea>|<pre.*?</pre>)!is", '@@@WBO:TRIM:SCRIPT@@@', $source);
  2290. }
  2291. }
  2292. /* add multiple hosts or redirects for static images */
  2293. if ((!empty($this->options['page']['parallel']) &&
  2294. !empty($this->options['page']['parallel_hosts'])) ||
  2295. !empty($this->options['page']['far_future_expires_rewrite']) ||
  2296. !empty($this->options['page']['sprites'])) {
  2297. $source = $this->add_multiple_hosts($source,
  2298. explode(" ", $this->options['page']['parallel_hosts']),
  2299. explode(" ", $this->options['page']['parallel_satellites']),
  2300. explode(" ", $this->options['page']['parallel_satellites_hosts']));
  2301. }
  2302. /* remove all leading spaces, tabs and carriage returns NOT preceeded by a php close tag */
  2303. if (!empty($this->options['page']['minify'])) {
  2304. $source = trim(preg_replace('/((?<!\?>)\n)[\t\s]+/m', '\1', $source));
  2305. /* replace ' >' with '>', remove \r symbols */
  2306. $source = str_replace(array(' >', "\r"), array('>', ''), $source);
  2307. }
  2308. /* one-strig-HTML takes about 20-50ms */
  2309. if (!empty($this->options['page']['minify_aggressive'])) {
  2310. /* ' />' with '/>' breaks System - Cache in Joomla! */
  2311. $source = str_replace(' />', '/>', $source);
  2312. /* replace breaks with nothing for block tags */
  2313. $source = preg_replace("@[\s\t\r\n]*(</?)(!--|!DOCTYPE|address|area|audioscope|base|bgsound|blockquote|body|br|caption|center|col|colgroup|comment|dd|div|dl|dt|embed|fieldset|form|frame|frameset|h[123456]|head|hr|html|iframe|keygen|layer|legend|li|link|map|marquee|menu|meta|noembed|noframes|noscript|object|ol|optgroup|option|p|param|samp|select|sidebar|style|table|tbody|td|tfoot|th|title|tr|ul|var)([\s/][^>]*)?>[\s\t\r\n]+@si", "$1$2$3>", $source);
  2314. /* replace breaks with space for inline tags */
  2315. $source = preg_replace("@(</?)(a|abbr|acronym|b|basefont|bdo|big|blackface|blink|button|cite|code|del|dfn|dir|em|font|i|img|input|ins|isindex|kbd|label|q|s|small|span|strike|strong|sub|sup|u)([\s/][^>]*)?>[\s\t\r\n]+@si", "$1$2$3> ", $source);
  2316. }
  2317. /* replace multiple spaces with single one
  2318. $source = preg_replace("/[\s\t\r\n]+/", " ", $source); */
  2319. /* replace script, textarea, pre blocks */
  2320. if (!empty($this->options['page']['unobtrusive_all']) ||
  2321. !empty($this->options['page']['minify'])) {
  2322. $before_body = '';
  2323. if (!empty($this->options['page']['html_tidy'])) {
  2324. for ($i = 0; $i < 6; $i++) {
  2325. $_block = $_script_blocks[$i];
  2326. if (count($_block)) {
  2327. $before_body .=
  2328. $this->trimwhitespace_replace("@@@WBO:TRIM:SCRIPT" .
  2329. $i . "@@@", $_block, $source);
  2330. }
  2331. }
  2332. } else {
  2333. $before_body =
  2334. $this->trimwhitespace_replace("@@@WBO:TRIM:SCRIPT@@@",
  2335. $_script_blocks, $source);
  2336. }
  2337. /* move all scripts to </body> */
  2338. if (!empty($before_body)) {
  2339. if (!empty($this->options['page']['html_tidy']) &&
  2340. ($bodypos = strpos($source, "</body>"))) {
  2341. $source = substr_replace($source,
  2342. $before_body, $bodypos, 0);
  2343. } elseif (!empty($this->options['page']['html_tidy']) &&
  2344. ($bodypos = strpos($this->content, "</BODY>"))) {
  2345. $source = substr_replace($source,
  2346. $before_body, $bodypos, 0);
  2347. } else {
  2348. /* a number of engines doesn't set </body> */
  2349. if (!preg_match('@</body>@is', $source)) {
  2350. $source .= $before_body;
  2351. } else {
  2352. $source = preg_replace('@(</body>)@is',
  2353. $before_body . "$1", $source);
  2354. }
  2355. }
  2356. }
  2357. }
  2358. /* remove website host */
  2359. if (!empty($this->options['page']['minify_aggressive'])) {
  2360. /* fix for base tag */
  2361. preg_match_all("@<base[^>]+>@is", $source, $matches);
  2362. $basetag = false;
  2363. if (count($matches) && count($matches[0])) {
  2364. $basetag = $matches[0][0];
  2365. }
  2366. if ($basetag) {
  2367. $source = str_replace($basetag, '@@@WSSBASE@@@', $source);
  2368. }
  2369. $source = preg_replace("@(src|href)=(['\"])(http" .
  2370. $this->https . "://)(www\.)?" .
  2371. $this->host . "/*@", "$1=$2/", $source);
  2372. if ($basetag) {
  2373. $source = str_replace('@@@WSSBASE@@@', $basetag, $source);
  2374. }
  2375. }
  2376. return $source;
  2377. }
  2378. /**
  2379. * Helper function for trimwhitespace, finds all blocks
  2380. *
  2381. **/
  2382. function trimwhitespace_find ($block_begin, $block_end, $spot, &$subject, &$return) {
  2383. $len = strlen($block_end);
  2384. while ($posbegin = strpos($subject, $block_begin)) {
  2385. if ((($posend = strpos($subject, $block_end)) !== false) && ($posend > $posbegin)) {
  2386. $return[] = substr($subject, $posbegin, $posend - $posbegin + $len);
  2387. $subject = substr_replace($subject, $spot, $posbegin, $posend - $posbegin + $len);
  2388. } else {
  2389. break;
  2390. }
  2391. }
  2392. }
  2393. /**
  2394. * Helper function for trimwhitespace
  2395. *
  2396. **/
  2397. function trimwhitespace_replace ($search_str, $replace, &$subject) {
  2398. $_len = strlen($search_str);
  2399. $_pos = 0;
  2400. $_to_body = '';
  2401. for ($_i=0, $_count = count($replace); $_i<$_count; $_i++) {
  2402. if (($_pos = strpos($subject, $search_str, $_pos)) !== false) {
  2403. /* move scripts to </body>. Skip dynamic styles loader */
  2404. if (!empty($this->options['page']['unobtrusive_all']) &&
  2405. !strpos($replace[$_i], '_weboptimizer_load')) {
  2406. if ((!empty($this->options['html_tidy']) &&
  2407. (strpos($replace[$_i], '<script') ||
  2408. strpos($replace[$_i], '<SCRIPT'))) ||
  2409. preg_match("@<script@is", $replace[$_i])) {
  2410. $_to_body .= $replace[$_i];
  2411. $replace[$_i] = '';
  2412. }
  2413. }
  2414. $subject = substr_replace($subject, $replace[$_i], $_pos, $_len);
  2415. } else {
  2416. break;
  2417. }
  2418. }
  2419. return $_to_body;
  2420. }
  2421. /**
  2422. * Replaces one JS code in HTML with another
  2423. * Returns string to place before </body>
  2424. *
  2425. **/
  2426. function replace_unobtrusive_generic ($match_string, $stuff, $height = 0, $inline = false, $onload_mask = false, $onload_result = false) {
  2427. $return = '';
  2428. $initial_height = empty($height) ? 0 : $height;
  2429. $onload = !empty($this->options['page']['unobtrusive_onload']) &&
  2430. $onload_mask && $onload_result;
  2431. preg_match_all($match_string, $this->content, $matches, PREG_SET_ORDER);
  2432. if (!empty($matches)) {
  2433. foreach ($matches as $key => $value) {
  2434. $height = $initial_height;
  2435. if (empty($height)) {
  2436. /* try to calculate height for AdWords */
  2437. switch ($stuff) {
  2438. case 'gw':
  2439. $height = round(substr($value[0], strpos($value[0], 'google_ad_height =') + 18, 5));
  2440. break;
  2441. case 'aa':
  2442. $height = round(substr($value[0], strpos($value[0], 'amazon_ad_height = "') + 20, 5));
  2443. break;
  2444. case 'cp':
  2445. $pos = strpos($value[0], 'thumb_size:') + 11;
  2446. $posend = strpos($value[0], ',', $pos);
  2447. $dims = explode('x', str_replace(array("'", " ", '"'), array(), substr($value[0], $pos, $posend)));
  2448. $height = round($dims[1]);
  2449. break;
  2450. case 'if':
  2451. case 'IF':
  2452. if (preg_match("@height\s*=@is", $value[0])) {
  2453. $height = round(preg_replace("@.*height\s*=[\s'\"](.*)[\s'\"]@", "$1", $value[0]));
  2454. }
  2455. break;
  2456. }
  2457. }
  2458. /* count param for str_replace available only in PHP5 */
  2459. $pos = strpos($this->content, $value[0]);
  2460. $len = strlen($value[0]);
  2461. $tag = $inline ? 'span' : 'div';
  2462. $this->content = substr_replace($this->content,
  2463. ($stuff == 'fc' ? '<?xml:namespace prefix="fb"/>' : '') .
  2464. '<' .
  2465. $tag .
  2466. ' id="' .
  2467. $stuff .
  2468. '_dst_' .
  2469. $key .
  2470. '"' .
  2471. ($height && $inline ? ' style="'.
  2472. ($onload ? 'position:relative;' : '') .
  2473. 'height:' .
  2474. $height .
  2475. 'px;display:inline-block"' : '') .
  2476. ($height && !$inline ? ' style="'.
  2477. ($onload ? 'position:relative;' : '') .
  2478. 'height:' .
  2479. $height .
  2480. 'px"' : '') .
  2481. '></' .
  2482. $tag .
  2483. '>', $pos, $len);
  2484. if (!$onload) {
  2485. $return .= '<' .
  2486. $tag .
  2487. ' id="'.
  2488. $stuff .'_src_' . $key .
  2489. '">' .
  2490. $value[0] .
  2491. '</' .
  2492. $tag .
  2493. '><script type="text/javascript">(function(){var a=document,b=a.getElementById("' .
  2494. $stuff . '_dst_' . $key . '"),c=b.parentNode,d=a.getElementById("' .
  2495. $stuff . '_src_' . $key . '");if(c===a.body){c.insertBefore(d,b);c.removeChild(b)}else{c.innerHTML=c.innerHTML.replace(/\x3c' .
  2496. $tag .
  2497. '[^>]+id="?' .
  2498. $stuff .
  2499. '_dst_' .
  2500. $key .
  2501. '["\s>].*?\x3c\/' .
  2502. $tag .
  2503. '>/i,d.innerHTML);b=a.getElementById("' .
  2504. $stuff . '_src_' . $key . '");b.parentNode.removeChild(b)}}())</script>';
  2505. } else {
  2506. $return .= '<script type="text/javascript">wss_onload[wss_onload.length]=function(){wss_parentNode=document.getElementById(\'' .
  2507. $stuff . '_dst_' . $key
  2508. .'\');' .
  2509. str_replace(array("\n", "\r", '###WSS###', '<div', '</div', '// ]]>'),
  2510. array(' ', '', $key, '\x3cdiv', '\x3c/div', ''),
  2511. preg_replace("@(<!--.*?-->|/\*.*?\*/)@is", "", preg_replace("@" . $onload_mask . "@is",
  2512. $onload_result, $value[0]))) .
  2513. '}</script>';
  2514. }
  2515. }
  2516. }
  2517. return $return;
  2518. }
  2519. /**
  2520. * Moves all known informers before </body>
  2521. * Also handles counters and ads
  2522. * Leaves placeholders for them in content
  2523. *
  2524. **/
  2525. function replace_informers ($options) {
  2526. $before_body = '';
  2527. $before_body_onload = empty($this->options['page']['unobtrusive_onload']) ?
  2528. '' : (empty($this->options['page']['unobtrusive_inline']) ?
  2529. '<script type="text/javascript" src="//' .
  2530. (empty($this->options['javascript']['host']) ?
  2531. $this->options['page']['host'] :
  2532. $this->options['javascript']['host']) .
  2533. (empty($this->options['javascript']['far_future_expires_rewrite']) ?
  2534. '' : $this->options['page']['cachedir_relative'] . 'wo.static.php?') .
  2535. $this->options['javascript']['cachedir_relative'] .
  2536. 'yass.loader.js"></script>' :
  2537. '<script type="text/javascript">(function(){function j(a){var b={};a=a.split(",");for(var g=0;g<a.length;g++)b[a[g]]=true;return b}var o=document,h;o.write=function(a){h=wss_parentNode||document.body;new x(a,{start:function(b,g,k){b=o.createElement(b);for(var d=0;d<g.length;d++)b.setAttribute(g[d].name,g[d].value);h.appendChild(b);k||(h=b)},end:function(){h=h.parentNode},chars:function(b){switch(h.nodeName.toLowerCase()){case "script":b&&eval(b);break;default:b&&h.appendChild(o.createTextNode(b));break}},comment:function(b){h.appendChild(o.createComment(b))}})};var r=/^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>\s]+))?)*)\s*(\/?)>/,s=/^<\/(\w+)[^>]*>/,y=/(\w+)(?:\s*=\s*(?:(?:"((?:\\\\.|[^"])*)")|(?:\'((?:\\\\.|[^\'])*)\')|([^>\s]+)))?/g,z=j("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed"),A=j("address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul"),B=j("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var"),C=j("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr"),D=j("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected"),E=j("script,style"),x=function(a,b){function g(m,f,e,i){if(A[f])for(;c.last()&&B[c.last()];)k("",c.last());C[f]&&c.last()==f&&k("",f);(i=z[f]||!!i)||c.push(f);if(b.start){var t=[];e.replace(y,function(p,q,u,v,w){p=u?u:v?v:w?w:D[q]?q:"";t.push({name:q,value:p,escaped:p.replace(/(^|[^\\\\])"/g,\'$1\\\\"\')})});b.start&&b.start(f,t,i)}}function k(m,f){if(f)for(e=c.length-1;e>=0;e--){if(c[e]==f)break}else var e=0;if(e>=0){for(var i=c.length-1;i>=e;i--)b.end&&b.end(c[i]);c.length=e}}var d,n,l,c=[];c.last=function(){return this[this.length-1]};this.parse=function(m){for(last=a=m;a;){n=true;if(!c.last()||!E[c.last()]){if(a.indexOf("<!--")==0){d=a.indexOf("--\>");if(d>=0){b.comment&&b.comment(a.substring(4,d));a=a.substring(d+3);n=false}}else if(a.indexOf("</")==0){if(l=a.match(s)){a=a.substring(l[0].length);l[0].replace(s,k);n=false}}else if(a.indexOf("<")==0)if(l=a.match(r)){a=a.substring(l[0].length);l[0].replace(r,g);n=false}if(n){d=a.indexOf("<");m=d<0?a:a.substring(0,d);a=d<0?"":a.substring(d);b.chars&&b.chars(m)}}else{a=a.replace(new RegExp("(.*)</"+c.last()+"[^>]*>"),function(f,e){e=e.replace(/<!--(.*?)--\>/g,"$1").replace(/<!\[CDATA\[(.*?)]]\>/g,"$1");b.chars&&b.chars(e);return""});k("",c.last())}if(a&&a==last)throw"Parse Error: "+a;last=a}};this.parse(a)}})();</script>') .
  2538. '<script type="text/javascript">wss_onload=[]</script>';
  2539. require($options['installdir'] . 'libs/php/config.unobtrusive.php');
  2540. foreach ($unobtrusive_items as $group => $items) {
  2541. if (!empty($options[$group])) {
  2542. foreach ($items as $key => $item) {
  2543. if (strpos($this->content, $item['marker'])) {
  2544. $before = $this->replace_unobtrusive_generic("@" . $item['regexp'] . "@is",
  2545. $key, empty($item['height']) ? 0 : $item['height'],
  2546. empty($item['inline']) ? false : $item['inline'],
  2547. empty($item['onload_before']) ? false : $item['onload_before'],
  2548. empty($item['onload_after']) ? false : $item['onload_after']);
  2549. /* switch between window.onload and onDOMready handlers */
  2550. if (!empty($item['onload_before']) && !empty($item['onload_after'])) {
  2551. $before_body_onload .= $before;
  2552. } else {
  2553. $before_body .= $before;
  2554. }
  2555. }
  2556. }
  2557. }
  2558. }
  2559. $before_body .= $before_body_onload;
  2560. $before_body .= empty($this->options['page']['unobtrusive_onload']) ?
  2561. '' : '<script type="text/javascript">wss_onload_ready=1;window[/*@cc_on!@*/0?"attachEvent":"addEventListener"](/*@cc_on "on"+@*/"load",function(){wss_onload_counter=0;setTimeout(function(){var a=wss_onload[wss_onload_counter];if(wss_onload_ready){wss_onload_ready=0;if(a){a()}wss_onload_counter++}if(a){setTimeout(arguments.callee,10)}},10)},false)</script>';
  2562. if (!empty($before_body)) {
  2563. if (!empty($options['html_tidy']) && ($bodypos = strpos($this->content, '</body>'))) {
  2564. $this->content = substr_replace($this->content, $before_body, $bodypos, 0);
  2565. } elseif (!empty($options['html_tidy']) && ($bodypos = strpos($this->content, '</BODY>'))) {
  2566. $this->content = substr_replace($this->content, $before_body, $bodypos, 0);
  2567. } else {
  2568. $this->content = preg_replace('@</body>@i', $before_body . "$0" , $this->content);
  2569. /* a number of engines doesn't set </body> */
  2570. if (!strpos($this->content, $before_body)) {
  2571. $this->content .= $before_body;
  2572. }
  2573. }
  2574. }
  2575. }
  2576. /**
  2577. * Removes all secondary stuff from HTML code
  2578. *
  2579. **/
  2580. function prepare_html ($source, $cssonly = false) {
  2581. $dest = $source;
  2582. /* remove conditional comments for current browser */
  2583. $dest = $this->remove_conditional_comments($dest);
  2584. /* Pull out the comment blocks to avoid touching conditional comments,
  2585. and some semi-standard complaint hacks, skip if we fetch body but not head */
  2586. if (!empty($this->options['javascript']['inline_scripts']) && !$cssonly) {
  2587. $dest = str_replace(
  2588. array('//]]>', '// ]]>', '<!--//-->', '<!-- // -->',
  2589. '<![CDATA[', '//><!--', '//--><!]]>', '//-->',
  2590. '<!--/*--><![CDATA[//><!--','//-->', '//<!--',
  2591. '// <!--', '// -->'), '', $dest);
  2592. $dest = preg_replace("@(<script[^>]*>)[\r\n\t\s]*<!--@is", "$1", $dest);
  2593. $dest = preg_replace("@-->[\r\n\t\s]*(</script>)@is", "$1", $dest);
  2594. }
  2595. /* remove comments from <style> constructions */
  2596. if (!empty($this->options['css']['inline_scripts'])) {
  2597. $dest = preg_replace("@(<style type=[\"']text/css[^>]*>)[\t\s\r\n]*<!--@is", "$1", $dest);
  2598. $dest = preg_replace("@[\t\s\r\n]*-->(</style>)@is", "$1", $dest);
  2599. }
  2600. if ($dest !== $source) {
  2601. /* replace current content with updated version */
  2602. $this->content = str_replace($source, $dest, $this->content);
  2603. }
  2604. /* and now remove all comments and parse result code -- to avoid IE code mixing with other browsers */
  2605. while ($compos = strpos($dest, '<!--')) {
  2606. $end = strpos(substr($dest, $compos), '-->');
  2607. $dest = substr_replace($dest, '', $compos, $end + 3);
  2608. }
  2609. return $dest;
  2610. }
  2611. /**
  2612. * Gets the head (and body) part(s) of the HTML document
  2613. *
  2614. **/
  2615. function get_head () {
  2616. if (empty($this->head) && empty($this->body)) {
  2617. /* try to define base URI for the document */
  2618. if ($this->options['page']['html_tidy']) {
  2619. if (($basepos = strpos($this->content, '<base')) || ($basepos = strpos($this->content, '<BASE'))) {
  2620. $basepos = strpos($this->content, 'href=', $basepos);
  2621. if (!$basepos) {
  2622. $basepos = strpos($this->content, 'HREF=', $basepos);
  2623. }
  2624. $baseend = strpos($this->content, '>', $basepos);
  2625. if ($this->content{$baseend-1} === '/') {
  2626. $baseend--;
  2627. }
  2628. $this->basehref = trim(str_replace(array('"', ""), array(), substr($this->content, $basepos + 5, $baseend - $basepos - 6)));
  2629. }
  2630. } elseif (preg_match("@<base\s+href@is", $this->content)) {
  2631. $this->basehref = preg_replace("@.*<base\s*href\s*=\s*['\"](.*?)['\"].*@is", "$1", $this->content);
  2632. }
  2633. if (!empty($this->basehref) && preg_match("@https?://(www\.)?" . $this->host . "@", $this->basehref)) {
  2634. $this->basehref = '';
  2635. }
  2636. /* change all links on the page according to DEBUG mode */
  2637. if ($this->debug_mode) {
  2638. $this->content = preg_replace("@(<a[^>]+href\s*=\s*['\"])([^\?]*?)(\?(.+?))?(['\"])@is", "$1$2?$4&amp;web_optimizer_debug=1$5", $this->content);
  2639. }
  2640. /* Remove comments ?*/
  2641. if (!empty($this->options['page']['remove_comments'])) {
  2642. /* skip removing escaped JavaScript code, thx to smart */
  2643. $this->content = str_replace(
  2644. array('//]]>', '// ]]>', '<!--//-->', '<!-- // -->',
  2645. '<![CDATA[', '//><!--', '//--><!]]>', '// -->',
  2646. '<!--/*--><![CDATA[//><!--','//-->', '--></script>',
  2647. '<script type="text/javascript"><!--',
  2648. '<script language="javascript" type="text/javascript" ><!--'),
  2649. array('@@@WSSLEAVE1@@@', '@@@WSSLEAVE2@@@', '@@@WSSLEAVE3@@@', '@@@WSSLEAVE4@@@',
  2650. '@@@WSSLEAVE5@@@', '@@@WSSLEAVE6@@@', '@@@WSSLEAVE7@@@', '@@@WSSLEAVE8@@@',
  2651. '@@@WSSLEAVE9@@@', '@@@WSSLEAVE10@@@', '@@@WSSLEAVE11@@@', '@@@WSSLEAVE12@@@',
  2652. '@@@WSSLEAVE13@@@'), $this->content);
  2653. $this->content = preg_replace("@<!--[^\[].*?-->@is", '', $this->content);
  2654. $this->content = str_replace(
  2655. array('@@@WSSLEAVE1@@@', '@@@WSSLEAVE2@@@', '@@@WSSLEAVE3@@@', '@@@WSSLEAVE4@@@',
  2656. '@@@WSSLEAVE5@@@', '@@@WSSLEAVE6@@@', '@@@WSSLEAVE7@@@', '@@@WSSLEAVE8@@@',
  2657. '@@@WSSLEAVE9@@@', '@@@WSSLEAVE10@@@', '@@@WSSLEAVE11@@@', '@@@WSSLEAVE12@@@',
  2658. '@@@WSSLEAVE13@@@'),
  2659. array('//]]>', '// ]]>', '<!--//-->', '<!-- // -->',
  2660. '<![CDATA[', '//><!--', '//--><!]]>', '// -->',
  2661. '<!--/*--><![CDATA[//><!--','//-->', '--></script>',
  2662. '<script type="text/javascript"><!--',
  2663. '<script language="javascript" type="text/javascript" ><!--'), $this->content);
  2664. }
  2665. /* fix script positioning for DLE */
  2666. if ($this->options['javascript']['minify'] && strpos($this->content, '<div id="loading-layer"')) {
  2667. $this->content = preg_replace("@(</head>)[\r\n\t\s]*(<body[^>]*>)?[\r\n\t\s]*(<script.*?)(<div id=\"loading-layer.*=10\); \"></div>)[\r\n\t\s]*(<script.*?)<(body|div|table)@is", "$3$5$1$2$4<$6", $this->content);
  2668. }
  2669. /* fix Shadowbox inclusions */
  2670. if (($this->options['javascript']['minify'] || $this->options['css']['minify']) && strpos($this->content, 'Shadowbox.load')) {
  2671. $this->content = preg_replace("@Shadowbox.loadSkin\(['\"](.+?)['\"]\s*,\s*['\"](.+?)['\"]\);@is", "</script><link rel=\"stylesheet\" type=\"text/css\" href=\"$2/$1/skin.css\"><script type=\"text/javascript\" src=\"$2/$1/skin.js\"></script><script type=\"text/javascript\">", $this->content);
  2672. $this->content = preg_replace("@Shadowbox.loadLanguage\(['\"](.+?)['\"]\s*,\s*['\"](.+?)['\"]\);@is", "</script><script type=\"text/javascript\" src=\"$2/shadowbox-$1.js\"></script><script type=\"text/javascript\">", $this->content);
  2673. preg_match_all("@Shadowbox.loadPlayer\(\[([^\]]+?)\]\s*,\s*['\"](.*?)['\"]\);@", $this->content, $matches, PREG_SET_ORDER);
  2674. foreach ($matches as $match) {
  2675. $inclusion = '</script>';
  2676. $players = explode(",", $match[1]);
  2677. foreach ($players as $player) {
  2678. $player = str_replace(array('\'', '"', ' '), '', $player);
  2679. $inclusion .= "<script type=\"text/javascript\" src=\"" .
  2680. $match[2] .
  2681. "/shadowbox-" .
  2682. $player .
  2683. ".js\"></script>";
  2684. }
  2685. $this->content = str_replace($match[0], $inclusion . "<script type=\"text/javascript\">", $this->content);
  2686. }
  2687. }
  2688. /* skip parsing head if we include both CSS and JavaScript from head+body */
  2689. if (empty($this->options['javascript']['minify_body']) ||
  2690. empty($this->options['css']['minify_body'])) {
  2691. if (empty($this->options['page']['html_tidy'])) {
  2692. preg_match("!<head(\s+[^>]+)?>.*?<body!is",
  2693. $this->content, $matches);
  2694. $head = $matches[0];
  2695. } else {
  2696. if ($headpos = strpos($this->content, '<head')) {
  2697. $head = substr($this->content, $headpos,
  2698. strpos($this->content, '</head>') - $headpos);
  2699. } elseif ($headpos = strpos($this->content, '<HEAD')) {
  2700. $head = substr($this->content, $headpos,
  2701. strpos($this->content, '</HEAD>') - $headpos);
  2702. }
  2703. }
  2704. if (!empty($head)) {
  2705. $this->head = $this->prepare_html($head);
  2706. }
  2707. }
  2708. /* get head+body if required */
  2709. if (!empty($this->options['javascript']['minify_body']) || !empty($this->options['css']['minify_body'])) {
  2710. preg_match("!<head.*!is", $this->content, $matches);
  2711. if (!empty($matches[0])) {
  2712. $this->body = $this->prepare_html($matches[0], empty($this->options['javascript']['minify_body']));
  2713. }
  2714. }
  2715. $xhtml = strpos($this->content, 'XHTML');
  2716. /* split XHTML behavior from HTML */
  2717. $this->xhtml = $xhtml > 34 && $xhtml < 100;
  2718. /* add WEBO Site SpeedUp spot */
  2719. if (!empty($this->options['page']['spot'])) {
  2720. $this->content .= '<!--WSS-->';
  2721. }
  2722. if ($this->debug_mode || !empty($this->options['page']['counter'])) {
  2723. $stamp = '<script type="text/javascript">__WSS=(new Date()).getTime()</script>';
  2724. if ($this->options['page']['html_tidy'] &&
  2725. ($headpos = strpos($this->content, '<head'))) {
  2726. $headend = strpos($this->content, '>', $headpos);
  2727. $this->content = substr_replace($this->content,
  2728. $stamp, $headend + 1, 0);
  2729. } elseif ($this->options['page']['html_tidy'] &&
  2730. ($headpos = strpos($this->content, '<HEAD'))) {
  2731. $headend = strpos($this->content, '>', $headpos);
  2732. $this->content = substr_replace($this->content,
  2733. $stamp, $headend + 1, 0);
  2734. } else {
  2735. $this->content = preg_replace("@<head[^>]*>@is",
  2736. "$0" . $stamp, $this->content);
  2737. }
  2738. }
  2739. /* add info about client side load speed */
  2740. if ($this->debug_mode) {
  2741. $this->content = preg_replace("@(<head[^>]*>)@is", "$1<script type=\"text/javascript\">window[/*@cc_on !@*/0?'attachEvent':'addEventListener'](/*@cc_on 'on'+@*/'load',function(){__WSS=(new Date()).getTime()-__WSS},false);window.onerror=function(){window.__WSSERR=(typeof window.__WSSERR!=='undefined'?window.__WSSERR:0)+1;return false}</script>", $this->content);
  2742. }
  2743. if (!empty($this->options['page']['footer']) || !empty($this->options['page']['counter'])) {
  2744. $stamp = '';
  2745. /* add WEBO Site SpeedUp stamp */
  2746. if (!empty($this->options['page']['footer'])) {
  2747. $style = empty($this->options['page']['footer_style']) ? '' :
  2748. ' style="' . $this->options['page']['footer_style'] . '"';
  2749. $title = empty($this->options['page']['footer_text']) ? '' :
  2750. ' title="' . $this->options['page']['footer_text'] . '"';
  2751. $text = empty($this->options['page']['footer_text']) ||
  2752. !empty($this->options['page']['footer_image']) ? '' : $this->options['page']['footer_text'];
  2753. /* place or not image? */
  2754. if (empty($this->options['page']['footer_image'])) {
  2755. $background_image = $background_style = '';
  2756. } else {
  2757. $background_image = $this->options['css']['cachedir_relative'] . $this->options['page']['footer_image'];
  2758. $image_style =
  2759. 'display:block;text-decoration:none;width:100px;height:100px;';
  2760. if (in_array($this->ua_mod, array('.ie5', '.ie6'))) {
  2761. $background_style = $image_style .
  2762. 'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=' .
  2763. $background_image .
  2764. ',sizingMethod=\'scale\')';
  2765. } else {
  2766. $background_style = $image_style .
  2767. 'background:url(' .
  2768. $background_image .
  2769. ')';
  2770. }
  2771. $background_style = ' style="' . $background_style . '"';
  2772. }
  2773. /* choose between link or span */
  2774. if (empty($text)) {
  2775. $el = 'a href="http://www.webogroup.com/" rel="nofollow"';
  2776. $el_close = 'a';
  2777. } else {
  2778. $el = $el_close = 'span';
  2779. }
  2780. /* finally from stamp */
  2781. $stamp .= '<div' .
  2782. $style .
  2783. '><' .
  2784. $el .
  2785. $title .
  2786. $background_style .
  2787. '>'.
  2788. $text .
  2789. '</' .
  2790. $el_close .
  2791. '></div>';
  2792. }
  2793. /* add WEBO Site SpeedUp page load counter */
  2794. if (!empty($this->options['page']['counter'])) {
  2795. $stamp .= '<script type="text/javascript">(function(){window[/*@cc_on !@*/0?"attachEvent":"addEventListener"](/*@cc_on "on"+@*/"load",function(){if(typeof _gat!=="undefined"){var a,b=_gat.vb,c;for(a in _gat.vb){c=b[a].s}a=_gat._getTracker(c);b=(new Date()).getTime()-__WSS;a._trackEvent("WEBO Site SpeedUp","Page Load Time",50*Math.round(b/50)+"ms",b)}},false)})()</script>';
  2796. }
  2797. if ($this->options['page']['html_tidy'] &&
  2798. ($bodypos = strpos($this->content, '</body>'))) {
  2799. $this->content = substr_replace($this->content,
  2800. $stamp, $bodypos, 0);
  2801. } elseif ($this->options['page']['html_tidy'] &&
  2802. ($bodypos = strpos($this->content, '</BODY>'))) {
  2803. $this->content = substr_replace($this->content,
  2804. $stamp, $bodypos, 0);
  2805. } else {
  2806. $this->content = preg_replace("@</body>@i",
  2807. $stamp . "$0", $this->content);
  2808. /* a number of engines doesn't set </body> */
  2809. if (!strpos($this->content, $stamp)) {
  2810. $this->content .= $stamp;
  2811. }
  2812. }
  2813. }
  2814. }
  2815. }
  2816. /**
  2817. * Removes conditional comments for MSIE 5-9
  2818. *
  2819. **/
  2820. function remove_conditional_comments ($source) {
  2821. if (!empty($this->ua_mod)) {
  2822. /* preliminary strpos saves about 50% of CPU */
  2823. if (strpos($source, 'IE]>') !== false) {
  2824. $source = preg_replace("@<!--\[if \(?IE\)?\]>(.*?)<!\[endif\]-->@s", "$1", $source);
  2825. }
  2826. for ($version = $this->min_ie_version; $version < $this->max_ie_version; $version++) {
  2827. /* detect */
  2828. if ($this->ua_mod == ".ie" . $version || ($version == 7 && $this->ua_mod == '.ie7')) {
  2829. /* detect equality */
  2830. if (strpos($source, 'IE ' . $version . ']>') !== false) {
  2831. $source = preg_replace("@<!--\[if ((gte|lte) )?\(?IE " . $version . "[^\]]*\)?\]>(.*?)<!\[endif\]-->@s", "$3", $source);
  2832. }
  2833. /* detect lesser versions */
  2834. for ($i = $this->min_ie_version; $i < $version; $i++) {
  2835. if (strpos($source, 'IE ' . $i . ']>') !== false) {
  2836. $source = preg_replace("@<!--\[if gte? IE " . $i . "[^\]]*\]>(.*?)<!\[endif\]-->@s", "$1", $source);
  2837. }
  2838. }
  2839. /* detect greater versions */
  2840. for ($i = $version + 1; $i < $this->max_ie_version; $i++) {
  2841. if (strpos($source, 'IE ' . $i . ']>') !== false) {
  2842. $source = preg_replace("@<!--\[if lte? IE " . $i . "[^\]]*\]>(.*?)<!\[endif\]-->@s", "$1", $source);
  2843. }
  2844. }
  2845. }
  2846. }
  2847. } elseif (empty($this->options['uniform_cache']) && strpos($source, '<!--[if') !== false) {
  2848. $source = preg_replace("@<!--\[if.*?\[endif\]-->@s", "", $source);
  2849. }
  2850. return $source;
  2851. }
  2852. /**
  2853. * Converts sinlge path to the absolute one
  2854. *
  2855. **/
  2856. function convert_path_to_absolute ($file, $path, $leave_querystring = false) {
  2857. $endfile = '';
  2858. $root = $this->options['document_root'];
  2859. if (!empty($path['file'])) {
  2860. $endfile = $path['file'];
  2861. }
  2862. if (!$leave_querystring) {
  2863. $file = $this->strip_querystring($file);
  2864. $endfile = $this->strip_querystring($endfile);
  2865. }
  2866. /* Don't touch data URIs, or mhtml:, or external files */
  2867. if (preg_match("!^(https?|data|mhtml):!is", $file) && !preg_match("@//(www\.)?". $this->host_escaped ."@is", $file)) {
  2868. return false;
  2869. }
  2870. $absolute_path = $file;
  2871. /* external source file */
  2872. if (preg_match("!^https?://!", $endfile) && !preg_match("!^https?://!", $file)) {
  2873. if (substr($file, 0, 1) != '/') {
  2874. $absolute_path = preg_replace("@[^\/]+$@", "", $endfile) . $absolute_path;
  2875. } else {
  2876. $absolute_path = preg_replace("@(https?://[^\/]+).*@", "$1", $endfile) . $absolute_path;
  2877. }
  2878. } else {
  2879. /* Not absolute or external */
  2880. if (substr($file, 0, 1) != '/' && !preg_match("!^https?://!", $file)) {
  2881. /* add relative directory. Need somehow parse current meta base... */
  2882. if (substr($endfile, 0, 1) != "/" && !preg_match("!^https?://!", $endfile)) {
  2883. $endfile = preg_replace("@([^\?&]*/).*@", "$1", $_SERVER['REQUEST_URI']) . $endfile;
  2884. }
  2885. $full_path_to_image = preg_replace("@[^/\\\]+$@", "", $endfile);
  2886. $absolute_path = str_replace($root, "/", $this->view->unify_dir_separator($full_path_to_image . $file));
  2887. }
  2888. }
  2889. /* remove HTTP host from absolute URL */
  2890. return preg_replace("!https?://(www\.)?". $this->host_escaped ."/+!i", "/", $absolute_path);
  2891. }
  2892. /**
  2893. * Finds background images in the CSS and converts their paths to absolute
  2894. *
  2895. **/
  2896. function convert_paths_to_absolute ($content, $path, $leave_querystring = false) {
  2897. preg_match_all( "/url\s*\(\s*['\"]?(.*?)['\"]?\s*\)/is", $content, $matches);
  2898. if(count($matches[1]) > 0) {
  2899. foreach($matches[1] as $key => $file) {
  2900. $absolute_path = $this->convert_path_to_absolute($file, $path, $leave_querystring);
  2901. if (!empty($absolute_path)) {
  2902. /* add quotes if there is not plain URL */
  2903. if (strpos($absolute_path, ' ')) {
  2904. $absolute_path = "'" . $absolute_path . "'";
  2905. }
  2906. /* replace path in initial CSS */
  2907. $content = preg_replace("@url\s*\(\s*['\"]?" .
  2908. str_replace("?", "\?", $file) .
  2909. "['\"]?\s*\)@is", "url(" . $absolute_path . ")", $content);
  2910. }
  2911. }
  2912. }
  2913. return $content;
  2914. }
  2915. /**
  2916. * Convert all background image to CSS Sprites if possible
  2917. **/
  2918. function convert_css_sprites ($content, $options, $css_url) {
  2919. /* try to get and increase memory limit */
  2920. $memory_limit = round(str_replace("M", "000000", str_replace("K", "000", @ini_get('memory_limit'))));
  2921. /* 64M must enough for any operations with images. I hope... */
  2922. if ($memory_limit < 64000000) {
  2923. @ini_set('memory_limit', '64M');
  2924. }
  2925. @chdir($options['cachedir']);
  2926. $css_sprites = new css_sprites($content, array(
  2927. 'root_dir' => $options['installdir'],
  2928. 'current_dir' => $options['cachedir'],
  2929. 'html_cache' => $this->options['page']['cachedir'],
  2930. 'website_root' => $this->options['document_root'],
  2931. 'truecolor_in_jpeg' => $options['truecolor_in_jpeg'],
  2932. 'aggressive' => $options['aggressive'],
  2933. 'no_ie6' => $options['no_ie6'],
  2934. 'ignore' => $options['css_sprites_ignore'],
  2935. 'ignore_list' => $options['css_sprites_exclude'],
  2936. 'partly' => $options['css_sprites_partly'],
  2937. 'extra_space' => $options['css_sprites_extra_space'],
  2938. 'expires_rewrite' => $options['css_sprites_expires_rewrite'],
  2939. 'cache_images' => $this->options['page']['cache_images'],
  2940. 'cache_images_rewrite' => $this->options['page']['far_future_expires_rewrite'],
  2941. 'data_uris' => $options['data_uris'],
  2942. 'data_uris_separate' => $options['data_uris_separate'],
  2943. 'data_uris_size' => $options['data_uris_size'],
  2944. 'data_uris_ignore_list' => $options['data_uris_exclude'],
  2945. 'mhtml' => $options['mhtml'],
  2946. 'mhtml_size' => $options['mhtml_size'],
  2947. 'mhtml_ignore_list' => $options['mhtml_exclude'],
  2948. 'css_url' => $css_url,
  2949. 'dimensions_limited' => $options['dimensions_limited'],
  2950. 'no_css_sprites' => !$options['css_sprites'],
  2951. 'multiple_hosts' => empty($options['parallel']) ? array() : explode(" ", $options['parallel_hosts']),
  2952. 'user_agent' => $this->ua_mod,
  2953. 'punypng' => $options['punypng'],
  2954. 'restore_properties' => $options['css_restore_properties'],
  2955. 'ftp_access' => $this->options['page']['parallel_ftp'],
  2956. 'http_host' => $this->options['page']['host'],
  2957. 'https_host' => $this->options['page']['parallel_https'],
  2958. 'uniform_cache' => $this->options['uniform_cache']
  2959. ));
  2960. return $css_sprites->process();
  2961. }
  2962. /**
  2963. * Convert all background image to data:URI / mhtml
  2964. **/
  2965. function convert_data_uri ($content, $options, $css_url) {
  2966. @chdir($options['cachedir']);
  2967. $compressed = '';
  2968. preg_match_all("!([^\{\}]+){[^\}]*(background[^:]*):([^;]+);[^\}]*}!is", $content, $imgs, PREG_SET_ORDER);
  2969. if (is_array($imgs)) {
  2970. $replaced = array();
  2971. $mhtml = in_array($this->ua_mod, array('.ie6', '.ie7'));
  2972. $location = 0;
  2973. $data_uri_exclude = explode(" ", $options['data_uris_exclude']);
  2974. $mhtml_exclude = explode(" ", $options['data_uris_exclude_html']);
  2975. foreach ($imgs as $image) {
  2976. $base64 = '';
  2977. if (strpos(strtolower($image[3]), "url") !== false) {
  2978. $css_image = trim(str_replace(array('"', "'"), '', preg_replace("@.*url\(([^\)]+)\).*@is", "$1", $image[3])));
  2979. $image_saved = $css_image;
  2980. $css_image = $css_image{0} == '/' ? $this->options['document_root'] . $css_image : $options['cachedir'] . '/' .$css_image;
  2981. $chunks = explode(".", $css_image);
  2982. $extension = str_replace('jpg', 'jpeg', strtolower(array_pop($chunks)));
  2983. $chunks = explode("/", $css_image);
  2984. $filename = array_pop($chunks);
  2985. if (empty($replaced[$image_saved])) {
  2986. if (!@is_file($css_image) ||
  2987. in_array($extension, array('htc', 'cur', 'eot', 'ttf', 'svg', 'otf', 'woff')) ||
  2988. strpos($css_image, "://") ||
  2989. strpos($css_image, "mhtml:") !== false ||
  2990. strpos($css_image, "data:") !== false) {
  2991. $css_image = $image_saved;
  2992. } else {
  2993. $encoded = base64_encode(@file_get_contents($css_image));
  2994. if ($mhtml) {
  2995. if (@filesize($css_image) < $options['data_uris_mhtml_size'] &&
  2996. !in_array($filename, $mhtml_exclude) &&
  2997. !empty($encoded)) {
  2998. $compressed .= "\n\n--_\nContent-Location:" .
  2999. $location .
  3000. "\nContent-Transfer-Encoding:base64\n\n" .
  3001. $encoded;
  3002. $css_image = 'mhtml:' . $css_url . '!' . $location;
  3003. $location++;
  3004. } else {
  3005. $css_image = $image_saved;
  3006. }
  3007. } else {
  3008. if (@filesize($css_image) < $options['data_uris_size'] &&
  3009. !in_array($filename, $data_uri_exclude) &&
  3010. !empty($encoded)) {
  3011. $css_image = '';
  3012. $base64 = 'data:image/' .
  3013. $extension .
  3014. ';base64,' .
  3015. $encoded;
  3016. } else {
  3017. $css_image = $image_saved;
  3018. }
  3019. }
  3020. }
  3021. $replaced[$image_saved] = $css_image;
  3022. $content = str_replace($image_saved, $css_image, $content);
  3023. }
  3024. if (!$mhtml && $base64) {
  3025. $compressed .= $image[1] .
  3026. '{' .
  3027. $image[2] .
  3028. ':' .
  3029. str_replace($image_saved, $base64, $image[3]) .
  3030. '}';
  3031. }
  3032. if ($this->options['uniform_cache']) {
  3033. $sel = 'html ' .
  3034. $image[1] .
  3035. '{' .
  3036. $image[2] .
  3037. ':' .
  3038. $image[3] .
  3039. '}';
  3040. $content .= '* ' . $sel . '*+' . $sel;
  3041. }
  3042. } else {
  3043. $compressed .= $image[1] .
  3044. '{' .
  3045. $image[2] .
  3046. ':' .
  3047. $image[3] .
  3048. '}';
  3049. }
  3050. }
  3051. }
  3052. return array($content, $compressed);
  3053. }
  3054. /**
  3055. * Converts REQUEST_URI to cached file name
  3056. *
  3057. **/
  3058. function convert_request_uri ($uri = false) {
  3059. $uri = $uri ? $uri : preg_replace("@index\.php$@", "", $_SERVER['REQUEST_URI']);
  3060. /* remove excessive GET params */
  3061. if (trim($this->options['page']['cache_params'])) {
  3062. $params = str_replace(" ", "|", $this->options['page']['cache_params']);
  3063. $uri = preg_replace("@(" . $params . ")=[^&\?]+[\?&]?@", "", $uri);
  3064. }
  3065. /* replace /, ?, & with - */
  3066. $uri = str_replace(
  3067. array('/?', '/', '?', '&'),
  3068. array('+', '+', '+', '+'),
  3069. $uri);
  3070. return $uri;
  3071. }
  3072. /**
  3073. * Downloads remote files to include
  3074. *
  3075. **/
  3076. function get_remote_file ($file, $tag = "link") {
  3077. /* check if we already have this file */
  3078. if (preg_match("/\/wo[abcdef0-9]+$/", $file)) {
  3079. return preg_replace("/.*(wo[abcdef0-9]+$)/", "$1", $file);
  3080. }
  3081. $current_directory = @getcwd();
  3082. /* dirty fix for buggy getcwd call */
  3083. if ($current_directory === '/') {
  3084. $current_directory = $this->options['css']['installdir'];
  3085. }
  3086. if (function_exists('curl_init')) {
  3087. if ($tag == 'link') {
  3088. @chdir($this->options['css']['cachedir']);
  3089. } else {
  3090. @chdir($this->options['javascript']['cachedir']);
  3091. }
  3092. $ua = empty($_SERVER['HTTP_USER_AGENT']) ||
  3093. !empty($this->options['uniform_cache']) ?
  3094. "Mozilla/5.0 (WEBO Site SpeedUp; http://www.webogroup.com/) Firefox 3.6" :
  3095. $_SERVER['HTTP_USER_AGENT'];
  3096. $return_filename = 'wo' . md5($file . $ua) . '.' . ($tag == 'link' ? 'css' : 'js');
  3097. if (@file_exists($return_filename)) {
  3098. $timestamp = @filemtime($return_filename);
  3099. } else {
  3100. $timestamp = 0;
  3101. }
  3102. /* prevent download more than 1 time a day */
  3103. if (!empty($timestamp) && $timestamp + 86400 > $this->time) {
  3104. @chdir($current_directory);
  3105. return $return_filename;
  3106. }
  3107. /* try to download remote file */
  3108. $ch = @curl_init($file);
  3109. $fp = @fopen($return_filename, "w");
  3110. $fph = @fopen($return_filename . '.headers', "w");
  3111. if ($fp && $ch) {
  3112. @curl_setopt($ch, CURLOPT_FILE, $fp);
  3113. @curl_setopt($ch, CURLOPT_HEADER, 0);
  3114. @curl_setopt($ch, CURLOPT_USERAGENT, $ua);
  3115. @curl_setopt($ch, CURLOPT_ENCODING, "");
  3116. @curl_setopt($ch, CURLOPT_REFERER, $host);
  3117. @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  3118. @curl_setopt($ch, CURLOPT_WRITEHEADER, $fph);
  3119. if (!empty($this->options['page']['htaccess_username']) && !empty($this->options['page']['htaccess_password'])) {
  3120. @curl_setopt($ch, CURLOPT_USERPWD, $this->options['page']['htaccess_username'] . ':' . $this->options['page']['htaccess_password']);
  3121. }
  3122. @curl_exec($ch);
  3123. @curl_close($ch);
  3124. @fclose($fp);
  3125. @fclose($fph);
  3126. /* check if we deal with 404 error, remove result */
  3127. $headers = @file_get_contents($return_filename . '.headers');
  3128. if (strpos($headers, 'HTTP/1.1 404') !== false ||
  3129. strpos($headers, 'HTTP/1.0 404') !== false ||
  3130. strpos($headers, 'HTTP/0.1 404') !== false ||
  3131. strpos($headers, 'HTTP/0.9 404') !== false) {
  3132. @unlink($return_filename);
  3133. } else {
  3134. /* try to replace background images to local ones */
  3135. $contents = @file_get_contents($return_filename);
  3136. if (!empty($contents) && $tag == 'link') {
  3137. /* correct background-images in CSS file */
  3138. $this->write_file($return_filename, $this->convert_paths_to_absolute($contents, array('file' => $file)));
  3139. }
  3140. }
  3141. @unlink($return_filename . '.headers');
  3142. chdir($current_directory);
  3143. return $return_filename;
  3144. }
  3145. }
  3146. @chdir($current_directory);
  3147. return false;
  3148. }
  3149. /**
  3150. * Sets User Agent modificator
  3151. *
  3152. **/
  3153. function set_user_agent () {
  3154. $this->ua_mod = '';
  3155. if (!$this->options['performance']['uniform_cache']) {
  3156. /* min. supported IE version */
  3157. $this->min_ie_version = 5;
  3158. /* max. supported IE version */
  3159. $this->max_ie_version = 11;
  3160. if (strpos($this->ua, 'MSIE') && !strpos($this->ua, 'Opera')) {
  3161. for ($version = $this->min_ie_version; $version < $this->max_ie_version; $version++) {
  3162. if (strpos($this->ua, 'MSIE ' . $version)) {
  3163. $this->ua_mod = '.ie' . $version;
  3164. }
  3165. }
  3166. }
  3167. /* check for mobile agents */
  3168. if (empty($this->ua_mod)) {
  3169. $mobiles = array(
  3170. 'Android',
  3171. 'BlackBerry',
  3172. 'HTC',
  3173. 'iPhone',
  3174. 'iPod',
  3175. 'LG',
  3176. 'MOT',
  3177. 'Mobile',
  3178. 'NetFront',
  3179. 'Nokia',
  3180. 'Opera Mini',
  3181. 'Palm',
  3182. 'PPC',
  3183. 'SAMSUNG',
  3184. 'Smartphone',
  3185. 'SonyEricsson',
  3186. 'Symbian',
  3187. 'UP.Browser',
  3188. 'webOS');
  3189. $j = 0;
  3190. /* strpos here is 2.5x faster than stristr and 6x faster than regexp */
  3191. while (strpos($this->ua, $mobiles[$j++]) === false &&
  3192. !empty($mobiles[$j])) {}
  3193. if ($j != count($mobiles)) {
  3194. $this->ua_mod = '.ma';
  3195. }
  3196. }
  3197. }
  3198. }
  3199. /**
  3200. * Replaces html entities with amps
  3201. *
  3202. */
  3203. function resolve_amps ($str) {
  3204. return str_replace(array('&amp;', '&#38;', ''), array('&', '&', ''), $str);
  3205. }
  3206. /**
  3207. * Determines cache engine and create instance of it
  3208. *
  3209. **/
  3210. function start_cache_engine () {
  3211. $cache_engines = array('0' => 'files',
  3212. '1' => 'memcached',
  3213. '2' => 'apc',
  3214. '3' => 'xcache'
  3215. );
  3216. $cache_engines_options = array('0' => array('cache_dir' => $this->options['page']['cachedir']),
  3217. '1' => array('server' => @$this->options['page']['cache_engine_options']),
  3218. '2' => '',
  3219. '3' => ''
  3220. );
  3221. if (!empty($cache_engines[$this->options['page']['cache_engine']]))
  3222. {
  3223. $engine_name = 'webo_cache_' . $cache_engines[$this->options['page']['cache_engine']];
  3224. $engine_num = $this->options['page']['cache_engine'];
  3225. }
  3226. else
  3227. {
  3228. $engine_name = 'webo_cache_' . $cache_engines[0];
  3229. $engine_num = 0;
  3230. }
  3231. include_once($this->options['page']['installdir'] . 'libs/php/cache_engine.php');
  3232. $this->cache_engine = new $engine_name ($cache_engines_options[$engine_num]);
  3233. }
  3234. /**
  3235. * Deletes cached HTML files determined by patterns. Just an interface for cache_engine delete_entries method.
  3236. *
  3237. **/
  3238. function clear_html_cache ($patterns) {
  3239. if (!empty($patterns))
  3240. {
  3241. $this->cache_engine->delete_entries($patterns);
  3242. }
  3243. }
  3244. /**
  3245. * Convert given URL to another with base URI (<base> tag / header)
  3246. *
  3247. **/
  3248. function convert_basehref ($uri) {
  3249. /* check if BASE URI is given */
  3250. $slash = substr($uri, 0, 1) != '/';
  3251. if (!empty($this->basehref) &&
  3252. /* convert only non-external URI */
  3253. strpos($uri, '//') !== 0 && !strpos($uri, '://')) {
  3254. /* convert absolute URL, change host */
  3255. if ($slash) {
  3256. return preg_replace("@^https?://[^/]+/@", '', $this->basehref) . $uri;
  3257. /* convert relative URL, change host + folder */
  3258. } else {
  3259. return preg_replace("@[^/]*$@", '', $this->basehref) . $uri;
  3260. }
  3261. } else {
  3262. return $uri;
  3263. }
  3264. }
  3265. } // end class
  3266. ?>