ajax.php

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