X-Git-Url: https://dev.renevier.net/?p=syj.git;a=blobdiff_plain;f=public%2Fjs%2Futils.js;h=a81262fac5a89db5ae9151831225f078209c66fa;hp=b1dd910ccaee06c62f78b0528ddc3716e5670354;hb=79315e38eea4482f92049e0d1b40fb35f8f4a3ff;hpb=21102dd850917ee1973c29864e2268b45b710c44 diff --git a/public/js/utils.js b/public/js/utils.js index b1dd910..a81262f 100644 --- a/public/js/utils.js +++ b/public/js/utils.js @@ -1,6 +1,8 @@ -/* 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. */ +"use strict"; + var CloseBtn = Class.create({ initialize: function(elt, options) { var btn, imgsrc, style; @@ -10,22 +12,82 @@ 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) { - elt.hide(); + 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); + } + if (typeof elt.clearMessages === "function") { + elt.clearMessages(); + } else { + 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 +131,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 +164,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 +181,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 +194,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 +218,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 +227,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 +235,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 +277,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,49 +355,195 @@ 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; } }); -Element.addMethods('div', { - setMessage: function(div, message, status) { - div.clearMessages(); - if (status) { - div.setMessageStatus(status); +Element.addMethods('div', (function() { + var supportsTransition = false, endTransitionEventName = null; + + if (window.addEventListener) { // fails badly in ie: prevents page from loading + var div = $(document.createElement('div')); + var timeout = null; + + var cleanup = function() { + if (timeout) { + window.clearTimeout(timeout); + timeout = null; + div.stopObserving('webkitTransitionEnd'); + div.stopObserving('transitionend'); + div.stopObserving('oTransitionend'); + Element.remove.defer(div); + } } - if (message) { - div.addMessage(message); + + var handler = function(e) { + supportsTransition = true; + endTransitionEventName = e.type; + cleanup(); } - return div; - }, + div.observe('webkitTransitionEnd', handler).observe('transitionend', handler) .observe('oTransitionend', handler); + div.setStyle({'transitionProperty': 'opacity', + 'MozTransitionProperty': 'opacity', + 'WebkitTransitionProperty': 'opacity', + 'OTransitionProperty': 'opacity', + 'transitionDuration': '1ms', + 'MozTransitionDuration': '1ms', + 'WebkitTransitionDuration': '1ms', + 'OTransitionDuration': '1ms'}); + $(document.documentElement).insert(div); + Element.setOpacity.defer(div, 0); + window.setTimeout(cleanup, 100); + } - clearMessages: function(div) { + function removeMessages(div) { var node = div.firstChild, nextNode; 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; } + return div; + }; + + function hasOpacityTransition(div) { + return ([div.getStyle('transition-property'), + div.getStyle('-moz-transition-property'), + div.getStyle('-webkit-transition-property'), + div.getStyle('-o-transition-property') + ].join(' ').split(' ').indexOf('opacity') !== -1); + } + + function hide(div) { + div = $(div); + if (supportsTransition && hasOpacityTransition(div)) { + div.observe(endTransitionEventName, function() { + div.stopObserving(endTransitionEventName); + if (!div.getOpacity()) { // in case show has been called in-between + div.hide(); + } + }); + div.setOpacity(0); + } else { + div.hide(); + } + } + + function show(div) { + div = $(div); + div.show(); + // we need to set opacity to 0 before calling hasOpacityTransition + // otherwise we trigger mozilla #601190 + div.setOpacity(0); + if (supportsTransition && hasOpacityTransition(div)) { + // display = '' then opacity = 1; + Element.setOpacity.defer(div, 1); + } else { + div.setOpacity(1); + } + } + function clearMessages(div) { + if (div.getOpacity()) { + hide(div); + } return div; - }, + } - addMessage: function(div, message) { + function setMessage(div, message, status) { + removeMessages(div); + if (status) { + div.setMessageStatus(status); + } + if (message) { + div.addMessage(message); + } + return div; + } + + function addMessage(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(); - }, + if (!div.getOpacity()) { + show(div); + } + return div; + } - setMessageStatus: function(div, status) { - return div.removeClassName('error'). - removeClassName('warn'). - removeClassName('info'). - removeClassName('success'). - addClassName(status); + function setMessageStatus(div, 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; } -}); + + return { + setMessage: setMessage, + clearMessages: clearMessages, + addMessage: addMessage, + setMessageStatus: setMessageStatus + }; + +})());