]> dev.renevier.net Git - syj.git/blobdiff - public/js/utils.js
better mobile support
[syj.git] / public / js / utils.js
index b1dd910ccaee06c62f78b0528ddc3716e5670354..8936797b79db1080f973fde7d49764eeaa44a1be 100644 (file)
@@ -1,4 +1,4 @@
-/*  This file is part of Syj, Copyright (c) 2010 Arnaud Renevier,
+/*  This file is part of Syj, Copyright (c) 2010-2011 Arnaud Renevier,
     and is published under the AGPL license. */
 
 var CloseBtn = Class.create({
@@ -10,22 +10,78 @@ var CloseBtn = Class.create({
             return;
         }
 
+        if (typeof options !== "object") {
+            options = {};
+        }
+
         style = Object.extend({
-            float: "right",
+            'float': "right",
             margin: "2px",
             fontWeight: "bold",
             padding: "0px"
-        }, typeof options === "object" ? options.style: {});
+        }, options.style);
 
-        imgsrc = (options && options.closeBtnSrc) || "icons/close.png";
+        imgsrc = (options.closeBtnSrc) || "icons/close.png";
         btn = new Element("input", { type: "image", src: imgsrc, alt: "X"}).setStyle(style);
         elt.insert({top: btn});
         btn.observe("click", function(evt) {
+            evt.stop();
+            if (evt.detail === 0) { // it's not a real click, possibly a submit event
+                return;
+            }
+            if (typeof options.callback === "function") {
+                options.callback.call(elt);
+            }
             elt.hide();
         });
     }
 });
 
