//mootools more, . copyright (c) 2006-2009 aaron newton , valerio proietti & the mootools team , mit style license. /* --- script: more.js description: mootools more license: mit-style license authors: - guillermo rauch - thomas aylott - scott kyle requires: - core:1.2.4/mootools provides: [mootools.more] ... */ mootools.more = { 'version': '1.2.4.4', 'build': '6f6057dc645fdb7547689183b2311063bd653ddf' }; /* --- script: mootools.lang.js description: provides methods for localization. license: mit-style license authors: - aaron newton requires: - core:1.2.4/events - /mootools.more provides: [mootools.lang] ... */ (function(){ var data = { language: 'en-us', languages: { 'en-us': {} }, cascades: ['en-us'] }; var cascaded; mootools.lang = new events(); $extend(mootools.lang, { setlanguage: function(lang){ if (!data.languages[lang]) return this; data.language = lang; this.load(); this.fireevent('langchange', lang); return this; }, load: function() { var langs = this.cascade(this.getcurrentlanguage()); cascaded = {}; $each(langs, function(set, setname){ cascaded[setname] = this.lambda(set); }, this); }, getcurrentlanguage: function(){ return data.language; }, addlanguage: function(lang){ data.languages[lang] = data.languages[lang] || {}; return this; }, cascade: function(lang){ var cascades = (data.languages[lang] || {}).cascades || []; cascades.combine(data.cascades); cascades.erase(lang).push(lang); var langs = cascades.map(function(lng){ return data.languages[lng]; }, this); return $merge.apply(this, langs); }, lambda: function(set) { (set || {}).get = function(key, args){ return $lambda(set[key]).apply(this, $splat(args)); }; return set; }, get: function(set, key, args){ if (cascaded && cascaded[set]) return (key ? cascaded[set].get(key, args) : cascaded[set]); }, set: function(lang, set, members){ this.addlanguage(lang); langdata = data.languages[lang]; if (!langdata[set]) langdata[set] = {}; $extend(langdata[set], members); if (lang == this.getcurrentlanguage()){ this.load(); this.fireevent('langchange', lang); } return this; }, list: function(){ return hash.getkeys(data.languages); } }); })(); /* --- script: log.js description: provides basic logging functionality for plugins to implement. license: mit-style license authors: - guillermo rauch - thomas aylott - scott kyle requires: - core:1.2.4/class - /mootools.more provides: [log] ... */ (function(){ var global = this; var log = function(){ if (global.console && console.log){ try { console.log.apply(console, arguments); } catch(e) { console.log(array.slice(arguments)); } } else { log.logged.push(arguments); } return this; }; var disabled = function(){ this.logged.push(arguments); return this; }; this.log = new class({ logged: [], log: disabled, resetlog: function(){ this.logged.empty(); return this; }, enablelog: function(){ this.log = log; this.logged.each(function(args){ this.log.apply(this, args); }, this); return this.resetlog(); }, disablelog: function(){ this.log = disabled; return this; } }); log.extend(new log).enablelog(); // legacy log.logger = function(){ return this.log.apply(this, arguments); }; })(); /* --- script: depender.js description: a stand alone dependency loader for the mootools library. license: mit-style license authors: - aaron newton requires: - core:1.2.4/element.events - core:1.2.4/request.json - /mootools.more - /log provides: depender ... */ var depender = { options: { /* onrequire: $empty(options), onrequirementloaded: $empty([scripts, options]), onscriptloaded: $empty({ script: script, totalloaded: percentoftotalloaded, loaded: scriptsstate }), serial: false, target: null, nocache: false, log: false,*/ loadedsources: [], loadedscripts: ['core', 'browser', 'array', 'string', 'function', 'number', 'hash', 'element', 'event', 'element.event', 'class', 'domready', 'class.extras', 'request', 'json', 'request.json', 'more', 'depender', 'log'], usescriptinjection: true }, loaded: [], sources: {}, libs: {}, include: function(libs){ this.log('include: ', libs); this.maploaded = false; var loader = function(data){ this.libs = $merge(this.libs, data); $each(this.libs, function(data, lib){ if (data.scripts) this.loadsource(lib, data.scripts); }, this); }.bind(this); if ($type(libs) == 'string'){ this.log('fetching libs ', libs); this.request(libs, loader); } else { loader(libs); } return this; }, required: [], require: function(options){ var loaded = function(){ var scripts = this.calculatedependencies(options.scripts); if (options.sources){ options.sources.each(function(source){ scripts.combine(this.libs[source].files); }, this); } if (options.serial) scripts.combine(this.getloadedscripts()); options.scripts = scripts; this.required.push(options); this.fireevent('require', options); this.loadscripts(options.scripts); }; if (this.maploaded) loaded.call(this); else this.addevent('maploaded', loaded.bind(this)); return this; }, cleandoubleslash: function(str){ if (!str) return str; var prefix = ''; if (str.test(/^http:\/\//)){ prefix = 'http://'; str = str.substring(7, str.length); } str = str.replace(/\/\//g, '/'); return prefix + str; }, request: function(url, callback){ new request.json({ url: url, secure: false, onsuccess: callback }).send(); }, loadsource: function(lib, source){ if (this.libs[lib].files){ this.dataloaded(); return; } this.log('loading source: ', source); this.request(this.cleandoubleslash(source + '/scripts.json'), function(result){ this.log('loaded source: ', source); this.libs[lib].files = result; this.dataloaded(); }.bind(this)); }, dataloaded: function(){ var loaded = true; $each(this.libs, function(v, k){ if (!this.libs[k].files) loaded = false; }, this); if (loaded){ this.maptree(); this.maploaded = true; this.calculateloaded(); this.lastloaded = this.getloadedscripts().getlength(); this.fireevent('maploaded'); this.removeevents('maploaded'); } }, calculateloaded: function(){ var set = function(script){ this.scriptsstate[script] = true; }.bind(this); if (this.options.loadedscripts) this.options.loadedscripts.each(set); if (this.options.loadedsources){ this.options.loadedsources.each(function(lib){ $each(this.libs[lib].files, function(dir){ $each(dir, function(data, file){ set(file); }, this); }, this); }, this); } }, deps: {}, pathmap: {}, maptree: function(){ $each(this.libs, function(data, source){ $each(data.files, function(scripts, folder){ $each(scripts, function(details, script){ var path = source + ':' + folder + ':' + script; if (this.deps[path]) return; this.deps[path] = details.deps; this.pathmap[script] = path; }, this); }, this); }, this); }, getdepsforscript: function(script){ return this.deps[this.pathmap[script]] || []; }, calculatedependencies: function(scripts){ var reqs = []; $splat(scripts).each(function(script){ if (script == 'none' || !script) return; var deps = this.getdepsforscript(script); if (!deps){ if (window.console && console.warn) console.warn('dependencies not mapped: script: %o, map: %o, :deps: %o', script, this.pathmap, this.deps); } else { deps.each(function(scr){ if (scr == script || scr == 'none' || !scr) return; if (!reqs.contains(scr)) reqs.combine(this.calculatedependencies(scr)); reqs.include(scr); }, this); } reqs.include(script); }, this); return reqs; }, getpath: function(script){ try { var chunks = this.pathmap[script].split(':'); var lib = this.libs[chunks[0]]; var dir = (lib.path || lib.scripts) + '/'; chunks.shift(); return this.cleandoubleslash(dir + chunks.join('/') + '.js'); } catch(e){ return script; } }, loadscripts: function(scripts){ scripts = scripts.filter(function(s){ if (!this.scriptsstate[s] && s != 'none'){ this.scriptsstate[s] = false; return true; } }, this); if (scripts.length){ scripts.each(function(scr){ this.loadscript(scr); }, this); } else { this.check(); } }, toload: [], loadscript: function(script){ if (this.scriptsstate[script] && this.toload.length){ this.loadscript(this.toload.shift()); return; } else if (this.loading){ this.toload.push(script); return; } var finish = function(){ this.loading = false; this.scriptloaded(script); if (this.toload.length) this.loadscript(this.toload.shift()); }.bind(this); var error = function(){ this.log('could not load: ', scriptpath); }.bind(this); this.loading = true; var scriptpath = this.getpath(script); if (this.options.usescriptinjection){ this.log('injecting script: ', scriptpath); var loaded = function(){ this.log('loaded script: ', scriptpath); finish(); }.bind(this); new element('script', { src: scriptpath + (this.options.nocache ? '?nocache=' + new date().gettime() : ''), events: { load: loaded, readystatechange: function(){ if (['loaded', 'complete'].contains(this.readystate)) loaded(); }, error: error } }).inject(this.options.target || document.head); } else { this.log('requesting script: ', scriptpath); new request({ url: scriptpath, nocache: this.options.nocache, oncomplete: function(js){ this.log('loaded script: ', scriptpath); $exec(js); finish(); }.bind(this), onfailure: error, onexception: error }).send(); } }, scriptsstate: $h(), getloadedscripts: function(){ return this.scriptsstate.filter(function(state){ return state; }); }, scriptloaded: function(script){ this.log('loaded script: ', script); this.scriptsstate[script] = true; this.check(); var loaded = this.getloadedscripts(); var loadedlength = loaded.getlength(); var toload = this.scriptsstate.getlength(); this.fireevent('scriptloaded', { script: script, totalloaded: (loadedlength / toload * 100).round(), currentloaded: ((loadedlength - this.lastloaded) / (toload - this.lastloaded) * 100).round(), loaded: loaded }); if (loadedlength == toload) this.lastloaded = loadedlength; }, lastloaded: 0, check: function(){ var incomplete = []; this.required.each(function(required){ var loaded = []; required.scripts.each(function(script){ if (this.scriptsstate[script]) loaded.push(script); }, this); if (required.onstep){ required.onstep({ percent: loaded.length / required.scripts.length * 100, scripts: loaded }); }; if (required.scripts.length != loaded.length) return; required.callback(); this.required.erase(required); this.fireevent('requirementloaded', [loaded, required]); }, this); } }; $extend(depender, new events); $extend(depender, new options); $extend(depender, new log); depender._setoptions = depender.setoptions; depender.setoptions = function(){ depender._setoptions.apply(depender, arguments); if (this.options.log) depender.enablelog(); return this; }; /* --- script: class.refactor.js description: extends a class onto itself with new property, preserving any items attached to the class's namespace. license: mit-style license authors: - aaron newton requires: - core:1.2.4/class - /mootools.more provides: [class.refactor] ... */ class.refactor = function(original, refactors){ $each(refactors, function(item, name){ var origin = original.prototype[name]; if (origin && (origin = origin._origin) && typeof item == 'function') original.implement(name, function(){ var old = this.previous; this.previous = origin; var value = item.apply(this, arguments); this.previous = old; return value; }); else original.implement(name, item); }); return original; }; /* --- script: class.binds.js description: automagically binds specified methods in a class to the instance of the class. license: mit-style license authors: - aaron newton requires: - core:1.2.4/class - /mootools.more provides: [class.binds] ... */ class.mutators.binds = function(binds){ return binds; }; class.mutators.initialize = function(initialize){ return function(){ $splat(this.binds).each(function(name){ var original = this[name]; if (original) this[name] = original.bind(this); }, this); return initialize.apply(this, arguments); }; }; /* --- script: class.occlude.js description: prevents a class from being applied to a dom element twice. license: mit-style license. authors: - aaron newton requires: - core/1.2.4/class - core:1.2.4/element - /mootools.more provides: [class.occlude] ... */ class.occlude = new class({ occlude: function(property, element){ element = document.id(element || this.element); var instance = element.retrieve(property || this.property); if (instance && !$defined(this.occluded)) return this.occluded = instance; this.occluded = false; element.store(property || this.property, this); return this.occluded; } }); /* --- script: chain.wait.js description: value, adds a method to inject pauses between chained events. license: mit-style license. authors: - aaron newton requires: - core:1.2.4/chain - core:1.2.4/element - core:1.2.4/fx - /mootools.more provides: [chain.wait] ... */ (function(){ var wait = { wait: function(duration){ return this.chain(function(){ this.callchain.delay($pick(duration, 500), this); }.bind(this)); } }; chain.implement(wait); if (window.fx){ fx.implement(wait); ['css', 'tween', 'elements'].each(function(cls){ if (fx[cls]) fx[cls].implement(wait); }); } element.implement({ chains: function(effects){ $splat($pick(effects, ['tween', 'morph', 'reveal'])).each(function(effect){ effect = this.get(effect); if (!effect) return; effect.setoptions({ link:'chain' }); }, this); return this; }, pausefx: function(duration, effect){ this.chains(effect).get($pick(effect, 'tween')).wait(duration); return this; } }); })(); /* --- script: array.extras.js description: extends the array native object to include useful methods to work with arrays. license: mit-style license authors: - christoph pojer requires: - core:1.2.4/array provides: [array.extras] ... */ array.implement({ min: function(){ return math.min.apply(null, this); }, max: function(){ return math.max.apply(null, this); }, average: function(){ return this.length ? this.sum() / this.length : 0; }, sum: function(){ var result = 0, l = this.length; if (l){ do { result += this[--l]; } while (l); } return result; }, unique: function(){ return [].combine(this); }, shuffle: function(){ for (var i = this.length; i && --i;){ var temp = this[i], r = math.floor(math.random() * ( i + 1 )); this[i] = this[r]; this[r] = temp; } return this; } }); /* --- script: date.js description: extends the date native object to include methods useful in managing dates. license: mit-style license authors: - aaron newton - nicholas barthelemy - https://svn.nbarthelemy.com/date-js/ - harald kirshner - mail [at] digitarald.de; http://digitarald.de - scott kyle - scott [at] appden.com; http://appden.com requires: - core:1.2.4/array - core:1.2.4/string - core:1.2.4/number - core:1.2.4/lang - core:1.2.4/date.english.us - /mootools.more provides: [date] ... */ (function(){ var date = this.date; if (!date.now) date.now = $time; date.methods = { ms: 'milliseconds', year: 'fullyear', min: 'minutes', mo: 'month', sec: 'seconds', hr: 'hours' }; ['date', 'day', 'fullyear', 'hours', 'milliseconds', 'minutes', 'month', 'seconds', 'time', 'timezoneoffset', 'week', 'timezone', 'gmtoffset', 'dayofyear', 'lastmonth', 'lastdayofmonth', 'utcdate', 'utcday', 'utcfullyear', 'ampm', 'ordinal', 'utchours', 'utcmilliseconds', 'utcminutes', 'utcmonth', 'utcseconds'].each(function(method){ date.methods[method.tolowercase()] = method; }); var pad = function(what, length){ return new array(length - string(what).length + 1).join('0') + what; }; date.implement({ set: function(prop, value){ switch ($type(prop)){ case 'object': for (var p in prop) this.set(p, prop[p]); break; case 'string': prop = prop.tolowercase(); var m = date.methods; if (m[prop]) this['set' + m[prop]](value); } return this; }, get: function(prop){ prop = prop.tolowercase(); var m = date.methods; if (m[prop]) return this['get' + m[prop]](); return null; }, clone: function(){ return new date(this.get('time')); }, increment: function(interval, times){ interval = interval || 'day'; times = $pick(times, 1); switch (interval){ case 'year': return this.increment('month', times * 12); case 'month': var d = this.get('date'); this.set('date', 1).set('mo', this.get('mo') + times); return this.set('date', d.min(this.get('lastdayofmonth'))); case 'week': return this.increment('day', times * 7); case 'day': return this.set('date', this.get('date') + times); } if (!date.units[interval]) throw new error(interval + ' is not a supported interval'); return this.set('time', this.get('time') + times * date.units[interval]()); }, decrement: function(interval, times){ return this.increment(interval, -1 * $pick(times, 1)); }, isleapyear: function(){ return date.isleapyear(this.get('year')); }, cleartime: function(){ return this.set({hr: 0, min: 0, sec: 0, ms: 0}); }, diff: function(date, resolution){ if ($type(date) == 'string') date = date.parse(date); return ((date - this) / date.units[resolution || 'day'](3, 3)).toint(); // non-leap year, 30-day month }, getlastdayofmonth: function(){ return date.daysinmonth(this.get('mo'), this.get('year')); }, getdayofyear: function(){ return (date.utc(this.get('year'), this.get('mo'), this.get('date') + 1) - date.utc(this.get('year'), 0, 1)) / date.units.day(); }, getweek: function(){ return (this.get('dayofyear') / 7).ceil(); }, getordinal: function(day){ return date.getmsg('ordinal', day || this.get('date')); }, gettimezone: function(){ return this.tostring() .replace(/^.*? ([a-z]{3}).[0-9]{4}.*$/, '$1') .replace(/^.*?\(([a-z])[a-z]+ ([a-z])[a-z]+ ([a-z])[a-z]+\)$/, '$1$2$3'); }, getgmtoffset: function(){ var off = this.get('timezoneoffset'); return ((off > 0) ? '-' : '+') + pad((off.abs() / 60).floor(), 2) + pad(off % 60, 2); }, setampm: function(ampm){ ampm = ampm.touppercase(); var hr = this.get('hr'); if (hr > 11 && ampm == 'am') return this.decrement('hour', 12); else if (hr < 12 && ampm == 'pm') return this.increment('hour', 12); return this; }, getampm: function(){ return (this.get('hr') < 12) ? 'am' : 'pm'; }, parse: function(str){ this.set('time', date.parse(str)); return this; }, isvalid: function(date) { return !!(date || this).valueof(); }, format: function(f){ if (!this.isvalid()) return 'invalid date'; f = f || '%x %x'; f = formats[f.tolowercase()] || f; // replace short-hand with actual format var d = this; return f.replace(/%([a-z%])/gi, function($0, $1){ switch ($1){ case 'a': return date.getmsg('days')[d.get('day')].substr(0, 3); case 'a': return date.getmsg('days')[d.get('day')]; case 'b': return date.getmsg('months')[d.get('month')].substr(0, 3); case 'b': return date.getmsg('months')[d.get('month')]; case 'c': return d.tostring(); case 'd': return pad(d.get('date'), 2); case 'h': return pad(d.get('hr'), 2); case 'i': return ((d.get('hr') % 12) || 12); case 'j': return pad(d.get('dayofyear'), 3); case 'm': return pad((d.get('mo') + 1), 2); case 'm': return pad(d.get('min'), 2); case 'o': return d.get('ordinal'); case 'p': return date.getmsg(d.get('ampm')); case 's': return pad(d.get('seconds'), 2); case 'u': return pad(d.get('week'), 2); case 'w': return d.get('day'); case 'x': return d.format(date.getmsg('shortdate')); case 'x': return d.format(date.getmsg('shorttime')); case 'y': return d.get('year').tostring().substr(2); case 'y': return d.get('year'); case 't': return d.get('gmtoffset'); case 'z': return d.get('timezone'); } return $1; } ); }, toisostring: function(){ return this.format('iso8601'); } }); date.alias('toisostring', 'tojson'); date.alias('diff', 'compare'); date.alias('format', 'strftime'); var formats = { db: '%y-%m-%d %h:%m:%s', compact: '%y%m%dt%h%m%s', iso8601: '%y-%m-%dt%h:%m:%s%t', rfc822: '%a, %d %b %y %h:%m:%s %z', 'short': '%d %b %h:%m', 'long': '%b %d, %y %h:%m' }; var parsepatterns = []; var nativeparse = date.parse; var parseword = function(type, word, num){ var ret = -1; var translated = date.getmsg(type + 's'); switch ($type(word)){ case 'object': ret = translated[word.get(type)]; break; case 'number': ret = translated[month - 1]; if (!ret) throw new error('invalid ' + type + ' index: ' + index); break; case 'string': var match = translated.filter(function(name){ return this.test(name); }, new regexp('^' + word, 'i')); if (!match.length) throw new error('invalid ' + type + ' string'); if (match.length > 1) throw new error('ambiguous ' + type); ret = match[0]; } return (num) ? translated.indexof(ret) : ret; }; date.extend({ getmsg: function(key, args) { return mootools.lang.get('date', key, args); }, units: { ms: $lambda(1), second: $lambda(1000), minute: $lambda(60000), hour: $lambda(3600000), day: $lambda(86400000), week: $lambda(608400000), month: function(month, year){ var d = new date; return date.daysinmonth($pick(month, d.get('mo')), $pick(year, d.get('year'))) * 86400000; }, year: function(year){ year = year || new date().get('year'); return date.isleapyear(year) ? 31622400000 : 31536000000; } }, daysinmonth: function(month, year){ return [31, date.isleapyear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; }, isleapyear: function(year){ return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0); }, parse: function(from){ var t = $type(from); if (t == 'number') return new date(from); if (t != 'string') return from; from = from.clean(); if (!from.length) return null; var parsed; parsepatterns.some(function(pattern){ var bits = pattern.re.exec(from); return (bits) ? (parsed = pattern.handler(bits)) : false; }); return parsed || new date(nativeparse(from)); }, parseday: function(day, num){ return parseword('day', day, num); }, parsemonth: function(month, num){ return parseword('month', month, num); }, parseutc: function(value){ var localdate = new date(value); var utcseconds = date.utc( localdate.get('year'), localdate.get('mo'), localdate.get('date'), localdate.get('hr'), localdate.get('min'), localdate.get('sec') ); return new date(utcseconds); }, orderindex: function(unit){ return date.getmsg('dateorder').indexof(unit) + 1; }, defineformat: function(name, format){ formats[name] = format; }, defineformats: function(formats){ for (var name in formats) date.defineformat(name, formats[name]); }, parsepatterns: parsepatterns, // this is deprecated defineparser: function(pattern){ parsepatterns.push((pattern.re && pattern.handler) ? pattern : build(pattern)); }, defineparsers: function(){ array.flatten(arguments).each(date.defineparser); }, define2digityearstart: function(year){ startyear = year % 100; startcentury = year - startyear; } }); var startcentury = 1900; var startyear = 70; var regexof = function(type){ return new regexp('(?:' + date.getmsg(type).map(function(name){ return name.substr(0, 3); }).join('|') + ')[a-z]*'); }; var replacers = function(key){ switch(key){ case 'x': // iso8601 covers yyyy-mm-dd, so just check if month is first return ((date.orderindex('month') == 1) ? '%m[.-/]%d' : '%d[.-/]%m') + '([.-/]%y)?'; case 'x': return '%h([.:]%m)?([.:]%s([.:]%s)?)? ?%p? ?%t?'; } return null; }; var keys = { d: /[0-2]?[0-9]|3[01]/, h: /[01]?[0-9]|2[0-3]/, i: /0?[1-9]|1[0-2]/, m: /[0-5]?\d/, s: /\d+/, o: /[a-z]*/, p: /[ap]\.?m\.?/, y: /\d{2}|\d{4}/, y: /\d{4}/, t: /z|[+-]\d{2}(?::?\d{2})?/ }; keys.m = keys.i; keys.s = keys.m; var currentlanguage; var recompile = function(language){ currentlanguage = language; keys.a = keys.a = regexof('days'); keys.b = keys.b = regexof('months'); parsepatterns.each(function(pattern, i){ if (pattern.format) parsepatterns[i] = build(pattern.format); }); }; var build = function(format){ if (!currentlanguage) return {format: format}; var parsed = []; var re = (format.source || format) // allow format to be regex .replace(/%([a-z])/gi, function($0, $1){ return replacers($1) || $0; } ).replace(/\((?!\?)/g, '(?:') // make all groups non-capturing .replace(/ (?!\?|\*)/g, ',? ') // be forgiving with spaces and commas .replace(/%([a-z%])/gi, function($0, $1){ var p = keys[$1]; if (!p) return $1; parsed.push($1); return '(' + p.source + ')'; } ).replace(/\[a-z\]/gi, '[a-z\\u00c0-\\uffff]'); // handle unicode words return { format: format, re: new regexp('^' + re + '$', 'i'), handler: function(bits){ bits = bits.slice(1).associate(parsed); var date = new date().cleartime(); if ('d' in bits) handle.call(date, 'd', 1); if ('m' in bits || 'b' in bits || 'b' in bits) handle.call(date, 'm', 1); for (var key in bits) handle.call(date, key, bits[key]); return date; } }; }; var handle = function(key, value){ if (!value) return this; switch(key){ case 'a': case 'a': return this.set('day', date.parseday(value, true)); case 'b': case 'b': return this.set('mo', date.parsemonth(value, true)); case 'd': return this.set('date', value); case 'h': case 'i': return this.set('hr', value); case 'm': return this.set('mo', value - 1); case 'm': return this.set('min', value); case 'p': return this.set('ampm', value.replace(/\./g, '')); case 's': return this.set('sec', value); case 's': return this.set('ms', ('0.' + value) * 1000); case 'w': return this.set('day', value); case 'y': return this.set('year', value); case 'y': value = +value; if (value < 100) value += startcentury + (value < startyear ? 100 : 0); return this.set('year', value); case 't': if (value == 'z') value = '+00'; var offset = value.match(/([+-])(\d{2}):?(\d{2})?/); offset = (offset[1] + '1') * (offset[2] * 60 + (+offset[3] || 0)) + this.gettimezoneoffset(); return this.set('time', this - offset * 60000); } return this; }; date.defineparsers( '%y([-./]%m([-./]%d((t| )%x)?)?)?', // "1999-12-31", "1999-12-31 11:59pm", "1999-12-31 23:59:59", iso8601 '%y%m%d(t%h(%m%s?)?)?', // "19991231", "19991231t1159", compact '%x( %x)?', // "12/31", "12.31.99", "12-31-1999", "12/31/2008 11:59 pm" '%d%o( %b( %y)?)?( %x)?', // "31st", "31st december", "31 dec 1999", "31 dec 1999 11:59pm" '%b( %d%o)?( %y)?( %x)?', // same as above with month and day switched '%y %b( %d%o( %x)?)?', // same as above with year coming first '%o %b %d %x %t %y' // "thu oct 22 08:11:23 +0000 2009" ); mootools.lang.addevent('langchange', function(language){ if (mootools.lang.get('date')) recompile(language); }).fireevent('langchange', mootools.lang.getcurrentlanguage()); })(); /* --- script: date.extras.js description: extends the date native object to include extra methods (on top of those in date.js). license: mit-style license authors: - aaron newton - scott kyle requires: - /date provides: [date.extras] ... */ date.implement({ timediffinwords: function(relative_to){ return date.distanceoftimeinwords(this, relative_to || new date); }, timediff: function(to, joiner){ if (to == null) to = new date; var delta = ((to - this) / 1000).toint(); if (!delta) return '0s'; var durations = {s: 60, m: 60, h: 24, d: 365, y: 0}; var duration, vals = []; for (var step in durations){ if (!delta) break; if ((duration = durations[step])){ vals.unshift((delta % duration) + step); delta = (delta / duration).toint(); } else { vals.unshift(delta + step); } } return vals.join(joiner || ':'); } }); date.alias('timediffinwords', 'timeagoinwords'); date.extend({ distanceoftimeinwords: function(from, to){ return date.gettimephrase(((to - from) / 1000).toint()); }, gettimephrase: function(delta){ var suffix = (delta < 0) ? 'until' : 'ago'; if (delta < 0) delta *= -1; var units = { minute: 60, hour: 60, day: 24, week: 7, month: 52 / 12, year: 12, eon: infinity }; var msg = 'lessthanminute'; for (var unit in units){ var interval = units[unit]; if (delta < 1.5 * interval){ if (delta > 0.75 * interval) msg = unit; break; } delta /= interval; msg = unit + 's'; } return date.getmsg(msg + suffix).substitute({delta: delta.round()}); } }); date.defineparsers( { // "today", "tomorrow", "yesterday" re: /^(?:tod|tom|yes)/i, handler: function(bits){ var d = new date().cleartime(); switch(bits[0]){ case 'tom': return d.increment(); case 'yes': return d.decrement(); default: return d; } } }, { // "next wednesday", "last thursday" re: /^(next|last) ([a-z]+)$/i, handler: function(bits){ var d = new date().cleartime(); var day = d.getday(); var newday = date.parseday(bits[2], true); var adddays = newday - day; if (newday <= day) adddays += 7; if (bits[1] == 'last') adddays -= 7; return d.set('date', d.getdate() + adddays); } } ); /* --- script: hash.extras.js description: extends the hash native object to include getfrompath which allows a path notation to child elements. license: mit-style license authors: - aaron newton requires: - core:1.2.4/hash.base - /mootools.more provides: [hash.extras] ... */ hash.implement({ getfrompath: function(notation){ var source = this.getclean(); notation.replace(/\[([^\]]+)\]|\.([^.[]+)|[^[.]+/g, function(match){ if (!source) return null; var prop = arguments[2] || arguments[1] || arguments[0]; source = (prop in source) ? source[prop] : null; return match; }); return source; }, cleanvalues: function(method){ method = method || $defined; this.each(function(v, k){ if (!method(v)) this.erase(k); }, this); return this; }, run: function(){ var args = arguments; this.each(function(v, k){ if ($type(v) == 'function') v.run(args); }); } }); /* --- script: string.extras.js description: extends the string native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc). license: mit-style license authors: - aaron newton - guillermo rauch requires: - core:1.2.4/string - core:1.2.4/$util - core:1.2.4/array provides: [string.extras] ... */ (function(){ var special = ['à','à','á','á','â','â','ã','ã','ä','ä','å','å','ă','ă','ą','ą','ć','ć','č','č','ç','ç', 'ď','ď','đ','đ', 'è','è','é','é','ê','ê','ë','ë','ě','ě','ę','ę', 'ğ','ğ','ì','ì','í','í','î','î','ï','ï', 'ĺ','ĺ','ľ','ľ','ł','ł', 'ñ','ñ','ň','ň','ń','ń','ò','ò','ó','ó','ô','ô','õ','õ','ö','ö','ø','ø','ő','ř','ř','ŕ','ŕ','š','š','ş','ş','ś','ś', 'ť','ť','ť','ť','ţ','ţ','ù','ù','ú','ú','û','û','ü','ü','ů','ů', 'ÿ','ÿ','ý','ý','ž','ž','ź','ź','ż','ż', 'þ','þ','ð','ð','ß','œ','œ','æ','æ','µ']; var standard = ['a','a','a','a','a','a','a','a','ae','ae','a','a','a','a','a','a','c','c','c','c','c','c','d','d','d','d', 'e','e','e','e','e','e','e','e','e','e','e','e','g','g','i','i','i','i','i','i','i','i','l','l','l','l','l','l', 'n','n','n','n','n','n', 'o','o','o','o','o','o','o','o','oe','oe','o','o','o', 'r','r','r','r', 's','s','s','s','s','s','t','t','t','t','t','t', 'u','u','u','u','u','u','ue','ue','u','u','y','y','y','y','z','z','z','z','z','z','th','th','dh','dh','ss','oe','oe','ae','ae','u']; var tidymap = { "[\xa0\u2002\u2003\u2009]": " ", "\xb7": "*", "[\u2018\u2019]": "'", "[\u201c\u201d]": '"', "\u2026": "...", "\u2013": "-", "\u2014": "--", "\ufffd": "»" }; var getregfortag = function(tag, contents) { tag = tag || ''; var regstr = contents ? "<" + tag + "[^>]*>([\\s\\s]*?)<\/" + tag + ">" : "<\/?" + tag + "([^>]+)?>"; reg = new regexp(regstr, "gi"); return reg; }; string.implement({ standardize: function(){ var text = this; special.each(function(ch, i){ text = text.replace(new regexp(ch, 'g'), standard[i]); }); return text; }, repeat: function(times){ return new array(times + 1).join(this); }, pad: function(length, str, dir){ if (this.length >= length) return this; var pad = (str == null ? ' ' : '' + str).repeat(length - this.length).substr(0, length - this.length); if (!dir || dir == 'right') return this + pad; if (dir == 'left') return pad + this; return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil()); }, gettags: function(tag, contents){ return this.match(getregfortag(tag, contents)) || []; }, striptags: function(tag, contents){ return this.replace(getregfortag(tag, contents), ''); }, tidy: function(){ var txt = this.tostring(); $each(tidymap, function(value, key){ txt = txt.replace(new regexp(key, 'g'), value); }); return txt; } }); })(); /* --- script: string.querystring.js description: methods for dealing with uri query strings. license: mit-style license authors: - sebastian markbåge, aaron newton, lennart pilon, valerio proietti requires: - core:1.2.4/array - core:1.2.4/string - /mootools.more provides: [string.querystring] ... */ string.implement({ parsequerystring: function(){ var vars = this.split(/[&;]/), res = {}; if (vars.length) vars.each(function(val){ var index = val.indexof('='), keys = index < 0 ? [''] : val.substr(0, index).match(/[^\]\[]+/g), value = decodeuricomponent(val.substr(index + 1)), obj = res; keys.each(function(key, i){ var current = obj[key]; if(i < keys.length - 1) obj = obj[key] = current || {}; else if($type(current) == 'array') current.push(value); else obj[key] = $defined(current) ? [current, value] : value; }); }); return res; }, cleanquerystring: function(method){ return this.split('&').filter(function(val){ var index = val.indexof('='), key = index < 0 ? '' : val.substr(0, index), value = val.substr(index + 1); return method ? method.run([key, value]) : $chk(value); }).join('&'); } }); /* --- script: uri.js description: provides methods useful in managing the window location and uris. license: mit-style license authors: - sebastian markb�ge - aaron newton requires: - core:1.2.4/selectors - /string.querystring provides: uri ... */ var uri = new class({ implements: options, options: { /*base: false*/ }, regex: /^(?:(\w+):)?(?:\/\/(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)?(\.\.?$|(?:[^?#\/]*\/)*)([^?#]*)(?:\?([^#]*))?(?:#(.*))?/, parts: ['scheme', 'user', 'password', 'host', 'port', 'directory', 'file', 'query', 'fragment'], schemes: {http: 80, https: 443, ftp: 21, rtsp: 554, mms: 1755, file: 0}, initialize: function(uri, options){ this.setoptions(options); var base = this.options.base || uri.base; if(!uri) uri = base; if (uri && uri.parsed) this.parsed = $unlink(uri.parsed); else this.set('value', uri.href || uri.tostring(), base ? new uri(base) : false); }, parse: function(value, base){ var bits = value.match(this.regex); if (!bits) return false; bits.shift(); return this.merge(bits.associate(this.parts), base); }, merge: function(bits, base){ if ((!bits || !bits.scheme) && (!base || !base.scheme)) return false; if (base){ this.parts.every(function(part){ if (bits[part]) return false; bits[part] = base[part] || ''; return true; }); } bits.port = bits.port || this.schemes[bits.scheme.tolowercase()]; bits.directory = bits.directory ? this.parsedirectory(bits.directory, base ? base.directory : '') : '/'; return bits; }, parsedirectory: function(directory, basedirectory) { directory = (directory.substr(0, 1) == '/' ? '' : (basedirectory || '/')) + directory; if (!directory.test(uri.regs.directorydot)) return directory; var result = []; directory.replace(uri.regs.endslash, '').split('/').each(function(dir){ if (dir == '..' && result.length > 0) result.pop(); else if (dir != '.') result.push(dir); }); return result.join('/') + '/'; }, combine: function(bits){ return bits.value || bits.scheme + '://' + (bits.user ? bits.user + (bits.password ? ':' + bits.password : '') + '@' : '') + (bits.host || '') + (bits.port && bits.port != this.schemes[bits.scheme] ? ':' + bits.port : '') + (bits.directory || '/') + (bits.file || '') + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : ''); }, set: function(part, value, base){ if (part == 'value'){ var scheme = value.match(uri.regs.scheme); if (scheme) scheme = scheme[1]; if (scheme && !$defined(this.schemes[scheme.tolowercase()])) this.parsed = { scheme: scheme, value: value }; else this.parsed = this.parse(value, (base || this).parsed) || (scheme ? { scheme: scheme, value: value } : { value: value }); } else if (part == 'data') { this.setdata(value); } else { this.parsed[part] = value; } return this; }, get: function(part, base){ switch(part){ case 'value': return this.combine(this.parsed, base ? base.parsed : false); case 'data' : return this.getdata(); } return this.parsed[part] || ''; }, go: function(){ document.location.href = this.tostring(); }, touri: function(){ return this; }, getdata: function(key, part){ var qs = this.get(part || 'query'); if (!$chk(qs)) return key ? null : {}; var obj = qs.parsequerystring(); return key ? obj[key] : obj; }, setdata: function(values, merge, part){ if (typeof values == 'string'){ data = this.getdata(); data[arguments[0]] = arguments[1]; values = data; } else if (merge) { values = $merge(this.getdata(), values); } return this.set(part || 'query', hash.toquerystring(values)); }, cleardata: function(part){ return this.set(part || 'query', ''); } }); uri.prototype.tostring = uri.prototype.valueof = function(){ return this.get('value'); }; uri.regs = { endslash: /\/$/, scheme: /^(\w+):/, directorydot: /\.\/|\.$/ }; uri.base = new uri(document.getelements('base[href]', true).getlast(), {base: document.location}); string.implement({ touri: function(options){ return new uri(this, options); } }); /* --- script: uri.relative.js description: extends the uri class to add methods for computing relative and absolute urls. license: mit-style license authors: - sebastian markbåge requires: - /class.refactor - /uri provides: [uri.relative] ... */ uri = class.refactor(uri, { combine: function(bits, base){ if (!base || bits.scheme != base.scheme || bits.host != base.host || bits.port != base.port) return this.previous.apply(this, arguments); var end = bits.file + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : ''); if (!base.directory) return (bits.directory || (bits.file ? '' : './')) + end; var basedir = base.directory.split('/'), reldir = bits.directory.split('/'), path = '', offset; var i = 0; for(offset = 0; offset < basedir.length && offset < reldir.length && basedir[offset] == reldir[offset]; offset++); for(i = 0; i < basedir.length - offset - 1; i++) path += '../'; for(i = offset; i < reldir.length - 1; i++) path += reldir[i] + '/'; return (path || (bits.file ? '' : './')) + end; }, toabsolute: function(base){ base = new uri(base); if (base) base.set('directory', '').set('file', ''); return this.torelative(base); }, torelative: function(base){ return this.get('value', new uri(base)); } }); /* --- script: element.forms.js description: extends the element native object to include methods useful in managing inputs. license: mit-style license authors: - aaron newton requires: - core:1.2.4/element - /mootools.more provides: [element.forms] ... */ element.implement({ tidy: function(){ this.set('value', this.get('value').tidy()); }, gettextinrange: function(start, end){ return this.get('value').substring(start, end); }, getselectedtext: function(){ if (this.setselectionrange) return this.gettextinrange(this.getselectionstart(), this.getselectionend()); return document.selection.createrange().text; }, getselectedrange: function() { if ($defined(this.selectionstart)) return {start: this.selectionstart, end: this.selectionend}; var pos = {start: 0, end: 0}; var range = this.getdocument().selection.createrange(); if (!range || range.parentelement() != this) return pos; var dup = range.duplicate(); if (this.type == 'text') { pos.start = 0 - dup.movestart('character', -100000); pos.end = pos.start + range.text.length; } else { var value = this.get('value'); var offset = value.length; dup.movetoelementtext(this); dup.setendpoint('starttoend', range); if(dup.text.length) offset -= value.match(/[\n\r]*$/)[0].length; pos.end = offset - dup.text.length; dup.setendpoint('starttostart', range); pos.start = offset - dup.text.length; } return pos; }, getselectionstart: function(){ return this.getselectedrange().start; }, getselectionend: function(){ return this.getselectedrange().end; }, setcaretposition: function(pos){ if (pos == 'end') pos = this.get('value').length; this.selectrange(pos, pos); return this; }, getcaretposition: function(){ return this.getselectedrange().start; }, selectrange: function(start, end){ if (this.setselectionrange) { this.focus(); this.setselectionrange(start, end); } else { var value = this.get('value'); var diff = value.substr(start, end - start).replace(/\r/g, '').length; start = value.substr(0, start).replace(/\r/g, '').length; var range = this.createtextrange(); range.collapse(true); range.moveend('character', start + diff); range.movestart('character', start); range.select(); } return this; }, insertatcursor: function(value, select){ var pos = this.getselectedrange(); var text = this.get('value'); this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length)); if ($pick(select, true)) this.selectrange(pos.start, pos.start + value.length); else this.setcaretposition(pos.start + value.length); return this; }, insertaroundcursor: function(options, select){ options = $extend({ before: '', defaultmiddle: '', after: '' }, options); var value = this.getselectedtext() || options.defaultmiddle; var pos = this.getselectedrange(); var text = this.get('value'); if (pos.start == pos.end){ this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length)); this.selectrange(pos.start + options.before.length, pos.end + options.before.length + value.length); } else { var current = text.substring(pos.start, pos.end); this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length)); var selstart = pos.start + options.before.length; if ($pick(select, true)) this.selectrange(selstart, selstart + current.length); else this.setcaretposition(selstart + text.length); } return this; } }); /* --- script: elements.from.js description: returns a collection of elements from a string of html. license: mit-style license authors: - aaron newton requires: - core:1.2.4/element - /mootools.more provides: [elements.from] ... */ elements.from = function(text, excludescripts){ if ($pick(excludescripts, true)) text = text.stripscripts(); var container, match = text.match(/^\s*<(t[dhr]|tbody|tfoot|thead)/i); if (match){ container = new element('table'); var tag = match[1].tolowercase(); if (['td', 'th', 'tr'].contains(tag)){ container = new element('tbody').inject(container); if (tag != 'tr') container = new element('tr').inject(container); } } return (container || new element('div')).set('html', text).getchildren(); }; /* --- script: element.delegation.js description: extends the element native object to include the delegate method for more efficient event management. credits: - "event checking based on the work of daniel steigerwald. license: mit-style license. copyright: copyright (c) 2008 daniel steigerwald, daniel.steigerwald.cz" license: mit-style license authors: - aaron newton - daniel steigerwald requires: - core:1.2.4/element.event - core:1.2.4/selectors - /mootools.more provides: [element.delegation] ... */ (function(addevent, removeevent){ var match = /(.*?):relay\(([^)]+)\)$/, combinators = /[+>~\s]/, splittype = function(type){ var bits = type.match(match); return !bits ? {event: type} : { event: bits[1], selector: bits[2] }; }, check = function(e, selector){ var t = e.target; if (combinators.test(selector = selector.trim())){ var els = this.getelements(selector); for (var i = els.length; i--; ){ var el = els[i]; if (t == el || el.haschild(t)) return el; } } else { for ( ; t && t != this; t = t.parentnode){ if (element.match(t, selector)) return document.id(t); } } return null; }; element.implement({ addevent: function(type, fn){ var splitted = splittype(type); if (splitted.selector){ var monitors = this.retrieve('$moo:delegatemonitors', {}); if (!monitors[type]){ var monitor = function(e){ var el = check.call(this, e, splitted.selector); if (el) this.fireevent(type, [e, el], 0, el); }.bind(this); monitors[type] = monitor; addevent.call(this, splitted.event, monitor); } } return addevent.apply(this, arguments); }, removeevent: function(type, fn){ var splitted = splittype(type); if (splitted.selector){ var events = this.retrieve('events'); if (!events || !events[type] || (fn && !events[type].keys.contains(fn))) return this; if (fn) removeevent.apply(this, [type, fn]); else removeevent.apply(this, type); events = this.retrieve('events'); if (events && events[type] && events[type].keys.length == 0){ var monitors = this.retrieve('$moo:delegatemonitors', {}); removeevent.apply(this, [splitted.event, monitors[type]]); delete monitors[type]; } return this; } return removeevent.apply(this, arguments); }, fireevent: function(type, args, delay, bind){ var events = this.retrieve('events'); if (!events || !events[type]) return this; events[type].keys.each(function(fn){ fn.create({bind: bind || this, delay: delay, arguments: args})(); }, this); return this; } }); })(element.prototype.addevent, element.prototype.removeevent); /* --- script: element.measure.js description: extends the element native object to include methods useful in measuring dimensions. credits: "element.measure / .expose methods by daniel steigerwald license: mit-style license. copyright: copyright (c) 2008 daniel steigerwald, daniel.steigerwald.cz" license: mit-style license authors: - aaron newton requires: - core:1.2.4/element.style - core:1.2.4/element.dimensions - /mootools.more provides: [element.measure] ... */ element.implement({ measure: function(fn){ var vis = function(el) { return !!(!el || el.offsetheight || el.offsetwidth); }; if (vis(this)) return fn.apply(this); var parent = this.getparent(), restorers = [], tomeasure = []; while (!vis(parent) && parent != document.body) { tomeasure.push(parent.expose()); parent = parent.getparent(); } var restore = this.expose(); var result = fn.apply(this); restore(); tomeasure.each(function(restore){ restore(); }); return result; }, expose: function(){ if (this.getstyle('display') != 'none') return $empty; var before = this.style.csstext; this.setstyles({ display: 'block', position: 'absolute', visibility: 'hidden' }); return function(){ this.style.csstext = before; }.bind(this); }, getdimensions: function(options){ options = $merge({computesize: false},options); var dim = {}; var getsize = function(el, options){ return (options.computesize)?el.getcomputedsize(options):el.getsize(); }; var parent = this.getparent('body'); if (parent && this.getstyle('display') == 'none'){ dim = this.measure(function(){ return getsize(this, options); }); } else if (parent){ try { //safari sometimes crashes here, so catch it dim = getsize(this, options); }catch(e){} } else { dim = {x: 0, y: 0}; } return $chk(dim.x) ? $extend(dim, {width: dim.x, height: dim.y}) : $extend(dim, {x: dim.width, y: dim.height}); }, getcomputedsize: function(options){ options = $merge({ styles: ['padding','border'], plains: { height: ['top','bottom'], width: ['left','right'] }, mode: 'both' }, options); var size = {width: 0,height: 0}; switch (options.mode){ case 'vertical': delete size.width; delete options.plains.width; break; case 'horizontal': delete size.height; delete options.plains.height; break; } var getstyles = []; //this function might be useful in other places; perhaps it should be outside this function? $each(options.plains, function(plain, key){ plain.each(function(edge){ options.styles.each(function(style){ getstyles.push((style == 'border') ? style + '-' + edge + '-' + 'width' : style + '-' + edge); }); }); }); var styles = {}; getstyles.each(function(style){ styles[style] = this.getcomputedstyle(style); }, this); var subtracted = []; $each(options.plains, function(plain, key){ //keys: width, height, plains: ['left', 'right'], ['top','bottom'] var capitalized = key.capitalize(); size['total' + capitalized] = size['computed' + capitalized] = 0; plain.each(function(edge){ //top, left, right, bottom size['computed' + edge.capitalize()] = 0; getstyles.each(function(style, i){ //padding, border, etc. //'padding-left'.test('left') size['totalwidth'] = size['width'] + [padding-left] if (style.test(edge)){ styles[style] = styles[style].toint() || 0; //styles['padding-left'] = 5; size['total' + capitalized] = size['total' + capitalized] + styles[style]; size['computed' + edge.capitalize()] = size['computed' + edge.capitalize()] + styles[style]; } //if width != width (so, padding-left, for instance), then subtract that from the total if (style.test(edge) && key != style && (style.test('border') || style.test('padding')) && !subtracted.contains(style)){ subtracted.push(style); size['computed' + capitalized] = size['computed' + capitalized]-styles[style]; } }); }); }); ['width', 'height'].each(function(value){ var lower = value.tolowercase(); if(!$chk(size[lower])) return; size[lower] = size[lower] + this['offset' + value] + size['computed' + value]; size['total' + value] = size[lower] + size['total' + value]; delete size['computed' + value]; }, this); return $extend(styles, size); } }); /* --- script: element.pin.js description: extends the element native object to include the pin method useful for fixed positioning for elements. license: mit-style license authors: - aaron newton requires: - core:1.2.4/element.event - core:1.2.4/element.dimensions - core:1.2.4/element.style - /mootools.more provides: [element.pin] ... */ (function(){ var supportspositionfixed = false; window.addevent('domready', function(){ var test = new element('div').setstyles({ position: 'fixed', top: 0, right: 0 }).inject(document.body); supportspositionfixed = (test.offsettop === 0); test.dispose(); }); element.implement({ pin: function(enable){ if (this.getstyle('display') == 'none') return null; var p, scroll = window.getscroll(); if (enable !== false){ p = this.getposition(); if (!this.retrieve('pinned')){ var pos = { top: p.y - scroll.y, left: p.x - scroll.x }; if (supportspositionfixed){ this.setstyle('position', 'fixed').setstyles(pos); } else { this.store('pinnedbyjs', true); this.setstyles({ position: 'absolute', top: p.y, left: p.x }).addclass('ispinned'); this.store('scrollfixer', (function(){ if (this.retrieve('pinned')) var scroll = window.getscroll(); this.setstyles({ top: pos.top.toint() + scroll.y, left: pos.left.toint() + scroll.x }); }).bind(this)); window.addevent('scroll', this.retrieve('scrollfixer')); } this.store('pinned', true); } } else { var op; if (!browser.engine.trident){ var parent = this.getparent(); op = (parent.getcomputedstyle('position') != 'static' ? parent : parent.getoffsetparent()); } p = this.getposition(op); this.store('pinned', false); var reposition; if (supportspositionfixed && !this.retrieve('pinnedbyjs')){ reposition = { top: p.y + scroll.y, left: p.x + scroll.x }; } else { this.store('pinnedbyjs', false); window.removeevent('scroll', this.retrieve('scrollfixer')); reposition = { top: p.y, left: p.x }; } this.setstyles($merge(reposition, {position: 'absolute'})).removeclass('ispinned'); } return this; }, unpin: function(){ return this.pin(false); }, togglepin: function(){ this.pin(!this.retrieve('pinned')); } }); })(); /* --- script: element.position.js description: extends the element native object to include methods useful positioning elements relative to others. license: mit-style license authors: - aaron newton requires: - core:1.2.4/element.dimensions - /element.measure provides: [elements.position] ... */ (function(){ var original = element.prototype.position; element.implement({ position: function(options){ //call original position if the options are x/y values if (options && ($defined(options.x) || $defined(options.y))) return original ? original.apply(this, arguments) : this; $each(options||{}, function(v, k){ if (!$defined(v)) delete options[k]; }); options = $merge({ // minimum: { x: 0, y: 0 }, // maximum: { x: 0, y: 0}, relativeto: document.body, position: { x: 'center', //left, center, right y: 'center' //top, center, bottom }, edge: false, offset: {x: 0, y: 0}, returnpos: false, relfixedposition: false, ignoremargins: false, ignorescroll: false, allownegative: false }, options); //compute the offset of the parent positioned element if this element is in one var parentoffset = {x: 0, y: 0}, parentpositioned = false; /* dollar around getoffsetparent should not be necessary, but as it does not return * a mootools extended element in ie, an error occurs on the call to expose. see: * http://mootools.lighthouseapp.com/projects/2706/tickets/333-element-getoffsetparent-inconsistency-between-ie-and-other-browsers */ var offsetparent = this.measure(function(){ return document.id(this.getoffsetparent()); }); if (offsetparent && offsetparent != this.getdocument().body){ parentoffset = offsetparent.measure(function(){ return this.getposition(); }); parentpositioned = offsetparent != document.id(options.relativeto); options.offset.x = options.offset.x - parentoffset.x; options.offset.y = options.offset.y - parentoffset.y; } //upperright, bottomright, centerright, upperleft, bottomleft, centerleft //topright, topleft, centertop, centerbottom, center var fixvalue = function(option){ if ($type(option) != 'string') return option; option = option.tolowercase(); var val = {}; if (option.test('left')) val.x = 'left'; else if (option.test('right')) val.x = 'right'; else val.x = 'center'; if (option.test('upper') || option.test('top')) val.y = 'top'; else if (option.test('bottom')) val.y = 'bottom'; else val.y = 'center'; return val; }; options.edge = fixvalue(options.edge); options.position = fixvalue(options.position); if (!options.edge){ if (options.position.x == 'center' && options.position.y == 'center') options.edge = {x:'center', y:'center'}; else options.edge = {x:'left', y:'top'}; } this.setstyle('position', 'absolute'); var rel = document.id(options.relativeto) || document.body, calc = rel == document.body ? window.getscroll() : rel.getposition(), top = calc.y, left = calc.x; var dim = this.getdimensions({computesize: true, styles:['padding', 'border','margin']}); var pos = {}, prefy = options.offset.y, prefx = options.offset.x, winsize = window.getsize(); switch(options.position.x){ case 'left': pos.x = left + prefx; break; case 'right': pos.x = left + prefx + rel.offsetwidth; break; default: //center pos.x = left + ((rel == document.body ? winsize.x : rel.offsetwidth)/2) + prefx; break; } switch(options.position.y){ case 'top': pos.y = top + prefy; break; case 'bottom': pos.y = top + prefy + rel.offsetheight; break; default: //center pos.y = top + ((rel == document.body ? winsize.y : rel.offsetheight)/2) + prefy; break; } if (options.edge){ var edgeoffset = {}; switch(options.edge.x){ case 'left': edgeoffset.x = 0; break; case 'right': edgeoffset.x = -dim.x-dim.computedright-dim.computedleft; break; default: //center edgeoffset.x = -(dim.totalwidth/2); break; } switch(options.edge.y){ case 'top': edgeoffset.y = 0; break; case 'bottom': edgeoffset.y = -dim.y-dim.computedtop-dim.computedbottom; break; default: //center edgeoffset.y = -(dim.totalheight/2); break; } pos.x += edgeoffset.x; pos.y += edgeoffset.y; } pos = { left: ((pos.x >= 0 || parentpositioned || options.allownegative) ? pos.x : 0).toint(), top: ((pos.y >= 0 || parentpositioned || options.allownegative) ? pos.y : 0).toint() }; var xy = {left: 'x', top: 'y'}; ['minimum', 'maximum'].each(function(minmax) { ['left', 'top'].each(function(lr) { var val = options[minmax] ? options[minmax][xy[lr]] : null; if (val != null && pos[lr] < val) pos[lr] = val; }); }); if (rel.getstyle('position') == 'fixed' || options.relfixedposition){ var winscroll = window.getscroll(); pos.top+= winscroll.y; pos.left+= winscroll.x; } if (options.ignorescroll) { var relscroll = rel.getscroll(); pos.top-= relscroll.y; pos.left-= relscroll.x; } if (options.ignoremargins) { pos.left += ( options.edge.x == 'right' ? dim['margin-right'] : options.edge.x == 'center' ? -dim['margin-left'] + ((dim['margin-right'] + dim['margin-left'])/2) : - dim['margin-left'] ); pos.top += ( options.edge.y == 'bottom' ? dim['margin-bottom'] : options.edge.y == 'center' ? -dim['margin-top'] + ((dim['margin-bottom'] + dim['margin-top'])/2) : - dim['margin-top'] ); } pos.left = math.ceil(pos.left); pos.top = math.ceil(pos.top); if (options.returnpos) return pos; else this.setstyles(pos); return this; } }); })(); /* --- script: element.shortcuts.js description: extends the element native object to include some shortcut methods. license: mit-style license authors: - aaron newton requires: - core:1.2.4/element.style - /mootools.more provides: [element.shortcuts] ... */ element.implement({ isdisplayed: function(){ return this.getstyle('display') != 'none'; }, isvisible: function(){ var w = this.offsetwidth, h = this.offsetheight; return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.isdisplayed(); }, toggle: function(){ return this[this.isdisplayed() ? 'hide' : 'show'](); }, hide: function(){ var d; try { //ie fails here if the element is not in the dom d = this.getstyle('display'); } catch(e){} return this.store('originaldisplay', d || '').setstyle('display', 'none'); }, show: function(display){ display = display || this.retrieve('originaldisplay') || 'block'; return this.setstyle('display', (display == 'none') ? 'block' : display); }, swapclass: function(remove, add){ return this.removeclass(remove).addclass(add); } }); /* --- script: form.request.js description: handles the basic functionality of submitting a form and updating a dom element with the result. license: mit-style license authors: - aaron newton requires: - core:1.2.4/element.event - core:1.2.4/request.html - /class.binds - /class.occlude - /spinner - /string.querystring provides: [form.request] ... */ if (!window.form) window.form = {}; (function(){ form.request = new class({ binds: ['onsubmit', 'onformvalidate'], implements: [options, events, class.occlude], options: { //onfailure: $empty, //onsuccess: #empty, //aliased to oncomplete, //onsend: $empty requestoptions: { evalscripts: true, usespinner: true, emulation: false, link: 'ignore' }, extradata: {}, resetform: true }, property: 'form.request', initialize: function(form, update, options) { this.element = document.id(form); if (this.occlude()) return this.occluded; this.update = document.id(update); this.setoptions(options); this.makerequest(); if (this.options.resetform) { this.request.addevent('success', function(){ $try(function(){ this.element.reset(); }.bind(this)); if (window.overtext) overtext.update(); }.bind(this)); } this.attach(); }, toelement: function() { return this.element; }, makerequest: function(){ this.request = new request.html($merge({ update: this.update, emulation: false, spinnertarget: this.element, method: this.element.get('method') || 'post' }, this.options.requestoptions)).addevents({ success: function(text, xml){ ['complete', 'success'].each(function(evt){ this.fireevent(evt, [this.update, text, xml]); }, this); }.bind(this), failure: function(xhr){ this.fireevent('complete').fireevent('failure', xhr); }.bind(this), exception: function(){ this.fireevent('failure', xhr); }.bind(this) }); }, attach: function(attach){ attach = $pick(attach, true); method = attach ? 'addevent' : 'removeevent'; var fv = this.element.retrieve('validator'); if (fv) fv[method]('onformvalidate', this.onformvalidate); if (!fv || !attach) this.element[method]('submit', this.onsubmit); }, detach: function(){ this.attach(false); }, //public method enable: function(){ this.attach(); }, //public method disable: function(){ this.detach(); }, onformvalidate: function(valid, form, e) { var fv = this.element.retrieve('validator'); if (valid || (fv && !fv.options.stoponfailure)) { if (e && e.stop) e.stop(); this.send(); } }, onsubmit: function(e){ if (this.element.retrieve('validator')) { //form validator was created after form.request this.detach(); return; } e.stop(); this.send(); }, send: function(){ var str = this.element.toquerystring().trim(); var data = $h(this.options.extradata).toquerystring(); if (str) str += "&" + data; else str = data; this.fireevent('send', [this.element, str.parsequerystring()]); this.request.send({data: str, url: this.element.get("action")}); return this; } }); element.properties.formrequest = { set: function(){ var opt = array.link(arguments, {options: object.type, update: element.type, updateid: string.type}); var update = opt.update || opt.updateid; var updater = this.retrieve('form.request'); if (update) { if (updater) updater.update = document.id(update); this.store('form.request:update', update); } if (opt.options) { if (updater) updater.setoptions(opt.options); this.store('form.request:options', opt.options); } return this; }, get: function(){ var opt = array.link(arguments, {options: object.type, update: element.type, updateid: string.type}); var update = opt.update || opt.updateid; if (opt.options || update || !this.retrieve('form.request')){ if (opt.options || !this.retrieve('form.request:options')) this.set('form.request', opt.options); if (update) this.set('form.request', update); this.store('form.request', new form.request(this, this.retrieve('form.request:update'), this.retrieve('form.request:options'))); } return this.retrieve('form.request'); } }; element.implement({ formupdate: function(update, options){ this.get('form.request', update, options).send(); return this; } }); })(); /* --- script: form.request.append.js description: handles the basic functionality of submitting a form and updating a dom element with the result. the result is appended to the dom element instead of replacing its contents. license: mit-style license authors: - aaron newton requires: - /form.request - /fx.reveal - /elements.from provides: [form.request.append] ... */ form.request.append = new class({ extends: form.request, options: { //onbeforeeffect: $empty, usereveal: true, revealoptions: {}, inject: 'bottom' }, makerequest: function(){ this.request = new request.html($merge({ url: this.element.get('action'), method: this.element.get('method') || 'post', spinnertarget: this.element }, this.options.requestoptions, { evalscripts: false }) ).addevents({ success: function(tree, elements, html, javascript){ var container; var kids = elements.from(html); if (kids.length == 1) { container = kids[0]; } else { container = new element('div', { styles: { display: 'none' } }).adopt(kids); } container.inject(this.update, this.options.inject); if (this.options.requestoptions.evalscripts) $exec(javascript); this.fireevent('beforeeffect', container); var finish = function(){ this.fireevent('success', [container, this.update, tree, elements, html, javascript]); }.bind(this); if (this.options.usereveal) { container.get('reveal', this.options.revealoptions).chain(finish); container.reveal(); } else { finish(); } }.bind(this), failure: function(xhr){ this.fireevent('failure', xhr); }.bind(this) }); } }); /* --- script: form.validator.js description: a css-class based form validation system. license: mit-style license authors: - aaron newton requires: - core:1.2.4/options - core:1.2.4/events - core:1.2.4/selectors - core:1.2.4/element.event - core:1.2.4/element.style - core:1.2.4/json - /lang- /class.binds - /date element.forms - /form.validator.english - /element.shortcuts provides: [form.validator, inputvalidator, formvalidator.basevalidators] ... */ if (!window.form) window.form = {}; var inputvalidator = new class({ implements: [options], options: { errormsg: 'validation failed.', test: function(field){return true;} }, initialize: function(classname, options){ this.setoptions(options); this.classname = classname; }, test: function(field, props){ if (document.id(field)) return this.options.test(document.id(field), props||this.getprops(field)); else return false; }, geterror: function(field, props){ var err = this.options.errormsg; if ($type(err) == 'function') err = err(document.id(field), props||this.getprops(field)); return err; }, getprops: function(field){ if (!document.id(field)) return {}; return field.get('validatorprops'); } }); element.properties.validatorprops = { set: function(props){ return this.eliminate('validatorprops').store('validatorprops', props); }, get: function(props){ if (props) this.set(props); if (this.retrieve('validatorprops')) return this.retrieve('validatorprops'); if (this.getproperty('validatorprops')){ try { this.store('validatorprops', json.decode(this.getproperty('validatorprops'))); }catch(e){ return {}; } } else { var vals = this.get('class').split(' ').filter(function(cls){ return cls.test(':'); }); if (!vals.length){ this.store('validatorprops', {}); } else { props = {}; vals.each(function(cls){ var split = cls.split(':'); if (split[1]) { try { props[split[0]] = json.decode(split[1]); } catch(e) {} } }); this.store('validatorprops', props); } } return this.retrieve('validatorprops'); } }; form.validator = new class({ implements:[options, events], binds: ['onsubmit'], options: {/* onformvalidate: $empty(isvalid, form, event), onelementvalidate: $empty(isvalid, field, classname, warn), onelementpass: $empty(field), onelementfail: $empty(field, validatorsfailed) */ fieldselectors: 'input, select, textarea', ignorehidden: true, ignoredisabled: true, usetitles: false, evaluateonsubmit: true, evaluatefieldsonblur: true, evaluatefieldsonchange: true, serial: true, stoponfailure: true, warningprefix: function(){ return form.validator.getmsg('warningprefix') || 'warning: '; }, errorprefix: function(){ return form.validator.getmsg('errorprefix') || 'error: '; } }, initialize: function(form, options){ this.setoptions(options); this.element = document.id(form); this.element.store('validator', this); this.warningprefix = $lambda(this.options.warningprefix)(); this.errorprefix = $lambda(this.options.errorprefix)(); if (this.options.evaluateonsubmit) this.element.addevent('submit', this.onsubmit); if (this.options.evaluatefieldsonblur || this.options.evaluatefieldsonchange) this.watchfields(this.getfields()); }, toelement: function(){ return this.element; }, getfields: function(){ return (this.fields = this.element.getelements(this.options.fieldselectors)); }, watchfields: function(fields){ fields.each(function(el){ if (this.options.evaluatefieldsonblur) el.addevent('blur', this.validationmonitor.pass([el, false], this)); if (this.options.evaluatefieldsonchange) el.addevent('change', this.validationmonitor.pass([el, true], this)); }, this); }, validationmonitor: function(){ $clear(this.timer); this.timer = this.validatefield.delay(50, this, arguments); }, onsubmit: function(event){ if (!this.validate(event) && event) event.preventdefault(); else this.reset(); }, reset: function(){ this.getfields().each(this.resetfield, this); return this; }, validate: function(event){ var result = this.getfields().map(function(field){ return this.validatefield(field, true); }, this).every(function(v){ return v;}); this.fireevent('formvalidate', [result, this.element, event]); if (this.options.stoponfailure && !result && event) event.preventdefault(); return result; }, validatefield: function(field, force){ if (this.paused) return true; field = document.id(field); var passed = !field.hasclass('validation-failed'); var failed, warned; if (this.options.serial && !force){ failed = this.element.getelement('.validation-failed'); warned = this.element.getelement('.warning'); } if (field && (!failed || force || field.hasclass('validation-failed') || (failed && !this.options.serial))){ var validators = field.classname.split(' ').some(function(cn){ return this.getvalidator(cn); }, this); var validatorsfailed = []; field.classname.split(' ').each(function(classname){ if (classname && !this.test(classname, field)) validatorsfailed.include(classname); }, this); passed = validatorsfailed.length === 0; if (validators && !field.hasclass('warnonly')){ if (passed){ field.addclass('validation-passed').removeclass('validation-failed'); this.fireevent('elementpass', field); } else { field.addclass('validation-failed').removeclass('validation-passed'); this.fireevent('elementfail', [field, validatorsfailed]); } } if (!warned){ var warnings = field.classname.split(' ').some(function(cn){ if (cn.test('^warn-') || field.hasclass('warnonly')) return this.getvalidator(cn.replace(/^warn-/,'')); else return null; }, this); field.removeclass('warning'); var warnresult = field.classname.split(' ').map(function(cn){ if (cn.test('^warn-') || field.hasclass('warnonly')) return this.test(cn.replace(/^warn-/,''), field, true); else return null; }, this); } } return passed; }, test: function(classname, field, warn){ field = document.id(field); if((this.options.ignorehidden && !field.isvisible()) || (this.options.ignoredisabled && field.get('disabled'))) return true; var validator = this.getvalidator(classname); if (field.hasclass('ignorevalidation')) return true; warn = $pick(warn, false); if (field.hasclass('warnonly')) warn = true; var isvalid = validator ? validator.test(field) : true; if (validator && field.isvisible()) this.fireevent('elementvalidate', [isvalid, field, classname, warn]); if (warn) return true; return isvalid; }, resetfield: function(field){ field = document.id(field); if (field){ field.classname.split(' ').each(function(classname){ if (classname.test('^warn-')) classname = classname.replace(/^warn-/, ''); field.removeclass('validation-failed'); field.removeclass('warning'); field.removeclass('validation-passed'); }, this); } return this; }, stop: function(){ this.paused = true; return this; }, start: function(){ this.paused = false; return this; }, ignorefield: function(field, warn){ field = document.id(field); if (field){ this.enforcefield(field); if (warn) field.addclass('warnonly'); else field.addclass('ignorevalidation'); } return this; }, enforcefield: function(field){ field = document.id(field); if (field) field.removeclass('warnonly').removeclass('ignorevalidation'); return this; } }); form.validator.getmsg = function(key){ return mootools.lang.get('form.validator', key); }; form.validator.adders = { validators:{}, add : function(classname, options){ this.validators[classname] = new inputvalidator(classname, options); //if this is a class (this method is used by instances of form.validator and the form.validator namespace) //extend these validators into it //this allows validators to be global and/or per instance if (!this.initialize){ this.implement({ validators: this.validators }); } }, addallthese : function(validators){ $a(validators).each(function(validator){ this.add(validator[0], validator[1]); }, this); }, getvalidator: function(classname){ return this.validators[classname.split(':')[0]]; } }; $extend(form.validator, form.validator.adders); form.validator.implement(form.validator.adders); form.validator.add('isempty', { errormsg: false, test: function(element){ if (element.type == 'select-one' || element.type == 'select') return !(element.selectedindex >= 0 && element.options[element.selectedindex].value != ''); else return ((element.get('value') == null) || (element.get('value').length == 0)); } }); form.validator.addallthese([ ['required', { errormsg: function(){ return form.validator.getmsg('required'); }, test: function(element){ return !form.validator.getvalidator('isempty').test(element); } }], ['minlength', { errormsg: function(element, props){ if ($type(props.minlength)) return form.validator.getmsg('minlength').substitute({minlength:props.minlength,length:element.get('value').length }); else return ''; }, test: function(element, props){ if ($type(props.minlength)) return (element.get('value').length >= $pick(props.minlength, 0)); else return true; } }], ['maxlength', { errormsg: function(element, props){ //props is {maxlength:10} if ($type(props.maxlength)) return form.validator.getmsg('maxlength').substitute({maxlength:props.maxlength,length:element.get('value').length }); else return ''; }, test: function(element, props){ //if the value is <= than the maxlength value, element passes test return (element.get('value').length <= $pick(props.maxlength, 10000)); } }], ['validate-integer', { errormsg: form.validator.getmsg.pass('integer'), test: function(element){ return form.validator.getvalidator('isempty').test(element) || (/^(-?[1-9]\d*|0)$/).test(element.get('value')); } }], ['validate-numeric', { errormsg: form.validator.getmsg.pass('numeric'), test: function(element){ return form.validator.getvalidator('isempty').test(element) || (/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(element.get('value')); } }], ['validate-digits', { errormsg: form.validator.getmsg.pass('digits'), test: function(element){ return form.validator.getvalidator('isempty').test(element) || (/^[\d() .:\-\+#]+$/.test(element.get('value'))); } }], ['validate-alpha', { errormsg: form.validator.getmsg.pass('alpha'), test: function(element){ return form.validator.getvalidator('isempty').test(element) || (/^[a-za-z]+$/).test(element.get('value')); } }], ['validate-alphanum', { errormsg: form.validator.getmsg.pass('alphanum'), test: function(element){ return form.validator.getvalidator('isempty').test(element) || !(/\w/).test(element.get('value')); } }], ['validate-date', { errormsg: function(element, props){ if (date.parse){ var format = props.dateformat || '%x'; return form.validator.getmsg('datesuchas').substitute({date: new date().format(format)}); } else { return form.validator.getmsg('dateinformatmdy'); } }, test: function(element, props){ if (form.validator.getvalidator('isempty').test(element)) return true; var d; if (date.parse){ var format = props.dateformat || '%x'; d = date.parse(element.get('value')); var formatted = d.format(format); if (formatted != 'invalid date') element.set('value', formatted); return !isnan(d); } else { var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/; if (!regex.test(element.get('value'))) return false; d = new date(element.get('value').replace(regex, '$1/$2/$3')); return (parseint(regexp.$1, 10) == (1 + d.getmonth())) && (parseint(regexp.$2, 10) == d.getdate()) && (parseint(regexp.$3, 10) == d.getfullyear()); } } }], ['validate-email', { errormsg: form.validator.getmsg.pass('email'), test: function(element){ return form.validator.getvalidator('isempty').test(element) || (/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i).test(element.get('value')); } }], ['validate-url', { errormsg: form.validator.getmsg.pass('url'), test: function(element){ return form.validator.getvalidator('isempty').test(element) || (/^(https?|ftp|rmtp|mms):\/\/(([a-z0-9][a-z0-9_-]*)(\.[a-z0-9][a-z0-9_-]*)+)(:(\d+))?\/?/i).test(element.get('value')); } }], ['validate-currency-dollar', { errormsg: form.validator.getmsg.pass('currencydollar'), test: function(element){ // [$]1[##][,###]+[.##] // [$]1###+[.##] // [$]0.## // [$].## return form.validator.getvalidator('isempty').test(element) || (/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(element.get('value')); } }], ['validate-one-required', { errormsg: form.validator.getmsg.pass('onerequired'), test: function(element, props){ var p = document.id(props['validate-one-required']) || element.getparent(); return p.getelements('input').some(function(el){ if (['checkbox', 'radio'].contains(el.get('type'))) return el.get('checked'); return el.get('value'); }); } }] ]); element.properties.validator = { set: function(options){ var validator = this.retrieve('validator'); if (validator) validator.setoptions(options); return this.store('validator:options'); }, get: function(options){ if (options || !this.retrieve('validator')){ if (options || !this.retrieve('validator:options')) this.set('validator', options); this.store('validator', new form.validator(this, this.retrieve('validator:options'))); } return this.retrieve('validator'); } }; element.implement({ validate: function(options){ this.set('validator', options); return this.get('validator', options).validate(); } }); //legacy var formvalidator = form.validator; /* --- script: form.validator.inline.js description: extends form.validator to add inline messages. license: mit-style license authors: - aaron newton requires: - /form.validator provides: [form.validator.inline] ... */ form.validator.inline = new class({ extends: form.validator, options: { scrolltoerrorsonsubmit: true, scrollfxoptions: { transition: 'quad:out', offset: { y: -20 } } }, initialize: function(form, options){ this.parent(form, options); this.addevent('onelementvalidate', function(isvalid, field, classname, warn){ var validator = this.getvalidator(classname); if (!isvalid && validator.geterror(field)){ if (warn) field.addclass('warning'); var advice = this.makeadvice(classname, field, validator.geterror(field), warn); this.insertadvice(advice, field); this.showadvice(classname, field); } else { this.hideadvice(classname, field); } }); }, makeadvice: function(classname, field, error, warn){ var errormsg = (warn)?this.warningprefix:this.errorprefix; errormsg += (this.options.usetitles) ? field.title || error:error; var cssclass = (warn) ? 'warning-advice' : 'validation-advice'; var advice = this.getadvice(classname, field); if(advice) { advice = advice.set('html', errormsg); } else { advice = new element('div', { html: errormsg, styles: { display: 'none' }, id: 'advice-' + classname + '-' + this.getfieldid(field) }).addclass(cssclass); } field.store('advice-' + classname, advice); return advice; }, getfieldid : function(field){ return field.id ? field.id : field.id = 'input_' + field.name; }, showadvice: function(classname, field){ var advice = this.getadvice(classname, field); if (advice && !field.retrieve(this.getpropname(classname)) && (advice.getstyle('display') == 'none' || advice.getstyle('visiblity') == 'hidden' || advice.getstyle('opacity') == 0)){ field.store(this.getpropname(classname), true); if (advice.reveal) advice.reveal(); else advice.setstyle('display', 'block'); } }, hideadvice: function(classname, field){ var advice = this.getadvice(classname, field); if (advice && field.retrieve(this.getpropname(classname))){ field.store(this.getpropname(classname), false); //if fx.reveal.js is present, transition the advice out if (advice.dissolve) advice.dissolve(); else advice.setstyle('display', 'none'); } }, getpropname: function(classname){ return 'advice' + classname; }, resetfield: function(field){ field = document.id(field); if (!field) return this; this.parent(field); field.classname.split(' ').each(function(classname){ this.hideadvice(classname, field); }, this); return this; }, getalladvicemessages: function(field, force){ var advice = []; if (field.hasclass('ignorevalidation') && !force) return advice; var validators = field.classname.split(' ').some(function(cn){ var warner = cn.test('^warn-') || field.hasclass('warnonly'); if (warner) cn = cn.replace(/^warn-/, ''); var validator = this.getvalidator(cn); if (!validator) return; advice.push({ message: validator.geterror(field), warnonly: warner, passed: validator.test(), validator: validator }); }, this); return advice; }, getadvice: function(classname, field){ return field.retrieve('advice-' + classname); }, insertadvice: function(advice, field){ //check for error position prop var props = field.get('validatorprops'); //build advice if (!props.msgpos || !document.id(props.msgpos)){ if(field.type.tolowercase() == 'radio') field.getparent().adopt(advice); else advice.inject(document.id(field), 'after'); } else { document.id(props.msgpos).grab(advice); } }, validatefield: function(field, force){ var result = this.parent(field, force); if (this.options.scrolltoerrorsonsubmit && !result){ var failed = document.id(this).getelement('.validation-failed'); var par = document.id(this).getparent(); while (par != document.body && par.getscrollsize().y == par.getsize().y){ par = par.getparent(); } var fx = par.retrieve('fvscroller'); if (!fx && window.fx && fx.scroll){ fx = new fx.scroll(par, this.options.scrollfxoptions); par.store('fvscroller', fx); } if (failed){ if (fx) fx.toelement(failed); else par.scrollto(par.getscroll().x, failed.getposition(par).y - 20); } } return result; } }); /* --- script: form.validator.extras.js description: additional validators for the form.validator class. license: mit-style license authors: - aaron newton requires: - /form.validator provides: [form.validator.extras] ... */ form.validator.addallthese([ ['validate-enforce-oncheck', { test: function(element, props){ if (element.checked){ var fv = element.getparent('form').retrieve('validator'); if (!fv) return true; (props.toenforce || document.id(props.enforcechildrenof).getelements('input, select, textarea')).map(function(item){ fv.enforcefield(item); }); } return true; } }], ['validate-ignore-oncheck', { test: function(element, props){ if (element.checked){ var fv = element.getparent('form').retrieve('validator'); if (!fv) return true; (props.toignore || document.id(props.ignorechildrenof).getelements('input, select, textarea')).each(function(item){ fv.ignorefield(item); fv.resetfield(item); }); } return true; } }], ['validate-nospace', { errormsg: function(){ return form.validator.getmsg('nospace'); }, test: function(element, props){ return !element.get('value').test(/\s/); } }], ['validate-toggle-oncheck', { test: function(element, props){ var fv = element.getparent('form').retrieve('validator'); if (!fv) return true; var elearr = props.totoggle || document.id(props.totogglechildrenof).getelements('input, select, textarea'); if (!element.checked){ elearr.each(function(item){ fv.ignorefield(item); fv.resetfield(item); }); } else { elearr.each(function(item){ fv.enforcefield(item); }); } return true; } }], ['validate-reqchk-bynode', { errormsg: function(){ return form.validator.getmsg('reqchkbynode'); }, test: function(element, props){ return (document.id(props.nodeid).getelements(props.selector || 'input[type=checkbox], input[type=radio]')).some(function(item){ return item.checked; }); } }], ['validate-required-check', { errormsg: function(element, props){ return props.usetitle ? element.get('title') : form.validator.getmsg('requiredchk'); }, test: function(element, props){ return !!element.checked; } }], ['validate-reqchk-byname', { errormsg: function(element, props){ return form.validator.getmsg('reqchkbyname').substitute({label: props.label || element.get('type')}); }, test: function(element, props){ var grpname = props.groupname || element.get('name'); var onecheckeditem = $$(document.getelementsbyname(grpname)).some(function(item, index){ return item.checked; }); var fv = element.getparent('form').retrieve('validator'); if (onecheckeditem && fv) fv.resetfield(element); return onecheckeditem; } }], ['validate-match', { errormsg: function(element, props){ return form.validator.getmsg('match').substitute({matchname: props.matchname || document.id(props.matchinput).get('name')}); }, test: function(element, props){ var eleval = element.get('value'); var matchval = document.id(props.matchinput) && document.id(props.matchinput).get('value'); return eleval && matchval ? eleval == matchval : true; } }], ['validate-after-date', { errormsg: function(element, props){ return form.validator.getmsg('afterdate').substitute({ label: props.afterlabel || (props.afterelement ? form.validator.getmsg('startdate') : form.validator.getmsg('currentdate')) }); }, test: function(element, props){ var start = document.id(props.afterelement) ? date.parse(document.id(props.afterelement).get('value')) : new date(); var end = date.parse(element.get('value')); return end && start ? end >= start : true; } }], ['validate-before-date', { errormsg: function(element, props){ return form.validator.getmsg('beforedate').substitute({ label: props.beforelabel || (props.beforeelement ? form.validator.getmsg('enddate') : form.validator.getmsg('currentdate')) }); }, test: function(element, props){ var start = date.parse(element.get('value')); var end = document.id(props.beforeelement) ? date.parse(document.id(props.beforeelement).get('value')) : new date(); return end && start ? end >= start : true; } }], ['validate-custom-required', { errormsg: function(){ return form.validator.getmsg('required'); }, test: function(element, props){ return element.get('value') != props.emptyvalue; } }], ['validate-same-month', { errormsg: function(element, props){ var startmo = document.id(props.samemonthas) && document.id(props.samemonthas).get('value'); var eleval = element.get('value'); if (eleval != '') return form.validator.getmsg(startmo ? 'samemonth' : 'startmonth'); }, test: function(element, props){ var d1 = date.parse(element.get('value')); var d2 = date.parse(document.id(props.samemonthas) && document.id(props.samemonthas).get('value')); return d1 && d2 ? d1.format('%b') == d2.format('%b') : true; } }], ['validate-cc-num', { errormsg: function(element){ var ccnum = element.get('value').replace(/[^0-9]/g, ''); return form.validator.getmsg('creditcard').substitute({length: ccnum.length}); }, test: function(element){ // required is a different test if (form.validator.getvalidator('isempty').test(element)) { return true; } // clean number value var ccnum = element.get('value'); ccnum = ccnum.replace(/[^0-9]/g, ''); var valid_type = false; if (ccnum.test(/^4[0-9]{12}([0-9]{3})?$/)) valid_type = 'visa'; else if (ccnum.test(/^5[1-5]([0-9]{14})$/)) valid_type = 'master card'; else if (ccnum.test(/^3[47][0-9]{13}$/)) valid_type = 'american express'; else if (ccnum.test(/^6011[0-9]{12}$/)) valid_type = 'discover'; if (valid_type) { var sum = 0; var cur = 0; for(var i=ccnum.length-1; i>=0; --i) { cur = ccnum.charat(i).toint(); if (cur == 0) { continue; } if ((ccnum.length-i) % 2 == 0) { cur += cur; } if (cur > 9) { cur = cur.tostring().charat(0).toint() + cur.tostring().charat(1).toint(); } sum += cur; } if ((sum % 10) == 0) { return true; } } var chunks = ''; while (ccnum != '') { chunks += ' ' + ccnum.substr(0,4); ccnum = ccnum.substr(4); } element.getparent('form').retrieve('validator').ignorefield(element); element.set('value', chunks.clean()); element.getparent('form').retrieve('validator').enforcefield(element); return false; } }] ]); /* --- script: overtext.js description: shows text over an input that disappears when the user clicks into it. the text remains hidden if the user adds a value. license: mit-style license authors: - aaron newton requires: - core:1.2.4/options - core:1.2.4/events - core:1.2.4/element.event - /class.binds - /class.occlude - /element.position - /element.shortcuts provides: [overtext] ... */ var overtext = new class({ implements: [options, events, class.occlude], binds: ['reposition', 'assert', 'focus', 'hide'], options: {/* textoverride: null, onfocus: $empty() ontexthide: $empty(textel, inputel), ontextshow: $empty(textel, inputel), */ element: 'label', positionoptions: { position: 'upperleft', edge: 'upperleft', offset: { x: 4, y: 2 } }, poll: false, pollinterval: 250, wrap: false }, property: 'overtext', initialize: function(element, options){ this.element = document.id(element); if (this.occlude()) return this.occluded; this.setoptions(options); this.attach(this.element); overtext.instances.push(this); if (this.options.poll) this.poll(); return this; }, toelement: function(){ return this.element; }, attach: function(){ var val = this.options.textoverride || this.element.get('alt') || this.element.get('title'); if (!val) return; this.text = new element(this.options.element, { 'class': 'overtxtlabel', styles: { lineheight: 'normal', position: 'absolute', cursor: 'text' }, html: val, events: { click: this.hide.pass(this.options.element == 'label', this) } }).inject(this.element, 'after'); if (this.options.element == 'label') { if (!this.element.get('id')) this.element.set('id', 'input_' + new date().gettime()); this.text.set('for', this.element.get('id')); } if (this.options.wrap) { this.textholder = new element('div', { styles: { lineheight: 'normal', position: 'relative' }, 'class':'overtxtwrapper' }).adopt(this.text).inject(this.element, 'before'); } this.element.addevents({ focus: this.focus, blur: this.assert, change: this.assert }).store('overtextdiv', this.text); window.addevent('resize', this.reposition.bind(this)); this.assert(true); this.reposition(); }, wrap: function(){ if (this.options.element == 'label') { if (!this.element.get('id')) this.element.set('id', 'input_' + new date().gettime()); this.text.set('for', this.element.get('id')); } }, startpolling: function(){ this.pollingpaused = false; return this.poll(); }, poll: function(stop){ //start immediately //pause on focus //resumeon blur if (this.poller && !stop) return this; var test = function(){ if (!this.pollingpaused) this.assert(true); }.bind(this); if (stop) $clear(this.poller); else this.poller = test.periodical(this.options.pollinterval, this); return this; }, stoppolling: function(){ this.pollingpaused = true; return this.poll(true); }, focus: function(){ if (this.text && (!this.text.isdisplayed() || this.element.get('disabled'))) return; this.hide(); }, hide: function(suppressfocus, force){ if (this.text && (this.text.isdisplayed() && (!this.element.get('disabled') || force))){ this.text.hide(); this.fireevent('texthide', [this.text, this.element]); this.pollingpaused = true; if (!suppressfocus){ try { this.element.fireevent('focus'); this.element.focus(); } catch(e){} //ie barfs if you call focus on hidden elements } } return this; }, show: function(){ if (this.text && !this.text.isdisplayed()){ this.text.show(); this.reposition(); this.fireevent('textshow', [this.text, this.element]); this.pollingpaused = false; } return this; }, assert: function(suppressfocus){ this[this.test() ? 'show' : 'hide'](suppressfocus); }, test: function(){ var v = this.element.get('value'); return !v; }, reposition: function(){ this.assert(true); if (!this.element.isvisible()) return this.stoppolling().hide(); if (this.text && this.test()) this.text.position($merge(this.options.positionoptions, {relativeto: this.element})); return this; } }); overtext.instances = []; $extend(overtext, { each: function(fn) { return overtext.instances.map(function(ot, i){ if (ot.element && ot.text) return fn.apply(overtext, [ot, i]); return null; //the input or the text was destroyed }); }, update: function(){ return overtext.each(function(ot){ return ot.reposition(); }); }, hideall: function(){ return overtext.each(function(ot){ return ot.hide(true, true); }); }, showall: function(){ return overtext.each(function(ot) { return ot.show(); }); } }); if (window.fx && fx.reveal) { fx.reveal.implement({ hideinputs: browser.engine.trident ? 'select, input, textarea, object, embed, .overtxtlabel' : false }); } /* --- script: fx.elements.js description: effect to change any number of css properties of any number of elements. license: mit-style license authors: - valerio proietti requires: - core:1.2.4/fx.css - /mootools.more provides: [fx.elements] ... */ fx.elements = new class({ extends: fx.css, initialize: function(elements, options){ this.elements = this.subject = $$(elements); this.parent(options); }, compute: function(from, to, delta){ var now = {}; for (var i in from){ var ifrom = from[i], ito = to[i], inow = now[i] = {}; for (var p in ifrom) inow[p] = this.parent(ifrom[p], ito[p], delta); } return now; }, set: function(now){ for (var i in now){ var inow = now[i]; for (var p in inow) this.render(this.elements[i], p, inow[p], this.options.unit); } return this; }, start: function(obj){ if (!this.check(obj)) return this; var from = {}, to = {}; for (var i in obj){ var iprops = obj[i], ifrom = from[i] = {}, ito = to[i] = {}; for (var p in iprops){ var parsed = this.prepare(this.elements[i], p, iprops[p]); ifrom[p] = parsed.from; ito[p] = parsed.to; } } return this.parent(from, to); } }); /* --- script: fx.accordion.js description: an fx.elements extension which allows you to easily create accordion type controls. license: mit-style license authors: - valerio proietti requires: - core:1.2.4/element.event - /fx.elements provides: [fx.accordion] ... */ fx.accordion = new class({ extends: fx.elements, options: {/* onactive: $empty(toggler, section), onbackground: $empty(toggler, section), fixedheight: false, fixedwidth: false, */ display: 0, show: false, height: true, width: false, opacity: true, alwayshide: false, trigger: 'click', initialdisplayfx: true, returnheighttoauto: true }, initialize: function(){ var params = array.link(arguments, { 'container': element.type, //deprecated 'options': object.type, 'togglers': $defined, 'elements': $defined }); this.parent(params.elements, params.options); this.togglers = $$(params.togglers); this.previous = -1; this.internalchain = new chain(); if (this.options.alwayshide) this.options.wait = true; if ($chk(this.options.show)){ this.options.display = false; this.previous = this.options.show; } if (this.options.start){ this.options.display = false; this.options.show = false; } this.effects = {}; if (this.options.opacity) this.effects.opacity = 'fullopacity'; if (this.options.width) this.effects.width = this.options.fixedwidth ? 'fullwidth' : 'offsetwidth'; if (this.options.height) this.effects.height = this.options.fixedheight ? 'fullheight' : 'scrollheight'; for (var i = 0, l = this.togglers.length; i < l; i++) this.addsection(this.togglers[i], this.elements[i]); this.elements.each(function(el, i){ if (this.options.show === i){ this.fireevent('active', [this.togglers[i], el]); } else { for (var fx in this.effects) el.setstyle(fx, 0); } }, this); if ($chk(this.options.display) || this.options.initialdisplayfx === false) this.display(this.options.display, this.options.initialdisplayfx); if (this.options.fixedheight !== false) this.options.returnheighttoauto = false; this.addevent('complete', this.internalchain.callchain.bind(this.internalchain)); }, addsection: function(toggler, element){ toggler = document.id(toggler); element = document.id(element); var test = this.togglers.contains(toggler); this.togglers.include(toggler); this.elements.include(element); var idx = this.togglers.indexof(toggler); var displayer = this.display.bind(this, idx); toggler.store('accordion:display', displayer); toggler.addevent(this.options.trigger, displayer); if (this.options.height) element.setstyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'}); if (this.options.width) element.setstyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'}); element.fullopacity = 1; if (this.options.fixedwidth) element.fullwidth = this.options.fixedwidth; if (this.options.fixedheight) element.fullheight = this.options.fixedheight; element.setstyle('overflow', 'hidden'); if (!test){ for (var fx in this.effects) element.setstyle(fx, 0); } return this; }, detach: function(){ this.togglers.each(function(toggler) { toggler.removeevent(this.options.trigger, toggler.retrieve('accordion:display')); }, this); }, display: function(index, usefx){ if (!this.check(index, usefx)) return this; usefx = $pick(usefx, true); if (this.options.returnheighttoauto){ var prev = this.elements[this.previous]; if (prev && !this.selfhidden){ for (var fx in this.effects){ prev.setstyle(fx, prev[this.effects[fx]]); } } } index = ($type(index) == 'element') ? this.elements.indexof(index) : index; if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwayshide)) return this; this.previous = index; var obj = {}; this.elements.each(function(el, i){ obj[i] = {}; var hide; if (i != index){ hide = true; } else if (this.options.alwayshide && ((el.offsetheight > 0 && this.options.height) || el.offsetwidth > 0 && this.options.width)){ hide = true; this.selfhidden = true; } this.fireevent(hide ? 'background' : 'active', [this.togglers[i], el]); for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]]; }, this); this.internalchain.chain(function(){ if (this.options.returnheighttoauto && !this.selfhidden){ var el = this.elements[index]; if (el) el.setstyle('height', 'auto'); }; }.bind(this)); return usefx ? this.start(obj) : this.set(obj); } }); /* compatibility with 1.2.0 */ var accordion = new class({ extends: fx.accordion, initialize: function(){ this.parent.apply(this, arguments); var params = array.link(arguments, {'container': element.type}); this.container = params.container; }, addsection: function(toggler, element, pos){ toggler = document.id(toggler); element = document.id(element); var test = this.togglers.contains(toggler); var len = this.togglers.length; if (len && (!test || pos)){ pos = $pick(pos, len - 1); toggler.inject(this.togglers[pos], 'before'); element.inject(toggler, 'after'); } else if (this.container && !test){ toggler.inject(this.container); element.inject(this.container); } return this.parent.apply(this, arguments); } }); /* --- script: fx.move.js description: defines fx.move, a class that works with element.position.js to transition an element from one location to another. license: mit-style license authors: - aaron newton requires: - core:1.2.4/fx.morph - /element.position provides: [fx.move] ... */ fx.move = new class({ extends: fx.morph, options: { relativeto: document.body, position: 'center', edge: false, offset: {x: 0, y: 0} }, start: function(destination){ return this.parent(this.element.position($merge(this.options, destination, {returnpos: true}))); } }); element.properties.move = { set: function(options){ var morph = this.retrieve('move'); if (morph) morph.cancel(); return this.eliminate('move').store('move:options', $extend({link: 'cancel'}, options)); }, get: function(options){ if (options || !this.retrieve('move')){ if (options || !this.retrieve('move:options')) this.set('move', options); this.store('move', new fx.move(this, this.retrieve('move:options'))); } return this.retrieve('move'); } }; element.implement({ move: function(options){ this.get('move').start(options); return this; } }); /* --- script: fx.reveal.js description: defines fx.reveal, a class that shows and hides elements with a transition. license: mit-style license authors: - aaron newton requires: - core:1.2.4/fx.morph - /element.shortcuts - /element.measure provides: [fx.reveal] ... */ fx.reveal = new class({ extends: fx.morph, options: {/* onshow: $empty(thiselement), onhide: $empty(thiselement), oncomplete: $empty(thiselement), heightoverride: null, widthoverride: null, */ link: 'cancel', styles: ['padding', 'border', 'margin'], transitionopacity: !browser.engine.trident4, mode: 'vertical', display: 'block', hideinputs: browser.engine.trident ? 'select, input, textarea, object, embed' : false }, dissolve: function(){ try { if (!this.hiding && !this.showing){ if (this.element.getstyle('display') != 'none'){ this.hiding = true; this.showing = false; this.hidden = true; this.csstext = this.element.style.csstext; var startstyles = this.element.getcomputedsize({ styles: this.options.styles, mode: this.options.mode }); this.element.setstyle('display', this.options.display); if (this.options.transitionopacity) startstyles.opacity = 1; var zero = {}; $each(startstyles, function(style, name){ zero[name] = [style, 0]; }, this); this.element.setstyle('overflow', 'hidden'); var hidethese = this.options.hideinputs ? this.element.getelements(this.options.hideinputs) : null; this.$chain.unshift(function(){ if (this.hidden){ this.hiding = false; $each(startstyles, function(style, name){ startstyles[name] = style; }, this); this.element.style.csstext = this.csstext; this.element.setstyle('display', 'none'); if (hidethese) hidethese.setstyle('visibility', 'visible'); } this.fireevent('hide', this.element); this.callchain(); }.bind(this)); if (hidethese) hidethese.setstyle('visibility', 'hidden'); this.start(zero); } else { this.callchain.delay(10, this); this.fireevent('complete', this.element); this.fireevent('hide', this.element); } } else if (this.options.link == 'chain'){ this.chain(this.dissolve.bind(this)); } else if (this.options.link == 'cancel' && !this.hiding){ this.cancel(); this.dissolve(); } } catch(e){ this.hiding = false; this.element.setstyle('display', 'none'); this.callchain.delay(10, this); this.fireevent('complete', this.element); this.fireevent('hide', this.element); } return this; }, reveal: function(){ try { if (!this.showing && !this.hiding){ if (this.element.getstyle('display') == 'none' || this.element.getstyle('visiblity') == 'hidden' || this.element.getstyle('opacity') == 0){ this.showing = true; this.hiding = this.hidden = false; var startstyles; this.csstext = this.element.style.csstext; //toggle display, but hide it this.element.measure(function(){ //create the styles for the opened/visible state startstyles = this.element.getcomputedsize({ styles: this.options.styles, mode: this.options.mode }); }.bind(this)); $each(startstyles, function(style, name){ startstyles[name] = style; }); //if we're overridding height/width if ($chk(this.options.heightoverride)) startstyles.height = this.options.heightoverride.toint(); if ($chk(this.options.widthoverride)) startstyles.width = this.options.widthoverride.toint(); if (this.options.transitionopacity) { this.element.setstyle('opacity', 0); startstyles.opacity = 1; } //create the zero state for the beginning of the transition var zero = { height: 0, display: this.options.display }; $each(startstyles, function(style, name){ zero[name] = 0; }); //set to zero this.element.setstyles($merge(zero, {overflow: 'hidden'})); //hide inputs var hidethese = this.options.hideinputs ? this.element.getelements(this.options.hideinputs) : null; if (hidethese) hidethese.setstyle('visibility', 'hidden'); //start the effect this.start(startstyles); this.$chain.unshift(function(){ this.element.style.csstext = this.csstext; this.element.setstyle('display', this.options.display); if (!this.hidden) this.showing = false; if (hidethese) hidethese.setstyle('visibility', 'visible'); this.callchain(); this.fireevent('show', this.element); }.bind(this)); } else { this.callchain(); this.fireevent('complete', this.element); this.fireevent('show', this.element); } } else if (this.options.link == 'chain'){ this.chain(this.reveal.bind(this)); } else if (this.options.link == 'cancel' && !this.showing){ this.cancel(); this.reveal(); } } catch(e){ this.element.setstyles({ display: this.options.display, visiblity: 'visible', opacity: 1 }); this.showing = false; this.callchain.delay(10, this); this.fireevent('complete', this.element); this.fireevent('show', this.element); } return this; }, toggle: function(){ if (this.element.getstyle('display') == 'none' || this.element.getstyle('visiblity') == 'hidden' || this.element.getstyle('opacity') == 0){ this.reveal(); } else { this.dissolve(); } return this; }, cancel: function(){ this.parent.apply(this, arguments); this.element.style.csstext = this.csstext; this.hidding = false; this.showing = false; } }); element.properties.reveal = { set: function(options){ var reveal = this.retrieve('reveal'); if (reveal) reveal.cancel(); return this.eliminate('reveal').store('reveal:options', options); }, get: function(options){ if (options || !this.retrieve('reveal')){ if (options || !this.retrieve('reveal:options')) this.set('reveal', options); this.store('reveal', new fx.reveal(this, this.retrieve('reveal:options'))); } return this.retrieve('reveal'); } }; element.properties.dissolve = element.properties.reveal; element.implement({ reveal: function(options){ this.get('reveal', options).reveal(); return this; }, dissolve: function(options){ this.get('reveal', options).dissolve(); return this; }, nix: function(){ var params = array.link(arguments, {destroy: boolean.type, options: object.type}); this.get('reveal', params.options).dissolve().chain(function(){ this[params.destroy ? 'destroy' : 'dispose'](); }.bind(this)); return this; }, wink: function(){ var params = array.link(arguments, {duration: number.type, options: object.type}); var reveal = this.get('reveal', params.options); reveal.reveal().chain(function(){ (function(){ reveal.dissolve(); }).delay(params.duration || 2000); }); } }); /* --- script: fx.scroll.js description: effect to smoothly scroll any element, including the window. license: mit-style license authors: - valerio proietti requires: - core:1.2.4/fx - core:1.2.4/element.event - core:1.2.4/element.dimensions - /mootools.more provides: [fx.scroll] ... */ fx.scroll = new class({ extends: fx, options: { offset: {x: 0, y: 0}, wheelstops: true }, initialize: function(element, options){ this.element = this.subject = document.id(element); this.parent(options); var cancel = this.cancel.bind(this, false); if ($type(this.element) != 'element') this.element = document.id(this.element.getdocument().body); var stopper = this.element; if (this.options.wheelstops){ this.addevent('start', function(){ stopper.addevent('mousewheel', cancel); }, true); this.addevent('complete', function(){ stopper.removeevent('mousewheel', cancel); }, true); } }, set: function(){ var now = array.flatten(arguments); if (browser.engine.gecko) now = [math.round(now[0]), math.round(now[1])]; this.element.scrollto(now[0], now[1]); }, compute: function(from, to, delta){ return [0, 1].map(function(i){ return fx.compute(from[i], to[i], delta); }); }, start: function(x, y){ if (!this.check(x, y)) return this; var scrollsize = this.element.getscrollsize(), scroll = this.element.getscroll(), values = {x: x, y: y}; for (var z in values){ var max = scrollsize[z]; if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z] : max; else values[z] = scroll[z]; values[z] += this.options.offset[z]; } return this.parent([scroll.x, scroll.y], [values.x, values.y]); }, totop: function(){ return this.start(false, 0); }, toleft: function(){ return this.start(0, false); }, toright: function(){ return this.start('right', false); }, tobottom: function(){ return this.start(false, 'bottom'); }, toelement: function(el){ var position = document.id(el).getposition(this.element); return this.start(position.x, position.y); }, scrollintoview: function(el, axes, offset){ axes = axes ? $splat(axes) : ['x','y']; var to = {}; el = document.id(el); var pos = el.getposition(this.element); var size = el.getsize(); var scroll = this.element.getscroll(); var containersize = this.element.getsize(); var edge = { x: pos.x + size.x, y: pos.y + size.y }; ['x','y'].each(function(axis) { if (axes.contains(axis)) { if (edge[axis] > scroll[axis] + containersize[axis]) to[axis] = edge[axis] - containersize[axis]; if (pos[axis] < scroll[axis]) to[axis] = pos[axis]; } if (to[axis] == null) to[axis] = scroll[axis]; if (offset && offset[axis]) to[axis] = to[axis] + offset[axis]; }, this); if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y); return this; }, scrolltocenter: function(el, axes, offset){ axes = axes ? $splat(axes) : ['x', 'y']; el = $(el); var to = {}, pos = el.getposition(this.element), size = el.getsize(), scroll = this.element.getscroll(), containersize = this.element.getsize(), edge = { x: pos.x + size.x, y: pos.y + size.y }; ['x','y'].each(function(axis){ if(axes.contains(axis)){ to[axis] = pos[axis] - (containersize[axis] - size[axis])/2; } if(to[axis] == null) to[axis] = scroll[axis]; if(offset && offset[axis]) to[axis] = to[axis] + offset[axis]; }, this); if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y); return this; } }); /* --- script: fx.slide.js description: effect to slide an element in and out of view. license: mit-style license authors: - valerio proietti requires: - core:1.2.4/fx element.style - /mootools.more provides: [fx.slide] ... */ fx.slide = new class({ extends: fx, options: { mode: 'vertical', wrapper: false, hideoverflow: true }, initialize: function(element, options){ this.addevent('complete', function(){ this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0); if (this.open) this.wrapper.setstyle('height', ''); if (this.open && browser.engine.webkit419) this.element.dispose().inject(this.wrapper); }, true); this.element = this.subject = document.id(element); this.parent(options); var wrapper = this.element.retrieve('wrapper'); var styles = this.element.getstyles('margin', 'position', 'overflow'); if (this.options.hideoverflow) styles = $extend(styles, {overflow: 'hidden'}); if (this.options.wrapper) wrapper = document.id(this.options.wrapper).setstyles(styles); this.wrapper = wrapper || new element('div', { styles: styles }).wraps(this.element); this.element.store('wrapper', this.wrapper).setstyle('margin', 0); this.now = []; this.open = true; }, vertical: function(){ this.margin = 'margin-top'; this.layout = 'height'; this.offset = this.element.offsetheight; }, horizontal: function(){ this.margin = 'margin-left'; this.layout = 'width'; this.offset = this.element.offsetwidth; }, set: function(now){ this.element.setstyle(this.margin, now[0]); this.wrapper.setstyle(this.layout, now[1]); return this; }, compute: function(from, to, delta){ return [0, 1].map(function(i){ return fx.compute(from[i], to[i], delta); }); }, start: function(how, mode){ if (!this.check(how, mode)) return this; this[mode || this.options.mode](); var margin = this.element.getstyle(this.margin).toint(); var layout = this.wrapper.getstyle(this.layout).toint(); var casein = [[margin, layout], [0, this.offset]]; var caseout = [[margin, layout], [-this.offset, 0]]; var start; switch (how){ case 'in': start = casein; break; case 'out': start = caseout; break; case 'toggle': start = (layout == 0) ? casein : caseout; } return this.parent(start[0], start[1]); }, slidein: function(mode){ return this.start('in', mode); }, slideout: function(mode){ return this.start('out', mode); }, hide: function(mode){ this[mode || this.options.mode](); this.open = false; return this.set([-this.offset, 0]); }, show: function(mode){ this[mode || this.options.mode](); this.open = true; return this.set([0, this.offset]); }, toggle: function(mode){ return this.start('toggle', mode); } }); element.properties.slide = { set: function(options){ var slide = this.retrieve('slide'); if (slide) slide.cancel(); return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options)); }, get: function(options){ if (options || !this.retrieve('slide')){ if (options || !this.retrieve('slide:options')) this.set('slide', options); this.store('slide', new fx.slide(this, this.retrieve('slide:options'))); } return this.retrieve('slide'); } }; element.implement({ slide: function(how, mode){ how = how || 'toggle'; var slide = this.get('slide'), toggle; switch (how){ case 'hide': slide.hide(mode); break; case 'show': slide.show(mode); break; case 'toggle': var flag = this.retrieve('slide:flag', slide.open); slide[flag ? 'slideout' : 'slidein'](mode); this.store('slide:flag', !flag); toggle = true; break; default: slide.start(how, mode); } if (!toggle) this.eliminate('slide:flag'); return this; } }); /* --- script: fx.smoothscroll.js description: class for creating a smooth scrolling effect to all internal links on the page. license: mit-style license authors: - valerio proietti requires: - core:1.2.4/selectors - /fx.scroll provides: [fx.smoothscroll] ... */ var smoothscroll = fx.smoothscroll = new class({ extends: fx.scroll, initialize: function(options, context){ context = context || document; this.doc = context.getdocument(); var win = context.getwindow(); this.parent(this.doc, options); this.links = $$(this.options.links || this.doc.links); var location = win.location.href.match(/^[^#]*/)[0] + '#'; this.links.each(function(link){ if (link.href.indexof(location) != 0) {return;} var anchor = link.href.substr(location.length); if (anchor) this.uselink(link, anchor); }, this); if (!browser.engine.webkit419) { this.addevent('complete', function(){ win.location.hash = this.anchor; }, true); } }, uselink: function(link, anchor){ var el; link.addevent('click', function(event){ if (el !== false && !el) el = document.id(anchor) || this.doc.getelement('a[name=' + anchor + ']'); if (el) { event.preventdefault(); this.anchor = anchor; this.toelement(el).chain(function(){ this.fireevent('scrolledto', [link, el]); }.bind(this)); link.blur(); } }.bind(this)); } }); /* --- script: fx.sort.js description: defines fx.sort, a class that reorders lists with a transition. license: mit-style license authors: - aaron newton requires: - core:1.2.4/element.dimensions - /fx.elements - /element.measure provides: [fx.sort] ... */ fx.sort = new class({ extends: fx.elements, options: { mode: 'vertical' }, initialize: function(elements, options){ this.parent(elements, options); this.elements.each(function(el){ if (el.getstyle('position') == 'static') el.setstyle('position', 'relative'); }); this.setdefaultorder(); }, setdefaultorder: function(){ this.currentorder = this.elements.map(function(el, index){ return index; }); }, sort: function(neworder){ if ($type(neworder) != 'array') return false; var top = 0, left = 0, next = {}, zero = {}, vert = this.options.mode == 'vertical'; var current = this.elements.map(function(el, index){ var size = el.getcomputedsize({styles: ['border', 'padding', 'margin']}); var val; if (vert){ val = { top: top, margin: size['margin-top'], height: size.totalheight }; top += val.height - size['margin-top']; } else { val = { left: left, margin: size['margin-left'], width: size.totalwidth }; left += val.width; } var plain = vert ? 'top' : 'left'; zero[index] = {}; var start = el.getstyle(plain).toint(); zero[index][plain] = start || 0; return val; }, this); this.set(zero); neworder = neworder.map(function(i){ return i.toint(); }); if (neworder.length != this.elements.length){ this.currentorder.each(function(index){ if (!neworder.contains(index)) neworder.push(index); }); if (neworder.length > this.elements.length) neworder.splice(this.elements.length-1, neworder.length - this.elements.length); } var margin = top = left = 0; neworder.each(function(item, index){ var newpos = {}; if (vert){ newpos.top = top - current[item].top - margin; top += current[item].height; } else { newpos.left = left - current[item].left; left += current[item].width; } margin = margin + current[item].margin; next[item]=newpos; }, this); var mapped = {}; $a(neworder).sort().each(function(index){ mapped[index] = next[index]; }); this.start(mapped); this.currentorder = neworder; return this; }, rearrangedom: function(neworder){ neworder = neworder || this.currentorder; var parent = this.elements[0].getparent(); var rearranged = []; this.elements.setstyle('opacity', 0); //move each element and store the new default order neworder.each(function(index){ rearranged.push(this.elements[index].inject(parent).setstyles({ top: 0, left: 0 })); }, this); this.elements.setstyle('opacity', 1); this.elements = $$(rearranged); this.setdefaultorder(); return this; }, getdefaultorder: function(){ return this.elements.map(function(el, index){ return index; }); }, forward: function(){ return this.sort(this.getdefaultorder()); }, backward: function(){ return this.sort(this.getdefaultorder().reverse()); }, reverse: function(){ return this.sort(this.currentorder.reverse()); }, sortbyelements: function(elements){ return this.sort(elements.map(function(el){ return this.elements.indexof(el); }, this)); }, swap: function(one, two){ if ($type(one) == 'element') one = this.elements.indexof(one); if ($type(two) == 'element') two = this.elements.indexof(two); var neworder = $a(this.currentorder); neworder[this.currentorder.indexof(one)] = two; neworder[this.currentorder.indexof(two)] = one; return this.sort(neworder); } }); /* --- script: drag.js description: the base drag class. can be used to drag and resize elements using mouse events. license: mit-style license authors: - valerio proietti - tom occhinno - jan kassens requires: - core:1.2.4/events - core:1.2.4/options - core:1.2.4/element.event - core:1.2.4/element.style - /mootools.more provides: [drag] */ var drag = new class({ implements: [events, options], options: {/* onbeforestart: $empty(thiselement), onstart: $empty(thiselement, event), onsnap: $empty(thiselement) ondrag: $empty(thiselement, event), oncancel: $empty(thiselement), oncomplete: $empty(thiselement, event),*/ snap: 6, unit: 'px', grid: false, style: true, limit: false, handle: false, invert: false, preventdefault: false, stoppropagation: false, modifiers: {x: 'left', y: 'top'} }, initialize: function(){ var params = array.link(arguments, {'options': object.type, 'element': $defined}); this.element = document.id(params.element); this.document = this.element.getdocument(); this.setoptions(params.options || {}); var htype = $type(this.options.handle); this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element; this.mouse = {'now': {}, 'pos': {}}; this.value = {'start': {}, 'now': {}}; this.selection = (browser.engine.trident) ? 'selectstart' : 'mousedown'; this.bound = { start: this.start.bind(this), check: this.check.bind(this), drag: this.drag.bind(this), stop: this.stop.bind(this), cancel: this.cancel.bind(this), eventstop: $lambda(false) }; this.attach(); }, attach: function(){ this.handles.addevent('mousedown', this.bound.start); return this; }, detach: function(){ this.handles.removeevent('mousedown', this.bound.start); return this; }, start: function(event){ if (event.rightclick) return; if (this.options.preventdefault) event.preventdefault(); if (this.options.stoppropagation) event.stoppropagation(); this.mouse.start = event.page; this.fireevent('beforestart', this.element); var limit = this.options.limit; this.limit = {x: [], y: []}; for (var z in this.options.modifiers){ if (!this.options.modifiers[z]) continue; if (this.options.style) this.value.now[z] = this.element.getstyle(this.options.modifiers[z]).toint(); else this.value.now[z] = this.element[this.options.modifiers[z]]; if (this.options.invert) this.value.now[z] *= -1; this.mouse.pos[z] = event.page[z] - this.value.now[z]; if (limit && limit[z]){ for (var i = 2; i--; i){ if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])(); } } } if ($type(this.options.grid) == 'number') this.options.grid = {x: this.options.grid, y: this.options.grid}; this.document.addevents({mousemove: this.bound.check, mouseup: this.bound.cancel}); this.document.addevent(this.selection, this.bound.eventstop); }, check: function(event){ if (this.options.preventdefault) event.preventdefault(); var distance = math.round(math.sqrt(math.pow(event.page.x - this.mouse.start.x, 2) + math.pow(event.page.y - this.mouse.start.y, 2))); if (distance > this.options.snap){ this.cancel(); this.document.addevents({ mousemove: this.bound.drag, mouseup: this.bound.stop }); this.fireevent('start', [this.element, event]).fireevent('snap', this.element); } }, drag: function(event){ if (this.options.preventdefault) event.preventdefault(); this.mouse.now = event.page; for (var z in this.options.modifiers){ if (!this.options.modifiers[z]) continue; this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z]; if (this.options.invert) this.value.now[z] *= -1; if (this.options.limit && this.limit[z]){ if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){ this.value.now[z] = this.limit[z][1]; } else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){ this.value.now[z] = this.limit[z][0]; } } if (this.options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % this.options.grid[z]); if (this.options.style) { this.element.setstyle(this.options.modifiers[z], this.value.now[z] + this.options.unit); } else { this.element[this.options.modifiers[z]] = this.value.now[z]; } } this.fireevent('drag', [this.element, event]); }, cancel: function(event){ this.document.removeevent('mousemove', this.bound.check); this.document.removeevent('mouseup', this.bound.cancel); if (event){ this.document.removeevent(this.selection, this.bound.eventstop); this.fireevent('cancel', this.element); } }, stop: function(event){ this.document.removeevent(this.selection, this.bound.eventstop); this.document.removeevent('mousemove', this.bound.drag); this.document.removeevent('mouseup', this.bound.stop); if (event) this.fireevent('complete', [this.element, event]); } }); element.implement({ makeresizable: function(options){ var drag = new drag(this, $merge({modifiers: {x: 'width', y: 'height'}}, options)); this.store('resizer', drag); return drag.addevent('drag', function(){ this.fireevent('resize', drag); }.bind(this)); } }); /* --- script: drag.move.js description: a drag extension that provides support for the constraining of draggables to containers and droppables. license: mit-style license authors: - valerio proietti - tom occhinno - jan kassens - aaron newton - scott kyle requires: - core:1.2.4/element.dimensions - /drag provides: [drag.move] ... */ drag.move = new class({ extends: drag, options: {/* onenter: $empty(thiselement, overed), onleave: $empty(thiselement, overed), ondrop: $empty(thiselement, overed, event),*/ droppables: [], container: false, precalculate: false, includemargins: true, checkdroppables: true }, initialize: function(element, options){ this.parent(element, options); element = this.element; this.droppables = $$(this.options.droppables); this.container = document.id(this.options.container); if (this.container && $type(this.container) != 'element') this.container = document.id(this.container.getdocument().body); var styles = element.getstyles('left', 'top', 'position'); if (styles.left == 'auto' || styles.top == 'auto') element.setposition(element.getposition(element.getoffsetparent())); if (styles.position == 'static') element.setstyle('position', 'absolute'); this.addevent('start', this.checkdroppables, true); this.overed = null; }, start: function(event){ if (this.container) this.options.limit = this.calculatelimit(); if (this.options.precalculate){ this.positions = this.droppables.map(function(el){ return el.getcoordinates(); }); } this.parent(event); }, calculatelimit: function(){ var offsetparent = this.element.getoffsetparent(), containercoordinates = this.container.getcoordinates(offsetparent), containerborder = {}, elementmargin = {}, elementborder = {}, containermargin = {}, offsetparentpadding = {}; ['top', 'right', 'bottom', 'left'].each(function(pad){ containerborder[pad] = this.container.getstyle('border-' + pad).toint(); elementborder[pad] = this.element.getstyle('border-' + pad).toint(); elementmargin[pad] = this.element.getstyle('margin-' + pad).toint(); containermargin[pad] = this.container.getstyle('margin-' + pad).toint(); offsetparentpadding[pad] = offsetparent.getstyle('padding-' + pad).toint(); }, this); var width = this.element.offsetwidth + elementmargin.left + elementmargin.right, height = this.element.offsetheight + elementmargin.top + elementmargin.bottom, left = 0, top = 0, right = containercoordinates.right - containerborder.right - width, bottom = containercoordinates.bottom - containerborder.bottom - height; if (this.options.includemargins){ left += elementmargin.left; top += elementmargin.top; } else { right += elementmargin.right; bottom += elementmargin.bottom; } if (this.element.getstyle('position') == 'relative'){ var coords = this.element.getcoordinates(offsetparent); coords.left -= this.element.getstyle('left').toint(); coords.top -= this.element.getstyle('top').toint(); left += containerborder.left - coords.left; top += containerborder.top - coords.top; right += elementmargin.left - coords.left; bottom += elementmargin.top - coords.top; if (this.container != offsetparent){ left += containermargin.left + offsetparentpadding.left; top += (browser.engine.trident4 ? 0 : containermargin.top) + offsetparentpadding.top; } } else { left -= elementmargin.left; top -= elementmargin.top; if (this.container == offsetparent){ right -= containerborder.left; bottom -= containerborder.top; } else { left += containercoordinates.left + containerborder.left; top += containercoordinates.top + containerborder.top; } } return { x: [left, right], y: [top, bottom] }; }, checkagainst: function(el, i){ el = (this.positions) ? this.positions[i] : el.getcoordinates(); var now = this.mouse.now; return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top); }, checkdroppables: function(){ var overed = this.droppables.filter(this.checkagainst, this).getlast(); if (this.overed != overed){ if (this.overed) this.fireevent('leave', [this.element, this.overed]); if (overed) this.fireevent('enter', [this.element, overed]); this.overed = overed; } }, drag: function(event){ this.parent(event); if (this.options.checkdroppables && this.droppables.length) this.checkdroppables(); }, stop: function(event){ this.checkdroppables(); this.fireevent('drop', [this.element, this.overed, event]); this.overed = null; return this.parent(event); } }); element.implement({ makedraggable: function(options){ var drag = new drag.move(this, options); this.store('dragger', drag); return drag; } }); /* --- script: slider.js description: class for creating horizontal and vertical slider controls. license: mit-style license authors: - valerio proietti requires: - core:1.2.4/element.dimensions - /class.binds - /drag - /element.dimensions - /element.measure provides: [slider] ... */ var slider = new class({ implements: [events, options], binds: ['clickedelement', 'draggedknob', 'scrolledelement'], options: {/* ontick: $empty(intposition), onchange: $empty(intstep), oncomplete: $empty(strstep),*/ ontick: function(position){ if (this.options.snap) position = this.toposition(this.step); this.knob.setstyle(this.property, position); }, initialstep: 0, snap: false, offset: 0, range: false, wheel: false, steps: 100, mode: 'horizontal' }, initialize: function(element, knob, options){ this.setoptions(options); this.element = document.id(element); this.knob = document.id(knob); this.previouschange = this.previousend = this.step = -1; var offset, limit = {}, modifiers = {'x': false, 'y': false}; switch (this.options.mode){ case 'vertical': this.axis = 'y'; this.property = 'top'; offset = 'offsetheight'; break; case 'horizontal': this.axis = 'x'; this.property = 'left'; offset = 'offsetwidth'; } this.full = this.element.measure(function(){ this.half = this.knob[offset] / 2; return this.element[offset] - this.knob[offset] + (this.options.offset * 2); }.bind(this)); this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0; this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps; this.range = this.max - this.min; this.steps = this.options.steps || this.full; this.stepsize = math.abs(this.range) / this.steps; this.stepwidth = this.stepsize * this.full / math.abs(this.range) ; this.knob.setstyle('position', 'relative').setstyle(this.property, this.options.initialstep ? this.toposition(this.options.initialstep) : - this.options.offset); modifiers[this.axis] = this.property; limit[this.axis] = [- this.options.offset, this.full - this.options.offset]; var dragoptions = { snap: 0, limit: limit, modifiers: modifiers, ondrag: this.draggedknob, onstart: this.draggedknob, onbeforestart: (function(){ this.isdragging = true; }).bind(this), oncancel: function() { this.isdragging = false; }.bind(this), oncomplete: function(){ this.isdragging = false; this.draggedknob(); this.end(); }.bind(this) }; if (this.options.snap){ dragoptions.grid = math.ceil(this.stepwidth); dragoptions.limit[this.axis][1] = this.full; } this.drag = new drag(this.knob, dragoptions); this.attach(); }, attach: function(){ this.element.addevent('mousedown', this.clickedelement); if (this.options.wheel) this.element.addevent('mousewheel', this.scrolledelement); this.drag.attach(); return this; }, detach: function(){ this.element.removeevent('mousedown', this.clickedelement); this.element.removeevent('mousewheel', this.scrolledelement); this.drag.detach(); return this; }, set: function(step){ if (!((this.range > 0) ^ (step < this.min))) step = this.min; if (!((this.range > 0) ^ (step > this.max))) step = this.max; this.step = math.round(step); this.checkstep(); this.fireevent('tick', this.toposition(this.step)); this.end(); return this; }, clickedelement: function(event){ if (this.isdragging || event.target == this.knob) return; var dir = this.range < 0 ? -1 : 1; var position = event.page[this.axis] - this.element.getposition()[this.axis] - this.half; position = position.limit(-this.options.offset, this.full -this.options.offset); this.step = math.round(this.min + dir * this.tostep(position)); this.checkstep(); this.fireevent('tick', position); this.end(); }, scrolledelement: function(event){ var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0); this.set(mode ? this.step - this.stepsize : this.step + this.stepsize); event.stop(); }, draggedknob: function(){ var dir = this.range < 0 ? -1 : 1; var position = this.drag.value.now[this.axis]; position = position.limit(-this.options.offset, this.full -this.options.offset); this.step = math.round(this.min + dir * this.tostep(position)); this.checkstep(); }, checkstep: function(){ if (this.previouschange != this.step){ this.previouschange = this.step; this.fireevent('change', this.step); } }, end: function(){ if (this.previousend !== this.step){ this.previousend = this.step; this.fireevent('complete', this.step + ''); } }, tostep: function(position){ var step = (position + this.options.offset) * this.stepsize / this.full * this.steps; return this.options.steps ? math.round(step -= step % this.stepsize) : step; }, toposition: function(step){ return (this.full * math.abs(this.min - step)) / (this.steps * this.stepsize) - this.options.offset; } }); /* --- script: sortables.js description: class for creating a drag and drop sorting interface for lists of items. license: mit-style license authors: - tom occhino requires: - /drag.move provides: [slider] ... */ var sortables = new class({ implements: [events, options], options: {/* onsort: $empty(element, clone), onstart: $empty(element, clone), oncomplete: $empty(element),*/ snap: 4, opacity: 1, clone: false, revert: false, handle: false, constrain: false }, initialize: function(lists, options){ this.setoptions(options); this.elements = []; this.lists = []; this.idle = true; this.addlists($$(document.id(lists) || lists)); if (!this.options.clone) this.options.revert = false; if (this.options.revert) this.effect = new fx.morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert)); }, attach: function(){ this.addlists(this.lists); return this; }, detach: function(){ this.lists = this.removelists(this.lists); return this; }, additems: function(){ array.flatten(arguments).each(function(element){ this.elements.push(element); var start = element.retrieve('sortables:start', this.start.bindwithevent(this, element)); (this.options.handle ? element.getelement(this.options.handle) || element : element).addevent('mousedown', start); }, this); return this; }, addlists: function(){ array.flatten(arguments).each(function(list){ this.lists.push(list); this.additems(list.getchildren()); }, this); return this; }, removeitems: function(){ return $$(array.flatten(arguments).map(function(element){ this.elements.erase(element); var start = element.retrieve('sortables:start'); (this.options.handle ? element.getelement(this.options.handle) || element : element).removeevent('mousedown', start); return element; }, this)); }, removelists: function(){ return $$(array.flatten(arguments).map(function(list){ this.lists.erase(list); this.removeitems(list.getchildren()); return list; }, this)); }, getclone: function(event, element){ if (!this.options.clone) return new element('div').inject(document.body); if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list); var clone = element.clone(true).setstyles({ margin: '0px', position: 'absolute', visibility: 'hidden', 'width': element.getstyle('width') }); //prevent the duplicated radio inputs from unchecking the real one if (clone.get('html').test('radio')) { clone.getelements('input[type=radio]').each(function(input, i) { input.set('name', 'clone_' + i); }); } return clone.inject(this.list).setposition(element.getposition(element.getoffsetparent())); }, getdroppables: function(){ var droppables = this.list.getchildren(); if (!this.options.constrain) droppables = this.lists.concat(droppables).erase(this.list); return droppables.erase(this.clone).erase(this.element); }, insert: function(dragging, element){ var where = 'inside'; if (this.lists.contains(element)){ this.list = element; this.drag.droppables = this.getdroppables(); } else { where = this.element.getallprevious().contains(element) ? 'before' : 'after'; } this.element.inject(element, where); this.fireevent('sort', [this.element, this.clone]); }, start: function(event, element){ if (!this.idle) return; this.idle = false; this.element = element; this.opacity = element.get('opacity'); this.list = element.getparent(); this.clone = this.getclone(event, element); this.drag = new drag.move(this.clone, { snap: this.options.snap, container: this.options.constrain && this.element.getparent(), droppables: this.getdroppables(), onsnap: function(){ event.stop(); this.clone.setstyle('visibility', 'visible'); this.element.set('opacity', this.options.opacity || 0); this.fireevent('start', [this.element, this.clone]); }.bind(this), onenter: this.insert.bind(this), oncancel: this.reset.bind(this), oncomplete: this.end.bind(this) }); this.clone.inject(this.element, 'before'); this.drag.start(event); }, end: function(){ this.drag.detach(); this.element.set('opacity', this.opacity); if (this.effect){ var dim = this.element.getstyles('width', 'height'); var pos = this.clone.computeposition(this.element.getposition(this.clone.offsetparent)); this.effect.element = this.clone; this.effect.start({ top: pos.top, left: pos.left, width: dim.width, height: dim.height, opacity: 0.25 }).chain(this.reset.bind(this)); } else { this.reset(); } }, reset: function(){ this.idle = true; this.clone.destroy(); this.fireevent('complete', this.element); }, serialize: function(){ var params = array.link(arguments, {modifier: function.type, index: $defined}); var serial = this.lists.map(function(list){ return list.getchildren().map(params.modifier || function(element){ return element.get('id'); }, this); }, this); var index = params.index; if (this.lists.length == 1) index = 0; return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial; } }); /* --- script: request.jsonp.js description: defines request.jsonp, a class for cross domain javascript via script injection. license: mit-style license authors: - aaron newton - guillermo rauch requires: - core:1.2.4/element - core:1.2.4/request - /log provides: [request.jsonp] ... */ request.jsonp = new class({ implements: [chain, events, options, log], options: {/* onretry: $empty(intretries), onrequest: $empty(scriptelement), oncomplete: $empty(data), onsuccess: $empty(data), oncancel: $empty(), log: false, */ url: '', data: {}, retries: 0, timeout: 0, link: 'ignore', callbackkey: 'callback', injectscript: document.head }, initialize: function(options){ this.setoptions(options); if (this.options.log) this.enablelog(); this.running = false; this.requests = 0; this.triesremaining = []; }, check: function(){ if (!this.running) return true; switch (this.options.link){ case 'cancel': this.cancel(); return true; case 'chain': this.chain(this.caller.bind(this, arguments)); return false; } return false; }, send: function(options){ if (!$chk(arguments[1]) && !this.check(options)) return this; var type = $type(options), old = this.options, index = $chk(arguments[1]) ? arguments[1] : this.requests++; if (type == 'string' || type == 'element') options = {data: options}; options = $extend({data: old.data, url: old.url}, options); if (!$chk(this.triesremaining[index])) this.triesremaining[index] = this.options.retries; var remaining = this.triesremaining[index]; (function(){ var script = this.getscript(options); this.log('jsonp retrieving script with url: ' + script.get('src')); this.fireevent('request', script); this.running = true; (function(){ if (remaining){ this.triesremaining[index] = remaining - 1; if (script){ script.destroy(); this.send(options, index).fireevent('retry', this.triesremaining[index]); } } else if(script && this.options.timeout){ script.destroy(); this.cancel().fireevent('failure'); } }).delay(this.options.timeout, this); }).delay(browser.engine.trident ? 50 : 0, this); return this; }, cancel: function(){ if (!this.running) return this; this.running = false; this.fireevent('cancel'); return this; }, getscript: function(options){ var index = request.jsonp.counter, data; request.jsonp.counter++; switch ($type(options.data)){ case 'element': data = document.id(options.data).toquerystring(); break; case 'object': case 'hash': data = hash.toquerystring(options.data); } var src = options.url + (options.url.test('\\?') ? '&' :'?') + (options.callbackkey || this.options.callbackkey) + '=request.jsonp.request_map.request_'+ index + (data ? '&' + data : ''); if (src.length > 2083) this.log('jsonp '+ src +' will fail in internet explorer, which enforces a 2083 bytes length limit on uris'); var script = new element('script', {type: 'text/javascript', src: src}); request.jsonp.request_map['request_' + index] = function(){ this.success(arguments, script); }.bind(this); return script.inject(this.options.injectscript); }, success: function(args, script){ if (script) script.destroy(); this.running = false; this.log('jsonp successfully retrieved: ', args); this.fireevent('complete', args).fireevent('success', args).callchain(); } }); request.jsonp.counter = 0; request.jsonp.request_map = {}; /* --- script: request.queue.js description: controls several instances of request and its variants to run only one request at a time. license: mit-style license authors: - aaron newton requires: - core:1.2.4/element - core:1.2.4/request - /log provides: [request.queue] ... */ request.queue = new class({ implements: [options, events], binds: ['attach', 'request', 'complete', 'cancel', 'success', 'failure', 'exception'], options: {/* onrequest: $empty(argspassedtoonrequest), onsuccess: $empty(argspassedtoonsuccess), oncomplete: $empty(argspassedtooncomplete), oncancel: $empty(argspassedtooncancel), onexception: $empty(argspassedtoonexception), onfailure: $empty(argspassedtoonfailure), onend: $empty, */ stoponfailure: true, autoadvance: true, concurrent: 1, requests: {} }, initialize: function(options){ if(options){ var requests = options.requests; delete options.requests; } this.setoptions(options); this.requests = new hash; this.queue = []; this.reqbinders = {}; if(requests) this.addrequests(requests); }, addrequest: function(name, request){ this.requests.set(name, request); this.attach(name, request); return this; }, addrequests: function(obj){ $each(obj, function(req, name){ this.addrequest(name, req); }, this); return this; }, getname: function(req){ return this.requests.keyof(req); }, attach: function(name, req){ if (req._groupsend) return this; ['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){ if(!this.reqbinders[name]) this.reqbinders[name] = {}; this.reqbinders[name][evt] = function(){ this['on' + evt.capitalize()].apply(this, [name, req].extend(arguments)); }.bind(this); req.addevent(evt, this.reqbinders[name][evt]); }, this); req._groupsend = req.send; req.send = function(options){ this.send(name, options); return req; }.bind(this); return this; }, removerequest: function(req){ var name = $type(req) == 'object' ? this.getname(req) : req; if (!name && $type(name) != 'string') return this; req = this.requests.get(name); if (!req) return this; ['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){ req.removeevent(evt, this.reqbinders[name][evt]); }, this); req.send = req._groupsend; delete req._groupsend; return this; }, getrunning: function(){ return this.requests.filter(function(r){ return r.running; }); }, isrunning: function(){ return !!(this.getrunning().getkeys().length); }, send: function(name, options){ var q = function(){ this.requests.get(name)._groupsend(options); this.queue.erase(q); }.bind(this); q.name = name; if (this.getrunning().getkeys().length >= this.options.concurrent || (this.error && this.options.stoponfailure)) this.queue.push(q); else q(); return this; }, hasnext: function(name){ return (!name) ? !!this.queue.length : !!this.queue.filter(function(q){ return q.name == name; }).length; }, resume: function(){ this.error = false; (this.options.concurrent - this.getrunning().getkeys().length).times(this.runnext, this); return this; }, runnext: function(name){ if (!this.queue.length) return this; if (!name){ this.queue[0](); } else { var found; this.queue.each(function(q){ if (!found && q.name == name){ found = true; q(); } }); } return this; }, runall: function() { this.queue.each(function(q) { q(); }); return this; }, clear: function(name){ if (!name){ this.queue.empty(); } else { this.queue = this.queue.map(function(q){ if (q.name != name) return q; else return false; }).filter(function(q){ return q; }); } return this; }, cancel: function(name){ this.requests.get(name).cancel(); return this; }, onrequest: function(){ this.fireevent('request', arguments); }, oncomplete: function(){ this.fireevent('complete', arguments); if (!this.queue.length) this.fireevent('end'); }, oncancel: function(){ if (this.options.autoadvance && !this.error) this.runnext(); this.fireevent('cancel', arguments); }, onsuccess: function(){ if (this.options.autoadvance && !this.error) this.runnext(); this.fireevent('success', arguments); }, onfailure: function(){ this.error = true; if (!this.options.stoponfailure && this.options.autoadvance) this.runnext(); this.fireevent('failure', arguments); }, onexception: function(){ this.error = true; if (!this.options.stoponfailure && this.options.autoadvance) this.runnext(); this.fireevent('exception', arguments); } }); /* --- script: request.periodical.js description: requests the same url to pull data from a server but increases the intervals if no data is returned to reduce the load license: mit-style license authors: - christoph pojer requires: - core:1.2.4/request - /mootools.more provides: [request.periodical] ... */ request.implement({ options: { initialdelay: 5000, delay: 5000, limit: 60000 }, starttimer: function(data){ var fn = function(){ if (!this.running) this.send({data: data}); }; this.timer = fn.delay(this.options.initialdelay, this); this.lastdelay = this.options.initialdelay; this.completecheck = function(response){ $clear(this.timer); this.lastdelay = (response) ? this.options.delay : (this.lastdelay + this.options.delay).min(this.options.limit); this.timer = fn.delay(this.lastdelay, this); }; return this.addevent('complete', this.completecheck); }, stoptimer: function(){ $clear(this.timer); return this.removeevent('complete', this.completecheck); } }); /* --- script: assets.js description: provides methods to dynamically load javascript, css, and image files into the document. license: mit-style license authors: - valerio proietti requires: - core:1.2.4/element.event - /mootools.more provides: [assets] ... */ var asset = { javascript: function(source, properties){ properties = $extend({ onload: $empty, document: document, check: $lambda(true) }, properties); if (properties.onload) properties.onload = properties.onload; var script = new element('script', {src: source, type: 'text/javascript'}); var load = properties.onload.bind(script), check = properties.check, doc = properties.document; delete properties.onload; delete properties.check; delete properties.document; script.addevents({ load: load, readystatechange: function(){ if (['loaded', 'complete'].contains(this.readystate)) load(); } }).set(properties); if (browser.engine.webkit419) var checker = (function(){ if (!$try(check)) return; $clear(checker); load(); }).periodical(50); return script.inject(doc.head); }, css: function(source, properties){ return new element('link', $merge({ rel: 'stylesheet', media: 'screen', type: 'text/css', href: source }, properties)).inject(document.head); }, image: function(source, properties){ properties = $merge({ onload: $empty, onabort: $empty, onerror: $empty }, properties); var image = new image(); var element = document.id(image) || new element('img'); ['load', 'abort', 'error'].each(function(name){ var type = 'on' + name; var cap = name.capitalize(); if (properties['on' + cap]) properties[type] = properties['on' + cap]; var event = properties[type]; delete properties[type]; image[type] = function(){ if (!image) return; if (!element.parentnode){ element.width = image.width; element.height = image.height; } image = image.onload = image.onabort = image.onerror = null; event.delay(1, element, element); element.fireevent(name, element, 1); }; }); image.src = element.src = source; if (image && image.complete) image.onload.delay(1); return element.set(properties); }, images: function(sources, options){ options = $merge({ oncomplete: $empty, onprogress: $empty, onerror: $empty, properties: {} }, options); sources = $splat(sources); var images = []; var counter = 0; return new elements(sources.map(function(source){ return asset.image(source, $extend(options.properties, { onload: function(){ options.onprogress.call(this, counter, sources.indexof(source)); counter++; if (counter == sources.length) options.oncomplete(); }, onerror: function(){ options.onerror.call(this, counter, sources.indexof(source)); counter++; if (counter == sources.length) options.oncomplete(); } })); })); } }; /* --- script: color.js description: class for creating and manipulating colors in javascript. supports hsb -> rgb conversions and vice versa. license: mit-style license authors: - valerio proietti requires: - core:1.2.4/array - core:1.2.4/string - core:1.2.4/number - core:1.2.4/hash - core:1.2.4/function - core:1.2.4/$util provides: [color] ... */ var color = new native({ initialize: function(color, type){ if (arguments.length >= 3){ type = 'rgb'; color = array.slice(arguments, 0, 3); } else if (typeof color == 'string'){ if (color.match(/rgb/)) color = color.rgbtohex().hextorgb(true); else if (color.match(/hsb/)) color = color.hsbtorgb(); else color = color.hextorgb(true); } type = type || 'rgb'; switch (type){ case 'hsb': var old = color; color = color.hsbtorgb(); color.hsb = old; break; case 'hex': color = color.hextorgb(true); break; } color.rgb = color.slice(0, 3); color.hsb = color.hsb || color.rgbtohsb(); color.hex = color.rgbtohex(); return $extend(color, this); } }); color.implement({ mix: function(){ var colors = array.slice(arguments); var alpha = ($type(colors.getlast()) == 'number') ? colors.pop() : 50; var rgb = this.slice(); colors.each(function(color){ color = new color(color); for (var i = 0; i < 3; i++) rgb[i] = math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha)); }); return new color(rgb, 'rgb'); }, invert: function(){ return new color(this.map(function(value){ return 255 - value; })); }, sethue: function(value){ return new color([value, this.hsb[1], this.hsb[2]], 'hsb'); }, setsaturation: function(percent){ return new color([this.hsb[0], percent, this.hsb[2]], 'hsb'); }, setbrightness: function(percent){ return new color([this.hsb[0], this.hsb[1], percent], 'hsb'); } }); var $rgb = function(r, g, b){ return new color([r, g, b], 'rgb'); }; var $hsb = function(h, s, b){ return new color([h, s, b], 'hsb'); }; var $hex = function(hex){ return new color(hex, 'hex'); }; array.implement({ rgbtohsb: function(){ var red = this[0], green = this[1], blue = this[2], hue = 0; var max = math.max(red, green, blue), min = math.min(red, green, blue); var delta = max - min; var brightness = max / 255, saturation = (max != 0) ? delta / max : 0; if(saturation != 0) { var rr = (max - red) / delta; var gr = (max - green) / delta; var br = (max - blue) / delta; if (red == max) hue = br - gr; else if (green == max) hue = 2 + rr - br; else hue = 4 + gr - rr; hue /= 6; if (hue < 0) hue++; } return [math.round(hue * 360), math.round(saturation * 100), math.round(brightness * 100)]; }, hsbtorgb: function(){ var br = math.round(this[2] / 100 * 255); if (this[1] == 0){ return [br, br, br]; } else { var hue = this[0] % 360; var f = hue % 60; var p = math.round((this[2] * (100 - this[1])) / 10000 * 255); var q = math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255); var t = math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255); switch (math.floor(hue / 60)){ case 0: return [br, t, p]; case 1: return [q, br, p]; case 2: return [p, br, t]; case 3: return [p, q, br]; case 4: return [t, p, br]; case 5: return [br, p, q]; } } return false; } }); string.implement({ rgbtohsb: function(){ var rgb = this.match(/\d{1,3}/g); return (rgb) ? rgb.rgbtohsb() : null; }, hsbtorgb: function(){ var hsb = this.match(/\d{1,3}/g); return (hsb) ? hsb.hsbtorgb() : null; } }); /* --- script: group.js description: class for monitoring collections of events license: mit-style license authors: - valerio proietti requires: - core:1.2.4/events - /mootools.more provides: [group] ... */ var group = new class({ initialize: function(){ this.instances = array.flatten(arguments); this.events = {}; this.checker = {}; }, addevent: function(type, fn){ this.checker[type] = this.checker[type] || {}; this.events[type] = this.events[type] || []; if (this.events[type].contains(fn)) return false; else this.events[type].push(fn); this.instances.each(function(instance, i){ instance.addevent(type, this.check.bind(this, [type, instance, i])); }, this); return this; }, check: function(type, instance, i){ this.checker[type][i] = true; var every = this.instances.every(function(current, j){ return this.checker[type][j] || false; }, this); if (!every) return; this.checker[type] = {}; this.events[type].each(function(event){ event.call(this, this.instances, instance); }, this); } }); /* --- script: hash.cookie.js description: class for creating, reading, and deleting cookies in json format. license: mit-style license authors: - valerio proietti - aaron newton requires: - core:1.2.4/cookie - core:1.2.4/json - /mootools.more provides: [hash.cookie] ... */ hash.cookie = new class({ extends: cookie, options: { autosave: true }, initialize: function(name, options){ this.parent(name, options); this.load(); }, save: function(){ var value = json.encode(this.hash); if (!value || value.length > 4096) return false; //cookie would be truncated! if (value == '{}') this.dispose(); else this.write(value); return true; }, load: function(){ this.hash = new hash(json.decode(this.read(), true)); return this; } }); hash.each(hash.prototype, function(method, name){ if (typeof method == 'function') hash.cookie.implement(name, function(){ var value = method.apply(this.hash, arguments); if (this.options.autosave) this.save(); return value; }); }); /* --- script: iframeshim.js description: defines iframeshim, a class for obscuring select lists and flash objects in ie. license: mit-style license authors: - aaron newton requires: - core:1.2.4/element.event - core:1.2.4/element.style - core:1.2.4/options events - /element.position - /class.occlude provides: [iframeshim] ... */ var iframeshim = new class({ implements: [options, events, class.occlude], options: { classname: 'iframeshim', src: 'javascript:false;document.write("");', display: false, zindex: null, margin: 0, offset: {x: 0, y: 0}, browsers: (browser.engine.trident4 || (browser.engine.gecko && !browser.engine.gecko19 && browser.platform.mac)) }, property: 'iframeshim', initialize: function(element, options){ this.element = document.id(element); if (this.occlude()) return this.occluded; this.setoptions(options); this.makeshim(); return this; }, makeshim: function(){ if(this.options.browsers){ var zindex = this.element.getstyle('zindex').toint(); if (!zindex){ zindex = 1; var pos = this.element.getstyle('position'); if (pos == 'static' || !pos) this.element.setstyle('position', 'relative'); this.element.setstyle('zindex', zindex); } zindex = ($chk(this.options.zindex) && zindex > this.options.zindex) ? this.options.zindex : zindex - 1; if (zindex < 0) zindex = 1; this.shim = new element('iframe', { src: this.options.src, scrolling: 'no', frameborder: 0, styles: { zindex: zindex, position: 'absolute', border: 'none', filter: 'progid:dximagetransform.microsoft.alpha(style=0,opacity=0)' }, 'class': this.options.classname }).store('iframeshim', this); var inject = (function(){ this.shim.inject(this.element, 'after'); this[this.options.display ? 'show' : 'hide'](); this.fireevent('inject'); }).bind(this); if (!iframeshim.ready) window.addevent('load', inject); else inject(); } else { this.position = this.hide = this.show = this.dispose = $lambda(this); } }, position: function(){ if (!iframeshim.ready || !this.shim) return this; var size = this.element.measure(function(){ return this.getsize(); }); if (this.options.margin != undefined){ size.x = size.x - (this.options.margin * 2); size.y = size.y - (this.options.margin * 2); this.options.offset.x += this.options.margin; this.options.offset.y += this.options.margin; } this.shim.set({width: size.x, height: size.y}).position({ relativeto: this.element, offset: this.options.offset }); return this; }, hide: function(){ if (this.shim) this.shim.setstyle('display', 'none'); return this; }, show: function(){ if (this.shim) this.shim.setstyle('display', 'block'); return this.position(); }, dispose: function(){ if (this.shim) this.shim.dispose(); return this; }, destroy: function(){ if (this.shim) this.shim.destroy(); return this; } }); window.addevent('load', function(){ iframeshim.ready = true; }); /* --- script: htmltable.js description: builds table elements with methods to add rows. license: mit-style license authors: - aaron newton requires: - core:1.2.4/options - core:1.2.4/events - /class.occlude provides: [htmltable] ... */ var htmltable = new class({ implements: [options, events, class.occlude], options: { properties: { cellpadding: 0, cellspacing: 0, border: 0 }, rows: [], headers: [], footers: [] }, property: 'htmltable', initialize: function(){ var params = array.link(arguments, {options: object.type, table: element.type}); this.setoptions(params.options); this.element = params.table || new element('table', this.options.properties); if (this.occlude()) return this.occluded; this.build(); }, build: function(){ this.element.store('htmltable', this); this.body = document.id(this.element.tbodies[0]) || new element('tbody').inject(this.element); $$(this.body.rows); if (this.options.headers.length) this.setheaders(this.options.headers); else this.thead = document.id(this.element.thead); if (this.thead) this.head = document.id(this.thead.rows[0]); if (this.options.footers.length) this.setfooters(this.options.footers); this.tfoot = document.id(this.element.tfoot); if (this.tfoot) this.foot = document.id(this.thead.rows[0]); this.options.rows.each(function(row){ this.push(row); }, this); ['adopt', 'inject', 'wraps', 'grab', 'replaces', 'dispose'].each(function(method){ this[method] = this.element[method].bind(this.element); }, this); }, toelement: function(){ return this.element; }, empty: function(){ this.body.empty(); return this; }, set: function(what, items) { var target = (what == 'headers') ? 'thead' : 'tfoot'; this[target.tolowercase()] = (document.id(this.element[target]) || new element(target.tolowercase()).inject(this.element, 'top')).empty(); var data = this.push(items, {}, this[target.tolowercase()], what == 'headers' ? 'th' : 'td'); if (what == 'headers') this.head = document.id(this.thead.rows[0]); else this.foot = document.id(this.thead.rows[0]); return data; }, setheaders: function(headers){ this.set('headers', headers); return this; }, setfooters: function(footers){ this.set('footers', footers); return this; }, push: function(row, rowproperties, target, tag){ var tds = row.map(function(data){ var td = new element(tag || 'td', data.properties), type = data.content || data || '', element = document.id(type); if($type(type) != 'string' && element) td.adopt(element); else td.set('html', type); return td; }); return { tr: new element('tr', rowproperties).inject(target || this.body).adopt(tds), tds: tds }; } }); /* --- script: htmltable.zebra.js description: builds a stripy table with methods to add rows. license: mit-style license authors: - harald kirschner - aaron newton requires: - /htmltable - /class.refactor provides: [htmltable.zebra] ... */ htmltable = class.refactor(htmltable, { options: { classzebra: 'table-tr-odd', zebra: true }, initialize: function(){ this.previous.apply(this, arguments); if (this.occluded) return this.occluded; if (this.options.zebra) this.updatezebras(); }, updatezebras: function(){ array.each(this.body.rows, this.zebra, this); }, zebra: function(row, i){ return row[((i % 2) ? 'remove' : 'add')+'class'](this.options.classzebra); }, push: function(){ var pushed = this.previous.apply(this, arguments); if (this.options.zebra) this.updatezebras(); return pushed; } }); /* --- script: htmltable.select.js description: builds a stripy, sortable table with methods to add rows. rows can be selected with the mouse or keyboard navigation. license: mit-style license authors: - harald kirschner - aaron newton requires: - /keyboard - /htmltable - /class.refactor - /element.delegation provides: [htmltable.select] ... */ htmltable = class.refactor(htmltable, { options: { /*onrowfocus: $empty, onrowunfocus: $empty,*/ usekeyboard: true, classrowselected: 'table-tr-selected', classrowhovered: 'table-tr-hovered', classselectable: 'table-selectable', allowmultiselect: true, selectable: false }, initialize: function(){ this.previous.apply(this, arguments); if (this.occluded) return this.occluded; this.selectedrows = new elements(); this.bound = { mouseleave: this.mouseleave.bind(this), focusrow: this.focusrow.bind(this) }; if (this.options.selectable) this.enableselect(); }, enableselect: function(){ this.selectenabled = true; this.attachselects(); this.element.addclass(this.options.classselectable); }, disableselect: function(){ this.selectenabled = false; this.attach(false); this.element.removeclass(this.options.classselectable); }, attachselects: function(attach){ attach = $pick(attach, true); var method = attach ? 'addevents' : 'removeevents'; this.element[method]({ mouseleave: this.bound.mouseleave }); this.body[method]({ 'click:relay(tr)': this.bound.focusrow }); if (this.options.usekeyboard || this.keyboard){ if (!this.keyboard) this.keyboard = new keyboard({ events: { down: function(e) { e.preventdefault(); this.shiftfocus(1); }.bind(this), up: function(e) { e.preventdefault(); this.shiftfocus(-1); }.bind(this), enter: function(e) { e.preventdefault(); if (this.hover) this.focusrow(this.hover); }.bind(this) }, active: true }); this.keyboard[attach ? 'activate' : 'deactivate'](); } this.updateselects(); }, mouseleave: function(){ if (this.hover) this.leaverow(this.hover); }, focus: function(){ if (this.keyboard) this.keyboard.activate(); }, blur: function(){ if (this.keyboard) this.keyboard.deactivate(); }, push: function(){ var ret = this.previous.apply(this, arguments); this.updateselects(); return ret; }, updateselects: function(){ array.each(this.body.rows, function(row){ var binders = row.retrieve('binders'); if ((binders && this.selectenabled) || (!binders && !this.selectenabled)) return; if (!binders){ binders = { mouseenter: this.enterrow.bind(this, [row]), mouseleave: this.leaverow.bind(this, [row]) }; row.store('binders', binders).addevents(binders); } else { row.removeevents(binders); } }, this); }, enterrow: function(row){ if (this.hover) this.hover = this.leaverow(this.hover); this.hover = row.addclass(this.options.classrowhovered); }, shiftfocus: function(offset){ if (!this.hover) return this.enterrow(this.body.rows[0]); var to = array.indexof(this.body.rows, this.hover) + offset; if (to < 0) to = 0; if (to >= this.body.rows.length) to = this.body.rows.length - 1; if (this.hover == this.body.rows[to]) return this; this.enterrow(this.body.rows[to]); }, leaverow: function(row){ row.removeclass(this.options.classrowhovered); }, focusrow: function(){ var row = arguments[1] || arguments[0]; //delegation passes the event first if (!this.body.getchildren().contains(row)) return; var unfocus = function(row){ this.selectedrows.erase(row); row.removeclass(this.options.classrowselected); this.fireevent('rowunfocus', [row, this.selectedrows]); }.bind(this); if (!this.options.allowmultiselect) this.selectedrows.each(unfocus); if (!this.selectedrows.contains(row)) { this.selectedrows.push(row); row.addclass(this.options.classrowselected); this.fireevent('rowfocus', [row, this.selectedrows]); } else { unfocus(row); } return false; }, selectall: function(status){ status = $pick(status, true); if (!this.options.allowmultiselect && status) return; if (!status) this.selectedrows.removeclass(this.options.classrowselected).empty(); else this.selectedrows.combine(this.body.rows).addclass(this.options.classrowselected); return this; }, selectnone: function(){ return this.selectall(false); } }); /* --- script: keyboard.js description: keyboardevents used to intercept events on a class for keyboard and format modifiers in a specific order so as to make alt+shift+c the same as shift+alt+c. license: mit-style license authors: - perrin westrich - aaron newton - scott kyle requires: - core:1.2.4/events - core:1.2.4/options - core:1.2.4/element.event - /log provides: [keyboard] ... */ (function(){ var keyboard = this.keyboard = new class({ extends: events, implements: [options, log], options: { /* onactivate: $empty, ondeactivate: $empty, */ defaulteventtype: 'keydown', active: false, events: {}, nonparsedevents: ['activate', 'deactivate', 'onactivate', 'ondeactivate', 'changed', 'onchanged'] }, initialize: function(options){ this.setoptions(options); this.setup(); }, setup: function(){ this.addevents(this.options.events); //if this is the root manager, nothing manages it if (keyboard.manager && !this.manager) keyboard.manager.manage(this); if (this.options.active) this.activate(); }, handle: function(event, type){ //keyboard.stop(event) prevents key propagation if (event.preventkeyboardpropagation) return; var bubbles = !!this.manager; if (bubbles && this.activekb){ this.activekb.handle(event, type); if (event.preventkeyboardpropagation) return; } this.fireevent(type, event); if (!bubbles && this.activekb) this.activekb.handle(event, type); }, addevent: function(type, fn, internal){ return this.parent(keyboard.parse(type, this.options.defaulteventtype, this.options.nonparsedevents), fn, internal); }, removeevent: function(type, fn){ return this.parent(keyboard.parse(type, this.options.defaulteventtype, this.options.nonparsedevents), fn); }, toggleactive: function(){ return this[this.active ? 'deactivate' : 'activate'](); }, activate: function(instance){ if (instance) { //if we're stealing focus, store the last keyboard to have it so the relenquish command works if (instance != this.activekb) this.previous = this.activekb; //if we're enabling a child, assign it so that events are now passed to it this.activekb = instance.fireevent('activate'); keyboard.manager.fireevent('changed'); } else if (this.manager) { //else we're enabling ourselves, we must ask our parent to do it for us this.manager.activate(this); } return this; }, deactivate: function(instance){ if (instance) { if(instance === this.activekb) { this.activekb = null; instance.fireevent('deactivate'); keyboard.manager.fireevent('changed'); } } else if (this.manager) { this.manager.deactivate(this); } return this; }, relenquish: function(){ if (this.previous) this.activate(this.previous); }, //management logic manage: function(instance){ if (instance.manager) instance.manager.drop(instance); this.instances.push(instance); instance.manager = this; if (!this.activekb) this.activate(instance); else this._disable(instance); }, _disable: function(instance){ if (this.activekb == instance) this.activekb = null; }, drop: function(instance){ this._disable(instance); this.instances.erase(instance); }, instances: [], trace: function(){ keyboard.trace(this); }, each: function(fn){ keyboard.each(this, fn); } }); var parsed = {}; var modifiers = ['shift', 'control', 'alt', 'meta']; var regex = /^(?:shift|control|ctrl|alt|meta)$/; keyboard.parse = function(type, eventtype, ignore){ if (ignore && ignore.contains(type.tolowercase())) return type; type = type.tolowercase().replace(/^(keyup|keydown):/, function($0, $1){ eventtype = $1; return ''; }); if (!parsed[type]){ var key, mods = {}; type.split('+').each(function(part){ if (regex.test(part)) mods[part] = true; else key = part; }); mods.control = mods.control || mods.ctrl; // allow both control and ctrl var keys = []; modifiers.each(function(mod){ if (mods[mod]) keys.push(mod); }); if (key) keys.push(key); parsed[type] = keys.join('+'); } return eventtype + ':' + parsed[type]; }; keyboard.each = function(keyboard, fn){ var current = keyboard || keyboard.manager; while (current){ fn.run(current); current = current.activekb; } }; keyboard.stop = function(event){ event.preventkeyboardpropagation = true; }; keyboard.manager = new keyboard({ active: true }); keyboard.trace = function(keyboard){ keyboard = keyboard || keyboard.manager; keyboard.enablelog(); keyboard.log('the following items have focus: '); keyboard.each(keyboard, function(current){ keyboard.log(document.id(current.widget) || current.wiget || current); }); }; var handler = function(event){ var keys = []; modifiers.each(function(mod){ if (event[mod]) keys.push(mod); }); if (!regex.test(event.key)) keys.push(event.key); keyboard.manager.handle(event, event.type + ':' + keys.join('+')); }; document.addevents({ 'keyup': handler, 'keydown': handler }); event.keys.extend({ 'shift': 16, 'control': 17, 'alt': 18, 'capslock': 20, 'pageup': 33, 'pagedown': 34, 'end': 35, 'home': 36, 'numlock': 144, 'scrolllock': 145, ';': 186, '=': 187, ',': 188, '-': browser.engine.gecko ? 109 : 189, '.': 190, '/': 191, '`': 192, '[': 219, '\\': 220, ']': 221, "'": 222 }); })(); /* --- script: keyboard.js description: enhances keyboard by adding the ability to name and describe keyboard shortcuts, and the ability to grab shortcuts by name and bind the shortcut to different keys. license: mit-style license authors: - perrin westrich requires: - core:1.2.4/function - /keyboard.extras provides: [keyboard.extras] ... */ keyboard.prototype.options.nonparsedevents.combine(['rebound', 'onrebound']); keyboard.implement({ /* shortcut should be in the format of: { 'keys': 'shift+s', // the default to add as an event. 'description': 'blah blah blah', // a brief description of the functionality. 'handler': function(){} // the event handler to run when keys are pressed. } */ addshortcut: function(name, shortcut) { this.shortcuts = this.shortcuts || []; this.shortcutindex = this.shortcutindex || {}; shortcut.getkeyboard = $lambda(this); shortcut.name = name; this.shortcutindex[name] = shortcut; this.shortcuts.push(shortcut); if(shortcut.keys) this.addevent(shortcut.keys, shortcut.handler); return this; }, addshortcuts: function(obj){ for(var name in obj) this.addshortcut(name, obj[name]); return this; }, getshortcuts: function(){ return this.shortcuts || []; }, getshortcut: function(name){ return (this.shortcutindex || {})[name]; } }); keyboard.rebind = function(newkeys, shortcuts){ $splat(shortcuts).each(function(shortcut){ shortcut.getkeyboard().removeevent(shortcut.keys, shortcut.handler); shortcut.getkeyboard().addevent(newkeys, shortcut.handler); shortcut.keys = newkeys; shortcut.getkeyboard().fireevent('rebound'); }); }; keyboard.getactiveshortcuts = function(keyboard) { var activekbs = [], activescs = []; keyboard.each(keyboard, [].push.bind(activekbs)); activekbs.each(function(kb){ activescs.extend(kb.getshortcuts()); }); return activescs; }; keyboard.getshortcut = function(name, keyboard, opts){ opts = opts || {}; var shortcuts = opts.many ? [] : null, set = opts.many ? function(kb){ var shortcut = kb.getshortcut(name); if(shortcut) shortcuts.push(shortcut); } : function(kb) { if(!shortcuts) shortcuts = kb.getshortcut(name); }; keyboard.each(keyboard, set); return shortcuts; }; keyboard.getshortcuts = function(name, keyboard) { return keyboard.getshortcut(name, keyboard, { many: true }); }; /* --- script: mask.js description: creates a mask element to cover another. license: mit-style license authors: - aaron newton requires: - core:1.2.4/options - core:1.2.4/events - core:1.2.4/element.event - /class.binds - /element.position - /iframeshim provides: [mask] ... */ var mask = new class({ implements: [options, events], binds: ['position'], options: { // onshow: $empty, // onhide: $empty, // ondestroy: $empty, // onclick: $empty, //inject: { // where: 'after', // target: null, //}, // hideonclick: false, // id: null, // destroyonhide: false, style: {}, 'class': 'mask', maskmargins: false, useiframeshim: true, iframeshimoptions: {} }, initialize: function(target, options){ this.target = document.id(target) || document.id(document.body); this.target.store('mask', this); this.setoptions(options); this.render(); this.inject(); }, render: function() { this.element = new element('div', { 'class': this.options['class'], id: this.options.id || 'mask-' + $time(), styles: $merge(this.options.style, { display: 'none' }), events: { click: function(){ this.fireevent('click'); if (this.options.hideonclick) this.hide(); }.bind(this) } }); this.hidden = true; }, toelement: function(){ return this.element; }, inject: function(target, where){ where = where || this.options.inject ? this.options.inject.where : '' || this.target == document.body ? 'inside' : 'after'; target = target || this.options.inject ? this.options.inject.target : '' || this.target; this.element.inject(target, where); if (this.options.useiframeshim) { this.shim = new iframeshim(this.element, this.options.iframeshimoptions); this.addevents({ show: this.shim.show.bind(this.shim), hide: this.shim.hide.bind(this.shim), destroy: this.shim.destroy.bind(this.shim) }); } }, position: function(){ this.resize(this.options.width, this.options.height); this.element.position({ relativeto: this.target, position: 'topleft', ignoremargins: !this.options.maskmargins, ignorescroll: this.target == document.body }); return this; }, resize: function(x, y){ var opt = { styles: ['padding', 'border'] }; if (this.options.maskmargins) opt.styles.push('margin'); var dim = this.target.getcomputedsize(opt); if (this.target == document.body) { var win = window.getsize(); if (dim.totalheight < win.y) dim.totalheight = win.y; if (dim.totalwidth < win.x) dim.totalwidth = win.x; } this.element.setstyles({ width: $pick(x, dim.totalwidth, dim.x), height: $pick(y, dim.totalheight, dim.y) }); return this; }, show: function(){ if (!this.hidden) return this; window.addevent('resize', this.position); this.position(); this.showmask.apply(this, arguments); return this; }, showmask: function(){ this.element.setstyle('display', 'block'); this.hidden = false; this.fireevent('show'); }, hide: function(){ if (this.hidden) return this; window.removeevent('resize', this.position); this.hidemask.apply(this, arguments); if (this.options.destroyonhide) return this.destroy(); return this; }, hidemask: function(){ this.element.setstyle('display', 'none'); this.hidden = true; this.fireevent('hide'); }, toggle: function(){ this[this.hidden ? 'show' : 'hide'](); }, destroy: function(){ this.hide(); this.element.destroy(); this.fireevent('destroy'); this.target.eliminate('mask'); } }); element.properties.mask = { set: function(options){ var mask = this.retrieve('mask'); return this.eliminate('mask').store('mask:options', options); }, get: function(options){ if (options || !this.retrieve('mask')){ if (this.retrieve('mask')) this.retrieve('mask').destroy(); if (options || !this.retrieve('mask:options')) this.set('mask', options); this.store('mask', new mask(this, this.retrieve('mask:options'))); } return this.retrieve('mask'); } }; element.implement({ mask: function(options){ this.get('mask', options).show(); return this; }, unmask: function(){ this.get('mask').hide(); return this; } }); /* --- script: scroller.js description: class which scrolls the contents of any element (including the window) when the mouse reaches the element's boundaries. license: mit-style license authors: - valerio proietti requires: - core:1.2.4/events - core:1.2.4/options - core:1.2.4/element.event - core:1.2.4/element.dimensions provides: [scroller] ... */ var scroller = new class({ implements: [events, options], options: { area: 20, velocity: 1, onchange: function(x, y){ this.element.scrollto(x, y); }, fps: 50 }, initialize: function(element, options){ this.setoptions(options); this.element = document.id(element); this.docbody = document.id(this.element.getdocument().body); this.listener = ($type(this.element) != 'element') ? this.docbody : this.element; this.timer = null; this.bound = { attach: this.attach.bind(this), detach: this.detach.bind(this), getcoords: this.getcoords.bind(this) }; }, start: function(){ this.listener.addevents({ mouseover: this.bound.attach, mouseout: this.bound.detach }); }, stop: function(){ this.listener.removeevents({ mouseover: this.bound.attach, mouseout: this.bound.detach }); this.detach(); this.timer = $clear(this.timer); }, attach: function(){ this.listener.addevent('mousemove', this.bound.getcoords); }, detach: function(){ this.listener.removeevent('mousemove', this.bound.getcoords); this.timer = $clear(this.timer); }, getcoords: function(event){ this.page = (this.listener.get('tag') == 'body') ? event.client : event.page; if (!this.timer) this.timer = this.scroll.periodical(math.round(1000 / this.options.fps), this); }, scroll: function(){ var size = this.element.getsize(), scroll = this.element.getscroll(), pos = this.element != this.docbody ? this.element.getoffsets() : {x: 0, y:0}, scrollsize = this.element.getscrollsize(), change = {x: 0, y: 0}; for (var z in this.page){ if (this.page[z] < (this.options.area + pos[z]) && scroll[z] != 0) { change[z] = (this.page[z] - this.options.area - pos[z]) * this.options.velocity; } else if (this.page[z] + this.options.area > (size[z] + pos[z]) && scroll[z] + size[z] != scrollsize[z]) { change[z] = (this.page[z] - size[z] + this.options.area - pos[z]) * this.options.velocity; } } if (change.y || change.x) this.fireevent('change', [scroll.x + change.x, scroll.y + change.y]); } }); /* --- script: tips.js description: class for creating nice tips that follow the mouse cursor when hovering an element. license: mit-style license authors: - valerio proietti - christoph pojer requires: - core:1.2.4/options - core:1.2.4/events - core:1.2.4/element.event - core:1.2.4/element.style - core:1.2.4/element.dimensions - /mootools.more provides: [tips] ... */ (function(){ var read = function(option, element){ return (option) ? ($type(option) == 'function' ? option(element) : element.get(option)) : ''; }; this.tips = new class({ implements: [events, options], options: { /* onattach: $empty(element), ondetach: $empty(element), */ onshow: function(){ this.tip.setstyle('display', 'block'); }, onhide: function(){ this.tip.setstyle('display', 'none'); }, title: 'title', text: function(element){ return element.get('rel') || element.get('href'); }, showdelay: 100, hidedelay: 100, classname: 'tip-wrap', offset: {x: 16, y: 16}, windowpadding: {x:0, y:0}, fixed: false }, initialize: function(){ var params = array.link(arguments, {options: object.type, elements: $defined}); this.setoptions(params.options); if (params.elements) this.attach(params.elements); this.container = new element('div', {'class': 'tip'}); }, toelement: function(){ if (this.tip) return this.tip; return this.tip = new element('div', { 'class': this.options.classname, styles: { position: 'absolute', top: 0, left: 0 } }).adopt( new element('div', {'class': 'tip-top'}), this.container, new element('div', {'class': 'tip-bottom'}) ).inject(document.body); }, attach: function(elements){ $$(elements).each(function(element){ var title = read(this.options.title, element), text = read(this.options.text, element); element.erase('title').store('tip:native', title).retrieve('tip:title', title); element.retrieve('tip:text', text); this.fireevent('attach', [element]); var events = ['enter', 'leave']; if (!this.options.fixed) events.push('move'); events.each(function(value){ var event = element.retrieve('tip:' + value); if (!event) event = this['element' + value.capitalize()].bindwithevent(this, element); element.store('tip:' + value, event).addevent('mouse' + value, event); }, this); }, this); return this; }, detach: function(elements){ $$(elements).each(function(element){ ['enter', 'leave', 'move'].each(function(value){ element.removeevent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value); }); this.fireevent('detach', [element]); if (this.options.title == 'title'){ // this is necessary to check if we can revert the title var original = element.retrieve('tip:native'); if (original) element.set('title', original); } }, this); return this; }, elemententer: function(event, element){ this.container.empty(); ['title', 'text'].each(function(value){ var content = element.retrieve('tip:' + value); if (content) this.fill(new element('div', {'class': 'tip-' + value}).inject(this.container), content); }, this); $clear(this.timer); this.timer = (function(){ this.show(this, element); this.position((this.options.fixed) ? {page: element.getposition()} : event); }).delay(this.options.showdelay, this); }, elementleave: function(event, element){ $clear(this.timer); this.timer = this.hide.delay(this.options.hidedelay, this, element); this.fireforparent(event, element); }, fireforparent: function(event, element){ element = element.getparent(); if (!element || element == document.body) return; if (element.retrieve('tip:enter')) element.fireevent('mouseenter', event); else this.fireforparent(event, element); }, elementmove: function(event, element){ this.position(event); }, position: function(event){ if (!this.tip) document.id(this); var size = window.getsize(), scroll = window.getscroll(), tip = {x: this.tip.offsetwidth, y: this.tip.offsetheight}, props = {x: 'left', y: 'top'}, obj = {}; for (var z in props){ obj[props[z]] = event.page[z] + this.options.offset[z]; if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - this.options.windowpadding[z]) obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z]; } this.tip.setstyles(obj); }, fill: function(element, contents){ if(typeof contents == 'string') element.set('html', contents); else element.adopt(contents); }, show: function(element){ if (!this.tip) document.id(this); this.fireevent('show', [this.tip, element]); }, hide: function(element){ if (!this.tip) document.id(this); this.fireevent('hide', [this.tip, element]); } }); })(); /* --- script: spinner.js description: adds a semi-transparent overlay over a dom element with a spinnin ajax icon. license: mit-style license authors: - aaron newton requires: - core:1.2.4/fx.tween - /class.refactor - /mask provides: [spinner] ... */ var spinner = new class({ extends: mask, options: { /*message: false,*/ 'class':'spinner', containerposition: {}, content: { 'class':'spinner-content' }, messagecontainer: { 'class':'spinner-msg' }, img: { 'class':'spinner-img' }, fxoptions: { link: 'chain' } }, initialize: function(){ this.parent.apply(this, arguments); this.target.store('spinner', this); //add this to events for when nofx is true; parent methods handle hide/show var deactivate = function(){ this.active = false; }.bind(this); this.addevents({ hide: deactivate, show: deactivate }); }, render: function(){ this.parent(); this.element.set('id', this.options.id || 'spinner-'+$time()); this.content = document.id(this.options.content) || new element('div', this.options.content); this.content.inject(this.element); if (this.options.message) { this.msg = document.id(this.options.message) || new element('p', this.options.messagecontainer).appendtext(this.options.message); this.msg.inject(this.content); } if (this.options.img) { this.img = document.id(this.options.img) || new element('div', this.options.img); this.img.inject(this.content); } this.element.set('tween', this.options.fxoptions); }, show: function(nofx){ if (this.active) return this.chain(this.show.bind(this)); if (!this.hidden) { this.callchain.delay(20, this); return this; } this.active = true; return this.parent(nofx); }, showmask: function(nofx){ var pos = function(){ this.content.position($merge({ relativeto: this.element }, this.options.containerposition)); }.bind(this); if (nofx) { this.parent(); pos(); } else { this.element.setstyles({ display: 'block', opacity: 0 }).tween('opacity', this.options.style.opacity || 0.9); pos(); this.hidden = false; this.fireevent('show'); this.callchain(); } }, hide: function(nofx){ if (this.active) return this.chain(this.hide.bind(this)); if (this.hidden) { this.callchain.delay(20, this); return this; } this.active = true; return this.parent(nofx); }, hidemask: function(nofx){ if (nofx) return this.parent(); this.element.tween('opacity', 0).get('tween').chain(function(){ this.element.setstyle('display', 'none'); this.hidden = true; this.fireevent('hide'); this.callchain(); }.bind(this)); }, destroy: function(){ this.content.destroy(); this.parent(); this.target.eliminate('spinner'); } }); spinner.implement(new chain); if (window.request) { request = class.refactor(request, { options: { usespinner: false, spinneroptions: {}, spinnertarget: false }, initialize: function(options){ this._send = this.send; this.send = function(options){ if (this.spinner) this.spinner.chain(this._send.bind(this, options)).show(); else this._send(options); return this; }; this.previous(options); var update = document.id(this.options.spinnertarget) || document.id(this.options.update); if (this.options.usespinner && update) { this.spinner = update.get('spinner', this.options.spinneroptions); ['oncomplete', 'onexception', 'oncancel'].each(function(event){ this.addevent(event, this.spinner.hide.bind(this.spinner)); }, this); } }, getspinner: function(){ return this.spinner; } }); } element.properties.spinner = { set: function(options){ var spinner = this.retrieve('spinner'); return this.eliminate('spinner').store('spinner:options', options); }, get: function(options){ if (options || !this.retrieve('spinner')){ if (this.retrieve('spinner')) this.retrieve('spinner').destroy(); if (options || !this.retrieve('spinner:options')) this.set('spinner', options); new spinner(this, this.retrieve('spinner:options')); } return this.retrieve('spinner'); } }; element.implement({ spin: function(options){ this.get('spinner', options).show(); return this; }, unspin: function(){ var opt = array.link(arguments, {options: object.type, callback: function.type}); this.get('spinner', opt.options).hide(opt.callback); return this; } }); /* --- script: date.english.us.js description: date messages for us english. license: mit-style license authors: - aaron newton requires: - /lang - /date provides: [date.english.us] ... */ mootools.lang.set('en-us', 'date', { months: ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'], days: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'], //culture's date order: mm/dd/yyyy dateorder: ['month', 'date', 'year'], shortdate: '%m/%d/%y', shorttime: '%i:%m%p', am: 'am', pm: 'pm', /* date.extras */ ordinal: function(dayofmonth){ //1st, 2nd, 3rd, etc. return (dayofmonth > 3 && dayofmonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][math.min(dayofmonth % 10, 4)]; }, lessthanminuteago: 'less than a minute ago', minuteago: 'about a minute ago', minutesago: '{delta} minutes ago', hourago: 'about an hour ago', hoursago: 'about {delta} hours ago', dayago: '1 day ago', daysago: '{delta} days ago', weekago: '1 week ago', weeksago: '{delta} weeks ago', monthago: '1 month ago', monthsago: '{delta} months ago', yearago: '1 year ago', yearsago: '{delta} years ago', lessthanminuteuntil: 'less than a minute from now', minuteuntil: 'about a minute from now', minutesuntil: '{delta} minutes from now', houruntil: 'about an hour from now', hoursuntil: 'about {delta} hours from now', dayuntil: '1 day from now', daysuntil: '{delta} days from now', weekuntil: '1 week from now', weeksuntil: '{delta} weeks from now', monthuntil: '1 month from now', monthsuntil: '{delta} months from now', yearuntil: '1 year from now', yearsuntil: '{delta} years from now' }); /* --- script: form.validator.english.js description: form validator messages for english. license: mit-style license authors: - aaron newton requires: - /lang - /form.validator provides: [form.validator.english] ... */ mootools.lang.set('en-us', 'form.validator', { required:'this field is required.', minlength:'please enter at least {minlength} characters (you entered {length} characters).', maxlength:'please enter no more than {maxlength} characters (you entered {length} characters).', integer:'please enter an integer in this field. numbers with decimals (e.g. 1.25) are not permitted.', numeric:'please enter only numeric values in this field (i.e. "1" or "1.1" or "-1" or "-1.1").', digits:'please use numbers and punctuation only in this field (for example, a phone number with dashes or dots is permitted).', alpha:'please use letters only (a-z) with in this field. no spaces or other characters are allowed.', alphanum:'please use only letters (a-z) or numbers (0-9) only in this field. no spaces or other characters are allowed.', datesuchas:'please enter a valid date such as {date}', dateinformatmdy:'please enter a valid date such as mm/dd/yyyy (i.e. "12/31/1999")', email:'please enter a valid email address. for example "fred@domain.com".', url:'please enter a valid url such as http://www.google.com.', currencydollar:'please enter a valid $ amount. for example $100.00 .', onerequired:'please enter something for at least one of these inputs.', errorprefix: 'error: ', warningprefix: 'warning: ', //form.validator.extras nospace: 'there can be no spaces in this input.', reqchkbynode: 'no items are selected.', requiredchk: 'this field is required.', reqchkbyname: 'please select a {label}.', match: 'this field needs to match the {matchname} field', startdate: 'the start date', enddate: 'the end date', currenddate: 'the current date', afterdate: 'the date should be the same or after {label}.', beforedate: 'the date should be the same or before {label}.', startmonth: 'please select a start month', samemonth: 'these two dates must be in the same month - you must change one or the other.', creditcard: 'the credit card number entered is invalid. please check the number and try again. {length} digits entered.' });