1 /* This file is part of Syj, Copyright (c) 2010-2011 Arnaud Renevier,
2 and is published under the AGPL license. */
6 var CloseBtn = Class.create({
7 initialize: function(elt, options) {
8 var btn, imgsrc, style;
15 if (typeof options !== "object") {
19 style = Object.extend({
26 imgsrc = (options.closeBtnSrc) || "icons/close.png";
27 btn = new Element("input", { type: "image", src: imgsrc, alt: "X"}).setStyle(style);
28 elt.insert({top: btn});
29 btn.observe("click", function(evt) {
31 if (evt.detail === 0) { // it's not a real click, possibly a submit event
34 if (typeof options.callback === "function") {
35 options.callback.call(elt);
37 if (typeof elt.clearMessages === "function") {
46 var Toggler = Class.create({
50 this.element.src = this.options.openIcn;
52 document.fire('toggler:close', this);
56 this.element.src = this.options.closeIcn;
58 document.fire('toggler:open', this);
61 toggle: function(evt) {
62 if (evt && typeof evt.stop === "function") {
65 if (this.target.visible()) {
72 initialize: function(target, options) {
73 this.options = Object.extend({
74 openIcn: 'icons/bullet_arrow_right.png',
75 closeIcn: 'icons/bullet_arrow_down.png'
78 this.target = $(target).hide();
79 this.element = new Element("img").setStyle({ border: 'none', // in firefox, in image inside an anchor has a border
80 verticalAlign: "middle"});
81 this.element.observe('click', this.toggle.bindAsEventListener(this));
83 if (this.options.autoOpen) {
91 var Deck = Class.create({
92 initialize: function(elt, options) {
93 this.element = $(elt);
95 this.setIndex(parseInt(this.element.readAttribute("selectedindex") || 0, 10));
97 setIndex: function(idx) {
98 if (idx === this.index) {
102 var childs = this.element.childElements();
103 if (childs.length === 0) {
107 idx = Math.max(0, idx);
108 idx = Math.min(childs.length - 1, idx);
110 childs.each(function(item, i) {
119 getIndex: function() {
125 highlight: function(element, color, timeout) {
127 if (typeof timeout === "undefined") {
130 current = element.getStyle('backgroundColor');
131 Element.setStyle(element, {'backgroundColor': color});
132 Element.setStyle.delay(timeout, element, {'backgroundColor': current});
135 text: function(element, content) {
136 if (typeof content === "undefined") { // getter
137 if (element.nodeType === 8) {
139 } else if (element.nodeType === 3 || element.nodeType === 4) {
140 return element.nodeValue;
142 return $A(element.childNodes).inject("", function(acc, el) {
143 return acc + Element.text(el);
147 var node = document.createTextNode(content);
148 element.update().appendChild(node);
154 Ajax.TimedRequest = Class.create(Ajax.Request, {
159 // see http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html
160 this.transport.onreadystatechange = Prototype.emptyFunction;
161 this.transport.abort();
162 Ajax.activeRequestCount--;
165 initialize: function($super, url, delay, options) {
171 options.onSuccess = options.onSuccess &&
172 options.onSuccess.wrap(function(proceed, transport, json) {
174 window.clearTimeout(this.timeout);
177 if (transport.getStatus() === 0) {
178 this.options.onFailure(transport, json);
180 proceed(transport, json);
184 options.onFailure = options.onFailure &&
185 options.onFailure.wrap(function(proceed, transport, json) {
187 window.clearTimeout(this.timeout);
190 proceed(transport, json);
193 $super(url, options);
196 request: function($super, url) {
197 this.timeout = function() {
198 if (this.options.onFailure) {
199 this.options.onFailure(null);
202 }.bind(this).delay(this.delay);
207 Ajax.Responders.register({
208 // needed for Ajax.TimedRequest.abort to work: see
209 // http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html
211 onComplete: function() {
212 Ajax.activeRequestCount--;
213 if (Ajax.activeRequestCount < 0) {
214 Ajax.activeRequestCount = 0;
219 // wrapper around Form.request that sets up the submit listener, stops the
220 // submit event, calls presubmit function, calls Form.request and calls a
221 // postsubmit function. If form has some visible and activated file inputs,
222 // execute presubmit, but do not send the file with ajax.
223 Element.addMethods('form', {
224 ajaxize : function(form, options) {
227 options = Object.clone(options || {});
229 $(form).observe('submit', function(evt) {
231 reqoptions = Object.clone(options);
232 delete(reqoptions.presubmit);
233 delete(reqoptions.postsubmit);
234 delete(reqoptions.delay);
236 if (Object.isFunction(options.presubmit)) {
237 if (options.presubmit(this) === false) {
238 evt.stop(); // cancel form submission
243 // get list of input file not disabled, and not hidden
244 if (this.getInputs('file').find(function(elt) {
248 while (elt && $(elt).identify() !== this.identify()) {
249 if (!elt.visible()) {
252 elt = elt.parentNode;
256 // form has some file inputs. Do not manage on our own.
260 evt.stop(); // cancel form submission
262 var params = reqoptions.parameters, action = this.readAttribute('action') || '';
264 if (action.blank()) {
265 action = window.location.href;
267 reqoptions.parameters = this.serialize(true);
270 if (Object.isString(params)) {
271 params = params.toQueryParams();
273 Object.extend(reqoptions.parameters, params);
276 if (this.hasAttribute('method') && !reqoptions.method) {
277 reqoptions.method = this.method;
280 if (reqoptions.onFailure) {
281 reqoptions.onFailure = reqoptions.onFailure.wrap(function(proceed, transport, json) {
283 proceed(transport, json);
286 reqoptions.onFailure = function() {
291 if (reqoptions.onSuccess) {
292 reqoptions.onSuccess = reqoptions.onSuccess.wrap(function(proceed, transport, json) {
294 proceed(transport, json);
297 reqoptions.onSuccess = function() {
302 new Ajax.TimedRequest(action, options.delay || 20, reqoptions);
304 if (Object.isFunction(options.postsubmit)) {
305 options.postsubmit(this);
307 Form.getElements(form).each(function(elt) {
314 setfocus: function(form) {
318 error = form.down('.error');
320 tofocus = error.previous('input,textarea');
322 tofocus = form.down('input:not([readonly],[disabled]),textarea:not([readonly][disabled])');
325 if (error && (typeof tofocus.highlight === "function")) {
326 tofocus.highlight('#F08080');
332 checkEmptyElements: function(form, errorMessage) {
334 form.select('.required').each(function(elt) {
335 var id = elt.getAttribute('for'), control = $(id);
339 if (!control.check(function() {
340 return !this.value.strip().empty();
342 results.push(control);
349 Element.addMethods(['input', 'textarea'], {
350 check: function(control, callback, errorMessage) {
351 if (callback.call(control)) {
355 after: new Element("div", {className: 'error'}).update(errorMessage)
360 observe : Element.Methods.observe.wrap(function(proceed, element, eventName, handler) {
361 if (eventName === "contentchange") {
362 proceed(element, 'keyup', function(evt) {
363 if (evt.keyCode === 13) {
366 handler.apply(null, arguments);
368 proceed(element, 'paste', handler.defer.bind(handler));
369 return proceed(element, 'change', handler);
371 return proceed(element, eventName, handler);
374 timedobserve: function(element, callback, delay) {
375 var timeout = null, initialvalue = element.value;
377 if (typeof delay !== "number") {
380 delay = delay * 1000;
382 var canceltimer = function() {
384 clearTimeout(timeout);
388 var resettimer = function() {
390 timeout = setTimeout(triggercallback, delay);
392 var triggercallback = function() {
394 if (initialvalue !== element.value) {
395 initialvalue = element.value;
396 callback.call(element);
400 element.observe('blur', triggercallback).
401 observe('keyup', resettimer).
402 observe('paste', resettimer);
407 Element.addMethods('div', (function() {
408 var supportsTransition = false, endTransitionEventName = null;
410 if (window.addEventListener) { // fails badly in ie: prevents page from loading
411 var div = $(document.createElement('div'));
414 var cleanup = function() {
416 window.clearTimeout(timeout);
418 div.stopObserving('webkitTransitionEnd');
419 div.stopObserving('transitionend');
420 div.stopObserving('oTransitionend');
421 Element.remove.defer(div);
425 var handler = function(e) {
426 supportsTransition = true;
427 endTransitionEventName = e.type;
430 div.observe('webkitTransitionEnd', handler).observe('transitionend', handler) .observe('oTransitionend', handler);
431 div.setStyle({'transitionProperty': 'opacity',
432 'MozTransitionProperty': 'opacity',
433 'WebkitTransitionProperty': 'opacity',
434 'OTransitionProperty': 'opacity',
435 'transitionDuration': '1ms',
436 'MozTransitionDuration': '1ms',
437 'WebkitTransitionDuration': '1ms',
438 'OTransitionDuration': '1ms'});
439 $(document.documentElement).insert(div);
440 Element.setOpacity.defer(div, 0);
441 window.setTimeout(cleanup, 100);
444 function removeMessages(div) {
445 var node = div.firstChild, nextNode;
448 nextNode = node.nextSibling;
449 if (node.nodeType === 3 || node.tagName.toLowerCase() === 'br' || node.textContent || node.innerText) {
450 div.removeChild(node);
457 function hasOpacityTransition(div) {
458 return ([div.getStyle('transition-property'),
459 div.getStyle('-moz-transition-property'),
460 div.getStyle('-webkit-transition-property'),
461 div.getStyle('-o-transition-property')
462 ].join(' ').split(' ').indexOf('opacity') !== -1);
467 if (supportsTransition && hasOpacityTransition(div)) {
468 div.observe(endTransitionEventName, function() {
469 div.stopObserving(endTransitionEventName);
481 // we need to set opacity to 0 before calling hasOpacityTransition
482 // otherwise we trigger mozilla #601190
484 if (supportsTransition && hasOpacityTransition(div)) {
485 // display = '' then opacity = 1;
486 Element.setOpacity.defer(div, 1);
492 function clearMessages(div) {
499 function setMessage(div, message, status) {
502 div.setMessageStatus(status);
505 div.addMessage(message);
510 function addMessage(div, message) {
511 var node = (div.ownerDocument || document).createTextNode(message);
513 if ($A(div.childNodes).filter(function(node) {
514 return (node.nodeType === 3 || node.tagName.toLowerCase() === 'br' || node.textContent || node.innerText);
516 div.insert(new Element('br'));
519 div.appendChild(node);
520 if (!div.visible()) {
526 function setMessageStatus(div, status) {
527 $A(["error", "warn", "info", "success", "optional"]).each(function(clname) {
528 div.removeClassName(clname);
530 if (typeof status === "string") {
531 div.addClassName(status);
533 $A(status).each(function(clname) {
534 div.addClassName(clname);
541 setMessage: setMessage,
542 clearMessages: clearMessages,
543 addMessage: addMessage,
544 setMessageStatus: setMessageStatus