1 /* This file is part of Syj, Copyright (c) 2010 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);
40 var Toggler = Class.create({
44 this.element.src = this.options.openIcn;
46 document.fire('toggler:close', this);
50 this.element.src = this.options.closeIcn;
52 document.fire('toggler:open', this);
55 toggle: function(evt) {
56 if (evt && typeof evt.stop === "function") {
59 if (this.target.visible()) {
66 initialize: function(target, options) {
67 this.options = Object.extend({
68 openIcn: 'icons/bullet_arrow_right.png',
69 closeIcn: 'icons/bullet_arrow_down.png'
72 this.target = $(target).hide();
73 this.element = new Element("img").setStyle({ border: 'none', // in firefox, in image inside an anchor has a border
74 verticalAlign: "middle"});
75 this.element.observe('click', this.toggle.bindAsEventListener(this));
77 if (this.options.autoOpen) {
85 var Deck = Class.create({
86 initialize: function(elt, options) {
87 this.element = $(elt);
89 this.setIndex(parseInt(this.element.readAttribute("selectedindex") || 0, 10));
91 setIndex: function(idx) {
92 if (idx === this.index) {
96 var childs = this.element.childElements();
97 if (childs.length === 0) {
101 idx = Math.max(0, idx);
102 idx = Math.min(childs.length - 1, idx);
104 childs.each(function(item, i) {
113 getIndex: function() {
119 highlight: function(element, color, timeout) {
121 if (typeof timeout === "undefined") {
124 current = element.getStyle('backgroundColor');
125 Element.setStyle(element, {'backgroundColor': color});
126 Element.setStyle.delay(timeout, element, {'backgroundColor': current});
129 text: function(element, content) {
130 if (typeof content === "undefined") { // getter
131 if (element.nodeType === 8) {
133 } else if (element.nodeType === 3 || element.nodeType === 4) {
134 return element.nodeValue;
136 return $A(element.childNodes).inject("", function(acc, el) {
137 return acc + Element.text(el);
141 var node = document.createTextNode(content);
142 element.update().appendChild(node);
148 Ajax.TimedRequest = Class.create(Ajax.Request, {
153 // see http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html
154 this.transport.onreadystatechange = Prototype.emptyFunction;
155 this.transport.abort();
156 Ajax.activeRequestCount--;
159 initialize: function($super, url, delay, options) {
165 options.onSuccess = options.onSuccess &&
166 options.onSuccess.wrap(function(proceed, transport, json) {
168 window.clearTimeout(this.timeout);
171 if (transport.getStatus() === 0) {
172 this.options.onFailure(transport, json);
174 proceed(transport, json);
178 options.onFailure = options.onFailure &&
179 options.onFailure.wrap(function(proceed, transport, json) {
181 window.clearTimeout(this.timeout);
184 proceed(transport, json);
187 $super(url, options);
190 request: function($super, url) {
191 this.timeout = function() {
192 if (this.options.onFailure) {
193 this.options.onFailure(null);
196 }.bind(this).delay(this.delay);
201 Ajax.Responders.register({
202 // needed for Ajax.TimedRequest.abort to work: see
203 // http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html
205 onComplete: function() {
206 Ajax.activeRequestCount--;
207 if (Ajax.activeRequestCount < 0) {
208 Ajax.activeRequestCount = 0;
213 // wrapper around Form.request that sets up the submit listener, stops the
214 // submit event, calls presubmit function, calls Form.request and calls a
215 // postsubmit function. If form has some visible and activated file inputs,
216 // execute presubmit, but do not send the file with ajax.
217 Element.addMethods('form', {
218 ajaxize : function(form, options) {
221 options = Object.clone(options || {});
223 $(form).observe('submit', function(evt) {
225 reqoptions = Object.clone(options);
226 delete(reqoptions.presubmit);
227 delete(reqoptions.postsubmit);
228 delete(reqoptions.delay);
230 if (Object.isFunction(options.presubmit)) {
231 if (options.presubmit(this) === false) {
232 evt.stop(); // cancel form submission
237 // get list of input file not disabled, and not hidden
238 if (this.getInputs('file').find(function(elt) {
242 while (elt && $(elt).identify() !== this.identify()) {
243 if (!elt.visible()) {
246 elt = elt.parentNode;
250 // form has some file inputs. Do not manage on our own.
254 evt.stop(); // cancel form submission
256 var params = reqoptions.parameters, action = this.readAttribute('action') || '';
258 if (action.blank()) {
259 action = window.location.href;
261 reqoptions.parameters = this.serialize(true);
264 if (Object.isString(params)) {
265 params = params.toQueryParams();
267 Object.extend(reqoptions.parameters, params);
270 if (this.hasAttribute('method') && !reqoptions.method) {
271 reqoptions.method = this.method;
274 if (reqoptions.onFailure) {
275 reqoptions.onFailure = reqoptions.onFailure.wrap(function(proceed, transport, json) {
277 proceed(transport, json);
280 reqoptions.onFailure = function() {
285 if (reqoptions.onSuccess) {
286 reqoptions.onSuccess = reqoptions.onSuccess.wrap(function(proceed, transport, json) {
288 proceed(transport, json);
291 reqoptions.onSuccess = function() {
296 new Ajax.TimedRequest(action, options.delay || 20, reqoptions);
298 if (Object.isFunction(options.postsubmit)) {
299 options.postsubmit(this);
301 Form.getElements(form).each(function(elt) {
308 setfocus: function(form) {
312 error = form.down('.error');
314 tofocus = error.previous('input,textarea');
316 tofocus = form.down('input:not([readonly],[disabled]),textarea:not([readonly][disabled])');
319 if (error && (typeof tofocus.highlight === "function")) {
320 tofocus.highlight('#F08080');
326 checkEmptyElements: function(form, errorMessage) {
328 form.select('.required').each(function(elt) {
329 var id = elt.getAttribute('for'), control = $(id);
333 if (!control.check(function() {
334 return !this.value.strip().empty();
336 results.push(control);
343 Element.addMethods(['input', 'textarea'], {
344 check: function(control, callback, errorMessage) {
345 if (callback.call(control)) {
349 after: new Element("div", {className: 'error'}).update(errorMessage)
354 observe : Element.Methods.observe.wrap(function(proceed, element, eventName, handler) {
355 if (eventName === "contentchange") {
356 proceed(element, 'keyup', function(evt) {
357 if (evt.keyCode === 13) {
360 handler.apply(null, arguments);
362 proceed(element, 'paste', handler.defer.bind(handler));
363 return proceed(element, 'change', handler);
365 return proceed(element, eventName, handler);
368 timedobserve: function(element, callback, delay) {
369 var timeout = null, initialvalue = element.value;
371 if (typeof delay !== "number") {
374 delay = delay * 1000;
376 var canceltimer = function() {
378 clearTimeout(timeout);
382 var resettimer = function() {
384 timeout = setTimeout(triggercallback, delay);
386 var triggercallback = function() {
388 if (initialvalue !== element.value) {
389 initialvalue = element.value;
390 callback.call(element);
394 element.observe('blur', triggercallback).
395 observe('keyup', resettimer).
396 observe('paste', resettimer);
401 Element.addMethods('div', {
402 setMessage: function(div, message, status) {
405 div.setMessageStatus(status);
408 div.addMessage(message);
413 clearMessages: function(div) {
414 var node = div.firstChild, nextNode;
417 nextNode = node.nextSibling;
418 if (node.nodeType === 3 || node.tagName.toLowerCase() === 'br' || node.textContent || node.innerText) {
419 div.removeChild(node);
427 addMessage: function(div, message) {
428 var node = (div.ownerDocument || document).createTextNode(message);
430 if ($A(div.childNodes).filter(function(node) {
431 return (node.nodeType === 3 || node.tagName.toLowerCase() === 'br' || node.textContent || node.innerText);
433 div.insert(new Element('br'));
436 div.appendChild(node);
440 setMessageStatus: function(div, status) {
441 return div.removeClassName('error').
442 removeClassName('warn').
443 removeClassName('info').
444 removeClassName('success').
445 addClassName(status);