diff --git a/www/extras/js/at/AjaxRequest.js b/www/extras/js/at/AjaxRequest.js new file mode 100644 index 000000000..49ec5da1d --- /dev/null +++ b/www/extras/js/at/AjaxRequest.js @@ -0,0 +1,128 @@ +// =================================================================== +// Author: Matt Kruse +// WWW: http://www.AjaxToolbox.com/ +// +// NOTICE: You may use this code for any purpose, commercial or +// private, without any further permission from the author. You may +// remove this notice from your final code if you wish, however it is +// appreciated by the author if at least my web site address is kept. +// +// You may *NOT* re-distribute this code in any way except through its +// use. That means, you can include it in your product, or your web +// site, or any other form where the code is actually being used. You +// may not put the plain javascript up on your site for download or +// include it in your javascript libraries for download. +// If you wish to share this code with others, please just point them +// to the URL instead. +// Please DO NOT link directly to my .js files from your site. Copy +// the files to your server and use them there. Thank you. +// =================================================================== + +function AjaxRequest(){var req =new Object(); +req.timeout =null; +req.generateUniqueUrl =true; +req.url =window.location.href; +req.method ="GET"; +req.async =true; +req.username =null; +req.password =null; +req.parameters =new Object(); +req.requestIndex =AjaxRequest.numAjaxRequests++; +req.responseReceived =false; +req.groupName =null; +req.queryString =""; +req.responseText =null; +req.responseXML =null; +req.status =null; +req.statusText =null; +req.aborted =false; +req.xmlHttpRequest =null; +req.onTimeout=null; +req.onLoading=null; +req.onLoaded=null; +req.onInteractive=null; +req.onComplete=null; +req.onSuccess=null; +req.onError=null; +req.onGroupBegin=null; +req.onGroupEnd=null; +req.xmlHttpRequest =AjaxRequest.getXmlHttpRequest(); +if(req.xmlHttpRequest==null){return null;}req.xmlHttpRequest.onreadystatechange = +function(){if(req==null || req.xmlHttpRequest==null){return;}if(req.xmlHttpRequest.readyState==1){req.onLoadingInternal(req);}if(req.xmlHttpRequest.readyState==2){req.onLoadedInternal(req);}if(req.xmlHttpRequest.readyState==3){req.onInteractiveInternal(req);}if(req.xmlHttpRequest.readyState==4){req.onCompleteInternal(req);}}; +req.onLoadingInternalHandled=false; +req.onLoadedInternalHandled=false; +req.onInteractiveInternalHandled=false; +req.onCompleteInternalHandled=false; +req.onLoadingInternal= +function(){if(req.onLoadingInternalHandled){return;}AjaxRequest.numActiveAjaxRequests++; +if(AjaxRequest.numActiveAjaxRequests==1 && typeof(window['AjaxRequestBegin'])=="function"){AjaxRequestBegin();}if(req.groupName!=null){if(typeof(AjaxRequest.numActiveAjaxGroupRequests[req.groupName])=="undefined"){AjaxRequest.numActiveAjaxGroupRequests[req.groupName] =0;}AjaxRequest.numActiveAjaxGroupRequests[req.groupName]++; +if(AjaxRequest.numActiveAjaxGroupRequests[req.groupName]==1 && typeof(req.onGroupBegin)=="function"){req.onGroupBegin(req.groupName);}}if(typeof(req.onLoading)=="function"){req.onLoading(req);}req.onLoadingInternalHandled=true;}; +req.onLoadedInternal= +function(){if(req.onLoadedInternalHandled){return;}if(typeof(req.onLoaded)=="function"){req.onLoaded(req);}req.onLoadedInternalHandled=true;}; +req.onInteractiveInternal= +function(){if(req.onInteractiveInternalHandled){return;}if(typeof(req.onInteractive)=="function"){req.onInteractive(req);}req.onInteractiveInternalHandled=true;}; +req.onCompleteInternal= +function(){if(req.onCompleteInternalHandled || req.aborted){return;}req.onCompleteInternalHandled=true; +AjaxRequest.numActiveAjaxRequests--; +if(AjaxRequest.numActiveAjaxRequests==0 && typeof(window['AjaxRequestEnd'])=="function"){AjaxRequestEnd(req.groupName);}if(req.groupName!=null){AjaxRequest.numActiveAjaxGroupRequests[req.groupName]--; +if(AjaxRequest.numActiveAjaxGroupRequests[req.groupName]==0 && typeof(req.onGroupEnd)=="function"){req.onGroupEnd(req.groupName);}}req.responseReceived =true; +req.status =req.xmlHttpRequest.status; +req.statusText =req.xmlHttpRequest.statusText; +req.responseText =req.xmlHttpRequest.responseText; +req.responseXML =req.xmlHttpRequest.responseXML; +if(typeof(req.onComplete)=="function"){req.onComplete(req);}if(req.xmlHttpRequest.status==200 && typeof(req.onSuccess)=="function"){req.onSuccess(req);}else if(typeof(req.onError)=="function"){req.onError(req);}delete req.xmlHttpRequest['onreadystatechange']; +req.xmlHttpRequest =null;}; +req.onTimeoutInternal= +function(){if(req!=null && req.xmlHttpRequest!=null && !req.onCompleteInternalHandled){req.aborted =true; +req.xmlHttpRequest.abort(); +AjaxRequest.numActiveAjaxRequests--; +if(AjaxRequest.numActiveAjaxRequests==0 && typeof(window['AjaxRequestEnd'])=="function"){AjaxRequestEnd(req.groupName);}if(req.groupName!=null){AjaxRequest.numActiveAjaxGroupRequests[req.groupName]--; +if(AjaxRequest.numActiveAjaxGroupRequests[req.groupName]==0 && typeof(req.onGroupEnd)=="function"){req.onGroupEnd(req.groupName);}}if(typeof(req.onTimeout)=="function"){req.onTimeout(req);}delete req.xmlHttpRequest['onreadystatechange']; +req.xmlHttpRequest =null;}}; +req.process = +function(){if(req.xmlHttpRequest!=null){if(req.generateUniqueUrl && req.method=="GET"){req.parameters["AjaxRequestUniqueId"] =new Date().getTime() + "" + req.requestIndex;}var content =null; +for(var i in req.parameters){if(req.queryString.length>0){req.queryString +="&";}req.queryString +=encodeURIComponent(i) + "=" + encodeURIComponent(req.parameters[i]);}if(req.method=="GET"){if(req.queryString.length>0){req.url +=((req.url.indexOf("?")>-1)?"&":"?") + req.queryString;}}req.xmlHttpRequest.open(req.method,req.url,req.async,req.username,req.password); +if(req.method=="POST"){if(typeof(req.xmlHttpRequest.setRequestHeader)!="undefined"){req.xmlHttpRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');}content =req.queryString;}if(req.timeout>0){setTimeout(req.onTimeoutInternal,req.timeout);}req.xmlHttpRequest.send(content);}}; +req.handleArguments = +function(args){for(var i in args){if(typeof(req[i])=="undefined"){req.parameters[i] =args[i];}else{req[i] =args[i];}}}; +req.getAllResponseHeaders = +function(){if(req.xmlHttpRequest!=null){if(req.responseReceived){return req.xmlHttpRequest.getAllResponseHeaders();}alert("Cannot getAllResponseHeaders because a response has not yet been received");}}; +req.getResponseHeader = +function(headerName){if(req.xmlHttpRequest!=null){if(req.responseReceived){return req.xmlHttpRequest.getResponseHeader(headerName);}alert("Cannot getResponseHeader because a response has not yet been received");}}; +return req;}AjaxRequest.getXmlHttpRequest =function(){if(window.XMLHttpRequest){return new XMLHttpRequest();}else if(window.ActiveXObject){/*@cc_on @*/ +/*@if(@_jscript_version >=5) +try{return new ActiveXObject("Msxml2.XMLHTTP");}catch(e){try{return new ActiveXObject("Microsoft.XMLHTTP");}catch(E){return null;}}@end @*/}else{return null;}}; +AjaxRequest.isActive =function(){return(AjaxRequest.numActiveAjaxRequests>0);}; +AjaxRequest.get =function(args){AjaxRequest.doRequest("GET",args);}; +AjaxRequest.post =function(args){AjaxRequest.doRequest("POST",args);}; +AjaxRequest.doRequest =function(method,args){if(typeof(args)!="undefined" && args!=null){var myRequest =new AjaxRequest(); +myRequest.method =method; +myRequest.handleArguments(args); +myRequest.process();}}; +AjaxRequest.submit =function(theform, args){var myRequest =new AjaxRequest(); +if(myRequest==null){return false;}var serializedForm =AjaxRequest.serializeForm(theform); +myRequest.method =theform.method.toUpperCase(); +myRequest.url =theform.action; +myRequest.handleArguments(args); +myRequest.queryString =serializedForm; +myRequest.process(); +return true;}; +AjaxRequest.serializeForm =function(theform){var els =theform.elements; +var len =els.length; +var queryString =""; +this.addField = +function(name,value){if(queryString.length>0){queryString +="&";}queryString +=encodeURIComponent(name) + "=" + encodeURIComponent(value);}; +for(var i=0;i=0){this.addField(el.name,el.options[el.selectedIndex].value);}break; +case 'select-multiple': +for(var j=0;j 0 ) + return arguments[0]; +}; +Function.This = function() { + return this; +}; +Function.args = function() { + return Array.from(arguments); +}; +Function.invoke = function(f) { + return f(); +}; +Function.event = function(e) { + return e ? e : event; +}; + +Function.from = function(obj, field, args) { + var f; + if ( isUndefined(obj) || obj === null ) { + f = function(obj) { + return Function.from(obj, f.field, args).apply(obj, Array.from(arguments).slice(1)); + }; + if ( args && args.length ) + f = f.withArgString("obj," + args); + } + else if ( isUndefined(field) ) { + if ( !args ) + args = Function.argString(obj); + if ( typeof(args) != "string" ) + f = function() { + return f.obj; + }; + else + f = function() { + return f.obj.apply(this, arguments); + }.withArgString(args); + } + else if ( field === null ) + f = function(x) { + return f.obj[x]; + }; + else if ( typeof(field) == "function" ) + f = function() { + return f.field.apply(f.obj, arguments); + }.withArgString(args ? args : Function.argString(field)); + else { + if ( !args ) + args = Function.argString(obj[field]); + if ( typeof(args) != "string" ) + f = function() { + if ( f.field.search(/\s/) > -1 ) + return f.obj[f.field]; + return eval("f.obj." + f.field); + }; + else + f = function() { + if ( f.field.search(/\s/) > -1 ) + return f.obj[f.field].apply(f.obj, arguments); + var s = "f.obj." + f.field + "("; + Array.from(arguments).foreach(function(v, i) { s += "arguments[" + i + "],"; }); + return eval(( arguments.length ? s.slice(0, -1) : s ) + ");"); + }.withArgString(args); + } + f.obj = obj; + f.field = field; + return f; +} +Function.set = function(obj, property) { + var f; + if ( isUndefined(obj) || obj === null ) + f = function(obj, value) { + return Function.set(obj, f.property).call(this, value); + }; + else if ( isUndefined(property) ) + f = function(property, value) { + return Function.set(f.obj, property).call(this, value); + }; + else + f = function(value) { + if ( f.property.search(/\s/) > -1 ) + f.obj[f.property] = value; + else + eval("f.obj." + f.property + " = value"); + return value; + } + f.obj = obj; + f.property = property; + return f; +} + +var _AP = Array.prototype; +if ( typeof(_AP.push) != "function" ) + _AP.push = function() { + for ( var i = 0 ; i < arguments.length ; ++i ) + this[this.length] = arguments[i]; + return this.length; + } +if ( typeof(_AP.pop) != "function" ) + _AP.pop = function() { + if ( this.length ) { + var item = this[this.length-1]; + --this.length; + return item; + } + } +if ( typeof(_AP.shift) != "function" ) + _AP.shift = function() { + this.foreach(function(v, i, self) { self[i] = self[i+1]; }); + --this.length; + return this; + } +if ( typeof(_AP.unshift) != "function" ) + _AP.unshift = function() { + var self = this; + Array.from(arguments).concat(this).foreach(function(v, i) { self[i] = v; }); + return this; + } +if ( typeof(_AP.splice) != "function" ) + _AP.splice = function(start, deleteCount) { + var a = start > 0 ? this.slice(0, start) : []; + a = a.concat(Array.from(arguments).slice(2), this.slice(start+deleteCount)); + var self = this, deleted = this.slice(start, start+deleteCount); + a.foreach(function(v, i) { self[i] = v; }); + this.length = a.length; + return deleted; + } + +_AP.head = function(length) { + return isUndefined(length) ? this[0] : this.slice(0, length); +}; +_AP.tail = function(start) { + return this.slice(start ? start : 1); +}; +_AP.itemAt = function(i) { + return this[i]; +}; +_AP.isEmpty = function() { + return this.length === 0; +}; +_AP.empty = function() { + this.length = 0; + return this; +}; + +_AP.order = function(f) { + if ( !f ) + f = function(a, b) { + return a < b ? -1 : a > b ? 1 : 0; + }; + return this.sort(function(a, b) { + var r = 0; + a.zip(b).foreach(function(pair) { + if ( r = f(pair[0], pair[1]) ) + return false; + }); + return r; + }); +}; + +_AP.join2 = function(prefix, infix, postfix) { + if ( isUndefined(prefix) || prefix === null ) prefix = ""; + if ( isUndefined(infix) || infix === null ) infix = ""; + if ( isUndefined(postfix) || postfix === null ) postfix = ""; + var r = this.join(postfix + infix + prefix); + return r.length ? prefix + r + postfix : r; +}; + +_AP.append = function() { + for ( var i = 0 ; i < arguments.length ; ++i ) + if ( isDefined(arguments[i]) ) + this[this.length] = arguments[i]; + return this; +}; +_AP.extend = function(x) { + if ( isDefined(x) ) { + if ( x.constructor == Array || typeof(x.foreach) != "function") + this.concat(x); + else { + var self = this; + x.foreach(function(v) { self.append(v); }); + } + } + return this; +}; +_AP.feed = function(x) { + x.extend(this); + return this; +}; + +_AP.foreach = function(f) { + for ( var i = 0 ; i < this.length ; ++i ) + if ( f(this[i], i, this) === false ) + return i; +}; +_AP.coalesce = function(r, f) { + if ( isUndefined(f) ) { + f = arguments[0]; + r = arguments[1]; + } + if ( typeof(f) == "string" ) + f = f.asMethod(); + this.foreach(function(v, i, self) { + var t = f(r, v, i, self); + if ( isDefined(t) ) + r = t; + }); + return r; +}; +_AP.fold = function(r, f) { + if ( isUndefined(f) ) { + f = arguments[0]; + r = arguments[1]; + } + if ( typeof(f) == "string" ) + f = f.toFunction(); + return this.coalesce(r, function(r, v) { + return isDefined(r) ? f(r, v) : v; + }); +}; +_AP.foldr = function(r, f) { + if ( isUndefined(f) ) { + f = arguments[0]; + r = arguments[1]; + } + if ( typeof(f) == "string" ) + f = f.toFunction(); + return this.fold(r, function(r, v) { return f(v, r); }); +}; +_AP.collect = function(a, f) { + if ( isUndefined(f) ) { + f = arguments[0]; + a = arguments[1]; + } + if ( typeof(f) == "string" ) + f = f.asMethod(); + if ( !a ) + a = this.constructor ? new this.constructor : []; + return this.coalesce(a, function(r, v, i, self) { return r.append(f(v, i, self)); }); +}; +_AP.call = function(f) { + this.foreach(f); + return this; +}; + +_AP.asLongAs = function(a, f) { + if ( isUndefined(f) ) { + f = arguments[0]; + a = arguments[1]; + } + if ( f.constructor === RegExp ) + f = Function.from(f, "test"); + if ( !a ) + a = this.constructor ? new this.constructor : []; + this.foreach(function(v, i, self) { + if ( !f(v, i, self) ) return false; + a.append(v); + }); + return a; +}; +_AP.filter = function(f, other) { + if ( f.constructor === RegExp ) + f = Function.from(f, "test"); + return this.collect(other ? + function(v, i, a) { + if ( f(v, i, a) ) + return v; + other.append(v); + } : + function(v, i, a) { + if ( f(v, i, a) ) + return v; + } + ); +}; +_AP.search = function(f) { + if ( f.constructor === RegExp ) + f = Function.from(f, "test"); + else if ( typeof(f) != "function" ) { + var x = f; + f = function(y) { return y === x; }; + } + var result = this.foreach(function(v, i, self) { return !f(v, i, self); }); + return typeof(result) == "number" ? result : -1; +}; +_AP.indexOf = function(pattern) { + if ( typeof(pattern.foreach) != "function" ) + pattern = [].append(pattern); + var result = this.foreach(function(v, i, self) { + return -1 !== pattern.foreach(function(v) { + return v === self[i++]; + }); + }); + return typeof(result) == "number" ? result : -1; +}; + +_AP.inverse = function() { + return this.collect(function(v) { + return typeof(v.reverse) == "function" ? v.reverse() : v; + }).reverse(); +} + +_AP.flush = function(f) { + for ( ; this.length ; this.shift() ) + if ( f(this[0], this) === false ) + break; + return this; +}; +_AP.split = function(f) { + if ( f.constructor === RegExp ) + f = Function.from(f, "test"); + else if ( typeof(f) != "function" ) + f = "===".curry({ 0 : f }); + var a = [[]], index = 0; + this.foreach(function(v) { + if ( f(v) ) + a[++index] = []; + else + a[index].append(v); + }); + return a; +}; +_AP.zip = function() { + var result = this.collect(function(v) { return Function.args(v); }); + Array.from(arguments).foreach(function(a) { + if ( !a || isUndefined(a.foreach) ) + a = Function.args(v); + var last = 0; + if ( IsUndefined(a.foreach(function(v, i) { + var x = result.itemAt(i); + if ( isUndefined(x) ) return false; + x.push(v); + last = i; + })) ) { + if ( isDefined(result.length) ) + result.length = last+1; + else + result = result.slice(0, last+1); + } + }); + return result; +}; +_AP.zipWith = function(f) { + if ( typeof(f) == "string" ) f = f.toFunction(); + return this.zip.apply(this, Array.from(arguments).tail()).collect(function(v) { + return f.apply(null, v); + }); +}; + +_AP.spread = function() { + return this.coalesce([], "extend"); +}; + +Array.from = function(x) { + if ( typeof(x.toArray) == "function" ) { + x = x.toArray(); + if ( this === Array ) + return x; + } + if ( typeof(x.foreach) == "function" ) { + var a = new this; + x.foreach(function(v) { a.push(v); }); + return a; + } + if ( typeof(x.length) == "number" ) { + var a = new this; + if ( typeof(x) != "string" ) + for ( i = 0 ; i < x.length ; ++i ) + a.push(x[i]); + else + for ( i = 0 ; i < x.length ; ++i ) + a.push(x.charAt(i)); + return a; + } + return (new this).push(x); +} +Array.fill = function(f, a) { + if ( !a ) a = new this; + var i = a.length; + if ( !i ) i = 0; + for ( ; ; ++i ) { + var v = f(i, a); + if ( isUndefined(v) ) + return a; + a.append(v); + } +}; +Array.recurse = function(f, resultIfEmpty) { + if ( isUndefined(resultIfEmpty) ) resultIfEmpty = new this; + return function(x) { + return this.isEmpty() ? + typeof(resultIfEmpty) == "function" ? + resultIfEmpty(x) : resultIfEmpty : + f(this.head(), this.tail(), x); + }; +}; + +_AP.equals = Array.recurse(function(h, t, x) { + return typeof(x.head) == "function" && typeof(x.tail) == "function" && + ( typeof(h.equals) == "function" ? h.equals(x.head()) : h == x.head() ) && + t.equals(x.tail()); + }, + Function.from(null, "isEmpty") +); + +function iteratable(x) { + var p = x.prototype; + if ( !p || p == Object ) + p = x; + else { + if ( typeof(p.push) == "function" ) { + if ( !x.from ) + x.from = Array.from; + } + if ( typeof(p.append) == "function" ) { + if ( !x.fill ) + x.fill = Array.fill; + } + } + if ( typeof(p.foreach) == "function" ) { + if ( !p.coalesce ) + p.coalesce = _AP.coalesce; + if ( !p.fold ) + p.fold = _AP.fold; + if ( !p.foldr ) + p.foldr = _AP.foldr; + if ( !p.collect ) + p.collect = _AP.collect; + if ( !p.filter ) + p.filter = _AP.filter; + if ( !p.search ) + p.search = _AP.search; + } +} + +var _NP = Number.prototype; +_NP.times = function(f, counter) { + counter = counter || 0; + for ( var i = 0 ; i < this ; ++i ) + f(counter++); +}; +_NP.upTo = function(target, step) { + if ( !step ) step = 1; + var a = []; + for ( var i = this.valueOf() ; i <= target ; i += step ) + a.append(i); + return a; +}; +_NP.downTo = function(target, step) { + if ( !step ) step = 1; + var a = []; + for ( var i = this.valueOf() ; i >= target ; i -= step ) + a.append(i); + return a; +}; +_NP.to = function(target, step) { + return this < target ? this.upTo(target, step) : this.downTo(target, step); +}; +_NP.chr = function() { + return String.fromCharCode(this); +}; + +var _SP = String.prototype; +_SP.itemAt = String.charAt; +_SP.asc = function() { + return this.charCodeAt(0); +}; +_SP.to = function(target, step) { + return this.asc().to(target.toString().asc(), step).collect(Function.from(null, "chr")); +}; +_SP.toFunctionUnary = function() { + eval("function __unary__(op) { return " + this + " op; }"); + __unary__.op = this; + return __unary__; +}; +_SP.toFunctionBinary = function() { + eval("function __binary__(op1, op2) { return op1 " + this + " op2; }"); + __binary__.op = this; + return __binary__; +}; +_SP.toFunction = function() { + return ",!,~,++,--,new,delete,typeof,void,".indexOf("," + this + ",") > -1 ? + this.toFunctionUnary() : this.toFunctionBinary(); +}; +_SP.toMethod = function() { + return this.toFunction().toMethod(); +}; +_SP.apply = function(obj, args) { + return this.toFunction().apply(obj, args); +}; +_SP.call = function(obj) { + return this.toFunction().apply(obj, Array.from(arguments).slice(1)); +}; +_SP.curry = function() { + return _FP.curry.apply(this.toFunction(), arguments); +}; +_SP.using = function() { + return _FP.using.apply(this.toFunction(), arguments); +}; +_SP.asArgName = function() { + var self = this; + return { toString : function() { return self; }, asArgNameFlag : true }; +}; + +_SP.asMethod = function(args) { + eval("function __method__(obj) { return obj." + this + ".apply(obj, Array.from(arguments).slice(1)); }"); + return args ? __method__.withArgString("obj," + args) : __method__; +}; +_SP.asMethodUsing = function() { + var args = (1).upTo(arguments.length).collect(function(v) { return "arg" + v; }).join(","); + return _FP.curry.apply(this.asMethod(args), (new Array(1)).concat(Array.from(arguments))); +}; + +var _RP = RegExp.prototype; +_RP.iterate = function(s, f) { + var sre = this.toString(), sep = sre.lastIndexOf("/"); + var re = new RegExp(sre.slice(1, sep), sre.slice(sep+1) + "g"); + for ( var a ; a = re.exec(s) ; ) + if ( f(a, s, re) === false ) + return a; + return null; +}; +_RP.not = function() { + return "!".using(Function.from(this, "test", "str")); +}; + +if ( typeof(Enumerator) == "function" && typeof(Enumerator.prototype) == "object" ) { + var _EP = Enumerator.prototype; + _EP.foreach = function(f) { + for ( this.moveFirst() ; !this.atEnd() ; this.moveNext() ) + if ( f(this.item(), this) === false ) + return this.item(); + return null; + }; + iteratable(Enumerator); +} + +function select(prep, map, selector) { + if ( arguments.length < 3 ) { + selector = map; + map = prep; + prep = arguments[2]; + } + var r; + switch ( typeof(prep) ) { + case "string": + prep = prep.toFunction(); + case "function": + case "object": + if ( prep ) { + r = map[prep(selector, this)]; + break; + } + default: + r = map[selector]; + } + if ( isUndefined(r) ) r = map[r]; + return typeof(r) == "function" ? r.call(this, selector, prep) : r; +} + +function stringSet(initItems) { + if ( initItems ) + this.addToSet.apply(this, initItems); +} +stringSet.prototype = { + addToSet : function(item) { + var set = this; + return Array.from(arguments).coalesce(false, function(r, item) { + return !set[item] ? set[item] = true : r; + }); + }, + foreach : function(f) { + for ( var item in this ) + if ( this[item] !== stringSet.prototype[item] && f(item, this) === false ) + return item; + return null; + }, + mergeSet : function(set) { + return Array.from(this).concat(Array.from(set)).toStringSet(); + }, + include : function() { + return "&&".using( + Function.from(this, null), + Function.from(stringSet.prototype, null).andThen(isUndefined)); + }, + exclude : function() { + return this.include().andThen("!".toFunctionUnary()); + } +}; +_AP.toStringSet = function() { + return new stringSet(this); +}; + +Math.even = function(n) { + return n % 2 == 0; +}; +Math.odd = function(n) { + return n % 2 != 0; +}; + +var ff = isUndefined(ff) ? Function.from : ff; + +function debugAlert(x) { + if ( debugAlert.on ) + Array.from(arguments).foreach(alert); + if ( arguments.length > 0 ) + return x; +} +debugAlert.on = true; diff --git a/www/extras/js/b/beyondBrowser.js b/www/extras/js/b/beyondBrowser.js new file mode 100644 index 000000000..765702a25 --- /dev/null +++ b/www/extras/js/b/beyondBrowser.js @@ -0,0 +1,93 @@ +/* + beyondBrowser.js + by Dan Shappir and Sjoerd Visscher + For more information see http://w3future.com/html/beyondJS +*/ +if ( typeof(beyondVer) !== "number" || beyondVer < 0.98 ) + alert("beyondBrowser requires beyond JS library ver 0.98 or higher"); +var beyondBrowserVer = 0.96; + +Array.as = function(c) { + if ( typeof(c.length) != "number" ) + return [].append(c); + if ( !c.foreach ) + c.foreach = _AP.foreach; + if ( !c.head ) + c.head = _AP.head; + if ( !c.tail ) + c.tail = _AP.tail; + if ( !c.itemAt ) + c.tail = _AP.itemAt; + if ( !c.isEmpty ) + c.isEmpty = _AP.isEmpty; + if ( !c.slice ) + c.slice = function(start, end) { + var a = Array.from(this); + return isDefined(end) ? a.slice(start, end) : a.slice(start); + }; + iteratable(c); + return c; +}; + +_SP.element = function() { + return document.getElementById(this); +}; +_SP.children = function(index, subIndex) { + var e = this.element(); + var c = isDefined(subIndex) ? e.children(index, subIndex) : + isDefined(index) ? e.children(index) : e.children; + return isDefined(c.length) ? Array.as(c) : c; +}; +_SP.foreach = function(f, r) { + return ( isUndefined(document.all) ? [ this.element() ] : Array.as(document.all[this]) ).foreach(f, r); +}; +iteratable(""); +_SP.setTo = function(html) { + this.foreach(function(e) { e.innerHTML = html; }); + return this; +}; +_SP.into = function(id) { + id.toString().setTo(this); + return this; +}; +_SP.add = function(where, html) { + this.foreach(function(e) { e.insertAdjacentHTML(where, html); }); + return this; +}; +_SP.insert = _SP.add.curry("afterBegin"); +_SP.append = _SP.add.curry("beforeEnd"); +_SP.showHide = function(show) { + var display = show ? "" : "none"; + this.foreach(function(e) { e.style.display = display; }); + return this; +}; +_SP.show = _SP.showHide.curry(true); +_SP.hide = _SP.showHide.curry(false); +_SP.toggleShow = function() { + this.foreach(function(e) { e.style.display = e.style.display.length ? "" : "none"; }); + return this; +}; +_SP.moveTo = function(clientX, clientY) { + this.foreach(function(e) { e.style.left = clientX + "px"; e.style.top = clientY + "px"; }); + return this; +}; +_SP.w = function(text) { + return "<" + this + ">" + text + ""; +}; +_SP.w.re = /\s.*/; +_SP.tag = function(tag) { + return tag.toString().w(this); +}; +_SP.write = function(s) { + return document.write(this.w(s)); +}; + +if ( isUndefined(document.getElementById) ) + document.getElementById = function(id) { + return Array.as(document.all[id])[0]; + }; +if ( isUndefined(document.getElementsByTagName) ) + document.getElementsByTagName = function(tagName) { + tagName = tagName.toUpperCase() + return Array.as(document.all).filter(function(e) { return e.tagName.toUpperCase() == tagName; }); + }; diff --git a/www/extras/js/b/beyondDispatch.js b/www/extras/js/b/beyondDispatch.js new file mode 100644 index 000000000..b81fd969e --- /dev/null +++ b/www/extras/js/b/beyondDispatch.js @@ -0,0 +1,126 @@ +/* + BeyondDispatch.JS + by Dan Shappir and Sjoerd Visscher + For more information see http://w3future.com/html/beyondJS +*/ +if ( typeof(beyondVer) !== "number" || beyondVer < 0.98 ) + alert("beyondDispatch requires Beyond JS library ver 0.98 or higher"); +var beyondDispatchVer = 0.90; + +function dispatch(otherwise, argString) { + var options = []; + var isFunction = typeof(otherwise) == "function"; + if ( isFunction && isUndefined(argString) ) + argString = Function.argString(otherwise); + var dispatcher = function() { + var self = this, args = arguments, result; + return 0 <= options.foreach(function(v) { + if ( v != null && v[0].apply(self, args) ) { + result = typeof(v[1]) == "function" ? v[1].apply(self, args) : v[1]; + return false; + } + }) ? result : isFunction ? otherwise.apply(self, args) : otherwise; + }; + dispatcher.add = function(test, target) { + if ( isUndefined(target) ) { + target = test; + test = target.length; + } + if ( typeof(test) == "string" ) { + var args = typeof(target) == "function" ? Function.argString(target) : ""; + eval("test = function(" + args + ") { return " + test + "; }"); + return options.push([test, target]) - 1; + } + test = dispatch.fix(test); + return isUndefined(test) ? -1 : options.push([test, target]) - 1; + }; + dispatcher.addList = function() { + var result = -1; + for ( var i = 0 ; i < arguments.length ; i += 2 ) + result = this.add(arguments[i], arguments[i + 1]); + return result; + }; + dispatcher.remove = function(index) { + options[index] = null; + }; + dispatcher.removeAll = function() { + options = []; + }; + dispatcher.clone = function() { + var clone = dispatch(otherwise, argString); + options.foreach(function(e) { + if ( e != null ) + clone.add(e[0], e[1]); + }); + return clone; + }; + return dispatcher.withArgString(argString); +} + +dispatch.fix = select.curry({ + prep: "typeof", + map : { + number : function(x) { + if ( x >= 0 ) + return function() { return arguments.length == x; }; + x = -x; + return function() { return arguments.length >= x; } + }, + object : function(x) { + if ( typeof(x.foreach) == "function" ) + return function() { + var args = arguments; + return -1 == x.foreach(function(v, i) { + var t = typeof(v); + if ( isUndefined(t) ) + return true; + if ( i >= args.length ) + return false; + var a = args[i]; + if ( t == "function" ) + return v.call(this, a); + if ( v instanceof RegExp ) + return v.test(a); + if ( t == "string" ) { + t = v.slice(1); + switch ( v.charAt(0) ) { + case ".": + switch ( typeof(a) ) { + case "number": + a = new Number(a); + break; + case "boolean": + a = new Boolean(a); + break; + case "string": + a = new String(a); + } + var i = t.indexOf(":"); + if ( i > -1 ) { + v = t.slice(i + 1); + t = t.slice(0, i); + } + if ( !(t in a) ) + return false; + if ( i == -1 ) + return true; + a = a[t]; + t = v; + case ":": + return typeof(a) == t || + ( t != "function" && eval("typeof(" + t + ") == 'function' && a instanceof " + t) ); + case "^": + return eval(t + ".isPrototypeOf(a)"); + case "?": + return eval("a " + t); + case "\\": + v = t; + } + } + return typeof(v.equals) == "function" ? v.equals(a) : v == a; + }); + }; + }, + "function" : function(x) { return x; } + } +}); diff --git a/www/extras/js/b/beyondLazy.js b/www/extras/js/b/beyondLazy.js new file mode 100644 index 000000000..c2aec0d67 --- /dev/null +++ b/www/extras/js/b/beyondLazy.js @@ -0,0 +1,367 @@ +/* + BeyondLazy.JS + by Dan Shappir and Sjoerd Visscher + For more information see http://w3future.com/html/beyondJS +*/ +if ( typeof(beyondVer) !== "number" || beyondVer < 0.98 ) + alert("beyondLazy requires Beyond JS library ver 0.98 or higher"); +var beyondLazyVer = 0.96; + +function Lazy(generator, length) { + this.generator = generator ? generator : function() { return []; }; + this.length = length; +} +var _LP = Lazy.prototype; + +Lazy.cache = true; + +_LP.foreach = function(f) { + var item = []; + for ( var i = 0 ; (item = this.generator(item)).length ; ++i ) + if ( f(item[0], i, this) === false ) + return i; +}; +_LP.collect = function(f) { + var self = this; + if ( typeof(f) == "string" ) + f = f.asMethod(); + return function(prev) { + var current = self.generator(isDefined(prev[1]) ? prev[1] : []); + return current.length ? [ f(current[0], prev[0]), current ] : current; + }.lazy(this.length); +}; +_LP.call = function(f) { + var self = this; + return function(prev) { + var current = self.generator(isDefined(prev[1]) ? prev[1] : []); + f(current[0]); + return current; + }.lazy(); +}; +_LP.asLongAs = function(f) { + var self = this; + if ( f.constructor === RegExp ) + f = Function.from(f, "test"); + return function(prev) { + var current = self.generator(prev); + return current.length && f(current[0]) ? current : []; + }.lazy(); +}; +_LP.filter = function(f, other) { + var self = this; + if ( f.constructor === RegExp ) + f = Function.from(f, "test"); + return function(item) { + while ( (item = self.generator(item)).length && !f(item[0]) ) + if ( other ) + other.append(item[0]); + return item; + }.lazy(); +}; +_LP.zip = function() { + var collections = [ this ].concat(Array.from(arguments).collect(Function.from(null, "lazy"))); + return function(prev) { + var length = Number.POSITIVE_INFINITY; + collections.foreach(function(c) { + if ( c.length && c.length < length ) + length = c.length; + }); + var current_length = prev[1] ? prev[1] : 0; + if ( current_length >= length ) + return []; + if ( !prev.length ) + (collections.length + 2).times(function() { prev.push([]); }); + var current = [ [] , current_length >= 0 ? current_length + 1 : -1 ]; + collections.foreach(function(c, i) { + var x = c.generator(prev[i + 2]); + if ( !x.length ) { + current = []; + return false; + } + current[0].push(x[0]); + current.push(x); + }); + return current; + }.lazy(); +}; +_LP.zipWith = _AP.zipWith; + +_LP.spread = function() { + var self = this; + return function(prev) { + var current; + for (;;) { + switch ( prev.length ) { + case 0: + current = self.generator([]); + break; + + case 2: + current = self.generator(prev[1]); + break; + + default: + current = prev[2].generator(prev[3]); + if ( current.length ) + return [ current[0], prev[1], prev[2], current ]; + prev = [, prev[1]]; + continue; + } + if ( !current.length ) + return []; + var value = current[0]; + if ( !value || typeof(value.foreach) != "function" || typeof(value.lazy) != "function" ) + return [ value, current ]; + prev = [, current, value.lazy(), []]; + } + }.lazy(); +}; + +_LP.head = function(length) { + if ( !length ) return this.generator([])[0]; + var self = this; + return function(prev) { + var index; + if ( prev.length ) { + index = prev[2] + 1; + prev = prev[1]; + } + else + index = 1; + if ( index > length ) + return []; + var current = self.generator(prev); + return current.length ? [ current[0], current, index ] : current; + }.lazy(length); +}; +_LP.tail = function(start) { + if ( !start ) start = 1; + return this.collect(function(current, prev) { return [ current, isUndefined(prev) ? 0 : prev[1]+1 ]; }). + filter(function(current) { return current[1] >= start; }). + collect(function(current) { return current[0]; }); +}; +_LP.slice = function(start, end) { + var result = start ? this.tail(start) : this; + if ( isDefined(end) ) end -= start; + return end >= 1 ? result.head(end) : result; +}; +_LP.isEmpty = function() { + return !this.generator([]).length; +}; +_LP.itemAt = function(index) { + return this.tail(start).head(); +}; +_LP.empty = function() { + this.generator = function() { + return []; + }; + this.length = 0; + return this; +}; + +_LP.toString = function() { + return Array.from(this).toString(); +}; +_LP.toLocaleString = function() { + return Array.from(this).toLocaleString(); +}; +_LP.valueOf = function() { + return Array.from(this).valueOf(); +}; +_LP.join = function(separator) { + var a = Array.from(this); + return isDefined(separator) ? a.join(separator) : a.join(); +}; +_LP.reverse = function() { + return Array.from(this).reverse(); +}; +_LP.concat = function() { + var a = [ this ].concat(Array.from(arguments)).filter(isDefined).collect(function(v) { + return v != null && v.constructor == Array ? v.lazy() : v; + }); + return a.length == 1 ? this : function(prev) { + if ( !prev.length ) prev = [ null, [], 0 ]; + for (;;) { + var current = a[prev[2]]; + if ( isUndefined(current) ) + return []; + if ( current === null || typeof(current.constructor) !== "function" || current.constructor !== Lazy ) + return [ current, [], prev[2]+1 ]; + current = current.generator(prev[1]); + if ( current.length ) + return [ current[0], current, prev[2] ]; + prev = [ null, [], prev[2]+1 ]; + } + }.lazy(); +}; +_LP.push = _LP.append = function() { + var a = Array.from(arguments).filter(isDefined); + if ( a.length ) { + var generator = this.generator; + this.generator = function(prev) { + if ( isUndefined(prev[2]) ) { + var current = generator(prev); + if ( current.length ) + return current; + prev = [ null, null, 0 ]; + } + return isDefined(a[prev[2]]) ? [ a[prev[2]], null, prev[2]+1 ] : []; + }; + } + return this; +}; +_LP.shift = function() { + var result = this.head(); + if ( isDefined(result) ) + this.generator = this.generator.lazy().tail().generator; + return result; +}; +_LP.unshift = function() { + var a = Array.from(arguments).filter(isDefined); + if ( a.length ) + this.generator = a.lazy().concat(this.generator.lazy()).generator; + return this; +}; +_LP.splice = function(start, deleteCount) { + var a = Array.from(arguments).slice(2).filter(isDefined); + var clone = this.generator.lazy(); + var result = clone.tail(start+deleteCount); + if ( a.length ) result = a.lazy().concat(result); + if ( start > 0 ) result = clone.head(start).concat(result); + this.generator = result.generator; + return clone.slice(start, start+deleteCount); +}; + +_LP.extend = function(x) { + if ( isDefined(x) ) { + if ( typeof(x) == "function" ) + x = x().lazy(); + var generator = this.generator; + this.generator = function(prev) { + if ( !prev.length ) + prev = [ null, prev, generator ]; + var current = prev[2](prev[1]); + if ( current.length ) + return [ current[0], current, prev[2] ]; + if ( prev[2] === x.generator ) + return current; + current = x.generator(current); + return current.length ? [ current[0], current, x.generator ] : current; + }; + this.length = this.u; + if ( Lazy.cache ) + return this.cached(); + } + return this; +}; +_LP.feed = _AP.feed; + +_LP.cached = function() { + if ( isUndefined(this.isCached) ) { + this.isCached = true; + var cache = [], generator = this.generator; + this.generator = function(prev) { + if ( !prev.length ) + prev = [ null, [], 0 ]; + if ( prev[2] < cache.length ) + return cache[prev[2]]; + var current = generator(prev[1]); + return cache[cache.length] = current.length ? [ current[0], current, cache.length+1 ] : []; + } + } + return this; +}; + +_LP.equals = _AP.equals; + +_LP.lazy = Function.This; + +_FP.lazy = function(length) { + return new Lazy(this, length); +}; + +_NP.lazyUp = function(end, step) { + var undef, start = this.valueOf(); + if ( !(step > 0) ) step = 1; + var length; + if ( isUndefined(end) || end === null ) + end = Number.POSITIVE_INFINITY; + else + length = Math.floor((end - start + 1)/step); + return function(item) { + if ( !item.length ) return [ start, undef ]; + var value = item[0] + step; + return value <= end ? [ value, undef ] : []; + }.lazy(length); +}; +_NP.lazyDown = function(end, step) { + var undef, start = this.valueOf(); + if ( !(step > 0) ) step = 1; + var length; + if ( isUndefined(end) || end === null ) + end = Number.NEGATIVE_INFINITY; + else + length = Math.floor((start - end + 1)/step); + return function(item) { + if ( !item.length ) return [ start, undef ]; + var value = item[0] - step; + return value >= end ? [ value, undef ] : []; + }.lazy(length); +}; +_NP.lazy = function(end, step) { + if ( isUndefined(end) || end === null ) end = Number.POSITIVE_INFINITY; + return this < end ? this.lazyUp(end, step) : this.lazyDown(end, step); +}; + +_SP.lazy = function(target, step) { + return this.asc().lazy(target.toString().asc(), step).collect(Function.from(null, "chr")); +}; + +_AP.lazy = function() { + return (0).lazy(this.length-1).collect(Function.from(this, "itemAt")); +}; +_AP.cycle = function(n) { + var self = this; + var cycle = isUndefined(n) ? + function() { + return self.lazy().extend(cycle); + } : + 1 <= n ? + function(n) { + return 1 == n ? self.lazy() : self.lazy().extend(cycle.curry(n-1)); + } : + function() { + return [].lazy(); + }; + return cycle(n); +}; + +if ( _EP ) + _EP.lazy = function() { + var self = this; + return function(item) { + if ( !item.length ) + self.moveFirst(); + else + self.moveNext(); + return !self.atEnd() ? [ self.item(), self ] : []; + }.lazy(); + }; + +Lazy.from = function(x) { + if ( typeof(x.lazy) == "function" ) + return x.lazy(); + if ( typeof(x.length) == "number" ) + return this.fill(function(index, item) { return x[index]; }); + return Array.from(x).lazy(); +}; +Lazy.fill = function(f) { + var lazy = function(item) { + var index = item[1] ? item[1] : 0; + var value = f(index, item[0]); + return isDefined(value) ? [ value, index+1 ] : []; + }.lazy(); + return this.cache ? lazy.cached() : lazy; +}; +Lazy.recurse = Array.recurse; +iteratable(Lazy); diff --git a/www/extras/js/b/beyondRhino.js b/www/extras/js/b/beyondRhino.js new file mode 100644 index 000000000..0a4e8482b --- /dev/null +++ b/www/extras/js/b/beyondRhino.js @@ -0,0 +1,467 @@ +/* + BeyondRhino.JS + by Dan Shappir and Sjoerd Visscher + For more information see http://w3future.com/html/beyondJS +*/ +load("beyond.js", "beyondLazy.js", "beyondStreams.js"); + +var beyondRhinoVer = 0.9; +var Beyond = {}; + +importPackage(java.util); + +function javaArray(type, length) { + var alloc = new Function("length", "return java.lang.reflect.Array.newInstance(" + type + ", length)"); + return alloc(length); +} + +function alert(s) { + print(s); +} + +function toJava(v, asObjects) { + return typeof(v.toJava) == "function" ? v.toJava(asObjects) : v; +} + +_NP.toJava = function() { + var v = this.valueOf(); + return v == Math.round(v) ? new java.lang.Integer(v) : new java.lang.Double(v); +}; +_SP.toJava = function() { + var v = this.valueOf(); + return new java.lang.String(v); +}; +Boolean.prototype.toJava = function() { + var v = this.valueOf(); + return new java.lang.Boolean(v); +}; + +function javaType(v) { + switch ( typeof(v) ) { + case "boolean": + return java.lang.Boolean.TYPE; + + case "number": + return v == Math.round(v) ? java.lang.Integer.TYPE : java.lang.Double.TYPE; + + case "string": + return java.lang.String; + } + return java.lang.Object; +} +_AP.toJava = function(asObjects) { + if ( !this.length ) + return java.lang.reflect.Array.newInstance(java.lang.Object, 0); + var r; + if ( asObjects ) + r = java.lang.reflect.Array.newInstance(java.lang.Object, this.length); + else { + var type = this.coalesce(function(type, v) { + var jt = javaType(v); + return isUndefined(type) || type == jt || ( type == java.lang.Integer.TYPE && jt == java.lang.Double.TYPE ) ? + jt : java.lang.Object; + }); + r = java.lang.reflect.Array.newInstance(type, this.length); + } + this.foreach(function(v, i) { + r[i] = toJava(v); + }); + return r; +}; + +function toJavaScript(v) { + if ( typeof(v) != "object" || v == null ) + return v; + if ( typeof(v.getClass) != "function" ) { + if ( typeof(v.valueOf) == "function" ) + v = v.valueOf(); + return "undefined" != v ? v : undefined; + } + var cls = v.getClass(); + switch ( String(cls.getName()) ) { + case "java.lang.Boolean": + return "true" == v; + + case "java.lang.Byte": + case "java.lang.Short": + case "java.lang.Integer": + case "java.lang.Long": + return parseInt(v); + + case "java.lang.Float": + case "java.lang.Double": + return parseFloat(v); + + default: + var s = String(v); + return s == v ? s : v; + } +} + +// Hack to enable use of Array.from() on Java arrays +_AP.toArray = null; + +_AP.enumeration = function() { + var self = this; + var index = 0; + return new Enumeration() { + hasMoreElements : function() { + return index < self.length; + }, + nextElement : function() { + return toJava(self[index++]); + } + }; +}; +_LP.enumeration = function() { + var self = this; + var item = []; + var cached = false; + var reuse = false; + return new Enumeration() { + hasMoreElements : function() { + if ( !cached ) { + cached = reuse = true; + item = self.generator(item); + } + return item.length; + }, + nextElement : function() { + if ( !reuse ) + item = self.generator(item); + cached = !cached; + reuse = false; + return toJava(item[0]); + } + }; +}; +Beyond.enumeration = function(e) { + return function() { + return e.hasMoreElements() ? [ toJavaScript(e.nextElement()) ] : []; + }.lazy(); +}; + +_AP.iterator = function() { + var self = this; + var index = 0; + return new Iterator() { + hasNext : function() { + return index < self.length; + }, + next : function() { + return toJava(self[index++]); + }, + remove : function() { + if ( index > 0 ) + self.splice(--index, 1); + } + }; +}; +_LP.iterator = function() { + var self = this; + var item = []; + var cached = false; + var reuse = false; + return new Iterator() { + hasNext : function() { + if ( !cached ) { + cached = reuse = true; + item = self.generator(item); + } + return item.length; + }, + next : function() { + if ( !reuse ) + item = self.generator(item); + cached = !cached; + reuse = false; + return toJava(item[0]); + } + }; +}; +Beyond.iterator = function(i) { + return function() { + return i.hasNext() ? [ toJavaScript(i.next()) ] : []; + }.lazy(); +}; + +function isTypeOrDerived(v, type) { + for ( var c = v.getClass() ; c ; c = c.getSuperclass() ) + if ( c.getName() == type ) + return true; + return false; +} + +_AP.listIterator = function() { + var self = this; + var index, prev; + return new ListIterator() { +hasNext : function() { + return isDefined(index) ? index < self.length : self.length > 0; + }, +hasPrevious : function() { + return isDefined(index) ? index > 0 : self.length > 0; + }, +next : function() { + if ( isUndefined(index) ) + index = 0; + prev = index; + return toJava(self[index++]); + }, +previous : function() { + if ( isUndefined(index) ) + index = self.length; + prev = index; + return toJava(self[--index]); + }, +nextIndex : function() { + return isUndefined(index) ? 0 : index; + }, +previousIndex : function() { + return ( isUndefiend(index) ? self.length : index ) - 1; + }, +set : function(element) { + if ( isDefined(prev) ) + self.splice(prev, 1, toJavaScript(element)); + }, +add : function(element) { + prev = index; + self.splice(index++, 0, toJavaScript(element)); + }, +remove : function() { + if ( isDefined(prev) ) + self.splice(prev, 1); + } + }; +}; +Beyond.reverseIterator = function(i) { + return function() { + return i.hasPrevious() ? [ toJavaScript(i.previous()) ] : []; + }.lazy(); +}; + +_AP.collection = function() { + var self = this; + return new Collection() { +add : function(element) { + element = toJavaScript(element) + if ( self.search(element) != -1 ) + return false; + self.push(element); + return true; + }, +addAll : function(collection) { + var me = this; + return Beyond.iterator(collection.iterator()).coalesce(function(r, v) { + return me.add(v) || r; + }); + }, +clear : function() { + self.length = 0; + }, +contains : function(element) { + return self.search(toJavaScript(element)) != -1; + }, +containsAll : function(collection) { + var me = this; + return Beyond.iterator(collection.iterator()).coalesce(true, function(r, v) { + return r && me.contains(v); + }); + }, +isEmpty : function() { + return self.length == 0; + }, +iterator : function() { + return self.iterator(); + }, +remove : function(element) { + element = toJavaScript(element) + var index = self.search(element); + if ( index == -1 ) + return false; + self.splice(index, 1); + return true; + }, +removeAll : function(collection) { + var me = this; + return Beyond.iterator(collection.iterator()).coalesce(function(r, v) { return me.remove(v) || r; }); + }, +retainAll : function(collection) { + var length = self.length; + var filtered = self.filter(function(v) { return collection.contains(toJava(v)); }); + filtered.foreach(function(v, i) { self[i] = v; }); + self.length = filtered.length; + return self.length != length; + }, +size : function() { + return self.length; + }, +toArray : function(a) { + if ( typeof(a) != "object" ) + return self.toJava(true); + var type = a.getClass().getName().slice(2, -1); + var java = self.collect(function(v) { return toJava(v, true); }). + filter(function(v) { return isTypeOrDerived(v, type); }); + if ( a.length < java.length ) + a = javaArray(type, java.length); + java.foreach(function(v, i) { a[i] = v; }); + if ( a.length > java.length ) + Arrays.fill(a, java.length, a.length, null); + return a; + } + }; +}; +Beyond.collection = function(collection) { + return function(item) { + var i = item.length ? item[1] : collection.iterator(); + return i.hasNext() ? [ toJavaScript(i.next()) , i ] : []; + }.lazy(); +}; + +_AP.list = function() { + var self = this; + return new List() { +add : function(index, element) { + if ( isUndefined(element) ) + self.push(toJavaScript(index)); + else + self.splice(index, 0, toJavaScript(element)); + return true; + }, +addAll : function(index, collection) { + var me = this; + if ( isUndefined(collection) ) + Beyond.iterator(index.iterator()).foreach(function(v) { me.add(v); }); + else + Beyond.iterator(collection.iterator()).foreach(function(v) { me.add(index++, v); }); + return true; + }, +clear : function() { + self.length = 0; + }, +contains : function(element) { + return self.search(toJavaScript(element)) != -1; + }, +containsAll : function(collection) { + var me = this; + return Beyond.iterator(collection.iterator()).coalesce(true, function(r, v) { return r && me.contains(v); }); + }, +get : function(index) { + var x = toJava(self[index]); + return toJava(self[index]); + }, +indexOf : function(element) { + return self.search(toJavaScript(element)); + }, +lastIndexOf : function(element) { + var index = self.reverese().search(toJavaScript(element)); + return index == -1 ? -1 : self.length - index - 1; + }, +isEmpty : function() { + return self.length == 0; + }, +iterator : function() { + return self.iterator(); + }, +listIterator : function(index) { + var i = self.listIterator(); + if ( isDefined(index) ) + for ( ; i.hasNext() && i.nextIndex() < index ; i.next() ); + return i; + }, +remove : function(index) { + return self.splice(index, 1)[0]; + }, +removeAll : function(collection) { + var me = this; + return Beyond.iterator(collection.iterator()).coalesce(function(r, v) { return me.remove(v) || r; }); + }, +retainAll : function(collection) { + var length = self.length; + var filtered = self.filter(function(v) { return collection.contains(toJava(v)); }); + filtered.foreach(function(v, i) { self[i] = v; }); + self.length = filtered.length; + return self.length != length; + }, +set : function(index, element) { + var prev = self[index]; + self[index] = toJavaScript(element); + return toJava(prev, true); + }, +size : function() { + return self.length; + }, +subList : function(from, to) { + return self.slice(from, to).list(); + }, +toArray : function(a) { + if ( typeof(a) != "object" ) + return self.toJava(true); + var type = a.getClass().getName().slice(2, -1); + var java = self.collect(function(v) { return toJava(v, true); }). + filter(function(v) { return isTypeOrDerived(v, type); }); + if ( a.length < java.length ) + a = javaArray(type, java.length); + java.foreach(function(v, i) { a[i] = v; }); + if ( a.length > java.length ) + Arrays.fill(a, java.length, a.length, null); + return a; + } + }; +}; +Beyond.list = Beyond.collection; +Beyond.reverseList = function(list) { + return function(item) { + var i = item.length ? item[1] : list.listIterator(); + return i.hasPrevious() ? [ toJavaScript(i.previous()) , i ] : []; + }.lazy(); +}; + +var File = { + +read : function(file, cache, reader) { + var rf = function(item) { + var stream = item[1] ? + item[1] : + new java.io.BufferedReader(reader ? file : new java.io.FileReader(file)); + var line = stream.readLine(); + return line != null ? [ String(line), stream ] : []; + }.lazy(); + if ( cache ) + rf = rf.cached(); + rf.file = file; + return rf; + }, +write : function(file, overwrite, writer) { + var st = new Stream(writer ? file : new java.io.FileWriter(file, !overwrite)); + var handle = st.foreach(function(v, stream) { + if ( isDefined(v) ) { + var s = String(v) + "\n"; + stream.write(s, 0, s.length); + stream.flush(); + } + }); + st.stop = function() { + this.detach(handle); + this.owner.Close(); + }; + return st; + }, +process : function(input, output, f, append) { + if ( !f ) + f = Function.NOP; + else if ( f.constructor === RegExp ) { + var re = f; + f = function(line) { + if ( re.test(line) ) + return line; + }; + } + return this.write(output, !append).extend(this.read(input).collect(f)); + } + +}; + +File.StdIn = File.read(new java.io.InputStreamReader(java.lang.System["in"]), false, true); +File.StdOut = File.write(new java.io.OutputStreamWriter(java.lang.System["out"]), false, true); +File.StdErr = File.write(new java.io.OutputStreamWriter(java.lang.System["err"]), false, true); \ No newline at end of file diff --git a/www/extras/js/b/beyondStreams.js b/www/extras/js/b/beyondStreams.js new file mode 100644 index 000000000..6656ff0d4 --- /dev/null +++ b/www/extras/js/b/beyondStreams.js @@ -0,0 +1,221 @@ +/* + BeyondStreams.JS + by Dan Shappir and Sjoerd Visscher + For more information see http://w3future.com/html/beyondJS +*/ +if ( typeof(beyondVer) !== "number" || beyondVer < 0.99 ) + alert("beyondStreams requires Beyond JS library ver 0.99 or higher"); +var beyondStreamsVer = 0.91; + +function Stream(owner) { + this.owner = owner; + this.callbacks = []; +} +var _STP = Stream.prototype; + +_STP.foreach = function(f) { + return this.callbacks.push(f)-1; +}; +_STP.detach = function(x) { + if ( isUndefined(x) || x === null ) { + if ( this.callbacks.isEmpty() ) + return false; + this.callbacks.empty(); + return true; + } + if ( typeof(x) == "function" || typeof(x) == "object" ) + x = this.callbacks.search("===".curry(x)); + if ( x < 0 || x >= this.callbacks.length ) + return false; + if ( x == this.callbacks.length-1 ) + this.callbacks.pop(); + else { + this.callbacks[x] = this.undef; + if ( this.callbacks.filter(isDefined).isEmpty() ) + this.callbacks.empty(); + } + return true; +}; + +_STP.push = function() { + var self = this; + Array.from(arguments).foreach(function(v) { + self.callbacks.foreach(function(f, i) { + if ( typeof(f) == "function" ) { + if ( f.call(self.owner, v, self.owner, self) === false ) + self.detach(i); + } + else if ( f(v, self.owner, self) === false ) + self.detach(i); + }); + }); + return this; +}; +_STP.append = function() { + this.push.apply(this, Array.from(arguments).filter(isDefined)); + return this; +}; +_STP.extend = function(x) { + if ( !x || isUndefined(x.foreach) ) + this.push(x); + else { + var self = this; + x.foreach(function(v) { self.push(v); }); + } + return this; +}; + +_STP.feed = function(st) { + return this.foreach(function(v) { st.push(v); }); +}; + +_STP.pre = function(f) { + var st = new Stream(this.owner); + st.collect(f).feed(this); + return st; +}; + +_STP.fold = function(r, f) { + if ( isUndefined(f) ) { + f = arguments[0]; + r = arguments[1]; + } + if ( typeof(f) == "string" ) + f = f.toFunction(); + var st = new Stream(this.owner); + this.coalesce(function(x, v) { + r = isDefined(r) ? f(r, v) : v; + st.push(r); + }); + return st; +}; +_STP.search = function(f) { + if ( f.constructor === RegExp ) + f = Function.from(f, "test"); + else if ( typeof(f) != "function" ) { + var x = f; + f = function(y) { return y === x; }; + } + var st = new Stream(this.owner); + this.foreach(function(v, owner, self) { + if ( f(v, owner, self) ) { + st.push(v); + return false; + } + }); + return st; +}; + +_STP.delay = function(iMilliSeconds) { + var st = new Stream(this.owner); + this.foreach(function(v) { Function.from(st, "push").delay(iMilliSeconds)(v); }); + return st; +}; +_STP.buffer = function(f) { + if ( typeof(f) == "number" ) { + var count = f; + f = function(v, buffer) { + return buffer.length+1 >= count; + }; + } + else if ( f.constructor === RegExp ) + f = Function.from(f, "test"); + var st = new Stream(this.owner), buffer = []; + this.foreach(function(v, owner, self) { + var r = f.call(owner, v, buffer, owner, self); + buffer.push(v); + if ( r ) { + st.push(buffer); + buffer = []; + } + }); + return st; +}; + +Stream.from = function(x) { + if ( typeof(x.foreach) != "function" ) + x = Array.from(x); + var st = new this(x); + x.foreach(function(v) { st.push(v); }); + return st; +}; +iteratable(Stream); + +function timerStream(iMilliSeconds) { + var st = new Stream(iMilliSeconds); + var nInterval = setInterval(function() { st.push(new Date); }, iMilliSeconds); + st.stop = function() { + if ( isDefined(nInterval) ) { + clearInterval(nInterval); + nInterval = timerStream.undef; + } + }; + return st; +} + +function eventStream(element, eventName, noAttach) { + if ( typeof(element) == "string" ) + element = element.element(); + var st = new Stream(element); + + if ( !noAttach && isDefined(element.attachEvent) ) { + function pushEvent(event) { + st.push(event ? event : window.event); + } + if ( element.attachEvent(eventName, pushEvent) ) { + st.stop = function() { + element.detachEvent(eventName, pushEvent); + }; + return st; + } + } + + function pushEvent(event) { + if ( !st.stopped ) + st.push(event ? event : window.event); + } + var prev = eval("element." + eventName); + if ( !prev ) + eval("element." + eventName + " = pushEvent"); + else + eval("element." + eventName + " = prev.andThen(pushEvent)"); + st.stop = function() { + st.stopped = true; + }; + return st; +} +function propertyStream(element, propertyName) { + if ( typeof(element) == "string" ) + element = element.element(); + var st = new Stream(element); + var handle = st.foreach(Function.set(element, propertyName)); + st.stop = function() { + this.detach(handle); + this.stop = Function.NOP; + }; + return st; +} + +function cancelDefaultAction(e) { + e.returnValue = false; + return e; +} +function cancelBubble(e) { + e.cancelBubble = true; + return e; +} + +function logStream(log) { + var st = new Stream(log ? log : []); + var handle = st.foreach(function(v) { + st.owner.push([ new Date, v ]); + }); + st.stop = function() { + st.detach(handle); + this.stop = Function.NOP; + }; + st.flush = function() { + st.owner.empty(); + }; + return st; +} diff --git a/www/extras/js/b/beyondXML.js b/www/extras/js/b/beyondXML.js new file mode 100644 index 000000000..e1777b3e4 --- /dev/null +++ b/www/extras/js/b/beyondXML.js @@ -0,0 +1,85 @@ +/* + beyondXML.js + by Dan Shappir and Sjoerd Visscher + For more information see http://w3future.com/html/beyondJS +*/ +if ( typeof(beyondVer) !== "number" || beyondVer < 0.95 ) + alert("BeyondXML requires Beyond JS library ver 0.95 or higher"); +var beyondXMLVer = 0.96; + +var beyondXML = new function() { + var isMozilla = typeof(Document) != "undefined"; + + this.createDocument = function() { + return isMozilla ? new Document() : new ActiveXObject("Microsoft.XMLDOM"); + }; + this.parseFromString = function(doc, xml) { + return isMozilla ? doc = (new DOMParser()).parseFromString(xml, "text/xml") : doc.loadXML(xml); + }; + this.load = function(doc, url) { + return doc.load(url); + }; + this.invoke = function(method) { + return Function.from(null, method); + }; + + var origToArray = Array.from; + Array.from = function(x) { + var n = x.nextNode; + return typeof(n) != "object" ? origToArray.call(this, x) : + !n ? [] : Array.fill(function() { if ( n = x.nextNode ) return n; }, [ n ]); + }; + + if ( typeof(beyondLazyVer) == "number" && beyondLazyVer >= 0.95 ) { + var origToLazy = Lazy.from; + Lazy.from = function(x) { + var n = x.nextNode; + if ( typeof(n) != "object" ) + return origToLazy.call(this, x); + return function(prev) { + if ( !prev.length ) x.reset(); + var n = x.nextNode; + return n ? [ n ] : []; + }.lazy(); + }; + this.lazyLists = false; + this.list = function(list) { + return beyondXML.lazyLists ? Lazy.from(list) : Array.from(list); + }; + } +}; + +function childNodes(n) { + return beyondXML.list(n.childNodes); +} + +_SP.parseFromString = function(doc) { + if ( !doc ) + doc = beyondXML.createDocument(); + return beyondXML.parseFromString(doc, this) ? doc : null; +}; +_SP.load = function(document) { + if ( !doc ) + doc = beyondXML.createDocument(); + return beyondXML.load(doc, this) ? doc : null; +}; + +_SP.selectSingleNode = function(node) { + return node.selectSingleNode(this); +}; +_SP.selectNodes = function(node) { + return beyondXML.list(node.selectNodes(this)); +}; +_SP.nodeFromID = function(node) { + return node.ownerDocument.nodeFromID(this); +}; +_SP.getElementsByTagName = function(node) { + return beyondXML.list(node.getElementsByTagName(this)); +}; + +_SP.node = function(node) { + return node.getElementsByTagName(this).nextNode(); +}; +_SP.value = function(node) { + return this.node(node).nodeTypedValue; +}; \ No newline at end of file diff --git a/www/extras/js/json.js b/www/extras/js/json.js new file mode 100644 index 000000000..44797aa59 --- /dev/null +++ b/www/extras/js/json.js @@ -0,0 +1,38 @@ + +Array.prototype.______array='______array';var JSON={org:'http://www.JSON.org',copyright:'(c)2005 JSON.org',license:'http://www.crockford.com/JSON/license.html',stringify:function(arg){var c,i,l,s='',v;switch(typeof arg){case'object':if(arg){if(arg.______array=='______array'){for(i=0;i=' '){if(c=='\\'||c=='"'){s+='\\';} +s+=c;}else{switch(c){case'\b':s+='\\b';break;case'\f':s+='\\f';break;case'\n':s+='\\n';break;case'\r':s+='\\r';break;case'\t':s+='\\t';break;default:c=c.charCodeAt();s+='\\u00'+Math.floor(c/16).toString(16)+ +(c%16).toString(16);}}} +return s+'"';case'boolean':return String(arg);default:return'null';}},parse:function(text){var at=0;var ch=' ';function error(m){throw{name:'JSONError',message:m,at:at-1,text:text};} +function next(){ch=text.charAt(at);at+=1;return ch;} +function white(){while(ch!==''&&ch<=' '){next();}} +function str(){var i,s='',t,u;if(ch=='"'){outer:while(next()){if(ch=='"'){next();return s;}else if(ch=='\\'){switch(next()){case'b':s+='\b';break;case'f':s+='\f';break;case'n':s+='\n';break;case'r':s+='\r';break;case't':s+='\t';break;case'u':u=0;for(i=0;i<4;i+=1){t=parseInt(next(),16);if(!isFinite(t)){break outer;} +u=u*16+t;} +s+=String.fromCharCode(u);break;default:s+=ch;}}else{s+=ch;}}} +error("Bad string");} +function arr(){var a=[];if(ch=='['){next();white();if(ch==']'){next();return a;} +while(ch){a.push(val());white();if(ch==']'){next();return a;}else if(ch!=','){break;} +next();white();}} +error("Bad array");} +function obj(){var k,o={};if(ch=='{'){next();white();if(ch=='}'){next();return o;} +while(ch){k=str();white();if(ch!=':'){break;} +next();o[k]=val();white();if(ch=='}'){next();return o;}else if(ch!=','){break;} +next();white();}} +error("Bad object");} +function num(){var n='',v;if(ch=='-'){n='-';next();} +while(ch>='0'&&ch<='9'){n+=ch;next();} +if(ch=='.'){n+='.';while(next()&&ch>='0'&&ch<='9'){n+=ch;}} +if(ch=='e'||ch=='E'){n+='e';next();if(ch=='-'||ch=='+'){n+=ch;next();} +while(ch>='0'&&ch<='9'){n+=ch;next();}} +v=+n;if(!isFinite(v)){error("Bad number");}else{return v;}} +function word(){switch(ch){case't':if(next()=='r'&&next()=='u'&&next()=='e'){next();return true;} +break;case'f':if(next()=='a'&&next()=='l'&&next()=='s'&&next()=='e'){next();return false;} +break;case'n':if(next()=='u'&&next()=='l'&&next()=='l'){next();return null;} +break;} +error("Syntax error");} +function val(){white();switch(ch){case'{':return obj();case'[':return arr();case'"':return str();case'-':return num();default:return ch>='0'&&ch<='9'?num():word();}} +return val();}}; \ No newline at end of file diff --git a/www/extras/js/sau/builder.js b/www/extras/js/sau/builder.js new file mode 100644 index 000000000..5b15ba939 --- /dev/null +++ b/www/extras/js/sau/builder.js @@ -0,0 +1,101 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// See scriptaculous.js for full license. + +var Builder = { + NODEMAP: { + AREA: 'map', + CAPTION: 'table', + COL: 'table', + COLGROUP: 'table', + LEGEND: 'fieldset', + OPTGROUP: 'select', + OPTION: 'select', + PARAM: 'object', + TBODY: 'table', + TD: 'table', + TFOOT: 'table', + TH: 'table', + THEAD: 'table', + TR: 'table' + }, + // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, + // due to a Firefox bug + node: function(elementName) { + elementName = elementName.toUpperCase(); + + // try innerHTML approach + var parentTag = this.NODEMAP[elementName] || 'div'; + var parentElement = document.createElement(parentTag); + try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 + parentElement.innerHTML = "<" + elementName + ">"; + } catch(e) {} + var element = parentElement.firstChild || null; + + // see if browser added wrapping tags + if(element && (element.tagName != elementName)) + element = element.getElementsByTagName(elementName)[0]; + + // fallback to createElement approach + if(!element) element = document.createElement(elementName); + + // abort if nothing could be created + if(!element) return; + + // attributes (or text) + if(arguments[1]) + if(this._isStringOrNumber(arguments[1]) || + (arguments[1] instanceof Array)) { + this._children(element, arguments[1]); + } else { + var attrs = this._attributes(arguments[1]); + if(attrs.length) { + try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 + parentElement.innerHTML = "<" +elementName + " " + + attrs + ">"; + } catch(e) {} + element = parentElement.firstChild || null; + // workaround firefox 1.0.X bug + if(!element) { + element = document.createElement(elementName); + for(attr in arguments[1]) + element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; + } + if(element.tagName != elementName) + element = parentElement.getElementsByTagName(elementName)[0]; + } + } + + // text, or array of children + if(arguments[2]) + this._children(element, arguments[2]); + + return element; + }, + _text: function(text) { + return document.createTextNode(text); + }, + _attributes: function(attributes) { + var attrs = []; + for(attribute in attributes) + attrs.push((attribute=='className' ? 'class' : attribute) + + '="' + attributes[attribute].toString().escapeHTML() + '"'); + return attrs.join(" "); + }, + _children: function(element, children) { + if(typeof children=='object') { // array can hold nodes and text + children.flatten().each( function(e) { + if(typeof e=='object') + element.appendChild(e) + else + if(Builder._isStringOrNumber(e)) + element.appendChild(Builder._text(e)); + }); + } else + if(Builder._isStringOrNumber(children)) + element.appendChild(Builder._text(children)); + }, + _isStringOrNumber: function(param) { + return(typeof param=='string' || typeof param=='number'); + } +} \ No newline at end of file diff --git a/www/extras/js/sau/controls.js b/www/extras/js/sau/controls.js new file mode 100644 index 000000000..9742b6918 --- /dev/null +++ b/www/extras/js/sau/controls.js @@ -0,0 +1,750 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +// (c) 2005 Jon Tirsen (http://www.tirsen.com) +// Contributors: +// Richard Livsey +// Rahul Bhargava +// Rob Wills +// +// See scriptaculous.js for full license. + +// Autocompleter.Base handles all the autocompletion functionality +// that's independent of the data source for autocompletion. This +// includes drawing the autocompletion menu, observing keyboard +// and mouse events, and similar. +// +// Specific autocompleters need to provide, at the very least, +// a getUpdatedChoices function that will be invoked every time +// the text inside the monitored textbox changes. This method +// should get the text for which to provide autocompletion by +// invoking this.getToken(), NOT by directly accessing +// this.element.value. This is to allow incremental tokenized +// autocompletion. Specific auto-completion logic (AJAX, etc) +// belongs in getUpdatedChoices. +// +// Tokenized incremental autocompletion is enabled automatically +// when an autocompleter is instantiated with the 'tokens' option +// in the options parameter, e.g.: +// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); +// will incrementally autocomplete with a comma as the token. +// Additionally, ',' in the above example can be replaced with +// a token array, e.g. { tokens: [',', '\n'] } which +// enables autocompletion on multiple tokens. This is most +// useful when one of the tokens is \n (a newline), as it +// allows smart autocompletion after linebreaks. + +var Autocompleter = {} +Autocompleter.Base = function() {}; +Autocompleter.Base.prototype = { + baseInitialize: function(element, update, options) { + this.element = $(element); + this.update = $(update); + this.hasFocus = false; + this.changed = false; + this.active = false; + this.index = 0; + this.entryCount = 0; + + if (this.setOptions) + this.setOptions(options); + else + this.options = options || {}; + + this.options.paramName = this.options.paramName || this.element.name; + this.options.tokens = this.options.tokens || []; + this.options.frequency = this.options.frequency || 0.4; + this.options.minChars = this.options.minChars || 1; + this.options.onShow = this.options.onShow || + function(element, update){ + if(!update.style.position || update.style.position=='absolute') { + update.style.position = 'absolute'; + Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight}); + } + Effect.Appear(update,{duration:0.15}); + }; + this.options.onHide = this.options.onHide || + function(element, update){ new Effect.Fade(update,{duration:0.15}) }; + + if (typeof(this.options.tokens) == 'string') + this.options.tokens = new Array(this.options.tokens); + + this.observer = null; + + this.element.setAttribute('autocomplete','off'); + + Element.hide(this.update); + + Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this)); + Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this)); + }, + + show: function() { + if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); + if(!this.iefix && + (navigator.appVersion.indexOf('MSIE')>0) && + (navigator.userAgent.indexOf('Opera')<0) && + (Element.getStyle(this.update, 'position')=='absolute')) { + new Insertion.After(this.update, + ''); + this.iefix = $(this.update.id+'_iefix'); + } + if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); + }, + + fixIEOverlapping: function() { + Position.clone(this.update, this.iefix); + this.iefix.style.zIndex = 1; + this.update.style.zIndex = 2; + Element.show(this.iefix); + }, + + hide: function() { + this.stopIndicator(); + if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); + if(this.iefix) Element.hide(this.iefix); + }, + + startIndicator: function() { + if(this.options.indicator) Element.show(this.options.indicator); + }, + + stopIndicator: function() { + if(this.options.indicator) Element.hide(this.options.indicator); + }, + + onKeyPress: function(event) { + if(this.active) + switch(event.keyCode) { + case Event.KEY_TAB: + case Event.KEY_RETURN: + this.selectEntry(); + Event.stop(event); + case Event.KEY_ESC: + this.hide(); + this.active = false; + Event.stop(event); + return; + case Event.KEY_LEFT: + case Event.KEY_RIGHT: + return; + case Event.KEY_UP: + this.markPrevious(); + this.render(); + if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); + return; + case Event.KEY_DOWN: + this.markNext(); + this.render(); + if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); + return; + } + else + if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN) + return; + + this.changed = true; + this.hasFocus = true; + + if(this.observer) clearTimeout(this.observer); + this.observer = + setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); + }, + + onHover: function(event) { + var element = Event.findElement(event, 'LI'); + if(this.index != element.autocompleteIndex) + { + this.index = element.autocompleteIndex; + this.render(); + } + Event.stop(event); + }, + + onClick: function(event) { + var element = Event.findElement(event, 'LI'); + this.index = element.autocompleteIndex; + this.selectEntry(); + this.hide(); + }, + + onBlur: function(event) { + // needed to make click events working + setTimeout(this.hide.bind(this), 250); + this.hasFocus = false; + this.active = false; + }, + + render: function() { + if(this.entryCount > 0) { + for (var i = 0; i < this.entryCount; i++) + this.index==i ? + Element.addClassName(this.getEntry(i),"selected") : + Element.removeClassName(this.getEntry(i),"selected"); + + if(this.hasFocus) { + this.show(); + this.active = true; + } + } else { + this.active = false; + this.hide(); + } + }, + + markPrevious: function() { + if(this.index > 0) this.index-- + else this.index = this.entryCount-1; + }, + + markNext: function() { + if(this.index < this.entryCount-1) this.index++ + else this.index = 0; + }, + + getEntry: function(index) { + return this.update.firstChild.childNodes[index]; + }, + + getCurrentEntry: function() { + return this.getEntry(this.index); + }, + + selectEntry: function() { + this.active = false; + this.updateElement(this.getCurrentEntry()); + }, + + updateElement: function(selectedElement) { + if (this.options.updateElement) { + this.options.updateElement(selectedElement); + return; + } + + var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); + var lastTokenPos = this.findLastToken(); + if (lastTokenPos != -1) { + var newValue = this.element.value.substr(0, lastTokenPos + 1); + var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/); + if (whitespace) + newValue += whitespace[0]; + this.element.value = newValue + value; + } else { + this.element.value = value; + } + this.element.focus(); + + if (this.options.afterUpdateElement) + this.options.afterUpdateElement(this.element, selectedElement); + }, + + updateChoices: function(choices) { + if(!this.changed && this.hasFocus) { + this.update.innerHTML = choices; + Element.cleanWhitespace(this.update); + Element.cleanWhitespace(this.update.firstChild); + + if(this.update.firstChild && this.update.firstChild.childNodes) { + this.entryCount = + this.update.firstChild.childNodes.length; + for (var i = 0; i < this.entryCount; i++) { + var entry = this.getEntry(i); + entry.autocompleteIndex = i; + this.addObservers(entry); + } + } else { + this.entryCount = 0; + } + + this.stopIndicator(); + + this.index = 0; + this.render(); + } + }, + + addObservers: function(element) { + Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); + Event.observe(element, "click", this.onClick.bindAsEventListener(this)); + }, + + onObserverEvent: function() { + this.changed = false; + if(this.getToken().length>=this.options.minChars) { + this.startIndicator(); + this.getUpdatedChoices(); + } else { + this.active = false; + this.hide(); + } + }, + + getToken: function() { + var tokenPos = this.findLastToken(); + if (tokenPos != -1) + var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,''); + else + var ret = this.element.value; + + return /\n/.test(ret) ? '' : ret; + }, + + findLastToken: function() { + var lastTokenPos = -1; + + for (var i=0; i lastTokenPos) + lastTokenPos = thisTokenPos; + } + return lastTokenPos; + } +} + +Ajax.Autocompleter = Class.create(); +Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { + initialize: function(element, update, url, options) { + this.baseInitialize(element, update, options); + this.options.asynchronous = true; + this.options.onComplete = this.onComplete.bind(this); + this.options.defaultParams = this.options.parameters || null; + this.url = url; + }, + + getUpdatedChoices: function() { + entry = encodeURIComponent(this.options.paramName) + '=' + + encodeURIComponent(this.getToken()); + + this.options.parameters = this.options.callback ? + this.options.callback(this.element, entry) : entry; + + if(this.options.defaultParams) + this.options.parameters += '&' + this.options.defaultParams; + + new Ajax.Request(this.url, this.options); + }, + + onComplete: function(request) { + this.updateChoices(request.responseText); + } + +}); + +// The local array autocompleter. Used when you'd prefer to +// inject an array of autocompletion options into the page, rather +// than sending out Ajax queries, which can be quite slow sometimes. +// +// The constructor takes four parameters. The first two are, as usual, +// the id of the monitored textbox, and id of the autocompletion menu. +// The third is the array you want to autocomplete from, and the fourth +// is the options block. +// +// Extra local autocompletion options: +// - choices - How many autocompletion choices to offer +// +// - partialSearch - If false, the autocompleter will match entered +// text only at the beginning of strings in the +// autocomplete array. Defaults to true, which will +// match text at the beginning of any *word* in the +// strings in the autocomplete array. If you want to +// search anywhere in the string, additionally set +// the option fullSearch to true (default: off). +// +// - fullSsearch - Search anywhere in autocomplete array strings. +// +// - partialChars - How many characters to enter before triggering +// a partial match (unlike minChars, which defines +// how many characters are required to do any match +// at all). Defaults to 2. +// +// - ignoreCase - Whether to ignore case when autocompleting. +// Defaults to true. +// +// It's possible to pass in a custom function as the 'selector' +// option, if you prefer to write your own autocompletion logic. +// In that case, the other options above will not apply unless +// you support them. + +Autocompleter.Local = Class.create(); +Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), { + initialize: function(element, update, array, options) { + this.baseInitialize(element, update, options); + this.options.array = array; + }, + + getUpdatedChoices: function() { + this.updateChoices(this.options.selector(this)); + }, + + setOptions: function(options) { + this.options = Object.extend({ + choices: 10, + partialSearch: true, + partialChars: 2, + ignoreCase: true, + fullSearch: false, + selector: function(instance) { + var ret = []; // Beginning matches + var partial = []; // Inside matches + var entry = instance.getToken(); + var count = 0; + + for (var i = 0; i < instance.options.array.length && + ret.length < instance.options.choices ; i++) { + + var elem = instance.options.array[i]; + var foundPos = instance.options.ignoreCase ? + elem.toLowerCase().indexOf(entry.toLowerCase()) : + elem.indexOf(entry); + + while (foundPos != -1) { + if (foundPos == 0 && elem.length != entry.length) { + ret.push("
  • " + elem.substr(0, entry.length) + "" + + elem.substr(entry.length) + "
  • "); + break; + } else if (entry.length >= instance.options.partialChars && + instance.options.partialSearch && foundPos != -1) { + if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { + partial.push("
  • " + elem.substr(0, foundPos) + "" + + elem.substr(foundPos, entry.length) + "" + elem.substr( + foundPos + entry.length) + "
  • "); + break; + } + } + + foundPos = instance.options.ignoreCase ? + elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : + elem.indexOf(entry, foundPos + 1); + + } + } + if (partial.length) + ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) + return "
      " + ret.join('') + "
    "; + } + }, options || {}); + } +}); + +// AJAX in-place editor +// +// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor + +// Use this if you notice weird scrolling problems on some browsers, +// the DOM might be a bit confused when this gets called so do this +// waits 1 ms (with setTimeout) until it does the activation +Field.scrollFreeActivate = function(field) { + setTimeout(function() { + Field.activate(field); + }, 1); +} + +Ajax.InPlaceEditor = Class.create(); +Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99"; +Ajax.InPlaceEditor.prototype = { + initialize: function(element, url, options) { + this.url = url; + this.element = $(element); + + this.options = Object.extend({ + okText: "ok", + cancelText: "cancel", + savingText: "Saving...", + clickToEditText: "Click to edit", + okText: "ok", + rows: 1, + onComplete: function(transport, element) { + new Effect.Highlight(element, {startcolor: this.options.highlightcolor}); + }, + onFailure: function(transport) { + alert("Error communicating with the server: " + transport.responseText.stripTags()); + }, + callback: function(form) { + return Form.serialize(form); + }, + handleLineBreaks: true, + loadingText: 'Loading...', + savingClassName: 'inplaceeditor-saving', + loadingClassName: 'inplaceeditor-loading', + formClassName: 'inplaceeditor-form', + highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, + highlightendcolor: "#FFFFFF", + externalControl: null, + ajaxOptions: {} + }, options || {}); + + if(!this.options.formId && this.element.id) { + this.options.formId = this.element.id + "-inplaceeditor"; + if ($(this.options.formId)) { + // there's already a form with that name, don't specify an id + this.options.formId = null; + } + } + + if (this.options.externalControl) { + this.options.externalControl = $(this.options.externalControl); + } + + this.originalBackground = Element.getStyle(this.element, 'background-color'); + if (!this.originalBackground) { + this.originalBackground = "transparent"; + } + + this.element.title = this.options.clickToEditText; + + this.onclickListener = this.enterEditMode.bindAsEventListener(this); + this.mouseoverListener = this.enterHover.bindAsEventListener(this); + this.mouseoutListener = this.leaveHover.bindAsEventListener(this); + Event.observe(this.element, 'click', this.onclickListener); + Event.observe(this.element, 'mouseover', this.mouseoverListener); + Event.observe(this.element, 'mouseout', this.mouseoutListener); + if (this.options.externalControl) { + Event.observe(this.options.externalControl, 'click', this.onclickListener); + Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener); + Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener); + } + }, + enterEditMode: function(evt) { + if (this.saving) return; + if (this.editing) return; + this.editing = true; + this.onEnterEditMode(); + if (this.options.externalControl) { + Element.hide(this.options.externalControl); + } + Element.hide(this.element); + this.createForm(); + this.element.parentNode.insertBefore(this.form, this.element); + Field.scrollFreeActivate(this.editField); + // stop the event to avoid a page refresh in Safari + if (evt) { + Event.stop(evt); + } + return false; + }, + createForm: function() { + this.form = document.createElement("form"); + this.form.id = this.options.formId; + Element.addClassName(this.form, this.options.formClassName) + this.form.onsubmit = this.onSubmit.bind(this); + + this.createEditField(); + + if (this.options.textarea) { + var br = document.createElement("br"); + this.form.appendChild(br); + } + + okButton = document.createElement("input"); + okButton.type = "submit"; + okButton.value = this.options.okText; + this.form.appendChild(okButton); + + cancelLink = document.createElement("a"); + cancelLink.href = "#"; + cancelLink.appendChild(document.createTextNode(this.options.cancelText)); + cancelLink.onclick = this.onclickCancel.bind(this); + this.form.appendChild(cancelLink); + }, + hasHTMLLineBreaks: function(string) { + if (!this.options.handleLineBreaks) return false; + return string.match(/
    /i); + }, + convertHTMLLineBreaks: function(string) { + return string.replace(/
    /gi, "\n").replace(//gi, "\n").replace(/<\/p>/gi, "\n").replace(/

    /gi, ""); + }, + createEditField: function() { + var text; + if(this.options.loadTextURL) { + text = this.options.loadingText; + } else { + text = this.getText(); + } + + if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { + this.options.textarea = false; + var textField = document.createElement("input"); + textField.type = "text"; + textField.name = "value"; + textField.value = text; + textField.style.backgroundColor = this.options.highlightcolor; + var size = this.options.size || this.options.cols || 0; + if (size != 0) textField.size = size; + this.editField = textField; + } else { + this.options.textarea = true; + var textArea = document.createElement("textarea"); + textArea.name = "value"; + textArea.value = this.convertHTMLLineBreaks(text); + textArea.rows = this.options.rows; + textArea.cols = this.options.cols || 40; + this.editField = textArea; + } + + if(this.options.loadTextURL) { + this.loadExternalText(); + } + this.form.appendChild(this.editField); + }, + getText: function() { + return this.element.innerHTML; + }, + loadExternalText: function() { + Element.addClassName(this.form, this.options.loadingClassName); + this.editField.disabled = true; + new Ajax.Request( + this.options.loadTextURL, + Object.extend({ + asynchronous: true, + onComplete: this.onLoadedExternalText.bind(this) + }, this.options.ajaxOptions) + ); + }, + onLoadedExternalText: function(transport) { + Element.removeClassName(this.form, this.options.loadingClassName); + this.editField.disabled = false; + this.editField.value = transport.responseText.stripTags(); + }, + onclickCancel: function() { + this.onComplete(); + this.leaveEditMode(); + return false; + }, + onFailure: function(transport) { + this.options.onFailure(transport); + if (this.oldInnerHTML) { + this.element.innerHTML = this.oldInnerHTML; + this.oldInnerHTML = null; + } + return false; + }, + onSubmit: function() { + // onLoading resets these so we need to save them away for the Ajax call + var form = this.form; + var value = this.editField.value; + + // do this first, sometimes the ajax call returns before we get a chance to switch on Saving... + // which means this will actually switch on Saving... *after* we've left edit mode causing Saving... + // to be displayed indefinitely + this.onLoading(); + + new Ajax.Updater( + { + success: this.element, + // don't update on failure (this could be an option) + failure: null + }, + this.url, + Object.extend({ + parameters: this.options.callback(form, value), + onComplete: this.onComplete.bind(this), + onFailure: this.onFailure.bind(this) + }, this.options.ajaxOptions) + ); + // stop the event to avoid a page refresh in Safari + if (arguments.length > 1) { + Event.stop(arguments[0]); + } + return false; + }, + onLoading: function() { + this.saving = true; + this.removeForm(); + this.leaveHover(); + this.showSaving(); + }, + showSaving: function() { + this.oldInnerHTML = this.element.innerHTML; + this.element.innerHTML = this.options.savingText; + Element.addClassName(this.element, this.options.savingClassName); + this.element.style.backgroundColor = this.originalBackground; + Element.show(this.element); + }, + removeForm: function() { + if(this.form) { + if (this.form.parentNode) Element.remove(this.form); + this.form = null; + } + }, + enterHover: function() { + if (this.saving) return; + this.element.style.backgroundColor = this.options.highlightcolor; + if (this.effect) { + this.effect.cancel(); + } + Element.addClassName(this.element, this.options.hoverClassName) + }, + leaveHover: function() { + if (this.options.backgroundColor) { + this.element.style.backgroundColor = this.oldBackground; + } + Element.removeClassName(this.element, this.options.hoverClassName) + if (this.saving) return; + this.effect = new Effect.Highlight(this.element, { + startcolor: this.options.highlightcolor, + endcolor: this.options.highlightendcolor, + restorecolor: this.originalBackground + }); + }, + leaveEditMode: function() { + Element.removeClassName(this.element, this.options.savingClassName); + this.removeForm(); + this.leaveHover(); + this.element.style.backgroundColor = this.originalBackground; + Element.show(this.element); + if (this.options.externalControl) { + Element.show(this.options.externalControl); + } + this.editing = false; + this.saving = false; + this.oldInnerHTML = null; + this.onLeaveEditMode(); + }, + onComplete: function(transport) { + this.leaveEditMode(); + this.options.onComplete.bind(this)(transport, this.element); + }, + onEnterEditMode: function() {}, + onLeaveEditMode: function() {}, + dispose: function() { + if (this.oldInnerHTML) { + this.element.innerHTML = this.oldInnerHTML; + } + this.leaveEditMode(); + Event.stopObserving(this.element, 'click', this.onclickListener); + Event.stopObserving(this.element, 'mouseover', this.mouseoverListener); + Event.stopObserving(this.element, 'mouseout', this.mouseoutListener); + if (this.options.externalControl) { + Event.stopObserving(this.options.externalControl, 'click', this.onclickListener); + Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener); + Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener); + } + } +}; + +// Delayed observer, like Form.Element.Observer, +// but waits for delay after last key input +// Ideal for live-search fields + +Form.Element.DelayedObserver = Class.create(); +Form.Element.DelayedObserver.prototype = { + initialize: function(element, delay, callback) { + this.delay = delay || 0.5; + this.element = $(element); + this.callback = callback; + this.timer = null; + this.lastValue = $F(this.element); + Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); + }, + delayedListener: function(event) { + if(this.lastValue == $F(this.element)) return; + if(this.timer) clearTimeout(this.timer); + this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); + this.lastValue = $F(this.element); + }, + onTimerEvent: function() { + this.timer = null; + this.callback(this.element, $F(this.element)); + } +}; \ No newline at end of file diff --git a/www/extras/js/sau/dragdrop.js b/www/extras/js/sau/dragdrop.js new file mode 100644 index 000000000..db74c547e --- /dev/null +++ b/www/extras/js/sau/dragdrop.js @@ -0,0 +1,579 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// See scriptaculous.js for full license. + +/*--------------------------------------------------------------------------*/ + +var Droppables = { + drops: [], + + remove: function(element) { + this.drops = this.drops.reject(function(d) { return d.element==$(element) }); + }, + + add: function(element) { + element = $(element); + var options = Object.extend({ + greedy: true, + hoverclass: null + }, arguments[1] || {}); + + // cache containers + if(options.containment) { + options._containers = []; + var containment = options.containment; + if((typeof containment == 'object') && + (containment.constructor == Array)) { + containment.each( function(c) { options._containers.push($(c)) }); + } else { + options._containers.push($(containment)); + } + } + + if(options.accept) options.accept = [options.accept].flatten(); + + Element.makePositioned(element); // fix IE + options.element = element; + + this.drops.push(options); + }, + + isContained: function(element, drop) { + var parentNode = element.parentNode; + return drop._containers.detect(function(c) { return parentNode == c }); + }, + + isAffected: function(point, element, drop) { + return ( + (drop.element!=element) && + ((!drop._containers) || + this.isContained(element, drop)) && + ((!drop.accept) || + (Element.classNames(element).detect( + function(v) { return drop.accept.include(v) } ) )) && + Position.within(drop.element, point[0], point[1]) ); + }, + + deactivate: function(drop) { + if(drop.hoverclass) + Element.removeClassName(drop.element, drop.hoverclass); + this.last_active = null; + }, + + activate: function(drop) { + if(drop.hoverclass) + Element.addClassName(drop.element, drop.hoverclass); + this.last_active = drop; + }, + + show: function(point, element) { + if(!this.drops.length) return; + + if(this.last_active) this.deactivate(this.last_active); + this.drops.each( function(drop) { + if(Droppables.isAffected(point, element, drop)) { + if(drop.onHover) + drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); + if(drop.greedy) { + Droppables.activate(drop); + throw $break; + } + } + }); + }, + + fire: function(event, element) { + if(!this.last_active) return; + Position.prepare(); + + if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) + if (this.last_active.onDrop) + this.last_active.onDrop(element, this.last_active.element, event); + }, + + reset: function() { + if(this.last_active) + this.deactivate(this.last_active); + } +} + +var Draggables = { + drags: [], + observers: [], + + register: function(draggable) { + if(this.drags.length == 0) { + this.eventMouseUp = this.endDrag.bindAsEventListener(this); + this.eventMouseMove = this.updateDrag.bindAsEventListener(this); + this.eventKeypress = this.keyPress.bindAsEventListener(this); + + Event.observe(document, "mouseup", this.eventMouseUp); + Event.observe(document, "mousemove", this.eventMouseMove); + Event.observe(document, "keypress", this.eventKeypress); + } + this.drags.push(draggable); + }, + + unregister: function(draggable) { + this.drags = this.drags.reject(function(d) { return d==draggable }); + if(this.drags.length == 0) { + Event.stopObserving(document, "mouseup", this.eventMouseUp); + Event.stopObserving(document, "mousemove", this.eventMouseMove); + Event.stopObserving(document, "keypress", this.eventKeypress); + } + }, + + activate: function(draggable) { + window.focus(); // allows keypress events if window isn't currently focused, fails for Safari + this.activeDraggable = draggable; + }, + + deactivate: function(draggbale) { + this.activeDraggable = null; + }, + + updateDrag: function(event) { + if(!this.activeDraggable) return; + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + // Mozilla-based browsers fire successive mousemove events with + // the same coordinates, prevent needless redrawing (moz bug?) + if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; + this._lastPointer = pointer; + this.activeDraggable.updateDrag(event, pointer); + }, + + endDrag: function(event) { + if(!this.activeDraggable) return; + this._lastPointer = null; + this.activeDraggable.endDrag(event); + }, + + keyPress: function(event) { + if(this.activeDraggable) + this.activeDraggable.keyPress(event); + }, + + addObserver: function(observer) { + this.observers.push(observer); + this._cacheObserverCallbacks(); + }, + + removeObserver: function(element) { // element instead of observer fixes mem leaks + this.observers = this.observers.reject( function(o) { return o.element==element }); + this._cacheObserverCallbacks(); + }, + + notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' + if(this[eventName+'Count'] > 0) + this.observers.each( function(o) { + if(o[eventName]) o[eventName](eventName, draggable, event); + }); + }, + + _cacheObserverCallbacks: function() { + ['onStart','onEnd','onDrag'].each( function(eventName) { + Draggables[eventName+'Count'] = Draggables.observers.select( + function(o) { return o[eventName]; } + ).length; + }); + } +} + +/*--------------------------------------------------------------------------*/ + +var Draggable = Class.create(); +Draggable.prototype = { + initialize: function(element) { + var options = Object.extend({ + handle: false, + starteffect: function(element) { + new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7}); + }, + reverteffect: function(element, top_offset, left_offset) { + var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; + new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur}); + }, + endeffect: function(element) { + new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0}); + }, + zindex: 1000, + revert: false, + snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] } + }, arguments[1] || {}); + + this.element = $(element); + + if(options.handle && (typeof options.handle == 'string')) + this.handle = Element.childrenWithClassName(this.element, options.handle)[0]; + if(!this.handle) this.handle = $(options.handle); + if(!this.handle) this.handle = this.element; + + Element.makePositioned(this.element); // fix IE + + this.delta = this.currentDelta(); + this.options = options; + this.dragging = false; + + this.eventMouseDown = this.initDrag.bindAsEventListener(this); + Event.observe(this.handle, "mousedown", this.eventMouseDown); + + Draggables.register(this); + }, + + destroy: function() { + Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); + Draggables.unregister(this); + }, + + currentDelta: function() { + return([ + parseInt(this.element.style.left || '0'), + parseInt(this.element.style.top || '0')]); + }, + + initDrag: function(event) { + if(Event.isLeftClick(event)) { + // abort on form elements, fixes a Firefox issue + var src = Event.element(event); + if(src.tagName && ( + src.tagName=='INPUT' || + src.tagName=='SELECT' || + src.tagName=='BUTTON' || + src.tagName=='TEXTAREA')) return; + + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var pos = Position.cumulativeOffset(this.element); + this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); + + Draggables.activate(this); + Event.stop(event); + } + }, + + startDrag: function(event) { + this.dragging = true; + + if(this.options.zindex) { + this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); + this.element.style.zIndex = this.options.zindex; + } + + if(this.options.ghosting) { + this._clone = this.element.cloneNode(true); + Position.absolutize(this.element); + this.element.parentNode.insertBefore(this._clone, this.element); + } + + Draggables.notify('onStart', this, event); + if(this.options.starteffect) this.options.starteffect(this.element); + }, + + updateDrag: function(event, pointer) { + if(!this.dragging) this.startDrag(event); + Position.prepare(); + Droppables.show(pointer, this.element); + Draggables.notify('onDrag', this, event); + this.draw(pointer); + if(this.options.change) this.options.change(this); + + // fix AppleWebKit rendering + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); + Event.stop(event); + }, + + finishDrag: function(event, success) { + this.dragging = false; + + if(this.options.ghosting) { + Position.relativize(this.element); + Element.remove(this._clone); + this._clone = null; + } + + if(success) Droppables.fire(event, this.element); + Draggables.notify('onEnd', this, event); + + var revert = this.options.revert; + if(revert && typeof revert == 'function') revert = revert(this.element); + + var d = this.currentDelta(); + if(revert && this.options.reverteffect) { + this.options.reverteffect(this.element, + d[1]-this.delta[1], d[0]-this.delta[0]); + } else { + this.delta = d; + } + + if(this.options.zindex) + this.element.style.zIndex = this.originalZ; + + if(this.options.endeffect) + this.options.endeffect(this.element); + + Draggables.deactivate(this); + Droppables.reset(); + }, + + keyPress: function(event) { + if(!event.keyCode==Event.KEY_ESC) return; + this.finishDrag(event, false); + Event.stop(event); + }, + + endDrag: function(event) { + if(!this.dragging) return; + this.finishDrag(event, true); + Event.stop(event); + }, + + draw: function(point) { + var pos = Position.cumulativeOffset(this.element); + var d = this.currentDelta(); + pos[0] -= d[0]; pos[1] -= d[1]; + + var p = [0,1].map(function(i){ return (point[i]-pos[i]-this.offset[i]) }.bind(this)); + + if(this.options.snap) { + if(typeof this.options.snap == 'function') { + p = this.options.snap(p[0],p[1]); + } else { + if(this.options.snap instanceof Array) { + p = p.map( function(v, i) { + return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this)) + } else { + p = p.map( function(v) { + return Math.round(v/this.options.snap)*this.options.snap }.bind(this)) + } + }} + + var style = this.element.style; + if((!this.options.constraint) || (this.options.constraint=='horizontal')) + style.left = p[0] + "px"; + if((!this.options.constraint) || (this.options.constraint=='vertical')) + style.top = p[1] + "px"; + if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering + } +} + +/*--------------------------------------------------------------------------*/ + +var SortableObserver = Class.create(); +SortableObserver.prototype = { + initialize: function(element, observer) { + this.element = $(element); + this.observer = observer; + this.lastValue = Sortable.serialize(this.element); + }, + + onStart: function() { + this.lastValue = Sortable.serialize(this.element); + }, + + onEnd: function() { + Sortable.unmark(); + if(this.lastValue != Sortable.serialize(this.element)) + this.observer(this.element) + } +} + +var Sortable = { + sortables: new Array(), + + options: function(element){ + element = $(element); + return this.sortables.detect(function(s) { return s.element == element }); + }, + + destroy: function(element){ + element = $(element); + this.sortables.findAll(function(s) { return s.element == element }).each(function(s){ + Draggables.removeObserver(s.element); + s.droppables.each(function(d){ Droppables.remove(d) }); + s.draggables.invoke('destroy'); + }); + this.sortables = this.sortables.reject(function(s) { return s.element == element }); + }, + + create: function(element) { + element = $(element); + var options = Object.extend({ + element: element, + tag: 'li', // assumes li children, override with tag: 'tagname' + dropOnEmpty: false, + tree: false, // fixme: unimplemented + overlap: 'vertical', // one of 'vertical', 'horizontal' + constraint: 'vertical', // one of 'vertical', 'horizontal', false + containment: element, // also takes array of elements (or id's); or false + handle: false, // or a CSS class + only: false, + hoverclass: null, + ghosting: false, + format: null, + onChange: Prototype.emptyFunction, + onUpdate: Prototype.emptyFunction + }, arguments[1] || {}); + + // clear any old sortable with same element + this.destroy(element); + + // build options for the draggables + var options_for_draggable = { + revert: true, + ghosting: options.ghosting, + constraint: options.constraint, + handle: options.handle }; + + if(options.starteffect) + options_for_draggable.starteffect = options.starteffect; + + if(options.reverteffect) + options_for_draggable.reverteffect = options.reverteffect; + else + if(options.ghosting) options_for_draggable.reverteffect = function(element) { + element.style.top = 0; + element.style.left = 0; + }; + + if(options.endeffect) + options_for_draggable.endeffect = options.endeffect; + + if(options.zindex) + options_for_draggable.zindex = options.zindex; + + // build options for the droppables + var options_for_droppable = { + overlap: options.overlap, + containment: options.containment, + hoverclass: options.hoverclass, + onHover: Sortable.onHover, + greedy: !options.dropOnEmpty + } + + // fix for gecko engine + Element.cleanWhitespace(element); + + options.draggables = []; + options.droppables = []; + + // make it so + + // drop on empty handling + if(options.dropOnEmpty) { + Droppables.add(element, + {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false}); + options.droppables.push(element); + } + + (this.findElements(element, options) || []).each( function(e) { + // handles are per-draggable + var handle = options.handle ? + Element.childrenWithClassName(e, options.handle)[0] : e; + options.draggables.push( + new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); + Droppables.add(e, options_for_droppable); + options.droppables.push(e); + }); + + // keep reference + this.sortables.push(options); + + // for onupdate + Draggables.addObserver(new SortableObserver(element, options.onUpdate)); + + }, + + // return all suitable-for-sortable elements in a guaranteed order + findElements: function(element, options) { + if(!element.hasChildNodes()) return null; + var elements = []; + $A(element.childNodes).each( function(e) { + if(e.tagName && e.tagName==options.tag.toUpperCase() && + (!options.only || (Element.hasClassName(e, options.only)))) + elements.push(e); + if(options.tree) { + var grandchildren = this.findElements(e, options); + if(grandchildren) elements.push(grandchildren); + } + }); + + return (elements.length>0 ? elements.flatten() : null); + }, + + onHover: function(element, dropon, overlap) { + if(overlap>0.5) { + Sortable.mark(dropon, 'before'); + if(dropon.previousSibling != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, dropon); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } else { + Sortable.mark(dropon, 'after'); + var nextElement = dropon.nextSibling || null; + if(nextElement != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, nextElement); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } + }, + + onEmptyHover: function(element, dropon) { + if(element.parentNode!=dropon) { + var oldParentNode = element.parentNode; + dropon.appendChild(element); + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon).onChange(element); + } + }, + + unmark: function() { + if(Sortable._marker) Element.hide(Sortable._marker); + }, + + mark: function(dropon, position) { + // mark on ghosting only + var sortable = Sortable.options(dropon.parentNode); + if(sortable && !sortable.ghosting) return; + + if(!Sortable._marker) { + Sortable._marker = $('dropmarker') || document.createElement('DIV'); + Element.hide(Sortable._marker); + Element.addClassName(Sortable._marker, 'dropmarker'); + Sortable._marker.style.position = 'absolute'; + document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); + } + var offsets = Position.cumulativeOffset(dropon); + Sortable._marker.style.left = offsets[0] + 'px'; + Sortable._marker.style.top = offsets[1] + 'px'; + + if(position=='after') + if(sortable.overlap == 'horizontal') + Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px'; + else + Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px'; + + Element.show(Sortable._marker); + }, + + serialize: function(element) { + element = $(element); + var sortableOptions = this.options(element); + var options = Object.extend({ + tag: sortableOptions.tag, + only: sortableOptions.only, + name: element.id, + format: sortableOptions.format || /^[^_]*_(.*)$/ + }, arguments[1] || {}); + return $(this.findElements(element, options) || []).map( function(item) { + return (encodeURIComponent(options.name) + "[]=" + + encodeURIComponent(item.id.match(options.format) ? item.id.match(options.format)[1] : '')); + }).join(";"); + } +} \ No newline at end of file diff --git a/www/extras/js/sau/effects.js b/www/extras/js/sau/effects.js new file mode 100644 index 000000000..3cce77a53 --- /dev/null +++ b/www/extras/js/sau/effects.js @@ -0,0 +1,841 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Contributors: +// Justin Palmer (http://encytemedia.com/) +// Mark Pilgrim (http://diveintomark.org/) +// Martin Bialasinki +// +// See scriptaculous.js for full license. + +/* ------------- element ext -------------- */ + +// converts rgb() and #xxx to #xxxxxx format, +// returns self (or first argument) if not convertable +String.prototype.parseColor = function() { + var color = '#'; + if(this.slice(0,4) == 'rgb(') { + var cols = this.slice(4,this.length-1).split(','); + var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); + } else { + if(this.slice(0,1) == '#') { + if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); + if(this.length==7) color = this.toLowerCase(); + } + } + return(color.length==7 ? color : (arguments[0] || this)); +} + +Element.collectTextNodesIgnoreClass = function(element, ignoreclass) { + var children = $(element).childNodes; + var text = ''; + var classtest = new RegExp('^([^ ]+ )*' + ignoreclass+ '( [^ ]+)*$','i'); + + for (var i = 0; i < children.length; i++) { + if(children[i].nodeType==3) { + text+=children[i].nodeValue; + } else { + if((!children[i].className.match(classtest)) && children[i].hasChildNodes()) + text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass); + } + } + + return text; +} + +Element.setStyle = function(element, style) { + element = $(element); + for(k in style) element.style[k.camelize()] = style[k]; +} + +Element.setContentZoom = function(element, percent) { + Element.setStyle(element, {fontSize: (percent/100) + 'em'}); + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); +} + +Element.getOpacity = function(element){ + var opacity; + if (opacity = Element.getStyle(element, 'opacity')) + return parseFloat(opacity); + if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/)) + if(opacity[1]) return parseFloat(opacity[1]) / 100; + return 1.0; +} + +Element.setOpacity = function(element, value){ + element= $(element); + if (value == 1){ + Element.setStyle(element, {opacity: 0.999999}); + if(/MSIE/.test(navigator.userAgent)) + Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')}); + } else { + if(value < 0.00001) value = 0; + Element.setStyle(element, {opacity: value}); + if(/MSIE/.test(navigator.userAgent)) + Element.setStyle(element, + { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') + + 'alpha(opacity='+value*100+')' }); + } +} + +Element.getInlineOpacity = function(element){ + return $(element).style.opacity || ''; +} + +Element.childrenWithClassName = function(element, className) { + return $A($(element).getElementsByTagName('*')).select( + function(c) { return Element.hasClassName(c, className) }); +} + +Array.prototype.call = function() { + var args = arguments; + this.each(function(f){ f.apply(this, args) }); +} + +/*--------------------------------------------------------------------------*/ + +var Effect = { + tagifyText: function(element) { + var tagifyStyle = 'position:relative'; + if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1'; + element = $(element); + $A(element.childNodes).each( function(child) { + if(child.nodeType==3) { + child.nodeValue.toArray().each( function(character) { + element.insertBefore( + Builder.node('span',{style: tagifyStyle}, + character == ' ' ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if(((typeof element == 'object') || + (typeof element == 'function')) && + (element.length)) + elements = element; + else + elements = $(element).childNodes; + + var options = Object.extend({ + speed: 0.1, + delay: 0.0 + }, arguments[2] || {}); + + $A(elements).each( function(element, index) { + new effect(element, Object.extend(options, { delay: options.delay + index * options.speed })); + }); + } +}; + +var Effect2 = Effect; // deprecated + +/* ------------- transitions ------------- */ + +Effect.Transitions = {} + +Effect.Transitions.linear = function(pos) { + return pos; +} +Effect.Transitions.sinoidal = function(pos) { + return (-Math.cos(pos*Math.PI)/2) + 0.5; +} +Effect.Transitions.reverse = function(pos) { + return 1-pos; +} +Effect.Transitions.flicker = function(pos) { + return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; +} +Effect.Transitions.wobble = function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; +} +Effect.Transitions.pulse = function(pos) { + return (Math.floor(pos*10) % 2 == 0 ? + (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10))); +} +Effect.Transitions.none = function(pos) { + return 0; +} +Effect.Transitions.full = function(pos) { + return 1; +} + +/* ------------- core effects ------------- */ + +Effect.Queue = { + effects: [], + _each: function(iterator) { + this.effects._each(iterator); + }, + interval: null, + add: function(effect) { + var timestamp = new Date().getTime(); + + switch(effect.options.queue) { + case 'front': + // move unstarted effects after this effect + this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { + e.startOn += effect.finishOn; + e.finishOn += effect.finishOn; + }); + break; + case 'end': + // start effect after last queued effect has finished + timestamp = this.effects.pluck('finishOn').max() || timestamp; + break; + } + + effect.startOn += timestamp; + effect.finishOn += timestamp; + this.effects.push(effect); + if(!this.interval) + this.interval = setInterval(this.loop.bind(this), 40); + }, + remove: function(effect) { + this.effects = this.effects.reject(function(e) { return e==effect }); + if(this.effects.length == 0) { + clearInterval(this.interval); + this.interval = null; + } + }, + loop: function() { + var timePos = new Date().getTime(); + this.effects.invoke('loop', timePos); + } +} +Object.extend(Effect.Queue, Enumerable); + +Effect.Base = function() {}; +Effect.Base.prototype = { + position: null, + setOptions: function(options) { + this.options = Object.extend({ + transition: Effect.Transitions.sinoidal, + duration: 1.0, // seconds + fps: 25.0, // max. 25fps due to Effect.Queue implementation + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' + }, options || {}); + }, + start: function(options) { + this.setOptions(options || {}); + this.currentFrame = 0; + this.state = 'idle'; + this.startOn = this.options.delay*1000; + this.finishOn = this.startOn + (this.options.duration*1000); + this.event('beforeStart'); + if(!this.options.sync) Effect.Queue.add(this); + }, + loop: function(timePos) { + if(timePos >= this.startOn) { + if(timePos >= this.finishOn) { + this.render(1.0); + this.cancel(); + this.event('beforeFinish'); + if(this.finish) this.finish(); + this.event('afterFinish'); + return; + } + var pos = (timePos - this.startOn) / (this.finishOn - this.startOn); + var frame = Math.round(pos * this.options.fps * this.options.duration); + if(frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + render: function(pos) { + if(this.state == 'idle') { + this.state = 'running'; + this.event('beforeSetup'); + if(this.setup) this.setup(); + this.event('afterSetup'); + } + if(this.options.transition) pos = this.options.transition(pos); + pos *= (this.options.to-this.options.from); + pos += this.options.from; + this.position = pos; + this.event('beforeUpdate'); + if(this.update) this.update(pos); + this.event('afterUpdate'); + }, + cancel: function() { + if(!this.options.sync) Effect.Queue.remove(this); + this.state = 'finished'; + }, + event: function(eventName) { + if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); + if(this.options[eventName]) this.options[eventName](this); + } +} + +Effect.Parallel = Class.create(); +Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { + initialize: function(effects) { + this.effects = effects || []; + this.start(arguments[1]); + }, + update: function(position) { + this.effects.invoke('render', position); + }, + finish: function(position) { + this.effects.each( function(effect) { + effect.render(1.0); + effect.cancel(); + effect.event('beforeFinish'); + if(effect.finish) effect.finish(position); + effect.event('afterFinish'); + }); + } +}); + +Effect.Opacity = Class.create(); +Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + // make this work on IE on elements without 'layout' + if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout)) + Element.setStyle(this.element, {zoom: 1}); + var options = Object.extend({ + from: Element.getOpacity(this.element) || 0.0, + to: 1.0 + }, arguments[1] || {}); + this.start(options); + }, + update: function(position) { + Element.setOpacity(this.element, position); + } +}); + +Effect.MoveBy = Class.create(); +Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), { + initialize: function(element, toTop, toLeft) { + this.element = $(element); + this.toTop = toTop; + this.toLeft = toLeft; + this.start(arguments[3]); + }, + setup: function() { + // Bug in Opera: Opera returns the "real" position of a static element or + // relative element that does not have top/left explicitly set. + // ==> Always set top and left for position relative elements in your stylesheets + // (to 0 if you do not need them) + Element.makePositioned(this.element); + this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0'); + this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0'); + }, + update: function(position) { + Element.setStyle(this.element, { + top: this.toTop * position + this.originalTop + 'px', + left: this.toLeft * position + this.originalLeft + 'px' + }); + } +}); + +Effect.Scale = Class.create(); +Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { + initialize: function(element, percent) { + this.element = $(element) + var options = Object.extend({ + scaleX: true, + scaleY: true, + scaleContent: true, + scaleFromCenter: false, + scaleMode: 'box', // 'box' or 'contents' or {} with provided values + scaleFrom: 100.0, + scaleTo: percent + }, arguments[2] || {}); + this.start(options); + }, + setup: function() { + this.restoreAfterFinish = this.options.restoreAfterFinish || false; + this.elementPositioning = Element.getStyle(this.element,'position'); + + this.originalStyle = {}; + ['top','left','width','height','fontSize'].each( function(k) { + this.originalStyle[k] = this.element.style[k]; + }.bind(this)); + + this.originalTop = this.element.offsetTop; + this.originalLeft = this.element.offsetLeft; + + var fontSize = Element.getStyle(this.element,'font-size') || '100%'; + ['em','px','%'].each( function(fontSizeType) { + if(fontSize.indexOf(fontSizeType)>0) { + this.fontSize = parseFloat(fontSize); + this.fontSizeType = fontSizeType; + } + }.bind(this)); + + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; + + this.dims = null; + if(this.options.scaleMode=='box') + this.dims = [this.element.clientHeight, this.element.clientWidth]; + if(/^content/.test(this.options.scaleMode)) + this.dims = [this.element.scrollHeight, this.element.scrollWidth]; + if(!this.dims) + this.dims = [this.options.scaleMode.originalHeight, + this.options.scaleMode.originalWidth]; + }, + update: function(position) { + var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); + if(this.options.scaleContent && this.fontSize) + Element.setStyle(this.element, {fontSize: this.fontSize * currentScale + this.fontSizeType }); + this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); + }, + finish: function(position) { + if (this.restoreAfterFinish) Element.setStyle(this.element, this.originalStyle); + }, + setDimensions: function(height, width) { + var d = {}; + if(this.options.scaleX) d.width = width + 'px'; + if(this.options.scaleY) d.height = height + 'px'; + if(this.options.scaleFromCenter) { + var topd = (height - this.dims[0])/2; + var leftd = (width - this.dims[1])/2; + if(this.elementPositioning == 'absolute') { + if(this.options.scaleY) d.top = this.originalTop-topd + 'px'; + if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; + } else { + if(this.options.scaleY) d.top = -topd + 'px'; + if(this.options.scaleX) d.left = -leftd + 'px'; + } + } + Element.setStyle(this.element, d); + } +}); + +Effect.Highlight = Class.create(); +Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {}); + this.start(options); + }, + setup: function() { + // Prevent executing on elements not in the layout flow + if(Element.getStyle(this.element, 'display')=='none') { this.cancel(); return; } + // Disable background image during the effect + this.oldStyle = { + backgroundImage: Element.getStyle(this.element, 'background-image') }; + Element.setStyle(this.element, {backgroundImage: 'none'}); + if(!this.options.endcolor) + this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff'); + if(!this.options.restorecolor) + this.options.restorecolor = Element.getStyle(this.element, 'background-color'); + // init color calculations + this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); + this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); + }, + update: function(position) { + Element.setStyle(this.element,{backgroundColor: $R(0,2).inject('#',function(m,v,i){ + return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) }); + }, + finish: function() { + Element.setStyle(this.element, Object.extend(this.oldStyle, { + backgroundColor: this.options.restorecolor + })); + } +}); + +Effect.ScrollTo = Class.create(); +Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + this.start(arguments[1] || {}); + }, + setup: function() { + Position.prepare(); + var offsets = Position.cumulativeOffset(this.element); + var max = window.innerHeight ? + window.height - window.innerHeight : + document.body.scrollHeight - + (document.documentElement.clientHeight ? + document.documentElement.clientHeight : document.body.clientHeight); + this.scrollStart = Position.deltaY; + this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; + }, + update: function(position) { + Position.prepare(); + window.scrollTo(Position.deltaX, + this.scrollStart + (position*this.delta)); + } +}); + +/* ------------- combination effects ------------- */ + +Effect.Fade = function(element) { + var oldOpacity = Element.getInlineOpacity(element); + var options = Object.extend({ + from: Element.getOpacity(element) || 1.0, + to: 0.0, + afterFinishInternal: function(effect) { with(Element) { + if(effect.options.to!=0) return; + hide(effect.element); + setStyle(effect.element, {opacity: oldOpacity}); }} + }, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Appear = function(element) { + var options = Object.extend({ + from: (Element.getStyle(element, 'display') == 'none' ? 0.0 : Element.getOpacity(element) || 0.0), + to: 1.0, + beforeSetup: function(effect) { with(Element) { + setOpacity(effect.element, effect.options.from); + show(effect.element); }} + }, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Puff = function(element) { + element = $(element); + var oldStyle = { opacity: Element.getInlineOpacity(element), position: Element.getStyle(element, 'position') }; + return new Effect.Parallel( + [ new Effect.Scale(element, 200, + { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], + Object.extend({ duration: 1.0, + beforeSetupInternal: function(effect) { with(Element) { + setStyle(effect.effects[0].element, {position: 'absolute'}); }}, + afterFinishInternal: function(effect) { with(Element) { + hide(effect.effects[0].element); + setStyle(effect.effects[0].element, oldStyle); }} + }, arguments[1] || {}) + ); +} + +Effect.BlindUp = function(element) { + element = $(element); + Element.makeClipping(element); + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + restoreAfterFinish: true, + afterFinishInternal: function(effect) { with(Element) { + [hide, undoClipping].call(effect.element); }} + }, arguments[1] || {}) + ); +} + +Effect.BlindDown = function(element) { + element = $(element); + var oldHeight = Element.getStyle(element, 'height'); + var elementDimensions = Element.getDimensions(element); + return new Effect.Scale(element, 100, + Object.extend({ scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { with(Element) { + makeClipping(effect.element); + setStyle(effect.element, {height: '0px'}); + show(effect.element); + }}, + afterFinishInternal: function(effect) { with(Element) { + undoClipping(effect.element); + setStyle(effect.element, {height: oldHeight}); + }} + }, arguments[1] || {}) + ); +} + +Effect.SwitchOff = function(element) { + element = $(element); + var oldOpacity = Element.getInlineOpacity(element); + return new Effect.Appear(element, { + duration: 0.4, + from: 0, + transition: Effect.Transitions.flicker, + afterFinishInternal: function(effect) { + new Effect.Scale(effect.element, 1, { + duration: 0.3, scaleFromCenter: true, + scaleX: false, scaleContent: false, restoreAfterFinish: true, + beforeSetup: function(effect) { with(Element) { + [makePositioned,makeClipping].call(effect.element); + }}, + afterFinishInternal: function(effect) { with(Element) { + [hide,undoClipping,undoPositioned].call(effect.element); + setStyle(effect.element, {opacity: oldOpacity}); + }} + }) + } + }); +} + +Effect.DropOut = function(element) { + element = $(element); + var oldStyle = { + top: Element.getStyle(element, 'top'), + left: Element.getStyle(element, 'left'), + opacity: Element.getInlineOpacity(element) }; + return new Effect.Parallel( + [ new Effect.MoveBy(element, 100, 0, { sync: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 }) ], + Object.extend( + { duration: 0.5, + beforeSetup: function(effect) { with(Element) { + makePositioned(effect.effects[0].element); }}, + afterFinishInternal: function(effect) { with(Element) { + [hide, undoPositioned].call(effect.effects[0].element); + setStyle(effect.effects[0].element, oldStyle); }} + }, arguments[1] || {})); +} + +Effect.Shake = function(element) { + element = $(element); + var oldStyle = { + top: Element.getStyle(element, 'top'), + left: Element.getStyle(element, 'left') }; + return new Effect.MoveBy(element, 0, 20, + { duration: 0.05, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, -40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, 40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, -40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, 40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, -20, + { duration: 0.05, afterFinishInternal: function(effect) { with(Element) { + undoPositioned(effect.element); + setStyle(effect.element, oldStyle); + }}}) }}) }}) }}) }}) }}); +} + +Effect.SlideDown = function(element) { + element = $(element); + Element.cleanWhitespace(element); + // SlideDown need to have the content of the element wrapped in a container element with fixed height! + var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom'); + var elementDimensions = Element.getDimensions(element); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { with(Element) { + makePositioned(effect.element.firstChild); + if(window.opera) setStyle(effect.element, {top: ''}); + makeClipping(effect.element); + setStyle(effect.element, {height: '0px'}); + show(element); }}, + afterUpdateInternal: function(effect) { with(Element) { + setStyle(effect.element.firstChild, {bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); }}, + afterFinishInternal: function(effect) { with(Element) { + undoClipping(effect.element); + undoPositioned(effect.element.firstChild); + setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }} + }, arguments[1] || {}) + ); +} + +Effect.SlideUp = function(element) { + element = $(element); + Element.cleanWhitespace(element); + var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom'); + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + restoreAfterFinish: true, + beforeStartInternal: function(effect) { with(Element) { + makePositioned(effect.element.firstChild); + if(window.opera) setStyle(effect.element, {top: ''}); + makeClipping(effect.element); + show(element); }}, + afterUpdateInternal: function(effect) { with(Element) { + setStyle(effect.element.firstChild, {bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); }}, + afterFinishInternal: function(effect) { with(Element) { + [hide, undoClipping].call(effect.element); + undoPositioned(effect.element.firstChild); + setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }} + }, arguments[1] || {}) + ); +} + +// Bug in opera makes the TD containing this element expand for a instance after finish +Effect.Squish = function(element) { + return new Effect.Scale(element, window.opera ? 1 : 0, + { restoreAfterFinish: true, + beforeSetup: function(effect) { with(Element) { + makeClipping(effect.element); }}, + afterFinishInternal: function(effect) { with(Element) { + hide(effect.element); + undoClipping(effect.element); }} + }); +} + +Effect.Grow = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransistion: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.full + }, arguments[1] || {}); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: Element.getInlineOpacity(element) }; + + var dims = Element.getDimensions(element); + var initialMoveX, initialMoveY; + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + initialMoveX = initialMoveY = moveX = moveY = 0; + break; + case 'top-right': + initialMoveX = dims.width; + initialMoveY = moveY = 0; + moveX = -dims.width; + break; + case 'bottom-left': + initialMoveX = moveX = 0; + initialMoveY = dims.height; + moveY = -dims.height; + break; + case 'bottom-right': + initialMoveX = dims.width; + initialMoveY = dims.height; + moveX = -dims.width; + moveY = -dims.height; + break; + case 'center': + initialMoveX = dims.width / 2; + initialMoveY = dims.height / 2; + moveX = -dims.width / 2; + moveY = -dims.height / 2; + break; + } + + return new Effect.MoveBy(element, initialMoveY, initialMoveX, { + duration: 0.01, + beforeSetup: function(effect) { with(Element) { + hide(effect.element); + makeClipping(effect.element); + makePositioned(effect.element); + }}, + afterFinishInternal: function(effect) { + new Effect.Parallel( + [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), + new Effect.MoveBy(effect.element, moveY, moveX, { sync: true, transition: options.moveTransition }), + new Effect.Scale(effect.element, 100, { + scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, + sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) + ], Object.extend({ + beforeSetup: function(effect) { with(Element) { + setStyle(effect.effects[0].element, {height: '0px'}); + show(effect.effects[0].element); }}, + afterFinishInternal: function(effect) { with(Element) { + [undoClipping, undoPositioned].call(effect.effects[0].element); + setStyle(effect.effects[0].element, oldStyle); }} + }, options) + ) + } + }); +} + +Effect.Shrink = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransistion: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.none + }, arguments[1] || {}); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: Element.getInlineOpacity(element) }; + + var dims = Element.getDimensions(element); + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + moveX = moveY = 0; + break; + case 'top-right': + moveX = dims.width; + moveY = 0; + break; + case 'bottom-left': + moveX = 0; + moveY = dims.height; + break; + case 'bottom-right': + moveX = dims.width; + moveY = dims.height; + break; + case 'center': + moveX = dims.width / 2; + moveY = dims.height / 2; + break; + } + + return new Effect.Parallel( + [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), + new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), + new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: options.moveTransition }) + ], Object.extend({ + beforeStartInternal: function(effect) { with(Element) { + [makePositioned, makeClipping].call(effect.effects[0].element) }}, + afterFinishInternal: function(effect) { with(Element) { + [hide, undoClipping, undoPositioned].call(effect.effects[0].element); + setStyle(effect.effects[0].element, oldStyle); }} + }, options) + ); +} + +Effect.Pulsate = function(element) { + element = $(element); + var options = arguments[1] || {}; + var oldOpacity = Element.getInlineOpacity(element); + var transition = options.transition || Effect.Transitions.sinoidal; + var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) }; + reverser.bind(transition); + return new Effect.Opacity(element, + Object.extend(Object.extend({ duration: 3.0, from: 0, + afterFinishInternal: function(effect) { Element.setStyle(el, {opacity: oldOpacity}); } + }, options), {transition: reverser})); +} + +Effect.Fold = function(element) { + element = $(element); + var oldStyle = { + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height }; + Element.makeClipping(element); + return new Effect.Scale(element, 5, Object.extend({ + scaleContent: false, + scaleX: false, + afterFinishInternal: function(effect) { + new Effect.Scale(element, 1, { + scaleContent: false, + scaleY: false, + afterFinishInternal: function(effect) { with(Element) { + [hide, undoClipping].call(effect.element); + setStyle(effect.element, oldStyle); + }} }); + }}, arguments[1] || {})); +} diff --git a/www/extras/js/sau/prototype.js b/www/extras/js/sau/prototype.js new file mode 100644 index 000000000..211814fdc --- /dev/null +++ b/www/extras/js/sau/prototype.js @@ -0,0 +1,1758 @@ +/* Prototype JavaScript framework, version 1.4.0_rc3 + * (c) 2005 Sam Stephenson + * + * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff + * against the source tree, available from the Prototype darcs repository. + * + * Prototype is freely distributable under the terms of an MIT-style license. + * + * For details, see the Prototype web site: http://prototype.conio.net/ + * +/*--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.4.0_rc3', + ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)', + + emptyFunction: function() {}, + K: function(x) {return x} +} + +var Class = { + create: function() { + return function() { + this.initialize.apply(this, arguments); + } + } +} + +var Abstract = new Object(); + +Object.extend = function(destination, source) { + for (property in source) { + destination[property] = source[property]; + } + return destination; +} + +Object.inspect = function(object) { + try { + if (object == undefined) return 'undefined'; + if (object == null) return 'null'; + return object.inspect ? object.inspect() : object.toString(); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } +} + +Function.prototype.bind = function(object) { + var __method = this; + return function() { + return __method.apply(object, arguments); + } +} + +Function.prototype.bindAsEventListener = function(object) { + var __method = this; + return function(event) { + return __method.call(object, event || window.event); + } +} + +Object.extend(Number.prototype, { + toColorPart: function() { + var digits = this.toString(16); + if (this < 16) return '0' + digits; + return digits; + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + } +}); + +var Try = { + these: function() { + var returnValue; + + for (var i = 0; i < arguments.length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) {} + } + + return returnValue; + } +} + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create(); +PeriodicalExecuter.prototype = { + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.callback(); + } finally { + this.currentlyExecuting = false; + } + } + } +} + +/*--------------------------------------------------------------------------*/ + +function $() { + var elements = new Array(); + + for (var i = 0; i < arguments.length; i++) { + var element = arguments[i]; + if (typeof element == 'string') + element = document.getElementById(element); + + if (arguments.length == 1) + return element; + + elements.push(element); + } + + return elements; +} +Object.extend(String.prototype, { + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, + + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, + + evalScripts: function() { + return this.extractScripts().map(eval); + }, + + escapeHTML: function() { + var div = document.createElement('div'); + var text = document.createTextNode(this); + div.appendChild(text); + return div.innerHTML; + }, + + unescapeHTML: function() { + var div = document.createElement('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; + }, + + toQueryParams: function() { + var pairs = this.match(/^\??(.*)$/)[1].split('&'); + return pairs.inject({}, function(params, pairString) { + var pair = pairString.split('='); + params[pair[0]] = pair[1]; + return params; + }); + }, + + toArray: function() { + return this.split(''); + }, + + camelize: function() { + var oStringList = this.split('-'); + if (oStringList.length == 1) return oStringList[0]; + + var camelizedString = this.indexOf('-') == 0 + ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) + : oStringList[0]; + + for (var i = 1, len = oStringList.length; i < len; i++) { + var s = oStringList[i]; + camelizedString += s.charAt(0).toUpperCase() + s.substring(1); + } + + return camelizedString; + }, + + inspect: function() { + return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'"; + } +}); + +String.prototype.parseQuery = String.prototype.toQueryParams; + +var $break = new Object(); +var $continue = new Object(); + +var Enumerable = { + each: function(iterator) { + var index = 0; + try { + this._each(function(value) { + try { + iterator(value, index++); + } catch (e) { + if (e != $continue) throw e; + } + }); + } catch (e) { + if (e != $break) throw e; + } + }, + + all: function(iterator) { + var result = true; + this.each(function(value, index) { + result = result && !!(iterator || Prototype.K)(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator) { + var result = true; + this.each(function(value, index) { + if (result = !!(iterator || Prototype.K)(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator) { + var results = []; + this.each(function(value, index) { + results.push(iterator(value, index)); + }); + return results; + }, + + detect: function (iterator) { + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator) { + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(pattern, iterator) { + var results = []; + this.each(function(value, index) { + var stringValue = value.toString(); + if (stringValue.match(pattern)) + results.push((iterator || Prototype.K)(value, index)); + }) + return results; + }, + + include: function(object) { + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inject: function(memo, iterator) { + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.collect(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (value >= (result || value)) + result = value; + }); + return result; + }, + + min: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (value <= (result || value)) + result = value; + }); + return result; + }, + + partition: function(iterator) { + var trues = [], falses = []; + this.each(function(value, index) { + ((iterator || Prototype.K)(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value, index) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator) { + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator) { + return this.collect(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.collect(Prototype.K); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (typeof args.last() == 'function') + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + iterator(value = collections.pluck(index)); + return value; + }); + }, + + inspect: function() { + return '#'; + } +} + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray +}); +var $A = Array.from = function(iterable) { + if (iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0; i < iterable.length; i++) + results.push(iterable[i]); + return results; + } +} + +Object.extend(Array.prototype, Enumerable); + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0; i < this.length; i++) + iterator(this[i]); + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + }, + + compact: function() { + return this.select(function(value) { + return value != undefined || value != null; + }); + }, + + flatten: function() { + return this.inject([], function(array, value) { + return array.concat(value.constructor == Array ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + indexOf: function(object) { + for (var i = 0; i < this.length; i++) + if (this[i] == object) return i; + return -1; + }, + + reverse: function() { + var result = []; + for (var i = this.length; i > 0; i--) + result.push(this[i-1]); + return result; + }, + + inspect: function() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } +}); +var Hash = { + _each: function(iterator) { + for (key in this) { + var value = this[key]; + if (typeof value == 'function') continue; + + var pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + merge: function(hash) { + return $H(hash).inject($H(this), function(mergedHash, pair) { + mergedHash[pair.key] = pair.value; + return mergedHash; + }); + }, + + toQueryString: function() { + return this.map(function(pair) { + return pair.map(encodeURIComponent).join('='); + }).join('&'); + }, + + inspect: function() { + return '#'; + } +} + +function $H(object) { + var hash = Object.extend({}, object || {}); + Object.extend(hash, Enumerable); + Object.extend(hash, Hash); + return hash; +} +ObjectRange = Class.create(); +Object.extend(ObjectRange.prototype, Enumerable); +Object.extend(ObjectRange.prototype, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + do { + iterator(value); + value = value.succ(); + } while (this.include(value)); + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')}, + function() {return new XMLHttpRequest()} + ) || false; + }, + + activeRequestCount: 0 +} + +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responderToAdd) { + if (!this.include(responderToAdd)) + this.responders.push(responderToAdd); + }, + + unregister: function(responderToRemove) { + this.responders = this.responders.without(responderToRemove); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (responder[callback] && typeof responder[callback] == 'function') { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) {} + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { + Ajax.activeRequestCount++; + }, + + onComplete: function() { + Ajax.activeRequestCount--; + } +}); + +Ajax.Base = function() {}; +Ajax.Base.prototype = { + setOptions: function(options) { + this.options = { + method: 'post', + asynchronous: true, + parameters: '' + } + Object.extend(this.options, options || {}); + }, + + responseIsSuccess: function() { + return this.transport.status == undefined + || this.transport.status == 0 + || (this.transport.status >= 200 && this.transport.status < 300); + }, + + responseIsFailure: function() { + return !this.responseIsSuccess(); + } +} + +Ajax.Request = Class.create(); +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Request.prototype = Object.extend(new Ajax.Base(), { + initialize: function(url, options) { + this.transport = Ajax.getTransport(); + this.setOptions(options); + this.request(url); + }, + + request: function(url) { + var parameters = this.options.parameters || ''; + if (parameters.length > 0) parameters += '&_='; + + try { + this.url = url; + if (this.options.method == 'get' && parameters.length > 0) + this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; + + Ajax.Responders.dispatch('onCreate', this, this.transport); + + this.transport.open(this.options.method, this.url, + this.options.asynchronous); + + if (this.options.asynchronous) { + this.transport.onreadystatechange = this.onStateChange.bind(this); + setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); + } + + this.setRequestHeaders(); + + var body = this.options.postBody ? this.options.postBody : parameters; + this.transport.send(this.options.method == 'post' ? body : null); + + } catch (e) { + this.dispatchException(e); + } + }, + + setRequestHeaders: function() { + var requestHeaders = + ['X-Requested-With', 'XMLHttpRequest', + 'X-Prototype-Version', Prototype.Version]; + + if (this.options.method == 'post') { + requestHeaders.push('Content-type', + 'application/x-www-form-urlencoded'); + + /* Force "Connection: close" for Mozilla browsers to work around + * a bug where XMLHttpReqeuest sends an incorrect Content-length + * header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType) + requestHeaders.push('Connection', 'close'); + } + + if (this.options.requestHeaders) + requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); + + for (var i = 0; i < requestHeaders.length; i += 2) + this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState != 1) + this.respondToReadyState(this.transport.readyState); + }, + + header: function(name) { + try { + return this.transport.getResponseHeader(name); + } catch (e) {} + }, + + evalJSON: function() { + try { + return eval(this.header('X-JSON')); + } catch (e) {} + }, + + evalResponse: function() { + try { + return eval(this.transport.responseText); + } catch (e) { + this.dispatchException(e); + } + }, + + respondToReadyState: function(readyState) { + var event = Ajax.Request.Events[readyState]; + var transport = this.transport, json = this.evalJSON(); + + if (event == 'Complete') { + try { + (this.options['on' + this.transport.status] + || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(transport, json); + } catch (e) { + this.dispatchException(e); + } + + if (this.header('Content-type') == 'text/javascript') + this.evalResponse(); + } + + try { + (this.options['on' + event] || Prototype.emptyFunction)(transport, json); + Ajax.Responders.dispatch('on' + event, this, transport, json); + } catch (e) { + this.dispatchException(e); + } + + /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ + if (event == 'Complete') + this.transport.onreadystatechange = Prototype.emptyFunction; + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Updater = Class.create(); + +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { + initialize: function(container, url, options) { + this.containers = { + success: container.success ? $(container.success) : $(container), + failure: container.failure ? $(container.failure) : + (container.success ? null : $(container)) + } + + this.transport = Ajax.getTransport(); + this.setOptions(options); + + var onComplete = this.options.onComplete || Prototype.emptyFunction; + this.options.onComplete = (function(transport, object) { + this.updateContent(); + onComplete(transport, object); + }).bind(this); + + this.request(url); + }, + + updateContent: function() { + var receiver = this.responseIsSuccess() ? + this.containers.success : this.containers.failure; + var response = this.transport.responseText; + + if (!this.options.evalScripts) + response = response.stripScripts(); + + if (receiver) { + if (this.options.insertion) { + new this.options.insertion(receiver, response); + } else { + Element.update(receiver, response); + } + } + + if (this.responseIsSuccess()) { + if (this.onComplete) + setTimeout(this.onComplete.bind(this), 10); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(); +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { + initialize: function(container, url, options) { + this.setOptions(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = {}; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(request) { + if (this.options.decay) { + this.decay = (request.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = request.responseText; + } + this.timer = setTimeout(this.onTimerEvent.bind(this), + this.decay * this.frequency * 1000); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); +document.getElementsByClassName = function(className, parentElement) { + var children = ($(parentElement) || document.body).getElementsByTagName('*'); + return $A(children).inject([], function(elements, child) { + if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) + elements.push(child); + return elements; + }); +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Element) { + var Element = new Object(); +} + +Object.extend(Element, { + visible: function(element) { + return $(element).style.display != 'none'; + }, + + toggle: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + Element[Element.visible(element) ? 'hide' : 'show'](element); + } + }, + + hide: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = 'none'; + } + }, + + show: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = ''; + } + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + }, + + update: function(element, html) { + $(element).innerHTML = html.stripScripts(); + setTimeout(function() {html.evalScripts()}, 10); + }, + + getHeight: function(element) { + element = $(element); + return element.offsetHeight; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).include(className); + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).add(className); + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + return Element.classNames(element).remove(className); + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + element = $(element); + for (var i = 0; i < element.childNodes.length; i++) { + var node = element.childNodes[i]; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + Element.remove(node); + } + }, + + empty: function(element) { + return $(element).innerHTML.match(/^\s*$/); + }, + + scrollTo: function(element) { + element = $(element); + var x = element.x ? element.x : element.offsetLeft, + y = element.y ? element.y : element.offsetTop; + window.scrollTo(x, y); + }, + + getStyle: function(element, style) { + element = $(element); + var value = element.style[style.camelize()]; + if (!value) { + if (document.defaultView && document.defaultView.getComputedStyle) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css.getPropertyValue(style) : null; + } else if (element.currentStyle) { + value = element.currentStyle[style.camelize()]; + } + } + + if (window.opera && ['left', 'top', 'right', 'bottom'].include(style)) + if (Element.getStyle(element, 'position') == 'static') value = 'auto'; + + return value == 'auto' ? null : value; + }, + + getDimensions: function(element) { + element = $(element); + if (Element.getStyle(element, 'display') != 'none') + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = ''; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = 'none'; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + makePositioned: function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if (pos == 'static' || !pos) { + element._madePositioned = true; + element.style.position = 'relative'; + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) { + element.style.top = 0; + element.style.left = 0; + } + } + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return; + element._overflow = element.style.overflow; + if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') + element.style.overflow = 'hidden'; + }, + + undoClipping: function(element) { + element = $(element); + if (element._overflow) return; + element.style.overflow = element._overflow; + element._overflow = undefined; + } +}); + +var Toggle = new Object(); +Toggle.display = Element.toggle; + +/*--------------------------------------------------------------------------*/ + +Abstract.Insertion = function(adjacency) { + this.adjacency = adjacency; +} + +Abstract.Insertion.prototype = { + initialize: function(element, content) { + this.element = $(element); + this.content = content.stripScripts(); + + if (this.adjacency && this.element.insertAdjacentHTML) { + try { + this.element.insertAdjacentHTML(this.adjacency, this.content); + } catch (e) { + if (this.element.tagName.toLowerCase() == 'tbody') { + this.insertContent(this.contentFromAnonymousTable()); + } else { + throw e; + } + } + } else { + this.range = this.element.ownerDocument.createRange(); + if (this.initializeRange) this.initializeRange(); + this.insertContent([this.range.createContextualFragment(this.content)]); + } + + setTimeout(function() {content.evalScripts()}, 10); + }, + + contentFromAnonymousTable: function() { + var div = document.createElement('div'); + div.innerHTML = '' + this.content + '
    '; + return $A(div.childNodes[0].childNodes[0].childNodes); + } +} + +var Insertion = new Object(); + +Insertion.Before = Class.create(); +Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { + initializeRange: function() { + this.range.setStartBefore(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, this.element); + }).bind(this)); + } +}); + +Insertion.Top = Class.create(); +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(true); + }, + + insertContent: function(fragments) { + fragments.reverse().each((function(fragment) { + this.element.insertBefore(fragment, this.element.firstChild); + }).bind(this)); + } +}); + +Insertion.Bottom = Class.create(); +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.appendChild(fragment); + }).bind(this)); + } +}); + +Insertion.After = Class.create(); +Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { + initializeRange: function() { + this.range.setStartAfter(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, + this.element.nextSibling); + }).bind(this)); + } +}); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set(this.toArray().concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set(this.select(function(className) { + return className != classNameToRemove; + }).join(' ')); + }, + + toString: function() { + return this.toArray().join(' '); + } +} + +Object.extend(Element.ClassNames.prototype, Enumerable); +var Field = { + clear: function() { + for (var i = 0; i < arguments.length; i++) + $(arguments[i]).value = ''; + }, + + focus: function(element) { + $(element).focus(); + }, + + present: function() { + for (var i = 0; i < arguments.length; i++) + if ($(arguments[i]).value == '') return false; + return true; + }, + + select: function(element) { + $(element).select(); + }, + + activate: function(element) { + element = $(element); + element.focus(); + if (element.select) + element.select(); + } +} + +/*--------------------------------------------------------------------------*/ + +var Form = { + serialize: function(form) { + var elements = Form.getElements($(form)); + var queryComponents = new Array(); + + for (var i = 0; i < elements.length; i++) { + var queryComponent = Form.Element.serialize(elements[i]); + if (queryComponent) + queryComponents.push(queryComponent); + } + + return queryComponents.join('&'); + }, + + getElements: function(form) { + form = $(form); + var elements = new Array(); + + for (tagName in Form.Element.Serializers) { + var tagElements = form.getElementsByTagName(tagName); + for (var j = 0; j < tagElements.length; j++) + elements.push(tagElements[j]); + } + return elements; + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) + return inputs; + + var matchingInputs = new Array(); + for (var i = 0; i < inputs.length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || + (name && input.name != name)) + continue; + matchingInputs.push(input); + } + + return matchingInputs; + }, + + disable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.blur(); + element.disabled = 'true'; + } + }, + + enable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.disabled = ''; + } + }, + + findFirstElement: function(form) { + return Form.getElements(form).find(function(element) { + return element.type != 'hidden' && !element.disabled && + ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + Field.activate(Form.findFirstElement(form)); + }, + + reset: function(form) { + $(form).reset(); + } +} + +Form.Element = { + serialize: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) + return encodeURIComponent(parameter[0]) + '=' + + encodeURIComponent(parameter[1]); + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) + return parameter[1]; + } +} + +Form.Element.Serializers = { + input: function(element) { + switch (element.type.toLowerCase()) { + case 'submit': + case 'hidden': + case 'password': + case 'text': + return Form.Element.Serializers.textarea(element); + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element); + } + return false; + }, + + inputSelector: function(element) { + if (element.checked) + return [element.name, element.value]; + }, + + textarea: function(element) { + return [element.name, element.value]; + }, + + select: function(element) { + return Form.Element.Serializers[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + }, + + selectOne: function(element) { + var value = '', opt, index = element.selectedIndex; + if (index >= 0) { + opt = element.options[index]; + value = opt.value; + if (!value && !('value' in opt)) + value = opt.text; + } + return [element.name, value]; + }, + + selectMany: function(element) { + var value = new Array(); + for (var i = 0; i < element.length; i++) { + var opt = element.options[i]; + if (opt.selected) { + var optValue = opt.value; + if (!optValue && !('value' in opt)) + optValue = opt.text; + value.push(optValue); + } + } + return [element.name, value]; + } +} + +/*--------------------------------------------------------------------------*/ + +var $F = Form.Element.getValue; + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = function() {} +Abstract.TimedObserver.prototype = { + initialize: function(element, frequency, callback) { + this.frequency = frequency; + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + } +} + +Form.Element.Observer = Class.create(); +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(); +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = function() {} +Abstract.EventObserver.prototype = { + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + var elements = Form.getElements(this.element); + for (var i = 0; i < elements.length; i++) + this.registerCallback(elements[i]); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + case 'password': + case 'text': + case 'textarea': + case 'select-one': + case 'select-multiple': + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +} + +Form.Element.EventObserver = Class.create(); +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(); +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) { + var Event = new Object(); +} + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + + element: function(event) { + return event.target || event.srcElement; + }, + + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + pointerX: function(event) { + return event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)); + }, + + pointerY: function(event) { + return event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)); + }, + + stop: function(event) { + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } else { + event.returnValue = false; + event.cancelBubble = true; + } + }, + + // find the first node with the given tagName, starting from the + // node the event was triggered on; traverses the DOM upwards + findElement: function(event, tagName) { + var element = Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))) + element = element.parentNode; + return element; + }, + + observers: false, + + _observeAndCache: function(element, name, observer, useCapture) { + if (!this.observers) this.observers = []; + if (element.addEventListener) { + this.observers.push([element, name, observer, useCapture]); + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + this.observers.push([element, name, observer, useCapture]); + element.attachEvent('on' + name, observer); + } + }, + + unloadCache: function() { + if (!Event.observers) return; + for (var i = 0; i < Event.observers.length; i++) { + Event.stopObserving.apply(this, Event.observers[i]); + Event.observers[i][0] = null; + } + Event.observers = false; + }, + + observe: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.attachEvent)) + name = 'keydown'; + + this._observeAndCache(element, name, observer, useCapture); + }, + + stopObserving: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (navigator.appVersion.match(/Konqueror|Safari|KHTML/) + || element.detachEvent)) + name = 'keydown'; + + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element.detachEvent) { + element.detachEvent('on' + name, observer); + } + } +}); + +/* prevent memory leaks in IE */ +Event.observe(window, 'unload', Event.unloadCache, false); +var Position = { + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + realOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return [valueL, valueT]; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + p = Element.getStyle(element, 'position'); + if (p == 'relative' || p == 'absolute') break; + } + } while (element); + return [valueL, valueT]; + }, + + offsetParent: function(element) { + if (element.offsetParent) return element.offsetParent; + if (element == document.body) return element; + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return element; + + return document.body; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = this.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = this.realOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = this.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + clone: function(source, target) { + source = $(source); + target = $(target); + target.style.position = 'absolute'; + var offsets = this.cumulativeOffset(source); + target.style.top = offsets[1] + 'px'; + target.style.left = offsets[0] + 'px'; + target.style.width = source.offsetWidth + 'px'; + target.style.height = source.offsetHeight + 'px'; + }, + + page: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent==document.body) + if (Element.getStyle(element,'position')=='absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } while (element = element.parentNode); + + return [valueL, valueT]; + }, + + clone: function(source, target) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || {}) + + // find page position of source + source = $(source); + var p = Position.page(source); + + // find coordinate system to use + target = $(target); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(target,'position') == 'absolute') { + parent = Position.offsetParent(target); + delta = Position.page(parent); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if(options.setWidth) target.style.width = source.offsetWidth + 'px'; + if(options.setHeight) target.style.height = source.offsetHeight + 'px'; + }, + + absolutize: function(element) { + element = $(element); + if (element.style.position == 'absolute') return; + Position.prepare(); + + var offsets = Position.positionedOffset(element); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px';; + element.style.left = left + 'px';; + element.style.width = width + 'px';; + element.style.height = height + 'px';; + }, + + relativize: function(element) { + element = $(element); + if (element.style.position == 'relative') return; + Position.prepare(); + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; + } +} + +// Safari returns margins on body which is incorrect if the child is absolutely +// positioned. For performance reasons, redefine Position.cumulativeOffset for +// KHTML/WebKit only. +if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + Position.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return [valueL, valueT]; + } +} \ No newline at end of file diff --git a/www/extras/js/sau/scriptaculous.js b/www/extras/js/sau/scriptaculous.js new file mode 100644 index 000000000..5290a8893 --- /dev/null +++ b/www/extras/js/sau/scriptaculous.js @@ -0,0 +1,48 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +var Scriptaculous = { + Version: '1.5_rc5', + require: function(libraryName) { + // inserting via DOM fails in Safari 2.0, so brute force approach + document.write(''); + }, + load: function() { + if((typeof Prototype=='undefined') || + parseFloat(Prototype.Version.split(".")[0] + "." + + Prototype.Version.split(".")[1]) < 1.4) + throw("script.aculo.us requires the Prototype JavaScript framework >= 1.4.0"); + var scriptTags = document.getElementsByTagName("script"); + for(var i=0;i= this.allowedValues.max()) return(this.allowedValues.max()); + if(value <= this.allowedValues.min()) return(this.allowedValues.min()); + + var offset = Math.abs(this.allowedValues[0] - value); + var newValue = this.allowedValues[0]; + this.allowedValues.each( function(v) { + var currentOffset = Math.abs(v - value); + if(currentOffset <= offset){ + newValue = v; + offset = currentOffset; + } + }); + return newValue; + } + if(value > this.range.end) return this.range.end; + if(value < this.range.start) return this.range.start; + return value; + }, + setValue: function(sliderValue, handleIdx){ + if(!this.active) { + this.activeHandle = this.handles[handleIdx]; + this.activeHandleIdx = handleIdx; + this.updateStyles(); + } + handleIdx = handleIdx || this.activeHandleIdx || 0; + if(this.initialized && this.restricted) { + if((handleIdx>0) && (sliderValuethis.values[handleIdx+1])) + sliderValue = this.values[handleIdx+1]; + } + sliderValue = this.getNearestValue(sliderValue); + this.values[handleIdx] = sliderValue; + this.value = this.values[0]; // assure backwards compat + + this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = + this.translateToPx(sliderValue); + + this.drawSpans(); + if(!this.dragging || !this.event) this.updateFinished(); + }, + setValueBy: function(delta, handleIdx) { + this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, + handleIdx || this.activeHandleIdx || 0); + }, + translateToPx: function(value) { + return Math.round((this.trackLength / (this.range.end - this.range.start)) * (value - this.range.start)) + "px"; + }, + translateToValue: function(offset) { + return ((offset/this.trackLength) * (this.range.end - this.range.start)) + this.range.start; + }, + getRange: function(range) { + var v = this.values.sortBy(Prototype.K); + range = range || 0; + return $R(v[range],v[range+1]); + }, + minimumOffset: function(){ + return(this.isVertical() ? this.alignY : this.alignX); + }, + maximumOffset: function(){ + return(this.isVertical() ? + this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX); + }, + isVertical: function(){ + return (this.axis == 'vertical'); + }, + drawSpans: function() { + var slider = this; + if(this.spans) + $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) }); + if(this.options.startSpan) + this.setSpan(this.options.startSpan, + $R(0, this.values.length>1 ? this.getRange(0).min() : this.value )); + if(this.options.endSpan) + this.setSpan(this.options.endSpan, + $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum)); + }, + setSpan: function(span, range) { + if(this.isVertical()) { + span.style.top = this.translateToPx(range.start); + span.style.height = this.translateToPx(range.end - range.start); + } else { + span.style.left = this.translateToPx(range.start); + span.style.width = this.translateToPx(range.end - range.start); + } + }, + updateStyles: function() { + this.handles.each( function(h){ Element.removeClassName(h, 'selected') }); + Element.addClassName(this.activeHandle, 'selected'); + }, + startDrag: function(event) { + if(Event.isLeftClick(event)) { + if(!this.disabled){ + this.active = true; + + var handle = Event.element(event); + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + if(handle==this.track) { + var offsets = Position.cumulativeOffset(this.track); + this.event = event; + this.setValue(this.translateToValue( + this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0] + )); + } else { + // find the handle (prevents issues with Safari) + while((this.handles.indexOf(handle) == -1) && handle.parentNode) + handle = handle.parentNode; + + this.activeHandle = handle; + this.activeHandleIdx = this.handles.indexOf(this.activeHandle); + this.updateStyles(); + + var offsets = Position.cumulativeOffset(this.activeHandle); + this.offsetX = (pointer[0] - offsets[0]); + this.offsetY = (pointer[1] - offsets[1]); + } + } + Event.stop(event); + } + }, + update: function(event) { + if(this.active) { + if(!this.dragging) this.dragging = true; + this.draw(event); + // fix AppleWebKit rendering + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); + Event.stop(event); + } + }, + draw: function(event) { + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var offsets = Position.cumulativeOffset(this.track); + pointer[0] -= this.offsetX + offsets[0]; + pointer[1] -= this.offsetY + offsets[1]; + this.event = event; + this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] )); + if(this.initialized && this.options.onSlide) this.options.onSlide(this.values.length>1 ? this.values : this.value, this); + }, + endDrag: function(event) { + if(this.active && this.dragging) { + this.finishDrag(event, true); + Event.stop(event); + } + this.active = false; + this.dragging = false; + }, + finishDrag: function(event, success) { + this.active = false; + this.dragging = false; + this.updateFinished(); + }, + updateFinished: function() { + if(this.initialized && this.options.onChange) + this.options.onChange(this.values.length>1 ? this.values : this.value, this); + this.event = null; + } +} diff --git a/www/extras/js/sau/unittest.js b/www/extras/js/sau/unittest.js new file mode 100644 index 000000000..20941ad34 --- /dev/null +++ b/www/extras/js/sau/unittest.js @@ -0,0 +1,363 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005 Jon Tirsen (http://www.tirsen.com) +// (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/) +// +// See scriptaculous.js for full license. + +// experimental, Firefox-only +Event.simulateMouse = function(element, eventName) { + var options = Object.extend({ + pointerX: 0, + pointerY: 0, + buttons: 0 + }, arguments[2] || {}); + var oEvent = document.createEvent("MouseEvents"); + oEvent.initMouseEvent(eventName, true, true, document.defaultView, + options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, + false, false, false, false, 0, $(element)); + + if(this.mark) Element.remove(this.mark); + this.mark = document.createElement('div'); + this.mark.appendChild(document.createTextNode(" ")); + document.body.appendChild(this.mark); + this.mark.style.position = 'absolute'; + this.mark.style.top = options.pointerY + "px"; + this.mark.style.left = options.pointerX + "px"; + this.mark.style.width = "5px"; + this.mark.style.height = "5px;"; + this.mark.style.borderTop = "1px solid red;" + this.mark.style.borderLeft = "1px solid red;" + + if(this.step) + alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options)); + + $(element).dispatchEvent(oEvent); +}; + +// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2. +// You need to downgrade to 1.0.4 for now to get this working +// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much +Event.simulateKey = function(element, eventName) { + var options = Object.extend({ + ctrlKey: false, + altKey: false, + shiftKey: false, + metaKey: false, + keyCode: 0, + charCode: 0 + }, arguments[2] || {}); + + var oEvent = document.createEvent("KeyEvents"); + oEvent.initKeyEvent(eventName, true, true, window, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, + options.keyCode, options.charCode ); + $(element).dispatchEvent(oEvent); +}; + +Event.simulateKeys = function(element, command) { + for(var i=0; i ' + + (typeof obj[property] == "string" ? + '"' + obj[property] + '"' : + obj[property])); + } + + return ("'" + obj + "' #" + typeof obj + + ": {" + info.join(", ") + "}"); +} + +Test.Unit.Logger = Class.create(); +Test.Unit.Logger.prototype = { + initialize: function(log) { + this.log = $(log); + if (this.log) { + this._createLogTable(); + } + }, + start: function(testName) { + if (!this.log) return; + this.testName = testName; + this.lastLogLine = document.createElement('tr'); + this.statusCell = document.createElement('td'); + this.nameCell = document.createElement('td'); + this.nameCell.appendChild(document.createTextNode(testName)); + this.messageCell = document.createElement('td'); + this.lastLogLine.appendChild(this.statusCell); + this.lastLogLine.appendChild(this.nameCell); + this.lastLogLine.appendChild(this.messageCell); + this.loglines.appendChild(this.lastLogLine); + }, + finish: function(status, summary) { + if (!this.log) return; + this.lastLogLine.className = status; + this.statusCell.innerHTML = status; + this.messageCell.innerHTML = this._toHTML(summary); + }, + message: function(message) { + if (!this.log) return; + this.messageCell.innerHTML = this._toHTML(message); + }, + summary: function(summary) { + if (!this.log) return; + this.logsummary.innerHTML = this._toHTML(summary); + }, + _createLogTable: function() { + this.log.innerHTML = + '

    ' + + '' + + '' + + '' + + '
    StatusTestMessage
    '; + this.logsummary = $('logsummary') + this.loglines = $('loglines'); + }, + _toHTML: function(txt) { + return txt.escapeHTML().replace(/\n/g,"
    "); + } +} + +Test.Unit.Runner = Class.create(); +Test.Unit.Runner.prototype = { + initialize: function(testcases) { + this.options = Object.extend({ + testLog: 'testlog' + }, arguments[1] || {}); + this.options.resultsURL = this.parseResultsURLQueryParameter(); + if (this.options.testLog) { + this.options.testLog = $(this.options.testLog) || null; + } + if(this.options.tests) { + this.tests = []; + for(var i = 0; i < this.options.tests.length; i++) { + if(/^test/.test(this.options.tests[i])) { + this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"])); + } + } + } else { + if (this.options.test) { + this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])]; + } else { + this.tests = []; + for(var testcase in testcases) { + if(/^test/.test(testcase)) { + this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"])); + } + } + } + } + this.currentTest = 0; + this.logger = new Test.Unit.Logger(this.options.testLog); + setTimeout(this.runTests.bind(this), 1000); + }, + parseResultsURLQueryParameter: function() { + return window.location.search.parseQuery()["resultsURL"]; + }, + // Returns: + // "ERROR" if there was an error, + // "FAILURE" if there was a failure, or + // "SUCCESS" if there was neither + getResult: function() { + var hasFailure = false; + for(var i=0;i 0) { + return "ERROR"; + } + if (this.tests[i].failures > 0) { + hasFailure = true; + } + } + if (hasFailure) { + return "FAILURE"; + } else { + return "SUCCESS"; + } + }, + postResults: function() { + if (this.options.resultsURL) { + new Ajax.Request(this.options.resultsURL, + { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false }); + } + }, + runTests: function() { + var test = this.tests[this.currentTest]; + if (!test) { + // finished! + this.postResults(); + this.logger.summary(this.summary()); + return; + } + if(!test.isWaiting) { + this.logger.start(test.name); + } + test.run(); + if(test.isWaiting) { + this.logger.message("Waiting for " + test.timeToWait + "ms"); + setTimeout(this.runTests.bind(this), test.timeToWait || 1000); + } else { + this.logger.finish(test.status(), test.summary()); + this.currentTest++; + // tail recursive, hopefully the browser will skip the stackframe + this.runTests(); + } + }, + summary: function() { + var assertions = 0; + var failures = 0; + var errors = 0; + var messages = []; + for(var i=0;i 0) return 'failed'; + if (this.errors > 0) return 'error'; + return 'passed'; + }, + assert: function(expression) { + var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"'; + try { expression ? this.pass() : + this.fail(message); } + catch(e) { this.error(e); } + }, + assertEqual: function(expected, actual) { + var message = arguments[2] || "assertEqual"; + try { (expected == actual) ? this.pass() : + this.fail(message + ': expected "' + Test.Unit.inspect(expected) + + '", actual "' + Test.Unit.inspect(actual) + '"'); } + catch(e) { this.error(e); } + }, + assertNotEqual: function(expected, actual) { + var message = arguments[2] || "assertNotEqual"; + try { (expected != actual) ? this.pass() : + this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); } + catch(e) { this.error(e); } + }, + assertNull: function(obj) { + var message = arguments[1] || 'assertNull' + try { (obj==null) ? this.pass() : + this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); } + catch(e) { this.error(e); } + }, + assertHidden: function(element) { + var message = arguments[1] || 'assertHidden'; + this.assertEqual("none", element.style.display, message); + }, + assertNotNull: function(object) { + var message = arguments[1] || 'assertNotNull'; + this.assert(object != null, message); + }, + assertInstanceOf: function(expected, actual) { + var message = arguments[2] || 'assertInstanceOf'; + try { + (actual instanceof expected) ? this.pass() : + this.fail(message + ": object was not an instance of the expected type"); } + catch(e) { this.error(e); } + }, + assertNotInstanceOf: function(expected, actual) { + var message = arguments[2] || 'assertNotInstanceOf'; + try { + !(actual instanceof expected) ? this.pass() : + this.fail(message + ": object was an instance of the not expected type"); } + catch(e) { this.error(e); } + }, + _isVisible: function(element) { + element = $(element); + if(!element.parentNode) return true; + this.assertNotNull(element); + if(element.style && Element.getStyle(element, 'display') == 'none') + return false; + + return this._isVisible(element.parentNode); + }, + assertNotVisible: function(element) { + this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1])); + }, + assertVisible: function(element) { + this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1])); + } +} + +Test.Unit.Testcase = Class.create(); +Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), { + initialize: function(name, test, setup, teardown) { + Test.Unit.Assertions.prototype.initialize.bind(this)(); + this.name = name; + this.test = test || function() {}; + this.setup = setup || function() {}; + this.teardown = teardown || function() {}; + this.isWaiting = false; + this.timeToWait = 1000; + }, + wait: function(time, nextPart) { + this.isWaiting = true; + this.test = nextPart; + this.timeToWait = time; + }, + run: function() { + try { + try { + if (!this.isWaiting) this.setup.bind(this)(); + this.isWaiting = false; + this.test.bind(this)(); + } finally { + if(!this.isWaiting) { + this.teardown.bind(this)(); + } + } + } + catch(e) { this.error(e); } + } +}); \ No newline at end of file