/[drupal]/drupal/misc/ajax.js
ViewVC logotype

Contents of /drupal/misc/ajax.js

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


Revision 1.5 - (show annotations) (download) (as text)
Wed Nov 4 04:38:30 2009 UTC (3 weeks, 2 days ago) by webchick
Branch: MAIN
CVS Tags: DRUPAL-7-0-UNSTABLE-10
Changes since 1.4: +2 -1 lines
File MIME type: text/javascript
#615504 by rfay and katbailey: Fixed Ajax framework's use-ajax class on links.
1 // $Id: ajax.js,v 1.4 2009/11/03 05:34:37 webchick Exp $
2 (function ($) {
3
4 /**
5 * Provides AJAX page updating via jQuery $.ajax (Asynchronous JavaScript and XML).
6 *
7 * AJAX is a method of making a request via Javascript while viewing an HTML
8 * page. The request returns an array of commands encoded in JSON, which is
9 * then executed to make any changes that are necessary to the page.
10 *
11 * Drupal uses this file to enhance form elements with #ajax['path'] and
12 * #ajax['wrapper'] properties. If set, this file will automatically be included
13 * to provide AJAX capabilities.
14 */
15
16 Drupal.ajax = Drupal.ajax || {};
17
18 /**
19 * Attaches the AJAX behavior to each AJAX form element.
20 */
21 Drupal.behaviors.AJAX = {
22 attach: function (context, settings) {
23 // Load all AJAX behaviors specified in the settings.
24 for (var base in settings.ajax) {
25 if (!$('#' + base + '.ajax-processed').length) {
26 var element_settings = settings.ajax[base];
27
28 $(element_settings.selector).each(function () {
29 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
30 });
31
32 $('#' + base).addClass('ajax-processed');
33 }
34 }
35
36 // Bind AJAX behaviors to all items showing the class.
37 $('.use-ajax:not(.ajax-processed)').addClass('ajax-processed').each(function () {
38 var element_settings = {};
39
40 // For anchor tags, these will go to the target of the anchor rather
41 // than the usual location.
42 if ($(this).attr('href')) {
43 element_settings.url = $(this).attr('href');
44 element_settings.event = 'click';
45 }
46 var base = $(this).attr('id');
47 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
48 });
49
50 // This class means to submit the form to the action using AJAX.
51 $('.use-ajax-submit:not(.ajax-processed)').addClass('ajax-processed').each(function () {
52 var element_settings = {};
53
54 // AJAX submits specified in this manner automatically submit to the
55 // normal form action.
56 element_settings.url = $(this.form).attr('action');
57 element_settings.set_click = TRUE;
58
59 var base = $(this).attr('id');
60 Drupal.ajax[base] = new Drupal.ajax(base, this, element_settings);
61 });
62 }
63 };
64
65 /**
66 * AJAX object.
67 *
68 * All AJAX objects on a page are accessible through the global Drupal.ajax
69 * object and are keyed by the submit button's ID. You can access them from
70 * your module's JavaScript file to override properties or functions.
71 *
72 * For example, if your AJAX enabled button has the ID 'edit-submit', you can
73 * redefine the function that is called to insert the new content like this
74 * (inside a Drupal.behaviors attach block):
75 * @code
76 * Drupal.behaviors.myCustomAJAXStuff = {
77 * attach: function (context, settings) {
78 * Drupal.ajax['edit-submit'].commands.insert = function (ajax, response, status) {
79 * new_content = $(response.data);
80 * $('#my-wrapper').append(new_content);
81 * alert('New content was appended to #my-wrapper');
82 * }
83 * }
84 * };
85 * @endcode
86 */
87 Drupal.ajax = function (base, element, element_settings) {
88 var defaults = {
89 url: 'system/ajax',
90 event: 'mousedown',
91 keypress: true,
92 selector: '#' + base,
93 effect: 'none',
94 speed: 'slow',
95 method: 'replace',
96 progress: {
97 type: 'bar',
98 message: 'Please wait...'
99 },
100 button: {}
101 };
102
103 $.extend(this, defaults, element_settings);
104
105 this.element = element;
106
107 // Replacing 'nojs' with 'ajax' in the URL allows for an easy method to let
108 // the server detect when it needs to degrade gracefully.
109 this.url = element_settings.url.replace('/nojs/', '/ajax/');
110 this.wrapper = '#' + element_settings.wrapper;
111
112 // If there isn't a form, jQuery.ajax() will be used instead, allowing us to
113 // bind AJAX to links as well.
114 if (this.element.form) {
115 this.form = $(this.element.form);
116 }
117
118 // Set the options for the ajaxSubmit function.
119 // The 'this' variable will not persist inside of the options object.
120 var ajax = this;
121 var options = {
122 url: ajax.url,
123 data: ajax.button,
124 beforeSerialize: function (element_settings, options) {
125 return ajax.beforeSerialize(element_settings, options);
126 },
127 beforeSubmit: function (form_values, element_settings, options) {
128 return ajax.beforeSubmit(form_values, element_settings, options);
129 },
130 success: function (response, status) {
131 // Sanity check for browser support (object expected).
132 // When using iFrame uploads, responses must be returned as a string.
133 if (typeof response == 'string') {
134 response = Drupal.parseJson(response);
135 }
136 return ajax.success(response, status);
137 },
138 complete: function (response, status) {
139 if (status == 'error' || status == 'parsererror') {
140 return ajax.error(response, ajax.url);
141 }
142 },
143 dataType: 'json',
144 type: 'POST'
145 };
146
147 // Bind the ajaxSubmit function to the element event.
148 $(this.element).bind(element_settings.event, function () {
149 if (ajax.form) {
150 // If setClick is set, we must set this to ensure that the button's
151 // value is passed.
152 if (ajax.setClick) {
153 // Mark the clicked button. 'form.clk' is a special variable for
154 // ajaxSubmit that tells the system which element got clicked to
155 // trigger the submit. Without it there would be no 'op' or
156 // equivalent.
157 ajax.form.clk = this.element;
158 }
159
160 ajax.form.ajaxSubmit(options);
161 }
162 else {
163 $.ajax(options);
164 }
165
166 return false;
167 });
168
169 // If necessary, enable keyboard submission so that AJAX behaviors
170 // can be triggered through keyboard input as well as e.g. a mousedown
171 // action.
172 if (element_settings.keypress) {
173 $(element_settings.element).keypress(function (event) {
174 // Detect enter key.
175 if (event.keyCode == 13) {
176 $(element_settings.element).trigger(element_settings.event);
177 return false;
178 }
179 });
180 }
181 };
182
183 /**
184 * Handler for the form serialization.
185 *
186 * Runs before the beforeSubmit() handler (see below), and unlike that one, runs
187 * before field data is collected.
188 */
189 Drupal.ajax.prototype.beforeSerialize = function (element, options) {
190 // Allow detaching behaviors to update field values before collecting them.
191 var settings = this.settings || Drupal.settings;
192 Drupal.detachBehaviors(this.form, settings, 'serialize');
193 };
194
195 /**
196 * Handler for the form redirection submission.
197 */
198 Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) {
199 // Disable the element that received the change.
200 $(this.element).addClass('progress-disabled').attr('disabled', true);
201
202 // Server-side code needs to know what element triggered the call, so it can
203 // find the #ajax binding.
204 form_values.push({ name: 'ajax_triggering_element', value: this.formPath });
205
206 // Insert progressbar or throbber.
207 if (this.progress.type == 'bar') {
208 var progressBar = new Drupal.progressBar('ajax-progress-' + this.element.id, eval(this.progress.update_callback), this.progress.method, eval(this.progress.error_callback));
209 if (this.progress.message) {
210 progressBar.setProgress(-1, this.progress.message);
211 }
212 if (this.progress.url) {
213 progressBar.startMonitoring(this.progress.url, this.progress.interval || 1500);
214 }
215 this.progress.element = $(progressBar.element).addClass('ajax-progress ajax-progress-bar');
216 this.progress.object = progressBar;
217 $(this.element).after(this.progress.element);
218 }
219 else if (this.progress.type == 'throbber') {
220 this.progress.element = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber">&nbsp;</div></div>');
221 if (this.progress.message) {
222 $('.throbber', this.progress.element).after('<div class="message">' + this.progress.message + '</div>');
223 }
224 $(this.element).after(this.progress.element);
225 }
226 };
227
228 /**
229 * Handler for the form redirection completion.
230 */
231 Drupal.ajax.prototype.success = function (response, status) {
232 // Remove the progress element.
233 if (this.progress.element) {
234 $(this.progress.element).remove();
235 }
236 if (this.progress.object) {
237 this.progress.object.stopMonitoring();
238 }
239 $(this.element).removeClass('progress-disabled').attr('disabled', false);
240
241 Drupal.freezeHeight();
242
243 for (i in response) {
244 if (response[i]['command'] && this.commands[response[i]['command']]) {
245 this.commands[response[i]['command']](this, response[i], status);
246 }
247 }
248
249 // Reattach behaviors that were detached in beforeSerialize(). The
250 // attachBehaviors() called on the new content from processing the response
251 // commands is not sufficient, because behaviors from the entire form need
252 // to be reattached.
253 var settings = this.settings || Drupal.settings;
254 Drupal.attachBehaviors(this.form, settings);
255
256 Drupal.unfreezeHeight();
257
258 // Remove any response-specific settings so they don't get used on the next
259 // call by mistake.
260 this.settings = {};
261 };
262
263 /**
264 * Build an effect object which tells us how to apply the effect when adding new HTML.
265 */
266 Drupal.ajax.prototype.getEffect = function (response) {
267 var type = response.effect || this.effect;
268 var speed = response.speed || this.speed;
269
270 var effect = {};
271 if (type == 'none') {
272 effect.showEffect = 'show';
273 effect.hideEffect = 'hide';
274 effect.showSpeed = '';
275 }
276 else if (type == 'fade') {
277 effect.showEffect = 'fadeIn';
278 effect.hideEffect = 'fadeOut';
279 effect.showSpeed = speed;
280 }
281 else {
282 effect.showEffect = type + 'Toggle';
283 effect.hideEffect = type + 'Toggle';
284 effect.showSpeed = speed;
285 }
286
287 return effect;
288 }
289
290 /**
291 * Handler for the form redirection error.
292 */
293 Drupal.ajax.prototype.error = function (response, uri) {
294 alert(Drupal.ajaxError(response, uri));
295 // Remove the progress element.
296 if (this.progress.element) {
297 $(this.progress.element).remove();
298 }
299 if (this.progress.object) {
300 this.progress.object.stopMonitoring();
301 }
302 // Undo hide.
303 $(this.wrapper).show();
304 // Re-enable the element.
305 $(this.element).removeClass('progress-disabled').attr('disabled', false);
306 // Reattach behaviors that were detached in beforeSerialize().
307 var settings = response.settings || this.settings || Drupal.settings;
308 Drupal.attachBehaviors(this.form, settings);
309 };
310
311 /**
312 * Provide a series of commands that the server can request the client perform.
313 */
314 Drupal.ajax.prototype.commands = {
315 /**
316 * Command to insert new content into the DOM.
317 */
318 insert: function (ajax, response, status) {
319 // Get information from the response. If it is not there, default to
320 // our presets.
321 var wrapper = response.selector ? $(response.selector) : $(ajax.wrapper);
322 var method = response.method || ajax.method;
323 var effect = ajax.getEffect(response);
324
325 // Manually insert HTML into the jQuery object, using $() directly crashes
326 // Safari with long string lengths. http://dev.jquery.com/ticket/3178
327 var new_content = $('<div></div>').html(response.data);
328
329 // If removing content from the wrapper, detach behaviors first.
330 switch (method) {
331 case 'html':
332 case 'replaceWith':
333 case 'replaceAll':
334 case 'empty':
335 case 'remove':
336 var settings = response.settings || ajax.settings || Drupal.settings;
337 Drupal.detachBehaviors(wrapper, settings);
338 }
339
340 // Add the new content to the page.
341 wrapper[method](new_content);
342
343 // Immediately hide the new content if we're using any effects.
344 if (effect.showEffect != 'show') {
345 new_content.hide();
346 }
347
348 // Determine which effect to use and what content will receive the
349 // effect, then show the new content.
350 if ($('.ajax-new-content', new_content).length > 0) {
351 $('.ajax-new-content', new_content).hide();
352 new_content.show();
353 $('.ajax-new-content', new_content)[effect.showEffect](effect.showSpeed);
354 }
355 else if (effect.showEffect != 'show') {
356 new_content[effect.showEffect](effect.showSpeed);
357 }
358
359 // Attach all JavaScript behaviors to the new content, if it was successfully
360 // added to the page, this if statement allows #ajax['wrapper'] to be
361 // optional.
362 if (new_content.parents('html').length > 0) {
363 // Apply any settings from the returned JSON if available.
364 var settings = response.settings || ajax.settings || Drupal.settings;
365 Drupal.attachBehaviors(new_content, settings);
366 }
367 },
368
369 /**
370 * Command to remove a chunk from the page.
371 */
372 remove: function (ajax, response, status) {
373 var settings = response.settings || ajax.settings || Drupal.settings;
374 Drupal.detachBehaviors($(response.selector), settings);
375 $(response.selector).remove();
376 },
377
378 /**
379 * Command to mark a chunk changed.
380 */
381 changed: function (ajax, response, status) {
382 if (!$(response.selector).hasClass('ajax-changed')) {
383 $(response.selector).addClass('ajax-changed');
384 if (response.asterisk) {
385 $(response.selector).find(response.asterisk).append(' <span class="ajax-changed">*</span> ');
386 }
387 }
388 },
389
390 /**
391 * Command to provide an alert.
392 */
393 alert: function (ajax, response, status) {
394 alert(response.text, response.title);
395 },
396
397 /**
398 * Command to set the settings that will be used for other commands in this response.
399 */
400 settings: function (ajax, response, status) {
401 ajax.settings = response.settings;
402 },
403
404 /**
405 * Command to attach data using jQuery's data API.
406 */
407 data: function (ajax, response, status) {
408 $(response.selector).data(response.name, response.value);
409 },
410
411 /**
412 * Command to restripe a table.
413 */
414 restripe: function (ajax, response, status) {
415 // :even and :odd are reversed because jQuery counts from 0 and
416 // we count from 1, so we're out of sync.
417 $('tbody tr:not(:hidden)', $(response.selector))
418 .removeClass('even').removeClass('odd')
419 .filter(':even')
420 .addClass('odd').end()
421 .filter(':odd')
422 .addClass('even');
423 }
424 };
425
426 })(jQuery);

  ViewVC Help
Powered by ViewVC 1.1.2