ajax.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: ajax.php 8250 2009-07-23 13:35:14Z mark_story $ */
00003 /**
00004  * Helper for AJAX operations.
00005  *
00006  * Helps doing AJAX using the Prototype library.
00007  *
00008  * PHP versions 4 and 5
00009  *
00010  * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
00011  * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
00012  *
00013  * Licensed under The MIT License
00014  * Redistributions of files must retain the above copyright notice.
00015  *
00016  * @filesource
00017  * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
00018  * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00019  * @package       cake
00020  * @subpackage    cake.cake.libs.view.helpers
00021  * @since         CakePHP(tm) v 0.10.0.1076
00022  * @version       $Revision: 8250 $
00023  * @modifiedby    $LastChangedBy: mark_story $
00024  * @lastmodified  $Date: 2009-07-23 09:35:14 -0400 (Thu, 23 Jul 2009) $
00025  * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
00026  */
00027 /**
00028  * AjaxHelper helper library.
00029  *
00030  * Helps doing AJAX using the Prototype library.
00031  *
00032  * @package       cake
00033  * @subpackage    cake.cake.libs.view.helpers
00034  */
00035 class AjaxHelper extends AppHelper {
00036 /**
00037  * Included helpers.
00038  *
00039  * @var array
00040  */
00041     var $helpers = array('Html', 'Javascript', 'Form');
00042 /**
00043  * HtmlHelper instance
00044  *
00045  * @var HtmlHelper
00046  * @access public
00047  */
00048     var $Html = null;
00049 /**
00050  * JavaScriptHelper instance
00051  *
00052  * @var JavaScriptHelper
00053  * @access public
00054  */
00055     var $Javascript = null;
00056 /**
00057  * Names of Javascript callback functions.
00058  *
00059  * @var array
00060  */
00061     var $callbacks = array(
00062         'complete', 'create', 'exception', 'failure', 'interactive', 'loading',
00063         'loaded', 'success', 'uninitialized'
00064     );
00065 /**
00066  * Names of AJAX options.
00067  *
00068  * @var array
00069  */
00070     var $ajaxOptions = array(
00071         'after', 'asynchronous', 'before', 'confirm', 'condition', 'contentType', 'encoding',
00072         'evalScripts', 'failure', 'fallback', 'form', 'indicator', 'insertion', 'interactive',
00073         'loaded', 'loading', 'method', 'onCreate', 'onComplete', 'onException', 'onFailure',
00074         'onInteractive', 'onLoaded', 'onLoading', 'onSuccess', 'onUninitialized', 'parameters',
00075         'position', 'postBody', 'requestHeaders', 'success', 'type', 'update', 'with'
00076     );
00077 /**
00078  * Options for draggable.
00079  *
00080  * @var array
00081  */
00082     var $dragOptions = array(
00083         'handle', 'revert', 'snap', 'zindex', 'constraint', 'change', 'ghosting',
00084         'starteffect', 'reverteffect', 'endeffect', 'scroll', 'scrollSensitivity',
00085         'onStart', 'onDrag', 'onEnd'
00086     );
00087 /**
00088  * Options for droppable.
00089  *
00090  * @var array
00091  */
00092     var $dropOptions = array(
00093         'accept', 'containment', 'greedy', 'hoverclass', 'onHover', 'onDrop', 'overlap'
00094     );
00095 /**
00096  * Options for sortable.
00097  *
00098  * @var array
00099  */
00100     var $sortOptions = array(
00101         'constraint', 'containment', 'dropOnEmpty', 'ghosting', 'handle', 'hoverclass', 'onUpdate',
00102         'onChange', 'only', 'overlap', 'scroll', 'scrollSensitivity', 'scrollSpeed', 'tag', 'tree',
00103         'treeTag', 'update'
00104     );
00105 /**
00106  * Options for slider.
00107  *
00108  * @var array
00109  */
00110     var $sliderOptions = array(
00111         'alignX', 'alignY', 'axis', 'disabled', 'handleDisabled', 'handleImage', 'increment',
00112         'maximum', 'minimum', 'onChange', 'onSlide', 'range', 'sliderValue', 'values'
00113     );
00114 /**
00115  * Options for in-place editor.
00116  *
00117  * @var array
00118  */
00119     var $editorOptions = array(
00120         'okText', 'cancelText', 'savingText', 'formId', 'externalControl', 'rows', 'cols', 'size',
00121         'highlightcolor', 'highlightendcolor', 'savingClassName', 'formClassName', 'loadTextURL',
00122         'loadingText', 'callback', 'ajaxOptions', 'clickToEditText', 'collection', 'okControl',
00123         'cancelControl', 'submitOnBlur'
00124     );
00125 /**
00126  * Options for auto-complete editor.
00127  *
00128  * @var array
00129  */
00130     var $autoCompleteOptions = array(
00131         'afterUpdateElement', 'callback', 'frequency', 'indicator', 'minChars', 'onShow', 'onHide',
00132         'parameters', 'paramName', 'tokens', 'updateElement'
00133     );
00134 /**
00135  * Output buffer for Ajax update content
00136  *
00137  * @var array
00138  */
00139     var $__ajaxBuffer = array();
00140 /**
00141  * Returns link to remote action
00142  *
00143  * Returns a link to a remote action defined by <i>options[url]</i>
00144  * (using the url() format) that's called in the background using
00145  * XMLHttpRequest. The result of that request can then be inserted into a
00146  * DOM object whose id can be specified with <i>options[update]</i>.
00147  *
00148  * Examples:
00149  * <code>
00150  *  link("Delete this post",
00151  * array("update" => "posts", "url" => "delete/{$postid->id}"));
00152  *  link(imageTag("refresh"),
00153  *      array("update" => "emails", "url" => "list_emails" ));
00154  * </code>
00155  *
00156  * By default, these remote requests are processed asynchronous during
00157  * which various callbacks can be triggered (for progress indicators and
00158  * the likes).
00159  *
00160  * Example:
00161  * <code>
00162  *  link (word,
00163  *      array("url" => "undo", "n" => word_counter),
00164  *      array("complete" => "undoRequestCompleted(request)"));
00165  * </code>
00166  *
00167  * The callbacks that may be specified are:
00168  *
00169  * - <i>loading</i>::       Called when the remote document is being
00170  *                          loaded with data by the browser.
00171  * - <i>loaded</i>::        Called when the browser has finished loading
00172  *                          the remote document.
00173  * - <i>interactive</i>::   Called when the user can interact with the
00174  *                          remote document, even though it has not
00175  *                          finished loading.
00176  * - <i>complete</i>:: Called when the XMLHttpRequest is complete.
00177  *
00178  * If you for some reason or another need synchronous processing (that'll
00179  * block the browser while the request is happening), you can specify
00180  * <i>options[type] = synchronous</i>.
00181  *
00182  * You can customize further browser side call logic by passing
00183  * in Javascript code snippets via some optional parameters. In
00184  * their order of use these are:
00185  *
00186  * - <i>confirm</i>:: Adds confirmation dialog.
00187  * -<i>condition</i>::  Perform remote request conditionally
00188  *                      by this expression. Use this to
00189  *                      describe browser-side conditions when
00190  *                      request should not be initiated.
00191  * - <i>before</i>::        Called before request is initiated.
00192  * - <i>after</i>::     Called immediately after request was
00193  *                      initiated and before <i>loading</i>.
00194  *
00195  * @param string $title Title of link
00196  * @param string $href Href string "/products/view/12"
00197  * @param array $options        Options for JavaScript function
00198  * @param string $confirm       Confirmation message. Calls up a JavaScript confirm() message.
00199  * @param boolean $escapeTitle  Escaping the title string to HTML entities
00200  *
00201  * @return string               HTML code for link to remote action
00202  */
00203     function link($title, $href = null, $options = array(), $confirm = null, $escapeTitle = true) {
00204         if (!isset($href)) {
00205             $href = $title;
00206         }
00207         if (!isset($options['url'])) {
00208             $options['url'] = $href;
00209         }
00210 
00211         if (isset($confirm)) {
00212             $options['confirm'] = $confirm;
00213             unset($confirm);
00214         }
00215         $htmlOptions = $this->__getHtmlOptions($options, array('url'));
00216 
00217         if (empty($options['fallback']) || !isset($options['fallback'])) {
00218             $options['fallback'] = $href;
00219         }
00220         $htmlDefaults = array('id' => 'link' . intval(mt_rand()), 'onclick' => '');
00221         $htmlOptions = array_merge($htmlDefaults, $htmlOptions);
00222 
00223         $htmlOptions['onclick'] .= ' event.returnValue = false; return false;';
00224         $return = $this->Html->link($title, $href, $htmlOptions, null, $escapeTitle);
00225         $callback = $this->remoteFunction($options);
00226         $script = $this->Javascript->event("'{$htmlOptions['id']}'", "click", $callback);
00227 
00228         if (is_string($script)) {
00229             $return .= $script;
00230         }
00231         return $return;
00232     }
00233 /**
00234  * Creates JavaScript function for remote AJAX call
00235  *
00236  * This function creates the javascript needed to make a remote call
00237  * it is primarily used as a helper for AjaxHelper::link.
00238  *
00239  * @param array $options options for javascript
00240  * @return string html code for link to remote action
00241  * @see AjaxHelper::link() for docs on options parameter.
00242  */
00243     function remoteFunction($options) {
00244         if (isset($options['update'])) {
00245             if (!is_array($options['update'])) {
00246                 $func = "new Ajax.Updater('{$options['update']}',";
00247             } else {
00248                 $func = "new Ajax.Updater(document.createElement('div'),";
00249             }
00250             if (!isset($options['requestHeaders'])) {
00251                 $options['requestHeaders'] = array();
00252             }
00253             if (is_array($options['update'])) {
00254                 $options['update'] = join(' ', $options['update']);
00255             }
00256             $options['requestHeaders']['X-Update'] = $options['update'];
00257         } else {
00258             $func = "new Ajax.Request(";
00259         }
00260 
00261         $func .= "'" . $this->url(isset($options['url']) ? $options['url'] : "") . "'";
00262         $func .= ", " . $this->__optionsForAjax($options) . ")";
00263 
00264         if (isset($options['before'])) {
00265             $func = "{$options['before']}; $func";
00266         }
00267         if (isset($options['after'])) {
00268             $func = "$func; {$options['after']};";
00269         }
00270         if (isset($options['condition'])) {
00271             $func = "if ({$options['condition']}) { $func; }";
00272         }
00273 
00274         if (isset($options['confirm'])) {
00275             $func = "if (confirm('" . $this->Javascript->escapeString($options['confirm'])
00276                 . "')) { $func; } else { event.returnValue = false; return false; }";
00277         }
00278         return $func;
00279     }
00280 /**
00281  * Periodically call remote url via AJAX.
00282  *
00283  * Periodically calls the specified url (<i>options[url]</i>) every <i>options[frequency]</i>
00284  * seconds (default is 10).  Usually used to update a specified div (<i>options[update]</i>) with
00285  * the results of the remote call.  The options for specifying the target with url and defining
00286  * callbacks is the same as AjaxHelper::link().
00287  *
00288  * @param array $options Callback options
00289  * @return string Javascript code
00290  * @see AjaxHelper::link()
00291  */
00292     function remoteTimer($options = null) {
00293         $frequency = (isset($options['frequency'])) ? $options['frequency'] : 10;
00294         $callback = $this->remoteFunction($options);
00295         $code = "new PeriodicalExecuter(function() {{$callback}}, $frequency)";
00296         return $this->Javascript->codeBlock($code);
00297     }
00298 /**
00299  * Returns form tag that will submit using Ajax.
00300  *
00301  * Returns a form tag that will submit using XMLHttpRequest in the background instead of the regular
00302  * reloading POST arrangement. Even though it's using Javascript to serialize the form elements,
00303  * the form submission will work just like a regular submission as viewed by the receiving side
00304  * (all elements available in params).  The options for defining callbacks is the same
00305  * as AjaxHelper::link().
00306  *
00307  * @param mixed $params Either a string identifying the form target, or an array of method parameters, including:
00308  *  - 'params' => Acts as the form target
00309  *  - 'type' => 'post' or 'get'
00310  *  - 'options' => An array containing all HTML and script options used to
00311  *  generate the form tag and Ajax request.
00312  * @param array $type How form data is posted: 'get' or 'post'
00313  * @param array $options Callback/HTML options
00314  * @return string JavaScript/HTML code
00315  * @see AjaxHelper::link()
00316  */
00317     function form($params = null, $type = 'post', $options = array()) {
00318         $model = false;
00319         if (is_array($params)) {
00320             extract($params, EXTR_OVERWRITE);
00321         }
00322 
00323         if (empty($options['url'])) {
00324             $options['url'] = array('action' => $params);
00325         }
00326 
00327         $htmlDefaults = array(
00328             'id' => 'form' . intval(mt_rand()),
00329             'onsubmit'  => "event.returnValue = false; return false;",
00330             'type' => $type
00331         );
00332         $htmlOptions = $this->__getHtmlOptions($options, array('model', 'with'));
00333         $htmlOptions = array_merge($htmlDefaults, $htmlOptions);
00334 
00335         $defaults = array('model' => $model, 'with' => "Form.serialize('{$htmlOptions['id']}')");
00336         $options = array_merge($defaults, $options);
00337         $callback = $this->remoteFunction($options);
00338 
00339         $form = $this->Form->create($options['model'], $htmlOptions);
00340         $script = $this->Javascript->event("'" . $htmlOptions['id']. "'", 'submit', $callback);
00341         return $form . $script;
00342     }
00343 /**
00344  * Returns a button input tag that will submit using Ajax
00345  *
00346  * Returns a button input tag that will submit form using XMLHttpRequest in the background instead
00347  * of regular reloading POST arrangement. <i>options</i> argument is the same as
00348  * in AjaxHelper::form().
00349  *
00350  * @param string $title Input button title
00351  * @param array $options Callback options
00352  * @return string Ajaxed input button
00353  * @see AjaxHelper::form()
00354  */
00355     function submit($title = 'Submit', $options = array()) {
00356         $htmlOptions = $this->__getHtmlOptions($options);
00357         $htmlOptions['value'] = $title;
00358 
00359         if (!isset($options['with'])) {
00360             $options['with'] = 'Form.serialize(Event.element(event).form)';
00361         }
00362         if (!isset($htmlOptions['id'])) {
00363             $htmlOptions['id'] = 'submit' . intval(mt_rand());
00364         }
00365 
00366         $htmlOptions['onclick'] = "event.returnValue = false; return false;";
00367         $callback = $this->remoteFunction($options);
00368 
00369         $form = $this->Form->submit($title, $htmlOptions);
00370         $script = $this->Javascript->event('"' . $htmlOptions['id'] . '"', 'click', $callback);
00371         return $form . $script;
00372     }
00373 /**
00374  * Observe field and call ajax on change.
00375  *
00376  * Observes the field with the DOM ID specified by <i>field</i> and makes
00377  * an Ajax when its contents have changed.
00378  *
00379  * Required +options+ are:
00380  * - <i>frequency</i>:: The frequency (in seconds) at which changes to
00381  *                      this field will be detected.
00382  * - <i>url</i>::       @see url() -style options for the action to call
00383  *                      when the field has changed.
00384  *
00385  * Additional options are:
00386  * - <i>update</i>::    Specifies the DOM ID of the element whose
00387  *                      innerHTML should be updated with the
00388  *                      XMLHttpRequest response text.
00389  * - <i>with</i>:: A Javascript expression specifying the
00390  *                      parameters for the XMLHttpRequest. This defaults
00391  *                      to Form.Element.serialize('$field'), which can be
00392  *                      accessed from params['form']['field_id'].
00393  *
00394  * Additionally, you may specify any of the options documented in
00395  * @see linkToRemote().
00396  *
00397  * @param string $field DOM ID of field to observe
00398  * @param array $options ajax options
00399  * @return string ajax script
00400  */
00401     function observeField($field, $options = array()) {
00402         if (!isset($options['with'])) {
00403             $options['with'] = 'Form.Element.serialize(\'' . $field . '\')';
00404         }
00405         $observer = 'Observer';
00406         if (!isset($options['frequency']) || intval($options['frequency']) == 0) {
00407             $observer = 'EventObserver';
00408         }
00409         return $this->Javascript->codeBlock(
00410             $this->_buildObserver('Form.Element.' . $observer, $field, $options)
00411         );
00412     }
00413 /**
00414  * Observe entire form and call ajax on change.
00415  *
00416  * Like @see observeField(), but operates on an entire form identified by the
00417  * DOM ID <b>form</b>. <b>options</b> are the same as <b>observeField</b>, except
00418  * the default value of the <i>with</i> option evaluates to the
00419  * serialized (request string) value of the form.
00420  *
00421  * @param string $form DOM ID of form to observe
00422  * @param array $options ajax options
00423  * @return string ajax script
00424  */
00425     function observeForm($form, $options = array()) {
00426         if (!isset($options['with'])) {
00427             $options['with'] = 'Form.serialize(\'' . $form . '\')';
00428         }
00429         $observer = 'Observer';
00430         if (!isset($options['frequency']) || intval($options['frequency']) == 0) {
00431             $observer = 'EventObserver';
00432         }
00433         return $this->Javascript->codeBlock(
00434             $this->_buildObserver('Form.' . $observer, $form, $options)
00435         );
00436     }
00437 /**
00438  * Create a text field with Autocomplete.
00439  *
00440  * Creates an autocomplete field with the given ID and options.
00441  *
00442  * options['with'] defaults to "Form.Element.serialize('$field')",
00443  * but can be any valid javascript expression defining the additional fields.
00444  *
00445  * @param string $field DOM ID of field to observe
00446  * @param string $url URL for the autocomplete action
00447  * @param array $options Ajax options
00448  * @return string Ajax script
00449  */
00450     function autoComplete($field, $url = "", $options = array()) {
00451         $var = '';
00452         if (isset($options['var'])) {
00453             $var = 'var ' . $options['var'] . ' = ';
00454             unset($options['var']);
00455         }
00456 
00457         if (!isset($options['id'])) {
00458             $options['id'] = Inflector::camelize(str_replace(".", "_", $field));
00459         }
00460 
00461         $divOptions = array(
00462             'id' => $options['id'] . "_autoComplete",
00463             'class' => isset($options['class']) ? $options['class'] : 'auto_complete'
00464         );
00465 
00466         if (isset($options['div_id'])) {
00467             $divOptions['id'] = $options['div_id'];
00468             unset($options['div_id']);
00469         }
00470 
00471         $htmlOptions = $this->__getHtmlOptions($options);
00472         $htmlOptions['autocomplete'] = "off";
00473 
00474         foreach ($this->autoCompleteOptions as $opt) {
00475             unset($htmlOptions[$opt]);
00476         }
00477 
00478         if (isset($options['tokens'])) {
00479             if (is_array($options['tokens'])) {
00480                 $options['tokens'] = $this->Javascript->object($options['tokens']);
00481             } else {
00482                 $options['tokens'] = '"' . $options['tokens'] . '"';
00483             }
00484         }
00485 
00486         $options = $this->_optionsToString($options, array('paramName', 'indicator'));
00487         $options = $this->_buildOptions($options, $this->autoCompleteOptions);
00488 
00489 
00490         $text = $this->Form->text($field, $htmlOptions);
00491         $div = $this->Html->div(null, '', $divOptions);
00492         $script = "{$var}new Ajax.Autocompleter('{$htmlOptions['id']}', '{$divOptions['id']}', '";
00493         $script .= $this->Html->url($url) . "', {$options});";
00494 
00495         return  "{$text}\n{$div}\n" . $this->Javascript->codeBlock($script);
00496     }
00497 /**
00498  * Creates an Ajax-updateable DIV element
00499  *
00500  * @param string $id options for javascript
00501  * @return string HTML code
00502  */
00503     function div($id, $options = array()) {
00504         if (env('HTTP_X_UPDATE') != null) {
00505             $this->Javascript->enabled = false;
00506             $divs = explode(' ', env('HTTP_X_UPDATE'));
00507 
00508             if (in_array($id, $divs)) {
00509                 @ob_end_clean();
00510                 ob_start();
00511                 return '';
00512             }
00513         }
00514         $attr = $this->_parseAttributes(array_merge($options, array('id' => $id)));
00515         return $this->output(sprintf($this->Html->tags['blockstart'], $attr));
00516     }
00517 /**
00518  * Closes an Ajax-updateable DIV element
00519  *
00520  * @param string $id The DOM ID of the element
00521  * @return string HTML code
00522  */
00523     function divEnd($id) {
00524         if (env('HTTP_X_UPDATE') != null) {
00525             $divs = explode(' ', env('HTTP_X_UPDATE'));
00526             if (in_array($id, $divs)) {
00527                 $this->__ajaxBuffer[$id] = ob_get_contents();
00528                 ob_end_clean();
00529                 ob_start();
00530                 return '';
00531             }
00532         }
00533         return $this->output($this->Html->tags['blockend']);
00534     }
00535 /**
00536  * Detects Ajax requests
00537  *
00538  * @return boolean True if the current request is a Prototype Ajax update call
00539  */
00540     function isAjax() {
00541         return (isset($this->params['isAjax']) && $this->params['isAjax'] === true);
00542     }
00543 /**
00544  * Creates a draggable element.  For a reference on the options for this function,
00545  * check out http://github.com/madrobby/scriptaculous/wikis/draggable
00546  *
00547  * @param unknown_type $id
00548  * @param array $options
00549  * @return unknown
00550  */
00551     function drag($id, $options = array()) {
00552         $var = '';
00553         if (isset($options['var'])) {
00554             $var = 'var ' . $options['var'] . ' = ';
00555             unset($options['var']);
00556         }
00557         $options = $this->_buildOptions(
00558             $this->_optionsToString($options, array('handle', 'constraint')), $this->dragOptions
00559         );
00560         return $this->Javascript->codeBlock("{$var}new Draggable('$id', " .$options . ");");
00561     }
00562 /**
00563  * For a reference on the options for this function, check out
00564  * http://github.com/madrobby/scriptaculous/wikis/droppables
00565  *
00566  * @param unknown_type $id
00567  * @param array $options
00568  * @return string
00569  */
00570     function drop($id, $options = array()) {
00571         $optionsString = array('overlap', 'hoverclass');
00572         if (!isset($options['accept']) || !is_array($options['accept'])) {
00573             $optionsString[] = 'accept';
00574         } else if (isset($options['accept'])) {
00575             $options['accept'] = $this->Javascript->object($options['accept']);
00576         }
00577         $options = $this->_buildOptions(
00578             $this->_optionsToString($options, $optionsString), $this->dropOptions
00579         );
00580         return $this->Javascript->codeBlock("Droppables.add('{$id}', {$options});");
00581     }
00582 /**
00583  * Make an element with the given $id droppable, and trigger an Ajax call when a draggable is
00584  * dropped on it.
00585  *
00586  * For a reference on the options for this function, check out
00587  * http://wiki.script.aculo.us/scriptaculous/show/Droppables.add
00588  *
00589  * @param string $id
00590  * @param array $options
00591  * @param array $ajaxOptions
00592  * @return string JavaScript block to create a droppable element
00593  */
00594     function dropRemote($id, $options = array(), $ajaxOptions = array()) {
00595         $callback = $this->remoteFunction($ajaxOptions);
00596         $options['onDrop'] = "function(element, droppable, event) {{$callback}}";
00597         $optionsString = array('overlap', 'hoverclass');
00598 
00599         if (!isset($options['accept']) || !is_array($options['accept'])) {
00600             $optionsString[] = 'accept';
00601         } else if (isset($options['accept'])) {
00602             $options['accept'] = $this->Javascript->object($options['accept']);
00603         }
00604 
00605         $options = $this->_buildOptions(
00606             $this->_optionsToString($options, $optionsString),
00607             $this->dropOptions
00608         );
00609         return $this->Javascript->codeBlock("Droppables.add('{$id}', {$options});");
00610     }
00611 /**
00612  * Makes a slider control.
00613  *
00614  * @param string $id DOM ID of slider handle
00615  * @param string $trackId DOM ID of slider track
00616  * @param array $options Array of options to control the slider
00617  * @link          http://github.com/madrobby/scriptaculous/wikis/slider
00618  */
00619     function slider($id, $trackId, $options = array()) {
00620         if (isset($options['var'])) {
00621             $var = 'var ' . $options['var'] . ' = ';
00622             unset($options['var']);
00623         } else {
00624             $var = 'var ' . $id . ' = ';
00625         }
00626 
00627         $options = $this->_optionsToString($options, array(
00628             'axis', 'handleImage', 'handleDisabled'
00629         ));
00630         $callbacks = array('change', 'slide');
00631 
00632         foreach ($callbacks as $callback) {
00633             if (isset($options[$callback])) {
00634                 $call = $options[$callback];
00635                 $options['on' . ucfirst($callback)] = "function(value) {{$call}}";
00636                 unset($options[$callback]);
00637             }
00638         }
00639 
00640         if (isset($options['values']) && is_array($options['values'])) {
00641             $options['values'] = $this->Javascript->object($options['values']);
00642         }
00643 
00644         $options = $this->_buildOptions($options, $this->sliderOptions);
00645         $script = "{$var}new Control.Slider('$id', '$trackId', $options);";
00646         return $this->Javascript->codeBlock($script);
00647     }
00648 /**
00649  * Makes an Ajax In Place editor control.
00650  *
00651  * @param string $id DOM ID of input element
00652  * @param string $url Postback URL of saved data
00653  * @param array $options Array of options to control the editor, including ajaxOptions (see link).
00654  * @link          http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor
00655  */
00656     function editor($id, $url, $options = array()) {
00657         $url = $this->url($url);
00658         $options['ajaxOptions'] = $this->__optionsForAjax($options);
00659 
00660         foreach ($this->ajaxOptions as $opt) {
00661             if (isset($options[$opt])) {
00662                 unset($options[$opt]);
00663             }
00664         }
00665 
00666         if (isset($options['callback'])) {
00667             $options['callback'] = 'function(form, value) {' . $options['callback'] . '}';
00668         }
00669 
00670         $type = 'InPlaceEditor';
00671         if (isset($options['collection']) && is_array($options['collection'])) {
00672             $options['collection'] = $this->Javascript->object($options['collection']);
00673             $type = 'InPlaceCollectionEditor';
00674         }
00675 
00676         $var = '';
00677         if (isset($options['var'])) {
00678             $var = 'var ' . $options['var'] . ' = ';
00679             unset($options['var']);
00680         }
00681 
00682         $options = $this->_optionsToString($options, array(
00683             'okText', 'cancelText', 'savingText', 'formId', 'externalControl', 'highlightcolor',
00684             'highlightendcolor', 'savingClassName', 'formClassName', 'loadTextURL', 'loadingText',
00685             'clickToEditText', 'okControl', 'cancelControl'
00686         ));
00687         $options = $this->_buildOptions($options, $this->editorOptions);
00688         $script = "{$var}new Ajax.{$type}('{$id}', '{$url}', {$options});";
00689         return $this->Javascript->codeBlock($script);
00690     }
00691 /**
00692  * Makes a list or group of floated objects sortable.
00693  *
00694  * @param string $id DOM ID of parent
00695  * @param array $options Array of options to control sort.
00696  * @link          http://github.com/madrobby/scriptaculous/wikis/sortable
00697  */
00698     function sortable($id, $options = array()) {
00699         if (!empty($options['url'])) {
00700             if (empty($options['with'])) {
00701                 $options['with'] = "Sortable.serialize('$id')";
00702             }
00703             $options['onUpdate'] = 'function(sortable) {' . $this->remoteFunction($options) . '}';
00704         }
00705         $block = true;
00706 
00707         if (isset($options['block'])) {
00708             $block = $options['block'];
00709             unset($options['block']);
00710         }
00711         $strings = array(
00712             'tag', 'constraint', 'only', 'handle', 'hoverclass', 'tree',
00713             'treeTag', 'update', 'overlap'
00714         );
00715         $scrollIsObject = (
00716             isset($options['scroll']) &&
00717             $options['scroll'] != 'window' &&
00718             strpos($options['scroll'], '$(') !== 0
00719         );
00720 
00721         if ($scrollIsObject) {
00722             $strings[] = 'scroll';
00723         }
00724 
00725         $options = $this->_optionsToString($options, $strings);
00726         $options = array_merge($options, $this->_buildCallbacks($options));
00727         $options = $this->_buildOptions($options, $this->sortOptions);
00728         $result = "Sortable.create('$id', $options);";
00729 
00730         if (!$block) {
00731             return $result;
00732         }
00733         return $this->Javascript->codeBlock($result);
00734     }
00735 /**
00736  * Private helper function for Javascript.
00737  *
00738  * @param array $options Set of options
00739  * @access private
00740  */
00741     function __optionsForAjax($options) {
00742         if (isset($options['indicator'])) {
00743             if (isset($options['loading'])) {
00744                 $loading = $options['loading'];
00745 
00746                 if (!empty($loading) && substr(trim($loading), -1, 1) != ';') {
00747                     $options['loading'] .= '; ';
00748                 }
00749                 $options['loading'] .= "Element.show('{$options['indicator']}');";
00750             } else {
00751                 $options['loading'] = "Element.show('{$options['indicator']}');";
00752             }
00753             if (isset($options['complete'])) {
00754                 $complete = $options['complete'];
00755 
00756                 if (!empty($complete) && substr(trim($complete), -1, 1) != ';') {
00757                     $options['complete'] .= '; ';
00758                 }
00759                 $options['complete'] .= "Element.hide('{$options['indicator']}');";
00760             } else {
00761                 $options['complete'] = "Element.hide('{$options['indicator']}');";
00762             }
00763             unset($options['indicator']);
00764         }
00765 
00766         $jsOptions = array_merge(
00767             array('asynchronous' => 'true', 'evalScripts'  => 'true'),
00768             $this->_buildCallbacks($options)
00769         );
00770 
00771         $options = $this->_optionsToString($options, array(
00772             'contentType', 'encoding', 'fallback', 'method', 'postBody', 'update', 'url'
00773         ));
00774         $jsOptions = array_merge($jsOptions, array_intersect_key($options, array_flip(array(
00775             'contentType', 'encoding', 'method', 'postBody'
00776         ))));
00777 
00778         foreach ($options as $key => $value) {
00779             switch ($key) {
00780                 case 'type':
00781                     $jsOptions['asynchronous'] = ($value == 'synchronous') ? 'false' : 'true';
00782                 break;
00783                 case 'evalScripts':
00784                     $jsOptions['evalScripts'] = ($value) ? 'true' : 'false';
00785                 break;
00786                 case 'position':
00787                     $pos = Inflector::camelize($options['position']);
00788                     $jsOptions['insertion'] = "Insertion.{$pos}";
00789                 break;
00790                 case 'with':
00791                     $jsOptions['parameters'] = $options['with'];
00792                 break;
00793                 case 'form':
00794                     $jsOptions['parameters'] = 'Form.serialize(this)';
00795                 break;
00796                 case 'requestHeaders':
00797                     $keys = array();
00798                     foreach ($value as $key => $val) {
00799                         $keys[] = "'" . $key . "'";
00800                         $keys[] = "'" . $val . "'";
00801                     }
00802                     $jsOptions['requestHeaders'] = '[' . join(', ', $keys) . ']';
00803                 break;
00804             }
00805         }
00806         return $this->_buildOptions($jsOptions, $this->ajaxOptions);
00807     }
00808 /**
00809  * Private Method to return a string of html options
00810  * option data as a JavaScript options hash.
00811  *
00812  * @param array $options    Options in the shape of keys and values
00813  * @param array $extra  Array of legal keys in this options context
00814  * @return array Array of html options
00815  * @access private
00816  */
00817     function __getHtmlOptions($options, $extra = array()) {
00818         foreach (array_merge($this->ajaxOptions, $this->callbacks, $extra) as $key) {
00819             if (isset($options[$key])) {
00820                 unset($options[$key]);
00821             }
00822         }
00823         return $options;
00824     }
00825 /**
00826  * Returns a string of JavaScript with the given option data as a JavaScript options hash.
00827  *
00828  * @param array $options    Options in the shape of keys and values
00829  * @param array $acceptable Array of legal keys in this options context
00830  * @return string   String of Javascript array definition
00831  */
00832     function _buildOptions($options, $acceptable) {
00833         if (is_array($options)) {
00834             $out = array();
00835 
00836             foreach ($options as $k => $v) {
00837                 if (in_array($k, $acceptable)) {
00838                     if ($v === true) {
00839                         $v = 'true';
00840                     } elseif ($v === false) {
00841                         $v = 'false';
00842                     }
00843                     $out[] = "$k:$v";
00844                 } elseif ($k === 'with' && in_array('parameters', $acceptable)) {
00845                     $out[] = "parameters:${v}";
00846                 }
00847             }
00848 
00849             $out = join(', ', $out);
00850             $out = '{' . $out . '}';
00851             return $out;
00852         } else {
00853             return false;
00854         }
00855     }
00856 /**
00857  * Return JavaScript text for an observer...
00858  *
00859  * @param string $klass Name of JavaScript class
00860  * @param string $name
00861  * @param array $options    Ajax options
00862  * @return string Formatted JavaScript
00863  */
00864     function _buildObserver($klass, $name, $options = null) {
00865         if (!isset($options['with']) && isset($options['update'])) {
00866             $options['with'] = 'value';
00867         }
00868 
00869         $callback = $this->remoteFunction($options);
00870         $hasFrequency = !(!isset($options['frequency']) || intval($options['frequency']) == 0);
00871         $frequency = $hasFrequency ? $options['frequency'] . ', ' : '';
00872 
00873         return "new $klass('$name', {$frequency}function(element, value) {{$callback}})";
00874     }
00875 /**
00876  * Return Javascript text for callbacks.
00877  *
00878  * @param array $options Option array where a callback is specified
00879  * @return array Options with their callbacks properly set
00880  * @access protected
00881  */
00882     function _buildCallbacks($options) {
00883         $callbacks = array();
00884 
00885         foreach ($this->callbacks as $callback) {
00886             if (isset($options[$callback])) {
00887                 $name = 'on' . ucfirst($callback);
00888                 $code = $options[$callback];
00889                 switch ($name) {
00890                     case 'onComplete':
00891                         $callbacks[$name] = "function(request, json) {" . $code . "}";
00892                         break;
00893                     case 'onCreate':
00894                         $callbacks[$name] = "function(request, xhr) {" . $code . "}";
00895                         break;
00896                     case 'onException':
00897                         $callbacks[$name] = "function(request, exception) {" . $code . "}";
00898                         break;
00899                     default:
00900                         $callbacks[$name] = "function(request) {" . $code . "}";
00901                         break;
00902                 }
00903                 if (isset($options['bind'])) {
00904                     $bind = $options['bind'];
00905 
00906                     $hasBinding = (
00907                         (is_array($bind) && in_array($callback, $bind)) ||
00908                         (is_string($bind) && strpos($bind, $callback) !== false)
00909                     );
00910 
00911                     if ($hasBinding) {
00912                         $callbacks[$name] .= ".bind(this)";
00913                     }
00914                 }
00915             }
00916         }
00917         return $callbacks;
00918     }
00919 /**
00920  * Returns a string of JavaScript with a string representation of given options array.
00921  *
00922  * @param array $options    Ajax options array
00923  * @param array $stringOpts Options as strings in an array
00924  * @access private
00925  * @return array
00926  */
00927     function _optionsToString($options, $stringOpts = array()) {
00928         foreach ($stringOpts as $option) {
00929             $hasOption = (
00930                 isset($options[$option]) && !empty($options[$option]) &&
00931                 is_string($options[$option]) && $options[$option][0] != "'"
00932             );
00933 
00934             if ($hasOption) {
00935                 if ($options[$option] === true || $options[$option] === 'true') {
00936                     $options[$option] = 'true';
00937                 } elseif ($options[$option] === false || $options[$option] === 'false') {
00938                     $options[$option] = 'false';
00939                 } else {
00940                     $options[$option] = "'{$options[$option]}'";
00941                 }
00942             }
00943         }
00944         return $options;
00945     }
00946 /**
00947  * Executed after a view has rendered, used to include bufferred code
00948  * blocks.
00949  *
00950  * @access public
00951  */
00952     function afterRender() {
00953         if (env('HTTP_X_UPDATE') != null && !empty($this->__ajaxBuffer)) {
00954             @ob_end_clean();
00955 
00956             $data = array();
00957             $divs = explode(' ', env('HTTP_X_UPDATE'));
00958             $keys = array_keys($this->__ajaxBuffer);
00959 
00960             if (count($divs) == 1 && in_array($divs[0], $keys)) {
00961                 echo $this->__ajaxBuffer[$divs[0]];
00962             } else {
00963                 foreach ($this->__ajaxBuffer as $key => $val) {
00964                     if (in_array($key, $divs)) {
00965                         $data[] = $key . ':"' . rawurlencode($val) . '"';
00966                     }
00967                 }
00968                 $out  = 'var __ajaxUpdater__ = {' . join(", \n", $data) . '};' . "\n";
00969                 $out .= 'for (n in __ajaxUpdater__) { if (typeof __ajaxUpdater__[n] == "string"';
00970                 $out .= ' && $(n)) Element.update($(n), unescape(decodeURIComponent(';
00971                 $out .= '__ajaxUpdater__[n]))); }';
00972                 echo $this->Javascript->codeBlock($out, false);
00973             }
00974             $scripts = $this->Javascript->getCache();
00975 
00976             if (!empty($scripts)) {
00977                 echo $this->Javascript->codeBlock($scripts, false);
00978             }
00979             $this->_stop();
00980         }
00981     }
00982 }
00983 
00984 ?>

Generated on Sun Nov 22 00:30:54 2009 for CakePHP 1.2.x.x (v1.2.4.8284) by doxygen 1.4.7