adding all weblabels from weblabels.fsf.org
[weblabels.fsf.org.git] / www.fsf.org / 20131028 / files / static.fsf.org / plone2012 / ++resource++plone.app.jquerytools.overlayhelpers.js
CommitLineData
5a920362 1/*****************
2
3 jQuery Tools overlay helpers.
4
5 Copyright © 2010, The Plone Foundation
6 Licensed under the GPL, see LICENSE.txt for details.
7
8*****************/
9
10/*jslint browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, newcap: true, immed: true, regexp: false, white:true */
11/*global jQuery, ajax_noresponse_message, window */
12
13// Name space object for pipbox
14var pb = {spinner: {}, overlay_counter: 1};
15
16jQuery.tools.overlay.conf.oneInstance = false;
17
18jQuery(function ($) {
19
20 pb.spinner.show = function () {
21 $('body').css('cursor', 'wait');
22 };
23 pb.spinner.hide = function () {
24 $('body').css('cursor', '');
25 };
26
27 /******
28 $.fn.prepOverlay jQuery plugin to inject overlay target into DOM and
29 annotate it with the data we'll need in order to display it.
30 ******/
31 $.fn.prepOverlay = function (pba) {
32 return this.each(function () {
33 var o, pbo, config, onBeforeLoad, onLoad, src, parts;
34
35 o = $(this);
36
37 // copy options so that it's not just a reference
38 // to the parameter.
39 pbo = $.extend(true, {}, pba);
40
41 // set overlay configuration
42 config = pbo.config || {};
43
44 // set onBeforeLoad handler
45 onBeforeLoad = pb[pbo.subtype];
46 if (onBeforeLoad) {
47 config.onBeforeLoad = onBeforeLoad;
48 }
49 onLoad = config.onLoad;
50 config.onLoad = function () {
51 if (onLoad) {
52 onLoad.apply(this, arguments);
53 }
54 pb.fi_focus(this.getOverlay());
55 };
56
57 // be promiscuous, pick up the url from
58 // href, src or action attributes
59 src = o.attr('href') || o.attr('src') || o.attr('action');
60
61 // translate url with config specifications
62 if (pbo.urlmatch) {
63 src = src.replace(new RegExp(pbo.urlmatch), pbo.urlreplace);
64 }
65
66 if (pbo.subtype === 'inline') {
67 // we're going to let tools' overlay do all the real
68 // work. Just get the markers in place.
69 src = src.replace(/^.+#/, '#');
70 $("[id='" + src.replace('#', '') + "']")
71 .addClass('overlay');
72 o.removeAttr('href').attr('rel', src);
73 // use overlay on the source (clickable) element
74 o.overlay();
75 } else {
76 // save various bits of information from the pbo options,
77 // and enable the overlay.
78
79 // this is not inline, so in one fashion or another
80 // we'll be loading it via the beforeLoad callback.
81 // create a unique id for a target element
82 pbo.nt = 'pb_' + pb.overlay_counter;
83 pb.overlay_counter += 1;
84
85 pbo.selector = pbo.filter || pbo.selector;
86 if (!pbo.selector) {
87 // see if one's been supplied in the src
88 parts = src.split(' ');
89 src = parts.shift();
90 pbo.selector = parts.join(' ');
91 }
92
93 pbo.src = src;
94 pbo.config = config;
95
96 // remove any existing overlay and overlay handler
97 pb.remove_overlay(o);
98
99 // save options on trigger element
100 o.data('pbo', pbo);
101
102 // mark the source with a rel attribute so we can find
103 // the overlay, and a special class for styling
104 o.attr('rel', '#' + pbo.nt);
105 o.addClass('link-overlay');
106
107 // for some subtypes, we're setting click handlers
108 // and attaching overlay to the target element. That's
109 // so we'll know the dimensions early.
110 // Others, like iframe, just use overlay.
111 switch (pbo.subtype) {
112 case 'image':
113 o.click(pb.image_click);
114 break;
115 case 'ajax':
116 o.click(pb.ajax_click);
117 break;
118 case 'iframe':
119 pb.create_content_div(pbo);
120 o.overlay(config);
121 break;
122 default:
123 throw "Unsupported overlay type";
124 }
125
126 // in case the click source wasn't
127 // already a link.
128 o.css('cursor', 'pointer');
129 }
130 });
131 };
132
133
134 /******
135 pb.remove_overlay
136 Remove the overlay and handler associated with a jquery wrapped
137 trigger object
138 ******/
139 pb.remove_overlay = function (o) {
140 var old_data = o.data('pbo');
141 if (old_data) {
142 switch (old_data.subtype) {
143 case 'image':
144 o.unbind('click', pb.image_click);
145 break;
146 case 'ajax':
147 o.unbind('click', pb.ajax_click);
148 break;
149 default:
150 // it's probably the jqt overlay click handler,
151 // but we don't know the handler and are forced
152 // to do a generic unbind of click handlers.
153 o.unbind('click');
154 }
155 if (old_data.nt) {
156 $('#' + old_data.nt).remove();
157 }
158 }
159 };
160
161
162 /******
163 pb.create_content_div
164 create a div to act as an overlay; append it to
165 the body; return it
166 ******/
167 pb.create_content_div = function (pbo) {
168 var content;
169
170 content = $(
171 '<div id="' + pbo.nt +
172 '" class="overlay overlay-' + pbo.subtype +
173 ' ' + (pbo.cssclass || '') +
174 '"><div class="close"><span>Close</span></div></div>'
175 );
176
177 content.data('pbo', pbo);
178
179 // if we've a width specified, set it on the overlay div
180 if (pbo.width) {
181 content.width(pbo.width);
182 }
183
184 // add the target element at the end of the body.
185 content.appendTo($("body"));
186
187 return content;
188 };
189
190
191 /******
192 pb.image_click
193 click handler for ajax sources.
194 ******/
195 pb.image_click = function (event) {
196 var ethis, content, api, img, el, pbo;
197
198 ethis = $(this);
199 pbo = ethis.data('pbo');
200
201 // find target container
202 content = $(ethis.attr('rel'));
203 if (!content.length) {
204 content = pb.create_content_div(pbo);
205 content.overlay(pbo.config);
206 }
207 api = content.overlay();
208
209 // is the image loaded yet?
210 if (content.find('img').length === 0) {
211 // load the image.
212 if (pbo.src) {
213 pb.spinner.show();
214
215 // create the image and stuff it
216 // into our target
217 img = new Image();
218 img.src = pbo.src;
219 el = $(img);
220 content.append(el.addClass('pb-image'));
221
222 // Now, we'll cause the overlay to
223 // load when the image is loaded.
224 el.load(function () {
225 pb.spinner.hide();
226 api.load();
227 });
228
229 }
230 } else {
231 api.load();
232 }
233
234 return false;
235 };
236
237
238 /******
239 pb.fi_focus
240 First-input focus inside $ selection.
241 ******/
242 pb.fi_focus = function (jqo) {
243 if (! jqo.find("form div.error :input:first").focus().length) {
244 jqo.find("form :input:visible:first").focus();
245 }
246 };
247
248
249 /******
250 pb.ajax_error_recover
251 jQuery's ajax load function does not load error responses.
252 This routine returns the cooked error response.
253 ******/
254 pb.ajax_error_recover = function (responseText, selector) {
255 var tcontent = $('<div/>')
256 .append(responseText.replace(/<script(.|\s)*?\/script>/gi, ""));
257 return selector ? tcontent.find(selector) : tcontent;
258 };
259
260
261 /******
262 pb.add_ajax_load
263 Adds a hidden ajax_load input to form
264 ******/
265 pb.add_ajax_load = function (form) {
266 if (form.find('input[name=ajax_load]').length === 0) {
267 form.prepend($('<input type="hidden" name="ajax_load" value="' +
268 (new Date().getTime()) +
269 '" />'));
270 }
271 };
272
273 /******
274 pb.prep_ajax_form
275 Set up form with ajaxForm, including success and error handlers.
276 ******/
277 pb.prep_ajax_form = function (form) {
278 var ajax_parent = form.closest('.pb-ajax'),
279 data_parent = ajax_parent.closest('.overlay-ajax'),
280 pbo = data_parent.data('pbo'),
281 formtarget = pbo.formselector,
282 closeselector = pbo.closeselector,
283 beforepost = pbo.beforepost,
284 afterpost = pbo.afterpost,
285 noform = pbo.noform,
286 api = data_parent.overlay(),
287 selector = pbo.selector,
288 options = {};
289
290 options.beforeSerialize = function () {
291 pb.spinner.show();
292 };
293
294 if (beforepost) {
295 options.beforeSubmit = function (arr, form, options) {
296 return beforepost(form, arr, options);
297 };
298 }
299 options.success = function (responseText, statusText, xhr, form) {
300 $(document).trigger('formOverlayStart', [this, responseText, statusText, xhr, form]);
301 // success comes in many forms, some of which are errors;
302 //
303
304 var el, myform, success, target;
305
306 success = statusText === "success" || statusText === "notmodified";
307
308 if (! success) {
309 // The responseText parameter is actually xhr
310 responseText = responseText.responseText;
311 }
312 // strip inline script tags
313 responseText = responseText.replace(/<script(.|\s)*?\/script>/gi, "");
314
315 // create a div containing the optionally filtered response
316 el = $('<div />').append(
317 selector ?
318 // a lesson learned from the jQuery source: $(responseText)
319 // will not work well unless responseText is well-formed;
320 // appending to a div is more robust, and automagically
321 // removes the html/head/body outer tagging.
322 $('<div />').append(responseText).find(selector)
323 :
324 responseText
325 );
326
327 // afterpost callback
328 if (success && afterpost) {
329 afterpost(el, data_parent);
330 }
331
332 myform = el.find(formtarget);
333 if (success && myform.length) {
334 ajax_parent.empty().append(el);
335 pb.fi_focus(ajax_parent);
336
337 pb.add_ajax_load(myform);
338 // attach submit handler with the same options
339 myform.ajaxForm(options);
340
341 // attach close to element id'd by closeselector
342 if (closeselector) {
343 el.find(closeselector).click(function (event) {
344 api.close();
345 return false;
346 });
347 }
348 $(document).trigger('formOverlayLoadSuccess', [this, myform, api, pb, ajax_parent]);
349 } else {
350 // there's no form in our new content or there's been an error
351 if (success) {
352 if (typeof(noform) === "function") {
353 // get action from callback
354 noform = noform(this);
355 }
356 } else {
357 noform = statusText;
358 }
359
360
361 switch (noform) {
362 case 'close':
363 api.close();
364 break;
365 case 'reload':
366 api.close();
367 pb.spinner.show();
368 // location.reload results in a repost
369 // dialog in some browsers; very unlikely to
370 // be what we want.
371 location.replace(location.href);
372 break;
373 case 'redirect':
374 api.close();
375 pb.spinner.show();
376 target = pbo.redirect;
377 if (typeof(target) === "function") {
378 // get target from callback
379 target = target(this, responseText);
380 }
381 location.replace(target);
382 break;
383 default:
384 if (el.children()) {
385 // show what we've got
386 ajax_parent.empty().append(el);
387 } else {
388 api.close();
389 }
390 }
391 $(document).trigger('formOverlayLoadFailure', [this, myform, api, pb, ajax_parent, noform]);
392 }
393 pb.spinner.hide();
394 };
395 // error and success callbacks are the same
396 options.error = options.success;
397
398 pb.add_ajax_load(form);
399 form.ajaxForm(options);
400 };
401
402
403 /******
404 pb.ajax_click
405 Click handler for ajax sources. The job of this routine
406 is to do the ajax load of the overlay element, then
407 call the JQT overlay loader.
408 ******/
409 pb.ajax_click = function (event) {
410 var ethis = $(this),
411 pbo,
412 content,
413 api,
414 src,
415 el,
416 selector,
417 formtarget,
418 closeselector,
419 sep;
420
421 e = $.Event();
422 e.type = "beforeAjaxClickHandled";
423 $(document).trigger(e, [this, event]);
424 if (e.isDefaultPrevented()) { return; }
425
426 pbo = ethis.data('pbo');
427
428 content = pb.create_content_div(pbo);
429 content.overlay(pbo.config);
430 api = content.overlay();
431 src = pbo.src;
432 selector = pbo.selector;
433 formtarget = pbo.formselector;
434 closeselector = pbo.closeselector;
435
436 pb.spinner.show();
437
438 // prevent double click warning for this form
439 $(this).find("input.submitting").removeClass('submitting');
440
441 el = $('<div class="pb-ajax" />');
442 if (api.getConf().fixed) {
443 // don't let it be over 75% of the viewport's height
444 el.css('max-height', Math.floor($(window).height() * 0.75));
445 }
446 content.append(el);
447
448 // affix a random query argument to prevent
449 // loading from browser cache
450 sep = (src.indexOf('?') >= 0) ? '&': '?';
451 src += sep + "ajax_load=" + (new Date().getTime());
452
453 // add selector, if any
454 if (selector) {
455 src += ' ' + selector;
456 }
457
458 // set up callback to be used whenever new contents are loaded
459 // into the overlay, to prepare links and forms to stay within
460 // the overlay
461 el[0].handle_load_inside_overlay = function(responseText, errorText) {
462 var el = $(this);
463
464 if (errorText === 'error') {
465 el.append(pb.ajax_error_recover(responseText, selector));
466 } else if (!responseText.length) {
467 el.append(ajax_noresponse_message || 'No response from server.');
468 }
469
470 // a non-semantic div here will make sure we can
471 // do enough formatting.
472 el.wrapInner('<div />');
473
474 // add the submit handler if we've a formtarget
475 if (formtarget) {
476 var target = el.find(formtarget);
477 if (target.length > 0) {
478 pb.prep_ajax_form(target);
479 }
480 }
481
482 // if a closeselector has been specified, tie it to the overlay's
483 // close method via closure
484 if (closeselector) {
485 el.find(closeselector).click(function (event) {
486 api.close();
487 return false;
488 });
489 }
490
491 // This may be a complex form.
492 if ($.fn.ploneTabInit) {
493 el.ploneTabInit();
494 }
495
496 // remove element on close so that it doesn't congest the DOM
497 api.onClose = function () {
498 content.remove();
499 };
500 $(document).trigger('loadInsideOverlay', [this, responseText, errorText, api]);
501 }
502
503 // and load the div
504 el.load(src, null, function (responseText, errorText) {
505 // post-process the overlay contents
506 el[0].handle_load_inside_overlay.apply(this, [responseText, errorText]);
507
508 // Now, it's all ready to display; hide the
509 // spinner and call JQT overlay load.
510 pb.spinner.hide();
511 api.load();
512
513 return true;
514 });
515
516 // don't do the default action
517 return false;
518 };
519
520
521 /******
522 pb.iframe
523 onBeforeLoad handler for iframe overlays.
524
525 Note that the spinner is handled a little differently
526 so that we can keep it displayed while the iframe's
527 content is loading.
528 ******/
529 pb.iframe = function () {
530 var content, pbo;
531
532 pb.spinner.show();
533
534 content = this.getOverlay();
535 pbo = this.getTrigger().data('pbo');
536
537 if (content.find('iframe').length === 0 && pbo.src) {
538 content.append(
539 '<iframe src="' + pbo.src + '" width="' +
540 content.width() + '" height="' + content.height() +
541 '" onload="pb.spinner.hide()"/>'
542 );
543 } else {
544 pb.spinner.hide();
545 }
546 return true;
547 };
548
549 // $('.newsImageContainer a')
550 // .prepOverlay({
551 // subtype: 'image',
552 // urlmatch: '/image_view_fullscreen$',
553 // urlreplace: '_preview'
554 // });
555
556});