PageRenderTime 60ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/cake/libs/view/helpers/ajax.php

https://github.com/mariuz/firetube
PHP | 991 lines | 562 code | 53 blank | 376 comment | 104 complexity | e0813618ff5d0391c444963c09654b07 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /* SVN FILE: $Id$ */
  3. /**
  4. * Helper for AJAX operations.
  5. *
  6. * Helps doing AJAX using the Prototype library.
  7. *
  8. * PHP versions 4 and 5
  9. *
  10. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  11. * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  12. *
  13. * Licensed under The MIT License
  14. * Redistributions of files must retain the above copyright notice.
  15. *
  16. * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  17. * @link http://cakephp.org CakePHP(tm) Project
  18. * @package cake
  19. * @subpackage cake.cake.libs.view.helpers
  20. * @since CakePHP(tm) v 0.10.0.1076
  21. * @version $Revision$
  22. * @modifiedby $LastChangedBy$
  23. * @lastmodified $Date$
  24. * @license http://www.opensource.org/licenses/mit-license.php The MIT License
  25. */
  26. /**
  27. * AjaxHelper helper library.
  28. *
  29. * Helps doing AJAX using the Prototype library.
  30. *
  31. * @package cake
  32. * @subpackage cake.cake.libs.view.helpers
  33. */
  34. class AjaxHelper extends AppHelper {
  35. /**
  36. * Included helpers.
  37. *
  38. * @var array
  39. */
  40. var $helpers = array('Html', 'Javascript', 'Form');
  41. /**
  42. * HtmlHelper instance
  43. *
  44. * @var HtmlHelper
  45. * @access public
  46. */
  47. var $Html = null;
  48. /**
  49. * JavaScriptHelper instance
  50. *
  51. * @var JavaScriptHelper
  52. * @access public
  53. */
  54. var $Javascript = null;
  55. /**
  56. * Names of Javascript callback functions.
  57. *
  58. * @var array
  59. */
  60. var $callbacks = array(
  61. 'complete', 'create', 'exception', 'failure', 'interactive', 'loading',
  62. 'loaded', 'success', 'uninitialized'
  63. );
  64. /**
  65. * Names of AJAX options.
  66. *
  67. * @var array
  68. */
  69. var $ajaxOptions = array(
  70. 'after', 'asynchronous', 'before', 'confirm', 'condition', 'contentType', 'encoding',
  71. 'evalScripts', 'failure', 'fallback', 'form', 'indicator', 'insertion', 'interactive',
  72. 'loaded', 'loading', 'method', 'onCreate', 'onComplete', 'onException', 'onFailure',
  73. 'onInteractive', 'onLoaded', 'onLoading', 'onSuccess', 'onUninitialized', 'parameters',
  74. 'position', 'postBody', 'requestHeaders', 'success', 'type', 'update', 'with'
  75. );
  76. /**
  77. * Options for draggable.
  78. *
  79. * @var array
  80. */
  81. var $dragOptions = array(
  82. 'handle', 'revert', 'snap', 'zindex', 'constraint', 'change', 'ghosting',
  83. 'starteffect', 'reverteffect', 'endeffect', 'scroll', 'scrollSensitivity',
  84. 'onStart', 'onDrag', 'onEnd'
  85. );
  86. /**
  87. * Options for droppable.
  88. *
  89. * @var array
  90. */
  91. var $dropOptions = array(
  92. 'accept', 'containment', 'greedy', 'hoverclass', 'onHover', 'onDrop', 'overlap'
  93. );
  94. /**
  95. * Options for sortable.
  96. *
  97. * @var array
  98. */
  99. var $sortOptions = array(
  100. 'constraint', 'containment', 'dropOnEmpty', 'ghosting', 'handle', 'hoverclass', 'onUpdate',
  101. 'onChange', 'only', 'overlap', 'scroll', 'scrollSensitivity', 'scrollSpeed', 'tag', 'tree',
  102. 'treeTag', 'update'
  103. );
  104. /**
  105. * Options for slider.
  106. *
  107. * @var array
  108. */
  109. var $sliderOptions = array(
  110. 'alignX', 'alignY', 'axis', 'disabled', 'handleDisabled', 'handleImage', 'increment',
  111. 'maximum', 'minimum', 'onChange', 'onSlide', 'range', 'sliderValue', 'values'
  112. );
  113. /**
  114. * Options for in-place editor.
  115. *
  116. * @var array
  117. */
  118. var $editorOptions = array(
  119. 'okText', 'cancelText', 'savingText', 'formId', 'externalControl', 'rows', 'cols', 'size',
  120. 'highlightcolor', 'highlightendcolor', 'savingClassName', 'formClassName', 'loadTextURL',
  121. 'loadingText', 'callback', 'ajaxOptions', 'clickToEditText', 'collection', 'okControl',
  122. 'cancelControl', 'submitOnBlur'
  123. );
  124. /**
  125. * Options for auto-complete editor.
  126. *
  127. * @var array
  128. */
  129. var $autoCompleteOptions = array(
  130. 'afterUpdateElement', 'callback', 'frequency', 'indicator', 'minChars', 'onShow', 'onHide',
  131. 'parameters', 'paramName', 'tokens', 'updateElement'
  132. );
  133. /**
  134. * Output buffer for Ajax update content
  135. *
  136. * @var array
  137. */
  138. var $__ajaxBuffer = array();
  139. /**
  140. * Returns link to remote action
  141. *
  142. * Returns a link to a remote action defined by <i>options[url]</i>
  143. * (using the url() format) that's called in the background using
  144. * XMLHttpRequest. The result of that request can then be inserted into a
  145. * DOM object whose id can be specified with <i>options[update]</i>.
  146. *
  147. * Examples:
  148. * <code>
  149. * link("Delete this post",
  150. * array("update" => "posts", "url" => "delete/{$postid->id}"));
  151. * link(imageTag("refresh"),
  152. * array("update" => "emails", "url" => "list_emails" ));
  153. * </code>
  154. *
  155. * By default, these remote requests are processed asynchronous during
  156. * which various callbacks can be triggered (for progress indicators and
  157. * the likes).
  158. *
  159. * Example:
  160. * <code>
  161. * link (word,
  162. * array("url" => "undo", "n" => word_counter),
  163. * array("complete" => "undoRequestCompleted(request)"));
  164. * </code>
  165. *
  166. * The callbacks that may be specified are:
  167. *
  168. * - <i>loading</i>:: Called when the remote document is being
  169. * loaded with data by the browser.
  170. * - <i>loaded</i>:: Called when the browser has finished loading
  171. * the remote document.
  172. * - <i>interactive</i>:: Called when the user can interact with the
  173. * remote document, even though it has not
  174. * finished loading.
  175. * - <i>complete</i>:: Called when the XMLHttpRequest is complete.
  176. *
  177. * If you for some reason or another need synchronous processing (that'll
  178. * block the browser while the request is happening), you can specify
  179. * <i>options[type] = synchronous</i>.
  180. *
  181. * You can customize further browser side call logic by passing
  182. * in Javascript code snippets via some optional parameters. In
  183. * their order of use these are:
  184. *
  185. * - <i>confirm</i>:: Adds confirmation dialog.
  186. * -<i>condition</i>:: Perform remote request conditionally
  187. * by this expression. Use this to
  188. * describe browser-side conditions when
  189. * request should not be initiated.
  190. * - <i>before</i>:: Called before request is initiated.
  191. * - <i>after</i>:: Called immediately after request was
  192. * initiated and before <i>loading</i>.
  193. *
  194. * @param string $title Title of link
  195. * @param string $href Href string "/products/view/12"
  196. * @param array $options Options for JavaScript function
  197. * @param string $confirm Confirmation message. Calls up a JavaScript confirm() message.
  198. * @param boolean $escapeTitle Escaping the title string to HTML entities
  199. *
  200. * @return string HTML code for link to remote action
  201. */
  202. function link($title, $href = null, $options = array(), $confirm = null, $escapeTitle = true) {
  203. if (!isset($href)) {
  204. $href = $title;
  205. }
  206. if (!isset($options['url'])) {
  207. $options['url'] = $href;
  208. }
  209. if (!empty($confirm)) {
  210. $options['confirm'] = $confirm;
  211. unset($confirm);
  212. }
  213. $htmlOptions = $this->__getHtmlOptions($options, array('url'));
  214. $options += array('safe' => true);
  215. if (empty($options['fallback']) || !isset($options['fallback'])) {
  216. $options['fallback'] = $href;
  217. }
  218. $htmlDefaults = array('id' => 'link' . intval(mt_rand()), 'onclick' => '');
  219. $htmlOptions = array_merge($htmlDefaults, $htmlOptions);
  220. $htmlOptions['onclick'] .= ' event.returnValue = false; return false;';
  221. $return = $this->Html->link($title, $href, $htmlOptions, null, $escapeTitle);
  222. $callback = $this->remoteFunction($options);
  223. $script = $this->Javascript->event("'{$htmlOptions['id']}'", "click", $callback);
  224. if (is_string($script)) {
  225. $return .= $script;
  226. }
  227. return $return;
  228. }
  229. /**
  230. * Creates JavaScript function for remote AJAX call
  231. *
  232. * This function creates the javascript needed to make a remote call
  233. * it is primarily used as a helper for AjaxHelper::link.
  234. *
  235. * @param array $options options for javascript
  236. * @return string html code for link to remote action
  237. * @see AjaxHelper::link() for docs on options parameter.
  238. */
  239. function remoteFunction($options) {
  240. if (isset($options['update'])) {
  241. if (!is_array($options['update'])) {
  242. $func = "new Ajax.Updater('{$options['update']}',";
  243. } else {
  244. $func = "new Ajax.Updater(document.createElement('div'),";
  245. }
  246. if (!isset($options['requestHeaders'])) {
  247. $options['requestHeaders'] = array();
  248. }
  249. if (is_array($options['update'])) {
  250. $options['update'] = implode(' ', $options['update']);
  251. }
  252. $options['requestHeaders']['X-Update'] = $options['update'];
  253. } else {
  254. $func = "new Ajax.Request(";
  255. }
  256. $url = isset($options['url']) ? $options['url'] : "";
  257. if (empty($options['safe'])) {
  258. $url = $this->url($url);
  259. } else {
  260. $url = Router::url($url);
  261. }
  262. $func .= "'" . $url . "'";
  263. $func .= ", " . $this->__optionsForAjax($options) . ")";
  264. if (isset($options['before'])) {
  265. $func = "{$options['before']}; $func";
  266. }
  267. if (isset($options['after'])) {
  268. $func = "$func; {$options['after']};";
  269. }
  270. if (isset($options['condition'])) {
  271. $func = "if ({$options['condition']}) { $func; }";
  272. }
  273. if (isset($options['confirm'])) {
  274. $func = "if (confirm('" . $this->Javascript->escapeString($options['confirm'])
  275. . "')) { $func; } else { event.returnValue = false; return false; }";
  276. }
  277. return $func;
  278. }
  279. /**
  280. * Periodically call remote url via AJAX.
  281. *
  282. * Periodically calls the specified url (<i>options[url]</i>) every <i>options[frequency]</i>
  283. * seconds (default is 10). Usually used to update a specified div (<i>options[update]</i>) with
  284. * the results of the remote call. The options for specifying the target with url and defining
  285. * callbacks is the same as AjaxHelper::link().
  286. *
  287. * @param array $options Callback options
  288. * @return string Javascript code
  289. * @see AjaxHelper::link()
  290. */
  291. function remoteTimer($options = null) {
  292. $frequency = (isset($options['frequency'])) ? $options['frequency'] : 10;
  293. $callback = $this->remoteFunction($options);
  294. $code = "new PeriodicalExecuter(function(pe) {{$callback}}, $frequency)";
  295. return $this->Javascript->codeBlock($code);
  296. }
  297. /**
  298. * Returns form tag that will submit using Ajax.
  299. *
  300. * Returns a form tag that will submit using XMLHttpRequest in the background instead of the regular
  301. * reloading POST arrangement. Even though it's using Javascript to serialize the form elements,
  302. * the form submission will work just like a regular submission as viewed by the receiving side
  303. * (all elements available in params). The options for defining callbacks is the same
  304. * as AjaxHelper::link().
  305. *
  306. * @param mixed $params Either a string identifying the form target, or an array of method parameters, including:
  307. * - 'params' => Acts as the form target
  308. * - 'type' => 'post' or 'get'
  309. * - 'options' => An array containing all HTML and script options used to
  310. * generate the form tag and Ajax request.
  311. * @param array $type How form data is posted: 'get' or 'post'
  312. * @param array $options Callback/HTML options
  313. * @return string JavaScript/HTML code
  314. * @see AjaxHelper::link()
  315. */
  316. function form($params = null, $type = 'post', $options = array()) {
  317. $model = false;
  318. if (is_array($params)) {
  319. extract($params, EXTR_OVERWRITE);
  320. }
  321. if (empty($options['url'])) {
  322. $options['url'] = array('action' => $params);
  323. }
  324. $htmlDefaults = array(
  325. 'id' => 'form' . intval(mt_rand()),
  326. 'onsubmit' => "event.returnValue = false; return false;",
  327. 'type' => $type
  328. );
  329. $htmlOptions = $this->__getHtmlOptions($options, array('model', 'with'));
  330. $htmlOptions = array_merge($htmlDefaults, $htmlOptions);
  331. $defaults = array('model' => $model, 'with' => "Form.serialize('{$htmlOptions['id']}')");
  332. $options = array_merge($defaults, $options);
  333. $callback = $this->remoteFunction($options);
  334. $form = $this->Form->create($options['model'], $htmlOptions);
  335. $script = $this->Javascript->event("'" . $htmlOptions['id']. "'", 'submit', $callback);
  336. return $form . $script;
  337. }
  338. /**
  339. * Returns a button input tag that will submit using Ajax
  340. *
  341. * Returns a button input tag that will submit form using XMLHttpRequest in the background instead
  342. * of regular reloading POST arrangement. <i>options</i> argument is the same as
  343. * in AjaxHelper::form().
  344. *
  345. * @param string $title Input button title
  346. * @param array $options Callback options
  347. * @return string Ajaxed input button
  348. * @see AjaxHelper::form()
  349. */
  350. function submit($title = 'Submit', $options = array()) {
  351. $htmlOptions = $this->__getHtmlOptions($options);
  352. $htmlOptions['value'] = $title;
  353. if (!isset($options['with'])) {
  354. $options['with'] = 'Form.serialize(Event.element(event).form)';
  355. }
  356. if (!isset($htmlOptions['id'])) {
  357. $htmlOptions['id'] = 'submit' . intval(mt_rand());
  358. }
  359. $htmlOptions['onclick'] = "event.returnValue = false; return false;";
  360. $callback = $this->remoteFunction($options);
  361. $form = $this->Form->submit($title, $htmlOptions);
  362. $script = $this->Javascript->event('"' . $htmlOptions['id'] . '"', 'click', $callback);
  363. return $form . $script;
  364. }
  365. /**
  366. * Observe field and call ajax on change.
  367. *
  368. * Observes the field with the DOM ID specified by <i>field</i> and makes
  369. * an Ajax when its contents have changed.
  370. *
  371. * Required +options+ are:
  372. * - <i>frequency</i>:: The frequency (in seconds) at which changes to
  373. * this field will be detected.
  374. * - <i>url</i>:: @see url() -style options for the action to call
  375. * when the field has changed.
  376. *
  377. * Additional options are:
  378. * - <i>update</i>:: Specifies the DOM ID of the element whose
  379. * innerHTML should be updated with the
  380. * XMLHttpRequest response text.
  381. * - <i>with</i>:: A Javascript expression specifying the
  382. * parameters for the XMLHttpRequest. This defaults
  383. * to Form.Element.serialize('$field'), which can be
  384. * accessed from params['form']['field_id'].
  385. *
  386. * Additionally, you may specify any of the options documented in
  387. * @see linkToRemote().
  388. *
  389. * @param string $field DOM ID of field to observe
  390. * @param array $options ajax options
  391. * @return string ajax script
  392. */
  393. function observeField($field, $options = array()) {
  394. if (!isset($options['with'])) {
  395. $options['with'] = 'Form.Element.serialize(\'' . $field . '\')';
  396. }
  397. $observer = 'Observer';
  398. if (!isset($options['frequency']) || intval($options['frequency']) == 0) {
  399. $observer = 'EventObserver';
  400. }
  401. return $this->Javascript->codeBlock(
  402. $this->_buildObserver('Form.Element.' . $observer, $field, $options)
  403. );
  404. }
  405. /**
  406. * Observe entire form and call ajax on change.
  407. *
  408. * Like @see observeField(), but operates on an entire form identified by the
  409. * DOM ID <b>form</b>. <b>options</b> are the same as <b>observeField</b>, except
  410. * the default value of the <i>with</i> option evaluates to the
  411. * serialized (request string) value of the form.
  412. *
  413. * @param string $form DOM ID of form to observe
  414. * @param array $options ajax options
  415. * @return string ajax script
  416. */
  417. function observeForm($form, $options = array()) {
  418. if (!isset($options['with'])) {
  419. $options['with'] = 'Form.serialize(\'' . $form . '\')';
  420. }
  421. $observer = 'Observer';
  422. if (!isset($options['frequency']) || intval($options['frequency']) == 0) {
  423. $observer = 'EventObserver';
  424. }
  425. return $this->Javascript->codeBlock(
  426. $this->_buildObserver('Form.' . $observer, $form, $options)
  427. );
  428. }
  429. /**
  430. * Create a text field with Autocomplete.
  431. *
  432. * Creates an autocomplete field with the given ID and options.
  433. *
  434. * options['with'] defaults to "Form.Element.serialize('$field')",
  435. * but can be any valid javascript expression defining the additional fields.
  436. *
  437. * @param string $field DOM ID of field to observe
  438. * @param string $url URL for the autocomplete action
  439. * @param array $options Ajax options
  440. * @return string Ajax script
  441. */
  442. function autoComplete($field, $url = "", $options = array()) {
  443. $var = '';
  444. if (isset($options['var'])) {
  445. $var = 'var ' . $options['var'] . ' = ';
  446. unset($options['var']);
  447. }
  448. if (!isset($options['id'])) {
  449. $options['id'] = Inflector::camelize(str_replace(".", "_", $field));
  450. }
  451. $divOptions = array(
  452. 'id' => $options['id'] . "_autoComplete",
  453. 'class' => isset($options['class']) ? $options['class'] : 'auto_complete'
  454. );
  455. if (isset($options['div_id'])) {
  456. $divOptions['id'] = $options['div_id'];
  457. unset($options['div_id']);
  458. }
  459. $htmlOptions = $this->__getHtmlOptions($options);
  460. $htmlOptions['autocomplete'] = "off";
  461. foreach ($this->autoCompleteOptions as $opt) {
  462. unset($htmlOptions[$opt]);
  463. }
  464. if (isset($options['tokens'])) {
  465. if (is_array($options['tokens'])) {
  466. $options['tokens'] = $this->Javascript->object($options['tokens']);
  467. } else {
  468. $options['tokens'] = '"' . $options['tokens'] . '"';
  469. }
  470. }
  471. $options = $this->_optionsToString($options, array('paramName', 'indicator'));
  472. $options = $this->_buildOptions($options, $this->autoCompleteOptions);
  473. $text = $this->Form->text($field, $htmlOptions);
  474. $div = $this->Html->div(null, '', $divOptions);
  475. $script = "{$var}new Ajax.Autocompleter('{$htmlOptions['id']}', '{$divOptions['id']}', '";
  476. $script .= $this->Html->url($url) . "', {$options});";
  477. return "{$text}\n{$div}\n" . $this->Javascript->codeBlock($script);
  478. }
  479. /**
  480. * Creates an Ajax-updateable DIV element
  481. *
  482. * @param string $id options for javascript
  483. * @return string HTML code
  484. */
  485. function div($id, $options = array()) {
  486. if (env('HTTP_X_UPDATE') != null) {
  487. $this->Javascript->enabled = false;
  488. $divs = explode(' ', env('HTTP_X_UPDATE'));
  489. if (in_array($id, $divs)) {
  490. @ob_end_clean();
  491. ob_start();
  492. return '';
  493. }
  494. }
  495. $attr = $this->_parseAttributes(array_merge($options, array('id' => $id)));
  496. return $this->output(sprintf($this->Html->tags['blockstart'], $attr));
  497. }
  498. /**
  499. * Closes an Ajax-updateable DIV element
  500. *
  501. * @param string $id The DOM ID of the element
  502. * @return string HTML code
  503. */
  504. function divEnd($id) {
  505. if (env('HTTP_X_UPDATE') != null) {
  506. $divs = explode(' ', env('HTTP_X_UPDATE'));
  507. if (in_array($id, $divs)) {
  508. $this->__ajaxBuffer[$id] = ob_get_contents();
  509. ob_end_clean();
  510. ob_start();
  511. return '';
  512. }
  513. }
  514. return $this->output($this->Html->tags['blockend']);
  515. }
  516. /**
  517. * Detects Ajax requests
  518. *
  519. * @return boolean True if the current request is a Prototype Ajax update call
  520. */
  521. function isAjax() {
  522. return (isset($this->params['isAjax']) && $this->params['isAjax'] === true);
  523. }
  524. /**
  525. * Creates a draggable element. For a reference on the options for this function,
  526. * check out http://github.com/madrobby/scriptaculous/wikis/draggable
  527. *
  528. * @param unknown_type $id
  529. * @param array $options
  530. * @return unknown
  531. */
  532. function drag($id, $options = array()) {
  533. $var = '';
  534. if (isset($options['var'])) {
  535. $var = 'var ' . $options['var'] . ' = ';
  536. unset($options['var']);
  537. }
  538. $options = $this->_buildOptions(
  539. $this->_optionsToString($options, array('handle', 'constraint')), $this->dragOptions
  540. );
  541. return $this->Javascript->codeBlock("{$var}new Draggable('$id', " .$options . ");");
  542. }
  543. /**
  544. * For a reference on the options for this function, check out
  545. * http://github.com/madrobby/scriptaculous/wikis/droppables
  546. *
  547. * @param unknown_type $id
  548. * @param array $options
  549. * @return string
  550. */
  551. function drop($id, $options = array()) {
  552. $optionsString = array('overlap', 'hoverclass');
  553. if (!isset($options['accept']) || !is_array($options['accept'])) {
  554. $optionsString[] = 'accept';
  555. } else if (isset($options['accept'])) {
  556. $options['accept'] = $this->Javascript->object($options['accept']);
  557. }
  558. $options = $this->_buildOptions(
  559. $this->_optionsToString($options, $optionsString), $this->dropOptions
  560. );
  561. return $this->Javascript->codeBlock("Droppables.add('{$id}', {$options});");
  562. }
  563. /**
  564. * Make an element with the given $id droppable, and trigger an Ajax call when a draggable is
  565. * dropped on it.
  566. *
  567. * For a reference on the options for this function, check out
  568. * http://wiki.script.aculo.us/scriptaculous/show/Droppables.add
  569. *
  570. * @param string $id
  571. * @param array $options
  572. * @param array $ajaxOptions
  573. * @return string JavaScript block to create a droppable element
  574. */
  575. function dropRemote($id, $options = array(), $ajaxOptions = array()) {
  576. $callback = $this->remoteFunction($ajaxOptions);
  577. $options['onDrop'] = "function(element, droppable, event) {{$callback}}";
  578. $optionsString = array('overlap', 'hoverclass');
  579. if (!isset($options['accept']) || !is_array($options['accept'])) {
  580. $optionsString[] = 'accept';
  581. } else if (isset($options['accept'])) {
  582. $options['accept'] = $this->Javascript->object($options['accept']);
  583. }
  584. $options = $this->_buildOptions(
  585. $this->_optionsToString($options, $optionsString),
  586. $this->dropOptions
  587. );
  588. return $this->Javascript->codeBlock("Droppables.add('{$id}', {$options});");
  589. }
  590. /**
  591. * Makes a slider control.
  592. *
  593. * @param string $id DOM ID of slider handle
  594. * @param string $trackId DOM ID of slider track
  595. * @param array $options Array of options to control the slider
  596. * @link http://github.com/madrobby/scriptaculous/wikis/slider
  597. */
  598. function slider($id, $trackId, $options = array()) {
  599. if (isset($options['var'])) {
  600. $var = 'var ' . $options['var'] . ' = ';
  601. unset($options['var']);
  602. } else {
  603. $var = 'var ' . $id . ' = ';
  604. }
  605. $options = $this->_optionsToString($options, array(
  606. 'axis', 'handleImage', 'handleDisabled'
  607. ));
  608. $callbacks = array('change', 'slide');
  609. foreach ($callbacks as $callback) {
  610. if (isset($options[$callback])) {
  611. $call = $options[$callback];
  612. $options['on' . ucfirst($callback)] = "function(value) {{$call}}";
  613. unset($options[$callback]);
  614. }
  615. }
  616. if (isset($options['values']) && is_array($options['values'])) {
  617. $options['values'] = $this->Javascript->object($options['values']);
  618. }
  619. $options = $this->_buildOptions($options, $this->sliderOptions);
  620. $script = "{$var}new Control.Slider('$id', '$trackId', $options);";
  621. return $this->Javascript->codeBlock($script);
  622. }
  623. /**
  624. * Makes an Ajax In Place editor control.
  625. *
  626. * @param string $id DOM ID of input element
  627. * @param string $url Postback URL of saved data
  628. * @param array $options Array of options to control the editor, including ajaxOptions (see link).
  629. * @link http://github.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor
  630. */
  631. function editor($id, $url, $options = array()) {
  632. $url = $this->url($url);
  633. $options['ajaxOptions'] = $this->__optionsForAjax($options);
  634. foreach ($this->ajaxOptions as $opt) {
  635. if (isset($options[$opt])) {
  636. unset($options[$opt]);
  637. }
  638. }
  639. if (isset($options['callback'])) {
  640. $options['callback'] = 'function(form, value) {' . $options['callback'] . '}';
  641. }
  642. $type = 'InPlaceEditor';
  643. if (isset($options['collection']) && is_array($options['collection'])) {
  644. $options['collection'] = $this->Javascript->object($options['collection']);
  645. $type = 'InPlaceCollectionEditor';
  646. }
  647. $var = '';
  648. if (isset($options['var'])) {
  649. $var = 'var ' . $options['var'] . ' = ';
  650. unset($options['var']);
  651. }
  652. $options = $this->_optionsToString($options, array(
  653. 'okText', 'cancelText', 'savingText', 'formId', 'externalControl', 'highlightcolor',
  654. 'highlightendcolor', 'savingClassName', 'formClassName', 'loadTextURL', 'loadingText',
  655. 'clickToEditText', 'okControl', 'cancelControl'
  656. ));
  657. $options = $this->_buildOptions($options, $this->editorOptions);
  658. $script = "{$var}new Ajax.{$type}('{$id}', '{$url}', {$options});";
  659. return $this->Javascript->codeBlock($script);
  660. }
  661. /**
  662. * Makes a list or group of floated objects sortable.
  663. *
  664. * @param string $id DOM ID of parent
  665. * @param array $options Array of options to control sort.
  666. * @link http://github.com/madrobby/scriptaculous/wikis/sortable
  667. */
  668. function sortable($id, $options = array()) {
  669. if (!empty($options['url'])) {
  670. if (empty($options['with'])) {
  671. $options['with'] = "Sortable.serialize('$id')";
  672. }
  673. $options['onUpdate'] = 'function(sortable) {' . $this->remoteFunction($options) . '}';
  674. }
  675. $block = true;
  676. if (isset($options['block'])) {
  677. $block = $options['block'];
  678. unset($options['block']);
  679. }
  680. $strings = array(
  681. 'tag', 'constraint', 'only', 'handle', 'hoverclass', 'tree',
  682. 'treeTag', 'update', 'overlap'
  683. );
  684. $scrollIsObject = (
  685. isset($options['scroll']) &&
  686. $options['scroll'] != 'window' &&
  687. strpos($options['scroll'], '$(') !== 0
  688. );
  689. if ($scrollIsObject) {
  690. $strings[] = 'scroll';
  691. }
  692. $options = $this->_optionsToString($options, $strings);
  693. $options = array_merge($options, $this->_buildCallbacks($options));
  694. $options = $this->_buildOptions($options, $this->sortOptions);
  695. $result = "Sortable.create('$id', $options);";
  696. if (!$block) {
  697. return $result;
  698. }
  699. return $this->Javascript->codeBlock($result);
  700. }
  701. /**
  702. * Private helper function for Javascript.
  703. *
  704. * @param array $options Set of options
  705. * @access private
  706. */
  707. function __optionsForAjax($options) {
  708. if (isset($options['indicator'])) {
  709. if (isset($options['loading'])) {
  710. $loading = $options['loading'];
  711. if (!empty($loading) && substr(trim($loading), -1, 1) != ';') {
  712. $options['loading'] .= '; ';
  713. }
  714. $options['loading'] .= "Element.show('{$options['indicator']}');";
  715. } else {
  716. $options['loading'] = "Element.show('{$options['indicator']}');";
  717. }
  718. if (isset($options['complete'])) {
  719. $complete = $options['complete'];
  720. if (!empty($complete) && substr(trim($complete), -1, 1) != ';') {
  721. $options['complete'] .= '; ';
  722. }
  723. $options['complete'] .= "Element.hide('{$options['indicator']}');";
  724. } else {
  725. $options['complete'] = "Element.hide('{$options['indicator']}');";
  726. }
  727. unset($options['indicator']);
  728. }
  729. $jsOptions = array_merge(
  730. array('asynchronous' => 'true', 'evalScripts' => 'true'),
  731. $this->_buildCallbacks($options)
  732. );
  733. $options = $this->_optionsToString($options, array(
  734. 'contentType', 'encoding', 'fallback', 'method', 'postBody', 'update', 'url'
  735. ));
  736. $jsOptions = array_merge($jsOptions, array_intersect_key($options, array_flip(array(
  737. 'contentType', 'encoding', 'method', 'postBody'
  738. ))));
  739. foreach ($options as $key => $value) {
  740. switch ($key) {
  741. case 'type':
  742. $jsOptions['asynchronous'] = ($value == 'synchronous') ? 'false' : 'true';
  743. break;
  744. case 'evalScripts':
  745. $jsOptions['evalScripts'] = ($value) ? 'true' : 'false';
  746. break;
  747. case 'position':
  748. $pos = Inflector::camelize($options['position']);
  749. $jsOptions['insertion'] = "Insertion.{$pos}";
  750. break;
  751. case 'with':
  752. $jsOptions['parameters'] = $options['with'];
  753. break;
  754. case 'form':
  755. $jsOptions['parameters'] = 'Form.serialize(this)';
  756. break;
  757. case 'requestHeaders':
  758. $keys = array();
  759. foreach ($value as $key => $val) {
  760. $keys[] = "'" . $key . "'";
  761. $keys[] = "'" . $val . "'";
  762. }
  763. $jsOptions['requestHeaders'] = '[' . implode(', ', $keys) . ']';
  764. break;
  765. }
  766. }
  767. return $this->_buildOptions($jsOptions, $this->ajaxOptions);
  768. }
  769. /**
  770. * Private Method to return a string of html options
  771. * option data as a JavaScript options hash.
  772. *
  773. * @param array $options Options in the shape of keys and values
  774. * @param array $extra Array of legal keys in this options context
  775. * @return array Array of html options
  776. * @access private
  777. */
  778. function __getHtmlOptions($options, $extra = array()) {
  779. foreach (array_merge($this->ajaxOptions, $this->callbacks, $extra) as $key) {
  780. if (isset($options[$key])) {
  781. unset($options[$key]);
  782. }
  783. }
  784. return $options;
  785. }
  786. /**
  787. * Returns a string of JavaScript with the given option data as a JavaScript options hash.
  788. *
  789. * @param array $options Options in the shape of keys and values
  790. * @param array $acceptable Array of legal keys in this options context
  791. * @return string String of Javascript array definition
  792. */
  793. function _buildOptions($options, $acceptable) {
  794. if (is_array($options)) {
  795. $out = array();
  796. foreach ($options as $k => $v) {
  797. if (in_array($k, $acceptable)) {
  798. if ($v === true) {
  799. $v = 'true';
  800. } elseif ($v === false) {
  801. $v = 'false';
  802. }
  803. $out[] = "$k:$v";
  804. } elseif ($k === 'with' && in_array('parameters', $acceptable)) {
  805. $out[] = "parameters:${v}";
  806. }
  807. }
  808. $out = implode(', ', $out);
  809. $out = '{' . $out . '}';
  810. return $out;
  811. } else {
  812. return false;
  813. }
  814. }
  815. /**
  816. * Return JavaScript text for an observer...
  817. *
  818. * @param string $klass Name of JavaScript class
  819. * @param string $name
  820. * @param array $options Ajax options
  821. * @return string Formatted JavaScript
  822. */
  823. function _buildObserver($klass, $name, $options = null) {
  824. if (!isset($options['with']) && isset($options['update'])) {
  825. $options['with'] = 'value';
  826. }
  827. $callback = $this->remoteFunction($options);
  828. $hasFrequency = !(!isset($options['frequency']) || intval($options['frequency']) == 0);
  829. $frequency = $hasFrequency ? $options['frequency'] . ', ' : '';
  830. return "new $klass('$name', {$frequency}function(element, value) {{$callback}})";
  831. }
  832. /**
  833. * Return Javascript text for callbacks.
  834. *
  835. * @param array $options Option array where a callback is specified
  836. * @return array Options with their callbacks properly set
  837. * @access protected
  838. */
  839. function _buildCallbacks($options) {
  840. $callbacks = array();
  841. foreach ($this->callbacks as $callback) {
  842. if (isset($options[$callback])) {
  843. $name = 'on' . ucfirst($callback);
  844. $code = $options[$callback];
  845. switch ($name) {
  846. case 'onComplete':
  847. $callbacks[$name] = "function(request, json) {" . $code . "}";
  848. break;
  849. case 'onCreate':
  850. $callbacks[$name] = "function(request, xhr) {" . $code . "}";
  851. break;
  852. case 'onException':
  853. $callbacks[$name] = "function(request, exception) {" . $code . "}";
  854. break;
  855. default:
  856. $callbacks[$name] = "function(request) {" . $code . "}";
  857. break;
  858. }
  859. if (isset($options['bind'])) {
  860. $bind = $options['bind'];
  861. $hasBinding = (
  862. (is_array($bind) && in_array($callback, $bind)) ||
  863. (is_string($bind) && strpos($bind, $callback) !== false)
  864. );
  865. if ($hasBinding) {
  866. $callbacks[$name] .= ".bind(this)";
  867. }
  868. }
  869. }
  870. }
  871. return $callbacks;
  872. }
  873. /**
  874. * Returns a string of JavaScript with a string representation of given options array.
  875. *
  876. * @param array $options Ajax options array
  877. * @param array $stringOpts Options as strings in an array
  878. * @access private
  879. * @return array
  880. */
  881. function _optionsToString($options, $stringOpts = array()) {
  882. foreach ($stringOpts as $option) {
  883. $hasOption = (
  884. isset($options[$option]) && !empty($options[$option]) &&
  885. is_string($options[$option]) && $options[$option][0] != "'"
  886. );
  887. if ($hasOption) {
  888. if ($options[$option] === true || $options[$option] === 'true') {
  889. $options[$option] = 'true';
  890. } elseif ($options[$option] === false || $options[$option] === 'false') {
  891. $options[$option] = 'false';
  892. } else {
  893. $options[$option] = "'{$options[$option]}'";
  894. }
  895. }
  896. }
  897. return $options;
  898. }
  899. /**
  900. * Executed after a view has rendered, used to include bufferred code
  901. * blocks.
  902. *
  903. * @access public
  904. */
  905. function afterRender() {
  906. if (env('HTTP_X_UPDATE') != null && !empty($this->__ajaxBuffer)) {
  907. @ob_end_clean();
  908. $data = array();
  909. $divs = explode(' ', env('HTTP_X_UPDATE'));
  910. $keys = array_keys($this->__ajaxBuffer);
  911. if (count($divs) == 1 && in_array($divs[0], $keys)) {
  912. echo $this->__ajaxBuffer[$divs[0]];
  913. } else {
  914. foreach ($this->__ajaxBuffer as $key => $val) {
  915. if (in_array($key, $divs)) {
  916. $data[] = $key . ':"' . rawurlencode($val) . '"';
  917. }
  918. }
  919. $out = 'var __ajaxUpdater__ = {' . implode(", \n", $data) . '};' . "\n";
  920. $out .= 'for (n in __ajaxUpdater__) { if (typeof __ajaxUpdater__[n] == "string"';
  921. $out .= ' && $(n)) Element.update($(n), unescape(decodeURIComponent(';
  922. $out .= '__ajaxUpdater__[n]))); }';
  923. echo $this->Javascript->codeBlock($out, false);
  924. }
  925. $scripts = $this->Javascript->getCache();
  926. if (!empty($scripts)) {
  927. echo $this->Javascript->codeBlock($scripts, false);
  928. }
  929. $this->_stop();
  930. }
  931. }
  932. }
  933. ?>