| [ Index ] |
|
Code source de Symfony 1.0.0 |
1 <?php 2 3 /* 4 * This file is part of the symfony package. 5 * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com> 6 * (c) 2004 David Heinemeier Hansson 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12 /** 13 * JavascriptHelper. 14 * 15 * @package symfony 16 * @subpackage helper 17 * @author Fabien Potencier <fabien.potencier@symfony-project.com> 18 * @author John Christopher <john.christopher@symfony-project.com> 19 * @author David Heinemeier Hansson 20 * @version SVN: $Id: JavascriptHelper.php 3500 2007-02-18 10:25:13Z fabien $ 21 */ 22 23 /* 24 * Provides a set of helpers for calling JavaScript functions and, most importantly, 25 * to call remote methods using what has been labelled AJAX[http://www.adaptivepath.com/publications/essays/archives/000385.php]. 26 * This means that you can call actions in your controllers without reloading the page, 27 * but still update certain parts of it using injections into the DOM. 28 * The common use case is having a form that adds a new element to a list without reloading the page. 29 * 30 * To be able to use the JavaScript helpers, you must include the Prototype JavaScript Framework 31 * and for some functions script.aculo.us (which both come with symfony) on your pages. 32 * Choose one of these options: 33 * 34 * * Use <tt><?php echo javascript_include_tag :defaults ?></tt> in the HEAD section of your page (recommended): 35 * The function will return references to the JavaScript files created by the +rails+ command in your 36 * <tt>public/javascripts</tt> directory. Using it is recommended as the browser can then cache the libraries 37 * instead of fetching all the functions anew on every request. 38 * * Use <tt><?php echo javascript_include_tag 'prototype' ?></tt>: As above, but will only include the Prototype core library, 39 * which means you are able to use all basic AJAX functionality. For the script.aculo.us-based JavaScript helpers, 40 * like visual effects, autocompletion, drag and drop and so on, you should use the method described above. 41 * * Use <tt><?php echo define_javascript_functions ?></tt>: this will copy all the JavaScript support functions within a single 42 * script block. 43 * 44 * For documentation on +javascript_include_tag+ see ActionView::Helpers::AssetTagHelper. 45 * 46 * If you're the visual type, there's an AJAX movie[http://www.rubyonrails.com/media/video/rails-ajax.mov] demonstrating 47 * the use of form_remote_tag. 48 */ 49 50 function get_callbacks() 51 { 52 static $callbacks; 53 if (!$callbacks) 54 { 55 $callbacks = array_merge(array( 56 'uninitialized', 'loading', 'loaded', 'interactive', 'complete', 'failure', 'success' 57 ), range(100, 599)); 58 } 59 60 return $callbacks; 61 } 62 63 function get_ajax_options() 64 { 65 static $ajax_options; 66 if (!$ajax_options) 67 { 68 $ajax_options = array_merge(array( 69 'before', 'after', 'condition', 'url', 'asynchronous', 'method', 70 'insertion', 'position', 'form', 'with', 'update', 'script' 71 ), get_callbacks()); 72 } 73 74 return $ajax_options; 75 } 76 77 /** 78 * Returns a link that'll trigger a javascript function using the 79 * onclick handler and return false after the fact. 80 * 81 * Examples: 82 * <?php echo link_to_function('Greeting', "alert('Hello world!')") ?> 83 * <?php echo link_to_function(image_tag('delete'), "if confirm('Really?'){ do_delete(); }") ?> 84 */ 85 function link_to_function($name, $function, $html_options = array()) 86 { 87 $html_options = _parse_attributes($html_options); 88 89 $html_options['href'] = isset($html_options['href']) ? $html_options['href'] : '#'; 90 $html_options['onclick'] = $function.'; return false;'; 91 92 return content_tag('a', $name, $html_options); 93 } 94 95 /** 96 * Returns a button that'll trigger a javascript function using the 97 * onclick handler and return false after the fact. 98 * 99 * Examples: 100 * <?php echo button_to_function('Greeting', "alert('Hello world!')") ?> 101 */ 102 function button_to_function($name, $function, $html_options = array()) 103 { 104 $html_options = _parse_attributes($html_options); 105 106 $html_options['onclick'] = $function.'; return false;'; 107 $html_options['type'] = 'button'; 108 $html_options['value'] = $name; 109 110 return tag('input', $html_options); 111 } 112 113 /** 114 * Returns an html button to a remote action defined by 'url' (using the 115 * 'url_for()' format) that's called in the background using XMLHttpRequest. 116 * 117 * See link_to_remote() for details. 118 * 119 */ 120 function button_to_remote($name, $options = array(), $html_options = array()) 121 { 122 return button_to_function($name, remote_function($options), $html_options); 123 } 124 125 /** 126 * Returns a link to a remote action defined by 'url' 127 * (using the 'url_for()' format) that's called in the background using 128 * XMLHttpRequest. The result of that request can then be inserted into a 129 * DOM object whose id can be specified with 'update'. 130 * Usually, the result would be a partial prepared by the controller with 131 * either 'render_partial()'. 132 * 133 * Examples: 134 * <?php echo link_to_remote('Delete this post'), array( 135 * 'update' => 'posts', 136 * 'url' => 'destroy?id='.$post.id, 137 * )) ?> 138 * <?php echo link_to_remote(image_tag('refresh'), array( 139 * 'update' => 'emails', 140 * 'url' => '@list_emails', 141 * )) ?> 142 * 143 * You can also specify a hash for 'update' to allow for 144 * easy redirection of output to an other DOM element if a server-side error occurs: 145 * 146 * Example: 147 * <?php echo link_to_remote('Delete this post', array( 148 * 'update' => array('success' => 'posts', 'failure' => 'error'), 149 * 'url' => 'destroy?id='.$post.id, 150 * )) ?> 151 * 152 * Optionally, you can use the 'position' parameter to influence 153 * how the target DOM element is updated. It must be one of 154 * 'before', 'top', 'bottom', or 'after'. 155 * 156 * By default, these remote requests are processed asynchronous during 157 * which various JavaScript callbacks can be triggered (for progress indicators and 158 * the likes). All callbacks get access to the 'request' object, 159 * which holds the underlying XMLHttpRequest. 160 * 161 * To access the server response, use 'request.responseText', to 162 * find out the HTTP status, use 'request.status'. 163 * 164 * Example: 165 * <?php echo link_to_remote($word, array( 166 * 'url' => '@undo?n='.$word_counter, 167 * 'complete' => 'undoRequestCompleted(request)' 168 * )) ?> 169 * 170 * The callbacks that may be specified are (in order): 171 * 172 * 'loading' Called when the remote document is being 173 * loaded with data by the browser. 174 * 'loaded' Called when the browser has finished loading 175 * the remote document. 176 * 'interactive' Called when the user can interact with the 177 * remote document, even though it has not 178 * finished loading. 179 * 'success' Called when the XMLHttpRequest is completed, 180 * and the HTTP status code is in the 2XX range. 181 * 'failure' Called when the XMLHttpRequest is completed, 182 * and the HTTP status code is not in the 2XX 183 * range. 184 * 'complete' Called when the XMLHttpRequest is complete 185 * (fires after success/failure if they are present)., 186 * 187 * You can further refine 'success' and 'failure' by adding additional 188 * callbacks for specific status codes: 189 * 190 * Example: 191 * <?php echo link_to_remote($word, array( 192 * 'url' => '@rule', 193 * '404' => "alert('Not found...? Wrong URL...?')", 194 * 'failure' => "alert('HTTP Error ' + request.status + '!')", 195 * )) ?> 196 * 197 * A status code callback overrides the success/failure handlers if present. 198 * 199 * If you for some reason or another need synchronous processing (that'll 200 * block the browser while the request is happening), you can specify 201 * 'type' => 'synchronous'. 202 * 203 * You can customize further browser side call logic by passing 204 * in JavaScript code snippets via some optional parameters. In 205 * their order of use these are: 206 * 207 * 'confirm' Adds confirmation dialog. 208 * 'condition' Perform remote request conditionally 209 * by this expression. Use this to 210 * describe browser-side conditions when 211 * request should not be initiated. 212 * 'before' Called before request is initiated. 213 * 'after' Called immediately after request was 214 * initiated and before 'loading'. 215 * 'submit' Specifies the DOM element ID that's used 216 * as the parent of the form elements. By 217 * default this is the current form, but 218 * it could just as well be the ID of a 219 * table row or any other DOM element. 220 */ 221 function link_to_remote($name, $options = array(), $html_options = array()) 222 { 223 return link_to_function($name, remote_function($options), $html_options); 224 } 225 226 /** 227 * Periodically calls the specified url ('url') every 'frequency' seconds (default is 10). 228 * Usually used to update a specified div ('update') with the results of the remote call. 229 * The options for specifying the target with 'url' and defining callbacks is the same as 'link_to_remote()'. 230 */ 231 function periodically_call_remote($options = array()) 232 { 233 $frequency = isset($options['frequency']) ? $options['frequency'] : 10; // every ten seconds by default 234 $code = 'new PeriodicalExecuter(function() {'.remote_function($options).'}, '.$frequency.')'; 235 236 return javascript_tag($code); 237 } 238 239 /** 240 * Returns a form tag that will submit using XMLHttpRequest in the background instead of the regular 241 * reloading POST arrangement. Even though it's using JavaScript to serialize the form elements, the form submission 242 * will work just like a regular submission as viewed by the receiving side (all elements available in 'params'). 243 * The options for specifying the target with 'url' and defining callbacks are the same as 'link_to_remote()'. 244 * 245 * A "fall-through" target for browsers that don't do JavaScript can be specified 246 * with the 'action'/'method' options on '$options_html' 247 * 248 * Example: 249 * <?php echo form_remote_tag(array( 250 * 'url' => '@tag_add', 251 * 'update' => 'question_tags', 252 * 'loading' => "Element.show('indicator'); \$('tag').value = ''", 253 * 'complete' => "Element.hide('indicator');".visual_effect('highlight', 'question_tags'), 254 * )) ?> 255 * 256 * The hash passed as a second argument is equivalent to the options (2nd) argument in the form_tag() helper. 257 * 258 * By default the fall-through action is the same as the one specified in the 'url' 259 * (and the default method is 'post'). 260 */ 261 function form_remote_tag($options = array(), $options_html = array()) 262 { 263 $options = _parse_attributes($options); 264 $options_html = _parse_attributes($options_html); 265 266 $options['form'] = true; 267 268 $options_html['onsubmit'] = remote_function($options).'; return false;'; 269 $options_html['action'] = isset($options_html['action']) ? $options_html['action'] : url_for($options['url']); 270 $options_html['method'] = isset($options_html['method']) ? $options_html['method'] : 'post'; 271 272 return tag('form', $options_html, true); 273 } 274 275 /** 276 * Returns a button input tag that will submit form using XMLHttpRequest in the background instead of regular 277 * reloading POST arrangement. The '$options' argument is the same as in 'form_remote_tag()'. 278 */ 279 function submit_to_remote($name, $value, $options = array(), $options_html = array()) 280 { 281 $options = _parse_attributes($options); 282 $options_html = _parse_attributes($options_html); 283 284 if (!isset($options['with'])) 285 { 286 $options['with'] = 'Form.serialize(this.form)'; 287 } 288 289 $options_html['type'] = 'button'; 290 $options_html['onclick'] = remote_function($options).'; return false;'; 291 $options_html['name'] = $name; 292 $options_html['value'] = $value; 293 294 return tag('input', $options_html, false); 295 } 296 297 /** 298 * Returns a Javascript function (or expression) that will update a DOM element '$element_id' 299 * according to the '$options' passed. 300 * 301 * Possible '$options' are: 302 * 'content' The content to use for updating. Can be left out if using block, see example. 303 * 'action' Valid options are 'update' (assumed by default), 'empty', 'remove' 304 * 'position' If the 'action' is 'update', you can optionally specify one of the following positions: 305 * 'before', 'top', 'bottom', 'after'. 306 * 307 * Example: 308 * <?php echo javascript_tag( 309 * update_element_function('products', array( 310 * 'position' => 'bottom', 311 * 'content' => "<p>New product!</p>", 312 * )) 313 * ) ?> 314 * 315 * 316 * This method can also be used in combination with remote method call 317 * where the result is evaluated afterwards to cause multiple updates on a page. 318 * 319 * Example: 320 * 321 * # Calling view 322 * <?php echo form_remote_tag(array( 323 * 'url' => '@buy', 324 * 'complete' => evaluate_remote_response() 325 * )) ?> 326 * all the inputs here... 327 * 328 * # Target action 329 * public function executeBuy() 330 * { 331 * $this->product = ProductPeer::retrieveByPk(1); 332 * } 333 * 334 * # Returning view 335 * <php echo update_element_function('cart', array( 336 * 'action' => 'update', 337 * 'position' => 'bottom', 338 * 'content' => '<p>New Product: '.$product->getName().'</p>', 339 * )) ?> 340 */ 341 function update_element_function($element_id, $options = array()) 342 { 343 sfContext::getInstance()->getResponse()->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); 344 345 $content = escape_javascript(isset($options['content']) ? $options['content'] : ''); 346 347 $value = isset($options['action']) ? $options['action'] : 'update'; 348 switch ($value) 349 { 350 case 'update': 351 if (isset($options['position']) && $options['position']) 352 { 353 $javascript_function = "new Insertion.".sfInflector::camelize($options['position'])."('$element_id','$content')"; 354 } 355 else 356 { 357 $javascript_function = "\$('$element_id').innerHTML = '$content'"; 358 } 359 break; 360 361 case 'empty': 362 $javascript_function = "\$('$element_id').innerHTML = ''"; 363 break; 364 365 case 'remove': 366 $javascript_function = "Element.remove('$element_id')"; 367 break; 368 369 default: 370 throw new sfException('Invalid action, choose one of update, remove, empty'); 371 } 372 373 $javascript_function .= ";\n"; 374 375 return (isset($options['binding']) ? $javascript_function.$options['binding'] : $javascript_function); 376 } 377 378 /** 379 * Returns 'eval(request.responseText)', which is the Javascript function that 380 * 'form_remote_tag()' can call in 'complete' to evaluate a multiple update return document 381 * using 'update_element_function()' calls. 382 */ 383 function evaluate_remote_response() 384 { 385 return 'eval(request.responseText)'; 386 } 387 388 /** 389 * Returns the javascript needed for a remote function. 390 * Takes the same arguments as 'link_to_remote()'. 391 * 392 * Example: 393 * <select id="options" onchange="<?php echo remote_function(array('update' => 'options', 'url' => '@update_options')) ?>"> 394 * <option value="0">Hello</option> 395 * <option value="1">World</option> 396 * </select> 397 */ 398 function remote_function($options) 399 { 400 sfContext::getInstance()->getResponse()->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); 401 402 $javascript_options = _options_for_ajax($options); 403 404 $update = ''; 405 if (isset($options['update']) && is_array($options['update'])) 406 { 407 $update = array(); 408 if (isset($options['update']['success'])) 409 { 410 $update[] = "success:'".$options['update']['success']."'"; 411 } 412 if (isset($options['update']['failure'])) 413 { 414 $update[] = "failure:'".$options['update']['failure']."'"; 415 } 416 $update = '{'.join(',', $update).'}'; 417 } 418 else if (isset($options['update'])) 419 { 420 $update .= "'".$options['update']."'"; 421 } 422 423 $function = !$update ? "new Ajax.Request(" : "new Ajax.Updater($update, "; 424 425 $function .= '\''.url_for($options['url']).'\''; 426 $function .= ', '.$javascript_options.')'; 427 428 if (isset($options['before'])) 429 { 430 $function = $options['before'].'; '.$function; 431 } 432 if (isset($options['after'])) 433 { 434 $function = $function.'; '.$options['after']; 435 } 436 if (isset($options['condition'])) 437 { 438 $function = 'if ('.$options['condition'].') { '.$function.'; }'; 439 } 440 if (isset($options['confirm'])) 441 { 442 $function = "if (confirm('".escape_javascript($options['confirm'])."')) { $function; }"; 443 if (isset($options['cancel'])) 444 { 445 $function = $function.' else { '.$options['cancel'].' }'; 446 } 447 } 448 449 return $function; 450 } 451 452 /** 453 * Observes the field with the DOM ID specified by '$field_id' and makes 454 * an AJAX call when its contents have changed. 455 * 456 * Required '$options' are: 457 * 'url' 'url_for()'-style options for the action to call 458 * when the field has changed. 459 * 460 * Additional options are: 461 * 'frequency' The frequency (in seconds) at which changes to 462 * this field will be detected. Not setting this 463 * option at all or to a value equal to or less than 464 * zero will use event based observation instead of 465 * time based observation. 466 * 'update' Specifies the DOM ID of the element whose 467 * innerHTML should be updated with the 468 * XMLHttpRequest response text. 469 * 'with' A JavaScript expression specifying the 470 * parameters for the XMLHttpRequest. This defaults 471 * to 'value', which in the evaluated context 472 * refers to the new field value. 473 * 474 * Additionally, you may specify any of the options documented in 475 * link_to_remote(). 476 */ 477 function observe_field($field_id, $options = array()) 478 { 479 sfContext::getInstance()->getResponse()->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); 480 481 if (isset($options['frequency']) && $options['frequency'] > 0) 482 { 483 return _build_observer('Form.Element.Observer', $field_id, $options); 484 } 485 else 486 { 487 return _build_observer('Form.Element.EventObserver', $field_id, $options); 488 } 489 } 490 491 /** 492 * Like 'observe_field()', but operates on an entire form identified by the 493 * DOM ID '$form_id'. '$options' are the same as 'observe_field()', except 494 * the default value of the 'with' option evaluates to the 495 * serialized (request string) value of the form. 496 */ 497 function observe_form($form_id, $options = array()) 498 { 499 sfContext::getInstance()->getResponse()->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); 500 501 if (isset($options['frequency']) && $options['frequency'] > 0) 502 { 503 return _build_observer('Form.Observer', $form_id, $options); 504 } 505 else 506 { 507 return _build_observer('Form.EventObserver', $form_id, $options); 508 } 509 } 510 511 /** 512 * Returns a JavaScript snippet to be used on the AJAX callbacks for starting 513 * visual effects. 514 * 515 * Example: 516 * <?php echo link_to_remote('Reload', array( 517 * 'update' => 'posts', 518 * 'url' => '@reload', 519 * 'complete => visual_effect('highlight', 'posts', array('duration' => 0.5 )), 520 * )) ?> 521 * 522 * If no '$element_id' is given, it assumes "element" which should be a local 523 * variable in the generated JavaScript execution context. This can be used 524 * for example with drop_receiving_element(): 525 * 526 * <?php echo drop_receving_element( ..., array( 527 * ... 528 * 'loading' => visual_effect('fade'), 529 * )) ?> 530 * 531 * This would fade the element that was dropped on the drop receiving element. 532 * 533 * You can change the behaviour with various options, see 534 * http://script.aculo.us for more documentation. 535 */ 536 function visual_effect($name, $element_id = false, $js_options = array()) 537 { 538 $response = sfContext::getInstance()->getResponse(); 539 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); 540 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/builder'); 541 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/effects'); 542 543 $element = $element_id ? "'$element_id'" : 'element'; 544 545 if (in_array($name, array('toggle_appear', 'toggle_blind', 'toggle_slide'))) 546 { 547 return "new Effect.toggle($element, '".substr($name, 7)."', "._options_for_javascript($js_options).");"; 548 } 549 else 550 { 551 return "new Effect.".sfInflector::camelize($name)."($element, "._options_for_javascript($js_options).");"; 552 } 553 } 554 555 /** 556 * Makes the elements with the DOM ID specified by '$element_id' sortable 557 * by drag-and-drop and make an AJAX call whenever the sort order has 558 * changed. By default, the action called gets the serialized sortable 559 * element as parameters. 560 * 561 * Example: 562 * <php echo sortable_element($my_list, array( 563 * 'url' => '@order', 564 * )) ?> 565 * 566 * In the example, the action gets a '$my_list' array parameter 567 * containing the values of the ids of elements the sortable consists 568 * of, in the current order. 569 * 570 * You can change the behaviour with various options, see 571 * http://script.aculo.us for more documentation. 572 */ 573 function sortable_element($element_id, $options = array()) 574 { 575 $response = sfContext::getInstance()->getResponse(); 576 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); 577 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/builder'); 578 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/effects'); 579 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/dragdrop'); 580 581 if (!isset($options['with'])) 582 { 583 $options['with'] = "Sortable.serialize('$element_id')"; 584 } 585 586 if (!isset($options['onUpdate'])) 587 { 588 $options['onUpdate'] = "function(){".remote_function($options)."}"; 589 } 590 591 foreach (get_ajax_options() as $key) 592 { 593 unset($options[$key]); 594 } 595 596 foreach (array('tag', 'overlap', 'constraint', 'handle') as $option) 597 { 598 if (isset($options[$option])) 599 { 600 $options[$option] = "'{$options[$option]}'"; 601 } 602 } 603 604 if (isset($options['containment'])) 605 { 606 $options['containment'] = _array_or_string_for_javascript($options['containment']); 607 } 608 609 if (isset($options['hoverclass'])) 610 { 611 $options['hoverclass'] = "'{$options['hoverclass']}'"; 612 } 613 614 if (isset($options['only'])) 615 { 616 $options['only'] = _array_or_string_for_javascript($options['only']); 617 } 618 619 return javascript_tag("Sortable.create('$element_id', "._options_for_javascript($options).")"); 620 } 621 622 /** 623 * Makes the element with the DOM ID specified by '$element_id' draggable. 624 * 625 * Example: 626 * <?php echo draggable_element('my_image', array( 627 * 'revert' => true, 628 * )) ?> 629 * 630 * You can change the behaviour with various options, see 631 * http://script.aculo.us for more documentation. 632 */ 633 function draggable_element($element_id, $options = array()) 634 { 635 $response = sfContext::getInstance()->getResponse(); 636 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); 637 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/builder'); 638 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/effects'); 639 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/dragdrop'); 640 641 return javascript_tag("new Draggable('$element_id', "._options_for_javascript($options).")"); 642 } 643 644 /** 645 * Makes the element with the DOM ID specified by '$element_id' receive 646 * dropped draggable elements (created by 'draggable_element()') and make an AJAX call. 647 * By default, the action called gets the DOM ID of the element as parameter. 648 * 649 * Example: 650 * <?php drop_receiving_element('my_cart', array( 651 * 'url' => 'cart/add', 652 * )) ?> 653 * 654 * You can change the behaviour with various options, see 655 * http://script.aculo.us for more documentation. 656 */ 657 function drop_receiving_element($element_id, $options = array()) 658 { 659 $response = sfContext::getInstance()->getResponse(); 660 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); 661 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/builder'); 662 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/effects'); 663 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/dragdrop'); 664 665 if (!isset($options['with'])) 666 { 667 $options['with'] = "'id=' + encodeURIComponent(element.id)"; 668 } 669 if (!isset($options['onDrop'])) 670 { 671 $options['onDrop'] = "function(element){".remote_function($options)."}"; 672 } 673 674 foreach (get_ajax_options() as $key) 675 { 676 unset($options[$key]); 677 } 678 679 if (isset($options['accept'])) 680 { 681 $options['accept'] = _array_or_string_for_javascript($options['accept']); 682 } 683 684 if (isset($options['hoverclass'])) 685 { 686 $options['hoverclass'] = "'{$options['hoverclass']}'"; 687 } 688 689 return javascript_tag("Droppables.add('$element_id', "._options_for_javascript($options).")"); 690 } 691 692 /** 693 * Returns a JavaScript tag with the '$content' inside. 694 * Example: 695 * <?php echo javascript_tag("alert('All is good')") ?> 696 * => <script type="text/javascript">alert('All is good')</script> 697 */ 698 function javascript_tag($content) 699 { 700 return content_tag('script', javascript_cdata_section($content), array('type' => 'text/javascript')); 701 } 702 703 function javascript_cdata_section($content) 704 { 705 return "\n//".cdata_section("\n$content\n//")."\n"; 706 } 707 708 /** 709 * wrapper for script.aculo.us/prototype Ajax.Autocompleter. 710 * @param string name value of input field 711 * @param string default value for input field 712 * @param array input tag options. (size, autocomplete, etc...) 713 * @param array completion options. (use_style, etc...) 714 * 715 * @return string input field tag, div for completion results, and 716 * auto complete javascript tags 717 */ 718 function input_auto_complete_tag($name, $value, $url, $tag_options = array(), $completion_options = array()) 719 { 720 $context = sfContext::getInstance(); 721 722 $tag_options = _convert_options($tag_options); 723 724 $response = $context->getResponse(); 725 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); 726 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/effects'); 727 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/controls'); 728 729 $comp_options = _convert_options($completion_options); 730 if (isset($comp_options['use_style']) && $comp_options['use_style'] == true) 731 { 732 $response->addStylesheet(sfConfig::get('sf_prototype_web_dir').'/css/input_auto_complete_tag'); 733 } 734 735 $tag_options['id'] = get_id_from_name(isset($tag_options['id']) ? $tag_options['id'] : $name); 736 737 $javascript = input_tag($name, $value, $tag_options); 738 $javascript .= content_tag('div', '' , array('id' => $tag_options['id'].'_auto_complete', 'class' => 'auto_complete')); 739 $javascript .= _auto_complete_field($tag_options['id'], $url, $comp_options); 740 741 return $javascript; 742 } 743 744 /** 745 * wrapper for script.aculo.us/prototype Ajax.InPlaceEditor. 746 * @param string name id of field that can be edited 747 * @param string url of module/action to be called when ok is clicked 748 * @param array editor tag options. (rows, cols, highlightcolor, highlightendcolor, etc...) 749 * 750 * @return string javascript to manipulate the id field to allow click and edit functionality 751 */ 752 function input_in_place_editor_tag($name, $url, $editor_options = array()) 753 { 754 $response = sfContext::getInstance()->getResponse(); 755 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); 756 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/effects'); 757 $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/controls'); 758 759 $editor_options = _convert_options($editor_options); 760 $default_options = array('tag' => 'span', 'id' => '\''.$name.'_in_place_editor', 'class' => 'in_place_editor_field'); 761 762 return _in_place_editor($name, $url, array_merge($default_options, $editor_options)); 763 } 764 765 /** 766 * Mark the start of a block that should only be shown in the browser if JavaScript 767 * is switched on. 768 */ 769 function if_javascript() 770 { 771 ob_start(); 772 } 773 774 /** 775 * Mark the end of a block that should only be shown in the browser if JavaScript 776 * is switched on. 777 */ 778 function end_if_javascript() 779 { 780 $content = ob_get_clean(); 781 782 echo javascript_tag("document.write('" . esc_js_no_entities($content) . "');"); 783 } 784 785 /* 786 * Makes an HTML element specified by the DOM ID '$field_id' become an in-place 787 * editor of a property. 788 * 789 * A form is automatically created and displayed when the user clicks the element, 790 * something like this: 791 * <form id="myElement-in-place-edit-form" target="specified url"> 792 * <input name="value" text="The content of myElement"/> 793 * <input type="submit" value="ok"/> 794 * <a onclick="javascript to cancel the editing">cancel</a> 795 * </form> 796 * 797 * The form is serialized and sent to the server using an AJAX call, the action on 798 * the server should process the value and return the updated value in the body of 799 * the reponse. The element will automatically be updated with the changed value 800 * (as returned from the server). 801 * 802 * Required '$options' are: 803 * 'url' Specifies the url where the updated value should 804 * be sent after the user presses "ok". 805 * 806 * Addtional '$options' are: 807 * 'rows' Number of rows (more than 1 will use a TEXTAREA) 808 * 'cancel_text' The text on the cancel link. (default: "cancel") 809 * 'save_text' The text on the save link. (default: "ok") 810 * 'external_control' The id of an external control used to enter edit mode. 811 * 'options' Pass through options to the AJAX call (see prototype's Ajax.Updater) 812 * 'with' JavaScript snippet that should return what is to be sent 813 * in the AJAX call, 'form' is an implicit parameter 814 */ 815 function _in_place_editor($field_id, $url, $options = array()) 816 { 817 $javascript = "new Ajax.InPlaceEditor("; 818 819 $javascript .= "'$field_id', "; 820 $javascript .= "'" . url_for($url) . "'"; 821 822 $js_options = array(); 823 824 if (isset($options['tokens'])) $js_options['tokens'] = _array_or_string_for_javascript($options['tokens']); 825 826 if (isset($options['cancel_text'])) 827 { 828 $js_options['cancelText'] = "'".$options['cancel_text']."'"; 829 } 830 if (isset($options['save_text'])) 831 { 832 $js_options['okText'] = "'".$options['save_text']."'"; 833 } 834 if (isset($options['cols'])) 835 { 836 $js_options['cols'] = $options['cols']; 837 } 838 if (isset($options['rows'])) 839 { 840 $js_options['rows'] = $options['rows']; 841 } 842 if (isset($options['external_control'])) 843 { 844 $js_options['externalControl'] = "'".$options['external_control']."'"; 845 } 846 if (isset($options['options'])) 847 { 848 $js_options['ajaxOptions'] = $options['options']; 849 } 850 if (isset($options['with'])) 851 { 852 $js_options['callback'] = "function(form, value) { return ".$options['with']." }"; 853 } 854 if (isset($options['highlightcolor'])) 855 { 856 $js_options['highlightcolor'] = "'".$options['highlightcolor']."'"; 857 } 858 if (isset($options['highlightendcolor'])) 859 { 860 $js_options['highlightendcolor'] = "'".$options['highlightendcolor']."'"; 861 } 862 if (isset($options['loadTextURL'])) 863 { 864 $js_options['loadTextURL'] = "'".$options['loadTextURL']."'"; 865 } 866 867 $javascript .= ', '._options_for_javascript($js_options); 868 $javascript .= ');'; 869 870 return javascript_tag($javascript); 871 } 872 873 /** 874 * wrapper for script.aculo.us/prototype Ajax.Autocompleter. 875 * @param string id value of input field 876 * @param string url of module/action to execute for autocompletion 877 * @param array completion options 878 * @return string javascript tag for Ajax.Autocompleter 879 */ 880 function _auto_complete_field($field_id, $url, $options = array()) 881 { 882 $javascript = "new Ajax.Autocompleter("; 883 884 $javascript .= "'".get_id_from_name($field_id)."', "; 885 if (isset($options['update'])) 886 { 887 $javascript .= "'".$options['update']."', "; 888 } 889 else 890 { 891 $javascript .= "'".get_id_from_name($field_id)."_auto_complete', "; 892 } 893 894 $javascript .= "'".url_for($url)."'"; 895 896 $js_options = array(); 897 if (isset($options['tokens'])) 898 { 899 $js_options['tokens'] = _array_or_string_for_javascript($options['tokens']); 900 } 901 if (isset ($options['with'])) 902 { 903 $js_options['callback'] = "function(element, value) { return".$options['with']."}"; 904 } 905 if (isset($options['indicator'])) 906 { 907 $js_options['indicator'] = "'".$options['indicator']."'"; 908 } 909 if (isset($options['on_show'])) 910 { 911 $js_options['onShow'] = $options['on_show']; 912 } 913 if (isset($options['on_hide'])) 914 { 915 $js_options['onHide'] = $options['on_hide']; 916 } 917 if (isset($options['min_chars'])) 918 { 919 $js_options['minChars'] = $options['min_chars']; 920 } 921 if (isset($options['frequency'])) 922 { 923 $js_options['frequency'] = $options['frequency']; 924 } 925 if (isset($options['update_element'])) 926 { 927 $js_options['updateElement'] = $options['update_element']; 928 } 929 if (isset($options['after_update_element'])) 930 { 931 $js_options['afterUpdateElement'] = $options['after_update_element']; 932 } 933 934 $javascript .= ', '._options_for_javascript($js_options).');'; 935 936 return javascript_tag($javascript); 937 } 938 939 function _options_for_javascript($options) 940 { 941 $opts = array(); 942 foreach ($options as $key => $value) 943 { 944 $opts[] = "$key:$value"; 945 } 946 sort($opts); 947 948 return '{'.join(', ', $opts).'}'; 949 } 950 951 function _array_or_string_for_javascript($option) 952 { 953 if (is_array($option)) 954 { 955 return "['".join('\',\'', $option)."']"; 956 } 957 else if ($option) 958 { 959 return "'$option'"; 960 } 961 } 962 963 function _options_for_ajax($options) 964 { 965 $js_options = _build_callbacks($options); 966 967 $js_options['asynchronous'] = (isset($options['type']) && ($options['type'] == 'synchronous')) ? 'false' : 'true'; 968 if (isset($options['method'])) $js_options['method'] = _method_option_to_s($options['method']); 969 if (isset($options['position'])) $js_options['insertion'] = "Insertion.".sfInflector::camelize($options['position']); 970 $js_options['evalScripts'] = (!isset($options['script']) || $options['script'] == '0' || $options['script'] == false) ? 'false' : 'true'; 971 972 if (isset($options['form'])) 973 { 974 $js_options['parameters'] = 'Form.serialize(this)'; 975 } 976 else if (isset($options['submit'])) 977 { 978 $js_options['parameters'] = "Form.serialize(document.getElementById('{$options['submit']}'))"; 979 } 980 else if (isset($options['with'])) 981 { 982 $js_options['parameters'] = $options['with']; 983 } 984 985 return _options_for_javascript($js_options); 986 } 987 988 function _method_option_to_s($method) 989 { 990 return (is_string($method) && $method[0] != "'") ? "'$method'" : $method; 991 } 992 993 function _build_observer($klass, $name, $options = array()) 994 { 995 if (!isset($options['with']) && $options['update']) 996 { 997 $options['with'] = 'value'; 998 } 999 1000 $callback = remote_function($options); 1001 1002 $javascript = 'new '.$klass.'("'.$name.'", '; 1003 if (isset($options['frequency']) && $options['frequency'] > 0) 1004 { 1005 $javascript .= $options['frequency'].", "; 1006 } 1007 $javascript .= "function(element, value) {"; 1008 $javascript .= $callback.'});'; 1009 1010 return javascript_tag($javascript); 1011 } 1012 1013 function _build_callbacks($options) 1014 { 1015 $callbacks = array(); 1016 foreach (get_callbacks() as $callback) 1017 { 1018 if (isset($options[$callback])) 1019 { 1020 $name = 'on'.ucfirst($callback); 1021 $code = $options[$callback]; 1022 $callbacks[$name] = 'function(request, json){'.$code.'}'; 1023 } 1024 } 1025 1026 return $callbacks; 1027 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Fri Mar 16 22:42:14 2007 | par Balluche grâce à PHPXref 0.7 |