__engineName = $className . 'Engine'; $engineClass = $engineName . 'Engine'; $this->helpers[] = $engineClass; parent::__construct(); } /** * call__ * * @param string $method Method to be called * @param array $params Parameters for the method being called. * @access public * @return void **/ function call__($method, $params) { if (isset($this->{$this->__engineName}) && method_exists($this->{$this->__engineName}, $method)) { return $this->{$this->__engineName}->dispatchMethod($method, $params); } if (method_exists($this, $method . '_')) { return $this->dispatchMethod($method . '_', $params); } trigger_error(sprintf(__('JsHelper:: Missing Method %s is undefined', true), $method), E_USER_WARNING); } /** * Writes all Javascript generated so far to a code block or * caches them to a file and returns a linked script. * * Options * * - 'inline' - Set to true to have scripts output as a script block inline * if 'cache' is also true, a script link tag will be generated. (default true) * - 'cache' - Set to true to have scripts cached to a file and linked in (default true) * - 'clear' - Set to false to prevent script cache from being cleared (default true) * - 'onDomReady' - wrap cached scripts in domready event (default true) * - 'safe' - if an inline block is generated should it be wrapped in (default true) * * @param array $options options for the code block * @return string completed javascript tag. **/ function writeScripts($options = array()) { $defaults = array('onDomReady' => true, 'inline' => true, 'cache' => true, 'clear' => true, 'safe' => true); $options = array_merge($defaults, $options); $script = implode("\n", $this->{$this->__engineName}->getCache($options['clear'])); if ($options['onDomReady']) { $script = $this->{$this->__engineName}->domReady($script); } if (!$options['cache'] && $options['inline']) { return $this->Html->scriptBlock($script, $options); } if ($options['cache'] && $options['inline']) { //cache to file and return script tag. } $view =& ClassRegistry::getObject('view'); $view->addScript($script); return null; } /** * Loads a remote URL * * @param string $url * @param array $options * @return string **/ /* function load_($url = null, $options = array()) { if (isset($options['update'])) { if (!is_array($options['update'])) { $func = "new Ajax.Updater('{$options['update']}',"; } else { $func = "new Ajax.Updater(document.createElement('div'),"; } if (!isset($options['requestHeaders'])) { $options['requestHeaders'] = array(); } if (is_array($options['update'])) { $options['update'] = join(' ', $options['update']); } $options['requestHeaders']['X-Update'] = $options['update']; } else { $func = "new Ajax.Request("; } $func .= "'" . Router::url($url) . "'"; $ajax =& new AjaxHelper(); $func .= ", " . $ajax->__optionsForAjax($options) . ")"; if (isset($options['before'])) { $func = "{$options['before']}; $func"; } if (isset($options['after'])) { $func = "$func; {$options['after']};"; } if (isset($options['condition'])) { $func = "if ({$options['condition']}) { $func; }"; } if (isset($options['confirm'])) { $func = "if (confirm('" . $this->Javascript->escapeString($options['confirm']) . "')) { $func; } else { return false; }"; } return $func; } /* function get__($name) { return $this->__object($name, 'id'); } function select($pattern) { return $this->__object($pattern, 'pattern'); } function real($var) { return $this->__object($var, 'real'); } function __object($name, $var) { if (!isset($this->__objects[$name])) { $this->__objects[$name] = new JsHelperObject($this); $this->__objects[$name]->{$var} = $name; } return $this->__objects[$name]; } */ } /** * JsEngineBaseClass * * Abstract Base Class for All JsEngines to extend. Provides generic methods. * * @package cake.view.helpers **/ class JsBaseEngineHelper extends AppHelper { /** * Determines whether native JSON extension is used for encoding. Set by object constructor. * * @var boolean * @access public **/ var $useNative = false; /** * The js snippet for the current selection. * * @var string * @access protected **/ var $_selection; /** * Scripts that are queued for output * * @var array **/ var $__cachedScripts = array(); /** * Constructor. * * @return void **/ function __construct() { $this->useNative = function_exists('json_encode'); } /** * Create an alert message in Javascript * * @param string $message Message you want to alter. * @access public * @return void **/ function alert($message) { return 'alert("' . $this->escape($message) . '");'; } /** * Redirects to a URL * * @param mixed $url * @param array $options * @return string **/ function redirect($url = null) { return 'window.location = "' . Router::url($url) . '";'; } /** * Create a confirm() message * * @param string $message Message you want confirmed. * @access public * @return void **/ function confirm($message) { return 'confirm("' . $this->escape($message) . '");'; } /** * Create a prompt() Javascript function * * @param string $message Message you want to prompt. * @param string $default Default message * @access public * @return void **/ function prompt($message, $default = '') { return 'prompt("' . $this->escape($message) . '", "' . $this->escape($default) . '");'; } /** * Generates a JavaScript object in JavaScript Object Notation (JSON) * from an array. Will use native JSON encode method if available, and $useNative == true * * Options: * * - 'prefix' - String prepended to the returned data. * - 'postfix' - String appended to the returned data. * - 'stringKeys' - A list of array keys to be treated as a string * - 'quoteKeys' - If false treats $options['stringKeys'] as a list of keys **not** to be quoted. * - 'q' - Type of quote to use. * * @param array $data Data to be converted. * @param array $options Set of options, see above. * @return string A JSON code block * @access public **/ function object($data = array(), $options = array()) { $defaultOptions = array( 'block' => false, 'prefix' => '', 'postfix' => '', 'stringKeys' => array(), 'quoteKeys' => true, 'q' => '"' ); $options = array_merge($defaultOptions, $options); if (is_object($data)) { $data = get_object_vars($data); } $out = $keys = array(); $numeric = true; if ($this->useNative) { $rt = json_encode($data); } else { if (is_array($data)) { $keys = array_keys($data); } if (!empty($keys)) { $numeric = (array_values($keys) === array_keys(array_values($keys))); } foreach ($data as $key => $val) { if (is_array($val) || is_object($val)) { $val = $this->object($val, array_merge($options, array('block' => false))); } else { $quoteStrings = ( !count($options['stringKeys']) || ($options['quoteKeys'] && in_array($key, $options['stringKeys'], true)) || (!$options['quoteKeys'] && !in_array($key, $options['stringKeys'], true)) ); $val = $this->value($val, $quoteStrings); } if (!$numeric) { $val = $options['q'] . $this->value($key, false) . $options['q'] . ':' . $val; } $out[] = $val; } if (!$numeric) { $rt = '{' . join(',', $out) . '}'; } else { $rt = '[' . join(',', $out) . ']'; } } $rt = $options['prefix'] . $rt . $options['postfix']; if ($options['block']) { $rt = $this->codeBlock($rt, array_diff_key($options, $defaultOptions)); } return $rt; } /** * Converts a PHP-native variable of any type to a JSON-equivalent representation * * @param mixed $val A PHP variable to be converted to JSON * @param boolean $quoteStrings If false, leaves string values unquoted * @return string a JavaScript-safe/JSON representation of $val * @access public **/ function value($val, $quoteStrings = true) { switch (true) { case (is_array($val) || is_object($val)): $val = $this->object($val); break; case ($val === null): $val = 'null'; break; case (is_bool($val)): $val = ($val === true) ? 'true' : 'false'; break; case (is_int($val)): $val = $val; break; case (is_float($val)): $val = sprintf("%.11f", $val); break; default: $val = $this->escape($val); if ($quoteStrings) { $val = '"' . $val . '"'; } break; } return $val; } /** * Escape a string to be JavaScript friendly. * * List of escaped ellements: * + "\r\n" => '\n' * + "\r" => '\n' * + "\n" => '\n' * + '"' => '\"' * + "'" => "\\'" * * @param string $script String that needs to get escaped. * @return string Escaped string. * @access public **/ function escape($string) { $escape = array("\r\n" => '\n', "\r" => '\n', "\n" => '\n', '"' => '\"', "'" => "\\'"); return str_replace(array_keys($escape), array_values($escape), $string); } /** * Write a script to the cached scripts. * * @return void **/ function writeCache($script) { $this->__cachedScripts[] = $script; } /** * Get all the cached scripts * * @param boolean $clear Whether or not to clear the script cache.s * @return array Array of scripts added to the request. **/ function getCache($clear = true) { $scripts = $this->__cachedScripts; if ($clear) { $this->__cachedScripts = array(); } return $scripts; } /** * Create javascript selector for a CSS rule * * @param string $selector The selector that is targeted * @param boolean $multiple Whether or not the selector could target more than one element. * @return object instance of $this. Allows chained methods. **/ function select($selector, $multiple = false) { return $this; } /** * Add an event to the script cache. Operates on the currently selected elements. * * @param string $type Type of event to bind to the current dom id * @param string $callback The Javascript function you wish to trigger or the function literal * @return string completed event handler **/ function addEvent($type, $callback) { } /** * Create a domReady event. This is a special event in many libraries * * @param string $functionBody The code to run on domReady * @return string completed domReady method **/ function domReady($functionBody) { } /** * Create an iteration over the current selection result. * * @param string $method The method you want to apply to the selection * @param string $callback The function body you wish to apply during the iteration. * @return string completed iteration **/ function each($method, $callback) { } /** * Parse an options assoc array into an Javascript object literal. * Similar to object() but treats any non-integer value as a string, * does not include { } * * @param array $options Options to be converted * @return string * @access protected **/ function _parseOptions($options) { $out = array(); foreach ($options as $key => $value) { if (!is_int($val)) { $val = '"' . $val . '"'; } $out[] = $key . ':' . $val; } return join(', ', $out); } } class JsHelperObject { var $__parent = null; var $id = null; var $pattern = null; var $real = null; function __construct(&$parent) { if (is_object($parent)) { $this->setParent($parent); } } function toString() { return $this->__toString(); } function __toString() { return $this->literal; } function ref($ref = null) { if ($ref == null) { foreach (array('id', 'pattern', 'real') as $ref) { if ($this->{$ref} !== null) { return $this->{$ref}; } } } else { return ($this->{$ref} !== null); } return null; } function literal($append = null) { if (!empty($this->id)) { $data = '$("' . $this->id . '")'; } if (!empty($this->pattern)) { $data = '$$("' . $this->pattern . '")'; } if (!empty($this->real)) { $data = $this->real; } if (!empty($append)) { $data .= '.' . $append; } return $data; } function __call($name, $args) { $data = ''; if (isset($this->__parent->effectMap[strtolower($name)])) { array_unshift($args, $this->__parent->effectMap[strtolower($name)]); $name = 'effect'; } switch ($name) { case 'effect': case 'visualEffect': if (strpos($args[0], '_') || $args[0]{0} != strtoupper($args[0]{0})) { $args[0] = Inflector::camelize($args[0]); } if (strtolower($args[0]) == 'highlight') { $data .= 'new '; } if ($this->pattern == null) { $data .= 'Effect.' . $args[0] . '(' . $this->literal(); } else { $data .= 'Effect.' . $args[0] . '(item'; } if (isset($args[1]) && is_array($args[1])) { $data .= ', {' . $this->__options($args[1]) . '}'; } $data .= ');'; if ($this->pattern !== null) { $data = $this->each($data); } break; case 'remove': case 'toggle': case 'show': case 'hide': if (empty($args)) { $obj = 'Element'; $params = ''; } else { $obj = 'Effect'; $params = ', "' . $args[0] . '"'; } if ($this->pattern != null) { $data = $this->each($obj . ".{$name}(item);"); } else { $data = $obj . ".{$name}(" . $this->literal() . ');'; } break; case 'visible': $data = $this->literal() . '.visible();'; break; case 'update': $data = $this->literal() . ".update({$args[0]});"; break; case 'load': $data = 'new Ajax.Updater("' . $this->id . '", "' . $args[0] . '"'; if (isset($args[1]) && is_array($args[1])) { $data .= ', {' . $this->__options($args[1]) . '}'; } $data .= ');'; break; case 'each': case 'all': case 'any': case 'detect': case 'findAll': if ($this->pattern != null) { $data = $this->__iterate($name, $args[0]); } break; case 'addClass': case 'removeClass': case 'hasClass': case 'toggleClass': $data = $this->literal() . ".{$name}Name(\"{$args[0]}\");"; break; case 'clone': case 'inspect': case 'keys': case 'values': $data = "Object.{$name}(" . $this->literal() . ");"; break; case 'extend': $data = "Object.extend(" . $this->literal() . ", {$args[0]});"; break; case '...': // Handle other methods here // including interfaces to load other files on-the-fly // that add support for additional methods/replacing existing methods break; default: $data = $this->literal() . '.' . $name . '();'; break; } if ($this->__parent->output) { echo $data; } else { return $data; } } function __iterate($method, $data) { return '$$("' . $this->pattern . '").' . $method . '(function(item) {' . $data . '});'; } function setParent(&$parent) { $this->__parent =& $parent; } function __options($opts) { $options = array(); foreach ($opts as $key => $val) { if (!is_int($val)) { $val = '"' . $val . '"'; } $options[] = $key . ':' . $val; } return join(', ', $options); } } ?>