/[drupal]/contributions/modules/spajax/spajax.module
ViewVC logotype

Contents of /contributions/modules/spajax/spajax.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.5 - (show annotations) (download) (as text)
Sun Mar 19 13:34:39 2006 UTC (3 years, 8 months ago) by jjeff
Branch: MAIN
CVS Tags: HEAD
Changes since 1.4: +1 -4 lines
File MIME type: text/x-php
more comments stuff removed.
1 <?php
2 // $Id: spajax.module,v 1.4 2006/03/19 13:31:59 jjeff Exp $
3
4 // spajax.module for Drupal
5 // Script.aculo.us/Prototype/Ajax Module by Jeff Robbins
6
7 /**
8 * @abstract
9 * This module implements the Prototype and Scriptaculous AJAX libraries
10 * for use in Drupal
11 *
12 * It adds form element types and themeing functions to display several
13 * types of AJAX and DHTML elements on the page.
14 *
15 * Prototype functions available:
16 * http://wiki.script.aculo.us/scriptaculous/show/Prototype
17 *
18 * Other documentation:
19 * http://particletree.com/features/quick-guide-to-prototype/
20 * http://www.sergiopereira.com/articles/prototype.js.html
21 *
22 * (eventually) see help for more info
23 *
24 * @todo
25 * - add option to output JS in header (cleaner) or footer (more compatible)
26 * - move Menu Magic css and js into that module's dir
27 *
28 */
29
30 /**
31 * Thoughts, ideas, todo:
32 *
33 * - run sortable form element through theme('form_element') - can't get it to work...
34 * - make a "slider" form element
35 * - work on u.i. for settings page
36 * - only list active themes
37 * - make default position for floating menus
38 * - in-place editing of node content
39 *
40 * floating menus
41 * - make sure that floating menu is within page boundaries
42 * - allow resizable width
43 * - system for floating local tasks
44 * - float other blocks?
45 * - privileges for floating blocks?
46 *
47 *
48 */
49
50
51 /******************* Drupal hooks ***********************/
52
53 /**
54 * Implementation of hook_help()
55 */
56 function spajax_help($section){
57 switch($section){
58 case 'admin/modules#name':
59 return 'script.aculo.us/prototype/ajax';
60 case 'admin/modules#description':
61 return t('Adds ajax and dhtml functions using the script.aculo.us library');
62 }
63 }
64
65 function spajax_menu($may_cache){
66 $items = array();
67 if($may_cache){
68 // settings
69 $items[] = array('path' => 'admin/settings/spajax', 'access' => user_access('administer site configuration'), 'title' => 's/p ajax', 'callback' => 'spajax_settings_page');
70 $items[] = array('path' => 'admin/settings/spajax/settings', 'access' => user_access('administer site configuration'), 'title' => 'general', 'type' => MENU_DEFAULT_LOCAL_TASK);
71
72 // ajax callbacks
73 // access is TRUE on these so that there will be no Access Denied page returned
74 // but these callback functions should be sure to check access if necessary
75 $items[] = array('path' => 'spajax/set', 'access' => TRUE, 'title' => 'spajax set', 'callback' => 'spajax_set', 'type' => MENU_CALLBACK);
76
77 }
78 else{
79 // check for messages at the beginning of each page call
80 spajax_is_message();
81 }
82 return $items;
83 }
84
85 /**
86 * Implementation of hook_elements
87 * Custom form elements using Prototype/Scriptaculous
88 *
89 * @return unknown
90 */
91 function spajax_elements(){
92 // a Prototype version of autocomplete
93 $type['spajax_autocomplete'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128, '#autocomplete_path' => FALSE);
94
95 $type['sortable'] = array(
96 '#input' => TRUE,
97 '#process' => array('spajax_form_sortable' => array()),
98 '#items' => array(),
99 '#outer_tag' => 'div',
100 '#inner_tag' => 'div',
101 '#options' => array(),
102 );
103
104 return $type;
105 }
106
107 /**
108 * Implementation of hook_footer()
109 * Add JS to page header after the rest of the page has been rendered
110 *
111 * @todo
112 * Check that this works with all theme engines
113 */
114 function spajax_footer(){
115 //spajax_all_pages();
116
117 // invoke 'spajax' hook in each module
118 module_invoke_all('spajax');
119
120 $spajax_output = spajax_output();
121 if(!empty($spajax_output)) {
122 spajax_create_function('spajax_init', $spajax_output);
123 }
124
125 // there's no "header" equivalent of hook_footer(), so....
126 // (this may be getting called too late with some theme engines)
127 $head_scripts = spajax_create_function();
128 if (!empty($head_scripts)){
129 $head = <<<EOT
130 <script type="text/javascript">
131 // <![CDATA[
132 Event.observe(window, 'load', spajax_init);
133 $head_scripts
134 // ]]>
135 </script>
136 EOT;
137 drupal_set_html_head($head);
138 }
139
140 // and then the footer itself
141 /*
142 if (!empty($spajax_output)) {
143 $output = <<<EOT
144 <script type="text/javascript">
145 // <![CDATA[
146 spajax_init();
147 // ]]>
148 </script>
149 EOT;
150
151 return $output;
152 }*/
153 }
154
155 /**
156 * Implementation of hook_form_alter()
157 *
158 * This should really be a part of core, but we're going to ensure that all form tags
159 * are written to the page using <form id="form_id"... so that we can target them easily
160 * using javascript
161 *
162 */
163
164 function spajax_form_alter($form_id, &$form_values){
165 if (!isset($form_values['#attributes']['id'])){
166 $form_values['#attributes']['id'] = $form_id;
167 }
168 }
169
170 function spajax_settings_page() {
171 drupal_set_title(t('Scriptaculous/Prototype/AJAX Settings'));
172 $form['fx'] = array('#type' => 'fieldset', '#title' => t('Effects'));
173 $fx_options = variable_get('spajax_fx_options', array('' => 'none', 'Pulsate' => 'Pulsate', 'Shake' => 'Shake', 'Highlight' => 'Highlight'));
174 $form['fx']['status_effect'] = array(
175 '#type' => 'select',
176 '#title' => t('Status Message Effect'),
177 '#default_value' => variable_get('status_effect', 'Pulsate'),
178 '#options' => $fx_options,
179 '#description' => t('Scriptaculous effect added to Drupal status messages and triggered when page loads'),
180 );
181 $form['fx']['error_effect'] = array(
182 '#type' => 'select',
183 '#title' => t('Error Message Effect'),
184 '#default_value' => variable_get('error_effect', 'Pulsate'),
185 '#options' => $fx_options,
186 '#description' => t('Effect added to Drupal error messages and triggered when page loads'),
187 );
188 $form['#tree'] = TRUE;
189
190 $form[] = array('#type' => 'submit', '#value' => t('Submit'));
191 return drupal_get_form('spajax_settings', $form);
192 }
193
194 function spajax_settings_submit($form_id, $form_values){
195 //drupal_set_message('<pre>'. print_r($form_values, true) .'</pre>');
196 variable_set('status_effect', $form_values['fx']['status_effect']);
197 variable_set('error_effect', $form_values['fx']['error_effect']);
198 drupal_set_message(t('Settings have been saved.'));
199 // reload the page with the new variables
200 drupal_goto('admin/settings/spajax');
201 }
202
203 /**************** Custom form elements *********************/
204
205 /**
206 * Expansion function for 'sortable' type form element
207 *
208 * extra arguments:
209 *
210 * #items => array()
211 * required - Keyed array of items to be sorted.
212 * Keys will be returned in sorted order.
213 * Values can be anything, but they will get wrapped in <li> elements
214 *
215 * #options => array()
216 * options to modify the behaviour of the sorting
217 * see: http://wiki.script.aculo.us/scriptaculous/show/Sortable.create
218 *
219 * #outer_tag => 'div'
220 * #inner_tag => 'div'
221 * type of tag to use for sortables (could also be 'ul' and 'li'... or anything)
222 *
223 * @param array $element
224 * the array representing the sortable element
225 */
226 function spajax_form_sortable($element){
227 //var_dump($element);
228 static $i = 1;
229 $innertag = $element['#inner_tag'];
230 $outertag = $element['#outer_tag'];
231 //$outerid = $element['#attributes']['id'] ? $element['#attributes']['id'] : 'sortable'.$i++;
232 $outerid = $element['#parents'][0];
233
234 $sortables['#prefix'] = "<$outertag id='$outerid'>\n";
235 $sortables['#suffix'] = "</$outertag>\n";
236
237 // if there's a #value, eval it and resort
238 if ($element['#value']){
239 $keyorder = spajax_get_sort($element['#value']);
240 $items = $element['#items'];
241 foreach($keyorder as $key){
242 $newitems[$key] = $items[$key];
243 }
244 $element['#items'] = $newitems;
245 unset($element['#value']);
246 }
247
248 foreach($element['#items'] as $k => $v){
249 $sortables[$k] = array('#type' => 'markup', '#value' => $v, '#prefix' => "<$innertag id='{$outerid}_{$k}' class='sortable'>", '#suffix' => "</$innertag>\n");
250 $order[] = $k;
251 }
252 $value = $outerid.'[]='.implode('&'.$outerid.'[]=', $order);
253 $element['#suffix'] .= '<input type="hidden" name="'. $element['#name'] .'" value="'. $value .'" id="'.$element['#id'].'" />';
254
255 $element['#options']['tag'] = $innertag;
256
257 if (!isset($element['#callback_path'])){
258 // store in a hidden field
259 $element['#options']['#onUpdate'] = "function(){\$('". $element['#id'] ."').value = Sortable.serialize('$outerid');}";
260 }
261 else {
262 // immediately send to the server
263 $element['#options']['#onUpdate'] = "function(){\$('". $element['#id'] ."').value = Sortable.serialize('$outerid');}";
264 }
265
266 spajax_sortable_element($outerid, $element['#options']);
267
268 $element['#type'] = 'markup';
269 $element[] = $sortables;
270
271 // to do: get theme function working to add <label> and description
272 // $element['#theme'] = 'sortable';
273
274 return $element;
275
276 }
277
278
279 /**
280 * Convert form output from spajax_form_sortable into an array
281 *
282 * Prototype's sortable output is designed as a GET argument (e.g. ?sort[]=second&sort[]=thrid&sort[]=first)
283 *
284 * This function converts the output of the hidden field into an array
285 *
286 * note: maybe this function should be combined with spajax_unserialize();
287 *
288 * @param string $sort
289 * @return array
290 */
291
292 function spajax_get_sort($sort){
293 parse_str($sort, $result);
294 $return = array_shift($result);
295 $return = is_array($return) ? $return : array();
296 return $return;
297 }
298
299 /**
300 * Decrypt data serialized by prototype's Form.serialize() javascript function
301 *
302 * This function presumes that it is unserializing Drupal form elements
303 * such as edit[foo]=bar&edit[foo2]=bar2
304 * it will not work for foo=bar&foo2=bar2 -- currently commented out
305 *
306 * @param $string
307 * the serialized string
308 */
309
310 function spajax_unserialize($string){
311 $string = urldecode($string);
312 parse_str($string, $result);
313 //$return = array_shift($result); // un-nest the array
314 //$return = is_array($return) ? $return : array();
315 return $return;
316 }
317
318 /**
319 * Stores javascript snippets to be output in a javascript block at end of page
320 *
321 * @param unknown_type $content
322 */
323 function spajax_output($content = NULL){
324 static $scripts = array();
325 if (!is_null($content)){
326 $scripts[] = $content;
327 }
328 else {
329 return implode("\n", $scripts);
330 }
331 }
332
333 /**
334 * Adds functions into the head that can be called later
335 * functions will appear in one <script> in the <head>
336 *
337 * // would it just be easier to take the whole function?
338 * // this may be overly complicated
339 *
340 * @param string $function_name
341 * the name of the function
342 * @param string $function
343 * the code of the function
344 * @param array $arguments
345 * an array of argument variable names for the function
346 * @return unknown
347 */
348 function spajax_create_function($function_name = NULL, $function = NULL, $arguments = NULL){
349 static $functions = array();
350 if (!is_null($function_name)){
351 $args = is_null($arguments) ? '' : implode(', ', $arguments);
352 $functions[$function_name] = "function $function_name($args){\n".$function."\n}";
353 }
354 else {
355 return implode("\n", $functions);
356 }
357 }
358
359 /**
360 * Put necessary CSS and JS into <HEAD>
361 *
362 * This function is called whenever a Prototype-dependent function is called.
363 * This way
364 *
365 *
366 */
367 function spajax_head(){
368 static $done = FALSE;
369 if (!$done){
370 $path = drupal_get_path('module', 'spajax').'/';
371 theme_add_style($path .'spajax.css');
372 if (file_exists($path .'scriptaculous/lib/prototype.js')) {
373 drupal_add_js($path .'scriptaculous/lib/prototype.js');
374 drupal_add_js($path .'scriptaculous/src/scriptaculous.js');
375 drupal_add_js($path .'spajax.js');
376 }
377 else {
378 drupal_set_message(t('The script.aculo.us library is in not installed correctly. Please download from <a href="http://script.aculo.us/downloads">http://script.aculo.us/downloads</a>, rename directory to "scriptaculous" and place in Drupal\'s modules/spajax/ directory.'), 'error');
379 }
380 $done = TRUE;
381 }
382 }
383
384 /**
385 * Implementation of hook_spajax
386 * Adds effects to page if there is a message.
387 *
388 * This hook takes no arguments and returns no value
389 * Instead, implementations should use spajax_output() to add their output
390 * to the JavaScript written to the page by the SPAjax module.
391 *
392 */
393 function spajax_spajax(){
394 // only load things in if there are messages in the queue
395 if (spajax_is_message()){
396 $status_effect = variable_get('status_effect', 'Pulsate');
397 if ($status_effect){
398 $function = "msgs = document.getElementsByClassName('status');\n";
399 $function .= "for (i=0; i<msgs.length; i++) {\n";
400 $function .= "element = msgs[i];\n";
401 $function .= spajax_visual_effect($status_effect).";\n";
402 $function .= "}";
403 spajax_create_function('status_effect', $function);
404 spajax_output("status_effect();");
405 }
406 $error_effect = variable_get('error_effect', 'Pulsate');
407 if ($error_effect){
408 $function = "msgs = document.getElementsByClassName('error');\n";
409 $function .= "for (i=0; i<msgs.length; i++) {\n";
410 $function .= "element = msgs[i];\n";
411 $function .= spajax_visual_effect($error_effect).";\n";
412 $function .= "}";
413 spajax_create_function('error_effect', $function);
414 spajax_output("error_effect();");
415 }
416 }
417 }
418
419 /**
420 * Create a sortable list from an array of elements (divs, form fields, whatever)
421 *
422 * @param
423 * $items is an array of elements - keys are ids for the elements
424 * $title is a title for the list
425 * $options is a keyed array of options for sortable.create options
426 * see: http://wiki.script.aculo.us/scriptaculous/show/Sortable.create
427 * $ul_attributes are attributes for the ul
428 * $li_attributes are attributes for the lis
429 * $sortoptions is an array of options for the javascript behaviour
430 * $description (not yet implemented)
431 *
432 */
433 function theme_sortable_list($items, $title = '', $options = array(), $ul_attributes = array(), $li_attributes = array(), $description = ''){
434 // we're going to need the CSS and the JS
435 spajax_head();
436
437 // id for non-keyed items without an $attributes['id'] item
438 static $assignid = 'a';
439 // id counter
440 $idc = 1;
441
442 // figure out the id for the ul
443 if (!isset($ul_attributes['id'])){
444 $ul_attributes['id'] = $assignid;
445 }
446 if (strlen($title)){
447 $output .= "<h2>$title</h2>\n";
448 }
449 $output .= '<ul'.drupal_attributes($ul_attributes).">\n";
450
451 $li_attr = $li_attributes;
452 unset($li_attributes['id']);
453 if (!isset($li_attributes['class'])){
454 $li_attributes['class'] = "sortable";
455 }
456 $li_attrstring = drupal_attributes($li_attributes);
457 foreach($items as $key => $item){
458 // figure out what the id is going to be
459 if (!is_numeric($key)){
460 $this_id = $key;
461 }
462 else {
463 $this_id = $li_attributes['id'] ? $li_attributes['id'].$idc++ : $assignid.$idc++;
464 }
465 $output .= "<li id=\"$this_id\"$li_attrstring>";
466 $output .= $item;
467 $output .= "</li>\n";
468 }
469 $output .= "</ul>\n";
470
471 spajax_sortable_element($ul_attributes['id'], $options);
472
473 $assignid++;
474 return $output;
475 }
476
477 /************* Prototype/Scriptaculous implementations **************/
478
479 /**
480 * Makes the element with the DOM ID specified by element_id draggable.
481 * This method requires the inclusion of the script.aculo.us JavaScript library.
482 * Example:
483 * spajax_draggable_element("my_image", array('revert' => true))
484 *
485 * No return output. Instead, code is added for spajax_output() in the footer.
486 *
487 * @param string $element_id
488 * id of the element that will become draggable
489 * @param array $options
490 * array of options to modify behaviour
491 * see: http://wiki.script.aculo.us/scriptaculous/show/Draggable
492 */
493 function spajax_draggable_element($element_id, $options = NULL){
494 spajax_head();
495 $output = "new Draggable('$element_id', ".spajax_to_js($options).");\n";
496 // add 'draggable' css class to the element
497 if (isset($options['handle'])){
498 // handle is a element id
499 $output .= "\$('". $options['handle']. "').className += ' draggable';";
500 }
501 elseif (isset($options['#handle'])) {
502 // handle is a variable or element reference
503 $output .= $options['#handle']. ".className += ' draggable';";
504 }
505 else {
506 // no handle
507 $output .= "\$('$element_id').className += ' draggable';";
508 }
509 spajax_output($output);
510 }
511
512 /**
513 * Makes the element with the DOM ID specified by element_id receive dropped
514 * draggable elements (created by draggable_element). and make an AJAX call
515 * By default, the action called gets the DOM ID of the element as parameter.
516 * This method requires the inclusion of the script.aculo.us JavaScript library.
517 *
518 * @example:
519 * spajax_drop_receiving_element("my_cart", url => { :controller => "cart", :action => "add" })
520 * [[[fix above]]]
521 *
522 * You can change the behaviour with various options, see script.aculo.us
523 * for more documentation.
524 *
525 * No return output. Instead, code is added for spajax_output() in the footer.
526 *
527 * @param string $element_id
528 * @param array $options
529 * see: http://wiki.script.aculo.us/scriptaculous/show/Droppables.add
530 */
531
532 function spajax_drop_receiving_element($element_id, $options = NULL){
533 spajax_head();
534 $output = "Droppables.add('$element_id', ".spajax_to_js($options).");";
535 spajax_output($output);
536 }
537
538
539 /**
540 * Make a slider control
541 *
542 * http://wiki.script.aculo.us/scriptaculous/show/Slider
543 *
544 * @param string $handle_id
545 * id of the element that will be the handle for the slider
546 * @param string $track_id
547 * id of the element that will be the track of the slider
548 * @param array $options
549 * options to modify behaviour of the slider
550 * see: http://wiki.script.aculo.us/scriptaculous/show/Slider
551 */
552
553 function spajax_slider($handle_id, $track_id, $options = NULL){
554 spajax_head();
555 $output = "new Control.Slider('$handle_id', '$track_id', ".spajax_to_js($options).");";
556 spajax_output($output);
557 }
558
559 /**
560 * Makes the element with the DOM ID specified by element_id sortable by
561 * drag-and-drop and make an AJAX call whenever the sort order has changed.
562 * By default, the action called gets the serialized sortable element as parameters.
563 * This method requires the inclusion of the script.aculo.us JavaScript library.
564 *
565 * @Example:
566 * spajax_sortable_element("my_list", array('action' => 'order'))
567 *
568 * In the example, the action gets a "my_list" array parameter containing the values
569 * of the ids of elements the sortable consists of, in the current order.
570 *
571 * You can change the behaviour with various options, see script.aculo.us for more documentation.
572 *
573 * No return output. Instead, code is added for spajax_output() in the footer.
574 *
575 * @param string $element_id
576 * @param array $options
577 */
578
579 function spajax_sortable_element($element_id, $options = array()){
580 spajax_head();
581 $output = "Sortable.create('$element_id', ".spajax_to_js($options).");";
582 if (isset($options['handle'])){
583 // handle is a element id
584 $output .= "\$(". $options['handle']. ").className += ' sortable'";
585 }
586 elseif (isset($options['#handle'])) {
587 // handle is a variable or element reference
588 $output .= $options['#handle']. ".className += ' sortable'";
589 }
590 else {
591 // no handle
592 $output .= "\$('$element_id').className += ' sortable'";
593 }
594 spajax_output($output);
595 }
596
597 //$name = sortable($title, array('value' => "some stuff", 'value2' => 'other stuff'));
598
599 /**
600 * Returns a JavaScript snippet to be used on the AJAX callbacks for
601 * starting visual effects.
602 * This method requires the inclusion of the script.aculo.us JavaScript library.
603 *
604 * @example:
605 * spajax_link_to_function('blind up', spajax_visual_effect('BlindUp', 'blinddiv'));
606 *
607 * If no element_id is given, it assumes "element" which should be a local
608 * variable in the generated JavaScript execution context. This can be used
609 * for example with drop_receiving_element:
610 *
611 * spajax_drop_receving_element (...), :loading => visual_effect('fade')
612 *
613 * This would fade the element that was dropped on the drop receiving element.
614 *
615 * You can change the behaviour with various options, see script.aculo.us
616 * for more documentation.
617 *
618 * http://wiki.script.aculo.us/scriptaculous/show/CoreEffects
619 * http://wiki.script.aculo.us/scriptaculous/show/CombinationEffects
620 *
621 * @todo
622 * - option for using effects queue:
623 * http://www.railsdevelopment.com/2006/01/15/effectqueue/
624 *
625 * @param string $name
626 * name of the effect (see script.aculo.us
627 * @param string $element_id
628 * id of element to effect
629 * @param array $options
630 * options for the effect - see docs:
631 *
632 * @return string
633 * the function
634 */
635
636 function spajax_visual_effect($name, $element_id = FALSE, $options = NULL){
637 spajax_head();
638 $element_id = $element_id ? "'".$element_id."'" : 'element';
639 $output = "new Effect.$name($element_id,".spajax_to_js($options).")";
640 return $output;
641 }
642
643 /**
644 * Return a function to build (nested) DOM element(s)
645 *
646 * @param string $tag_name
647 * tag name of the element to be built
648 * @param keyed array or object $attributes
649 * attributes for the tag
650 * @param array $children
651 * (non keyed) array of children for this element
652 * @return string
653 *
654 * Notes:
655 * When specifying the class attribute for the node, use className to circumvent a Firefox bug.
656 * This can not be used to create TR or TD elements
657 *
658 * see also: http://wiki.script.aculo.us/scriptaculous/show/Builder
659 *
660 */
661
662 function spajax_build($tag_name, $attributes, $children){
663 // Builder.node( elementName, attributes, children )
664 spajax_head();
665 $output = "Builder.node('$tag_name',".spajax_to_js($attributes).", ".spajax_to_js($children).");";
666 return $output;
667 }
668
669 /*
670 function spajax_append_to_element($element_id, $tag_name, $attributes, $children){
671 spajax_head();
672 $output = "new Effect.$name('$element_id',".spajax_to_js($options).");";
673 return $output;
674 }
675 */
676
677 /**
678 * Insert HTML before, after, or inside an element
679 *
680 * @param string $target_element
681 * id of element to insert before/after/inside
682 * @param string $html
683 * html or text to insert
684 * @param string $location
685 * possible values:
686 * Before - insert before the element
687 * After - insert after the element
688 * Top - insert inside at the top
689 * Bottom - insert inside at the bottom
690 */
691
692 function spajax_insert_html($target_element, $html, $location = 'after'){
693 spajax_head();
694 $html = addslashes($html);
695 $html = str_replace("\n", '\n', $html);
696 $output = "new Insertion.$location('$target_element', '$html');";
697 return $output;
698 }
699
700
701 function spajax_observe($element, $name, $observer_func){
702 return "Event.observe('$element', );";
703 }
704
705 /**
706 * Enable an element for in-place editing
707 *
708 * @param string $element_id
709 * id of the element to be edited
710 * @param string $path
711 * Drupal path for callback (example: "mymodule/callback")
712 * @param array $options
713 * An array of options to change the behaviour - see docs:
714 * http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
715 *
716 */
717
718 function spajax_in_place_editor($element_id, $path, $options = NULL){
719 spajax_head();
720 $url = url($path, NULL, NULL, TRUE); // absolute because JS may not read the base href
721 $output = "new Ajax.InPlaceEditor('$element_id', '$url', ".spajax_to_js($options).");";
722 spajax_output($output);
723 }
724
725 /**
726 * Make a textfield autocomplete
727 *
728 * @param string $textfield_id
729 * id of the textfield
730 * @param string $div_id
731 * id of the div that gets populated
732 * @param string $path
733 * Drupal menu path for the callback function to return autocomplete items
734 * @param array $options
735 * options to modify behaviour of the autocompleter
736 * see: http://wiki.script.aculo.us/scriptaculous/show/Ajax.Autocompleter
737 */
738
739 function spajax_autocompleter($textfield_id, $div_id, $path, $options = NULL){
740 spajax_head();
741 $url = url($path, NULL, NULL, TRUE); // absolute because JS may not read the base href
742 $output = "new Ajax.Autocompleter('$textfield_id', '$div_id', '$url', ".spajax_to_js($options).");";
743 spajax_output($output);
744 }
745
746
747 function spajax_ajax_request($path, $options = NULL){
748 $url = url($path, NULL, NULL, TRUE);
749 $opts = spajax_to_js($options);
750 $output = "new Ajax.Request('$url', $opts)";
751 return $output;
752 }
753
754 /******************** Helper functions **************************/
755
756 /**
757 * Returns a link thatÕll trigger a javascript function using the onclick
758 * handler and return false after the fact.
759 *
760 * @examples:
761 * spajax_link_to_function("Greeting", "alert('Hello world!')")
762 * spajax_link_to_function(theme('image', 'delete.png'), "if confirm('Really?'){ do_delete(); }")
763 *
764 * @param html $linkhtml
765 * the link (text, image, etc) itself
766 * @param string $function
767 * the javascript function to be run when the link is clicked
768 * @param string $fallback
769 * the href of the link. non-javascript will follow this link.
770 * @param array $attributes
771 * an array of html attributes
772 *
773 * @return html string representing the link
774 */
775
776 function spajax_link_to_function($linkhtml, $function, $fallback = '#', $attributes = array()){
777 spajax_head();
778 $fallback = ($fallback == '#') ? $_GET['q'].'#' : $fallback;
779 $attributes = array_merge($attributes, array('onclick' => $function .'; return false;'));
780 return l($linkhtml, $fallback, $attributes, NULL, NULL, FALSE, TRUE);
781 }
782
783 /**
784 * This callback receives an ajax call with POST information
785 * it looks through the $_POST['vars'] array and saves each into
786 * the user's session for retrieval using the spajax_get() function
787 *
788 */
789
790 function spajax_set(){
791 if ($_POST['vars']){
792 if(is_array($_POST['vars'])){
793 foreach($_POST['vars'] as $block_id => $pos)
794 $_SESSION['proto_vars'][$block_id] = $pos;
795 }
796 }
797 }
798
799 function spajax_get($type, $uid = NULL){
800 return $_SESSION['proto_vars'][$type];
801 }
802
803 /**
804 * This function tests to see if there is a drupal message
805 * and stores it for later
806 *
807 * workaround for checking drupal_set_message() in the footer
808 * because by that point it has been cleared out
809 *
810 * @return unknown
811 */
812
813 function spajax_is_message(){
814 static $bool;
815 if (!isset($bool)){
816 $bool = !is_null(drupal_set_message());
817 }
818 return $bool;
819 }
820
821 /******************** Core rewrites *************************/
822
823 /**
824 * Modification of drupal_to_js()
825 * adds support for anonymous arrays & function (non-quoted) strings
826 *
827 * prefix key with # to prevent quoting of value
828 *
829 * @example
830 * spajax_to_js(
831 * array('containment' => array('element_1', 'element_2'), '#onUpdate' => 'function(){alert("hi");}'));
832 * returns:
833 * {containment: ['element_1', 'element_2'], onUpdate: function(){alert("hi");}}
834 *
835 * [[[^^^check that^^^]]]
836 *
837 * @param mixed $var
838 * any type of variable
839 *
840 * @param string $key
841 * used internally
842 *
843 * @return string
844 * a javascript representation of the variable
845 */
846 function spajax_to_js($var, $key = NULL) {
847 switch (gettype($var)) {
848 case 'boolean':
849 case 'integer':
850 case 'double':
851 return $var;
852 case 'resource':
853 case 'string':
854 // if the key for this variable starts with a #, don't quote it
855 if ($key{0} == '#') {
856 $output = $var;
857 }
858 else {
859 $output = str_replace(array("\r", "\n"), array('\r', '\n'), addslashes($var));
860 $output = '"'. $output .'"';
861 }
862 return $output;
863 case 'array':
864 //check to see if there are non-numeric keys
865 $keys = false;
866 foreach(array_keys($var) as $key){
867 if(!is_numeric($key)){
868 $keys = true;
869 break;
870 }
871 }
872 if (!$keys) {
873 foreach($var as $v) {
874 $output[] = spajax_to_js($v);
875 }
876 return '[ '.implode(', ', $output) .' ]';
877 }
878 // if it's an array with keys, continue to object treatment
879 case 'object':
880 $output = array();
881 foreach ($var as $k => $v) {
882 if ($k{0} == '#'){
883 $ok = substr($k, 1);
884 }
885 else {
886 $ok = $k;
887 }
888 $output[] = $ok .': '. spajax_to_js($v, $k);
889 }
890 return '{ '. implode(', ', $output) .' }';
891 default:
892 return 'null';
893 }
894 }
895
896 ?>

  ViewVC Help
Powered by ViewVC 1.1.2