1 /* This file is part of Syj, Copyright (c) 2010-2011 Arnaud Renevier,
2 and is published under the AGPL license. */
4 var CloseBtn = Class.create({
5 initialize: function(elt, options) {
6 var btn, imgsrc, style;
13 if (typeof options !== "object") {
17 style = Object.extend({
24 imgsrc = (options.closeBtnSrc) || "icons/close.png";
25 btn = new Element("input", { type: "image", src: imgsrc, alt: "X"}).setStyle(style);
26 elt.insert({top: btn});
27 btn.observe("click", function(evt) {
29 if (evt.detail === 0) { // it's not a real click, possibly a submit event
32 if (typeof options.callback === "function") {
33 options.callback.call(elt);
35 if (typeof elt.clearMessages === "function") {
44 var Toggler = Class.create({
48 this.element.src = this.options.openIcn;
50 document.fire('toggler:close', this);
54 this.element.src = this.options.closeIcn;
56 document.fire('toggler:open', this);
59 toggle: function(evt) {
60 if (evt && typeof evt.stop === "function") {
63 if (this.target.visible()) {
70 initialize: function(target, options) {
71 this.options = Object.extend({
72 openIcn: 'icons/bullet_arrow_right.png',
73 closeIcn: 'icons/bullet_arrow_down.png'
76 this.target = $(target).hide();
77 this.element = new Element("img").setStyle({ border: 'none', // in firefox, in image inside an anchor has a border
78 verticalAlign: "middle"});
79 this.element.observe('click', this.toggle.bindAsEventListener(this));
81 if (this.options.autoOpen) {
89 var Deck = Class.create({
90 initialize: function(elt, options) {
91 this.element = $(elt);
93 this.setIndex(parseInt(this.element.readAttribute("selectedindex") || 0, 10));
95 setIndex: function(idx) {
96 if (idx === this.index) {
100 var childs = this.element.childElements();
101 if (childs.length === 0) {
105 idx = Math.max(0, idx);
106 idx = Math.min(childs.length - 1, idx);
108 childs.each(function(item, i) {
117 getIndex: function() {
123 highlight: function(element, color, timeout) {
125 if (typeof timeout === "undefined") {
128 current = element.getStyle('backgroundColor');
129 Element.setStyle(element, {'backgroundColor': color});
130 Element.setStyle.delay(timeout, element, {'backgroundColor': current});
133 text: function(element, content) {
134 if (typeof content === "undefined") { // getter
135 if (element.nodeType === 8) {
137 } else if (element.nodeType === 3 || element.nodeType === 4) {
138 return element.nodeValue;
140 return $A(element.childNodes).inject("", function(acc, el) {
141 return acc + Element.text(el);
145 var node = document.createTextNode(content);
146 element.update().appendChild(node);
152 Ajax.TimedRequest = Class.create(Ajax.Request, {
157 // see http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html
158 this.transport.onreadystatechange = Prototype.emptyFunction;
159 this.transport.abort();
160 Ajax.activeRequestCount--;
163 initialize: function($super, url, delay, options) {
169 options.onSuccess = options.onSuccess &&
170 options.onSuccess.wrap(function(proceed, transport, json) {
172 window.clearTimeout(this.timeout);
175 if (transport.getStatus() === 0) {
176 this.options.onFailure(transport, json);
178 proceed(transport, json);
182 options.onFailure = options.onFailure &&
183 options.onFailure.wrap(function(proceed, transport, json) {
185 window.clearTimeout(this.timeout);
188 proceed(transport, json);
191 $super(url, options);
194 request: function($super, url) {
195 this.timeout = function() {
196 if (this.options.onFailure) {
197 this.options.onFailure(null);
200 }.bind(this).delay(this.delay);
205 Ajax.Responders.register({
206 // needed for Ajax.TimedRequest.abort to work: see
207 // http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html
209 onComplete: function() {
210 Ajax.activeRequestCount--;
211 if (Ajax.activeRequestCount < 0) {
212 Ajax.activeRequestCount = 0;
217 // wrapper around Form.request that sets up the submit listener, stops the
218 // submit event, calls presubmit function, calls Form.request and calls a
219 // postsubmit function. If form has some visible and activated file inputs,
220 // execute presubmit, but do not send the file with ajax.
221 Element.addMethods('form', {
222 ajaxize : function(form, options) {
225 options = Object.clone(options || {});
227 $(form).observe('submit', function(evt) {
229 reqoptions = Object.clone(options);
230 delete(reqoptions.presubmit);
231 delete(reqoptions.postsubmit);
232 delete(reqoptions.delay);
234 if (Object.isFunction(options.presubmit)) {
235 if (options.presubmit(this) === false) {
236 evt.stop(); // cancel form submission
241 // get list of input file not disabled, and not hidden
242 if (this.getInputs('file').find(function(elt) {
246 while (elt && $(elt).identify() !== this.identify()) {
247 if (!elt.visible()) {
250 elt = elt.parentNode;
254 // form has some file inputs. Do not manage on our own.
258 evt.stop(); // cancel form submission
260 var params = reqoptions.parameters, action = this.readAttribute('action') || '';
262 if (action.blank()) {
263 action = window.location.href;
265 reqoptions.parameters = this.serialize(true);
268 if (Object.isString(params)) {
269 params = params.toQueryParams();
271 Object.extend(reqoptions.parameters, params);
274 if (this.hasAttribute('method') && !reqoptions.method) {
275 reqoptions.method = this.method;
278 if (reqoptions.onFailure) {
279 reqoptions.onFailure = reqoptions.onFailure.wrap(function(proceed, transport, json) {
281 proceed(transport, json);
284 reqoptions.onFailure = function() {
289 if (reqoptions.onSuccess) {
290 reqoptions.onSuccess = reqoptions.onSuccess.wrap(function(proceed, transport, json) {
292 proceed(transport, json);
295 reqoptions.onSuccess = function() {
300 new Ajax.TimedRequest(action, options.delay || 20, reqoptions);
302 if (Object.isFunction(options.postsubmit)) {
303 options.postsubmit(this);
305 Form.getElements(form).each(function(elt) {
312 setfocus: function(form) {
316 error = form.down('.error');
318 tofocus = error.previous('input,textarea');
320 tofocus = form.down('input:not([readonly],[disabled]),textarea:not([readonly][disabled])');
323 if (error && (typeof tofocus.highlight === "function")) {
324 tofocus.highlight('#F08080');
330 checkEmptyElements: function(form, errorMessage) {
332 form.select('.required').each(function(elt) {
333 var id = elt.getAttribute('for'), control = $(id);
337 if (!control.check(function() {
338 return !this.value.strip().empty();
340 results.push(control);
347 Element.addMethods(['input', 'textarea'], {
348 check: function(control, callback, errorMessage) {
349 if (callback.call(control)) {
353 after: new Element("div", {className: 'error'}).update(errorMessage)
358 observe : Element.Methods.observe.wrap(function(proceed, element, eventName, handler) {
359 if (eventName === "contentchange") {
360 proceed(element, 'keyup', function(evt) {
361 if (evt.keyCode === 13) {
364 handler.apply(null, arguments);
366 proceed(element, 'paste', handler.defer.bind(handler));
367 return proceed(element, 'change', handler);
369 return proceed(element, eventName, handler);
372 timedobserve: function(element, callback, delay) {
373 var timeout = null, initialvalue = element.value;
375 if (typeof delay !== "number") {
378 delay = delay * 1000;
380 var canceltimer = function() {
382 clearTimeout(timeout);
386 var resettimer = function() {
388 timeout = setTimeout(triggercallback, delay);
390 var triggercallback = function() {
392 if (initialvalue !== element.value) {
393 initialvalue = element.value;
394 callback.call(element);
398 element.observe('blur', triggercallback).
399 observe('keyup', resettimer).
400 observe('paste', resettimer);
405 Element.addMethods('div', (function() {
406 var supportsTransition = false, endTransitionEventName = null;
408 if (window.addEventListener) { // fails badly in ie: prevents page from loading
409 var div = $(document.createElement('div'));
412 var cleanup = function() {
414 window.clearTimeout(timeout);
416 div.stopObserving('webkitTransitionEnd');
417 div.stopObserving('transitionend');
418 div.stopObserving('oTransitionend');
419 Element.remove.defer(div);
423 var handler = function(e) {
424 supportsTransition = true;
425 endTransitionEventName = e.type;
428 div.observe('webkitTransitionEnd', handler).observe('transitionend', handler) .observe('oTransitionend', handler);
429 div.setStyle({'transitionProperty': 'opacity',
430 'MozTransitionProperty': 'opacity',
431 'WebkitTransitionProperty': 'opacity',
432 'OTransitionProperty': 'opacity',
433 'transitionDuration': '1ms',
434 'MozTransitionDuration': '1ms',
435 'WebkitTransitionDuration': '1ms',
436 'OTransitionDuration': '1ms'});
437 $(document.documentElement).insert(div);
438 Element.setOpacity.defer(div, 0);
439 window.setTimeout(cleanup, 100);
442 function removeMessages(div) {
443 var node = div.firstChild, nextNode;
446 nextNode = node.nextSibling;
447 if (node.nodeType === 3 || node.tagName.toLowerCase() === 'br' || node.textContent || node.innerText) {
448 div.removeChild(node);
455 function hasOpacityTransition(div) {
456 return ([div.getStyle('transition-property'),
457 div.getStyle('-moz-transition-property'),
458 div.getStyle('-webkit-transition-property'),
459 div.getStyle('-o-transition-property')
460 ].join(' ').split(' ').indexOf('opacity') !== -1);
465 if (supportsTransition && hasOpacityTransition(div)) {
466 div.observe(endTransitionEventName, function() {
467 div.stopObserving(endTransitionEventName);
479 // we need to set opacity to 0 before calling hasOpacityTransition
480 // otherwise we trigger mozilla #601190
482 if (supportsTransition && hasOpacityTransition(div)) {
483 // display = '' then opacity = 1;
484 Element.setOpacity.defer(div, 1);
490 function clearMessages(div) {
497 function setMessage(div, message, status) {
500 div.setMessageStatus(status);
503 div.addMessage(message);
508 function addMessage(div, message) {
509 var node = (div.ownerDocument || document).createTextNode(message);
511 if ($A(div.childNodes).filter(function(node) {
512 return (node.nodeType === 3 || node.tagName.toLowerCase() === 'br' || node.textContent || node.innerText);
514 div.insert(new Element('br'));
517 div.appendChild(node);
518 if (!div.visible()) {
524 function setMessageStatus(div, status) {
525 $A(["error", "warn", "info", "success", "optional"]).each(function(clname) {
526 div.removeClassName(clname);
528 if (typeof status === "string") {
529 div.addClassName(status);
531 $A(status).each(function(clname) {
532 div.addClassName(clname);
539 setMessage: setMessage,
540 clearMessages: clearMessages,
541 addMessage: addMessage,
542 setMessageStatus: setMessageStatus