PageRenderTime 25ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/system/expressionengine/controllers/cp/javascript.php

https://bitbucket.org/tdevonshire/hoolux
PHP | 625 lines | 388 code | 89 blank | 148 comment | 45 complexity | a0623dde70d093546e2ad406132a2115 MD5 | raw file
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * ExpressionEngine - by EllisLab
  4. *
  5. * @package ExpressionEngine
  6. * @author EllisLab Dev Team
  7. * @copyright Copyright (c) 2003 - 2012, EllisLab, Inc.
  8. * @license http://ellislab.com/expressionengine/user-guide/license.html
  9. * @link http://ellislab.com
  10. * @since Version 2.0
  11. * @filesource
  12. */
  13. // ------------------------------------------------------------------------
  14. /**
  15. * ExpressionEngine CP CSS Loading Class
  16. *
  17. * @package ExpressionEngine
  18. * @subpackage Control Panel
  19. * @category Control Panel
  20. * @author EllisLab Dev Team
  21. * @link http://ellislab.com
  22. */
  23. class Javascript extends CI_Controller {
  24. /**
  25. * Constructor
  26. */
  27. function __construct()
  28. {
  29. parent::__construct();
  30. if ( ! defined('PATH_JQUERY'))
  31. {
  32. if ($this->config->item('use_compressed_js') == 'n')
  33. {
  34. define('PATH_JQUERY', PATH_THEMES.'javascript/src/jquery/');
  35. }
  36. else
  37. {
  38. define('PATH_JQUERY', PATH_THEMES.'javascript/compressed/jquery/');
  39. }
  40. }
  41. $this->lang->loadfile('jquery');
  42. }
  43. // --------------------------------------------------------------------
  44. /**
  45. * Index function
  46. *
  47. * @access public
  48. * @return void
  49. */
  50. function index()
  51. {
  52. $this->load('jquery');
  53. }
  54. // --------------------------------------------------------------------
  55. /**
  56. * Spellcheck iFrame
  57. *
  58. * Used by the Spellcheck crappola
  59. *
  60. * @access public
  61. * @return void
  62. */
  63. function spellcheck_iframe()
  64. {
  65. $this->output->enable_profiler(FALSE);
  66. if ( ! class_exists('EE_Spellcheck'))
  67. {
  68. require APPPATH.'libraries/Spellcheck.php';
  69. }
  70. return EE_Spellcheck::iframe();
  71. }
  72. // --------------------------------------------------------------------
  73. /**
  74. * Spellcheck
  75. *
  76. * Used by the Spellcheck crappola
  77. *
  78. * @access public
  79. * @return void
  80. */
  81. function spellcheck()
  82. {
  83. $this->output->enable_profiler(FALSE);
  84. if ( ! class_exists('EE_Spellcheck'))
  85. {
  86. require APPPATH.'libraries/Spellcheck.php';
  87. }
  88. return EE_Spellcheck::check();
  89. }
  90. // --------------------------------------------------------------------
  91. /**
  92. * Load
  93. *
  94. * Sends jQuery files to the browser
  95. *
  96. * @access public
  97. * @return type
  98. */
  99. function load($loadfile = '')
  100. {
  101. $this->output->enable_profiler(FALSE);
  102. $file = '';
  103. $contents = ''; // needed for css parsing
  104. // trying to load a specific js file?
  105. $loadfile = ($loadfile) ? $loadfile : $this->input->get_post('file');
  106. $package = $this->input->get_post('package');
  107. $loadfile = $this->security->sanitize_filename($loadfile, TRUE);
  108. if ($loadfile == 'ext_scripts')
  109. {
  110. return $this->_ext_scripts();
  111. }
  112. if ($package && $loadfile)
  113. {
  114. $file = PATH_THIRD.$package.'/javascript/'.$loadfile.'.js';
  115. }
  116. elseif ($loadfile == 'jquery')
  117. {
  118. $file = PATH_JQUERY.'jquery.js';
  119. }
  120. elseif ($loadfile == '')
  121. {
  122. if (($plugin = $this->input->get_post('plugin')) !== FALSE)
  123. {
  124. $file = PATH_JQUERY.'plugins/'.$plugin.'.js';
  125. }
  126. elseif (($ui = $this->input->get_post('ui')) !== FALSE)
  127. {
  128. $file = PATH_JQUERY.'ui/jquery.ui.'.$ui.'.js';
  129. }
  130. elseif (($effect = $this->input->get_post('effect')) !== FALSE)
  131. {
  132. $file = PATH_JQUERY.'ui/jquery.effect.'.$effect.'.js';
  133. }
  134. }
  135. elseif ($loadfile == 'css')
  136. {
  137. $contents = 'css';
  138. $css_paths = array(
  139. PATH_CP_THEME.$this->session->userdata('cp_theme').'/',
  140. PATH_CP_THEME.'default/'
  141. );
  142. if ($this->session->userdata('cp_theme') == 'default')
  143. {
  144. array_shift($css_paths);
  145. }
  146. foreach ($css_paths as $a_path)
  147. {
  148. $file = $a_path.'css/advanced.css';
  149. if (file_exists($file))
  150. {
  151. break;
  152. }
  153. }
  154. }
  155. else
  156. {
  157. if ($this->config->item('use_compressed_js') == 'n')
  158. {
  159. $file = PATH_THEMES.'javascript/src/'.$loadfile.'.js';
  160. }
  161. else
  162. {
  163. $file = PATH_THEMES.'javascript/compressed/'.$loadfile.'.js';
  164. }
  165. }
  166. if ( ! $file OR ! file_exists($file))
  167. {
  168. if ($this->config->item('debug') >= 1)
  169. {
  170. $this->output->fatal_error(lang('missing_jquery_file'));
  171. }
  172. else
  173. {
  174. return FALSE;
  175. }
  176. }
  177. // Can't do any of this if we're not allowed
  178. // to send any headers
  179. $this->_set_headers($file);
  180. // Grab the file, content length and serve
  181. // it up with the proper content type!
  182. if ($contents == 'css')
  183. {
  184. // File exists and not in client cache - reparse
  185. $contents = $this->_css_javascript($file);
  186. }
  187. else
  188. {
  189. $contents = file_get_contents($file);
  190. }
  191. $this->output->set_header('Content-Length: '.strlen($contents));
  192. $this->output->set_output($contents);
  193. }
  194. // --------------------------------------------------------------------
  195. /**
  196. * Javascript from extensions
  197. *
  198. * This private method is intended for usage by the 'add_global_cp_js' hook
  199. *
  200. * @access private
  201. * @return void
  202. */
  203. function _ext_scripts()
  204. {
  205. $str = '';
  206. /* -------------------------------------------
  207. /* 'cp_js_end' hook.
  208. /* - Add Javascript into a file call at the end of the control panel
  209. /* - Added 2.1.2
  210. */
  211. $str = $this->extensions->call('cp_js_end');
  212. /*
  213. /* -------------------------------------------*/
  214. $this->output->out_type = 'cp_asset';
  215. $this->output->set_header("Content-Type: text/javascript");
  216. $this->output->set_header("Cache-Control: no-cache, must-revalidate");
  217. $this->output->set_header('Content-Length: '.strlen($str));
  218. $this->output->set_output($str);
  219. }
  220. // --------------------------------------------------------------------
  221. /**
  222. * Javascript Combo Loader
  223. *
  224. * Combo load multiple javascript files to reduce HTTP requests
  225. * BASE.AMP.'C=javascript&M=combo&ui=ui,packages&file=another&plugin=plugins&package=third,party,packages'
  226. *
  227. * @access public
  228. * @return string
  229. */
  230. function combo_load()
  231. {
  232. $this->output->enable_profiler(FALSE);
  233. $contents = '';
  234. $folder = $this->config->item('use_compressed_js') == 'n' ? 'src' : 'compressed';
  235. $types = array(
  236. 'effect' => PATH_JQUERY.'ui/jquery.effects.',
  237. 'ui' => PATH_JQUERY.'ui/jquery.ui.',
  238. 'plugin' => PATH_JQUERY.'plugins/',
  239. 'file' => PATH_THEMES.'javascript/'.$folder.'/',
  240. 'package' => PATH_THIRD,
  241. 'fp_module' => PATH_MOD
  242. );
  243. $mock_name = '';
  244. foreach($types as $type => $path)
  245. {
  246. $mock_name .= $this->input->get_post($type);
  247. $files = explode(',', $this->input->get_post($type));
  248. foreach($files as $file)
  249. {
  250. if ($type == 'package' OR $type == 'fp_module')
  251. {
  252. $file = $file.'/javascript/'.$file;
  253. }
  254. elseif ($type == 'file')
  255. {
  256. $parts = explode('/', $file);
  257. $file = array();
  258. foreach ($parts as $part)
  259. {
  260. if ($part != '..')
  261. {
  262. $file[] = $this->security->sanitize_filename($part);
  263. }
  264. }
  265. $file = implode('/', $file);
  266. }
  267. else
  268. {
  269. $file = $this->security->sanitize_filename($file);
  270. }
  271. $file = $path.$file.'.js';
  272. if (file_exists($file))
  273. {
  274. $contents .= file_get_contents($file)."\n\n";
  275. }
  276. }
  277. }
  278. $modified = $this->input->get_post('v');
  279. $this->_set_headers($mock_name, $modified);
  280. $this->output->set_header('Content-Length: '.strlen($contents));
  281. $this->output->set_output($contents);
  282. }
  283. // --------------------------------------------------------------------
  284. /**
  285. * Set Headers
  286. *
  287. * @access private
  288. * @param string
  289. * @return string
  290. */
  291. function _set_headers($file, $mtime = FALSE)
  292. {
  293. $this->output->out_type = 'cp_asset';
  294. $this->output->set_header("Content-Type: text/javascript");
  295. if ($this->config->item('send_headers') != 'y')
  296. {
  297. // All we need is content type - we're done
  298. return;
  299. }
  300. $max_age = 5184000;
  301. $modified = ($mtime !== FALSE) ? $mtime : @filemtime($file);
  302. $modified_since = $this->input->server('HTTP_IF_MODIFIED_SINCE');
  303. // Remove anything after the semicolon
  304. if ($pos = strrpos($modified_since, ';') !== FALSE)
  305. {
  306. $modified_since = substr($modified_since, 0, $pos);
  307. }
  308. // If the file is in the client cache, we'll
  309. // send a 304 and be done with it.
  310. if ($modified_since && (strtotime($modified_since) == $modified))
  311. {
  312. $this->output->set_status_header(304);
  313. exit;
  314. }
  315. // Send a custom ETag to maintain a useful cache in
  316. // load-balanced environments
  317. $this->output->set_header("ETag: ".md5($modified.$file));
  318. // All times GMT
  319. $modified = gmdate('D, d M Y H:i:s', $modified).' GMT';
  320. $expires = gmdate('D, d M Y H:i:s', time() + $max_age).' GMT';
  321. $this->output->set_status_header(200);
  322. $this->output->set_header("Cache-Control: max-age={$max_age}, must-revalidate");
  323. $this->output->set_header('Vary: Accept-Encoding');
  324. $this->output->set_header('Last-Modified: '.$modified);
  325. $this->output->set_header('Expires: '.$expires);
  326. }
  327. // --------------------------------------------------------------------
  328. /**
  329. * CSS Javascript
  330. *
  331. * Javascript string that handles the client side css processing
  332. *
  333. * @access private
  334. * @return string
  335. */
  336. function _css_javascript($file)
  337. {
  338. $js = '(function($, doc) {
  339. var adv_css = '.$this->_advanced_css($file).', selector,
  340. compat_el = doc.createElement("ee_compat"),
  341. supported = false,
  342. inline_css = [],
  343. use = ["", "", "$1$3"],
  344. prefixes = " webkit o ms moz Moz".split(" "),
  345. corners = " -top-right -top-left -bottom-right -bottom-left".split(" "),
  346. regex = /^-(.)(.*?)-(.)(.*)/,
  347. css_radii = {};
  348. /* Detect browser support and define a proper prefix */
  349. $.each(prefixes, function(i) {
  350. var name = i ? this+"BorderRadius" : "borderRadius";
  351. if (compat_el.style[name] !== undefined) {
  352. if (i == 0) {
  353. use = ["border", "-radius", ""];
  354. }
  355. else if (i < 3) {
  356. use = ["-"+this+"-border", "-radius", ""];
  357. }
  358. else {
  359. use = ["-moz-border-radius", "", "-$1$2$3$4"]; /* ... thanks mozilla */
  360. }
  361. supported = true;
  362. return false;
  363. }
  364. });
  365. /*
  366. * Different names for the same thing.
  367. * Spec: border-bottom-left-radius, Moz: border-radius-bottomleft, Plugin: bl
  368. */
  369. $.each(corners, function(i, v) {
  370. if (use[2]) {
  371. v = v.replace(regex, use[2]);
  372. }
  373. css_radii["border"+this+"-radius"] = use[0]+v+use[1];
  374. });
  375. function process_css(key, value) {
  376. if (key.indexOf("@") == -1) {
  377. var apply_radius = "",
  378. sep = (supported) ? ":" : " ",
  379. jQel;
  380. for (radius in css_radii) {
  381. if (value[radius]) {
  382. apply_radius += css_radii[radius]+sep+value[radius]+";";
  383. delete(value[radius]);
  384. }
  385. }
  386. if (supported) {
  387. inline_css.push(key+"{"+apply_radius+"}");
  388. }
  389. else {
  390. jQel = $(key).css(value);
  391. if (apply_radius) {
  392. /* jQel.uncorner().corner(apply_radius); */
  393. }
  394. }
  395. }
  396. else if (key.indexOf("@"+EE.router_class) != -1) {
  397. $.each(value, process_css);
  398. }
  399. }
  400. if (supported) {
  401. $.each(adv_css, process_css);
  402. var head = doc.getElementsByTagName("head")[0],
  403. ss_txt = doc.createTextNode(inline_css.join("\n")),
  404. ss_el = doc.createElement("style");
  405. ss_el.setAttribute("type", "text/css");
  406. ss_el.appendChild(ss_txt);
  407. head.appendChild(ss_el);
  408. }
  409. else {
  410. $(doc).ready(function() {
  411. $.each(adv_css, process_css);
  412. });
  413. }
  414. })(jQuery, this.document)';
  415. $js = preg_replace('|/\*.*?\*/|s', '', $js);
  416. return str_replace(array("\t", "\n"), '', $js);
  417. }
  418. // --------------------------------------------------------------------
  419. /**
  420. * Advanced CSS Parser
  421. *
  422. * Uses javascript to provide cross browser capabilities for
  423. * advanced selectors and corner rounding for the advanced.css file.
  424. *
  425. * @access private
  426. * @return mixed
  427. */
  428. function _advanced_css($file)
  429. {
  430. if ( ! $file)
  431. {
  432. return array();
  433. }
  434. $this->css = file_get_contents($file);
  435. $this->css = preg_replace('/\/\*.+?\*\//s', '', $this->css);
  436. if (trim($this->css) == '')
  437. {
  438. return array();
  439. }
  440. // Used by the loop to track bracing depth
  441. $selector_stack = array();
  442. $open = FALSE;
  443. $depth = 0;
  444. /* The regex here is a bit crazy, but we need it to be
  445. * really quick if we're going to parse css on the fly.
  446. * The basic version is:
  447. * /\s*(([^\}\{;]*?)\s*\{|\})/
  448. *
  449. * I've changed it to use a whitelist of characters instead,
  450. * which pushes the regex processing time on a 2000 line test file
  451. * down to 0.07 seconds. Acceptable - result cached by browser.
  452. */
  453. $brackets = '/\s*(([@\w+~>\-\[\]=\(\'"):,.#\s]*?)\s*\{|\})\s*/';
  454. if (preg_match_all($brackets, $this->css, $matches, PREG_OFFSET_CAPTURE))
  455. {
  456. foreach($matches['1'] as $key => $data) // data[0] - string | data[1] - offset
  457. {
  458. if ($data['0'] == '}')
  459. {
  460. if ($open)
  461. {
  462. // selector array, start offset, selector /w open-bracket, closing offset
  463. $this->_add_css($selector_stack, $open['0'], $open['1'], $data['1']);
  464. $open = FALSE;
  465. }
  466. array_pop($selector_stack);
  467. $depth--;
  468. continue;
  469. }
  470. $selector_stack[] = $matches['2'][$key]['0'];
  471. $open = array($data['1'], $data['0']);
  472. $depth++;
  473. }
  474. }
  475. $this->load->library('javascript');
  476. return $this->javascript->generate_json($this->parsed_css, TRUE);
  477. }
  478. // --------------------------------------------------------------------
  479. /**
  480. * Add css
  481. *
  482. * Parses block of css rules and creates an array
  483. *
  484. * @access private
  485. * @return mixed
  486. */
  487. function _add_css($selectors, $start, $open_string, $end) // selector stack, start offset, selector /w open-bracket, closing offset
  488. {
  489. // Get the css rules
  490. $attr = array();
  491. $start += strlen($open_string);
  492. $attr_s = substr($this->css, $start, $end-$start);
  493. foreach(explode(';', $attr_s) as $rule)
  494. {
  495. if (trim($rule))
  496. {
  497. list($key, $value) = explode(':', $rule);
  498. $attr[trim($key)] = trim($value);
  499. }
  500. }
  501. // Create controller/selector hierarchy
  502. $_ref =& $this->parsed_css;
  503. $selector = array_pop($selectors);
  504. foreach($selectors as $s)
  505. {
  506. $_ref =& $_ref[$s];
  507. }
  508. $_ref[$selector] = isset($_ref[$selector]) ? $attr + $_ref[$selector] : $attr;
  509. }
  510. }
  511. /* End of file javascript.php */
  512. /* Location: ./system/expressionengine/controllers/cp/javascript.php */