ajax.php

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