Prototip Helper
This is a helper for people using prototip. Prototip is available here: http://www.nickstakenburg.com/projects/prototip/files/prototip1.1.0.zip
The provided code was written for the 1.1.x branch of CakePHP. I would imagine that it would easily be updated for 1.2.x
The provided code was written for the 1.1.x branch of CakePHP. I would imagine that it would easily be updated for 1.2.x
There are three public functions in this class:
1. $prototip->tooltip()
2. $prototip->addTooltip()
3. $prototip->renderTooltips()
A single private function parses argument arrays:
$this->_parseOptions()
There are three arguments to pass to the tooltip() function: el the element, content the content of the tooltip, and an optional options array.
tooltip() simply adds a single toolip for the element specified in the argument. It automagically detects if it should render a simple tooltip, 2 arguments provided, or a fancy tooltip, 3 arguments provided.
addTooltip() adds a tooltip to an internal array, but does not render it to screen until told to do so. This is helpful if you are generating tooltips for each row in a database in an element, but wish to render all tooltips in a single script tag.
Update 03/02/08 - modified to allow any DOM element as the first argument for both tooltip() and addTootip(). (See code below).
renderTooltips() renders all tooltips added to the internal array via addTooltip() in a single script tag.
When using this helper in ajax callbacks, it's best to use the addTooltip() + renderTootlip() combination in your calling code, and in the callback update the tooltip with tooltip().
Here is the code:
Download code
NB: when using the prototip options that require a hash, you may need some funky quotes...e.g.Download code
unless you have numeric values, e.g.
Download code
Loosey goosey scripting languages indeed.
1. $prototip->tooltip()
2. $prototip->addTooltip()
3. $prototip->renderTooltips()
A single private function parses argument arrays:
$this->_parseOptions()
There are three arguments to pass to the tooltip() function: el the element, content the content of the tooltip, and an optional options array.
tooltip() simply adds a single toolip for the element specified in the argument. It automagically detects if it should render a simple tooltip, 2 arguments provided, or a fancy tooltip, 3 arguments provided.
addTooltip() adds a tooltip to an internal array, but does not render it to screen until told to do so. This is helpful if you are generating tooltips for each row in a database in an element, but wish to render all tooltips in a single script tag.
Update 03/02/08 - modified to allow any DOM element as the first argument for both tooltip() and addTootip(). (See code below).
renderTooltips() renders all tooltips added to the internal array via addTooltip() in a single script tag.
When using this helper in ajax callbacks, it's best to use the addTooltip() + renderTootlip() combination in your calling code, and in the callback update the tooltip with tooltip().
Here is the code:
Download code
<?php
class PrototipHelper extends Helper {
/**
* @link http://www.nickstakenburg.com/projects/prototip/ the js library this helper works with
* @var Array a flat array of the allowed options
*/
var $allowed_options = array('className','closeButton','duration','delay','effect','fixed','hideOn','hook','offset','showOn','target','title','viewport');
/**
* @var Array a flat array with the special case option names
*/
var $sc_options = array('hook','offset','hideOn');
/**
* @var Array holds the basic string for creating a new Tooltip
*/
var $tooltip = array('base'=>"new Tip('%s',%s);", 'fancy'=>"new Tip('%s',%s,%s);");
/**
* @var Array cakephp helpers used by this helper
*/
var $helpers = array('Html', 'Javascript');
/**
* @var Array holds tooltips to be rendered in a block
*/
var $tips = array();
/**
* @public
*
* @param String $el the id of the element
* @param String $content html to show in the tip
* @param Array $options the tooltip options
*
* @return String a formatted tooltip instantiation
*/
function tooltip($el,$content,$options=array()) {
if ( substr($content,0,1)!='$') $content = "'$content'";
if($options) {
return $this->Javascript->codeBlock($this->output(sprintf($this->tooltip['fancy'],$el,$content,$this->_parseOptions($options))));
} else {
return $this->Javascript->codeBlock($this->output(sprintf($this->tooltip['base'],$el,$content)));
}
}
/**
* @public
*
* @param String $el the id of the element
* @param String $content html to show in the tip
* @param Array $options the tooltip options
*
* Adds a formatted tooltip to the $tips array
*/
function addTooltip($el, $content, $options=array()) {
if ( substr($content,0,1)!='$') $content = "'$content'";
if($options) {
$this->tips[] = sprintf($this->tooltip['fancy'],$el,$content,$this->_parseOptions($options));
} else {
$this->tips[] = sprintf($this->tooltip['base'],$el,$content);
}
}
/**
* @public
*
* Renders the array of tooltips in $tips as a Javascript code block
*/
function renderTooltips() {
$tips_string = "<script type=\"text/javascript\">\n";
foreach($this->tips as $tip) {
$tips_string .= $tip . "\n";
}
$tips_string .= '</script>';
return $this->output($tips_string);
}
/**
* @private
*
* @param Array $options an array of the options available to prototip
*
* @return String a formatted string of options i.e. {'opt':'value'...}
*/
function _parseOptions($options=array()) {
$opts = "{";
$arr_opts = array();
foreach($options as $key => $value) {
if(in_array($key,$this->allowed_options)) {
if(in_array($key, $this->sc_options)) { //special case for formatting options
if(strpos($value, '{') !== false) { // the option has a tuple...e.g. 'hook:{target:'topLeft',tip:'rightMiddle'}
$sc = explode(',' ,$value);
$str_sc_opts = "'$key':";
$sc_arr_opts = array();
foreach($sc as $opt => $val) {
$sc_arr_opts[] = "$val";
}
$arr_opts[] = $str_sc_opts . join(",", $sc_arr_opts);
}
} else {
$arr_opts[] = "'$key':'$value'";
}
}
}
$opts .= join(",", $arr_opts);
$opts .="}";
return $opts;
}
}
?>
NB: when using the prototip options that require a hash, you may need some funky quotes...e.g.Download code
'hook'=>"{'key':'value','key':'value'}"
unless you have numeric values, e.g.
Download code
'offset'=>"{'x':5,'y':10}"
. Loosey goosey scripting languages indeed.
Comments
Comment
1 Hack for ajax div problem
Changes to renderTooltips()
function renderTooltips($hideCurrent=false) {
$tips_string = '<script type="text/javascript">';
if($hideCurrent)
$tips_string .= $this->_hideTips();
foreach($this->tips as $tip) {
$tips_string .= $tip . "\n";
}
$tips_string .= '</script>';
return $this->output($tips_string);
}
Add the following code at the bottom of the helper.
function _hideTips(){
$str = "for (i = 0; i < Tips.tips.length; ++i) {";
$str .= "Tips.tips.hide();";
$str .= "}";
return $str;
}
And to force a hide on all tips on the renderTooltips change
<?php echo $prototip->renderTooltips(); ?>
To
<?php echo $prototip->renderTooltips(true); ?>
Hope I didn't miss anything...
Comment
2 Suggested change
I like the $allowed_options array as a sanity check, but I don't really like how static-feeling the 'Special Case' options array is.
As a result, rather than parsing certain options as special cases, I decided to accept all options as nested arrays. This code might need a bit of work, but here is a changed _parseOptions function with the recursive _parseValues function that *should* allow infinitely deep option arrays for Prototip regardless of the option.
The quotes also looked slightly confusing so I added a check if the value is an integer, and added the quotes separately only if it is not.
PHP Snippet:
<?php function _parseOptions($options = array()) {
$opts = '{ ';
$arr_opts = array();
foreach($options as $key => $value) {
if (in_array($key, $this->allowed_options)) {
$value = _parseValues($value);
$arr_opts[] = "$key: $value";
}
}
$opts .= join(",", $arr_opts);
$opts .= ' }';
return $opts;
}
function _parseValues($values = null) {
if (!is_array($values)) {
if (!is_int($values)) $values = "'$values'";
return $values;
}
$value = '{ ';
$val_opts = array();
foreach($values as $key=>$val) {
if (is_array($val)) {
$val = $this->_parseValues($val);
}
if (!is_int($val)) $val = "'$val'";
$val_opts[] = "$key: $val";
}
$value .= join(',', $val_opts);
$value .= ' }';
return $value;
}
?>
Comment
3 Rewritten and expanded
You can add other options to the options array and not worry about them getting included in the actual tooltip. I used this to combine the tooltip and addTooltip functions by parsing a 'render' option in the options array. Now if $options['render'] == true, the tooptip will be rendered. Else it will be stored in the tips array.
I didn't explain how to specify the options array last time. Here it is:
THIS:
option1: value1,
option2: { opt1: { op: va }, opt2: val2 }
BECOMES THIS:
array(
'option1' => 'value1',
'option2' => array(
'opt1' => array(
'op' => 'va'
),
'opt2' => 'val2'
)
)
Here is my rewritten function with stripped comments to save space. This needs further testing.
PHP Snippet:
<?php class PrototipHelper extends Helper {
var $helpers = array('Html', 'Javascript');
var $allowed_options = array('ajax', 'border', 'borderColor', 'closeButton', 'delay', 'hideAfter', 'hideOn', 'hideOthers', 'hook', 'images', 'offset', 'radius', 'showOn', 'stem', 'style', 'target', 'title', 'viewport', 'width');
var $tips = array();
function tooltip($el, $content, $options = array()) {
if ($options['render'] == true) {
return $this->Javascript->codeBlock($this->output(_createTip($el, $content, $options);));
} else {
$this->tips[] = _createTip($el, $content, $options);
}
}
function renderTooltips() {
$tips_string = '';
foreach($this->tips as $tip) {
$tips_string .= $tip."\n";
}
return $this->JavaScript->codeBlock($tips_string);
}
function _createTip($el, $content, $options = array()) {
$valid_el = array('\'', '$');
if (!in_array(substr($el, 0, 1), $valid_el)) $el = "'$el'";
if (substr($content, 0, 1) != '\'' &&
substr($content, 0, 4) != 'new ') $content = "'$content'";
if ($options) {
return 'new Tip('.$el.', '.$content.', '.$this->_parseOptions($options).');';
} else {
return 'new Tip('.$el.', '.$content');';
}
}
function _parseOptions($options = array()) {
$opts = '{ ';
$arr_opts = array();
foreach($options as $key => $value) {
if (in_array($key, $this->allowed_options)) {
$value = _parseValues($value);
$arr_opts[] = "$key: $value";
}
}
$opts .= join(",", $arr_opts);
$opts .= ' }';
return $opts;
}
function _parseValues($values = null) {
if (!is_array($values)) {
if (!is_int($values)) $values = "'$values'";
return $values;
}
$value = '{ ';
$val_opts = array();
foreach($values as $key=>$val) {
if (is_array($val)) {
$val = $this->_parseValues($val);
}
if (!is_int($val)) $val = "'$val'";
$val_opts[] = "$key: $val";
}
$value .= join(',', $val_opts);
$value .= ' }';
return $value;
}?>
Comment
4 Sloppy code fix from my last comment
PHP Snippet:
<?php class PrototipHelper extends Helper {
var $helpers = array('Html', 'Javascript');
var $allowed_options = array('ajax', 'border', 'borderColor', 'closeButton', 'delay', 'hideAfter', 'hideOn', 'hideOthers', 'hook', 'images', 'offset', 'radius', 'showOn', 'stem', 'style', 'target', 'title', 'viewport', 'width');
var $tips = array();
function tooltip($el, $content, $options = array()) {
if ($options['render'] == true) {
return $this->Javascript->codeBlock($this->output($this->_createTip($el, $content, $options)));
} else {
$this->tips[] = _createTip($el, $content, $options);
}
}
function renderTooltips() {
$tips_string = '';
foreach($this->tips as $tip) {
$tips_string .= $tip."\n";
}
return $this->JavaScript->codeBlock($tips_string);
}
function _createTip($el, $content, $options = array()) {
$valid_el = array('\'', '$');
if (!in_array(substr($el, 0, 1), $valid_el)) $el = "'$el'";
if (substr($content, 0, 1) != '\'' &&
substr($content, 0, 4) != 'new ') $content = "'$content'";
if ($options) {
return 'new Tip('.$el.', '.$content.', '.$this->_parseOptions($options).');';
} else {
return 'new Tip('.$el.', '.$content.');';
}
}
function _parseOptions($options = array()) {
$opts = '{ ';
$arr_opts = array();
foreach($options as $key => $value) {
if (in_array($key, $this->allowed_options)) {
$value = $this->_parseValues($value);
$arr_opts[] = "$key: $value";
}
}
$opts .= join(",", $arr_opts);
$opts .= ' }';
return $opts;
}
function _parseValues($values = null) {
if (!is_array($values)) {
if (!is_int($values)) $values = "'$values'";
return $values;
}
$value = '{ ';
$val_opts = array();
foreach($values as $key=>$val) {
if (is_array($val)) {
$val = $this->_parseValues($val);
}
if (!is_int($val)) $val = "'$val'";
$val_opts[] = "$key: $val";
}
$value .= join(',', $val_opts);
$value .= ' }';
return $value;
}
}?>
Comment
5 Another enhancement
Calling it like this would automatically create tooltips for all links with 'rel' elements:
PHP Snippet:
<?php echo $prototip->autoRender('a[rel]', 'rel');?>And the code:
PHP Snippet:
<?php function autoRender($cssSelector, $tipField) {return $this->Javascript->codeBlock(
$this->output(
'document.observe(\'dom:loaded\', function() {
$$(\''.$cssSelector.'\').each(function(element) {
new Tip(element, element.'.$tipField.');
});
});'
)
);
}?>