+var Toggler = Class.create({
+    options: {},
+
+    close: function() {
+        this.element.src = this.options.openIcn;
+        this.target.hide();
+        document.fire('toggler:close', this);
+    },
+
+    open: function() {
+        this.element.src = this.options.closeIcn;
+        this.target.show();
+        document.fire('toggler:open', this);
+    },
+
+    toggle: function(evt) {
+        if (evt && typeof evt.stop === "function") {
+            evt.stop();
+        }
+        if (this.target.visible()) {
+            this.close();
+        } else {
+            this.open();
+        }
+    },
+
+    initialize: function(target, options) {
+        this.options = Object.extend({
+                openIcn: 'icons/bullet_arrow_right.png',
+                closeIcn: 'icons/bullet_arrow_down.png'
+            }, options);
+
+        this.target = $(target).hide();
+        this.element = new Element("img").setStyle({ border: 'none',  // in firefox, in image inside an anchor has a border
+                                                    verticalAlign: "middle"});
+        this.element.observe('click', this.toggle.bindAsEventListener(this));
+
+        if (this.options.autoOpen) {
+            this.open();
+        } else {
+            this.close();
+        }
+    }
+});
+
 var Deck = Class.create({
     initialize: function(elt, options) {
         this.element = $(elt);
@@ -69,6 +125,23 @@ Element.addMethods({
         Element.setStyle(element, {'backgroundColor': color});
         Element.setStyle.delay(timeout, element, {'backgroundColor': current});
         return element;
+    },
+    text: function(element, content) {
+        if (typeof content === "undefined") { // getter
+            if (element.nodeType === 8) {
+                return "";
+            } else if (element.nodeType === 3 || element.nodeType === 4)  {
+                return element.nodeValue;
+            } else {
+                return $A(element.childNodes).inject("", function(acc, el) {
+                    return acc + Element.text(el);
+                 });
+            }
+        } else { // setter
+            var node = document.createTextNode(content);
+            element.update().appendChild(node);
+            return element;
+        }
     }
 });
 
@@ -85,8 +158,12 @@ Ajax.TimedRequest = Class.create(Ajax.Request, {
 
     initialize: function($super, url, delay, options) {
         this.delay = delay;
+        if (!options) {
+            options = {};
+        }
 
-        options.onSuccess = options.onSuccess.wrap(function(proceed, transport, json) {
+        options.onSuccess = options.onSuccess &&
+            options.onSuccess.wrap(function(proceed, transport, json) {
             if (this.timeout) {
                 window.clearTimeout(this.timeout);
                 this.timeout = null;
@@ -98,7 +175,8 @@ Ajax.TimedRequest = Class.create(Ajax.Request, {
             }
         }).bind(this);
 
-        options.onFailure = options.onFailure.wrap(function(proceed, transport, json) {
+        options.onFailure = options.onFailure &&
+            options.onFailure.wrap(function(proceed, transport, json) {
             if (this.timeout) {
                 window.clearTimeout(this.timeout);
                 this.timeout = null;
@@ -110,10 +188,12 @@ Ajax.TimedRequest = Class.create(Ajax.Request, {
     },
 
     request: function($super, url) {
-        this.timeout = (function() {
-            this.options.onFailure(null);
+        this.timeout = function() {
+            if (this.options.onFailure) {
+                this.options.onFailure(null);
+            }
             this.abort();
-        }).bind(this).delay(this.delay);
+        }.bind(this).delay(this.delay);
         $super(url);
     }
 });
@@ -132,7 +212,8 @@ Ajax.Responders.register({
 
 // wrapper around Form.request that sets up the submit listener, stops the
 // submit event, calls presubmit function, calls Form.request and calls a
-// postsubmit function
+// postsubmit function. If form has some visible and activated file inputs,
+// execute presubmit, but do not send the file with ajax.
 Element.addMethods('form', {
     ajaxize : function(form, options) {
         var reqoptions;
@@ -140,7 +221,6 @@ Element.addMethods('form', {
         options = Object.clone(options || {});
 
         $(form).observe('submit', function(evt) {
-            evt.stop(); // cancel form submission
 
             reqoptions = Object.clone(options);
             delete(reqoptions.presubmit);
@@ -149,10 +229,30 @@ Element.addMethods('form', {
 
             if (Object.isFunction(options.presubmit)) {
                 if (options.presubmit(this) === false) {
+                    evt.stop(); // cancel form submission
                     return;
                 }
             }
 
+            // get list of input file not disabled, and not hidden
+            if (this.getInputs('file').find(function(elt) {
+                if (elt.disabled) {
+                    return false;
+                }
+                while (elt && $(elt).identify() !== this.identify()) {
+                    if (!elt.visible()) {
+                        return false;
+                    }
+                    elt = elt.parentNode;
+                }
+                return true;
+             }.bind(this))) {
+                // form has some file inputs. Do not manage on our own.
+                return;
+            }
+
+            evt.stop(); // cancel form submission
+
             var params = reqoptions.parameters, action = this.readAttribute('action') || '';
 
             if (action.blank()) {
@@ -171,15 +271,41 @@ Element.addMethods('form', {
                 reqoptions.method = this.method;
             }
 
+            if (reqoptions.onFailure) {
+                reqoptions.onFailure = reqoptions.onFailure.wrap(function(proceed, transport, json) {
+                    form.enable();
+                    proceed(transport, json);
+                });
+            } else {
+                reqoptions.onFailure = function() {
+                    form.enable();
+                };
+            }
+
+            if (reqoptions.onSuccess) {
+                reqoptions.onSuccess = reqoptions.onSuccess.wrap(function(proceed, transport, json) {
+                    form.enable();
+                    proceed(transport, json);
+                });
+            } else {
+                reqoptions.onSuccess = function() {
+                    form.enable();
+                };
+            }
+
             new Ajax.TimedRequest(action, options.delay || 20, reqoptions);
 
             if (Object.isFunction(options.postsubmit)) {
                 options.postsubmit(this);
             }
+            Form.getElements(form).each(function(elt) {
+                elt.blur();
+                elt.disable();
+            });
         });
     },
 
-    focus: function(form) {
+    setfocus: function(form) {
         var tofocus, error;
 
         tofocus = null;
@@ -223,6 +349,52 @@ Element.addMethods(['input', 'textarea'], {
             after: new Element("div", {className: 'error'}).update(errorMessage)
         });
         return false;
+    },
+
+    observe : Element.Methods.observe.wrap(function(proceed, element, eventName, handler) {
+        if (eventName === "contentchange") {
+            proceed(element, 'keyup', function(evt) {
+                if (evt.keyCode === 13) {
+                    return;
+                }
+                handler.apply(null, arguments);
+            });
+            proceed(element, 'paste', handler.defer.bind(handler));
+            return proceed(element, 'change', handler);
+        }
+        return proceed(element, eventName, handler);
+    }),
+
+    timedobserve: function(element, callback, delay) {
+        var timeout = null, initialvalue = element.value;
+
+        if (typeof delay !== "number") {
+            delay = 0.5;
+        }
+        delay = delay * 1000;
+
+        var canceltimer = function() {
+            if (timeout) {
+                clearTimeout(timeout);
+                timeout = null;
+            }
+        };
+        var resettimer = function() {
+            canceltimer();
+            timeout = setTimeout(triggercallback, delay);
+        };
+        var triggercallback = function() {
+            canceltimer();
+            if (initialvalue !== element.value) {
+                initialvalue = element.value;
+                callback.call(element);
+            }
+        };
+
+        element.observe('blur', triggercallback).
+             observe('keyup', resettimer).
+             observe('paste', resettimer);
+        return element;
     }
 });
 
@@ -243,7 +415,7 @@ Element.addMethods('div', {
 
         while (node) {
             nextNode = node.nextSibling;
-            if (node.nodeType === 3 || node.tagName.toLowerCase() === 'br') {
+            if (node.nodeType === 3 || node.tagName.toLowerCase() === 'br' || node.textContent || node.innerText) {
                 div.removeChild(node);
             }
                 node = nextNode;
@@ -254,18 +426,28 @@ Element.addMethods('div', {
 
     addMessage: function(div, message) {
         var node = (div.ownerDocument || document).createTextNode(message);
-        if (!div.empty()) {
+
+        if ($A(div.childNodes).filter(function(node) {
+                return (node.nodeType === 3 || node.tagName.toLowerCase() === 'br' || node.textContent || node.innerText);
+             }).length) {
             div.insert(new Element('br'));
         }
+
         div.appendChild(node);
         return div.show();
     },
 
     setMessageStatus: function(div, status) {
-        return div.removeClassName('error').
-                removeClassName('warn').
-                removeClassName('info').
-                removeClassName('success').
-                addClassName(status);
+        $A(["error", "warn", "info", "success", "optional"]).each(function(clname) {
+            div.removeClassName(clname);
+        });
+        if (typeof status === "string") {
+            div.addClassName(status);
+        } else {
+            $A(status).each(function(clname) {
+                div.addClassName(clname);
+            });
+        }
+        return div;
     }
 });