c9974758070c77f1dceb48ba8f02532efcf76195

Author: AD7six

Date: 2009-04-18 18:05:08 +0200

updating mi compressor lib renaming markitup.pack to markitup.min for the compression script using the current git hash as the mi_compressor salt (updating the app means no stale cache files) using tmp/cache/assets for cached css and js files

diff --git a/config/bootstrap.php b/config/bootstrap.php index 54c7c49..fb9b98a 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -54,6 +54,10 @@ $langs = array('ar', 'en', 'fa', 'fr', 'de', 'es', 'pt', 'nl', 'id', 'it', 'ja', sort($langs); Configure::write('Languages.all', $langs); +Configure::write('MiCompressor.clearCache', false); +Configure::write('MiCompressor.cacheDir', CACHE . 'assets' . DS); +Configure::write('MiCompressor.salt', trim(file_get_contents(APP . '.git/refs/heads/master'))); + define('ADMIN', '800'); define('EDITOR', '700'); define('MODERATOR', '600'); diff --git a/tmp/cache/assets/empty b/tmp/cache/assets/empty new file mode 100644 index 0000000..e69de29 diff --git a/vendors/css/mini.css b/vendors/css/mini.css index ca0f11b..f103ea2 100644 --- a/vendors/css/mini.css +++ b/vendors/css/mini.css @@ -22,12 +22,5 @@ */ App::import('Vendor', 'MiCompressor'); list($_, $request) = explode('?', $_SERVER['REQUEST_URI']); -echo MiCompressor::serve(compact('request')); return; -echo MiCompressor::serve(array( - 'request' => $request, - 'compress' => Configure::read() < 2, - 'clear' => false, - 'log' => true, - 'type' => 'css' -)); +echo MiCompressor::serve($request, 'css'); ?> \ No newline at end of file diff --git a/vendors/js/mini.js b/vendors/js/mini.js index a5c8024..52328b7 100644 --- a/vendors/js/mini.js +++ b/vendors/js/mini.js @@ -22,12 +22,5 @@ */ App::import('Vendor', 'MiCompressor'); list($_, $request) = explode('?', $_SERVER['REQUEST_URI']); -echo MiCompressor::serve(compact('request')); return; -echo MiCompressor::serve(array( - 'request' => $request, - 'compress' => Configure::read() < 2, - 'clear' => true, - 'log' => true, - 'type' => 'js' -)); +echo MiCompressor::serve($request, 'js'); return; ?> \ No newline at end of file diff --git a/vendors/mi_compressor.php b/vendors/mi_compressor.php index a821e6d..0883a9b 100644 --- a/vendors/mi_compressor.php +++ b/vendors/mi_compressor.php @@ -1,9 +1,11 @@ <?php -/* SVN FILE: $Id: mi_compressor.php 800 2009-02-26 18:49:55Z ad7six $ */ +/* SVN FILE: $Id: mi_compressor.php 939 2009-04-16 21:25:05Z ad7six $ */ /** - * Short description for mi_compressor.php + * MiCompressor, a class used for shrinking CSS and JS files * - * Long description for mi_compressor.php + * MiCompressor is a utility which serves 2 purposes: + * Ask the class to return the request(s) necessary for a set of css/js files + * Process a request for css/js file(s) * * PHP version 5 * @@ -18,18 +20,33 @@ * @package base * @subpackage base.vendors * @since v 1.0 - * @version $Revision: 800 $ + * @version $Revision: 939 $ * @modifiedby $LastChangedBy: ad7six $ - * @lastmodified $Date: 2009-02-26 19:49:55 +0100 (Thu, 26 Feb 2009) $ + * @lastmodified $Date: 2009-04-16 23:25:05 +0200 (Thu, 16 Apr 2009) $ * @license http://www.opensource.org/licenses/mit-license.php The MIT License */ /** * MiCompressor class * - * Compress multiple css and js files into a single file on demand. + * Compress and minify multiple css and js files into a single file on demand. * * By default, in debug mode it only concatonates, in production mode contents are also runs the output through a - * minifying routine + * minifying routine. Some term definitions: + * compress is used within the class to refer to data-compression (such as gzip etc.); + * minify means stripping whitespace, rewriting to be less chars etc. + * + * This class is designed to be used with CakePHP but can be used alone (dependent on Minify only, if minified assets + * don't already exist). If Minify can't be found minification will be skipped + * + * Note: + * To avoid stale js/css files being served, The default salt is a constant SITE_VERSION (if defined). The salt + * is used for the hash generation/comparison logic and allows you to avoid stale css/js e.g.: + * define('SITE_VERSION', filemtime('somefilethatgetsupdatedwhenyoudeploy')); + * In CakePHP a suggestion would be: + * define('SITE_VERSION', filemtime('/app/config/bootstrap.php')); + * Or call this before using the class: + * MiCompressor::config(array('salt' => 'some value')); + * * * @abstract * @package base @@ -37,22 +54,6 @@ */ abstract class MiCompressor { /** - * start property - * - * @static - * @var mixed null - * @access public - */ - public static $start = null; -/** - * cacheDuration property - * - * @static - * @var string '+1 year' - * @access public - */ - public static $cacheDuration = '+1 year'; -/** * map property * * To allow disassociation from parameters used in the request, and file path locations @@ -66,38 +67,95 @@ abstract class MiCompressor { 'jquery' => array( 'aplugin' => 'subfolder/path/here.js' ) + ), + 'css' => array( + 'jquery' => array( + 'aplugin' => 'subfolder/path/here.css', + 'aplugin' => array( + 'subfolder/path/moreThan.css', + 'subfolder/path/oneFileRequired.css' + ) + ) ) + ); /** - * compressCss method + * settings property + * + * Active settings - edit/set/see via MiCompressor::config() + * + * @var array + * @access protected + */ + protected static $settings = array(); +/** + * defaultSettings property + * + * Don't edit this. + * The settings which can be auto-set are set here, these settings are used as a fallback if a requested setting + * hasn't been set, or the Configure class (if it exists) return null + * + * @var array + * @access protected + */ + protected static $defaultSettings = array( + 'MiCompressor.debug' => null, + 'MiCompressor.log' => null, + 'MiCompressor.cacheClear' => null, + 'MiCompressor.cacheDuration' => null, + 'MiCompressor.cacheDir' => null, + 'MiCompressor.salt' => null, + 'MiCompressor.bypassLoadMinifyLib' => null, + 'MiCompressor.minify' => null, + 'MiCompressor.minify.css' => null, + 'MiCompressor.minify.js' => null, + 'Asset.compress' => null, + ); +/** + * initialized property + * + * Flag to know if the class variables have been initialized yet. + * + * @var bool false + * @access protected + */ + protected static $initialized = false; +/** + * start property + * + * Start time * - * @param mixed $css - * @param string $file - * @param string $logPrefix * @static - * @return void - * @access public + * @var mixed null + * @access protected */ - public static function compressCss($css, $file = '', $logPrefix = '') { - MiCompressor::log("$logPrefix compress $file.css"); - return Minify_CSS::minify($css); - } + protected static $start = null; /** - * compressJs method + * loadedFiles property * - * This can be rather intensive - use with care + * To prevent a valid request which includes the same file twice (either expicitly or through @import logic) + * this variable holds the names of the files already loaded for the current request * - * @TODO don't strip license blocks - * @param mixed $js - * @param string $file - * @param string $logPrefix + * @var array + * @access protected + */ + protected static $loadedFiles = array(); +/** + * config method + * + * Set/See settings + * + * @param array $settings array() + * @param bool $reset false - reset to defaults before porcessing $settings? * @static - * @return void + * @return current settings * @access public */ - public static function compressJs($js, $file = '', $logPrefix = '') { - MiCompressor::log("$logPrefix compressing $file.js"); - return JSMin::minify($js); + public static function config($settings = array(), $reset = false) { + if ($reset) { + MiCompressor::$settings = array(); + } + return MiCompressor::$settings = array_merge(MiCompressor::$settings, $settings); } /** * log method @@ -106,7 +164,7 @@ abstract class MiCompressor { * * @param mixed $string * @static - * @return void + * @return logs contents if requested, otherwise null * @access public */ public static function log($string = null) { @@ -115,9 +173,16 @@ abstract class MiCompressor { } static $log = array(); if ($string === null) { - $log = am(array( - 'MiCompressor log - only generated in debug mode. Generated ' . date("D, M jS Y, H:i:s"), - null), $log); + $settings = MiCompressor::$settings; + ksort($settings); + foreach ($settings as $k => &$v) { + $v = ' ' . str_pad(str_replace('MiCompressor.', '', $k), 15, ' ', STR_PAD_RIGHT) . "\t: " . $v; + } + $settings[] = ''; + $head = array_merge(array( + 'MiCompressor log - (only generated in debug mode, or if MiCompressor.log is set to true) ' . date("D, M jS Y, H:i:s"), + null), $settings); + $log = array_merge($head, $log); $return = "/**\r\n * " . implode("\r\n * ", $log) . "\r\n */\r\n"; $log = array(); return $return; @@ -126,85 +191,53 @@ abstract class MiCompressor { $log[] = str_pad(number_format($time, 3, '.', ''), 6, ' ', STR_PAD_LEFT) . 's ' . $string; } /** - * parse method + * minifyCss method * - * Used to determine which component files, and sub files have been requested - * For the request string of the form: file|file2|file3|file4,x,y=300,z|file5.. generate an array of the form: - * array( - * file => array(), - * file2 => array(), - * file3 => array(), - * file4 => array('x', 'y' => 300, 'z'), - * file5 => array() - * ) - * This format allows passing parameters to scripts when included (accessible as $params); + * Pass in a string of css, get out a minified version * - * A hash is automatically added to the url by the MiHtml and MiJavascript helpers. The hash is checked in this - * method and if it doesn't match (this should never happen for a valid request), will either add a log message if not - * in production mode or return false. Compressing (in particular Js files) is expensive, therefore using a hash - * prevents a malicious user from requesting arbritary varying urls and tying up the server + * @param mixed $css + * @param string $logPrefix the indent + * @static + * @return string minified css + * @access public + */ + public static function minifyCss($css, $logPrefix = '') { + MiCompressor::log("{$logPrefix}minifying combined css file"); + return Minify_CSS::minify($css); + } +/** + * minifyJs method * - * The hash is returned primarily to be used as the default cache key + * Pass in a string of javascript, and get out a minified version + * This can be rather intensive - use with care * - * @param string $requestString - * @param mixed $hash + * @TODO don't strip license blocks + * @param mixed $js + * @param string $file + * @param string $logPrefix the indent * @static - * @return mixed array of files to process, or false for an invalid/doctored request + * @return minified js * @access public */ - public static function parse($requestString = '', &$hash = null) { - $debug = (class_exists('Configure')?Configure::read():false); - $requests = explode('|', $requestString); - $hash = array_pop($requests); - $problem = false; - if (strpos($hash, 'hash=') !== 0) { - $problem = true; - MiCompressor::log('PROBLEM: No hash in the request string'); - } else { - list(,$hash) = explode('=', $hash); - uses('Security'); - $salt = defined('SITE_VERSION')?SITE_VERSION:''; - if ($hash != Security::hash($salt . implode('|', $requests), null, true)) { - $problem = true; - MiCompressor::log('PROBLEM: Hash doesn\'t match'); - } - } - if ($problem && $debug) { - return false; - } - - $return = array(); - foreach ($requests as $filename) { - $params = array(); - if (strpos($filename, ',')) { - $params = explode(',', $filename); - $filename = array_shift($params); - foreach ($params as $k => $v) { - if (strpos($v, '=')) { - unset ($params[$k]); - list($k, $v) = explode('=', $v); - $params[$k] = $v; - } - } - } - $return[$filename] = $params; - } - return $return; + public static function minifyJs($js, $file = '', $logPrefix = '') { + MiCompressor::log("$logPrefix minifying $file.js"); + return JSMin::minify($js); } /** * process method * - * For each of the requested files, find them, concatonate them - if requested compress them - and return - * For js files, each individual file is compressed. For css, their combined contents are compressed + * For each of the requested files, find them, concatonate them - if requested minify them - and return + * For js files, each individual file is minifyed. For css, their combined contents are minifyed + * Called internally by serve, public to allow other (external) parse logic if necessary * * @param mixed $files * @param mixed $type - * @param mixed $compress + * @param mixed $minify * @static - * @return string the files' contents, optionally compressed, as a string + * @return string the files contents, optionally minifyed, as a string * @access public */ - public static function process($files, $type = null, $compress = null) { + public static function process($files, $type = null, $minify = null) { if ($type === null) { if (strpos($_GET['url'], 'js/') === 0) { $type = 'js'; @@ -212,21 +245,17 @@ abstract class MiCompressor { $type = 'css'; } } - $debug = (class_exists('Configure')?Configure::read():false); - if ($compress === null) { - $compress = !$debug; + if ($minify === null) { + $minify = MiCompressor::cRead('MiCompressor.minify.' . $type, 'minify'); } - if ($compress && !MiCompressor::loadCompressLib($type)) { - MiCompressor::log("PROBLEM: Unable to load $type compressor. No minifying"); - $compress = false; + + if ($minify && !MiCompressor::loadMinifyLib($type)) { + MiCompressor::log("PROBLEM: Unable to load $type . No minifying"); + $minify = false; } $files = (array)$files; $return = ''; - if ($type === 'css') { - $_compress = $compress; - $compress = false; - } foreach ($files as $filename => $params) { if (is_string($params) && is_numeric($filename)) { $filename = $params; @@ -235,18 +264,26 @@ abstract class MiCompressor { if (substr($filename, - strlen($type)) === $type) { $filename = substr($filename, 0, - strlen($type) - 1); } - $method = 'load' . Inflector::camelize($type) . Inflector::camelize($filename); + $iType = $type; + $iType[0] = strtoupper($iType[0]); + $iFilename = $filename; + $iFilename[0] = strtoupper($iFilename[0]); + $method = 'load' . $iType . $iFilename; if (method_exists('MiCompressor', $method)) { MiCompressor::log("Loading $filename.$type with method $method"); - $return .= MiCompressor::$method($params, $compress) . "\r\n"; + $return .= MiCompressor::$method($params, $minify) . "\r\n"; + } elseif (strpos($filename, 'jquery.') === 0) { + $method = "load{$iType}JqueryPlugin"; + MiCompressor::log("Loading $filename.$type with method $method"); + $return .= MiCompressor::$method(str_replace('jquery.', '', $filename), $minify) . "\r\n"; } else { MiCompressor::log("Loading $filename.$type with method loadFile"); - $return .= MiCompressor::loadFile($filename, $params, $compress, $type) . "\r\n"; + $return .= MiCompressor::loadFile($filename, $params, $minify, $type) . "\r\n"; } } - if (!empty($_compress) && $type === 'css') { - $return = MiCompressor::compressCss($return, 'combined'); + if ($minify && $type === 'css') { + $return = MiCompressor::minifyCss($return, "\t"); } return $return; } @@ -257,39 +294,53 @@ abstract class MiCompressor { * If the request is invalid issue a 404 header and return no content * otherwise generate the content and send to the browser * - * If in debug mode, the cache duration is set to +10 minutes. otherwise it is +1 year - * $compress defaults to true in production mode, false in debug mode - * $log defaults to true in production mode, false in debug mode - * $cachePath defaults to $type _ the hash _ d(ebug)|p(roduction) . t(emp)|p(ermanent) - * $clear defaults to false in production mode, true in debug mode + * Example Cake use (contents of mini.css file): + * App::import('Vendor', 'MiCompressor'); + * list($_, $request) = explode('?', $_SERVER['REQUEST_URI']); + * echo MiCompressor::serve($request, 'css'); * - * A cache file is always generated, but if $clear is true, the cache is cleared for all cache files of the same type - * The log is always generated - but it's only included in the content if $log is true. Otherwise it is available - * for debugging purposes + * Example Standalone use (contents of mini.css file): + * $base = '/path/to/mi_compressor/containing/folder/'; + * include($base . 'mi_compressor.php'); + * // Optional Start + * ini_set('include_path', $base . 'minify/lib:' . ini_get('include_path')); + * include($base . 'minify/lib/JSMin.php'); + * include($base . 'minify/lib/Minify/CSS.php'); + * include($base . 'minify/lib/HTTP/ConditionalGet.php'); + * // Optional End + * MiCompressor::config(array( + * 'MiCompressor.debug' => 0, + * 'MiCompressor.minify' => 1, + * )); + * echo MiCompressor::serve($_GET, 'css'); * * @param string $request * @param mixed $type - * @param mixed $compress - * @param mixed $log - * @param mixed $cachePath - * @param mixed $clear * @static - * @return void + * @return contents to be served up * @access public */ - public static function serve($request = '', $type = null, $compress = null, $log = null, $cachePath = null, $clear = null) { + public static function serve($request = '', $type = null) { + MiCompressor::$loadedFiles = array(); + if (is_array($request)) { - extract($request); + if (count($request) == 1 && !isset($request['request'])) { + $hash = current($request); + $request = key($request); + } else { + extract($request); + } } MiCompressor::log('Request String: ' . $request); $start = getMicrotime(); ob_start(); - if (Configure::read('Asset.compress') && @ini_get("zlib.output_compression") != true && extension_loaded("zlib") && + if (MiCompressor::cRead('Asset.compress') && @ini_get("zlib.output_compression") != true && extension_loaded("zlib") && (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false)) { MiCompressor::log('Enabling compression, turn on zlib.output_compression if possible'); ob_start('ob_gzhandler'); } $requests = MiCompressor::parse($request, $hash); + if (!$requests) { header('HTTP/1.0 404 Not Found'); return false; @@ -301,179 +352,454 @@ abstract class MiCompressor { $type = 'css'; } } - $appDebug = (class_exists('Configure')?Configure::read():false); - if ($compress === null) { - if (!class_exists('Configure')) { - $compress = true; - } else { - $compress = Configure::read('Asset.compress.' . $type); - if ($compress === null) { - $compress = Configure::read('Asset.compress'); - } - if ($compress === null) { - $compress = !$appDebug; - } - } - } - if ($log === null) { - $log = $appDebug; - } - if ($clear === null) { - $clear = $appDebug; - } - if ($cachePath === null) { - $cachePath = MiCompressor::__cachePath($type, $hash, $appDebug, $clear, true); - } - MiCompressor::log('Cache Path: ' . $cachePath); - if ($appDebug) { - MiCompressor::$cacheDuration = '+10 minutes'; - } - - if ($clear && function_exists('uses')) { - uses('Folder'); - if (class_exists('Folder')) { - $folder = new Folder(CACHE . 'views/'); - $files = $folder->find($type . '_' . '.*'); + if ($cachePath = MiCompressor::cachePath($type, $hash, true)) { + MiCompressor::log('Cache Path: ' . $cachePath); + if (MiCompressor::cRead('cacheClear')) { + $path = MiCompressor::cRead('cacheDir') . $type . '_*'; + $files = glob($path); foreach ($files as $file) { - MiCompressor::log("Removing Cache File " . $file); - @unlink(CACHE . 'views/' . $file); + MiCompressor::log("Removing Cache File " . str_replace(MiCompressor::cRead('cacheDir'), '', $file)); + @unlink($file); } } - } - if ($cachePath) { - $cached = cache($cachePath, null, MiCompressor::$cacheDuration); + $cached = cache($cachePath, null, MiCompressor::cRead('cacheDuration')); if ($cached) { MiCompressor::log("Cache File: $cachePath found and returned"); return MiCompressor::out($cachePath, $cached); } } - $return = MiCompressor::process($requests, $type, $compress); + $return = MiCompressor::process($requests, $type); $eTag = md5($return); if ($cachePath) { - MiCompressor::log("Generating cache file $cachePath to expire in " . MiCompressor::$cacheDuration); + MiCompressor::log("Generating cache file $cachePath to expire in " . MiCompressor::cRead('cacheDuration')); } MiCompressor::log('eTag: ' . $eTag); MiCompressor::log('Finished'); - if ($log) { + if (MiCompressor::cRead('log')) { $return = MiCompressor::log() . $return; } $return = '/* eTag:' . $eTag . ' */' . $return; // Slightly out of sequence so the cache file contains the log if ($cachePath) { - cache($cachePath, $return, MiCompressor::$cacheDuration); + cache($cachePath, $return, MiCompressor::cRead('cacheDuration')); } - return MiCompressor::out(CACHE . $cachePath, $return); + return MiCompressor::out(MiCompressor::cRead('cacheDir') . $cachePath, $return); } /** - * loadCompressLib method + * url method + * + * Generate the url(s) corresponding to the requested files + * + * @param array $request + * @param array $params + * @static + * @return mixed string or array of strings correpsonding to the request + * @access public + */ + public static function url($request = array(), $params = array()) { + extract(am(array( + 'sendAlone' => MiCompressor::cRead(), + 'type' => 'js', + 'sizeLimit' => false, + ), $params)); + $stack = array(); + $i = 0; + foreach ($request as $key => &$value) { + $_sendAlone = $sendAlone; + if (is_string($key)) { + if (is_array($value) && isset($value['sendAlone'])) { + $_sendAlone = $value['sendAlone']; + unset($value['sendAlone']); + } + if (!$value) { + $value = $key; + } elseif($type === 'js' && $key === 'jquery' && ($sizeLimit || $_sendAlone)) { + $i++; + $stack[$i][] = 'jquery'; + foreach ($value as $plugin) { + $i++; + $stack[$i][] = 'jquery.' . $plugin; + $i++; + } + continue; + } else { + foreach ($value as $_k => &$_v) { + if (!is_numeric($_k)) { + $_v = $_k . '=' . $_v; + } + } + $value = $key . ',' . implode(',', $value); + } + } + if ($_sendAlone) { + $i++; + } + $stack[$i][] = $value; + if ($_sendAlone) { + $i++; + } + } + $return = array(); + foreach ($stack as &$files) { + $url = MiCompressor::_url($files, $type, $sizeLimit); + foreach((array)$url as $u) { + $return[] = $u; + } + } + if (count($return) === 1) { + return $return[0]; + } + return $return; + } +/** + * cachePath method * * @param mixed $type + * @param mixed $hash + * @param bool $relative return a relative or absolute path * @static - * @return void - * @access private + * @return string the absolute/relative path to the corresponding cache file + * @access protected + */ + protected static function cachePath($type, $hash, $relative = false) { + $debug = MiCompressor::cRead(); + $clear = MiCompressor::cRead('cacheClear'); + $return = $type . '_' . $hash . '_' . ($debug?'d':'p') . ($clear?'t':'p'); + $return = MiCompressor::cRead('cacheDir') . $return; + if ($relative) { + return str_replace(CACHE, '', $return); + } + return $return; + } +/** + * c(onfigure)Read method + * + * Write default settings as appropriate on first read + * Use the Configure class if it exists, otherwise use default setting + * First none-null result encountered wins. + * + * Call with multiple paramters naming fallback settings e.g. + * $debug = MiCompressor::cRead('cacheClear', 'debug', 'Asset.someothersetting'); + * Again, first none-null result wins. + * + * @param string $setting 'debug' + * @param bool $prefix true + * @static + * @return mixed, config value + * @access protected + */ + protected static function cRead($setting = 'debug', $prefix = true) { + if (!strpos($setting, '.') && $prefix) { + $setting = 'MiCompressor.' . $setting; + } + if (!MiCompressor::$initialized) { + MiCompressor::$initialized = true; + + $debug = MiCompressor::cRead('debug'); + if ($debug === null) { + $debug = MiCompressor::cRead('debug', false); + } + if ($debug) { + MiCompressor::$defaultSettings['MiCompressor.cacheDuration'] = '+10 minutes'; + } else { + MiCompressor::$defaultSettings['MiCompressor.cacheDuration'] = '+1 year'; + } + MiCompressor::$settings['MiCompressor.debug'] = $debug; + unset (MiCompressor::$settings['debug']); + + MiCompressor::cRead('log', 'debug'); + MiCompressor::cRead('cacheClear', 'debug'); + + $minify = MiCompressor::cRead('minify'); + if ($minify === null) { + MiCompressor::$settings['MiCompressor.minify'] = !$debug; + } + + $dir = MiCompressor::cRead('cacheDir'); + if (!$dir) { + MiCompressor::$defaultSettings['MiCompressor.cacheDir'] = CACHE; + } + MiCompressor::$defaultSettings['MiCompressor.webrootDir'] = WWW_ROOT; + MiCompressor::$defaultSettings['MiCompressor.cssDir'] = CSS; + MiCompressor::$defaultSettings['MiCompressor.jsDir'] = JS; + $salt = MiCompressor::cRead('salt'); + if (!$salt) { + MiCompressor::$defaultSettings['MiCompressor.salt'] = SITE_VERSION; + } + + if (!class_exists('App')) { + MiCompressor::$defaultSettings['MiCompressor.bypassLoadMinifyLib'] = true; + } + } + if (array_key_exists($setting, MiCompressor::$settings)) { + return MiCompressor::$settings[$setting]; + } + + if (isset(MiCompressor::$defaultSettings[$setting])) { + return MiCompressor::$settings[$setting] = MiCompressor::$defaultSettings[$setting]; + } + + if (class_exists('Configure')) { + $return = Configure::read($setting); + $fallbacks = func_get_args(); + array_shift($fallbacks); + if ($return === null && $fallbacks) { + $return = call_user_func_array(array('MiCompressor', 'cRead'), $fallbacks); + } + return MiCompressor::$settings[$setting] = $return; + } + + $return = MiCompressor::$defaultSettings[$setting]; + $fallbacks = func_get_args(); + array_shift($fallbacks); + if ($return === null && $fallbacks) { + $return = call_user_func_array(array('MiCompressor', 'cRead'), $fallbacks); + } + return MiCompressor::$settings[$setting] = $return; + } +/** + * hash method + * + * Return the hash to be used for the passed arg + * + * @param mixed $arg array or string of requests + * @static + * @return string the hash of the passed arg + * @access protected */ - private static function loadCompressLib($type) { + protected static function hash($arg) { + $salt = MiCompressor::cRead('salt'); + if (is_array($arg)) { + $arg = implode('|', $arg); + } + if (function_exists('uses')) { + uses('Security'); + return Security::hash($salt . $arg, null, true); + } + return sha1($salt . $arg); + } +/** + * loadMinifyLib method + * + * @param mixed $type + * @static + * @return bool true on success + * @access protected + */ + protected static function loadMinifyLib($type) { + if (MiCompressor::cRead('bypassLoadMinifyLib')) { + if (!class_exists('HTTP_ConditionalGet')) { + MiCompressor::log('PROBLEM: HTTP_ConditionalGet (part of the Minify lib) not found, couldn\'t load the minify classes.'); + return false; + } + return true; + } + if (!class_exists('App')) { + MiCompressor::log('PROBLEM: App class (part of CakePHP lib) not found, couldn\'t load the minify classes.'); + MiCompressor::log(' To Resolve set MiCompressor.bypassLoadMinifyLib to true and include the minify classes manually.'); + return false; + } if($type === 'js') { return App::import('Vendor', $type . 'Min', array('file' => 'minify/lib/JSMin.php')); } elseif ($type === 'css') { - ini_set('include_path', dirname(__FILE__) . '/minify/lib:' . ini_get('include_path')); + ini_set('include_path', dirname(__FILE__) . '/minify/lib'. PATH_SEPARATOR . ini_get('include_path')); return App::import('Vendor', $type . 'Min', array('file' => 'minify/lib/Minify/CSS.php')); } else { return App::import('Vendor', $type . 'Min', array('file' => 'minify/lib/HTTP/ConditionalGet.php')); } } /** + * loadCssJqueryPlugin method + * + * @param string $plugin '' + * @param bool $minify false + * @return void + * @access protected + */ + protected static function loadCssJqueryPlugin($plugin = '', $minify = false) { + if (isset(MiCompressor::$map['css']['jquery'][$plugin])) { + $filename = MiCompressor::$map['css']['jquery'][$plugin]; + } else { + $filename = "jquery.$plugin.css"; + } + if (is_array($filename)) { + $return = ''; + foreach($filename as $f) { + $return .= MiCompressor::loadCssJqueryPlugin($f, $minify); + } + return $return; + } + if ($minify) { + $found = true; + $filename = str_replace('.css', '.min.css', $filename); + if (file_exists(CSS . $filename)) { + MiCompressor::log(' ' . $filename); + MiCompressor::log(' File ' . CSS . "$filename found"); + echo file_get_contents(CSS . $filename); + } elseif (App::import('Vendor', 'css/jquery/' . $plugin, array('file' => "css/jquery/$filename"))) { + MiCompressor::log(' ' . $filename); + MiCompressor::log(" Found vendor version css/jquery/$filename for Jquery $plugin"); + } elseif (App::import('Vendor', 'css/jquery/' . $plugin, array('file' => "css/$filename"))) { + MiCompressor::log(' ' . $filename); + MiCompressor::log(" Found vendor version css/$filename for Jquery $plugin"); + } elseif (App::import('Vendor', 'css/jquery/' . $plugin, array('file' => "jquery/plugins/$plugin/$filename"))) { + MiCompressor::log(' ' . $filename); + MiCompressor::log(" Found vendor version jquery/plugins/$plugin/$filename for Jquery $plugin"); + } else { + $found = false; + } + if ($found) { + return ob_get_clean(); + } + $filename = str_replace('.min.css', '.css', $filename); + } + MiCompressor::log(' ' . $filename); + if (file_exists(CSS . $filename)) { + MiCompressor::log(' File ' . CSS . "$filename found"); + echo file_get_contents(CSS . $filename); + } elseif (App::import('Vendor', 'css/jquery/' . $plugin, array('file' => "css/jquery/$filename"))) { + MiCompressor::log(" Found vendor version css/jquery/$filename for Jquery $plugin"); + } elseif (App::import('Vendor', 'css/jquery/' . $plugin, array('file' => "css/$filename"))) { + MiCompressor::log(" Found vendor version css/$filename for Jquery $plugin"); + } elseif (App::import('Vendor', 'css/jquery/' . $plugin, array('file' => "jquery/plugins/$plugin/$filename"))) { + MiCompressor::log(" Found vendor version jquery/plugins/$plugin/$filename for Jquery $plugin"); + } else { + MiCompressor::log(" PROBLEM: The jquery plugin jquery/plugins/$plugin/$filename could not be loaded."); + } + return ob_get_clean(); + + } +/** * loadFile method * - * For the requested file, find it and return it. if compress is set to true send through compress(Css|Js) as - * appropriate first + * For the requested file, find it and return it. if minify is set to true send through minify(Css|Js) as + * appropriate first. + * + * For CSS files, check for @import declarations and auto-correct any url() references in the file * * @param string $filename * @param mixed $params - * @param bool $compress + * @param bool $minify * @static - * @return void - * @access private + * @return string the file's contents if appropriate + * @access protected */ - private static function loadFile($filename = '', $params = array(), $compress = false, $type = 'js') { - if ($type === 'js') { - $base = JS; + protected static function loadFile($filename = '', $params = array(), $minify = false, $type = 'js') { + if ($filename[0] === '/') { + $base = MiCompressor::cRead('webrootDir'); + } elseif ($type === 'js') { + $base = MiCompressor::cRead('jsDir'); } elseif($type === 'css') { - $base = CSS; + $base = MiCompressor::cRead('cssDir'); } else { var_dump($base); die; } $file = $base . $filename . '.' . $type; - if ($compress && file_exists($base . $filename . '.min.' . $type)) { - $file = $base . $filename . '.min.' . $type; + $minFile = $base . $filename . '.min.' . $type; + if (in_array($filename . '.' . $type, MiCompressor::$loadedFiles)) { + MiCompressor::log("File $filename.type already loaded"); + return; + } + MiCompressor::$loadedFiles[] = $filename . '.' . $type; + + if ($minify && file_exists($minFile)) { + $file = $minFile; MiCompressor::log("File $file found"); return file_get_contents($file); } elseif (file_exists($file)) { MiCompressor::log("File $file found"); $return = file_get_contents($file); } else { + if ($filename[0] === '/') { + $vendorFile = substr($filename . '.' . $type, 1); + } else { + $vendorFile = $type . '/' . $filename . '.' . $type; + } ob_start(); - if (!App::import('Vendor', str_replace('.', '_', $filename . $type), array('file' => $type . '/' . $filename . '.' . $type))) { - $compress = false; - MiCompressor::log("PROBLEM: No file for $type/$filename.{$type} could be found"); + if (class_exists('App') && + !App::import('Vendor', str_replace('.', '_', $filename . $type), array('file' => $vendorFile))) { + $minify = false; + MiCompressor::log("PROBLEM: No file for $vendorFile could be found"); } $return = ob_get_clean(); } if ($type === 'css') { + if (strpos($filename, '/', 1)) { + $baseFolder = dirname($filename) . '/'; + } else { + $baseFolder = ''; + } preg_match_all('/@import\s*(?:url\()?(?:["\'])([^"\']*)\.css(?:["\'])\)?;/', $return, $matches); foreach ($matches[1] as $i => $cssFile) { - $cssFile = dirname($filename) . '/' . $cssFile; - $import = MiCompressor::loadFile($cssFile, $params, false, 'css'); - $return = str_replace($matches[0][$i], $import, $return); + if ($minify) { + $return = str_replace($matches[0][$i], '', $return); + $return .= MiCompressor::loadFile($baseFolder . $cssFile, $params, $minify, 'css'); + } elseif ($baseFolder) { + $replace = str_replace($cssFile, $baseFolder . $cssFile, $matches[0][$i]); + $return = str_replace($matches[0][$i], $replace, $return); + } } - } - if ($compress) { - $compressMethod = 'compress' . Inflector::camelize($type); - return MiCompressor::$compressMethod($return, $filename); + if ($baseFolder && strpos($return, 'url')) { + preg_match_all('@url\s*\((?:[\s"\']*)([^\s"\']*)(?:[\s"\']*)\)@', $return, $matches); + $corrected = false; + $urls = array_unique($matches[1]); + foreach ($urls as $url) { + if (strpos($url, $baseFolder) !== 0 && $url[0] !== '/') { + $corrected = true; + $return = str_replace($url, $baseFolder . $url, $return); + } + } + if ($corrected) { + MiCompressor::log("\t Auto corrected url paths in $filename"); + } + } + } elseif ($minify) { + $minifyMethod = 'minify' . Inflector::camelize($type); + return MiCompressor::$minifyMethod($return, $filename); } return $return; } /** * Bespoke load method for Jquery. * - * Allow for loading the distributed, already compressed, version of jquery; and load plugins from + * Allow for loading the distributed, already minifyed, version of jquery; and load plugins from * parameters for ease in views. requesting 'mini.js?...|jquery,abc,xyz|...' will load jquery, with the abc and xyz * plugins * * @param array $plugins - * @param bool $compress + * @param bool $minify * @static * @return void - * @access private + * @access protected */ - private static function loadJsJquery($plugins = array(), $compress = false) { + protected static function loadJsJquery($plugins = array(), $minify = false) { ob_start(); - if ($compress && file_exists(JS . 'jquery.min.js')) { + if ($minify && file_exists(JS . 'jquery.min.js')) { MiCompressor::log(' File ' . JS . 'jquery.min.js found'); echo file_get_contents(JS . 'jquery.min.js'); } elseif (file_exists(JS . 'jquery.js')) { MiCompressor::log(' File ' . JS . 'jquery.js found'); $return = file_get_contents(JS . 'jquery.js'); - if ($compress) { - MiCompressor::log(' WARNING: ' . JS . 'jquery.min.js not found. compressing on the fly'); - echo MiCompressor::compressJs($return, 'jquery'); + if ($minify) { + MiCompressor::log(' WARNING: ' . JS . 'jquery.min.js not found. minifying on the fly'); + echo MiCompressor::minifyJs($return, 'jquery'); } else { echo $return; } - } elseif ($compress && App::import('Vendor', 'jquery', array('file' => 'jquery/jquery/dist/jquery.min.js'))) { + } elseif ($minify && App::import('Vendor', 'jquery', array('file' => 'jquery/jquery/dist/jquery.min.js'))) { MiCompressor::log(' Found vendor version for jquery/jquery/dist/jquery.min.js'); } elseif (App::import('Vendor', 'jquery', array('file' => 'jquery/jquery/dist/jquery.js'))) { - if ($compress) { + if ($minify) { MiCompressor::log(' WARNING: No vendor version for jquery/jquery/dist/jquery.min.js.' - . ' compressing on the fly'); + . ' minifying on the fly'); $contents = ob_get_clean(); ob_start(); - echo MiCompressor::compressJs($contents, 'jquery', "\t"); + echo MiCompressor::minifyJs($contents, 'jquery', "\t"); } else { MiCompressor::log(' Found vendor version for jquery/jquery/dist/jquery.js'); } } else { - if ($compress) { + if ($minify) { MiCompressor::log(' PROBLEM: No vendor version for jquery/jquery/dist/jquery.min.js'); } MiCompressor::log(' PROBLEM: No vendor version for jquery/jquery/dist/jquery.js found'); @@ -483,41 +809,40 @@ abstract class MiCompressor { MiCompressor::log(' make'); } foreach ($plugins as $plugin) { - if (isset(MiCompressor::$map['js']['jquery'][$plugin])) { - $filename = MiCompressor::$map['js']['jquery'][$plugin]; - } else { - $filename = "jquery.$plugin.js"; - } - $_compress = $compress; - ob_start(); - echo "\r\n"; - if ($compress) { - $found = true; - $filename = str_replace('.js', '.min.js', $filename); - if (file_exists(JS . $filename)) { - MiCompressor::log(' ' . $filename); - MiCompressor::log(' File ' . JS . "$filename found"); - echo file_get_contents(JS . $filename); - } elseif (App::import('Vendor', 'js/jquery/' . $plugin, array('file' => "js/jquery/$filename"))) { - MiCompressor::log(' ' . $filename); - MiCompressor::log(" Found vendor version js/jquery/$filename for Jquery $plugin"); - } elseif (App::import('Vendor', 'js/jquery/' . $plugin, array('file' => "js/$filename"))) { - MiCompressor::log(' ' . $filename); - MiCompressor::log(" Found vendor version js/$filename for Jquery $plugin"); - } elseif (App::import('Vendor', 'jquery/' . $plugin, array('file' => "jquery/plugins/$plugin/$filename"))) { - MiCompressor::log(' ' . $filename); - MiCompressor::log(" Found vendor version jquery/plugins/$plugin/$filename for Jquery $plugin"); - } else { - $found = false; - } - if ($found) { - echo ob_get_clean(); - continue; - } - $filename = str_replace('.min.js', '.js', $filename); + echo MiCompressor::loadJsJqueryPlugin($plugin, $minify); + } + return ob_get_clean(); + } +/** + * Bespoke load method for a Jquery plugin file + * + * @param stirng $plugin + * @param bool $minify + * @static + * @return void + * @access protected + */ + protected static function loadJsJqueryPlugin($plugin = '', $minify = false) { + if (isset(MiCompressor::$map['js']['jquery'][$plugin])) { + $filename = MiCompressor::$map['js']['jquery'][$plugin]; + } else { + $filename = "jquery.$plugin.js"; + } + if (is_array($filename)) { + $return = ''; + foreach($filename as $f) { + $return .= MiCompressor::loadJsJqueryPlugin($f, $minify); } - MiCompressor::log(' ' . $filename); + return $return; + } + + $_minify = $minify; + echo "\r\n"; + if ($minify) { + $found = true; + $filename = str_replace('.js', '.min.js', $filename); if (file_exists(JS . $filename)) { + MiCompressor::log(' ' . $filename); MiCompressor::log(' File ' . JS . "$filename found"); echo file_get_contents(JS . $filename); } elseif (App::import('Vendor', 'js/jquery/' . $plugin, array('file' => "js/jquery/$filename"))) { @@ -526,18 +851,37 @@ abstract class MiCompressor { } elseif (App::import('Vendor', 'js/jquery/' . $plugin, array('file' => "js/$filename"))) { MiCompressor::log(' ' . $filename); MiCompressor::log(" Found vendor version js/$filename for Jquery $plugin"); - } elseif (App::import('Vendor', 'jquery/' . $plugin, array('file' => "jquery/plugins/$plugin/$filename"))) { + } elseif (App::import('Vendor', 'js/jquery/' . $plugin, array('file' => "jquery/plugins/$plugin/$filename"))) { + MiCompressor::log(' ' . $filename); MiCompressor::log(" Found vendor version jquery/plugins/$plugin/$filename for Jquery $plugin"); } else { - MiCompressor::log(" PROBLEM: The jquery plugin jquery/plugins/$plugin/$filename could not be loaded."); - $_compress = false; + $found = false; } - if ($_compress) { - $pluginContents = ob_get_clean(); - ob_start(); - echo MiCompressor::compressJs($pluginContents, 'jquery.' . $plugin, "\t"); + if ($found) { + echo ob_get_clean(); + return; } - echo ob_get_clean(); + $filename = str_replace('.min.js', '.js', $filename); + } + ob_start(); + MiCompressor::log(' ' . $filename); + if (file_exists(JS . $filename)) { + MiCompressor::log(' File ' . JS . "$filename found"); + echo file_get_contents(JS . $filename); + } elseif (App::import('Vendor', 'js/jquery/' . $plugin, array('file' => "js/jquery/$filename"))) { + MiCompressor::log(" Found vendor version js/jquery/$filename for Jquery $plugin"); + } elseif (App::import('Vendor', 'js/jquery/' . $plugin, array('file' => "js/$filename"))) { + MiCompressor::log(" Found vendor version js/$filename for Jquery $plugin"); + } elseif (App::import('Vendor', 'jquery/' . $plugin, array('file' => "jquery/plugins/$plugin/$filename"))) { + MiCompressor::log(" Found vendor version jquery/plugins/$plugin/$filename for Jquery $plugin"); + } else { + MiCompressor::log(" PROBLEM: The jquery plugin jquery/plugins/$plugin/$filename could not be loaded."); + $_minify = false; + } + if ($_minify) { + $pluginContents = ob_get_clean(); + ob_start(); + echo MiCompressor::minifyJs($pluginContents, 'jquery.' . $plugin, "\t"); } return ob_get_clean(); } @@ -550,21 +894,16 @@ abstract class MiCompressor { * @param string $contents * @static * @return string the contents to output, or null if no output is to be sent (to trigger a 304 header upstream) - * @access private + * @access protected */ - private static function out($file, $contents = null, $eTag = null) { - if ($contents === null) { - $contents = file_get_contents($file); - debug ($contents); - die; - } + protected static function out($file, $contents = null, $eTag = null) { if (preg_match('@^/\* eTag:(\S+) \*/@', $contents, $match)) { $eTag = $match['1']; } if ($eTag) { $contents = str_replace('/* eTag:' . $eTag .' */', '', $contents); } - if (!MiCompressor::loadCompressLib('headers')) { + if (!MiCompressor::loadMinifyLib('headers')) { return $contents; } if (file_exists($file)) { @@ -572,7 +911,7 @@ abstract class MiCompressor { } else { $fileCreated = time(); } - $fileExpires = strtotime(MiCompressor::$cacheDuration, $fileCreated); + $fileExpires = strtotime(MiCompressor::cRead('cacheDuration'), $fileCreated); $params = array( 'lastModifiedTime' => $fileCreated, @@ -589,111 +928,95 @@ abstract class MiCompressor { return $contents; } /** - * url method + * parse method * - * @param array $request - * @param array $params - * @return void - * @access public + * Used to determine which component files, and sub files have been requested + * For the request string of the form: file|file2|file3|file4,x,y=300,z|file5.. generate an array of the form: + * array( + * file => array(), + * file2 => array(), + * file3 => array(), + * file4 => array('x', 'y' => 300, 'z'), + * file5 => array() + * ) + * This format allows passing parameters to scripts when included (accessible as $params); + * + * A hash is automatically added to the url by the MiHtml and MiJavascript helpers. The hash is checked in this + * method and if it doesn't match (this should never happen for a valid request), will either add a log message if not + * in production mode or return false. Compressing (in particular Js files) is expensive, therefore using a hash + * prevents a malicious user from requesting arbritary varying urls and tying up the server + * + * The hash is returned primarily to be used as the default cache key + * + * @param string $requestString + * @param mixed $hash + * @static + * @return mixed array of files to process, or false for an invalid/doctored request + * @access protected */ - function url($request = array(), $params = array()) { - extract(am(array( - 'salt' => defined('SITE_VERSION')?SITE_VERSION:'', - 'sendAlone' => Configure::read(), - 'type' => 'js', - 'sizeLimit' => false, - ), $params)); - $stack = array(); - $i = 0; - foreach ($request as $key => &$value) { - $_sendAlone = $sendAlone; - if (is_string($key)) { - if (is_array($value) && isset($value['sendAlone'])) { - $_sendAlone = $value['sendAlone']; - unset($value['sendAlone']); - } - if (!$value) { - $value = $key; - } elseif($type === 'js' && $key === 'jquery' && ($sizeLimit || $_sendAlone)) { - $i++; - $stack[$i][] = 'jquery'; - foreach ($value as $plugin) { - $i++; - $stack[$i][] = 'jquery.' . $plugin; - $i++; - } - continue; - } else { - foreach ($value as $_k => &$_v) { - if (!is_numeric($_k)) { - $_v = $_k . '=' . $_v; - } - } - $value = $key . ',' . implode(',', $value); - } - } - if ($_sendAlone) { - $i++; + protected static function parse($requestString = '', &$hash = null) { + if ($hash === null) { + $requests = explode('|', $requestString); + $hash = array_pop($requests); + } else { + $requests = explode('|', $requestString); + if ($key = array_search('hash', $requests)) { + unset ($requests[$key]); + $hash = 'hash=' . $hash; } - $stack[$i][] = $value; - if ($_sendAlone) { - $i++; + } + $problem = false; + if (strpos($hash, 'hash=') !== 0) { + $problem = true; + MiCompressor::log('PROBLEM: No hash in the request string'); + } else { + list(,$hash) = explode('=', $hash); + $_hash = MiCompressor::hash($requests); + if ($hash !== $_hash) { + $problem = true; + MiCompressor::log('PROBLEM: Hash doesn\'t match. Expected: ' . $_hash); } } + if ($problem && !MiCompressor::cRead()) { + return false; + } + $return = array(); - foreach ($stack as &$files) { - $url = MiCompressor::__url($files, $type, $salt, $sizeLimit); - foreach((array)$url as $u) { - $return[] = $u; + foreach ($requests as $filename) { + $params = array(); + if (strpos($filename, ',')) { + $params = explode(',', $filename); + $filename = array_shift($params); + foreach ($params as $k => $v) { + if (strpos($v, '=')) { + unset ($params[$k]); + list($k, $v) = explode('=', $v); + $params[$k] = $v; + } + } } - } - if (count($return) === 1) { - return $return[0]; + $return[$filename] = $params; } return $return; } /** - * cachePath method - * - * @param mixed $type - * @param mixed $hash - * @param mixed $appDebug - * @param bool $clear - * @param bool $relative - * @return void - * @access private - */ - function __cachePath($type, $hash, $appDebug = null, $clear = false, $relative = false) { - if ($appDebug === null) { - $appDebug = (class_exists('Configure')?Configure::read():false); - } - if ($clear === null) { - $clear = $appDebug; - } - $return = 'views/' . $type . '_' . $hash . '_' . ($appDebug?'d':'p') . ($clear?'t':'p'); - if ($relative) { - return $return; - } - return CACHE . $return; - } -/** * url method * * @param mixed $files * @param string $type - * @param string $salt * @param bool $sizeLimit - * @return void - * @access private + * @static + * @return mixed the url or urls corresponding to the requested files + * @access protected */ - function __url($files, $type = 'js', $salt = '', $sizeLimit = false) { + protected static function _url($files, $type = 'js', $sizeLimit = false) { $string = implode('|', $files); - $hash = Security::hash($salt . $string, null, true); + $hash = MiCompressor::hash($string); if ($sizeLimit && count($files) > 1) { - $cachePath = MiCompressor::__cachePath($type, $hash); + $cachePath = MiCompressor::cachePath($type, $hash); if (file_exists($cachePath) && filesize($cachePath) > ($sizeLimit - 45)) { foreach($files as $file) { - $return[] = MiCompressor::__url(array($file), $type, $salt); + $return[] = MiCompressor::_url(array($file), $type); } return $return; } @@ -705,11 +1028,25 @@ abstract class MiCompressor { /** * Included for compatibility to allow use and testing in a standalone manner */ +if (!defined('DS')) { + define('DS', DIRECTORY_SEPARATOR); +} +if (!defined('WWW_ROOT')) { + define('WWW_ROOT', dirname(__FILE__)); +} +if (!defined('CSS')) { + define('CSS', WWW_ROOT . 'css' . DS); +} +if (!defined('JS')) { + define('JS', WWW_ROOT . 'js' . DS); +} if (!defined('CACHE')) { - define('CACHE', 'cache'); - define('CSS', 'css'); - define('JS', 'js'); + define('CACHE', dirname(__FILE__) . DS . 'cache' . DS); +} +if (!defined('SITE_VERSION')) { + define('SITE_VERSION', 42); } + if (!function_exists('getMicrotime')) { /** * Returns microtime for execution time checking @@ -721,4 +1058,42 @@ if (!function_exists('getMicrotime')) { return ((float)$usec + (float)$sec); } } +if (!function_exists('cache')) { +/** + * cache method + * + * Duplicate of CakePHP cache method to allow using this script standalone + * + * @param mixed $path + * @param mixed $data + * @param mixed $expires null + * @return string cache contents + * @access public + */ + function cache($path, $data, $expires = null) { + $now = time(); + if (!is_numeric($expires)) { + $expires = strtotime($expires, $now); + } + + $filename = MiCompressor::cRead('cacheDir') . $path; + $timediff = $expires - $now; + $filetime = false; + if (file_exists($filename)) { + $filetime = @filemtime($filename); + } + if ($data === null) { + if (file_exists($filename) && $filetime !== false) { + if ($filetime + $timediff < $now) { + @unlink($filename); + } else { + $data = @file_get_contents($filename); + } + } + } elseif (is_writable(dirname($filename))) { + @file_put_contents($filename, $data); + } + return $data; + } +} ?> \ No newline at end of file diff --git a/views/elements/markitup.ctp b/views/elements/markitup.ctp index c7c1bab..c081691 100644 --- a/views/elements/markitup.ctp +++ b/views/elements/markitup.ctp @@ -1,9 +1,9 @@ <?php /* SVN FILE: $Id: markitup.ctp 673 2008-10-06 14:05:17Z AD7six $ */ -$miJavascript->link('markitup/jquery.markitup.pack.js', false); +$miJavascript->link('markitup/jquery.markitup.js', false); $miJavascript->link('markitup/sets/default/set.js', false); $html->css(array('/js/markitup/skins/markitup/style.css', '/js/markitup/sets/default/style.css'), null, array(), false); $miJavascript->codeBlock( '$(document).ready(function() { $("' . $process . '").markItUp(mySettings); });', array('inline' => false)); -?> +?> \ No newline at end of file diff --git a/webroot/css/cake.cookbook.css b/webroot/css/cake.cookbook.css index b0298c2..5b67e38 100644 --- a/webroot/css/cake.cookbook.css +++ b/webroot/css/cake.cookbook.css @@ -476,7 +476,7 @@ a.leavePopup { margin:-10px 0 0; padding:1px; position:absolute; - right:1.5em; + right:26px; top:50%; width:19px; } \ No newline at end of file diff --git a/webroot/js/markitup/jquery.markitup.min.js b/webroot/js/markitup/jquery.markitup.min.js new file mode 100644 index 0000000..73c8c01 --- /dev/null +++ b/webroot/js/markitup/jquery.markitup.min.js @@ -0,0 +1,9 @@ +// ---------------------------------------------------------------------------- +// markItUp! Universal MarkUp Engine, JQuery plugin +// v 1.1.0 beta +// Dual licensed under the MIT and GPL licenses. +// ---------------------------------------------------------------------------- +// Copyright (C) 2007-2008 Jay Salvat +// http://markitup.jaysalvat.com/ +// ---------------------------------------------------------------------------- +eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(3($){$.2L.Y=3(f,g){C k,u,A,I;u=A=I=l;k={B:\'\',17:\'\',T:\'\',1w:\'\',1U:o,2w:\'2s\',1j:\'~/3l/1D.15\',1c:\'\',28:\'25\',1k:o,1B:\'\',1A:\'\',1z:{},1X:{},1V:{},1T:{},2E:[{}]};$.R(k,f,g);2(!k.T){$(\'3v\').1f(3(a,b){1N=$(b).11(0).3j.3f(/(.*)3c\\.3a(\\.37)?\\.34$/);2(1N!==2g){k.T=1N[1]}})}4 G.1f(3(){C d,q,16,18,m,D,J,L,Z,1x,w,1u,K,14;d=$(G);q=G;16=[];14=l;18=m=0;D=-1;k.1c=1i(k.1c);k.1j=1i(k.1j);3 1i(a,b){2(b){4 a.W(/("|\')~\\//g,"$1"+k.T)}4 a.W(/^~\\//,k.T)}3 2C(){B=\'\';17=\'\';2(k.B){B=\'B="\'+k.B+\'"\'}7 2(d.1S("B")){B=\'B="Y\'+(d.1S("B").2y(0,1).3z())+(d.1S("B").2y(1))+\'"\'}2(k.17){17=\'O="\'+k.17+\'"\'}d.1P(\'<v \'+17+\'"></v>\');d.1P(\'<v \'+B+\' O="Y"></v>\');d.1P(\'<v O="3u"></v>\');d.2r("2q");Z=$(\'<v O="3r"></v>\').2p(d);$(1M(k.2E)).1L(Z);1x=$(\'<v O="3p"></v>\').1K(d);2(k.1k===o&&$.1a.3k!==o){1k=$(\'<v O="3i"></v>\').1K(d).1b("3d",3(e){C h=d.2a(),y=e.2d,1n,1o;1n=3(e){d.2u("2a",31.30(20,e.2d+h-y)+"2Y");4 l};1o=3(e){$("15").1I("2t",1n).1I("1t",1o);4 l};$("15").1b("2t",1n).1b("1t",1o)});1x.2n(1k)}d.23(1Q).2R(1Q);d.1b("1C",3(e,a){2(a.1r!==l){11()}2(q===$.Y.21){V(a)}});d.1h(3(){$.Y.21=G})}3 1M(b){C c=$(\'<U></U>\'),i=0;$(\'z:1Y > U\',c).2u(\'2M\',\'p\');$(b).1f(3(){C a=G,t=\'\',1m,z,j;1m=(a.12)?(a.1W||\'\')+\' [3K+\'+a.12+\']\':(a.1W||\'\');12=(a.12)?\'2J="\'+a.12+\'"\':\'\';2(a.2I){z=$(\'<z O="3J">\'+(a.2I||\'\')+\'</z>\').1L(c)}7{i++;2H(j=16.5-1;j>=0;j--){t+=16[j]+"-"}z=$(\'<z O="2G 2G\'+t+(i)+\' \'+(a.3I||\'\')+\'"><a 3H="" \'+12+\' 1m="\'+1m+\'">\'+(a.1W||\'\')+\'</a></z>\').1b("3G",3(){4 l}).2F(3(){4 l}).1t(3(){2(a.2D){3F(a.2D)()}V(a);4 l}).1Y(3(){$(\'> U\',G).3E();$(N).3D(\'2F\',3(){$(\'U U\',Z).2A()})},3(){$(\'> U\',G).2A()}).1L(c);2(a.2z){16.3C(i);$(z).2r(\'3B\').2n(1M(a.2z))}}});16.3A();4 c}3 2x(c){2(c){c=c.3w();c=c.W(/\\(\\!\\(([\\s\\S]*?)\\)\\!\\)/g,3(x,a){C b=a.1R(\'|!|\');2(I===o){4(b[1]!==2v)?b[1]:b[0]}7{4(b[1]===2v)?"":b[0]}});c=c.W(/\\[\\!\\[([\\s\\S]*?)\\]\\!\\]/g,3(x,a){C b=a.1R(\':!:\');2(14===o){4 l}1O=3t(b[0],(b[1])?b[1]:\'\');2(1O===2g){14=o}4 1O});4 c}4""}3 F(a){2($.3s(a)){a=a(L)}4 2x(a)}3 1g(a){H=F(J.H);19=F(J.19);Q=F(J.Q);M=F(J.M);2(Q!==""){p=H+Q+M}7 2(6===\'\'&&19!==\'\'){p=H+19+M}7{p=H+(a||6)+M}4{p:p,H:H,Q:Q,19:19,M:M}}3 V(a){C b,j,n,i;L=J=a;11();$.R(L,{1v:"",T:k.T,q:q,6:(6||\'\'),m:m});F(k.1B);F(J.1B);2(u===o&&A===o){F(J.3q)}$.R(L,{1v:1});2(u===o&&A===o){P=6.1R(/\\r?\\n/);2H(j=0,n=P.5,i=0;i<n;i++){2($.3o(P[i])!==\'\'){$.R(L,{1v:++j,6:P[i]});P[i]=1g(P[i]).p}7{P[i]=""}}8={p:P.3n(\'\\n\')};X=m;b=8.p.5+(($.1a.2m)?n:0)}7 2(u===o){8=1g(6);X=m+8.H.5;b=8.p.5-8.H.5-8.M.5;b-=1q(8.p)}7 2(A===o){8=1g(6);X=m;b=8.p.5;b-=1q(8.p)}7{8=1g(6);X=m+8.p.5;b=0;X-=1q(8.p)}2((6===\'\'&&8.Q===\'\')){D+=1J(8.p);X=m+8.H.5;b=8.p.5-8.H.5-8.M.5;D=d.E().1e(m,d.E().5).5;D-=1J(d.E().1e(0,m))}$.R(L,{m:m,18:18});2(8.p!==6&&14===l){2l(8.p);1H(X,b)}7{D=-1}11();$.R(L,{1v:\'\',6:6});2(u===o&&A===o){F(J.3h)}F(J.1A);F(k.1A);2(w&&k.1U){1F()}A=I=u=14=l}3 1J(a){2($.1a.2m){4 a.5-a.W(/\\n*/g,\'\').5}4 0}3 1q(a){2($.1a.2i){4 a.5-a.W(/\\r*/g,\'\').5}4 0}3 2l(a){2(N.6){C b=N.6.1E();b.2h=a}7{d.E(d.E().1e(0,m)+a+d.E().1e(m+6.5,d.E().5))}}3 1H(a,b){2(q.2e){1d=q.2e();1d.3b(o);1d.29(\'1G\',a);1d.39(\'1G\',b);1d.38()}7 2(q.2c){q.2c(a,a+b)}q.2b=18;q.1h()}3 11(){q.1h();18=q.2b;2(N.6){6=N.6.1E().2h;2($.1a.2i){C a=N.6.1E(),1p=a.36();1p.35(q);m=-1;33(1p.32(a)){1p.29(\'1G\');m++}}7{m=q.2k}}7{m=q.2k;6=d.E().1e(m,q.3e)}4 6}3 1D(){2(!w||w.2Z){2(k.1w){w=3g.2f(\'\',\'1D\',k.1w)}7{K=$(\'<2j O="2X"></2j>\');2(k.2w==\'2s\'){K.1K(1x)}7{K.2p(Z)}w=K[K.5-1].2W||2V[K.5-1]}}7 2(I===o){2(K){K.3m()}w.27();w=K=l}2(!k.1U){1F()}2(k.1w){w.1h()}}3 1F(){2(w){w.N.2f();w.N.2U(26());w.N.27()}}3 26(){2(k.1c!==\'\'){$.2o({24:\'2T\',2K:l,2B:k.1c,25:k.28+\'=\'+2S(d.E()),22:3(a){15=1i(a,1)}})}7{2(!1u){$.2o({2K:l,2B:k.1j,22:3(a){1u=1i(a,1)}})}15=1u.W(/<!-- 3x -->/g,d.E())}4 15}3 1Q(e){A=e.A;I=e.I;u=(!(e.I&&e.u))?e.u:l;$.R(L,{u:u,A:A,I:I});2(e.24===\'23\'){2(u===o){z=$("a[2J="+3y.2Q(e.1l)+"]",Z).1s(\'z\');2(z.5!==0){u=l;z.2P(\'1t\');4 l}}2(e.1l===13||e.1l===10){2(u===o){u=l;V(k.1V);4 k.1V.1y}7 2(A===o){A=l;V(k.1X);4 k.1X.1y}7{V(k.1z);4 k.1z.1y}}2(e.1l===9){2(D!==-1){11();D=d.E().5-D;1H(D,0);D=-1;4 l}7{V(k.1T);4 k.1T.1y}}}}2C()})};$.2L.2O=3(){4 G.1f(3(){$$=$(G).1I().2N(\'2q\');$$.1s(\'v\').1s(\'v.Y\').1s(\'v\').Q($$)})};$.Y=3(a){C b={1r:l};$.R(b,a);2(b.1r){4 $(b.1r).1f(3(){$(G).1h();$(G).1Z(\'1C\',[b])})}7{$(\'q\').1Z(\'1C\',[b])}}})(3L);',62,234,'||if|function|return|length|selection|else|string|||||||||||||false|caretPosition||true|block|textarea||||ctrlKey|div|previewWindow|||li|shiftKey|id|var|caretOffset|val|prepare|this|openWith|altKey|clicked|iFrame|hash|closeWith|document|class|lines|replaceWith|extend||root|ul|markup|replace|start|markItUp|header||get|key||abort|html|levels|nameSpace|scrollPosition|placeHolder|browser|bind|previewParserPath|range|substring|each|build|focus|localize|previewTemplatePath|resizeHandle|keyCode|title|mouseMove|mouseUp|rangeCopy|fixIeBug|target|parent|mouseup|template|line|previewInWindow|footer|keepDefault|onEnter|afterInsert|beforeInsert|insertion|preview|createRange|refreshPreview|character|set|unbind|fixOperaBug|insertAfter|appendTo|dropMenus|miuScript|value|wrap|keyPressed|split|attr|onTab|previewAutoRefresh|onCtrlEnter|name|onShiftEnter|hover|trigger||focused|success|keydown|type|data|renderPreview|close|previewParserVar|moveStart|height|scrollTop|setSelectionRange|clientY|createTextRange|open|null|text|msie|iframe|selectionStart|insert|opera|append|ajax|insertBefore|markItUpEditor|addClass|after|mousemove|css|undefined|previewPosition|magicMarkups|substr|dropMenu|hide|url|init|call|markupSet|click|markItUpButton|for|separator|accesskey|async|fn|display|removeClass|markItUpRemove|triggerHandler|fromCharCode|keyup|encodeURIComponent|POST|write|frame|contentWindow|markItUpPreviewFrame|px|closed|max|Math|inRange|while|js|moveToElementText|duplicate|pack|select|moveEnd|markitup|collapse|jquery|mousedown|selectionEnd|match|window|afterMultiInsert|markItUpResizeHandle|src|safari|templates|remove|join|trim|markItUpFooter|beforeMultiInsert|markItUpHeader|isFunction|prompt|markItUpContainer|script|toString|content|String|toUpperCase|pop|markItUpDropMenu|push|one|show|eval|contextmenu|href|className|markItUpSeparator|Ctrl|jQuery'.split('|'),0,{})) \ No newline at end of file diff --git a/webroot/js/markitup/jquery.markitup.pack.js b/webroot/js/markitup/jquery.markitup.pack.js deleted file mode 100644 index 73c8c01..0000000 --- a/webroot/js/markitup/jquery.markitup.pack.js +++ /dev/null @@ -1,9 +0,0 @@ -// ---------------------------------------------------------------------------- -// markItUp! Universal MarkUp Engine, JQuery plugin -// v 1.1.0 beta -// Dual licensed under the MIT and GPL licenses. -// ---------------------------------------------------------------------------- -// Copyright (C) 2007-2008 Jay Salvat -// http://markitup.jaysalvat.com/ -// ---------------------------------------------------------------------------- -eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(3($){$.2L.Y=3(f,g){C k,u,A,I;u=A=I=l;k={B:\'\',17:\'\',T:\'\',1w:\'\',1U:o,2w:\'2s\',1j:\'~/3l/1D.15\',1c:\'\',28:\'25\',1k:o,1B:\'\',1A:\'\',1z:{},1X:{},1V:{},1T:{},2E:[{}]};$.R(k,f,g);2(!k.T){$(\'3v\').1f(3(a,b){1N=$(b).11(0).3j.3f(/(.*)3c\\.3a(\\.37)?\\.34$/);2(1N!==2g){k.T=1N[1]}})}4 G.1f(3(){C d,q,16,18,m,D,J,L,Z,1x,w,1u,K,14;d=$(G);q=G;16=[];14=l;18=m=0;D=-1;k.1c=1i(k.1c);k.1j=1i(k.1j);3 1i(a,b){2(b){4 a.W(/("|\')~\\//g,"$1"+k.T)}4 a.W(/^~\\//,k.T)}3 2C(){B=\'\';17=\'\';2(k.B){B=\'B="\'+k.B+\'"\'}7 2(d.1S("B")){B=\'B="Y\'+(d.1S("B").2y(0,1).3z())+(d.1S("B").2y(1))+\'"\'}2(k.17){17=\'O="\'+k.17+\'"\'}d.1P(\'<v \'+17+\'"></v>\');d.1P(\'<v \'+B+\' O="Y"></v>\');d.1P(\'<v O="3u"></v>\');d.2r("2q");Z=$(\'<v O="3r"></v>\').2p(d);$(1M(k.2E)).1L(Z);1x=$(\'<v O="3p"></v>\').1K(d);2(k.1k===o&&$.1a.3k!==o){1k=$(\'<v O="3i"></v>\').1K(d).1b("3d",3(e){C h=d.2a(),y=e.2d,1n,1o;1n=3(e){d.2u("2a",31.30(20,e.2d+h-y)+"2Y");4 l};1o=3(e){$("15").1I("2t",1n).1I("1t",1o);4 l};$("15").1b("2t",1n).1b("1t",1o)});1x.2n(1k)}d.23(1Q).2R(1Q);d.1b("1C",3(e,a){2(a.1r!==l){11()}2(q===$.Y.21){V(a)}});d.1h(3(){$.Y.21=G})}3 1M(b){C c=$(\'<U></U>\'),i=0;$(\'z:1Y > U\',c).2u(\'2M\',\'p\');$(b).1f(3(){C a=G,t=\'\',1m,z,j;1m=(a.12)?(a.1W||\'\')+\' [3K+\'+a.12+\']\':(a.1W||\'\');12=(a.12)?\'2J="\'+a.12+\'"\':\'\';2(a.2I){z=$(\'<z O="3J">\'+(a.2I||\'\')+\'</z>\').1L(c)}7{i++;2H(j=16.5-1;j>=0;j--){t+=16[j]+"-"}z=$(\'<z O="2G 2G\'+t+(i)+\' \'+(a.3I||\'\')+\'"><a 3H="" \'+12+\' 1m="\'+1m+\'">\'+(a.1W||\'\')+\'</a></z>\').1b("3G",3(){4 l}).2F(3(){4 l}).1t(3(){2(a.2D){3F(a.2D)()}V(a);4 l}).1Y(3(){$(\'> U\',G).3E();$(N).3D(\'2F\',3(){$(\'U U\',Z).2A()})},3(){$(\'> U\',G).2A()}).1L(c);2(a.2z){16.3C(i);$(z).2r(\'3B\').2n(1M(a.2z))}}});16.3A();4 c}3 2x(c){2(c){c=c.3w();c=c.W(/\\(\\!\\(([\\s\\S]*?)\\)\\!\\)/g,3(x,a){C b=a.1R(\'|!|\');2(I===o){4(b[1]!==2v)?b[1]:b[0]}7{4(b[1]===2v)?"":b[0]}});c=c.W(/\\[\\!\\[([\\s\\S]*?)\\]\\!\\]/g,3(x,a){C b=a.1R(\':!:\');2(14===o){4 l}1O=3t(b[0],(b[1])?b[1]:\'\');2(1O===2g){14=o}4 1O});4 c}4""}3 F(a){2($.3s(a)){a=a(L)}4 2x(a)}3 1g(a){H=F(J.H);19=F(J.19);Q=F(J.Q);M=F(J.M);2(Q!==""){p=H+Q+M}7 2(6===\'\'&&19!==\'\'){p=H+19+M}7{p=H+(a||6)+M}4{p:p,H:H,Q:Q,19:19,M:M}}3 V(a){C b,j,n,i;L=J=a;11();$.R(L,{1v:"",T:k.T,q:q,6:(6||\'\'),m:m});F(k.1B);F(J.1B);2(u===o&&A===o){F(J.3q)}$.R(L,{1v:1});2(u===o&&A===o){P=6.1R(/\\r?\\n/);2H(j=0,n=P.5,i=0;i<n;i++){2($.3o(P[i])!==\'\'){$.R(L,{1v:++j,6:P[i]});P[i]=1g(P[i]).p}7{P[i]=""}}8={p:P.3n(\'\\n\')};X=m;b=8.p.5+(($.1a.2m)?n:0)}7 2(u===o){8=1g(6);X=m+8.H.5;b=8.p.5-8.H.5-8.M.5;b-=1q(8.p)}7 2(A===o){8=1g(6);X=m;b=8.p.5;b-=1q(8.p)}7{8=1g(6);X=m+8.p.5;b=0;X-=1q(8.p)}2((6===\'\'&&8.Q===\'\')){D+=1J(8.p);X=m+8.H.5;b=8.p.5-8.H.5-8.M.5;D=d.E().1e(m,d.E().5).5;D-=1J(d.E().1e(0,m))}$.R(L,{m:m,18:18});2(8.p!==6&&14===l){2l(8.p);1H(X,b)}7{D=-1}11();$.R(L,{1v:\'\',6:6});2(u===o&&A===o){F(J.3h)}F(J.1A);F(k.1A);2(w&&k.1U){1F()}A=I=u=14=l}3 1J(a){2($.1a.2m){4 a.5-a.W(/\\n*/g,\'\').5}4 0}3 1q(a){2($.1a.2i){4 a.5-a.W(/\\r*/g,\'\').5}4 0}3 2l(a){2(N.6){C b=N.6.1E();b.2h=a}7{d.E(d.E().1e(0,m)+a+d.E().1e(m+6.5,d.E().5))}}3 1H(a,b){2(q.2e){1d=q.2e();1d.3b(o);1d.29(\'1G\',a);1d.39(\'1G\',b);1d.38()}7 2(q.2c){q.2c(a,a+b)}q.2b=18;q.1h()}3 11(){q.1h();18=q.2b;2(N.6){6=N.6.1E().2h;2($.1a.2i){C a=N.6.1E(),1p=a.36();1p.35(q);m=-1;33(1p.32(a)){1p.29(\'1G\');m++}}7{m=q.2k}}7{m=q.2k;6=d.E().1e(m,q.3e)}4 6}3 1D(){2(!w||w.2Z){2(k.1w){w=3g.2f(\'\',\'1D\',k.1w)}7{K=$(\'<2j O="2X"></2j>\');2(k.2w==\'2s\'){K.1K(1x)}7{K.2p(Z)}w=K[K.5-1].2W||2V[K.5-1]}}7 2(I===o){2(K){K.3m()}w.27();w=K=l}2(!k.1U){1F()}2(k.1w){w.1h()}}3 1F(){2(w){w.N.2f();w.N.2U(26());w.N.27()}}3 26(){2(k.1c!==\'\'){$.2o({24:\'2T\',2K:l,2B:k.1c,25:k.28+\'=\'+2S(d.E()),22:3(a){15=1i(a,1)}})}7{2(!1u){$.2o({2K:l,2B:k.1j,22:3(a){1u=1i(a,1)}})}15=1u.W(/<!-- 3x -->/g,d.E())}4 15}3 1Q(e){A=e.A;I=e.I;u=(!(e.I&&e.u))?e.u:l;$.R(L,{u:u,A:A,I:I});2(e.24===\'23\'){2(u===o){z=$("a[2J="+3y.2Q(e.1l)+"]",Z).1s(\'z\');2(z.5!==0){u=l;z.2P(\'1t\');4 l}}2(e.1l===13||e.1l===10){2(u===o){u=l;V(k.1V);4 k.1V.1y}7 2(A===o){A=l;V(k.1X);4 k.1X.1y}7{V(k.1z);4 k.1z.1y}}2(e.1l===9){2(D!==-1){11();D=d.E().5-D;1H(D,0);D=-1;4 l}7{V(k.1T);4 k.1T.1y}}}}2C()})};$.2L.2O=3(){4 G.1f(3(){$$=$(G).1I().2N(\'2q\');$$.1s(\'v\').1s(\'v.Y\').1s(\'v\').Q($$)})};$.Y=3(a){C b={1r:l};$.R(b,a);2(b.1r){4 $(b.1r).1f(3(){$(G).1h();$(G).1Z(\'1C\',[b])})}7{$(\'q\').1Z(\'1C\',[b])}}})(3L);',62,234,'||if|function|return|length|selection|else|string|||||||||||||false|caretPosition||true|block|textarea||||ctrlKey|div|previewWindow|||li|shiftKey|id|var|caretOffset|val|prepare|this|openWith|altKey|clicked|iFrame|hash|closeWith|document|class|lines|replaceWith|extend||root|ul|markup|replace|start|markItUp|header||get|key||abort|html|levels|nameSpace|scrollPosition|placeHolder|browser|bind|previewParserPath|range|substring|each|build|focus|localize|previewTemplatePath|resizeHandle|keyCode|title|mouseMove|mouseUp|rangeCopy|fixIeBug|target|parent|mouseup|template|line|previewInWindow|footer|keepDefault|onEnter|afterInsert|beforeInsert|insertion|preview|createRange|refreshPreview|character|set|unbind|fixOperaBug|insertAfter|appendTo|dropMenus|miuScript|value|wrap|keyPressed|split|attr|onTab|previewAutoRefresh|onCtrlEnter|name|onShiftEnter|hover|trigger||focused|success|keydown|type|data|renderPreview|close|previewParserVar|moveStart|height|scrollTop|setSelectionRange|clientY|createTextRange|open|null|text|msie|iframe|selectionStart|insert|opera|append|ajax|insertBefore|markItUpEditor|addClass|after|mousemove|css|undefined|previewPosition|magicMarkups|substr|dropMenu|hide|url|init|call|markupSet|click|markItUpButton|for|separator|accesskey|async|fn|display|removeClass|markItUpRemove|triggerHandler|fromCharCode|keyup|encodeURIComponent|POST|write|frame|contentWindow|markItUpPreviewFrame|px|closed|max|Math|inRange|while|js|moveToElementText|duplicate|pack|select|moveEnd|markitup|collapse|jquery|mousedown|selectionEnd|match|window|afterMultiInsert|markItUpResizeHandle|src|safari|templates|remove|join|trim|markItUpFooter|beforeMultiInsert|markItUpHeader|isFunction|prompt|markItUpContainer|script|toString|content|String|toUpperCase|pop|markItUpDropMenu|push|one|show|eval|contextmenu|href|className|markItUpSeparator|Ctrl|jQuery'.split('|'),0,{})) \ No newline at end of file