PageRenderTime 83ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/controller/compressor.php

http://web-optimizator.googlecode.com/
PHP | 4401 lines | 3986 code | 31 blank | 384 comment | 620 complexity | 93e1035a585d931b99fe8efa4fe67a25 MD5 | raw file
  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. $currency = empty($_COOKIE['WSS_CURRENCY']) ? $options['options']['currency'] : $_COOKIE['WSS_CURRENCY'];
  17. $rurl = empty($options['uri']) ? empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'] : $options['uri'];
  18. $homepage = empty($options['options']['html_cache']['ignore_list']) && empty($options['options']['restricted']) ? '' :
  19. in_array($rurl, array('/', '/index.php', '/index.html', '/#' . $currency));
  20. /* skip processing if disabled or restricted */
  21. if (!empty($_GET['web_optimizer_disabled']) || (!empty($options['options']['restricted']) &&
  22. (preg_match("@" . preg_replace("/ /", "|", preg_replace("/([\?!\^\$\|\(\)\[\]\{\}])/", "\\\\$1", $options['options']['restricted'])) . "@", $rurl))) ||
  23. (strpos($options['options']['restricted'], '#') !== false && $homepage)) {
  24. $this->options['active'] = 0;
  25. return;
  26. }
  27. /* A/B testing, */
  28. if (!empty($options['options']['footer']['ab'])) {
  29. if (empty($_COOKIE['WSS_DISABLED']) && empty($_COOKIE['WSS_ENABLED'])) {
  30. $ab = (microtime()*100)%100 < round($options['options']['footer']['ab']);
  31. setcookie($ab ? "WSS_ENABLED" : "WSS_DISABLED", 1, time() + 60*60, '/', $_SERVER['HTTP_HOST'], false, true);
  32. if (!$ab) {
  33. $_COOKIE['WSS_DISABLED'] = 1;
  34. }
  35. }
  36. }
  37. /* initialize chained optimization */
  38. $this->web_optimizer_stage = round(empty($_GET['web_optimizer_stage']) ? 0 : $_GET['web_optimizer_stage']);
  39. $this->debug_mode = empty($_GET['web_optimizer_debug']) && empty($_COOKIE['web_optimizer_debug']) ? 0 : 1;
  40. /* allow merging of other classes with this one */
  41. foreach ($options as $key => $value) {
  42. $this->$key = $value;
  43. }
  44. /* get chained optimization params */
  45. if (!empty($this->web_optimizer_stage)) {
  46. $this->username = htmlspecialchars(empty($_GET['username']) ? '' :
  47. $_GET['username']);
  48. $this->password = htmlspecialchars(empty($_GET['password']) ? '' :
  49. $_GET['password']);
  50. $this->auto_rewrite = round(empty($_GET['auto_rewrite']) ? '' :
  51. $_GET['auto_rewrite']);
  52. $this->chained_redirect = 'http://' .
  53. $this->options['host'] .
  54. str_replace($this->options['document_root'], '/', $this->options['html_cachedir']) .
  55. 'optimizing.php';
  56. $this->cache_version = round(empty($_GET['cache_version']) ? '' :
  57. $_GET['cache_version']);
  58. /* get major stage number, all stages:
  59. -1 - system, envelope all <script> to try-catch-document.write
  60. 0-9 - inilialization, starts in administrative interface
  61. 10-13 - JS file generation, 1st major stage (common browsers)
  62. 14-19 - CSS Sprites / data:URI generation, 1st major stage
  63. 20-24 - CSS file generation + page parsing, 1st major stage
  64. 25-28 - JS file generation, 2nd major stage (IE 6.0)
  65. 29-34 - CSS Sprites / mhtml generation, 2nd major stage
  66. 35-39 - CSS file generation + page parsing, 2nd major stage
  67. 40-43 - JS file generation, 3rd major stage (IE 7.0)
  68. 44-49 - CSS Sprites / mhtml generation, 2nd major stage
  69. 50-54 - CSS file generation + page parsing, 2nd major stage
  70. 55-58 - JS file generation, 4th major stage (IE 8.0)
  71. 59-64 - CSS Sprites / data:URI generation, 4th major stage
  72. 65-69 - CSS file generation + page parsing, 4th major stage
  73. 70-73 - JS file generation, 5th major stage (IE 7.0 @ Vista)
  74. 74-79 - CSS Sprites generation, 5th major stage
  75. 80-84 - CSS file generation + page parsing, 5th major stage
  76. */
  77. $this->cache_stage = floor(($this->web_optimizer_stage - 10) / 15);
  78. }
  79. $this->options['active'] = $this->debug_mode ? 1 : $this->options['active'];
  80. /* disable any actions if not active */
  81. if (empty($this->options['active'])) {
  82. return;
  83. }
  84. /* define head of the webpage for scripts / styles */
  85. $this->head = '';
  86. /* remember current time */
  87. $this->time = empty($_SERVER['REQUEST_TIME']) ? time() : $_SERVER['REQUEST_TIME'];
  88. /* skip buffering (need for integration as plugin) */
  89. $this->buffered = $this->options['buffered'];
  90. /* Sets User Agent to differ IE from non-IE */
  91. $this->ua = empty($_SERVER['HTTP_USER_AGENT']) ? '' : $_SERVER['HTTP_USER_AGENT'];
  92. /* HTTPS or not ? */
  93. $this->https = empty($_SERVER['HTTPS']) ? '' : 's';
  94. /* Set list of outdated IE */
  95. $this->ies = array('.ie4', '.ie5', '.ie6', '.ie7');
  96. /* Set options */
  97. $this->set_options();
  98. /* Include base plugin class */
  99. if (is_array($this->options['plugins'])) {
  100. include_once($this->options['css']['installdir'] . 'libs/php/class.plugin.php');
  101. }
  102. /* Remember current page encoding */
  103. $this->encoding = '';
  104. /* Define the gzip headers */
  105. $this->set_gzip_headers();
  106. /* Deal with flushed content or not? */
  107. $this->flushed = false;
  108. $excluded_html_pages = '';
  109. $included_user_agents = '';
  110. $restricted_cookie = 0;
  111. if (!empty($this->options['page']['cache'])) {
  112. $this->start_cache_engine();
  113. if (!empty($this->clear_cache_key)) {
  114. $this->clear_html_cache($this->clear_cache_key);
  115. }
  116. /* HTML cache ? */
  117. if (!empty($this->options['page']['cache_ignore'])) {
  118. $excluded_html_pages = preg_replace("/ /", "|", preg_replace("/([\?!\^\$\|\(\)\[\]\{\}])/", "\\\\$1", str_replace(array('# ', '#'), '', $this->options['page']['cache_ignore'])));
  119. }
  120. if (!empty($this->options['page']['allowed_user_agents'])) {
  121. $included_user_agents = preg_replace("/ /", "|", preg_replace("/([\?!\^\$\|\(\)\[\]\{\}])/", "\\\\$1", $this->options['page']['allowed_user_agents']));
  122. }
  123. if (!empty($this->options['page']['exclude_cookies'])) {
  124. $cookies = explode(" ", $this->options['page']['exclude_cookies']);
  125. foreach ($cookies as $cookie) {
  126. if ($e = strpos($cookie, '=')) {
  127. $c = substr($cookie, 0, $e);
  128. $e = substr($cookie, $e+1);
  129. if (isset($_COOKIE[$c]) && $_COOKIE[$c] == $e) {
  130. $restricted_cookie = 1;
  131. }
  132. } else {
  133. if (isset($_COOKIE[$cookie])) {
  134. $restricted_cookie = 1;
  135. }
  136. }
  137. }
  138. }
  139. }
  140. /* cache if
  141. - option is enabled,
  142. - don't parse excluded pages (including home page),
  143. - or parse included USER AGENTS,
  144. - don't parse pages with excluded coockies,
  145. - flush or gzip for HTML are disabled,
  146. - headers have not been sent,
  147. - page is requested by GET,
  148. - no chained optimization,
  149. - no debug mode,
  150. - external cache restriction,
  151. - exclude domains except the activated one for non-corporate licenses,
  152. - disable cache in case of negative A/B test.
  153. */
  154. $this->cache_me = !empty($this->options['page']['cache']) &&
  155. ((empty($this->options['page']['cache_ignore']) && !$this->options['page']['ignore_include']) ||
  156. (!$this->options['page']['ignore_include'] &&
  157. (!$excluded_html_pages || !preg_match("!" . $excluded_html_pages . "!is", $this->uri)) &&
  158. (strpos($this->options['page']['cache_ignore'], '#') === false || !$homepage)) ||
  159. ($this->options['page']['ignore_include'] &&
  160. (($excluded_html_pages && preg_match("!" . $excluded_html_pages . "!is", $this->uri)) ||
  161. (strpos($this->options['page']['cache_ignore'], '#') !== false && $homepage))) ||
  162. !$this->ua ||
  163. ($included_user_agents && preg_match("!" . $included_user_agents . "!is", $this->ua))) &&
  164. !$restricted_cookie &&
  165. (empty($this->options['page']['gzip']) ||
  166. empty($this->options['page']['flush'])) &&
  167. !headers_sent() &&
  168. (getenv('REQUEST_METHOD') == 'GET') &&
  169. empty($this->web_optimizer_stage) &&
  170. !$this->debug_mode &&
  171. empty($this->no_cache) &&
  172. ($this->premium == 3 || strpos($this->options['host'], $this->host) !== false) &&
  173. (empty($this->options['page']['ab']) || empty($_COOKIE['WSS_DISABLED']));
  174. /* check if we can get out cached page */
  175. if (!empty($this->cache_me)) {
  176. $this->uri = $this->convert_request_uri(empty($this->uri) ? '' : $this->uri);
  177. $jutility = class_exists('JUtility', false);
  178. $jsession = class_exists('JSession', false);
  179. /* gzip cached content before output? (plugins have onCache), JUtility must parse content */
  180. $gzip_me = is_array($this->options['plugins']) || $jutility || $jsession;
  181. $cache_plain_key = $this->view->ensure_trailing_slash($this->uri) .
  182. 'index' .
  183. $this->ua_mod .
  184. '.html' .
  185. ($this->options['page']['https_separate'] ? $this->https : '');
  186. $cache_key = $cache_plain_key .
  187. ($this->options['page']['flush'] ||
  188. empty($this->encoding_ext) ||
  189. $gzip_me ? '' : $this->encoding_ext);
  190. $timestamp_ajax = 0;
  191. $cache_key_ajax = $cache_plain_key . '.ajax';
  192. if (defined('WSS_CACHE_MISS')) {
  193. $timestamp = 0;
  194. } else {
  195. $timestamp = $this->cache_engine->get_mtime($cache_key);
  196. if ($this->options['page']['ajax_timeout']) {
  197. $timestamp_ajax = $this->cache_engine->get_mtime($cache_key_ajax);
  198. }
  199. }
  200. /* try to get from cache non-gzipped page if gzipped one doesn't exist */
  201. if (!$timestamp && !$this->options['page']['flush'] && !empty($this->encoding_ext) && !$gzip_me) {
  202. $timestamp = $this->cache_engine->get_mtime($cache_plain_key);
  203. $gzip_me = 1;
  204. }
  205. if (!$timestamp && !$timestamp_ajax && !defined('WSS_CACHE_MISS')) {
  206. define('WSS_CACHE_MISS', 1);
  207. }
  208. if (($timestamp &&
  209. $this->time - $timestamp < $this->options['page']['cache_timeout'] &&
  210. ($content = $this->cache_engine->get_entry($gzip_me ? $cache_plain_key : $cache_key))) ||
  211. ($timestamp_ajax &&
  212. $this->time - $timestamp_ajax < $this->options['page']['ajax_timeout'] &&
  213. ($content = $this->cache_engine->get_entry($cache_key_ajax)))) {
  214. if ($jutility) {
  215. $token = JUtility::getToken();
  216. }
  217. elseif ($jsession) {
  218. $token = JSession::getFormToken();
  219. }
  220. if (!empty($token)) {
  221. $content = str_replace('##WSS_JTOKEN_WSS##', $token, $content);
  222. }
  223. /* execute plugin-specific logic */
  224. if (is_array($this->options['plugins'])) {
  225. foreach ($this->options['plugins'] as $plugin) {
  226. $plugin_file = $this->options['css']['installdir'] . 'plugins/' . $plugin . '.php';
  227. if (@is_file($plugin_file)) {
  228. include_once($plugin_file);
  229. $web_optimizer_plugin = new $plugin;
  230. $content = $web_optimizer_plugin->onAfterOptimization($content);
  231. }
  232. }
  233. }
  234. $hash = crc32($content) . (empty($this->encoding) ? '' : '-' . str_replace("x-", "", $this->encoding));
  235. /* check for return visits */
  236. if ((isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
  237. stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) == '"' . $hash . '"') ||
  238. (isset($_SERVER['HTTP_IF_MATCH']) &&
  239. stripslashes($_SERVER['HTTP_IF_MATCH']) == '"' . $hash . '"')) {
  240. /* return visit and no modifications, so do not send anything */
  241. @header ("HTTP/1.0 304 Not Modified");
  242. @header ("Content-Length: 0");
  243. $this->di();
  244. }
  245. /* define gzip headers */
  246. $this->set_gzip_header();
  247. if ($gzip_me && $this->encoding) {
  248. $cnt = $this->create_gz_compress($content, in_array($this->encoding, array('gzip', 'x-gzip')));
  249. if (!empty($cnt)) {
  250. $content = $cnt;
  251. /* skip gzip if we can't compress content */
  252. } else {
  253. $this->options['page']['gzip'] = 0;
  254. $this->encoding = '';
  255. }
  256. }
  257. /* set ETag, thx to merzmarkus */
  258. @header("ETag: \"" . $hash . "\"");
  259. if ($gzip_me && ($this->encoding || empty($this->gzip_set)) && (empty($_SERVER['SERVER_PROTOCOL']) || $_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.0')) {
  260. @header("Content-Length: " . strlen($content));
  261. }
  262. /* set content-type */
  263. if (!empty($this->options['charset'])) {
  264. @header("Content-Type: text/html; charset=" . $this->options['charset']);
  265. }
  266. if (empty($this->web_optimizer_stage) &&
  267. $this->options['page']['clientside_cache']) {
  268. /* not really GMT but is valid locally */
  269. $ExpStr = date("D, d M Y H:i:s",
  270. $this->time + $this->options['page']['clientside_timeout']) . " GMT";
  271. @header("Cache-Control: " .
  272. ($this->options['page']['gzip'] ? 'private' : 'public') .
  273. ", max-age=" .
  274. $this->options['page']['clientside_timeout']);
  275. @header("Expires: " . $ExpStr);
  276. }
  277. while (@ob_end_clean());
  278. @header('WEBO: cache hit');
  279. echo $content;
  280. /* content is a head part, flush it after */
  281. if ($this->options['page']['flush']) {
  282. flush();
  283. $this->flushed = true;
  284. } else {
  285. $this->di(1);
  286. }
  287. } else {
  288. @header('WEBO: cache miss');
  289. }
  290. } elseif (!empty($this->options['page']['cache'])) {
  291. @header('WEBO: cache miss');
  292. }
  293. /* remember Joomla! caching (VirtueMart) */
  294. $this->joomla_cache = $this->options['page']['cache'] && (class_exists('JUtility', false) || class_exists('JSession', false));
  295. /* remember WordPress caching (WP Digi Cart) */
  296. $this->wp_cache = defined('WP_CACHE') && @is_dir($this->options['document_root'] . 'wp-content/plugins/wp-cart-for-digital-products/');
  297. /* remember Generic caching for other carts */
  298. $this->generic_cache = !$this->joomla_cache && !$this->wp_cache && $this->options['page']['cache'];
  299. /* change some hosts if HTTPS is used */
  300. if ($this->https && !empty($this->options['page']['parallel_https'])) {
  301. $this->options['javascript']['host'] =
  302. $this->options['css']['host'] =
  303. $this->options['page']['parallel_hosts'] =
  304. $this->options['page']['parallel_https'];
  305. }
  306. /* number of external files calls to process */
  307. $this->initial_files = array();
  308. /* set internal encoding */
  309. $this->charset = empty($wss_encoding) ? 'utf8' : $wss_encoding;
  310. /* prepare escaped host */
  311. $this->host_escaped = str_replace('.', '\.', $this->host);
  312. /* activate application */
  313. $this->options['active'] = 1;
  314. if ($this->buffered) {
  315. /* Start things off */
  316. $this->start();
  317. }
  318. }
  319. /**
  320. * Write installation progress to JavaScript file
  321. *
  322. **/
  323. function write_progress ($progress) {
  324. $this->write_file($this->options['javascript']['cachedir'] . 'progress.php', $progress);
  325. }
  326. /**
  327. * Options are read from config.webo.php
  328. **/
  329. function set_options () {
  330. /* Set paths with new options */
  331. $this->options['document_root'] = empty($this->options['document_root']) ? '' : $this->options['document_root'];
  332. $this->view->set_paths($this->options['document_root']);
  333. /* Set local root if chained optimization */
  334. if (!empty($this->web_optimizer_stage)) {
  335. $this->view->paths['full']['current_directory'] = $this->view->paths['full']['document_root'];
  336. $this->view->paths['relative']['current_directory'] = $this->view->paths['relative']['document_root'];
  337. $_SERVER['REQUEST_URI'] = '/';
  338. /* force User Agent on chained optimization */
  339. $mods = array(
  340. /* all common browsers except IE */
  341. '',
  342. /* IE 6.0, when will it die? */
  343. '.ie6',
  344. /* IE 7.0 */
  345. '.ie7',
  346. /* IE 8.0 */
  347. '.ie8',
  348. /* Mobile Agents */
  349. '.ma',
  350. /* dummy UA, thx to peterbowey */
  351. '.end'
  352. );
  353. $this->ua_mod = $mods[$this->cache_stage];
  354. }
  355. $this->premium = $this->view->validate_license($this->options['license'], $this->options['html_cachedir'], $this->options['host']);
  356. $this->set_user_agent();
  357. $webo_cachedir = $this->view->unify_dir_separator(realpath(dirname(__FILE__) . '/../') . '/');
  358. /* ensure trailing slashes */
  359. $this->options['html_cachedir'] = $this->view->ensure_trailing_slash($this->options['html_cachedir']);
  360. $this->options['css_cachedir'] = $this->view->ensure_trailing_slash($this->options['css_cachedir']);
  361. $this->options['javascript_cachedir'] = $this->view->ensure_trailing_slash($this->options['javascript_cachedir']);
  362. /* normalize host */
  363. if (!empty($this->options['host'])) {
  364. $this->options['host'] = preg_replace("!^https?://!", "", $this->options['host']);
  365. }
  366. /* Read in options */
  367. $full_options = array(
  368. "javascript" => array(
  369. "cachedir" => $this->options['javascript_cachedir'],
  370. "cachedir_relative" => str_replace($this->options['document_root'], "/", $this->options['javascript_cachedir']),
  371. "installdir" => $webo_cachedir,
  372. "gzip" => $this->options['gzip']['javascript'] &&
  373. ((!$this->options['htaccess']['mod_gzip'] &&
  374. !$this->options['htaccess']['mod_deflate'] &&
  375. (!$this->options['htaccess']['mod_rewrite'] ||
  376. !$this->options['htaccess']['mod_mime'] ||
  377. !$this->options['htaccess']['mod_expires'])) ||
  378. !$this->options['htaccess']['enabled']),
  379. "gzip_level" => round($this->options['gzip']['javascript_level']),
  380. "minify" => $this->options['minify']['javascript'],
  381. "minify_body" => $this->options['minify']['javascript_body'],
  382. "minify_with" => $this->options['minify']['with_jsmin'] ?
  383. 'jsmin' : ($this->options['minify']['with_yui'] && $this->premium > 1 ?
  384. 'yui' : ($this->options['minify']['with_packer'] && $this->premium ?
  385. 'packer' : ($this->options['minify']['with_google'] && $this->premium > 1 ?
  386. 'google' : ''))),
  387. "minify_try" => $this->options['external_scripts']['include_try'] &&
  388. $this->premium,
  389. "minify_exclude" => $this->options['external_scripts']['minify_exclude'],
  390. "remove_duplicates" => $this->options['external_scripts']['duplicates'] &&
  391. $this->premium,
  392. "far_future_expires" => $this->options['far_future_expires']['javascript'] &&
  393. !$this->options['htaccess']['mod_expires'],
  394. "far_future_expires_php" => $this->options['far_future_expires']['javascript'],
  395. "far_future_expires_rewrite" => $this->options['htaccess']['mod_rewrite'] &&
  396. $this->options['htaccess']['enabled'] &&
  397. $this->options['far_future_expires']['javascript'],
  398. "far_future_expires_static" => ((!($this->options['htaccess']['mod_rewrite'] ||
  399. $this->options['htaccess']['mod_expires']) ||
  400. !$this->options['htaccess']['enabled']) &&
  401. $this->options['far_future_expires']['javascript']) ||
  402. ((!($this->options['htaccess']['mod_rewrite'] ||
  403. $this->options['htaccess']['mod_deflate'] ||
  404. $this->options['htaccess']['mod_gzip']) ||
  405. !$this->options['htaccess']['enabled']) &&
  406. $this->options['gzip']['javascript']),
  407. "unobtrusive_body" => $this->premium && $this->options['unobtrusive']['body'] &&
  408. !$this->options['unobtrusive']['all'],
  409. "external_scripts" => $this->options['external_scripts']['on'],
  410. "inline_scripts" => $this->options['external_scripts']['inline'] &&
  411. ($this->options['minify']['javascript'] || $this->options['rocket']['reorder']),
  412. "inline_scripts_body" => $this->options['external_scripts']['inline_body'] &&
  413. ($this->options['minify']['javascript'] || $this->options['rocket']['reorder']),
  414. "external_scripts_head_end" => $this->options['external_scripts']['head_end'],
  415. "external_scripts_exclude" => $this->options['external_scripts']['ignore_list'],
  416. "external_scripts_mask" => $this->premium > 1 ? $this->options['external_scripts']['include_mask'] : '',
  417. "remove_list" => $this->premium > 1 ? $this->options['external_scripts']['remove_list'] : '',
  418. "dont_check_file_mtime" => $this->options['performance']['mtime'],
  419. "file" => $this->premium > 1 ? $this->options['minify']['javascript_file'] : '',
  420. "host" => $this->premium ? $this->options['minify']['javascript_host'] : '',
  421. "https" => $this->premium > 1 ? $this->options['parallel']['https'] : '',
  422. "rocket" => $this->options['rocket']['javascript'] && !$this->options['minify']['javascript'],
  423. "rocket_external" => $this->options['rocket']['javascript_external'] && !$this->options['minify']['javascript'],
  424. "reorder" => $this->options['rocket']['reorder']
  425. ),
  426. "css" => array(
  427. "cachedir" => $this->options['css_cachedir'],
  428. "cachedir_relative" => str_replace($this->options['document_root'], "/", $this->options['css_cachedir']),
  429. "installdir" => $webo_cachedir,
  430. "gzip" => $this->options['gzip']['css'] &&
  431. ((!$this->options['htaccess']['mod_gzip'] &&
  432. !$this->options['htaccess']['mod_deflate'] &&
  433. (!$this->options['htaccess']['mod_rewrite'] ||
  434. !$this->options['htaccess']['mod_mime'] ||
  435. !$this->options['htaccess']['mod_expires'])) ||
  436. !$this->options['htaccess']['enabled']),
  437. "gzip_level" => round($this->options['gzip']['css_level']),
  438. "minify" => $this->options['minify']['css'],
  439. "minify_body" => $this->options['minify']['css_body'],
  440. "minify_with" => $this->premium > 1 && $this->options['minify']['css_min'] == 2 ?
  441. 'tidy' :
  442. ($this->premium > 1 && $this->options['minify']['css_min'] == 3 ? 'cssmin' :
  443. ($this->options['minify']['css_min'] ? 'basic' : '')),
  444. "far_future_expires" => $this->options['far_future_expires']['css'] &&
  445. !$this->options['htaccess']['mod_expires'],
  446. "far_future_expires_php" => $this->options['far_future_expires']['css'],
  447. "far_future_expires_rewrite" => $this->options['htaccess']['mod_rewrite'] &&
  448. $this->options['htaccess']['enabled'] &&
  449. $this->options['far_future_expires']['css'],
  450. "far_future_expires_static" => ((!($this->options['htaccess']['mod_rewrite'] ||
  451. $this->options['htaccess']['mod_expires']) ||
  452. !$this->options['htaccess']['enabled']) &&
  453. $this->options['far_future_expires']['css']) ||
  454. ((!($this->options['htaccess']['mod_rewrite'] ||
  455. $this->options['htaccess']['mod_deflate'] ||
  456. $this->options['htaccess']['mod_gzip']) ||
  457. !$this->options['htaccess']['enabled']) &&
  458. $this->options['gzip']['css']),
  459. "data_uris" => $this->options['data_uris']['on'],
  460. /* disable mhtml for IE7- under HTTPS */
  461. "data_uris_mhtml" => $this->options['data_uris']['mhtml'] && !$this->https,
  462. "data_uris_separate" => $this->premium > 1 && $this->options['data_uris']['separate'] &&
  463. ((in_array($this->ua_mod, $this->ies) &&
  464. $this->options['data_uris']['mhtml']) ||
  465. (!in_array($this->ua_mod, $this->ies) &&
  466. $this->options['data_uris']['on'])),
  467. "data_uris_domloaded" => $this->options['unobtrusive']['background'] &&
  468. $this->premium > 1,
  469. "data_uris_size" => round($this->options['data_uris']['size']),
  470. "data_uris_mhtml_size" => round($this->options['data_uris']['mhtml_size']),
  471. "data_uris_exclude" => $this->options['data_uris']['ignore_list'],
  472. "data_uris_exclude_mhtml" => $this->options['data_uris']['additional_list'],
  473. "css_sprites" => $this->premium && $this->options['css_sprites']['enabled'],
  474. "css_sprites_expires_rewrite" => (!($this->options['htaccess']['mod_rewrite'] &&
  475. $this->options['htaccess']['mod_expires']) ||
  476. !$this->options['htaccess']['enabled']) &&
  477. $this->options['far_future_expires']['images'],
  478. "css_sprites_ignore" => $this->options['css_sprites']['ignore'],
  479. "css_sprites_exclude" => $this->options['css_sprites']['ignore_list'],
  480. "truecolor_in_jpeg" => $this->premium > 1 && $this->options['css_sprites']['truecolor_in_jpeg'],
  481. "aggressive" => $this->premium > 1 && $this->options['css_sprites']['aggressive'],
  482. "no_ie6" => $this->premium > 1 && $this->options['css_sprites']['no_ie6'],
  483. "dimensions_limited" => $this->premium ? round($this->options['css_sprites']['dimensions_limited']) : 0,
  484. "css_sprites_extra_space" => $this->premium && $this->options['css_sprites']['extra_space'],
  485. "punypng" => $this->premium > 1 ? $this->options['punypng'] : '',
  486. "css_restore_properties" => $this->options['performance']['restore_properties'] &&
  487. $this->premium > 1,
  488. "unobtrusive_body" => false,
  489. "parallel" => $this->options['parallel']['enabled'],
  490. "parallel_hosts" => $this->options['parallel']['allowed_list'],
  491. "external_scripts" => $this->options['external_scripts']['css'],
  492. "inline_scripts" => $this->options['external_scripts']['css_inline'],
  493. "external_scripts_exclude" => $this->options['external_scripts']['additional_list'],
  494. "include_code" => $this->options['external_scripts']['include_code'],
  495. "remove_list" => $this->premium > 1 ? $this->options['external_scripts']['remove_list_css'] : '',
  496. "dont_check_file_mtime" => $this->options['performance']['mtime'],
  497. "file" => $this->premium > 1 ? $this->options['minify']['css_file'] : '',
  498. "host" => $this->premium ? $this->options['minify']['css_host'] : '',
  499. "https" => $this->premium > 1 ? $this->options['parallel']['https'] : '',
  500. "rocket" => $this->options['rocket']['css'] && !$this->options['minify']['css'],
  501. "reorder" => $this->options['rocket']['reorder']
  502. ),
  503. "page" => array(
  504. "cachedir" => $this->options['html_cachedir'],
  505. "cache_engine" => $this->premium ? $this->options['performance']['cache_engine'] : 0,
  506. "cache_engine_options" => $this->premium ? $this->options['performance']['cache_engine_options'] : '',
  507. "cachedir_relative" => str_replace($this->options['document_root'], "/", $this->options['html_cachedir']),
  508. "installdir" => $webo_cachedir,
  509. "host" => $this->options['host'],
  510. "gzip" => $this->options['gzip']['page'] &&
  511. ((!$this->options['htaccess']['mod_gzip'] &&
  512. !$this->options['htaccess']['mod_deflate']) ||
  513. !$this->options['htaccess']['enabled']),
  514. "zlib" => $this->options['gzip']['zlib'],
  515. "gzip_noie" => $this->options['gzip']['noie'] &&
  516. $this->premium > 1,
  517. "gzip_level" => round($this->options['gzip']['page_level']),
  518. "gzip_cookie" => $this->options['gzip']['cookie'] &&
  519. $this->premium > 1,
  520. "minify" => $this->options['minify']['page'],
  521. "minify_aggressive" => $this->options['minify']['html_one_string'] &&
  522. $this->premium,
  523. "remove_comments" => $this->options['minify']['html_comments'] &&
  524. $this->premium,
  525. "dont_check_file_mtime" => $this->options['performance']['mtime'],
  526. "cache_images" => $this->options['far_future_expires']['images'],
  527. "far_future_expires_rewrite" => (!($this->options['htaccess']['mod_rewrite'] ||
  528. $this->options['htaccess']['mod_expires']) ||
  529. !$this->options['htaccess']['enabled']) &&
  530. $this->options['far_future_expires']['images'],
  531. "far_future_expires_external" => $this->options['far_future_expires']['external'],
  532. "clientside_cache" => $this->premium > 1 && $this->options['far_future_expires']['html'],
  533. "clientside_timeout" => $this->options['far_future_expires']['html_timeout'],
  534. "cache" => $this->options['html_cache']['enabled'] &&
  535. $this->premium,
  536. "cache_timeout" => $this->options['html_cache']['timeout'],
  537. "cart_timeout" => $this->premium > 1 ? $this->options['html_cache']['timeout_cart'] : $this->options['html_cache']['timeout'],
  538. "ajax_timeout" => $this->premium > 1 ? $this->options['html_cache']['timeout_ajax'] : $this->options['html_cache']['timeout'],
  539. "flush" => $this->options['html_cache']['flush_only'] &&
  540. $this->premium > 1,
  541. "flush_size" => $this->options['html_cache']['flush_size'],
  542. "cache_ignore" => $this->premium ? $this->options['html_cache']['ignore_list'] : '',
  543. "ignore_include" => $this->premium ? $this->options['html_cache']['ignore'] : '',
  544. "cache_params" => $this->premium > 1 ? $this->options['html_cache']['params'] : '',
  545. "allowed_user_agents" => $this->premium > 1 ? $this->options['html_cache']['allowed_list'] : '',
  546. "exclude_cookies" => $this->premium > 1 ? $this->options['html_cache']['additional_list'] : '',
  547. "parallel" => $this->options['parallel']['enabled'] && !empty($this->options['parallel']['allowed_list']),
  548. "parallel_regexp" => $this->premium > 1 ? $this->options['parallel']['regexp'] : '',
  549. "parallel_hosts" => $this->options['parallel']['allowed_list'],
  550. "parallel_satellites" => $this->options['parallel']['additional'],
  551. "parallel_satellites_hosts" => $this->options['parallel']['additional_list'],
  552. "parallel_ignore" => $this->options['parallel']['ignore_list'],
  553. "parallel_css" => $this->options['parallel']['css'] && !empty($this->options['parallel']['allowed_list']),
  554. "parallel_javascript" => $this->options['parallel']['javascript'] && !empty($this->options['parallel']['allowed_list']),
  555. "parallel_ftp" => $this->premium ? $this->options['parallel']['ftp'] : '',
  556. "parallel_https" => $this->premium > 1 ? $this->options['parallel']['https'] : '',
  557. "unobtrusive_informers" => $this->options['unobtrusive']['informers'] &&
  558. ($this->premium > 1),
  559. "unobtrusive_counters" => $this->options['unobtrusive']['counters'] &&
  560. ($this->premium > 1),
  561. "unobtrusive_ads" => $this->options['unobtrusive']['ads'] &&
  562. ($this->premium > 1),
  563. "unobtrusive_all" => $this->options['unobtrusive']['all'] &&
  564. $this->premium,
  565. "unobtrusive_iframes" => $this->options['unobtrusive']['iframes'] &&
  566. ($this->premium > 1),
  567. "unobtrusive_onload" => $this->options['unobtrusive']['on'] &&
  568. ($this->premium > 1),
  569. "unobtrusive_inline" => $this->options['unobtrusive']['on'] == 2 &&
  570. ($this->premium > 1),
  571. "unobtrusive_configuration" => $this->premium > 1 ? explode(" ", $this->options['unobtrusive']['configuration']) : array(),
  572. "postload" => $this->premium > 1 ? $this->options['unobtrusive']['postload'] : '',
  573. "postload_frames" => $this->premium > 1 ? $this->options['unobtrusive']['frames'] : '',
  574. "footer" => $this->options['footer']['text'],
  575. "footer_image" => $this->options['footer']['image'],
  576. "footer_text" => $this->options['footer']['link'],
  577. "footer_style" => $this->options['footer']['css_code'],
  578. "spot" => $this->premium ? $this->options['footer']['spot'] : 1,
  579. "counter" => $this->premium > 1 ? $this->options['footer']['counter'] : '',
  580. "ab" => $this->options['footer']['ab'] &&
  581. $this->premium > 1,
  582. "htaccess_username" => $this->premium > 1 ? $this->options['external_scripts']['user'] : '',
  583. "htaccess_password" => $this->premium > 1 ? $this->options['external_scripts']['pass'] : '',
  584. "html_tidy" => $this->options['performance']['plain_string'] &&
  585. $this->premium,
  586. "sprites" => $this->premium && $this->options['css_sprites']['html_sprites'],
  587. "sprites_domloaded" => $this->options['unobtrusive']['background'] &&
  588. $this->premium > 1,
  589. "dimensions_limited" => $this->premium ? round($this->options['css_sprites']['html_limit']) : 0,
  590. "per_page" => $this->premium && $this->options['css_sprites']['html_page'],
  591. "https_separate" => $this->premium > 1 ? $this->options['performance']['https'] : 0,
  592. "scale_images" => $this->premium > 1 ? $this->options['performance']['scale'] : 0,
  593. "scale_restriction" => $this->premium > 1 ? $this->options['performance']['scale_restriction'] : 0,
  594. ),
  595. "document_root" => $this->options['document_root'],
  596. "document_root_relative" => str_replace("//", "/", str_replace($this->options['document_root'], "/", $this->options['website_root'])),
  597. "website_root" => $this->options['website_root'],
  598. "cache_version" => round($this->options['performance']['cache_version']) &&
  599. $this->premium > 1,
  600. "uniform_cache" => $this->options['performance']['uniform_cache'] &&
  601. $this->premium > 1,
  602. "plugins" => !empty($this->options['plugins']) &&
  603. $this->premium > 1 ? explode(" ", $this->options['plugins']) : '',
  604. "days_to_delete" => $this->premium > 1 ? round($this->options['performance']['delete_old']) : 0,
  605. "charset" => $this->options['charset'],
  606. "currency" => $this->premium > 1 ? $this->options['currency'] : '',
  607. 'host' => $this->options['host'],
  608. "clean_html_cache" => $this->options['html_cache']['cleanup'] &&
  609. $this->premium > 1 ? $this->options['html_cache']['timeout'] : 0
  610. );
  611. $this->lc = $this->options['license'];
  612. /* overwrite other options array that we passed in */
  613. $this->options = $full_options;
  614. /* some additional checks */
  615. if ($this->options['page']['parallel_css'] && empty($this->options['css']['host'])) {
  616. if ($this->options['page']['parallel_hosts']) {
  617. $hosts = explode(" ", $this->options['page']['parallel_hosts']);
  618. $this->options['css']['host'] = $hosts[0];
  619. } else {
  620. $this->options['page']['parallel_css'] = 0;
  621. }
  622. }
  623. if ($this->options['page']['parallel_javascript'] && empty($this->options['javascript']['host'])) {
  624. if ($this->options['page']['parallel_hosts']) {
  625. $hosts = explode(" ", $this->options['page']['parallel_hosts']);
  626. $this->options['javascript']['host'] = $hosts[0];
  627. } else {
  628. $this->options['page']['parallel_javascript'] = 0;
  629. }
  630. }
  631. }
  632. /**
  633. * Start saving the output buffer
  634. *
  635. **/
  636. function start () {
  637. ob_start();
  638. ob_start();
  639. ob_implicit_flush(0);
  640. }
  641. /**
  642. * Compress passes content directly
  643. *
  644. **/
  645. function compress ($content) {
  646. $this->finish($content);
  647. }
  648. /**
  649. * Do work and return output buffer
  650. *
  651. **/
  652. function finish ($content = false) {
  653. /* disable any actions if not active */
  654. if (empty($this->options['active'])) {
  655. return $content;
  656. }
  657. @ini_set("max_execution_time", 600);
  658. if ($content === false) {
  659. $this->content = @ob_get_clean();
  660. /* clear all other buffers */
  661. while (@ob_end_clean());
  662. } else {
  663. $this->content = $content;
  664. }
  665. /* execute plugin-specific logic, BeforeOptimization event */
  666. if (is_array($this->options['plugins'])) {
  667. foreach ($this->options['plugins'] as $plugin) {
  668. $plugin_file =
  669. $this->options['css']['installdir'] .
  670. 'plugins/' . $plugin . '.php';
  671. if (@is_file($plugin_file)) {
  672. include_once($plugin_file);
  673. $web_optimizer_plugin = new $plugin;
  674. $this->content =
  675. $web_optimizer_plugin->onBeforeOptimization($this->content);
  676. }
  677. }
  678. }
  679. $skip = 0;
  680. $ajax = 0;
  681. if (function_exists('headers_list')) {
  682. $headers = headers_list();
  683. /* define if Content-Type is text/html and allow it */
  684. foreach ($headers as $head) {
  685. $header = strtolower($head);
  686. if (strpos($header, 'content-type:') !== false || (strpos($header, 'location:') !== false && strpos($header, '-location:') === false)) {
  687. $skip++;
  688. }
  689. if (strpos($header, 'text/html') || strpos($header, 'application/xhtml+xml')) {
  690. $skip--;
  691. }
  692. if (strpos($header, 'application/json')) {
  693. $skip++;
  694. $ajax = 1;
  695. }
  696. if (strpos($header, 'content-base') !== false) {
  697. $this->basehref = substr($head, 14);
  698. }
  699. if ($this->ua_mod && strpos($header, 'x-ua-compatible') !== false) {
  700. $this->ua_mod = '.ie' . substr($head, 20);
  701. }
  702. }
  703. }
  704. /* also skip AJAX requests with X-Requested-With: XMLHttpRequest */
  705. if (!$skip &&
  706. !empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
  707. $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
  708. $skip = 1;
  709. $ajax = 1;
  710. }
  711. /* also skip some CMS-related parameters */
  712. if (!$skip && !empty($_GET['no_html'])) {
  713. $skip = 1;
  714. $ajax = 1;
  715. }
  716. /* skip some extensions */
  717. if (!$skip && !empty($_SERVER['QUERY_STRING'])) {
  718. $query = explode('.', $_SERVER['QUERY_STRING']);
  719. $ext = strtolower($query[count($query) - 1]);
  720. if (in_array($ext, array('pdf', 'doc', 'xls', 'docx', 'xlsx'))) {
  721. $skip = 1;
  722. }
  723. }
  724. /* skip some known cases of non-HTML content */
  725. if (!$skip) {
  726. /* reduce amount of viewing content, accelerate 'fast check' by 1% */
  727. $spot = substr($this->content, 0, 200);
  728. if (strpos($spot, '<methodResponse') !== false ||
  729. strpos($spot, '<rss') !== false ||
  730. strpos($spot, '<feed') !== false ||
  731. strpos($spot, '<urlset') !== false ||
  732. strpos($spot, '<smf') !== false ||
  733. strlen($spot) < 200) {
  734. $skip = 1;
  735. /* mark AJAX requests */
  736. } elseif (strpos($spot, '{') === 0 || !preg_match("!<h(tml|ead)!is", $spot)) {
  737. $skip = 1;
  738. $ajax = 1;
  739. }
  740. }
  741. /* enable A/B testing */
  742. if (!$skip && !empty($this->options['page']['ab']) && !empty($_COOKIE['WSS_DISABLED'])) {
  743. $this->content = preg_replace("!(<head[^>]*>)!i", "$1" . '<script type="text/javascript">//<![CDATA[' .
  744. "\n" . 'var _gaq=_gaq||[];_gaq.push(["_setCustomVar",1,"WEBOSiteSpeedUp","0"])' . "\n//]]></script>", $this->content);
  745. $skip = 1;
  746. }
  747. /* skip RSS, SMF xml format */
  748. if (!$skip) {
  749. /* define gzip headers at the end */
  750. $this->set_gzip_header();
  751. /* Add UA-Compatible support */
  752. if ($this->ua_mod && strpos($this->content, 'X-UA-Compatible') && !strpos($this->content, 'IE=edge')) {
  753. $this->ua_mod = '.ie' . preg_replace("!.*<meta[^>]+content=['\"]?IE=([0-9]+).*!is", "$1", $this->content);
  754. }
  755. /* create DOMready chunk of JavaScript code, is required for different tasks */
  756. $this->domready_include = $this->domready_include2 = $this->domready_include3 = '';
  757. if ($this->options['css']['data_uris_separate'] || $this->options['page']['sprites_domloaded'] || $this->joomla_cache || $this->wp_cache || $this->generic_cache) {
  758. $this->domready_include = '__WSSLOADED=0;function _weboptimizer_load(){if(__WSSLOADED){return}';
  759. if ($this->options['page']['sprites_domloaded']) {
  760. $this->domready_include .= '_webo_hsprites();';
  761. }
  762. $cart_class = $this->generic_cache ? 'wss_cart' : ($this->joomla_cache ? 'vmCartModule' : 'widget_wp_digi_cart');
  763. if ($this->options['page']['cache'] && $this->cache_me) {
  764. $this->domready_include .= 'var g,x=document,f,h,j;if(typeof x.getElementsByClassName!="undefined"){g=x.getElementsByClassName("' .
  765. $cart_class .
  766. '")[0];f=x.getElementsByClassName("wss_cart_qty")[0];h=x.getElementsByClassName("wss_cart2")[0];j=x.getElementsByClassName("wss_cart2_qty")[0]}else{var b=x.getElementsByTagName("*"),c,d=0;while(c=b[d++]){if(c.className){if(/(^|\s)' .
  767. $cart_class .
  768. '(\s|$)/.test(c.className)){g=c}if(/(^|\s)wss_cart_qty(\s|$)/.test(c.className)){f=c}if(/(^|\s)wss_cart2(\s|$)/.test(c.className)){h=c}if(/(^|\s)wss_cart2_qty(\s|$)/.test(c.className)){j=c}}}}if(g&&!(f&&(parseInt(f.innerHTML)))&&!(j&&(parseInt(j.innerHTML)))){var a,a1;if(typeof window.localStorage!="undefined"){a=window.localStorage.wss_cart||"";a1=window.localStorage.wss_cart2||"";if(x.cookie.indexOf("WSS_CART=0")!==-1){delete window.localStorage["wss_cart"];delete window.localStorage["wss_cart2"]}}else{var b=x.cookie.split(";"),c,d=0,e;while(c=b[d++]){e=c.indexOf("wss_cart=");if(!e||e==1){a=c.substr(e+11).replace(/@#/g,";")}e=c.indexOf("wss_cart2=");if(!e||e==1){a1=c.substr(e+11).replace(/@#/g,";")}}}if(x.cookie.indexOf("WSS_CART=1")!==-1'.
  769. ($this->wp_cache ? '&&x.location.pathname!="/cart/"' : '') .
  770. '){if(a&&a!="undefined"){WSS_CART=g.innerHTML=a}if(a1&&a1!="undefined"){WSS_CART2=h.innerHTML=a1}}}';
  771. }
  772. $this->domready_include .= '__WSSLOADED=1}(function(){var d=document;if(d.addEventListener){d.addEventListener("DOMContentLoaded",_weboptimizer_load,false)}';
  773. if (!empty($this->ua_mod) && substr($this->ua_mod, 3, 1) < 8) {
  774. $this->domready_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)}};';
  775. } else {
  776. $this->domready_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)}';
  777. }
  778. $this->domready_include .= 'window[/*@cc_on !@*/0?"attachEvent":"addEventListener"](/*@cc_on "on"+@*/"load",_weboptimizer_load,false)}());';
  779. if ($this->options['page']['cache']) {
  780. /* clean-up all localStorage values */
  781. if ($this->logged) {
  782. $this->domready_include2 .= 'if(typeof window.localStorage!="undefined"){window.localStorage.wss_cart="";window.localStorage.wss_cart2=""}else{document.cookie="wss_cart=;path=/;expires="+(new Date(new Date().getTime()+' .
  783. ($this->options['page']['cart_timeout'] * 1000) .
  784. ').toGMTString());document.cookie="wss_cart2=;path=/;expires="+(new Date(new Date().getTime()+' .
  785. ($this->options['page']['cart_timeout'] * 1000) .
  786. ').toGMTString())}';
  787. /* store user-specific values into localStorage */
  788. } else {
  789. $this->domready_include2 .= '(function(){var a=window;if(a.parent===a)a[/*@cc_on !@*/0?"attachEvent":"addEventListener"](/*@cc_on "on"+@*/"unload",function(){var a,x=document,y,z,r;if(typeof x.getElementsByClassName!="undefined"){a=x.getElementsByClassName("' .
  790. $cart_class .
  791. '")[0]||0;y=x.getElementsByClassName("wss_cart_qty")[0]||0;z=x.getElementsByClassName("wss_cart2")[0]||0;r=x.getElementsByClassName("wss_cart2_qty")[0]||0}else{var b=x.getElementsByTagName("*"),c,d=0;while(c=b[d++]){if(c.className){if(/(^|\s)' .
  792. $cart_class .
  793. '(\s|$)/.test(c.className)){a=c}if(/(^|\s)wss_cart_qty(\s|$)/.test(c.className)){y=c}if(/(^|\s)wss_cart2(\s|$)/.test(c.className)){z=c}if(/(^|\s)wss_cart2_qty(\s|$)/.test(c.className)){r=c}}}}y=y||a.getElementsByTagName("b")[0];if(a||z){if(a){a=a.innerHTML.replace(/[\r\n]/g," ").replace(/\s+/g," ").replace(/;">/g,"\">").replace(/&amp;/,"&")}if(z){z=z.innerHTML.replace(/[\r\n]/g," ").replace(/\s+/g," ").replace(/;">/g,"\">").replace(/&amp;/,"&")}if(typeof window.localStorage!="undefined"){window.localStorage.wss_cart=a;window.localStorage.wss_cart2=z}else{document.cookie="wss_cart="+a.replace(/;/g,"@#")+";path=/;expires="+(new Date(new Date().getTime()+' .
  794. ($this->options['page']['cart_timeout'] * 1000) .
  795. ').toGMTString());document.cookie="wss_cart2="+z.replace(/;/g,"@#")+";path=/;expires="+(new Date(new Date().getTime()+' .
  796. ($this->options['page']['cart_timeout'] * 1000) .
  797. ').toGMTString())}document.cookie="WSS_CART="+(((a&&y&&parseInt(y.innerHTML))||(z&&r&&parseInt(r.innerHTML)))?1:0)+";path=/;expires="+(new Date(new Date().getTime()+' .
  798. ($this->options['page']['cart_timeout'] * 1000) .
  799. ').toGMTString())}},false)})();';
  800. $this->domready_include3 = '(function(){var a=window;if(a.parent===a)a[/*@cc_on !@*/0?"attachEvent":"addEventListener"](/*@cc_on "on"+@*/"unload",function(){var a,x=document,y,z,r;document.cookie="WSS_CART=0;path=/;expires="+(new Date(new Date().getTime()+' .
  801. ($this->options['page']['cart_timeout'] * 1000) .
  802. ').toGMTString())},false)})();';
  803. }
  804. }
  805. }
  806. /* find all files in head to process */
  807. $this->get_script_array();
  808. /* Run the functions specified in options */
  809. if (is_array($this->options)) {
  810. foreach ($this->options as $func => $option) {
  811. if (method_exists($this, $func)) {
  812. if (!empty($option['gzip']) ||
  813. !empty($option['minify']) ||
  814. !empty($option['far_future_expires']) ||
  815. !empty($option['parallel']) ||
  816. !empty($option['unobtrusive_onload']) ||
  817. !empty($option['unobtrusive_all']) ||
  818. !empty($option['unobtrusive_ads']) ||
  819. !empty($option['unobtrusive_counters']) ||
  820. !empty($option['unobtrusive_informers']) ||
  821. !empty($option['unobtrusive_iframes']) ||
  822. !empty($option['postload']) ||
  823. !empty($option['postload_frames']) ||
  824. !empty($option['cache']) ||
  825. !empty($option['sprites']) ||
  826. !empty($option['counter']) ||
  827. !empty($option['scale_images']) ||
  828. !empty($option['ab']) ||
  829. !empty($this->domready_include)) {
  830. if (!empty($this->web_optimizer_stage)) {
  831. $this->write_progress($this->web_optimizer_stage++);
  832. }
  833. $this->$func($option, $func);
  834. }
  835. }
  836. if ($func == 'page') {
  837. $this->clear_trash();
  838. }
  839. }
  840. }
  841. if (!empty($this->web_optimizer_stage)) {
  842. $this->write_progress($this->web_optimizer_stage);
  843. /* redirect to installation page if chained optimization if finished */
  844. if ($this->web_optimizer_stage > 85) {
  845. $this->write_progress(97);
  846. @header('Location: http://' .
  847. $this->host .
  848. str_replace($this->options['document_root'], '/', realpath(dirname(__FILE__) . '/../')) .
  849. '/index.php?page=install_stage_3&Submit=1&web_optimizer_stage=97&wss__password=' .
  850. $this->password);
  851. /* else redirect to the next stage */
  852. } else {
  853. @header('Location: ' . $this->chained_redirect . '?web_optimizer_stage=' .
  854. $this->web_optimizer_stage .
  855. '&password=' .
  856. $this->password .
  857. '&web_optimizer_debug=1');
  858. }
  859. $this->di();
  860. }
  861. }
  862. if ($ajax && $this->options['page']['ajax_timeout']) {
  863. $this->options['page']['cache_timeout'] = $this->options['page']['ajax_timeout'];
  864. $this->cache_generic(1);
  865. }
  866. /* Return content to requestor */
  867. if ($content) {
  868. return $this->content;
  869. /* or echo content to the browser */
  870. } else {
  871. /* HTTP/1.0 needs Content-Length */
  872. if (($this->encoding || empty($this->gzip_set)) && (empty($_SERVER['SERVER_PROTOCOL']) || $_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.0')) {
  873. @header('Content-Length: ' . strlen($this->content));
  874. }
  875. echo $this->content;
  876. /* It's obvious to send anything right after gzipped content */
  877. if (!empty($this->encoding)) {
  878. $this->di();
  879. }
  880. }
  881. }
  882. /**
  883. * GZIP and minify the javascript as required
  884. *
  885. **/
  886. function javascript ($options,$type) {
  887. /* prepare list of files to process */
  888. $script_files = array();
  889. foreach ($this->initial_files as $file) {
  890. if (!empty($file['tag']) && $file['tag'] == 'script') {
  891. $script_files[] = $file;
  892. }
  893. }
  894. if (!empty($options['minify']) && !empty($script_files)) {
  895. $this->content = $this->do_compress(
  896. array(
  897. 'cachedir' => $options['cachedir'],
  898. 'cachedir_relative' => $options['cachedir_relative'],
  899. 'installdir' => $options['installdir'],
  900. 'host' => $options['host'],
  901. 'tag' => 'script',
  902. 'type' => 'text/javascript',
  903. 'ext' => 'js',
  904. 'src' => 'src',
  905. 'self_close' => 0,
  906. 'gzip' => $options['gzip'],
  907. 'gzip_level' => $options['gzip_level'],
  908. 'minify' => $options['minify'],
  909. 'minify_body' => $options['minify_body'],
  910. 'minify_with' => $options['minify_with'],
  911. 'minify_try' => $options['minify_try'],
  912. 'minify_exclude' => $options['minify_exclude'],
  913. 'remove_duplicates' => $options['remove_duplicates'],
  914. 'far_future_expires' => $options['far_future_expires'],
  915. 'far_future_expires_php' => $options['far_future_expires_php'],
  916. 'far_future_expires_rewrite' => $options['far_future_expires_rewrite'],
  917. 'header' => $type,
  918. 'css_sprites' => 0,
  919. 'css_sprites_exclude' => 0,
  920. 'parallel' => 0,
  921. 'aggressive' => 0,
  922. 'no_ie6' => 0,
  923. 'dimensions_limited' => 0,
  924. 'css_sprites_extra_space' => 0,
  925. 'data_uris' => 0,
  926. 'mhtml' => 0,
  927. 'unobtrusive_body' => $options['unobtrusive_body'],
  928. 'external_scripts' => $options['external_scripts'],
  929. 'inline_scripts' => $options['inline_scripts'],
  930. 'inline_scripts_body' => $options['inline_scripts_body'],
  931. 'external_scripts_head_end' => $options['external_scripts_head_end'],
  932. 'external_scripts_exclude' => $options['external_scripts_exclude'],
  933. 'remove_list' => $options['remove_list'],
  934. 'dont_check_file_mtime' => $options['dont_check_file_mtime'],
  935. 'file' => $options['file'],
  936. 'https' => $options['https'],
  937. 'rocket' => $options['rocket'],
  938. 'rocket_external' => $options['rocket_external'],
  939. 'reorder' => $options['reorder']
  940. ),
  941. $this->content,
  942. $script_files
  943. );
  944. }
  945. }
  946. /**
  947. * GZIP and minify the CSS as required
  948. *
  949. **/
  950. function css ($options, $type) {
  951. /* prepare list of files to process */
  952. $link_files = array();
  953. foreach ($this->initial_files as $file) {
  954. if (!empty($file['tag']) && $file['tag'] == 'link') {
  955. $link_files[] = $file;
  956. }
  957. }
  958. if (!empty($options['minify']) && !empty($link_files)) {
  959. /* Compress separately for each media type*/
  960. $this->content = $this->do_compress(
  961. array(
  962. 'cachedir' => $options['cachedir'],
  963. 'cachedir_relative' => $options['cachedir_relative'],
  964. 'installdir' => $options['installdir'],
  965. 'host' => $options['host'],
  966. 'tag' => 'link',
  967. 'type' => 'text/css',
  968. 'ext' => 'css',
  969. 'src' => 'href',
  970. 'rel' => 'stylesheet',
  971. 'data_uris' => $options['data_uris'],
  972. 'mhtml' => $options['data_uris_mhtml'],
  973. 'data_uris_separate' => $options['data_uris_separate'],
  974. 'data_uris_domloaded' => $options['data_uris_domloaded'],
  975. 'data_uris_size' => $options['data_uris_size'],
  976. 'data_uris_exclude' => $options['data_uris_exclude'],
  977. 'mhtml' => $options['data_uris_mhtml'],
  978. 'mhtml_size' => $options['data_uris_mhtml_size'],
  979. 'mhtml_exclude' => $options['data_uris_exclude_mhtml'],
  980. 'css_sprites' => $options['css_sprites'],
  981. 'css_sprites_ignore' => $options['css_sprites_ignore'],
  982. 'css_sprites_exclude' => $options['css_sprites_exclude'],
  983. 'truecolor_in_jpeg' => $options['truecolor_in_jpeg'],
  984. 'aggressive' => $options['aggressive'],
  985. 'no_ie6' => $options['no_ie6'],
  986. 'dimensions_limited' => $options['dimensions_limited'],
  987. 'css_sprites_extra_space' => $options['css_sprites_extra_space'],
  988. 'css_sprites_expires_rewrite' => $options['css_sprites_expires_rewrite'],
  989. 'punypng' => $options['punypng'],
  990. 'css_restore_properties' => $options['css_restore_properties'],
  991. 'self_close' => 1,
  992. 'gzip' => $options['gzip'],
  993. 'gzip_level' => $options['gzip_level'],
  994. 'minify' => $options['minify'],
  995. 'minify_body' => $options['minify_body'],
  996. 'minify_with' => $options['minify_with'],
  997. 'far_future_expires' => $options['far_future_expires'],
  998. 'far_future_expires_php' => $options['far_future_expires_php'],
  999. 'far_future_expires_rewrite' => $options['far_future_expires_rewrite'],
  1000. 'header' => $type,
  1001. 'unobtrusive_body' => $options['unobtrusive_body'],
  1002. 'parallel' => $options['parallel'],
  1003. 'parallel_hosts' => $options['parallel_hosts'],
  1004. 'external_scripts' => $options['external_scripts'],
  1005. 'inline_scripts' => $options['inline_scripts'],
  1006. 'external_scripts_exclude' => $options['external_scripts_exclude'],
  1007. 'include_code' => $options['include_code'],
  1008. 'remove_list' => $options['remove_list'],
  1009. 'dont_check_file_mtime' => $options['dont_check_file_mtime'],
  1010. 'file' => $options['file'],
  1011. 'https' => $options['https'],
  1012. 'rocket' => $options['rocket'],
  1013. 'rocket_external' => $options['rocket'],
  1014. 'reorder' => $options['reorder']
  1015. ),
  1016. $this->content,
  1017. $link_files
  1018. );
  1019. }
  1020. }
  1021. /**
  1022. * GZIP and minify the page itself as required
  1023. *
  1024. **/
  1025. function page ($options, $type) {
  1026. if (empty($this->web_optimizer_stage) && $options['clientside_cache'] && empty($this->flushed)) {
  1027. /* not really GMT but is valid locally */
  1028. $ExpStr = date("D, d M Y H:i:s",
  1029. $this->time + $this->options['page']['clientside_timeout']) . " GMT";
  1030. @header("Cache-Control: private, max-age=" .
  1031. $this->options['page']['clientside_timeout']);
  1032. @header("Expires: " . $ExpStr);
  1033. }
  1034. /* move informers, counters, ads, and iframes before </body> */
  1035. $this->replace_informers($options);
  1036. /* Minify page itself or parse multiple hosts */
  1037. if (!empty($options['minify']) ||
  1038. (!empty($options['parallel']) &&
  1039. !empty($options['parallel_hosts'])) ||
  1040. !empty($options['unobtrusive_all']) ||
  1041. !empty($this->options['page']['far_future_expires_rewrite']) ||
  1042. !empty($this->options['page']['far_future_expires_external']) ||
  1043. !empty($this->options['page']['sprites']) ||
  1044. !empty($this->options['page']['scale_images'])) {
  1045. $this->content = $this->trimwhitespace($this->content);
  1046. }
  1047. if (!empty($this->options['page']['counter']) || !empty($this->options['page']['sprites_domloaded']) || !empty($this->options['page']['ab']) || !empty($this->options['page']['parallel'])) {
  1048. $stamp = '';
  1049. if (!empty($this->options['page']['counter']) || !empty($this->options['page']['sprites_domloaded']) || !empty($this->options['page']['ab'])) {
  1050. $stamp .= '<script type="text/javascript">//<![CDATA[' . "\n";
  1051. if (!empty($this->options['page']['counter'])) {
  1052. $stamp .= '__WSS=(new Date()).getTime();';
  1053. }
  1054. if (!empty($this->options['page']['sprites_domloaded'])) {
  1055. $stamp .= 'var _webo_hsprites=function(){};';
  1056. }
  1057. if (!empty($this->options['page']['ab'])) {
  1058. $stamp .= 'var _gaq=_gaq||[];_gaq.push(["_setCustomVar",1,"WEBOSiteSpeedUp","1"]);';
  1059. }
  1060. $stamp .= "\n//]]></script>";
  1061. }
  1062. if (!empty($this->options['page']['parallel_hosts'])) {
  1063. $stamp .= '<meta http-equiv="x-dns-prefetch-control" content="on"' .
  1064. ($this->xhtml ? '/' : '') .
  1065. '>';
  1066. $hosts = explode(" ", $this->options['page']['parallel_hosts']);
  1067. $hosts_to_cache = array();
  1068. foreach ($hosts as $host) {
  1069. $s = $host . ((strpos($host, '.') === false) ? '.' . $this->host : '');
  1070. $stamp .= '<link rel="dns-prefetch" href="http' .
  1071. ($this->https ? 's' : '') .
  1072. '://' .
  1073. $s .
  1074. '"' .
  1075. ($this->xhtml ? '/' : '') .
  1076. '>';
  1077. $hosts_to_cache[] = '//' . $s;
  1078. }
  1079. $stamp .= '<script type="text/javascript">//<![CDATA[' . "\n" . 'new Image().src="' .
  1080. implode($this->options['page']['cachedir_relative'] . '0.gif";new Image().src="', $hosts_to_cache) .
  1081. $this->options['page']['cachedir_relative'] . '0.gif"' . "\n//]]></script>";
  1082. }
  1083. if ($this->options['page']['html_tidy'] &&
  1084. ($headpos = strpos($this->content, '<head'))) {
  1085. $headend = strpos($this->content, '>', $headpos);
  1086. $this->content = substr_replace($this->content,
  1087. $stamp, $headend + 1, 0);
  1088. } elseif ($this->options['page']['html_tidy'] &&
  1089. ($headpos = strpos($this->content, '<HEAD'))) {
  1090. $headend = strpos($this->content, '>', $headpos);
  1091. $this->content = substr_replace($this->content,
  1092. $stamp, $headend + 1, 0);
  1093. } else {
  1094. $this->content = preg_replace("@<head[^>]*>@is",
  1095. "$0" . $stamp, $this->content);
  1096. }
  1097. }
  1098. /* execute plugin-specific logic, AfterOptimization event */
  1099. if (is_array($this->options['plugins'])) {
  1100. foreach ($this->options['plugins'] as $plugin) {
  1101. $plugin_file =
  1102. $this->options['css']['installdir'] .
  1103. 'plugins/' . $plugin . '.php';
  1104. if (@is_file($plugin_file)) {
  1105. include_once($plugin_file);
  1106. $web_optimizer_plugin = new $plugin;
  1107. $this->content =
  1108. $web_optimizer_plugin->onAfterOptimization($this->content);
  1109. }
  1110. }
  1111. }
  1112. if (!empty($this->web_optimizer_stage)) {
  1113. $this->write_progress($this->web_optimizer_stage++);
  1114. }
  1115. $this->clear_trash();
  1116. $this->cache_generic();
  1117. /* strip from content flushed part */
  1118. if (!empty($this->flushed)) {
  1119. if (empty($options['flush_size'])) {
  1120. $options['flush_size'] = strlen($content_to_write);
  1121. }
  1122. $this->content = substr($this->content, $options['flush_size']);
  1123. }
  1124. /* Gzip page itself */
  1125. if(!empty($options['gzip']) && !empty($this->encoding)) {
  1126. $content = $this->create_gz_compress($this->content,
  1127. in_array($this->encoding, array('gzip', 'x-gzip')));
  1128. if (!empty($content)) {
  1129. $this->content = $content;
  1130. }
  1131. }
  1132. }
  1133. /**
  1134. * Generic cache function
  1135. *
  1136. **/
  1137. function cache_generic ($ajax = false) {
  1138. $chunk = '';
  1139. /* on-fly caching cart */
  1140. if ((!empty($this->cache_me) || defined('WP_CACHE')) && !$ajax) {
  1141. /* add client side replacement for WordPress comment fields */
  1142. if (defined('WP_CACHE')) {
  1143. foreach ($_COOKIE as $key => $value) {
  1144. if (strpos($key, 'comment_author') === 0) {
  1145. $this->content = str_replace('value="'. urldecode($value) .'"', 'value=""', $this->content);
  1146. }
  1147. }
  1148. $chunk = '(function(){' .
  1149. (@is_dir($this->options['website_root'] . 'wp-content/plugins/wp-e-commerce/') ? 'jQuery.post("index.php?ajax=true","wpsc_ajax_action=get_cart",function(returned_data){eval(returned_data)});' : '') .
  1150. '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," "))}}}}());';
  1151. }
  1152. /* Add on-fly caching for Cart in Joomla! and WP, and others */
  1153. if ($this->options['page']['cache'] && !$this->options['css']['data_uris_separate'] && !$this->options['page']['sprites_domloaded']) {
  1154. $chunk .= $this->domready_include . $this->domready_include2;
  1155. }
  1156. /* Add client-side cache flush on pages excluded from caching */
  1157. } elseif ($this->options['page']['cache'] && !$this->options['css']['data_uris_separate'] && !$this->options['page']['sprites_domloaded'] && !$ajax) {
  1158. /* need to refresh localStorage Cache */
  1159. if (@class_exists('JFactory') && ($user = &JFactory::getUser()) && ($user->get('aid') || $user->get('id'))) {
  1160. $chunk = $this->domready_include3;
  1161. } else {
  1162. $chunk = $this->domready_include2;
  1163. }
  1164. }
  1165. if ($chunk) {
  1166. $chunk = ($this->premium > 1 ? '<!--noindex-->' : '').
  1167. "<script type='text/javascript'>//<![CDATA[\n" . $chunk . "\n//]]></script>" .
  1168. ($this->premium > 1 ? '<!--/noindex-->' : '');
  1169. if (preg_match("!</body>!i", $this->content)) {
  1170. $this->content = preg_replace("!(</body>)!is", $chunk . "$1", $this->content);
  1171. } else {
  1172. $this->content .= $chunk;
  1173. }
  1174. }
  1175. /* check if we need to store cached page */
  1176. if (!empty($this->cache_me) && empty($_COOKIE['WSS_CART'])) {
  1177. /* prepare flushed part of content */
  1178. if (!empty($this->options['page']['flush']) && empty($this->encoding)) {
  1179. if (empty($this->options['page']['flush_size'])) {
  1180. if ($this->options['page']['html_tidy'] && ($headpos = strpos($source, '</head>'))) {
  1181. $content_to_write = substr($this->content, 0, $headpos + 7);
  1182. } elseif ($this->options['page']['html_tidy'] && ($headpos = strpos($source, '</HEAD>'))) {
  1183. $content_to_write = substr($this->content, 0, $headpos + 7);
  1184. } else {
  1185. $content_to_write = preg_replace("!(.*<\/head>).*!is", "$1", $this->content);
  1186. }
  1187. } else {
  1188. $content_to_write =
  1189. substr($this->content, 0, $this->options['page']['flush_size']);
  1190. }
  1191. }
  1192. $ordinary_cache_key = $this->view->ensure_trailing_slash($this->uri) .
  1193. 'index' .
  1194. $this->ua_mod .
  1195. '.html' .
  1196. ($this->options['page']['https_separate'] ? $this->https : '');
  1197. $cache_key = $ordinary_cache_key .
  1198. (empty($this->encoding_ext) ? '' : $this->encoding_ext) .
  1199. ($ajax ? '.ajax' : '');
  1200. $ordinary_cache_key .= ($ajax ? '.ajax' : '');
  1201. $timestamp = $this->cache_engine->get_mtime($cache_key);
  1202. $content = $this->cache_engine->get_entry($cache_key);
  1203. /* set ETag, thx to merzmarkus */
  1204. if (empty($this->options['page']['flush'])) {
  1205. @header("ETag: \"" .
  1206. crc32($this->content) .
  1207. (empty($this->encoding) && empty($this->gzip_set) ? '' : '-' .
  1208. (empty($this->gzip_set) ? str_replace("x-", "", $this->encoding) : 'gzip')) .
  1209. "\"");
  1210. }
  1211. if (empty($timestamp) || empty($content) || $this->time - $timestamp > $this->options['page']['cache_timeout']) {
  1212. $c = $this->content;
  1213. $jutility = class_exists('JUtility', false);
  1214. $jsession = class_exists('JSession', false);
  1215. if ($jutility) {
  1216. $token = JUtility::getToken();
  1217. }
  1218. elseif ($jsession) {
  1219. $token = JSession::getFormToken();
  1220. }
  1221. if (!empty($token)) {
  1222. $c = str_replace('##WSS_JTOKEN_WSS##', $token, $c);
  1223. }
  1224. if (!empty($this->options['page']['gzip']) && !empty($this->encoding) && !$jutility && !$jsession) {
  1225. $content_to_write = $this->create_gz_compress($c,
  1226. in_array($this->encoding, array('gzip', 'x-gzip')));
  1227. /* or just write full or non-gzipped content */
  1228. } elseif ((empty($this->options['page']['flush']) || !empty($this->encoding)) && !$jutility && !$jsession) {
  1229. $content_to_write = $c;
  1230. }
  1231. /* don't create empty files */
  1232. if (!empty($content_to_write)) {
  1233. $this->cache_engine->put_entry($cache_key, $content_to_write, $this->time);
  1234. }
  1235. /* create uncompressed file for plugins */
  1236. if ($cache_key != $ordinary_cache_key || empty($content_to_write)) {
  1237. $this->cache_engine->put_entry($ordinary_cache_key, $c, $this->time);
  1238. }
  1239. /* clean expired entries */
  1240. if ($this->options['clean_html_cache']) {
  1241. $this->cache_engine->delete_entries_by_time($this->time, $this->options['page']['cache_timeout']);
  1242. }
  1243. }
  1244. }
  1245. /* execute plugin-specific logic, Cache event */
  1246. if (is_array($this->options['plugins'])) {
  1247. foreach ($this->options['plugins'] as $plugin) {
  1248. $plugin_file =
  1249. $this->options['css']['installdir'] .
  1250. 'plugins/' . $plugin . '.php';
  1251. if (@is_file($plugin_file)) {
  1252. include_once($plugin_file);
  1253. $web_optimizer_plugin = new $plugin;
  1254. $this->content =
  1255. $web_optimizer_plugin->onCache($this->content);
  1256. }
  1257. }
  1258. }
  1259. }
  1260. /**
  1261. * Write array to content
  1262. *
  1263. **/
  1264. function write_file_array ($file, $arr, $name) {
  1265. $str = "<?php\n";
  1266. $mq = get_magic_quotes_runtime();
  1267. foreach ($arr as $key => $value) {
  1268. $str .= '$' . $name . "['" . $key . "']='" . ($mq ? $value : str_replace(array("\\", "'"), array("\\\\", "\\'"), $value)) . "';\n";
  1269. }
  1270. $str .= "?>";
  1271. return $this->write_file($file, $str);
  1272. }
  1273. /**
  1274. * Write content to file
  1275. *
  1276. **/
  1277. function write_file ($file, $content, $upload = 0, $mime = '', $recursion = 0) {
  1278. if (@function_exists('file_put_contents')) {
  1279. @file_put_contents($file . '.tmp', $content, LOCK_EX);
  1280. @rename($file . '.tmp', $file);
  1281. } else {
  1282. $fp = @fopen($file, "a+");
  1283. if ($fp) {
  1284. /* block file from writing */
  1285. @flock($fp, LOCK_EX);
  1286. /* erase content and move to the beginning */
  1287. @ftruncate($fp, 0);
  1288. @fseek($fp, 0);
  1289. @fwrite($fp, $content);
  1290. @fclose($fp);
  1291. }
  1292. }
  1293. if (!@is_file($file) && $recursion < 3) {
  1294. $this->write_file($file, $content, $upload, $mime, $recursion+1);
  1295. return;
  1296. }
  1297. @touch($file, $this->time);
  1298. @chmod($file, octdec("0644"));
  1299. if ($upload && !empty($this->options['page']['parallel_ftp'])) {
  1300. $this->view->upload_cdn($file,
  1301. $this->options['document_root'],
  1302. $this->options['page']['parallel_ftp'],
  1303. $mime,
  1304. $this->options['page']['host']);
  1305. }
  1306. }
  1307. /**
  1308. * Adds multiple hosts to HTML for images
  1309. *
  1310. **/
  1311. function add_multiple_hosts ($content, $hosts, $satellites, $satellites_hosts) {
  1312. /* limit by 4 */
  1313. if (count($hosts) > 4) {
  1314. $hosts = array($hosts[0], $hosts[1], $hosts[2], $hosts[3]);
  1315. }
  1316. if (count($satellites_hosts) > 4) {
  1317. $satellites_hosts = array($satellites_hosts[0], $satellites_hosts[1], $satellites_hosts[2], $satellites_hosts[3]);
  1318. }
  1319. $count = count($hosts);
  1320. $count_satellites = count($satellites_hosts);
  1321. $replaced = array();
  1322. $IMG = strpos($content, '<IMG');
  1323. if ($this->options['page']['html_tidy'] && !$IMG && !$this->options['page']['parallel_regexp']) {
  1324. $_content = $content;
  1325. $pos = $pos1 = false;
  1326. while (($pos = strpos($_content, '<img')) !== false || ($pos1 = strpos($_content, '<input')) !== false) {
  1327. $pos = $pos1 !== false && $pos1 < $pos ? $pos1 : $pos;
  1328. $len = strpos(substr($_content, $pos), '>');
  1329. if (strpos($_content, 'src') > $pos && strpos($_content, 'src') < $pos + $len) {
  1330. /* gets image tag w/o the closing >, it's OK */
  1331. $imgs[] = array(substr($_content, $pos, $len), $pos1 ? 'input' : 'img', 'src');
  1332. }
  1333. $_content = substr_replace($_content, '', 0, $pos + $len + 1);
  1334. $pos = $pos1 = false;
  1335. }
  1336. } else {
  1337. $regexp = $this->options['page']['parallel_regexp'] ? $this->options['page']['parallel_regexp'] : '<(img|input)[^>]+(src)[^>]+>';
  1338. preg_match_all("!" . $regexp . "!is", $content, $imgs, PREG_SET_ORDER);
  1339. }
  1340. if (($this->options['page']['sprites'] || $this->options['page']['scale_images']) && !empty($imgs)) {
  1341. require_once($this->options['css']['installdir'] . 'libs/php/html.sprites.php');
  1342. $html_sprites = new html_sprites($imgs, $this->options, $this);
  1343. if ($this->options['page']['scale_images']) {
  1344. $content = $html_sprites->scale($content);
  1345. }
  1346. if ($this->options['page']['sprites']) {
  1347. $content = $html_sprites->process($content);
  1348. }
  1349. }
  1350. if (!empty($imgs)) {
  1351. $ignore_list = explode(" ", $this->options['page']['parallel_ignore']);
  1352. $ignore_sprites = explode(" ", $this->options['css']['css_sprites_exclude']);
  1353. $request_file = empty($this->basehref) ? $_SERVER['REQUEST_URI'] : $this->basehref;
  1354. if ($this->options['page']['scale_images']) {
  1355. if (!$this->options['page']['sprites']) {
  1356. @include($html_sprites->convert_file_name('cache'));
  1357. }
  1358. $images_scaled = array();
  1359. @include($html_sprites->convert_file_name('scale'));
  1360. $images_scaled_source = array_keys($images_scaled);
  1361. }
  1362. foreach ($imgs as $image) {
  1363. $l = strlen($image[2]) + 3;
  1364. if ($this->options['page']['html_tidy'] && ($pos=strpos($image[0], ' ' . $image[2] . '="'))) {
  1365. $old_src = substr($image[0], $pos + $l, strpos(substr($image[0], $pos + $l), '"'));
  1366. } elseif ($this->options['page']['html_tidy'] && ($pos=strpos($image[0], " " . $image[2] . "='"))) {
  1367. $old_src = substr($image[0], $pos + $l, strpos(substr($image[0], $pos + $l), "'"));
  1368. } else {
  1369. $old_src = preg_replace("!^['\"\s]*(.*?)['\"\s]*$!is", "$1", preg_replace("!.*[\"'\s]" . $image[2] . "\s*=\s*(\"[^\"]+\"|'[^']+'|[\S]+).*!is", "$1", $image[0]));
  1370. }
  1371. $old_src_param = ($old_src_param_pos = strpos($old_src, '?')) ? substr($old_src, $old_src_param_pos) : '';
  1372. /* image file name to check through ignore list */
  1373. $img = preg_replace("@.*/@", "", $old_src);
  1374. $absolute_src = $this->convert_path_to_absolute($old_src, array('file' => $_SERVER['REQUEST_URI']));
  1375. if (empty($replaced[$image[0]])) {
  1376. if ($this->options['page']['sprites'] &&
  1377. ((!in_array($img, $ignore_sprites) && !$this->options['css']['css_sprites_ignore']) ||
  1378. (in_array($img, $ignore_sprites) && $this->options['css']['css_sprites_ignore'])) &&
  1379. !empty($html_sprites->css_images[$absolute_src]) && !empty($html_sprites->css_images[$absolute_src][2]) &&
  1380. (empty($this->ua_mod) || $this->ua_mod != '.ie6' || empty($this->options['css']['no_ie6']))) {
  1381. $class = substr($html_sprites->css_images[$absolute_src][8], 1);
  1382. if (!empty($this->options['page']['html_tidy']) &&
  1383. (strpos($image[0], 'class') || strpos($image[0], 'CLASS'))) {
  1384. if ($pos=strpos($image[0], ' class="')) {
  1385. $new_image = substr($image[0], 0, $pos + 8) .
  1386. $class . ' ' . substr($image[0], $pos + 8);
  1387. } elseif ($pos=strpos($image[0], " class='")) {
  1388. $new_image = substr($image[0], 0, $pos + 8) .
  1389. $class . ' ' . substr($image[0], $end);
  1390. } else {
  1391. $new_image = preg_replace("!(.*['\"\s]class\s*=\s*)([\"'])?([^\"']+)([\"'])?([\s/>])(.*)!is", "$1$2$3 " .
  1392. $class . "$4$5$6", $image[0]);
  1393. }
  1394. } elseif (preg_match("@['\"\s]class\s*=\s*['\"]@is", $image[0])) {
  1395. $new_image = preg_replace("!(.*['\"\s]class\s*=\s*)([\"'])?([^\"']+)([\"'])?([\s/>])(.*)!is", "$1$2$3 " .
  1396. $class . "$4$5$6", $image[0]);
  1397. } elseif (preg_match("@['\"\s]class\s*=\s*@is", $image[0])) {
  1398. $new_image = preg_replace("!(.*['\"\s]class\s*=\s*)([^\s]+)\s!is", "$1\"$2 " .
  1399. $class . "\" ", $image[0]);
  1400. } else {
  1401. $s = strlen($image[1]);
  1402. $new_image = substr($image[0], 0, $s + 1) . ' class="' .
  1403. $class . '"' . substr($image[0], $s + 1);
  1404. }
  1405. /* add transparent GIF or data:URI chunk */
  1406. $new_src = (empty($this->ua_mod) ||
  1407. substr($this->ua_mod, 3, 1) > 7) &&
  1408. !$this->options['uniform_cache'] ?
  1409. 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' :
  1410. ((empty($this->options['page']['far_future_expires_rewrite']) ?
  1411. '' : $this->options['page']['cachedir_relative'] . 'wo.static.php?') .
  1412. $this->options['page']['cachedir_relative'] . '0.gif');
  1413. /* are we operating with multiple hosts */
  1414. } elseif (!empty($this->options['page']['parallel']) &&
  1415. !empty($this->options['page']['parallel_hosts']) &&
  1416. (!count($ignore_list) || (!in_array(str_replace($old_src_param, '', $img), $ignore_list) && (empty($ignore_list[0]) || strpos($old_src, $ignore_list[0]) === false)))) {
  1417. /* skip images on different hosts */
  1418. if (preg_match("!//(www\.)?" . $this->host_escaped . "/+!i", $old_src) || ($absolute_src && strpos($absolute_src, '//') === false)) {
  1419. /* using secure host */
  1420. if ($this->https && !empty($this->options['page']['parallel_https'])) {
  1421. $new_host = $this->options['page']['parallel_https'];
  1422. } else {
  1423. /* calculating unique sum from image src */
  1424. $sum = 0;
  1425. $i = ceil(strlen($old_src)/2);
  1426. while (isset($old_src{$i++})) {
  1427. $sum += ord($old_src{$i-1});
  1428. }
  1429. $host = $hosts[$sum%$count];
  1430. /* if we have dot in the distribution host - it's a domain name */
  1431. $new_host = $host .
  1432. ((strpos($host, '.') === false) ?
  1433. '.' . $this->host : '');
  1434. }
  1435. $new_src = "//" .
  1436. $new_host .
  1437. $absolute_src .
  1438. preg_replace("!(www\.)?" . $this->host_escaped . "!i",
  1439. $new_host, $old_src_param);
  1440. } elseif ($count_satellites && !empty($satellites_hosts[0]) && empty($replaced[$old_src])) {
  1441. $img_host = preg_replace("@(https?:)?//(www\.)?([^/]+)/.*@", "$3", $old_src);
  1442. /* check if we can distribute this image through satellites' hosts */
  1443. if (in_array($img_host, $satellites)) {
  1444. $new_src = preg_replace("@(https?://)(www\.)?([^/]+)/@", "$1" . $hosts[strlen($old_src)%$count] . ".$3/", $old_src);
  1445. }
  1446. }
  1447. /* or replacing images with rewrite to Expires setter? */
  1448. } elseif (!empty($this->options['page']['far_future_expires_rewrite']) ||
  1449. !empty($this->options['page']['far_future_expires_external'])) {
  1450. /* add static proxy for external images */
  1451. if (!$absolute_src &&
  1452. $this->options['page']['far_future_expires_external']) {
  1453. $absolute_src = $old_src;
  1454. }
  1455. /* do not touch dynamic images / styles / scripts -- how we can handle them? */
  1456. if ($absolute_src &&
  1457. preg_match("@\.(bmp|gif|png|ico|jpe?g)$@is", $absolute_src) &&
  1458. ((!empty($this->options['page']['far_future_expires_external']) &&
  1459. ($absolute_src{0} !== '/' || $absolute_src{1} === '/')) ||
  1460. !empty($this->options['page']['far_future_expires_rewrite']))) {
  1461. $new_src =
  1462. $this->options['page']['cachedir_relative'] .
  1463. 'wo.static.php?' . $absolute_src;
  1464. }
  1465. }
  1466. if (!empty($new_src)) {
  1467. if (empty($new_image)) {
  1468. /* prevent replacing images from oher domains with the same file name */
  1469. $new_src_image = str_replace($old_src, $new_src, $image[0]);
  1470. } else {
  1471. /* replace src in image with new class */
  1472. $new_src_image = str_replace($old_src, $new_src, $new_image);
  1473. }
  1474. $content = str_replace($image[0], $new_src_image, $content);
  1475. $new_src = '';
  1476. $new_image = '';
  1477. }
  1478. $replaced[$image[0]] = 1;
  1479. }
  1480. }
  1481. }
  1482. return $content;
  1483. }
  1484. /**
  1485. * Returns GZIP compressed content string with header
  1486. *
  1487. **/
  1488. function create_gz_compress ($content, $force_gzip = true) {
  1489. if (!empty($this->encoding)) {
  1490. if (!empty($force_gzip) && function_exists('gzencode')) {
  1491. $cnt = gzencode($content, $this->options['page']['gzip_level'], FORCE_GZIP);
  1492. } elseif (!empty($force_gzip) && function_exists('gzcompress')) {
  1493. $size = strlen($content);
  1494. $crc = crc32($content);
  1495. $cnt = "\x1f\x8b\x08\x00\x00\x00\x00\x00";
  1496. $content = gzcompress($content, $this->options['page']['gzip_level']);
  1497. $content = substr($content, 0, -4);
  1498. $cnt .= $content;
  1499. $cnt .= pack('V', $crc);
  1500. $cnt .= pack('V', $size);
  1501. } elseif (empty($force_gzip) && function_exists('gzdeflate')) {
  1502. $cnt = gzdeflate($content, $this->options['page']['gzip_level']);
  1503. }
  1504. return $cnt;
  1505. } else {
  1506. return false;
  1507. }
  1508. }
  1509. /**
  1510. * Sets current encoding for HTML document
  1511. *
  1512. **/
  1513. function set_gzip_encoding () {
  1514. if (!empty($_SERVER["HTTP_ACCEPT_ENCODING"]) && !empty($this->options['page']['gzip'])) {
  1515. $gzip_no_ie = !in_array($this->ua_mod, $this->ies) || empty($this->options['page']['gzip_noie']);
  1516. $ae = strtolower($_SERVER["HTTP_ACCEPT_ENCODING"]);
  1517. if (strpos($ae, 'x-gzip') !== false && $gzip_no_ie) {
  1518. $this->encoding = 'x-gzip';
  1519. $this->encoding_ext = '.gz';
  1520. } elseif ((strpos($ae, 'gzip') !== false || !empty($_COOKIE['_wo_gzip'])) && $gzip_no_ie) {
  1521. $this->encoding = 'gzip';
  1522. $this->encoding_ext = '.gz';
  1523. } elseif (strpos($ae, 'x-deflate') !== false) {
  1524. $this->encoding = 'x-deflate';
  1525. $this->encoding_ext = '.df';
  1526. } elseif (strpos($ae, 'deflate') !== false) {
  1527. $this->encoding = 'deflate';
  1528. $this->encoding_ext = '.df';
  1529. }
  1530. } elseif (empty($_SERVER['HTTP_ACCEPT_ENCODING']) && !empty($_COOKIE['_wo_gzip'])) {
  1531. $this->encoding = 'gzip';
  1532. $this->encoding_ext = '.gz';
  1533. }
  1534. }
  1535. /**
  1536. * Sets the correct gzip header
  1537. *
  1538. **/
  1539. function set_gzip_header () {
  1540. if(!empty($this->encoding) && empty($this->gzip_set)) {
  1541. @header("Vary: Accept-Encoding,User-Agent");
  1542. @header("Content-Encoding: " . $this->encoding);
  1543. /* try to use zlib instead of raw PHP */
  1544. if ($this->options['page']['zlib'] && strlen(@ini_get('zlib.output_compression_level'))) {
  1545. @ini_set('zlib.output_compression', 'On');
  1546. @ini_set('zlib.output_compression_level', $this->options['page']['gzip_level']);
  1547. $this->encoding = '';
  1548. $this->encoding_ext = '';
  1549. }
  1550. $this->gzip_set = 1;
  1551. }
  1552. }
  1553. /**
  1554. * Compress JS or CSS and return source
  1555. *
  1556. **/
  1557. function do_compress ($options, $source, $files) {
  1558. /* Save the original extension */
  1559. $options['original_ext'] = $options['ext'];
  1560. /* Change the extension */
  1561. if (!empty($options['gzip']) || !empty($options['far_future_expires'])) {
  1562. $options['ext'] = "php";
  1563. }
  1564. /* Set cachedir */
  1565. $cachedir = $options['cachedir'];
  1566. if ($this->web_optimizer_stage) {
  1567. $this->write_progress($this->web_optimizer_stage++);
  1568. }
  1569. $source = $this->do_include($options, $source, $cachedir, $files);
  1570. return $source;
  1571. }
  1572. /**
  1573. * Include a single file
  1574. *
  1575. **/
  1576. function include_bundle ($source, $newfile, $handlers, $cachedir, $include, $href = '') {
  1577. switch ($include) {
  1578. /* move CSS file to the first occurent of CSS or before any scripts */
  1579. default:
  1580. if ($this->options['page']['html_tidy']) {
  1581. $styles = strpos($source, "@@@WSSSTYLES@@@");
  1582. $script1 = strpos($source, "<script");
  1583. $script2 = strpos($source, "<SCRIPT");
  1584. if ($script1 !== false && $script1 < $styles) {
  1585. $source = substr_replace($source, $newfile . '@@@WSSSTL@@@', $script1, 0);
  1586. $source = str_replace('@@@WSSSTYLES@@@', '', $source);
  1587. $source = str_replace('@@@WSSSTL@@@', '@@@WSSSTYLES@@@', $source);
  1588. } elseif ($script2 !== false && $script2 < $styles) {
  1589. $source = substr_replace($source, $newfile . '@@@WSSSTL@@@', $script2, 0);
  1590. $source = str_replace('@@@WSSSTYLES@@@', '', $source);
  1591. $source = str_replace('@@@WSSSTL@@@', '@@@WSSSTYLES@@@', $source);
  1592. } else {
  1593. $source = substr_replace($source, $newfile, $styles, 0);
  1594. }
  1595. } else {
  1596. $source = preg_replace('!@@@WSSSTYLES@@@!', $newfile . '@@@WSSSTYLES@@@', $source, 1);
  1597. }
  1598. break;
  1599. /* no unobtrusive but external scripts exist, avoid excluded scripts */
  1600. case 1:
  1601. if ($this->options['page']['html_tidy'] && ($headpos = strpos($source, '</head>'))) {
  1602. $source = substr_replace($source, $newfile, $headpos, 0);
  1603. } elseif ($this->options['page']['html_tidy'] && ($headpos = strpos($source, '</HEAD>'))) {
  1604. $source = substr_replace($source, $newfile, $headpos, 0);
  1605. } else {
  1606. $source = preg_replace("!</head>!is", $newfile . "$0", $source);
  1607. }
  1608. /* additional check in case of non-existing </head>, insert before <body> */
  1609. if (!strpos($source, $newfile)) {
  1610. $source = preg_replace("!<body[^>]*>!is", $newfile . "$0", $source);
  1611. }
  1612. break;
  1613. /* inject merged script to the first <script> occurrence, replace WSSSCRIPT */
  1614. case 2:
  1615. $source = preg_replace("!@@@WSSSCRIPT@@@!", $newfile, $source, 1);
  1616. break;
  1617. /* add JavaScript calls before </body> */
  1618. case 3:
  1619. if ($this->options['page']['html_tidy'] && ($bodypos = strpos($source, '</body>'))) {
  1620. $source = substr_replace($source, $newfile, $bodypos, 0);
  1621. } elseif ($this->options['page']['html_tidy'] && ($bodypos = strpos($source, '</BODY>'))) {
  1622. $source = substr_replace($source, $newfile, $bodypos, 0);
  1623. } else {
  1624. $source = preg_replace("!</body>!is", $newfile . "$0", $source);
  1625. /* a number of engines doesn't set </body> */
  1626. if (!strpos($source, $newfile)) {
  1627. $source .= $newfile;
  1628. }
  1629. }
  1630. /* remove JS file marker from content */
  1631. $source = str_replace('@@@WSSSCRIPT@@@', '', $source);
  1632. break;
  1633. /* place second CSS call to onDOMready */
  1634. case 4:
  1635. if (!$this->options['css']['data_uris_domloaded']) {
  1636. $source = preg_replace("!@@@WSSSTYLES@@@!", "@@@WSSSTYLES@@@" . $newfile , $source, 1);
  1637. } else {
  1638. $inc = ($this->premium > 1 ? '<!--noindex-->' : '') .
  1639. '<script type="text/javascript">//<![CDATA[' . "\n" .
  1640. $this->domready_include .
  1641. 'var d=document,l=d.createElement("link");l.rel="stylesheet";l.type="text/css";l.href="' .
  1642. $href .
  1643. '";d.getElementsByTagName("head")[0].appendChild(l);' .
  1644. $this->domready_include2 .
  1645. (@class_exists('JFactory') && ($user = &JFactory::getUser()) && ($user->get('aid') || $user->get('id')) ? $this->domready_include3 : '') .
  1646. "\n//]]></script>@@@WSSREADY@@@" .
  1647. ($this->premium > 1 ? '<!--/noindex-->' : '');
  1648. /* separate scripts for 2 parts, the second move to the end of the document */
  1649. if ($this->options['page']['html_tidy'] && ($bodypos = strpos($source, '</body>'))) {
  1650. $source = substr_replace($source, $inc, $bodypos, 0);
  1651. } elseif ($this->options['page']['html_tidy'] && ($bodypos = strpos($source, '</BODY>'))) {
  1652. $source = substr_replace($source, $inc, $bodypos, 0);
  1653. } else {
  1654. $source = preg_replace("!</body>!is", $inc . "$0", $source);
  1655. if (!strpos($source, $inc)) {
  1656. $source .= $inc;
  1657. }
  1658. }
  1659. }
  1660. break;
  1661. }
  1662. return $source;
  1663. }
  1664. /**
  1665. * Include compressed JS or CSS into source and return it
  1666. *
  1667. **/
  1668. function do_include ($options, $source, $cachedir, $external_array) {
  1669. $cachedir_relative = $options['cachedir_relative'];
  1670. $handlers = '';
  1671. /* If only one script found */
  1672. if (!is_array($external_array)) {
  1673. $external_array = array($external_array);
  1674. }
  1675. if (empty($options['file'])) {
  1676. /* Glue scripts' content / filenames */
  1677. $scripts_string = $this->https;
  1678. foreach ($external_array as $script) {
  1679. $scripts_string .= (empty($script['source']) ? '' : $script['source']) . (empty($script['content']) ? '' : $script['content']);
  1680. }
  1681. /* Get date string to make hash */
  1682. $datestring = $this->get_file_dates($external_array, $options);
  1683. /* get options string */
  1684. $optstring = '';
  1685. foreach ($options as $key => $value) {
  1686. if (is_array($value)) {
  1687. $optstring .= '_' . implode('_', $value);
  1688. } else {
  1689. $optstring .= '_' . $value;
  1690. }
  1691. }
  1692. /* Get the cache hash, restrict by 10 symbols */
  1693. $cache_file = substr(md5($scripts_string . $datestring . $optstring), 0, 10);
  1694. /* use general file if it has been defined */
  1695. } else {
  1696. $cache_file = $options['file'];
  1697. }
  1698. $cache_file = urlencode($cache_file . $this->ua_mod);
  1699. $physical_file = $options['cachedir'] . $cache_file . "." . $options['ext'];
  1700. if (empty($this->options['cache_version'])) {
  1701. if (@is_file($physical_file)) {
  1702. $timestamp = @filemtime($physical_file);
  1703. } else {
  1704. $timestamp = 0;
  1705. }
  1706. } else {
  1707. $timestamp = $this->options['cache_version'];
  1708. }
  1709. /* add BackgroundImageCache for IE6 to prevent CSS Sprites blinking */
  1710. if (in_array($this->ua_mod, $this->ies) && !empty($options['css_sprites'])) {
  1711. $source = $this->include_bundle($source, '<script type="text/javascript">//<![CDATA[' . "\n" .
  1712. 'try{document.execCommand("BackgroundImageCache",false,true)}catch(e){}' . "\n//]]></script>", $handlers, $cachedir, 1);
  1713. }
  1714. /* Check if the cache file exists */
  1715. if ($timestamp) {
  1716. /* Put in locations and remove certain scripts */
  1717. if (!is_array($external_array)) {
  1718. $external_array = array($external_array);
  1719. }
  1720. $source = $this->_remove_scripts($external_array, $source,
  1721. $options['header'] != 'css' ? $options['header'] == 'javascript' && !$options['external_scripts_head_end'] ? 1 : 0 : 2);
  1722. /* Create the link to the new file with data:URI / mhtml */
  1723. if (!empty($options['data_uris_separate']) && (!empty($this->options['cache_version']) || @is_file($physical_file . '.' . $options['ext']))) {
  1724. $newfile = $this->get_new_file($options, $cache_file, $timestamp, '.' . $options['ext']);
  1725. /* raw include right after the main CSS file */
  1726. if (empty($options['data_uris_domloaded'])) {
  1727. $source = $this->include_bundle($source, $newfile, $handlers, $cachedir_relative, 0);
  1728. /* include via JS loader to provide fast flush of content */
  1729. } else {
  1730. $source = $this->include_bundle($source, $newfile, $handlers, $cachedir_relative, 4, $this->get_new_file_name($options, $cache_file, $timestamp, '.' . $options['ext']));
  1731. }
  1732. /* include spot for HTML Sprites */
  1733. } elseif (!empty($options['data_uris_domloaded']) && !empty($this->options['page']['sprites'])) {
  1734. $source = $this->include_bundle($source, '', $handlers, $cachedir_relative, 4, '');
  1735. }
  1736. $newfile = $this->get_new_file($options, $cache_file, $timestamp);
  1737. $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)));
  1738. /* fix for some JS libraries to load resrouces dynamically */
  1739. if (!empty($this->shadowbox_base) &&
  1740. $options['header'] == 'javascript' &&
  1741. !$options['inline_scripts']) {
  1742. $source = str_replace('Shadowbox.init(', 'Shadowbox.path="' .
  1743. $this->shadowbox_base . '";Shadowbox.init(', $source);
  1744. }
  1745. return $source;
  1746. }
  1747. $timestamp = $this->time;
  1748. $external_file = 'http' . $this->https . '://' .
  1749. (empty($options['host']) ? $_SERVER['HTTP_HOST'] :
  1750. (empty($options['https']) || !$this->https ?
  1751. $options['host'] :
  1752. $options['https'])) .
  1753. $this->get_new_file_name($options, $cache_file, $timestamp);
  1754. foreach ($this->libraries as $klass => $library) {
  1755. if (!class_exists($klass, false)) {
  1756. require_once($options['installdir'] . 'libs/php/' . $library);
  1757. }
  1758. }
  1759. /* If the file didn't exist, continue. Get files' content */
  1760. if (!empty($options['dont_check_file_mtime'])) {
  1761. $this->get_script_content($options['tag']);
  1762. /* Replace existing array with the new content */
  1763. $external_array = array();
  1764. foreach ($this->initial_files as $key => $file) {
  1765. if ($file['tag'] == $options['tag']) {
  1766. $external_array[] = $file;
  1767. }
  1768. }
  1769. }
  1770. /* Delete old files from cache */
  1771. if (!empty($this->options['days_to_delete'])) {
  1772. $dir = @getcwd();
  1773. @chdir($options['cachedir']);
  1774. $files = glob('*.' . $options['ext']);
  1775. if (!empty($files)) {
  1776. foreach ($files as $file) {
  1777. if (!in_array($file, array('index.php', 'wo.static.php', 'wo.gzip.php', 'yass.loader.js', 'webo-site-speedup.php')) &&
  1778. $this->time - filemtime($file) >
  1779. $this->options['days_to_delete'] * 86400) {
  1780. @unlink($file);
  1781. }
  1782. }
  1783. }
  1784. $files = glob('*.' . $options['ext'] . '.gz');
  1785. if (!empty($files)) {
  1786. foreach ($files as $file) {
  1787. if ($this->time - filemtime($file) >
  1788. $this->options['days_to_delete'] * 86400) {
  1789. @unlink($file);
  1790. }
  1791. }
  1792. }
  1793. $files = glob('*.png');
  1794. if (!empty($files)) {
  1795. foreach ($files as $file) {
  1796. if (!in_array($file, array('webo-site-speedup88.png', 'webo-site-speedup125.png', 'webo-site-speedup161.png', 'webo-site-speedup250.png')) &&
  1797. $this->time - filemtime($file) >
  1798. $this->options['days_to_delete'] * 86400) {
  1799. @unlink($file);
  1800. }
  1801. }
  1802. }
  1803. $files = glob('*.gif');
  1804. if (!empty($files)) {
  1805. foreach ($files as $file) {
  1806. if ($file != '0.gif' && $this->time - filemtime($file) >
  1807. $this->options['days_to_delete'] * 86400) {
  1808. @unlink($file);
  1809. }
  1810. }
  1811. }
  1812. $files = glob('*.jpg');
  1813. if (!empty($files)) {
  1814. foreach ($files as $file) {
  1815. if ($this->time - filemtime($file) >
  1816. $this->options['days_to_delete'] * 86400) {
  1817. @unlink($file);
  1818. }
  1819. }
  1820. }
  1821. @chdir($dir);
  1822. }
  1823. /* Create file */
  1824. $contents = "";
  1825. if (is_array($external_array)) {
  1826. /* can't simply merge&minify if we need to exclude some files */
  1827. if (empty($options['minify_exclude']) || empty($options['minify'])) {
  1828. foreach($external_array as $key => $info) {
  1829. /* Get the code */
  1830. if ($file_contents = $info['content']) {
  1831. if (!empty($options['minify_try'])) {
  1832. $contents .= 'try{';
  1833. }
  1834. $contents .= $file_contents .
  1835. ($options['header'] == 'javascript' && !empty($external_array[$key+1]) ? "\n" : '');
  1836. if (!empty($options['minify_try'])) {
  1837. $contents .= '}catch(e){';
  1838. if (!empty($info['file'])) {
  1839. $contents .= 'document.write("' .
  1840. str_replace(array('"', "\n", "\r"), array('\"', ' ', ''), $info['source']) .
  1841. '")';
  1842. }
  1843. $contents .= '}';
  1844. }
  1845. }
  1846. }
  1847. }
  1848. if ($options['tag'] === 'link' && !empty($options['include_code'])) {
  1849. $contents .= str_replace("<br/>", "", $options['include_code']);
  1850. }
  1851. $source = $this->_remove_scripts($external_array, $source,
  1852. $options['header'] != 'css' ? $options['header'] == 'javascript' && !$options['external_scripts_head_end'] ? 1 : 0 : 2);
  1853. /* add spot for HTML Sprites ? */
  1854. $addhtml = 1;
  1855. if ($options['css_sprites'] || ($options['data_uris'] && !in_array($this->ua_mod, $this->ies)) || ($options['mhtml'] && in_array($this->ua_mod, $this->ies)) || $options['parallel']) {
  1856. $options['css_sprites_partly'] = 0;
  1857. $remembered_data_uri = $options['data_uris'];
  1858. $remembered_mhtml = $options['mhtml'];
  1859. $options['data_uris'] = $options['mhtml'] = 0;
  1860. /* start new PHP process to create CSS Sprites */
  1861. if (!empty($this->web_optimizer_stage) && !(($this->web_optimizer_stage - 13)%15) && $this->web_optimizer_stage < 85) {
  1862. /* skip 2 minor stages if we not in the first major stage */
  1863. $delta = $this->web_optimizer_stage > 20 ? 6 : 0;
  1864. @header('Location: ' . $this->chained_redirect .
  1865. '?web_optimizer_stage=' .
  1866. ($this->web_optimizer_stage + $delta) .
  1867. '&username=' .
  1868. $this->username .
  1869. '&password=' .
  1870. $this->password .
  1871. '&auto_rewrite=' .
  1872. $this->auto_rewrite .
  1873. '&cache_version=' .
  1874. $this->cache_version .
  1875. '&web_optimizer_debug=1');
  1876. $this->di();
  1877. /* prepare first 4 Sprites */
  1878. } elseif (!empty($this->web_optimizer_stage) && !(($this->web_optimizer_stage - 16)%15) && $this->web_optimizer_stage < 85) {
  1879. $options['css_sprites_partly'] = 1;
  1880. $this->convert_css_sprites($contents, $options, $external_file);
  1881. @header('Location: ' . $this->chained_redirect .
  1882. '?web_optimizer_stage=' .
  1883. $this->web_optimizer_stage .
  1884. '&username=' .
  1885. $this->username .
  1886. '&password=' .
  1887. $this->password .
  1888. '&auto_rewrite=' .
  1889. $this->auto_rewrite .
  1890. '&cache_version=' .
  1891. $this->cache_version .
  1892. '&web_optimizer_debug=1');
  1893. $this->di();
  1894. } elseif (!empty($this->web_optimizer_stage) && !(($this->web_optimizer_stage - 19)%15) && $this->web_optimizer_stage < 85) {
  1895. /* Create CSS Sprites in CSS dir */
  1896. $this->convert_css_sprites($contents, $options, $external_file);
  1897. /* start new PHP process to create data:URI */
  1898. @header('Location: ' . $this->chained_redirect .
  1899. '?web_optimizer_stage=' .
  1900. ($this->web_optimizer_stage + 1) .
  1901. '&username=' .
  1902. $this->username .
  1903. '&password=' .
  1904. $this->password .
  1905. '&auto_rewrite=' .
  1906. $this->auto_rewrite .
  1907. '&cache_version=' .
  1908. $this->cache_version .
  1909. '&web_optimizer_debug=1');
  1910. $this->di();
  1911. } else {
  1912. /* we created all Sprites -- ready for data:URI + mhtml */
  1913. $options['data_uris'] = $remembered_data_uri;
  1914. $options['mhtml'] = $remembered_mhtml;
  1915. /* create correct resource file name for data:URI / mhtml inclusion */
  1916. $resource_file = $external_file;
  1917. if (!empty($options['data_uris_separate'])) {
  1918. $resource_file = $this->get_new_file_name($options, $cache_file, $timestamp, '.' . $options['ext']);
  1919. }
  1920. if (!empty($options['minify_with']) && $options['minify_with'] == 'tidy') {
  1921. $minified_content_array = $this->convert_css_sprites($contents, $options, $resource_file);
  1922. } else {
  1923. /* need to remove comments before */
  1924. $contents = $this->minify_text($contents);
  1925. $minified_content_array = $this->convert_data_uri($contents, $options, $resource_file);
  1926. }
  1927. $minified_content = $minified_content_array[0];
  1928. $minified_resource = $minified_content_array[1];
  1929. /* write data:URI / mhtml content */
  1930. if (!empty($minified_resource) && !empty($options['data_uris_separate'])) {
  1931. /* Allow for gzipping and headers */
  1932. if (($options['gzip'] || $options['far_future_expires']) && !empty($minified_resource)) {
  1933. $minified_resource = $this->gzip_header[$options['header']] . $minified_resource;
  1934. }
  1935. $this->write_file($physical_file . '.' . $options['ext'], $minified_resource, in_array($options['ext'], array('css', 'js')), 'text/css');
  1936. /* create static gzipped versions for static gzip in nginx, Apache */
  1937. if ($options['ext'] == 'css') {
  1938. $c = @gzencode($minified_resource, $options['gzip_level'], FORCE_GZIP);
  1939. if (!empty($c)) {
  1940. $this->write_file($physical_file . '.' . $options['ext']. '.gz', $c);
  1941. }
  1942. }
  1943. $newfile = $this->get_new_file($options, $cache_file, $this->time, '.' . $options['ext']);
  1944. /* raw include right after the main CSS file */
  1945. if (empty($options['data_uris_domloaded'])) {
  1946. $source = $this->include_bundle($source, $newfile, $handlers, $cachedir_relative, 0);
  1947. /* include via JS loader to provide fast flush of content */
  1948. } else {
  1949. $addhtml = 0;
  1950. $source = $this->include_bundle($source, $newfile, $handlers, $cachedir_relative, 4, $this->get_new_file_name($options, $cache_file, $this->time, '.' . $options['ext']));
  1951. }
  1952. } elseif (!empty($minified_content)) {
  1953. $minified_content .= $minified_resource;
  1954. }
  1955. }
  1956. if (!empty($minified_content)) {
  1957. $contents = $minified_content;
  1958. }
  1959. }
  1960. /* include spot for HTML Sprites */
  1961. if ($addhtml && !empty($options['data_uris_domloaded']) && !empty($this->options['page']['sprites'])) {
  1962. $source = $this->include_bundle($source, '', $handlers, $cachedir_relative, 4, '');
  1963. }
  1964. }
  1965. if (!empty($contents)) {
  1966. /* Allow for minification of javascript */
  1967. if ($options['header'] == "javascript" && $options['minify']) {
  1968. $contents = $this->minify_javascript($contents, $options);
  1969. }
  1970. /* we need to exclude some files from minify */
  1971. } elseif(!empty($options['minify_exclude']) && $options['minify']) {
  1972. $exclude_list = explode(" ", trim($options['minify_exclude']));
  1973. foreach($external_array as $key => $info) {
  1974. /* Get the code */
  1975. if ($file_contents = $info['content']) {
  1976. $content = '';
  1977. if (!empty($options['minify_try'])) {
  1978. $content .= 'try{';
  1979. }
  1980. $content .= $file_contents;
  1981. if (!empty($options['minify_try'])) {
  1982. $content .= '}catch(e){';
  1983. if (!empty($info['file'])) {
  1984. $content .= 'document.write("' .
  1985. str_replace(array('<', '"', "\n", "\r"), array('\x3c', '\"', ' ', ''), $info['source']) .
  1986. '")';
  1987. }
  1988. $content .= '}';
  1989. }
  1990. if ($options['header'] == "javascript" &&
  1991. !in_array(preg_replace("@.*/@", "", $info['file']), $exclude_list)) {
  1992. $content = $this->minify_javascript($content, $options);
  1993. }
  1994. $contents .= $content .
  1995. ($options['header'] == 'javascript' && !empty($external_array[$key+1]) ? "\n" : '');
  1996. }
  1997. }
  1998. }
  1999. if (!empty($contents)) {
  2000. /* fix for some JS libraries to load resrouces dynamically */
  2001. if (!empty($this->shadowbox_base) &&
  2002. $options['header'] == 'javascript' &&
  2003. $options['inline_scripts']) {
  2004. $contents = str_replace('Shadowbox.init(', 'Shadowbox.path="' .
  2005. $this->shadowbox_base . '";Shadowbox.init(', $contents);
  2006. }
  2007. /* remove HTML comments fro CSS code */
  2008. if ($options['header'] == 'css') {
  2009. $contents = str_replace(array('<!--', '-->'), '', $contents);
  2010. }
  2011. /* Allow for minification of CSS, CSS Sprites uses CSS Tidy -- already minified CSS */
  2012. if (!empty($options['minify']) &&
  2013. empty($options['css_sprites']) &&
  2014. /* data:URI with regular expressions must clean the comments before */
  2015. empty($options['data_uris']) &&
  2016. empty($options['mhtml'])) {
  2017. if ($options['minify_with'] == 'basic') {
  2018. /* Minify CSS */
  2019. $contents = $this->minify_text($contents);
  2020. } elseif ($options['minify_with'] == 'cssmin') {
  2021. try {
  2022. $this->cssmin = new CSSmin();
  2023. $minified_contents = $this->cssmin->run($contents);
  2024. } catch (Exception $e) {}
  2025. if (!empty($minified_contents)) {
  2026. $contents = $minified_contents;
  2027. } else {
  2028. $contents = $this->minify_text($contents);
  2029. }
  2030. }
  2031. }
  2032. /* Change absolute paths for distributed URLs */
  2033. if ($options['host'] && $options['header'] == 'css') {
  2034. $host = empty($options['https']) || !$this->https ? $options['host'] : $options['https'];
  2035. if (strpos($host, "/") !== false) {
  2036. $folder = preg_replace("@^[^/]+/(.*)/?$@is", "$1/", $host);
  2037. $contents = preg_replace("@(url\(\s*['\"]?)/@is", "$1/" . $folder, $contents);
  2038. }
  2039. }
  2040. if (!empty($contents) && (!empty($this->ua_mod) || $this->options['page']['uniform_cache']) && $options['header'] == 'css') {
  2041. $selectors = explode("}", $contents);
  2042. if (count($selectors) > 4096) {
  2043. $contents_import = implode("}", array_slice($selectors, 4096));
  2044. $contents = implode("}", array_slice($selectors, 0, 4095)) .
  2045. '}@import (' . str_replace("." . $options['ext'], '-import.' . $options['ext'], $cache_file) . ');';
  2046. $physical_file_import = str_replace("." . $options['ext'], '-import.' . $options['ext'], $physical_file);
  2047. }
  2048. }
  2049. /* Allow for gzipping and headers */
  2050. if ($options['gzip'] || $options['far_future_expires']) {
  2051. $contents = $this->gzip_header[$options['header']] .
  2052. /* fix <?xml includes into JS code not to break PHP file */
  2053. (in_array($options['ext'], array('css', 'js')) || $options['header'] == 'css' ? $contents : str_replace("<?", "\\x3C?", $contents));
  2054. if (!empty($contents_import)) {
  2055. $contents_import = $this->gzip_header[$options['header']] . $contents_import;
  2056. }
  2057. }
  2058. if (!empty($contents)) {
  2059. /* write first 4096 selectors for IE */
  2060. if (!empty($contents_import)) {
  2061. /* Write to cache and display */
  2062. $this->write_file($physical_file_import, $contents_import, in_array($options['ext'], array('css', 'js')), 'text/css');
  2063. if ($options['ext'] == 'css') {
  2064. $c = @gzencode($contents_import, $options['gzip_level'], FORCE_GZIP);
  2065. if (!empty($c)) {
  2066. $this->write_file($physical_file_import . '.gz', $c);
  2067. }
  2068. }
  2069. }
  2070. /* create static gzipped versions for static gzip in nginx, Apache */
  2071. if ($options['ext'] == 'css' || $options['ext'] == 'js') {
  2072. $c = @gzencode($contents, $options['gzip_level'], FORCE_GZIP);
  2073. if (!empty($c)) {
  2074. $this->write_file($physical_file . '.gz', $c);
  2075. }
  2076. }
  2077. /* Write to cache and display */
  2078. $this->write_file($physical_file, $contents, in_array($options['ext'], array('css', 'js')), $options['ext'] == 'js' ? 'application/javascript' : 'text/css');
  2079. /* create static gzipped versions for static gzip in nginx, Apache */
  2080. if ($options['ext'] == 'css' || $options['ext'] == 'js') {
  2081. $c = @gzencode($contents, $options['gzip_level'], FORCE_GZIP);
  2082. if (!empty($c)) {
  2083. $this->write_file($physical_file . '.gz', $c);
  2084. }
  2085. }
  2086. /* Create the link to the new file */
  2087. $newfile = $this->get_new_file($options, $cache_file, $this->time);
  2088. $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)));
  2089. }
  2090. }
  2091. return $source;
  2092. }
  2093. /**
  2094. * Minifies JavaScript code according to current options
  2095. *
  2096. */
  2097. function minify_javascript ($code, $options) {
  2098. $minified_code = '';
  2099. if ($options['minify_with'] == 'google') {
  2100. try {
  2101. $this->googlecompiler = new GoogleCompiler($options['cachedir'], $options['installdir']);
  2102. $minified_code = $this->googlecompiler->compress($code);
  2103. } catch (Exception $e) {}
  2104. } elseif ($options['minify_with'] == 'packer') {
  2105. try {
  2106. $this->packer = new JavaScriptPacker($code, 'Normal', false, false);
  2107. $minified_code = $this->packer->pack();
  2108. } catch (Exception $e) {}
  2109. } elseif ($options['minify_with'] == 'yui' ) {
  2110. try {
  2111. $this->yuicompressor = new YuiCompressor($options['cachedir'], $options['installdir'], $this->charset);
  2112. $minified_code = $this->yuicompressor->compress($code);
  2113. } catch (Exception $e) {}
  2114. }
  2115. if ($options['minify_with'] == 'jsmin' ||
  2116. (!empty($options['minify_with']) &&
  2117. empty($minified_code))) {
  2118. try {
  2119. $this->jsmin = new JSMin($code);
  2120. $minified_code = $this->jsmin->minify($code);
  2121. } catch (Exception $e) {}
  2122. }
  2123. if (empty($options['minify_with']) || empty($minified_code)) {
  2124. /* Remove comments //, can't handle easily in-string case */
  2125. $minified_code = preg_replace("!^//.*?\r?\n!is", "\n", $code);
  2126. /* Remove comments /**, leave conditional compilation */
  2127. $minified_code = preg_replace("!(^|[\r\n\t\s]+)/\*[^@].*?\*/!is", "", $minified_code);
  2128. }
  2129. if (!empty($minified_code)) {
  2130. $code = preg_replace("!^[\r\n\t\s]*!s", "", $minified_code);
  2131. }
  2132. return $code;
  2133. }
  2134. /**
  2135. * Replaces scripts calls or css links in the source with a marker
  2136. *
  2137. */
  2138. function _remove_scripts ($external_array, $source, $mark = false) {
  2139. $replacement = $mark ? $mark > 1 ? '@@@WSSSTYLES@@@' : '@@@WSSSCRIPT@@@' : '';
  2140. if (is_array($external_array)) {
  2141. foreach ($external_array as $key => $value) {
  2142. /* Remove script, replace the first one with the mark to insert merged script */
  2143. $source = str_replace($value['source'], $replacement, $source);
  2144. if ($mark) {
  2145. $replacement = '';
  2146. }
  2147. }
  2148. }
  2149. return $source;
  2150. }
  2151. /**
  2152. * Returns the HTML code for our new compressed file
  2153. *
  2154. **/
  2155. function get_new_file ($options, $cache_file, $timestamp = false, $add = false) {
  2156. $nofollow = empty($this->ua_mod) && $options['ext'] == 'php' && !$this->options['uniform_cache'];
  2157. $newfile = '<' . $options['tag'] .
  2158. ' type="' . $options['type'] . '" ' .
  2159. $options['src'] . '="' . $this->get_new_file_name($options, $cache_file, $timestamp, $add) . '"'.
  2160. /* IE7- don't understand stylesheet nofollow in rel */
  2161. ($nofollow || !empty($options['rel']) ? ' rel="' .
  2162. (empty($options['rel']) ? '' : $options['rel']) .
  2163. (!empty($options['rel']) && $nofollow ? ' ' : '') .
  2164. ($nofollow ? 'nofollow' : '') . '"' : '') .
  2165. (empty($options['self_close']) ? '></' . $options['tag'] . '>' : (empty($this->xhtml) ? '>' : '/>'));
  2166. return $newfile;
  2167. }
  2168. /**
  2169. * Returns the filename for our new compressed file
  2170. *
  2171. **/
  2172. function get_new_file_name ($options, $cache_file, $timestamp = false, $add = false) {
  2173. $timestamp = $options['far_future_expires_php'] ? $timestamp : false;
  2174. return (empty($options['host']) ? '' :
  2175. ($this->options['page']['cache'] && !$this->options['page']['https_separate'] ? '//' : 'http' . $this->https . '://') .
  2176. $options['host']) .
  2177. $options['cachedir_relative'] .
  2178. $cache_file .
  2179. ($add ? '.' . $options['ext'] : '') .
  2180. ($timestamp && $options['far_future_expires_rewrite'] ? '.wo' . $timestamp : '') .
  2181. ($add ? $add : '.' . $options['ext']) .
  2182. ($timestamp && !$options['far_future_expires_rewrite'] ? '?' . $timestamp : '');
  2183. }
  2184. /**
  2185. * Returns the last modified dates of the files being compressed
  2186. * In this way we can see if any changes have been made
  2187. **/
  2188. function get_file_dates ($files, $options) {
  2189. /* option added by janvarev */
  2190. if (!empty($options['dont_check_file_mtime']) && strlen($this->lc) == 29) {
  2191. return;
  2192. }
  2193. $dates = false;
  2194. if (!empty($files) && is_array($files)) {
  2195. foreach($files AS $key => $value) {
  2196. if (!empty($value['file'])) {
  2197. $value['file'] = $this->get_file_name($value['file']);
  2198. if (@file_exists($value['file'])) {
  2199. $thedate = filemtime($value['file']);
  2200. $dates[] = $thedate;
  2201. }
  2202. }
  2203. }
  2204. }
  2205. if (is_array($dates)) {
  2206. return implode(".", $dates);
  2207. }
  2208. return;
  2209. }
  2210. /**
  2211. * Get full pathname for the given file
  2212. *
  2213. **/
  2214. function get_file_name ($file) {
  2215. if (is_array($file) && count($file)>0) {
  2216. $file = $file[0];
  2217. }
  2218. $file = $this->strip_querystring(preg_replace("@^https?://(www\.)?" . $this->host_escaped . "/+@", "/", $file));
  2219. if (substr($file, 0, 1) == "/") {
  2220. return $this->view->prevent_trailing_slash($this->options['document_root']) . $file;
  2221. } else {
  2222. return $this->view->paths['full']['current_directory'] . $file;
  2223. }
  2224. }
  2225. /**
  2226. * Resursively resolve all @import in CSS files and get files' content
  2227. * The second param marks inline styles case
  2228. *
  2229. **/
  2230. function resolve_css_imports ($src, $inline = false, $previous = false) {
  2231. $content = false;
  2232. $file = '';
  2233. if (!$inline && $src) {
  2234. $file = $this->get_file_name($src);
  2235. /* dynamic file */
  2236. if (!preg_match("!\.css$!is", $file) || strpos($file, 'index.php') !== false) {
  2237. $dynamic_file = $src;
  2238. /* touch only non-external scripts */
  2239. if (!strpos($dynamic_file, "://") && strpos($dynamic_file, '//') !== 0) {
  2240. $dynamic_file = "http://" . $_SERVER['HTTP_HOST'] . $this->convert_path_to_absolute($file, array('file' => $_SERVER['REQUEST_URI']), true);
  2241. }
  2242. $file = $this->options['css']['cachedir'] . $this->get_remote_file($this->resolve_amps($dynamic_file), 'link');
  2243. }
  2244. if (@is_file($file)) {
  2245. $content = $this->file_get_contents($file);
  2246. }
  2247. } else {
  2248. $content = $src;
  2249. }
  2250. $content = $this->resolve_amps($content);
  2251. if (@is_file($file) || $inline) {
  2252. /* remove commented @import. First of all glue CSS files, optimiza only secondly */
  2253. $content = preg_replace("!/\*\s*@import.*?\*/!is", "", $content);
  2254. /* new RegExp from xandrx */
  2255. preg_match_all('!@import\s*(url)?\s*\(?([^;]+?)\)?;!i', $content, $imports, PREG_SET_ORDER);
  2256. if (is_array($imports)) {
  2257. foreach ($imports as $import) {
  2258. $src = false;
  2259. if (isset($import[2])) {
  2260. $src = $import[2];
  2261. $src = trim($src, '\'" ');
  2262. }
  2263. $remote = '';
  2264. if (strpos($src, "//") !== false && !preg_match('@//(www\.)?' . $this->host_escaped . '/@', $src)) {
  2265. $remote = $src;
  2266. $src = $this->get_remote_file($src);
  2267. }
  2268. if ($src) {
  2269. $saved_directory = $this->view->paths['full']['current_directory'];
  2270. $this->view->paths['full']['current_directory'] = preg_replace("/[^\/]+$/", "", $file);
  2271. /* start recursion */
  2272. if (!$previous || $remote != $previous) {
  2273. $content = str_replace($import[0], $this->convert_paths_to_absolute($this->resolve_css_imports($src, 0, $remote), array('file' => str_replace($this->options['document_root'], "/", $this->get_file_name($src))), 0, 1), $content);
  2274. }
  2275. /* return remembed directory */
  2276. $this->view->paths['full']['current_directory'] = $saved_directory;
  2277. }
  2278. }
  2279. }
  2280. }
  2281. return $content;
  2282. }
  2283. /**
  2284. * Gets an array of scripts/css files to be processed
  2285. *
  2286. **/
  2287. function get_script_array () {
  2288. /* get head with all content */
  2289. $this->get_head();
  2290. $curl = function_exists('curl_init');
  2291. /* strange thing: array is filled even if string is empty */
  2292. $excluded_scripts_css = explode(" ", $this->options['css']['external_scripts_exclude']);
  2293. $excluded_scripts_js = explode(" ", $this->options['javascript']['external_scripts_exclude']);
  2294. if ($this->options['javascript']['minify'] || $this->options['javascript']['gzip'] || $this->options['page']['parallel_javascript'] || $this->options['javascript']['rocket'] || $this->options['javascript']['reorder'] || $this->options['javascript']['external_scripts']) {
  2295. if (empty($this->options['javascript']['minify_body']) || empty($this->options['javascript']['inline_scripts_body'])) {
  2296. $toparse = $this->head;
  2297. } else {
  2298. $toparse = $this->body;
  2299. }
  2300. /* find all scripts from head */
  2301. $regex = "!(<script[^>]*>)(.*?</script>)!is";
  2302. preg_match_all($regex, $toparse, $matches, PREG_SET_ORDER);
  2303. $i = 0;
  2304. if (!empty($matches)) {
  2305. foreach($matches as $match) {
  2306. $file = array(
  2307. 'tag' => 'script',
  2308. 'source' => $match[0],
  2309. 'content' => preg_replace("@(<script[^>]*>|</script>)@is", "", $match[0])
  2310. );
  2311. if ($this->options['css']['reorder']) {
  2312. $file['position'] = strpos($toparse, $file['source']);
  2313. }
  2314. preg_match_all("@src\s*=\s*(?:\"([^\"]+)\"|'([^']+)'|([\S]+))@is", $match[1], $variants, PREG_SET_ORDER);
  2315. if (is_array($variants)) {
  2316. foreach ($variants as $variant_type) {
  2317. $variant_type[1] = ($variant_type[1] === '') ? (($variant_type[2] === '') ? str_replace('>', '', $variant_type[3]) : $variant_type[2]) : $variant_type[1];
  2318. $file['file'] = $this->convert_basehref(trim($this->strip_querystring($variant_type[1])));
  2319. $file['file_raw'] = $variant_type[1];
  2320. }
  2321. }
  2322. /* skip external files if option is disabled */
  2323. if (($this->options['javascript']['external_scripts'] && $curl) ||
  2324. (!empty($file['file']) && preg_match("@(index\.php/|\.js$)@i", $file['file']) &&
  2325. (!strpos($file['file'], '//') ||
  2326. preg_match("@//(www\.)?" . $this->host_escaped . "/@is", $file['file']))) ||
  2327. (empty($file['file']) &&
  2328. $this->options['javascript']['inline_scripts'])) {
  2329. /* filter scripts through include mask */
  2330. if (!isset($this->options['javascript']['external_scripts_mask']) ||
  2331. !isset($this->options['javascript']['external_scripts_mask']{$i}) ||
  2332. $this->options['javascript']['external_scripts_mask']{$i} == 'x') {
  2333. $this->initial_files[] = $file;
  2334. }
  2335. if (isset($this->options['javascript']['external_scripts_mask']) &&
  2336. (empty($file['file']) || !in_array(preg_replace("@.*/@", "", $file['file']), $excluded_scripts_js))) {
  2337. $i++;
  2338. }
  2339. /* fix shadowbox loader */
  2340. if (!empty($file['file']) && strpos($file['file'], 'shadowbox.js')) {
  2341. $this->shadowbox_base_raw = preg_replace("@^(https?://" .
  2342. $this->host_escaped . ")?/?(.*/)[^/]+$@", "$2", $file['file']);
  2343. $this->shadowbox_base = (empty($this->options['javascript']['host']) ?
  2344. '' : '//' . $this->options['javascript']['host']) .
  2345. '/' . $this->shadowbox_base_raw;
  2346. }
  2347. /* fix scriptaculous loader */
  2348. if (!empty($file['file']) && ($acpos = strpos($variant_type[1], '?load='))) {
  2349. $scripts = explode(',', substr($variant_type[1], $acpos + 6));
  2350. $acbase = preg_replace("@/[^/]+$@", '/', $file['file']);
  2351. foreach ($scripts as $script) {
  2352. $acfile = array(
  2353. 'tag' => 'script',
  2354. 'source' => '',
  2355. 'content' => '',
  2356. 'file' => $acbase . $script . '.js'
  2357. );
  2358. $this->initial_files[] = $acfile;
  2359. }
  2360. }
  2361. /* fix niftycube loader */
  2362. if (!empty($file['file']) && strpos($file['file'], 'niftycube.js')) {
  2363. $niftycube_base = preg_replace("@^(https?://" .
  2364. $this->host_escaped . ")?/(.*/)[^/]+$@", "$2", $file['file']);
  2365. $source = '<link type="text/css" rel="stylesheet" href="' . $niftycube_base . 'niftyCorners.css"' . ($this->xhtml ? '/' : '') . '>';
  2366. $this->initial_files[] = array(
  2367. 'tag' => 'link',
  2368. 'source' => '<link type="text/css" rel="stylesheet" href="' .
  2369. $niftycube_base . 'niftyCorners.css">',
  2370. 'file' => $niftycube_base . 'niftyCorners.css',
  2371. 'file_raw' => $niftycube_base . 'niftyCorners.css'
  2372. );
  2373. $this->content = str_replace($file['source'], $source . $file['source'], $this->content);
  2374. }
  2375. }
  2376. }
  2377. }
  2378. /* additional cycle - due to complex logic of JS inline/files merging */
  2379. if ($this->options['javascript']['minify_body'] + $this->options['javascript']['inline_scripts_body'] == 1) {
  2380. /* find all scripts from body */
  2381. preg_match_all("!(<script[^>]*>)(.*?</script>)!is", str_replace($this->head, "", $this->body), $matches, PREG_SET_ORDER);
  2382. if (!empty($matches)) {
  2383. foreach($matches as $match) {
  2384. $file = array(
  2385. 'tag' => 'script',
  2386. 'source' => $match[0],
  2387. 'content' => preg_replace("@(<script[^>]*>|</script>)@is", "", $match[0])
  2388. );
  2389. if ($this->options['javascript']['reorder']) {
  2390. $file['position'] = strpos($toparse, $file['source']);
  2391. }
  2392. preg_match_all("@src\s*=\s*(?:\"([^\"]+)\"|'([^']+)'|([\S]+))@is", $match[1], $variants, PREG_SET_ORDER);
  2393. if (is_array($variants)) {
  2394. foreach ($variants as $variant_type) {
  2395. $variant_type[1] = ($variant_type[1] === '') ? (($variant_type[2] === '') ? str_replace('>', '', $variant_type[3]) : $variant_type[2]) : $variant_type[1];
  2396. $file['file'] = $this->convert_basehref(trim($this->strip_querystring($variant_type[1])));
  2397. $file['file_raw'] = $variant_type[1];
  2398. }
  2399. }
  2400. /* skip external files if option is disabled */
  2401. if ((empty($file['file']) &&
  2402. $this->options['javascript']['inline_scripts_body']) ||
  2403. (!empty($file['file']) &&
  2404. $this->options['javascript']['minify_body'] && ($curl ||
  2405. (preg_match("@(index\.php/|\.js$)@i", $file['file']) &&
  2406. (!strpos($file['file'], '//') ||
  2407. preg_match("@//(www\.)?" . $this->host_escaped . "/@is", $file['file'])))))) {
  2408. /* filter scripts through include mask */
  2409. if (!isset($this->options['javascript']['external_scripts_mask']) ||
  2410. !isset($this->options['javascript']['external_scripts_mask']{$i}) ||
  2411. $this->options['javascript']['external_scripts_mask']{$i} == 'x') {
  2412. $this->initial_files[] = $file;
  2413. }
  2414. if (!empty($file['file']) &&
  2415. isset($this->options['javascript']['external_scripts_mask']) &&
  2416. !in_array(preg_replace("@.*/@", "", $file['file']), $excluded_scripts_js)) {
  2417. $i++;
  2418. }
  2419. }
  2420. }
  2421. }
  2422. }
  2423. }
  2424. if ($this->options['css']['minify'] || $this->options['css']['gzip'] || $this->options['page']['parallel_css'] || $this->options['css']['rocket'] || $this->options['css']['reorder']) {
  2425. if (empty($this->options['css']['minify_body'])) {
  2426. $toparse = $this->head;
  2427. } else {
  2428. $toparse = $this->body;
  2429. }
  2430. /* find all CSS links from head and inine styles */
  2431. if (empty($this->options['css']['inline_scripts']) && $this->options['page']['html_tidy'] && !strpos($toparse, '<style') && !strpos($toparse, '<STYLE')) {
  2432. $regex = "!(<link[^>]+rel\\s*=\\s*(\"stylesheet\"|'stylesheet'|stylesheet)[^>]*>(</link>)?)!is";
  2433. } else {
  2434. $regex = "!(<link[^>]+rel\\s*=\\s*(\"stylesheet\"|'stylesheet'|stylesheet)[^>]*>(</link>)?|<style[^>]*>.*?</style>)!is";
  2435. }
  2436. preg_match_all($regex, $toparse, $matches, PREG_SET_ORDER);
  2437. if (!empty($matches)) {
  2438. foreach($matches as $match) {
  2439. $file = array(
  2440. 'tag' => 'link',
  2441. 'source' => $match[0],
  2442. 'content' => preg_replace("@(<link[^>]+>|<style[^>]*>|<\/style>)@is", "", $match[0]),
  2443. );
  2444. if ($this->options['javascript']['reorder']) {
  2445. $file['position'] = strpos($toparse, $file['source']);
  2446. }
  2447. preg_match_all("@(media|href)\s*=\s*(?:\"([^\"]*)\"|'([^']*)'|([^\s>]*))@is", $match[0], $variants, PREG_SET_ORDER);
  2448. if (is_array($variants)) {
  2449. foreach($variants as $variant_type) {
  2450. $variant_type[1] = strtolower($variant_type[1]);
  2451. $variant_type[2] = empty($variant_type[2]) ? (empty($variant_type[3]) ? (empty($variant_type[4]) ? '' : $variant_type[4]) : $variant_type[3]) : $variant_type[2];
  2452. switch ($variant_type[1]) {
  2453. case "href":
  2454. $file['file'] = $this->convert_basehref(trim($this->strip_querystring($variant_type[2])));
  2455. $file['file_raw'] = $variant_type[2];
  2456. break;
  2457. default:
  2458. /* skip media="all|screen" to prevent Safari bug with @media all{} and @media screen{} */
  2459. if ($variant_type[1] != 'media' || ($variant_type[1] == 'media' && !preg_match("@^(all|screen|''|\"\")$@i", $variant_type[2]))) {
  2460. $file[$variant_type[1]] = $variant_type[2];
  2461. }
  2462. break;
  2463. }
  2464. }
  2465. }
  2466. /* skip external files if option is disabled */
  2467. if (($this->options['css']['external_scripts'] && $curl) ||
  2468. (!empty($file['file']) && preg_match("@(index\.php/|\.css$)@i", $file['file']) &&
  2469. (!strpos($file['file'], '//') ||
  2470. preg_match("@//(www\.)?" . $this->host_escaped . "/@is", $file['file']))) ||
  2471. (empty($file['file']) && $this->options['css']['inline_scripts'])) {
  2472. $this->initial_files[] = $file;
  2473. }
  2474. }
  2475. }
  2476. /* add mark for HTML sprites styles */
  2477. } elseif ($this->options['page']['sprites']) {
  2478. $this->content = preg_replace("!(<head[^>]*>)!is", "$1@@@WSSSTYLES@@@", $this->content);
  2479. }
  2480. if (is_array($this->initial_files)) {
  2481. /* remove duplicates of styles/scripts from head */
  2482. if ($this->options['javascript']['remove_duplicates'] && !$this->options['javascript']['minify_body']) {
  2483. $e = count($this->initial_files);
  2484. $to_remove = array();
  2485. for ($i=0; $i<$e-2; $i++) {
  2486. if (!empty($this->initial_files[$i])) {
  2487. $value = $this->initial_files[$i];
  2488. for ($j=$i+1; $j<$e; $j++) {
  2489. if (!empty($this->initial_files[$j])) {
  2490. $v = $this->initial_files[$j];
  2491. if ($value['source'] == $v['source']) {
  2492. unset($this->initial_files[$j]);
  2493. // $to_remove[] = "!" . preg_quote($v['source']) . "!is";
  2494. }
  2495. }
  2496. }
  2497. }
  2498. }
  2499. /* if (count($to_remove)) {
  2500. $this->content = preg_replace($to_remove, '', $this->content, 1);
  2501. }*/
  2502. }
  2503. /* get remote files */
  2504. foreach ($this->initial_files as $key => $value) {
  2505. if (!empty($value['file'])) {
  2506. $dynamic = !preg_match("/\.(css|js)$/is", $value['file']) || strpos($value['file'], 'index.php') !== false;
  2507. $external = strlen($value['file']) > 7 && (strpos($value['file'], "://") || strpos($value['file'], '//') === 0);
  2508. if ($dynamic || $external) {
  2509. /* exclude files from the same host */
  2510. if(!preg_match("@//(www\.)?". $this->host_escaped . "@s", $value['file'])) {
  2511. /* don't get actual files' content if option isn't enabled */
  2512. if ($this->options[$value['tag'] == 'script' ? 'javascript' : 'css']['external_scripts']) {
  2513. /* get an external file */
  2514. if ($dynamic) {
  2515. /* dynamic file */
  2516. $file = $this->get_remote_file(($external ? '' : 'http://' . $this->host) . $this->resolve_amps($value['file_raw']), $value['tag']);
  2517. /* static file */
  2518. } else {
  2519. $file = $this->get_remote_file($value['file'], $value['tag']);
  2520. }
  2521. if (!empty($file)) {
  2522. $value['file'] = $this->initial_files[$key]['file'] = $this->options['javascript']['cachedir_relative'] . $file;
  2523. } else {
  2524. unset($this->initial_files[$key]);
  2525. }
  2526. } else {
  2527. if (empty($value['content'])) {
  2528. unset($this->initial_files[$key]);
  2529. }
  2530. }
  2531. } else {
  2532. $value['file'] = preg_replace("!https?://(www\.)?".
  2533. $this->host_escaped . "/+!s", "/", $value['file']);
  2534. }
  2535. }
  2536. }
  2537. }
  2538. /* get files' content if Rocket */
  2539. if ($this->options['javascript']['rocket'] || $this->options['css']['rocket']) {
  2540. $this->get_script_content();
  2541. }
  2542. /* enable caching / gzipping proxy? */
  2543. $rewrite_css = ($this->options['css']['far_future_expires_static'] ||
  2544. $this->options['css']['gzip']);
  2545. $rewrite_js = ($this->options['javascript']['far_future_expires_static'] ||
  2546. $this->options['javascript']['gzip']);
  2547. $niftyUsed = 0;
  2548. $replace_from = $replace_to = $replace_position_type = $replace_position = array();
  2549. /* Remove empty sources and any externally linked files */
  2550. foreach ($this->initial_files as $key => $value) {
  2551. /* exclude niftyCorners duplicate */
  2552. if (!empty($value['file']) && strpos($value['file'], 'niftyCorners.css')) {
  2553. if ($niftyUsed) {
  2554. unset($this->initial_files[$key]);
  2555. } else {
  2556. $niftyUsed = 1;
  2557. }
  2558. }
  2559. $use_proxy = (!$this->options['javascript']['minify'] && $value['tag'] == 'script') ||
  2560. (!$this->options['css']['minify'] && $value['tag'] == 'link');
  2561. $replaces_set = 0;
  2562. /* but keep CSS/JS w/o src to merge into unobtrusive loader, also exclude files from ignore_list */
  2563. if (($value['tag'] == 'script' && ((empty($value['file']) &&
  2564. !$this->options['javascript']['inline_scripts']) ||
  2565. (!empty($excluded_scripts_js[0]) &&
  2566. !empty($value['file']) &&
  2567. (in_array(preg_replace("@.*/@", "", $value['file']), $excluded_scripts_js) ||
  2568. in_array($value['file'], $excluded_scripts_js))) ||
  2569. (!$this->options['javascript']['minify'] && $this->options['page']['parallel_javascript']))) ||
  2570. ($value['tag'] == 'link' && ((empty($value['file']) &&
  2571. !$this->options['css']['inline_scripts']) ||
  2572. (!empty($excluded_scripts_css[0]) &&
  2573. !empty($value['file']) &&
  2574. (in_array(preg_replace("@.*/@", "", $value['file']), $excluded_scripts_css) ||
  2575. in_array($value['file'], $excluded_scripts_css))) ||
  2576. (!$this->options['css']['minify'] && $this->options['page']['parallel_css']))) ||
  2577. /* skip optimization if WEBONOTOPTIMIZE flag is used */
  2578. (!empty($value['content']) && strpos($value['content'], 'WEBONOTOPTIMIZE') !== false && $this->premium > 1)) {
  2579. /* just skip them */
  2580. unset($this->initial_files[$key]);
  2581. $use_proxy = 1;
  2582. /* rewrite skipped file with CDN host */
  2583. if (!empty($value['file']) &&
  2584. (($value['tag'] == 'link' && $this->options['page']['parallel_css']) ||
  2585. ($value['tag'] == 'script' && $this->options['page']['parallel_javascript'])) &&
  2586. (preg_match("@//(www\.)?" . $this->host_escaped . "/+@", $value['file']) ||
  2587. (substr($value['file'], 0, 1) == '/' && substr($value['file'], 1, 1) != '/'))) {
  2588. $host = $value['tag'] == 'link' ?
  2589. $this->options['css']['host'] :
  2590. $this->options['javascript']['host'];
  2591. $new_src = (empty($host) ? "" : "//" . $host) .
  2592. preg_replace("@https?://(www\.)?" .
  2593. $this->host_escaped .
  2594. "/+@", "/", $value['file']);
  2595. $new_script = str_replace($value['file'],
  2596. $new_src, $value['file_raw']);
  2597. $this->content = str_replace($value['file_raw'],
  2598. $new_script, $this->content);
  2599. if ($this->options['javascript']['reorder']) {
  2600. $value['source'] = str_replace($value['file_raw'], $new_script, $value['source']);
  2601. $value['file_raw'] = $new_script;
  2602. }
  2603. $use_proxy = 0;
  2604. }
  2605. }
  2606. $proxy = $use_proxy &&
  2607. (($value['tag'] == 'link' && $rewrite_css) ||
  2608. ($value['tag'] == 'script' && $rewrite_js)) &&
  2609. !empty($value['file']) && !preg_match("!\.php$!", $value['file']);
  2610. $rewrite_to = empty($value['file_raw']) ? '' : $value['file_raw'];
  2611. if ($proxy && !empty($value['file'])) {
  2612. $value['file'] = preg_replace("@https?://(www\.)?" .
  2613. $this->host_escaped . "/+@", "/", $value['file']);
  2614. if ($f = $this->convert_path_to_absolute($value['file'], array('file' => $_SERVER['REQUEST_URI']))) {
  2615. $rewrite_to = '//' . $this->options['host'] . str_replace($value['file'],
  2616. $this->options['page']['cachedir_relative'] .
  2617. (($value['tag'] == 'link' && $this->options['css']['gzip']) ||
  2618. ($value['tag'] == 'script' && $this->options['javascript']['gzip']) ?
  2619. 'wo.gzip.php' : 'wo.static.php') .
  2620. '?' . $f, $value['file']);
  2621. }
  2622. }
  2623. /* rewrite skipped file with caching proxy, skip dynamic files */
  2624. if (empty($_COOKIE['WSS_ROCKET']) &&
  2625. (($value['tag'] == 'link' && $this->options['css']['rocket']) ||
  2626. ($value['tag'] == 'script' && $this->options['javascript']['rocket']))) {
  2627. $matched = 0;
  2628. $replace_from[] = $value['source'];
  2629. $replace_type[] = $value['tag'];
  2630. $matched = preg_match("!(mootools(\.js|-more|-core|_release|\.x|\.v|\.min)|[^\.-]jquery(-ui|.min|\.js|\.1|-1|\.v|\.core|\.pack)|prototype(\.min|\.js|\.rev))!is", $value['file_raw']);
  2631. if (empty($matched)) {
  2632. $replace_to[] = ($value['tag'] == 'link' ? '<style type="text/css"' .
  2633. (empty($value['media']) ? '' : ' media="' . $value['media'] . '"') . '>' : "<script type='text/javascript'>//<![CDATA[\n") .
  2634. str_replace(array('</', '<!--'), array('\x3C/', '\x3C!--'), $value['content']) .
  2635. ($value['tag'] == 'link' ? '</style>' : "\n//]]></script>");
  2636. if (!empty($value['file'])) {
  2637. $files_postload[] = (strpos($rewrite_to, '//') !== false ? '' : $this->options['host']) . $rewrite_to;
  2638. }
  2639. } elseif (!empty($value['file']) && $proxy) {
  2640. $replace_to[] = str_replace($value['file_raw'], $rewrite_to, $value['source']);
  2641. } else {
  2642. $replace_to[] = $value['source'];
  2643. }
  2644. $use_proxy = $proxy = 0;
  2645. $replaces_set = 1;
  2646. }
  2647. /* rewrite skipped file with caching proxy, skip dynamic files */
  2648. if ($proxy && !empty($value['file']) && (!preg_match("!wo[0-9a-f]+!", $value['file']) || $this->options['page']['far_future_expires_external'])) {
  2649. $replace_from[] = $value['source'];
  2650. $replace_to[] = str_replace($value['file_raw'], $rewrite_to, $value['source']);
  2651. $replaces_set = 1;
  2652. }
  2653. if ($this->options['javascript']['reorder']) {
  2654. if (!$replaces_set) {
  2655. $replace_from[] = $replace_to[] = $value['source'];
  2656. }
  2657. $i = $value['position'];
  2658. $i = ($i < 1000000 ? '0' : '') . ($i < 100000 ? '0' : '') . ($i < 10000 ? '0' : '') . ($i < 1000 ? '0' : '') . ($i < 100 ? '0' : '') . ($i < 10 ? '0' : '') . $i;
  2659. $j = count($replace_to) - 1;
  2660. $j = ($j < 1000 ? '0' : '') . ($j < 100 ? '0' : '') . ($j < 10 ? '0' : '') . $j;
  2661. $replace_position_type[] = ($value['tag'] == 'link' ? 'a' : 'b') . $i . $j;
  2662. $replace_position[] = $i . $j;
  2663. }
  2664. }
  2665. /* reorder scripts/styles */
  2666. if ($this->options['javascript']['reorder']) {
  2667. sort($replace_position_type);
  2668. $rto = array();
  2669. foreach ($replace_position_type as $value) {
  2670. $rto[] = $replace_to[round(substr($value, 8))];
  2671. }
  2672. $replace_to = $rto;
  2673. sort($replace_position);
  2674. $rfrom = array();
  2675. foreach ($replace_position as $value) {
  2676. $rfrom[] = $replace_from[round(substr($value, 7))];
  2677. }
  2678. $replace_from = $rfrom;
  2679. }
  2680. /* rewrite scripts and styles */
  2681. if (count($replace_from)) {
  2682. if ($this->options['javascript']['reorder']) {
  2683. /* remove duplicates */
  2684. foreach ($replace_from as $key => $value) {
  2685. if ($replace_to[$key] == $value) {
  2686. unset($replace_from[$key]);
  2687. unset($replace_to[$key]);
  2688. }
  2689. }
  2690. /* make safe replacement a->b, b->a */
  2691. foreach ($replace_from as $key => $value) {
  2692. $this->content = str_replace($value, '@@@WSS_REPLACEMENT' . $key . '@@@', $this->content);
  2693. }
  2694. foreach ($replace_to as $key => $value) {
  2695. $this->content = str_replace('@@@WSS_REPLACEMENT' . $key . '@@@', $value, $this->content);
  2696. }
  2697. } else {
  2698. $this->content = str_replace($replace_from, $replace_to, $this->content);
  2699. }
  2700. if (!empty($files_postload)) {
  2701. $this->options['page']['postload'] = (empty($this->options['page']['postload']) ? '' : ' ') . implode(" ", $files_postload);
  2702. }
  2703. }
  2704. /* skip mining files' content if don't check MTIME */
  2705. if (!$this->options['javascript']['dont_check_file_mtime'] && !$this->options['javascript']['rocket'] && !$this->options['css']['rocket']) {
  2706. $this->get_script_content();
  2707. }
  2708. }
  2709. }
  2710. /**
  2711. * Gets an content for array of scripts/css files
  2712. *
  2713. **/
  2714. function get_script_content ($tag = false) {
  2715. /* to get inline values */
  2716. $last_key = array();
  2717. /* to get inline values on empty non-inline */
  2718. $last_key_flushed = array();
  2719. $stored = array();
  2720. /* duplicates spots */
  2721. $duplicates = array(
  2722. /* jQuery */
  2723. array(
  2724. 'regexp' => 'jquery([v0-9\.\-\[\]])*(pack|min)?\.(js|php)(\.gz)?',
  2725. 'exists' => 0
  2726. ),
  2727. /* Prototype */
  2728. array(
  2729. 'regexp' => 'prototype([rev0-9\.\-_])*(packer|min|lite)?\.(js|php)(\.gz)?',
  2730. 'exists' => 0
  2731. ),
  2732. /* MooTools */
  2733. array(
  2734. 'regexp' => 'mootools(_release)?([xv0-9\.\-_])*(core-yc|core|yui-compressed|comp|min)?\.(js|php)(\.gz)?',
  2735. 'exists' => 0
  2736. ),
  2737. /* OpenAPI */
  2738. array(
  2739. 'regexp' => 'openapi.js?',
  2740. 'exists' => 0
  2741. ),
  2742. /* SWFobject */
  2743. array(
  2744. 'regexp' => 'swfobject.js',
  2745. 'exists' => 0
  2746. )
  2747. );
  2748. if (is_array($this->initial_files)) {
  2749. $rocket_file = $this->options['page']['cachedir'] . 'wo.content.php';
  2750. if (!is_file($rocket_file)) {
  2751. $this->write_file($rocket_file, '<?php ?>');
  2752. }
  2753. if ($this->options['css']['rocket'] || $this->options['javascript']['rocket']) {
  2754. @include($rocket_file);
  2755. }
  2756. $remove_list = array(
  2757. 'css' => explode(" ", $this->options['css']['remove_list']),
  2758. 'script' => explode(" ", $this->options['javascript']['remove_list']),
  2759. );
  2760. foreach($this->initial_files as $key => $value) {
  2761. $k = md5($value['source']);
  2762. if (!empty($webo_scripts[$k])) {
  2763. $this->initial_files[$key]['content'] = $webo_scripts[$k];
  2764. } else {
  2765. /* don't touch all files -- just only requested ones */
  2766. if (!$tag || $value['tag'] == $tag) {
  2767. $content_from_file = '';
  2768. if (!empty($value['file'])) {
  2769. $value['file'] = preg_replace("@^/?(\.\./)+@", "", $value['file']);
  2770. /* convert dynamic files to static ones */
  2771. if (!preg_match("/\.(css|js)$/is", $value['file']) || strpos($value['file'], 'index.php/')) {
  2772. $dynamic_file = $value['file_raw'];
  2773. /* touch only non-external scripts */
  2774. if (!strpos($dynamic_file, "://") && strpos($dynamic_file, '//') !== 0) {
  2775. $dynamic_file = "http://" . $_SERVER['HTTP_HOST'] . $this->convert_path_to_absolute($value['file'], array('file' => $_SERVER['REQUEST_URI']), true);
  2776. }
  2777. $static_file = ($this->options[$value['tag'] == 'script' ? 'javascript' : 'css']['cachedir']) . $this->get_remote_file($this->resolve_amps($dynamic_file), $value['tag']);
  2778. if (@is_file($static_file)) {
  2779. $value['file'] = str_replace($this->options['document_root'], "/", $static_file);
  2780. } else {
  2781. unset($value['file']);
  2782. }
  2783. }
  2784. /* remove some files from pages at all */
  2785. if (empty($remove_list[$value['tag']]) || (!in_array(preg_replace("!^.*/!", "", $value['file']), $remove_list[$value['tag']]))) {
  2786. if ($value['tag'] == 'link') {
  2787. $media_array = explode("@media", $this->resolve_css_imports($value['file']));
  2788. /* recursively resolve @import in files */
  2789. $content_from_file = (empty($value['media']) ? "" : "@media " . $value['media'] . "{") .
  2790. $media_array[0] .
  2791. (empty($value['media']) ? "" : "}");
  2792. /* add the rest, may be erroneous - if @media breaks initial file */
  2793. $content_from_file .= str_replace($media_array[0], "", implode("@media", $media_array));
  2794. /* convert CSS images' paths to absolute */
  2795. $content_from_file = $this->convert_paths_to_absolute($content_from_file, array('file' => $value['file']), 0, 1);
  2796. } else {
  2797. $content_from_file = $this->file_get_contents($this->get_file_name($value['file']));
  2798. }
  2799. }
  2800. /* detect Shadowbox variables */
  2801. if (strpos($value['file'], 'shadowbox.js') !== false) {
  2802. $this->shadowbox_sizzle = preg_match("@useSizzle:\s*true@is", $content_from_file);
  2803. $this->shadowbox_language = preg_replace("@.*language:\s*['\"]([^'\"]+)['\"].*@is", "$1", $content_from_file);
  2804. }
  2805. /* fix niftycube loader */
  2806. if (strpos($value['file'], 'niftycube.js') !== false) {
  2807. $content_from_file = preg_replace("!function AddCss()\{.*?\}!is", "function AddCss(){niftyCss=true}", $content_from_file);
  2808. }
  2809. /* remove duplicates */
  2810. if ($value['tag'] == 'script' &&
  2811. $this->options['javascript']['remove_duplicates']) {
  2812. foreach ($duplicates as $k => $duplicate) {
  2813. if (preg_match("@" . $duplicate['regexp'] . "$@is", $value['file'])) {
  2814. if ($duplicate['exists']) {
  2815. $content_from_file = '';
  2816. } else {
  2817. $duplicates[$k]['exists'] = 1;
  2818. continue;
  2819. }
  2820. }
  2821. }
  2822. }
  2823. }
  2824. /* remove BOM */
  2825. $content_from_file = str_replace('', '', $content_from_file);
  2826. /* don't delete any detected scripts from array -- we need to clean up HTML page from them */
  2827. if (empty($value['file']) && (empty($last_key[$value['tag']]) || $key != $last_key[$value['tag']])) {
  2828. /* glue inline and external content */
  2829. if (($this->options['javascript']['inline_scripts'] && $value['tag'] == 'script') || ($this->options['css']['inline_scripts'] && $value['tag'] == 'link')) {
  2830. /* resolve @import from inline styles */
  2831. if ($value['tag'] == 'link') {
  2832. $value['content'] = (empty($value['media']) ? "" : "@media " . $value['media'] . "{") .
  2833. $this->resolve_css_imports($value['content'], true) .
  2834. (empty($value['media']) ? "" : "}");
  2835. /* convert CSS images' paths to absolute */
  2836. $value['content'] = $this->convert_paths_to_absolute($value['content'],
  2837. array('file' => $this->options['document_root_relative']), 0, 1);
  2838. } else {
  2839. /* fix to merge dynamic Shadowbox files */
  2840. if (!empty($this->shadowbox_base) && preg_match("@players:\s*\[@is", $value['content'])) {
  2841. $players = preg_replace("@.*players:\s*\[([^\]]+)]\s*,\r?\n?.*@is", "$1", $value['content']);
  2842. $value['content'] = str_replace($players, '', $value['content']);
  2843. $players = str_replace(array(" ", "'", '"'), '', $players);
  2844. $players = explode(',', $players);
  2845. $d = $this->options['document_root'] . $this->shadowbox_base_raw;
  2846. $c = '';
  2847. if (!empty($this->shadowbox_sizzle)) {
  2848. $c .= $this->file_get_contents($d . 'libraries/sizzle/sizzle.js');
  2849. }
  2850. if (!empty($this->shadowbox_language)) {
  2851. $c .= $this->file_get_contents($d . 'languages/shadowbox-' . $this->shadowbox_language . '.js');
  2852. }
  2853. foreach ($players as $player) {
  2854. if ($player == 'swf' || $player == 'flv') {
  2855. $c .= $this->file_get_contents($d . 'libraries/swfobject/swfobject.js');
  2856. }
  2857. $c .= $this->file_get_contents($d . 'players/shadowbox-' . $player . '.js');
  2858. }
  2859. $value['content'] = $c . $value['content'];
  2860. }
  2861. }
  2862. $text = (empty($value['content']) ? '' : "\n" . $value['content']);
  2863. /* if we can't add to existing tag -- store for the future */
  2864. if (empty($last_key[$value['tag']])) {
  2865. $stored[$value['tag']] = empty($stored[$value['tag']]) ? $text : $stored[$value['tag']] . $text;
  2866. $last_key_flushed[$value['tag']] = $key;
  2867. } else {
  2868. $this->initial_files[$last_key[$value['tag']]]['content'] .= $text;
  2869. }
  2870. /* null content not to include anywhere, we still have source code in 'source' */
  2871. $this->initial_files[$key]['content'] = '';
  2872. }
  2873. } elseif (!empty($content_from_file)) {
  2874. /* don't rewrite existing content inside script tags */
  2875. $this->initial_files[$key]['content'] = $content_from_file . (empty($value['content']) ? '' : "\n" . $value['content']);
  2876. /* add stored content before, but leave styles stored */
  2877. if (!empty($stored[$value['tag']])) {
  2878. /* preserve order of merged content */
  2879. if ($last_key_flushed[$value['tag']] < $key) {
  2880. $this->initial_files[$key]['content'] = $stored[$value['tag']] . "\n" . $this->initial_files[$key]['content'];
  2881. } else {
  2882. $this->initial_files[$key]['content'] .= "\n" . $stored[$value['tag']];
  2883. }
  2884. $stored[$value['tag']] = '';
  2885. }
  2886. $last_key[$value['tag']] = $key;
  2887. }
  2888. }
  2889. $need_rewrite = 1;
  2890. }
  2891. }
  2892. /* check for stored content and flush it */
  2893. foreach ($stored as $tag => $stored_content) {
  2894. $this->initial_files[$last_key_flushed[$tag]]['content'] = $stored_content;
  2895. }
  2896. if ((($this->options['css']['rocket'] && $value['tag'] == 'link') ||
  2897. ($this->options['javascript']['rocket'] && $value['tag'] == 'script')) && !empty($need_rewrite)) {
  2898. $webo_scripts = array();
  2899. foreach ($this->libraries as $klass => $library) {
  2900. if (!class_exists($klass, false)) {
  2901. require_once($this->options['css']['installdir'] . 'libs/php/' . $library);
  2902. }
  2903. }
  2904. foreach ($this->initial_files as $key => $value) {
  2905. $webo_scripts[md5($value['source'])] = $value['tag'] == 'link' ?
  2906. $this->minify_text($value['content'], $this->options['css']) :
  2907. $this->minify_javascript($value['content'], $this->options['javascript']);
  2908. }
  2909. $this->write_file_array($rocket_file, $webo_scripts, 'webo_scripts');
  2910. }
  2911. }
  2912. }
  2913. /**
  2914. * Sets the headers to be sent in the javascript and css files
  2915. *
  2916. **/
  2917. function set_gzip_headers () {
  2918. /* define encoding for HTML page */
  2919. $this->set_gzip_encoding();
  2920. /* check if content must be gzipped, can't gzip twice via php for flush */
  2921. if ($this->options['page']['gzip'] && !$this->options['page']['flush'] && !$this->nogzip) {
  2922. $this->set_gzip_header();
  2923. }
  2924. /* When will the file expire? */
  2925. $offset = 6000000 * 60 ;
  2926. $ExpStr = "Expires: " .
  2927. gmdate("D, d M Y H:i:s",
  2928. $this->time + $offset) . " GMT";
  2929. $types = array("css", "javascript");
  2930. foreach ($types as $type) {
  2931. /* Always send etag */
  2932. $this->gzip_header[$type] = '<?php
  2933. // Determine supported compression method
  2934. if (!empty($_SERVER["HTTP_ACCEPT_ENCODING"])) {
  2935. $_SERVER["HTTP_ACCEPT_ENCODING"] = strtolower($_SERVER["HTTP_ACCEPT_ENCODING"]);
  2936. $gzip = strstr($_SERVER["HTTP_ACCEPT_ENCODING"], "gzip") || !empty($_COOKIE["_wo_gzip"]);
  2937. $xgzip = strstr($_SERVER["HTTP_ACCEPT_ENCODING"], "x-gzip");
  2938. $deflate = strstr($_SERVER["HTTP_ACCEPT_ENCODING"], "deflate");
  2939. $xdeflate = strstr($_SERVER["HTTP_ACCEPT_ENCODING"], "x-deflate");
  2940. } elseif (empty($_SERVER["HTTP_ACCEPT_ENCODING"]) && !empty($_COOKIE["_wo_gzip"])) {
  2941. $gzip = 1;
  2942. }
  2943. // Determine used compression method
  2944. $encoding = empty($gzip) ? (empty($xgzip) ? (empty($deflate) ? (empty($xdeflate) ? "none" : "x-deflate") : "deflate") : "x-gzip") : "gzip";
  2945. $hash = "' . $this->time . '-" . str_replace("x-", "", $encoding);
  2946. @header ("Etag: \"" . $hash . "\"");
  2947. ?>';
  2948. /* Send 304? */
  2949. $this->gzip_header[$type] .= '<?php
  2950. if ((isset($_SERVER["HTTP_IF_NONE_MATCH"]) &&
  2951. stripslashes($_SERVER["HTTP_IF_NONE_MATCH"]) == "\"" . $hash . "\"") ||
  2952. (isset($_SERVER["HTTP_IF_MATCH"]) &&
  2953. stripslashes($_SERVER["HTTP_IF_MATCH"]) == "\"" . $hash . "\"")) {
  2954. // Return visit and no modifications, so do not send anything
  2955. @header ("HTTP/1.0 304 Not Modified");
  2956. @header ("Content-Length: 0");
  2957. exit();
  2958. }
  2959. ?>';
  2960. /* ob_start ("ob_gzhandler"); */
  2961. if (!empty($this->options[$type]['gzip'])) {
  2962. $this->gzip_header[$type] .= '<?php';
  2963. if ($this->options['page']['zlib']) {
  2964. $this->gzip_header[$type] .= '
  2965. $zlib = 0;
  2966. if (strlen(@ini_get("zlib.output_compression_level"))) {
  2967. @ini_set("zlib.output_compression", "On");
  2968. @ini_set("zlib.output_compression_level", ' . $this->options['page']['gzip_level'] . ');
  2969. $zlib = 1;
  2970. }';
  2971. }
  2972. $this->gzip_header[$type] .= '
  2973. ob_start("compress_output_option");
  2974. function compress_output_option($contents) {
  2975. global $encoding, $gzip, $xgzip, $zlib;
  2976. // Check for buggy versions of Internet Explorer
  2977. if (!empty($_SERVER["HTTP_USER_AGENT"]) && !strstr($_SERVER["HTTP_USER_AGENT"], "Opera") &&
  2978. preg_match("/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i", $_SERVER["HTTP_USER_AGENT"], $matches)) {
  2979. $version = floatval($matches[1]);
  2980. // IE6- can loose first 2048 bytes of gzipped content, code from Bitrix
  2981. if ($version < 7) {
  2982. $contents = str_repeat(" ", 2048) . "\r\n" . $contents;
  2983. }
  2984. }
  2985. if (isset($encoding) && $encoding != "none") {
  2986. if (!$zlib) {
  2987. // try to get gzipped content from file
  2988. $extension = $gzip || $xgzip ? "gz" : "df";
  2989. $content = @file_get_contents(__FILE__ . "." . $extension);
  2990. if (get_magic_quotes_runtime()) {
  2991. $content = stripslashes($content);
  2992. }
  2993. $gzipped = 0;
  2994. if (empty($content)) {
  2995. // Send compressed contents
  2996. if ($gzip || $xgzip) {
  2997. if (function_exists("gzencode")) {
  2998. $contents = gzencode($contents, '. $this->options[$type]['gzip_level'] .', FORCE_GZIP);
  2999. $gzipped = 1;
  3000. }
  3001. } else {
  3002. if (function_exists("gzdeflate")) {
  3003. $contents = gzdeflate($contents, '. $this->options[$type]['gzip_level'] .');
  3004. $gzipped = 1;
  3005. }
  3006. }
  3007. if ($gzipped) {
  3008. $fp = @fopen(__FILE__ . "." . $extension, "wb");
  3009. if ($fp) {
  3010. @fwrite($fp, $contents);
  3011. @fclose($fp);
  3012. }
  3013. }
  3014. } else {
  3015. $contents = $content;
  3016. $gzipped = 1;
  3017. }
  3018. }
  3019. if ($gzipped || $zlib) {
  3020. @header ("Content-Encoding: " . $encoding);
  3021. }
  3022. if ($gzipped && !$zlib && (empty($_SERVER["SERVER_PROTOCOL"]) || $_SERVER["SERVER_PROTOCOL"] == "HTTP/1.0")) {
  3023. @header ("Content-Length: " . strlen($contents));
  3024. }
  3025. }
  3026. return $contents;
  3027. }
  3028. ?>';
  3029. }
  3030. if (!empty($this->options[$type]['far_future_expires_php'])) {
  3031. $this->gzip_header[$type] .= '<?php
  3032. @header("Cache-Control: private, max-age=315360000");
  3033. @header("' . $ExpStr . '");
  3034. ?>';
  3035. }
  3036. $this->gzip_header[$type] .= '<?php
  3037. @header("Content-type: text/' . $type . '; charset=' . $this->options['charset'] . '");
  3038. ?>';
  3039. } // end FE
  3040. }
  3041. /**
  3042. * Returns a path or url without the querystring and anchor
  3043. *
  3044. **/
  3045. function strip_querystring ($path) {
  3046. if ($commapos = strpos($path, '?')) {
  3047. $path = substr($path, 0, $commapos);
  3048. }
  3049. if ($numberpos = strpos($path, '#')) {
  3050. $path = substr($path, 0, $numberpos);
  3051. }
  3052. return $path;
  3053. }
  3054. /**
  3055. * Minifies CSS - removes unnecessary symbols
  3056. *
  3057. **/
  3058. function minify_text ($txt) {
  3059. /* Remove simple comments */
  3060. $txt = preg_replace('!(/\*.*?\*/|^ | $)!is', '', $txt);
  3061. /* Remove line breaks, compress whitespaces */
  3062. $txt = preg_replace('![\s\t\r\n]+!', ' ', $txt);
  3063. /* Remove spaces for }, {, ;, ,: */
  3064. $txt = str_replace(array(' :', ': ', ' ,', ', ', ' ;', '; ', ' {', '{ ', ' }', '} '), array(':', ':', ',', ',', ';', ';', '{', '{', '}', '}'), $txt);
  3065. /* Remove excessive symbols */
  3066. $txt = str_replace(array(' 0px', ':0px', ';}', ':0 0 0 0', ':0.', ' 0.', ', '), array(' 0', ':0', '}', ':0', ':.', ' .', ','), $txt);
  3067. return trim($txt);
  3068. }
  3069. /**
  3070. * Safely trims whitespace from an HTML page
  3071. * Adapted from smarty code http://www.smarty.net/
  3072. **/
  3073. function trimwhitespace ($source) {
  3074. if (!empty($this->options['page']['minify']) ||
  3075. !empty($this->options['page']['unobtrusive_all'])) {
  3076. if (!empty($this->options['page']['html_tidy'])) {
  3077. $_script_blocks = array(array(), array(), array(),
  3078. array(), array(), array());
  3079. /* Pull out the script, textarea and pre blocks */
  3080. $this->trimwhitespace_find('<script', '</script>',
  3081. '@@@WBO:TRIM:SCRIPT0@@@', $source, $_script_blocks[0]);
  3082. $this->trimwhitespace_find('<SCRIPT', '</SCRIPT>',
  3083. '@@@WBO:TRIM:SCRIPT1@@@', $source, $_script_blocks[1]);
  3084. $this->trimwhitespace_find('<textarea', '</textarea>',
  3085. '@@@WBO:TRIM:SCRIPT2@@@', $source, $_script_blocks[2]);
  3086. $this->trimwhitespace_find('<TEXTAREA', '</TEXTAREA>',
  3087. '@@@WBO:TRIM:SCRIPT3@@@', $source, $_script_blocks[3]);
  3088. $this->trimwhitespace_find('<pre', '</pre>',
  3089. '@@@WBO:TRIM:SCRIPT4@@@', $source, $_script_blocks[4]);
  3090. $this->trimwhitespace_find('<PRE', '</PRE>',
  3091. '@@@WBO:TRIM:SCRIPT5@@@', $source, $_script_blocks[5]);
  3092. } else {
  3093. /* get all scripts */
  3094. preg_match_all("!(<script.*?</script>)!is", $source, $match);
  3095. $_script_blocks = $match[0];
  3096. array_unique($_script_blocks);
  3097. foreach ($_script_blocks as $k => $b) {
  3098. if (strpos($b, 'WEBONOTOPTIMIZE') !== false) {
  3099. unset($_script_blocks[$k]);
  3100. }
  3101. }
  3102. $_script_blocks_to = array();
  3103. $c = count($_script_blocks);
  3104. for ($i = 0; $i < $c; $i++) {
  3105. $_script_blocks_to[] = '@@@WBO:TRIM:SCRIPT' . $i . '@@@';
  3106. }
  3107. $source = str_replace($_script_blocks, $_script_blocks_to, $source);
  3108. /* get all textarea / pre */
  3109. preg_match_all("!(<textarea.*?</textarea>|<pre.*?</pre>)!is", $source, $match);
  3110. $_script_blocks_pre = $match[0];
  3111. $_script_blocks_to_pre = array();
  3112. $c1 = count($_script_blocks_pre);
  3113. for ($i = $c; $i < $c + $c1; $i++) {
  3114. $_script_blocks_to_pre[] = '@@@WBO:TRIM:SCRIPT' . $i . '@@@';
  3115. }
  3116. $source = str_replace($_script_blocks_pre, $_script_blocks_to_pre, $source);
  3117. }
  3118. }
  3119. /* add multiple hosts or redirects for static images */
  3120. if ((!empty($this->options['page']['parallel']) &&
  3121. !empty($this->options['page']['parallel_hosts'])) ||
  3122. !empty($this->options['page']['far_future_expires_rewrite']) ||
  3123. !empty($this->options['page']['sprites']) ||
  3124. !empty($this->options['page']['scale_images'])) {
  3125. $source = $this->add_multiple_hosts($source,
  3126. explode(" ", $this->options['page']['parallel_hosts']),
  3127. explode(" ", $this->options['page']['parallel_satellites']),
  3128. explode(" ", $this->options['page']['parallel_satellites_hosts']));
  3129. }
  3130. /* remove all leading spaces, tabs and carriage returns NOT preceeded by a php close tag */
  3131. if (!empty($this->options['page']['minify'])) {
  3132. $source = trim(preg_replace('/((?<!\?>)\n)[\t\s]+/m', '\1', $source));
  3133. /* replace ' >' with '>', remove \r symbols */
  3134. $source = str_replace(array(' >', "\r"), array('>', ''), $source);
  3135. }
  3136. /* one-strig-HTML takes about 20-50ms */
  3137. if (!empty($this->options['page']['minify_aggressive'])) {
  3138. /* ' />' with '/>' breaks System - Cache in Joomla! */
  3139. $source = str_replace(' />', '/>', $source);
  3140. /* replace breaks with nothing for block tags */
  3141. $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);
  3142. /* replace breaks with space for inline tags */
  3143. $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);
  3144. }
  3145. /* replace multiple spaces with single one
  3146. $source = preg_replace("/[\s\t\r\n]+/", " ", $source); */
  3147. /* replace script, textarea, pre blocks */
  3148. if (!empty($this->options['page']['unobtrusive_all']) ||
  3149. !empty($this->options['page']['minify'])) {
  3150. $before_body = '';
  3151. if (!empty($this->options['page']['html_tidy'])) {
  3152. for ($i = 0; $i < 6; $i++) {
  3153. $_block = $_script_blocks[$i];
  3154. if (count($_block)) {
  3155. $a = $this->trimwhitespace_replace("@@@WBO:TRIM:SCRIPT" . $i . "@@@", $_block, $source);
  3156. $before_body .= $i < 2 ? $a : '';
  3157. }
  3158. }
  3159. } else {
  3160. if (!empty($this->options['page']['unobtrusive_all'])) {
  3161. $before_body .= implode('', $_script_blocks);
  3162. $_script_blocks = array();
  3163. }
  3164. $source = str_replace($_script_blocks_to, $_script_blocks, $source);
  3165. $source = str_replace($_script_blocks_to_pre, $_script_blocks_pre, $source);
  3166. }
  3167. /* move all scripts to </body> */
  3168. if (!empty($before_body)) {
  3169. if ($this->premium > 1) {
  3170. $before_body = '<!--noindex-->' . $before_body . '<!--/noindex-->';
  3171. }
  3172. if (!empty($this->options['page']['html_tidy']) &&
  3173. ($bodypos = strpos($source, "</body>"))) {
  3174. $source = substr_replace($source,
  3175. $before_body, $bodypos, 0);
  3176. } elseif (!empty($this->options['page']['html_tidy']) &&
  3177. ($bodypos = strpos($this->content, "</BODY>"))) {
  3178. $source = substr_replace($source,
  3179. $before_body, $bodypos, 0);
  3180. } else {
  3181. /* a number of engines doesn't set </body> */
  3182. if (!preg_match('@</body>@is', $source)) {
  3183. $source .= $before_body;
  3184. } else {
  3185. $source = preg_replace('@(</body>)@is',
  3186. $before_body . "$1", $source);
  3187. }
  3188. }
  3189. }
  3190. }
  3191. /* remove website host */
  3192. if (!empty($this->options['page']['minify_aggressive'])) {
  3193. /* fix for base tag */
  3194. preg_match_all("@<base[^>]+>@is", $source, $matches);
  3195. $basetag = false;
  3196. if (count($matches) && count($matches[0])) {
  3197. $basetag = $matches[0][0];
  3198. }
  3199. if ($basetag) {
  3200. $source = str_replace($basetag, '@@@WSSBASE@@@', $source);
  3201. }
  3202. $source = preg_replace("@(src|href)=(['\"])(http" .
  3203. $this->https . "://)(www\.)?" .
  3204. $this->host . "/*@", "$1=$2/", $source);
  3205. if ($basetag) {
  3206. $source = str_replace('@@@WSSBASE@@@', $basetag, $source);
  3207. }
  3208. }
  3209. return $source;
  3210. }
  3211. /**
  3212. * Helper function for trimwhitespace, finds all blocks
  3213. *
  3214. **/
  3215. function trimwhitespace_find ($block_begin, $block_end, $spot, &$subject, &$return) {
  3216. $len = strlen($block_end);
  3217. while ($posbegin = strpos($subject, $block_begin)) {
  3218. if ((($posend = strpos($subject, $block_end)) !== false) && ($posend > $posbegin)) {
  3219. $return[] = substr($subject, $posbegin, $posend - $posbegin + $len);
  3220. $subject = substr_replace($subject, $spot, $posbegin, $posend - $posbegin + $len);
  3221. } else {
  3222. break;
  3223. }
  3224. }
  3225. }
  3226. /**
  3227. * Helper function for trimwhitespace
  3228. *
  3229. **/
  3230. function trimwhitespace_replace ($search_str, $replace, &$subject) {
  3231. $_len = strlen($search_str);
  3232. $_pos = 0;
  3233. $_to_body = '';
  3234. for ($_i=0, $_count = count($replace); $_i<$_count; $_i++) {
  3235. if (($_pos = strpos($subject, $search_str, $_pos)) !== false) {
  3236. /* move scripts to </body>. Skip dynamic styles loader */
  3237. if (!empty($this->options['page']['unobtrusive_all']) &&
  3238. !strpos($replace[$_i], '\x3c!--')) {
  3239. if ((!empty($this->options['html_tidy']) &&
  3240. (strpos($replace[$_i], '<script') ||
  3241. strpos($replace[$_i], '<SCRIPT'))) ||
  3242. preg_match("@<script@is", $replace[$_i])) {
  3243. $_to_body .= $replace[$_i];
  3244. $replace[$_i] = '';
  3245. }
  3246. }
  3247. if (strpos($replace[$_i], 'WEBONOTOPTIMIZE') === false) {
  3248. $subject = substr_replace($subject, $replace[$_i], $_pos, $_len);
  3249. }
  3250. } else {
  3251. break;
  3252. }
  3253. }
  3254. return $_to_body;
  3255. }
  3256. /**
  3257. * Replaces one JS code in HTML with another
  3258. * Returns string to place before </body>
  3259. *
  3260. **/
  3261. function replace_unobtrusive_generic ($match_string, $stuff, $height = 0, $width = 0, $inline = false, $onload_mask = false, $onload_result = false) {
  3262. $return = '';
  3263. $initial_height = empty($height) ? 0 : $height;
  3264. $initial_width = empty($width) ? 0 : $width;
  3265. $onload = !empty($this->options['page']['unobtrusive_onload']) &&
  3266. $onload_mask && $onload_result;
  3267. preg_match_all($match_string, $this->content, $matches, PREG_SET_ORDER);
  3268. if (!empty($matches)) {
  3269. foreach ($matches as $key => $value) {
  3270. /* skip marked unobtrusive items */
  3271. if (!count($this->options['page']['unobtrusive_configuration']) ||
  3272. empty($this->options['page']['unobtrusive_configuration'][$stuff]) ||
  3273. $key >= $this->options['page']['unobtrusive_configuration'][$stuff]) {
  3274. $height = $initial_height;
  3275. $width = $initial_height;
  3276. if (empty($height) || empty($width)) {
  3277. /* try to calculate height for AdWords */
  3278. switch ($stuff) {
  3279. case 'gw':
  3280. $height = round(substr($value[0], strpos($value[0], 'google_ad_height =') + 18, 5));
  3281. $width = round(substr($value[0], strpos($value[0], 'google_ad_width =') + 18, 5));
  3282. break;
  3283. case 'aa':
  3284. $height = round(substr($value[0], strpos($value[0], 'amazon_ad_height = "') + 20, 5));
  3285. $width = round(substr($value[0], strpos($value[0], 'amazon_ad_width = "') + 20, 5));
  3286. break;
  3287. case 'cp':
  3288. $pos = strpos($value[0], 'thumb_size:') + 11;
  3289. $posend = strpos($value[0], ',', $pos);
  3290. $dims = explode('x', str_replace(array("'", " ", '"'), array(), substr($value[0], $pos, $posend)));
  3291. $width = round($dims[0]);
  3292. $height = round($dims[1]);
  3293. break;
  3294. case 'if':
  3295. case 'IF':
  3296. if (preg_match("@height\s*=@is", $value[0])) {
  3297. $height = round(preg_replace("@.*height\s*=[\s'\"](.*)[\s'\"]@", "$1", $value[0]));
  3298. }
  3299. if (preg_match("@width\s*=@is", $value[0])) {
  3300. $height = round(preg_replace("@.*width\s*=[\s'\"](.*)[\s'\"]@", "$1", $value[0]));
  3301. }
  3302. break;
  3303. }
  3304. }
  3305. /* count param for str_replace available only in PHP5 */
  3306. $pos = strpos($this->content, $value[0]);
  3307. $len = strlen($value[0]);
  3308. $tag = $inline ? 'span' : 'div';
  3309. $this->content = substr_replace($this->content,
  3310. ($stuff == 'fc' ? '<?xml:namespace prefix="fb"/>' : '') .
  3311. '<' .
  3312. $tag .
  3313. ' id="' .
  3314. $stuff .
  3315. '_dst_' .
  3316. $key .
  3317. '"' .
  3318. ($height && $inline ? ' style="'.
  3319. ($onload ? 'position:relative;' : '') .
  3320. 'height:' .
  3321. $height .
  3322. 'px;width:' .
  3323. $width .
  3324. 'px;display:inline-block"' : '') .
  3325. ($height && !$inline ? ' style="'.
  3326. ($onload ? 'position:relative;margin:0 auto;' : '') .
  3327. 'height:' .
  3328. $height .
  3329. 'px;width:' .
  3330. $width .
  3331. 'px"' : '') .
  3332. '></' .
  3333. $tag .
  3334. '>', $pos, $len);
  3335. if (!$onload) {
  3336. $return .= '<' .
  3337. $tag .
  3338. ' id="'.
  3339. $stuff .'_src_' . $key .
  3340. '">' .
  3341. $value[0] .
  3342. '</' .
  3343. $tag .
  3344. '><script type="text/javascript">//<![CDATA[' . "\n" . '(function(){var a=document,b=a.getElementById("' .
  3345. $stuff . '_dst_' . $key . '"),c=b.parentNode,d=a.getElementById("' .
  3346. $stuff . '_src_' . $key . '");if(c===a.body){c.insertBefore(d,b);c.removeChild(b)}else{c.innerHTML=c.innerHTML.replace(/\x3c' .
  3347. $tag .
  3348. '[^>]+id="?' .
  3349. $stuff .
  3350. '_dst_' .
  3351. $key .
  3352. '["\s>].*?\x3c\/' .
  3353. $tag .
  3354. '>/i,d.innerHTML);b=a.getElementById("' .
  3355. $stuff . '_src_' . $key . '");b.parentNode.removeChild(b)}}())' . "\n//]]></script>";
  3356. } else {
  3357. $return .= '<script type="text/javascript">//<![CDATA[' . "\n" . 'wss_onload[wss_onload.length]=function(){wss_parentNode=document.getElementById(\'' .
  3358. $stuff . '_dst_' . $key
  3359. .'\');' .
  3360. str_replace(array("\n", "\r", '###WSS###', '<div', '</div', '// ]]>'),
  3361. array(' ', '', $key, '\x3cdiv', '\x3c/div', ''),
  3362. preg_replace("@(<!--.*?-->|/\*.*?\*/)@is", "", preg_replace("@" . $onload_mask . "@is",
  3363. $onload_result, $value[0]))) .
  3364. "}\n//]]></script>";
  3365. }
  3366. }
  3367. }
  3368. }
  3369. return $return;
  3370. }
  3371. /**
  3372. * Moves all known informers before </body>
  3373. * Also handles counters and ads
  3374. * Leaves placeholders for them in content
  3375. *
  3376. **/
  3377. function replace_informers ($options) {
  3378. if (count($this->options['page']['unobtrusive_configuration'])) {
  3379. $b = $this->options['page']['unobtrusive_configuration'];
  3380. $conf = array();
  3381. foreach ($b as $skip) {
  3382. $a = explode(":", $skip);
  3383. $conf[$a[0]] = $a[1];
  3384. }
  3385. $this->options['page']['unobtrusive_configuration'] = $conf;
  3386. }
  3387. $before_body = '';
  3388. $host = (empty($this->options['javascript']['host']) ?
  3389. $this->options['page']['host'] :
  3390. $this->options['javascript']['host']);
  3391. $before_body_onload = empty($this->options['page']['unobtrusive_onload']) ?
  3392. '' : (empty($this->options['page']['unobtrusive_inline']) ?
  3393. '<script type="text/javascript" src="' .
  3394. (empty($host) ? '' : '//' . $host) .
  3395. (empty($this->options['javascript']['far_future_expires_static']) ?
  3396. '' : $this->options['page']['cachedir_relative'] .
  3397. 'wo.' . ($this->options['javascript']['gzip'] ? 'gzip' : 'static') . '.php?') .
  3398. $this->options['javascript']['cachedir_relative'] .
  3399. 'yass.loader.js"></script>' :
  3400. '<script type="text/javascript">//<![CDATA[' . "\n" . '(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.replace(/&gt;/g,">").replace(/&lt;g/,"<").replace(/&amp;/g,"&"));h.appendChild(b);k||(h=b)},end:function(){h=h.parentNode},chars:function(b){switch(h.nodeName.toLowerCase()){case"script":b&&eval(b);break;case"style":b&&h.appendChild(o.createTextNode(b));break;default:if(b){h.innerHTML+=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,center,dd,div,dl,dt,fieldset,form,frameset,hr,iframe,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("\\\\x3C!--")==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(/\\\\x3C!--(.*?)--\>/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)}})();' . "\n//]]></script>") .
  3401. '<script type="text/javascript">//<![CDATA[' . "\n" . 'wss_onload=[]' . "\n//]]></script>";
  3402. require($options['installdir'] . 'libs/php/config.unobtrusive.php');
  3403. /* convert vKontakte to async load */
  3404. if (!empty($options['unobtrusive_informers']) && strpos($this->content, 'vkAsyncInit') === false && strpos($this->content, 'VK.') !== false) {
  3405. preg_match_all("!VK\.[^\)]+\);!s", $this->content, $matches, PREG_SET_ORDER);
  3406. $vk = '';
  3407. $replace_from = array();
  3408. foreach ($matches as $match) {
  3409. $vk .= $match[0];
  3410. $replace_from[] = $match[0];
  3411. }
  3412. if ($vk) {
  3413. $before_body .= '<script type="text/javascript">//<![CDATA[' . "\n" . 'window.vkAsyncInit=function(){' . $vk . '};window[/*@cc_on !@*/0?"attachEvent":"addEventListener"](/*@cc_on "on"+@*/"load",vkAsyncInit,false)' . "\n//]]></script>";
  3414. $this->content = str_replace($replace_from, '', $this->content);
  3415. }
  3416. }
  3417. if (is_array($unobtrusive_items)) {
  3418. foreach ($unobtrusive_items as $group => $items) {
  3419. if (!empty($options[$group])) {
  3420. foreach ($items as $key => $item) {
  3421. if (strpos($this->content, $item['marker'])) {
  3422. $before = $this->replace_unobtrusive_generic("@" . $item['regexp'] . "@is",
  3423. $key, empty($item['height']) ? 0 : $item['height'],
  3424. empty($item['width']) ? 0 : $item['width'],
  3425. empty($item['inline']) ? false : $item['inline'],
  3426. empty($item['onload_before']) ? false : $item['onload_before'],
  3427. empty($item['onload_after']) ? false : $item['onload_after']);
  3428. /* switch between window.onload and onDOMready handlers */
  3429. if (!empty($item['onload_before']) && !empty($item['onload_after'])) {
  3430. $before_body_onload .= $before;
  3431. } else {
  3432. $before_body .= $before;
  3433. }
  3434. }
  3435. }
  3436. }
  3437. }
  3438. }
  3439. $before_body .= $before_body_onload;
  3440. $onload = $onload_func = '';
  3441. $onload .= empty($this->options['page']['unobtrusive_onload']) && empty($this->options['page']['postload']) && empty($this->options['page']['postload_frames']) ? '' : 'wss_onload_ready=1;window[/*@cc_on!@*/0?"attachEvent":"addEventListener"](/*@cc_on "on"+@*/"load",function(){';
  3442. $onload_func .= empty($this->options['page']['unobtrusive_onload']) ? '' : '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);';
  3443. $onload_func .= empty($this->options['page']['postload']) ? '' : 'var a=0,b,c,d=["' .
  3444. str_replace(" ", '","', $this->options['page']['postload']) .
  3445. '"],e=navigator.appName.indexOf("Microsoft")===0,f=document;while(b=d[a++]){b=b.indexOf("//")==-1?"//"+b:b;if(e){new Image().src=b}else{c=f.createElement("object");c.data=b;c.width=c.height=0;f.body.appendChild(c)}};';
  3446. $onload_func .= empty($this->options['page']['postload_frames']) ? '' : 'var a=0,b,c,d=["' .
  3447. str_replace(" ", '","', $this->options['page']['postload_frames']) .
  3448. '"],f=document;while(b=d[a++]){b=b.indexOf("//")==-1?"//"+b:b;c=f.createElement("iframe");c.style.display="none";c.src=b;f.body.appendChild(c)};';
  3449. $onload_func .= $this->options['css']['rocket'] || $this->options['javascript']['rocket'] ? 'document.cookie="WSS_ROCKET=1;path=/;expires="+(new Date(new Date().getTime()+31536000).toGMTString());' : '';
  3450. if ($onload) {
  3451. $before_body .= '<script type="text/javascript">//<![CDATA['. "\n" . $onload . $onload_func . "},false)\n//]]></script>";
  3452. }
  3453. if (!empty($before_body)) {
  3454. if ($this->premium > 1) {
  3455. $before_body = '<!--noindex-->' . $before_body . '<!--/noindex-->';
  3456. }
  3457. if (!empty($options['html_tidy']) && ($bodypos = strpos($this->content, '</body>'))) {
  3458. $this->content = substr_replace($this->content, $before_body, $bodypos, 0);
  3459. } elseif (!empty($options['html_tidy']) && ($bodypos = strpos($this->content, '</BODY>'))) {
  3460. $this->content = substr_replace($this->content, $before_body, $bodypos, 0);
  3461. } else {
  3462. if (preg_match('@</body>@is', $this->content)) {
  3463. $this->content = preg_replace('@</body>@i', $before_body . "$0" , $this->content);
  3464. } else {
  3465. /* a number of engines doesn't set </body> */
  3466. $this->content .= $before_body;
  3467. }
  3468. }
  3469. }
  3470. }
  3471. /**
  3472. * Removes all secondary stuff from HTML code
  3473. *
  3474. **/
  3475. function prepare_html ($source, $cssonly = false) {
  3476. $dest = $source;
  3477. /* remove conditional comments for current browser */
  3478. $dest = $this->remove_conditional_comments($dest);
  3479. /* remove comments from <style> constructions */
  3480. if (!empty($this->options['css']['inline_scripts'])) {
  3481. $dest = preg_replace("@(<style type=[\"']text/css[^>]*>)[\t\s\r\n]*<!--(\/\*--><!\[CDATA\[\/\*><!--\*\/)?@is", "$1", $dest);
  3482. $dest = preg_replace("@([\t\s\r\n]*-->|\/\*\]\]>\*\/-->\r?\n?)(</style>)@is", "$2", $dest);
  3483. }
  3484. /* Pull out the comment blocks to avoid touching conditional comments,
  3485. and some semi-standard complaint hacks, skip if we fetch body but not head */
  3486. if (!empty($this->options['javascript']['inline_scripts']) && !$cssonly) {
  3487. $dest = str_replace(
  3488. array('//]]>', '// ]]>', '<!--//-->',
  3489. '<![CDATA[', '//><!--', '//--><!]]>', '//-->',
  3490. '<!--/*--><![CDATA[//><!--','//-->', '//<!--',
  3491. '// <!--', '<!-- // -->', '<!--/*-->', '// -->',
  3492. '/* ]]> */'), '', $dest);
  3493. $dest = preg_replace("@(<script[^>]*>)[\r\n\t\s]*<!--@is", "$1", $dest);
  3494. $dest = preg_replace("@-->[\r\n\t\s]*(</script>)@is", "$1", $dest);
  3495. }
  3496. if ($dest !== $source) {
  3497. /* replace current content with updated version */
  3498. $this->content = str_replace($source, $dest, $this->content);
  3499. }
  3500. /* and now remove all comments and parse result code -- to avoid IE code mixing with other browsers */
  3501. $dest = preg_replace("@<!--.*?-->@is", '', $dest);
  3502. return $dest;
  3503. }
  3504. /**
  3505. * Gets the head (and body) part(s) of the HTML document
  3506. *
  3507. **/
  3508. function get_head () {
  3509. if (empty($this->head) && empty($this->body)) {
  3510. /* try to define base URI for the document */
  3511. if ($this->options['page']['html_tidy']) {
  3512. if (($basepos = strpos($this->content, '<base')) || ($basepos = strpos($this->content, '<BASE'))) {
  3513. $basepos = strpos($this->content, 'href=', $basepos);
  3514. if (!$basepos) {
  3515. $basepos = strpos($this->content, 'HREF=', $basepos);
  3516. }
  3517. $baseend = strpos($this->content, '>', $basepos);
  3518. if ($this->content{$baseend-1} === '/') {
  3519. $baseend--;
  3520. }
  3521. $this->basehref = trim(str_replace(array('"', ""), array(), substr($this->content, $basepos + 5, $baseend - $basepos - 6)));
  3522. }
  3523. } elseif (preg_match("@<base\s+href@is", $this->content)) {
  3524. $this->basehref = preg_replace("@^.*?<base\s+href\s*=\s*['\"](.*?)['\"].*$@is", "$1", $this->content);
  3525. }
  3526. if (!empty($this->basehref)) {
  3527. $this->basehref_url = preg_replace("@^((https?:)?//[^/]+).*$@is", "$1", $this->basehref);
  3528. $this->basehref_url = preg_replace("@https?://(www\.)?" . $this->host . "@", "", $this->basehref_url);
  3529. $this->basehref = preg_replace("@https?://(www\.)?" . $this->host . "@", "", $this->basehref);
  3530. $this->basehref = $this->basehref ? $this->basehref : '/';
  3531. }
  3532. /* change all links on the page according to DEBUG mode */
  3533. if ($this->debug_mode) {
  3534. /* don't touch content inside <script> */
  3535. $c = preg_replace("!<script[^>]*>.*?</script>!is", "", $this->content);
  3536. preg_match_all("!(<a[^>]+href\s*=\s*['\"])([^\?]*?)(\?(.+?))?(['\"])!is", $c, $m, PREG_SET_ORDER);
  3537. foreach ($m as $match) {
  3538. /* skip javascript: etc links */
  3539. if (!preg_match('!^((javascript|mailto|skype):|#)!i', $match[2]) && (preg_match('!^/[^/]*!', $match[2]) || preg_match('!https?://(www\.)?' . $this->host_escaped . '/!i', $match[2]) || preg_match('!^[^:/][^:]+$!', $match[2]))) {
  3540. $this->content = str_replace($match[0], $match[1] . $match[2] . ($match[3] ? $match[3] . '&amp;' : '?') . 'web_optimizer_debug=1' . $match[5], $this->content);
  3541. }
  3542. }
  3543. }
  3544. /* Remove comments ?*/
  3545. if (!empty($this->options['page']['remove_comments'])) {
  3546. $added = $this->premium > 1 ? '' : '|(<\!--/?noindex-->)';
  3547. /* skip removing escaped JavaScript code, thx to smart */
  3548. preg_match_all("!((<script[^>]*>.*?</script>)|(<style[^>]*>.*?</style>)|(<\!--\[.*?<\!\[endif\]-->)" . $added . ")!is", $this->content, $matches, PREG_SET_ORDER);
  3549. $i = 0;
  3550. $to_store = array();
  3551. $stubs = array();
  3552. foreach ($matches as $match) {
  3553. $to_store[] = $match[0];
  3554. $stubs[] = '@@@WSSLEAVE' . $i . '@@@';
  3555. $i++;
  3556. }
  3557. $this->content = str_replace($to_store, $stubs, $this->content);
  3558. $this->content = preg_replace("@<!--.*?-->@is", '', $this->content);
  3559. $this->content = str_replace($stubs, $to_store, $this->content);
  3560. }
  3561. /* fix Shadowbox inclusions */
  3562. if (($this->options['javascript']['minify'] || $this->options['css']['minify']) && strpos($this->content, 'Shadowbox.load')) {
  3563. $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);
  3564. $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);
  3565. preg_match_all("@Shadowbox.loadPlayer\(\[([^\]]+?)\]\s*,\s*['\"](.*?)['\"]\);@", $this->content, $matches, PREG_SET_ORDER);
  3566. foreach ($matches as $match) {
  3567. $inclusion = '</script>';
  3568. $players = explode(",", $match[1]);
  3569. foreach ($players as $player) {
  3570. $player = str_replace(array('\'', '"', ' '), '', $player);
  3571. $inclusion .= "<script type=\"text/javascript\" src=\"" .
  3572. $match[2] .
  3573. "/shadowbox-" .
  3574. $player .
  3575. ".js\"></script>";
  3576. }
  3577. $this->content = str_replace($match[0], $inclusion . "<script type=\"text/javascript\">", $this->content);
  3578. }
  3579. }
  3580. /* skip parsing head if we include both CSS and JavaScript from head+body */
  3581. if (empty($this->options['javascript']['minify_body']) ||
  3582. empty($this->options['css']['minify_body']) ||
  3583. empty($this->options['javascript']['inline_scripts_body'])) {
  3584. if (empty($this->options['page']['html_tidy'])) {
  3585. preg_match("!<head[^>]*>.*?<body!is",
  3586. $this->content, $matches);
  3587. $head = $matches[0];
  3588. } else {
  3589. if ($headpos = strpos($this->content, '<head')) {
  3590. $head = substr($this->content, $headpos,
  3591. strpos($this->content, '</head>') - $headpos);
  3592. } elseif ($headpos = strpos($this->content, '<HEAD')) {
  3593. $head = substr($this->content, $headpos,
  3594. strpos($this->content, '</HEAD>') - $headpos);
  3595. }
  3596. }
  3597. if (!empty($head)) {
  3598. $this->head = $this->prepare_html($head);
  3599. }
  3600. }
  3601. /* get head+body if required */
  3602. if ($this->options['javascript']['minify_body'] + $this->options['css']['minify_body'] + $this->options['javascript']['inline_scripts_body']) {
  3603. preg_match("!<head.*!is", $this->content, $matches);
  3604. if (!empty($matches[0])) {
  3605. $this->body = $this->prepare_html($matches[0], empty($this->options['javascript']['minify_body']));
  3606. }
  3607. }
  3608. $xhtml = strpos($this->content, 'XHTML');
  3609. /* split XHTML behavior from HTML */
  3610. $this->xhtml = $xhtml > 34 && $xhtml < 100;
  3611. /* add WEBO Site SpeedUp spot */
  3612. if (!empty($this->options['page']['spot'])) {
  3613. $this->content .= '<!--WSS-->';
  3614. }
  3615. /* add info about client side load speed */
  3616. if ($this->debug_mode) {
  3617. $this->content = preg_replace("@(<head([^e][^>]*>|>))@is", "$1" .
  3618. "<script type=\"text/javascript\">//<![CDATA[\n" .
  3619. (empty($this->options['page']['counter']) ? '__WSS=(new Date()).getTime();' : '') .
  3620. "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}\n//]]></script>", $this->content);
  3621. }
  3622. $stamp = '';
  3623. if (!empty($this->options['page']['sprites']) && empty($this->options['css']['minify']) && empty($this->options['javascript']['minify'])) {
  3624. $stamp .= '@@@WSSREADY@@@';
  3625. }
  3626. /* add WEBO Site SpeedUp stamp */
  3627. if (!empty($this->options['page']['footer'])) {
  3628. $style = empty($this->options['page']['footer_style']) ? '' :
  3629. ' style="' . $this->options['page']['footer_style'] . '"';
  3630. $title = empty($this->options['page']['footer_text']) ? '' :
  3631. ' title="' . $this->options['page']['footer_text'] . '"';
  3632. $text = empty($this->options['page']['footer_text']) ||
  3633. !empty($this->options['page']['footer_image']) ? '' : $this->options['page']['footer_text'];
  3634. /* place or not image? */
  3635. if (empty($this->options['page']['footer_image'])) {
  3636. $background_image = $background_style = '';
  3637. } else {
  3638. $background_image = $this->options['css']['cachedir_relative'] . $this->options['page']['footer_image'];
  3639. $image_style =
  3640. 'display:block;text-decoration:none;width:100px;height:100px;';
  3641. if (in_array($this->ua_mod, $this->ies)) {
  3642. $background_style = $image_style .
  3643. 'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=' .
  3644. $background_image .
  3645. ',sizingMethod=\'scale\')';
  3646. } else {
  3647. $background_style = $image_style .
  3648. 'background:url(' .
  3649. $background_image .
  3650. ')';
  3651. }
  3652. $background_style = ' style="' . $background_style . '"';
  3653. }
  3654. /* choose between link or span */
  3655. if (empty($text)) {
  3656. $el = 'a href="http://www.webogroup.com/" rel="nofollow"';
  3657. $el_close = 'a';
  3658. } else {
  3659. $el = $el_close = 'span';
  3660. }
  3661. /* finally from stamp */
  3662. $stamp .= '<div' .
  3663. $style .
  3664. '><' .
  3665. $el .
  3666. $title .
  3667. $background_style .
  3668. '>'.
  3669. $text .
  3670. '</' .
  3671. $el_close .
  3672. '></div>';
  3673. }
  3674. /* add WEBO Site SpeedUp page load counter, ideas from
  3675. http://www.optimisationbeacon.com/analytics/track-page-load-times-with-google-analytics-asynchronous-script/
  3676. http://www.panalysis.com/tracking-webpage-load-times.php
  3677. */
  3678. if (!empty($this->options['page']['counter'])) {
  3679. $stamp .= '<script type="text/javascript">//<![CDATA[' . "\n" . '(function(){window[/*@cc_on !@*/0?"attachEvent":"addEventListener"](/*@cc_on "on"+@*/"load",function(){var a=0,c=document.location;if(typeof _gat!=="undefined"){a=_gat._getTracker("'.
  3680. $this->options['page']['counter'] .
  3681. '")}if(typeof _gaq!=="undefined"){a=_gaq._getAsyncTracker();a._setAccount("'.
  3682. $this->options['page']['counter'] .
  3683. '")}if(a){b=(new Date()).getTime()-__WSS;a._trackEvent("Page Load Time (WEBO)",50*Math.round(b/50)+"ms",c.pathname+c.search,b)}},false)})()' . "\n//]]></script>";
  3684. }
  3685. /* Add script to check gzip possibility */
  3686. if (!empty($options['gzip_cookie']) && empty($_COOKIE['_wo_gzip_checked']) && empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
  3687. $stamp .= '<script type="text/javascript" src="' . $options['cachedir_relative'] . 'index.php"></script>';
  3688. }
  3689. if ($stamp) {
  3690. if ($this->premium > 1) {
  3691. $stamp = '<!--noindex-->' . $stamp . '<!--/noindex-->';
  3692. }
  3693. if ($this->options['page']['html_tidy'] &&
  3694. ($bodypos = strpos($this->content, '</body>'))) {
  3695. $this->content = substr_replace($this->content,
  3696. $stamp, $bodypos, 0);
  3697. } elseif ($this->options['page']['html_tidy'] &&
  3698. ($bodypos = strpos($this->content, '</BODY>'))) {
  3699. $this->content = substr_replace($this->content,
  3700. $stamp, $bodypos, 0);
  3701. } else {
  3702. $this->content = preg_replace("@</body>@i",
  3703. $stamp . "$0", $this->content);
  3704. /* a number of engines doesn't set </body> */
  3705. if (!strpos($this->content, $stamp)) {
  3706. $this->content .= $stamp;
  3707. }
  3708. }
  3709. }
  3710. }
  3711. }
  3712. /**
  3713. * Removes conditional comments for MSIE 5-9
  3714. *
  3715. **/
  3716. function remove_conditional_comments ($source) {
  3717. if (!empty($this->ua_mod)) {
  3718. /* preliminary strpos saves about 50% of CPU */
  3719. if (strpos($source, 'IE]>') !== false) {
  3720. $source = preg_replace("@<!--\[if \(?IE\)?\]>(.*?)<!\[endif\]-->@s", "$1", $source);
  3721. }
  3722. for ($version = $this->min_ie_version; $version < $this->max_ie_version; $version++) {
  3723. /* detect */
  3724. if ($this->ua_mod == ".ie" . $version) {
  3725. /* detect equality */
  3726. if (strpos($source, 'IE ' . $version . ']>') !== false) {
  3727. $source = preg_replace("@<!--\[if ((gte|lte) )?\(?IE " . $version . "[^\]]*\)?\]>(.*?)<!\[endif\]-->@s", "$3", $source);
  3728. }
  3729. /* detect lesser versions */
  3730. for ($i = $this->min_ie_version; $i < $version; $i++) {
  3731. if (strpos($source, 'IE ' . $i . ']>') !== false) {
  3732. $source = preg_replace("@<!--\[if gte? IE " . $i . "[^\]]*\]>(.*?)<!\[endif\]-->@s", "$1", $source);
  3733. }
  3734. }
  3735. /* detect greater versions */
  3736. for ($i = $version + 1; $i < $this->max_ie_version; $i++) {
  3737. if (strpos($source, 'IE ' . $i . ']>') !== false) {
  3738. $source = preg_replace("@<!--\[if lte? IE " . $i . "[^\]]*\]>(.*?)<!\[endif\]-->@s", "$1", $source);
  3739. }
  3740. }
  3741. }
  3742. }
  3743. if (!empty($this->options['plain_string'])) {
  3744. while (($a = strpos($source, '<!--[if')) !== false && ($b = strpos($source, '[endif]-->')) !== false) {
  3745. $source = substr($source, 0, $a) . substr($source, $b+10);
  3746. }
  3747. } else {
  3748. $source = preg_replace("@<!--\[if.*?\[endif\]-->@s", "", $source);
  3749. }
  3750. } elseif (!$this->options['uniform_cache']) {
  3751. if (!empty($this->options['plain_string'])) {
  3752. while ((($a = strpos($source, '<!--[if IE')) !== false || ($a = strpos($source, '<!--[if lt')) !== false || ($a = strpos($source, '<!--[if gt')) !== false) && ($b = strpos($source, '[endif]-->')) !== false) {
  3753. $source = substr($source, 0, $a) . substr($source, $b+10);
  3754. }
  3755. } else {
  3756. $source = preg_replace("@<!--\[if ((lt|gt)e? )?IE.*?\[endif\]-->@s", "", $source);
  3757. }
  3758. }
  3759. return $source;
  3760. }
  3761. /**
  3762. * Converts sinlge path to the absolute one
  3763. *
  3764. **/
  3765. function convert_path_to_absolute ($file, $path, $leave_querystring = false, $css_images = false) {
  3766. $endfile = '';
  3767. $root = $this->options['document_root'];
  3768. if (!empty($path['file'])) {
  3769. $endfile = $path['file'];
  3770. }
  3771. if (!$leave_querystring) {
  3772. $file = $this->strip_querystring($file);
  3773. $endfile = $this->strip_querystring($endfile);
  3774. }
  3775. /* Don't touch data URIs, or mhtml:, or external files */
  3776. if (preg_match("!^(https?|data|mhtml):!is", $file) && !preg_match("@//(www\.)?". $this->host_escaped ."@is", $file)) {
  3777. return false;
  3778. }
  3779. $absolute_path = $file;
  3780. /* external source file */
  3781. if (preg_match("!^https?://!", $endfile) && !preg_match("!^https?://!", $file)) {
  3782. if (substr($file, 0, 1) != '/') {
  3783. $absolute_path = preg_replace("@[^\/]+$@", "", $endfile) . $absolute_path;
  3784. } else {
  3785. $absolute_path = preg_replace("@(https?://[^\/]+).*@", "$1", $endfile) . $absolute_path;
  3786. }
  3787. } else {
  3788. /* Not absolute or external */
  3789. if (substr($file, 0, 1) != '/' && !preg_match("!^https?://!", $file)) {
  3790. /* add base */
  3791. if (!empty($this->basehref) && !$css_images) {
  3792. $absolute_path = $this->view->unify_dir_separator($this->basehref . $file);
  3793. } else {
  3794. /* add relative directory */
  3795. if (substr($endfile, 0, 1) != "/" && !preg_match("!^https?://!", $endfile)) {
  3796. $endfile = preg_replace("@([^\?&]*/).*@", "$1", $_SERVER['REQUEST_URI']) . $endfile;
  3797. }
  3798. $full_path_to_image = preg_replace("@[^/\\\]+$@", "", $endfile);
  3799. $absolute_path = $this->view->unify_dir_separator($full_path_to_image . $file);
  3800. }
  3801. } elseif ($endfile && substr($endfile, 0, 1) != "/" && !preg_match("!^https?://!", $endfile)) {
  3802. $absolute_path = $this->view->unify_dir_separator(preg_replace("@[^/\\\]+$@", "", $file) . $endfile);
  3803. }
  3804. }
  3805. $apath_stripped = $this->strip_querystring($absolute_path);
  3806. $query = str_replace($apath_stripped, "", $absolute_path);
  3807. $correct_path = strpos(realpath($root . $apath_stripped), $root) !== false ?
  3808. str_replace($root, "/", str_replace('\\', '/', realpath($root . $apath_stripped))) :
  3809. str_replace('\\', '/', $apath_stripped);
  3810. /* remove HTTP host from absolute URL */
  3811. return strpos($absolute_path, "http") !== false || strpos($absolute_path, "HTTP") !== false ?
  3812. preg_replace("!https?://(www\.)?". $this->host_escaped ."/+!i", "/", $absolute_path) :
  3813. $correct_path . $query;
  3814. }
  3815. /**
  3816. * Finds background images in the CSS and converts their paths to absolute
  3817. *
  3818. **/
  3819. function convert_paths_to_absolute ($content, $path, $leave_querystring = false, $css_images = false) {
  3820. preg_match_all("!url\s*\(\s*['\"]?(.*?)['\"]?\s*\)!is", $content, $matches);
  3821. if(count($matches[1]) > 0) {
  3822. foreach($matches[1] as $file) {
  3823. if (strpos($file, '.eot?') || strpos($file, '.svg#')) {
  3824. $leave_querystring = true;
  3825. }
  3826. $absolute_path = $this->convert_path_to_absolute($file, $path, $leave_querystring, $css_images);
  3827. if (!empty($absolute_path)) {
  3828. /* add quotes if there is not plain URL */
  3829. if (strpos($absolute_path, ' ')) {
  3830. $absolute_path = "'" . $absolute_path . "'";
  3831. }
  3832. /* replace path in initial CSS */
  3833. $content = preg_replace("@url\s*\(\s*['\"]?" .
  3834. str_replace("?", "\?", $file) .
  3835. "['\"]?\s*\)@is", "url(" . $absolute_path . ")", $content);
  3836. }
  3837. }
  3838. }
  3839. /* AlphaImageLoader */
  3840. if (strpos($content, 'src=') !== false) {
  3841. preg_match_all("!src=['\"](.*?)['\"],!is", $content, $matches);
  3842. foreach($matches[1] as $file) {
  3843. $absolute_path = $this->convert_path_to_absolute($file, $path, $leave_querystring, $css_images);
  3844. if (!empty($absolute_path)) {
  3845. $content = preg_replace("!src=['\"]" . $file . "['\"]!", "src='" . $absolute_path . "'", $content);
  3846. }
  3847. }
  3848. }
  3849. return $content;
  3850. }
  3851. /**
  3852. * Convert all background image to CSS Sprites if possible
  3853. **/
  3854. function convert_css_sprites ($content, $options, $css_url) {
  3855. /* try to get and increase memory limit */
  3856. $memory_limit = round(str_replace("G", "000000000", str_replace("M", "000000", str_replace("K", "000", @ini_get('memory_limit')))));
  3857. /* 64M must enough for any operations with images. I hope... */
  3858. if ($memory_limit < 128000000) {
  3859. @ini_set('memory_limit', '128M');
  3860. }
  3861. $content = preg_replace("!/\*.*?\*/!is", "", $content);
  3862. $dir = @getcwd();
  3863. @chdir($options['cachedir']);
  3864. $css_sprites = new css_sprites($content, array(
  3865. 'root_dir' => $options['installdir'],
  3866. 'current_dir' => $options['cachedir'],
  3867. 'html_cache' => $this->options['page']['cachedir'],
  3868. 'website_root' => $this->options['document_root'],
  3869. 'truecolor_in_jpeg' => $options['truecolor_in_jpeg'],
  3870. 'aggressive' => $options['aggressive'],
  3871. 'no_ie6' => $options['no_ie6'],
  3872. 'ignore' => $options['css_sprites_ignore'],
  3873. 'ignore_list' => $options['css_sprites_exclude'],
  3874. 'partly' => $options['css_sprites_partly'],
  3875. 'extra_space' => $options['css_sprites_extra_space'],
  3876. 'expires_rewrite' => $options['css_sprites_expires_rewrite'],
  3877. 'cache_images' => $this->options['page']['cache_images'],
  3878. 'cache_images_rewrite' => $this->options['page']['far_future_expires_rewrite'],
  3879. 'data_uris' => $options['data_uris'],
  3880. 'data_uris_separate' => $options['data_uris_separate'],
  3881. 'data_uris_size' => $options['data_uris_size'],
  3882. 'data_uris_ignore_list' => $options['data_uris_exclude'],
  3883. 'mhtml' => $options['mhtml'],
  3884. 'mhtml_size' => $options['mhtml_size'],
  3885. 'mhtml_ignore_list' => $options['mhtml_exclude'],
  3886. 'css_url' => $css_url,
  3887. 'dimensions_limited' => $options['dimensions_limited'],
  3888. 'no_css_sprites' => !$options['css_sprites'],
  3889. 'multiple_hosts' => empty($options['parallel']) ? array() : explode(" ", $options['parallel_hosts']),
  3890. 'user_agent' => $this->ua_mod,
  3891. 'punypng' => $options['punypng'],
  3892. 'restore_properties' => $options['css_restore_properties'],
  3893. 'ftp_access' => $this->options['page']['parallel_ftp'],
  3894. 'http_host' => $this->options['page']['host'],
  3895. 'https_host' => $this->options['page']['parallel_https'],
  3896. 'uniform_cache' => $this->options['uniform_cache']
  3897. ));
  3898. @chdir($dir);
  3899. return $css_sprites->process();
  3900. }
  3901. /**
  3902. * Convert all background image to data:URI / mhtml / CDN
  3903. **/
  3904. function convert_data_uri ($content, $options, $css_url) {
  3905. $dir = @getcwd();
  3906. @chdir($options['cachedir']);
  3907. $compressed = '';
  3908. preg_match_all("!([^\{\}@]+)\{([^\}]*?background(-image|-position|-color|-repeat)?[^\}]+?)\}!is", preg_replace("!@media print\{.*?\}\}!", "", $content), $ims, PREG_SET_ORDER);
  3909. $imgs = array();
  3910. /* form correct array with background properties */
  3911. foreach ($ims as $im) {
  3912. preg_match_all("!(background(-image|-position|-color|-repeat)?):([^;]*?(url[^;]*?\([^\)]+\))?[^;]*);?!is", $im[2], $is, PREG_SET_ORDER);
  3913. foreach ($is as $i) {
  3914. $imgs[] = array($im[0], $im[1], $i[1], '', $i[3]);
  3915. }
  3916. }
  3917. if (count($imgs)) {
  3918. $replaced = array();
  3919. $replaced_base64 = array();
  3920. $mhtml = in_array($this->ua_mod, $this->ies) && $options['mhtml'];
  3921. $mhtml_code = "/*\nContent-Type:multipart/related;boundary=\"_\"";
  3922. $location = 0;
  3923. $sels = '';
  3924. $data_uri_exclude = explode(" ", $options['data_uris_exclude']);
  3925. $mhtml_exclude = explode(" ", $options['mhtml_exclude']);
  3926. foreach ($imgs as $image) {
  3927. $base64 = '';
  3928. if (strpos(strtolower($image[4]), "url") !== false || strpos(strtolower($image[4]), "URL") !== false) {
  3929. /* strip all after ) - to get URL */
  3930. $rule = preg_replace("@((url\([^\)]+\)(,\s*)?)+).*$@is", "$1", $image[4]);
  3931. $css_image = preg_replace("@^.*((url\([^\)]+\)(,\s*)?)+)@is", "$1", $rule);
  3932. /* add rules after ) but before ; */
  3933. $tale = preg_replace("@;.*$@is", "", str_replace($rule, '', $image[4]));
  3934. /* add rules to URL */
  3935. $rule .= $tale;
  3936. $rule_initial = str_replace(str_replace($rule, '', $image[4]), '', $image[0]);
  3937. $image_saved = $css_image;
  3938. if (empty($replaced[$image_saved])) {
  3939. $css_image = explode(',', str_replace('base64,', '###', $css_image));
  3940. $images = array();
  3941. $b64 = array();
  3942. foreach ($css_image as $im) {
  3943. $arr = $this->convert_single_background(trim($im), $location, $css_url, $data_uri_exclude, $mhtml_exclude, $mhtml, $options);
  3944. $images[] = $arr[0];
  3945. $location = $arr[1];
  3946. $b64[] = $arr[2];
  3947. }
  3948. if (count($images)) {
  3949. $css_image = 'url('. implode('),url(', $images) . ')';
  3950. if (implode('', $b64)) {
  3951. $base64 = 'url('. implode('),url(', $b64) . ')';
  3952. }
  3953. } else {
  3954. $css_image = $image_saved;
  3955. $base64 = '';
  3956. }
  3957. $replaced[$image_saved] = $css_image;
  3958. $replaced_base64[$image_saved] = $base64;
  3959. } else {
  3960. $base64 = $replaced_base64[$image_saved];
  3961. }
  3962. $content = str_replace($rule, str_replace($image_saved, $replaced[$image_saved], $rule), $content);
  3963. if (!$mhtml && $base64) {
  3964. $compressed .= $image[1] .
  3965. '{' .
  3966. $image[2] .
  3967. ':' .
  3968. str_replace($image_saved, $base64, $rule) .
  3969. '}';
  3970. }
  3971. /* * html, body* matches Chrome, using *property for IE7- */
  3972. if ($this->options['uniform_cache'] && !empty($base64) && $base64 != 'url()') {
  3973. $sels .= $image[1] .
  3974. '{*' .
  3975. $image[2] .
  3976. ':' .
  3977. $image[4] .
  3978. '}';
  3979. }
  3980. } else {
  3981. /* get all background rules w/o URL and apply them */
  3982. preg_match_all("@(background(-image|-position|-color|-repeat)?\s*):([^\}]+?)[;\}]@is", $image[0], $backs, PREG_SET_ORDER);
  3983. if (is_array($backs)) {
  3984. $b = '';
  3985. foreach ($backs as $back) {
  3986. $b .= $back[1] . ':' . $back[3] . ';';
  3987. }
  3988. $compressed .= $image[1] . '{' . $b . '}';
  3989. }
  3990. }
  3991. }
  3992. if ($mhtml && !empty($mhtml_code)) {
  3993. $compressed .= $mhtml_code . "\n\n--_--\n*/";
  3994. }
  3995. /* add IE6/7 selectors */
  3996. $content .= $sels;
  3997. /* clear content from junk */
  3998. $content = preg_replace("@(background(-image)?:)?url\(\)([;\}])@is", "$3", $content);
  3999. $content = preg_replace("@(background(-image)?:)?url\(\)(\s|;)?(\})?@is", "$1$4", $content);
  4000. $content = preg_replace("@[^\{\}]+\{\}@is", "", $content);
  4001. }
  4002. @chdir($dir);
  4003. return array($content, $compressed);
  4004. }
  4005. /**
  4006. * Convert single background image to data:URI / mhtml / CDN
  4007. **/
  4008. function convert_single_background ($css_image, $location, $css_url, $data_uri_exclude, $mhtml_uri_exclude, $mhtml, $options) {
  4009. $css_image = trim(substr($css_image, 4, strlen($css_image) - 5));
  4010. $image_saved = $css_image;
  4011. /* remove quotes */
  4012. if ($css_image{0} == '"' || $css_image{0} == "'") {
  4013. $css_image = substr($css_image, 1, strlen($css_image) - 2);
  4014. }
  4015. $chunks = explode(".", $css_image);
  4016. $extension = str_replace('jpg', 'jpeg', strtolower(array_pop($chunks)));
  4017. /* download external images */
  4018. if (strpos($css_image, "//") !== false) {
  4019. $css_image = $this->get_remote_file($css_image, $extension);
  4020. }
  4021. $css_image = $css_image{0} == '/' ? $this->options['document_root'] . substr($css_image, 1) : $options['cachedir'] . $css_image;
  4022. $chunks = explode("/", $css_image);
  4023. $filename = array_pop($chunks);
  4024. $base64 = '';
  4025. if (!@is_file($css_image) ||
  4026. in_array($extension, array('htc', 'cur', 'eot', 'ttf', 'svg', 'otf', 'woff')) ||
  4027. strpos($css_image, "mhtml:") !== false ||
  4028. strpos($css_image, "data:") !== false) {
  4029. $css_image = $image_saved;
  4030. } else {
  4031. $encoded = base64_encode($this->file_get_contents($css_image));
  4032. $next = !$mhtml && !$options['data_uris'];
  4033. if ($mhtml) {
  4034. if (@filesize($css_image) < $options['mhtml_size'] &&
  4035. !in_array($filename, $mhtml_uri_exclude) && !empty($encoded)) {
  4036. $mhtml_code .= "\n\n--_\nContent-Location:" .
  4037. $location .
  4038. "\nContent-Transfer-Encoding:base64\n\n" .
  4039. $encoded;
  4040. $css_image = 'mhtml:' . $css_url . '!' . $location;
  4041. $location++;
  4042. } else {
  4043. $next = 1;
  4044. }
  4045. } elseif ($options['data_uris']) {
  4046. if (@filesize($css_image) < $options['data_uris_size'] &&
  4047. !in_array($filename, $data_uri_exclude) && !empty($encoded)) {
  4048. $css_image = '';
  4049. $base64 = 'data:image/' . $extension . ';base64,' . $encoded;
  4050. } else {
  4051. $next = 1;
  4052. }
  4053. }
  4054. /* add multiple hosts/wo.static.php */
  4055. if ($next) {
  4056. $img = str_replace($this->options['document_root'], '/', $css_image);
  4057. if ($this->options['page']['parallel'] && !empty($this->options['page']['parallel_hosts'])) {
  4058. $hosts = explode(" ", $this->options['page']['parallel_hosts']);
  4059. $host = $hosts[strlen($img)%count($hosts)];
  4060. /* if we have dot in the distribution host - it's a domain name */
  4061. if (!$this->https || !($new_host = $this->options['page']['parallel_https'])) {
  4062. $new_host = $host .
  4063. ((strpos($host, '.') === false) ? '.' . preg_replace("/^www\./", "", $_SERVER['HTTP_HOST']): '');
  4064. }
  4065. $css_image = "//" . $new_host . $img;
  4066. } elseif ($this->options['page']['far_future_expires_rewrite']) {
  4067. if (in_array($extension, array('bmp', 'gif', 'png', 'ico', 'jpeg'))) {
  4068. /* do not touch dynamic images -- how we can handle them? */
  4069. $css_image = $this->options['page']['cachedir_relative'] . 'wo.static.php?' . $img;
  4070. }
  4071. } else {
  4072. $css_image = $image_saved;
  4073. }
  4074. }
  4075. /* add quotes for background images with spaces */
  4076. if (strpos($css_image, ' ')) {
  4077. $css_image = "'" . $css_image . "'";
  4078. }
  4079. }
  4080. return array(str_replace('###', 'base64,', $css_image), $location, $base64);
  4081. }
  4082. /**
  4083. * Converts REQUEST_URI to cached file name
  4084. *
  4085. **/
  4086. function convert_request_uri ($uri = false) {
  4087. $uri = $uri ? $uri : preg_replace("@index\.php$@", "", $_SERVER['REQUEST_URI']);
  4088. $exclude = trim($this->options['page']['cache_params']);
  4089. $exclude = ($exclude ? $exclude . ' ' : '') . 'utm_[^=]+ _openstat gclid';
  4090. $uri = preg_replace("@(" . str_replace(" ", "|", $exclude) . ")=[^&\?]+[\?&]?@", "", $uri);
  4091. /* replace /, ?, & with - */
  4092. $uri = str_replace(
  4093. array('/?', '/', '?', '&', "'", '^', '%', '"', '<', '>', '$'),
  4094. array('+', '+', '+', '+', '', '', '', '', '', '', ''),
  4095. $uri);
  4096. return $uri;
  4097. }
  4098. /**
  4099. * Downloads remote files to include
  4100. *
  4101. **/
  4102. function get_remote_file ($file, $tag = "link", $recursion = 0) {
  4103. /* check if we already have this file */
  4104. if (preg_match("/\/wo[abcdef0-9]+$/", $file)) {
  4105. return preg_replace("/.*(wo[abcdef0-9]+$)/", "$1", $file);
  4106. }
  4107. $current_directory = @getcwd();
  4108. /* dirty fix for buggy getcwd call */
  4109. if ($current_directory === '/') {
  4110. $current_directory = $this->options['css']['installdir'];
  4111. }
  4112. if (function_exists('curl_init')) {
  4113. if ($tag == 'javascript') {
  4114. @chdir($this->options['javascript']['cachedir']);
  4115. } else {
  4116. @chdir($this->options['css']['cachedir']);
  4117. }
  4118. $ua = '';
  4119. if (empty($_SERVER['HTTP_USER_AGENT']) && empty($this->options['uniform_cache'])) {
  4120. if (($a = strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ')) !== false) {
  4121. $ua = 'MSIE ' . substr($_SERVER['HTTP_USER_AGENT'], $a+5, 3);
  4122. } elseif (($a = strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome/')) !== false) {
  4123. $ua = 'Chrome ' . substr($_SERVER['HTTP_USER_AGENT'], $a+7, 3);
  4124. } elseif (($a = strpos($_SERVER['HTTP_USER_AGENT'], 'Safari/')) !== false) {
  4125. $ua = 'Safari ' . substr($_SERVER['HTTP_USER_AGENT'], $a+7, 3);
  4126. } elseif (($a = strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox ')) !== false) {
  4127. $ua = 'Firefox ' . substr($_SERVER['HTTP_USER_AGENT'], $a+8, 3);
  4128. } elseif (($a = strpos($_SERVER['HTTP_USER_AGENT'], 'Opera/')) !== false) {
  4129. $ua = 'Opera ' . substr($_SERVER['HTTP_USER_AGENT'], $a+6, 3);
  4130. }
  4131. }
  4132. $ua = $ua ? $ua : "Mozilla/5.0 (WEBO Site SpeedUp; http://www.webogroup.com/) Firefox 3.6";
  4133. $return_filename = 'wo' . md5($file . $ua) . '.' . ($tag == 'link' ? 'css' : ($tag == 'script' ? 'js' : $tag));
  4134. if (@file_exists($return_filename)) {
  4135. $timestamp = @filemtime($return_filename);
  4136. } else {
  4137. $timestamp = 0;
  4138. }
  4139. /* prevent download more than 1 time a day */
  4140. if (!empty($timestamp) && $timestamp + 86400 > $this->time) {
  4141. @chdir($current_directory);
  4142. return $return_filename;
  4143. }
  4144. $return_filename_headers = $return_filename . '.headers';
  4145. /* try to download remote file */
  4146. $ch = @curl_init((strpos($file, '//') == 0 ? 'http' . $this->https . ':' : '') . $file);
  4147. $fph = @fopen($return_filename_headers, "w");
  4148. if ($ch) {
  4149. @curl_setopt($ch, CURLOPT_HEADER, 0);
  4150. @curl_setopt($ch, CURLOPT_USERAGENT, empty($_SERVER['HTTP_USER_AGENT']) ? $ua : $_SERVER['HTTP_USER_AGENT']);
  4151. @curl_setopt($ch, CURLOPT_ENCODING, "deflate");
  4152. @curl_setopt($ch, CURLOPT_REFERER, 'http://' . $this->options['host']);
  4153. if (!@ini_set('open_basedir')) {
  4154. @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  4155. }
  4156. @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  4157. @curl_setopt($ch, CURLOPT_WRITEHEADER, $fph);
  4158. if (!empty($this->options['page']['htaccess_username']) && !empty($this->options['page']['htaccess_password'])) {
  4159. @curl_setopt($ch, CURLOPT_USERPWD, $this->options['page']['htaccess_username'] . ':' . $this->options['page']['htaccess_password']);
  4160. }
  4161. $contents = @curl_exec($ch);
  4162. @file_put_contents($return_filename, $contents);
  4163. @curl_close($ch);
  4164. @fclose($fph);
  4165. /* check if we deal with 404 error, remove result */
  4166. $headers = $this->file_get_contents($return_filename_headers);
  4167. /* Fix non-supported 301/302/303 redirect */
  4168. if (strpos($headers, 'Location:') && strpos($headers, "HTTP/1.1 200") === false && strpos($headers, "HTTP/1.0 200") === false && $recursion < 10) {
  4169. @unlink($return_filename);
  4170. @unlink($return_filename_headers);
  4171. return $this->get_remote_file(preg_replace("!.*\nLocation:\s*([^\r\n]+).*!is", "$1", $headers), $tag, $recursion++);
  4172. }
  4173. if (empty($contents) ||
  4174. in_array(substr($headers, 0, 12),
  4175. array('HTTP/1.1 400', 'HTTP/1.1 400', 'HTTP/1.1 404', 'HTTP/1.0 404', 'HTTP/0.1 404', 'HTTP/0.9 404', 'HTTP/1.1 502', 'HTTP/1.0 502', 'HTTP/0.1 502', 'HTTP/0.9 502', 'HTTP/1.1 500', 'HTTP/1.0 500', 'HTTP/0.1 500', 'HTTP/0.9 500')) ||
  4176. ($tag == 'link' && preg_match("!<body!is", $contents))) {
  4177. @unlink($return_filename);
  4178. $return_filename = '';
  4179. } else {
  4180. /* try to replace background images to local ones */
  4181. if (!empty($contents) && $tag == 'link') {
  4182. /* correct background-images in CSS file */
  4183. $this->write_file($return_filename, $this->convert_paths_to_absolute($contents, array('file' => $file)));
  4184. }
  4185. }
  4186. @unlink($return_filename . '.headers');
  4187. @chdir($current_directory);
  4188. return $return_filename;
  4189. }
  4190. }
  4191. @chdir($current_directory);
  4192. return false;
  4193. }
  4194. /**
  4195. * Sets User Agent modificator
  4196. *
  4197. **/
  4198. function set_user_agent () {
  4199. $this->ua_mod = '';
  4200. if (!$this->options['performance']['uniform_cache']) {
  4201. /* min. supported IE version */
  4202. $this->min_ie_version = 5;
  4203. /* max. supported IE version */
  4204. $this->max_ie_version = 12;
  4205. if (strpos($this->ua, 'MSIE') && strpos($this->ua, 'Opera') === false) {
  4206. for ($version = $this->min_ie_version; $version < $this->max_ie_version; $version++) {
  4207. if (strpos($this->ua, 'MSIE ' . $version)) {
  4208. $this->ua_mod = '.ie' . $version;
  4209. }
  4210. }
  4211. }
  4212. /* check for mobile agents */
  4213. if (empty($this->ua_mod)) {
  4214. $mobiles = array(
  4215. 'Android',
  4216. 'BlackBerry',
  4217. 'HTC',
  4218. 'iPhone',
  4219. 'iPod',
  4220. 'LG',
  4221. 'MOT',
  4222. 'Mobile',
  4223. 'NetFront',
  4224. 'Nokia',
  4225. 'Opera Mini',
  4226. 'Palm',
  4227. 'PPC',
  4228. 'SAMSUNG',
  4229. 'Smartphone',
  4230. 'SonyEricsson',
  4231. 'Symbian',
  4232. 'UP.Browser',
  4233. 'webOS');
  4234. $j = 0;
  4235. /* strpos here is 2.5x faster than stristr and 6x faster than regexp */
  4236. while (!empty($mobiles[$j]) && strpos($this->ua, $mobiles[$j++]) === false) {}
  4237. if ($j != count($mobiles)) {
  4238. $this->ua_mod = '.ma';
  4239. }
  4240. }
  4241. }
  4242. }
  4243. /**
  4244. * Replaces html entities with amps
  4245. *
  4246. */
  4247. function resolve_amps ($str) {
  4248. return str_replace(array('&amp;', '&#38;', '&#038;', ''), array('&', '&', '&', ''), $str);
  4249. }
  4250. /**
  4251. * Determines cache engine and create instance of it
  4252. *
  4253. **/
  4254. function start_cache_engine () {
  4255. $cache_engines = array('0' => 'files',
  4256. '1' => 'memcached',
  4257. '2' => 'apc',
  4258. '3' => 'xcache'
  4259. );
  4260. $cache_engines_options = array('0' => array('cache_dir' => $this->options['page']['cachedir']),
  4261. '1' => array('server' => @$this->options['page']['cache_engine_options']),
  4262. '2' => '',
  4263. '3' => ''
  4264. );
  4265. if (!empty($cache_engines[$this->options['page']['cache_engine']])) {
  4266. $engine_name = 'webo_cache_' . $cache_engines[$this->options['page']['cache_engine']];
  4267. $engine_num = $this->options['page']['cache_engine'];
  4268. } else {
  4269. $engine_name = 'webo_cache_' . $cache_engines[0];
  4270. $engine_num = 0;
  4271. }
  4272. include_once($this->options['page']['installdir'] . 'libs/php/cache_engine.php');
  4273. $this->cache_engine = new $engine_name ($cache_engines_options[$engine_num]);
  4274. }
  4275. /**
  4276. * Deletes cached HTML files determined by patterns. Just an interface for cache_engine delete_entries method.
  4277. *
  4278. **/
  4279. function clear_html_cache ($patterns) {
  4280. if (!empty($patterns)) {
  4281. $this->cache_engine->delete_entries($patterns);
  4282. }
  4283. }
  4284. /**
  4285. * Converts given URL to another with base URI (<base> tag / header)
  4286. *
  4287. **/
  4288. function convert_basehref ($uri) {
  4289. /* check if BASE URI is given */
  4290. if (!empty($this->basehref) &&
  4291. /* convert only non-external URI */
  4292. strpos($uri, '//') !== 0 && !strpos($uri, '://')) {
  4293. if ($uri{0} == '/') {
  4294. return $this->basehref_url . $uri;
  4295. } else {
  4296. return $this->basehref . $uri;
  4297. }
  4298. } else {
  4299. return $uri;
  4300. }
  4301. }
  4302. /**
  4303. * Removes quotes from file content if magic_quotes_runtime enabled
  4304. *
  4305. **/
  4306. function file_get_contents ($file) {
  4307. if (get_magic_quotes_runtime()) {
  4308. return stripslashes(@file_get_contents($file));
  4309. } else {
  4310. return @file_get_contents($file);
  4311. }
  4312. }
  4313. /**
  4314. * Removes markers for styles and BOM
  4315. *
  4316. **/
  4317. function clear_trash () {
  4318. $this->content = str_replace(array("@@@WSSSTYLES@@@", "@@@WSSSCRIPT@@@", "@@@WSSREADY@@@", ""), "", $this->content);
  4319. }
  4320. /**
  4321. * Finishes all operations
  4322. *
  4323. **/
  4324. function di ($clean = 0) {
  4325. if (!$clean) {
  4326. while (@ob_end_clean());
  4327. }
  4328. if (function_exists('fastcgi_finish_request')) {
  4329. fastcgi_finish_request();
  4330. }
  4331. die();
  4332. }
  4333. } // end class
  4334. ?>