Move to vendor folder

This commit is contained in:
Ruud
2014-12-05 11:29:25 +01:00
parent 5609536f46
commit d6cfcae45b
13 changed files with 0 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
/*
---
script: Array.stableSort.js
description: Add a stable sort algorithm for all browsers
license: MIT-style license.
authors:
- Yorick Sijsling
requires:
core/1.3: '*'
provides:
- [Array.stableSort, Array.mergeSort]
...
*/
(function() {
var defaultSortFunction = function(a, b) {
return a > b ? 1 : (a < b ? -1 : 0);
}
Array.implement({
stableSort: function(compare) {
// I would love some real feature recognition. Problem is that an unstable algorithm sometimes/often gives the same result as an unstable algorithm.
return (Browser.chrome || Browser.firefox2 || Browser.opera9) ? this.mergeSort(compare) : this.sort(compare);
},
mergeSort: function(compare, token) {
compare = compare || defaultSortFunction;
if (this.length > 1) {
// Split and sort both parts
var right = this.splice(Math.floor(this.length / 2)).mergeSort(compare);
var left = this.splice(0).mergeSort(compare); // 'this' is now empty.
// Merge parts together
while (left.length > 0 || right.length > 0) {
this.push(
right.length === 0 ? left.shift()
: left.length === 0 ? right.shift()
: compare(left[0], right[0]) > 0 ? right.shift()
: left.shift());
}
}
return this;
}
});
})();
+955
View File
@@ -0,0 +1,955 @@
/*global setImmediate: false, setTimeout: false, console: false */
(function () {
var async = {};
// global on the server, window in the browser
var root, previous_async;
root = this;
if (root != null) {
previous_async = root.async;
}
async.noConflict = function () {
root.async = previous_async;
return async;
};
function only_once(fn) {
var called = false;
return function() {
if (called) throw new Error("Callback was already called.");
called = true;
fn.apply(root, arguments);
}
}
//// cross-browser compatiblity functions ////
var _each = function (arr, iterator) {
if (arr.forEach) {
return arr.forEach(iterator);
}
for (var i = 0; i < arr.length; i += 1) {
iterator(arr[i], i, arr);
}
};
var _map = function (arr, iterator) {
if (arr.map) {
return arr.map(iterator);
}
var results = [];
_each(arr, function (x, i, a) {
results.push(iterator(x, i, a));
});
return results;
};
var _reduce = function (arr, iterator, memo) {
if (arr.reduce) {
return arr.reduce(iterator, memo);
}
_each(arr, function (x, i, a) {
memo = iterator(memo, x, i, a);
});
return memo;
};
var _keys = function (obj) {
if (Object.keys) {
return Object.keys(obj);
}
var keys = [];
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
keys.push(k);
}
}
return keys;
};
//// exported async module functions ////
//// nextTick implementation with browser-compatible fallback ////
if (typeof process === 'undefined' || !(process.nextTick)) {
if (typeof setImmediate === 'function') {
async.nextTick = function (fn) {
// not a direct alias for IE10 compatibility
setImmediate(fn);
};
async.setImmediate = async.nextTick;
}
else {
async.nextTick = function (fn) {
setTimeout(fn, 0);
};
async.setImmediate = async.nextTick;
}
}
else {
async.nextTick = process.nextTick;
if (typeof setImmediate !== 'undefined') {
async.setImmediate = setImmediate;
}
else {
async.setImmediate = async.nextTick;
}
}
async.each = function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length) {
return callback();
}
var completed = 0;
_each(arr, function (x) {
iterator(x, only_once(function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
if (completed >= arr.length) {
callback(null);
}
}
}));
});
};
async.forEach = async.each;
async.eachSeries = function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length) {
return callback();
}
var completed = 0;
var iterate = function () {
iterator(arr[completed], function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
if (completed >= arr.length) {
callback(null);
}
else {
iterate();
}
}
});
};
iterate();
};
async.forEachSeries = async.eachSeries;
async.eachLimit = function (arr, limit, iterator, callback) {
var fn = _eachLimit(limit);
fn.apply(null, [arr, iterator, callback]);
};
async.forEachLimit = async.eachLimit;
var _eachLimit = function (limit) {
return function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length || limit <= 0) {
return callback();
}
var completed = 0;
var started = 0;
var running = 0;
(function replenish () {
if (completed >= arr.length) {
return callback();
}
while (running < limit && started < arr.length) {
started += 1;
running += 1;
iterator(arr[started - 1], function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
running -= 1;
if (completed >= arr.length) {
callback();
}
else {
replenish();
}
}
});
}
})();
};
};
var doParallel = function (fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
return fn.apply(null, [async.each].concat(args));
};
};
var doParallelLimit = function(limit, fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
return fn.apply(null, [_eachLimit(limit)].concat(args));
};
};
var doSeries = function (fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
return fn.apply(null, [async.eachSeries].concat(args));
};
};
var _asyncMap = function (eachfn, arr, iterator, callback) {
var results = [];
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
eachfn(arr, function (x, callback) {
iterator(x.value, function (err, v) {
results[x.index] = v;
callback(err);
});
}, function (err) {
callback(err, results);
});
};
async.map = doParallel(_asyncMap);
async.mapSeries = doSeries(_asyncMap);
async.mapLimit = function (arr, limit, iterator, callback) {
return _mapLimit(limit)(arr, iterator, callback);
};
var _mapLimit = function(limit) {
return doParallelLimit(limit, _asyncMap);
};
// reduce only has a series version, as doing reduce in parallel won't
// work in many situations.
async.reduce = function (arr, memo, iterator, callback) {
async.eachSeries(arr, function (x, callback) {
iterator(memo, x, function (err, v) {
memo = v;
callback(err);
});
}, function (err) {
callback(err, memo);
});
};
// inject alias
async.inject = async.reduce;
// foldl alias
async.foldl = async.reduce;
async.reduceRight = function (arr, memo, iterator, callback) {
var reversed = _map(arr, function (x) {
return x;
}).reverse();
async.reduce(reversed, memo, iterator, callback);
};
// foldr alias
async.foldr = async.reduceRight;
var _filter = function (eachfn, arr, iterator, callback) {
var results = [];
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
eachfn(arr, function (x, callback) {
iterator(x.value, function (v) {
if (v) {
results.push(x);
}
callback();
});
}, function (err) {
callback(_map(results.sort(function (a, b) {
return a.index - b.index;
}), function (x) {
return x.value;
}));
});
};
async.filter = doParallel(_filter);
async.filterSeries = doSeries(_filter);
// select alias
async.select = async.filter;
async.selectSeries = async.filterSeries;
var _reject = function (eachfn, arr, iterator, callback) {
var results = [];
arr = _map(arr, function (x, i) {
return {index: i, value: x};
});
eachfn(arr, function (x, callback) {
iterator(x.value, function (v) {
if (!v) {
results.push(x);
}
callback();
});
}, function (err) {
callback(_map(results.sort(function (a, b) {
return a.index - b.index;
}), function (x) {
return x.value;
}));
});
};
async.reject = doParallel(_reject);
async.rejectSeries = doSeries(_reject);
var _detect = function (eachfn, arr, iterator, main_callback) {
eachfn(arr, function (x, callback) {
iterator(x, function (result) {
if (result) {
main_callback(x);
main_callback = function () {};
}
else {
callback();
}
});
}, function (err) {
main_callback();
});
};
async.detect = doParallel(_detect);
async.detectSeries = doSeries(_detect);
async.some = function (arr, iterator, main_callback) {
async.each(arr, function (x, callback) {
iterator(x, function (v) {
if (v) {
main_callback(true);
main_callback = function () {};
}
callback();
});
}, function (err) {
main_callback(false);
});
};
// any alias
async.any = async.some;
async.every = function (arr, iterator, main_callback) {
async.each(arr, function (x, callback) {
iterator(x, function (v) {
if (!v) {
main_callback(false);
main_callback = function () {};
}
callback();
});
}, function (err) {
main_callback(true);
});
};
// all alias
async.all = async.every;
async.sortBy = function (arr, iterator, callback) {
async.map(arr, function (x, callback) {
iterator(x, function (err, criteria) {
if (err) {
callback(err);
}
else {
callback(null, {value: x, criteria: criteria});
}
});
}, function (err, results) {
if (err) {
return callback(err);
}
else {
var fn = function (left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
};
callback(null, _map(results.sort(fn), function (x) {
return x.value;
}));
}
});
};
async.auto = function (tasks, callback) {
callback = callback || function () {};
var keys = _keys(tasks);
if (!keys.length) {
return callback(null);
}
var results = {};
var listeners = [];
var addListener = function (fn) {
listeners.unshift(fn);
};
var removeListener = function (fn) {
for (var i = 0; i < listeners.length; i += 1) {
if (listeners[i] === fn) {
listeners.splice(i, 1);
return;
}
}
};
var taskComplete = function () {
_each(listeners.slice(0), function (fn) {
fn();
});
};
addListener(function () {
if (_keys(results).length === keys.length) {
callback(null, results);
callback = function () {};
}
});
_each(keys, function (k) {
var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
var taskCallback = function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
if (err) {
var safeResults = {};
_each(_keys(results), function(rkey) {
safeResults[rkey] = results[rkey];
});
safeResults[k] = args;
callback(err, safeResults);
// stop subsequent errors hitting callback multiple times
callback = function () {};
}
else {
results[k] = args;
async.setImmediate(taskComplete);
}
};
var requires = task.slice(0, Math.abs(task.length - 1)) || [];
var ready = function () {
return _reduce(requires, function (a, x) {
return (a && results.hasOwnProperty(x));
}, true) && !results.hasOwnProperty(k);
};
if (ready()) {
task[task.length - 1](taskCallback, results);
}
else {
var listener = function () {
if (ready()) {
removeListener(listener);
task[task.length - 1](taskCallback, results);
}
};
addListener(listener);
}
});
};
async.waterfall = function (tasks, callback) {
callback = callback || function () {};
if (tasks.constructor !== Array) {
var err = new Error('First argument to waterfall must be an array of functions');
return callback(err);
}
if (!tasks.length) {
return callback();
}
var wrapIterator = function (iterator) {
return function (err) {
if (err) {
callback.apply(null, arguments);
callback = function () {};
}
else {
var args = Array.prototype.slice.call(arguments, 1);
var next = iterator.next();
if (next) {
args.push(wrapIterator(next));
}
else {
args.push(callback);
}
async.setImmediate(function () {
iterator.apply(null, args);
});
}
};
};
wrapIterator(async.iterator(tasks))();
};
var _parallel = function(eachfn, tasks, callback) {
callback = callback || function () {};
if (tasks.constructor === Array) {
eachfn.map(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
callback.call(null, err, args);
});
}
}, callback);
}
else {
var results = {};
eachfn.each(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
results[k] = args;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
};
async.parallel = function (tasks, callback) {
_parallel({ map: async.map, each: async.each }, tasks, callback);
};
async.parallelLimit = function(tasks, limit, callback) {
_parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
};
async.series = function (tasks, callback) {
callback = callback || function () {};
if (tasks.constructor === Array) {
async.mapSeries(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
callback.call(null, err, args);
});
}
}, callback);
}
else {
var results = {};
async.eachSeries(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
results[k] = args;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
};
async.iterator = function (tasks) {
var makeCallback = function (index) {
var fn = function () {
if (tasks.length) {
tasks[index].apply(null, arguments);
}
return fn.next();
};
fn.next = function () {
return (index < tasks.length - 1) ? makeCallback(index + 1): null;
};
return fn;
};
return makeCallback(0);
};
async.apply = function (fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function () {
return fn.apply(
null, args.concat(Array.prototype.slice.call(arguments))
);
};
};
var _concat = function (eachfn, arr, fn, callback) {
var r = [];
eachfn(arr, function (x, cb) {
fn(x, function (err, y) {
r = r.concat(y || []);
cb(err);
});
}, function (err) {
callback(err, r);
});
};
async.concat = doParallel(_concat);
async.concatSeries = doSeries(_concat);
async.whilst = function (test, iterator, callback) {
if (test()) {
iterator(function (err) {
if (err) {
return callback(err);
}
async.whilst(test, iterator, callback);
});
}
else {
callback();
}
};
async.doWhilst = function (iterator, test, callback) {
iterator(function (err) {
if (err) {
return callback(err);
}
if (test()) {
async.doWhilst(iterator, test, callback);
}
else {
callback();
}
});
};
async.until = function (test, iterator, callback) {
if (!test()) {
iterator(function (err) {
if (err) {
return callback(err);
}
async.until(test, iterator, callback);
});
}
else {
callback();
}
};
async.doUntil = function (iterator, test, callback) {
iterator(function (err) {
if (err) {
return callback(err);
}
if (!test()) {
async.doUntil(iterator, test, callback);
}
else {
callback();
}
});
};
async.queue = function (worker, concurrency) {
if (concurrency === undefined) {
concurrency = 1;
}
function _insert(q, data, pos, callback) {
if(data.constructor !== Array) {
data = [data];
}
_each(data, function(task) {
var item = {
data: task,
callback: typeof callback === 'function' ? callback : null
};
if (pos) {
q.tasks.unshift(item);
} else {
q.tasks.push(item);
}
if (q.saturated && q.tasks.length === concurrency) {
q.saturated();
}
async.setImmediate(q.process);
});
}
var workers = 0;
var q = {
tasks: [],
concurrency: concurrency,
saturated: null,
empty: null,
drain: null,
push: function (data, callback) {
_insert(q, data, false, callback);
},
unshift: function (data, callback) {
_insert(q, data, true, callback);
},
process: function () {
if (workers < q.concurrency && q.tasks.length) {
var task = q.tasks.shift();
if (q.empty && q.tasks.length === 0) {
q.empty();
}
workers += 1;
var next = function () {
workers -= 1;
if (task.callback) {
task.callback.apply(task, arguments);
}
if (q.drain && q.tasks.length + workers === 0) {
q.drain();
}
q.process();
};
var cb = only_once(next);
worker(task.data, cb);
}
},
length: function () {
return q.tasks.length;
},
running: function () {
return workers;
}
};
return q;
};
async.cargo = function (worker, payload) {
var working = false,
tasks = [];
var cargo = {
tasks: tasks,
payload: payload,
saturated: null,
empty: null,
drain: null,
push: function (data, callback) {
if(data.constructor !== Array) {
data = [data];
}
_each(data, function(task) {
tasks.push({
data: task,
callback: typeof callback === 'function' ? callback : null
});
if (cargo.saturated && tasks.length === payload) {
cargo.saturated();
}
});
async.setImmediate(cargo.process);
},
process: function process() {
if (working) return;
if (tasks.length === 0) {
if(cargo.drain) cargo.drain();
return;
}
var ts = typeof payload === 'number'
? tasks.splice(0, payload)
: tasks.splice(0);
var ds = _map(ts, function (task) {
return task.data;
});
if(cargo.empty) cargo.empty();
working = true;
worker(ds, function () {
working = false;
var args = arguments;
_each(ts, function (data) {
if (data.callback) {
data.callback.apply(null, args);
}
});
process();
});
},
length: function () {
return tasks.length;
},
running: function () {
return working;
}
};
return cargo;
};
var _console_fn = function (name) {
return function (fn) {
var args = Array.prototype.slice.call(arguments, 1);
fn.apply(null, args.concat([function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (typeof console !== 'undefined') {
if (err) {
if (console.error) {
console.error(err);
}
}
else if (console[name]) {
_each(args, function (x) {
console[name](x);
});
}
}
}]));
};
};
async.log = _console_fn('log');
async.dir = _console_fn('dir');
/*async.info = _console_fn('info');
async.warn = _console_fn('warn');
async.error = _console_fn('error');*/
async.memoize = function (fn, hasher) {
var memo = {};
var queues = {};
hasher = hasher || function (x) {
return x;
};
var memoized = function () {
var args = Array.prototype.slice.call(arguments);
var callback = args.pop();
var key = hasher.apply(null, args);
if (key in memo) {
callback.apply(null, memo[key]);
}
else if (key in queues) {
queues[key].push(callback);
}
else {
queues[key] = [callback];
fn.apply(null, args.concat([function () {
memo[key] = arguments;
var q = queues[key];
delete queues[key];
for (var i = 0, l = q.length; i < l; i++) {
q[i].apply(null, arguments);
}
}]));
}
};
memoized.memo = memo;
memoized.unmemoized = fn;
return memoized;
};
async.unmemoize = function (fn) {
return function () {
return (fn.unmemoized || fn).apply(null, arguments);
};
};
async.times = function (count, iterator, callback) {
var counter = [];
for (var i = 0; i < count; i++) {
counter.push(i);
}
return async.map(counter, iterator, callback);
};
async.timesSeries = function (count, iterator, callback) {
var counter = [];
for (var i = 0; i < count; i++) {
counter.push(i);
}
return async.mapSeries(counter, iterator, callback);
};
async.compose = function (/* functions... */) {
var fns = Array.prototype.reverse.call(arguments);
return function () {
var that = this;
var args = Array.prototype.slice.call(arguments);
var callback = args.pop();
async.reduce(fns, args, function (newargs, fn, cb) {
fn.apply(that, newargs.concat([function () {
var err = arguments[0];
var nextargs = Array.prototype.slice.call(arguments, 1);
cb(err, nextargs);
}]))
},
function (err, results) {
callback.apply(that, [err].concat(results));
});
};
};
var _applyEach = function (eachfn, fns /*args...*/) {
var go = function () {
var that = this;
var args = Array.prototype.slice.call(arguments);
var callback = args.pop();
return eachfn(fns, function (fn, cb) {
fn.apply(that, args.concat([cb]));
},
callback);
};
if (arguments.length > 2) {
var args = Array.prototype.slice.call(arguments, 2);
return go.apply(this, args);
}
else {
return go;
}
};
async.applyEach = doParallel(_applyEach);
async.applyEachSeries = doSeries(_applyEach);
async.forever = function (fn, callback) {
function next(err) {
if (err) {
if (callback) {
return callback(err);
}
throw err;
}
fn(next);
}
next();
};
// AMD / RequireJS
if (typeof define !== 'undefined' && define.amd) {
define([], function () {
return async;
});
}
// Node.js
else if (typeof module !== 'undefined' && module.exports) {
module.exports = async;
}
// included directly via <script> tag
else {
root.async = async;
}
}());
@@ -0,0 +1,126 @@
/*
---
name: Form.Check
description: Class to represent a checkbox
authors: Bryan J Swift (@bryanjswift)
license: MIT-style license.
requires: [Core/Class.Extras, Core/Element, Core/Element.Event]
provides: Form.Check
...
*/
if (typeof window.Form === 'undefined') { window.Form = {}; }
Form.Check = new Class({
Implements: [Events, Options],
options: {
checked: false,
disabled: false
},
bound: {},
checked: false,
config: {
checkedClass: 'checked',
disabledClass: 'disabled',
elementClass: 'check',
highlightedClass: 'highlighted',
storage: 'Form.Check::data'
},
disabled: false,
element: null,
input: null,
label: null,
value: null,
initialize: function(input, options) {
this.setOptions(options);
this.bound = {
disable: this.disable.bind(this),
enable: this.enable.bind(this),
highlight: this.highlight.bind(this),
removeHighlight: this.removeHighlight.bind(this),
keyToggle: this.keyToggle.bind(this),
toggle: this.toggle.bind(this)
};
var bound = this.bound;
input = this.input = $(input);
var id = input.get('id');
this.label = document.getElement('label[for=' + id + ']');
this.element = new Element('div', {
'class': input.get('class') + ' ' + this.config.elementClass,
id: id ? id + 'Check' : '',
events: {
click: bound.toggle,
mouseenter: bound.highlight,
mouseleave: bound.removeHighlight
}
});
this.input.addEvents({
keypress: bound.keyToggle,
keydown: bound.keyToggle,
keyup: bound.keyToggle
});
if (this.label) { this.label.addEvent('click', bound.toggle); }
this.element.wraps(input);
this.value = input.get('value');
if (this.input.checked) { this.check(); } else { this.uncheck(); }
if (this.input.disabled) { this.disable(); } else { this.enable(); }
input.store(this.config.storage, this).addEvents({
blur: bound.removeHighlight,
focus: bound.highlight
});
this.fireEvent('create', this);
},
check: function() {
this.element.addClass(this.config.checkedClass);
this.input.set('checked', 'checked').focus();
this.checked = true;
this.fireEvent('check', this);
},
disable: function() {
this.element.addClass(this.config.disabledClass);
this.input.set('disabled', 'disabled');
this.disabled = true;
this.fireEvent('disable', this);
},
enable: function() {
this.element.removeClass(this.config.disabledClass);
this.input.erase('disabled');
this.disabled = false;
this.fireEvent('enable', this);
},
highlight: function() {
this.element.addClass(this.config.highlightedClass);
this.fireEvent('highlight', this);
},
removeHighlight: function() {
this.element.removeClass(this.config.highlightedClass);
this.fireEvent('removeHighlight', this);
},
keyToggle: function(e) {
var evt = (e);
if (evt.key === 'space') { this.toggle(e); }
},
toggle: function(e) {
var evt;
if (this.disabled) { return this; }
if (e) {
evt = (e).stopPropagation();
if (evt.target.tagName.toLowerCase() !== 'a') {
evt.stop();
}
}
if (this.checked) {
this.uncheck();
} else {
this.check();
}
this.fireEvent('change', this);
this.input.fireEvent('change', this);
return this;
},
uncheck: function() {
this.element.removeClass(this.config.checkedClass);
this.input.erase('checked');
this.checked = false;
this.fireEvent('uncheck', this);
}
});
@@ -0,0 +1,51 @@
/*
---
name: Form.CheckGroup
description: Class to represent a group of Form.Check wrapped checkboxes
authors: Bryan J Swift (@bryanjswift)
license: MIT-style license.
requires: [Core/Class.Extras, Core/Element, Core/Element.Event, Form-Replacement/Form.Check]
provides: Form.CheckGroup
...
*/
if (typeof window.Form === 'undefined') { window.Form = {}; }
Form.CheckGroup = new Class({
Implements: [Events,Options],
options: {
checkOptions: {},
initialValues: {}
},
checks: [],
initialize: function(group,options) {
if (!Form.Check) { throw 'required Class Form.Check not found'; }
this.setOptions(options);
group = $(group);
if (!group) { return this; }
var checkboxes = group.getElements('input[type=checkbox]');
checkboxes.each(this.addCheck,this);
},
addCheck: function(checkbox) {
var initialValues = this.options.initialValues[checkbox.get('name')];
var checkOptions = {};
checkOptions.checked = initialValues ? initialValues.contains(checkbox.get('value')) : checkbox.get('checked');
checkOptions.disabled = checkbox.get('disabled');
checkbox.store('Form.CheckGroup::data',this);
var check = checkbox.retrieve('Form.Check::data') || new Form.Check(checkbox, Object.append(checkOptions,this.options.checkOptions));
this.checks.push(check);
},
checkAll: function() {
this.checks.each(function(check) { if (!check.checked) { check.toggle(); } });
},
disable: function() {
this.checks.each(function(check) { check.disable(); });
this.fireEvent('disable',this);
},
enable: function() {
this.checks.each(function(check) { check.enable(); });
this.fireEvent('enable',this);
},
uncheckAll: function() {
this.checks.each(function(check) { if (check.checked) { check.toggle(); } });
}
});
@@ -0,0 +1,325 @@
/*
---
name: Form.Dropdown
description: Class to represent a select input
authors: Bryan J Swift (@bryanjswift)
license: MIT-style license.
requires: [Core/Class.Extras, Core/Element, Core/Element.Event, Form-Replacement/Form.SelectOption]
provides: Form.Dropdown
...
*/
if (typeof window.Form === 'undefined') { window.Form = {}; }
Form.Dropdown = new Class({
Implements: [Events,Options],
options: {
excludedValues: [],
initialValue: null,
mouseLeaveDelay: 350,
selectOptions: {},
typeDelay: 500
},
bound: {},
dropdownOptions: [],
element: null,
events: {},
highlighted: null,
input: null,
open: true,
selected: null,
selection: null,
typed: { lastKey: null, value: null, timer: null, pressed: null, shortlist: [], startkey: null },
value: null,
initialize: function(select,options) {
this.setOptions(options);
select = $(select);
this.bound = {
collapse: this.collapse.bind(this),
expand: this.expand.bind(this),
focus: this.focus.bind(this),
highlightOption: this.highlightOption.bind(this),
keydown: this.keydown.bind(this),
keypress: this.keypress.bind(this),
mouseenterDropdown: this.mouseenterDropdown.bind(this),
mouseleaveDropdown: this.mouseleaveDropdown.bind(this),
mousemove: this.mousemove.bind(this),
removeHighlightOption: this.removeHighlightOption.bind(this),
select: this.select.bind(this),
toggle: this.toggle.bind(this)
};
this.events = { mouseenter: this.bound.mouseenterDropdown, mouseleave: this.bound.mouseleaveDropdown };
this.value = this.options.initialValue;
this.initializeCreateElements(select);
var optionElements = select.getElements('option');
this.updateOptions(optionElements);
this.element.replaces(select);
document.addEvent('click', this.bound.collapse);
var eventName = Browser.ie || Browser.webkit ? 'keydown' : 'keypress';
var target = Browser.ie ? $(document.body) : window;
target.addEvent('keydown',this.bound.keydown).addEvent(eventName,this.bound.keypress);
},
initializeCreateElements: function(select) {
var id = select.get('id');
var dropdown = new Element('div', {
'class': (select.get('class') + ' select').trim(),
'id': (id && id !== '') ? id + 'Dropdown' : ''
});
var menu = new Element('div', {'class': 'menu'});
var list = new Element('div', {'class': 'list'});
var options = new Element('ul', {'class': 'options'});
dropdown.adopt(menu.adopt(list.adopt(options)));
var dropdownSelection = new Element('div', {
'class': 'selection',
events: {click: this.bound.toggle}
});
var dropdownBackground = new Element('div', { 'class': 'dropdownBackground' });
var selection = new Element('span', { 'class': 'selectionDisplay' });
var input = new Element('input', {
type:'text',
id: id,
name: select.get('name'),
events: {
focus: this.bound.focus
}
});
dropdownSelection.adopt(dropdownBackground, selection, input);
dropdown.adopt(dropdownSelection);
this.element = dropdown;
this.selection = selection;
this.input = input;
return options;
},
collapse: function(e) {
this.open = false;
this.element.removeClass('active').removeClass('dropdown-active');
if (this.selected) { this.selected.removeHighlight(); }
this.element.removeEvents(this.events);
this.fireEvent('collapse', [this, e]);
},
deselect: function(option) {
option.deselect();
},
destroy: function() {
this.element = null;
this.selection = null;
this.input = null;
},
disable: function() {
this.collapse();
this.input.set('disabled', 'disabled').removeEvents({blur:this.bound.blur, focus:this.bound.focus});
this.selection.getParent().removeEvent('click', this.bound.toggle);
this.fireEvent('disable', this);
},
enable: function() {
this.input.erase('disabled').addEvents({blur:this.bound.blur, focus:this.bound.focus});
this.selection.getParent().addEvent('click', this.bound.toggle);
this.fireEvent('enable', this);
},
expand: function(e) {
clearTimeout(this.collapseInterval);
var evt = e ? (e).stop() : null;
this.open = true;
this.input.focus();
this.element.addClass('active').addClass('dropdown-active');
if (this.selected) { this.selected.highlight(); }
this.element.addEvents(this.events);
this.fireEvent('expand', [this, e]);
},
focus: function(e) {
this.expand();
},
foundMatch: function(e) {
var typed = this.typed;
var shortlist = typed.shortlist;
var value = typed.value;
var i = 0;
var optionsLength = shortlist.length;
var excludedValues = this.options.excludedValues;
var found = false;
if (!optionsLength) { return; }
var option;
do {
option = shortlist[i];
if (option.text.toLowerCase().indexOf(value) === 0 && !excludedValues.contains(option.value)) {
found = true;
option.highlight(e);
typed.pressed = i + 1;
i = optionsLength;
}
i = i + 1;
} while(i < optionsLength);
return found;
},
highlightOption: function(option) {
if (this.highlighted) { this.highlighted.removeHighlight(); }
this.highlighted = option;
},
isOpen: function() {
return this.open;
},
keydown: function(e) {
if (!this.open) { return; }
this.dropdownOptions.each(function(option) { option.disable(); });
document.addEvent('mousemove', this.bound.mousemove);
},
keypress: function(e) {
if (!this.open) { return; }
(e).stop();
var code = e.code, key = e.key;
var typed = this.typed;
var match, i, options, option, optionsLength, found, first, excludedValues, shortlist;
switch(code) {
case 38: // up
case 37: // left
if (typed.pressed > 0) { typed.pressed = typed.pressed - 1; }
if (!this.highlighted) { this.dropdownOptions.getLast().highlight(e); break; }
match = this.highlighted.element.getPrevious();
match = match ? match.retrieve('Form.SelectOption::data') : this.dropdownOptions.getLast();
match.highlight(e);
break;
case 40: // down
case 39: // right
if (typed.shortlist.length > 0) { typed.pressed = typed.pressed + 1; }
if (!this.highlighted) { this.dropdownOptions[0].highlight(e); break; }
match = this.highlighted.element.getNext();
match = match ? match.retrieve('Form.SelectOption::data') : this.dropdownOptions[0];
match.highlight(e);
break;
case 13: // enter
e.stop();
case 9: // tab - skips the stop event but selects the item
this.highlighted.select();
break;
case 27: // esc
e.stop();
this.toggle();
break;
case 32: // space
default: // anything else
if (!(code >= 48 && code <= 122 && (code <= 57 || (code >= 65 && code <= 90) || code >=97) || code === 32)) {
break;
}
if (evt.control || evt.alt || evt.meta) { return; }
// alphanumeric or space
key = code === 32 ? ' ' : key;
clearTimeout(typed.timer);
options = this.dropdownOptions;
optionsLength = options.length;
excludedValues = this.options.excludedValues;
if (typed.timer === null) { // timer is expired
typed.shortlist = [];
if (key === typed.lastKey || key === typed.startkey) { // get next
typed.pressed = typed.pressed + 1;
typed.value = key;
} else { // get first
typed = this.resetTyped();
typed.value = key;
typed.startkey = key;
typed.pressed = 1;
}
typed.timer = this.resetTyped.delay(500, this);
} else {
if (key === typed.lastKey) { // check for match, if no match get next
typed.value = typed.value + key;
if (this.foundMatch(e)) { // got a match so break
typed.timer = this.resetTyped.delay(500, this);
break;
} else { // no match fall through
typed.shortlist = [];
typed.value = key;
typed.pressed = typed.pressed + 1;
typed.timer = null;
}
} else { // reset timer, get first match, set pressed to found position
typed.timer = this.resetTyped.delay(500, this);
typed.value = typed.value + key;
typed.startkey = typed.value.substring(0, 1);
typed.lastKey = key;
this.foundMatch(e);
break;
}
}
typed.lastKey = key;
shortlist = typed.shortlist;
i = 0;
found = 0;
do {
option = options[i];
if (option.text.toLowerCase().indexOf(key) === 0 && !excludedValues.contains(option.value)) {
if (found === 0) { first = option; }
found = found + 1;
if (found === typed.pressed) { option.highlight(e); }
shortlist.push(option);
}
i = i + 1;
} while(i < optionsLength);
if (typed.pressed > found) {
first.highlight(e);
typed.pressed = 1;
}
break;
}
},
mouseenterDropdown: function() {
clearTimeout(this.collapseInterval);
},
mouseleaveDropdown: function() {
this.collapseInterval = this.options.mouseLeaveDelay ? this.collapse.delay(this.options.mouseLeaveDelay,this) : null;
},
mousemove: function() {
this.dropdownOptions.each(function(option) { option.enable(); });
document.removeEvent('mousemove', this.bound.mousemove);
},
removeHighlightOption: function(option) {
this.highlighted = null;
},
reset: function() {
if (this.options.initialValue) {
this.dropdownOptions.each(function(o) {
if (o.value === this.options.initialValue) { o.select(); }
}, this);
} else {
this.dropdownOptions[0].select();
}
},
resetTyped: function() {
var typed = this.typed;
typed.value = null;
typed.timer = null;
return typed;
},
select: function(option, e) {
this.dropdownOptions.each(this.deselect);
this.selection.set('html', option.element.get('html'));
var oldValue = this.value;
this.value = option.value;
this.input.set('value', option.value);
this.selected = option;
this.fireEvent('select', [this, e]);
if (oldValue && oldValue !== this.value) { this.fireEvent('change', [this, e]); }
this.collapse(e);
},
toggle: function(e) {
if (this.open) { this.collapse(e); }
else { this.expand(e); }
},
updateOptions: function(optionElements) {
var optionsList = this.element.getElement('ul').empty(),
dropdownOptions = this.dropdownOptions.empty(),
selectOptions = this.options.selectOptions;
optionElements.each(function(opt) {
var option = new Form.SelectOption(opt, selectOptions);
option.addEvents({
'onHighlight':this.bound.highlightOption,
'onRemoveHighlight':this.bound.removeHighlightOption,
'onSelect':this.bound.select
}).owner = this;
if (option.value === this.options.initialValue || opt.get('selected')) { this.select(option); }
dropdownOptions.push(option);
optionsList.adopt(option.element);
}, this);
if (!this.selected && optionElements[0]) { optionElements[0].retrieve('Form.SelectOption::data').select(); }
}
});
@@ -0,0 +1,34 @@
/*
---
name: Form.Radio
description: Class to represent a radio button
authors: Bryan J Swift (@bryanjswift)
license: MIT-style license.
requires: [Core/Class.Extras, Core/Element, Core/Element.Event, Form-Replacement/Form.Check]
provides: Form.Radio
...
*/
if (typeof window.Form === 'undefined') { window.Form = {}; }
Form.Radio = new Class({
Extends: Form.Check,
config: {
elementClass: 'radio',
storage: 'Form.Radio::data'
},
initialize: function(input,options) {
this.parent(input,options);
},
toggle: function(e) {
if (this.element.hasClass('checked') || this.disabled) { return; }
var evt;
if (e) { evt = (e).stop(); }
if (this.checked) {
this.uncheck();
} else {
this.check();
}
this.fireEvent(this.checked ? 'onCheck' : 'onUncheck',this);
this.fireEvent('onChange',this);
}
});
@@ -0,0 +1,59 @@
/*
---
name: Form.RadioGroup
description: Class to represent a group of Form.Radio buttons
authors: Bryan J Swift (@bryanjswift)
license: MIT-style license.
requires: [Core/Class.Extras, Core/Element, Core/Element.Event, Form-Replacement/Form.Radio]
provides: Form.RadioGroup
...
*/
if (typeof window.Form === 'undefined') { window.Form = {}; }
Form.RadioGroup = new Class({
Implements: [Events,Options],
options: {
radioOptions: {},
initialValues: {}
},
bound: {},
radios: [],
value: null,
initialize: function(group,options) {
if (!Form.Radio) { throw 'required Class Form.Radio not found'; }
this.setOptions(options);
this.bound = { select: this.select.bind(this) };
group = $(group);
if (!group) { return this; }
var radios = group.getElements('input[type=radio]');
radios.each(this.addCheck,this);
},
addCheck: function(radio,i) {
var initialValues = this.options.initialValues[radio.get('name')];
var radioOptions = {};
radioOptions.checked = initialValues ? initialValues.contains(radio.get('value')) : radio.get('checked');
radioOptions.disabled = radio.get('disabled');
var check = (radio.retrieve('Form.Radio::data')
|| new Form.Radio(radio,Object.append(radioOptions,this.options.radioOptions)));
check.addEvent('onCheck',this.bound.select);
if (check.checked) { i ? this.changed(check) : this.value = check.value; }
radio.store('Form.RadioGroup::data',this);
this.radios.push(check);
},
changed: function(radio) {
this.value = radio.value;
this.fireEvent('onChange',this);
},
disable: function() {
this.radios.each(function(radio) { radio.disable(); });
},
enable: function() {
this.radios.each(function(radio) { radio.enable(); });
},
select: function(checkedRadio) {
this.radios.each(function(radio) {
if (radio.checked && radio.value !== checkedRadio.value) { radio.uncheck(); }
});
if (checkedRadio.value !== this.value) { this.changed(checkedRadio); }
}
});
@@ -0,0 +1,93 @@
/*
---
name: Form.SelectOption
description: Class to represent an option for Form.Dropdown
authors: Bryan J Swift (@bryanjswift)
license: MIT-style license.
requires: [Core/Class.Extras, Core/Element, Core/Element.Event]
provides: Form.SelectOption
...
*/
if (typeof window.Form === 'undefined') { window.Form = {}; }
Form.SelectOption = new Class({
Implements: [Events, Options],
options: {
optionTag: 'li',
selected: false
},
config: {
highlightedClass: 'highlighted',
optionClass: 'option',
selectedClass: 'selected'
},
element: null,
bound: {},
option: null,
selected: false,
text: null,
value: null,
initialize: function(option, options) {
this.setOptions(options);
option = $(option);
this.option = option;
this.bound = {
deselect: this.deselect.bind(this),
highlight: this.highlight.bind(this),
removeHighlight: this.removeHighlight.bind(this),
select: this.select.bind(this)
};
this.text = option.get('text');
this.value = option.get('value');
this.element = new Element(this.options.optionTag, {
'class': (option.get('class') + ' ' + this.config.optionClass).trim(),
'html': option.get('html'),
'events': {
click: this.bound.select,
mouseenter: this.bound.highlight,
mouseleave: this.bound.removeHighlight
}
});
this.element.store('Form.SelectOption::data', this);
option.store('Form.SelectOption::data', this);
},
deselect: function(e) {
this.fireEvent('onDeselect', [this, e]);
this.element.removeClass(this.config.selectedClass).addEvent('click', this.bound.select);
this.selected = false;
},
destroy: function() {
this.element = null;
this.bound = null;
this.option = null;
},
disable: function() {
this.element.removeEvents({
mouseenter: this.bound.highlight,
mouseleave: this.bound.removeHighlight
});
this.fireEvent('onDisable', this);
},
enable: function() {
this.element.addEvents({
mouseenter: this.bound.highlight,
mouseleave: this.bound.removeHighlight
});
this.fireEvent('onEnable', this);
},
highlight: function(e) {
this.fireEvent('onHighlight', [this, e]);
this.element.addClass(this.config.highlightedClass);
return this;
},
removeHighlight: function(e) {
this.fireEvent('onRemoveHighlight', [this, e]);
this.element.removeClass(this.config.highlightedClass);
return this;
},
select: function(e) {
this.fireEvent('onSelect', [this, e]);
this.element.addClass(this.config.selectedClass).removeEvent('click', this.bound.select);
this.selected = true;
}
});
+176
View File
@@ -0,0 +1,176 @@
// packager build History/*
/*
---
name: Class.Binds
description: Alternate Class.Binds Implementation
authors: Scott Kyle (@appden), Christoph Pojer (@cpojer)
license: MIT-style license.
requires: [Core/Class, Core/Function]
provides: Class.Binds
...
*/
Class.Binds = new Class({
$bound: {},
bound: function(name){
return this.$bound[name] ? this.$bound[name] : this.$bound[name] = this[name].bind(this);
}
});
/*
---
name: History
description: History Management via popstate or hashchange.
authors: Christoph Pojer (@cpojer)
license: MIT-style license.
requires: [Core/Events, Core/Element.Event, Class-Extras/Class.Binds]
provides: History
...
*/
(function(){
var events = Element.NativeEvents,
location = window.location,
base = location.pathname,
history = window.history,
hasPushState = ('pushState' in history),
event = hasPushState ? 'popstate' : 'hashchange';
this.History = new new Class({
Implements: [Class.Binds, Events],
initialize: hasPushState ? function(){
events[event] = 2;
window.addEvent(event, this.bound('pop'));
} : function(){
events[event] = 1;
window.addEvent(event, this.bound('pop'));
this.hash = location.hash;
var hashchange = ('onhashchange' in window);
if (!(hashchange && (document.documentMode === undefined || document.documentMode > 7)))
this.timer = this.check.periodical(200, this);
},
push: hasPushState ? function(url, title, state){
if (base && base != url) base = null;
history.pushState(state || null, title || null, url);
this.onChange(url, state);
} : function(url){
location.hash = url;
},
replace: hasPushState ? function(url, title, state){
history.replaceState(state || null, title || null, url);
} : function(url){
this.hash = '#' + url;
this.push(url);
},
pop: hasPushState ? function(event){
var url = location.pathname;
if (url == base){
base = null;
return;
}
this.onChange(url, event.event.state);
} : function(){
var hash = location.hash;
if (this.hash == hash) return;
this.hash = hash;
this.onChange(hash.substr(1));
},
onChange: function(url, state){
this.fireEvent('change', [url, state || {}]);
},
back: function(){
history.back();
},
forward: function(){
history.forward();
},
getPath: function(){
return hasPushState ? location.pathname : location.hash.substr(1);
},
hasPushState: function(){
return hasPushState;
},
check: function(){
if (this.hash != location.hash) this.pop();
}
});
})();
/*
---
name: History.handleInitialState
description: Provides a helper method to handle the initial state of your application.
authors: Christoph Pojer (@cpojer)
license: MIT-style license.
requires: [History]
provides: History.handleInitialState
...
*/
History.handleInitialState = function(base){
if (!base) base = '';
var location = window.location,
pathname = location.pathname.substr(base.length),
hash = location.hash,
hasPushState = History.hasPushState();
if (!hasPushState && pathname.length > 1){
window.location = (base || '/') + '#' + pathname;
return true;
}
if (!hash || hash.length <= 1) return false;
if (hasPushState){
(function(){
History.push(hash.substr(1));
}).delay(1);
return false;
}
if (!pathname || pathname == '/') return false;
window.location = (base || '/') + hash;
return true;
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+141
View File
@@ -0,0 +1,141 @@
/*
---
name: Fx.Tween.CSS3.Replacement
script: Fx.Tween.CSS3.Replacement.js
license: MIT-style license.
description: Same behavior like Fx.Tween but tries to use native CSS3 transition if possible (Overwrites Fx.Tween).
copyright: Copyright (c) 2010, Dipl.-Ing. (FH) André Fiedler <kontakt at visualdrugs dot net>, based on code by eskimoblood (mootools users group)
authors: [André Fiedler, eskimoblood]
requires: [Core/Class.Extras, Core/Element.Event, Core/Element.Style, Core/Fx.Tween]
provides: [Fx.Tween]
...
*/
Element.NativeEvents.transitionend = 2;
Element.NativeEvents.webkitTransitionEnd = 2;
Element.NativeEvents.oTransitionEnd = 2;
Element.NativeEvents.msTransitionEnd = 2;
Element.Events.transitionend = {
base: Browser.safari || Browser.chrome ? 'webkitTransitionEnd' : (Browser.opera ? 'oTransitionEnd' : (Browser.ie && Browser.version > 8 ? 'msTransitionEnd' : 'transitionend'))
};
Event.implement({
getPropertyName: function(){
return this.event.propertyName;
},
getElapsedTime: function(nativeTime){
return nativeTime ? this.event.elapsedTime : (this.event.elapsedTime * 1000).toInt();
}
});
Element.implement({
supportStyle: function(style){
var value = this.style[style];
return !!(value || value === '');
},
supportVendorStyle: function(style){
var prefixedStyle = null;
return this.supportStyle(style) ? style : ['webkit', 'Moz', 'o', 'ms'].some(function(prefix){
prefixedStyle = prefix + style.capitalize();
return this.supportStyle(prefixedStyle);
}, this) ? prefixedStyle : null;
}
});
Fx.TweenCSS2 = Fx.Tween;
Fx.Tween = new Class({
Extends: Fx.TweenCSS2,
transitionTimings: {
'linear' : '0,0,1,1',
'expo:in' : '0.71,0.01,0.83,0',
'expo:out' : '0.14,1,0.32,0.99',
'expo:in:out' : '0.85,0,0.15,1',
'circ:in' : '0.34,0,0.96,0.23',
'circ:out' : '0,0.5,0.37,0.98',
'circ:in:out' : '0.88,0.1,0.12,0.9',
'sine:in' : '0.22,0.04,0.36,0',
'sine:out' : '0.04,0,0.5,1',
'sine:in:out' : '0.37,0.01,0.63,1',
'quad:in' : '0.14,0.01,0.49,0',
'quad:out' : '0.01,0,0.43,1',
'quad:in:out' : '0.47,0.04,0.53,0.96',
'cubic:in' : '0.35,0,0.65,0',
'cubic:out' : '0.09,0.25,0.24,1',
'cubic:in:out' : '0.66,0,0.34,1',
'quart:in' : '0.69,0,0.76,0.17',
'quart:out' : '0.26,0.96,0.44,1',
'quart:in:out' : '0.76,0,0.24,1',
'quint:in' : '0.64,0,0.78,0',
'quint:out' : '0.22,1,0.35,1',
'quint:in:out' : '0.9,0,0.1,1'
},
initialize: function(element, options){
options.transition = options.transition || 'sine:in:out';
this.parent(element, options);
if (typeof this.options.transition != 'string') alert('Only short notated transitions (like \'sine:in\') are supported by Fx.Tween.CSS3');
this.options.transition = this.options.transition.toLowerCase();
this.transition = this.element.supportVendorStyle('transition');
this.css3Supported = !!this.transition && !!this.transitionTimings[this.options.transition];
},
check: function(){
if (!this.boundComplete) return true;
return this.parent();
},
start: function(property, from, to){
if (this.css3Supported){
if (!this.check(property, from, to)) return this;
var args = Array.flatten(arguments);
this.property = this.options.property || args.shift();
var parsed = this.prepare(this.element, this.property, args);
this.from = parsed.from;
this.to = parsed.to;
this.boundComplete = function(event){
if (event.getPropertyName() == this.property /* && event.getElapsedTime() == this.options.duration */ ){
this.element.removeEvent('transitionend', this.boundComplete);
this.boundComplete = null;
this.fireEvent('complete', this);
}
}.bind(this);
this.element.addEvent('transitionend', this.boundComplete);
var trans = function(){
this.element.setStyle(this.transition, this.property + ' ' + this.options.duration + 'ms cubic-bezier(' + this.transitionTimings[this.options.transition] + ')');
this.element.setStyle(this.property, this.to[0].value + this.options.unit);
}.bind(this);
if (args[1]){
this.element.setStyle(this.transition, 'none');
this.element.setStyle(this.property, this.from[0].value + this.options.unit);
trans.delay(0.1);
} else
trans();
this.fireEvent('start', this);
return this;
}
return this.parent(property, from, to);
},
cancel: function(){
if (this.css3Supported){
this.element.setStyle(this.transition, 'none');
this.element.removeEvent('transitionend', this.boundComplete);
this.boundComplete = null;
}
this.parent();
return this;
}
});
+301
View File
@@ -0,0 +1,301 @@
//fgnass.github.com/spin.js#v1.2.4
(function(window, document, undefined) {
/**
* Copyright (c) 2011 Felix Gnass [fgnass at neteye dot de]
* Licensed under the MIT license
*/
var prefixes = ['webkit', 'Moz', 'ms', 'O']; /* Vendor prefixes */
var animations = {}; /* Animation rules keyed by their name */
var useCssAnimations;
/**
* Utility function to create elements. If no tag name is given,
* a DIV is created. Optionally properties can be passed.
*/
function createEl(tag, prop) {
var el = document.createElement(tag || 'div');
var n;
for(n in prop) {
el[n] = prop[n];
}
return el;
}
/**
* Appends children and returns the parent.
*/
function ins(parent /* child1, child2, ...*/) {
for (var i=1, n=arguments.length; i<n; i++) {
parent.appendChild(arguments[i]);
}
return parent;
}
/**
* Insert a new stylesheet to hold the @keyframe or VML rules.
*/
var sheet = function() {
var el = createEl('style');
ins(document.getElementsByTagName('head')[0], el);
return el.sheet || el.styleSheet;
}();
/**
* Creates an opacity keyframe animation rule and returns its name.
* Since most mobile Webkits have timing issues with animation-delay,
* we create separate rules for each line/segment.
*/
function addAnimation(alpha, trail, i, lines) {
var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-');
var start = 0.01 + i/lines*100;
var z = Math.max(1-(1-alpha)/trail*(100-start) , alpha);
var prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase();
var pre = prefix && '-'+prefix+'-' || '';
if (!animations[name]) {
sheet.insertRule(
'@' + pre + 'keyframes ' + name + '{' +
'0%{opacity:'+z+'}' +
start + '%{opacity:'+ alpha + '}' +
(start+0.01) + '%{opacity:1}' +
(start+trail)%100 + '%{opacity:'+ alpha + '}' +
'100%{opacity:'+ z + '}' +
'}', 0);
animations[name] = 1;
}
return name;
}
/**
* Tries various vendor prefixes and returns the first supported property.
**/
function vendor(el, prop) {
var s = el.style;
var pp;
var i;
if(s[prop] !== undefined) return prop;
prop = prop.charAt(0).toUpperCase() + prop.slice(1);
for(i=0; i<prefixes.length; i++) {
pp = prefixes[i]+prop;
if(s[pp] !== undefined) return pp;
}
}
/**
* Sets multiple style properties at once.
*/
function css(el, prop) {
for (var n in prop) {
el.style[vendor(el, n)||n] = prop[n];
}
return el;
}
/**
* Fills in default values.
*/
function merge(obj) {
for (var i=1; i < arguments.length; i++) {
var def = arguments[i];
for (var n in def) {
if (obj[n] === undefined) obj[n] = def[n];
}
}
return obj;
}
/**
* Returns the absolute page-offset of the given element.
*/
function pos(el) {
var o = {x:el.offsetLeft, y:el.offsetTop};
while((el = el.offsetParent)) {
o.x+=el.offsetLeft;
o.y+=el.offsetTop;
}
return o;
}
var defaults = {
lines: 12, // The number of lines to draw
length: 7, // The length of each line
width: 5, // The line thickness
radius: 10, // The radius of the inner circle
color: '#000', // #rgb or #rrggbb
speed: 1, // Rounds per second
trail: 100, // Afterglow percentage
opacity: 1/4, // Opacity of the lines
fps: 20, // Frames per second when using setTimeout()
zIndex: 2e9, // Use a high z-index by default
className: 'spinner', // CSS class to assign to the element
top: 'auto', // center vertically
left: 'auto' // center horizontally
};
/** The constructor */
var Spinner = function Spinner(o) {
if (!this.spin) return new Spinner(o);
this.opts = merge(o || {}, Spinner.defaults, defaults);
};
Spinner.defaults = {};
Spinner.prototype = {
spin: function(target) {
this.stop();
var self = this;
var o = self.opts;
var el = self.el = css(createEl(0, {className: o.className}), {position: 'relative', zIndex: o.zIndex});
var mid = o.radius+o.length+o.width;
var ep; // element position
var tp; // target position
if (target) {
target.insertBefore(el, target.firstChild||null);
tp = pos(target);
ep = pos(el);
css(el, {
left: (o.left == 'auto' ? tp.x-ep.x + (target.offsetWidth >> 1) : o.left+mid) + 'px',
top: (o.top == 'auto' ? tp.y-ep.y + (target.offsetHeight >> 1) : o.top+mid) + 'px'
});
}
el.setAttribute('aria-role', 'progressbar');
self.lines(el, self.opts);
if (!useCssAnimations) {
// No CSS animation support, use setTimeout() instead
var i = 0;
var fps = o.fps;
var f = fps/o.speed;
var ostep = (1-o.opacity)/(f*o.trail / 100);
var astep = f/o.lines;
!function anim() {
i++;
for (var s=o.lines; s; s--) {
var alpha = Math.max(1-(i+s*astep)%f * ostep, o.opacity);
self.opacity(el, o.lines-s, alpha, o);
}
self.timeout = self.el && setTimeout(anim, ~~(1000/fps));
}();
}
return self;
},
stop: function() {
var el = this.el;
if (el) {
clearTimeout(this.timeout);
if (el.parentNode) el.parentNode.removeChild(el);
this.el = undefined;
}
return this;
},
lines: function(el, o) {
var i = 0;
var seg;
function fill(color, shadow) {
return css(createEl(), {
position: 'absolute',
width: (o.length+o.width) + 'px',
height: o.width + 'px',
background: color,
boxShadow: shadow,
transformOrigin: 'left',
transform: 'rotate(' + ~~(360/o.lines*i) + 'deg) translate(' + o.radius+'px' +',0)',
borderRadius: (o.width>>1) + 'px'
});
}
for (; i < o.lines; i++) {
seg = css(createEl(), {
position: 'absolute',
top: 1+~(o.width/2) + 'px',
transform: o.hwaccel ? 'translate3d(0,0,0)' : '',
opacity: o.opacity,
animation: useCssAnimations && addAnimation(o.opacity, o.trail, i, o.lines) + ' ' + 1/o.speed + 's linear infinite'
});
if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'}));
ins(el, ins(seg, fill(o.color, '0 0 1px rgba(0,0,0,.1)')));
}
return el;
},
opacity: function(el, i, val) {
if (i < el.childNodes.length) el.childNodes[i].style.opacity = val;
}
};
/////////////////////////////////////////////////////////////////////////
// VML rendering for IE
/////////////////////////////////////////////////////////////////////////
/**
* Check and init VML support
*/
!function() {
var s = css(createEl('group'), {behavior: 'url(#default#VML)'});
var i;
if (!vendor(s, 'transform') && s.adj) {
// VML support detected. Insert CSS rules ...
for (i=4; i--;) sheet.addRule(['group', 'roundrect', 'fill', 'stroke'][i], 'behavior:url(#default#VML)');
Spinner.prototype.lines = function(el, o) {
var r = o.length+o.width;
var s = 2*r;
function grp() {
return css(createEl('group', {coordsize: s +' '+s, coordorigin: -r +' '+-r}), {width: s, height: s});
}
var margin = -(o.width+o.length)*2+'px';
var g = css(grp(), {position: 'absolute', top: margin, left: margin});
var i;
function seg(i, dx, filter) {
ins(g,
ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}),
ins(css(createEl('roundrect', {arcsize: 1}), {
width: r,
height: o.width,
left: o.radius,
top: -o.width>>1,
filter: filter
}),
createEl('fill', {color: o.color, opacity: o.opacity}),
createEl('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change
)
)
);
}
if (o.shadow) {
for (i = 1; i <= o.lines; i++) {
seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)');
}
}
for (i = 1; i <= o.lines; i++) seg(i);
return ins(el, g);
};
Spinner.prototype.opacity = function(el, i, val, o) {
var c = el.firstChild;
o = o.shadow && o.lines || 0;
if (c && i+o < c.childNodes.length) {
c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild;
if (c) c.opacity = val;
}
};
}
else {
useCssAnimations = vendor(s, 'animation');
}
}();
window.Spinner = Spinner;
})(window, document);