1 | |
---|
2 | /** |
---|
3 | * Provides AJAX-like page updating via AHAH (Asynchronous HTML and HTTP). |
---|
4 | * |
---|
5 | * AHAH is a method of making a request via Javascript while viewing an HTML |
---|
6 | * page. The request returns a small chunk of HTML, which is then directly |
---|
7 | * injected into the page. |
---|
8 | * |
---|
9 | * Drupal uses this file to enhance form elements with #ahah[path] and |
---|
10 | * #ahah[wrapper] properties. If set, this file will automatically be included |
---|
11 | * to provide AHAH capabilities. |
---|
12 | */ |
---|
13 | |
---|
14 | /** |
---|
15 | * Attaches the ahah behavior to each ahah form element. |
---|
16 | */ |
---|
17 | Drupal.behaviors.ahah = function(context) { |
---|
18 | for (var base in Drupal.settings.ahah) { |
---|
19 | if (!$('#'+ base + '.ahah-processed').size()) { |
---|
20 | var element_settings = Drupal.settings.ahah[base]; |
---|
21 | |
---|
22 | $(element_settings.selector).each(function() { |
---|
23 | element_settings.element = this; |
---|
24 | var ahah = new Drupal.ahah(base, element_settings); |
---|
25 | }); |
---|
26 | |
---|
27 | $('#'+ base).addClass('ahah-processed'); |
---|
28 | } |
---|
29 | } |
---|
30 | }; |
---|
31 | |
---|
32 | /** |
---|
33 | * AHAH object. |
---|
34 | */ |
---|
35 | Drupal.ahah = function(base, element_settings) { |
---|
36 | // Set the properties for this object. |
---|
37 | this.element = element_settings.element; |
---|
38 | this.selector = element_settings.selector; |
---|
39 | this.event = element_settings.event; |
---|
40 | this.keypress = element_settings.keypress; |
---|
41 | this.url = element_settings.url; |
---|
42 | this.wrapper = '#'+ element_settings.wrapper; |
---|
43 | this.effect = element_settings.effect; |
---|
44 | this.method = element_settings.method; |
---|
45 | this.progress = element_settings.progress; |
---|
46 | this.button = element_settings.button || { }; |
---|
47 | this.immutable = element_settings.immutable; |
---|
48 | this.buildId = null; |
---|
49 | |
---|
50 | if (this.effect == 'none') { |
---|
51 | this.showEffect = 'show'; |
---|
52 | this.hideEffect = 'hide'; |
---|
53 | this.showSpeed = ''; |
---|
54 | } |
---|
55 | else if (this.effect == 'fade') { |
---|
56 | this.showEffect = 'fadeIn'; |
---|
57 | this.hideEffect = 'fadeOut'; |
---|
58 | this.showSpeed = 'slow'; |
---|
59 | } |
---|
60 | else { |
---|
61 | this.showEffect = this.effect + 'Toggle'; |
---|
62 | this.hideEffect = this.effect + 'Toggle'; |
---|
63 | this.showSpeed = 'slow'; |
---|
64 | } |
---|
65 | |
---|
66 | // Record the form action and target, needed for iFrame file uploads. |
---|
67 | var form = $(this.element).parents('form'); |
---|
68 | this.form_action = form.attr('action'); |
---|
69 | this.form_target = form.attr('target'); |
---|
70 | this.form_encattr = form.attr('encattr'); |
---|
71 | |
---|
72 | // Set the options for the ajaxSubmit function. |
---|
73 | // The 'this' variable will not persist inside of the options object. |
---|
74 | var ahah = this; |
---|
75 | var options = { |
---|
76 | url: ahah.url, |
---|
77 | data: ahah.button, |
---|
78 | beforeSubmit: function(form_values, element_settings, options) { |
---|
79 | return ahah.beforeSubmit(form_values, element_settings, options); |
---|
80 | }, |
---|
81 | beforeSend: function(request, options) { |
---|
82 | return ahah.beforeSend(request, options); |
---|
83 | }, |
---|
84 | success: function(response, status) { |
---|
85 | // Sanity check for browser support (object expected). |
---|
86 | // When using iFrame uploads, responses must be returned as a string. |
---|
87 | if (typeof(response) == 'string') { |
---|
88 | response = Drupal.parseJson(response); |
---|
89 | } |
---|
90 | return ahah.success(response, status); |
---|
91 | }, |
---|
92 | complete: function(response, status) { |
---|
93 | ahah.complete(response, status); |
---|
94 | if (status == 'error' || status == 'parsererror') { |
---|
95 | return ahah.error(response, ahah.url); |
---|
96 | } |
---|
97 | }, |
---|
98 | dataType: 'json', |
---|
99 | type: 'POST' |
---|
100 | }; |
---|
101 | |
---|
102 | // Bind the ajaxSubmit function to the element event. |
---|
103 | $(element_settings.element).bind(element_settings.event, function() { |
---|
104 | $(element_settings.element).parents('form').ajaxSubmit(options); |
---|
105 | return false; |
---|
106 | }); |
---|
107 | // If necessary, enable keyboard submission so that AHAH behaviors |
---|
108 | // can be triggered through keyboard input as well as e.g. a mousedown |
---|
109 | // action. |
---|
110 | if (element_settings.keypress) { |
---|
111 | $(element_settings.element).keypress(function(event) { |
---|
112 | // Detect enter key. |
---|
113 | if (event.keyCode == 13) { |
---|
114 | $(element_settings.element).trigger(element_settings.event); |
---|
115 | return false; |
---|
116 | } |
---|
117 | }); |
---|
118 | } |
---|
119 | }; |
---|
120 | |
---|
121 | /** |
---|
122 | * Handler for the form redirection submission. |
---|
123 | */ |
---|
124 | Drupal.ahah.prototype.beforeSubmit = function (form_values, element, options) { |
---|
125 | // Disable the element that received the change. |
---|
126 | $(this.element).addClass('progress-disabled').attr('disabled', true); |
---|
127 | |
---|
128 | // Insert progressbar or throbber. |
---|
129 | if (this.progress.type == 'bar') { |
---|
130 | var progressBar = new Drupal.progressBar('ahah-progress-' + this.element.id, eval(this.progress.update_callback), this.progress.method, eval(this.progress.error_callback)); |
---|
131 | if (this.progress.message) { |
---|
132 | progressBar.setProgress(-1, this.progress.message); |
---|
133 | } |
---|
134 | if (this.progress.url) { |
---|
135 | progressBar.startMonitoring(this.progress.url, this.progress.interval || 1500); |
---|
136 | } |
---|
137 | this.progress.element = $(progressBar.element).addClass('ahah-progress ahah-progress-bar'); |
---|
138 | this.progress.object = progressBar; |
---|
139 | $(this.element).after(this.progress.element); |
---|
140 | } |
---|
141 | else if (this.progress.type == 'throbber') { |
---|
142 | this.progress.element = $('<div class="ahah-progress ahah-progress-throbber"><div class="throbber"> </div></div>'); |
---|
143 | if (this.progress.message) { |
---|
144 | $('.throbber', this.progress.element).after('<div class="message">' + this.progress.message + '</div>') |
---|
145 | } |
---|
146 | $(this.element).after(this.progress.element); |
---|
147 | } |
---|
148 | |
---|
149 | // Record the build-id. |
---|
150 | if (this.immutable) { |
---|
151 | var ahah = this; |
---|
152 | $.each(form_values, function () { |
---|
153 | if (this.name == 'form_build_id') { |
---|
154 | ahah.buildId = this.value; |
---|
155 | return false; |
---|
156 | } |
---|
157 | }); |
---|
158 | } |
---|
159 | }; |
---|
160 | |
---|
161 | /** |
---|
162 | * Modify the request object before it is sent. |
---|
163 | */ |
---|
164 | Drupal.ahah.prototype.beforeSend = function (request, options) { |
---|
165 | if (this.immutable) { |
---|
166 | request.setRequestHeader('X-Drupal-Accept-Build-Id', '1'); |
---|
167 | } |
---|
168 | } |
---|
169 | |
---|
170 | /** |
---|
171 | * Handler for the form redirection completion. |
---|
172 | */ |
---|
173 | Drupal.ahah.prototype.success = function (response, status) { |
---|
174 | var wrapper = $(this.wrapper); |
---|
175 | var form = $(this.element).parents('form'); |
---|
176 | // Manually insert HTML into the jQuery object, using $() directly crashes |
---|
177 | // Safari with long string lengths. http://dev.jquery.com/ticket/1152 |
---|
178 | var new_content = $('<div></div>').html(response.data); |
---|
179 | |
---|
180 | // Restore the previous action and target to the form. |
---|
181 | form.attr('action', this.form_action); |
---|
182 | this.form_target ? form.attr('target', this.form_target) : form.removeAttr('target'); |
---|
183 | this.form_encattr ? form.attr('target', this.form_encattr) : form.removeAttr('encattr'); |
---|
184 | |
---|
185 | // Remove the progress element. |
---|
186 | if (this.progress.element) { |
---|
187 | $(this.progress.element).remove(); |
---|
188 | } |
---|
189 | if (this.progress.object) { |
---|
190 | this.progress.object.stopMonitoring(); |
---|
191 | } |
---|
192 | $(this.element).removeClass('progress-disabled').attr('disabled', false); |
---|
193 | |
---|
194 | // Add the new content to the page. |
---|
195 | Drupal.freezeHeight(); |
---|
196 | if (this.method == 'replace') { |
---|
197 | wrapper.empty().append(new_content); |
---|
198 | } |
---|
199 | else { |
---|
200 | wrapper[this.method](new_content); |
---|
201 | } |
---|
202 | |
---|
203 | // Immediately hide the new content if we're using any effects. |
---|
204 | if (this.showEffect != 'show') { |
---|
205 | new_content.hide(); |
---|
206 | } |
---|
207 | |
---|
208 | // Determine what effect use and what content will receive the effect, then |
---|
209 | // show the new content. For browser compatibility, Safari is excluded from |
---|
210 | // using effects on table rows. |
---|
211 | if (($.browser.safari && $("tr.ahah-new-content", new_content).size() > 0)) { |
---|
212 | new_content.show(); |
---|
213 | } |
---|
214 | else if ($('.ahah-new-content', new_content).size() > 0) { |
---|
215 | $('.ahah-new-content', new_content).hide(); |
---|
216 | new_content.show(); |
---|
217 | $(".ahah-new-content", new_content)[this.showEffect](this.showSpeed); |
---|
218 | } |
---|
219 | else if (this.showEffect != 'show') { |
---|
220 | new_content[this.showEffect](this.showSpeed); |
---|
221 | } |
---|
222 | |
---|
223 | // Attach all javascript behaviors to the new content, if it was successfully |
---|
224 | // added to the page, this if statement allows #ahah[wrapper] to be optional. |
---|
225 | if (new_content.parents('html').length > 0) { |
---|
226 | Drupal.attachBehaviors(new_content); |
---|
227 | } |
---|
228 | |
---|
229 | Drupal.unfreezeHeight(); |
---|
230 | }; |
---|
231 | |
---|
232 | /** |
---|
233 | * Handler for the form redirection error. |
---|
234 | */ |
---|
235 | Drupal.ahah.prototype.error = function (response, uri) { |
---|
236 | alert(Drupal.ahahError(response, uri)); |
---|
237 | // Resore the previous action and target to the form. |
---|
238 | $(this.element).parent('form').attr( { action: this.form_action, target: this.form_target} ); |
---|
239 | // Remove the progress element. |
---|
240 | if (this.progress.element) { |
---|
241 | $(this.progress.element).remove(); |
---|
242 | } |
---|
243 | if (this.progress.object) { |
---|
244 | this.progress.object.stopMonitoring(); |
---|
245 | } |
---|
246 | // Undo hide. |
---|
247 | $(this.wrapper).show(); |
---|
248 | // Re-enable the element. |
---|
249 | $(this.element).removeClass('progess-disabled').attr('disabled', false); |
---|
250 | }; |
---|
251 | |
---|
252 | /** |
---|
253 | * Handler called when the request finishes, whether in failure or success. |
---|
254 | */ |
---|
255 | Drupal.ahah.prototype.complete = function (response, status) { |
---|
256 | // Update form build id if necessary. |
---|
257 | if (this.immutable) { |
---|
258 | var newBuildId = response.getResponseHeader('X-Drupal-Build-Id'); |
---|
259 | if (this.buildId && newBuildId && this.buildId != newBuildId) { |
---|
260 | var $element = $('input[name="form_build_id"][value="' + this.buildId + '"]'); |
---|
261 | $element.val(newBuildId); |
---|
262 | $element.attr('id', newBuildId); |
---|
263 | } |
---|
264 | this.buildId = null; |
---|
265 | } |
---|
266 | } |
---|