diff --git a/build/libs.js b/build/libs.js deleted file mode 100644 index 7f826da..0000000 --- a/build/libs.js +++ /dev/null @@ -1,2666 +0,0 @@ -// Underscore.js 1.5.1 -// http://underscorejs.org -// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors -// Underscore may be freely distributed under the MIT license. - -(function() { - - // Baseline setup - // -------------- - - // Establish the root object, `window` in the browser, or `global` on the server. - var root = this; - - // Save the previous value of the `_` variable. - var previousUnderscore = root._; - - // Establish the object that gets returned to break out of a loop iteration. - var breaker = {}; - - // Save bytes in the minified (but not gzipped) version: - var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; - - // Create quick reference variables for speed access to core prototypes. - var - push = ArrayProto.push, - slice = ArrayProto.slice, - concat = ArrayProto.concat, - toString = ObjProto.toString, - hasOwnProperty = ObjProto.hasOwnProperty; - - // All **ECMAScript 5** native function implementations that we hope to use - // are declared here. - var - nativeForEach = ArrayProto.forEach, - nativeMap = ArrayProto.map, - nativeReduce = ArrayProto.reduce, - nativeReduceRight = ArrayProto.reduceRight, - nativeFilter = ArrayProto.filter, - nativeEvery = ArrayProto.every, - nativeSome = ArrayProto.some, - nativeIndexOf = ArrayProto.indexOf, - nativeLastIndexOf = ArrayProto.lastIndexOf, - nativeIsArray = Array.isArray, - nativeKeys = Object.keys, - nativeBind = FuncProto.bind; - - // Create a safe reference to the Underscore object for use below. - var _ = function(obj) { - if (obj instanceof _) return obj; - if (!(this instanceof _)) return new _(obj); - this._wrapped = obj; - }; - - // Export the Underscore object for **Node.js**, with - // backwards-compatibility for the old `require()` API. If we're in - // the browser, add `_` as a global object via a string identifier, - // for Closure Compiler "advanced" mode. - if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { - exports = module.exports = _; - } - exports._ = _; - } else { - root._ = _; - } - - // Current version. - _.VERSION = '1.5.1'; - - // Collection Functions - // -------------------- - - // The cornerstone, an `each` implementation, aka `forEach`. - // Handles objects with the built-in `forEach`, arrays, and raw objects. - // Delegates to **ECMAScript 5**'s native `forEach` if available. - var each = _.each = _.forEach = function(obj, iterator, context) { - if (obj == null) return; - if (nativeForEach && obj.forEach === nativeForEach) { - obj.forEach(iterator, context); - } else if (obj.length === +obj.length) { - for (var i = 0, l = obj.length; i < l; i++) { - if (iterator.call(context, obj[i], i, obj) === breaker) return; - } - } else { - for (var key in obj) { - if (_.has(obj, key)) { - if (iterator.call(context, obj[key], key, obj) === breaker) return; - } - } - } - }; - - // Return the results of applying the iterator to each element. - // Delegates to **ECMAScript 5**'s native `map` if available. - _.map = _.collect = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); - each(obj, function(value, index, list) { - results.push(iterator.call(context, value, index, list)); - }); - return results; - }; - - var reduceError = 'Reduce of empty array with no initial value'; - - // **Reduce** builds up a single result from a list of values, aka `inject`, - // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. - _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - if (nativeReduce && obj.reduce === nativeReduce) { - if (context) iterator = _.bind(iterator, context); - return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); - } - each(obj, function(value, index, list) { - if (!initial) { - memo = value; - initial = true; - } else { - memo = iterator.call(context, memo, value, index, list); - } - }); - if (!initial) throw new TypeError(reduceError); - return memo; - }; - - // The right-associative version of reduce, also known as `foldr`. - // Delegates to **ECMAScript 5**'s native `reduceRight` if available. - _.reduceRight = _.foldr = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { - if (context) iterator = _.bind(iterator, context); - return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); - } - var length = obj.length; - if (length !== +length) { - var keys = _.keys(obj); - length = keys.length; - } - each(obj, function(value, index, list) { - index = keys ? keys[--length] : --length; - if (!initial) { - memo = obj[index]; - initial = true; - } else { - memo = iterator.call(context, memo, obj[index], index, list); - } - }); - if (!initial) throw new TypeError(reduceError); - return memo; - }; - - // Return the first value which passes a truth test. Aliased as `detect`. - _.find = _.detect = function(obj, iterator, context) { - var result; - any(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) { - result = value; - return true; - } - }); - return result; - }; - - // Return all the elements that pass a truth test. - // Delegates to **ECMAScript 5**'s native `filter` if available. - // Aliased as `select`. - _.filter = _.select = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); - each(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) results.push(value); - }); - return results; - }; - - // Return all the elements for which a truth test fails. - _.reject = function(obj, iterator, context) { - return _.filter(obj, function(value, index, list) { - return !iterator.call(context, value, index, list); - }, context); - }; - - // Determine whether all of the elements match a truth test. - // Delegates to **ECMAScript 5**'s native `every` if available. - // Aliased as `all`. - _.every = _.all = function(obj, iterator, context) { - iterator || (iterator = _.identity); - var result = true; - if (obj == null) return result; - if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); - each(obj, function(value, index, list) { - if (!(result = result && iterator.call(context, value, index, list))) return breaker; - }); - return !!result; - }; - - // Determine if at least one element in the object matches a truth test. - // Delegates to **ECMAScript 5**'s native `some` if available. - // Aliased as `any`. - var any = _.some = _.any = function(obj, iterator, context) { - iterator || (iterator = _.identity); - var result = false; - if (obj == null) return result; - if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); - each(obj, function(value, index, list) { - if (result || (result = iterator.call(context, value, index, list))) return breaker; - }); - return !!result; - }; - - // Determine if the array or object contains a given value (using `===`). - // Aliased as `include`. - _.contains = _.include = function(obj, target) { - if (obj == null) return false; - if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; - return any(obj, function(value) { - return value === target; - }); - }; - - // Invoke a method (with arguments) on every item in a collection. - _.invoke = function(obj, method) { - var args = slice.call(arguments, 2); - var isFunc = _.isFunction(method); - return _.map(obj, function(value) { - return (isFunc ? method : value[method]).apply(value, args); - }); - }; - - // Convenience version of a common use case of `map`: fetching a property. - _.pluck = function(obj, key) { - return _.map(obj, function(value){ return value[key]; }); - }; - - // Convenience version of a common use case of `filter`: selecting only objects - // containing specific `key:value` pairs. - _.where = function(obj, attrs, first) { - if (_.isEmpty(attrs)) return first ? void 0 : []; - return _[first ? 'find' : 'filter'](obj, function(value) { - for (var key in attrs) { - if (attrs[key] !== value[key]) return false; - } - return true; - }); - }; - - // Convenience version of a common use case of `find`: getting the first object - // containing specific `key:value` pairs. - _.findWhere = function(obj, attrs) { - return _.where(obj, attrs, true); - }; - - // Return the maximum element or (element-based computation). - // Can't optimize arrays of integers longer than 65,535 elements. - // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) - _.max = function(obj, iterator, context) { - if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { - return Math.max.apply(Math, obj); - } - if (!iterator && _.isEmpty(obj)) return -Infinity; - var result = {computed : -Infinity, value: -Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed > result.computed && (result = {value : value, computed : computed}); - }); - return result.value; - }; - - // Return the minimum element (or element-based computation). - _.min = function(obj, iterator, context) { - if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { - return Math.min.apply(Math, obj); - } - if (!iterator && _.isEmpty(obj)) return Infinity; - var result = {computed : Infinity, value: Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed < result.computed && (result = {value : value, computed : computed}); - }); - return result.value; - }; - - // Shuffle an array. - _.shuffle = function(obj) { - var rand; - var index = 0; - var shuffled = []; - each(obj, function(value) { - rand = _.random(index++); - shuffled[index - 1] = shuffled[rand]; - shuffled[rand] = value; - }); - return shuffled; - }; - - // An internal function to generate lookup iterators. - var lookupIterator = function(value) { - return _.isFunction(value) ? value : function(obj){ return obj[value]; }; - }; - - // Sort the object's values by a criterion produced by an iterator. - _.sortBy = function(obj, value, context) { - var iterator = lookupIterator(value); - return _.pluck(_.map(obj, function(value, index, list) { - return { - value : value, - index : index, - criteria : iterator.call(context, value, index, list) - }; - }).sort(function(left, right) { - var a = left.criteria; - var b = right.criteria; - if (a !== b) { - if (a > b || a === void 0) return 1; - if (a < b || b === void 0) return -1; - } - return left.index < right.index ? -1 : 1; - }), 'value'); - }; - - // An internal function used for aggregate "group by" operations. - var group = function(obj, value, context, behavior) { - var result = {}; - var iterator = lookupIterator(value == null ? _.identity : value); - each(obj, function(value, index) { - var key = iterator.call(context, value, index, obj); - behavior(result, key, value); - }); - return result; - }; - - // Groups the object's values by a criterion. Pass either a string attribute - // to group by, or a function that returns the criterion. - _.groupBy = function(obj, value, context) { - return group(obj, value, context, function(result, key, value) { - (_.has(result, key) ? result[key] : (result[key] = [])).push(value); - }); - }; - - // Counts instances of an object that group by a certain criterion. Pass - // either a string attribute to count by, or a function that returns the - // criterion. - _.countBy = function(obj, value, context) { - return group(obj, value, context, function(result, key) { - if (!_.has(result, key)) result[key] = 0; - result[key]++; - }); - }; - - // Use a comparator function to figure out the smallest index at which - // an object should be inserted so as to maintain order. Uses binary search. - _.sortedIndex = function(array, obj, iterator, context) { - iterator = iterator == null ? _.identity : lookupIterator(iterator); - var value = iterator.call(context, obj); - var low = 0, high = array.length; - while (low < high) { - var mid = (low + high) >>> 1; - iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; - } - return low; - }; - - // Safely create a real, live array from anything iterable. - _.toArray = function(obj) { - if (!obj) return []; - if (_.isArray(obj)) return slice.call(obj); - if (obj.length === +obj.length) return _.map(obj, _.identity); - return _.values(obj); - }; - - // Return the number of elements in an object. - _.size = function(obj) { - if (obj == null) return 0; - return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; - }; - - // Array Functions - // --------------- - - // Get the first element of an array. Passing **n** will return the first N - // values in the array. Aliased as `head` and `take`. The **guard** check - // allows it to work with `_.map`. - _.first = _.head = _.take = function(array, n, guard) { - if (array == null) return void 0; - return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; - }; - - // Returns everything but the last entry of the array. Especially useful on - // the arguments object. Passing **n** will return all the values in - // the array, excluding the last N. The **guard** check allows it to work with - // `_.map`. - _.initial = function(array, n, guard) { - return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); - }; - - // Get the last element of an array. Passing **n** will return the last N - // values in the array. The **guard** check allows it to work with `_.map`. - _.last = function(array, n, guard) { - if (array == null) return void 0; - if ((n != null) && !guard) { - return slice.call(array, Math.max(array.length - n, 0)); - } else { - return array[array.length - 1]; - } - }; - - // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. - // Especially useful on the arguments object. Passing an **n** will return - // the rest N values in the array. The **guard** - // check allows it to work with `_.map`. - _.rest = _.tail = _.drop = function(array, n, guard) { - return slice.call(array, (n == null) || guard ? 1 : n); - }; - - // Trim out all falsy values from an array. - _.compact = function(array) { - return _.filter(array, _.identity); - }; - - // Internal implementation of a recursive `flatten` function. - var flatten = function(input, shallow, output) { - if (shallow && _.every(input, _.isArray)) { - return concat.apply(output, input); - } - each(input, function(value) { - if (_.isArray(value) || _.isArguments(value)) { - shallow ? push.apply(output, value) : flatten(value, shallow, output); - } else { - output.push(value); - } - }); - return output; - }; - - // Return a completely flattened version of an array. - _.flatten = function(array, shallow) { - return flatten(array, shallow, []); - }; - - // Return a version of the array that does not contain the specified value(s). - _.without = function(array) { - return _.difference(array, slice.call(arguments, 1)); - }; - - // Produce a duplicate-free version of the array. If the array has already - // been sorted, you have the option of using a faster algorithm. - // Aliased as `unique`. - _.uniq = _.unique = function(array, isSorted, iterator, context) { - if (_.isFunction(isSorted)) { - context = iterator; - iterator = isSorted; - isSorted = false; - } - var initial = iterator ? _.map(array, iterator, context) : array; - var results = []; - var seen = []; - each(initial, function(value, index) { - if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { - seen.push(value); - results.push(array[index]); - } - }); - return results; - }; - - // Produce an array that contains the union: each distinct element from all of - // the passed-in arrays. - _.union = function() { - return _.uniq(_.flatten(arguments, true)); - }; - - // Produce an array that contains every item shared between all the - // passed-in arrays. - _.intersection = function(array) { - var rest = slice.call(arguments, 1); - return _.filter(_.uniq(array), function(item) { - return _.every(rest, function(other) { - return _.indexOf(other, item) >= 0; - }); - }); - }; - - // Take the difference between one array and a number of other arrays. - // Only the elements present in just the first array will remain. - _.difference = function(array) { - var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); - return _.filter(array, function(value){ return !_.contains(rest, value); }); - }; - - // Zip together multiple lists into a single array -- elements that share - // an index go together. - _.zip = function() { - var length = _.max(_.pluck(arguments, "length").concat(0)); - var results = new Array(length); - for (var i = 0; i < length; i++) { - results[i] = _.pluck(arguments, '' + i); - } - return results; - }; - - // Converts lists into objects. Pass either a single array of `[key, value]` - // pairs, or two parallel arrays of the same length -- one of keys, and one of - // the corresponding values. - _.object = function(list, values) { - if (list == null) return {}; - var result = {}; - for (var i = 0, l = list.length; i < l; i++) { - if (values) { - result[list[i]] = values[i]; - } else { - result[list[i][0]] = list[i][1]; - } - } - return result; - }; - - // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), - // we need this function. Return the position of the first occurrence of an - // item in an array, or -1 if the item is not included in the array. - // Delegates to **ECMAScript 5**'s native `indexOf` if available. - // If the array is large and already in sort order, pass `true` - // for **isSorted** to use binary search. - _.indexOf = function(array, item, isSorted) { - if (array == null) return -1; - var i = 0, l = array.length; - if (isSorted) { - if (typeof isSorted == 'number') { - i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); - } else { - i = _.sortedIndex(array, item); - return array[i] === item ? i : -1; - } - } - if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); - for (; i < l; i++) if (array[i] === item) return i; - return -1; - }; - - // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. - _.lastIndexOf = function(array, item, from) { - if (array == null) return -1; - var hasIndex = from != null; - if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { - return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); - } - var i = (hasIndex ? from : array.length); - while (i--) if (array[i] === item) return i; - return -1; - }; - - // Generate an integer Array containing an arithmetic progression. A port of - // the native Python `range()` function. See - // [the Python documentation](http://docs.python.org/library/functions.html#range). - _.range = function(start, stop, step) { - if (arguments.length <= 1) { - stop = start || 0; - start = 0; - } - step = arguments[2] || 1; - - var len = Math.max(Math.ceil((stop - start) / step), 0); - var idx = 0; - var range = new Array(len); - - while(idx < len) { - range[idx++] = start; - start += step; - } - - return range; - }; - - // Function (ahem) Functions - // ------------------ - - // Reusable constructor function for prototype setting. - var ctor = function(){}; - - // Create a function bound to a given object (assigning `this`, and arguments, - // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if - // available. - _.bind = function(func, context) { - var args, bound; - if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); - if (!_.isFunction(func)) throw new TypeError; - args = slice.call(arguments, 2); - return bound = function() { - if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); - ctor.prototype = func.prototype; - var self = new ctor; - ctor.prototype = null; - var result = func.apply(self, args.concat(slice.call(arguments))); - if (Object(result) === result) return result; - return self; - }; - }; - - // Partially apply a function by creating a version that has had some of its - // arguments pre-filled, without changing its dynamic `this` context. - _.partial = function(func) { - var args = slice.call(arguments, 1); - return function() { - return func.apply(this, args.concat(slice.call(arguments))); - }; - }; - - // Bind all of an object's methods to that object. Useful for ensuring that - // all callbacks defined on an object belong to it. - _.bindAll = function(obj) { - var funcs = slice.call(arguments, 1); - if (funcs.length === 0) throw new Error("bindAll must be passed function names"); - each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); - return obj; - }; - - // Memoize an expensive function by storing its results. - _.memoize = function(func, hasher) { - var memo = {}; - hasher || (hasher = _.identity); - return function() { - var key = hasher.apply(this, arguments); - return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); - }; - }; - - // Delays a function for the given number of milliseconds, and then calls - // it with the arguments supplied. - _.delay = function(func, wait) { - var args = slice.call(arguments, 2); - return setTimeout(function(){ return func.apply(null, args); }, wait); - }; - - // Defers a function, scheduling it to run after the current call stack has - // cleared. - _.defer = function(func) { - return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); - }; - - // Returns a function, that, when invoked, will only be triggered at most once - // during a given window of time. Normally, the throttled function will run - // as much as it can, without ever going more than once per `wait` duration; - // but if you'd like to disable the execution on the leading edge, pass - // `{leading: false}`. To disable execution on the trailing edge, ditto. - _.throttle = function(func, wait, options) { - var context, args, result; - var timeout = null; - var previous = 0; - options || (options = {}); - var later = function() { - previous = options.leading === false ? 0 : new Date; - timeout = null; - result = func.apply(context, args); - }; - return function() { - var now = new Date; - if (!previous && options.leading === false) previous = now; - var remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - } else if (!timeout && options.trailing !== false) { - timeout = setTimeout(later, remaining); - } - return result; - }; - }; - - // Returns a function, that, as long as it continues to be invoked, will not - // be triggered. The function will be called after it stops being called for - // N milliseconds. If `immediate` is passed, trigger the function on the - // leading edge, instead of the trailing. - _.debounce = function(func, wait, immediate) { - var result; - var timeout = null; - return function() { - var context = this, args = arguments; - var later = function() { - timeout = null; - if (!immediate) result = func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) result = func.apply(context, args); - return result; - }; - }; - - // Returns a function that will be executed at most one time, no matter how - // often you call it. Useful for lazy initialization. - _.once = function(func) { - var ran = false, memo; - return function() { - if (ran) return memo; - ran = true; - memo = func.apply(this, arguments); - func = null; - return memo; - }; - }; - - // Returns the first function passed as an argument to the second, - // allowing you to adjust arguments, run code before and after, and - // conditionally execute the original function. - _.wrap = function(func, wrapper) { - return function() { - var args = [func]; - push.apply(args, arguments); - return wrapper.apply(this, args); - }; - }; - - // Returns a function that is the composition of a list of functions, each - // consuming the return value of the function that follows. - _.compose = function() { - var funcs = arguments; - return function() { - var args = arguments; - for (var i = funcs.length - 1; i >= 0; i--) { - args = [funcs[i].apply(this, args)]; - } - return args[0]; - }; - }; - - // Returns a function that will only be executed after being called N times. - _.after = function(times, func) { - return function() { - if (--times < 1) { - return func.apply(this, arguments); - } - }; - }; - - // Object Functions - // ---------------- - - // Retrieve the names of an object's properties. - // Delegates to **ECMAScript 5**'s native `Object.keys` - _.keys = nativeKeys || function(obj) { - if (obj !== Object(obj)) throw new TypeError('Invalid object'); - var keys = []; - for (var key in obj) if (_.has(obj, key)) keys.push(key); - return keys; - }; - - // Retrieve the values of an object's properties. - _.values = function(obj) { - var values = []; - for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); - return values; - }; - - // Convert an object into a list of `[key, value]` pairs. - _.pairs = function(obj) { - var pairs = []; - for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); - return pairs; - }; - - // Invert the keys and values of an object. The values must be serializable. - _.invert = function(obj) { - var result = {}; - for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; - return result; - }; - - // Return a sorted list of the function names available on the object. - // Aliased as `methods` - _.functions = _.methods = function(obj) { - var names = []; - for (var key in obj) { - if (_.isFunction(obj[key])) names.push(key); - } - return names.sort(); - }; - - // Extend a given object with all the properties in passed-in object(s). - _.extend = function(obj) { - each(slice.call(arguments, 1), function(source) { - if (source) { - for (var prop in source) { - obj[prop] = source[prop]; - } - } - }); - return obj; - }; - - // Return a copy of the object only containing the whitelisted properties. - _.pick = function(obj) { - var copy = {}; - var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); - each(keys, function(key) { - if (key in obj) copy[key] = obj[key]; - }); - return copy; - }; - - // Return a copy of the object without the blacklisted properties. - _.omit = function(obj) { - var copy = {}; - var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); - for (var key in obj) { - if (!_.contains(keys, key)) copy[key] = obj[key]; - } - return copy; - }; - - // Fill in a given object with default properties. - _.defaults = function(obj) { - each(slice.call(arguments, 1), function(source) { - if (source) { - for (var prop in source) { - if (obj[prop] === void 0) obj[prop] = source[prop]; - } - } - }); - return obj; - }; - - // Create a (shallow-cloned) duplicate of an object. - _.clone = function(obj) { - if (!_.isObject(obj)) return obj; - return _.isArray(obj) ? obj.slice() : _.extend({}, obj); - }; - - // Invokes interceptor with the obj, and then returns obj. - // The primary purpose of this method is to "tap into" a method chain, in - // order to perform operations on intermediate results within the chain. - _.tap = function(obj, interceptor) { - interceptor(obj); - return obj; - }; - - // Internal recursive comparison function for `isEqual`. - var eq = function(a, b, aStack, bStack) { - // Identical objects are equal. `0 === -0`, but they aren't identical. - // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). - if (a === b) return a !== 0 || 1 / a == 1 / b; - // A strict comparison is necessary because `null == undefined`. - if (a == null || b == null) return a === b; - // Unwrap any wrapped objects. - if (a instanceof _) a = a._wrapped; - if (b instanceof _) b = b._wrapped; - // Compare `[[Class]]` names. - var className = toString.call(a); - if (className != toString.call(b)) return false; - switch (className) { - // Strings, numbers, dates, and booleans are compared by value. - case '[object String]': - // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is - // equivalent to `new String("5")`. - return a == String(b); - case '[object Number]': - // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for - // other numeric values. - return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); - case '[object Date]': - case '[object Boolean]': - // Coerce dates and booleans to numeric primitive values. Dates are compared by their - // millisecond representations. Note that invalid dates with millisecond representations - // of `NaN` are not equivalent. - return +a == +b; - // RegExps are compared by their source patterns and flags. - case '[object RegExp]': - return a.source == b.source && - a.global == b.global && - a.multiline == b.multiline && - a.ignoreCase == b.ignoreCase; - } - if (typeof a != 'object' || typeof b != 'object') return false; - // Assume equality for cyclic structures. The algorithm for detecting cyclic - // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. - var length = aStack.length; - while (length--) { - // Linear search. Performance is inversely proportional to the number of - // unique nested structures. - if (aStack[length] == a) return bStack[length] == b; - } - // Objects with different constructors are not equivalent, but `Object`s - // from different frames are. - var aCtor = a.constructor, bCtor = b.constructor; - if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && - _.isFunction(bCtor) && (bCtor instanceof bCtor))) { - return false; - } - // Add the first object to the stack of traversed objects. - aStack.push(a); - bStack.push(b); - var size = 0, result = true; - // Recursively compare objects and arrays. - if (className == '[object Array]') { - // Compare array lengths to determine if a deep comparison is necessary. - size = a.length; - result = size == b.length; - if (result) { - // Deep compare the contents, ignoring non-numeric properties. - while (size--) { - if (!(result = eq(a[size], b[size], aStack, bStack))) break; - } - } - } else { - // Deep compare objects. - for (var key in a) { - if (_.has(a, key)) { - // Count the expected number of properties. - size++; - // Deep compare each member. - if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; - } - } - // Ensure that both objects contain the same number of properties. - if (result) { - for (key in b) { - if (_.has(b, key) && !(size--)) break; - } - result = !size; - } - } - // Remove the first object from the stack of traversed objects. - aStack.pop(); - bStack.pop(); - return result; - }; - - // Perform a deep comparison to check if two objects are equal. - _.isEqual = function(a, b) { - return eq(a, b, [], []); - }; - - // Is a given array, string, or object empty? - // An "empty" object has no enumerable own-properties. - _.isEmpty = function(obj) { - if (obj == null) return true; - if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; - for (var key in obj) if (_.has(obj, key)) return false; - return true; - }; - - // Is a given value a DOM element? - _.isElement = function(obj) { - return !!(obj && obj.nodeType === 1); - }; - - // Is a given value an array? - // Delegates to ECMA5's native Array.isArray - _.isArray = nativeIsArray || function(obj) { - return toString.call(obj) == '[object Array]'; - }; - - // Is a given variable an object? - _.isObject = function(obj) { - return obj === Object(obj); - }; - - // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. - each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { - _['is' + name] = function(obj) { - return toString.call(obj) == '[object ' + name + ']'; - }; - }); - - // Define a fallback version of the method in browsers (ahem, IE), where - // there isn't any inspectable "Arguments" type. - if (!_.isArguments(arguments)) { - _.isArguments = function(obj) { - return !!(obj && _.has(obj, 'callee')); - }; - } - - // Optimize `isFunction` if appropriate. - if (typeof (/./) !== 'function') { - _.isFunction = function(obj) { - return typeof obj === 'function'; - }; - } - - // Is a given object a finite number? - _.isFinite = function(obj) { - return isFinite(obj) && !isNaN(parseFloat(obj)); - }; - - // Is the given value `NaN`? (NaN is the only number which does not equal itself). - _.isNaN = function(obj) { - return _.isNumber(obj) && obj != +obj; - }; - - // Is a given value a boolean? - _.isBoolean = function(obj) { - return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; - }; - - // Is a given value equal to null? - _.isNull = function(obj) { - return obj === null; - }; - - // Is a given variable undefined? - _.isUndefined = function(obj) { - return obj === void 0; - }; - - // Shortcut function for checking if an object has a given property directly - // on itself (in other words, not on a prototype). - _.has = function(obj, key) { - return hasOwnProperty.call(obj, key); - }; - - // Utility Functions - // ----------------- - - // Run Underscore.js in *noConflict* mode, returning the `_` variable to its - // previous owner. Returns a reference to the Underscore object. - _.noConflict = function() { - root._ = previousUnderscore; - return this; - }; - - // Keep the identity function around for default iterators. - _.identity = function(value) { - return value; - }; - - // Run a function **n** times. - _.times = function(n, iterator, context) { - var accum = Array(Math.max(0, n)); - for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); - return accum; - }; - - // Return a random integer between min and max (inclusive). - _.random = function(min, max) { - if (max == null) { - max = min; - min = 0; - } - return min + Math.floor(Math.random() * (max - min + 1)); - }; - - // List of HTML entities for escaping. - var entityMap = { - escape: { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '/': '/' - } - }; - entityMap.unescape = _.invert(entityMap.escape); - - // Regexes containing the keys and values listed immediately above. - var entityRegexes = { - escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), - unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') - }; - - // Functions for escaping and unescaping strings to/from HTML interpolation. - _.each(['escape', 'unescape'], function(method) { - _[method] = function(string) { - if (string == null) return ''; - return ('' + string).replace(entityRegexes[method], function(match) { - return entityMap[method][match]; - }); - }; - }); - - // If the value of the named `property` is a function then invoke it with the - // `object` as context; otherwise, return it. - _.result = function(object, property) { - if (object == null) return void 0; - var value = object[property]; - return _.isFunction(value) ? value.call(object) : value; - }; - - // Add your own custom functions to the Underscore object. - _.mixin = function(obj) { - each(_.functions(obj), function(name){ - var func = _[name] = obj[name]; - _.prototype[name] = function() { - var args = [this._wrapped]; - push.apply(args, arguments); - return result.call(this, func.apply(_, args)); - }; - }); - }; - - // Generate a unique integer id (unique within the entire client session). - // Useful for temporary DOM ids. - var idCounter = 0; - _.uniqueId = function(prefix) { - var id = ++idCounter + ''; - return prefix ? prefix + id : id; - }; - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /(.)^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - var render; - settings = _.defaults({}, settings, _.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); - - if (escape) { - source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } - if (interpolate) { - source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } - if (evaluate) { - source += "';\n" + evaluate + "\n__p+='"; - } - index = offset + match.length; - return match; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + "return __p;\n"; - - try { - render = new Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } - - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; - - return template; - }; - - // Add a "chain" function, which will delegate to the wrapper. - _.chain = function(obj) { - return _(obj).chain(); - }; - - // OOP - // --------------- - // If Underscore is called as a function, it returns a wrapped object that - // can be used OO-style. This wrapper holds altered versions of all the - // underscore functions. Wrapped objects may be chained. - - // Helper function to continue chaining intermediate results. - var result = function(obj) { - return this._chain ? _(obj).chain() : obj; - }; - - // Add all of the Underscore functions to the wrapper object. - _.mixin(_); - - // Add all mutator Array functions to the wrapper. - each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { - var method = ArrayProto[name]; - _.prototype[name] = function() { - var obj = this._wrapped; - method.apply(obj, arguments); - if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; - return result.call(this, obj); - }; - }); - - // Add all accessor Array functions to the wrapper. - each(['concat', 'join', 'slice'], function(name) { - var method = ArrayProto[name]; - _.prototype[name] = function() { - return result.call(this, method.apply(this._wrapped, arguments)); - }; - }); - - _.extend(_.prototype, { - - // Start chaining a wrapped Underscore object. - chain: function() { - this._chain = true; - return this; - }, - - // Extracts the result from a wrapped and chained object. - value: function() { - return this._wrapped; - } - - }); - -}).call(this); - -/* - * DSP.js - a comprehensive digital signal processing library for javascript - * - * Created by Corban Brook on 2010-01-01. - * Copyright 2010 Corban Brook. All rights reserved. - * - */ - -// Fourier Transform Module used by DFT, FFT, RFFT -function FourierTransform(bufferSize, sampleRate) { - this.bufferSize = bufferSize; - this.sampleRate = sampleRate; - this.bandwidth = 2 / bufferSize * sampleRate / 2; - - this.spectrum = new Float32Array(bufferSize/2); - this.real = new Float32Array(bufferSize); - this.imag = new Float32Array(bufferSize); - - this.peakBand = 0; - this.peak = 0; - - /** - * Calculates the *middle* frequency of an FFT band. - * - * @param {Number} index The index of the FFT band. - * - * @returns The middle frequency in Hz. - */ - this.getBandFrequency = function(index) { - return this.bandwidth * index + this.bandwidth / 2; - }; - - this.calculateSpectrum = function() { - var spectrum = this.spectrum, - real = this.real, - imag = this.imag, - bSi = 2 / this.bufferSize, - sqrt = Math.sqrt, - rval, - ival, - mag; - - for (var i = 0, N = bufferSize/2; i < N; i++) { - rval = real[i]; - ival = imag[i]; - mag = bSi * sqrt(rval * rval + ival * ival); - - if (mag > this.peak) { - this.peakBand = i; - this.peak = mag; - } - - spectrum[i] = mag; - } - }; -} - -/** - * FFT is a class for calculating the Discrete Fourier Transform of a signal - * with the Fast Fourier Transform algorithm. - * - * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2 - * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100) - * - * @constructor - */ -function FFT(bufferSize, sampleRate) { - FourierTransform.call(this, bufferSize, sampleRate); - - this.reverseTable = new Uint32Array(bufferSize); - - var limit = 1; - var bit = bufferSize >> 1; - - var i; - - while (limit < bufferSize) { - for (i = 0; i < limit; i++) { - this.reverseTable[i + limit] = this.reverseTable[i] + bit; - } - - limit = limit << 1; - bit = bit >> 1; - } - - this.sinTable = new Float32Array(bufferSize); - this.cosTable = new Float32Array(bufferSize); - - for (i = 0; i < bufferSize; i++) { - this.sinTable[i] = Math.sin(-Math.PI/i); - this.cosTable[i] = Math.cos(-Math.PI/i); - } -} - -/** - * Performs a forward transform on the sample buffer. - * Converts a time domain signal to frequency domain spectra. - * - * @param {Array} buffer The sample buffer. Buffer Length must be power of 2 - * - * @returns The frequency spectrum array - */ -FFT.prototype.forward = function(buffer) { - // Locally scope variables for speed up - var bufferSize = this.bufferSize, - cosTable = this.cosTable, - sinTable = this.sinTable, - reverseTable = this.reverseTable, - real = this.real, - imag = this.imag, - spectrum = this.spectrum; - - var k = Math.floor(Math.log(bufferSize) / Math.LN2); - - if (Math.pow(2, k) !== bufferSize) { throw "Invalid buffer size, must be a power of 2."; } - if (bufferSize !== buffer.length) { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; } - - var halfSize = 1, - phaseShiftStepReal, - phaseShiftStepImag, - currentPhaseShiftReal, - currentPhaseShiftImag, - off, - tr, - ti, - tmpReal, - i; - - for (i = 0; i < bufferSize; i++) { - real[i] = buffer[reverseTable[i]]; - imag[i] = 0; - } - - while (halfSize < bufferSize) { - //phaseShiftStepReal = Math.cos(-Math.PI/halfSize); - //phaseShiftStepImag = Math.sin(-Math.PI/halfSize); - phaseShiftStepReal = cosTable[halfSize]; - phaseShiftStepImag = sinTable[halfSize]; - - currentPhaseShiftReal = 1; - currentPhaseShiftImag = 0; - - for (var fftStep = 0; fftStep < halfSize; fftStep++) { - i = fftStep; - - while (i < bufferSize) { - off = i + halfSize; - tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]); - ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]); - - real[off] = real[i] - tr; - imag[off] = imag[i] - ti; - real[i] += tr; - imag[i] += ti; - - i += halfSize << 1; - } - - tmpReal = currentPhaseShiftReal; - currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag); - currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal); - } - - halfSize = halfSize << 1; - } - - return this.calculateSpectrum(); -}; - -/* - * Dancer.js (c) 2012 Jordan Santell - * MIT License - * http://github.com/jsantell/dancer.js - * - * v0.3.1 - */ - -(function() { - - var Dancer = function () { - this.audioAdapter = Dancer._getAdapter( this ); - this.events = {}; - this.sections = []; - this.bind( 'update', update ); - }; - - Dancer.adapters = {}; - - Dancer.prototype = { - - load : function ( source ) { - var path; - - // Loading an Audio element - if ( source instanceof HTMLElement ) { - this.source = source; - if ( Dancer.isSupported() === 'flash' ) { - this.source = { src: Dancer._getMP3SrcFromAudio( source ) }; - } - - // Loading an object with src, [codecs] - } else { - this.source = window.Audio ? new Audio() : {}; - this.source.src = Dancer._makeSupportedPath( source.src, source.codecs ); - } - - this.audio = this.audioAdapter.load( this.source ); - return this; - }, - - /* Controls */ - - play : function () { - this.audioAdapter.play(); - return this; - }, - - pause : function () { - this.audioAdapter.pause(); - return this; - }, - - setVolume : function ( volume ) { - this.audioAdapter.setVolume( volume ); - return this; - }, - - - /* Actions */ - - createKick : function ( options ) { - return new Dancer.Kick( this, options ); - }, - - bind : function ( name, callback ) { - if ( !this.events[ name ] ) { - this.events[ name ] = []; - } - this.events[ name ].push( callback ); - return this; - }, - - unbind : function ( name ) { - if ( this.events[ name ] ) { - delete this.events[ name ]; - } - return this; - }, - - trigger : function ( name ) { - var _this = this; - if ( this.events[ name ] ) { - this.events[ name ].forEach(function( callback ) { - callback.call( _this ); - }); - } - return this; - }, - - - /* Getters */ - - getVolume : function () { - return this.audioAdapter.getVolume(); - }, - - getProgress : function () { - return this.audioAdapter.getProgress(); - }, - - getTime : function () { - return this.audioAdapter.getTime(); - }, - - // Returns the magnitude of a frequency or average over a range of frequencies - getFrequency : function ( freq, endFreq ) { - var sum = 0; - if ( endFreq !== undefined ) { - for ( var i = freq; i <= endFreq; i++ ) { - sum += this.getSpectrum()[ i ]; - } - return sum / ( endFreq - freq + 1 ); - } else { - return this.getSpectrum()[ freq ]; - } - }, - - getWaveform : function () { - return this.audioAdapter.getWaveform(); - }, - - getSpectrum : function () { - return this.audioAdapter.getSpectrum(); - }, - - isLoaded : function () { - return this.audioAdapter.isLoaded; - }, - - isPlaying : function () { - return this.audioAdapter.isPlaying; - }, - - - /* Sections */ - - after : function ( time, callback ) { - var _this = this; - this.sections.push({ - condition : function () { - return _this.getTime() > time; - }, - callback : callback - }); - return this; - }, - - before : function ( time, callback ) { - var _this = this; - this.sections.push({ - condition : function () { - return _this.getTime() < time; - }, - callback : callback - }); - return this; - }, - - between : function ( startTime, endTime, callback ) { - var _this = this; - this.sections.push({ - condition : function () { - return _this.getTime() > startTime && _this.getTime() < endTime; - }, - callback : callback - }); - return this; - }, - - onceAt : function ( time, callback ) { - var - _this = this, - thisSection = null; - this.sections.push({ - condition : function () { - return _this.getTime() > time && !this.called; - }, - callback : function () { - callback.call( this ); - thisSection.called = true; - }, - called : false - }); - // Baking the section in the closure due to callback's this being the dancer instance - thisSection = this.sections[ this.sections.length - 1 ]; - return this; - } - }; - - function update () { - for ( var i in this.sections ) { - if ( this.sections[ i ].condition() ) - this.sections[ i ].callback.call( this ); - } - } - - window.Dancer = Dancer; -})(); - -(function ( Dancer ) { - - var CODECS = { - 'mp3' : 'audio/mpeg;', - 'ogg' : 'audio/ogg; codecs="vorbis"', - 'wav' : 'audio/wav; codecs="1"', - 'aac' : 'audio/mp4; codecs="mp4a.40.2"' - }, - audioEl = document.createElement( 'audio' ); - - Dancer.options = {}; - - Dancer.setOptions = function ( o ) { - for ( var option in o ) { - if ( o.hasOwnProperty( option ) ) { - Dancer.options[ option ] = o[ option ]; - } - } - }; - - Dancer.isSupported = function () { - if ( !window.Float32Array || !window.Uint32Array ) { - return null; - } else if ( !isUnsupportedSafari() && ( window.AudioContext || window.webkitAudioContext )) { - return 'webaudio'; - } else if ( audioEl && audioEl.mozSetup ) { - return 'audiodata'; - } else if ( FlashDetect.versionAtLeast( 9 ) ) { - return 'flash'; - } else { - return ''; - } - }; - - Dancer.canPlay = function ( type ) { - var canPlay = audioEl.canPlayType; - return !!( - Dancer.isSupported() === 'flash' ? - type.toLowerCase() === 'mp3' : - audioEl.canPlayType && - audioEl.canPlayType( CODECS[ type.toLowerCase() ] ).replace( /no/, '')); - }; - - Dancer.addPlugin = function ( name, fn ) { - if ( Dancer.prototype[ name ] === undefined ) { - Dancer.prototype[ name ] = fn; - } - }; - - Dancer._makeSupportedPath = function ( source, codecs ) { - if ( !codecs ) { return source; } - - for ( var i = 0; i < codecs.length; i++ ) { - if ( Dancer.canPlay( codecs[ i ] ) ) { - return source + '.' + codecs[ i ]; - } - } - return source; - }; - - Dancer._getAdapter = function ( instance ) { - switch ( Dancer.isSupported() ) { - case 'webaudio': - return new Dancer.adapters.webkit( instance ); - case 'audiodata': - return new Dancer.adapters.moz( instance ); - case 'flash': - return new Dancer.adapters.flash( instance ); - default: - return null; - } - }; - - Dancer._getMP3SrcFromAudio = function ( audioEl ) { - var sources = audioEl.children; - if ( audioEl.src ) { return audioEl.src; } - for ( var i = sources.length; i--; ) { - if (( sources[ i ].type || '' ).match( /audio\/mpeg/ )) return sources[ i ].src; - } - return null; - }; - - // Browser detection is lame, but Safari 6 has Web Audio API, - // but does not support processing audio from a Media Element Source - // https://gist.github.com/3265344 - function isUnsupportedSafari () { - var - isApple = !!( navigator.vendor || '' ).match( /Apple/ ), - version = navigator.userAgent.match( /Version\/([^ ]*)/ ); - version = version ? parseFloat( version[ 1 ] ) : 0; - return isApple && version <= 6; - } - -})( window.Dancer ); - -(function ( undefined ) { - var Kick = function ( dancer, o ) { - o = o || {}; - this.dancer = dancer; - this.frequency = o.frequency !== undefined ? o.frequency : [ 0, 10 ]; - this.threshold = o.threshold !== undefined ? o.threshold : 0.3; - this.decay = o.decay !== undefined ? o.decay : 0.02; - this.onKick = o.onKick; - this.offKick = o.offKick; - this.isOn = false; - this.currentThreshold = this.threshold; - - var _this = this; - this.dancer.bind( 'update', function () { - _this.onUpdate(); - }); - }; - - Kick.prototype = { - on : function () { - this.isOn = true; - return this; - }, - off : function () { - this.isOn = false; - return this; - }, - - set : function ( o ) { - o = o || {}; - this.frequency = o.frequency !== undefined ? o.frequency : this.frequency; - this.threshold = o.threshold !== undefined ? o.threshold : this.threshold; - this.decay = o.decay !== undefined ? o.decay : this.decay; - this.onKick = o.onKick || this.onKick; - this.offKick = o.offKick || this.offKick; - }, - - onUpdate : function () { - if ( !this.isOn ) { return; } - var magnitude = this.maxAmplitude( this.frequency ); - if ( magnitude >= this.currentThreshold && - magnitude >= this.threshold ) { - this.currentThreshold = magnitude; - this.onKick && this.onKick.call( this.dancer, magnitude ); - } else { - this.offKick && this.offKick.call( this.dancer, magnitude ); - this.currentThreshold -= this.decay; - } - }, - maxAmplitude : function ( frequency ) { - var - max = 0, - fft = this.dancer.getSpectrum(); - - // Sloppy array check - if ( !frequency.length ) { - return frequency < fft.length ? - fft[ ~~frequency ] : - null; - } - - for ( var i = frequency[ 0 ], l = frequency[ 1 ]; i <= l; i++ ) { - if ( fft[ i ] > max ) { max = fft[ i ]; } - } - return max; - } - }; - - window.Dancer.Kick = Kick; -})(); - -(function() { - var - SAMPLE_SIZE = 2048, - SAMPLE_RATE = 44100; - - var adapter = function ( dancer ) { - this.dancer = dancer; - this.audio = new Audio(); - this.context = window.AudioContext ? - new window.AudioContext() : - new window.webkitAudioContext(); - }; - - adapter.prototype = { - - load : function ( _source ) { - var _this = this; - this.audio = _source; - - this.isLoaded = false; - this.progress = 0; - - this.proc = this.context.createJavaScriptNode( SAMPLE_SIZE / 2, 1, 1 ); - this.proc.onaudioprocess = function ( e ) { - _this.update.call( _this, e ); - }; - this.gain = this.context.createGainNode(); - - this.fft = new FFT( SAMPLE_SIZE / 2, SAMPLE_RATE ); - this.signal = new Float32Array( SAMPLE_SIZE / 2 ); - - if ( this.audio.readyState < 3 ) { - this.audio.addEventListener( 'canplay', function () { - connectContext.call( _this ); - }); - } else { - connectContext.call( _this ); - } - - this.audio.addEventListener( 'progress', function ( e ) { - if ( e.currentTarget.duration ) { - _this.progress = e.currentTarget.seekable.end( 0 ) / e.currentTarget.duration; - } - }); - - return this.audio; - }, - - play : function () { - this.audio.play(); - this.isPlaying = true; - }, - - pause : function () { - this.audio.pause(); - this.isPlaying = false; - }, - - setVolume : function ( volume ) { - this.gain.gain.value = volume; - }, - - getVolume : function () { - return this.gain.gain.value; - }, - - getProgress : function() { - return this.progress; - }, - - getWaveform : function () { - return this.signal; - }, - - getSpectrum : function () { - return this.fft.spectrum; - }, - - getTime : function () { - return this.audio.currentTime; - }, - - update : function ( e ) { - if ( !this.isPlaying || !this.isLoaded ) return; - - var - buffers = [], - channels = e.inputBuffer.numberOfChannels, - resolution = SAMPLE_SIZE / channels, - i, - sum = function ( prev, curr ) { - return prev[ i ] + curr[ i ]; - }; - - for ( i = channels; i--; ) { - buffers.push( e.inputBuffer.getChannelData( i ) ); - } - - for ( i = 0; i < resolution; i++ ) { - this.signal[ i ] = channels > 1 ? - buffers.reduce( sum( prev, curr ) ) / channels : - buffers[ 0 ][ i ]; - } - - this.fft.forward( this.signal ); - this.dancer.trigger( 'update' ); - } - }; - - function connectContext () { - this.source = this.context.createMediaElementSource( this.audio ); - this.source.connect( this.proc ); - this.source.connect( this.gain ); - this.gain.connect( this.context.destination ); - this.proc.connect( this.context.destination ); - - this.isLoaded = true; - this.progress = 1; - this.dancer.trigger( 'loaded' ); - } - - Dancer.adapters.webkit = adapter; - -})(); - -(function() { - - var adapter = function ( dancer ) { - this.dancer = dancer; - this.audio = new Audio(); - }; - - adapter.prototype = { - - load : function ( _source ) { - var _this = this; - this.audio = _source; - - this.isLoaded = false; - this.progress = 0; - - if ( this.audio.readyState < 3 ) { - this.audio.addEventListener( 'loadedmetadata', function () { - getMetadata.call( _this ); - }, false); - } else { - getMetadata.call( _this ); - } - - this.audio.addEventListener( 'MozAudioAvailable', function ( e ) { - _this.update( e ); - }, false); - - this.audio.addEventListener( 'progress', function ( e ) { - if ( e.currentTarget.duration ) { - _this.progress = e.currentTarget.seekable.end( 0 ) / e.currentTarget.duration; - } - }, false); - - return this.audio; - }, - - play : function () { - this.audio.play(); - this.isPlaying = true; - }, - - pause : function () { - this.audio.pause(); - this.isPlaying = false; - }, - - setVolume : function ( volume ) { - this.audio.volume = volume; - }, - - getVolume : function () { - return this.audio.volume; - }, - - getProgress : function () { - return this.progress; - }, - - getWaveform : function () { - return this.signal; - }, - - getSpectrum : function () { - return this.fft.spectrum; - }, - - getTime : function () { - return this.audio.currentTime; - }, - - update : function ( e ) { - if ( !this.isPlaying || !this.isLoaded ) return; - - for ( var i = 0, j = this.fbLength / 2; i < j; i++ ) { - this.signal[ i ] = ( e.frameBuffer[ 2 * i ] + e.frameBuffer[ 2 * i + 1 ] ) / 2; - } - - this.fft.forward( this.signal ); - this.dancer.trigger( 'update' ); - } - }; - - function getMetadata () { - this.fbLength = this.audio.mozFrameBufferLength; - this.channels = this.audio.mozChannels; - this.rate = this.audio.mozSampleRate; - this.fft = new FFT( this.fbLength / this.channels, this.rate ); - this.signal = new Float32Array( this.fbLength / this.channels ); - this.isLoaded = true; - this.progress = 1; - this.dancer.trigger( 'loaded' ); - } - - Dancer.adapters.moz = adapter; - -})(); - -(function() { - var - SAMPLE_SIZE = 1024, - SAMPLE_RATE = 44100, - smLoaded = false, - smLoading = false, - CONVERSION_COEFFICIENT = 0.93; - - var adapter = function ( dancer ) { - this.dancer = dancer; - this.wave_L = []; - this.wave_R = []; - this.spectrum = []; - window.SM2_DEFER = true; - }; - - adapter.prototype = { - // `source` can be either an Audio element, if supported, or an object - // either way, the path is stored in the `src` property - load : function ( source ) { - var _this = this; - this.path = source ? source.src : this.path; - - this.isLoaded = false; - this.progress = 0; - - !window.soundManager && !smLoading && loadSM.call( this ); - - if ( window.soundManager ) { - this.audio = soundManager.createSound({ - id : 'dancer' + Math.random() + '', - url : this.path, - stream : true, - autoPlay : false, - autoLoad : true, - whileplaying : function () { - _this.update(); - }, - whileloading : function () { - _this.progress = this.bytesLoaded / this.bytesTotal; - }, - onload : function () { - _this.fft = new FFT( SAMPLE_SIZE, SAMPLE_RATE ); - _this.signal = new Float32Array( SAMPLE_SIZE ); - _this.waveform = new Float32Array( SAMPLE_SIZE ); - _this.isLoaded = true; - _this.progress = 1; - _this.dancer.trigger( 'loaded' ); - } - }); - this.dancer.audio = this.audio; - } - - // Returns audio if SM already loaded -- otherwise, - // sets dancer instance's audio property after load - return this.audio; - }, - - play : function () { - this.audio.play(); - this.isPlaying = true; - }, - - pause : function () { - this.audio.pause(); - this.isPlaying = false; - }, - - setVolume : function ( volume ) { - this.audio.setVolume( volume * 100 ); - }, - - getVolume : function () { - return this.audio.volume / 100; - }, - - getProgress : function () { - return this.progress; - }, - - getWaveform : function () { - return this.waveform; - }, - - getSpectrum : function () { - return this.fft.spectrum; - }, - - getTime : function () { - return this.audio.position / 1000; - }, - - update : function () { - if ( !this.isPlaying && !this.isLoaded ) return; - this.wave_L = this.audio.waveformData.left; - this.wave_R = this.audio.waveformData.right; - var avg; - for ( var i = 0, j = this.wave_L.length; i < j; i++ ) { - avg = parseFloat(this.wave_L[ i ]) + parseFloat(this.wave_R[ i ]); - this.waveform[ 2 * i ] = avg / 2; - this.waveform[ i * 2 + 1 ] = avg / 2; - this.signal[ 2 * i ] = avg * CONVERSION_COEFFICIENT; - this.signal[ i * 2 + 1 ] = avg * CONVERSION_COEFFICIENT; - } - - this.fft.forward( this.signal ); - this.dancer.trigger( 'update' ); - } - }; - - function loadSM () { - var adapter = this; - smLoading = true; - loadScript( Dancer.options.flashJS, function () { - soundManager = new SoundManager(); - soundManager.flashVersion = 9; - soundManager.flash9Options.useWaveformData = true; - soundManager.useWaveformData = true; - soundManager.useHighPerformance = true; - soundManager.useFastPolling = true; - soundManager.multiShot = false; - soundManager.debugMode = false; - soundManager.debugFlash = false; - soundManager.url = Dancer.options.flashSWF; - soundManager.onready(function () { - smLoaded = true; - adapter.load(); - }); - soundManager.ontimeout(function(){ - console.error( 'Error loading SoundManager2.swf' ); - }); - soundManager.beginDelayedInit(); - }); - } - - function loadScript ( url, callback ) { - var - script = document.createElement( 'script' ), - appender = document.getElementsByTagName( 'script' )[0]; - script.type = 'text/javascript'; - script.src = url; - script.onload = callback; - appender.parentNode.insertBefore( script, appender ); - } - - Dancer.adapters.flash = adapter; - -})(); - -/* - * DSP.js - a comprehensive digital signal processing library for javascript - * - * Created by Corban Brook on 2010-01-01. - * Copyright 2010 Corban Brook. All rights reserved. - * - */ - -// Fourier Transform Module used by DFT, FFT, RFFT -function FourierTransform(bufferSize, sampleRate) { - this.bufferSize = bufferSize; - this.sampleRate = sampleRate; - this.bandwidth = 2 / bufferSize * sampleRate / 2; - - this.spectrum = new Float32Array(bufferSize/2); - this.real = new Float32Array(bufferSize); - this.imag = new Float32Array(bufferSize); - - this.peakBand = 0; - this.peak = 0; - - /** - * Calculates the *middle* frequency of an FFT band. - * - * @param {Number} index The index of the FFT band. - * - * @returns The middle frequency in Hz. - */ - this.getBandFrequency = function(index) { - return this.bandwidth * index + this.bandwidth / 2; - }; - - this.calculateSpectrum = function() { - var spectrum = this.spectrum, - real = this.real, - imag = this.imag, - bSi = 2 / this.bufferSize, - sqrt = Math.sqrt, - rval, - ival, - mag; - - for (var i = 0, N = bufferSize/2; i < N; i++) { - rval = real[i]; - ival = imag[i]; - mag = bSi * sqrt(rval * rval + ival * ival); - - if (mag > this.peak) { - this.peakBand = i; - this.peak = mag; - } - - spectrum[i] = mag; - } - }; -} - -/** - * FFT is a class for calculating the Discrete Fourier Transform of a signal - * with the Fast Fourier Transform algorithm. - * - * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2 - * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100) - * - * @constructor - */ -function FFT(bufferSize, sampleRate) { - FourierTransform.call(this, bufferSize, sampleRate); - - this.reverseTable = new Uint32Array(bufferSize); - - var limit = 1; - var bit = bufferSize >> 1; - - var i; - - while (limit < bufferSize) { - for (i = 0; i < limit; i++) { - this.reverseTable[i + limit] = this.reverseTable[i] + bit; - } - - limit = limit << 1; - bit = bit >> 1; - } - - this.sinTable = new Float32Array(bufferSize); - this.cosTable = new Float32Array(bufferSize); - - for (i = 0; i < bufferSize; i++) { - this.sinTable[i] = Math.sin(-Math.PI/i); - this.cosTable[i] = Math.cos(-Math.PI/i); - } -} - -/** - * Performs a forward transform on the sample buffer. - * Converts a time domain signal to frequency domain spectra. - * - * @param {Array} buffer The sample buffer. Buffer Length must be power of 2 - * - * @returns The frequency spectrum array - */ -FFT.prototype.forward = function(buffer) { - // Locally scope variables for speed up - var bufferSize = this.bufferSize, - cosTable = this.cosTable, - sinTable = this.sinTable, - reverseTable = this.reverseTable, - real = this.real, - imag = this.imag, - spectrum = this.spectrum; - - var k = Math.floor(Math.log(bufferSize) / Math.LN2); - - if (Math.pow(2, k) !== bufferSize) { throw "Invalid buffer size, must be a power of 2."; } - if (bufferSize !== buffer.length) { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; } - - var halfSize = 1, - phaseShiftStepReal, - phaseShiftStepImag, - currentPhaseShiftReal, - currentPhaseShiftImag, - off, - tr, - ti, - tmpReal, - i; - - for (i = 0; i < bufferSize; i++) { - real[i] = buffer[reverseTable[i]]; - imag[i] = 0; - } - - while (halfSize < bufferSize) { - //phaseShiftStepReal = Math.cos(-Math.PI/halfSize); - //phaseShiftStepImag = Math.sin(-Math.PI/halfSize); - phaseShiftStepReal = cosTable[halfSize]; - phaseShiftStepImag = sinTable[halfSize]; - - currentPhaseShiftReal = 1; - currentPhaseShiftImag = 0; - - for (var fftStep = 0; fftStep < halfSize; fftStep++) { - i = fftStep; - - while (i < bufferSize) { - off = i + halfSize; - tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]); - ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]); - - real[off] = real[i] - tr; - imag[off] = imag[i] - ti; - real[i] += tr; - imag[i] += ti; - - i += halfSize << 1; - } - - tmpReal = currentPhaseShiftReal; - currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag); - currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal); - } - - halfSize = halfSize << 1; - } - - return this.calculateSpectrum(); -}; - -/* -Copyright (c) Copyright (c) 2007, Carl S. Yestrau All rights reserved. -Code licensed under the BSD License: http://www.featureblend.com/license.txt -Version: 1.0.4 -*/ -var FlashDetect = new function(){ - var self = this; - self.installed = false; - self.raw = ""; - self.major = -1; - self.minor = -1; - self.revision = -1; - self.revisionStr = ""; - var activeXDetectRules = [ - { - "name":"ShockwaveFlash.ShockwaveFlash.7", - "version":function(obj){ - return getActiveXVersion(obj); - } - }, - { - "name":"ShockwaveFlash.ShockwaveFlash.6", - "version":function(obj){ - var version = "6,0,21"; - try{ - obj.AllowScriptAccess = "always"; - version = getActiveXVersion(obj); - }catch(err){} - return version; - } - }, - { - "name":"ShockwaveFlash.ShockwaveFlash", - "version":function(obj){ - return getActiveXVersion(obj); - } - } - ]; - /** - * Extract the ActiveX version of the plugin. - * - * @param {Object} The flash ActiveX object. - * @type String - */ - var getActiveXVersion = function(activeXObj){ - var version = -1; - try{ - version = activeXObj.GetVariable("$version"); - }catch(err){} - return version; - }; - /** - * Try and retrieve an ActiveX object having a specified name. - * - * @param {String} name The ActiveX object name lookup. - * @return One of ActiveX object or a simple object having an attribute of activeXError with a value of true. - * @type Object - */ - var getActiveXObject = function(name){ - var obj = -1; - try{ - obj = new ActiveXObject(name); - }catch(err){ - obj = {activeXError:true}; - } - return obj; - }; - /** - * Parse an ActiveX $version string into an object. - * - * @param {String} str The ActiveX Object GetVariable($version) return value. - * @return An object having raw, major, minor, revision and revisionStr attributes. - * @type Object - */ - var parseActiveXVersion = function(str){ - var versionArray = str.split(",");//replace with regex - return { - "raw":str, - "major":parseInt(versionArray[0].split(" ")[1], 10), - "minor":parseInt(versionArray[1], 10), - "revision":parseInt(versionArray[2], 10), - "revisionStr":versionArray[2] - }; - }; - /** - * Parse a standard enabledPlugin.description into an object. - * - * @param {String} str The enabledPlugin.description value. - * @return An object having raw, major, minor, revision and revisionStr attributes. - * @type Object - */ - var parseStandardVersion = function(str){ - var descParts = str.split(/ +/); - var majorMinor = descParts[2].split(/\./); - var revisionStr = descParts[3]; - return { - "raw":str, - "major":parseInt(majorMinor[0], 10), - "minor":parseInt(majorMinor[1], 10), - "revisionStr":revisionStr, - "revision":parseRevisionStrToInt(revisionStr) - }; - }; - /** - * Parse the plugin revision string into an integer. - * - * @param {String} The revision in string format. - * @type Number - */ - var parseRevisionStrToInt = function(str){ - return parseInt(str.replace(/[a-zA-Z]/g, ""), 10) || self.revision; - }; - /** - * Is the major version greater than or equal to a specified version. - * - * @param {Number} version The minimum required major version. - * @type Boolean - */ - self.majorAtLeast = function(version){ - return self.major >= version; - }; - /** - * Is the minor version greater than or equal to a specified version. - * - * @param {Number} version The minimum required minor version. - * @type Boolean - */ - self.minorAtLeast = function(version){ - return self.minor >= version; - }; - /** - * Is the revision version greater than or equal to a specified version. - * - * @param {Number} version The minimum required revision version. - * @type Boolean - */ - self.revisionAtLeast = function(version){ - return self.revision >= version; - }; - /** - * Is the version greater than or equal to a specified major, minor and revision. - * - * @param {Number} major The minimum required major version. - * @param {Number} (Optional) minor The minimum required minor version. - * @param {Number} (Optional) revision The minimum required revision version. - * @type Boolean - */ - self.versionAtLeast = function(major){ - var properties = [self.major, self.minor, self.revision]; - var len = Math.min(properties.length, arguments.length); - for(i=0; i=arguments[i]){ - if(i+10){ - var type = 'application/x-shockwave-flash'; - var mimeTypes = navigator.mimeTypes; - if(mimeTypes && mimeTypes[type] && mimeTypes[type].enabledPlugin && mimeTypes[type].enabledPlugin.description){ - var version = mimeTypes[type].enabledPlugin.description; - var versionObj = parseStandardVersion(version); - self.raw = versionObj.raw; - self.major = versionObj.major; - self.minor = versionObj.minor; - self.revisionStr = versionObj.revisionStr; - self.revision = versionObj.revision; - self.installed = true; - } - }else if(navigator.appVersion.indexOf("Mac")==-1 && window.execScript){ - var version = -1; - for(var i=0; i prevTime + 1000 ) { - - fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) ); - fpsMin = Math.min( fpsMin, fps ); - fpsMax = Math.max( fpsMax, fps ); - - fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; - updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) ); - - prevTime = time; - frames = 0; - - } - - return time; - - }, - - update: function () { - - startTime = this.end(); - - } - - } - -}; diff --git a/build/pegs_expr_parser.js b/build/pegs_expr_parser.js deleted file mode 100644 index 92a0d09..0000000 --- a/build/pegs_expr_parser.js +++ /dev/null @@ -1,2164 +0,0 @@ -Webvs.PegExprParser = (function(){ - /* - * Generated by PEG.js 0.7.0. - * - * http://pegjs.majda.cz/ - */ - - function quote(s) { - /* - * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a - * string literal except for the closing quote character, backslash, - * carriage return, line separator, paragraph separator, and line feed. - * Any character may appear in the form of an escape sequence. - * - * For portability, we also escape escape all control and non-ASCII - * characters. Note that "\0" and "\v" escape sequences are not used - * because JSHint does not like the first and IE the second. - */ - return '"' + s - .replace(/\\/g, '\\\\') // backslash - .replace(/"/g, '\\"') // closing quote character - .replace(/\x08/g, '\\b') // backspace - .replace(/\t/g, '\\t') // horizontal tab - .replace(/\n/g, '\\n') // line feed - .replace(/\f/g, '\\f') // form feed - .replace(/\r/g, '\\r') // carriage return - .replace(/[\x00-\x07\x0B\x0E-\x1F\x80-\uFFFF]/g, escape) - + '"'; - } - - var result = { - /* - * Parses the input with a generated parser. If the parsing is successfull, - * returns a value explicitly or implicitly specified by the grammar from - * which the parser was generated (see |PEG.buildParser|). If the parsing is - * unsuccessful, throws |PEG.parser.SyntaxError| describing the error. - */ - parse: function(input, startRule) { - var parseFunctions = { - "program": parse_program, - "statement": parse_statement, - "unary_ops": parse_unary_ops, - "additive_ops": parse_additive_ops, - "multiplicative_ops": parse_multiplicative_ops, - "boolean_ops": parse_boolean_ops, - "boolean_expr": parse_boolean_expr, - "additive_expr": parse_additive_expr, - "multiplicative_expr": parse_multiplicative_expr, - "unary": parse_unary, - "func_call": parse_func_call, - "primary_expr": parse_primary_expr, - "assignable": parse_assignable, - "identifier": parse_identifier, - "constant": parse_constant, - "register": parse_register, - "value": parse_value, - "__": parse___, - "whiteSpace": parse_whiteSpace, - "lineEnd": parse_lineEnd, - "comment": parse_comment - }; - - if (startRule !== undefined) { - if (parseFunctions[startRule] === undefined) { - throw new Error("Invalid rule name: " + quote(startRule) + "."); - } - } else { - startRule = "program"; - } - - var pos = { offset: 0, line: 1, column: 1, seenCR: false }; - var reportFailures = 0; - var rightmostFailuresPos = { offset: 0, line: 1, column: 1, seenCR: false }; - var rightmostFailuresExpected = []; - var cache = {}; - - function padLeft(input, padding, length) { - var result = input; - - var padLength = length - input.length; - for (var i = 0; i < padLength; i++) { - result = padding + result; - } - - return result; - } - - function escape(ch) { - var charCode = ch.charCodeAt(0); - var escapeChar; - var length; - - if (charCode <= 0xFF) { - escapeChar = 'x'; - length = 2; - } else { - escapeChar = 'u'; - length = 4; - } - - return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length); - } - - function clone(object) { - var result = {}; - for (var key in object) { - result[key] = object[key]; - } - return result; - } - - function advance(pos, n) { - var endOffset = pos.offset + n; - - for (var offset = pos.offset; offset < endOffset; offset++) { - var ch = input.charAt(offset); - if (ch === "\n") { - if (!pos.seenCR) { pos.line++; } - pos.column = 1; - pos.seenCR = false; - } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { - pos.line++; - pos.column = 1; - pos.seenCR = true; - } else { - pos.column++; - pos.seenCR = false; - } - } - - pos.offset += n; - } - - function matchFailed(failure) { - if (pos.offset < rightmostFailuresPos.offset) { - return; - } - - if (pos.offset > rightmostFailuresPos.offset) { - rightmostFailuresPos = clone(pos); - rightmostFailuresExpected = []; - } - - rightmostFailuresExpected.push(failure); - } - - function parse_program() { - var cacheKey = "program@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2, result3, result4, result5, result6, result7; - var pos0, pos1, pos2; - - pos0 = clone(pos); - pos1 = clone(pos); - result0 = parse___(); - if (result0 !== null) { - result1 = parse_statement(); - if (result1 !== null) { - result2 = parse___(); - if (result2 !== null) { - result3 = []; - pos2 = clone(pos); - if (input.charCodeAt(pos.offset) === 59) { - result4 = ";"; - advance(pos, 1); - } else { - result4 = null; - if (reportFailures === 0) { - matchFailed("\";\""); - } - } - if (result4 !== null) { - result5 = parse___(); - if (result5 !== null) { - result6 = parse_statement(); - if (result6 !== null) { - result7 = parse___(); - if (result7 !== null) { - result4 = [result4, result5, result6, result7]; - } else { - result4 = null; - pos = clone(pos2); - } - } else { - result4 = null; - pos = clone(pos2); - } - } else { - result4 = null; - pos = clone(pos2); - } - } else { - result4 = null; - pos = clone(pos2); - } - while (result4 !== null) { - result3.push(result4); - pos2 = clone(pos); - if (input.charCodeAt(pos.offset) === 59) { - result4 = ";"; - advance(pos, 1); - } else { - result4 = null; - if (reportFailures === 0) { - matchFailed("\";\""); - } - } - if (result4 !== null) { - result5 = parse___(); - if (result5 !== null) { - result6 = parse_statement(); - if (result6 !== null) { - result7 = parse___(); - if (result7 !== null) { - result4 = [result4, result5, result6, result7]; - } else { - result4 = null; - pos = clone(pos2); - } - } else { - result4 = null; - pos = clone(pos2); - } - } else { - result4 = null; - pos = clone(pos2); - } - } else { - result4 = null; - pos = clone(pos2); - } - } - if (result3 !== null) { - if (input.charCodeAt(pos.offset) === 59) { - result4 = ";"; - advance(pos, 1); - } else { - result4 = null; - if (reportFailures === 0) { - matchFailed("\";\""); - } - } - result4 = result4 !== null ? result4 : ""; - if (result4 !== null) { - result5 = parse___(); - if (result5 !== null) { - result0 = [result0, result1, result2, result3, result4, result5]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, p) { - var stmts = [p[1]]; - stmts = stmts.concat(_.map(p[3], function(pp) { - return pp[2]; - })); - return new Webvs.AstProgram(stmts); - })(pos0.offset, pos0.line, pos0.column, result0); - } - if (result0 === null) { - pos = clone(pos0); - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_statement() { - var cacheKey = "statement@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2, result3, result4; - var pos0, pos1; - - pos0 = clone(pos); - pos1 = clone(pos); - result0 = parse_assignable(); - if (result0 !== null) { - result1 = parse___(); - if (result1 !== null) { - if (input.charCodeAt(pos.offset) === 61) { - result2 = "="; - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("\"=\""); - } - } - if (result2 !== null) { - result3 = parse___(); - if (result3 !== null) { - result4 = parse_boolean_expr(); - if (result4 !== null) { - result0 = [result0, result1, result2, result3, result4]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, lhs, e) { return new Webvs.AstAssignment(lhs, e); })(pos0.offset, pos0.line, pos0.column, result0[0], result0[4]); - } - if (result0 === null) { - pos = clone(pos0); - } - if (result0 === null) { - result0 = parse_boolean_expr(); - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_unary_ops() { - var cacheKey = "unary_ops@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0; - - if (input.charCodeAt(pos.offset) === 43) { - result0 = "+"; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"+\""); - } - } - if (result0 === null) { - if (input.charCodeAt(pos.offset) === 45) { - result0 = "-"; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"-\""); - } - } - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_additive_ops() { - var cacheKey = "additive_ops@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0; - - if (input.charCodeAt(pos.offset) === 43) { - result0 = "+"; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"+\""); - } - } - if (result0 === null) { - if (input.charCodeAt(pos.offset) === 45) { - result0 = "-"; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"-\""); - } - } - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_multiplicative_ops() { - var cacheKey = "multiplicative_ops@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0; - - if (input.charCodeAt(pos.offset) === 42) { - result0 = "*"; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"*\""); - } - } - if (result0 === null) { - if (input.charCodeAt(pos.offset) === 47) { - result0 = "/"; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"/\""); - } - } - if (result0 === null) { - if (input.charCodeAt(pos.offset) === 37) { - result0 = "%"; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"%\""); - } - } - } - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_boolean_ops() { - var cacheKey = "boolean_ops@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0; - - if (input.charCodeAt(pos.offset) === 38) { - result0 = "&"; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"&\""); - } - } - if (result0 === null) { - if (input.charCodeAt(pos.offset) === 124) { - result0 = "|"; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"|\""); - } - } - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_boolean_expr() { - var cacheKey = "boolean_expr@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2, result3, result4, result5; - var pos0, pos1, pos2; - - pos0 = clone(pos); - pos1 = clone(pos); - result0 = parse_additive_expr(); - if (result0 !== null) { - result1 = []; - pos2 = clone(pos); - result2 = parse___(); - if (result2 !== null) { - result3 = parse_boolean_ops(); - if (result3 !== null) { - result4 = parse___(); - if (result4 !== null) { - result5 = parse_additive_expr(); - if (result5 !== null) { - result2 = [result2, result3, result4, result5]; - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - while (result2 !== null) { - result1.push(result2); - pos2 = clone(pos); - result2 = parse___(); - if (result2 !== null) { - result3 = parse_boolean_ops(); - if (result3 !== null) { - result4 = parse___(); - if (result4 !== null) { - result5 = parse_additive_expr(); - if (result5 !== null) { - result2 = [result2, result3, result4, result5]; - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } - if (result1 !== null) { - result0 = [result0, result1]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, head, tail) { return makeBinaryExpr(head, tail); })(pos0.offset, pos0.line, pos0.column, result0[0], result0[1]); - } - if (result0 === null) { - pos = clone(pos0); - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_additive_expr() { - var cacheKey = "additive_expr@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2, result3, result4, result5; - var pos0, pos1, pos2; - - pos0 = clone(pos); - pos1 = clone(pos); - result0 = parse_multiplicative_expr(); - if (result0 !== null) { - result1 = []; - pos2 = clone(pos); - result2 = parse___(); - if (result2 !== null) { - result3 = parse_additive_ops(); - if (result3 !== null) { - result4 = parse___(); - if (result4 !== null) { - result5 = parse_multiplicative_expr(); - if (result5 !== null) { - result2 = [result2, result3, result4, result5]; - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - while (result2 !== null) { - result1.push(result2); - pos2 = clone(pos); - result2 = parse___(); - if (result2 !== null) { - result3 = parse_additive_ops(); - if (result3 !== null) { - result4 = parse___(); - if (result4 !== null) { - result5 = parse_multiplicative_expr(); - if (result5 !== null) { - result2 = [result2, result3, result4, result5]; - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } - if (result1 !== null) { - result0 = [result0, result1]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, head, tail) { return makeBinaryExpr(head, tail); })(pos0.offset, pos0.line, pos0.column, result0[0], result0[1]); - } - if (result0 === null) { - pos = clone(pos0); - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_multiplicative_expr() { - var cacheKey = "multiplicative_expr@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2, result3, result4, result5; - var pos0, pos1, pos2; - - pos0 = clone(pos); - pos1 = clone(pos); - result0 = parse_unary(); - if (result0 !== null) { - result1 = []; - pos2 = clone(pos); - result2 = parse___(); - if (result2 !== null) { - result3 = parse_multiplicative_ops(); - if (result3 !== null) { - result4 = parse___(); - if (result4 !== null) { - result5 = parse_unary(); - if (result5 !== null) { - result2 = [result2, result3, result4, result5]; - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - while (result2 !== null) { - result1.push(result2); - pos2 = clone(pos); - result2 = parse___(); - if (result2 !== null) { - result3 = parse_multiplicative_ops(); - if (result3 !== null) { - result4 = parse___(); - if (result4 !== null) { - result5 = parse_unary(); - if (result5 !== null) { - result2 = [result2, result3, result4, result5]; - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } else { - result2 = null; - pos = clone(pos2); - } - } - if (result1 !== null) { - result0 = [result0, result1]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, head, tail) { return makeBinaryExpr(head, tail); })(pos0.offset, pos0.line, pos0.column, result0[0], result0[1]); - } - if (result0 === null) { - pos = clone(pos0); - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_unary() { - var cacheKey = "unary@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2; - var pos0, pos1; - - pos0 = clone(pos); - pos1 = clone(pos); - result0 = parse_unary_ops(); - if (result0 !== null) { - result1 = parse___(); - if (result1 !== null) { - result2 = parse_func_call(); - if (result2 !== null) { - result0 = [result0, result1, result2]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, op, oper) { return new Webvs.AstUnaryExpr(op, oper); })(pos0.offset, pos0.line, pos0.column, result0[0], result0[2]); - } - if (result0 === null) { - pos = clone(pos0); - } - if (result0 === null) { - result0 = parse_func_call(); - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_func_call() { - var cacheKey = "func_call@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2, result3, result4, result5, result6, result7; - var pos0, pos1, pos2, pos3; - - pos0 = clone(pos); - pos1 = clone(pos); - pos2 = clone(pos); - if (/^[a-zA-Z_]/.test(input.charAt(pos.offset))) { - result0 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("[a-zA-Z_]"); - } - } - if (result0 !== null) { - result1 = []; - if (/^[a-zA-Z_0-9]/.test(input.charAt(pos.offset))) { - result2 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("[a-zA-Z_0-9]"); - } - } - while (result2 !== null) { - result1.push(result2); - if (/^[a-zA-Z_0-9]/.test(input.charAt(pos.offset))) { - result2 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("[a-zA-Z_0-9]"); - } - } - } - if (result1 !== null) { - result0 = [result0, result1]; - } else { - result0 = null; - pos = clone(pos2); - } - } else { - result0 = null; - pos = clone(pos2); - } - if (result0 !== null) { - result1 = parse___(); - if (result1 !== null) { - if (input.charCodeAt(pos.offset) === 40) { - result2 = "("; - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("\"(\""); - } - } - if (result2 !== null) { - pos2 = clone(pos); - result3 = []; - pos3 = clone(pos); - result4 = parse___(); - if (result4 !== null) { - result5 = parse_boolean_expr(); - if (result5 !== null) { - result6 = parse___(); - if (result6 !== null) { - if (input.charCodeAt(pos.offset) === 44) { - result7 = ","; - advance(pos, 1); - } else { - result7 = null; - if (reportFailures === 0) { - matchFailed("\",\""); - } - } - if (result7 !== null) { - result4 = [result4, result5, result6, result7]; - } else { - result4 = null; - pos = clone(pos3); - } - } else { - result4 = null; - pos = clone(pos3); - } - } else { - result4 = null; - pos = clone(pos3); - } - } else { - result4 = null; - pos = clone(pos3); - } - while (result4 !== null) { - result3.push(result4); - pos3 = clone(pos); - result4 = parse___(); - if (result4 !== null) { - result5 = parse_boolean_expr(); - if (result5 !== null) { - result6 = parse___(); - if (result6 !== null) { - if (input.charCodeAt(pos.offset) === 44) { - result7 = ","; - advance(pos, 1); - } else { - result7 = null; - if (reportFailures === 0) { - matchFailed("\",\""); - } - } - if (result7 !== null) { - result4 = [result4, result5, result6, result7]; - } else { - result4 = null; - pos = clone(pos3); - } - } else { - result4 = null; - pos = clone(pos3); - } - } else { - result4 = null; - pos = clone(pos3); - } - } else { - result4 = null; - pos = clone(pos3); - } - } - if (result3 !== null) { - result4 = parse___(); - if (result4 !== null) { - result5 = parse_boolean_expr(); - if (result5 !== null) { - result3 = [result3, result4, result5]; - } else { - result3 = null; - pos = clone(pos2); - } - } else { - result3 = null; - pos = clone(pos2); - } - } else { - result3 = null; - pos = clone(pos2); - } - result3 = result3 !== null ? result3 : ""; - if (result3 !== null) { - result4 = parse___(); - if (result4 !== null) { - if (input.charCodeAt(pos.offset) === 41) { - result5 = ")"; - advance(pos, 1); - } else { - result5 = null; - if (reportFailures === 0) { - matchFailed("\")\""); - } - } - if (result5 !== null) { - result0 = [result0, result1, result2, result3, result4, result5]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, funcName, args) { - var argsList = []; - _.each(args[0], function(toks) { - argsList.push(toks[1]); - }); - argsList.push(args[2]); - return new Webvs.AstFuncCall(flattenChars(funcName), argsList); - })(pos0.offset, pos0.line, pos0.column, result0[0], result0[3]); - } - if (result0 === null) { - pos = clone(pos0); - } - if (result0 === null) { - result0 = parse_primary_expr(); - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_primary_expr() { - var cacheKey = "primary_expr@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2; - var pos0, pos1; - - result0 = parse_value(); - if (result0 === null) { - result0 = parse_constant(); - if (result0 === null) { - result0 = parse_register(); - if (result0 === null) { - result0 = parse_identifier(); - if (result0 === null) { - pos0 = clone(pos); - pos1 = clone(pos); - if (input.charCodeAt(pos.offset) === 40) { - result0 = "("; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"(\""); - } - } - if (result0 !== null) { - result1 = parse_boolean_expr(); - if (result1 !== null) { - if (input.charCodeAt(pos.offset) === 41) { - result2 = ")"; - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("\")\""); - } - } - if (result2 !== null) { - result0 = [result0, result1, result2]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, e) { return e; })(pos0.offset, pos0.line, pos0.column, result0[1]); - } - if (result0 === null) { - pos = clone(pos0); - } - } - } - } - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_assignable() { - var cacheKey = "assignable@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0; - - result0 = parse_register(); - if (result0 === null) { - result0 = parse_identifier(); - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_identifier() { - var cacheKey = "identifier@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2; - var pos0, pos1; - - pos0 = clone(pos); - pos1 = clone(pos); - if (/^[a-zA-Z_]/.test(input.charAt(pos.offset))) { - result0 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("[a-zA-Z_]"); - } - } - if (result0 !== null) { - result1 = []; - if (/^[a-zA-Z_0-9]/.test(input.charAt(pos.offset))) { - result2 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("[a-zA-Z_0-9]"); - } - } - while (result2 !== null) { - result1.push(result2); - if (/^[a-zA-Z_0-9]/.test(input.charAt(pos.offset))) { - result2 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("[a-zA-Z_0-9]"); - } - } - } - if (result1 !== null) { - result0 = [result0, result1]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, val) { return new Webvs.AstPrimaryExpr(flattenChars(val).toLowerCase(), "ID"); })(pos0.offset, pos0.line, pos0.column, result0); - } - if (result0 === null) { - pos = clone(pos0); - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_constant() { - var cacheKey = "constant@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2; - var pos0, pos1; - - pos0 = clone(pos); - pos1 = clone(pos); - if (input.charCodeAt(pos.offset) === 36) { - result0 = "$"; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"$\""); - } - } - if (result0 !== null) { - result1 = []; - if (/^[a-zA-Z_0-9]/.test(input.charAt(pos.offset))) { - result2 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("[a-zA-Z_0-9]"); - } - } - while (result2 !== null) { - result1.push(result2); - if (/^[a-zA-Z_0-9]/.test(input.charAt(pos.offset))) { - result2 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("[a-zA-Z_0-9]"); - } - } - } - if (result1 !== null) { - result0 = [result0, result1]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, val) { return new Webvs.AstPrimaryExpr(flattenChars(val).toLowerCase(), "CONST"); })(pos0.offset, pos0.line, pos0.column, result0[1]); - } - if (result0 === null) { - pos = clone(pos0); - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_register() { - var cacheKey = "register@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2, result3, result4; - var pos0, pos1; - - pos0 = clone(pos); - pos1 = clone(pos); - if (input.charCodeAt(pos.offset) === 64) { - result0 = "@"; - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"@\""); - } - } - if (result0 !== null) { - result1 = []; - if (/^[a-zA-Z_0-9]/.test(input.charAt(pos.offset))) { - result2 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("[a-zA-Z_0-9]"); - } - } - while (result2 !== null) { - result1.push(result2); - if (/^[a-zA-Z_0-9]/.test(input.charAt(pos.offset))) { - result2 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("[a-zA-Z_0-9]"); - } - } - } - if (result1 !== null) { - result0 = [result0, result1]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, val) { return new Webvs.AstPrimaryExpr("__REG_AT_" + flattenChars(val).toLowerCase(), "REG"); })(pos0.offset, pos0.line, pos0.column, result0[1]); - } - if (result0 === null) { - pos = clone(pos0); - } - if (result0 === null) { - pos0 = clone(pos); - pos1 = clone(pos); - if (/^[rR]/.test(input.charAt(pos.offset))) { - result0 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("[rR]"); - } - } - if (result0 !== null) { - if (/^[eE]/.test(input.charAt(pos.offset))) { - result1 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result1 = null; - if (reportFailures === 0) { - matchFailed("[eE]"); - } - } - if (result1 !== null) { - if (/^[gG]/.test(input.charAt(pos.offset))) { - result2 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("[gG]"); - } - } - if (result2 !== null) { - if (/^[0-9]/.test(input.charAt(pos.offset))) { - result3 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result3 = null; - if (reportFailures === 0) { - matchFailed("[0-9]"); - } - } - if (result3 !== null) { - if (/^[0-9]/.test(input.charAt(pos.offset))) { - result4 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result4 = null; - if (reportFailures === 0) { - matchFailed("[0-9]"); - } - } - if (result4 !== null) { - result0 = [result0, result1, result2, result3, result4]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, val) { return new Webvs.AstPrimaryExpr("__REG_" + flattenChars(val).toLowerCase(), "REG"); })(pos0.offset, pos0.line, pos0.column, result0); - } - if (result0 === null) { - pos = clone(pos0); - } - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_value() { - var cacheKey = "value@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2, result3, result4, result5; - var pos0, pos1, pos2; - - pos0 = clone(pos); - pos1 = clone(pos); - result0 = []; - if (/^[0-9]/.test(input.charAt(pos.offset))) { - result1 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result1 = null; - if (reportFailures === 0) { - matchFailed("[0-9]"); - } - } - while (result1 !== null) { - result0.push(result1); - if (/^[0-9]/.test(input.charAt(pos.offset))) { - result1 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result1 = null; - if (reportFailures === 0) { - matchFailed("[0-9]"); - } - } - } - if (result0 !== null) { - if (input.charCodeAt(pos.offset) === 46) { - result1 = "."; - advance(pos, 1); - } else { - result1 = null; - if (reportFailures === 0) { - matchFailed("\".\""); - } - } - if (result1 !== null) { - if (/^[0-9]/.test(input.charAt(pos.offset))) { - result3 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result3 = null; - if (reportFailures === 0) { - matchFailed("[0-9]"); - } - } - if (result3 !== null) { - result2 = []; - while (result3 !== null) { - result2.push(result3); - if (/^[0-9]/.test(input.charAt(pos.offset))) { - result3 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result3 = null; - if (reportFailures === 0) { - matchFailed("[0-9]"); - } - } - } - } else { - result2 = null; - } - if (result2 !== null) { - pos2 = clone(pos); - if (/^[Ee]/.test(input.charAt(pos.offset))) { - result3 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result3 = null; - if (reportFailures === 0) { - matchFailed("[Ee]"); - } - } - if (result3 !== null) { - if (/^[0-9]/.test(input.charAt(pos.offset))) { - result5 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result5 = null; - if (reportFailures === 0) { - matchFailed("[0-9]"); - } - } - if (result5 !== null) { - result4 = []; - while (result5 !== null) { - result4.push(result5); - if (/^[0-9]/.test(input.charAt(pos.offset))) { - result5 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result5 = null; - if (reportFailures === 0) { - matchFailed("[0-9]"); - } - } - } - } else { - result4 = null; - } - if (result4 !== null) { - result3 = [result3, result4]; - } else { - result3 = null; - pos = clone(pos2); - } - } else { - result3 = null; - pos = clone(pos2); - } - result3 = result3 !== null ? result3 : ""; - if (result3 !== null) { - result0 = [result0, result1, result2, result3]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, val) { return new Webvs.AstPrimaryExpr(parseFloat(flattenChars(val)), "VALUE"); })(pos0.offset, pos0.line, pos0.column, result0); - } - if (result0 === null) { - pos = clone(pos0); - } - if (result0 === null) { - pos0 = clone(pos); - pos1 = clone(pos); - if (/^[a-fA-F0-9]/.test(input.charAt(pos.offset))) { - result1 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result1 = null; - if (reportFailures === 0) { - matchFailed("[a-fA-F0-9]"); - } - } - if (result1 !== null) { - result0 = []; - while (result1 !== null) { - result0.push(result1); - if (/^[a-fA-F0-9]/.test(input.charAt(pos.offset))) { - result1 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result1 = null; - if (reportFailures === 0) { - matchFailed("[a-fA-F0-9]"); - } - } - } - } else { - result0 = null; - } - if (result0 !== null) { - if (/^[hH]/.test(input.charAt(pos.offset))) { - result1 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result1 = null; - if (reportFailures === 0) { - matchFailed("[hH]"); - } - } - if (result1 !== null) { - result0 = [result0, result1]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, val) { return new Webvs.AstPrimaryExpr(parseInt(flattenChars(val), 16), "VALUE"); })(pos0.offset, pos0.line, pos0.column, result0[0]); - } - if (result0 === null) { - pos = clone(pos0); - } - if (result0 === null) { - pos0 = clone(pos); - pos1 = clone(pos); - if (/^[0-9]/.test(input.charAt(pos.offset))) { - result1 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result1 = null; - if (reportFailures === 0) { - matchFailed("[0-9]"); - } - } - if (result1 !== null) { - result0 = []; - while (result1 !== null) { - result0.push(result1); - if (/^[0-9]/.test(input.charAt(pos.offset))) { - result1 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result1 = null; - if (reportFailures === 0) { - matchFailed("[0-9]"); - } - } - } - } else { - result0 = null; - } - if (result0 !== null) { - if (/^[dD]/.test(input.charAt(pos.offset))) { - result1 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result1 = null; - if (reportFailures === 0) { - matchFailed("[dD]"); - } - } - result1 = result1 !== null ? result1 : ""; - if (result1 !== null) { - result0 = [result0, result1]; - } else { - result0 = null; - pos = clone(pos1); - } - } else { - result0 = null; - pos = clone(pos1); - } - if (result0 !== null) { - result0 = (function(offset, line, column, val) { return new Webvs.AstPrimaryExpr(parseInt(flattenChars(val), 10), "VALUE"); })(pos0.offset, pos0.line, pos0.column, result0[0]); - } - if (result0 === null) { - pos = clone(pos0); - } - } - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse___() { - var cacheKey = "__@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1; - - result0 = []; - result1 = parse_whiteSpace(); - if (result1 === null) { - result1 = parse_lineEnd(); - if (result1 === null) { - result1 = parse_comment(); - } - } - while (result1 !== null) { - result0.push(result1); - result1 = parse_whiteSpace(); - if (result1 === null) { - result1 = parse_lineEnd(); - if (result1 === null) { - result1 = parse_comment(); - } - } - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_whiteSpace() { - var cacheKey = "whiteSpace@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0; - - if (/^[\t\x0B\f \xA0\uFEFF]/.test(input.charAt(pos.offset))) { - result0 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("[\\t\\x0B\\f \\xA0\\uFEFF]"); - } - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_lineEnd() { - var cacheKey = "lineEnd@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0; - - if (/^[\n\r\u2028\u2029]/.test(input.charAt(pos.offset))) { - result0 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("[\\n\\r\\u2028\\u2029]"); - } - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - function parse_comment() { - var cacheKey = "comment@" + pos.offset; - var cachedResult = cache[cacheKey]; - if (cachedResult) { - pos = clone(cachedResult.nextPos); - return cachedResult.result; - } - - var result0, result1, result2, result3; - var pos0, pos1, pos2; - - pos0 = clone(pos); - if (input.substr(pos.offset, 2) === "/*") { - result0 = "/*"; - advance(pos, 2); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"/*\""); - } - } - if (result0 !== null) { - result1 = []; - pos1 = clone(pos); - pos2 = clone(pos); - reportFailures++; - if (input.substr(pos.offset, 2) === "*/") { - result2 = "*/"; - advance(pos, 2); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("\"*/\""); - } - } - reportFailures--; - if (result2 === null) { - result2 = ""; - } else { - result2 = null; - pos = clone(pos2); - } - if (result2 !== null) { - if (input.length > pos.offset) { - result3 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result3 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } - } - if (result3 !== null) { - result2 = [result2, result3]; - } else { - result2 = null; - pos = clone(pos1); - } - } else { - result2 = null; - pos = clone(pos1); - } - while (result2 !== null) { - result1.push(result2); - pos1 = clone(pos); - pos2 = clone(pos); - reportFailures++; - if (input.substr(pos.offset, 2) === "*/") { - result2 = "*/"; - advance(pos, 2); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("\"*/\""); - } - } - reportFailures--; - if (result2 === null) { - result2 = ""; - } else { - result2 = null; - pos = clone(pos2); - } - if (result2 !== null) { - if (input.length > pos.offset) { - result3 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result3 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } - } - if (result3 !== null) { - result2 = [result2, result3]; - } else { - result2 = null; - pos = clone(pos1); - } - } else { - result2 = null; - pos = clone(pos1); - } - } - if (result1 !== null) { - if (input.substr(pos.offset, 2) === "*/") { - result2 = "*/"; - advance(pos, 2); - } else { - result2 = null; - if (reportFailures === 0) { - matchFailed("\"*/\""); - } - } - if (result2 !== null) { - result0 = [result0, result1, result2]; - } else { - result0 = null; - pos = clone(pos0); - } - } else { - result0 = null; - pos = clone(pos0); - } - } else { - result0 = null; - pos = clone(pos0); - } - if (result0 === null) { - pos0 = clone(pos); - if (input.substr(pos.offset, 2) === "//") { - result0 = "//"; - advance(pos, 2); - } else { - result0 = null; - if (reportFailures === 0) { - matchFailed("\"//\""); - } - } - if (result0 !== null) { - result1 = []; - pos1 = clone(pos); - pos2 = clone(pos); - reportFailures++; - result2 = parse_lineEnd(); - reportFailures--; - if (result2 === null) { - result2 = ""; - } else { - result2 = null; - pos = clone(pos2); - } - if (result2 !== null) { - if (input.length > pos.offset) { - result3 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result3 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } - } - if (result3 !== null) { - result2 = [result2, result3]; - } else { - result2 = null; - pos = clone(pos1); - } - } else { - result2 = null; - pos = clone(pos1); - } - while (result2 !== null) { - result1.push(result2); - pos1 = clone(pos); - pos2 = clone(pos); - reportFailures++; - result2 = parse_lineEnd(); - reportFailures--; - if (result2 === null) { - result2 = ""; - } else { - result2 = null; - pos = clone(pos2); - } - if (result2 !== null) { - if (input.length > pos.offset) { - result3 = input.charAt(pos.offset); - advance(pos, 1); - } else { - result3 = null; - if (reportFailures === 0) { - matchFailed("any character"); - } - } - if (result3 !== null) { - result2 = [result2, result3]; - } else { - result2 = null; - pos = clone(pos1); - } - } else { - result2 = null; - pos = clone(pos1); - } - } - if (result1 !== null) { - result0 = [result0, result1]; - } else { - result0 = null; - pos = clone(pos0); - } - } else { - result0 = null; - pos = clone(pos0); - } - } - - cache[cacheKey] = { - nextPos: clone(pos), - result: result0 - }; - return result0; - } - - - function cleanupExpected(expected) { - expected.sort(); - - var lastExpected = null; - var cleanExpected = []; - for (var i = 0; i < expected.length; i++) { - if (expected[i] !== lastExpected) { - cleanExpected.push(expected[i]); - lastExpected = expected[i]; - } - } - return cleanExpected; - } - - - - function makeBinaryExpr(head, tail) { - var result = head; - _.each(tail, function(tailItem) { - result = new Webvs.AstBinaryExpr(tailItem[1], result, tailItem[3]); - }); - return result; - } - - function flattenChars(val) { - return _.flatten(val).join(""); - } - - - var result = parseFunctions[startRule](); - - /* - * The parser is now in one of the following three states: - * - * 1. The parser successfully parsed the whole input. - * - * - |result !== null| - * - |pos.offset === input.length| - * - |rightmostFailuresExpected| may or may not contain something - * - * 2. The parser successfully parsed only a part of the input. - * - * - |result !== null| - * - |pos.offset < input.length| - * - |rightmostFailuresExpected| may or may not contain something - * - * 3. The parser did not successfully parse any part of the input. - * - * - |result === null| - * - |pos.offset === 0| - * - |rightmostFailuresExpected| contains at least one failure - * - * All code following this comment (including called functions) must - * handle these states. - */ - if (result === null || pos.offset !== input.length) { - var offset = Math.max(pos.offset, rightmostFailuresPos.offset); - var found = offset < input.length ? input.charAt(offset) : null; - var errorPosition = pos.offset > rightmostFailuresPos.offset ? pos : rightmostFailuresPos; - - throw new this.SyntaxError( - cleanupExpected(rightmostFailuresExpected), - found, - offset, - errorPosition.line, - errorPosition.column - ); - } - - return result; - }, - - /* Returns the parser source code. */ - toSource: function() { return this._source; } - }; - - /* Thrown when a parser encounters a syntax error. */ - - result.SyntaxError = function(expected, found, offset, line, column) { - function buildMessage(expected, found) { - var expectedHumanized, foundHumanized; - - switch (expected.length) { - case 0: - expectedHumanized = "end of input"; - break; - case 1: - expectedHumanized = expected[0]; - break; - default: - expectedHumanized = expected.slice(0, expected.length - 1).join(", ") - + " or " - + expected[expected.length - 1]; - } - - foundHumanized = found ? quote(found) : "end of input"; - - return "Expected " + expectedHumanized + " but " + foundHumanized + " found."; - } - - this.name = "SyntaxError"; - this.expected = expected; - this.found = found; - this.message = buildMessage(expected, found); - this.offset = offset; - this.line = line; - this.column = column; - }; - - result.SyntaxError.prototype = Error.prototype; - - return result; -})(); \ No newline at end of file diff --git a/build/webvs.js b/build/webvs.js index a896a5e..f4da251 100644 --- a/build/webvs.js +++ b/build/webvs.js @@ -1,26 +1,68 @@ /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(window) { -/** - * Webvs namespace that contains all classes - * @alias Webvs - * @namespace - */ +// Webvs namespace that contains all classes var Webvs = {}; window.Webvs = Webvs; -/** - * A wrapper around Object.create to help with class definition - * @param {function} constructor - constructor function for which the prototype is to be defined - * @param {function} baseConstructor - base constructor whose prototype will be extended - * @param {...object} [properties] - additional properties to be added to the prototype - * @returns {function} the constructor - */ +// Events mixin. Use Backbone Events if available +// else we expect a global Events mixin with similar +// API to be present +Webvs.Events = (window.Backbone && Backbone.Events) || window.Events; +Webvs.ModelLike = _.extend(_.clone(Webvs.Events), { + get: function(key) { + throw new Error("get not implemented"); + }, + + toJSON: function(key) { + throw new Error("toJSON not implemented"); + }, + + setAttribute: function(key) { + throw new Error("setAttribute not implemented"); + }, + + set: function(key, value, options) { + var success, silent; + + if(!_.isString(key) && arguments.length <= 2) { + // if map of key values are passed + // then set each value separately + silent = value.silent; + options = _.defaults({silent:true}, value); + value = _.clone(key); + + success = false; + for(key in value) { + if(this.setAttribute(key, value[key], options)) { + success = true; + if(!silent) { + this.trigger("change:" + key, this, value[key], options); + } + } + } + if(success && !silent) { + this.trigger("change", this, options); + } + } else { + options = options || {}; + success = this.setAttribute(key, value, options); + if(success && !options.silent) { + this.trigger("change:" + key, this, value, options); + this.trigger("change", this, options); + } + } + + return success; + } +}); + +// A wrapper around Object.create to help with class definition Webvs.defineClass = function(constructor, baseConstructor) { constructor.prototype = Object.create(baseConstructor.prototype); constructor.prototype.constructor = constructor; // fix the constructor reference @@ -34,16 +76,17 @@ Webvs.defineClass = function(constructor, baseConstructor) { return constructor; }; -/** - * An empty function - */ +Webvs.ComponentRegistry = {}; +Webvs.registerComponent = function(componentClass, meta) { + Webvs.checkRequiredOptions(meta, ["name"]); + componentClass.Meta = meta; + Webvs[meta.name] = componentClass; + Webvs.ComponentRegistry[meta.name] = componentClass; +}; + Webvs.noop = function() {}; -/** - * Checks if an object contains the required properties - * @param {object} options - object to be checked - * @param {Array.} - properties to be checked - */ +// Checks if an object contains the required properties Webvs.checkRequiredOptions = function(options, requiredOptions) { for(var i in requiredOptions) { var key = requiredOptions[i]; @@ -53,21 +96,13 @@ Webvs.checkRequiredOptions = function(options, requiredOptions) { } }; -/** - * Returns a floating point value representation of a number - * embeddable in glsl shader code - * @param {number} val - value to be converted - * @returns {string} float represntation - */ +// Returns a floating point value representation of a number +// embeddable in glsl shader code Webvs.glslFloatRepr = function(val) { return val + (val%1 === 0?".0":""); }; -/** - * Parse css color string #RRGGBB or rgb(r, g, b) - * @param {string} color - color to be parsed - * @returns {Array.} triple of color values in 0-255 range - */ +// Parse css color string #RRGGBB or rgb(r, g, b) Webvs.parseColor = function(color) { if(_.isArray(color) && color.length == 3) { return color; @@ -92,18 +127,12 @@ Webvs.parseColor = function(color) { throw new Error("Invalid Color Format"); }; -/** - * 0-1 normalized version of {@link Webvs.parseColor} - */ +// 0-1 normalized version of Webvs.parseColor Webvs.parseColorNorm = function(color) { return _.map(Webvs.parseColor(color), function(value) { return value/255; }); }; -/** - * Pretty prints a shader compilation error - * @param {string} - shader source code - * @param {string} - error message from gl.getShaderInfoLog - */ +// Pretty prints a shader compilation error Webvs.logShaderError = function(src, error) { var lines = src.split("\n"); var ndigits = lines.length.toString().length; @@ -134,115 +163,47 @@ Webvs.logShaderError = function(src, error) { console.log("Shader Error : \n" + numberedLines); }; - -/** - * @class - * A simple promise object to notify async init of - * components - * @memberof Webvs - * @constructor - */ -var Promise = function() { - this.resolved = false; - this.listeners = []; -}; -Webvs.Promise = Webvs.defineClass(Promise, Object, { - /** - * resolves the promise object and runs all - * the callbacks - * @memberof Webvs.Promise# - */ - resolve: function() { - if(!this.resolved) { - this.resolved = true; - _.each(this.listeners, function(cb) { - cb(); - }); - } - }, - - /** - * register a callback which should be called - * when the promise resolves - * @param {function} cb - callback - * @memberof Webvs.Promise# - */ - onResolve : function(cb) { - if(this.resolved) { - cb(); - } else { - this.listeners.push(cb); - } - } -}); - -/** - * Combines several promises into one promise - * @param {Array.} promises - promises to be combined - * @returns {Webvs.Promise} - */ -Webvs.joinPromises = function(promises) { - var joinedPromise = new Promise(); - promises = _.filter(promises, function(p) {return !_.isUndefined(p);}); - if(promises.length === 0) { - joinedPromise.resolve(); - } else { - var counter = promises.length; - var onResolveCb = function() { - counter--; - if(counter === 0) { - joinedPromise.resolve(); - } - }; - _.each(promises, function(promise) { - if(promise.resolved) { - onResolveCb(); - } else { - promise.onResolve(onResolveCb); - } - }); - } - - return joinedPromise; -}; - _.flatMap = _.compose(_.flatten, _.map); -/** - * Blend mode constants - */ -Webvs.blendModes = { +// Blend mode constants +Webvs.BlendModes = { REPLACE: 1, MAXIMUM: 2, AVERAGE: 3, ADDITIVE: 4, SUBTRACTIVE1: 5, SUBTRACTIVE2: 6, - MULTIPLY: 7 + MULTIPLY: 7, + MULTIPLY2: 8, + ADJUSTABLE: 9, + ALPHA: 10 }; -_.extend(Webvs, Webvs.blendModes); +_.extend(Webvs, Webvs.BlendModes); -/** - * Returns the blendmode constant. Throws an error if its - * an invalid blend mode - * @param {string} name - the blend mode in string - * @returns {number} the code for the blend mode - */ -Webvs.getBlendMode = function(name) { - var mode = Webvs.blendModes[name]; - if(!mode) { - throw new Error("Unknown blendMode " + name); +Webvs.Channels = { + CENTER: 0, + LEFT: 1, + RIGHT: 2 +}; +_.extend(Webvs, Webvs.Channels); + +Webvs.Source = { + SPECTRUM: 1, + WAVEFORM: 2 +}; +_.extend(Webvs, Webvs.Source); + +// Returns an enumeration(plain object with numeric values) +// value or throws an exception if it doesnt exists +Webvs.getEnumValue = function(key, enumeration) { + key = key.toUpperCase(); + if(!(key in enumeration)) { + throw new Error("Unknown key " + key + ", expecting one of " + _.keys(enumeration).join(",")); } - return mode; + return enumeration[key]; }; -/** - * Returns a random string of given length - * @param {number} count - the number of characters required - * @param {string} chars - a string containing the characters - * from which to choose - * @returns {string} a random string - */ +// Returns a random string of given length Webvs.randString = function(count, chars) { var string = []; chars = chars || "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; @@ -252,127 +213,267 @@ Webvs.randString = function(count, chars) { return string.join(""); }; -/** - * Clamps a number between two given numbers - * @param {number} num - number to be clamped - * @param {number} min - clamp min edge - * @returns {number} max - clamp max edge - */ +// Clamps a number between two given numbers Webvs.clamp = function(num, min, max) { return Math.min(Math.max(num, min), max); }; +// Returns the component class with the given name. Throws Webvs.getComponentClass = function(name) { - var componentClass = Webvs[name]; + var componentClass = Webvs.ComponentRegistry[name]; if(!componentClass) { throw new Error("Unknown Component class " + name); } return componentClass; }; +// Returns the value of property given its (dot separated) path in an object +Webvs.getProperty = function(obj, name) { + if(_.isString(name)) { + name = name.split("."); + } + var value = obj[name.shift()]; + if(value) { + if(name.length > 0) { + return Webvs.getProperty(value, name); + } else { + return value; + } + } +}; + +// Sets a property, given its (dot separated) path in an object +Webvs.setProperty = function(obj, name, value) { + if(_.isString(name)) { + name = name.split("."); + } + var propertyName = name.shift(); + if(name.length === 0) { + obj[propertyName] = value; + } else { + Webvs.setProperty(obj[propertyName], name, value); + } +}; + })(window); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { - -Webvs.Resources = { - "avsres_texer_circle_edgeonly_19x19.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAMAAABFjsb+AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhFBMVEUAAAADAwMvLy9dXV1tbW0CAgJaWlrR0dH8/Pz+/v79/f1bW1sKCgqlpaXT09Nubm4xMTEeHh7S0tKCgoIGBgaBgYEuLi5sbGxeXl4cHBwbGxtvb29fX19ra2swMDDU1NQFBQV/f3+np6eoqKgLCwvQ0NBqamoEBAQyMjJhYWFxcXH///8GRExTAAAAAWJLR0QrJLnkCAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAK1JREFUGNN1kMkSgyAQRBViGMc1SpCYBYl7/v8Do2gocpBDF/Wqa3qmPe/w+YSeKPEdEpwZhBgCi4IfihNMs/ySZwUm8e5KoOTmx0tINmeEpZ1yxciMTwtuGS/SNUhA5cRVQBaVSBwmUC6a4c1hNd6NT/z5HosSeDrsCa81V7HGooYpcyBFbZlGut3xBr1t2Gho9x66FvtB1GLose1sU1KZXpR02xqn+TNP43HBX4kJCUk5wyykAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0wNC0yOVQwMTo0OTozMCswNTowMPYYqoAAAAAASUVORK5CYII=", - "avsres_texer_circle_edgeonly_29x29.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAMAAABhTZc9AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAA3lBMVEUAAAANDQ0/Pz9xcXGRkZGbm5tycnIfHx+IiIjg4OD7+/v+/v7///8EBARoaGjn5+f8/Pzl5eXHx8e7u7sICAiXl5f6+vrx8fGVlZU4ODgKCgoBAQEJCQm+vr4oKCi9vb39/f2YmJikpKQLCwujo6NpaWmJiYknJyeKiorh4eE3Nzc2NjZAQEDk5ORzc3OSkpLGxsacnJy6urq5ubmdnZ3FxcWTk5M1NTWUlJTw8PDo6Oi8vLwgICCioqJqamqZmZmamprj4+PExMS4uLiLi4sODg5BQUF0dHSenp4PDw8GiJKZAAAAAWJLR0QMgbNRYwAAAAlwSFlzAAALEgAACxIB0t1+/AAAATRJREFUKM+tkltXgkAUhUczL+xBQGNAwEuRKVlpNxC8oGZa//8PRRoIjqsnz9M561tnZu+zNiFnqVz+onBZKOZzJ1ipXBEACgiVcumIiVUJVFZq9ZoiU0hVMQ2vVAZNbxgmMY2GroGpVmpTldFsJWOrjc71YbvKcGOnnrJvwbqJIAlNOyPDbkOKpd1Ba5Fs9TT0/3xWqM4Z1Kmz950X5HuODmThYdcUoRgctR7xtGuGGJkcNUcY7ppn+nLisq/07Z/dd7jxvxZHk38jzQOOerHmnEPHHB3HfkkfWu/4Vn58K1IKMMk6NuoIpvHQZZilsTEDmyeTGHYwOTzem6ATptJhhQy+61m/2fBcH2yR8SjOgyhXy9XHahnlKphnchXVdO3I+0w66ynh63Ozdb/c7eabnKV+AJulHNGcTEkjAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0wNC0yOVQwMTo0OTozMCswNTowMPYYqoAAAAAASUVORK5CYII=", - "avsres_texer_circle_fade_13x13.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAA9lBMVEUAAAADAwMGBgYICAgCAgIKCgoVFRUeHh4hISEdHR0JCQkODg4jIyM4ODhISEhNTU1HR0c3NzcNDQ0LCwskJCRGRkZoaGh/f3+Hh4d+fn5lZWVEREQXFxc6OjppaWmSkpKtra22trarq6uPj49mZmYHBwcgICBLS0uCgoKurq7Kysrd3d3JycmsrKxRUVGMjIy4uLjf39/+/v63t7eIiIhMTEyDg4Ovr6/MzMzLy8uAgIBJSUkEBAQYGBg8PDxsbGyVlZWwsLC5ubmTk5M5OTkWFhYMDAwmJiZKSkqEhIRqamoQEBA9PT1SUlI7OzsPDw8BAQH///+Cg4ycAAAAAWJLR0RRlGl8KgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKRJREFUCNcljukWgVAAhO9tvRKVEhGyhISEspWsZef9n0bL/Jo5c+bMB0AsiOEYBJkIkqJRjmKINOTZQpHjBVRKoiiV5UpVqdURGW/URrOltTvdnkBBoPcHQ2NkjidKkcYAI02tmT03F0sO4UBHjrta2xttu4s7z98Hh+PJOMuXa/wZRrfgbrkPnhUTkGf04t7OR/qm9zD0kYRUkcjQfh7O6F7i/uybEULrc6ImAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwMy0xMC0wN1QwMzoxNzoxMiswNjowMNF3hcgAAAAASUVORK5CYII=", - "avsres_texer_circle_heavyblur_19x19.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAMAAABFjsb+AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABVlBMVEUAAAACAgIHBwcNDQ0RERETExMBAQEICAgVFRUiIiItLS01NTU3NzcgICA0NDRGRkZVVVVeXl5hYWEjIyM9PT1XV1dubm6BgYGMjIyQkJAfHx9dXV19fX2YmJirq6u2tra6uroDAwMUFBQzMzNWVlafn5+7u7vPz8/b29vf398hISFFRUVtbW2Xl5fY2Njr6+v09PT39/cMDAwsLCxTU1OAgICpqanOzs7q6ur4+Pj8/Pz+/v6qqqoQEBBcXFyKioq0tLTa2tr9/f3///+1tbWLi4sSEhJfX1+NjY24uLje3t729vYyMjKJiYnZ2dnz8/MLCwsrKytSUlJ+fn6oqKjNzc3p6ekGBgZDQ0Nra2uVlZXV1dXy8vL19fUxMTFUVFR5eXmcnJzMzMzc3NwdHR06OjpaWlqUlJSnp6eysrJqamp8fHyIiIgwMDBCQkJRUVEqKioPDw8hvXKsAAAAAWJLR0RDZ9ANYgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAU5JREFUGNM9kFVbwmAARr9tMEpKUhghoyRHCQyHhMTI0SnlSMn/f6OI+l6+z7k5B4DrIBjhcDkIDIG/oTy+QCi6EwkFfB56uyBELJHK5PdymVQiRn5QFFEoVWqN9kGrUauUCuRK8nRKPWYwmswmowHTK3U8AB4tuBWz2R3OJ6fDbsOsuAUCsMvt8Zp9fiJA+H12r8ftgkEwFI48R2NxMkHGY9GXSDgUBBSefE2liUwimyCJ9FsuiVMgLyoU6VI5k81mE+USXSyI8oCqJKupWp28cvVaqpqsUIBpNFvtTjdAZshAt9NuNRsM6In7gyE96o7fx90RPRz0xT0AcSfT2Zxe+D58C3o+m0643yIwu1zN1putabtZz1ZLFr76Bl3L8O5zn9tju/DSxdxSMezheFrpV6fjgWV+Y6FwXnee4JOzLg+j/1WhHnOhLkzv1vkLuBJAlyODjrgAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDA5LTA0LTI5VDAxOjQ5OjMwKzA1OjAw9hiqgAAAAABJRU5ErkJggg==", - "avsres_texer_circle_heavyblur_21x21.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABfVBMVEUAAAADAwMICAgNDQ0QEBASEhIBAQELCwsWFhYhISEqKiovLy8yMjIFBQUiIiJBQUFNTU1UVFRXV1cUFBQoKCg+Pj5TU1NlZWV0dHR9fX2AgIBmZmYRERFCQkJdXV13d3eNjY2cnJylpaWoqKgKCgqampqvr6/AwMDKysrNzc0VFRUxMTF2dnaZmZm2trbOzs7e3t7o6Ojr6+sHBwcfHx9AQECMjIzl5eXy8vL4+Pj6+voMDAxMTExzc3Obm5u/v7/d3d39/f3+/v4PDw8uLi58fHykpKTIyMjn5+f///8wMDBVVVV+fn6mpqbLy8vp6en5+fktLS1SUlJ7e3ujo6PHx8fm5ub39/dKSkpxcXG9vb3c3Nzx8fEGBgY/Pz9jY2OKioqtra3j4+NQUFCWlpazs7Pb29sCAgIJCQkgICA8PDxaWlp6enqsrKy8vLzGxsbJyckmJiaJiYmhoaEEBAQ7OztiYmJwcHB5eXlJSUkTExMeHh4sLCwdHR0ODg7+hS0uAAAAAWJLR0RJhwXkfAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAZhJREFUGNNFkfs3wnAYxr9jkynXXSqyXLZpGMXShWhbRWFCmUvlslgql1Xk/rdbEc+Pn/Oezznv8wDQDtTVDSNwdxfUA/4CWXrRPqvN2of2WqAO7EcGbINDwyPDQ4O2AaT/F2I4QdodzlGnY4wkcKyNIRc+TrknJqempyYn3NQ47mpJaJSgnAw74+E8MyzjpAiUNk9n58h5fmHR61vyebkFfp6cm4XAsuC3r7BcIBgKh4IBjl2x+4VlsIqvRdY3osGwKInhYHRjPbKGW4AcI+OJza2QmEwmxdDWZiJOxmQAp7Z3dpW9tGRSKb2n7O5sp2Ag7x8cZrJHqiglJVE9ymYOD/ZlYMGPT5jTs1zbmzs7ZU6OTW9eKJxfXCpXOTWt5q6Uy4vzgpAHEJbSHMVrz03AF7hRrosOLYXpAJRuy5Vq8e7+4fHh/q5YrZRvS+bHOmwUKpEaX0/U+Vqk4jdgvVVPAzGetOfmS/yl+aw9GUjjp8qGLFiJV43UXgmrIDc6teult3fjI/ZhfL6V9P+JgJ6nv+QvOv+7zzdrwFa1yCl9GAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDktMDQtMjlUMDE6NDk6MzArMDU6MDD2GKqAAAAAAElFTkSuQmCC", - "avsres_texer_circle_heavyblur_29x29.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAMAAABhTZc9AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACSVBMVEUAAAABAQEDAwMGBgYJCQkLCwsNDQ0ODg4EBAQPDw8WFhYcHBwgICAjIyMkJCQCAgIHBwcQEBAaGhosLCwzMzM4ODg8PDw9PT0ICAgiIiIuLi46OjpERERNTU1TU1NXV1dZWVkMDAwYGBgnJyc2NjZFRUVSUlJeXl5oaGhwcHB1dXV2dnZfX18KCgooKChMTExdXV1sbGx7e3uHh4ePj4+Tk5OVlZWUlJQVFRUmJiZOTk5iYmKIiIiXl5eioqKqqqqvr6+xsbEhISE1NTVLS0t5eXmhoaG8vLzExMTJycnLy8u7u7stLS1cXFykpKS2trbGxsbT09Pb29vh4eHj4+MZGRk5OTlRUVGgoKDKysra2trm5ubu7u7y8vLz8/Nra2sFBQUUFBQqKipDQ0N6enqWlpbZ2dno6Oj4+Pj6+vr7+/vp6ekxMTFnZ2eGhoa6urrR0dHl5eX5+fn8/Pz9/f3+/v7S0tIeHh5ubm6NjY2oqKjCwsLt7e339/f///9VVVVycnKRkZGtra3Hx8ff39/x8fGSkpI7Ozt0dHSurq6srKze3t4dHR1tbW2MjIynp6fBwcHs7OwwMDBKSkplZWWEhISfn5+5ubnQ0NDk5OQpKSlBQUFbW1t3d3fDw8PX19fn5+c3NzdPT09paWmFhYWdnZ2zs7Pr6+vw8PAXFxcrKyuLi4vPz8/Y2Njd3d0fHx9ISEirq6u3t7fAwMDFxcW4uLgTExNxcXGenp6mpqaCgoKKiopaWlpkZGRAQEBJSUkvLy80NDQSEhJl1IWqAAAAAWJLR0R+P7hBcwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAvpJREFUKM9dk/k/2gEcxr/VN+UrlcRKoRJRvs4awrTE2NxHzuZu1RzTMWdEGpqbXLly39mY+9xfNpnZ7Pn1/Xqe1+f1ep4PADwKgUSBaCeMExpEIRHAcyGwoDPkgnPFu+JcIGcQ+4wTiG4kV3eyh+cLTw+yuyvJjUL4C5FoiOpFo3v7+DJ8fbzpNC8qhEY+QSbLj+0fwAkM4vK4QYHBAf5sPxbzERNgVggtNCw8IpIveBnFj4wIDwulhbDgh3AEJTqGJozlxcW/ShC9FiW8io/jxQppMdFEx2lYZ3GiJDaJnyx6k5L6NjXlnSiZnxQrSRQ7Yx1WVho9ncvPyMzKzsnNy83JzsrM4HPT6Wn5FASAxFClBeFxhZlFxSWlsvey0pLioszCuLICKRWDBMorKqs41TWiLPkHmUKpUipkH+VZoppqTlVldDlAIdVK6uqTGz41ytQarU6rUcsaPzck19dJaklEAG5iBzS3tLYVt+s1uo6ODp1G317c1trSHMBuggEmjtZp6Oo29piU2g6HtEpTj7H7i6GThmMCTHGvb5+gPzVXptI9UJ1KlpvaL+jz7aUyATROeu81G7+a1NqHZK363mvuMnRKcWgAHmAXDA4Nj8hH9RoH1mr0o/KR4aHBAvYADBDzx8YnLJNT0zMmher+ZpXCNDM9NWmZGB/LJwLYilnr3HyUeWFxyaRXK9V609Ligjlqfs46W4EFCE5i2/LK6tq6cTFvY9O0uZG3aFxfW11Ztomd7luibG1bGYadwvWF3b39nv293YX1wh0Dw7q9RXR0j7GTDzjcnbVv3w+PjEeHP8xrO9zgA7Id89A/6hhvG+ecWASnZwnmhLNTgeWEI7HhK1C/9whCMbYDRiDv/ILfxb845wUyDmwxEIj4Mx0IT7YKL6/Kgk6Cyq4uhVYyHoKfVkkAj+2V7OubZZ9On+Wba3al/Rj8Z7IIFGbLHnJ75yG13d2G2CEM6vk7IInMY5aLHWd3Yf1kEpHA/yJgUSCMhkEUlvBk/AXCBOhMJK+/nwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMTAtMDZUMjI6MDc6NDYrMDY6MDA+xvzGAAAAAElFTkSuQmCC", - "avsres_texer_circle_sharp_09x09.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAMAAACecocUAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAOVBMVEUAAAACAgJhYWG+vr7b29tiYmKoqKj+/v7///9jY2PBwcHf39/CwsJlZWUDAwOsrKxmZmbDw8Pg4ODJk53hAAAAAWJLR0QIht6VegAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAElJREFUCNdVzUsSwCAIA1CtVakfUO9/WINlY1YvM0xw7op/whuiP0yZiHLSEpUoH1zoT4GrucLN3OBu9x1mOTvCOspjrjn4froBkegClm06guAAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDAzLTA2LTI5VDE5OjA5OjMyKzA2OjAw50C8rwAAAABJRU5ErkJggg==", - "avsres_texer_circle_sharp_19x19.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAe1BMVEUAAAAFBQVBQUGGhoaurq68vLyvr68GBgYBAQFNTU3Ozs78/Pz+/v7///+Kior6+vr9/f2Li4vR0dFCQkJDQ0OIiIiJiYmzs7PBwcG0tLRERETT09NRUVH7+/uQkJCRkZFTU1PU1NRUVFQHBwdHR0eMjIy1tbXDw8NISEjxdmZoAAAAAWJLR0QN9rRh9QAAAAlwSFlzAAALEgAACxIB0t1+/AAAAJ5JREFUGNN9ke0SgiAUBUUrQbhA9qGpWGZa7/+EgZoCNu7PnTPALEGwAQqj3f4QhbHlMEkoAwBGE4LnIRfwQ3A0LbmEBXkc10SAjSBGxim4pObKE/UsPWt7YZ5lV20z8Mm0zVc2/7u9actX53JtC+lZWZj3lp4th0SVcqSqxg6106GeqqF6Wav7nBg/Gjn0lc0TW93bV9e/++7Tbn3YF1ESEZb9e6HrAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0wNC0yOVQwMTo0OTozMCswNTowMPYYqoAAAAAASUVORK5CYII=", - "avsres_texer_circle_slightblur_13x13.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAASFBMVEUAAAABAQEODg4jIyMtLS0EBARsbGyZmZmoqKiKiorT09Py8vL4+Pj7+/v+/v7///8kJCRtbW3U1NSLi4uampqpqakPDw8uLi7NF0qwAAAAAWJLR0QPGLoA2QAAAAlwSFlzAAALEgAACxIB0t1+/AAAAGhJREFUCNdljkcOwCAMBCmmF9NC/v/TGJBySHwbjXbXjH2PCwlS8AMKtLFGg9oGnA8xeAfLCu1Txpy8FkTShIyIORhJBDbiumiBqLyurFxtO9d2jkNv1Nn67qS9Oq5Rzx7ZWe4y+e/HB1hhBEsscYLrAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwMy0xMC0wNlQyMTozODowMCswNTowMB7KOaoAAAAASUVORK5CYII=", - "avsres_texer_circle_slightblur_21x21.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAvVBMVEUAAAACAgIDAwMEBAQQEBAgICAsLCwwMDABAQEODg5MTExnZ2d5eXl+fn4TExM9PT1tbW2Xl5e0tLTFxcXKysqvr6/W1tbs7Oz19fX4+Pjg4OD39/f9/f3+/v6YmJj///8RERFoaGi1tbV6enrGxsb29vYxMTGAgIDLy8t/f38tLS0hISG2trbt7e1NTU2ZmZnX19dubm6xsbHh4eGwsLAPDw8+Pj57e3sUFBRvb2/MzMxpaWmBgYEiIiIyMjK3SG45AAAAAWJLR0QfBQ0QvQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAPNJREFUGNN9ketygjAQhZVLCpgKagEjRAKC2nAVC2itvv9jOcRI0Rk9P7/ZOXv27GDwXkNh+IxESQYfQJbEHlNUoI3gJxxpQFU6ONaNyXT2NZtODH18p6oOTcueo7ltmVBXuScwzIXjYoxdZ2Ea4OYtaUvLwV4r7FhLjTAq+4Hteje5duDLLCdYhRHmFEchBG1uYb3Zoo6i7fdaaGfp4+yKshtjP0k73zTwY7aNZHnBLTAq8ozwvLtyj1hetP/ZUd5FVTdlkUYoSouyqat7D4f6mCfhb5jkx/rwX09Fs1Pz15wyWim9LkVyphcak36/r37xpCumwx2LqyXT8gAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMTAtMDZUMjA6NDA6MjIrMDU6MDAsCqMMAAAAAElFTkSuQmCC", - "avsres_texer_hexagon-h_blur_123x123.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHsAAAB7AgMAAAApqRfsAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEUAAACIiIjMzMz///+lqQOCAAAAAWJLR0QDEQxM8gAAAAlwSFlzAAALEgAACxIB0t1+/AAAAXJJREFUSMft17FVwzAURuHgwhSMQKERvEI2ygh4BK1BltAKjECREVxAIQSH4MTvSfpvKhpQe8/J8efY0vNu979+Y90dzXr2/b6Y9eH7g+1l73pwfXb94PqL68n1k7t8l8ub7YPv75JXAT3PA0PVLfBQdQtMVTfAiueAQ92z5DngY90NMDR6lDwLTI2+ATZ4Bji0epY8A2zxtsCp2aPkbYGp2U+StwEO7Z4lbwNs867AqdNX4FOnv0peKYvkXYBjr2fJuwB7vBU4dXuUvBWYun2RvB/g2O9Z8r6Ae8k7AyfRo+SdgUn0RfK+gaPqubUzWmBQvXCn36frIx/dH7y/k+jxhv+Xng96vuj5pOcb348+MN70ftL7TfsD7S+0P+H+1gOu+yPtr7Q/0/5O5wOdL3g+tYHX843ORzpf6Xym853mA5wvguTxfEPzEc1XNJ/RfIfzYQ2cTaf5lOZbmo9pvqb5HOd7D5xdp+8L+j6h75u/uj4BxUT7mSYYfpEAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDExLTA0LTExVDAwOjM0OjU4KzA1OjAwAHKiyQAAAABJRU5ErkJggg==", - "avsres_texer_square_edgeonly_24x24.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYAQMAAADaua+7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEX///8AAABVwtN+AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABJJREFUCNdjYEAC8v9/UAUjAQD7uCWNIgeQwQAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMTAtMDZUMjE6NTc6NDYrMDU6MDDg7pOkAAAAAElFTkSuQmCC", - "avsres_texer_square_edgeonly_28x28.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcAQMAAABIw03XAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEX///8AAABVwtN+AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABNJREFUCNdjYEAF8v//N9CPQAUArWA5f2D2DX0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDAzLTEwLTA2VDIxOjU4OjQ0KzA1OjAwhnrZAAAAAABJRU5ErkJggg==", - "avsres_texer_square_edgeonly_30x30.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEX///8AAABVwtN+AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABRJREFUCNdjYEAF8v//PxgIAhUAAOmGR7lQ6SOhAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwMy0xMC0wNlQyMTo1OToxOCswNTowMOb41i4AAAAASUVORK5CYII=", - "avsres_texer_square_sharp_20x20.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYAQMAAADaua+7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABJJREFUCNdjYIAC+/9/qIqhAABLlCyJ9A7ihwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMDYtMjlUMTk6Mzg6MDQrMDU6MDBuiiynAAAAAElFTkSuQmCC", - "avsres_texer_square_sharp_32x32.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoAQMAAAC2MCouAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABVJREFUCNdjYMAB+P////9hCJM4AACQJX+BjmWyDAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDktMTAtMDhUMDE6NTM6NTYrMDU6MDBzUO7GAAAAAElFTkSuQmCC", - "avsres_texer_square_sharp_48x48.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4AQMAAACSSKldAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABZJREFUGNNjYCAA+P+DwIdReoBoAgAAldYe8LB8aUoAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDA5LTEwLTA4VDAxOjUzOjI4KzA1OjAwKaqcggAAAABJRU5ErkJggg==", - "avsres_texer_square_sharp_60x60.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQAAABEAQMAAAAC3QHxAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAALEgAACxIB0t1+/AAAABdJREFUKM9jYCAS8P+HgFHWKIuaLCIBAH8IpfBELmrmAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMTo1NDozMiswNTowMKOs2CsAAAAASUVORK5CYII=", - "avsres_texer_square_sharp_64x64.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQAQMAAAC032DuAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABdJREFUKM9jYKAV+A8Fo8xR5lBk0gYAAMbE/hBbX3sVAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMjoxODowMCswNjowML8mns0AAAAASUVORK5CYII=", - "avsres_texer_square_sharp_72x72.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFQAAABUAQMAAAAmpYKCAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABpJREFUKM9jYKASYP4PBX9G2aPsUTa12VQCABpBhZc9Qe3KAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMjowMjoxNiswNjowMMbw5GAAAAAASUVORK5CYII=", - "avsres_texer_square_sharp_96x96.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHAAAABwAQMAAAD8LmYIAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAB1JREFUOMtjYBgo8B8JjHJHuaPcUe4od/BwBwYAAB86e71LirdOAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMjoxODo0NiswNjowMFi8pQ0AAAAASUVORK5CYII=", - "avsres_texer_square_sharp_250x250.bmp": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAAEKAQMAAADQBYmKAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAALEgAACxIB0t1+/AAAADlJREFUaN7tykENAAAIBCD7p7KZFrgZwMGbKrK5taIoiqIoiqIoiqIoiqIoiqIoiqIoiqIoivKpkCyUd+T92W3T7QAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTEtMDEtMDVUMTg6NTk6NTQrMDU6MDBwiLErAAAAAElFTkSuQmCC", +Webvs.ResourcePack = { + name: "Builtin", + prefix: "./resources/", + fileNames: [ + "avsres_texer_circle_edgeonly_19x19.bmp", + "avsres_texer_circle_edgeonly_29x29.bmp", + "avsres_texer_circle_fade_13x13.bmp", + "avsres_texer_circle_heavyblur_19x19.bmp", + "avsres_texer_circle_heavyblur_21x21.bmp", + "avsres_texer_circle_heavyblur_29x29.bmp", + "avsres_texer_circle_sharp_09x09.bmp", + "avsres_texer_circle_sharp_19x19.bmp", + "avsres_texer_circle_slightblur_13x13.bmp", + "avsres_texer_circle_slightblur_21x21.bmp", + "avsres_texer_hexagon-h_blur_123x123.bmp", + "avsres_texer_square_edgeonly_24x24.bmp", + "avsres_texer_square_edgeonly_28x28.bmp", + "avsres_texer_square_edgeonly_30x30.bmp", + "avsres_texer_square_sharp_20x20.bmp", + "avsres_texer_square_sharp_32x32.bmp", + "avsres_texer_square_sharp_48x48.bmp", + "avsres_texer_square_sharp_60x60.bmp", + "avsres_texer_square_sharp_64x64.bmp", + "avsres_texer_square_sharp_72x72.bmp", + "avsres_texer_square_sharp_96x96.bmp", + "avsres_texer_square_sharp_250x250.bmp" + ] }; })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { +// ResourceManager manages async loading and caching of resources. +// Basically, it maintains a map of fileNames to URI for the resource. +// When a request for resource fileName is received, the uri is looked up +// and the file is either async loaded or served from cache. This also manages +// a ready state with callbacks that tells when one or more resources are being loaded and +// when all resources are ready. +function ResourceManager(packs) { + if(packs) { + if(!_.isArray(packs)) { + packs = [packs]; + } + this.packs = packs; + } else { + this.packs = []; + } + this.clear(); +} +Webvs.ResourceManager = Webvs.defineClass(ResourceManager, Object, Webvs.ModelLike, { + // Register a filename and a URI in the resource manager. + registerUri: function(fileName, uri) { + if(_.isString(fileName) && _.isString(uri)) { + this.uris[fileName] = uri; + } else { + _.extend(this.uris, fileName); + } + }, + + get: function(key, value) { + if(key == "uris") { + return this.uris; + } else if(key == "packs") { + return this.packs; + } + }, + + setAttribute: function(key, value, options) { + if(key == "uris") { + this.uris = value; + return true; + } + return false; + }, + + toJSON: function() { + return { + uris: _.clone(this.uris) + }; + }, + + // Clears state, uri mappings and caches. Browser caches still apply. + clear: function() { + this.uris = {}; + this.images = {}; + this.waitCount = 0; + this.ready = true; + }, + + destroy: function() { + this.stopListening(); + }, + + _getUri: function(fileName) { + var uri = this.uris[fileName]; + if(uri) { + return uri; + } + for(var i = this.packs.length-1;i >= 0;i--) { + var pack = this.packs[i]; + if(pack.fileNames.indexOf(fileName) != -1) { + return pack.prefix + fileName; + } + } + }, + + _loadStart: function() { + this.waitCount++; + if(this.waitCount == 1) { + this.ready = false; + this.trigger("wait"); + } + }, + + _loadEnd: function() { + this.waitCount--; + if(this.waitCount === 0) { + this.ready = true; + this.trigger("ready"); + } + }, + + // Loads an Image resource + getImage: function(fileName, success, error, context) { + context = context || this; + var this_ = this; + var image = this.images[fileName]; + if(image) { // check in cache + if(success) { + success.call(context, image); + } + return; + } + + // load file + var uri = this._getUri(fileName); + if(!uri) { + throw new Error("Unknown image file " + fileName); + } + image = new Image(); + if(uri.indexOf("data:") !== 0) { + // add cross origin attribute for + // remote images + image.crossOrigin = "anonymous"; + } + image.onload = function() { + this_.images[fileName] = image; + if(success) { + success.call(context, image); + } + this_._loadEnd(); + }; + if(error) { + image.onError = function() { + if(error.call(context)) { + + // then we treat this load as complete + // and handled properly + this_._loadEnd(); + } + }; + } + this._loadStart(); + image.src = uri; + } +}); + +})(Webvs); + /** - * @class - * AnalyserAdapter adapts music data analysers so that it can be plugged into Webvs. - * Adapters extend this class and define the required methods. - * @memberof Webvs - * @constructor + * Copyright (c) 2013-2015 Azeem Arshad + * See the file license.txt for copying permission. */ + +(function(Webvs) { + +// AnalyserAdapter adapts music data analysers so that it can be plugged into Webvs. +// Adapters extend this class and define the required methods. function AnalyserAdapter() {} Webvs.AnalyserAdapter = Webvs.defineClass(AnalyserAdapter, Object, { - /** - * boolean value indicating whether a beat - * is in progress or not - * @type boolean - * @memberof Webvs.AnalyserAdapter# - */ + // boolean value indicating whether a beat + // is in progress or not beat: false, - /** - * returns whether song is being played or not. - * @abstract - * @returns {boolean} - * @memberof Webvs.AnalyserAdapter# - */ - isPlaying: function() {return false;}, + // Called every frame. Override and implement analyser code + update: function() {}, - /** - * Returns array of waveform values - * @abstract - * @returns {Float32Array} - * @memberof Webvs.AnalyserAdapter# - */ - getWaveform: function() {return new Float32Array(0);}, + // Returns array of waveform values + getWaveform: function(channel) {return new Float32Array(0);}, - /** - * Returns array of spectrum values - * @abstract - * @returns {Float32Array} - * @memberof Webvs.AnalyserAdapter# - */ - getSpectrum: function() {return new Float32Array(0);} + // Returns array of spectrum values + getSpectrum: function(channel) {return new Float32Array(0);} }); })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * Analyser adapter that adapts the Dancer library. - * @param dancer - * @augments Webvs.AnalyserAdapter - * @constructor - * @memberof Webvs - */ +// Analyser adapter that adapts the Dancer library. function DancerAdapter(dancer) { this.dancer = dancer; this.beat = false; @@ -390,29 +491,12 @@ function DancerAdapter(dancer) { this.kick.on(); } Webvs.DancerAdapter = Webvs.defineClass(DancerAdapter, Webvs.AnalyserAdapter, { - /** - * returns whether song is being played or not. - * @returns {boolean} - * @memberof Webvs.DancerAdapter# - */ - isPlaying: function() { - return this.dancer.isPlaying(); - }, - - /** - * returns array of waveform values - * @returns {Float32Array} - * @memberof Webvs.DancerAdapter# - */ + // returns array of waveform values getWaveform: function() { return this.dancer.getWaveform(); }, - /** - * Returns array of spectrum values - * @returns {Float32Array} - * @memberof Webvs.DancerAdapter# - */ + // Returns array of spectrum values getSpectrum: function() { return this.dancer.getSpectrum(); } @@ -421,41 +505,270 @@ Webvs.DancerAdapter = Webvs.defineClass(DancerAdapter, Webvs.AnalyserAdapter, { })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad + * See the file license.txt for copying permission. + */ + +(function(Webvs) { + +// SMAnalyser connects SoundManager2 waveform and eqData to Webvs +function SMAnalyser(options) { + options = _.defaults(options||{}, { + threshold: 0.125, + decay: 0.02 + }); + + this.threshold = options.threshold; + this.movingThreshold = 0; + this.decay = options.decay; + + this.eqData = []; + this.waveformData = []; + + // initialize with empty data + for(var ch = 0;ch < 3;ch++) { + this.eqData[ch] = new Float32Array(256); + this.waveformData[ch] = new Float32Array(256); + } +} +Webvs.SMAnalyser = Webvs.defineClass(SMAnalyser, Webvs.AnalyserAdapter, { + // Creates a new SMSound object and attaches it to this analyser + createSound: function(options) { + options.useWaveformData = true; + options.useEQData = true; + this.setSound(soundManager.createSound(options)); + return this.sound; + }, + + setSound: function(sound) { + this.sound = sound; + }, + + update: function() { + if(!this.sound || + this.sound.eqData.left.length === 0 || + this.sound.waveformData.left.length === 0) { + return; // no sound. nothing to update + } + var i; + + this.eqData[1] = new Float32Array(this.sound.eqData.left); + this.eqData[2]= new Float32Array(this.sound.eqData.right); + + this.waveformData[1] = new Float32Array(this.sound.waveformData.left); + this.waveformData[2] = new Float32Array(this.sound.waveformData.right); + + for(i = 0;i < 256;i++) { + // compute center channel + this.eqData[0][i] = this.eqData[1][i]/2 + this.eqData[2][i]/2; + this.waveformData[0][i] = this.waveformData[1][i]/2 + this.waveformData[2][i]/2; + } + + // Simple kick detection + this.beat = false; + var peak_left = 0, peak_right = 0; + for(i = 0;i < 256;i++) { + peak_left += Math.abs(this.waveformData[1][i]); + peak_right += Math.abs(this.waveformData[2][i]); + } + var peak = Math.max(peak_left, peak_right)/256; + + if(peak >= this.movingThreshold && peak >= this.threshold) { + this.movingThreshold = peak; + this.beat = true; + } else { + this.movingThreshold = this.movingThreshold*(1-this.decay)+peak*this.decay; + } + }, + + // Returns array of waveform values + getWaveform: function(channel) { + channel = _.isUndefined(channel)?0:channel; + return this.waveformData[channel]; + }, + + // Returns array of spectrum values + getSpectrum: function(channel) { + channel = _.isUndefined(channel)?0:channel; + return this.eqData[channel]; + } +}); + +})(Webvs); + +/** + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { +// AnalyserAdapter adapts music data analysers so that it can be plugged into Webvs. +// Adapters extend this class and define the required methods. +function WebAudioAnalyser(options) { + options = _.defaults(options||{}, { + fftSize: 512, + threshold: 0.125, + decay: 0.02 + }); + + if(options.context) { + this.context = options.context; + } else if(window.webkitAudioContext) { + this.context = new webkitAudioContext(); + } else if(window.AudioContext) { + this.context = new AudioContext(); + } else { + throw new Error("Cannot create webaudio context"); + } + + this.fftSize = options.fftSize; + + this.threshold = options.threshold; + this.movingThreshold = 0; + this.decay = options.decay; + + this.visData = []; + for(var ch = 0;ch < 3;ch++) { + var spectrum = new Float32Array(this.fftSize/2); + var waveform = new Float32Array(this.fftSize); + this.visData[ch] = {spectrum: spectrum, waveform: waveform}; + } +} +Webvs.WebAudioAnalyser = Webvs.defineClass(WebAudioAnalyser, Webvs.AnalyserAdapter, { + // Connect this analyser to any WebAudio Node + connectToNode: function(sourceNode) { + this.source = sourceNode; + + // this gain node simply up/down mixes input source to stereo output + this.gain = this.context.createGain(); + this.gain.channelCountMode = "explicit"; + this.gain.channelCount = 2; + this.source.connect(this.gain); + + // split the stereo output into respective mono channels + this.channelSplit = this.context.createChannelSplitter(2); + this.gain.connect(this.channelSplit); + + // analser node for each channel + this.analysers = []; + for(var ch = 0;ch < 2;ch++) { + var analyser = this.context.createAnalyser(); + analyser.fftSize = this.fftSize; + this.channelSplit.connect(analyser, ch); + this.analysers[ch] = analyser; + } + }, + + update: function() { + if(!this.analysers) { + return; // analysers not ready. nothing update + } + var i; + var byteBuffer = new Uint8Array(this.fftSize); + for(var ch = 0;ch < 2;ch++) { + var visData = this.visData[ch+1]; + var analyser = this.analysers[ch]; + + analyser.getByteFrequencyData(byteBuffer); + for(i = 0;i < visData.spectrum.length;i++) { // scale to 0-1 range + visData.spectrum[i] = byteBuffer[i]/255; + } + + analyser.getByteTimeDomainData(byteBuffer); + for(i = 0;i < visData.waveform.length;i++) { // scale to -1 to 1 range + visData.waveform[i] = (byteBuffer[i]/255)*2-1; + } + } + + // center channel is average of left and right + var centerVisData = this.visData[0]; + for(i = 0;i < centerVisData.spectrum.length;i++) { + centerVisData.spectrum[i] = (this.visData[1].spectrum[i]/2+this.visData[2].spectrum[i]/2); + } + for(i = 0;i < centerVisData.waveform.length;i++) { + centerVisData.waveform[i] = (this.visData[1].waveform[i]/2+this.visData[2].waveform[i]/2); + } + + // Simple kick detection + this.beat = false; + var peak_left = 0, peak_right = 0; + for(i = 0;i < this.fftSize;i++) { + peak_left += Math.abs(this.visData[1].waveform[i]); + peak_right += Math.abs(this.visData[2].waveform[i]); + } + var peak = Math.max(peak_left, peak_right)/this.fftSize; + + if(peak >= this.movingThreshold && peak >= this.threshold) { + this.movingThreshold = peak; + this.beat = true; + } else { + this.movingThreshold = this.movingThreshold*(1-this.decay)+peak*this.decay; + } + }, + + // Helper for Webvs.WebAudioAnalyser#connectToNode. This creates Audio object + // for the audio file and connects this analyser to its mediaElementSource + load: function(source, readyFunc) { + var element; + if(source instanceof HTMLMediaElement) { + element = source; + this.source = this.context.createMediaElementSource(element); + } else { + element = new Audio(); + element.src = source; + this.source = this.context.createMediaElementSource(element); + } + + var onCanPlay = _.bind(function() { + this.connectToNode(this.source); + this.source.connect(this.context.destination); + + if(readyFunc) { + readyFunc(element); + } + + element.removeEventListener("canplay", onCanPlay); + }, this); + if(element.readyState < 3) { + element.addEventListener("canplay", onCanPlay); + } else { + onCanPlay(); + } + + return element; + }, + + // Returns array of waveform values + getWaveform: function(channel) { + channel = _.isUndefined(channel)?0:channel; + return this.visData[channel].waveform; + }, + + // Returns array of spectrum values + getSpectrum: function(channel) { + channel = _.isUndefined(channel)?0:channel; + return this.visData[channel].spectrum; + } +}); + +})(Webvs); + /** - * @class - * Main Webvs object, that represents a running webvs instance. - * - * @example - * var dancer = new Dancer(); - * var webvs = new Webvs.Main({ - * canvas: document.getElementById("canvas"), - * analyser: new Webvs.DancerAdapter(dancer), - * showStat: true - * }); - * webvs.loadPreset(samplePreset); - * webvs.start(); - * dancer.load({src: "music.ogg"}); // start playing musc - * dancer.play(); - * - * @param {object} options - options object - * @param {HTMLCanvasElement} options.canvas - canvas element on which the visualization will be rendered - * @param {Webvs.AnalyserAdapter} options.analyser - a music analyser instance - * @param {boolean} [options.showStat=false] - if set, then a framerate status indicator is inserted into the page - * @memberof Webvs - * @constructor + * Copyright (c) 2013-2015 Azeem Arshad + * See the file license.txt for copying permission. */ + +(function(Webvs) { + +// Main Webvs object, that represents a running webvs instance. function Main(options) { Webvs.checkRequiredOptions(options, ["canvas", "analyser"]); options = _.defaults(options, { showStat: false }); this.canvas = options.canvas; + this.msgElement = options.msgElement; this.analyser = options.analyser; this.isStarted = false; if(options.showStat) { @@ -467,18 +780,33 @@ function Main(options) { document.body.appendChild(stats.domElement); this.stats = stats; } - this.resources = {}; - this.rootComponent = new Webvs.EffectList({id:"root"}); + + this.meta = {}; + this._initResourceManager(options.resourcePrefix); this._registerContextEvents(); this._initGl(); + this._setupRoot({id: "root"}); } -Webvs.Main = Webvs.defineClass(Main, Object, { +Webvs.Main = Webvs.defineClass(Main, Object, Webvs.ModelLike, { + _initResourceManager: function(prefix) { + var builtinPack = Webvs.ResourcePack; + if(prefix) { + builtinPack = _.clone(builtinPack); + builtinPack.prefix = prefix; + } + this.rsrcMan = new Webvs.ResourceManager(builtinPack); + this.listenTo(this.rsrcMan, "wait", this.handleRsrcWait); + this.listenTo(this.rsrcMan, "ready", this.handleRsrcReady); + + var this_ = this; + }, + _registerContextEvents: function() { var _this = this; this.canvas.addEventListener("webglcontextlost", function(event) { event.preventDefault(); - _this.stop(); + _this.stop(); }); this.canvas.addEventListener("webglcontextrestored", function(event) { @@ -488,67 +816,29 @@ Webvs.Main = Webvs.defineClass(Main, Object, { _initGl: function() { try { - this.gl = this.canvas.getContext("experimental-webgl", {alpha: false}); - - this.copier = new Webvs.CopyProgram({dynamicBlend: true}); - this.copier.init(this.gl); - - this.resolution = { - width: this.canvas.width, - height: this.canvas.height - }; + this.gl = this.canvas.getContext("webgl", {alpha: false}); + this.copier = new Webvs.CopyProgram(this.gl, {dynamicBlend: true}); + this.buffers = new Webvs.FrameBufferManager(this.gl, this.copier, true, 0); } catch(e) { throw new Error("Couldnt get webgl context" + e); } }, - /** - * Loads a preset JSON. If a preset is already loaded and running, then - * the animation is stopped, and the new preset is loaded. - * @param {object} preset - JSON representation of the preset - * @memberof Webvs.Main# - */ - loadPreset: function(preset) { - preset = _.clone(preset); // use our own copy - preset.id = "root"; - var newRoot = new Webvs.EffectList(preset); - this.stop(); - this.rootComponent.destroy(); - this.rootComponent = newRoot; - this.resources = preset.resources || {}; - }, - - /** - * Reset all the components. Call this when canvas dimensions changes - * @memberof Webvs.Main# - */ - resetCanvas: function() { - this.stop(); - var preset = this.rootComponent.getOptions(); - this.rootComponent.destroy(); - this.copier.cleanup(); - this._initGl(); - this.rootComponent = new Webvs.EffectList(preset); + _setupRoot: function(preset) { + this.registerBank = {}; + this.bootTime = (new Date()).getTime(); + this.rootComponent = new Webvs.EffectList(this.gl, this, null, preset); }, - /** - * Starts the animation if not already started - * @memberof Webvs.Main# - */ - start: function() { - if(this.isStarted) { - return; - } - + _startAnimation: function() { var _this = this; var drawFrame = function() { - if(_this.analyser.isPlaying()) { - _this.rootComponent.update(); - } + _this.analyser.update(); + _this.rootComponent.draw(); _this.animReqId = requestAnimationFrame(drawFrame); }; - // wrap drawframe in stats collection if required + // Wrap drawframe in stats collection if required if(this.stats) { var oldDrawFrame = drawFrame; drawFrame = function() { @@ -557,183 +847,121 @@ Webvs.Main = Webvs.defineClass(Main, Object, { _this.stats.end(); }; } + this.animReqId = requestAnimationFrame(drawFrame); + }, + + _stopAnimation: function() { + cancelAnimationFrame(this.animReqId); + }, - if(!this.rootComponent.componentInited) { - this.registerBank = {}; - this.bootTime = (new Date()).getTime(); - this.rootComponent.init(this.gl, this); + // Starts the animation if not already started + start: function() { + if(this.isStarted) { + return; } - this.animReqId = requestAnimationFrame(drawFrame); this.isStarted = true; + if(this.rsrcMan.ready) { + this._startAnimation(); + } }, - /** - * Stops the animation - * @memberof Webvs.Main# - */ + // Stops the animation stop: function() { - if(!_.isUndefined(this.animReqId)) { - cancelAnimationFrame(this.animReqId); - this.isStarted = false; + if(!this.isStarted) { + return; + } + this.isStarted = false; + if(this.rsrcMan.ready) { + this._stopAnimation(); } }, - /** - * Generates and returns the instantaneous preset JSON - * representation - * @returns {object} preset json - * @memberof Webvs.Main# - */ - getPreset: function() { - var preset = this.rootComponent.getOptions(); - preset.resources = this.resources; - return preset; - }, + // Loads a preset JSON. If a preset is already loaded and running, then + // the animation is stopped, and the new preset is loaded. + loadPreset: function(preset) { + preset = _.clone(preset); // use our own copy + preset.id = "root"; + this.rootComponent.destroy(); - /** - * Adds a component under the given parent. Root has the id "root". - * @param {string} parentId - id of the parent under which the component is - * to be added - * @param {object} options - options for the new component - * @param {number} [pos] - position at which the component will be inserted. - * default is the end of the list - * @returns {string} id of the new component - * @memberof Webvs.Main# - */ - addComponent: function(parentId, options, pos) { - options = _.clone(options); // use our own copy - this.rootComponent.addComponent(parentId, options, pos); - return res; + // setup resources + this.rsrcMan.clear(); + if("resources" in preset && "uris" in preset.resources) { + this.rsrcMan.registerUri(preset.resources.uris); + } + + // load meta + this.meta = _.clone(preset.meta); + + this._setupRoot(preset); }, - /** - * Updates a component. - * @param {string} id - id of the component - * @param {object} options - options to be updated. - * @returns {boolean} - success of the operation - * @memberof Webvs.Main# - */ - updateComponent: function(id, options) { - options = _.clone(options); // use our own copy - options.id = id; - if(id == "root") { - var subComponents = this.rootComponent.detachAllComponents(); - options = _.defaults(options, this.rootComponent.options); - this.rootComponent.destroy(); - this.rootComponent = new Webvs.EffectList(options, subComponents); - this.rootComponent.init(this.gl, this); - return true; - } else { - return this.rootComponent.updateComponent(id, options); - } + // Reset all the components. + resetCanvas: function() { + var preset = this.rootComponent.generateOptionsObj(); + this.rootComponent.destroy(); + this.copier.cleanup(); + this.buffers.destroy(); + this._initGl(); + this._setupRoot(preset); }, + notifyResize: function() { + this.gl.viewport(0, 0, this.gl.drawingBufferWidth, this.gl.drawingBufferHeight); + this.buffers.resize(); + this.trigger("resize", this.gl.drawingBufferWidth, this.gl.drawingBufferHeight); + }, - /** - * Removes a component - * @param {string} id - id of the component to be removed - * @returns {boolean} - success of the operation - * @memberof Webvs.Main# - */ - removeComponent: function(id) { - var component = this.rootComponent.detachComponent(id); - if(component) { - component.destroy(); + setAttribute: function(key, value, options) { + if(key == "meta") { + this.meta = value; return true; - } else { - return false; } + return false; }, - /** - * Moves a component to a different parent - * @param {string} id - id of the component to be moved - * @param {string} newParentId - id of the new parent - * @param {number} pos - position in the new parent - * @returns {boolean} - success of the operation - * @memberof Webvs.Main# - */ - moveComponent: function(id, newParentId, pos) { - var component = this.rootComponent.detachComponent(id); - if(component) { - return this.rootComponent.addComponent(newParentId, component, pos); - } else { - return false; + get: function(key, value) { + if(key == "meta") { + return this.meta; } }, - /** - * Returns resource data. If resource is not defined - * at preset level, its searched in the global resources - * object - * @param {string} name - name of the resource - * @returns resource data. if resource is not found then name itself is returned - * @memberof Webvs.Main# - */ - getResource: function(name) { - var resource; - resource = this.resources[name]; - if(!resource) { - resource = Webvs.Resources[name]; - } - if(!resource) { - resource = name; - } - return resource; + // Generates and returns the instantaneous preset JSON + // representation + toJSON: function() { + var preset = this.rootComponent.toJSON(); + preset = _.pick(preset, "clearFrame", "components"); + preset.resources = this.rsrcMan.toJSON(); + preset.meta = _.clone(this.meta); + return preset; }, - /** - * Sets a preset level resource - * @param {string} name - name of the resource - * @param data - resource data - * @memberof Webvs.Main# - */ - setResource: function(name, data) { - this.resources[name] = data; + destroy: function() { + this.stop(); + this.rootComponent.destroy(); + this.rootComponent = null; + if(this.stats) { + var statsDomElement = this.stats.domElement; + statsDomElement.parentNode.removeChild(statsDomElement); + this.stats = null; + } + this.rsrcMan.destroy(); + this.rsrcMan = null; + this.stopListening(); }, - /** - * Traverses a callback over the component tree - * @param {Webvs.Main~traverseCallback} callback - callback. - * @memberof Webvs.Main# - */ - traverse: function(callback) { - this.rootComponent.traverse(callback); - } - - /** - * This function is called once for each component in the tree - * @callback Webvs.Main~traverseCallback - * @param {string} id - id of the component - * @param {string} parentId - id of the parent. Undefined for root - * @param {object} options - the options for this component. - */ -}); - -Main.ui = { - leaf: false, - disp: "Main", - schema: { - name: { - type: "string", - title: "Name" - }, - author: { - type: "string", - title: "Author" - }, - description: { - type: "string", - title: "Description" - }, - clearFrame: { - type: "boolean", - title: "Clear every frame", - default: false, - required: true + // event handlers + handleRsrcWait: function() { + if(this.isStarted) { + this._stopAnimation(); } }, -}; + + handleRsrcReady: function() { + if(this.isStarted) { + this._startAnimation(); + } + } +}); })(Webvs); @@ -742,106 +970,107 @@ Main.ui = { /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A base class that all Webvs effects extend from. - * @memberof Webvs - * @constructor - * @param {object} options - options object - * @param {object} [options.id] - id for this component. Default is a random string. - */ -function Component(options) { - this.id = options.id; +function Component(gl, main, parent, options) { + this.gl = gl; + this.main = main; + this.parent = parent; + + this.id = options.id; // TODO: check for id uniqueness + if(!this.id) { + this.id = _.uniqueId(this.constructor.Meta.name + "_"); + } this.enabled = _.isUndefined(options.enabled)?true:options.enabled; - this.componentInited = false; - this.options = options; + + this.opts = _.omit(options, ["id", "enabled"]); + if(this.defaultOptions) { + this.opts = _.defaults(this.opts, this.defaultOptions); + } + + this.init(); } -Webvs.Component = Webvs.defineClass(Component, Object, { - /** - * String name of the component class. Used to generate - * id strings. - * @memberof Webvs.Component - */ - componentName: "Component", +Webvs.Component = Webvs.defineClass(Component, Object, Webvs.ModelLike, { + init: function() {}, - /** - * Initialize component. Called once before animation starts. - * Override and implement initialization code - * @abstract - * @param {WebGLContext} gl - webgl context - * @param {Webvs.Main} main - container main object for this component - * @param {Webvs.Component} - parent component - * @memberof Webvs.Component# - */ - init: function(gl, main, parent) { - this.gl = gl; - this.main = main; - this.parent = parent; - this.componentInited = true; - }, + draw: function() {}, - /** - * Adopts or initializes this component, depending on whether - * it is already initialized - * @param {WebGLContext} gl - webgl context - * @param {Webvs.Main} main - container main object for this component - * @param {Webvs.Component} - parent component - * @memberof Webvs.Component# - */ - adoptOrInit: function(gl, main, parent) { - if(this.componentInited) { - return this.adopt(parent); - } else { - return this.init(gl, main, parent); - } + destroy: function() { + this.stopListening(); }, - /** - * Called when the component is moved to a different - * parent. Default implementation simply resets the parent reference. - * Override and implement additional logic if required - * @param {Webvs.Component} newParent - the new parent of this component - * @memberof Webvs.Component# - */ - adopt: function(newParent) { + setParent: function(newParent) { this.parent = newParent; }, - /** - * Render a frame. Called once for every frame, - * Override and implement rendering code - * @abstract - * @memberof Webvs.Component# - */ - update: function() {}, + toJSON: function() { + var opts = _.clone(this.opts); + opts.id = this.id; + opts.type = this.constructor.Meta.name; + opts.enabled = this.enabled; + return opts; + }, - /** - * Release any Webgl resources. Called during - * reinitialization. Override and implement cleanup code - * @abstract - * @memberof Webvs.Component# - */ - destroy: function() {}, + setAttribute: function(key, value, options) { + var oldValue = this.get(key); + if(key == "type" || _.isEqual(value, oldValue)) { + return false; + } - /** - * Returns the component's options - * @memberof Webvs.Component# - */ - getOptions: function() { - return this.options; + // set the property + if(key == "enabled") { + this.enabled = value; + } else if(key == "id") { + this.id = value; + } else { + this.opts[key] = value; + } + + // call all onchange handlers + // we just call these manually here no need to + // go through event triggers + if(this.onChange) { + try { + var onChange = _.flatten([ + this.onChange[key] || [], + this.onChange["*"] || [] + ]); + + for(var i = 0;i < onChange.length;i++) { + this[onChange[i]].call(this, value, key, oldValue); + } + } catch(e) { + // restore old value in case any of the onChange handlers fail + if(key == "enabled") { + this.enabled = oldValue; + } else if(key == "id") { + this.id = oldValue; + } else { + this.opts[key] = oldValue; + } + + this.lastError = e; + this.trigger("error:" + key, this, value, options, e); + } + } + + return true; + }, + + get: function(key) { + if(key == "enabled") { + return this.enabled; + } else if(key == "id") { + return this.id; + } else { + return this.opts[key]; + } }, - /** - * Generates a printable path of this component - * @returns {string} printable path generated from the parent hierarchy - * @memberof Webvs.Component# - */ getPath: function() { if(!_.isUndefined(this.parent) && !_.isUndefined(this.id)) { return this.parent.getIdString() + "/" + this.componentName + "#" + this.id; @@ -854,53 +1083,32 @@ Webvs.Component = Webvs.defineClass(Component, Object, { })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A base class for all components that can have sub components. - * Manages, cloning and component tree operations - * @memberof Webvs - * @constructor - * @param {object} options - options object - * @param {Array.} options.components - options array for all the subcomponents - * @param {Array.} subFactories - factories for subcomponents. If - * provided then subcomponents are added from this factory and options.components is ignored. - * useful when moving existing subcomponent instances into new container. - */ -function Container(options, subComponents) { - Container.super.constructor.call(this, options); - - this.components = []; - - // add all the sub components - _.each(subComponents || options.components || [], function(component) { - this.addComponent(this.id, component); - }, this); - +// A base class for all components that can have sub components. +// Manages, cloning and component tree operations +function Container(gl, main, parent, opts) { + Container.super.constructor.call(this, gl, main, parent, opts); + delete this.opts.components; } Webvs.Container = Webvs.defineClass(Container, Webvs.Component, { - /** - * initializes all the subcomponents - * @memberof Webvs.Container# - */ init: function(gl, main, parent) { - Container.super.init.call(this, gl, main, parent); - - for(var i = 0;i < this.components.length;i++) { - this.components[i].adoptOrInit(gl, main, this); + var components = []; + if(this.opts.components) { + for(var i = 0;i < this.opts.components.length;i++) { + var opts = this.opts.components[i]; + var component = new (Webvs.getComponentClass(opts.type))(this.gl, this.main, this, opts); + components.push(component); + } } + this.components = components; }, - /** - * destroys all subcomponents - * @memberof Webvs.Container# - */ destroy: function() { Container.super.destroy.call(this); for(var i = 0;i < this.components.length;i++) { @@ -908,275 +1116,162 @@ Webvs.Container = Webvs.defineClass(Container, Webvs.Component, { } }, - /** - * Adds a component as child of the given parent that - * resides under this containers subtree - * @param {string} parentId - id of the parent under which the component is - * to be added - * @param {Webvs.ComponentFactory} factory - factory from which component should be - * created. If an options object is passed then a Webvs.ComponentFactory - * is implicitly created from it - * @param {number} [pos] - position at which the component will be inserted. - * default is the end of the list - * @returns {string} - id of the new component - * @memberof Webvs.Container# - */ - addComponent: function(parentId, options, pos) { - if(!(options instanceof Webvs.Component)) { - options.id = options.id || Webvs.randString(5); - } - + createComponent: function(opts) { + return (new (Webvs.getComponentClass(opts.type))(this.gl, this.main, this, opts)); + }, + + // Adds a component as child of the given parent that + // resides under this containers subtree + addComponent: function(componentOpts, pos, options) { var component; - if(parentId == this.id) { - if(options instanceof Webvs.Component) { - component = options; - } else { - component = new (Webvs.getComponentClass(options.type))(options); - } - if(this.componentInited) { - component.adoptOrInit(this.gl, this.main, this); - } - - if(_.isNumber(pos)) { - this.components.splice(pos, 0, component); - } else { - this.components.push(component); - } - return component.id; + if(componentOpts instanceof Webvs.Component) { + component = componentOpts; + component.setParent(this); } else { - for(var i = 0;i < this.components.length;i++) { - component = this.components[i]; - if(component instanceof Container) { - var id = component.addComponent(parentId, options, pos); - if(id) { - return id; - } - } - } + component = this.createComponent(componentOpts); } - }, - - /** - * Updates a component under this container's subtree - * @param {string} id - id of the component - * @param {object} options - options to be updated. - * @returns {boolean} - true if update succeeded else false - * @memberof Webvs.Container# - */ - updateComponent: function(id, options) { - var component, i; - // find the component in this container - for(i = 0;i < this.components.length;i++) { - component = this.components[i]; - if(component.id != id) { - continue; - } - - options = _.defaults(options, component.options); - options.id = id; - var subComponents = component instanceof Container?component.detachAllComponents():undefined; - var newComponent = new (Webvs.getComponentClass(options.type))(options, subComponents); - - if(this.componentInited) { - newComponent.adoptOrInit(this.gl, this.main, this); - } - - this.components[i] = newComponent; - component.destroy(); - return true; + if(!_.isNumber(pos)) { + pos = this.components.length; } + this.components.splice(pos, 0, component); - for(i = 0;i < this.components.length;i++) { - component = this.components[i]; - if(component instanceof Container) { - if(component.updateComponent(id, options)) { - return true; + options = _.defaults({pos: pos}, options); + this.trigger("addComponent", component, this, options); + return component; + }, + + detachComponent: function(pos, options) { + if(_.isString(pos)) { + var id = pos; + var i; + for(i = 0;i < this.components.length;i++) { + if(this.components[i].id == id) { + pos = i; + break; } } + if(i == this.components.length) { + return; + } } - }, + var component = this.components[pos]; + this.components.splice(pos, 1); - /** - * Detaches all components in this container - * @returns {Array.} factories for each subcomponent - * @memberof Webvs.Container# - */ - detachAllComponents: function() { - var components = this.components; - this.components = []; - return components; + options = _.defaults({pos: pos}, options); + this.trigger("detachComponent", component, this, options); + return component; }, - /** - * Detaches a given component under this container's subtree - * @param {string} id - id of the component to be detached - * @returns {Webvs.ComponentFactory} - factory containing the detached component - * @memberof Webvs.Container# - */ - detachComponent: function(id) { - var component, i; - // search for the component in this container + findComponent: function(id) { + var i; for(i = 0;i < this.components.length;i++) { - component = this.components[i]; + var component = this.components[i]; if(component.id == id) { - this.components.splice(i, 1); return component; } } - // try detaching from any of the subcontainers - // aggregating results, in case they are cloned. + // search in any subcontainers for(i = 0;i < this.components.length;i++) { - component = this.components[i]; - if(component instanceof Container) { - var detached = component.detachComponent(id); - if(detached) { - return detached; - } + var container = this.components[i]; + if(!(container instanceof Container)) { + continue; + } + var subComponent = container.findComponent(id); + if(subComponent) { + return subComponent; } } }, - /** - * Constructs complete options object for this container and its - * subtree - * @returns {object} - the options object - * @memberof Webvs.Container# - */ - getOptions: function() { - var options = this.options; - options.components = []; - for(var i = 0;i < this.components.length;i++) { - options.components.push(this.components[i].getOptions()); - } - return options; - }, + // Constructs complete options object for this container and its + // subtree + toJSON: function() { + var opts = Container.super.toJSON.call(this); - /** - * Traverses a callback over this subtree, starting with this container - * @param {Webvs.Container~traverseCallback} callback - callback. - * @memberof Webvs.Container# - */ - traverse: function(callback) { - callback.call(this, this.id, (this.parent?this.parent.id:undefined), this.options); + opts.components = []; for(var i = 0;i < this.components.length;i++) { - var component = this.components[i]; - if(component instanceof Container) { - component.traverse(callback); - } else { - var parentId = component.parent?component.parent.id:undefined; - var id = component.id; - var options = component.options; - callback.call(component, id, parentId, options); - } + opts.components.push(this.components[i].toJSON()); } + return opts; } - - /** - * This function is called once for each component in the tree - * @callback Webvs.Container~traverseCallback - * @param {string} id - id of the component - * @param {string} parentId - id of the parent. Undefined for root - * @param {object} options - the options for this component. - */ }); })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * Effectlist is a container that renders components to a separate buffer. and blends - * it in with the parent buffer. Its also used as the root component in Webvs.Main - * - * @param {object} options - options object - * @param {Array.} options.components - the constructor options object for each subcomponent - * in this effectlist. - * @param {string} options.components[i].type - the component class name - * @param {number} [options.components[i].clone] - the number of times this component should be cloned - * @param {string} [options.output="REPLACE"] - the output blend mode - * @param {string} [options.input="IGNORE"] - the input blend mode - * @param {boolean} [options.clearFrame=false] - if set then the buffer is cleared for each frame - * @param {boolean} [options.enableOnBeat=false] - if set then the subcomponents are rendered only - * for a fixed number of frames on beat - * @param {number} [options.enableOnBeatFor=1] - the number frames for enableOnBeat setting - * - * @augments Webvs.Component - * @memberof Webvs - * @constructor - */ -function EffectList(options) { - options = _.defaults(options, { +// Effectlist is a container that renders components to a separate buffer. and blends +// it in with the parent buffer. Its also used as the root component in Webvs.Main +function EffectList(gl, main, parent, opts) { + EffectList.super.constructor.call(this, gl, main, parent, opts); +} + +Webvs.registerComponent(EffectList, { + name: "EffectList" +}); + +var ELBlendModes = _.extend({ + "IGNORE": 50 +}, Webvs.BlendModes); +EffectList.ELBlendModes = ELBlendModes; + +Webvs.defineClass(EffectList, Webvs.Container, { + defaultOptions: { + code: { + init: "", + perFrame: "" + }, output: "REPLACE", input: "IGNORE", clearFrame: false, enableOnBeat: false, enableOnBeatFor: 1 - }); - - this.output = options.output=="IGNORE"?-1:Webvs.blendModes[options.output]; - this.input = options.input=="IGNORE"?-1:Webvs.blendModes[options.input]; - this.clearFrame = options.clearFrame; - this.enableOnBeat = options.enableOnBeat; - this.enableOnBeatFor = options.enableOnBeatFor; - this.first = true; - this._frameCounter = 0; - this._inited = false; - - var codeGen = new Webvs.ExprCodeGenerator(options.code, ["beat", "enabled", "clear", "w", "h", "cid"]); - this.code = codeGen.generateJs(["init", "perFrame"]); - - EffectList.super.constructor.apply(this, arguments); -} -Webvs.EffectList = Webvs.defineClass(EffectList, Webvs.Container, { - componentName: "EffectList", - - /** - * Initializes the effect list - * @memberof Webvs.EffectList# - */ - init: function(gl, main, parent) { - EffectList.super.init.call(this, gl, main, parent); + }, - this.code.setup(main, this); + onChange: { + code: "updateCode", + output: "updateBlendMode", + input: "updateBlendMode" + }, - // create a framebuffer manager for this effect list - this.fm = new Webvs.FrameBufferManager(main.canvas.width, main.canvas.height, gl, main.copier, parent?true:false); + init: function() { + EffectList.super.init.call(this); + this.fm = new Webvs.FrameBufferManager(this.gl, this.main.copier, this.parent?true:false); + this.updateCode(); + this.updateBlendMode(this.opts.input, "input"); + this.updateBlendMode(this.opts.output, "output"); + this.frameCounter = 0; + this.first = true; + this.listenTo(this.main, "resize", this.handleResize); }, - /** - * Renders a frame of the effect list, by running - * all the subcomponents. - * @memberof Webvs.EffectList# - */ - update: function() { - EffectList.super.update.call(this); - var gl = this.gl; + draw: function() { + var opts = this.opts; - if(this.enableOnBeat) { + if(opts.enableOnBeat) { if(this.main.analyser.beat) { - this._frameCounter = this.enableOnBeatFor; - } else if(this._frameCounter > 0) { - this._frameCounter--; + this.frameCounter = opts.enableOnBeatFor; + } else if(this.frameCounter > 0) { + this.frameCounter--; } // only enable for enableOnBeatFor # of frames - if(this._frameCounter === 0) { + if(this.frameCounter === 0) { return; } } this.code.beat = this.main.analyser.beat?1:0; this.code.enabled = 1; - this.code.clear = this.clearFrame; - if(!this._inited) { - this._inited = true; + this.code.clear = opts.clearFrame; + if(!this.inited) { + this.inited = true; this.code.init(); } this.code.perFrame(); @@ -1188,14 +1283,14 @@ Webvs.EffectList = Webvs.defineClass(EffectList, Webvs.Container, { this.fm.setRenderTarget(); // clear frame - if(this.clearFrame || this.first || this.code.clear) { - gl.clearColor(0,0,0,1); - gl.clear(gl.COLOR_BUFFER_BIT); + if(opts.clearFrame || this.first || this.code.clear) { + this.gl.clearColor(0,0,0,1); + this.gl.clear(this.gl.COLOR_BUFFER_BIT); this.first = false; } // blend input texture onto internal texture - if(this.input !== -1) { + if(this.input !== ELBlendModes.IGNORE) { var inputTexture = this.parent.fm.getCurrentTexture(); this.main.copier.run(this.fm, this.input, inputTexture); } @@ -1203,7 +1298,7 @@ Webvs.EffectList = Webvs.defineClass(EffectList, Webvs.Container, { // render all the components for(var i = 0;i < this.components.length;i++) { if(this.components[i].enabled) { - this.components[i].update(); + this.components[i].draw(); } } @@ -1211,7 +1306,7 @@ Webvs.EffectList = Webvs.defineClass(EffectList, Webvs.Container, { this.fm.restoreRenderTarget(); // blend current texture to the output framebuffer - if(this.output != -1) { + if(this.output != ELBlendModes.IGNORE) { if(this.parent) { this.main.copier.run(this.parent.fm, this.output, this.fm.getCurrentTexture()); } else { @@ -1220,10 +1315,6 @@ Webvs.EffectList = Webvs.defineClass(EffectList, Webvs.Container, { } }, - /** - * Releases resources. - * @memberof Webvs.EffectList# - */ destroy: function() { EffectList.super.destroy.call(this); if(this.fm) { @@ -1231,295 +1322,149 @@ Webvs.EffectList = Webvs.defineClass(EffectList, Webvs.Container, { this.fm.destroy(); } }, + + updateCode: function() { + this.code = Webvs.compileExpr(this.opts.code, ["init", "perFrame"]).codeInst; + this.code.setup(this.main, this); + this.inited = false; + }, + + updateBlendMode: function(value, name) { + this[name] = Webvs.getEnumValue(value, ELBlendModes); + }, + + handleResize: function() { + this.fm.resize(); + this.code.updateDimVars(this.gl); + } }); -EffectList.ui = { - disp: "Effect List", - type: "EffectList", - leaf: false, - schema: { - clearFrame: { - type: "boolean", - title: "Clear Frame", - default: false, - required: true - }, - enableOnBeat: { - type: "boolean", - title: "Enable on beat", - default: false, - }, - enableOnBeatFor: { - type: "number", - title: "Enable on beat for frames", - default: 1 - }, - output: { - type: "string", - title: "Output", - default: "REPLACE", - enum: _.keys(Webvs.blendModes) - }, - input: { - type: "string", - title: "Input", - default: "IGNORE", - enum: _.union(_.keys(Webvs.blendModes), ["IGNORE"]) - } - } -}; - })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * Base class for Webgl Shaders. This provides an abstraction - * with support for blended output, easier variable bindings - * etc. - * - * For outputblending, we try to use GL blendEq and blendFunc - * if possible, otherwise we fallback to shader based blending, - * where we swap the frame, sample the previous texture, and blend - * the colors in the shader itself. To do this seamlessly, shader code in subclasses - * should use a set of macros. eg: setFragColor instead of - * setting gl_FragColor directly. The proper macro implementation - * is inserted based on the blending modes. - * - * #### glsl utilities - * - * The following utilities are usable inside the shader code in subclasses - * - * + `setPosition(vec2 pos)` - sets gl_Position - * + `getSrcColorAtPos(vec2 pos)` - pixel value at pos in u_srcTexture - * + `getSrcColor(vec2 pos)` - same as above, but uses v_position - * + `setFragColor(vec4 color)` - sets the correctly blended fragment color - * + `sampler2D u_srcTexture` - the source texture from previous frame. enabled - * when swapFrame is set to true - * + `vec2 u_resolution` - the screen resolution. enabled only if fm is - * passed to {@link Webvs.ShaderProgram.run} call - * + `vec2 v_position` - a 0-1, 0-1 normalized varying of the vertex. enabled - * when varyingPos option is used - * - * @param {object} options - refer class description - * @param {string} options.vertexShader - the source for the vertex shader - * @param {string} options.fragmentShader - the source for the fragment shader - * @param {boolean} [options.forceShaderBlend=false] - force the use of shader based blending mode - * @param {string} [options.oututBlendMode="REPLACE"] - the output blending mode. - * @param {boolean} [options.dynamicBlend=false] - when set to true, blending mode can be changed - * at runtime even after shader compilation - * @param {boolean} [options.swapFrame=false] - if set then a render target swap is done on the - * framebuffermanager, before rendering. This is used - * by programs where the previous rendering need to be - * sampled - * @param {boolean} [options.copyOnSwap=false] - if set to true then on swap, a copyOver is done on - * the framebuffermanager. This is used to maintain - * consistency during shader based blending in shaders - * that do not touch all the pixels - * @param {boolean} [options.varyingPos=false] - if true then a varying called v_position is added - * automatically - * @param {function} [options.draw ] - override the draw function - * @memberof Webvs - * @constructor - */ -function ShaderProgram(options) { - options = _.defaults(options, { - forceShaderBlend: false, - outputBlendMode: Webvs.REPLACE, - varyingPos: false, - dynamicBlend: false, +// Base class for Webgl Shaders. This provides an abstraction +// with support for blended output, easier variable bindings +// etc. + +// For outputblending, we try to use GL blendEq and blendFunc +// if possible, otherwise we fallback to shader based blending, +// where we swap the frame, sample the previous texture, and blend +// the colors in the shader itself. To do this seamlessly, shader code in subclasses +// should use a set of macros. eg: setFragColor instead of +// setting gl_FragColor directly. The proper macro implementation +// is inserted based on the blending modes. + +// #### glsl utilities + +// The following utilities are usable inside the shader code in subclasses + +// + `setPosition(vec2 pos)` - sets gl_Position +// + `getSrcColorAtPos(vec2 pos)` - pixel value at pos in u_srcTexture +// + `getSrcColor(vec2 pos)` - same as above, but uses v_position +// + `setFragColor(vec4 color)` - sets the correctly blended fragment color +// + `sampler2D u_srcTexture` - the source texture from previous frame. enabled +// when swapFrame is set to true +// + `vec2 u_resolution` - the screen resolution. enabled only if fm is +// passed to {@link Webvs.ShaderProgram.run} call +// + `vec2 v_position` - a 0-1, 0-1 normalized varying of the vertex. enabled +// when varyingPos option is used +function ShaderProgram(gl, opts) { + opts = _.defaults(opts, { + blendMode: Webvs.REPLACE, swapFrame: false, - copyOnSwap: false + copyOnSwap: false, + dynamicBlend: false, + blendValue: 0.5 }); - var fsrc = [ + + var vsrc = [ "precision mediump float;", + "varying vec2 v_position;", "uniform vec2 u_resolution;", - "#define PI "+Math.PI - ]; - var vsrc = _.clone(fsrc); + "uniform sampler2D u_srcTexture;", - if(_.isFunction(options.draw)) { - this.draw = options.draw; - } - this.copyOnSwap = options.copyOnSwap; - this.varyingPos = options.varyingPos; - this.dynamicBlend = options.dynamicBlend; - - // select the blend equation - this.outputBlendMode = options.outputBlendMode; - - if(options.swapFrame || this.dynamicBlend || options.forceShaderBlend || !_.contains(this.glBlendModes, this.outputBlendMode)) { - this.swapFrame = true; - this.glBlendMode = false; - this.varyingPos = true; - } else { - this.swapFrame = false; - this.glBlendMode = true; - } + "#define PI "+Math.PI, + "#define getSrcColorAtPos(pos) (texture2D(u_srcTexture, pos))", + "#define setPosition(pos) (v_position = (((pos)+1.0)/2.0),gl_Position = vec4((pos), 0, 1))" + ]; - // varying position and macros - if(this.varyingPos) { - fsrc.push("varying vec2 v_position;"); - vsrc.push( - "varying vec2 v_position;", - "#define setPosition(pos) (v_position = (((pos)+1.0)/2.0),gl_Position = vec4((pos), 0, 1))" - ); - } else { - vsrc.push("#define setPosition(pos) (gl_Position = vec4((pos), 0, 1))"); - } + var fsrc = [ + "precision mediump float;", + "varying vec2 v_position;", + "uniform vec2 u_resolution;", + "uniform sampler2D u_srcTexture;", - // source teture uniform variable and macors - if(this.swapFrame) { - vsrc.push( - "uniform sampler2D u_srcTexture;", - "#define getSrcColorAtPos(pos) (texture2D(u_srcTexture, pos))" - ); + "#define PI "+Math.PI, + "#define getSrcColorAtPos(pos) (texture2D(u_srcTexture, pos))", + "#define getSrcColor() (texture2D(u_srcTexture, v_position))" + ]; - fsrc.push( - "uniform sampler2D u_srcTexture;", - "#define getSrcColor() (texture2D(u_srcTexture, v_position))", - "#define getSrcColorAtPos(pos) (texture2D(u_srcTexture, pos))" - ); - } + this.gl = gl; + this.swapFrame = opts.swapFrame; + this.copyOnSwap = opts.copyOnSwap; + this.blendValue = opts.blendValue; + this.blendMode = opts.blendMode; + this.dynamicBlend = opts.dynamicBlend; - // color blend macro/function if(this.dynamicBlend) { fsrc.push( "uniform int u_blendMode;", "void setFragColor(vec4 color) {" ); - _.each(this.blendEqs, function(eq, mode) { + _.each(ShaderProgram.shaderBlendEq, function(eq, mode) { fsrc.push( " if(u_blendMode == "+mode+") {", " gl_FragColor = ("+eq+");", - " }" + " }", + " else" ); - }); + }, this); fsrc.push( + " {", + " gl_FragColor = color;", + " }", "}" ); } else { - var blendEq = this.blendEqs[this.glBlendMode?Webvs.REPLACE:this.outputBlendMode]; - if(_.isUndefined(blendEq)) { - throw new Error("Blend Mode " + this.outputBlendMode + " not supported"); + if(this._isShaderBlend(this.blendMode)) { + var eq = ShaderProgram.shaderBlendEq[this.blendMode]; + fsrc.push("#define setFragColor(color) (gl_FragColor = ("+eq+"))"); + } else { + fsrc.push("#define setFragColor(color) (gl_FragColor = color)"); } - fsrc.push("#define setFragColor(color) (gl_FragColor = ("+blendEq+"))"); } - this.fragmentSrc = fsrc.join("\n") + "\n" + options.fragmentShader.join("\n"); - this.vertexSrc = vsrc.join("\n") + "\n" + options.vertexShader.join("\n"); + this.fragmentSrc = fsrc.join("\n") + "\n" + opts.fragmentShader.join("\n"); + this.vertexSrc = vsrc.join("\n") + "\n" + opts.vertexShader.join("\n"); this._locations = {}; this._textureVars = []; this._arrBuffers = {}; -} - -Webvs.ShaderProgram = Webvs.defineClass(ShaderProgram, Object, { - // these are blend modes supported with gl.BLEND - // all other modes have to implemented with shaders - glBlendModes: [ - Webvs.REPLACE, - Webvs.AVERAGE, - Webvs.ADDITIVE, - Webvs.SUBTRACTIVE1, - Webvs.SUBTRACTIVE2, - Webvs.MULTIPLY - ], - - // the blending formulas to be used inside shaders - blendEqs: _.object([ - [Webvs.REPLACE, "color"], - [Webvs.MAXIMUM, "max(color, texture2D(u_srcTexture, v_position))"], - [Webvs.AVERAGE, "(color+texture2D(u_srcTexture, v_position))/2.0"], - [Webvs.ADDITIVE, "color+texture2D(u_srcTexture, v_position)"], - [Webvs.SUBTRACTIVE1, "texture2D(u_srcTexture, v_position)-color"], - [Webvs.SUBTRACTIVE2, "color-texture2D(u_srcTexture, v_position)"], - [Webvs.MULTIPLY, "color*texture2D(u_srcTexture, v_position)"] - ]), - - /** - * initializes and compiles the shaders - * @memberof Webvs.ShaderProgram# - */ - init: function(gl) { - this.gl = gl; - try { - this._compileProgram(this.vertexSrc, this.fragmentSrc); - } catch(e) { - throw e; - } - }, - - /** - * Sets the output blend mode for this shader - * @param {Webvs.blendModes} mode - the blending mode - * @memberof Webvs.ShaderProgram# - */ - setOutputBlendMode: function(mode) { - this.outputBlendMode = mode; - }, - - /** - * Runs this shader program - * @param {Webvs.FrameBufferManager} fm - frame manager. pass null, if no fm is required - * @param {Webvs.blendModes} outputBlendMode - overrides the blendmode. pass null to use default - * @param {...any} extraParams - remaining parameters are passed to the draw function - * @memberof Webvs.ShaderProgram# - */ - run: function(fm, outputBlendMode) { - var gl = this.gl; - var oldProgram = gl.getParameter(gl.CURRENT_PROGRAM); - gl.useProgram(this.program); - - if(fm) { - this.setUniform("u_resolution", "2f", fm.width, fm.height); - if(this.swapFrame) { - this.setUniform("u_srcTexture", "texture2D", fm.getCurrentTexture()); - fm.swapAttachment(); - if(this.copyOnSwap) { - fm.copyOver(); - } - } - } - - if(outputBlendMode && !this.dynamicBlend) { - throw new Error("Cannot set blendmode at runtime. Use dynamicBlend"); - } - outputBlendMode = outputBlendMode || this.outputBlendMode; - if(this.dynamicBlend) { - this.setUniform("u_blendMode", "1i", outputBlendMode); - } - if(this.glBlendMode && outputBlendMode != Webvs.REPLACE) { - gl.enable(gl.BLEND); - this._setGlBlendMode(gl, outputBlendMode); - } else { - gl.disable(gl.BLEND); - } + this._compile(); +} - this.draw.apply(this, _.drop(arguments, 2)); +// these are blend modes not supported with gl.BLEND +// and the formula to be used inside shader +ShaderProgram.shaderBlendEq = _.object([ + [Webvs.MAXIMUM, "max(color, texture2D(u_srcTexture, v_position))"], + [Webvs.MULTIPLY, "clamp(color * texture2D(u_srcTexture, v_position) * 256.0, 0.0, 1.0)"] +]); - gl.disable(gl.BLEND); - gl.useProgram(oldProgram); +Webvs.ShaderProgram = Webvs.defineClass(ShaderProgram, Object, { + _isShaderBlend: function(mode) { + return (mode in ShaderProgram.shaderBlendEq); }, - /** - * Performs the actual drawing and any further bindings and calculations if required. - * @param {...any} extraParams - the extra parameters passed to {@link Webvs.ShaderProgram.run} - * @abstract - * @memberof Webvs.ShaderProgram# - */ - draw: function() {}, - - _compileProgram: function(vertexSrc, fragmentSrc) { + _compile: function() { var gl = this.gl; - var vertex = this._compileShader(vertexSrc, gl.VERTEX_SHADER); - var fragment = this._compileShader(fragmentSrc, gl.FRAGMENT_SHADER); + var vertex = this._compileShader(this.vertexSrc, gl.VERTEX_SHADER); + var fragment = this._compileShader(this.fragmentSrc, gl.FRAGMENT_SHADER); var program = gl.createProgram(); gl.attachShader(program, vertex); gl.attachShader(program, fragment); @@ -1546,40 +1491,95 @@ Webvs.ShaderProgram = Webvs.defineClass(ShaderProgram, Object, { return shader; }, - _setGlBlendMode: function(gl, mode) { + // Performs the actual drawing and any further bindings and calculations if required. + draw: function() {}, + + // Runs this shader program + run: function(fm, blendMode) { + var gl = this.gl; + var oldProgram = gl.getParameter(gl.CURRENT_PROGRAM); + gl.useProgram(this.program); + + if(blendMode && !this.dynamicBlend) { + throw new Error("Cannot set blendmode at runtime. Use dynamicBlend"); + } + blendMode = blendMode || this.blendMode; + + if(fm) { + this.setUniform("u_resolution", "2f", gl.drawingBufferWidth, gl.drawingBufferHeight); + if(this.swapFrame || this._isShaderBlend(blendMode)) { + this.setUniform("u_srcTexture", "texture2D", fm.getCurrentTexture()); + fm.switchTexture(); + if(this.copyOnSwap) { + fm.copyOver(); + } + } else if(this.dynamicBlend) { + this.setUniform("u_srcTexture", "texture2D", null); + } + } + + if(this.dynamicBlend) { + this.setUniform("u_blendMode", "1i", blendMode); + } + + this._setGlBlendMode(blendMode); + this.draw.apply(this, _.drop(arguments, 2)); + gl.disable(gl.BLEND); + gl.useProgram(oldProgram); + }, + + _setGlBlendMode: function(mode) { + var gl = this.gl; switch(mode) { case Webvs.ADDITIVE: + gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE); gl.blendEquation(gl.FUNC_ADD); break; case Webvs.SUBTRACTIVE1: + gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE); gl.blendEquation(gl.FUNC_REVERSE_SUBTRACT); break; case Webvs.SUBTRACTIVE2: + gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE); gl.blendEquation(gl.FUNC_SUBTRACT); break; - case Webvs.MULTIPLY: + case Webvs.ALPHA: + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + gl.blendEquation(gl.FUNC_ADD); + break; + case Webvs.MULTIPLY2: + gl.enable(gl.BLEND); gl.blendFunc(gl.DST_COLOR, gl.ZERO); gl.blendEquation(gl.FUNC_ADD); break; + case Webvs.ADJUSTABLE: + gl.enable(gl.BLEND); + gl.blendColor(0, 0, 0, this.blendValue); + gl.blendFunc(gl.CONSTANT_ALPHA, gl.ONE_MINUS_CONSTANT_ALPHA); + gl.blendEquation(gl.FUNC_ADD); + break; case Webvs.AVERAGE: + gl.enable(gl.BLEND); gl.blendColor(0.5, 0.5, 0.5, 1); gl.blendFunc(gl.CONSTANT_COLOR, gl.CONSTANT_COLOR); gl.blendEquation(gl.FUNC_ADD); break; - default: throw new Error("Invalid blend mode"); + // shader blending cases + case Webvs.REPLACE: + case Webvs.MULTIPLY: + case Webvs.MAXIMUM: + gl.disable(gl.BLEND); + break; + default: + throw new Error("Unknown blend mode " + mode + " in shader"); } }, - /** - * returns the location of a uniform or attribute. locations are cached. - * @param {string} name - name of the variable - * @param {boolean} [attrib] - pass true if variable is attribute - * @returns {location} - * @memberof Webvs.ShaderProgram# - */ + // returns the location of a uniform or attribute. locations are cached. getLocation: function(name, attrib) { var location = this._locations[name]; if(_.isUndefined(location)) { @@ -1593,12 +1593,7 @@ Webvs.ShaderProgram = Webvs.defineClass(ShaderProgram, Object, { return location; }, - /** - * returns the index of a texture. assigns id if not already assigned. - * @param {string} name - name of the varaible - * @returns {number} index of the texture - * @memberof Webvs.ShaderProgram# - */ + // returns the index of a texture. assigns id if not already assigned. getTextureId: function(name) { var id = _.indexOf(this._textureVars, name); if(id === -1) { @@ -1608,13 +1603,7 @@ Webvs.ShaderProgram = Webvs.defineClass(ShaderProgram, Object, { return id; }, - /** - * binds value of a uniform variable in this program - * @param {string} name - name of the variable - * @param {string} type - type of the variable (texture2D, [1234]f, [1234]i, [1234]fv, [1234]iv) - * @param {...any} values - values to be assigned - * @memberof Webvs.ShaderProgram# - */ + // binds value of a uniform variable in this program setUniform: function(name, type, value) { var location = this.getLocation(name); var gl = this.gl; @@ -1637,17 +1626,7 @@ Webvs.ShaderProgram = Webvs.defineClass(ShaderProgram, Object, { } }, - /** - * binds the vertex attribute array - * @param {string} name - name of the variable - * @param {Array} array - array of vertex data - * @param {number} [size=2] - size of each item - * @param [type=gl.FLOAT] - * @param [normalized=false] - * @param [stride=0] - * @param [offset=0] - * @memberof Webvs.ShaderProgram# - */ + // binds the vertex attribute array setVertexAttribArray: function(name, array, size, type, normalized, stride, offset) { var gl = this.gl; size = size || 2; @@ -1682,12 +1661,9 @@ Webvs.ShaderProgram = Webvs.defineClass(ShaderProgram, Object, { gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, array, gl.STATIC_DRAW); }, - /** - * destroys webgl resources consumed by this program. - * call in component destroy - * @memberof Webvs.ShaderProgram# - */ - cleanup: function() { + // destroys webgl resources consumed by this program. + // call in component destroy + destroy: function() { var gl = this.gl; _.each(this._buffers, function(buffer) { gl.deleteBuffer(buffer); @@ -1695,46 +1671,34 @@ Webvs.ShaderProgram = Webvs.defineClass(ShaderProgram, Object, { gl.deleteProgram(this.program); gl.deleteShader(this.vertexShader); gl.deleteShader(this.fragmentShader); - }, + } }); - })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A Base for shaders that provides a vertexShader and vertices - * for a rectangle that fills the entire screen - * @param {object} options - the options object. passed along to {@link Webvs.ShaderProgram} - * @augments Webvs.QuadBoxProgram - * @memberof Webvs - * @constructor - */ -function QuadBoxProgram(options) { +// A Base for shaders that provides a vertexShader and vertices +// for a rectangle that fills the entire screen +function QuadBoxProgram(gl, options) { options = _.defaults(options, { vertexShader: [ "attribute vec2 a_position;", "void main() {", " setPosition(a_position);", "}" - ], - varyingPos: true + ] }); - QuadBoxProgram.super.constructor.call(this, options); + QuadBoxProgram.super.constructor.call(this, gl, options); } Webvs.QuadBoxProgram = Webvs.defineClass(QuadBoxProgram, Webvs.ShaderProgram, { - /** - * Sets the vertices for the quad box - * @memberof Webvs.QuadBoxProgram# - */ + // Sets the vertices for the quad box draw: function() { this.setVertexAttribArray( "a_position", @@ -1754,21 +1718,14 @@ Webvs.QuadBoxProgram = Webvs.defineClass(QuadBoxProgram, Webvs.ShaderProgram, { })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A Shader that copies given texture onto current buffer - * @param {object} options - the options object. passed along to {@link Webvs.ShaderProgram} - * @augments Webvs.QuadBoxProgram - * @memberof Webvs - * @constructor - */ -function CopyProgram(options) { +// A Shader that copies given texture onto current buffer +function CopyProgram(gl, options) { options = _.defaults(options||{}, { fragmentShader: [ "uniform sampler2D u_copySource;", @@ -1777,14 +1734,10 @@ function CopyProgram(options) { "}" ] }); - CopyProgram.super.constructor.call(this, options); + CopyProgram.super.constructor.call(this, gl, options); } Webvs.CopyProgram = Webvs.defineClass(CopyProgram, Webvs.QuadBoxProgram, { - /** - * Renders this shader - * @param {WebGLTexture} srcTexture - the texture to be copied to the screen - * @memberof Webvs.CopyProgram# - */ + // Renders this shader draw: function(srcTexture) { this.setUniform("u_copySource", "texture2D", srcTexture); CopyProgram.super.draw.call(this); @@ -1794,32 +1747,18 @@ Webvs.CopyProgram = Webvs.defineClass(CopyProgram, Webvs.QuadBoxProgram, { })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * FrameBufferManager maintains a set of render targets - * and can switch between them. - * - * @param {number} width - the width of the textures to be initialized - * @param {number} height - the height of the textures to be initialized - * @param {WebGLRenderingContext} gl - the webgl context to be used - * @param {Webvs.CopyProgram} copier - an instance of a CopyProgram that should be used - * when a frame copyOver is required - * @param {boolean} textureOnly - if set then only texture's and renderbuffers are maintained - * @constructor - * @memberof Webvs - */ -function FrameBufferManager(width, height, gl, copier, textureOnly, texCount) { +// FrameBufferManager maintains a set of render targets +// and can switch between them. +function FrameBufferManager(gl, copier, textureOnly, texCount) { this.gl = gl; - this.width = width; - this.height = height; this.copier = copier; - this.texCount = texCount || 2; + this.initTexCount = _.isNumber(texCount)?texCount:2; this.textureOnly = textureOnly; this._initFrameBuffers(); } @@ -1831,153 +1770,193 @@ Webvs.FrameBufferManager = Webvs.defineClass(FrameBufferManager, Object, { this.framebuffer = gl.createFramebuffer(); } - var attachments = []; - for(var i = 0;i < this.texCount;i++) { - var texture = gl.createTexture(); - gl.bindTexture(gl.TEXTURE_2D, texture); - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, - 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - - var renderbuffer = gl.createRenderbuffer(); - gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height); + this.names = {}; + this.textures = []; + for(var i = 0;i < this.initTexCount;i++) { + this.addTexture(); + } + this.curTex = 0; + this.isRenderTarget = false; + }, - attachments[i] = { - texture: texture, - renderbuffer: renderbuffer + addTexture: function(name) { + if(name && name in this.names) { + this.names[name].refCount++; + return this.names[name]; + } + var gl = this.gl; + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.drawingBufferWidth, + gl.drawingBufferHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + this.textures.push(texture); + if(name) { + this.names[name] = { + refCount: 1, + index: this.textures.length-1 }; } + return this.textures.length-1; + }, - this.frameAttachments = attachments; - this.currAttachment = 0; + removeTexture: function(arg) { + if(_.isString(arg) && arg in this.names) { + if(this.names[arg].refCount > 1) { + this.names[arg].refCount--; + return; + } + } + var index = this._findIndex(arg); + if(index == this.curTex && (this.oldTexture || this.oldFrameBuffer)) { + throw new Error("Cannot remove current texture when set as render target"); + } + var gl = this.gl; + gl.deleteTexture(this.textures[index]); + this.textures.splice(index, 1); + if(this.curTex >= this.textures.length) { + this.curTex = this.textures.lenght-1; + } + delete this.names[arg]; }, - /** - * Saves the current render target and sets this - * as the render target - * @memberof Webvs.FrameBufferManager# - */ - setRenderTarget: function() { + // Saves the current render target and sets this + // as the render target + setRenderTarget: function(texName) { var gl = this.gl; + var curFrameBuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); if(this.textureOnly) { - this.oldAttachment = this._getFBAttachment(); + if(!curFrameBuffer) { + throw new Error("Cannot use textureOnly when current rendertarget is the default FrameBuffer"); + } + this.oldTexture = gl.getFramebufferAttachmentParameter( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); } else { - this.oldFrameBuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); + this.oldFrameBuffer = curFrameBuffer; gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); - gl.viewport(0, 0, this.width, this.height); } - this._setFBAttachment(); + this.isRenderTarget = true; + if(!_.isUndefined(texName)) { + this.switchTexture(texName); + } else { + var texture = this.textures[this.curTex]; + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, + texture, 0); + } }, - /** - * Restores the render target previously saved with - * a {@link Webvs.FrameBufferManager.setRenderTarget} call - * @memberof Webvs.FrameBufferManager# - */ + // Restores the render target previously saved with + // a Webvs.FrameBufferManager.setRenderTarget call restoreRenderTarget: function() { var gl = this.gl; if(this.textureOnly) { - this._setFBAttachment(this.oldAttachment); + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, + this.oldTexture, 0); + this.oldTexture = null; } else { gl.bindFramebuffer(gl.FRAMEBUFFER, this.oldFrameBuffer); + this.oldFrameBuffer = null; } + this.isRenderTarget = false; }, - /** - * Returns the texture that is currently being used - * @returns {WebGLTexture} - * @memberof Webvs.FrameBufferManager# - */ + // Returns the texture that is currently being used getCurrentTexture: function() { - return this.frameAttachments[this.currAttachment].texture; + return this.textures[this.curTex]; }, - /** - * Copies the previous texture into the current texture - * @memberof Webvs.FrameBufferManager# - */ + getTexture: function(arg) { + var index = this._findIndex(arg); + return this.textures[index]; + }, + + // Copies the previous texture into the current texture copyOver: function() { - var prevTexture = this.frameAttachments[Math.abs(this.currAttachment-1)%this.texCount].texture; + var texCount = this.textures.length; + var prevTexture = this.textures[(texCount+this.curTex-1)%texCount]; this.copier.run(null, null, prevTexture); }, - /** - * Swaps the current texture - * @memberof Webvs.FrameBufferManager# - */ - swapAttachment : function() { - this.currAttachment = (this.currAttachment + 1) % this.texCount; - this._setFBAttachment(); + // Swaps the current texture + switchTexture: function(arg) { + if(!this.isRenderTarget) { + throw new Error("Cannot switch texture when not set as rendertarget"); + } + var gl = this.gl; + this.curTex = _.isUndefined(arg)?(this.curTex+1):this._findIndex(arg); + this.curTex %= this.textures.length; + var texture = this.textures[this.curTex]; + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, texture, 0); }, - /** - * cleans up all webgl resources - * @memberof Webvs.FrameBufferManager# - */ - destroy: function() { + resize: function() { + // TODO: investigate chrome warning: INVALID_OPERATION: no texture var gl = this.gl; - for(var i = 0;i < this.texCount;i++) { - gl.deleteRenderbuffer(this.frameAttachments[i].renderbuffer); - gl.deleteTexture(this.frameAttachments[i].texture); + for(var i = 0;i < this.textures.length;i++) { + gl.bindTexture(gl.TEXTURE_2D, this.textures[i]); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.drawingBufferWidth, + gl.drawingBufferHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); } }, - - _getFBAttachment: function() { + // cleans up all webgl resources + destroy: function() { var gl = this.gl; - return { - texture: gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME), - renderbuffer: gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) - }; + for(var i = 0;i < this.textures.length;i++) { + gl.deleteTexture(this.textures[i]); + } + if(!this.textureOnly) { + gl.deleteFramebuffer(this.frameBuffer); + } }, - _setFBAttachment: function(attachment) { - attachment = attachment || this.frameAttachments[this.currAttachment]; - var gl = this.gl; - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, attachment.texture, 0); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, attachment.renderbuffer); - }, + _findIndex: function(arg) { + var index; + if(_.isString(arg) && arg in this.names) { + index = this.names[arg].index; + } else if(_.isNumber(arg) && arg >=0 && arg < this.textures.length) { + index = arg; + } else { + throw new Error("Unknown texture '" + arg + "'"); + } + return index; + } }); })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A Shader that clears the screen to a given color - * @param {number} blendMode - blending mode for this shader - * @augments Webvs.QuadBoxProgram - * @memberof Webvs - * @constructor - */ -function ClearScreenProgram(blendMode) { - ClearScreenProgram.super.constructor.call(this, { +// A Shader that clears the screen to a given color +function ClearScreenProgram(gl, blendMode) { + ClearScreenProgram.super.constructor.call(this, gl, { fragmentShader: [ "uniform vec3 u_color;", "void main() {", " setFragColor(vec4(u_color, 1));", "}" ], - outputBlendMode: blendMode + blendMode: blendMode }); } Webvs.ClearScreenProgram = Webvs.defineClass(ClearScreenProgram, Webvs.QuadBoxProgram, { - /** - * Renders this shader - * @param {Array.} color - color to which the screen will be cleared - * @memberof Webvs.ClearScreenProgram# - */ + // Renders this shader draw: function(color) { this.setUniform.apply(this, ["u_color", "3f"].concat(color)); ClearScreenProgram.super.draw.call(this); @@ -1987,29 +1966,17 @@ Webvs.ClearScreenProgram = Webvs.defineClass(ClearScreenProgram, Webvs.QuadBoxPr })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * Base class for AVS expression Syntax Tree - * @memberof Webvs - */ +// Base class for AVS expression Syntax Tree function AstBase() {} Webvs.AstBase = Webvs.defineClass(AstBase, Object); -/** - * @class - * Binary Expression - * @augments Webvs.AstBase - * @param {string} operator - * @param {string} leftOperand - * @param {string} rightOperand - * @memberof Webvs - */ +// Binary Expression function AstBinaryExpr(operator, leftOperand, rightOperand) { this.operator = operator; this.leftOperand = leftOperand; @@ -2017,68 +1984,34 @@ function AstBinaryExpr(operator, leftOperand, rightOperand) { } Webvs.AstBinaryExpr = Webvs.defineClass(AstBinaryExpr, AstBase); -/** - * @class - * Unary Expression - * @augments Webvs.AstBase - * @param {string} operator - * @param {string} operand - * @memberof Webvs - */ +// Unary Expression function AstUnaryExpr(operator, operand) { this.operator = operator; this.operand = operand; } Webvs.AstUnaryExpr = Webvs.defineClass(AstUnaryExpr, AstBase); -/** - * @class - * Function call - * @augments Webvs.AstBase - * @param {string} funcName - function identifier - * @param {Array.} args - argument expressions - * @memberof Webvs - */ +// Function call function AstFuncCall(funcName, args) { this.funcName = funcName; this.args = args; } Webvs.AstFuncCall = Webvs.defineClass(AstFuncCall, AstBase); -/** - * @class - * Variable assignment - * @augments Webvs.AstBase - * @param {string} lhs - identifier - * @param {Array.} expr - expression being assigned - * @memberof Webvs - */ +// Variable assignment function AstAssignment(lhs, expr) { this.lhs = lhs; this.expr = expr; } Webvs.AstAssignment = Webvs.defineClass(AstAssignment, AstBase); -/** - * @class - * Code start symbol - * @augments Webvs.AstBase - * @param {Array.} statements - statements in the program - * @memberof Webvs - */ +// Code start symbol function AstProgram(statements) { this.statements = statements; } Webvs.AstProgram = Webvs.defineClass(AstProgram, AstBase); -/** - * @class - * Atomic expression - * @augments Webvs.AstBase - * @param value - * @param {String} type - type of the atom viz. "ID", "CONST", "REG", "VALUE" - * @memberof Webvs - */ +// Atomic expression function AstPrimaryExpr(value, type) { this.value = value; this.type = type; @@ -4252,34 +4185,23 @@ Webvs.PegExprParser = (function(){ return result; })(); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * An object that encapsulates the generated executable code - * and its state values. Also contains implementations of - * functions callable from expressions - * @constructor - * @memberof Webvs - */ +// An object that encapsulates the generated executable code +// and its state values. Also contains implementations of +// functions callable from expressions function CodeInstance() {} Webvs.CodeInstance = Webvs.defineClass(CodeInstance, Object, { - /** - * avs expression rand function - * @memberof Webvs.CodeInstance# - */ + // avs expression rand function rand: function(max) { return Math.floor(Math.random() * max) + 1; }, - /** - * avs expression gettime function - * @memberof Webvs.CodeInstance# - */ + // avs expression gettime function gettime: function(startTime) { switch(startTime) { case 0: @@ -4289,10 +4211,7 @@ Webvs.CodeInstance = Webvs.defineClass(CodeInstance, Object, { } }, - /** - * avs expression getosc function - * @memberof Webvs.CodeInstance# - */ + // avs expression getosc function getosc: function(band, width, channel) { var osc = this._analyser.getWaveform(); var pos = Math.floor((band - width/2)*(osc.length-1)); @@ -4305,42 +4224,27 @@ Webvs.CodeInstance = Webvs.defineClass(CodeInstance, Object, { return sum/(end-pos+1); }, - /** - * bind state values to uniforms - * @param {Webvs.ShaderProgram} program - program to which the state values - * should be bound - * @memberof Webvs.CodeInstance# - */ + // bind state values to uniforms bindUniforms: function(program) { - var that = this; // bind all values - var toBeBound = _.difference(_.keys(this), this._treatAsNonUniform); - _.each(toBeBound, function(name) { - var value = that[name]; - if(typeof value !== "number") { return; } - program.setUniform(name, "1f", value); - }); + _.each(this._uniforms, function(name) { + program.setUniform(name, "1f", this[name]); + }, this); // bind registers - _.each(this._registerUsages, function(name) { + _.each(this._glslRegisters, function(name) { program.setUniform(name, "1f", this._registerBank[name]); - }); + }, this); // bind random step value if there are usages of random - if(this.hasRandom) { + if(this._hasRandom) { var step = [Math.random()/100, Math.random()/100]; program.setUniform("__randStep", "2fv", step); } - // bind time values for gettime calls - if(this.hasGettime) { - var time0 = ((new Date()).getTime()-this._bootTime)/1000; - program.setUniform("__gettime0", "1f", time0); - } - // bind precomputed values - _.each(this._preCompute, function(item, index) { - var args = _.map(_.last(item, item.length-2), function(arg) { + _.each(this._preCompute, function(entry, name) { + var args = _.map(_.drop(entry), function(arg) { if(_.isString(arg)) { if(arg.substring(0, 5) == "__REG") { return this._registerBank[arg]; @@ -4350,25 +4254,18 @@ Webvs.CodeInstance = Webvs.defineClass(CodeInstance, Object, { } else { return arg; } - }); - var result = this[item[0]].apply(this, args); - program.setUniform(item[1], "1f", result); - }); + }, this); + var result = this[entry[0]].apply(this, args); + program.setUniform(name, "1f", result); + }, this); }, - /** - * initializes this codeinstance - * @param {Webvs.Main} main - webvs main instance - * @param {Webvs.Component} parent - the component thats using this codeinstance - * @memberof Webvs.CodeInstance# - */ + // initializes this codeinstance setup: function(main, parent) { this._registerBank = main.registerBank; this._bootTime = main.bootTime; this._analyser = main.analyser; - - this.w = main.canvas.width; - this.h = main.canvas.height; + this.updateDimVars(parent.gl); // clear all used registers _.each(this._registerUsages, function(name) { @@ -4376,553 +4273,558 @@ Webvs.CodeInstance = Webvs.defineClass(CodeInstance, Object, { main.registerBank[name] = 0; } }); + }, + + updateDimVars: function(gl) { + this.w = gl.drawingBufferWidth; + this.h = gl.drawingBufferHeight; } }); -CodeInstance.clone = function(codeInst, count) { - codeInst.cid = 0; - var clones = [codeInst]; - if(count > 1) { - _.times(count-1, function(index) { - var clone = _.clone(codeInst); - clone.cid = index+1; +// creates an array of clones of code instances +CodeInstance.clone = function(clones, count) { + if(!_.isArray(clones)) { + clones.cid = 0; + clones = [codeInst]; + } + + var clonesLength = clones.length; + if(clonesLength < count) { + _.times(count-clonesLength, function(index) { + var clone = Object.create(CodeInstance.prototype); + _.extend(clone, clones[0]); + clone.cid = index+clonesLength; clones.push(clone); }); + } else if(clonesLength > count) { + clones = _.first(this.clones, count); } return clones; }; +// copies instance values from one code instance to another +CodeInstance.copyValues = function(dest, src) { + _.each(src, function(name, value) { + if(!_.isFunction(value) && name.charAt(0) !== "_") { + dest[name] = value; + } + }); +}; + })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * AVS expression parser and code generator. - * Generates JS and GLSL code from avs expressions - * @param {object.} codeSrc - object containing avs expression code string - * @param {Array.} externalVars - list of variables that will be supplied externally. - * @memberof Webvs - * @constructor - */ -function ExprCodeGenerator(codeSrc, externalVars) { - this.codeSrc = {}; - for(var key in codeSrc) { - var code = codeSrc[key]; + +Webvs.compileExpr = function(codeSrc, jsFuncs, glslFuncs, nonUniforms) { + jsFuncs = jsFuncs || []; + glslFuncs = glslFuncs || []; + nonUniforms = nonUniforms || []; + + // cleanup code source + codeSrc = _.chain(codeSrc).map(function(code, name) { if(_.isArray(code)) { code = code.join("\n"); } code = code.trim(); - if(code !== "") { - this.codeSrc[key] = code; + return [name, code]; + }).filter(function(code) { + return code[1].length > 0; + }).object().value(); + + // 1) Parse the code + var codeAst = parseCode(codeSrc); + // 2) Process the AST + var tables = processAst(codeAst, jsFuncs, glslFuncs, nonUniforms); + // 3) Generate code + var codeInst = generateJs(codeAst, tables, jsFuncs); + var glslCode = generateGlsl(codeAst, tables, glslFuncs); + + return {codeInst: codeInst, glslCode: glslCode}; +}; + + +function parseCode(codeSrc) { + var codeAst = {}; // abstract syntax tree + for(var name in codeSrc) { + try { + codeAst[name] = Webvs.PegExprParser.parse(codeSrc[name]); + } catch(e) { + throw new Error("Error parsing " + name + " (" + e.line + ":" + e.column + ")" + " : " + e); } } - this.externalVars = _.union(externalVars || [], ["w", "h", "cid"]); - this._parseSrc(); + return codeAst; } -Webvs.ExprCodeGenerator = Webvs.defineClass(ExprCodeGenerator, Object, { - _parseSrc: function() { - // Generate AST and find variables usages in all the expressions - var codeAst = {}; - var variables = []; - var funcUsages = {}; - var registerUsages = []; - for(var name in this.codeSrc) { - try { - var codeSrc = this.codeSrc[name]; - codeAst[name] = Webvs.PegExprParser.parse(codeSrc); - var vars = []; - var fu = []; - this._getVars(codeAst[name], variables, fu, registerUsages); - funcUsages[name] = fu; - } catch(e) { - throw new Error("Error parsing " + name + "(" + e.line + ":" + e.column + ")" + " : " + e); - } - } - this.codeAst = codeAst; - this.funcUsages = funcUsages; - // find instance variables - this.instanceVars = _.uniq(this.externalVars.concat(variables)); +function processAst(codeAst, jsFuncs, glslFuncs, extraNonUniforms) { + var tables = { + funcCall: {}, + variable: {}, + register: {}, + preCompute: {} + }; - // find register variable usages - this.registerUsages = _.uniq(registerUsages); - }, + var preComputeCounter = 0; - /** - * Generates js and glsl executable code for each expression code string - * @param {Array.} jsFuncs - functions to be generated as javascript - * @param {Array.} jsFuncs - functions to be generated as glsl - * @param {Array.} treatAsNonUniform - variables to be treated as - * uniform variables in the glsl code - * @returns {Array} pair containing {@link Webvs.CodeInstance} and a glsl code - * @memberof Webvs.ExprCodeGenerator# - */ - generateJs: function(jsFuncs) { - var codeInst = new Webvs.CodeInstance(); - - _.each(this.instanceVars, function(ivar) { - codeInst[ivar] = 0; - }); - - var jsFuncList = _.intersection(_.keys(this.codeAst), jsFuncs); - var missingJsFuncList = _.difference(jsFuncs, jsFuncList); - - // generate javascript functions and assign to code instance - _.each(jsFuncList, function(name) { - var ast = this.codeAst[name]; - var codeString = this._generateJs(ast); - codeInst[name] = new Function(codeString); - }, this); - // add noops for missing expressions - _.each(missingJsFuncList, function(name) { - codeInst[name] = Webvs.noop; - }); - - codeInst._registerUsages = this.registerUsages; - - return codeInst; - }, + function processNode(ast, name) { + var i; + if(ast instanceof Webvs.AstProgram) { + for(i = 0;i < ast.statements.length;i++) { + processNode(ast.statements[i], name); + } + } else if(ast instanceof Webvs.AstBinaryExpr) { + processNode(ast.leftOperand, name); + processNode(ast.rightOperand, name); + } + else if(ast instanceof Webvs.AstUnaryExpr) { + processNode(ast.operand, name); + } + else if(ast instanceof Webvs.AstFuncCall) { + checkFunc(ast); + + // if its a precomputable function to be generated in glsl + // then build a table entry + if(_.contains(glslFuncs, name) && _.contains(glslPreComputeFuncs, ast.funcName)) { + var allStatic = _.every(ast.args, function(arg) { + return arg instanceof Webvs.AstPrimaryExpr; + }); + if(!allStatic) { + throw new Error("Non Pre-Computable arguments for "+ast.funcName+" in shader code, use variables or constants"); + } + var entry = [ast.funcName].concat(_.map(ast.args, function(arg) {return arg.value;})); + var uniformName; + for(var key in tables.preCompute) { + if(tables.preCompute[key] == entry) { + break; + } + } + if(!uniformName) { + uniformName = "__PC_" + ast.funcName + "_" + preComputeCounter++; + tables.preCompute[uniformName] = entry; + } - generateGlsl: function(glslFuncs, treatAsNonUniform, codeInst) { - var glsl = []; - treatAsNonUniform = treatAsNonUniform || []; + ast.preComputeUniformName = uniformName; + } - _.each(this.instanceVars, function(ivar) { - // create declarations for instance variables in glsl - var prefix = ""; - if(!_.contains(treatAsNonUniform, ivar)) { - prefix = "uniform "; + tables.funcCall[name].push(ast.funcName); + for(i = 0;i < ast.args.length;i++) { + processNode(ast.args[i], name); } - glsl.push(prefix + "float " + ivar + ";"); - }); + } + else if(ast instanceof Webvs.AstAssignment) { + processNode(ast.lhs, name); + processNode(ast.expr, name); + } + else if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "ID") { + tables.variable[name].push(ast.value); + } + else if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "REG") { + tables.register[name].push(ast.value); + } + } - var glslFuncList = _.intersection(_.keys(this.codeAst), glslFuncs); - var missingGlslFuncList = _.difference(glslFuncs, glslFuncList); - var glsFuncUsages = _.uniq( - _.flatMap(glslFuncList, function(name) { return this.funcUsages[name]; }, this) - ); - // include required functions in glsl - _.each(glsFuncUsages, function(usage) { - var code = this.glslFuncCode[usage]; - if(!code) { - return; - } - glsl.push(code); - }, this); - var preCompute = []; // list of precomputed bindings - var generatedGlslFuncs = []; - // generate glsl functions - _.each(glslFuncList, function(name) { - var ast = this.codeAst[name]; - var codeString = this._generateGlsl(ast, preCompute); - generatedGlslFuncs.push("void " + name + "() {"); - generatedGlslFuncs.push(codeString); - generatedGlslFuncs.push("}"); - }, this); - // add the uniform declarations for precomputed functions - glsl = glsl.concat(_.map(preCompute, function(item) { - return "uniform float " + item[1] + ";"; - })); - glsl = glsl.concat(generatedGlslFuncs); - - // generate noops for missing functions - _.each(missingGlslFuncList, function(name) { - glsl.push("void " + name + "() {}"); - }); + for(var name in codeAst) { + tables.funcCall[name] = []; + tables.variable[name] = []; + tables.register[name] = []; - // create required bindings in the code instance - codeInst._preCompute = preCompute; - if(_.contains(glslFuncList, "rand")) { - codeInst.hasRandom = true; - } - if(_.contains(glslFuncList, "gettime")) { - codeInst.hasGettime = true; - } - codeInst._treatAsNonUniform = treatAsNonUniform; - - return glsl.join("\n"); - }, - - funcArgLengths: { - "above": 2, - "below": 2, - "equal": 2, - "pow": 2, - "sqr": 1, - "sqrt": 1, - "invsqrt": 1, - "floor" : 1, - "ceil" : 1, - "abs": 1, - "if": 3, - "min": 2, - "max": 2, - "sin": 1, - "cos": 1, - "tan": 1, - "asin": 1, - "acos": 1, - "atan": 1, - "atan2": 2, - "log": 1, - "band": 2, - "bor": 2, - "bnot": 1, - "rand": 1, - "gettime": 1, - "getosc": 3, - "select": {min: 2} - }, - - jsMathFuncs: ["min", "max", "sin", "cos", "abs", "tan", "asin", "acos", "atan", "log", "pow", "sqrt", "floor", "ceil"], - - glslFuncCode: { - "rand": [ - "uniform vec2 __randStep;", - "vec2 __randSeed;", - "float rand(float max) {", - " __randCur += __randStep;", - " float val = fract(sin(dot(__randSeed.xy ,vec2(12.9898,78.233))) * 43758.5453);", - " return (floor(val*max)+1);", - "}" - ].join("\n"), - "gettime": [ - "uniform float __gettime0;", - "int gettime(int startTime) {", - " int time = 0;", - " if(startTime == 0) {", - " time = __gettime0;", - " }", - " return time;", - "}" - ].join("\n") - }, + processNode(codeAst[name], name); - _checkFunc: function(ast) { - var requiredArgLength = this.funcArgLengths[ast.funcName]; - if(requiredArgLength === undefined) { - throw Error("Unknown function " + ast.funcName); - } - if(_.isNumber(requiredArgLength)) { - if(ast.args.length != requiredArgLength) { - throw Error(ast.funcName + " accepts " + requiredArgLength + " arguments"); - } - } else if(requiredArgLength.min) { - if(ast.args.length < requiredArgLength.min) { - throw Error(ast.funcName + " accepts atleast " + requiredArgLength.min + " arguments"); - } - } - }, + tables.funcCall[name] = _.uniq(tables.funcCall[name]); + tables.variable[name] = _.uniq(tables.variable[name]); + tables.register[name] = _.uniq(tables.register[name]); + } - _generateGlsl: function(ast, preCompute) { + tables.jsVars = _.chain(tables.variable).pick(jsFuncs ).values().flatten().uniq().value(); + tables.glslVars = _.chain(tables.variable).pick(glslFuncs).values().flatten().uniq().value(); + tables.nonUniforms = _.chain(tables.glslVars).difference(tables.jsVars).union(extraNonUniforms).uniq().value(); + tables.uniforms = _.intersection(tables.glslVars, tables.jsVars); + tables.glslUsedFuncs = _.chain(tables.funcCall).pick(glslFuncs).values().flatten().uniq().value(); + tables.glslRegisters = _.chain(tables.register).pick(glslFuncs).values().flatten().uniq().value(); + return tables; +} + +function generateJs(codeAst, tables, jsFuncs) { + function generateNode(ast) { if(ast instanceof Webvs.AstBinaryExpr) { - return "(" + this._generateGlsl(ast.leftOperand, preCompute) + ast.operator + this._generateGlsl(ast.rightOperand, preCompute) + ")"; + return "(" + generateNode(ast.leftOperand) + ast.operator + generateNode(ast.rightOperand) + ")"; } if(ast instanceof Webvs.AstUnaryExpr) { - return "(" + ast.operator + this._generateGlsl(ast.operand, preCompute) + ")"; + return "(" + ast.operator + generateNode(ast.operand) + ")"; } if(ast instanceof Webvs.AstFuncCall) { - this._checkFunc(ast); switch(ast.funcName) { case "above": return [ "(", - this._generateGlsl(ast.args[0], preCompute), + generateNode(ast.args[0]), ">", - this._generateGlsl(ast.args[1], preCompute), - "?1.0:0.0)" + generateNode(ast.args[1]), + "?1:0)" ].join(""); case "below": return [ "(", - this._generateGlsl(ast.args[0], preCompute), + generateNode(ast.args[0]), "<", - this._generateGlsl(ast.args[1], preCompute), - "?1.0:0.0)" + generateNode(ast.args[1]), + "?1:0)" ].join(""); case "equal": return [ "(", - this._generateGlsl(ast.args[0], preCompute), + generateNode(ast.args[0]), "==", - this._generateGlsl(ast.args[1], preCompute), - "?1.0:0.0)" + generateNode(ast.args[1]), + "?1:0)" ].join(""); case "if": return [ "(", - this._generateGlsl(ast.args[0], preCompute), - "!=0.0?", - this._generateGlsl(ast.args[1], preCompute), + generateNode(ast.args[0]), + "!==0?", + generateNode(ast.args[1]), ":", - this._generateGlsl(ast.args[2], preCompute), + generateNode(ast.args[2]), ")" ].join(""); case "select": - var selectExpr = this._generateGlsl(ast.args[0], preCompute); - var that = this; - var generateSelect = function(args, i) { - if(args.length == 1) { - return that._generateGlsl(args[0], preCompute); - } - else { - return [ - "(("+selectExpr+" === "+i+")?", - "("+that._generateGlsl(args[0], preCompute)+"):", - "("+generateSelect(_.last(args, args.length-1), i+1)+"))" - ].join(""); - } - }; - return generateSelect(_.last(ast.args, ast.args.length-1), 0); + var code = ["((function() {"]; + code.push("switch("+generateNode(ast.args[0])+") {"); + _.each(_.last(ast.args, ast.args.length-1), function(arg, i) { + code.push("case "+i+": return "+generateNode(arg)+";"); + }); + code.push("default : throw new Error('Unknown selector value in select');"); + code.push("}}).call(this))"); + return code.join(""); case "sqr": - return "(pow((" + this._generateGlsl(ast.args[0], preCompute) + "), 2))"; + return "(Math.pow((" + generateNode(ast.args[0]) + "),2))"; case "band": - return "(float(("+this._generateGlsl(ast.args[0], preCompute)+")&&("+this._generateGlsl(ast.args[1], preCompute)+")))"; + return "((("+generateNode(ast.args[0])+")&&("+generateNode(ast.args[1])+"))?1:0)"; case "bor": - return "(float(("+this._generateGlsl(ast.args[0], preCompute)+")||("+this._generateGlsl(ast.args[1], preCompute)+")))"; + return "((("+generateNode(ast.args[0])+")||("+generateNode(ast.args[1])+"))?1:0)"; case "bnot": - return "(float(!("+this._generateGlsl(ast.args[0], preCompute)+")))"; + return "((!("+generateNode(ast.args[0])+"))?1:0)"; case "invsqrt": - return "(1/sqrt("+this._generateGlsl(ast.args[0], preCompute)+"))"; + return "(1/Math.sqrt("+generateNode(ast.args[0])+"))"; case "atan2": - return "(atan(("+this._generateGlsl(ast.args[0], preCompute)+"),("+this._generateGlsl(ast.args[1], preCompute)+"))"; - case "getosc": - var allStatic = _.every(ast.args, function(arg) { - return arg instanceof Webvs.AstPrimaryExpr; - }); - if(!allStatic) { - throw new Error("Non Pre-Computable arguments for getosc in shader code, use variables or constants"); - } - var uniformName = "__PC_" + ast.funcName + "_" + pos; - var item = [ast.funcName, uniformName].concat(_.map(ast.args, function(arg) {return arg.value;})); - var pos = _.indexOf(preCompute, item); - if(pos == -1) { - preCompute.push(item); - pos = preCompute.length-1; - } - return uniformName; + return "(Math.atan(("+generateNode(ast.args[0])+")/("+generateNode(ast.args[1])+")))"; default: - var args = _.map(ast.args, function(arg) {return this._generateGlsl(arg, preCompute);}, this).join(","); - var funcName = ast.funcName; - if(_.contains(this.varArgFuncs, ast.funcName)) { - funcName += ast.args.length; + var prefix; + var args = _.map(ast.args, function(arg) {return generateNode(arg);}).join(","); + if(_.contains(jsMathFuncs, ast.funcName)) { + prefix = "Math."; + } else { + prefix = "this."; } - return "(" + funcName + "(" + args + "))"; + return "(" + prefix + ast.funcName + "(" + args + "))"; } } if(ast instanceof Webvs.AstAssignment) { - return this._generateGlsl(ast.lhs, preCompute) + "=" + this._generateGlsl(ast.expr, preCompute); + return generateNode(ast.lhs) + "=" + generateNode(ast.expr); } if(ast instanceof Webvs.AstProgram) { - var stmts = _.map(ast.statements, function(stmt) {return this._generateGlsl(stmt, preCompute);}, this); - return stmts.join(";\n")+";"; + var stmts = _.map(ast.statements, function(stmt) {return generateNode(stmt);}); + return stmts.join(";\n"); } if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "VALUE") { - return Webvs.glslFloatRepr(ast.value); + return ast.value.toString(); } if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "CONST") { - return this._translateConstants(ast.value).toString(); + return translateConstants(ast.value).toString(); } - if(ast instanceof Webvs.AstPrimaryExpr && (ast.type === "ID" || ast.type === "REG")) { - return ast.value; + if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "ID") { + return "this." + ast.value; } - }, + if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "REG") { + return "this._registerBank[\"" + ast.value + "\"]"; + } + } + + var i; + var codeInst = new Webvs.CodeInstance(); - _generateJs: function(ast) { - var prefix; + // clear all variables + for(i = 0;i < tables.jsVars.length;i++) { + codeInst[tables.jsVars[i]] = 0; + } + + // generate code + for(i = 0;i < jsFuncs.length;i++) { + var name = jsFuncs[i]; + var ast = codeAst[name]; + if(ast) { + var jsCodeString = generateNode(ast); + codeInst[name] = new Function(jsCodeString); + } else { + codeInst[name] = Webvs.noop; + } + } + + codeInst._registerUsages = _.chain(tables.register).values().flatten().uniq().value(); + codeInst._glslRegisters = tables.glslRegisters; + if(_.contains(tables.glslUsedFuncs, "rand")) { + codeInst._hasRandom = true; + } + codeInst._uniforms = tables.uniforms; + codeInst._preCompute = tables.preCompute; + return codeInst; +} + +function generateGlsl(codeAst, tables, glslFuncs) { + function generateNode(ast) { if(ast instanceof Webvs.AstBinaryExpr) { - return "(" + this._generateJs(ast.leftOperand) + ast.operator + this._generateJs(ast.rightOperand) + ")"; + return "(" + generateNode(ast.leftOperand) + ast.operator + generateNode(ast.rightOperand) + ")"; } if(ast instanceof Webvs.AstUnaryExpr) { - return "(" + ast.operator + this._generateJs(ast.operand) + ")"; + return "(" + ast.operator + generateNode(ast.operand) + ")"; } if(ast instanceof Webvs.AstFuncCall) { - this._checkFunc(ast); + if(ast.preComputeUniformName) { + return "(" + ast.preComputeUniformName + ")"; + } switch(ast.funcName) { case "above": return [ "(", - this._generateJs(ast.args[0]), + generateNode(ast.args[0]), ">", - this._generateJs(ast.args[1]), - "?1:0)" + generateNode(ast.args[1]), + "?1.0:0.0)" ].join(""); case "below": return [ "(", - this._generateJs(ast.args[0]), + generateNode(ast.args[0]), "<", - this._generateJs(ast.args[1]), - "?1:0)" + generateNode(ast.args[1]), + "?1.0:0.0)" ].join(""); case "equal": return [ "(", - this._generateJs(ast.args[0]), + generateNode(ast.args[0]), "==", - this._generateJs(ast.args[1]), - "?1:0)" + generateNode(ast.args[1]), + "?1.0:0.0)" ].join(""); case "if": return [ "(", - this._generateJs(ast.args[0]), - "!==0?", - this._generateJs(ast.args[1]), + generateNode(ast.args[0]), + "!=0.0?", + generateNode(ast.args[1]), ":", - this._generateJs(ast.args[2]), + generateNode(ast.args[2]), ")" ].join(""); case "select": - var code = ["((function() {"]; - code.push("switch("+this._generateJs(ast.args[0])+") {"); - _.each(_.last(ast.args, ast.args.length-1), function(arg, i) { - code.push("case "+i+": return "+this._generateJs(arg)+";"); - }, this); - code.push("default : throw new Error('Unknown selector value in select');"); - code.push("}}).call(this))"); - return code.join(""); + var selectExpr = generateNode(ast.args[0]); + var generateSelect = function(args, i) { + if(args.length == 1) { + return generateNode(args[0]); + } + else { + return [ + "(("+selectExpr+" === "+i+")?", + "("+generateNode(args[0])+"):", + "("+generateSelect(_.last(args, args.length-1), i+1)+"))" + ].join(""); + } + }; + return generateSelect(_.last(ast.args, ast.args.length-1), 0); case "sqr": - return "(Math.pow((" + this._generateJs(ast.args[0]) + "),2))"; + return "(pow((" + generateNode(ast.args[0]) + "), 2))"; case "band": - return "((("+this._generateJs(ast.args[0])+")&&("+this._generateJs(ast.args[1])+"))?1:0)"; + return "(float(("+generateNode(ast.args[0])+")&&("+generateNode(ast.args[1])+")))"; case "bor": - return "((("+this._generateJs(ast.args[0])+")||("+this._generateJs(ast.args[1])+"))?1:0)"; + return "(float(("+generateNode(ast.args[0])+")||("+generateNode(ast.args[1])+")))"; case "bnot": - return "((!("+this._generateJs(ast.args[0])+"))?1:0)"; + return "(float(!("+generateNode(ast.args[0])+")))"; case "invsqrt": - return "(1/Math.sqrt("+this._generateJs(ast.args[0])+"))"; + return "(1/sqrt("+generateNode(ast.args[0])+"))"; case "atan2": - return "(Math.atan(("+this._generateJs(ast.args[0])+")/("+this._generateJs(ast.args[1])+")))"; + return "(atan(("+generateNode(ast.args[0])+"),("+generateNode(ast.args[1])+"))"; default: - var args = _.map(ast.args, function(arg) {return this._generateJs(arg);}, this).join(","); - if(_.contains(this.jsMathFuncs, ast.funcName)) { - prefix = "Math."; - } else { - prefix = "this."; - } - return "(" + prefix + ast.funcName + "(" + args + "))"; + var args = _.map(ast.args, function(arg) {return generateNode(arg);}).join(","); + return "(" + ast.funcName + "(" + args + "))"; } } if(ast instanceof Webvs.AstAssignment) { - return this._generateJs(ast.lhs) + "=" + this._generateJs(ast.expr); + return generateNode(ast.lhs) + "=" + generateNode(ast.expr); } if(ast instanceof Webvs.AstProgram) { - var stmts = _.map(ast.statements, function(stmt) {return this._generateJs(stmt);}, this); - return stmts.join(";\n"); + var stmts = _.map(ast.statements, function(stmt) {return generateNode(stmt);}); + return stmts.join(";\n")+";"; } if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "VALUE") { - return ast.value.toString(); + return Webvs.glslFloatRepr(ast.value); } if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "CONST") { - return this._translateConstants(ast.value).toString(); + return translateConstants(ast.value).toString(); } - if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "ID") { - return "this." + ast.value; - } - if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "REG") { - return "this._registerBank[\"" + ast.value + "\"]"; + if(ast instanceof Webvs.AstPrimaryExpr && (ast.type === "ID" || ast.type === "REG")) { + return ast.value; } - }, + } - _getVars: function(ast, vars, funcUsages, regUsages) { - if(ast instanceof Webvs.AstBinaryExpr) { - this._getVars(ast.leftOperand, vars, funcUsages, regUsages); - this._getVars(ast.rightOperand, vars, funcUsages, regUsages); + var glslCode = []; + var i; + + // glsl variable declarations + glslCode = glslCode.concat(_.map(tables.nonUniforms, function(name) { + return "float " + name + " = 0.0;"; + })); + glslCode = glslCode.concat(_.map(tables.uniforms, function(name) { + return "uniform float " + name + ";"; + })); + // include required functions in glsl + glslCode = glslCode.concat(_.chain(tables.glslUsedFuncs).map(function(name) { + return ((name in glslFuncCode)?(glslFuncCode[name]):[]); + }).flatten().value()); + + // declarations for precomputed functions + glslCode = glslCode.concat(_.chain(tables.preCompute).keys().map(function(name) { + return "uniform float " + name + ";"; + }).value()); + + // add the functions + for(i = 0;i < glslFuncs.length;i++) { + var name = glslFuncs[i]; + var ast = codeAst[name]; + if(ast) { + var codeString = generateNode(ast); + glslCode.push("void " + name + "() {"); + glslCode.push(codeString); + glslCode.push("}"); + } else { + glslCode.push("void " + name + "() {}"); } + } - else if(ast instanceof Webvs.AstUnaryExpr) { - this._getVars(ast.operand, vars, funcUsages, regUsages); - } - else if(ast instanceof Webvs.AstFuncCall) { - funcUsages.push(ast.funcName); - _.each(ast.args, function(arg) { - this._getVars(arg, vars, funcUsages, regUsages); - }, this); - } - else if(ast instanceof Webvs.AstAssignment) { - this._getVars(ast.lhs, vars, funcUsages, regUsages); - this._getVars(ast.expr, vars, funcUsages, regUsages); - } - else if(ast instanceof Webvs.AstProgram) { - _.each(ast.statements, function(stmt) { - this._getVars(stmt, vars, funcUsages, regUsages); - }, this); - } - else if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "ID") { - vars.push(ast.value); + return glslCode.join("\n"); +} + +var funcArgLengths = { + "above": 2, + "below": 2, + "equal": 2, + "pow": 2, + "sqr": 1, + "sqrt": 1, + "invsqrt": 1, + "floor" : 1, + "ceil" : 1, + "abs": 1, + "if": 3, + "min": 2, + "max": 2, + "sin": 1, + "cos": 1, + "tan": 1, + "asin": 1, + "acos": 1, + "atan": 1, + "atan2": 2, + "log": 1, + "band": 2, + "bor": 2, + "bnot": 1, + "rand": 1, + "gettime": 1, + "getosc": 3, + "select": {min: 2} +}; + +var jsMathFuncs = ["min", "max", "sin", "cos", "abs", "tan", "asin", "acos", "atan", "log", "pow", "sqrt", "floor", "ceil"]; + +var glslPreComputeFuncs = ["getosc", "gettime"]; + +var glslFuncCode = { + "rand": [ + "uniform vec2 __randStep;", + "vec2 __randSeed;", + "float rand(float max) {", + " __randSeed += __randStep;", + " float val = fract(sin(dot(__randSeed.xy ,vec2(12.9898,78.233))) * 43758.5453);", + " return (floor(val*max)+1);", + "}" + ].join("\n") +}; + +function checkFunc(ast) { + var requiredArgLength = funcArgLengths[ast.funcName]; + if(requiredArgLength === undefined) { + throw Error("Unknown function " + ast.funcName); + } + if(_.isNumber(requiredArgLength)) { + if(ast.args.length != requiredArgLength) { + throw Error(ast.funcName + " accepts " + requiredArgLength + " arguments"); } - else if(ast instanceof Webvs.AstPrimaryExpr && ast.type === "REG") { - regUsages.push(ast.value); + } else if(requiredArgLength.min) { + if(ast.args.length < requiredArgLength.min) { + throw Error(ast.funcName + " accepts atleast " + requiredArgLength.min + " arguments"); } - }, + } +} - _translateConstants: function(value) { - switch(value) { - case "pi": return Math.PI; - case "e": return Math.E; - case "phi": return 1.6180339887; - default: throw new Error("Unknown constant " + value); - } +function translateConstants(value) { + switch(value) { + case "pi": return Math.PI; + case "e": return Math.E; + case "phi": return 1.6180339887; + default: throw new Error("Unknown constant " + value); } -}); +} })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A component that simply runs some avs expressions. - * Useful to maintain global state - * - * @param {object} options - options object - * @param {string} [options.code.init] - code to be run at startup - * @param {string} [options.code.onBeat] - code to be run when a beat occurs - * @param {string} [ptions.code.perFrame]- code to be run on every frame - * @augments Webvs.Component - * @constructor - * @memberof Webvs - */ -function GlobalVar(options) { - Webvs.checkRequiredOptions(options, ["code"]); - var codeGen = new Webvs.ExprCodeGenerator(options.code, ["b"]); - this.code = codeGen.generateJs(["init", "onBeat", "perFrame"]); - this.inited = false; - - GlobalVar.super.constructor.apply(this, arguments); +// A component that simply runs some avs expressions. +// Useful to maintain global state +function GlobalVar(gl, main, parent, opts) { + GlobalVar.super.constructor.call(this, gl, main, parent, opts); } -Webvs.GlobalVar = Webvs.defineClass(GlobalVar, Webvs.Component, { - /** - * initializes the globalvar component - * @memberof Webvs.GlobalVar# - */ - init: function(gl, main, parent) { - GlobalVar.super.init.call(this, gl, main, parent); - this.code.setup(main, this); - }, +Webvs.registerComponent(GlobalVar, { + name: "GlobalVar", + menu: "Misc" +}); - /** - * Runs the code - * @memberof Webvs.GlobalVar# - */ - update: function() { +Webvs.defineClass(GlobalVar, Webvs.Component, { + defaultOptions: { + code: { + init: "", + onBeat: "", + perFrame: "" + } + }, + + onChange: { + "code": "updateCode" + }, + + init: function() { + this.updateCode(); + this.listenTo(this.main, "resize", this.handleResize); + }, + + draw: function() { var code = this.code; code.b = this.main.analyser.beat?1:0; @@ -4936,220 +4838,156 @@ Webvs.GlobalVar = Webvs.defineClass(GlobalVar, Webvs.Component, { } code.perFrame(); - } -}); + }, -GlobalVar.ui = { - disp: "Global Var", - type: "GlobalVar", - schema: { - code: { - type: "object", - title: "Code", - default: {}, - properties: { - init: { - type: "string", - title: "Init", - }, - onBeat: { - type: "string", - title: "On Beat", - }, - perFrame: { - type: "string", - title: "Per Frame", - } - }, - } + updateCode: function() { + this.code = Webvs.compileExpr(this.opts.code, ["init", "onBeat", "perFrame"]).codeInst; + this.code.setup(this.main, this); + this.inited = false; + }, + + handleResize: function() { + this.code.updateDimVars(this.gl); } -}; +}); })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A components that saves or restores a copy of the current - * frame buffer. - * - * @param {object} options - options object - * @param {string} [options.action="SAVE"] - the action to be performed. viz. "SAVE", - * "RESTORE", "RESTORESAVE", "SAVERESTORE" - * @param {number} [options.bufferId=1] - an identifying number for the buffer. This number - * is used to share buffer between different instances of BufferSave - * @param {string} [options.blendMode="REPLACE"] - blending mode when restoring buffers - * @constructor - * @augments Webvs.Component - * @memberof Webvs - */ -function BufferSave(options) { - options = _.defaults(options, { +// A components that saves or restores a copy of the current +// frame buffer. +function BufferSave(gl, main, parent, opts) { + BufferSave.super.constructor.call(this, gl, main, parent, opts); +} + +Webvs.registerComponent(BufferSave, { + name: "BufferSave", + menu: "Misc" +}); + +var Actions = { + "SAVE": 0, + "RESTORE": 1, + "SAVERESTORE": 2, + "RESTORESAVE": 3 +}; +BufferSave.Actions = Actions; + +Webvs.defineClass(BufferSave, Webvs.Component, { + defaultOptions: { action: "SAVE", - bufferId: 1, + bufferId: "buffer1", blendMode: "REPLACE" - }); - this.blendMode = Webvs.blendModes[options.blendMode]; - this.action = this.actions[options.action]; - if(!this.action) { - throw new Error("Unknown BufferSave action " + options.action); - } - - if(this.action == this.actions.SAVERESTORE) { - this._nextAction = this.actions.SAVE; - } else if(this.action == this.actions.RESTORESAVE) { - this._nextAction = this.actions.RESTORE; - } - this._bufferId = "__BUFFERSAVE_" + options.bufferId; - BufferSave.super.constructor.apply(this, arguments); -} -Webvs.BufferSave = Webvs.defineClass(BufferSave, Webvs.Component, { - actions: { - SAVE: 1, - RESTORE: 2, - SAVERESTORE: 3, - RESTORESAVE: 4 }, - /** - * Initializes the BufferSave component - * @memberof Webvs.BufferSave# - */ - init: function(gl, main, parent) { - BufferSave.super.init.call(this, gl, main, parent); - - // create frame buffer manager - if(!main.registerBank[this._bufferId]) { - var fm = new Webvs.FrameBufferManager(main.canvas.width, main.canvas.height, gl, main.copier, true, 1); - main.registerBank[this._bufferId] = fm; - } + onChange: { + "action": "updateAction", + "bufferId": "updateBuffer", + "blendMode": "updateBlendMode" }, - /** - * Saves or Renders the current frame - * @memberof Webvs.BufferSave# - */ - update: function() { - var gl = this.gl; - var fm = this.main.registerBank[this._bufferId]; + init: function() { + this.updateAction(); + this.updateBlendMode(); + this.updateBuffer(); + }, - // find the current action + draw: function() { var currentAction; - if(this.action == this.actions.SAVERESTORE || this.action == this.RESTORESAVE) { - currentAction = this._nextAction; - // set the next action - if(this._nextAction == this.actions.SAVE) { - this._nextAction = this.actions.RESTORE; - } else { - this._nextAction = this.actions.SAVE; - } + if(this.action == Actions.SAVERESTORE || + this.action == Actions.RESTORESAVE) { + currentAction = this.nextAction; + // toggle next action + this.nextAction = (this.nextAction == Actions.SAVE)?Actions.RESTORE:Actions.SAVE; } else { currentAction = this.action; } + var buffers = this.main.buffers; switch(currentAction) { - case this.actions.SAVE: - fm.setRenderTarget(); + case Actions.SAVE: + buffers.setRenderTarget(this.opts.bufferId); this.main.copier.run(null, null, this.parent.fm.getCurrentTexture()); - fm.restoreRenderTarget(); + buffers.restoreRenderTarget(); break; - case this.actions.RESTORE: - this.main.copier.run(this.parent.fm, this.blendMode, fm.getCurrentTexture()); + case Actions.RESTORE: + this.main.copier.run(this.parent.fm, this.blendMode, buffers.getTexture(this.opts.bufferId)); break; } }, - /** - * Releases resources. - * @memberof Webgl.BufferSave# - */ destroy: function() { BufferSave.super.destroy.call(this); - // destroy the framebuffermanager - this.main.registerBank[this._bufferId].destroy(); - } -}); + this.main.buffers.removeTexture(this.opts.bufferId); + }, + + updateAction: function() { + this.action = Webvs.getEnumValue(this.opts.action, Actions); + if(this.action == Actions.SAVERESTORE) { + this.nextAction = Actions.SAVE; + } else if(this.action == Actions.RESTORESAVE) { + this.nextAction = Actions.RESTORE; + } + }, -BufferSave.ui = { - disp: "Buffer Save", - type: "BufferSave", - schema: { - action: { - type: "string", - title: "Buffer save action", - enum: ["SAVE", "RESTORE", "SAVERESTORE", "RESTORESAVE"] - }, - bufferId: { - type: "number", - title: "Buffer Id", - enum: [1,2,3,4,5,6,7,8] - }, - blendMode: { - type: "string", - title: "Blend mode", - enum: _.keys(Webvs.blendModes) + updateBuffer: function(value, key, oldValue) { + // buffer names in FrameBufferManager have to be string + // converting to string to maintain backward compatibility + this.opts.bufferId = this.opts.bufferId + ""; + if(oldValue) { + this.main.buffers.removeTexture(oldValue); } + this.main.buffers.addTexture(this.opts.bufferId); + }, + + updateBlendMode: function() { + this.blendMode = Webvs.getEnumValue(this.opts.blendMode, Webvs.BlendModes); } -}; +}); })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A component that slowly fades the screen to a specified color - * - * @param {object} options - options object - * @param {number} [speed=1] - speed at which the screen is faded 0 (never) - 1 (fastest) - * @param {string} [color="#000000"] - fade color - * @augments Webvs.Component - * @constructor - * @memberof Webvs - * @constructor - */ -function FadeOut(options) { - options = _.defaults(options, { +// A component that slowly fades the screen to a specified color +function FadeOut(gl, main, parent, opts) { + FadeOut.super.constructor.call(this, gl, main, parent, opts); +} + +Webvs.registerComponent(FadeOut, { + name: "FadeOut", + menu: "Trans" +}); + +Webvs.defineClass(FadeOut, Webvs.Component, { + defaultOptions: { speed: 1, color: "#000000" - }); - this.color = Webvs.parseColorNorm(options.color); + }, - this.frameCount = 0; - this.maxFrameCount = Math.floor(1/options.speed); - this.program = new Webvs.ClearScreenProgram(Webvs.AVERAGE); - - FadeOut.super.constructor.apply(this, arguments); -} -Webvs.FadeOut = Webvs.defineClass(FadeOut, Webvs.Component, { - componentName: "FadeOut", + onChange: { + speed: "updateSpeed", + color: "updateColor" + }, - /** - * initializes the FadeOut component - * @memberof Webvs.FadeOut# - */ - init: function(gl, main, parent) { - FadeOut.super.init.call(this, gl, main, parent); - this.program.init(gl); + init: function() { + this.program = new Webvs.ClearScreenProgram(this.gl, Webvs.AVERAGE); + this.updateSpeed(); + this.updateColor(); }, - /** - * fades the screen - * @memberof Webvs.FadeOut# - */ - update: function() { - var gl = this.gl; + draw: function() { this.frameCount++; if(this.frameCount == this.maxFrameCount) { this.frameCount = 0; @@ -5157,162 +4995,116 @@ Webvs.FadeOut = Webvs.defineClass(FadeOut, Webvs.Component, { } }, - /** - * releases resources - * @memberof Webvs.FadeOut# - */ destroy: function() { FadeOut.super.destroy.call(this); - this.program.cleanup(); - } -}); + this.program.destroy(); + }, -FadeOut.ui = { - type: "FadeOut", - disp: "Fade Out", - schema: { - speed: { - type: "number", - title: "Speed", - maximum: 0, - minimum: 1, - default: 1 - }, - color: { - type: "string", - title: "Fadeout color", - format: "color", - default: "#FFFFFF" - } + updateSpeed: function() { + this.frameCount = 0; + this.maxFrameCount = Math.floor(1/this.opts.speed); }, - form: [ - {key: "speed", type: "range", step: "0.05"}, - "color" - ] -}; + + updateColor: function() { + this.color = Webvs.parseColorNorm(this.opts.color); + } +}); })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A component that applies a convolution kernel - * - * @param {object} options - options object - * @param {Array.>} options.kernel - an NxN array of numbers - * @param {number} [options.bias=0] - bias value to be added - * @param {number} [options.scale] - scale for the kernel. default is sum of kernel values - * @param {object} [options.edgeMode="EXTEND"] - how the frame edge cases should be handled viz. `WRAP`, `EXTEND` - * - * @constructor - * @augments Webvs.Component - * @memberof Webvs - */ -function Convolution(options) { - Webvs.checkRequiredOptions(options, ["kernel"]); - options = _.defaults(options, { - edgeMode: "EXTEND", - bias: 0 - }); +// A component that applies a convolution kernel +function Convolution(gl, main, parent, opts) { + Convolution.super.constructor.call(this, gl, main, parent, opts); +} - var kernel; - if(options.kernel in Convolution.kernels) { - kernel = Convolution.kernels[options.kernel]; - } else if(_.isArray(options.kernel) && options.kernel.length%2 === 1) { - kernel = options.kernel; - } else { - throw new Error("Invalid convolution kernel"); - } +Webvs.registerComponent(Convolution, { + name: "Convolution", + menu: "Trans" +}); - var kernelSize = Math.floor(Math.sqrt(kernel.length)); - if(kernelSize*kernelSize != kernel.length) { - throw new Error("Invalid convolution kernel"); - } +var EdgeModes = { + "EXTEND": 0, + "WRAP": 1, +}; +Convolution.EdgeModes = EdgeModes; - this.program = new Webvs.ConvolutionProgram(kernel, kernelSize, - options.edgeMode, options.scale, - options.bias); +Webvs.defineClass(Convolution, Webvs.Component, { + defaultOptions: { + edgeMode: "EXTEND", + autoScale: true, + scale: 0, + kernel: [ + 0, 0, 0, + 0, 1, 0, + 0, 0, 0 + ], + bias: 0 + }, - Convolution.super.constructor.apply(this, arguments); -} -Webvs.Convolution = Webvs.defineClass(Convolution, Webvs.Component, { - componentName: "Convolution", + onChange: { + "edgeMode": "updateProgram", + "kernel": ["updateProgram", "updateScale"], + "scale": "updateScale" + }, - /** - * initializes the Convolution component - * @method - * @memberof Webvs.Convolution# - */ - init: function(gl, main, parent) { - Convolution.super.init.call(this, gl, main, parent); - this.program.init(gl); + init: function() { + this.updateProgram(); + this.updateScale(); }, - /** - * applies the Convolution matrix - * @method - * @memberof Webvs.Convolution# - */ - update: function() { - this.program.run(this.parent.fm, null); + draw: function() { + this.program.run(this.parent.fm, null, this.scale, this.opts.bias); }, - /** - * releases resources - * @memberof Webvs.Convolution# - */ destroy: function() { Convolution.super.destroy.call(this); - this.program.cleanup(); + this.program.destroy(); + }, + + updateScale: function() { + var opts = this.opts; + if(opts.autoScale) { + this.scale = _.reduce(opts.kernel, function(memo, num){ return memo + num; }, 0); + } else { + this.scale = opts.scale; + } + }, + + updateProgram: function() { + var opts = this.opts; + if(!_.isArray(opts.kernel) || opts.kernel.length%2 !== 1) { + throw new Error("Invalid convolution kernel"); + } + var kernelSize = Math.floor(Math.sqrt(opts.kernel.length)); + if(kernelSize*kernelSize != opts.kernel.length) { + throw new Error("Invalid convolution kernel"); + } + + if(this.program) { + this.program.destroy(); + } + var edgeMode = Webvs.getEnumValue(this.opts.edgeMode, EdgeModes); + this.program = new Webvs.ConvolutionProgram(this.gl, opts.kernel, kernelSize, edgeMode); } }); -Convolution.kernels = { - normal: [ - 0, 0, 0, - 0, 1, 0, - 0, 0, 0 - ], - gaussianBlur: [ - 0.045, 0.122, 0.045, - 0.122, 0.332, 0.122, - 0.045, 0.122, 0.045 - ], - unsharpen: [ - -1, -1, -1, - -1, 9, -1, - -1, -1, -1 - ], - emboss: [ - -2, -1, 0, - -1, 1, 1, - 0, 1, 2 - ], - blur: [ - 1, 1, 1, - 1, 1, 1, - 1, 1, 1 - ] -}; - -function ConvolutionProgram(kernel, kernelSize, edgeMode, scale, bias) { +function ConvolutionProgram(gl, kernel, kernelSize, edgeMode) { // generate edge correction function var edgeFunc = ""; switch(edgeMode) { - case "WRAP": + case EdgeModes.WRAP: edgeFunc = "pos = vec2(pos.x<0?pos.x+1.0:pos.x%1, pos.y<0?pos.y+1.0:pos.y%1);"; break; - case "EXTEND": + case EdgeModes.EXTEND: edgeFunc = "pos = clamp(pos, vec2(0,0), vec2(1,1));"; break; - default: - throw new Error("Invalid edge mode"); } var i,j; @@ -5326,129 +5118,140 @@ function ConvolutionProgram(kernel, kernelSize, edgeMode, scale, bias) { if(value === 0) { continue; } - colorSumEq.push("pos = v_position + texel * vec2("+(i-mid)+","+(j-mid)+");"); + colorSumEq.push("pos = v_position + texel * vec2("+(j-mid)+","+(mid-i)+");"); colorSumEq.push(edgeFunc); colorSumEq.push("colorSum += texture2D(u_srcTexture, pos) * "+Webvs.glslFloatRepr(value)+";"); } } - // compute kernel scaling factor - if(_.isUndefined(scale)) { - scale = _.reduce(kernel, function(memo, num){ return memo + num; }, 0); - } - - ConvolutionProgram.super.constructor.call(this, { + ConvolutionProgram.super.constructor.call(this, gl, { swapFrame: true, fragmentShader: [ + "uniform float u_scale;", + "uniform float u_bias;", "void main() {", " vec2 texel = 1.0/(u_resolution-vec2(1,1));", " vec2 pos;", " vec4 colorSum = vec4(0,0,0,0);", colorSumEq.join("\n"), - " setFragColor(vec4(((colorSum+"+Webvs.glslFloatRepr(bias)+") / "+Webvs.glslFloatRepr(scale)+").rgb, 1.0));", + " setFragColor(vec4(((colorSum+u_bias)/u_scale).rgb, 1.0));", "}" ] }); } -Webvs.ConvolutionProgram = Webvs.defineClass(ConvolutionProgram, Webvs.QuadBoxProgram); +Webvs.ConvolutionProgram = Webvs.defineClass(ConvolutionProgram, Webvs.QuadBoxProgram, { + draw: function(scale, bias) { + this.setUniform("u_scale", "1f", scale); + this.setUniform("u_bias", "1f", bias); + ConvolutionProgram.super.draw.call(this); + } +}); })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * a component that changes colors according to a gradient map using - * a key generated from the source colors - * - * @param {object} options - options object - * @param {Array.>} options.maps - a set of color maps. each colormap consists of - * a set of keystones. The map is generated by interpolating colors between the keystones. - * @param {string} options.maps[i][j].color - keystone color - * @param {number} options.maps[i][j].index - position of keystone (0-255) - * @param {object} options.maps - a set of color map - * @param {string} [options.key="RED"] - the key function viz. `RED`, `GREEN`, `BLUE`, `(R+G+B)/2`, `(R+G+B)/3`, `MAX` - * @param {string} [options.output="REPLACE"] - output blending mode - * @param {string} [options.mapCycleMode="SINGLE"] - how to cycle between maps - * - * @augments Webvs.Component - * @constructor - * @memberof Webvs - */ -function ColorMap(options) { - Webvs.checkRequiredOptions(options, ["maps"]); - options = _.defaults(options, { - key: "RED", - output: "REPLACE", - mapCycleMode: "SINGLE", - }); +// a component that changes colors according to a gradient map using +// a key generated from the source colors +function ColorMap(gl, main, parent, opts) { + ColorMap.super.constructor.call(this, gl, main, parent, opts); +} - var that = this; - this.maps = options.maps; - this.currentMap = 0; +Webvs.registerComponent(ColorMap, { + name: "ColorMap", + menu: "Trans" +}); - this.mapCycleMode = this.mapCycleModes[options.mapCycleMode]; - if(!this.mapCycleMode) { - throw new Error("Unknown mapCycleMode " + options.mapCycleMode); - } +var MapKey = { + "RED": 0, + "GREEN": 1, + "BLUE": 2, + "(R+G+B)/2": 3, + "(R+G+B)/3": 4, + "MAX": 5 +}; +ColorMap.MapKey = MapKey; - this.program = new Webvs.ColorMapProgram(options.key, Webvs.getBlendMode(options.output)); +var MapCycleModes = { + "SINGLE": 0, + "ONBEATRANDOM": 1, + "ONBEATSEQUENTIAL": 2 +}; +ColorMap.MapCycleModes = MapCycleModes; - ColorMap.super.constructor.apply(this, arguments); -} -Webvs.ColorMap = Webvs.defineClass(ColorMap, Webvs.Component, { - mapCycleModes: { - SINGLE: 1, - ONBEATRANDOM: 2, - ONBEATSEQUENTIAL: 3 +Webvs.defineClass(ColorMap, Webvs.Component, { + defaultOptions: { + key: "RED", + output: "REPLACE", + mapCycleMode: "SINGLE", + maps: [ + [ + {index: 0, color: "#000000"}, + {index: 255, color: "#FFFFFF"} + ] + ], }, - /** - * initializes the ColorMap component - * @memberof Webvs.ColorMap# - */ - init: function(gl, main, parent) { - ColorMap.super.init.call(this, gl, main, parent); - - this.colorMaps = _.map(this.maps, function(map) { - return this._buildColorMap(map); - }, this); - this.currentMap = 0; + onChange: { + "maps": "updateMap", + "key": "updateProgram", + "mapCycleMode": "updateCycleMode", + "output": "updateProgram" + }, - this.program.init(gl); + init: function() { + this.updateProgram(); + this.updateMap(); + this.updateCycleMode(); }, - /** - * maps the colors - * @memberof Webvs.ColorMap# - */ - update: function() { + draw: function() { if(this.main.analyser.beat) { - switch(this.mapCycleMode) { - case this.mapCycleModes.ONBEATRANDOM: - this.currentMap = Math.floor(Math.random()*this.colorMaps.length); - break; - case this.mapCycleModes.ONBEATSEQUENTIAL: - this.currentMap = (this.currentMap+1)%this.colorMaps.length; - break; + if(this.mapCycleMode == MapCycleModes.ONBEATRANDOM) { + this.currentMap = Math.floor(Math.random()*this.opts.maps.length); + } else if(this.mapCycleMode == MapCycleModes.ONBEATSEQUENTIAL) { + this.currentMap = (this.currentMap+1)%this.colorMaps.length; } } - this.program.run(this.parent.fm, null, this.colorMaps[this.currentMap]); }, - /** - * releases resources - * @memberof Webvs.ColorMap# - */ destroy: function() { ColorMap.super.destroy.call(this); - this.program.cleanup(); + this.program.destroy(); + _.each(this.colorMaps, function(tex) { + this.gl.deleteTexture(tex); + }, this); + }, + + updateProgram: function() { + if(this.program) { + this.program.cleanup(); + } + var output = Webvs.getEnumValue(this.opts.output, Webvs.BlendModes); + var key = Webvs.getEnumValue(this.opts.key, MapKey); + this.program = new Webvs.ColorMapProgram(this.gl, key, output); + }, + + updateMap: function() { + if(this.colorMaps) { + _.each(this.colorMaps, function(tex) { + this.gl.deleteTexture(tex); + }, this); + } + this.colorMaps = _.map(this.opts.maps, function(map) { + return this._buildColorMap(map); + }, this); + this.currentMap = 0; + }, + + updateCycleMode: function() { + this.mapCycleMode = Webvs.getEnumValue(this.opts.mapCycleMode, MapCycleModes); }, _buildColorMap: function(map) { @@ -5486,9 +5289,9 @@ Webvs.ColorMap = Webvs.defineClass(ColorMap, Webvs.Component, { var second = pair[1]; var steps = second.index - first.index; _.times(steps, function(i) { - colorMap[cmi++] = Math.floor((first.color[0]*(255-i) + second.color[0]*i)/255); - colorMap[cmi++] = Math.floor((first.color[1]*(255-i) + second.color[1]*i)/255); - colorMap[cmi++] = Math.floor((first.color[2]*(255-i) + second.color[2]*i)/255); + colorMap[cmi++] = Math.floor((first.color[0]*(steps-i) + second.color[0]*i)/steps); + colorMap[cmi++] = Math.floor((first.color[1]*(steps-i) + second.color[1]*i)/steps); + colorMap[cmi++] = Math.floor((first.color[2]*(steps-i) + second.color[2]*i)/steps); }); }); colorMap[cmi++] = last.color[0]; @@ -5507,20 +5310,19 @@ Webvs.ColorMap = Webvs.defineClass(ColorMap, Webvs.Component, { } }); -function ColorMapProgram(key, blendMode) { +function ColorMapProgram(gl, key, blendMode) { var keyEq = ""; switch(key) { - case "RED": keyEq = "srcColor.r"; break; - case "GREEN": keyEq = "srcColor.g"; break; - case "BLUE": keyEq = "srcColor.b"; break; - case "(R+G+B)/2": keyEq = "mod((srcColor.r+srcColor.g+srcColor.b)/2.0, 1.0)"; break; - case "(R+G+B)/3": keyEq = "(srcColor.r+srcColor.g+srcColor.b)/3.0"; break; - case "MAX": keyEq = "max(srcColor.r, max(srcColor.g, srcColor.b))"; break; - default: throw new Error("Unknown colormap key function " + options.key); + case MapKey.RED: keyEq = "srcColor.r"; break; + case MapKey.GREEN: keyEq = "srcColor.g"; break; + case MapKey.BLUE: keyEq = "srcColor.b"; break; + case MapKey["(R+G+B)/2"]: keyEq = "min((srcColor.r+srcColor.g+srcColor.b)/2.0, 1.0)"; break; + case MapKey["(R+G+B)/3"]: keyEq = "(srcColor.r+srcColor.g+srcColor.b)/3.0"; break; + case MapKey.MAX: keyEq = "max(srcColor.r, max(srcColor.g, srcColor.b))"; break; } - ColorMapProgram.super.constructor.call(this, { - outputBlendMode: blendMode, + ColorMapProgram.super.constructor.call(this, gl, { + blendMode: blendMode, swapFrame: true, fragmentShader: [ "uniform sampler2D u_colorMap;", @@ -5538,137 +5340,74 @@ Webvs.ColorMapProgram = Webvs.defineClass(ColorMapProgram, Webvs.QuadBoxProgram, } }); -ColorMap.ui = { - disp: "Color Map", - type: "ColorMap", - schema: { - maps: { - type: "array", - items: { - type: "array", - title: "Map", - items: { - type: "object", - properties: { - color: { - type: "string", - title: "Color", - format: "color", - default: "#FFFFFF" - }, - index: { - type: "number", - title: "Index", - minimum: 0, - maximum: 255, - } - } - } - } - }, - key: { - type: "string", - title: "Map key", - enum: ["RED", "GREEN", "BLUE", "(R+G+B)/2", "(R+G+B)/3", "MAX"], - default: "RED" - }, - mapCycleMode: { - type: "string", - title: "Map Cycle Mode", - enum: ["SINGLE", "ONBEATRANDOM", "ONBEATSEQUENTIAL"], - default: "SINGLE" - }, - output: { - type: "string", - title: "Output blend mode", - enum: _.keys(Webvs.blendModes), - default: "REPLACE" - } - } -}; - })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A component that clips colors to a different color depending - * on whether the source colors are above or below a reference color. - * - * @see r_contrast.cpp - * @param {object} options - options object - * @param {string} [options.mode="BELOW"] - comparison mode viz. `BELOW`, `ABOVE`, `NEAR` - * @param {string} [options.color="#202020"] - reference color against which the - * the screen colors are compared - * @param {string} [options.outColor="#202020"] - output color for clipped pixels - * @param {number} [options.level=0] - when mode is `NEAR`, this value decides the distance - * between source and reference colors below which pixels would be clipped. 0-1 normalized - * - * @augments Webvs.Component - * @constructor - * @memberof Webvs - */ -function ColorClip(options) { - Webvs.checkRequiredOptions(options, ["mode", "color", "outColor"]); - options = _.defaults(options, { +// A component that clips colors to a different color depending +// on whether the source colors are above or below a reference color. +function ColorClip(gl, main, parent, opts) { + ColorClip.super.constructor.call(this, gl, main, parent, opts); +} + +Webvs.registerComponent(ColorClip, { + name: "ColorClip", + menu: "Trans" +}); + +var ClipModes = { + "BELOW": 0, + "ABOVE": 1, + "NEAR": 2 +}; +ColorClip.ClipModes = ClipModes; + +Webvs.defineClass(ColorClip, Webvs.Component, { + defaultOptions: { mode: "BELOW", color: "#202020", outColor: "#202020", level: 0 - }); - - this.mode = _.indexOf(this.modes, options.mode); - if(this.mode == -1) { - throw new Error("ColorClip: invalid mode"); - } - this.color = Webvs.parseColorNorm(options.color); - this.outColor = Webvs.parseColorNorm(options.outColor); - this.level = options.level; - - this.program = new Webvs.ColorClipProgram(); - - ColorClip.super.constructor.apply(this, arguments); -} -Webvs.ColorClip = Webvs.defineClass(ColorClip, Webvs.Component, { - modes: ["BELOW", "ABOVE", "NEAR"], - componentName: "ChannelShift", + }, - /** - * initializes the ColorClip component - * @memberof Webvs.ColorClip# - */ - init: function(gl, main, parent) { - ColorClip.super.init.call(this, gl, main, parent); + onChange: { + mode: "updateMode", + color: "updateColor", + outColor: "updateColor" + }, - this.program.init(gl); + init: function() { + this.program = new ColorClipProgram(this.gl); + this.updateColor(); + this.updateMode(); }, - /** - * clips the colors - * @memberof Webvs.ColorClip# - */ - update: function() { - this.program.run(this.parent.fm, null, this.mode, this.color, this.outColor, this.level); + draw: function() { + this.program.run(this.parent.fm, null, this.mode, this.color, this.outColor, this.opts.level); }, - /** - * releases resources - * @memberof Webvs.ColorClip# - */ destroy: function() { ColorClip.super.destroy.call(this); - this.program.cleanup(); + this.program.destroy(); + }, + + updateMode: function() { + this.mode = Webvs.getEnumValue(this.opts.mode, ClipModes); + }, + + updateColor: function() { + this.color = Webvs.parseColorNorm(this.opts.color); + this.outColor = Webvs.parseColorNorm(this.opts.outColor); } }); -function ColorClipProgram() { - ColorClipProgram.super.constructor({ +function ColorClipProgram(gl) { + ColorClipProgram.super.constructor.call(this, gl, { swapFrame: true, fragmentShader: [ "uniform int u_mode;", @@ -5711,113 +5450,133 @@ Webvs.ColorClipProgram = Webvs.defineClass(ColorClipProgram, Webvs.QuadBoxProgra })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A component that moves pixels according to user code. - * - * #### Code variables - * - * The following variables are available in the code - * - * + x - x position of the pixel (-1 to +1) - * + y - y position of the pixel (-1 to +1) - * + d - length of pixel position vector (0 to 1) - * + r - angle of the position vector with y axis in clockwise direction in radians - * + w - width of the screen - * + h - height of the screen - * + b - 1 if a beat has occured else 0 - * - * @param {object} options - options object - * @param {string} [options.code.init] - code to be run at startup - * @param {string} [options.code.onBeat] - code to be run when a beat occurs - * @param {string} [options.code.perFrame] - code to be run on every frame - * @param {string} [options.code.perPixel] - code that will be run once for every pixel. should set - * `x`, `y` or `d`, `r` variables (depending on coord) to specify point location. Note: state - * of this code does not persist. - * @param {number} [options.gridW=16] - width of the interpolation grid - * @param {number} [options.gridH=16] - height of the interpolation grid - * @param {boolean} [options.noGrid=false] - if true, then interpolation grid is not used - * ie. movement will be pixel accurate - * @param {boolean} [options.compat=false] - if true, then calculations are low precision. - * useful to map winamp AVS behaviour more closely - * @param {boolean} [options.bFilter=true] - use bilinear interpolation for pixel sampling - * @param {string} [options.coord="POLAR"] - coordinate system to be used viz. `POLAR`, `RECT` - * @augments Webvs.Component - * @constructor - * @memberof Webvs - * @constructor - */ -function DynamicMovement(options) { - Webvs.checkRequiredOptions(options, ["code"]); - options = _.defaults(options, { +// A component that moves pixels according to user code. +function DynamicMovement(gl, main, parent, opts) { + DynamicMovement.super.constructor.call(this, gl, main, parent, opts); +} + +Webvs.registerComponent(DynamicMovement, { + name: "DynamicMovement", + menu: "Trans" +}); + +var CoordModes = { + "POLAR": 0, + "RECT": 1 +}; +DynamicMovement.CoordModes = CoordModes; + +Webvs.defineClass(DynamicMovement, Webvs.Component, { + defaultOptions: { + code: { + init: "", + onBeat: "", + perFrame: "", + perPixel: "" + }, gridW: 16, gridH: 16, + blend: false, noGrid: false, - bFilter: true, compat: false, + bFilter: true, coord: "POLAR" - }); + }, - var codeSrc; - if(_.isObject(options.code)) { - codeSrc = options.code; - } else { - throw new Error("Invalid Dynamic movement code"); - } - var codeGen = new Webvs.ExprCodeGenerator(codeSrc, ["x", "y", "r", "d", "b"]); - this.code = codeGen.generateJs(["init", "onBeat", "perFrame"]); - var glslCode = codeGen.generateGlsl(["perPixel"], ["x", "y", "d", "r"], this.code); + onChange: { + "code": "updateCode", + "noGrid": ["updateProgram", "updateGrid"], + "compat": "updateProgram", + "bFilter": "updateProgram", + "coord": "updateProgram", + "blend": "updateProgram", + "gridW": "updateGrid", + "gridH": "updateGrid" + }, + + init: function() { + this.updateCode(); + this.updateGrid(); + this.listenTo(this.main, "resize", this.handleResize); + }, - this.inited = false; + draw: function() { + var code = this.code; - this.noGrid = options.noGrid; - this.gridW = options.gridW; - this.gridH = options.gridH; + // run init, if required + if(!this.inited) { + code.init(); + code.inited = true; + } - this.coordMode = options.coord; - this.bFilter = options.bFilter; - this.compat = options.compat; + var beat = this.main.analyser.beat; + code.b = beat?1:0; + // run per frame + code.perFrame(); + // run on beat + if(beat) { + code.onBeat(); + } - if(this.noGrid) { - this.program = new Webvs.DMovProgramNG(this.coordMode, this.bFilter, - this.compat, this.code.hasRandom, - glslCode); - } else { - this.program = new Webvs.DMovProgram(this.coordMode, this.bFilter, - this.compat, this.code.hasRandom, - glslCode); - } + this.program.run(this.parent.fm, null, this.code, this.gridVertices, this.gridVerticesSize); + }, - DynamicMovement.super.constructor.apply(this, arguments); -} -Webvs.DynamicMovement = Webvs.defineClass(DynamicMovement, Webvs.Component, { - componentName: "DynamicMovement", + destroy: function() { + DynamicMovement.super.destroy.call(this); + this.program.destroy(); + }, - /** - * initializes the DynamicMovement component - * @memberof Webvs.DynamicMovement# - */ - init: function(gl, main, parent) { - DynamicMovement.super.init.call(this, gl, main, parent); + updateCode: function() { + var compileResult = Webvs.compileExpr(this.opts.code, ["init", "onBeat", "perFrame"], ["perPixel"], ["x", "y", "d", "r", "alpha"]); + + // js code + var code = compileResult.codeInst; + code.setup(this.main, this); + this.inited = false; + this.code = code; - this.program.init(gl); + // glsl code + this.glslCode = compileResult.glslCode; + this.updateProgram(); + }, - this.code.setup(main, parent); + updateProgram: function() { + var opts = this.opts; + var program; + var coordMode = Webvs.getEnumValue(this.opts.coord, CoordModes); + if(opts.noGrid) { + program = new Webvs.DMovProgramNG(this.gl, coordMode, opts.bFilter, + opts.compat, this.code.hasRandom, + this.glslCode, opts.blend); + } else { + program = new Webvs.DMovProgram(this.gl, coordMode, opts.bFilter, + opts.compat, this.code.hasRandom, + this.glslCode, opts.blend); + } + if(this.program) { + this.program.destroy(); + } + this.program = program; + }, - // calculate grid vertices - if(!this.noGrid) { - var gridW = Webvs.clamp(this.gridW, 1, this.main.canvas.width); - var gridH = Webvs.clamp(this.gridH, 1, this.main.canvas.height); - var nGridW = (gridW/this.main.canvas.width)*2; - var nGridH = (gridH/this.main.canvas.height)*2; - var gridCountAcross = Math.ceil(this.main.canvas.width/gridW); - var gridCountDown = Math.ceil(this.main.canvas.height/gridH); + updateGrid: function() { + var opts = this.opts; + if(opts.noGrid) { + this.gridVertices = undefined; + this.gridVerticesSize = undefined; + } else { + var gridW = Webvs.clamp(opts.gridW, 1, this.gl.drawingBufferWidth); + var gridH = Webvs.clamp(opts.gridH, 1, this.gl.drawingBufferHeight); + var nGridW = (gridW/this.gl.drawingBufferWidth)*2; + var nGridH = (gridH/this.gl.drawingBufferHeight)*2; + var gridCountAcross = Math.ceil(this.gl.drawingBufferWidth/gridW); + var gridCountDown = Math.ceil(this.gl.drawingBufferHeight/gridH); var gridVertices = new Float32Array(gridCountAcross*gridCountDown*6*2); var pbi = 0; var curx = -1; @@ -5851,48 +5610,14 @@ Webvs.DynamicMovement = Webvs.defineClass(DynamicMovement, Webvs.Component, { } }, - /** - * moves the pixels - * @memberof Webvs.DynamicMovement# - */ - update: function() { - var code = this.code; - - // run init, if required - if(!this.inited) { - code.init(); - this.inited = true; - } - - var beat = this.main.analyser.beat; - code.b = beat?1:0; - // run per frame - code.perFrame(); - // run on beat - if(beat) { - code.onBeat(); - } - - if(this.noGrid) { - this.program.run(this.parent.fm, null, this.code); - } else { - this.program.run(this.parent.fm, null, this.code, this.gridVertices, this.gridVerticesSize); - } - }, - - /** - * releases resources - * @memberof Webvs.DynamicMovement# - */ - destroy: function() { - DynamicMovement.super.destroy.call(this); - this.program.cleanup(); + handleResize: function() { + this.code.updateDimVars(this.gl); } }); var GlslHelpers = { glslRectToPolar: function(coordMode) { - if(coordMode === "POLAR") { + if(coordMode === CoordModes.POLAR) { return [ "float ar = u_resolution.x/u_resolution.y;", "x=x*ar;", @@ -5905,7 +5630,7 @@ var GlslHelpers = { }, glslPolarToRect: function(coordMode) { - if(coordMode === "POLAR") { + if(coordMode === CoordModes.POLAR) { return [ "d = d*sqrt(2.0);", "x = d*sin(r)/ar;", @@ -5971,7 +5696,7 @@ var GlslHelpers = { } }; -function DMovProgramNG(coordMode, bFilter, compat, randSeed, exprCode) { +function DMovProgramNG(gl, coordMode, bFilter, compat, randSeed, exprCode, blend) { var fragmentShader = [ exprCode, this.glslFilter(bFilter, compat), @@ -5980,14 +5705,16 @@ function DMovProgramNG(coordMode, bFilter, compat, randSeed, exprCode) { " x = v_position.x*2.0-1.0;", " y = -(v_position.y*2.0-1.0);", this.glslRectToPolar(coordMode), + " alpha=0.5;", " perPixel();", this.glslPolarToRect(coordMode), - " setFragColor(vec4(filter(vec2(x, -y)), 1));", + " setFragColor(vec4(filter(vec2(x, -y)), "+(blend?"alpha":"1.0")+"));", "}" ]; - DMovProgramNG.super.constructor.call(this, { + DMovProgramNG.super.constructor.call(this, gl, { fragmentShader: fragmentShader, + blendMode: blend?Webvs.ALPHA:Webvs.REPLACE, swapFrame: true }); } @@ -5998,10 +5725,11 @@ Webvs.DMovProgramNG = Webvs.defineClass(DMovProgramNG, Webvs.QuadBoxProgram, Gls } }); -function DMovProgram(coordMode, bFilter, compat, randSeed, exprCode) { +function DMovProgram(gl, coordMode, bFilter, compat, randSeed, exprCode, blend) { var vertexShader = [ "attribute vec2 a_position;", "varying vec2 v_newPoint;", + "varying float v_alpha;", "uniform int u_coordMode;", exprCode, "void main() {", @@ -6009,7 +5737,9 @@ function DMovProgram(coordMode, bFilter, compat, randSeed, exprCode) { " x = a_position.x;", " y = -a_position.y;", this.glslRectToPolar(coordMode), + " alpha = 0.5;", " perPixel();", + " v_alpha = alpha;", this.glslPolarToRect(coordMode), " v_newPoint = vec2(x,-y);", " setPosition(a_position);", @@ -6018,13 +5748,15 @@ function DMovProgram(coordMode, bFilter, compat, randSeed, exprCode) { var fragmentShader = [ "varying vec2 v_newPoint;", + "varying float v_alpha;", this.glslFilter(bFilter, compat), "void main() {", - " setFragColor(vec4(filter(v_newPoint), 1));", + " setFragColor(vec4(filter(v_newPoint), "+(blend?"v_alpha":"1.0")+"));", "}" ]; - DMovProgram.super.constructor.call(this, { + DMovProgram.super.constructor.call(this, gl, { + blendMode: blend?Webvs.ALPHA:Webvs.REPLACE, fragmentShader: fragmentShader, vertexShader: vertexShader, swapFrame: true @@ -6038,190 +5770,78 @@ Webvs.DMovProgram = Webvs.defineClass(DMovProgram, Webvs.ShaderProgram, GlslHelp } }); -DynamicMovement.ui = { - type: "DynamicMovement", - disp: "Dynamic Movement", - schema: { - code: { - type: "object", - title: "Code", - default: {}, - properties: { - init: { - type: "string", - title: "Init", - }, - onBeat: { - type: "string", - title: "On Beat", - }, - perFrame: { - type: "string", - title: "Per Frame", - }, - perPixel: { - type: "string", - title: "Per Point", - } - }, - }, - gridW: { - type: "number", - title: "Grid Width", - default: 16, - }, - gridH: { - type: "number", - title: "Grid Height", - default: 16, - }, - coord: { - type: "string", - title: "Coordinate System", - enum: ["POLAR", "RECT"], - default: "POLAR" - } - }, - form: [ - { key: "code.init", type: "textarea" }, - { key: "code.onBeat", type: "textarea" }, - { key: "code.perFrame", type: "textarea" }, - { key: "code.perPixel", type: "textarea" }, - "gridW", - "gridH", - "coord" - ] -}; - })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * An alias class for {@link Webvs.DynamicMovement} with noGrid=true option - * @param {object} options - options object - * @param {string} [options.code.perPixel] - code that will be run once for every pixel. should set - * `x`, `y` or `d`, `r` variables (depending on coord) to specify point location. Note: state - * of this code does not persist. - * @param {boolean} [options.compat=false] - if true, then calculations are low precision. - * useful to map winamp AVS behaviour more closely - * @param {boolean} [options.bFilter=true] - use bilinear interpolation for pixel sampling - * @param {string} [options.coord="POLAR"] - coordinate system to be used viz. `POLAR`, `RECT` - * @augments Webvs.DynamicMovement - * @constructor - * @memberof Webvs - * @constructor - */ -function Movement(options) { - options = _.defaults(options, { - bFilter: true, - coord: "POLAR", - compat: false - }); - - Movement.super.constructor.call(this, { - noGrid: true, - bFilter: options.bFilter, - compat: options.compat, - coord: options.coord, - code: options.code - }); - this.options = options; +// A component that swizzles the color component +function ChannelShift(gl, main, parent, opts) { + ChannelShift.super.constructor.call(this, gl, main, parent, opts); } -Webvs.Movement = Webvs.defineClass(Movement, Webvs.DynamicMovement); -})(Webvs); +Webvs.registerComponent(ChannelShift, { + name: "ChannelShift", + menu: "Trans" +}); -/** - * Copyright (c) 2013 Azeem Arshad - * See the file license.txt for copying permission. - */ - -(function(Webvs) { - -var channels = ["RGB", "RBG", "BRG", "BGR", "GBR", "GRB"]; +var Channels = { + "RGB": 0, + "RBG": 1, + "BRG": 2, + "BGR": 3, + "GBR": 4, + "GRB": 5 +}; +ChannelShift.Channels = Channels; -/** - * @class - * A component that swizzles the color component - * - * @param {object} options - options object - * @param {string} [options.channel="RGB"] - the component combination - * viz. `RGB`, `RBG`, `BRG`, `BGR`, `GBR`, `GRB` - * @param {boolean} [options.onBeatRandom=false] - if set then the color components - * combination is changed randomly on beat - * @augments Webvs.Component - * @constructor - * @memberof Webvs - */ -function ChannelShift(options) { - options = _.defaults(options, { +Webvs.defineClass(ChannelShift, Webvs.Component, { + defaultOptions: { channel: "RGB", onBeatRandom: false - }); - - this.channel = channels.indexOf(options.channel); - if(this.channel == -1) { - throw new Error("Invalid Channel"); - } - this.onBeatRandom = options.onBeatRandom; - - this.program = new ChannelShiftProgram(); - - ChannelShift.super.constructor.apply(this, arguments); -} -Webvs.ChannelShift = Webvs.defineClass(ChannelShift, Webvs.Component, { - componentName: "ChannelShift", + }, - /** - * initializes the ChannelShift component - * @memberof Webvs.ChannelShift# - */ - init: function(gl, main, parent) { - ChannelShift.super.init.call(this, gl, main, parent); + onChange: { + channel: "updateChannel" + }, - this.program.init(gl); + init: function() { + this.program = new ChannelShiftProgram(this.gl); + this.updateChannel(); }, - /** - * shifts the colors - * @memberof Webvs.ChannelShift# - */ - update: function() { - if(this.onBeatRandom && this.main.analyser.beat) { - this.channel = Math.floor(Math.random() * channels.length); + draw: function() { + if(this.opts.onBeatRandom && this.main.analyser.beat) { + this.channel = Math.floor(Math.random() * ChannelShift.channels.length); } this.program.run(this.parent.fm, null, this.channel); }, - /** - * releases resources - * @memberof Webvs.ChannelShift# - */ destroy: function() { ChannelShift.super.destroy.call(this); - this.program.cleanup(); - } + this.program.destroy(); + }, + updateChannel: function() { + this.channel = Webvs.getEnumValue(this.opts.channel, Channels); + } }); -function ChannelShiftProgram() { - ChannelShiftProgram.super.constructor.call(this, { +function ChannelShiftProgram(gl) { + ChannelShiftProgram.super.constructor.call(this, gl, { swapFrame: true, fragmentShader: [ "uniform int u_channel;", "void main() {", " vec3 color = getSrcColor().rgb;", - _.flatMap(channels, function(channel, index) { + _.flatMap(_.keys(Channels), function(channel) { return [ - "if(u_channel == "+index+") {", + "if(u_channel == "+Channels[channel]+") {", " setFragColor(vec4(color." + channel.toLowerCase() + ",1));", "}" ]; @@ -6237,84 +5857,68 @@ Webvs.ChannelShiftProgram = Webvs.defineClass(ChannelShiftProgram, Webvs.QuadBox } }); -ChannelShift.ui = { - disp: "Channel Shift", - type: "ChannelShift", - schema: { - channel: { - type: "string", - title: "Channel", - enum: channels - }, - onBeatRandom: { - type: "boolean", - title: "On beat random", - } - } -}; - })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A Component that applies a unique color tone - * @param {object} options - options object - * @param {string} [options.color="#FFFFFF"] - the color tone - * @param {boolean} [options.invert=false] - if set then tone is inverted - * @param {string} [options.blendMode="REPLACE"] - blending mode for this component - * @augments Webvs.Component - * @memberof Webvs - * @constructor - */ -function UniqueTone(options) { - options = _.defaults(options, { +// A Component that applies a unique color tone +function UniqueTone(gl, main, parent, opts) { + UniqueTone.super.constructor.call(this, gl, main, parent, opts); +} + +Webvs.registerComponent(UniqueTone, { + name: "UniqueTone", + menu: "Trans" +}); + +Webvs.defineClass(UniqueTone, Webvs.Component, { + defaultOptions: { color: "#ffffff", invert: false, blendMode: "REPLACE" - }); + }, - this.tone = Webvs.parseColorNorm(options.color); - this.invert = options.invert; - this.program = new UniqueToneProgram(Webvs.getBlendMode(options.blendMode)); -} -Webvs.UniqueTone = Webvs.defineClass(UniqueTone, Webvs.Component, { - /** - * initializes the UniqueTone component - * @memberof Webvs.UniqueTone# - */ - init: function(gl, main, parent) { - UniqueTone.super.init.call(this, gl, main, parent); - this.program.init(gl); + onChange: { + color: "updateColor", + blendMode: "updateProgram" }, - /** - * applies unique tone - * @memberof Webvs.UniqueTone# - */ - update: function() { - this.program.run(this.parent.fm, null, this.tone, this.invert); + init: function() { + this.updateColor(); + this.updateProgram(); + }, + + draw: function() { + this.program.run(this.parent.fm, null, this.tone, this.opts.invert); }, - /** - * releases resources - * @memberof Webvs.UniqueTone# - */ destroy: function() { UniqueTone.super.destroy.call(this); - this.program.cleanup(); + this.program.destroy(); + }, + + updateColor: function() { + this.tone = Webvs.parseColorNorm(this.opts.color); + }, + + updateProgram: function() { + var blendMode = Webvs.getEnumValue(this.opts.blendMode, Webvs.BlendModes); + var program = new UniqueToneProgram(this.gl, blendMode); + if(this.program) { + this.program.cleanup(); + } + this.program = program; } }); -function UniqueToneProgram(blendMode) { - UniqueToneProgram.super.constructor.call(this, { - outputBlendMode: blendMode, +function UniqueToneProgram(gl, blendMode) { + UniqueToneProgram.super.constructor.call(this, gl, { + blendMode: blendMode, swapFrame: true, fragmentShader: [ "uniform vec3 u_tone;", @@ -6341,212 +5945,703 @@ Webvs.UniqueToneProgram = Webvs.defineClass(UniqueToneProgram, Webvs.QuadBoxProg })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { +function Invert(gl, main, parent, opts) { + Invert.super.constructor.call(this, gl, main, parent, opts); +} + +Webvs.registerComponent(Invert, { + name: "Invert", + menu: "Trans" +}); + +Webvs.defineClass(Invert, Webvs.Component, { + defaultOptions: {}, + init: function() { + this.program = new InvertProgram(this.gl); + }, + + draw: function() { + this.program.run(this.parent.fm, null); + }, + + destroy: function() { + Invert.super.destroy.call(this); + this.program.destroy(); + } +}); + +function InvertProgram(gl) { + InvertProgram.super.constructor.call(this, gl, { + swapFrame: true, + fragmentShader: [ + "void main() {", + " setFragColor(vec4(1,1,1,1)-getSrcColor());", + "}" + ] + }); +} +Webvs.InvertProgram = Webvs.defineClass(InvertProgram, Webvs.QuadBoxProgram); + +})(Webvs); + /** - * @class - * A generic scope, that can draw points or lines based on user code - * - * #### Code variables - * - * The following variables are available in the code - * - * + n (default: 100) - the number of points. - * + i - 0-1 normalized loop counter - * + v - the value of the superscope at current position (-1 to +1) - * + x - x position of the dot (-1 to +1) - * + y - y position of the dot (-1 to +1) - * + w - width of the screen - * + h - height of the screen - * + b - 1 if a beat has occured else 0 - * + red (default: set from colors option) - red component of color (0-1) - * + green (default: set from colors option) - green component of color (0-1) - * + blue (default: set from colors option) - blue component of color (0-1) - * + cid - the clone id of this component. if it is a clone - * - * @param {object} options - options object - * @param {string} [options.code.init] - code to be run at startup - * @param {string} [options.code.onBeat] - code to be run when a beat occurs - * @param {string} [options.code.perFrame] - code to be run on every frame - * @param {string} [options.code.perPoint] - code that will be run once for every point. should set - * `x`, `y` variables to specify point location. set `red`, `green` or `blue` variables - * to specify point color - * @param {string} [options.source="SPECTRUM"] - the scope data source viz. `SPECTRUM`, `WAVEFORM` - * @param {string} [options.drawMode="LINES"] - switch between drawing `LINES` or `DOTS` - * @param {Array.} [options.colors=["#FFFFFF"]] - rendering color cycles through these colors - * @param {number} [options.thickness] - thickenss of line or dot - * @augments Webvs.Component - * @constructor - * @memberof Webvs + * Copyright (c) 2013-2015 Azeem Arshad + * See the file license.txt for copying permission. */ -function SuperScope(options) { - Webvs.checkRequiredOptions(options, ["code"]); - options = _.defaults(options, { - source: "SPECTRUM", - drawMode: "LINES", - colors: ["#ffffff"] - }); - var codeSrc; - if(_.isObject(options.code)) { - codeSrc = options.code; - } else { - throw new Error("Invalid superscope"); +(function(Webvs) { + +function Mosaic(gl, main, parent, opts) { + Mosaic.super.constructor.call(this, gl, main, parent, opts); +} + +Webvs.registerComponent(Mosaic, { + name: "Mosaic", + menu: "Trans" +}); + +Webvs.defineClass(Mosaic, Webvs.Component, { + defaultOptions: { + blendMode: "REPLACE", + squareSize: 0.5, + onBeatSizeChange: false, + onBeatSquareSize: 1, + onBeatSizeDuration: 10 + }, + + onChange: { + blendMode: "updateProgram" + }, + + init: function() { + this.frameCount = 0; + this.size = this.opts.squareSize; + this.updateProgram(); + }, + + draw: function() { + if(this.opts.onBeatSizeChange && this.main.analyser.beat) { + this.size = this.opts.onBeatSquareSize; + this.frameCount = this.opts.onBeatSizeDuration; + } + + if(this.size !== 0) { + var sizeX = 1/Math.floor(this.size*(this.gl.drawingBufferWidth-1)+1); + var sizeY = 1/Math.floor(this.size*(this.gl.drawingBufferHeight-1)+1); + this.program.run(this.parent.fm, null, sizeX, sizeY); + } + + if(this.frameCount > 0) { + this.frameCount--; + if(this.frameCount === 0) { + this.size = this.opts.squareSize; + } else { + var incr = Math.abs(this.opts.squareSize-this.opts.onBeatSquareSize)/ + this.opts.onBeatSizeDuration; + this.size += incr * (this.opts.onBeatSquareSize>this.opts.squareSize?-1:1); + } + } + }, + + destroy: function() { + Mosaic.super.destroy.call(this); + this.program.destroy(); + }, + + updateProgram: function() { + var blendMode = Webvs.getEnumValue(this.opts.blendMode, Webvs.BlendModes); + var program = new Webvs.MosaicProgram(this.gl, blendMode); + if(this.program) { + this.program.destroy(); + } + this.program = program; } - var codeGen = new Webvs.ExprCodeGenerator(codeSrc, ["n", "v", "i", "x", "y", "b", "red", "green", "blue"]); - this.code = codeGen.generateJs(["init", "onBeat", "perFrame", "perPoint"]); - this.code.n = 100; - this.clone = options.clone || 1; +}); - this.spectrum = options.source == "SPECTRUM"; - this.dots = options.drawMode == "DOTS"; +function MosaicProgram(gl, blendMode) { + MosaicProgram.super.constructor.call(this, gl, { + swapFrame: true, + blendMode: blendMode, + fragmentShader: [ + "uniform vec2 u_size;", + "void main() {", + " vec2 samplePos = u_size * ( floor(v_position/u_size) + vec2(0.5,0.5) );", + " setFragColor(getSrcColorAtPos(samplePos));", + "}" + ] + }); +} +Webvs.MosaicProgram = Webvs.defineClass(MosaicProgram, Webvs.QuadBoxProgram, { + draw: function(sizeX, sizeY) { + this.setUniform("u_size", "2f", sizeX, sizeY); + MosaicProgram.super.draw.call(this); + } +}); - this.colors = _.map(options.colors, Webvs.parseColorNorm); - this.currentColor = []; - this.maxStep = 100; +})(Webvs); - this.step = this.maxStep; // so that we compute steps, the first time - this.colorId = 0; - this.colorStep = [0,0,0]; +/** + * Copyright (c) 2013-2015 Azeem Arshad + * See the file license.txt for copying permission. + */ - this.thickness = options.thickness?options.thickness:1; +(function(Webvs) { - this.inited = false; +// A component that mirror between quandrants +function Mirror(gl, main, parent, opts) { + Mirror.super.constructor.call(this, gl, main, parent, opts); +} - this.program = new SuperScopeShader(); +Webvs.registerComponent(Mirror, { + name: "Mirror", + menu: "Trans" +}); - SuperScope.super.constructor.apply(this, arguments); +Webvs.defineClass(Mirror, Webvs.Component, { + defaultOptions: { + onBeatRandom: false, + topToBottom: true, + bottomToTop: false, + leftToRight: false, + rightToLeft: false, + smoothTransition: false, + transitionDuration: 4 + }, + + onChange: { + topToBottom: "updateMap", + bottomToTop: "updateMap", + leftToRight: "updateMap", + rightToLeft: "updateMap" + }, + + init: function() { + this.program = new MirrorProgram(this.gl); + this.animFrameCount = 0; + this.mix = [ + [0, 0, 0, 0], + [1, 0, 0, 0], + [2, 0, 0, 0], + [3, 0, 0, 0] + ]; + this.mixDelta = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0] + ]; + this.updateMap(); + }, + + draw: function() { + if(this.opts.onBeatRandom && this.main.analyser.beat) { + this._setQuadrantMap(true); + } + + this.program.run(this.parent.fm, null, this._inTransition(), this.mix); + + if(this._inTransition()) { + this.animFrameCount--; + if(this.animFrameCount === 0) { + this._setMix(true); + } else { + for(var i = 0;i < 4;i++) { + for(var j = 0;j < 4;j++) { + this.mix[i][j] += this.mixDelta[i][j]; + } + } + } + } + }, + + updateMap: function(random) { + this._setQuadrantMap(false); + }, + + _inTransition: function() { + return (this.opts.smoothTransition && this.animFrameCount !== 0); + }, + + _setQuadrantMap: function(random) { + var map = [0, 1, 2, 3]; + var mirrorOpts = this.opts; + if(random) { + var randVal = Math.floor(Math.random()*16); + mirrorOpts = { + topToBottom: (randVal & 1) && this.opts.topToBottom, + bottomToTop: (randVal & 2) && this.opts.bottomToTop, + leftToRight: (randVal & 4) && this.opts.leftToRight, + rightToLeft: (randVal & 8) && this.opts.rightToLeft + }; + } + if(mirrorOpts.topToBottom) { + map[2] = map[0]; map[3] = map[1]; + } + if(mirrorOpts.bottomToTop) { + map[0] = map[2]; map[1] = map[3]; + } + if(mirrorOpts.leftToRight) { + map[1] = map[0]; map[3] = map[2]; + } + if(mirrorOpts.rightToLeft) { + map[0] = map[1]; map[2] = map[3]; + } + this.map = map; + + this._setMix(false); + }, + + _setMix: function(noTransition) { + var i, j; + if(this.opts.smoothTransition && !noTransition) { + // set mix vectors to second format if we are not already + // in the middle of a transition + if(this.animFrameCount === 0) { + for(i = 0;i < 4;i++) { + var quad = this.mix[i][0]; + this.mix[i][0] = 0; + this.mix[i][quad] = 1; + } + } + + // calculate the mix delta values + for(i = 0;i < 4;i++) { + for(j = 0;j < 4;j++) { + var endValue = (j == this.map[i])?1:0; + this.mixDelta[i][j] = (endValue - this.mix[i][j])/this.opts.transitionDuration; + } + } + + this.animFrameCount = this.opts.transitionDuration; + } else { + // set mix value to first format + for(i = 0;i < 4;i++) { + this.mix[i][0] = this.map[i]; + for(j = 1;j < 4;j++) { + this.mix[i][j] = 0; + } + } + } + } +}); + +// Working: +// The program accepts a mode and 4 mix vectors, one for each of the 4 quadrants. +// The mode decides between two scenarios Case 1. a simple +// mapping ie. one quadrant is copied over to another. +// In this case the first value of each vec4 will contain the +// id of the quadrant from where the pixels will be copied +// Case 2. This is used during transition animation. Here, the +// final color of pixels in each quadrant is a weighted mix +// of colors of corresponding mirrored points in all quadrants. +// Each of the vec4 contains a mix weight for each 4 quadrants. As +// the animation proceeds, one of the 4 in the vec4 becomes 1 while others +// become 0. This two mode allows to make fewer texture sampling when +// not doing transition animation. +// +// The quadrant ids are as follows +// | +// 0 | 1 +// ----------- +// 2 | 3 +// | +function MirrorProgram(gl) { + MirrorProgram.super.constructor.call(this, gl, { + swapFrame: true, + fragmentShader: [ + "uniform int u_mode;", + "uniform vec4 u_mix0;", + "uniform vec4 u_mix1;", + "uniform vec4 u_mix2;", + "uniform vec4 u_mix3;", + + "#define getQuadrant(pos) ( (pos.x<0.5) ? (pos.y<0.5?2:0) : (pos.y<0.5?3:1) )", + "#define check(a,b, c,d,e,f) ( ((a==c || a==d) && (b==e || b==f)) || ((a==e || a==f) && (b==c || b==d)) )", + "#define xFlip(qa, qb) (check(qa,qb, 0,2, 1,3)?-1:1)", + "#define yFlip(qa, qb) (check(qa,qb, 0,1, 2,3)?-1:1)", + "#define mirrorPos(pos,qa,qb) ((pos-vec2(0.5,0.5))*vec2(xFlip(qa,qb),yFlip(qa,qb))+vec2(0.5,0.5))", + "#define getMirrorColor(pos,qa,qb) (getSrcColorAtPos(mirrorPos(pos,qa,qb)))", + + "void main() {", + " int quadrant = getQuadrant(v_position);", + " vec4 mix;", + " if(quadrant == 0) { mix = u_mix0; }", + " else if(quadrant == 1) { mix = u_mix1; }", + " else if(quadrant == 2) { mix = u_mix2; }", + " else if(quadrant == 3) { mix = u_mix3; }", + " if(u_mode == 0) {", + " int otherQuadrant = int(mix.x);", + " setFragColor(getMirrorColor(v_position, quadrant, otherQuadrant));", + " } else {", + " vec4 c0 = getMirrorColor(v_position, quadrant, 0);", + " vec4 c1 = getMirrorColor(v_position, quadrant, 1);", + " vec4 c2 = getMirrorColor(v_position, quadrant, 2);", + " vec4 c3 = getMirrorColor(v_position, quadrant, 3);", + + " setFragColor(vec4(", + " dot(vec4(c0.r,c1.r,c2.r,c3.r), mix),", + " dot(vec4(c0.g,c1.g,c2.g,c3.g), mix),", + " dot(vec4(c0.b,c1.b,c2.b,c3.b), mix),", + " 1.0", + " ));", + " }", + "}" + ] + }); } -Webvs.SuperScope = Webvs.defineClass(SuperScope, Webvs.Component, { - componentName: "SuperScope", +Webvs.MirrorProgram = Webvs.defineClass(MirrorProgram, Webvs.QuadBoxProgram, { + draw: function(transition, mix) { + this.setUniform("u_mode", "1i", transition?1:0); + for(var i = 0;i < 4;i++) { + this.setUniform.apply(this, ["u_mix"+i, "4f"].concat(mix[i])); + } + MirrorProgram.super.draw.call(this); + } +}); - /** - * initializes the SuperScope component - * @memberof Webvs.SuperScope# - */ - init: function(gl, main, parent) { - SuperScope.super.init.call(this, gl, main, parent); - this.program.init(gl); - this.code.setup(main, this); + +})(Webvs); + +/** + * Copyright (c) 2013-2015 Azeem Arshad + * See the file license.txt for copying permission. + */ + +(function(Webvs) { + +// A generic scope, that can draw points or lines based on user code +function SuperScope(gl, main, parent, opts) { + SuperScope.super.constructor.call(this, gl, main, parent, opts); +} + +Webvs.registerComponent(SuperScope, { + name: "SuperScope", + menu: "Render" +}); + +var DrawModes = { + "LINES": 1, + "DOTS": 2 +}; +SuperScope.DrawModes = DrawModes; - this.code = Webvs.CodeInstance.clone(this.code, this.clone); +Webvs.defineClass(SuperScope, Webvs.Component, { + defaultOptions: { + code: { + init: "n=800", + perFrame: "t=t-0.05", + onBeat: "", + perPoint: "d=i+v*0.2; r=t+i*$PI*4; x=cos(r)*d; y=sin(r)*d" + }, + blendMode: "REPLACE", + channel: "CENTER", + source: "SPECTRUM", + drawMode: "LINES", + thickness: 1, + clone: 1, + colors: ["#ffffff"], + cycleSpeed: 0.01 }, - update: function() { - this._stepColor(); + onChange: { + code: ["updateCode", "updateClones"], + colors: "updateColors", + cycleSpeed: "updateSpeed", + clone: "updateClones", + channel: "updateChannel", + thickness: "updateThickness", + blendMode: "updateProgram", + drawMode: "updateDrawMode", + source: "updateSource", + }, + + init: function() { + this.updateDrawMode(); + this.updateSource(); + this.updateProgram(); + this.updateCode(); + this.updateClones(); + this.updateSpeed(); + this.updateColors(); + this.updateChannel(); + this.updateThickness(); + this.listenTo(this.main, "resize", this.handleResize); + }, + + draw: function() { + var color = this._makeColor(); _.each(this.code, function(code) { - this.drawScope(code, !this.inited); + this.drawScope(code, color, !this.inited); }, this); this.inited = true; }, + destroy: function() { + SuperScope.super.destroy.call(this); + this.program.destroy(); + }, + /** * renders the scope * @memberof Webvs.SuperScope# */ - drawScope: function(code, runInit) { + drawScope: function(code, color, runInit) { var gl = this.gl; - code.red = this.currentColor[0]; - code.green = this.currentColor[1]; - code.blue = this.currentColor[2]; + code.red = color[0]; + code.green = color[1]; + code.blue = color[2]; if(runInit) { code.init(); } - var beat = this.main.analyser.beat; - code.b = beat?1:0; - code.perFrame(); - if(beat) { - code.onBeat(); + var beat = this.main.analyser.beat; + code.b = beat?1:0; + code.perFrame(); + if(beat) { + code.onBeat(); + } + + var nPoints = Math.floor(code.n); + var data; + if(this.source == Webvs.Source.SPECTRUM) { + data = this.main.analyser.getSpectrum(this.channel); + } else { + data = this.main.analyser.getWaveform(this.channel); + } + var dots = this.drawMode == DrawModes.DOTS; + var bucketSize = data.length/nPoints; + var pbi = 0; + var cdi = 0; + + var bufferSize, thickX, thickY; + var lastX, lastY, lastR, lastG, lastB; + if(this.veryThick) { + bufferSize = (dots?(nPoints*6):(nPoints*6-6)); + thickX = this.opts.thickness/this.gl.drawingBufferWidth; + thickY = this.opts.thickness/this.gl.drawingBufferHeight; + } else { + bufferSize = (dots?nPoints:(nPoints*2-2)); + } + + var pointBufferData = new Float32Array(bufferSize * 2); + var colorData = new Float32Array(bufferSize * 3); + for(var i = 0;i < nPoints;i++) { + var value = 0; + var size = 0; + var j; + for(j = Math.floor(i*bucketSize);j < (i+1)*bucketSize;j++,size++) { + value += data[j]; + } + value = value/size; + + var pos = i/((nPoints > 1)?(nPoints-1):1); + code.i = pos; + code.v = value; + code.perPoint(); + code.y *= -1; + if(this.veryThick) { + if(dots) { + // just a box at current point + pointBufferData[pbi++] = code.x-thickX; + pointBufferData[pbi++] = code.y-thickY; + + pointBufferData[pbi++] = code.x+thickX; + pointBufferData[pbi++] = code.y-thickY; + + pointBufferData[pbi++] = code.x-thickX; + pointBufferData[pbi++] = code.y+thickY; + + pointBufferData[pbi++] = code.x+thickX; + pointBufferData[pbi++] = code.y-thickY; + + pointBufferData[pbi++] = code.x-thickX; + pointBufferData[pbi++] = code.y+thickY; + + pointBufferData[pbi++] = code.x+thickX; + pointBufferData[pbi++] = code.y+thickY; + + for(j = 0;j < 6;j++) { + colorData[cdi++] = code.red; + colorData[cdi++] = code.green; + colorData[cdi++] = code.blue; + } + } else { + if(i !== 0) { + var xdiff = Math.abs(lastX-code.x); + var ydiff = Math.abs(lastY-code.y); + var xoff = (xdiff <= ydiff)?thickX:0; + var yoff = (xdiff > ydiff)?thickY:0; + + // a rectangle from last point to the current point + pointBufferData[pbi++] = lastX+xoff; + pointBufferData[pbi++] = lastY+yoff; + + pointBufferData[pbi++] = code.x+xoff; + pointBufferData[pbi++] = code.y+yoff; + + pointBufferData[pbi++] = lastX-xoff; + pointBufferData[pbi++] = lastY-yoff; + + pointBufferData[pbi++] = code.x+xoff; + pointBufferData[pbi++] = code.y+yoff; + + pointBufferData[pbi++] = lastX-xoff; + pointBufferData[pbi++] = lastY-yoff; + + pointBufferData[pbi++] = code.x-xoff; + pointBufferData[pbi++] = code.y-yoff; + + for(j = 0;j < 6;j++) { + colorData[cdi++] = code.red; + colorData[cdi++] = code.green; + colorData[cdi++] = code.blue; + } + } + lastX = code.x; + lastY = code.y; + lastR = code.red; + lastG = code.green; + lastB = code.blue; + } + } else { + if(dots) { + // just a point at the current point + pointBufferData[pbi++] = code.x; + pointBufferData[pbi++] = code.y; + + colorData[cdi++] = code.red; + colorData[cdi++] = code.green; + colorData[cdi++] = code.blue; + } else { + if(i !== 0) { + // lines from last point to current point + pointBufferData[pbi++] = lastX; + pointBufferData[pbi++] = lastY; + + pointBufferData[pbi++] = code.x; + pointBufferData[pbi++] = code.y; + + for(j = 0;j < 2;j++) { + // use current color for both points because + // we dont want color interpolation between points + colorData[cdi++] = code.red; + colorData[cdi++] = code.green; + colorData[cdi++] = code.blue; + } + } + lastX = code.x; + lastY = code.y; + lastR = code.red; + lastG = code.green; + lastB = code.blue; + } + } + } + + this.program.run(this.parent.fm, null, pointBufferData, colorData, dots, this.veryThick?1:this.opts.thickness, this.veryThick); + }, + + updateProgram: function() { + var blendMode = Webvs.getEnumValue(this.opts.blendMode, Webvs.BlendModes); + var program = new SuperScopeShader(this.gl, blendMode); + if(this.program) { + this.program.destroy(); } + this.program = program; + }, - var nPoints = Math.floor(code.n); - var data = this.spectrum ? this.main.analyser.getSpectrum() : this.main.analyser.getWaveform(); - var bucketSize = data.length/nPoints; - var pbi = 0; - var cdi = 0; + updateCode: function() { + var code = Webvs.compileExpr(this.opts.code, ["init", "onBeat", "perFrame", "perPoint"]).codeInst; + code.n = 100; + code.setup(this.main, this); + this.inited = false; + this.code = [code]; + }, - var pointBufferData = new Float32Array((this.dots?nPoints:(nPoints*2-2)) * 2); - var colorData = new Float32Array((this.dots?nPoints:(nPoints*2-2)) * 3); - for(var i = 0;i < nPoints;i++) { - var value = 0; - var size = 0; - for(var j = Math.floor(i*bucketSize);j < (i+1)*bucketSize;j++,size++) { - value += data[j]; - } - value = value/size; + updateClones: function() { + this.code = Webvs.CodeInstance.clone(this.code, this.opts.clone); + }, - var pos = i/((nPoints > 1)?(nPoints-1):1); - code.i = pos; - code.v = value; - code.perPoint(); - pointBufferData[pbi++] = code.x; - pointBufferData[pbi++] = code.y*-1; - if(i !== 0 && i != nPoints-1 && !this.dots) { - pointBufferData[pbi++] = code.x; - pointBufferData[pbi++] = code.y*-1; - } - if(this.dots) { - colorData[cdi++] = code.red; - colorData[cdi++] = code.green; - colorData[cdi++] = code.blue; - } else if(i !== 0) { - colorData[cdi++] = code.red; - colorData[cdi++] = code.green; - colorData[cdi++] = code.blue; - colorData[cdi++] = code.red; - colorData[cdi++] = code.green; - colorData[cdi++] = code.blue; - } + updateColors: function() { + this.colors = _.map(this.opts.colors, Webvs.parseColorNorm); + this.curColorId = 0; + }, + + updateSpeed: function() { + var oldMaxStep = this.maxStep; + this.maxStep = Math.floor(1/this.opts.cycleSpeed); + if(this.curStep) { + // curStep adjustment when speed changes + this.curStep = Math.floor((this.curStep/oldMaxStep)*this.maxStep); + } else { + this.curStep = 0; } + }, - this.program.run(this.parent.fm, null, pointBufferData, colorData, this.dots, this.thickness); + updateChannel: function() { + this.channel = Webvs.getEnumValue(this.opts.channel, Webvs.Channels); }, - /** - * releases resources - * @memberof Webvs.SuperScope# - */ - destroy: function() { - SuperScope.super.destroy.call(this); - this.program.cleanup(); + updateSource: function() { + this.source = Webvs.getEnumValue(this.opts.source, Webvs.Source); }, - _stepColor: function() { - var i; - if(this.colors.length > 1) { - if(this.step == this.maxStep) { - var curColor = this.colors[this.colorId]; - this.colorId = (this.colorId+1)%this.colors.length; - var nextColor = this.colors[this.colorId]; - for(i = 0;i < 3;i++) { - this.colorStep[i] = (nextColor[i]-curColor[i])/this.maxStep; - } - this.step = 0; - for(i = 0;i < 3;i++) { - this.currentColor[i] = curColor[i]; - } - } else { - for(i = 0;i < 3;i++) { - this.currentColor[i] += this.colorStep[i]; - } - this.step++; - } + updateDrawMode: function() { + this.drawMode = Webvs.getEnumValue(this.opts.drawMode, DrawModes); + }, + + updateThickness: function() { + var range; + if(this.drawMode == DrawModes.DOTS) { + range = this.gl.getParameter(this.gl.ALIASED_POINT_SIZE_RANGE); + } else { + range = this.gl.getParameter(this.gl.ALIASED_LINE_WIDTH_RANGE); + } + if(this.opts.thickness < range[0] || this.opts.thickness > range[1]) { + this.veryThick = true; + } else { + this.veryThick = false; + } + }, + + _makeColor: function() { + if(this.colors.length == 1) { + return this.colors[0]; } else { - this.currentColor = this.colors[0]; + var color = []; + var currentColor = this.colors[this.curColorId]; + var nextColor = this.colors[(this.curColorId+1)%this.colors.length]; + var mix = this.curStep/this.maxStep; + for(var i = 0;i < 3;i++) { + color[i] = currentColor[i]*(1-mix) + nextColor[i]*mix; + } + this.curStep = (this.curStep+1)%this.maxStep; + if(this.curStep === 0) { + this.curColorId = (this.curColorId+1)%this.colors.length; + } + return color; } + }, + + handleResize: function() { + _.each(this.code, function(code) { + code.updateDimVars(this.gl); + }, this); } }); -function SuperScopeShader() { - SuperScopeShader.super.constructor.call(this, { +function SuperScopeShader(gl, blendMode) { + SuperScopeShader.super.constructor.call(this, gl, { copyOnSwap: true, + blendMode: blendMode, vertexShader: [ "attribute vec2 a_position;", "attribute vec3 a_color;", @@ -6554,7 +6649,7 @@ function SuperScopeShader() { "uniform float u_pointSize;", "void main() {", " gl_PointSize = u_pointSize;", - " setPosition(clamp(a_position, vec2(-1,-1), vec2(1,1)));", + " setPosition(a_position);", " v_color = a_color;", "}" ], @@ -6567,7 +6662,7 @@ function SuperScopeShader() { }); } Webvs.SuperScopeShader = Webvs.defineClass(SuperScopeShader, Webvs.ShaderProgram, { - draw: function(points, colors, dots, thickness) { + draw: function(points, colors, dots, thickness, triangles) { var gl = this.gl; this.setUniform("u_pointSize", "1f", thickness); @@ -6580,7 +6675,15 @@ Webvs.SuperScopeShader = Webvs.defineClass(SuperScopeShader, Webvs.ShaderProgram gl.lineWidth(thickness); } - gl.drawArrays(dots?gl.POINTS:gl.LINES, 0, points.length/2); + var mode; + if(triangles) { + mode = gl.TRIANGLES; + } else if(dots) { + mode = gl.POINTS; + } else { + mode = gl.LINES; + } + gl.drawArrays(mode, 0, points.length/2); if(!dots) { gl.lineWidth(prevLineWidth); @@ -6588,227 +6691,112 @@ Webvs.SuperScopeShader = Webvs.defineClass(SuperScopeShader, Webvs.ShaderProgram } }); -SuperScope.ui = { - disp: "SuperScope", - type: "SuperScope", - schema: { - code: { - type: "object", - title: "Code", - default: {}, - properties: { - init: { - type: "string", - title: "Init", - }, - onBeat: { - type: "string", - title: "On Beat", - }, - perFrame: { - type: "string", - title: "Per Frame", - }, - perPoint: { - type: "string", - title: "Per Point", - } - }, - }, - source: { - type: "string", - title: "Source", - default: "WAVEFORM", - enum: ["WAVEFORM", "SPECTRUM"] - }, - drawMode: { - type: "string", - title: "Draw Mode", - default: "LINES", - enum: ["DOTS", "LINES"] - }, - colors: { - type: "array", - title: "Cycle Colors", - items: { - type: "string", - format: "color", - default: "#FFFFFF" - } - } - } -}; - - })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A simple scope that displays either waveform or spectrum data - * @param {object} options - options object - * @param {string} [options.drawMode="SOLID"] - draw mode viz. `SOLID`, `DOTS`, `LINES` - * @param {string} [options.source="WAVEFORM"] - scope data source viz. `WAVEFORM`, `SPECTRUM` - * @param {string} [options.align="CENTER"] - scope alignment viz. `TOP`, `CENTER`, `BOTTOM` - * @param {Array.} [options.colors=["#FFFFFF"]] - rendering color cycles through these colors - * @augments Webvs.SuperScope - * @constructor - * @memberof Webvs - */ -function Simple(options) { - options = _.defaults(options, { - drawMode: "SOLID", - source: "WAVEFORM", - align: "CENTER", - colors: ["#ffffff"] - }); - - var code = {}; - if(options.drawMode != "SOLID") { - code.init = "n=w;"; - code.perPoint = ({ - "TOP": "x=i*2-1; y=-v/2-0.5;", - "CENTER": "x=i*2-1; y=-v/2;", - "BOTTOM": "x=i*2-1; y=v/2+0.5;" - })[options.align]; - } else { - code.init = "n=w*2;"; - code.perFrame = "c=0;"; - if(options.source == "SPECTRUM") { - code.perPoint = ({ - "TOP": "x=i*2-1; y=if(c%2,0,-v/2-0.5); c=c+1;", - "CENTER": "x=i*2-1; y=if(c%2,0.5,-v/2); c=c+1;", - "BOTTOM": "x=i*2-1; y=if(c%2,0,v/2+0.5); c=c+1;", - })[options.align]; - } else { - code.perPoint = ({ - "TOP": "x=i*2-1; y=if(c%2,-0.5,-v/2-0.5); c=c+1;", - "CENTER": "x=i*2-1; y=if(c%2,0,-v/2); c=c+1;", - "BOTTOM": "x=i*2-1; y=if(c%2,0.5,v/2+0.5); c=c+1;", - })[options.align]; - } - } - - Simple.super.constructor.call(this, { - source: options.source, - drawMode: (options.drawMode=="SOLID"?"LINES":options.drawMode), - colors: options.colors, - code: code - }); - this.options = options; // set Simple option instead of superscope options +// A SuperScope like component that places images at points. +function Texer(gl, main, parent, opts) { + Texer.super.constructor.call(this, gl, main, parent, opts); } -Webvs.Simple = Webvs.defineClass(Simple, Webvs.SuperScope); - -})(Webvs); - -/** - * Copyright (c) 2013 Azeem Arshad - * See the file license.txt for copying permission. - */ -(function(Webvs) { +Webvs.registerComponent(Texer, { + name: "Texer", + menu: "Render" +}); -/** - * @class - * A SuperScope like component that places images at points. - * - * The following variables are available in the code - * - * + n (default: 100) - the number of points. - * + i - 0-1 normalized loop counter - * + v - the value of the superscope at current position - * + x - x position of the image (-1 to +1) - * + y - y position of the image (-1 to +1) - * + sizex - horizontal scale of the image. 1 = original size, 0.5 = half the size etc. - * + sizey - vertical scale of the image. 1 = original size, 0.5 = half the size etc. - * + y - y position of the image (-1 to +1) - * + w - width of the screen - * + h - height of the screen - * + b - 1 if a beat has occured else 0 - * + red (default: 1) - red component of color (0-1) - * + green (default: 1) - green component of color (0-1) - * + blue (default: 1) - blue component of color (0-1) - * + cid - the clone id of this component. if it is a clone - * - * @param {object} options - options object - * @param {string} [options.code.init] - code to be run at startup - * @param {string} [options.code.onBeat] - code to be run when a beat occurs - * @param {string} [options.code.perFrame] - code to be run on every frame - * @param {string} [options.code.perPoint] - code that will be run once for every point. should set - * `x`, `y` variables to specify image location. set `red`, `green` or `blue` variables - * to specify color filter. set `sizex` or `sizey` for horizontal and vertical scaling. - * @param {string} [options.source="SPECTRUM"] - the scope data source viz. `SPECTRUM`, `WAVEFORM` - * @param {boolean} [options.wrapAround=false] - if set then images hanging off the edge wraps around - * from the other side - * @param {boolean} [options.colorFiltering=false] - if set then color filter is applied to image - * @augments Webvs.Component - * @constructor - * @memberof Webvs - */ -function Texer(options) { - Webvs.checkRequiredOptions(options, ["code", "imageSrc"]); - options = _.defaults(options, { +Webvs.defineClass(Texer, Webvs.Component, { + defaultOptions: { + code: { + init: "", + onBeat: "", + perFrame: "", + perPoint: "" + }, + imageSrc: "avsres_texer_circle_edgeonly_19x19.bmp", source: "SPECTRUM", resizing: false, wrapAround: false, + clone: 1, colorFiltering: true - }); + }, - this.resizing = options.resizing; - this.colorFiltering = options.colorFiltering; - this.wrapAround = options.wrapAround; - this.imageSrc = options.imageSrc; + onChange: { + code: "updateCode", + clone: "updateClone", + imageSrc: "updateImage", + source: "updateSource" + }, - var codeGen = new Webvs.ExprCodeGenerator(options.code, ["n", "v", "i", "x", "y", "b", "sizex", "sizey", "red", "green", "blue"]); - this.code = codeGen.generateJs(["init", "onBeat", "perFrame", "perPoint"]); - this.code.n = 100; - this.spectrum = options.source == "SPECTRUM"; + init: function() { + this.program = new TexerProgram(this.gl); + this.updateCode(); + this.updateClone(); + this.updateImage(); + this.updateSource(); + this.listenTo(this.main, "resize", this.handleResize); + }, - this._inited = false; + draw: function() { + _.each(this.code, function(code) { + this._drawScope(code, !this.inited); + }, this); + this.inited = true; + }, - this.program = new TexerProgram(); + destroy: function() { + Texer.super.destroy.call(this); + this.program.destroy(); + this.gl.deleteTexture(this.texture); + }, - Texer.super.constructor.apply(this, arguments); -} -Webvs.Texer = Webvs.defineClass(Texer, Webvs.Component, { - componentName: "Texer", + updateCode: function() { + var code = Webvs.compileExpr(this.opts.code, ["init", "onBeat", "perFrame", "perPoint"]).codeInst; + code.n = 100; + code.setup(this.main, this); + this.inited = false; + this.code = [code]; + }, - /** - * initializes the Texer component - * @memberof Webvs.Texer# - */ - init: function(gl, main, parent) { - Texer.super.init.call(this, gl, main, parent); - - this.program.init(gl); - this.code.setup(main, this); - - var image = new Image(); - image.src = main.getResource(this.imageSrc); - this.imagewidth = image.width; - this.imageHeight = image.height; - this.texture = gl.createTexture(); - gl.bindTexture(gl.TEXTURE_2D, this.texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + updateClone: function() { + this.code = Webvs.CodeInstance.clone(this.code, this.opts.clone); }, - /** - * renders the scope - * @memberof Webvs.Texer# - */ - update: function() { - var code = this.code; - if(!this._inited) { + updateImage: function() { + var gl = this.gl; + this.main.rsrcMan.getImage( + this.opts.imageSrc, + function(image) { + this.imagewidth = image.width; + this.imageHeight = image.height; + if(!this.texture) { + this.texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, this.texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } else { + gl.bindTexture(gl.TEXTURE_2D, this.texture); + } + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + }, + null, + this + ); + }, + + updateSource: function() { + this.source = Webvs.getEnumValue(this.opts.source, Webvs.Source); + }, + + _drawScope: function(code, runInit) { + if(runInit) { code.init(); } @@ -6820,13 +6808,18 @@ Webvs.Texer = Webvs.defineClass(Texer, Webvs.Component, { } var nPoints = Math.floor(code.n); - var data = this.spectrum ? this.main.analyser.getSpectrum() : this.main.analyser.getWaveform(); + var data; + if(this.source == Webvs.Source.SPECTRUM) { + data = this.main.analyser.getSpectrum(); + } else { + data = this.main.analyser.getWaveform(); + } var bucketSize = data.length/nPoints; var vertexData = []; var texVertexData = []; var vertexIndices = []; - var colorData = this.colorFiltering?[]:null; + var colorData = this.opts.colorFiltering?[]:null; var index = 0; function addRect(cornx, corny, sizex, sizey, red, green, blue) { if(cornx < -1-sizex || cornx > 1|| @@ -6867,8 +6860,8 @@ Webvs.Texer = Webvs.defineClass(Texer, Webvs.Component, { index += 4; } - var imageSizex = (this.imagewidth/this.parent.fm.width)*2; - var imageSizey = (this.imageHeight/this.parent.fm.height)*2; + var imageSizex = (this.imagewidth/this.gl.drawingBufferWidth)*2; + var imageSizey = (this.imageHeight/this.gl.drawingBufferHeight)*2; for(var i = 0;i < nPoints;i++) { var value = 0; @@ -6890,7 +6883,7 @@ Webvs.Texer = Webvs.defineClass(Texer, Webvs.Component, { var sizex = imageSizex; var sizey = imageSizey; - if(this.resizing) { + if(this.opts.resizing) { sizex *= code.sizex; sizey *= code.sizey; } @@ -6898,7 +6891,7 @@ Webvs.Texer = Webvs.defineClass(Texer, Webvs.Component, { var corny = (-code.y)-sizey/2; addRect(cornx, corny, sizex, sizey, code.red, code.green, code.blue); - if(this.wrapAround) { + if(this.opts.wrapAround) { // wrapped around x value is 1-(-1-cornx) or -1-(1-cornx) // depending on the edge // ie. 2+cornx or -2+cornx @@ -6924,19 +6917,14 @@ Webvs.Texer = Webvs.defineClass(Texer, Webvs.Component, { this.texture); }, - /** - * release resource - * @memberof Webvs.Texer# - */ - destroy: function() { - Texer.super.destroy.call(this); - this.gl.deleteTexture(this.texture); - this.program.cleanup(); + handleResize: function() { + this.code.updateDimVars(this.gl); } }); -function TexerProgram() { - TexerProgram.super.constructor.call(this, { +function TexerProgram(gl) { + TexerProgram.super.constructor.call(this, gl, { + copyOnSwap: true, vertexShader: [ "uniform bool u_colorFilter;", "attribute vec2 a_texVertex;", @@ -6987,67 +6975,197 @@ Webvs.TexerProgram = Webvs.defineClass(TexerProgram, Webvs.ShaderProgram, { })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A component that clears the screen - * - * @param {object} options - options object - * @param {number} [options.n=0] - beat counter, screen will be cleared for every n beats. - * use 0 to clear all frames. - * @param {string} [options.color="#000000"] - color to which screen is to be cleared - * @param {string} [options.blendMode="REPLACE"] - blend clearing onto previous buffer - * @augments Webvs.Component - * @constructor - * @memberof Webvs - */ -function ClearScreen(options) { - options = _.defaults(options, { - n: 0, - color: "#000000", +// A particle that moves around depending on beat changes +function MovingParticle(gl, main, parent, opts) { + MovingParticle.super.constructor.call(this, gl, main, parent, opts); +} + +Webvs.registerComponent(MovingParticle, { + name: "MovingParticle", + menu: "Render" +}); + +Webvs.defineClass(MovingParticle, Webvs.Component, { + defaultOptions: { + color: "#FFFFFF", + distance: 0.7, + particleSize: 10, + onBeatSizeChange: false, + onBeatParticleSize: 10, blendMode: "REPLACE" + }, + + onChange: { + color: "updateColor", + blendMode: "updateProgram" + }, + + init: function() { + this.centerX = 0; + this.centerY = 0; + this.velocityX = 0; + this.velocityY = 0; + this.posX = 0; + this.posY = 0; + + this._computeGeometry(); + this.updateProgram(); + this.updateColor(); + }, + + _computeGeometry: function() { + if(Webvs.MovingParticle.circleGeometry) { + return; + } + var pointCount = 100; + var points = new Float32Array((pointCount+2)*2); + var pbi = 0; + points[pbi++] = 0; // center + points[pbi++] = 0; + for(var i = 0;i < pointCount;i++) { + points[pbi++] = Math.sin(i*2*Math.PI/pointCount); + points[pbi++] = Math.cos(i*2*Math.PI/pointCount); + } + points[pbi++] = points[2]; // repeat last point again + points[pbi++] = points[3]; + Webvs.MovingParticle.circleGeometry = points; + }, + + draw: function() { + if(this.main.analyser.beat) { + this.centerX = (Math.random()*2-1)*0.3; + this.centerY = (Math.random()*2-1)*0.3; + } + + this.velocityX -= 0.004*(this.posX-this.centerX); + this.velocityY -= 0.004*(this.posY-this.centerY); + + this.posX += this.velocityX; + this.posY += this.velocityY; + + this.velocityX *= 0.991; + this.velocityY *= 0.991; + + var x = this.posX*this.opts.distance; + var y = this.posY*this.opts.distance; + + var scaleX, scaleY; + if(this.opts.onBeatSizeChange && this.main.analyser.beat) { + scaleX = this.opts.onBeatParticleSize; + scaleY = this.opts.onBeatParticleSize; + } else { + scaleX = this.opts.particleSize; + scaleY = this.opts.particleSize; + } + scaleX = 2*scaleX/this.gl.drawingBufferWidth; + scaleY = 2*scaleY/this.gl.drawingBufferHeight; + + this.program.run(this.parent.fm, null, Webvs.MovingParticle.circleGeometry, + scaleX, scaleY, x, y, this.color); + }, + + updateProgram: function() { + var blendMode = Webvs.getEnumValue(this.opts.blendMode, Webvs.BlendModes); + var program = new MovingParticleShader(this.gl, blendMode); + if(this.program) { + this.program.destroy(); + } + this.program = program; + }, + + updateColor: function() { + this.color = Webvs.parseColorNorm(this.opts.color); + }, + + destroy: function() { + MovingParticle.super.destroy.call(this); + this.program.destroy(); + } +}); + +function MovingParticleShader(gl, blendMode) { + MovingParticleShader.super.constructor.call(this, gl, { + copyOnSwap: true, + blendMode: blendMode, + vertexShader: [ + "attribute vec2 a_point;", + "uniform vec2 u_position;", + "uniform vec2 u_scale;", + "void main() {", + " setPosition((a_point*u_scale)+u_position);", + "}" + ], + fragmentShader: [ + "uniform vec3 u_color;", + "void main() {", + " setFragColor(vec4(u_color, 1));", + "}" + ] }); - this.n = options.n; - this.color = Webvs.parseColorNorm(options.color); +} +Webvs.MovingParticleShader = Webvs.defineClass(MovingParticleShader, Webvs.ShaderProgram, { + draw: function(points, scaleX, scaleY, x, y, color) { + this.setUniform("u_scale", "2f", scaleX, scaleY); + this.setUniform("u_position", "2f", x, y); + this.setUniform.apply(this, ["u_color", "3f"].concat(color)); + this.setVertexAttribArray("a_point", points); + this.gl.drawArrays(this.gl.TRIANGLE_FAN, 0, points.length/2); + } +}); - this.outputBlendMode = Webvs.blendModes[options.blendMode]; +})(Webvs); - this.prevBeat = false; - this.beatCount = 0; +/** + * Copyright (c) 2013-2015 Azeem Arshad + * See the file license.txt for copying permission. + */ - this.program = new Webvs.ClearScreenProgram(this.outputBlendMode); +(function(Webvs) { - ClearScreen.super.constructor.apply(this, arguments); +// A component that clears the screen +function ClearScreen(gl, main, parent, opts) { + ClearScreen.super.constructor.call(this, gl, main, parent, opts); } -Webvs.ClearScreen = Webvs.defineClass(ClearScreen, Webvs.Component, { - componentName: "ClearScreen", - /** - * initializes the ClearScreen component - * @memberof Webvs.ClearScreen# - */ - init: function(gl, main, parent) { - ClearScreen.super.init.call(this, gl, main, parent); - this.program.init(gl); +Webvs.registerComponent(ClearScreen, { + name: "ClearScreen", + menu: "Render" +}); + +Webvs.defineClass(ClearScreen, Webvs.Component, { + defaultOptions: { + beatCount: 0, + color: "#000000", + blendMode: "REPLACE" }, - /** - * clears the screen - * @memberof Webvs.ClearScreen# - */ - update: function() { + onChange: { + color: "updateColor", + blendMode: "updateProgram" + }, + + init: function() { + this.prevBeat = false; + this.beatCount = 0; + + this.updateColor(); + this.updateProgram(); + }, + + draw: function() { var clear = false; - if(this.n === 0) { + if(this.opts.beatCount === 0) { clear = true; } else { if(this.main.analyser.beat && !this.prevBeat) { this.beatCount++; - if(this.beatCount == this.n) { + if(this.beatCount >= this.opts.beatCount) { clear = true; this.beatCount = 0; } @@ -7060,112 +7178,99 @@ Webvs.ClearScreen = Webvs.defineClass(ClearScreen, Webvs.Component, { } }, - /** - * releases resources - * @memberof Webvs.ClearScreen# - */ destroy: function() { - this.program.cleanup(); - } -}); + ClearScreen.super.destroy.call(this); + this.program.destroy(); + }, -ClearScreen.ui = { - type: "ClearScreen", - disp: "Clear Screen", - schema: { - n: { - type: "number", - title: "Clear on beat (0 = always clear)", - default: 0 - }, - color: { - type: "string", - title: "Clear color", - format: "color", - default: "#000000" - }, - blendMode: { - type: "string", - title: "Blend Mode", - enum: _.keys(Webvs.blendModes) + updateColor: function() { + this.color = Webvs.parseColorNorm(this.opts.color); + }, + + updateProgram: function() { + var blendMode = Webvs.getEnumValue(this.opts.blendMode, Webvs.BlendModes); + var program = new Webvs.ClearScreenProgram(this.gl, blendMode); + if(this.program) { + this.program.destroy(); } + this.program = program; } -}; +}); })(Webvs); /** - * Copyright (c) 2013 Azeem Arshad + * Copyright (c) 2013-2015 Azeem Arshad * See the file license.txt for copying permission. */ (function(Webvs) { -/** - * @class - * A component that renders an image onto the screen - * - * @param {object} options - options object - * @param {string} src - image file source - * @param {number} x - image x position - * @param {number} y - image y position - * @augments Webvs.Component - * @constructor - * @memberof Webvs - */ -function Picture(options) { - Webvs.checkRequiredOptions(options, ["src", "x", "y"]); +// A component that renders an image onto the screen +function Picture(gl, main, parent, opts) { + Picture.super.constructor.call(this, gl, main, parent, opts); +} - this.x = options.x; - this.y = options.y; - this.src = options.src; +Webvs.registerComponent(Picture, { + name: "Picture", + menu: "Render" +}); - this.program = new Webvs.PictureProgram(); - Picture.super.constructor.apply(this, arguments); -} -Webvs.Picture = Webvs.defineClass(Picture, Webvs.Component, { - /** - * initializes the ClearScreen component - * @memberof Webvs.Picture# - */ - init: function(gl, main, parent) { - Picture.super.init.call(this, gl, main, parent); +Webvs.defineClass(Picture, Webvs.Component, { + defaultOptions: { + src: "avsres_texer_circle_edgeonly_19x19.bmp", + x: 0, + y: 0 + }, - this.program.init(gl); + onChange: { + src: "updateImage" + }, - var image = new Image(); - image.src = main.getResource(this.src); - this.width = image.width; - this.height = image.height; - this.texture = gl.createTexture(); - gl.bindTexture(gl.TEXTURE_2D, this.texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + init: function() { + this.program = new Webvs.PictureProgram(this.gl); + this.updateImage(); }, - /** - * renders the image - * @memberof Webvs.Picture# - */ - update: function() { - this.program.run(this.parent.fm, null, this.x, this.y, this.texture, this.width, this.height); + draw: function() { + this.program.run(this.parent.fm, null, + this.opts.x, this.opts.y, + this.texture, this.width, this.height); }, - /** - * releases resources - * @memberof Webvs.Picture# - */ destroy: function() { - this.program.cleanup(); + Picture.super.destroy.call(this); + this.program.destroy(); this.gl.deleteTexture(this.texture); + }, + + updateImage: function() { + var gl = this.gl; + this.main.rsrcMan.getImage( + this.opts.src, + function(image) { + this.width = image.width; + this.height = image.height; + if(!this.texture) { + this.texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, this.texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } else { + gl.bindTexture(gl.TEXTURE_2D, this.texture); + } + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + }, + null, + this + ); } }); -function PictureProgram() { - PictureProgram.super.constructor.call(this, { +function PictureProgram(gl) { + PictureProgram.super.constructor.call(this, gl, { copyOnSwap: true, vertexShader: [ "attribute vec2 a_texVertex;", diff --git a/dist/libs.min.js b/dist/libs.min.js deleted file mode 100644 index cedea3e..0000000 --- a/dist/libs.min.js +++ /dev/null @@ -1 +0,0 @@ -function FourierTransform(a,b){this.bufferSize=a,this.sampleRate=b,this.bandwidth=2/a*b/2,this.spectrum=new Float32Array(a/2),this.real=new Float32Array(a),this.imag=new Float32Array(a),this.peakBand=0,this.peak=0,this.getBandFrequency=function(a){return this.bandwidth*a+this.bandwidth/2},this.calculateSpectrum=function(){for(var b,c,d,e=this.spectrum,f=this.real,g=this.imag,h=2/this.bufferSize,i=Math.sqrt,j=0,k=a/2;k>j;j++)b=f[j],c=g[j],d=h*i(b*b+c*c),d>this.peak&&(this.peakBand=j,this.peak=d),e[j]=d}}function FFT(a,b){FourierTransform.call(this,a,b),this.reverseTable=new Uint32Array(a);for(var c,d=1,e=a>>1;a>d;){for(c=0;d>c;c++)this.reverseTable[c+d]=this.reverseTable[c]+e;d<<=1,e>>=1}for(this.sinTable=new Float32Array(a),this.cosTable=new Float32Array(a),c=0;a>c;c++)this.sinTable[c]=Math.sin(-Math.PI/c),this.cosTable[c]=Math.cos(-Math.PI/c)}function FourierTransform(a,b){this.bufferSize=a,this.sampleRate=b,this.bandwidth=2/a*b/2,this.spectrum=new Float32Array(a/2),this.real=new Float32Array(a),this.imag=new Float32Array(a),this.peakBand=0,this.peak=0,this.getBandFrequency=function(a){return this.bandwidth*a+this.bandwidth/2},this.calculateSpectrum=function(){for(var b,c,d,e=this.spectrum,f=this.real,g=this.imag,h=2/this.bufferSize,i=Math.sqrt,j=0,k=a/2;k>j;j++)b=f[j],c=g[j],d=h*i(b*b+c*c),d>this.peak&&(this.peakBand=j,this.peak=d),e[j]=d}}function FFT(a,b){FourierTransform.call(this,a,b),this.reverseTable=new Uint32Array(a);for(var c,d=1,e=a>>1;a>d;){for(c=0;d>c;c++)this.reverseTable[c+d]=this.reverseTable[c]+e;d<<=1,e>>=1}for(this.sinTable=new Float32Array(a),this.cosTable=new Float32Array(a),c=0;a>c;c++)this.sinTable[c]=Math.sin(-Math.PI/c),this.cosTable[c]=Math.cos(-Math.PI/c)}(function(){var a=this,b=a._,c={},d=Array.prototype,e=Object.prototype,f=Function.prototype,g=d.push,h=d.slice,i=d.concat,j=e.toString,k=e.hasOwnProperty,l=d.forEach,m=d.map,n=d.reduce,o=d.reduceRight,p=d.filter,q=d.every,r=d.some,s=d.indexOf,t=d.lastIndexOf,u=Array.isArray,v=Object.keys,w=f.bind,x=function(a){return a instanceof x?a:this instanceof x?(this._wrapped=a,void 0):new x(a)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=x),exports._=x):a._=x,x.VERSION="1.5.1";var y=x.each=x.forEach=function(a,b,d){if(null!=a)if(l&&a.forEach===l)a.forEach(b,d);else if(a.length===+a.length){for(var e=0,f=a.length;f>e;e++)if(b.call(d,a[e],e,a)===c)return}else for(var g in a)if(x.has(a,g)&&b.call(d,a[g],g,a)===c)return};x.map=x.collect=function(a,b,c){var d=[];return null==a?d:m&&a.map===m?a.map(b,c):(y(a,function(a,e,f){d.push(b.call(c,a,e,f))}),d)};var z="Reduce of empty array with no initial value";x.reduce=x.foldl=x.inject=function(a,b,c,d){var e=arguments.length>2;if(null==a&&(a=[]),n&&a.reduce===n)return d&&(b=x.bind(b,d)),e?a.reduce(b,c):a.reduce(b);if(y(a,function(a,f,g){e?c=b.call(d,c,a,f,g):(c=a,e=!0)}),!e)throw new TypeError(z);return c},x.reduceRight=x.foldr=function(a,b,c,d){var e=arguments.length>2;if(null==a&&(a=[]),o&&a.reduceRight===o)return d&&(b=x.bind(b,d)),e?a.reduceRight(b,c):a.reduceRight(b);var f=a.length;if(f!==+f){var g=x.keys(a);f=g.length}if(y(a,function(h,i,j){i=g?g[--f]:--f,e?c=b.call(d,c,a[i],i,j):(c=a[i],e=!0)}),!e)throw new TypeError(z);return c},x.find=x.detect=function(a,b,c){var d;return A(a,function(a,e,f){return b.call(c,a,e,f)?(d=a,!0):void 0}),d},x.filter=x.select=function(a,b,c){var d=[];return null==a?d:p&&a.filter===p?a.filter(b,c):(y(a,function(a,e,f){b.call(c,a,e,f)&&d.push(a)}),d)},x.reject=function(a,b,c){return x.filter(a,function(a,d,e){return!b.call(c,a,d,e)},c)},x.every=x.all=function(a,b,d){b||(b=x.identity);var e=!0;return null==a?e:q&&a.every===q?a.every(b,d):(y(a,function(a,f,g){return(e=e&&b.call(d,a,f,g))?void 0:c}),!!e)};var A=x.some=x.any=function(a,b,d){b||(b=x.identity);var e=!1;return null==a?e:r&&a.some===r?a.some(b,d):(y(a,function(a,f,g){return e||(e=b.call(d,a,f,g))?c:void 0}),!!e)};x.contains=x.include=function(a,b){return null==a?!1:s&&a.indexOf===s?-1!=a.indexOf(b):A(a,function(a){return a===b})},x.invoke=function(a,b){var c=h.call(arguments,2),d=x.isFunction(b);return x.map(a,function(a){return(d?b:a[b]).apply(a,c)})},x.pluck=function(a,b){return x.map(a,function(a){return a[b]})},x.where=function(a,b,c){return x.isEmpty(b)?c?void 0:[]:x[c?"find":"filter"](a,function(a){for(var c in b)if(b[c]!==a[c])return!1;return!0})},x.findWhere=function(a,b){return x.where(a,b,!0)},x.max=function(a,b,c){if(!b&&x.isArray(a)&&a[0]===+a[0]&&a.length<65535)return Math.max.apply(Math,a);if(!b&&x.isEmpty(a))return-1/0;var d={computed:-1/0,value:-1/0};return y(a,function(a,e,f){var g=b?b.call(c,a,e,f):a;g>d.computed&&(d={value:a,computed:g})}),d.value},x.min=function(a,b,c){if(!b&&x.isArray(a)&&a[0]===+a[0]&&a.length<65535)return Math.min.apply(Math,a);if(!b&&x.isEmpty(a))return 1/0;var d={computed:1/0,value:1/0};return y(a,function(a,e,f){var g=b?b.call(c,a,e,f):a;gd||void 0===c)return 1;if(d>c||void 0===d)return-1}return a.indexf;){var h=f+g>>>1;c.call(d,a[h])=0})})},x.difference=function(a){var b=i.apply(d,h.call(arguments,1));return x.filter(a,function(a){return!x.contains(b,a)})},x.zip=function(){for(var a=x.max(x.pluck(arguments,"length").concat(0)),b=new Array(a),c=0;a>c;c++)b[c]=x.pluck(arguments,""+c);return b},x.object=function(a,b){if(null==a)return{};for(var c={},d=0,e=a.length;e>d;d++)b?c[a[d]]=b[d]:c[a[d][0]]=a[d][1];return c},x.indexOf=function(a,b,c){if(null==a)return-1;var d=0,e=a.length;if(c){if("number"!=typeof c)return d=x.sortedIndex(a,b),a[d]===b?d:-1;d=0>c?Math.max(0,e+c):c}if(s&&a.indexOf===s)return a.indexOf(b,c);for(;e>d;d++)if(a[d]===b)return d;return-1},x.lastIndexOf=function(a,b,c){if(null==a)return-1;var d=null!=c;if(t&&a.lastIndexOf===t)return d?a.lastIndexOf(b,c):a.lastIndexOf(b);for(var e=d?c:a.length;e--;)if(a[e]===b)return e;return-1},x.range=function(a,b,c){arguments.length<=1&&(b=a||0,a=0),c=arguments[2]||1;for(var d=Math.max(Math.ceil((b-a)/c),0),e=0,f=new Array(d);d>e;)f[e++]=a,a+=c;return f};var E=function(){};x.bind=function(a,b){var c,d;if(w&&a.bind===w)return w.apply(a,h.call(arguments,1));if(!x.isFunction(a))throw new TypeError;return c=h.call(arguments,2),d=function(){if(!(this instanceof d))return a.apply(b,c.concat(h.call(arguments)));E.prototype=a.prototype;var e=new E;E.prototype=null;var f=a.apply(e,c.concat(h.call(arguments)));return Object(f)===f?f:e}},x.partial=function(a){var b=h.call(arguments,1);return function(){return a.apply(this,b.concat(h.call(arguments)))}},x.bindAll=function(a){var b=h.call(arguments,1);if(0===b.length)throw new Error("bindAll must be passed function names");return y(b,function(b){a[b]=x.bind(a[b],a)}),a},x.memoize=function(a,b){var c={};return b||(b=x.identity),function(){var d=b.apply(this,arguments);return x.has(c,d)?c[d]:c[d]=a.apply(this,arguments)}},x.delay=function(a,b){var c=h.call(arguments,2);return setTimeout(function(){return a.apply(null,c)},b)},x.defer=function(a){return x.delay.apply(x,[a,1].concat(h.call(arguments,1)))},x.throttle=function(a,b,c){var d,e,f,g=null,h=0;c||(c={});var i=function(){h=c.leading===!1?0:new Date,g=null,f=a.apply(d,e)};return function(){var j=new Date;h||c.leading!==!1||(h=j);var k=b-(j-h);return d=this,e=arguments,0>=k?(clearTimeout(g),g=null,h=j,f=a.apply(d,e)):g||c.trailing===!1||(g=setTimeout(i,k)),f}},x.debounce=function(a,b,c){var d,e=null;return function(){var f=this,g=arguments,h=function(){e=null,c||(d=a.apply(f,g))},i=c&&!e;return clearTimeout(e),e=setTimeout(h,b),i&&(d=a.apply(f,g)),d}},x.once=function(a){var b,c=!1;return function(){return c?b:(c=!0,b=a.apply(this,arguments),a=null,b)}},x.wrap=function(a,b){return function(){var c=[a];return g.apply(c,arguments),b.apply(this,c)}},x.compose=function(){var a=arguments;return function(){for(var b=arguments,c=a.length-1;c>=0;c--)b=[a[c].apply(this,b)];return b[0]}},x.after=function(a,b){return function(){return--a<1?b.apply(this,arguments):void 0}},x.keys=v||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[];for(var c in a)x.has(a,c)&&b.push(c);return b},x.values=function(a){var b=[];for(var c in a)x.has(a,c)&&b.push(a[c]);return b},x.pairs=function(a){var b=[];for(var c in a)x.has(a,c)&&b.push([c,a[c]]);return b},x.invert=function(a){var b={};for(var c in a)x.has(a,c)&&(b[a[c]]=c);return b},x.functions=x.methods=function(a){var b=[];for(var c in a)x.isFunction(a[c])&&b.push(c);return b.sort()},x.extend=function(a){return y(h.call(arguments,1),function(b){if(b)for(var c in b)a[c]=b[c]}),a},x.pick=function(a){var b={},c=i.apply(d,h.call(arguments,1));return y(c,function(c){c in a&&(b[c]=a[c])}),b},x.omit=function(a){var b={},c=i.apply(d,h.call(arguments,1));for(var e in a)x.contains(c,e)||(b[e]=a[e]);return b},x.defaults=function(a){return y(h.call(arguments,1),function(b){if(b)for(var c in b)void 0===a[c]&&(a[c]=b[c])}),a},x.clone=function(a){return x.isObject(a)?x.isArray(a)?a.slice():x.extend({},a):a},x.tap=function(a,b){return b(a),a};var F=function(a,b,c,d){if(a===b)return 0!==a||1/a==1/b;if(null==a||null==b)return a===b;a instanceof x&&(a=a._wrapped),b instanceof x&&(b=b._wrapped);var e=j.call(a);if(e!=j.call(b))return!1;switch(e){case"[object String]":return a==String(b);case"[object Number]":return a!=+a?b!=+b:0==a?1/a==1/b:a==+b;case"[object Date]":case"[object Boolean]":return+a==+b;case"[object RegExp]":return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if("object"!=typeof a||"object"!=typeof b)return!1;for(var f=c.length;f--;)if(c[f]==a)return d[f]==b;var g=a.constructor,h=b.constructor;if(g!==h&&!(x.isFunction(g)&&g instanceof g&&x.isFunction(h)&&h instanceof h))return!1;c.push(a),d.push(b);var i=0,k=!0;if("[object Array]"==e){if(i=a.length,k=i==b.length)for(;i--&&(k=F(a[i],b[i],c,d)););}else{for(var l in a)if(x.has(a,l)&&(i++,!(k=x.has(b,l)&&F(a[l],b[l],c,d))))break;if(k){for(l in b)if(x.has(b,l)&&!i--)break;k=!i}}return c.pop(),d.pop(),k};x.isEqual=function(a,b){return F(a,b,[],[])},x.isEmpty=function(a){if(null==a)return!0;if(x.isArray(a)||x.isString(a))return 0===a.length;for(var b in a)if(x.has(a,b))return!1;return!0},x.isElement=function(a){return!(!a||1!==a.nodeType)},x.isArray=u||function(a){return"[object Array]"==j.call(a)},x.isObject=function(a){return a===Object(a)},y(["Arguments","Function","String","Number","Date","RegExp"],function(a){x["is"+a]=function(b){return j.call(b)=="[object "+a+"]"}}),x.isArguments(arguments)||(x.isArguments=function(a){return!(!a||!x.has(a,"callee"))}),"function"!=typeof/./&&(x.isFunction=function(a){return"function"==typeof a}),x.isFinite=function(a){return isFinite(a)&&!isNaN(parseFloat(a))},x.isNaN=function(a){return x.isNumber(a)&&a!=+a},x.isBoolean=function(a){return a===!0||a===!1||"[object Boolean]"==j.call(a)},x.isNull=function(a){return null===a},x.isUndefined=function(a){return void 0===a},x.has=function(a,b){return k.call(a,b)},x.noConflict=function(){return a._=b,this},x.identity=function(a){return a},x.times=function(a,b,c){for(var d=Array(Math.max(0,a)),e=0;a>e;e++)d[e]=b.call(c,e);return d},x.random=function(a,b){return null==b&&(b=a,a=0),a+Math.floor(Math.random()*(b-a+1))};var G={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};G.unescape=x.invert(G.escape);var H={escape:new RegExp("["+x.keys(G.escape).join("")+"]","g"),unescape:new RegExp("("+x.keys(G.unescape).join("|")+")","g")};x.each(["escape","unescape"],function(a){x[a]=function(b){return null==b?"":(""+b).replace(H[a],function(b){return G[a][b]})}}),x.result=function(a,b){if(null==a)return void 0;var c=a[b];return x.isFunction(c)?c.call(a):c},x.mixin=function(a){y(x.functions(a),function(b){var c=x[b]=a[b];x.prototype[b]=function(){var a=[this._wrapped];return g.apply(a,arguments),M.call(this,c.apply(x,a))}})};var I=0;x.uniqueId=function(a){var b=++I+"";return a?a+b:b},x.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var J=/(.)^/,K={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},L=/\\|'|\r|\n|\t|\u2028|\u2029/g;x.template=function(a,b,c){var d;c=x.defaults({},c,x.templateSettings);var e=new RegExp([(c.escape||J).source,(c.interpolate||J).source,(c.evaluate||J).source].join("|")+"|$","g"),f=0,g="__p+='";a.replace(e,function(b,c,d,e,h){return g+=a.slice(f,h).replace(L,function(a){return"\\"+K[a]}),c&&(g+="'+\n((__t=("+c+"))==null?'':_.escape(__t))+\n'"),d&&(g+="'+\n((__t=("+d+"))==null?'':__t)+\n'"),e&&(g+="';\n"+e+"\n__p+='"),f=h+b.length,b}),g+="';\n",c.variable||(g="with(obj||{}){\n"+g+"}\n"),g="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+g+"return __p;\n";try{d=new Function(c.variable||"obj","_",g)}catch(h){throw h.source=g,h}if(b)return d(b,x);var i=function(a){return d.call(this,a,x)};return i.source="function("+(c.variable||"obj")+"){\n"+g+"}",i},x.chain=function(a){return x(a).chain()};var M=function(a){return this._chain?x(a).chain():a};x.mixin(x),y(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=d[a];x.prototype[a]=function(){var c=this._wrapped;return b.apply(c,arguments),"shift"!=a&&"splice"!=a||0!==c.length||delete c[0],M.call(this,c)}}),y(["concat","join","slice"],function(a){var b=d[a];x.prototype[a]=function(){return M.call(this,b.apply(this._wrapped,arguments))}}),x.extend(x.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this),FFT.prototype.forward=function(a){var b=this.bufferSize,c=this.cosTable,d=this.sinTable,e=this.reverseTable,f=this.real,g=this.imag;this.spectrum;var h=Math.floor(Math.log(b)/Math.LN2);if(Math.pow(2,h)!==b)throw"Invalid buffer size, must be a power of 2.";if(b!==a.length)throw"Supplied buffer is not the same size as defined FFT. FFT Size: "+b+" Buffer Size: "+a.length;var i,j,k,l,m,n,o,p,q,r=1;for(q=0;b>q;q++)f[q]=a[e[q]],g[q]=0;for(;b>r;){i=c[r],j=d[r],k=1,l=0;for(var s=0;r>s;s++){for(q=s;b>q;)m=q+r,n=k*f[m]-l*g[m],o=k*g[m]+l*f[m],f[m]=f[q]-n,g[m]=g[q]-o,f[q]+=n,g[q]+=o,q+=r<<1;p=k,k=p*i-l*j,l=p*j+l*i}r<<=1}return this.calculateSpectrum()},function(){function a(){for(var a in this.sections)this.sections[a].condition()&&this.sections[a].callback.call(this)}var b=function(){this.audioAdapter=b._getAdapter(this),this.events={},this.sections=[],this.bind("update",a)};b.adapters={},b.prototype={load:function(a){return a instanceof HTMLElement?(this.source=a,"flash"===b.isSupported()&&(this.source={src:b._getMP3SrcFromAudio(a)})):(this.source=window.Audio?new Audio:{},this.source.src=b._makeSupportedPath(a.src,a.codecs)),this.audio=this.audioAdapter.load(this.source),this},play:function(){return this.audioAdapter.play(),this},pause:function(){return this.audioAdapter.pause(),this},setVolume:function(a){return this.audioAdapter.setVolume(a),this},createKick:function(a){return new b.Kick(this,a)},bind:function(a,b){return this.events[a]||(this.events[a]=[]),this.events[a].push(b),this},unbind:function(a){return this.events[a]&&delete this.events[a],this},trigger:function(a){var b=this;return this.events[a]&&this.events[a].forEach(function(a){a.call(b)}),this},getVolume:function(){return this.audioAdapter.getVolume()},getProgress:function(){return this.audioAdapter.getProgress()},getTime:function(){return this.audioAdapter.getTime()},getFrequency:function(a,b){var c=0;if(void 0!==b){for(var d=a;b>=d;d++)c+=this.getSpectrum()[d];return c/(b-a+1)}return this.getSpectrum()[a]},getWaveform:function(){return this.audioAdapter.getWaveform()},getSpectrum:function(){return this.audioAdapter.getSpectrum()},isLoaded:function(){return this.audioAdapter.isLoaded},isPlaying:function(){return this.audioAdapter.isPlaying},after:function(a,b){var c=this;return this.sections.push({condition:function(){return c.getTime()>a},callback:b}),this},before:function(a,b){var c=this;return this.sections.push({condition:function(){return c.getTime()a&&d.getTime()a&&!this.called},callback:function(){b.call(this),d.called=!0},called:!1}),d=this.sections[this.sections.length-1],this}},window.Dancer=b}(),function(a){function b(){var a=!!(navigator.vendor||"").match(/Apple/),b=navigator.userAgent.match(/Version\/([^ ]*)/);return b=b?parseFloat(b[1]):0,a&&6>=b}var c={mp3:"audio/mpeg;",ogg:'audio/ogg; codecs="vorbis"',wav:'audio/wav; codecs="1"',aac:'audio/mp4; codecs="mp4a.40.2"'},d=document.createElement("audio");a.options={},a.setOptions=function(b){for(var c in b)b.hasOwnProperty(c)&&(a.options[c]=b[c])},a.isSupported=function(){return window.Float32Array&&window.Uint32Array?b()||!window.AudioContext&&!window.webkitAudioContext?d&&d.mozSetup?"audiodata":FlashDetect.versionAtLeast(9)?"flash":"":"webaudio":null},a.canPlay=function(b){return d.canPlayType,!!("flash"===a.isSupported()?"mp3"===b.toLowerCase():d.canPlayType&&d.canPlayType(c[b.toLowerCase()]).replace(/no/,""))},a.addPlugin=function(b,c){void 0===a.prototype[b]&&(a.prototype[b]=c)},a._makeSupportedPath=function(b,c){if(!c)return b;for(var d=0;d=this.currentThreshold&&a>=this.threshold?(this.currentThreshold=a,this.onKick&&this.onKick.call(this.dancer,a)):(this.offKick&&this.offKick.call(this.dancer,a),this.currentThreshold-=this.decay)}},maxAmplitude:function(a){var b=0,c=this.dancer.getSpectrum();if(!a.length)return a=d;d++)c[d]>b&&(b=c[d]);return b}},window.Dancer.Kick=b}(),function(){function a(){this.source=this.context.createMediaElementSource(this.audio),this.source.connect(this.proc),this.source.connect(this.gain),this.gain.connect(this.context.destination),this.proc.connect(this.context.destination),this.isLoaded=!0,this.progress=1,this.dancer.trigger("loaded")}var b=2048,c=44100,d=function(a){this.dancer=a,this.audio=new Audio,this.context=window.AudioContext?new window.AudioContext:new window.webkitAudioContext};d.prototype={load:function(d){var e=this;return this.audio=d,this.isLoaded=!1,this.progress=0,this.proc=this.context.createJavaScriptNode(b/2,1,1),this.proc.onaudioprocess=function(a){e.update.call(e,a)},this.gain=this.context.createGainNode(),this.fft=new FFT(b/2,c),this.signal=new Float32Array(b/2),this.audio.readyState<3?this.audio.addEventListener("canplay",function(){a.call(e)}):a.call(e),this.audio.addEventListener("progress",function(a){a.currentTarget.duration&&(e.progress=a.currentTarget.seekable.end(0)/a.currentTarget.duration)}),this.audio},play:function(){this.audio.play(),this.isPlaying=!0},pause:function(){this.audio.pause(),this.isPlaying=!1},setVolume:function(a){this.gain.gain.value=a},getVolume:function(){return this.gain.gain.value},getProgress:function(){return this.progress},getWaveform:function(){return this.signal},getSpectrum:function(){return this.fft.spectrum},getTime:function(){return this.audio.currentTime},update:function(a){if(this.isPlaying&&this.isLoaded){var c,d=[],e=a.inputBuffer.numberOfChannels,f=b/e,g=function(a,b){return a[c]+b[c]};for(c=e;c--;)d.push(a.inputBuffer.getChannelData(c));for(c=0;f>c;c++)this.signal[c]=e>1?d.reduce(g(prev,curr))/e:d[0][c];this.fft.forward(this.signal),this.dancer.trigger("update")}}},Dancer.adapters.webkit=d}(),function(){function a(){this.fbLength=this.audio.mozFrameBufferLength,this.channels=this.audio.mozChannels,this.rate=this.audio.mozSampleRate,this.fft=new FFT(this.fbLength/this.channels,this.rate),this.signal=new Float32Array(this.fbLength/this.channels),this.isLoaded=!0,this.progress=1,this.dancer.trigger("loaded")}var b=function(a){this.dancer=a,this.audio=new Audio};b.prototype={load:function(b){var c=this;return this.audio=b,this.isLoaded=!1,this.progress=0,this.audio.readyState<3?this.audio.addEventListener("loadedmetadata",function(){a.call(c)},!1):a.call(c),this.audio.addEventListener("MozAudioAvailable",function(a){c.update(a)},!1),this.audio.addEventListener("progress",function(a){a.currentTarget.duration&&(c.progress=a.currentTarget.seekable.end(0)/a.currentTarget.duration)},!1),this.audio},play:function(){this.audio.play(),this.isPlaying=!0},pause:function(){this.audio.pause(),this.isPlaying=!1},setVolume:function(a){this.audio.volume=a},getVolume:function(){return this.audio.volume},getProgress:function(){return this.progress},getWaveform:function(){return this.signal},getSpectrum:function(){return this.fft.spectrum},getTime:function(){return this.audio.currentTime},update:function(a){if(this.isPlaying&&this.isLoaded){for(var b=0,c=this.fbLength/2;c>b;b++)this.signal[b]=(a.frameBuffer[2*b]+a.frameBuffer[2*b+1])/2;this.fft.forward(this.signal),this.dancer.trigger("update")}}},Dancer.adapters.moz=b}(),function(){function a(){var a=this;f=!0,b(Dancer.options.flashJS,function(){soundManager=new SoundManager,soundManager.flashVersion=9,soundManager.flash9Options.useWaveformData=!0,soundManager.useWaveformData=!0,soundManager.useHighPerformance=!0,soundManager.useFastPolling=!0,soundManager.multiShot=!1,soundManager.debugMode=!1,soundManager.debugFlash=!1,soundManager.url=Dancer.options.flashSWF,soundManager.onready(function(){e=!0,a.load()}),soundManager.ontimeout(function(){console.error("Error loading SoundManager2.swf")}),soundManager.beginDelayedInit()})}function b(a,b){var c=document.createElement("script"),d=document.getElementsByTagName("script")[0];c.type="text/javascript",c.src=a,c.onload=b,d.parentNode.insertBefore(c,d)}var c=1024,d=44100,e=!1,f=!1,g=.93,h=function(a){this.dancer=a,this.wave_L=[],this.wave_R=[],this.spectrum=[],window.SM2_DEFER=!0};h.prototype={load:function(b){var e=this;return this.path=b?b.src:this.path,this.isLoaded=!1,this.progress=0,!window.soundManager&&!f&&a.call(this),window.soundManager&&(this.audio=soundManager.createSound({id:"dancer"+Math.random(),url:this.path,stream:!0,autoPlay:!1,autoLoad:!0,whileplaying:function(){e.update()},whileloading:function(){e.progress=this.bytesLoaded/this.bytesTotal},onload:function(){e.fft=new FFT(c,d),e.signal=new Float32Array(c),e.waveform=new Float32Array(c),e.isLoaded=!0,e.progress=1,e.dancer.trigger("loaded")}}),this.dancer.audio=this.audio),this.audio},play:function(){this.audio.play(),this.isPlaying=!0},pause:function(){this.audio.pause(),this.isPlaying=!1},setVolume:function(a){this.audio.setVolume(100*a)},getVolume:function(){return this.audio.volume/100},getProgress:function(){return this.progress},getWaveform:function(){return this.waveform},getSpectrum:function(){return this.fft.spectrum},getTime:function(){return this.audio.position/1e3},update:function(){if(this.isPlaying||this.isLoaded){this.wave_L=this.audio.waveformData.left,this.wave_R=this.audio.waveformData.right;for(var a,b=0,c=this.wave_L.length;c>b;b++)a=parseFloat(this.wave_L[b])+parseFloat(this.wave_R[b]),this.waveform[2*b]=a/2,this.waveform[2*b+1]=a/2,this.signal[2*b]=a*g,this.signal[2*b+1]=a*g;this.fft.forward(this.signal),this.dancer.trigger("update")}}},Dancer.adapters.flash=h}(),FFT.prototype.forward=function(a){var b=this.bufferSize,c=this.cosTable,d=this.sinTable,e=this.reverseTable,f=this.real,g=this.imag;this.spectrum;var h=Math.floor(Math.log(b)/Math.LN2);if(Math.pow(2,h)!==b)throw"Invalid buffer size, must be a power of 2.";if(b!==a.length)throw"Supplied buffer is not the same size as defined FFT. FFT Size: "+b+" Buffer Size: "+a.length;var i,j,k,l,m,n,o,p,q,r=1;for(q=0;b>q;q++)f[q]=a[e[q]],g[q]=0;for(;b>r;){i=c[r],j=d[r],k=1,l=0;for(var s=0;r>s;s++){for(q=s;b>q;)m=q+r,n=k*f[m]-l*g[m],o=k*g[m]+l*f[m],f[m]=f[q]-n,g[m]=g[q]-o,f[q]+=n,g[q]+=o,q+=r<<1;p=k,k=p*i-l*j,l=p*j+l*i}r<<=1}return this.calculateSpectrum()};var FlashDetect=new function(){var a=this;a.installed=!1,a.raw="",a.major=-1,a.minor=-1,a.revision=-1,a.revisionStr="";var b=[{name:"ShockwaveFlash.ShockwaveFlash.7",version:function(a){return c(a)}},{name:"ShockwaveFlash.ShockwaveFlash.6",version:function(a){var b="6,0,21";try{a.AllowScriptAccess="always",b=c(a)}catch(d){}return b}},{name:"ShockwaveFlash.ShockwaveFlash",version:function(a){return c(a)}}],c=function(a){var b=-1;try{b=a.GetVariable("$version")}catch(c){}return b},d=function(a){var b=-1;try{b=new ActiveXObject(a)}catch(c){b={activeXError:!0}}return b},e=function(a){var b=a.split(",");return{raw:a,major:parseInt(b[0].split(" ")[1],10),minor:parseInt(b[1],10),revision:parseInt(b[2],10),revisionStr:b[2]}},f=function(a){var b=a.split(/ +/),c=b[2].split(/\./),d=b[3];return{raw:a,major:parseInt(c[0],10),minor:parseInt(c[1],10),revisionStr:d,revision:g(d)}},g=function(b){return parseInt(b.replace(/[a-zA-Z]/g,""),10)||a.revision};a.majorAtLeast=function(b){return a.major>=b},a.minorAtLeast=function(b){return a.minor>=b},a.revisionAtLeast=function(b){return a.revision>=b},a.versionAtLeast=function(){var b=[a.major,a.minor,a.revision],c=Math.min(b.length,arguments.length);for(i=0;c>i;i++){if(b[i]>=arguments[i]){if(c>i+1&&b[i]==arguments[i])continue;return!0}return!1}},a.FlashDetect=function(){if(navigator.plugins&&navigator.plugins.length>0){var c="application/x-shockwave-flash",g=navigator.mimeTypes;if(g&&g[c]&&g[c].enabledPlugin&&g[c].enabledPlugin.description){var h=g[c].enabledPlugin.description,i=f(h);a.raw=i.raw,a.major=i.major,a.minor=i.minor,a.revisionStr=i.revisionStr,a.revision=i.revision,a.installed=!0}}else if(-1==navigator.appVersion.indexOf("Mac")&&window.execScript)for(var h=-1,j=0;jb+1e3&&(f=Math.round(1e3*i/(j-b)),g=Math.min(g,f),h=Math.max(h,f),m.textContent=f+" FPS ("+g+"-"+h+")",t(n,Math.min(30,30-30*(f/100))),b=j,i=0),j},update:function(){a=this.end()}}}; \ No newline at end of file diff --git a/dist/webvs.full.min.js b/dist/webvs.full.min.js deleted file mode 100644 index e3652c5..0000000 --- a/dist/webvs.full.min.js +++ /dev/null @@ -1,4 +0,0 @@ -function FourierTransform(a,b){this.bufferSize=a,this.sampleRate=b,this.bandwidth=2/a*b/2,this.spectrum=new Float32Array(a/2),this.real=new Float32Array(a),this.imag=new Float32Array(a),this.peakBand=0,this.peak=0,this.getBandFrequency=function(a){return this.bandwidth*a+this.bandwidth/2},this.calculateSpectrum=function(){for(var b,c,d,e=this.spectrum,f=this.real,g=this.imag,h=2/this.bufferSize,i=Math.sqrt,j=0,k=a/2;k>j;j++)b=f[j],c=g[j],d=h*i(b*b+c*c),d>this.peak&&(this.peakBand=j,this.peak=d),e[j]=d}}function FFT(a,b){FourierTransform.call(this,a,b),this.reverseTable=new Uint32Array(a);for(var c,d=1,e=a>>1;a>d;){for(c=0;d>c;c++)this.reverseTable[c+d]=this.reverseTable[c]+e;d<<=1,e>>=1}for(this.sinTable=new Float32Array(a),this.cosTable=new Float32Array(a),c=0;a>c;c++)this.sinTable[c]=Math.sin(-Math.PI/c),this.cosTable[c]=Math.cos(-Math.PI/c)}function FourierTransform(a,b){this.bufferSize=a,this.sampleRate=b,this.bandwidth=2/a*b/2,this.spectrum=new Float32Array(a/2),this.real=new Float32Array(a),this.imag=new Float32Array(a),this.peakBand=0,this.peak=0,this.getBandFrequency=function(a){return this.bandwidth*a+this.bandwidth/2},this.calculateSpectrum=function(){for(var b,c,d,e=this.spectrum,f=this.real,g=this.imag,h=2/this.bufferSize,i=Math.sqrt,j=0,k=a/2;k>j;j++)b=f[j],c=g[j],d=h*i(b*b+c*c),d>this.peak&&(this.peakBand=j,this.peak=d),e[j]=d}}function FFT(a,b){FourierTransform.call(this,a,b),this.reverseTable=new Uint32Array(a);for(var c,d=1,e=a>>1;a>d;){for(c=0;d>c;c++)this.reverseTable[c+d]=this.reverseTable[c]+e;d<<=1,e>>=1}for(this.sinTable=new Float32Array(a),this.cosTable=new Float32Array(a),c=0;a>c;c++)this.sinTable[c]=Math.sin(-Math.PI/c),this.cosTable[c]=Math.cos(-Math.PI/c)}(function(){var a=this,b=a._,c={},d=Array.prototype,e=Object.prototype,f=Function.prototype,g=d.push,h=d.slice,i=d.concat,j=e.toString,k=e.hasOwnProperty,l=d.forEach,m=d.map,n=d.reduce,o=d.reduceRight,p=d.filter,q=d.every,r=d.some,s=d.indexOf,t=d.lastIndexOf,u=Array.isArray,v=Object.keys,w=f.bind,x=function(a){return a instanceof x?a:this instanceof x?(this._wrapped=a,void 0):new x(a)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=x),exports._=x):a._=x,x.VERSION="1.5.1";var y=x.each=x.forEach=function(a,b,d){if(null!=a)if(l&&a.forEach===l)a.forEach(b,d);else if(a.length===+a.length){for(var e=0,f=a.length;f>e;e++)if(b.call(d,a[e],e,a)===c)return}else for(var g in a)if(x.has(a,g)&&b.call(d,a[g],g,a)===c)return};x.map=x.collect=function(a,b,c){var d=[];return null==a?d:m&&a.map===m?a.map(b,c):(y(a,function(a,e,f){d.push(b.call(c,a,e,f))}),d)};var z="Reduce of empty array with no initial value";x.reduce=x.foldl=x.inject=function(a,b,c,d){var e=arguments.length>2;if(null==a&&(a=[]),n&&a.reduce===n)return d&&(b=x.bind(b,d)),e?a.reduce(b,c):a.reduce(b);if(y(a,function(a,f,g){e?c=b.call(d,c,a,f,g):(c=a,e=!0)}),!e)throw new TypeError(z);return c},x.reduceRight=x.foldr=function(a,b,c,d){var e=arguments.length>2;if(null==a&&(a=[]),o&&a.reduceRight===o)return d&&(b=x.bind(b,d)),e?a.reduceRight(b,c):a.reduceRight(b);var f=a.length;if(f!==+f){var g=x.keys(a);f=g.length}if(y(a,function(h,i,j){i=g?g[--f]:--f,e?c=b.call(d,c,a[i],i,j):(c=a[i],e=!0)}),!e)throw new TypeError(z);return c},x.find=x.detect=function(a,b,c){var d;return A(a,function(a,e,f){return b.call(c,a,e,f)?(d=a,!0):void 0}),d},x.filter=x.select=function(a,b,c){var d=[];return null==a?d:p&&a.filter===p?a.filter(b,c):(y(a,function(a,e,f){b.call(c,a,e,f)&&d.push(a)}),d)},x.reject=function(a,b,c){return x.filter(a,function(a,d,e){return!b.call(c,a,d,e)},c)},x.every=x.all=function(a,b,d){b||(b=x.identity);var e=!0;return null==a?e:q&&a.every===q?a.every(b,d):(y(a,function(a,f,g){return(e=e&&b.call(d,a,f,g))?void 0:c}),!!e)};var A=x.some=x.any=function(a,b,d){b||(b=x.identity);var e=!1;return null==a?e:r&&a.some===r?a.some(b,d):(y(a,function(a,f,g){return e||(e=b.call(d,a,f,g))?c:void 0}),!!e)};x.contains=x.include=function(a,b){return null==a?!1:s&&a.indexOf===s?-1!=a.indexOf(b):A(a,function(a){return a===b})},x.invoke=function(a,b){var c=h.call(arguments,2),d=x.isFunction(b);return x.map(a,function(a){return(d?b:a[b]).apply(a,c)})},x.pluck=function(a,b){return x.map(a,function(a){return a[b]})},x.where=function(a,b,c){return x.isEmpty(b)?c?void 0:[]:x[c?"find":"filter"](a,function(a){for(var c in b)if(b[c]!==a[c])return!1;return!0})},x.findWhere=function(a,b){return x.where(a,b,!0)},x.max=function(a,b,c){if(!b&&x.isArray(a)&&a[0]===+a[0]&&a.length<65535)return Math.max.apply(Math,a);if(!b&&x.isEmpty(a))return-1/0;var d={computed:-1/0,value:-1/0};return y(a,function(a,e,f){var g=b?b.call(c,a,e,f):a;g>d.computed&&(d={value:a,computed:g})}),d.value},x.min=function(a,b,c){if(!b&&x.isArray(a)&&a[0]===+a[0]&&a.length<65535)return Math.min.apply(Math,a);if(!b&&x.isEmpty(a))return 1/0;var d={computed:1/0,value:1/0};return y(a,function(a,e,f){var g=b?b.call(c,a,e,f):a;gd||void 0===c)return 1;if(d>c||void 0===d)return-1}return a.indexf;){var h=f+g>>>1;c.call(d,a[h])=0})})},x.difference=function(a){var b=i.apply(d,h.call(arguments,1));return x.filter(a,function(a){return!x.contains(b,a)})},x.zip=function(){for(var a=x.max(x.pluck(arguments,"length").concat(0)),b=new Array(a),c=0;a>c;c++)b[c]=x.pluck(arguments,""+c);return b},x.object=function(a,b){if(null==a)return{};for(var c={},d=0,e=a.length;e>d;d++)b?c[a[d]]=b[d]:c[a[d][0]]=a[d][1];return c},x.indexOf=function(a,b,c){if(null==a)return-1;var d=0,e=a.length;if(c){if("number"!=typeof c)return d=x.sortedIndex(a,b),a[d]===b?d:-1;d=0>c?Math.max(0,e+c):c}if(s&&a.indexOf===s)return a.indexOf(b,c);for(;e>d;d++)if(a[d]===b)return d;return-1},x.lastIndexOf=function(a,b,c){if(null==a)return-1;var d=null!=c;if(t&&a.lastIndexOf===t)return d?a.lastIndexOf(b,c):a.lastIndexOf(b);for(var e=d?c:a.length;e--;)if(a[e]===b)return e;return-1},x.range=function(a,b,c){arguments.length<=1&&(b=a||0,a=0),c=arguments[2]||1;for(var d=Math.max(Math.ceil((b-a)/c),0),e=0,f=new Array(d);d>e;)f[e++]=a,a+=c;return f};var E=function(){};x.bind=function(a,b){var c,d;if(w&&a.bind===w)return w.apply(a,h.call(arguments,1));if(!x.isFunction(a))throw new TypeError;return c=h.call(arguments,2),d=function(){if(!(this instanceof d))return a.apply(b,c.concat(h.call(arguments)));E.prototype=a.prototype;var e=new E;E.prototype=null;var f=a.apply(e,c.concat(h.call(arguments)));return Object(f)===f?f:e}},x.partial=function(a){var b=h.call(arguments,1);return function(){return a.apply(this,b.concat(h.call(arguments)))}},x.bindAll=function(a){var b=h.call(arguments,1);if(0===b.length)throw new Error("bindAll must be passed function names");return y(b,function(b){a[b]=x.bind(a[b],a)}),a},x.memoize=function(a,b){var c={};return b||(b=x.identity),function(){var d=b.apply(this,arguments);return x.has(c,d)?c[d]:c[d]=a.apply(this,arguments)}},x.delay=function(a,b){var c=h.call(arguments,2);return setTimeout(function(){return a.apply(null,c)},b)},x.defer=function(a){return x.delay.apply(x,[a,1].concat(h.call(arguments,1)))},x.throttle=function(a,b,c){var d,e,f,g=null,h=0;c||(c={});var i=function(){h=c.leading===!1?0:new Date,g=null,f=a.apply(d,e)};return function(){var j=new Date;h||c.leading!==!1||(h=j);var k=b-(j-h);return d=this,e=arguments,0>=k?(clearTimeout(g),g=null,h=j,f=a.apply(d,e)):g||c.trailing===!1||(g=setTimeout(i,k)),f}},x.debounce=function(a,b,c){var d,e=null;return function(){var f=this,g=arguments,h=function(){e=null,c||(d=a.apply(f,g))},i=c&&!e;return clearTimeout(e),e=setTimeout(h,b),i&&(d=a.apply(f,g)),d}},x.once=function(a){var b,c=!1;return function(){return c?b:(c=!0,b=a.apply(this,arguments),a=null,b)}},x.wrap=function(a,b){return function(){var c=[a];return g.apply(c,arguments),b.apply(this,c)}},x.compose=function(){var a=arguments;return function(){for(var b=arguments,c=a.length-1;c>=0;c--)b=[a[c].apply(this,b)];return b[0]}},x.after=function(a,b){return function(){return--a<1?b.apply(this,arguments):void 0}},x.keys=v||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[];for(var c in a)x.has(a,c)&&b.push(c);return b},x.values=function(a){var b=[];for(var c in a)x.has(a,c)&&b.push(a[c]);return b},x.pairs=function(a){var b=[];for(var c in a)x.has(a,c)&&b.push([c,a[c]]);return b},x.invert=function(a){var b={};for(var c in a)x.has(a,c)&&(b[a[c]]=c);return b},x.functions=x.methods=function(a){var b=[];for(var c in a)x.isFunction(a[c])&&b.push(c);return b.sort()},x.extend=function(a){return y(h.call(arguments,1),function(b){if(b)for(var c in b)a[c]=b[c]}),a},x.pick=function(a){var b={},c=i.apply(d,h.call(arguments,1));return y(c,function(c){c in a&&(b[c]=a[c])}),b},x.omit=function(a){var b={},c=i.apply(d,h.call(arguments,1));for(var e in a)x.contains(c,e)||(b[e]=a[e]);return b},x.defaults=function(a){return y(h.call(arguments,1),function(b){if(b)for(var c in b)void 0===a[c]&&(a[c]=b[c])}),a},x.clone=function(a){return x.isObject(a)?x.isArray(a)?a.slice():x.extend({},a):a},x.tap=function(a,b){return b(a),a};var F=function(a,b,c,d){if(a===b)return 0!==a||1/a==1/b;if(null==a||null==b)return a===b;a instanceof x&&(a=a._wrapped),b instanceof x&&(b=b._wrapped);var e=j.call(a);if(e!=j.call(b))return!1;switch(e){case"[object String]":return a==String(b);case"[object Number]":return a!=+a?b!=+b:0==a?1/a==1/b:a==+b;case"[object Date]":case"[object Boolean]":return+a==+b;case"[object RegExp]":return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if("object"!=typeof a||"object"!=typeof b)return!1;for(var f=c.length;f--;)if(c[f]==a)return d[f]==b;var g=a.constructor,h=b.constructor;if(g!==h&&!(x.isFunction(g)&&g instanceof g&&x.isFunction(h)&&h instanceof h))return!1;c.push(a),d.push(b);var i=0,k=!0;if("[object Array]"==e){if(i=a.length,k=i==b.length)for(;i--&&(k=F(a[i],b[i],c,d)););}else{for(var l in a)if(x.has(a,l)&&(i++,!(k=x.has(b,l)&&F(a[l],b[l],c,d))))break;if(k){for(l in b)if(x.has(b,l)&&!i--)break;k=!i}}return c.pop(),d.pop(),k};x.isEqual=function(a,b){return F(a,b,[],[])},x.isEmpty=function(a){if(null==a)return!0;if(x.isArray(a)||x.isString(a))return 0===a.length;for(var b in a)if(x.has(a,b))return!1;return!0},x.isElement=function(a){return!(!a||1!==a.nodeType)},x.isArray=u||function(a){return"[object Array]"==j.call(a)},x.isObject=function(a){return a===Object(a)},y(["Arguments","Function","String","Number","Date","RegExp"],function(a){x["is"+a]=function(b){return j.call(b)=="[object "+a+"]"}}),x.isArguments(arguments)||(x.isArguments=function(a){return!(!a||!x.has(a,"callee"))}),"function"!=typeof/./&&(x.isFunction=function(a){return"function"==typeof a}),x.isFinite=function(a){return isFinite(a)&&!isNaN(parseFloat(a))},x.isNaN=function(a){return x.isNumber(a)&&a!=+a},x.isBoolean=function(a){return a===!0||a===!1||"[object Boolean]"==j.call(a)},x.isNull=function(a){return null===a},x.isUndefined=function(a){return void 0===a},x.has=function(a,b){return k.call(a,b)},x.noConflict=function(){return a._=b,this},x.identity=function(a){return a},x.times=function(a,b,c){for(var d=Array(Math.max(0,a)),e=0;a>e;e++)d[e]=b.call(c,e);return d},x.random=function(a,b){return null==b&&(b=a,a=0),a+Math.floor(Math.random()*(b-a+1))};var G={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};G.unescape=x.invert(G.escape);var H={escape:new RegExp("["+x.keys(G.escape).join("")+"]","g"),unescape:new RegExp("("+x.keys(G.unescape).join("|")+")","g")};x.each(["escape","unescape"],function(a){x[a]=function(b){return null==b?"":(""+b).replace(H[a],function(b){return G[a][b]})}}),x.result=function(a,b){if(null==a)return void 0;var c=a[b];return x.isFunction(c)?c.call(a):c},x.mixin=function(a){y(x.functions(a),function(b){var c=x[b]=a[b];x.prototype[b]=function(){var a=[this._wrapped];return g.apply(a,arguments),M.call(this,c.apply(x,a))}})};var I=0;x.uniqueId=function(a){var b=++I+"";return a?a+b:b},x.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var J=/(.)^/,K={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},L=/\\|'|\r|\n|\t|\u2028|\u2029/g;x.template=function(a,b,c){var d;c=x.defaults({},c,x.templateSettings);var e=new RegExp([(c.escape||J).source,(c.interpolate||J).source,(c.evaluate||J).source].join("|")+"|$","g"),f=0,g="__p+='";a.replace(e,function(b,c,d,e,h){return g+=a.slice(f,h).replace(L,function(a){return"\\"+K[a]}),c&&(g+="'+\n((__t=("+c+"))==null?'':_.escape(__t))+\n'"),d&&(g+="'+\n((__t=("+d+"))==null?'':__t)+\n'"),e&&(g+="';\n"+e+"\n__p+='"),f=h+b.length,b}),g+="';\n",c.variable||(g="with(obj||{}){\n"+g+"}\n"),g="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+g+"return __p;\n";try{d=new Function(c.variable||"obj","_",g)}catch(h){throw h.source=g,h}if(b)return d(b,x);var i=function(a){return d.call(this,a,x)};return i.source="function("+(c.variable||"obj")+"){\n"+g+"}",i},x.chain=function(a){return x(a).chain()};var M=function(a){return this._chain?x(a).chain():a};x.mixin(x),y(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=d[a];x.prototype[a]=function(){var c=this._wrapped;return b.apply(c,arguments),"shift"!=a&&"splice"!=a||0!==c.length||delete c[0],M.call(this,c)}}),y(["concat","join","slice"],function(a){var b=d[a];x.prototype[a]=function(){return M.call(this,b.apply(this._wrapped,arguments))}}),x.extend(x.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this),FFT.prototype.forward=function(a){var b=this.bufferSize,c=this.cosTable,d=this.sinTable,e=this.reverseTable,f=this.real,g=this.imag;this.spectrum;var h=Math.floor(Math.log(b)/Math.LN2);if(Math.pow(2,h)!==b)throw"Invalid buffer size, must be a power of 2.";if(b!==a.length)throw"Supplied buffer is not the same size as defined FFT. FFT Size: "+b+" Buffer Size: "+a.length;var i,j,k,l,m,n,o,p,q,r=1;for(q=0;b>q;q++)f[q]=a[e[q]],g[q]=0;for(;b>r;){i=c[r],j=d[r],k=1,l=0;for(var s=0;r>s;s++){for(q=s;b>q;)m=q+r,n=k*f[m]-l*g[m],o=k*g[m]+l*f[m],f[m]=f[q]-n,g[m]=g[q]-o,f[q]+=n,g[q]+=o,q+=r<<1;p=k,k=p*i-l*j,l=p*j+l*i}r<<=1}return this.calculateSpectrum()},function(){function a(){for(var a in this.sections)this.sections[a].condition()&&this.sections[a].callback.call(this)}var b=function(){this.audioAdapter=b._getAdapter(this),this.events={},this.sections=[],this.bind("update",a)};b.adapters={},b.prototype={load:function(a){return a instanceof HTMLElement?(this.source=a,"flash"===b.isSupported()&&(this.source={src:b._getMP3SrcFromAudio(a)})):(this.source=window.Audio?new Audio:{},this.source.src=b._makeSupportedPath(a.src,a.codecs)),this.audio=this.audioAdapter.load(this.source),this},play:function(){return this.audioAdapter.play(),this},pause:function(){return this.audioAdapter.pause(),this},setVolume:function(a){return this.audioAdapter.setVolume(a),this},createKick:function(a){return new b.Kick(this,a)},bind:function(a,b){return this.events[a]||(this.events[a]=[]),this.events[a].push(b),this},unbind:function(a){return this.events[a]&&delete this.events[a],this},trigger:function(a){var b=this;return this.events[a]&&this.events[a].forEach(function(a){a.call(b)}),this},getVolume:function(){return this.audioAdapter.getVolume()},getProgress:function(){return this.audioAdapter.getProgress()},getTime:function(){return this.audioAdapter.getTime()},getFrequency:function(a,b){var c=0;if(void 0!==b){for(var d=a;b>=d;d++)c+=this.getSpectrum()[d];return c/(b-a+1)}return this.getSpectrum()[a]},getWaveform:function(){return this.audioAdapter.getWaveform()},getSpectrum:function(){return this.audioAdapter.getSpectrum()},isLoaded:function(){return this.audioAdapter.isLoaded},isPlaying:function(){return this.audioAdapter.isPlaying},after:function(a,b){var c=this;return this.sections.push({condition:function(){return c.getTime()>a},callback:b}),this},before:function(a,b){var c=this;return this.sections.push({condition:function(){return c.getTime()a&&d.getTime()a&&!this.called},callback:function(){b.call(this),d.called=!0},called:!1}),d=this.sections[this.sections.length-1],this}},window.Dancer=b}(),function(a){function b(){var a=!!(navigator.vendor||"").match(/Apple/),b=navigator.userAgent.match(/Version\/([^ ]*)/);return b=b?parseFloat(b[1]):0,a&&6>=b}var c={mp3:"audio/mpeg;",ogg:'audio/ogg; codecs="vorbis"',wav:'audio/wav; codecs="1"',aac:'audio/mp4; codecs="mp4a.40.2"'},d=document.createElement("audio");a.options={},a.setOptions=function(b){for(var c in b)b.hasOwnProperty(c)&&(a.options[c]=b[c])},a.isSupported=function(){return window.Float32Array&&window.Uint32Array?b()||!window.AudioContext&&!window.webkitAudioContext?d&&d.mozSetup?"audiodata":FlashDetect.versionAtLeast(9)?"flash":"":"webaudio":null},a.canPlay=function(b){return d.canPlayType,!!("flash"===a.isSupported()?"mp3"===b.toLowerCase():d.canPlayType&&d.canPlayType(c[b.toLowerCase()]).replace(/no/,""))},a.addPlugin=function(b,c){void 0===a.prototype[b]&&(a.prototype[b]=c)},a._makeSupportedPath=function(b,c){if(!c)return b;for(var d=0;d=this.currentThreshold&&a>=this.threshold?(this.currentThreshold=a,this.onKick&&this.onKick.call(this.dancer,a)):(this.offKick&&this.offKick.call(this.dancer,a),this.currentThreshold-=this.decay)}},maxAmplitude:function(a){var b=0,c=this.dancer.getSpectrum();if(!a.length)return a=d;d++)c[d]>b&&(b=c[d]);return b}},window.Dancer.Kick=b}(),function(){function a(){this.source=this.context.createMediaElementSource(this.audio),this.source.connect(this.proc),this.source.connect(this.gain),this.gain.connect(this.context.destination),this.proc.connect(this.context.destination),this.isLoaded=!0,this.progress=1,this.dancer.trigger("loaded")}var b=2048,c=44100,d=function(a){this.dancer=a,this.audio=new Audio,this.context=window.AudioContext?new window.AudioContext:new window.webkitAudioContext};d.prototype={load:function(d){var e=this;return this.audio=d,this.isLoaded=!1,this.progress=0,this.proc=this.context.createJavaScriptNode(b/2,1,1),this.proc.onaudioprocess=function(a){e.update.call(e,a)},this.gain=this.context.createGainNode(),this.fft=new FFT(b/2,c),this.signal=new Float32Array(b/2),this.audio.readyState<3?this.audio.addEventListener("canplay",function(){a.call(e)}):a.call(e),this.audio.addEventListener("progress",function(a){a.currentTarget.duration&&(e.progress=a.currentTarget.seekable.end(0)/a.currentTarget.duration)}),this.audio},play:function(){this.audio.play(),this.isPlaying=!0},pause:function(){this.audio.pause(),this.isPlaying=!1},setVolume:function(a){this.gain.gain.value=a},getVolume:function(){return this.gain.gain.value},getProgress:function(){return this.progress},getWaveform:function(){return this.signal},getSpectrum:function(){return this.fft.spectrum},getTime:function(){return this.audio.currentTime},update:function(a){if(this.isPlaying&&this.isLoaded){var c,d=[],e=a.inputBuffer.numberOfChannels,f=b/e,g=function(a,b){return a[c]+b[c]};for(c=e;c--;)d.push(a.inputBuffer.getChannelData(c));for(c=0;f>c;c++)this.signal[c]=e>1?d.reduce(g(prev,curr))/e:d[0][c];this.fft.forward(this.signal),this.dancer.trigger("update")}}},Dancer.adapters.webkit=d}(),function(){function a(){this.fbLength=this.audio.mozFrameBufferLength,this.channels=this.audio.mozChannels,this.rate=this.audio.mozSampleRate,this.fft=new FFT(this.fbLength/this.channels,this.rate),this.signal=new Float32Array(this.fbLength/this.channels),this.isLoaded=!0,this.progress=1,this.dancer.trigger("loaded")}var b=function(a){this.dancer=a,this.audio=new Audio};b.prototype={load:function(b){var c=this;return this.audio=b,this.isLoaded=!1,this.progress=0,this.audio.readyState<3?this.audio.addEventListener("loadedmetadata",function(){a.call(c)},!1):a.call(c),this.audio.addEventListener("MozAudioAvailable",function(a){c.update(a)},!1),this.audio.addEventListener("progress",function(a){a.currentTarget.duration&&(c.progress=a.currentTarget.seekable.end(0)/a.currentTarget.duration)},!1),this.audio},play:function(){this.audio.play(),this.isPlaying=!0},pause:function(){this.audio.pause(),this.isPlaying=!1},setVolume:function(a){this.audio.volume=a},getVolume:function(){return this.audio.volume},getProgress:function(){return this.progress},getWaveform:function(){return this.signal},getSpectrum:function(){return this.fft.spectrum},getTime:function(){return this.audio.currentTime},update:function(a){if(this.isPlaying&&this.isLoaded){for(var b=0,c=this.fbLength/2;c>b;b++)this.signal[b]=(a.frameBuffer[2*b]+a.frameBuffer[2*b+1])/2;this.fft.forward(this.signal),this.dancer.trigger("update")}}},Dancer.adapters.moz=b}(),function(){function a(){var a=this;f=!0,b(Dancer.options.flashJS,function(){soundManager=new SoundManager,soundManager.flashVersion=9,soundManager.flash9Options.useWaveformData=!0,soundManager.useWaveformData=!0,soundManager.useHighPerformance=!0,soundManager.useFastPolling=!0,soundManager.multiShot=!1,soundManager.debugMode=!1,soundManager.debugFlash=!1,soundManager.url=Dancer.options.flashSWF,soundManager.onready(function(){e=!0,a.load()}),soundManager.ontimeout(function(){console.error("Error loading SoundManager2.swf")}),soundManager.beginDelayedInit()})}function b(a,b){var c=document.createElement("script"),d=document.getElementsByTagName("script")[0];c.type="text/javascript",c.src=a,c.onload=b,d.parentNode.insertBefore(c,d)}var c=1024,d=44100,e=!1,f=!1,g=.93,h=function(a){this.dancer=a,this.wave_L=[],this.wave_R=[],this.spectrum=[],window.SM2_DEFER=!0};h.prototype={load:function(b){var e=this;return this.path=b?b.src:this.path,this.isLoaded=!1,this.progress=0,!window.soundManager&&!f&&a.call(this),window.soundManager&&(this.audio=soundManager.createSound({id:"dancer"+Math.random(),url:this.path,stream:!0,autoPlay:!1,autoLoad:!0,whileplaying:function(){e.update()},whileloading:function(){e.progress=this.bytesLoaded/this.bytesTotal},onload:function(){e.fft=new FFT(c,d),e.signal=new Float32Array(c),e.waveform=new Float32Array(c),e.isLoaded=!0,e.progress=1,e.dancer.trigger("loaded")}}),this.dancer.audio=this.audio),this.audio},play:function(){this.audio.play(),this.isPlaying=!0},pause:function(){this.audio.pause(),this.isPlaying=!1},setVolume:function(a){this.audio.setVolume(100*a)},getVolume:function(){return this.audio.volume/100},getProgress:function(){return this.progress},getWaveform:function(){return this.waveform},getSpectrum:function(){return this.fft.spectrum},getTime:function(){return this.audio.position/1e3},update:function(){if(this.isPlaying||this.isLoaded){this.wave_L=this.audio.waveformData.left,this.wave_R=this.audio.waveformData.right;for(var a,b=0,c=this.wave_L.length;c>b;b++)a=parseFloat(this.wave_L[b])+parseFloat(this.wave_R[b]),this.waveform[2*b]=a/2,this.waveform[2*b+1]=a/2,this.signal[2*b]=a*g,this.signal[2*b+1]=a*g;this.fft.forward(this.signal),this.dancer.trigger("update")}}},Dancer.adapters.flash=h}(),FFT.prototype.forward=function(a){var b=this.bufferSize,c=this.cosTable,d=this.sinTable,e=this.reverseTable,f=this.real,g=this.imag;this.spectrum;var h=Math.floor(Math.log(b)/Math.LN2);if(Math.pow(2,h)!==b)throw"Invalid buffer size, must be a power of 2.";if(b!==a.length)throw"Supplied buffer is not the same size as defined FFT. FFT Size: "+b+" Buffer Size: "+a.length;var i,j,k,l,m,n,o,p,q,r=1;for(q=0;b>q;q++)f[q]=a[e[q]],g[q]=0;for(;b>r;){i=c[r],j=d[r],k=1,l=0;for(var s=0;r>s;s++){for(q=s;b>q;)m=q+r,n=k*f[m]-l*g[m],o=k*g[m]+l*f[m],f[m]=f[q]-n,g[m]=g[q]-o,f[q]+=n,g[q]+=o,q+=r<<1;p=k,k=p*i-l*j,l=p*j+l*i}r<<=1}return this.calculateSpectrum()};var FlashDetect=new function(){var a=this;a.installed=!1,a.raw="",a.major=-1,a.minor=-1,a.revision=-1,a.revisionStr="";var b=[{name:"ShockwaveFlash.ShockwaveFlash.7",version:function(a){return c(a)}},{name:"ShockwaveFlash.ShockwaveFlash.6",version:function(a){var b="6,0,21";try{a.AllowScriptAccess="always",b=c(a)}catch(d){}return b}},{name:"ShockwaveFlash.ShockwaveFlash",version:function(a){return c(a)}}],c=function(a){var b=-1;try{b=a.GetVariable("$version")}catch(c){}return b},d=function(a){var b=-1;try{b=new ActiveXObject(a)}catch(c){b={activeXError:!0}}return b},e=function(a){var b=a.split(",");return{raw:a,major:parseInt(b[0].split(" ")[1],10),minor:parseInt(b[1],10),revision:parseInt(b[2],10),revisionStr:b[2]}},f=function(a){var b=a.split(/ +/),c=b[2].split(/\./),d=b[3];return{raw:a,major:parseInt(c[0],10),minor:parseInt(c[1],10),revisionStr:d,revision:g(d)}},g=function(b){return parseInt(b.replace(/[a-zA-Z]/g,""),10)||a.revision};a.majorAtLeast=function(b){return a.major>=b},a.minorAtLeast=function(b){return a.minor>=b},a.revisionAtLeast=function(b){return a.revision>=b},a.versionAtLeast=function(){var b=[a.major,a.minor,a.revision],c=Math.min(b.length,arguments.length);for(i=0;c>i;i++){if(b[i]>=arguments[i]){if(c>i+1&&b[i]==arguments[i])continue;return!0}return!1}},a.FlashDetect=function(){if(navigator.plugins&&navigator.plugins.length>0){var c="application/x-shockwave-flash",g=navigator.mimeTypes;if(g&&g[c]&&g[c].enabledPlugin&&g[c].enabledPlugin.description){var h=g[c].enabledPlugin.description,i=f(h);a.raw=i.raw,a.major=i.major,a.minor=i.minor,a.revisionStr=i.revisionStr,a.revision=i.revision,a.installed=!0}}else if(-1==navigator.appVersion.indexOf("Mac")&&window.execScript)for(var h=-1,j=0;jb+1e3&&(f=Math.round(1e3*i/(j-b)),g=Math.min(g,f),h=Math.max(h,f),m.textContent=f+" FPS ("+g+"-"+h+")",t(n,Math.min(30,30-30*(f/100))),b=j,i=0),j},update:function(){a=this.end()}}};!function(a){var b={};a.Webvs=b,b.defineClass=function(a,b){return a.prototype=Object.create(b.prototype),a.prototype.constructor=a,a.super=b.prototype,_.chain(arguments).drop(2).each(function(b){_.extend(a.prototype,b)}),a},b.noop=function(){},b.checkRequiredOptions=function(a,b){for(var c in b){var d=b[c];if(!(d in a))throw new Error("Required option "+d+" not found")}},b.glslFloatRepr=function(a){return a+(0===a%1?".0":"")},b.parseColor=function(a){if(_.isArray(a)&&3==a.length)return a;if(_.isString(a)){var b;if(a=a.toLowerCase(),b=a.match(/^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/))return _.chain(b).last(3).map(function(a){return parseInt(a,16)}).value();if(b=a.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/))return _.chain(b).last(3).map(function(a){return Math.min(parseInt(a,10),255) -}).value()}throw new Error("Invalid Color Format")},b.parseColorNorm=function(a){return _.map(b.parseColor(a),function(a){return a/255})},b.logShaderError=function(a,b){var c=a.split("\n"),d=c.length.toString().length,e=b.match(/(\d+):(\d+)/);e&&(e=[parseInt(e[1],10),parseInt(e[2],10)]);var f=_.map(c,function(a,c){var f,g=c+1+"";for(f=0;fd;d++)c.push(b.charAt(Math.floor(Math.random()*b.length)));return c.join("")},b.clamp=function(a,b,c){return Math.min(Math.max(a,b),c)},b.getComponentClass=function(a){var c=b[a];if(!c)throw new Error("Unknown Component class "+a);return c}}(window),function(a){a.Resources={"avsres_texer_circle_edgeonly_19x19.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAMAAABFjsb+AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhFBMVEUAAAADAwMvLy9dXV1tbW0CAgJaWlrR0dH8/Pz+/v79/f1bW1sKCgqlpaXT09Nubm4xMTEeHh7S0tKCgoIGBgaBgYEuLi5sbGxeXl4cHBwbGxtvb29fX19ra2swMDDU1NQFBQV/f3+np6eoqKgLCwvQ0NBqamoEBAQyMjJhYWFxcXH///8GRExTAAAAAWJLR0QrJLnkCAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAK1JREFUGNN1kMkSgyAQRBViGMc1SpCYBYl7/v8Do2gocpBDF/Wqa3qmPe/w+YSeKPEdEpwZhBgCi4IfihNMs/ySZwUm8e5KoOTmx0tINmeEpZ1yxciMTwtuGS/SNUhA5cRVQBaVSBwmUC6a4c1hNd6NT/z5HosSeDrsCa81V7HGooYpcyBFbZlGut3xBr1t2Gho9x66FvtB1GLose1sU1KZXpR02xqn+TNP43HBX4kJCUk5wyykAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0wNC0yOVQwMTo0OTozMCswNTowMPYYqoAAAAAASUVORK5CYII=","avsres_texer_circle_edgeonly_29x29.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAMAAABhTZc9AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAA3lBMVEUAAAANDQ0/Pz9xcXGRkZGbm5tycnIfHx+IiIjg4OD7+/v+/v7///8EBARoaGjn5+f8/Pzl5eXHx8e7u7sICAiXl5f6+vrx8fGVlZU4ODgKCgoBAQEJCQm+vr4oKCi9vb39/f2YmJikpKQLCwujo6NpaWmJiYknJyeKiorh4eE3Nzc2NjZAQEDk5ORzc3OSkpLGxsacnJy6urq5ubmdnZ3FxcWTk5M1NTWUlJTw8PDo6Oi8vLwgICCioqJqamqZmZmamprj4+PExMS4uLiLi4sODg5BQUF0dHSenp4PDw8GiJKZAAAAAWJLR0QMgbNRYwAAAAlwSFlzAAALEgAACxIB0t1+/AAAATRJREFUKM+tkltXgkAUhUczL+xBQGNAwEuRKVlpNxC8oGZa//8PRRoIjqsnz9M561tnZu+zNiFnqVz+onBZKOZzJ1ipXBEACgiVcumIiVUJVFZq9ZoiU0hVMQ2vVAZNbxgmMY2GroGpVmpTldFsJWOrjc71YbvKcGOnnrJvwbqJIAlNOyPDbkOKpd1Ba5Fs9TT0/3xWqM4Z1Kmz950X5HuODmThYdcUoRgctR7xtGuGGJkcNUcY7ppn+nLisq/07Z/dd7jxvxZHk38jzQOOerHmnEPHHB3HfkkfWu/4Vn58K1IKMMk6NuoIpvHQZZilsTEDmyeTGHYwOTzem6ATptJhhQy+61m/2fBcH2yR8SjOgyhXy9XHahnlKphnchXVdO3I+0w66ynh63Ozdb/c7eabnKV+AJulHNGcTEkjAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0wNC0yOVQwMTo0OTozMCswNTowMPYYqoAAAAAASUVORK5CYII=","avsres_texer_circle_fade_13x13.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAA9lBMVEUAAAADAwMGBgYICAgCAgIKCgoVFRUeHh4hISEdHR0JCQkODg4jIyM4ODhISEhNTU1HR0c3NzcNDQ0LCwskJCRGRkZoaGh/f3+Hh4d+fn5lZWVEREQXFxc6OjppaWmSkpKtra22trarq6uPj49mZmYHBwcgICBLS0uCgoKurq7Kysrd3d3JycmsrKxRUVGMjIy4uLjf39/+/v63t7eIiIhMTEyDg4Ovr6/MzMzLy8uAgIBJSUkEBAQYGBg8PDxsbGyVlZWwsLC5ubmTk5M5OTkWFhYMDAwmJiZKSkqEhIRqamoQEBA9PT1SUlI7OzsPDw8BAQH///+Cg4ycAAAAAWJLR0RRlGl8KgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKRJREFUCNcljukWgVAAhO9tvRKVEhGyhISEspWsZef9n0bL/Jo5c+bMB0AsiOEYBJkIkqJRjmKINOTZQpHjBVRKoiiV5UpVqdURGW/URrOltTvdnkBBoPcHQ2NkjidKkcYAI02tmT03F0sO4UBHjrta2xttu4s7z98Hh+PJOMuXa/wZRrfgbrkPnhUTkGf04t7OR/qm9zD0kYRUkcjQfh7O6F7i/uybEULrc6ImAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwMy0xMC0wN1QwMzoxNzoxMiswNjowMNF3hcgAAAAASUVORK5CYII=","avsres_texer_circle_heavyblur_19x19.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAMAAABFjsb+AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABVlBMVEUAAAACAgIHBwcNDQ0RERETExMBAQEICAgVFRUiIiItLS01NTU3NzcgICA0NDRGRkZVVVVeXl5hYWEjIyM9PT1XV1dubm6BgYGMjIyQkJAfHx9dXV19fX2YmJirq6u2tra6uroDAwMUFBQzMzNWVlafn5+7u7vPz8/b29vf398hISFFRUVtbW2Xl5fY2Njr6+v09PT39/cMDAwsLCxTU1OAgICpqanOzs7q6ur4+Pj8/Pz+/v6qqqoQEBBcXFyKioq0tLTa2tr9/f3///+1tbWLi4sSEhJfX1+NjY24uLje3t729vYyMjKJiYnZ2dnz8/MLCwsrKytSUlJ+fn6oqKjNzc3p6ekGBgZDQ0Nra2uVlZXV1dXy8vL19fUxMTFUVFR5eXmcnJzMzMzc3NwdHR06OjpaWlqUlJSnp6eysrJqamp8fHyIiIgwMDBCQkJRUVEqKioPDw8hvXKsAAAAAWJLR0RDZ9ANYgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAU5JREFUGNM9kFVbwmAARr9tMEpKUhghoyRHCQyHhMTI0SnlSMn/f6OI+l6+z7k5B4DrIBjhcDkIDIG/oTy+QCi6EwkFfB56uyBELJHK5PdymVQiRn5QFFEoVWqN9kGrUauUCuRK8nRKPWYwmswmowHTK3U8AB4tuBWz2R3OJ6fDbsOsuAUCsMvt8Zp9fiJA+H12r8ftgkEwFI48R2NxMkHGY9GXSDgUBBSefE2liUwimyCJ9FsuiVMgLyoU6VI5k81mE+USXSyI8oCqJKupWp28cvVaqpqsUIBpNFvtTjdAZshAt9NuNRsM6In7gyE96o7fx90RPRz0xT0AcSfT2Zxe+D58C3o+m0643yIwu1zN1putabtZz1ZLFr76Bl3L8O5zn9tju/DSxdxSMezheFrpV6fjgWV+Y6FwXnee4JOzLg+j/1WhHnOhLkzv1vkLuBJAlyODjrgAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDA5LTA0LTI5VDAxOjQ5OjMwKzA1OjAw9hiqgAAAAABJRU5ErkJggg==","avsres_texer_circle_heavyblur_21x21.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABfVBMVEUAAAADAwMICAgNDQ0QEBASEhIBAQELCwsWFhYhISEqKiovLy8yMjIFBQUiIiJBQUFNTU1UVFRXV1cUFBQoKCg+Pj5TU1NlZWV0dHR9fX2AgIBmZmYRERFCQkJdXV13d3eNjY2cnJylpaWoqKgKCgqampqvr6/AwMDKysrNzc0VFRUxMTF2dnaZmZm2trbOzs7e3t7o6Ojr6+sHBwcfHx9AQECMjIzl5eXy8vL4+Pj6+voMDAxMTExzc3Obm5u/v7/d3d39/f3+/v4PDw8uLi58fHykpKTIyMjn5+f///8wMDBVVVV+fn6mpqbLy8vp6en5+fktLS1SUlJ7e3ujo6PHx8fm5ub39/dKSkpxcXG9vb3c3Nzx8fEGBgY/Pz9jY2OKioqtra3j4+NQUFCWlpazs7Pb29sCAgIJCQkgICA8PDxaWlp6enqsrKy8vLzGxsbJyckmJiaJiYmhoaEEBAQ7OztiYmJwcHB5eXlJSUkTExMeHh4sLCwdHR0ODg7+hS0uAAAAAWJLR0RJhwXkfAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAZhJREFUGNNFkfs3wnAYxr9jkynXXSqyXLZpGMXShWhbRWFCmUvlslgql1Xk/rdbEc+Pn/Oezznv8wDQDtTVDSNwdxfUA/4CWXrRPqvN2of2WqAO7EcGbINDwyPDQ4O2AaT/F2I4QdodzlGnY4wkcKyNIRc+TrknJqempyYn3NQ47mpJaJSgnAw74+E8MyzjpAiUNk9n58h5fmHR61vyebkFfp6cm4XAsuC3r7BcIBgKh4IBjl2x+4VlsIqvRdY3osGwKInhYHRjPbKGW4AcI+OJza2QmEwmxdDWZiJOxmQAp7Z3dpW9tGRSKb2n7O5sp2Ag7x8cZrJHqiglJVE9ymYOD/ZlYMGPT5jTs1zbmzs7ZU6OTW9eKJxfXCpXOTWt5q6Uy4vzgpAHEJbSHMVrz03AF7hRrosOLYXpAJRuy5Vq8e7+4fHh/q5YrZRvS+bHOmwUKpEaX0/U+Vqk4jdgvVVPAzGetOfmS/yl+aw9GUjjp8qGLFiJV43UXgmrIDc6teult3fjI/ZhfL6V9P+JgJ6nv+QvOv+7zzdrwFa1yCl9GAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDktMDQtMjlUMDE6NDk6MzArMDU6MDD2GKqAAAAAAElFTkSuQmCC","avsres_texer_circle_heavyblur_29x29.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAMAAABhTZc9AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACSVBMVEUAAAABAQEDAwMGBgYJCQkLCwsNDQ0ODg4EBAQPDw8WFhYcHBwgICAjIyMkJCQCAgIHBwcQEBAaGhosLCwzMzM4ODg8PDw9PT0ICAgiIiIuLi46OjpERERNTU1TU1NXV1dZWVkMDAwYGBgnJyc2NjZFRUVSUlJeXl5oaGhwcHB1dXV2dnZfX18KCgooKChMTExdXV1sbGx7e3uHh4ePj4+Tk5OVlZWUlJQVFRUmJiZOTk5iYmKIiIiXl5eioqKqqqqvr6+xsbEhISE1NTVLS0t5eXmhoaG8vLzExMTJycnLy8u7u7stLS1cXFykpKS2trbGxsbT09Pb29vh4eHj4+MZGRk5OTlRUVGgoKDKysra2trm5ubu7u7y8vLz8/Nra2sFBQUUFBQqKipDQ0N6enqWlpbZ2dno6Oj4+Pj6+vr7+/vp6ekxMTFnZ2eGhoa6urrR0dHl5eX5+fn8/Pz9/f3+/v7S0tIeHh5ubm6NjY2oqKjCwsLt7e339/f///9VVVVycnKRkZGtra3Hx8ff39/x8fGSkpI7Ozt0dHSurq6srKze3t4dHR1tbW2MjIynp6fBwcHs7OwwMDBKSkplZWWEhISfn5+5ubnQ0NDk5OQpKSlBQUFbW1t3d3fDw8PX19fn5+c3NzdPT09paWmFhYWdnZ2zs7Pr6+vw8PAXFxcrKyuLi4vPz8/Y2Njd3d0fHx9ISEirq6u3t7fAwMDFxcW4uLgTExNxcXGenp6mpqaCgoKKiopaWlpkZGRAQEBJSUkvLy80NDQSEhJl1IWqAAAAAWJLR0R+P7hBcwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAvpJREFUKM9dk/k/2gEcxr/VN+UrlcRKoRJRvs4awrTE2NxHzuZu1RzTMWdEGpqbXLly39mY+9xfNpnZ7Pn1/Xqe1+f1ep4PADwKgUSBaCeMExpEIRHAcyGwoDPkgnPFu+JcIGcQ+4wTiG4kV3eyh+cLTw+yuyvJjUL4C5FoiOpFo3v7+DJ8fbzpNC8qhEY+QSbLj+0fwAkM4vK4QYHBAf5sPxbzERNgVggtNCw8IpIveBnFj4wIDwulhbDgh3AEJTqGJozlxcW/ShC9FiW8io/jxQppMdFEx2lYZ3GiJDaJnyx6k5L6NjXlnSiZnxQrSRQ7Yx1WVho9ncvPyMzKzsnNy83JzsrM4HPT6Wn5FASAxFClBeFxhZlFxSWlsvey0pLioszCuLICKRWDBMorKqs41TWiLPkHmUKpUipkH+VZoppqTlVldDlAIdVK6uqTGz41ytQarU6rUcsaPzck19dJaklEAG5iBzS3tLYVt+s1uo6ODp1G317c1trSHMBuggEmjtZp6Oo29piU2g6HtEpTj7H7i6GThmMCTHGvb5+gPzVXptI9UJ1KlpvaL+jz7aUyATROeu81G7+a1NqHZK363mvuMnRKcWgAHmAXDA4Nj8hH9RoH1mr0o/KR4aHBAvYADBDzx8YnLJNT0zMmher+ZpXCNDM9NWmZGB/LJwLYilnr3HyUeWFxyaRXK9V609Ligjlqfs46W4EFCE5i2/LK6tq6cTFvY9O0uZG3aFxfW11Ztomd7luibG1bGYadwvWF3b39nv293YX1wh0Dw7q9RXR0j7GTDzjcnbVv3w+PjEeHP8xrO9zgA7Id89A/6hhvG+ecWASnZwnmhLNTgeWEI7HhK1C/9whCMbYDRiDv/ILfxb845wUyDmwxEIj4Mx0IT7YKL6/Kgk6Cyq4uhVYyHoKfVkkAj+2V7OubZZ9On+Wba3al/Rj8Z7IIFGbLHnJ75yG13d2G2CEM6vk7IInMY5aLHWd3Yf1kEpHA/yJgUSCMhkEUlvBk/AXCBOhMJK+/nwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMTAtMDZUMjI6MDc6NDYrMDY6MDA+xvzGAAAAAElFTkSuQmCC","avsres_texer_circle_sharp_09x09.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAMAAACecocUAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAOVBMVEUAAAACAgJhYWG+vr7b29tiYmKoqKj+/v7///9jY2PBwcHf39/CwsJlZWUDAwOsrKxmZmbDw8Pg4ODJk53hAAAAAWJLR0QIht6VegAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAElJREFUCNdVzUsSwCAIA1CtVakfUO9/WINlY1YvM0xw7op/whuiP0yZiHLSEpUoH1zoT4GrucLN3OBu9x1mOTvCOspjrjn4froBkegClm06guAAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDAzLTA2LTI5VDE5OjA5OjMyKzA2OjAw50C8rwAAAABJRU5ErkJggg==","avsres_texer_circle_sharp_19x19.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAe1BMVEUAAAAFBQVBQUGGhoaurq68vLyvr68GBgYBAQFNTU3Ozs78/Pz+/v7///+Kior6+vr9/f2Li4vR0dFCQkJDQ0OIiIiJiYmzs7PBwcG0tLRERETT09NRUVH7+/uQkJCRkZFTU1PU1NRUVFQHBwdHR0eMjIy1tbXDw8NISEjxdmZoAAAAAWJLR0QN9rRh9QAAAAlwSFlzAAALEgAACxIB0t1+/AAAAJ5JREFUGNN9ke0SgiAUBUUrQbhA9qGpWGZa7/+EgZoCNu7PnTPALEGwAQqj3f4QhbHlMEkoAwBGE4LnIRfwQ3A0LbmEBXkc10SAjSBGxim4pObKE/UsPWt7YZ5lV20z8Mm0zVc2/7u9actX53JtC+lZWZj3lp4th0SVcqSqxg6106GeqqF6Wav7nBg/Gjn0lc0TW93bV9e/++7Tbn3YF1ESEZb9e6HrAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0wNC0yOVQwMTo0OTozMCswNTowMPYYqoAAAAAASUVORK5CYII=","avsres_texer_circle_slightblur_13x13.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAASFBMVEUAAAABAQEODg4jIyMtLS0EBARsbGyZmZmoqKiKiorT09Py8vL4+Pj7+/v+/v7///8kJCRtbW3U1NSLi4uampqpqakPDw8uLi7NF0qwAAAAAWJLR0QPGLoA2QAAAAlwSFlzAAALEgAACxIB0t1+/AAAAGhJREFUCNdljkcOwCAMBCmmF9NC/v/TGJBySHwbjXbXjH2PCwlS8AMKtLFGg9oGnA8xeAfLCu1Txpy8FkTShIyIORhJBDbiumiBqLyurFxtO9d2jkNv1Nn67qS9Oq5Rzx7ZWe4y+e/HB1hhBEsscYLrAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwMy0xMC0wNlQyMTozODowMCswNTowMB7KOaoAAAAASUVORK5CYII=","avsres_texer_circle_slightblur_21x21.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAvVBMVEUAAAACAgIDAwMEBAQQEBAgICAsLCwwMDABAQEODg5MTExnZ2d5eXl+fn4TExM9PT1tbW2Xl5e0tLTFxcXKysqvr6/W1tbs7Oz19fX4+Pjg4OD39/f9/f3+/v6YmJj///8RERFoaGi1tbV6enrGxsb29vYxMTGAgIDLy8t/f38tLS0hISG2trbt7e1NTU2ZmZnX19dubm6xsbHh4eGwsLAPDw8+Pj57e3sUFBRvb2/MzMxpaWmBgYEiIiIyMjK3SG45AAAAAWJLR0QfBQ0QvQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAPNJREFUGNN9ketygjAQhZVLCpgKagEjRAKC2nAVC2itvv9jOcRI0Rk9P7/ZOXv27GDwXkNh+IxESQYfQJbEHlNUoI3gJxxpQFU6ONaNyXT2NZtODH18p6oOTcueo7ltmVBXuScwzIXjYoxdZ2Ea4OYtaUvLwV4r7FhLjTAq+4Hteje5duDLLCdYhRHmFEchBG1uYb3Zoo6i7fdaaGfp4+yKshtjP0k73zTwY7aNZHnBLTAq8ozwvLtyj1hetP/ZUd5FVTdlkUYoSouyqat7D4f6mCfhb5jkx/rwX09Fs1Pz15wyWim9LkVyphcak36/r37xpCumwx2LqyXT8gAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMTAtMDZUMjA6NDA6MjIrMDU6MDAsCqMMAAAAAElFTkSuQmCC","avsres_texer_hexagon-h_blur_123x123.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHsAAAB7AgMAAAApqRfsAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEUAAACIiIjMzMz///+lqQOCAAAAAWJLR0QDEQxM8gAAAAlwSFlzAAALEgAACxIB0t1+/AAAAXJJREFUSMft17FVwzAURuHgwhSMQKERvEI2ygh4BK1BltAKjECREVxAIQSH4MTvSfpvKhpQe8/J8efY0vNu979+Y90dzXr2/b6Y9eH7g+1l73pwfXb94PqL68n1k7t8l8ub7YPv75JXAT3PA0PVLfBQdQtMVTfAiueAQ92z5DngY90NMDR6lDwLTI2+ATZ4Bji0epY8A2zxtsCp2aPkbYGp2U+StwEO7Z4lbwNs867AqdNX4FOnv0peKYvkXYBjr2fJuwB7vBU4dXuUvBWYun2RvB/g2O9Z8r6Ae8k7AyfRo+SdgUn0RfK+gaPqubUzWmBQvXCn36frIx/dH7y/k+jxhv+Xng96vuj5pOcb348+MN70ftL7TfsD7S+0P+H+1gOu+yPtr7Q/0/5O5wOdL3g+tYHX843ORzpf6Xym853mA5wvguTxfEPzEc1XNJ/RfIfzYQ2cTaf5lOZbmo9pvqb5HOd7D5xdp+8L+j6h75u/uj4BxUT7mSYYfpEAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDExLTA0LTExVDAwOjM0OjU4KzA1OjAwAHKiyQAAAABJRU5ErkJggg==","avsres_texer_square_edgeonly_24x24.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYAQMAAADaua+7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEX///8AAABVwtN+AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABJJREFUCNdjYEAC8v9/UAUjAQD7uCWNIgeQwQAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMTAtMDZUMjE6NTc6NDYrMDU6MDDg7pOkAAAAAElFTkSuQmCC","avsres_texer_square_edgeonly_28x28.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcAQMAAABIw03XAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEX///8AAABVwtN+AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABNJREFUCNdjYEAF8v//N9CPQAUArWA5f2D2DX0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDAzLTEwLTA2VDIxOjU4OjQ0KzA1OjAwhnrZAAAAAABJRU5ErkJggg==","avsres_texer_square_edgeonly_30x30.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEX///8AAABVwtN+AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABRJREFUCNdjYEAF8v//PxgIAhUAAOmGR7lQ6SOhAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwMy0xMC0wNlQyMTo1OToxOCswNTowMOb41i4AAAAASUVORK5CYII=","avsres_texer_square_sharp_20x20.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYAQMAAADaua+7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABJJREFUCNdjYIAC+/9/qIqhAABLlCyJ9A7ihwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMDYtMjlUMTk6Mzg6MDQrMDU6MDBuiiynAAAAAElFTkSuQmCC","avsres_texer_square_sharp_32x32.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoAQMAAAC2MCouAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABVJREFUCNdjYMAB+P////9hCJM4AACQJX+BjmWyDAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDktMTAtMDhUMDE6NTM6NTYrMDU6MDBzUO7GAAAAAElFTkSuQmCC","avsres_texer_square_sharp_48x48.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4AQMAAACSSKldAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABZJREFUGNNjYCAA+P+DwIdReoBoAgAAldYe8LB8aUoAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDA5LTEwLTA4VDAxOjUzOjI4KzA1OjAwKaqcggAAAABJRU5ErkJggg==","avsres_texer_square_sharp_60x60.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQAAABEAQMAAAAC3QHxAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAALEgAACxIB0t1+/AAAABdJREFUKM9jYCAS8P+HgFHWKIuaLCIBAH8IpfBELmrmAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMTo1NDozMiswNTowMKOs2CsAAAAASUVORK5CYII=","avsres_texer_square_sharp_64x64.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQAQMAAAC032DuAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABdJREFUKM9jYKAV+A8Fo8xR5lBk0gYAAMbE/hBbX3sVAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMjoxODowMCswNjowML8mns0AAAAASUVORK5CYII=","avsres_texer_square_sharp_72x72.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFQAAABUAQMAAAAmpYKCAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABpJREFUKM9jYKASYP4PBX9G2aPsUTa12VQCABpBhZc9Qe3KAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMjowMjoxNiswNjowMMbw5GAAAAAASUVORK5CYII=","avsres_texer_square_sharp_96x96.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHAAAABwAQMAAAD8LmYIAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAB1JREFUOMtjYBgo8B8JjHJHuaPcUe4od/BwBwYAAB86e71LirdOAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMjoxODo0NiswNjowMFi8pQ0AAAAASUVORK5CYII=","avsres_texer_square_sharp_250x250.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAAEKAQMAAADQBYmKAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAALEgAACxIB0t1+/AAAADlJREFUaN7tykENAAAIBCD7p7KZFrgZwMGbKrK5taIoiqIoiqIoiqIoiqIoiqIoiqIoiqIoivKpkCyUd+T92W3T7QAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTEtMDEtMDVUMTg6NTk6NTQrMDU6MDBwiLErAAAAAElFTkSuQmCC"}}(Webvs),function(a){function b(){}a.AnalyserAdapter=a.defineClass(b,Object,{beat:!1,isPlaying:function(){return!1},getWaveform:function(){return new Float32Array(0)},getSpectrum:function(){return new Float32Array(0)}})}(Webvs),function(a){function b(a){this.dancer=a,this.beat=!1;var b=this;this.kick=a.createKick({onKick:function(){b.beat=!0},offKick:function(){b.beat=!1}}),this.kick.on()}a.DancerAdapter=a.defineClass(b,a.AnalyserAdapter,{isPlaying:function(){return this.dancer.isPlaying()},getWaveform:function(){return this.dancer.getWaveform()},getSpectrum:function(){return this.dancer.getSpectrum()}})}(Webvs),function(a){function b(b){if(a.checkRequiredOptions(b,["canvas","analyser"]),b=_.defaults(b,{showStat:!1}),this.canvas=b.canvas,this.analyser=b.analyser,this.isStarted=!1,b.showStat){var c=new Stats;c.setMode(0),c.domElement.style.position="absolute",c.domElement.style.right="5px",c.domElement.style.bottom="5px",document.body.appendChild(c.domElement),this.stats=c}this.resources={},this.rootComponent=new a.EffectList({id:"root"}),this._registerContextEvents(),this._initGl()}a.Main=a.defineClass(b,Object,{_registerContextEvents:function(){var a=this;this.canvas.addEventListener("webglcontextlost",function(b){b.preventDefault(),a.stop()}),this.canvas.addEventListener("webglcontextrestored",function(){a.resetCanvas()})},_initGl:function(){try{this.gl=this.canvas.getContext("experimental-webgl",{alpha:!1}),this.copier=new a.CopyProgram({dynamicBlend:!0}),this.copier.init(this.gl),this.resolution={width:this.canvas.width,height:this.canvas.height}}catch(b){throw new Error("Couldnt get webgl context"+b)}},loadPreset:function(b){b=_.clone(b),b.id="root";var c=new a.EffectList(b);this.stop(),this.rootComponent.destroy(),this.rootComponent=c,this.resources=b.resources||{}},resetCanvas:function(){this.stop();var b=this.rootComponent.getOptions();this.rootComponent.destroy(),this.copier.cleanup(),this._initGl(),this.rootComponent=new a.EffectList(b)},start:function(){if(!this.isStarted){var a=this,b=function(){a.analyser.isPlaying()&&a.rootComponent.update(),a.animReqId=requestAnimationFrame(b)};if(this.stats){var c=b;b=function(){a.stats.begin(),c.call(this,arguments),a.stats.end()}}this.rootComponent.componentInited||(this.registerBank={},this.bootTime=(new Date).getTime(),this.rootComponent.init(this.gl,this)),this.animReqId=requestAnimationFrame(b),this.isStarted=!0}},stop:function(){_.isUndefined(this.animReqId)||(cancelAnimationFrame(this.animReqId),this.isStarted=!1)},getPreset:function(){var a=this.rootComponent.getOptions();return a.resources=this.resources,a},addComponent:function(a,b,c){return b=_.clone(b),this.rootComponent.addComponent(a,b,c),res},updateComponent:function(b,c){if(c=_.clone(c),c.id=b,"root"==b){var d=this.rootComponent.detachAllComponents();return c=_.defaults(c,this.rootComponent.options),this.rootComponent.destroy(),this.rootComponent=new a.EffectList(c,d),this.rootComponent.init(this.gl,this),!0}return this.rootComponent.updateComponent(b,c)},removeComponent:function(a){var b=this.rootComponent.detachComponent(a);return b?(b.destroy(),!0):!1},moveComponent:function(a,b,c){var d=this.rootComponent.detachComponent(a);return d?this.rootComponent.addComponent(b,d,c):!1},getResource:function(b){var c;return c=this.resources[b],c||(c=a.Resources[b]),c||(c=b),c},setResource:function(a,b){this.resources[a]=b},traverse:function(a){this.rootComponent.traverse(a)}}),b.ui={leaf:!1,disp:"Main",schema:{name:{type:"string",title:"Name"},author:{type:"string",title:"Author"},description:{type:"string",title:"Description"},clearFrame:{type:"boolean",title:"Clear every frame","default":!1,required:!0}}}}(Webvs),function(a){function b(a){this.id=a.id,this.enabled=_.isUndefined(a.enabled)?!0:a.enabled,this.componentInited=!1,this.options=a}a.Component=a.defineClass(b,Object,{componentName:"Component",init:function(a,b,c){this.gl=a,this.main=b,this.parent=c,this.componentInited=!0},adoptOrInit:function(a,b,c){return this.componentInited?this.adopt(c):this.init(a,b,c)},adopt:function(a){this.parent=a},update:function(){},destroy:function(){},getOptions:function(){return this.options},getPath:function(){return _.isUndefined(this.parent)||_.isUndefined(this.id)?this.componentName+"#Main":this.parent.getIdString()+"/"+this.componentName+"#"+this.id}})}(Webvs),function(a){function b(a,c){b.super.constructor.call(this,a),this.components=[],_.each(c||a.components||[],function(a){this.addComponent(this.id,a)},this)}a.Container=a.defineClass(b,a.Component,{init:function(a,c,d){b.super.init.call(this,a,c,d);for(var e=0;e0&&this._frameCounter--,0!==this._frameCounter))&&(this.code.beat=this.main.analyser.beat?1:0,this.code.enabled=1,this.code.clear=this.clearFrame,this._inited||(this._inited=!0,this.code.init()),this.code.perFrame(),0!==this.code.enabled)){if(this.fm.setRenderTarget(),(this.clearFrame||this.first||this.code.clear)&&(a.clearColor(0,0,0,1),a.clear(a.COLOR_BUFFER_BIT),this.first=!1),-1!==this.input){var c=this.parent.fm.getCurrentTexture();this.main.copier.run(this.fm,this.input,c)}for(var d=0;de;e++){var f=b.charAt(e);"\n"===f?(a.seenCR||a.line++,a.column=1,a.seenCR=!1):"\r"===f||"\u2028"===f||"\u2029"===f?(a.line++,a.column=1,a.seenCR=!0):(a.column++,a.seenCR=!1)}a.offset+=c}function f(a){F.offsetH.offset&&(H=d(F),I=[]),I.push(a))}function g(){var a="program@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,i,j,k,l,m,n,o,p,q,r;if(p=d(F),q=d(F),g=x(),null!==g)if(i=h(),null!==i)if(j=x(),null!==j){for(k=[],r=d(F),59===b.charCodeAt(F.offset)?(l=";",e(F,1)):(l=null,0===G&&f('";"')),null!==l?(m=x(),null!==m?(n=h(),null!==n?(o=x(),null!==o?l=[l,m,n,o]:(l=null,F=d(r))):(l=null,F=d(r))):(l=null,F=d(r))):(l=null,F=d(r));null!==l;)k.push(l),r=d(F),59===b.charCodeAt(F.offset)?(l=";",e(F,1)):(l=null,0===G&&f('";"')),null!==l?(m=x(),null!==m?(n=h(),null!==n?(o=x(),null!==o?l=[l,m,n,o]:(l=null,F=d(r))):(l=null,F=d(r))):(l=null,F=d(r))):(l=null,F=d(r));null!==k?(59===b.charCodeAt(F.offset)?(l=";",e(F,1)):(l=null,0===G&&f('";"')),l=null!==l?l:"",null!==l?(m=x(),null!==m?g=[g,i,j,k,l,m]:(g=null,F=d(q))):(g=null,F=d(q))):(g=null,F=d(q))}else g=null,F=d(q);else g=null,F=d(q);else g=null,F=d(q);return null!==g&&(g=function(a,b,c,d){var e=[d[1]];return e=e.concat(_.map(d[3],function(a){return a[2]})),new Webvs.AstProgram(e)}(p.offset,p.line,p.column,g)),null===g&&(F=d(p)),J[a]={nextPos:d(F),result:g},g}function h(){var a="statement@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k,l,n;return l=d(F),n=d(F),g=s(),null!==g?(h=x(),null!==h?(61===b.charCodeAt(F.offset)?(i="=",e(F,1)):(i=null,0===G&&f('"="')),null!==i?(j=x(),null!==j?(k=m(),null!==k?g=[g,h,i,j,k]:(g=null,F=d(n))):(g=null,F=d(n))):(g=null,F=d(n))):(g=null,F=d(n))):(g=null,F=d(n)),null!==g&&(g=function(a,b,c,d,e){return new Webvs.AstAssignment(d,e)}(l.offset,l.line,l.column,g[0],g[4])),null===g&&(F=d(l)),null===g&&(g=m()),J[a]={nextPos:d(F),result:g},g}function i(){var a="unary_ops@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return 43===b.charCodeAt(F.offset)?(g="+",e(F,1)):(g=null,0===G&&f('"+"')),null===g&&(45===b.charCodeAt(F.offset)?(g="-",e(F,1)):(g=null,0===G&&f('"-"'))),J[a]={nextPos:d(F),result:g},g}function j(){var a="additive_ops@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return 43===b.charCodeAt(F.offset)?(g="+",e(F,1)):(g=null,0===G&&f('"+"')),null===g&&(45===b.charCodeAt(F.offset)?(g="-",e(F,1)):(g=null,0===G&&f('"-"'))),J[a]={nextPos:d(F),result:g},g}function k(){var a="multiplicative_ops@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return 42===b.charCodeAt(F.offset)?(g="*",e(F,1)):(g=null,0===G&&f('"*"')),null===g&&(47===b.charCodeAt(F.offset)?(g="/",e(F,1)):(g=null,0===G&&f('"/"')),null===g&&(37===b.charCodeAt(F.offset)?(g="%",e(F,1)):(g=null,0===G&&f('"%"')))),J[a]={nextPos:d(F),result:g},g}function l(){var a="boolean_ops@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return 38===b.charCodeAt(F.offset)?(g="&",e(F,1)):(g=null,0===G&&f('"&"')),null===g&&(124===b.charCodeAt(F.offset)?(g="|",e(F,1)):(g=null,0===G&&f('"|"'))),J[a]={nextPos:d(F),result:g},g}function m(){var a="boolean_expr@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c,e,f,g,h,i,j,k,m;if(j=d(F),k=d(F),c=n(),null!==c){for(e=[],m=d(F),f=x(),null!==f?(g=l(),null!==g?(h=x(),null!==h?(i=n(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==f;)e.push(f),m=d(F),f=x(),null!==f?(g=l(),null!==g?(h=x(),null!==h?(i=n(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==e?c=[c,e]:(c=null,F=d(k))}else c=null,F=d(k);return null!==c&&(c=function(a,b,c,d,e){return C(d,e)}(j.offset,j.line,j.column,c[0],c[1])),null===c&&(F=d(j)),J[a]={nextPos:d(F),result:c},c}function n(){var a="additive_expr@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c,e,f,g,h,i,k,l,m;if(k=d(F),l=d(F),c=o(),null!==c){for(e=[],m=d(F),f=x(),null!==f?(g=j(),null!==g?(h=x(),null!==h?(i=o(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==f;)e.push(f),m=d(F),f=x(),null!==f?(g=j(),null!==g?(h=x(),null!==h?(i=o(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==e?c=[c,e]:(c=null,F=d(l))}else c=null,F=d(l);return null!==c&&(c=function(a,b,c,d,e){return C(d,e)}(k.offset,k.line,k.column,c[0],c[1])),null===c&&(F=d(k)),J[a]={nextPos:d(F),result:c},c}function o(){var a="multiplicative_expr@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c,e,f,g,h,i,j,l,m;if(j=d(F),l=d(F),c=p(),null!==c){for(e=[],m=d(F),f=x(),null!==f?(g=k(),null!==g?(h=x(),null!==h?(i=p(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==f;)e.push(f),m=d(F),f=x(),null!==f?(g=k(),null!==g?(h=x(),null!==h?(i=p(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==e?c=[c,e]:(c=null,F=d(l))}else c=null,F=d(l);return null!==c&&(c=function(a,b,c,d,e){return C(d,e)}(j.offset,j.line,j.column,c[0],c[1])),null===c&&(F=d(j)),J[a]={nextPos:d(F),result:c},c}function p(){var a="unary@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c,e,f,g,h;return g=d(F),h=d(F),c=i(),null!==c?(e=x(),null!==e?(f=q(),null!==f?c=[c,e,f]:(c=null,F=d(h))):(c=null,F=d(h))):(c=null,F=d(h)),null!==c&&(c=function(a,b,c,d,e){return new Webvs.AstUnaryExpr(d,e)}(g.offset,g.line,g.column,c[0],c[2])),null===c&&(F=d(g)),null===c&&(c=q()),J[a]={nextPos:d(F),result:c},c}function q(){var a="func_call@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k,l,n,o,p,q,s,t;if(p=d(F),q=d(F),s=d(F),/^[a-zA-Z_]/.test(b.charAt(F.offset))?(g=b.charAt(F.offset),e(F,1)):(g=null,0===G&&f("[a-zA-Z_]")),null!==g){for(h=[],/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==i;)h.push(i),/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==h?g=[g,h]:(g=null,F=d(s))}else g=null,F=d(s);if(null!==g)if(h=x(),null!==h)if(40===b.charCodeAt(F.offset)?(i="(",e(F,1)):(i=null,0===G&&f('"("')),null!==i){for(s=d(F),j=[],t=d(F),k=x(),null!==k?(l=m(),null!==l?(n=x(),null!==n?(44===b.charCodeAt(F.offset)?(o=",",e(F,1)):(o=null,0===G&&f('","')),null!==o?k=[k,l,n,o]:(k=null,F=d(t))):(k=null,F=d(t))):(k=null,F=d(t))):(k=null,F=d(t));null!==k;)j.push(k),t=d(F),k=x(),null!==k?(l=m(),null!==l?(n=x(),null!==n?(44===b.charCodeAt(F.offset)?(o=",",e(F,1)):(o=null,0===G&&f('","')),null!==o?k=[k,l,n,o]:(k=null,F=d(t))):(k=null,F=d(t))):(k=null,F=d(t))):(k=null,F=d(t));null!==j?(k=x(),null!==k?(l=m(),null!==l?j=[j,k,l]:(j=null,F=d(s))):(j=null,F=d(s))):(j=null,F=d(s)),j=null!==j?j:"",null!==j?(k=x(),null!==k?(41===b.charCodeAt(F.offset)?(l=")",e(F,1)):(l=null,0===G&&f('")"')),null!==l?g=[g,h,i,j,k,l]:(g=null,F=d(q))):(g=null,F=d(q))):(g=null,F=d(q))}else g=null,F=d(q);else g=null,F=d(q);else g=null,F=d(q);return null!==g&&(g=function(a,b,c,d,e){var f=[];return _.each(e[0],function(a){f.push(a[1])}),f.push(e[2]),new Webvs.AstFuncCall(D(d),f)}(p.offset,p.line,p.column,g[0],g[3])),null===g&&(F=d(p)),null===g&&(g=r()),J[a]={nextPos:d(F),result:g},g}function r(){var a="primary_expr@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k;return g=w(),null===g&&(g=u(),null===g&&(g=v(),null===g&&(g=t(),null===g&&(j=d(F),k=d(F),40===b.charCodeAt(F.offset)?(g="(",e(F,1)):(g=null,0===G&&f('"("')),null!==g?(h=m(),null!==h?(41===b.charCodeAt(F.offset)?(i=")",e(F,1)):(i=null,0===G&&f('")"')),null!==i?g=[g,h,i]:(g=null,F=d(k))):(g=null,F=d(k))):(g=null,F=d(k)),null!==g&&(g=function(a,b,c,d){return d}(j.offset,j.line,j.column,g[1])),null===g&&(F=d(j)))))),J[a]={nextPos:d(F),result:g},g}function s(){var a="assignable@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c;return c=v(),null===c&&(c=t()),J[a]={nextPos:d(F),result:c},c}function t(){var a="identifier@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k;if(j=d(F),k=d(F),/^[a-zA-Z_]/.test(b.charAt(F.offset))?(g=b.charAt(F.offset),e(F,1)):(g=null,0===G&&f("[a-zA-Z_]")),null!==g){for(h=[],/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==i;)h.push(i),/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==h?g=[g,h]:(g=null,F=d(k))}else g=null,F=d(k);return null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr(D(d).toLowerCase(),"ID")}(j.offset,j.line,j.column,g)),null===g&&(F=d(j)),J[a]={nextPos:d(F),result:g},g}function u(){var a="constant@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k;if(j=d(F),k=d(F),36===b.charCodeAt(F.offset)?(g="$",e(F,1)):(g=null,0===G&&f('"$"')),null!==g){for(h=[],/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==i;)h.push(i),/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==h?g=[g,h]:(g=null,F=d(k))}else g=null,F=d(k);return null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr(D(d).toLowerCase(),"CONST")}(j.offset,j.line,j.column,g[1])),null===g&&(F=d(j)),J[a]={nextPos:d(F),result:g},g}function v(){var a="register@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k,l,m;if(l=d(F),m=d(F),64===b.charCodeAt(F.offset)?(g="@",e(F,1)):(g=null,0===G&&f('"@"')),null!==g){for(h=[],/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==i;)h.push(i),/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==h?g=[g,h]:(g=null,F=d(m))}else g=null,F=d(m);return null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr("__REG_AT_"+D(d).toLowerCase(),"REG")}(l.offset,l.line,l.column,g[1])),null===g&&(F=d(l)),null===g&&(l=d(F),m=d(F),/^[rR]/.test(b.charAt(F.offset))?(g=b.charAt(F.offset),e(F,1)):(g=null,0===G&&f("[rR]")),null!==g?(/^[eE]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[eE]")),null!==h?(/^[gG]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[gG]")),null!==i?(/^[0-9]/.test(b.charAt(F.offset))?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("[0-9]")),null!==j?(/^[0-9]/.test(b.charAt(F.offset))?(k=b.charAt(F.offset),e(F,1)):(k=null,0===G&&f("[0-9]")),null!==k?g=[g,h,i,j,k]:(g=null,F=d(m))):(g=null,F=d(m))):(g=null,F=d(m))):(g=null,F=d(m))):(g=null,F=d(m)),null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr("__REG_"+D(d).toLowerCase(),"REG")}(l.offset,l.line,l.column,g)),null===g&&(F=d(l))),J[a]={nextPos:d(F),result:g},g}function w(){var a="value@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k,l,m,n,o;for(m=d(F),n=d(F),g=[],/^[0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[0-9]"));null!==h;)g.push(h),/^[0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[0-9]"));if(null!==g)if(46===b.charCodeAt(F.offset)?(h=".",e(F,1)):(h=null,0===G&&f('"."')),null!==h){if(/^[0-9]/.test(b.charAt(F.offset))?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("[0-9]")),null!==j)for(i=[];null!==j;)i.push(j),/^[0-9]/.test(b.charAt(F.offset))?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("[0-9]"));else i=null;if(null!==i){if(o=d(F),/^[Ee]/.test(b.charAt(F.offset))?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("[Ee]")),null!==j){if(/^[0-9]/.test(b.charAt(F.offset))?(l=b.charAt(F.offset),e(F,1)):(l=null,0===G&&f("[0-9]")),null!==l)for(k=[];null!==l;)k.push(l),/^[0-9]/.test(b.charAt(F.offset))?(l=b.charAt(F.offset),e(F,1)):(l=null,0===G&&f("[0-9]"));else k=null;null!==k?j=[j,k]:(j=null,F=d(o))}else j=null,F=d(o);j=null!==j?j:"",null!==j?g=[g,h,i,j]:(g=null,F=d(n))}else g=null,F=d(n)}else g=null,F=d(n);else g=null,F=d(n);if(null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr(parseFloat(D(d)),"VALUE")}(m.offset,m.line,m.column,g)),null===g&&(F=d(m)),null===g){if(m=d(F),n=d(F),/^[a-fA-F0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[a-fA-F0-9]")),null!==h)for(g=[];null!==h;)g.push(h),/^[a-fA-F0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[a-fA-F0-9]"));else g=null;if(null!==g?(/^[hH]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[hH]")),null!==h?g=[g,h]:(g=null,F=d(n))):(g=null,F=d(n)),null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr(parseInt(D(d),16),"VALUE")}(m.offset,m.line,m.column,g[0])),null===g&&(F=d(m)),null===g){if(m=d(F),n=d(F),/^[0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[0-9]")),null!==h)for(g=[];null!==h;)g.push(h),/^[0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[0-9]"));else g=null;null!==g?(/^[dD]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[dD]")),h=null!==h?h:"",null!==h?g=[g,h]:(g=null,F=d(n))):(g=null,F=d(n)),null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr(parseInt(D(d),10),"VALUE")}(m.offset,m.line,m.column,g[0])),null===g&&(F=d(m))}}return J[a]={nextPos:d(F),result:g},g}function x(){var a="__@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c,e;for(c=[],e=y(),null===e&&(e=z(),null===e&&(e=A()));null!==e;)c.push(e),e=y(),null===e&&(e=z(),null===e&&(e=A()));return J[a]={nextPos:d(F),result:c},c}function y(){var a="whiteSpace@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return/^[\t\x0B\f \xA0\uFEFF]/.test(b.charAt(F.offset))?(g=b.charAt(F.offset),e(F,1)):(g=null,0===G&&f("[\\t\\x0B\\f \\xA0\\uFEFF]")),J[a]={nextPos:d(F),result:g},g}function z(){var a="lineEnd@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return/^[\n\r\u2028\u2029]/.test(b.charAt(F.offset))?(g=b.charAt(F.offset),e(F,1)):(g=null,0===G&&f("[\\n\\r\\u2028\\u2029]")),J[a]={nextPos:d(F),result:g},g}function A(){var a="comment@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k,l,m;if(k=d(F),"/*"===b.substr(F.offset,2)?(g="/*",e(F,2)):(g=null,0===G&&f('"/*"')),null!==g){for(h=[],l=d(F),m=d(F),G++,"*/"===b.substr(F.offset,2)?(i="*/",e(F,2)):(i=null,0===G&&f('"*/"')),G--,null===i?i="":(i=null,F=d(m)),null!==i?(b.length>F.offset?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("any character")),null!==j?i=[i,j]:(i=null,F=d(l))):(i=null,F=d(l));null!==i;)h.push(i),l=d(F),m=d(F),G++,"*/"===b.substr(F.offset,2)?(i="*/",e(F,2)):(i=null,0===G&&f('"*/"')),G--,null===i?i="":(i=null,F=d(m)),null!==i?(b.length>F.offset?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("any character")),null!==j?i=[i,j]:(i=null,F=d(l))):(i=null,F=d(l));null!==h?("*/"===b.substr(F.offset,2)?(i="*/",e(F,2)):(i=null,0===G&&f('"*/"')),null!==i?g=[g,h,i]:(g=null,F=d(k))):(g=null,F=d(k))}else g=null,F=d(k);if(null===g)if(k=d(F),"//"===b.substr(F.offset,2)?(g="//",e(F,2)):(g=null,0===G&&f('"//"')),null!==g){for(h=[],l=d(F),m=d(F),G++,i=z(),G--,null===i?i="":(i=null,F=d(m)),null!==i?(b.length>F.offset?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("any character")),null!==j?i=[i,j]:(i=null,F=d(l))):(i=null,F=d(l));null!==i;)h.push(i),l=d(F),m=d(F),G++,i=z(),G--,null===i?i="":(i=null,F=d(m)),null!==i?(b.length>F.offset?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("any character")),null!==j?i=[i,j]:(i=null,F=d(l))):(i=null,F=d(l));null!==h?g=[g,h]:(g=null,F=d(k))}else g=null,F=d(k);return J[a]={nextPos:d(F),result:g},g}function B(a){a.sort();for(var b=null,c=[],d=0;dH.offset?F:H;throw new this.SyntaxError(B(I),M,L,N.line,N.column)}return K},toSource:function(){return this._source}};return b.SyntaxError=function(b,c,d,e,f){function g(b,c){var d,e;switch(b.length){case 0:d="end of input";break;case 1:d=b[0];break;default:d=b.slice(0,b.length-1).join(", ")+" or "+b[b.length-1]}return e=c?a(c):"end of input","Expected "+d+" but "+e+" found."}this.name="SyntaxError",this.expected=b,this.found=c,this.message=g(b,c),this.offset=d,this.line=e,this.column=f},b.SyntaxError.prototype=Error.prototype,b}(),function(a){function b(){}a.CodeInstance=a.defineClass(b,Object,{rand:function(a){return Math.floor(Math.random()*a)+1},gettime:function(a){switch(a){case 0:var b=(new Date).getTime();return(b-this._bootTime)/1e3;default:throw new Error("Invalid startTime mode for gettime call")}},getosc:function(a,b){for(var c=this._analyser.getWaveform(),d=Math.floor((a-b/2)*(c.length-1)),e=Math.floor((a+b/2)*(c.length-1)),f=0,g=d;e>=g;g++)f+=c[g];return f/(e-d+1)},bindUniforms:function(a){var b=this,c=_.difference(_.keys(this),this._treatAsNonUniform);if(_.each(c,function(c){var d=b[c];"number"==typeof d&&a.setUniform(c,"1f",d)}),_.each(this._registerUsages,function(b){a.setUniform(b,"1f",this._registerBank[b])}),this.hasRandom){var d=[Math.random()/100,Math.random()/100];a.setUniform("__randStep","2fv",d)}if(this.hasGettime){var e=((new Date).getTime()-this._bootTime)/1e3;a.setUniform("__gettime0","1f",e)}_.each(this._preCompute,function(b){var c=_.map(_.last(b,b.length-2),function(a){return _.isString(a)?"__REG"==a.substring(0,5)?this._registerBank[a]:this[a]:a}),d=this[b[0]].apply(this,c);a.setUniform(b[1],"1f",d)})},setup:function(a){this._registerBank=a.registerBank,this._bootTime=a.bootTime,this._analyser=a.analyser,this.w=a.canvas.width,this.h=a.canvas.height,_.each(this._registerUsages,function(b){_.has(a.registerBank,b)||(a.registerBank[b]=0)})}}),b.clone=function(a,b){a.cid=0;var c=[a];return b>1&&_.times(b-1,function(b){var d=_.clone(a);d.cid=b+1,c.push(d)}),c}}(Webvs),function(a){function b(a,b){this.codeSrc={};for(var c in a){var d=a[c];_.isArray(d)&&(d=d.join("\n")),d=d.trim(),""!==d&&(this.codeSrc[c]=d)}this.externalVars=_.union(b||[],["w","h","cid"]),this._parseSrc()}a.ExprCodeGenerator=a.defineClass(b,Object,{_parseSrc:function(){var b={},c=[],d={},e=[];for(var f in this.codeSrc)try{var g=this.codeSrc[f];b[f]=a.PegExprParser.parse(g);var h=[];this._getVars(b[f],c,h,e),d[f]=h}catch(i){throw new Error("Error parsing "+f+"("+i.line+":"+i.column+")"+" : "+i)}this.codeAst=b,this.funcUsages=d,this.instanceVars=_.uniq(this.externalVars.concat(c)),this.registerUsages=_.uniq(e)},generateJs:function(b){var c=new a.CodeInstance;_.each(this.instanceVars,function(a){c[a]=0});var d=_.intersection(_.keys(this.codeAst),b),e=_.difference(b,d);return _.each(d,function(a){var b=this.codeAst[a],d=this._generateJs(b);c[a]=new Function(d)},this),_.each(e,function(b){c[b]=a.noop}),c._registerUsages=this.registerUsages,c},generateGlsl:function(a,b,c){var d=[];b=b||[],_.each(this.instanceVars,function(a){var c="";_.contains(b,a)||(c="uniform "),d.push(c+"float "+a+";")});var e=_.intersection(_.keys(this.codeAst),a),f=_.difference(a,e),g=_.uniq(_.flatMap(e,function(a){return this.funcUsages[a]},this));_.each(g,function(a){var b=this.glslFuncCode[a];b&&d.push(b)},this);var h=[],i=[];return _.each(e,function(a){var b=this.codeAst[a],c=this._generateGlsl(b,h);i.push("void "+a+"() {"),i.push(c),i.push("}")},this),d=d.concat(_.map(h,function(a){return"uniform float "+a[1]+";"})),d=d.concat(i),_.each(f,function(a){d.push("void "+a+"() {}")}),c._preCompute=h,_.contains(e,"rand")&&(c.hasRandom=!0),_.contains(e,"gettime")&&(c.hasGettime=!0),c._treatAsNonUniform=b,d.join("\n")},funcArgLengths:{above:2,below:2,equal:2,pow:2,sqr:1,sqrt:1,invsqrt:1,floor:1,ceil:1,abs:1,"if":3,min:2,max:2,sin:1,cos:1,tan:1,asin:1,acos:1,atan:1,atan2:2,log:1,band:2,bor:2,bnot:1,rand:1,gettime:1,getosc:3,select:{min:2}},jsMathFuncs:["min","max","sin","cos","abs","tan","asin","acos","atan","log","pow","sqrt","floor","ceil"],glslFuncCode:{rand:["uniform vec2 __randStep;","vec2 __randSeed;","float rand(float max) {"," __randCur += __randStep;"," float val = fract(sin(dot(__randSeed.xy ,vec2(12.9898,78.233))) * 43758.5453);"," return (floor(val*max)+1);","}"].join("\n"),gettime:["uniform float __gettime0;","int gettime(int startTime) {"," int time = 0;"," if(startTime == 0) {"," time = __gettime0;"," }"," return time;","}"].join("\n")},_checkFunc:function(a){var b=this.funcArgLengths[a.funcName];if(void 0===b)throw Error("Unknown function "+a.funcName);if(_.isNumber(b)){if(a.args.length!=b)throw Error(a.funcName+" accepts "+b+" arguments")}else if(b.min&&a.args.length",this._generateGlsl(b.args[1],c),"?1.0:0.0)"].join("");case"below":return["(",this._generateGlsl(b.args[0],c),"<",this._generateGlsl(b.args[1],c),"?1.0:0.0)"].join("");case"equal":return["(",this._generateGlsl(b.args[0],c),"==",this._generateGlsl(b.args[1],c),"?1.0:0.0)"].join("");case"if":return["(",this._generateGlsl(b.args[0],c),"!=0.0?",this._generateGlsl(b.args[1],c),":",this._generateGlsl(b.args[2],c),")"].join("");case"select":var d=this._generateGlsl(b.args[0],c),e=this,f=function(a,b){return 1==a.length?e._generateGlsl(a[0],c):["(("+d+" === "+b+")?","("+e._generateGlsl(a[0],c)+"):","("+f(_.last(a,a.length-1),b+1)+"))"].join("")};return f(_.last(b.args,b.args.length-1),0);case"sqr":return"(pow(("+this._generateGlsl(b.args[0],c)+"), 2))";case"band":return"(float(("+this._generateGlsl(b.args[0],c)+")&&("+this._generateGlsl(b.args[1],c)+")))";case"bor":return"(float(("+this._generateGlsl(b.args[0],c)+")||("+this._generateGlsl(b.args[1],c)+")))";case"bnot":return"(float(!("+this._generateGlsl(b.args[0],c)+")))";case"invsqrt":return"(1/sqrt("+this._generateGlsl(b.args[0],c)+"))";case"atan2":return"(atan(("+this._generateGlsl(b.args[0],c)+"),("+this._generateGlsl(b.args[1],c)+"))";case"getosc":var g=_.every(b.args,function(b){return b instanceof a.AstPrimaryExpr});if(!g)throw new Error("Non Pre-Computable arguments for getosc in shader code, use variables or constants");var h="__PC_"+b.funcName+"_"+j,i=[b.funcName,h].concat(_.map(b.args,function(a){return a.value})),j=_.indexOf(c,i);return-1==j&&(c.push(i),j=c.length-1),h;default:var k=_.map(b.args,function(a){return this._generateGlsl(a,c)},this).join(","),l=b.funcName;return _.contains(this.varArgFuncs,b.funcName)&&(l+=b.args.length),"("+l+"("+k+"))"}if(b instanceof a.AstAssignment)return this._generateGlsl(b.lhs,c)+"="+this._generateGlsl(b.expr,c);if(b instanceof a.AstProgram){var m=_.map(b.statements,function(a){return this._generateGlsl(a,c)},this);return m.join(";\n")+";"}return b instanceof a.AstPrimaryExpr&&"VALUE"===b.type?a.glslFloatRepr(b.value):b instanceof a.AstPrimaryExpr&&"CONST"===b.type?this._translateConstants(b.value).toString():b instanceof a.AstPrimaryExpr&&("ID"===b.type||"REG"===b.type)?b.value:void 0},_generateJs:function(b){var c;if(b instanceof a.AstBinaryExpr)return"("+this._generateJs(b.leftOperand)+b.operator+this._generateJs(b.rightOperand)+")";if(b instanceof a.AstUnaryExpr)return"("+b.operator+this._generateJs(b.operand)+")";if(b instanceof a.AstFuncCall)switch(this._checkFunc(b),b.funcName){case"above":return["(",this._generateJs(b.args[0]),">",this._generateJs(b.args[1]),"?1:0)"].join("");case"below":return["(",this._generateJs(b.args[0]),"<",this._generateJs(b.args[1]),"?1:0)"].join("");case"equal":return["(",this._generateJs(b.args[0]),"==",this._generateJs(b.args[1]),"?1:0)"].join("");case"if":return["(",this._generateJs(b.args[0]),"!==0?",this._generateJs(b.args[1]),":",this._generateJs(b.args[2]),")"].join("");case"select":var d=["((function() {"];return d.push("switch("+this._generateJs(b.args[0])+") {"),_.each(_.last(b.args,b.args.length-1),function(a,b){d.push("case "+b+": return "+this._generateJs(a)+";")},this),d.push("default : throw new Error('Unknown selector value in select');"),d.push("}}).call(this))"),d.join("");case"sqr":return"(Math.pow(("+this._generateJs(b.args[0])+"),2))";case"band":return"((("+this._generateJs(b.args[0])+")&&("+this._generateJs(b.args[1])+"))?1:0)";case"bor":return"((("+this._generateJs(b.args[0])+")||("+this._generateJs(b.args[1])+"))?1:0)";case"bnot":return"((!("+this._generateJs(b.args[0])+"))?1:0)";case"invsqrt":return"(1/Math.sqrt("+this._generateJs(b.args[0])+"))";case"atan2":return"(Math.atan(("+this._generateJs(b.args[0])+")/("+this._generateJs(b.args[1])+")))";default:var e=_.map(b.args,function(a){return this._generateJs(a)},this).join(",");return c=_.contains(this.jsMathFuncs,b.funcName)?"Math.":"this.","("+c+b.funcName+"("+e+"))"}if(b instanceof a.AstAssignment)return this._generateJs(b.lhs)+"="+this._generateJs(b.expr);if(b instanceof a.AstProgram){var f=_.map(b.statements,function(a){return this._generateJs(a)},this);return f.join(";\n")}return b instanceof a.AstPrimaryExpr&&"VALUE"===b.type?b.value.toString():b instanceof a.AstPrimaryExpr&&"CONST"===b.type?this._translateConstants(b.value).toString():b instanceof a.AstPrimaryExpr&&"ID"===b.type?"this."+b.value:b instanceof a.AstPrimaryExpr&&"REG"===b.type?'this._registerBank["'+b.value+'"]':void 0},_getVars:function(b,c,d,e){b instanceof a.AstBinaryExpr?(this._getVars(b.leftOperand,c,d,e),this._getVars(b.rightOperand,c,d,e)):b instanceof a.AstUnaryExpr?this._getVars(b.operand,c,d,e):b instanceof a.AstFuncCall?(d.push(b.funcName),_.each(b.args,function(a){this._getVars(a,c,d,e)},this)):b instanceof a.AstAssignment?(this._getVars(b.lhs,c,d,e),this._getVars(b.expr,c,d,e)):b instanceof a.AstProgram?_.each(b.statements,function(a){this._getVars(a,c,d,e)},this):b instanceof a.AstPrimaryExpr&&"ID"===b.type?c.push(b.value):b instanceof a.AstPrimaryExpr&&"REG"===b.type&&e.push(b.value) -},_translateConstants:function(a){switch(a){case"pi":return Math.PI;case"e":return Math.E;case"phi":return 1.6180339887;default:throw new Error("Unknown constant "+a)}}})}(Webvs),function(a){function b(c){a.checkRequiredOptions(c,["code"]);var d=new a.ExprCodeGenerator(c.code,["b"]);this.code=d.generateJs(["init","onBeat","perFrame"]),this.inited=!1,b.super.constructor.apply(this,arguments)}a.GlobalVar=a.defineClass(b,a.Component,{init:function(a,c,d){b.super.init.call(this,a,c,d),this.code.setup(c,this)},update:function(){var a=this.code;a.b=this.main.analyser.beat?1:0,this.inited||(a.init(),this.inited=!0),this.main.analyser.beat&&a.onBeat(),a.perFrame()}}),b.ui={disp:"Global Var",type:"GlobalVar",schema:{code:{type:"object",title:"Code","default":{},properties:{init:{type:"string",title:"Init"},onBeat:{type:"string",title:"On Beat"},perFrame:{type:"string",title:"Per Frame"}}}}}}(Webvs),function(a){function b(c){if(c=_.defaults(c,{action:"SAVE",bufferId:1,blendMode:"REPLACE"}),this.blendMode=a.blendModes[c.blendMode],this.action=this.actions[c.action],!this.action)throw new Error("Unknown BufferSave action "+c.action);this.action==this.actions.SAVERESTORE?this._nextAction=this.actions.SAVE:this.action==this.actions.RESTORESAVE&&(this._nextAction=this.actions.RESTORE),this._bufferId="__BUFFERSAVE_"+c.bufferId,b.super.constructor.apply(this,arguments)}a.BufferSave=a.defineClass(b,a.Component,{actions:{SAVE:1,RESTORE:2,SAVERESTORE:3,RESTORESAVE:4},init:function(c,d,e){if(b.super.init.call(this,c,d,e),!d.registerBank[this._bufferId]){var f=new a.FrameBufferManager(d.canvas.width,d.canvas.height,c,d.copier,!0,1);d.registerBank[this._bufferId]=f}},update:function(){this.gl;var a,b=this.main.registerBank[this._bufferId];switch(this.action==this.actions.SAVERESTORE||this.action==this.RESTORESAVE?(a=this._nextAction,this._nextAction=this._nextAction==this.actions.SAVE?this.actions.RESTORE:this.actions.SAVE):a=this.action,a){case this.actions.SAVE:b.setRenderTarget(),this.main.copier.run(null,null,this.parent.fm.getCurrentTexture()),b.restoreRenderTarget();break;case this.actions.RESTORE:this.main.copier.run(this.parent.fm,this.blendMode,b.getCurrentTexture())}},destroy:function(){b.super.destroy.call(this),this.main.registerBank[this._bufferId].destroy()}}),b.ui={disp:"Buffer Save",type:"BufferSave",schema:{action:{type:"string",title:"Buffer save action","enum":["SAVE","RESTORE","SAVERESTORE","RESTORESAVE"]},bufferId:{type:"number",title:"Buffer Id","enum":[1,2,3,4,5,6,7,8]},blendMode:{type:"string",title:"Blend mode","enum":_.keys(a.blendModes)}}}}(Webvs),function(a){function b(c){c=_.defaults(c,{speed:1,color:"#000000"}),this.color=a.parseColorNorm(c.color),this.frameCount=0,this.maxFrameCount=Math.floor(1/c.speed),this.program=new a.ClearScreenProgram(a.AVERAGE),b.super.constructor.apply(this,arguments)}a.FadeOut=a.defineClass(b,a.Component,{componentName:"FadeOut",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){this.gl,this.frameCount++,this.frameCount==this.maxFrameCount&&(this.frameCount=0,this.program.run(this.parent.fm,null,this.color))},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}}),b.ui={type:"FadeOut",disp:"Fade Out",schema:{speed:{type:"number",title:"Speed",maximum:0,minimum:1,"default":1},color:{type:"string",title:"Fadeout color",format:"color","default":"#FFFFFF"}},form:[{key:"speed",type:"range",step:"0.05"},"color"]}}(Webvs),function(a){function b(c){a.checkRequiredOptions(c,["kernel"]),c=_.defaults(c,{edgeMode:"EXTEND",bias:0});var d;if(c.kernel in b.kernels)d=b.kernels[c.kernel];else{if(!_.isArray(c.kernel)||1!==c.kernel.length%2)throw new Error("Invalid convolution kernel");d=c.kernel}var e=Math.floor(Math.sqrt(d.length));if(e*e!=d.length)throw new Error("Invalid convolution kernel");this.program=new a.ConvolutionProgram(d,e,c.edgeMode,c.scale,c.bias),b.super.constructor.apply(this,arguments)}function c(b,d,e,f,g){var h="";switch(e){case"WRAP":h="pos = vec2(pos.x<0?pos.x+1.0:pos.x%1, pos.y<0?pos.y+1.0:pos.y%1);";break;case"EXTEND":h="pos = clamp(pos, vec2(0,0), vec2(1,1));";break;default:throw new Error("Invalid edge mode")}var i,j,k=[],l=Math.floor(d/2);for(i=0;d>i;i++)for(j=0;d>j;j++){var m=b[i*d+j];0!==m&&(k.push("pos = v_position + texel * vec2("+(i-l)+","+(j-l)+");"),k.push(h),k.push("colorSum += texture2D(u_srcTexture, pos) * "+a.glslFloatRepr(m)+";"))}_.isUndefined(f)&&(f=_.reduce(b,function(a,b){return a+b},0)),c.super.constructor.call(this,{swapFrame:!0,fragmentShader:["void main() {"," vec2 texel = 1.0/(u_resolution-vec2(1,1));"," vec2 pos;"," vec4 colorSum = vec4(0,0,0,0);",k.join("\n")," setFragColor(vec4(((colorSum+"+a.glslFloatRepr(g)+") / "+a.glslFloatRepr(f)+").rgb, 1.0));","}"]})}a.Convolution=a.defineClass(b,a.Component,{componentName:"Convolution",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){this.program.run(this.parent.fm,null)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}}),b.kernels={normal:[0,0,0,0,1,0,0,0,0],gaussianBlur:[.045,.122,.045,.122,.332,.122,.045,.122,.045],unsharpen:[-1,-1,-1,-1,9,-1,-1,-1,-1],emboss:[-2,-1,0,-1,1,1,0,1,2],blur:[1,1,1,1,1,1,1,1,1]},a.ConvolutionProgram=a.defineClass(c,a.QuadBoxProgram)}(Webvs),function(a){function b(c){if(a.checkRequiredOptions(c,["maps"]),c=_.defaults(c,{key:"RED",output:"REPLACE",mapCycleMode:"SINGLE"}),this.maps=c.maps,this.currentMap=0,this.mapCycleMode=this.mapCycleModes[c.mapCycleMode],!this.mapCycleMode)throw new Error("Unknown mapCycleMode "+c.mapCycleMode);this.program=new a.ColorMapProgram(c.key,a.getBlendMode(c.output)),b.super.constructor.apply(this,arguments)}function c(a,b){var d="";switch(a){case"RED":d="srcColor.r";break;case"GREEN":d="srcColor.g";break;case"BLUE":d="srcColor.b";break;case"(R+G+B)/2":d="mod((srcColor.r+srcColor.g+srcColor.b)/2.0, 1.0)";break;case"(R+G+B)/3":d="(srcColor.r+srcColor.g+srcColor.b)/3.0";break;case"MAX":d="max(srcColor.r, max(srcColor.g, srcColor.b))";break;default:throw new Error("Unknown colormap key function "+options.key)}c.super.constructor.call(this,{outputBlendMode:b,swapFrame:!0,fragmentShader:["uniform sampler2D u_colorMap;","void main() {"," vec4 srcColor = getSrcColor();"," setFragColor(texture2D(u_colorMap, vec2(("+d+"), 0)));","}"]})}a.ColorMap=a.defineClass(b,a.Component,{mapCycleModes:{SINGLE:1,ONBEATRANDOM:2,ONBEATSEQUENTIAL:3},init:function(a,c,d){b.super.init.call(this,a,c,d),this.colorMaps=_.map(this.maps,function(a){return this._buildColorMap(a)},this),this.currentMap=0,this.program.init(a)},update:function(){if(this.main.analyser.beat)switch(this.mapCycleMode){case this.mapCycleModes.ONBEATRANDOM:this.currentMap=Math.floor(Math.random()*this.colorMaps.length);break;case this.mapCycleModes.ONBEATSEQUENTIAL:this.currentMap=(this.currentMap+1)%this.colorMaps.length}this.program.run(this.parent.fm,null,this.colorMaps[this.currentMap])},destroy:function(){b.super.destroy.call(this),this.program.cleanup()},_buildColorMap:function(b){var c=this.gl;b=_.sortBy(b,function(a){return a.index});var d=_.map(b,function(a){return a.index});if(_.uniq(d).length!=d.length)throw new Error("map cannot have repeated indices");b=_.map(b,function(b){var c=a.parseColor(b.color);return{color:c,index:b.index}});var e=_.first(b);0!==e.index&&b.splice(0,0,{color:e.color,index:0});var f=_.last(b);255!==f.index&&b.push({color:f.color,index:255});var g=new Uint8Array(768),h=0,i=_.zip(_.first(b,b.length-1),_.last(b,b.length-1));_.each(i,function(a){var b=a[0],c=a[1],d=c.index-b.index;_.times(d,function(a){g[h++]=Math.floor((b.color[0]*(255-a)+c.color[0]*a)/255),g[h++]=Math.floor((b.color[1]*(255-a)+c.color[1]*a)/255),g[h++]=Math.floor((b.color[2]*(255-a)+c.color[2]*a)/255)})}),g[h++]=f.color[0],g[h++]=f.color[1],g[h++]=f.color[2];var j=c.createTexture();return c.bindTexture(c.TEXTURE_2D,j),c.texImage2D(c.TEXTURE_2D,0,c.RGB,256,1,0,c.RGB,c.UNSIGNED_BYTE,g),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MAG_FILTER,c.NEAREST),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MIN_FILTER,c.NEAREST),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_S,c.CLAMP_TO_EDGE),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_T,c.CLAMP_TO_EDGE),j}}),a.ColorMapProgram=a.defineClass(c,a.QuadBoxProgram,{draw:function(a){this.setUniform("u_colorMap","texture2D",a),c.super.draw.call(this)}}),b.ui={disp:"Color Map",type:"ColorMap",schema:{maps:{type:"array",items:{type:"array",title:"Map",items:{type:"object",properties:{color:{type:"string",title:"Color",format:"color","default":"#FFFFFF"},index:{type:"number",title:"Index",minimum:0,maximum:255}}}}},key:{type:"string",title:"Map key","enum":["RED","GREEN","BLUE","(R+G+B)/2","(R+G+B)/3","MAX"],"default":"RED"},mapCycleMode:{type:"string",title:"Map Cycle Mode","enum":["SINGLE","ONBEATRANDOM","ONBEATSEQUENTIAL"],"default":"SINGLE"},output:{type:"string",title:"Output blend mode","enum":_.keys(a.blendModes),"default":"REPLACE"}}}}(Webvs),function(a){function b(c){if(a.checkRequiredOptions(c,["mode","color","outColor"]),c=_.defaults(c,{mode:"BELOW",color:"#202020",outColor:"#202020",level:0}),this.mode=_.indexOf(this.modes,c.mode),-1==this.mode)throw new Error("ColorClip: invalid mode");this.color=a.parseColorNorm(c.color),this.outColor=a.parseColorNorm(c.outColor),this.level=c.level,this.program=new a.ColorClipProgram,b.super.constructor.apply(this,arguments)}function c(){c.super.constructor({swapFrame:!0,fragmentShader:["uniform int u_mode;","uniform vec3 u_color;","uniform vec3 u_outColor;","uniform float u_level;","void main() {"," vec4 inColor4 = getSrcColor();"," vec3 inColor = inColor4.rgb;"," bool clip = false;"," if(u_mode == 0) {"," clip = all(lessThanEqual(inColor, u_color));"," }"," if(u_mode == 1) {"," clip = all(greaterThanEqual(inColor, u_color));"," }"," if(u_mode == 2) {"," clip = (distance(inColor, u_color) <= u_level*0.5);"," }"," if(clip) {"," setFragColor(vec4(u_outColor, inColor4.a));"," } else {"," setFragColor(inColor4);"," }","}"]})}a.ColorClip=a.defineClass(b,a.Component,{modes:["BELOW","ABOVE","NEAR"],componentName:"ChannelShift",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){this.program.run(this.parent.fm,null,this.mode,this.color,this.outColor,this.level)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}}),a.ColorClipProgram=a.defineClass(c,a.QuadBoxProgram,{draw:function(a,b,d,e){this.setUniform("u_mode","1i",a),this.setUniform.apply(this,["u_color","3f"].concat(b)),this.setUniform.apply(this,["u_outColor","3f"].concat(d)),this.setUniform("u_level","1f",e),c.super.draw.call(this)}})}(Webvs),function(a){function b(c){a.checkRequiredOptions(c,["code"]),c=_.defaults(c,{gridW:16,gridH:16,noGrid:!1,bFilter:!0,compat:!1,coord:"POLAR"});var d;if(!_.isObject(c.code))throw new Error("Invalid Dynamic movement code");d=c.code;var e=new a.ExprCodeGenerator(d,["x","y","r","d","b"]);this.code=e.generateJs(["init","onBeat","perFrame"]);var f=e.generateGlsl(["perPixel"],["x","y","d","r"],this.code);this.inited=!1,this.noGrid=c.noGrid,this.gridW=c.gridW,this.gridH=c.gridH,this.coordMode=c.coord,this.bFilter=c.bFilter,this.compat=c.compat,this.program=this.noGrid?new a.DMovProgramNG(this.coordMode,this.bFilter,this.compat,this.code.hasRandom,f):new a.DMovProgram(this.coordMode,this.bFilter,this.compat,this.code.hasRandom,f),b.super.constructor.apply(this,arguments)}function c(a,b,d,e,f){var g=[f,this.glslFilter(b,d),"void main() {",e?"__randSeed = v_position;":""," x = v_position.x*2.0-1.0;"," y = -(v_position.y*2.0-1.0);",this.glslRectToPolar(a)," perPixel();",this.glslPolarToRect(a)," setFragColor(vec4(filter(vec2(x, -y)), 1));","}"];c.super.constructor.call(this,{fragmentShader:g,swapFrame:!0})}function d(a,b,c,e,f){var g=["attribute vec2 a_position;","varying vec2 v_newPoint;","uniform int u_coordMode;",f,"void main() {",e?"__randSeed = a_position;":""," x = a_position.x;"," y = -a_position.y;",this.glslRectToPolar(a)," perPixel();",this.glslPolarToRect(a)," v_newPoint = vec2(x,-y);"," setPosition(a_position);","}"],h=["varying vec2 v_newPoint;",this.glslFilter(b,c),"void main() {"," setFragColor(vec4(filter(v_newPoint), 1));","}"];d.super.constructor.call(this,{fragmentShader:h,vertexShader:g,swapFrame:!0})}a.DynamicMovement=a.defineClass(b,a.Component,{componentName:"DynamicMovement",init:function(c,d,e){if(b.super.init.call(this,c,d,e),this.program.init(c),this.code.setup(d,e),!this.noGrid){for(var f=a.clamp(this.gridW,1,this.main.canvas.width),g=a.clamp(this.gridH,1,this.main.canvas.height),h=2*(f/this.main.canvas.width),i=2*(g/this.main.canvas.height),j=Math.ceil(this.main.canvas.width/f),k=Math.ceil(this.main.canvas.height/g),l=new Float32Array(2*6*j*k),m=0,n=-1,o=-1,p=0;k>p;p++){for(var q=0;j>q;q++){var r=Math.min(n+h,1),s=Math.min(o+i,1);l[m++]=n,l[m++]=o,l[m++]=r,l[m++]=o,l[m++]=n,l[m++]=s,l[m++]=r,l[m++]=o,l[m++]=r,l[m++]=s,l[m++]=n,l[m++]=s,n+=h}n=-1,o+=i}this.gridVertices=l,this.gridVerticesSize=m/2}},update:function(){var a=this.code;this.inited||(a.init(),this.inited=!0);var b=this.main.analyser.beat;a.b=b?1:0,a.perFrame(),b&&a.onBeat(),this.noGrid?this.program.run(this.parent.fm,null,this.code):this.program.run(this.parent.fm,null,this.code,this.gridVertices,this.gridVerticesSize)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}});var e={glslRectToPolar:function(a){return"POLAR"===a?["float ar = u_resolution.x/u_resolution.y;","x=x*ar;","d = distance(vec2(x, y), vec2(0,0))/sqrt(2.0);","r = mod(atan(y, x)+PI*0.5, 2.0*PI);"].join("\n"):""},glslPolarToRect:function(a){return"POLAR"===a?["d = d*sqrt(2.0);","x = d*sin(r)/ar;","y = -d*cos(r);"].join("\n"):""},glslFilter:function(a,b){return a&&!b?["vec3 filter(vec2 point) {"," vec2 texel = 1.0/(u_resolution-vec2(1,1));"," vec2 coord = (point+1.0)/2.0;"," vec2 cornoff = fract(coord/texel);"," vec2 corn = floor(coord/texel)*texel;"," vec3 tl = getSrcColorAtPos(corn).rgb;"," vec3 tr = getSrcColorAtPos(corn + vec2(texel.x, 0)).rgb;"," vec3 bl = getSrcColorAtPos(corn + vec2(0, texel.y)).rgb;"," vec3 br = getSrcColorAtPos(corn + texel).rgb;"," vec3 pt = mix(tl, tr, cornoff.x);"," vec3 pb = mix(bl, br, cornoff.x);"," return mix(pt, pb, cornoff.y);","}"].join("\n"):a&&b?["vec3 filter(vec2 point) {"," vec2 texel = 1.0/(u_resolution-vec2(1,1));"," vec2 coord = (point+1.0)/2.0;"," vec2 corn = floor(coord/texel)*texel;"," ivec2 cornoff = (ivec2(fract(coord/texel)*255.0));"," ivec3 tl = ivec3(255.0 * getSrcColorAtPos(corn).rgb);"," ivec3 tr = ivec3(255.0 * getSrcColorAtPos(corn + vec2(texel.x, 0)).rgb);"," ivec3 bl = ivec3(255.0 * getSrcColorAtPos(corn + vec2(0, texel.y)).rgb);"," ivec3 br = ivec3(255.0 * getSrcColorAtPos(corn + texel).rgb);"," #define bt(i, j) int((float(i)/255.0)*float(j))"," int a1 = bt(255-cornoff.x,255-cornoff.y);"," int a2 = bt(cornoff.x ,255-cornoff.y);"," int a3 = bt(255-cornoff.x,cornoff.y);"," int a4 = bt(cornoff.x ,cornoff.y);"," float r = float(bt(a1,tl.r) + bt(a2,tr.r) + bt(a3,bl.r) + bt(a4,br.r))/255.0;"," float g = float(bt(a1,tl.g) + bt(a2,tr.g) + bt(a3,bl.g) + bt(a4,br.g))/255.0;"," float b = float(bt(a1,tl.b) + bt(a2,tr.b) + bt(a3,bl.b) + bt(a4,br.b))/255.0;"," return vec3(r,g,b);","}"].join("\n"):["vec3 filter(vec2 point) {"," return getSrcColorAtPos((point+1.0)/2.0).rgb;","}"].join("\n")}};a.DMovProgramNG=a.defineClass(c,a.QuadBoxProgram,e,{draw:function(a){a.bindUniforms(this),c.super.draw.call(this)}}),a.DMovProgram=a.defineClass(d,a.ShaderProgram,e,{draw:function(a,b,c){a.bindUniforms(this),this.setVertexAttribArray("a_position",b,2,this.gl.FLOAT,!1,0,0),this.gl.drawArrays(this.gl.TRIANGLES,0,c)}}),b.ui={type:"DynamicMovement",disp:"Dynamic Movement",schema:{code:{type:"object",title:"Code","default":{},properties:{init:{type:"string",title:"Init"},onBeat:{type:"string",title:"On Beat"},perFrame:{type:"string",title:"Per Frame"},perPixel:{type:"string",title:"Per Point"}}},gridW:{type:"number",title:"Grid Width","default":16},gridH:{type:"number",title:"Grid Height","default":16},coord:{type:"string",title:"Coordinate System","enum":["POLAR","RECT"],"default":"POLAR"}},form:[{key:"code.init",type:"textarea"},{key:"code.onBeat",type:"textarea"},{key:"code.perFrame",type:"textarea"},{key:"code.perPixel",type:"textarea"},"gridW","gridH","coord"]}}(Webvs),function(a){function b(a){a=_.defaults(a,{bFilter:!0,coord:"POLAR",compat:!1}),b.super.constructor.call(this,{noGrid:!0,bFilter:a.bFilter,compat:a.compat,coord:a.coord,code:a.code}),this.options=a}a.Movement=a.defineClass(b,a.DynamicMovement)}(Webvs),function(a){function b(a){if(a=_.defaults(a,{channel:"RGB",onBeatRandom:!1}),this.channel=d.indexOf(a.channel),-1==this.channel)throw new Error("Invalid Channel");this.onBeatRandom=a.onBeatRandom,this.program=new c,b.super.constructor.apply(this,arguments)}function c(){c.super.constructor.call(this,{swapFrame:!0,fragmentShader:["uniform int u_channel;","void main() {"," vec3 color = getSrcColor().rgb;",_.flatMap(d,function(a,b){return["if(u_channel == "+b+") {"," setFragColor(vec4(color."+a.toLowerCase()+",1));","}"]}).join("\n"),"}"]})}var d=["RGB","RBG","BRG","BGR","GBR","GRB"];a.ChannelShift=a.defineClass(b,a.Component,{componentName:"ChannelShift",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){this.onBeatRandom&&this.main.analyser.beat&&(this.channel=Math.floor(Math.random()*d.length)),this.program.run(this.parent.fm,null,this.channel)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}}),a.ChannelShiftProgram=a.defineClass(c,a.QuadBoxProgram,{draw:function(a){this.setUniform("u_channel","1i",a),c.super.draw.call(this)}}),b.ui={disp:"Channel Shift",type:"ChannelShift",schema:{channel:{type:"string",title:"Channel","enum":d},onBeatRandom:{type:"boolean",title:"On beat random"}}}}(Webvs),function(a){function b(b){b=_.defaults(b,{color:"#ffffff",invert:!1,blendMode:"REPLACE"}),this.tone=a.parseColorNorm(b.color),this.invert=b.invert,this.program=new c(a.getBlendMode(b.blendMode))}function c(a){c.super.constructor.call(this,{outputBlendMode:a,swapFrame:!0,fragmentShader:["uniform vec3 u_tone;","uniform bool u_invert;","void main() {"," vec4 srcColor = getSrcColor();"," float depth = max(srcColor.r, max(srcColor.g, srcColor.b));"," if(u_invert) {"," depth = 1.0-depth;"," }"," setFragColor(vec4(depth*u_tone, 1));","}"]})}a.UniqueTone=a.defineClass(b,a.Component,{init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){this.program.run(this.parent.fm,null,this.tone,this.invert)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}}),a.UniqueToneProgram=a.defineClass(c,a.QuadBoxProgram,{draw:function(a,b){this.setUniform.apply(this,["u_tone","3f"].concat(a)),this.setUniform("u_invert","1f",b?1:0),c.super.draw.call(this)}})}(Webvs),function(a){function b(d){a.checkRequiredOptions(d,["code"]),d=_.defaults(d,{source:"SPECTRUM",drawMode:"LINES",colors:["#ffffff"]});var e;if(!_.isObject(d.code))throw new Error("Invalid superscope");e=d.code;var f=new a.ExprCodeGenerator(e,["n","v","i","x","y","b","red","green","blue"]);this.code=f.generateJs(["init","onBeat","perFrame","perPoint"]),this.code.n=100,this.clone=d.clone||1,this.spectrum="SPECTRUM"==d.source,this.dots="DOTS"==d.drawMode,this.colors=_.map(d.colors,a.parseColorNorm),this.currentColor=[],this.maxStep=100,this.step=this.maxStep,this.colorId=0,this.colorStep=[0,0,0],this.thickness=d.thickness?d.thickness:1,this.inited=!1,this.program=new c,b.super.constructor.apply(this,arguments)}function c(){c.super.constructor.call(this,{copyOnSwap:!0,vertexShader:["attribute vec2 a_position;","attribute vec3 a_color;","varying vec3 v_color;","uniform float u_pointSize;","void main() {"," gl_PointSize = u_pointSize;"," setPosition(clamp(a_position, vec2(-1,-1), vec2(1,1)));"," v_color = a_color;","}"],fragmentShader:["varying vec3 v_color;","void main() {"," setFragColor(vec4(v_color, 1));","}"]})}a.SuperScope=a.defineClass(b,a.Component,{componentName:"SuperScope",init:function(c,d,e){b.super.init.call(this,c,d,e),this.program.init(c),this.code.setup(d,this),this.code=a.CodeInstance.clone(this.code,this.clone)},update:function(){this._stepColor(),_.each(this.code,function(a){this.drawScope(a,!this.inited)},this),this.inited=!0},drawScope:function(a,b){this.gl,a.red=this.currentColor[0],a.green=this.currentColor[1],a.blue=this.currentColor[2],b&&a.init();var c=this.main.analyser.beat;a.b=c?1:0,a.perFrame(),c&&a.onBeat();for(var d=Math.floor(a.n),e=this.spectrum?this.main.analyser.getSpectrum():this.main.analyser.getWaveform(),f=e.length/d,g=0,h=0,i=new Float32Array(2*(this.dots?d:2*d-2)),j=new Float32Array(3*(this.dots?d:2*d-2)),k=0;d>k;k++){for(var l=0,m=0,n=Math.floor(k*f);(k+1)*f>n;n++,m++)l+=e[n];l/=m;var o=k/(d>1?d-1:1);a.i=o,a.v=l,a.perPoint(),i[g++]=a.x,i[g++]=-1*a.y,0===k||k==d-1||this.dots||(i[g++]=a.x,i[g++]=-1*a.y),this.dots?(j[h++]=a.red,j[h++]=a.green,j[h++]=a.blue):0!==k&&(j[h++]=a.red,j[h++]=a.green,j[h++]=a.blue,j[h++]=a.red,j[h++]=a.green,j[h++]=a.blue)}this.program.run(this.parent.fm,null,i,j,this.dots,this.thickness)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()},_stepColor:function(){var a;if(this.colors.length>1)if(this.step==this.maxStep){var b=this.colors[this.colorId];this.colorId=(this.colorId+1)%this.colors.length;var c=this.colors[this.colorId];for(a=0;3>a;a++)this.colorStep[a]=(c[a]-b[a])/this.maxStep;for(this.step=0,a=0;3>a;a++)this.currentColor[a]=b[a]}else{for(a=0;3>a;a++)this.currentColor[a]+=this.colorStep[a];this.step++}else this.currentColor=this.colors[0]}}),a.SuperScopeShader=a.defineClass(c,a.ShaderProgram,{draw:function(a,b,c,d){var e=this.gl;this.setUniform("u_pointSize","1f",d),this.setVertexAttribArray("a_position",a,2,e.FLOAT,!1,0,0),this.setVertexAttribArray("a_color",b,3,e.FLOAT,!1,0,0);var f;c||(f=e.getParameter(e.LINE_WIDTH),e.lineWidth(d)),e.drawArrays(c?e.POINTS:e.LINES,0,a.length/2),c||e.lineWidth(f)}}),b.ui={disp:"SuperScope",type:"SuperScope",schema:{code:{type:"object",title:"Code","default":{},properties:{init:{type:"string",title:"Init"},onBeat:{type:"string",title:"On Beat"},perFrame:{type:"string",title:"Per Frame"},perPoint:{type:"string",title:"Per Point"}}},source:{type:"string",title:"Source","default":"WAVEFORM","enum":["WAVEFORM","SPECTRUM"]},drawMode:{type:"string",title:"Draw Mode","default":"LINES","enum":["DOTS","LINES"]},colors:{type:"array",title:"Cycle Colors",items:{type:"string",format:"color","default":"#FFFFFF"}}}}}(Webvs),function(a){function b(a){a=_.defaults(a,{drawMode:"SOLID",source:"WAVEFORM",align:"CENTER",colors:["#ffffff"]});var c={};"SOLID"!=a.drawMode?(c.init="n=w;",c.perPoint={TOP:"x=i*2-1; y=-v/2-0.5;",CENTER:"x=i*2-1; y=-v/2;",BOTTOM:"x=i*2-1; y=v/2+0.5;"}[a.align]):(c.init="n=w*2;",c.perFrame="c=0;",c.perPoint="SPECTRUM"==a.source?{TOP:"x=i*2-1; y=if(c%2,0,-v/2-0.5); c=c+1;",CENTER:"x=i*2-1; y=if(c%2,0.5,-v/2); c=c+1;",BOTTOM:"x=i*2-1; y=if(c%2,0,v/2+0.5); c=c+1;"}[a.align]:{TOP:"x=i*2-1; y=if(c%2,-0.5,-v/2-0.5); c=c+1;",CENTER:"x=i*2-1; y=if(c%2,0,-v/2); c=c+1;",BOTTOM:"x=i*2-1; y=if(c%2,0.5,v/2+0.5); c=c+1;"}[a.align]),b.super.constructor.call(this,{source:a.source,drawMode:"SOLID"==a.drawMode?"LINES":a.drawMode,colors:a.colors,code:c}),this.options=a}a.Simple=a.defineClass(b,a.SuperScope)}(Webvs),function(a){function b(d){a.checkRequiredOptions(d,["code","imageSrc"]),d=_.defaults(d,{source:"SPECTRUM",resizing:!1,wrapAround:!1,colorFiltering:!0}),this.resizing=d.resizing,this.colorFiltering=d.colorFiltering,this.wrapAround=d.wrapAround,this.imageSrc=d.imageSrc;var e=new a.ExprCodeGenerator(d.code,["n","v","i","x","y","b","sizex","sizey","red","green","blue"]);this.code=e.generateJs(["init","onBeat","perFrame","perPoint"]),this.code.n=100,this.spectrum="SPECTRUM"==d.source,this._inited=!1,this.program=new c,b.super.constructor.apply(this,arguments)}function c(){c.super.constructor.call(this,{vertexShader:["uniform bool u_colorFilter;","attribute vec2 a_texVertex;","attribute vec2 a_vertex;","attribute vec3 a_color;","varying vec2 v_texVertex;","varying vec3 v_color;","void main() {"," if(u_colorFilter) {"," v_color = a_color;"," }"," v_texVertex = a_texVertex;"," setPosition(a_vertex);","}"],fragmentShader:["uniform bool u_colorFilter;","uniform sampler2D u_image;","varying vec2 v_texVertex;","varying vec3 v_color;","void main() {"," vec3 outColor = texture2D(u_image, v_texVertex).rgb;"," if(u_colorFilter) {"," outColor = outColor*v_color;"," }"," setFragColor(vec4(outColor, 1));","}"]})}a.Texer=a.defineClass(b,a.Component,{componentName:"Texer",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a),this.code.setup(c,this);var e=new Image;e.src=c.getResource(this.imageSrc),this.imagewidth=e.width,this.imageHeight=e.height,this.texture=a.createTexture(),a.bindTexture(a.TEXTURE_2D,this.texture),a.texImage2D(a.TEXTURE_2D,0,a.RGBA,a.RGBA,a.UNSIGNED_BYTE,e),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.LINEAR),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE)},update:function(){function a(a,b,c,d,e,f,l){-1-c>a||a>1||-1-d>b||b>1||(g.push(a,b,a+c,b,a+c,b+d,a,b+d),h.push(0,0,1,0,1,1,0,1),j&&j.push(e,f,l,e,f,l,e,f,l,e,f,l),i.push(k+0,k+1,k+2,k+0,k+2,k+3),k+=4)}var b=this.code;this._inited||b.init();var c=this.main.analyser.beat;b.b=c?1:0,b.perFrame(),c&&b.onBeat();for(var d=Math.floor(b.n),e=this.spectrum?this.main.analyser.getSpectrum():this.main.analyser.getWaveform(),f=e.length/d,g=[],h=[],i=[],j=this.colorFiltering?[]:null,k=0,l=2*(this.imagewidth/this.parent.fm.width),m=2*(this.imageHeight/this.parent.fm.height),n=0;d>n;n++){for(var o=0,p=0,q=Math.floor(n*f);(n+1)*f>q;q++,p++)o+=e[q];o/=p;var r=n/(d-1);b.i=r,b.v=o,b.sizex=1,b.sizey=1,b.red=1,b.green=1,b.blue=1,b.perPoint();var s=l,t=m;this.resizing&&(s*=b.sizex,t*=b.sizey);var u=b.x-s/2,v=-b.y-t/2;if(a(u,v,s,t,b.red,b.green,b.blue),this.wrapAround){var w=-1>u?2:u>1-s?-2:0,x=-1>v?2:v>1-t?-2:0;w&&a(w+u,v,s,t,b.red,b.green,b.blue),x&&a(u,x+v,s,t,b.red,b.green,b.blue),w&&x&&a(w+u,x+v,s,t,b.red,b.green,b.blue)}}this.program.run(this.parent.fm,null,new Float32Array(g),new Float32Array(h),new Uint16Array(i),j?new Float32Array(j):null,this.texture)},destroy:function(){b.super.destroy.call(this),this.gl.deleteTexture(this.texture),this.program.cleanup()}}),a.TexerProgram=a.defineClass(c,a.ShaderProgram,{draw:function(a,b,c,d,e){this.setUniform("u_image","texture2D",e),this.setVertexAttribArray("a_vertex",a),this.setVertexAttribArray("a_texVertex",b),d?(this.setUniform("u_colorFilter","1f",1),this.setVertexAttribArray("a_color",d,3)):this.setUniform("u_colorFilter","1f",0),this.setElementArray(c),this.gl.drawElements(this.gl.TRIANGLES,c.length,this.gl.UNSIGNED_SHORT,0)}})}(Webvs),function(a){function b(c){c=_.defaults(c,{n:0,color:"#000000",blendMode:"REPLACE"}),this.n=c.n,this.color=a.parseColorNorm(c.color),this.outputBlendMode=a.blendModes[c.blendMode],this.prevBeat=!1,this.beatCount=0,this.program=new a.ClearScreenProgram(this.outputBlendMode),b.super.constructor.apply(this,arguments)}a.ClearScreen=a.defineClass(b,a.Component,{componentName:"ClearScreen",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){var a=!1;0===this.n?a=!0:(this.main.analyser.beat&&!this.prevBeat&&(this.beatCount++,this.beatCount==this.n&&(a=!0,this.beatCount=0)),this.prevBeat=this.main.analyser.beat),a&&this.program.run(this.parent.fm,null,this.color)},destroy:function(){this.program.cleanup()}}),b.ui={type:"ClearScreen",disp:"Clear Screen",schema:{n:{type:"number",title:"Clear on beat (0 = always clear)","default":0},color:{type:"string",title:"Clear color",format:"color","default":"#000000"},blendMode:{type:"string",title:"Blend Mode","enum":_.keys(a.blendModes)}}}}(Webvs),function(a){function b(c){a.checkRequiredOptions(c,["src","x","y"]),this.x=c.x,this.y=c.y,this.src=c.src,this.program=new a.PictureProgram,b.super.constructor.apply(this,arguments)}function c(){c.super.constructor.call(this,{copyOnSwap:!0,vertexShader:["attribute vec2 a_texVertex;","uniform vec2 u_pos;","uniform vec2 u_texRes;","varying vec2 v_texCoord;","void main() {"," v_texCoord = a_texVertex;"," setPosition(a_texVertex*(u_texRes/u_resolution)*vec2(2,-2)+u_pos);","}"],fragmentShader:["uniform sampler2D u_image;","varying vec2 v_texCoord;","void main() {"," setFragColor(texture2D(u_image, v_texCoord));","}"]})}a.Picture=a.defineClass(b,a.Component,{init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a);var e=new Image;e.src=c.getResource(this.src),this.width=e.width,this.height=e.height,this.texture=a.createTexture(),a.bindTexture(a.TEXTURE_2D,this.texture),a.texImage2D(a.TEXTURE_2D,0,a.RGBA,a.RGBA,a.UNSIGNED_BYTE,e),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.NEAREST),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.NEAREST),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE)},update:function(){this.program.run(this.parent.fm,null,this.x,this.y,this.texture,this.width,this.height)},destroy:function(){this.program.cleanup(),this.gl.deleteTexture(this.texture)}}),a.PictureProgram=a.defineClass(c,a.ShaderProgram,{draw:function(a,b,c,d,e){this.setUniform("u_pos","2f",a,-b),this.setUniform("u_texRes","2f",d,e),this.setUniform("u_image","texture2D",c),this.setVertexAttribArray("a_texVertex",new Float32Array([0,0,0,1,1,1,0,0,1,1,1,0])),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}})}(Webvs); \ No newline at end of file diff --git a/dist/webvs.min.js b/dist/webvs.min.js deleted file mode 100644 index c579806..0000000 --- a/dist/webvs.min.js +++ /dev/null @@ -1,3 +0,0 @@ -!function(a){var b={};a.Webvs=b,b.defineClass=function(a,b){return a.prototype=Object.create(b.prototype),a.prototype.constructor=a,a.super=b.prototype,_.chain(arguments).drop(2).each(function(b){_.extend(a.prototype,b)}),a},b.noop=function(){},b.checkRequiredOptions=function(a,b){for(var c in b){var d=b[c];if(!(d in a))throw new Error("Required option "+d+" not found")}},b.glslFloatRepr=function(a){return a+(0===a%1?".0":"")},b.parseColor=function(a){if(_.isArray(a)&&3==a.length)return a;if(_.isString(a)){var b;if(a=a.toLowerCase(),b=a.match(/^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/))return _.chain(b).last(3).map(function(a){return parseInt(a,16)}).value();if(b=a.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/))return _.chain(b).last(3).map(function(a){return Math.min(parseInt(a,10),255)}).value()}throw new Error("Invalid Color Format")},b.parseColorNorm=function(a){return _.map(b.parseColor(a),function(a){return a/255})},b.logShaderError=function(a,b){var c=a.split("\n"),d=c.length.toString().length,e=b.match(/(\d+):(\d+)/);e&&(e=[parseInt(e[1],10),parseInt(e[2],10)]);var f=_.map(c,function(a,c){var f,g=c+1+"";for(f=0;fd;d++)c.push(b.charAt(Math.floor(Math.random()*b.length)));return c.join("")},b.clamp=function(a,b,c){return Math.min(Math.max(a,b),c)},b.getComponentClass=function(a){var c=b[a];if(!c)throw new Error("Unknown Component class "+a);return c}}(window),function(a){a.Resources={"avsres_texer_circle_edgeonly_19x19.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAMAAABFjsb+AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhFBMVEUAAAADAwMvLy9dXV1tbW0CAgJaWlrR0dH8/Pz+/v79/f1bW1sKCgqlpaXT09Nubm4xMTEeHh7S0tKCgoIGBgaBgYEuLi5sbGxeXl4cHBwbGxtvb29fX19ra2swMDDU1NQFBQV/f3+np6eoqKgLCwvQ0NBqamoEBAQyMjJhYWFxcXH///8GRExTAAAAAWJLR0QrJLnkCAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAK1JREFUGNN1kMkSgyAQRBViGMc1SpCYBYl7/v8Do2gocpBDF/Wqa3qmPe/w+YSeKPEdEpwZhBgCi4IfihNMs/ySZwUm8e5KoOTmx0tINmeEpZ1yxciMTwtuGS/SNUhA5cRVQBaVSBwmUC6a4c1hNd6NT/z5HosSeDrsCa81V7HGooYpcyBFbZlGut3xBr1t2Gho9x66FvtB1GLose1sU1KZXpR02xqn+TNP43HBX4kJCUk5wyykAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0wNC0yOVQwMTo0OTozMCswNTowMPYYqoAAAAAASUVORK5CYII=","avsres_texer_circle_edgeonly_29x29.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAMAAABhTZc9AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAA3lBMVEUAAAANDQ0/Pz9xcXGRkZGbm5tycnIfHx+IiIjg4OD7+/v+/v7///8EBARoaGjn5+f8/Pzl5eXHx8e7u7sICAiXl5f6+vrx8fGVlZU4ODgKCgoBAQEJCQm+vr4oKCi9vb39/f2YmJikpKQLCwujo6NpaWmJiYknJyeKiorh4eE3Nzc2NjZAQEDk5ORzc3OSkpLGxsacnJy6urq5ubmdnZ3FxcWTk5M1NTWUlJTw8PDo6Oi8vLwgICCioqJqamqZmZmamprj4+PExMS4uLiLi4sODg5BQUF0dHSenp4PDw8GiJKZAAAAAWJLR0QMgbNRYwAAAAlwSFlzAAALEgAACxIB0t1+/AAAATRJREFUKM+tkltXgkAUhUczL+xBQGNAwEuRKVlpNxC8oGZa//8PRRoIjqsnz9M561tnZu+zNiFnqVz+onBZKOZzJ1ipXBEACgiVcumIiVUJVFZq9ZoiU0hVMQ2vVAZNbxgmMY2GroGpVmpTldFsJWOrjc71YbvKcGOnnrJvwbqJIAlNOyPDbkOKpd1Ba5Fs9TT0/3xWqM4Z1Kmz950X5HuODmThYdcUoRgctR7xtGuGGJkcNUcY7ppn+nLisq/07Z/dd7jxvxZHk38jzQOOerHmnEPHHB3HfkkfWu/4Vn58K1IKMMk6NuoIpvHQZZilsTEDmyeTGHYwOTzem6ATptJhhQy+61m/2fBcH2yR8SjOgyhXy9XHahnlKphnchXVdO3I+0w66ynh63Ozdb/c7eabnKV+AJulHNGcTEkjAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0wNC0yOVQwMTo0OTozMCswNTowMPYYqoAAAAAASUVORK5CYII=","avsres_texer_circle_fade_13x13.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAA9lBMVEUAAAADAwMGBgYICAgCAgIKCgoVFRUeHh4hISEdHR0JCQkODg4jIyM4ODhISEhNTU1HR0c3NzcNDQ0LCwskJCRGRkZoaGh/f3+Hh4d+fn5lZWVEREQXFxc6OjppaWmSkpKtra22trarq6uPj49mZmYHBwcgICBLS0uCgoKurq7Kysrd3d3JycmsrKxRUVGMjIy4uLjf39/+/v63t7eIiIhMTEyDg4Ovr6/MzMzLy8uAgIBJSUkEBAQYGBg8PDxsbGyVlZWwsLC5ubmTk5M5OTkWFhYMDAwmJiZKSkqEhIRqamoQEBA9PT1SUlI7OzsPDw8BAQH///+Cg4ycAAAAAWJLR0RRlGl8KgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKRJREFUCNcljukWgVAAhO9tvRKVEhGyhISEspWsZef9n0bL/Jo5c+bMB0AsiOEYBJkIkqJRjmKINOTZQpHjBVRKoiiV5UpVqdURGW/URrOltTvdnkBBoPcHQ2NkjidKkcYAI02tmT03F0sO4UBHjrta2xttu4s7z98Hh+PJOMuXa/wZRrfgbrkPnhUTkGf04t7OR/qm9zD0kYRUkcjQfh7O6F7i/uybEULrc6ImAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwMy0xMC0wN1QwMzoxNzoxMiswNjowMNF3hcgAAAAASUVORK5CYII=","avsres_texer_circle_heavyblur_19x19.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAMAAABFjsb+AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABVlBMVEUAAAACAgIHBwcNDQ0RERETExMBAQEICAgVFRUiIiItLS01NTU3NzcgICA0NDRGRkZVVVVeXl5hYWEjIyM9PT1XV1dubm6BgYGMjIyQkJAfHx9dXV19fX2YmJirq6u2tra6uroDAwMUFBQzMzNWVlafn5+7u7vPz8/b29vf398hISFFRUVtbW2Xl5fY2Njr6+v09PT39/cMDAwsLCxTU1OAgICpqanOzs7q6ur4+Pj8/Pz+/v6qqqoQEBBcXFyKioq0tLTa2tr9/f3///+1tbWLi4sSEhJfX1+NjY24uLje3t729vYyMjKJiYnZ2dnz8/MLCwsrKytSUlJ+fn6oqKjNzc3p6ekGBgZDQ0Nra2uVlZXV1dXy8vL19fUxMTFUVFR5eXmcnJzMzMzc3NwdHR06OjpaWlqUlJSnp6eysrJqamp8fHyIiIgwMDBCQkJRUVEqKioPDw8hvXKsAAAAAWJLR0RDZ9ANYgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAU5JREFUGNM9kFVbwmAARr9tMEpKUhghoyRHCQyHhMTI0SnlSMn/f6OI+l6+z7k5B4DrIBjhcDkIDIG/oTy+QCi6EwkFfB56uyBELJHK5PdymVQiRn5QFFEoVWqN9kGrUauUCuRK8nRKPWYwmswmowHTK3U8AB4tuBWz2R3OJ6fDbsOsuAUCsMvt8Zp9fiJA+H12r8ftgkEwFI48R2NxMkHGY9GXSDgUBBSefE2liUwimyCJ9FsuiVMgLyoU6VI5k81mE+USXSyI8oCqJKupWp28cvVaqpqsUIBpNFvtTjdAZshAt9NuNRsM6In7gyE96o7fx90RPRz0xT0AcSfT2Zxe+D58C3o+m0643yIwu1zN1putabtZz1ZLFr76Bl3L8O5zn9tju/DSxdxSMezheFrpV6fjgWV+Y6FwXnee4JOzLg+j/1WhHnOhLkzv1vkLuBJAlyODjrgAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDA5LTA0LTI5VDAxOjQ5OjMwKzA1OjAw9hiqgAAAAABJRU5ErkJggg==","avsres_texer_circle_heavyblur_21x21.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABfVBMVEUAAAADAwMICAgNDQ0QEBASEhIBAQELCwsWFhYhISEqKiovLy8yMjIFBQUiIiJBQUFNTU1UVFRXV1cUFBQoKCg+Pj5TU1NlZWV0dHR9fX2AgIBmZmYRERFCQkJdXV13d3eNjY2cnJylpaWoqKgKCgqampqvr6/AwMDKysrNzc0VFRUxMTF2dnaZmZm2trbOzs7e3t7o6Ojr6+sHBwcfHx9AQECMjIzl5eXy8vL4+Pj6+voMDAxMTExzc3Obm5u/v7/d3d39/f3+/v4PDw8uLi58fHykpKTIyMjn5+f///8wMDBVVVV+fn6mpqbLy8vp6en5+fktLS1SUlJ7e3ujo6PHx8fm5ub39/dKSkpxcXG9vb3c3Nzx8fEGBgY/Pz9jY2OKioqtra3j4+NQUFCWlpazs7Pb29sCAgIJCQkgICA8PDxaWlp6enqsrKy8vLzGxsbJyckmJiaJiYmhoaEEBAQ7OztiYmJwcHB5eXlJSUkTExMeHh4sLCwdHR0ODg7+hS0uAAAAAWJLR0RJhwXkfAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAZhJREFUGNNFkfs3wnAYxr9jkynXXSqyXLZpGMXShWhbRWFCmUvlslgql1Xk/rdbEc+Pn/Oezznv8wDQDtTVDSNwdxfUA/4CWXrRPqvN2of2WqAO7EcGbINDwyPDQ4O2AaT/F2I4QdodzlGnY4wkcKyNIRc+TrknJqempyYn3NQ47mpJaJSgnAw74+E8MyzjpAiUNk9n58h5fmHR61vyebkFfp6cm4XAsuC3r7BcIBgKh4IBjl2x+4VlsIqvRdY3osGwKInhYHRjPbKGW4AcI+OJza2QmEwmxdDWZiJOxmQAp7Z3dpW9tGRSKb2n7O5sp2Ag7x8cZrJHqiglJVE9ymYOD/ZlYMGPT5jTs1zbmzs7ZU6OTW9eKJxfXCpXOTWt5q6Uy4vzgpAHEJbSHMVrz03AF7hRrosOLYXpAJRuy5Vq8e7+4fHh/q5YrZRvS+bHOmwUKpEaX0/U+Vqk4jdgvVVPAzGetOfmS/yl+aw9GUjjp8qGLFiJV43UXgmrIDc6teult3fjI/ZhfL6V9P+JgJ6nv+QvOv+7zzdrwFa1yCl9GAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDktMDQtMjlUMDE6NDk6MzArMDU6MDD2GKqAAAAAAElFTkSuQmCC","avsres_texer_circle_heavyblur_29x29.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdCAMAAABhTZc9AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACSVBMVEUAAAABAQEDAwMGBgYJCQkLCwsNDQ0ODg4EBAQPDw8WFhYcHBwgICAjIyMkJCQCAgIHBwcQEBAaGhosLCwzMzM4ODg8PDw9PT0ICAgiIiIuLi46OjpERERNTU1TU1NXV1dZWVkMDAwYGBgnJyc2NjZFRUVSUlJeXl5oaGhwcHB1dXV2dnZfX18KCgooKChMTExdXV1sbGx7e3uHh4ePj4+Tk5OVlZWUlJQVFRUmJiZOTk5iYmKIiIiXl5eioqKqqqqvr6+xsbEhISE1NTVLS0t5eXmhoaG8vLzExMTJycnLy8u7u7stLS1cXFykpKS2trbGxsbT09Pb29vh4eHj4+MZGRk5OTlRUVGgoKDKysra2trm5ubu7u7y8vLz8/Nra2sFBQUUFBQqKipDQ0N6enqWlpbZ2dno6Oj4+Pj6+vr7+/vp6ekxMTFnZ2eGhoa6urrR0dHl5eX5+fn8/Pz9/f3+/v7S0tIeHh5ubm6NjY2oqKjCwsLt7e339/f///9VVVVycnKRkZGtra3Hx8ff39/x8fGSkpI7Ozt0dHSurq6srKze3t4dHR1tbW2MjIynp6fBwcHs7OwwMDBKSkplZWWEhISfn5+5ubnQ0NDk5OQpKSlBQUFbW1t3d3fDw8PX19fn5+c3NzdPT09paWmFhYWdnZ2zs7Pr6+vw8PAXFxcrKyuLi4vPz8/Y2Njd3d0fHx9ISEirq6u3t7fAwMDFxcW4uLgTExNxcXGenp6mpqaCgoKKiopaWlpkZGRAQEBJSUkvLy80NDQSEhJl1IWqAAAAAWJLR0R+P7hBcwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAvpJREFUKM9dk/k/2gEcxr/VN+UrlcRKoRJRvs4awrTE2NxHzuZu1RzTMWdEGpqbXLly39mY+9xfNpnZ7Pn1/Xqe1+f1ep4PADwKgUSBaCeMExpEIRHAcyGwoDPkgnPFu+JcIGcQ+4wTiG4kV3eyh+cLTw+yuyvJjUL4C5FoiOpFo3v7+DJ8fbzpNC8qhEY+QSbLj+0fwAkM4vK4QYHBAf5sPxbzERNgVggtNCw8IpIveBnFj4wIDwulhbDgh3AEJTqGJozlxcW/ShC9FiW8io/jxQppMdFEx2lYZ3GiJDaJnyx6k5L6NjXlnSiZnxQrSRQ7Yx1WVho9ncvPyMzKzsnNy83JzsrM4HPT6Wn5FASAxFClBeFxhZlFxSWlsvey0pLioszCuLICKRWDBMorKqs41TWiLPkHmUKpUipkH+VZoppqTlVldDlAIdVK6uqTGz41ytQarU6rUcsaPzck19dJaklEAG5iBzS3tLYVt+s1uo6ODp1G317c1trSHMBuggEmjtZp6Oo29piU2g6HtEpTj7H7i6GThmMCTHGvb5+gPzVXptI9UJ1KlpvaL+jz7aUyATROeu81G7+a1NqHZK363mvuMnRKcWgAHmAXDA4Nj8hH9RoH1mr0o/KR4aHBAvYADBDzx8YnLJNT0zMmher+ZpXCNDM9NWmZGB/LJwLYilnr3HyUeWFxyaRXK9V609Ligjlqfs46W4EFCE5i2/LK6tq6cTFvY9O0uZG3aFxfW11Ztomd7luibG1bGYadwvWF3b39nv293YX1wh0Dw7q9RXR0j7GTDzjcnbVv3w+PjEeHP8xrO9zgA7Id89A/6hhvG+ecWASnZwnmhLNTgeWEI7HhK1C/9whCMbYDRiDv/ILfxb845wUyDmwxEIj4Mx0IT7YKL6/Kgk6Cyq4uhVYyHoKfVkkAj+2V7OubZZ9On+Wba3al/Rj8Z7IIFGbLHnJ75yG13d2G2CEM6vk7IInMY5aLHWd3Yf1kEpHA/yJgUSCMhkEUlvBk/AXCBOhMJK+/nwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMTAtMDZUMjI6MDc6NDYrMDY6MDA+xvzGAAAAAElFTkSuQmCC","avsres_texer_circle_sharp_09x09.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAMAAACecocUAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAOVBMVEUAAAACAgJhYWG+vr7b29tiYmKoqKj+/v7///9jY2PBwcHf39/CwsJlZWUDAwOsrKxmZmbDw8Pg4ODJk53hAAAAAWJLR0QIht6VegAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAElJREFUCNdVzUsSwCAIA1CtVakfUO9/WINlY1YvM0xw7op/whuiP0yZiHLSEpUoH1zoT4GrucLN3OBu9x1mOTvCOspjrjn4froBkegClm06guAAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDAzLTA2LTI5VDE5OjA5OjMyKzA2OjAw50C8rwAAAABJRU5ErkJggg==","avsres_texer_circle_sharp_19x19.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAe1BMVEUAAAAFBQVBQUGGhoaurq68vLyvr68GBgYBAQFNTU3Ozs78/Pz+/v7///+Kior6+vr9/f2Li4vR0dFCQkJDQ0OIiIiJiYmzs7PBwcG0tLRERETT09NRUVH7+/uQkJCRkZFTU1PU1NRUVFQHBwdHR0eMjIy1tbXDw8NISEjxdmZoAAAAAWJLR0QN9rRh9QAAAAlwSFlzAAALEgAACxIB0t1+/AAAAJ5JREFUGNN9ke0SgiAUBUUrQbhA9qGpWGZa7/+EgZoCNu7PnTPALEGwAQqj3f4QhbHlMEkoAwBGE4LnIRfwQ3A0LbmEBXkc10SAjSBGxim4pObKE/UsPWt7YZ5lV20z8Mm0zVc2/7u9actX53JtC+lZWZj3lp4th0SVcqSqxg6106GeqqF6Wav7nBg/Gjn0lc0TW93bV9e/++7Tbn3YF1ESEZb9e6HrAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0wNC0yOVQwMTo0OTozMCswNTowMPYYqoAAAAAASUVORK5CYII=","avsres_texer_circle_slightblur_13x13.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAASFBMVEUAAAABAQEODg4jIyMtLS0EBARsbGyZmZmoqKiKiorT09Py8vL4+Pj7+/v+/v7///8kJCRtbW3U1NSLi4uampqpqakPDw8uLi7NF0qwAAAAAWJLR0QPGLoA2QAAAAlwSFlzAAALEgAACxIB0t1+/AAAAGhJREFUCNdljkcOwCAMBCmmF9NC/v/TGJBySHwbjXbXjH2PCwlS8AMKtLFGg9oGnA8xeAfLCu1Txpy8FkTShIyIORhJBDbiumiBqLyurFxtO9d2jkNv1Nn67qS9Oq5Rzx7ZWe4y+e/HB1hhBEsscYLrAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwMy0xMC0wNlQyMTozODowMCswNTowMB7KOaoAAAAASUVORK5CYII=","avsres_texer_circle_slightblur_21x21.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAvVBMVEUAAAACAgIDAwMEBAQQEBAgICAsLCwwMDABAQEODg5MTExnZ2d5eXl+fn4TExM9PT1tbW2Xl5e0tLTFxcXKysqvr6/W1tbs7Oz19fX4+Pjg4OD39/f9/f3+/v6YmJj///8RERFoaGi1tbV6enrGxsb29vYxMTGAgIDLy8t/f38tLS0hISG2trbt7e1NTU2ZmZnX19dubm6xsbHh4eGwsLAPDw8+Pj57e3sUFBRvb2/MzMxpaWmBgYEiIiIyMjK3SG45AAAAAWJLR0QfBQ0QvQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAPNJREFUGNN9ketygjAQhZVLCpgKagEjRAKC2nAVC2itvv9jOcRI0Rk9P7/ZOXv27GDwXkNh+IxESQYfQJbEHlNUoI3gJxxpQFU6ONaNyXT2NZtODH18p6oOTcueo7ltmVBXuScwzIXjYoxdZ2Ea4OYtaUvLwV4r7FhLjTAq+4Hteje5duDLLCdYhRHmFEchBG1uYb3Zoo6i7fdaaGfp4+yKshtjP0k73zTwY7aNZHnBLTAq8ozwvLtyj1hetP/ZUd5FVTdlkUYoSouyqat7D4f6mCfhb5jkx/rwX09Fs1Pz15wyWim9LkVyphcak36/r37xpCumwx2LqyXT8gAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMTAtMDZUMjA6NDA6MjIrMDU6MDAsCqMMAAAAAElFTkSuQmCC","avsres_texer_hexagon-h_blur_123x123.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHsAAAB7AgMAAAApqRfsAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEUAAACIiIjMzMz///+lqQOCAAAAAWJLR0QDEQxM8gAAAAlwSFlzAAALEgAACxIB0t1+/AAAAXJJREFUSMft17FVwzAURuHgwhSMQKERvEI2ygh4BK1BltAKjECREVxAIQSH4MTvSfpvKhpQe8/J8efY0vNu979+Y90dzXr2/b6Y9eH7g+1l73pwfXb94PqL68n1k7t8l8ub7YPv75JXAT3PA0PVLfBQdQtMVTfAiueAQ92z5DngY90NMDR6lDwLTI2+ATZ4Bji0epY8A2zxtsCp2aPkbYGp2U+StwEO7Z4lbwNs867AqdNX4FOnv0peKYvkXYBjr2fJuwB7vBU4dXuUvBWYun2RvB/g2O9Z8r6Ae8k7AyfRo+SdgUn0RfK+gaPqubUzWmBQvXCn36frIx/dH7y/k+jxhv+Xng96vuj5pOcb348+MN70ftL7TfsD7S+0P+H+1gOu+yPtr7Q/0/5O5wOdL3g+tYHX843ORzpf6Xym853mA5wvguTxfEPzEc1XNJ/RfIfzYQ2cTaf5lOZbmo9pvqb5HOd7D5xdp+8L+j6h75u/uj4BxUT7mSYYfpEAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDExLTA0LTExVDAwOjM0OjU4KzA1OjAwAHKiyQAAAABJRU5ErkJggg==","avsres_texer_square_edgeonly_24x24.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYAQMAAADaua+7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEX///8AAABVwtN+AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABJJREFUCNdjYEAC8v9/UAUjAQD7uCWNIgeQwQAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMTAtMDZUMjE6NTc6NDYrMDU6MDDg7pOkAAAAAElFTkSuQmCC","avsres_texer_square_edgeonly_28x28.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcAQMAAABIw03XAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEX///8AAABVwtN+AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABNJREFUCNdjYEAF8v//N9CPQAUArWA5f2D2DX0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDAzLTEwLTA2VDIxOjU4OjQ0KzA1OjAwhnrZAAAAAABJRU5ErkJggg==","avsres_texer_square_edgeonly_30x30.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeAQMAAAAB/jzhAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEX///8AAABVwtN+AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABRJREFUCNdjYEAF8v//PxgIAhUAAOmGR7lQ6SOhAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwMy0xMC0wNlQyMTo1OToxOCswNTowMOb41i4AAAAASUVORK5CYII=","avsres_texer_square_sharp_20x20.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYAQMAAADaua+7AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABJJREFUCNdjYIAC+/9/qIqhAABLlCyJ9A7ihwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDMtMDYtMjlUMTk6Mzg6MDQrMDU6MDBuiiynAAAAAElFTkSuQmCC","avsres_texer_square_sharp_32x32.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoAQMAAAC2MCouAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABVJREFUCNdjYMAB+P////9hCJM4AACQJX+BjmWyDAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMDktMTAtMDhUMDE6NTM6NTYrMDU6MDBzUO7GAAAAAElFTkSuQmCC","avsres_texer_square_sharp_48x48.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4AQMAAACSSKldAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABZJREFUGNNjYCAA+P+DwIdReoBoAgAAldYe8LB8aUoAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTMtMTAtMjNUMTg6MDk6NDErMDY6MDDn3a6/AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDA5LTEwLTA4VDAxOjUzOjI4KzA1OjAwKaqcggAAAABJRU5ErkJggg==","avsres_texer_square_sharp_60x60.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQAAABEAQMAAAAC3QHxAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAALEgAACxIB0t1+/AAAABdJREFUKM9jYCAS8P+HgFHWKIuaLCIBAH8IpfBELmrmAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMTo1NDozMiswNTowMKOs2CsAAAAASUVORK5CYII=","avsres_texer_square_sharp_64x64.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQAQMAAAC032DuAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABdJREFUKM9jYKAV+A8Fo8xR5lBk0gYAAMbE/hBbX3sVAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMjoxODowMCswNjowML8mns0AAAAASUVORK5CYII=","avsres_texer_square_sharp_72x72.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFQAAABUAQMAAAAmpYKCAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABpJREFUKM9jYKASYP4PBX9G2aPsUTa12VQCABpBhZc9Qe3KAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMjowMjoxNiswNjowMMbw5GAAAAAASUVORK5CYII=","avsres_texer_square_sharp_96x96.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHAAAABwAQMAAAD8LmYIAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAB1JREFUOMtjYBgo8B8JjHJHuaPcUe4od/BwBwYAAB86e71LirdOAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEzLTEwLTIzVDE4OjA5OjQxKzA2OjAw592uvwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAwOS0xMC0wOFQwMjoxODo0NiswNjowMFi8pQ0AAAAASUVORK5CYII=","avsres_texer_square_sharp_250x250.bmp":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAAEKAQMAAADQBYmKAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUAAAD///+l2Z/dAAAAAWJLR0QB/wIt3gAAAAlwSFlzAAALEgAACxIB0t1+/AAAADlJREFUaN7tykENAAAIBCD7p7KZFrgZwMGbKrK5taIoiqIoiqIoiqIoiqIoiqIoiqIoiqIoivKpkCyUd+T92W3T7QAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMy0xMC0yM1QxODowOTo0MSswNjowMOfdrr8AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTEtMDEtMDVUMTg6NTk6NTQrMDU6MDBwiLErAAAAAElFTkSuQmCC"}}(Webvs),function(a){function b(){}a.AnalyserAdapter=a.defineClass(b,Object,{beat:!1,isPlaying:function(){return!1},getWaveform:function(){return new Float32Array(0)},getSpectrum:function(){return new Float32Array(0)}})}(Webvs),function(a){function b(a){this.dancer=a,this.beat=!1;var b=this;this.kick=a.createKick({onKick:function(){b.beat=!0},offKick:function(){b.beat=!1}}),this.kick.on()}a.DancerAdapter=a.defineClass(b,a.AnalyserAdapter,{isPlaying:function(){return this.dancer.isPlaying()},getWaveform:function(){return this.dancer.getWaveform()},getSpectrum:function(){return this.dancer.getSpectrum()}})}(Webvs),function(a){function b(b){if(a.checkRequiredOptions(b,["canvas","analyser"]),b=_.defaults(b,{showStat:!1}),this.canvas=b.canvas,this.analyser=b.analyser,this.isStarted=!1,b.showStat){var c=new Stats;c.setMode(0),c.domElement.style.position="absolute",c.domElement.style.right="5px",c.domElement.style.bottom="5px",document.body.appendChild(c.domElement),this.stats=c}this.resources={},this.rootComponent=new a.EffectList({id:"root"}),this._registerContextEvents(),this._initGl()}a.Main=a.defineClass(b,Object,{_registerContextEvents:function(){var a=this;this.canvas.addEventListener("webglcontextlost",function(b){b.preventDefault(),a.stop()}),this.canvas.addEventListener("webglcontextrestored",function(){a.resetCanvas()})},_initGl:function(){try{this.gl=this.canvas.getContext("experimental-webgl",{alpha:!1}),this.copier=new a.CopyProgram({dynamicBlend:!0}),this.copier.init(this.gl),this.resolution={width:this.canvas.width,height:this.canvas.height}}catch(b){throw new Error("Couldnt get webgl context"+b)}},loadPreset:function(b){b=_.clone(b),b.id="root";var c=new a.EffectList(b);this.stop(),this.rootComponent.destroy(),this.rootComponent=c,this.resources=b.resources||{}},resetCanvas:function(){this.stop();var b=this.rootComponent.getOptions();this.rootComponent.destroy(),this.copier.cleanup(),this._initGl(),this.rootComponent=new a.EffectList(b)},start:function(){if(!this.isStarted){var a=this,b=function(){a.analyser.isPlaying()&&a.rootComponent.update(),a.animReqId=requestAnimationFrame(b)};if(this.stats){var c=b;b=function(){a.stats.begin(),c.call(this,arguments),a.stats.end()}}this.rootComponent.componentInited||(this.registerBank={},this.bootTime=(new Date).getTime(),this.rootComponent.init(this.gl,this)),this.animReqId=requestAnimationFrame(b),this.isStarted=!0}},stop:function(){_.isUndefined(this.animReqId)||(cancelAnimationFrame(this.animReqId),this.isStarted=!1)},getPreset:function(){var a=this.rootComponent.getOptions();return a.resources=this.resources,a},addComponent:function(a,b,c){return b=_.clone(b),this.rootComponent.addComponent(a,b,c),res},updateComponent:function(b,c){if(c=_.clone(c),c.id=b,"root"==b){var d=this.rootComponent.detachAllComponents();return c=_.defaults(c,this.rootComponent.options),this.rootComponent.destroy(),this.rootComponent=new a.EffectList(c,d),this.rootComponent.init(this.gl,this),!0}return this.rootComponent.updateComponent(b,c)},removeComponent:function(a){var b=this.rootComponent.detachComponent(a);return b?(b.destroy(),!0):!1},moveComponent:function(a,b,c){var d=this.rootComponent.detachComponent(a);return d?this.rootComponent.addComponent(b,d,c):!1},getResource:function(b){var c;return c=this.resources[b],c||(c=a.Resources[b]),c||(c=b),c},setResource:function(a,b){this.resources[a]=b},traverse:function(a){this.rootComponent.traverse(a)}}),b.ui={leaf:!1,disp:"Main",schema:{name:{type:"string",title:"Name"},author:{type:"string",title:"Author"},description:{type:"string",title:"Description"},clearFrame:{type:"boolean",title:"Clear every frame","default":!1,required:!0}}}}(Webvs),function(a){function b(a){this.id=a.id,this.enabled=_.isUndefined(a.enabled)?!0:a.enabled,this.componentInited=!1,this.options=a}a.Component=a.defineClass(b,Object,{componentName:"Component",init:function(a,b,c){this.gl=a,this.main=b,this.parent=c,this.componentInited=!0},adoptOrInit:function(a,b,c){return this.componentInited?this.adopt(c):this.init(a,b,c)},adopt:function(a){this.parent=a},update:function(){},destroy:function(){},getOptions:function(){return this.options},getPath:function(){return _.isUndefined(this.parent)||_.isUndefined(this.id)?this.componentName+"#Main":this.parent.getIdString()+"/"+this.componentName+"#"+this.id}})}(Webvs),function(a){function b(a,c){b.super.constructor.call(this,a),this.components=[],_.each(c||a.components||[],function(a){this.addComponent(this.id,a)},this)}a.Container=a.defineClass(b,a.Component,{init:function(a,c,d){b.super.init.call(this,a,c,d);for(var e=0;e0&&this._frameCounter--,0!==this._frameCounter))&&(this.code.beat=this.main.analyser.beat?1:0,this.code.enabled=1,this.code.clear=this.clearFrame,this._inited||(this._inited=!0,this.code.init()),this.code.perFrame(),0!==this.code.enabled)){if(this.fm.setRenderTarget(),(this.clearFrame||this.first||this.code.clear)&&(a.clearColor(0,0,0,1),a.clear(a.COLOR_BUFFER_BIT),this.first=!1),-1!==this.input){var c=this.parent.fm.getCurrentTexture();this.main.copier.run(this.fm,this.input,c)}for(var d=0;de;e++){var f=b.charAt(e);"\n"===f?(a.seenCR||a.line++,a.column=1,a.seenCR=!1):"\r"===f||"\u2028"===f||"\u2029"===f?(a.line++,a.column=1,a.seenCR=!0):(a.column++,a.seenCR=!1)}a.offset+=c}function f(a){F.offsetH.offset&&(H=d(F),I=[]),I.push(a))}function g(){var a="program@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,i,j,k,l,m,n,o,p,q,r;if(p=d(F),q=d(F),g=x(),null!==g)if(i=h(),null!==i)if(j=x(),null!==j){for(k=[],r=d(F),59===b.charCodeAt(F.offset)?(l=";",e(F,1)):(l=null,0===G&&f('";"')),null!==l?(m=x(),null!==m?(n=h(),null!==n?(o=x(),null!==o?l=[l,m,n,o]:(l=null,F=d(r))):(l=null,F=d(r))):(l=null,F=d(r))):(l=null,F=d(r));null!==l;)k.push(l),r=d(F),59===b.charCodeAt(F.offset)?(l=";",e(F,1)):(l=null,0===G&&f('";"')),null!==l?(m=x(),null!==m?(n=h(),null!==n?(o=x(),null!==o?l=[l,m,n,o]:(l=null,F=d(r))):(l=null,F=d(r))):(l=null,F=d(r))):(l=null,F=d(r));null!==k?(59===b.charCodeAt(F.offset)?(l=";",e(F,1)):(l=null,0===G&&f('";"')),l=null!==l?l:"",null!==l?(m=x(),null!==m?g=[g,i,j,k,l,m]:(g=null,F=d(q))):(g=null,F=d(q))):(g=null,F=d(q))}else g=null,F=d(q);else g=null,F=d(q);else g=null,F=d(q);return null!==g&&(g=function(a,b,c,d){var e=[d[1]];return e=e.concat(_.map(d[3],function(a){return a[2]})),new Webvs.AstProgram(e)}(p.offset,p.line,p.column,g)),null===g&&(F=d(p)),J[a]={nextPos:d(F),result:g},g}function h(){var a="statement@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k,l,n;return l=d(F),n=d(F),g=s(),null!==g?(h=x(),null!==h?(61===b.charCodeAt(F.offset)?(i="=",e(F,1)):(i=null,0===G&&f('"="')),null!==i?(j=x(),null!==j?(k=m(),null!==k?g=[g,h,i,j,k]:(g=null,F=d(n))):(g=null,F=d(n))):(g=null,F=d(n))):(g=null,F=d(n))):(g=null,F=d(n)),null!==g&&(g=function(a,b,c,d,e){return new Webvs.AstAssignment(d,e)}(l.offset,l.line,l.column,g[0],g[4])),null===g&&(F=d(l)),null===g&&(g=m()),J[a]={nextPos:d(F),result:g},g}function i(){var a="unary_ops@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return 43===b.charCodeAt(F.offset)?(g="+",e(F,1)):(g=null,0===G&&f('"+"')),null===g&&(45===b.charCodeAt(F.offset)?(g="-",e(F,1)):(g=null,0===G&&f('"-"'))),J[a]={nextPos:d(F),result:g},g}function j(){var a="additive_ops@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return 43===b.charCodeAt(F.offset)?(g="+",e(F,1)):(g=null,0===G&&f('"+"')),null===g&&(45===b.charCodeAt(F.offset)?(g="-",e(F,1)):(g=null,0===G&&f('"-"'))),J[a]={nextPos:d(F),result:g},g}function k(){var a="multiplicative_ops@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return 42===b.charCodeAt(F.offset)?(g="*",e(F,1)):(g=null,0===G&&f('"*"')),null===g&&(47===b.charCodeAt(F.offset)?(g="/",e(F,1)):(g=null,0===G&&f('"/"')),null===g&&(37===b.charCodeAt(F.offset)?(g="%",e(F,1)):(g=null,0===G&&f('"%"')))),J[a]={nextPos:d(F),result:g},g}function l(){var a="boolean_ops@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return 38===b.charCodeAt(F.offset)?(g="&",e(F,1)):(g=null,0===G&&f('"&"')),null===g&&(124===b.charCodeAt(F.offset)?(g="|",e(F,1)):(g=null,0===G&&f('"|"'))),J[a]={nextPos:d(F),result:g},g}function m(){var a="boolean_expr@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c,e,f,g,h,i,j,k,m;if(j=d(F),k=d(F),c=n(),null!==c){for(e=[],m=d(F),f=x(),null!==f?(g=l(),null!==g?(h=x(),null!==h?(i=n(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==f;)e.push(f),m=d(F),f=x(),null!==f?(g=l(),null!==g?(h=x(),null!==h?(i=n(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==e?c=[c,e]:(c=null,F=d(k))}else c=null,F=d(k);return null!==c&&(c=function(a,b,c,d,e){return C(d,e)}(j.offset,j.line,j.column,c[0],c[1])),null===c&&(F=d(j)),J[a]={nextPos:d(F),result:c},c}function n(){var a="additive_expr@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c,e,f,g,h,i,k,l,m;if(k=d(F),l=d(F),c=o(),null!==c){for(e=[],m=d(F),f=x(),null!==f?(g=j(),null!==g?(h=x(),null!==h?(i=o(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==f;)e.push(f),m=d(F),f=x(),null!==f?(g=j(),null!==g?(h=x(),null!==h?(i=o(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==e?c=[c,e]:(c=null,F=d(l))}else c=null,F=d(l);return null!==c&&(c=function(a,b,c,d,e){return C(d,e)}(k.offset,k.line,k.column,c[0],c[1])),null===c&&(F=d(k)),J[a]={nextPos:d(F),result:c},c}function o(){var a="multiplicative_expr@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c,e,f,g,h,i,j,l,m;if(j=d(F),l=d(F),c=p(),null!==c){for(e=[],m=d(F),f=x(),null!==f?(g=k(),null!==g?(h=x(),null!==h?(i=p(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==f;)e.push(f),m=d(F),f=x(),null!==f?(g=k(),null!==g?(h=x(),null!==h?(i=p(),null!==i?f=[f,g,h,i]:(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m))):(f=null,F=d(m));null!==e?c=[c,e]:(c=null,F=d(l))}else c=null,F=d(l);return null!==c&&(c=function(a,b,c,d,e){return C(d,e)}(j.offset,j.line,j.column,c[0],c[1])),null===c&&(F=d(j)),J[a]={nextPos:d(F),result:c},c}function p(){var a="unary@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c,e,f,g,h;return g=d(F),h=d(F),c=i(),null!==c?(e=x(),null!==e?(f=q(),null!==f?c=[c,e,f]:(c=null,F=d(h))):(c=null,F=d(h))):(c=null,F=d(h)),null!==c&&(c=function(a,b,c,d,e){return new Webvs.AstUnaryExpr(d,e)}(g.offset,g.line,g.column,c[0],c[2])),null===c&&(F=d(g)),null===c&&(c=q()),J[a]={nextPos:d(F),result:c},c}function q(){var a="func_call@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k,l,n,o,p,q,s,t;if(p=d(F),q=d(F),s=d(F),/^[a-zA-Z_]/.test(b.charAt(F.offset))?(g=b.charAt(F.offset),e(F,1)):(g=null,0===G&&f("[a-zA-Z_]")),null!==g){for(h=[],/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==i;)h.push(i),/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==h?g=[g,h]:(g=null,F=d(s))}else g=null,F=d(s);if(null!==g)if(h=x(),null!==h)if(40===b.charCodeAt(F.offset)?(i="(",e(F,1)):(i=null,0===G&&f('"("')),null!==i){for(s=d(F),j=[],t=d(F),k=x(),null!==k?(l=m(),null!==l?(n=x(),null!==n?(44===b.charCodeAt(F.offset)?(o=",",e(F,1)):(o=null,0===G&&f('","')),null!==o?k=[k,l,n,o]:(k=null,F=d(t))):(k=null,F=d(t))):(k=null,F=d(t))):(k=null,F=d(t));null!==k;)j.push(k),t=d(F),k=x(),null!==k?(l=m(),null!==l?(n=x(),null!==n?(44===b.charCodeAt(F.offset)?(o=",",e(F,1)):(o=null,0===G&&f('","')),null!==o?k=[k,l,n,o]:(k=null,F=d(t))):(k=null,F=d(t))):(k=null,F=d(t))):(k=null,F=d(t));null!==j?(k=x(),null!==k?(l=m(),null!==l?j=[j,k,l]:(j=null,F=d(s))):(j=null,F=d(s))):(j=null,F=d(s)),j=null!==j?j:"",null!==j?(k=x(),null!==k?(41===b.charCodeAt(F.offset)?(l=")",e(F,1)):(l=null,0===G&&f('")"')),null!==l?g=[g,h,i,j,k,l]:(g=null,F=d(q))):(g=null,F=d(q))):(g=null,F=d(q))}else g=null,F=d(q);else g=null,F=d(q);else g=null,F=d(q);return null!==g&&(g=function(a,b,c,d,e){var f=[];return _.each(e[0],function(a){f.push(a[1])}),f.push(e[2]),new Webvs.AstFuncCall(D(d),f)}(p.offset,p.line,p.column,g[0],g[3])),null===g&&(F=d(p)),null===g&&(g=r()),J[a]={nextPos:d(F),result:g},g}function r(){var a="primary_expr@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k;return g=w(),null===g&&(g=u(),null===g&&(g=v(),null===g&&(g=t(),null===g&&(j=d(F),k=d(F),40===b.charCodeAt(F.offset)?(g="(",e(F,1)):(g=null,0===G&&f('"("')),null!==g?(h=m(),null!==h?(41===b.charCodeAt(F.offset)?(i=")",e(F,1)):(i=null,0===G&&f('")"')),null!==i?g=[g,h,i]:(g=null,F=d(k))):(g=null,F=d(k))):(g=null,F=d(k)),null!==g&&(g=function(a,b,c,d){return d}(j.offset,j.line,j.column,g[1])),null===g&&(F=d(j)))))),J[a]={nextPos:d(F),result:g},g}function s(){var a="assignable@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c;return c=v(),null===c&&(c=t()),J[a]={nextPos:d(F),result:c},c}function t(){var a="identifier@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k;if(j=d(F),k=d(F),/^[a-zA-Z_]/.test(b.charAt(F.offset))?(g=b.charAt(F.offset),e(F,1)):(g=null,0===G&&f("[a-zA-Z_]")),null!==g){for(h=[],/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==i;)h.push(i),/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==h?g=[g,h]:(g=null,F=d(k))}else g=null,F=d(k);return null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr(D(d).toLowerCase(),"ID")}(j.offset,j.line,j.column,g)),null===g&&(F=d(j)),J[a]={nextPos:d(F),result:g},g}function u(){var a="constant@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k;if(j=d(F),k=d(F),36===b.charCodeAt(F.offset)?(g="$",e(F,1)):(g=null,0===G&&f('"$"')),null!==g){for(h=[],/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==i;)h.push(i),/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==h?g=[g,h]:(g=null,F=d(k))}else g=null,F=d(k);return null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr(D(d).toLowerCase(),"CONST")}(j.offset,j.line,j.column,g[1])),null===g&&(F=d(j)),J[a]={nextPos:d(F),result:g},g}function v(){var a="register@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k,l,m;if(l=d(F),m=d(F),64===b.charCodeAt(F.offset)?(g="@",e(F,1)):(g=null,0===G&&f('"@"')),null!==g){for(h=[],/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==i;)h.push(i),/^[a-zA-Z_0-9]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[a-zA-Z_0-9]"));null!==h?g=[g,h]:(g=null,F=d(m))}else g=null,F=d(m);return null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr("__REG_AT_"+D(d).toLowerCase(),"REG")}(l.offset,l.line,l.column,g[1])),null===g&&(F=d(l)),null===g&&(l=d(F),m=d(F),/^[rR]/.test(b.charAt(F.offset))?(g=b.charAt(F.offset),e(F,1)):(g=null,0===G&&f("[rR]")),null!==g?(/^[eE]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[eE]")),null!==h?(/^[gG]/.test(b.charAt(F.offset))?(i=b.charAt(F.offset),e(F,1)):(i=null,0===G&&f("[gG]")),null!==i?(/^[0-9]/.test(b.charAt(F.offset))?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("[0-9]")),null!==j?(/^[0-9]/.test(b.charAt(F.offset))?(k=b.charAt(F.offset),e(F,1)):(k=null,0===G&&f("[0-9]")),null!==k?g=[g,h,i,j,k]:(g=null,F=d(m))):(g=null,F=d(m))):(g=null,F=d(m))):(g=null,F=d(m))):(g=null,F=d(m)),null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr("__REG_"+D(d).toLowerCase(),"REG")}(l.offset,l.line,l.column,g)),null===g&&(F=d(l))),J[a]={nextPos:d(F),result:g},g}function w(){var a="value@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k,l,m,n,o;for(m=d(F),n=d(F),g=[],/^[0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[0-9]"));null!==h;)g.push(h),/^[0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[0-9]"));if(null!==g)if(46===b.charCodeAt(F.offset)?(h=".",e(F,1)):(h=null,0===G&&f('"."')),null!==h){if(/^[0-9]/.test(b.charAt(F.offset))?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("[0-9]")),null!==j)for(i=[];null!==j;)i.push(j),/^[0-9]/.test(b.charAt(F.offset))?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("[0-9]"));else i=null;if(null!==i){if(o=d(F),/^[Ee]/.test(b.charAt(F.offset))?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("[Ee]")),null!==j){if(/^[0-9]/.test(b.charAt(F.offset))?(l=b.charAt(F.offset),e(F,1)):(l=null,0===G&&f("[0-9]")),null!==l)for(k=[];null!==l;)k.push(l),/^[0-9]/.test(b.charAt(F.offset))?(l=b.charAt(F.offset),e(F,1)):(l=null,0===G&&f("[0-9]"));else k=null;null!==k?j=[j,k]:(j=null,F=d(o))}else j=null,F=d(o);j=null!==j?j:"",null!==j?g=[g,h,i,j]:(g=null,F=d(n))}else g=null,F=d(n)}else g=null,F=d(n);else g=null,F=d(n);if(null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr(parseFloat(D(d)),"VALUE")}(m.offset,m.line,m.column,g)),null===g&&(F=d(m)),null===g){if(m=d(F),n=d(F),/^[a-fA-F0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[a-fA-F0-9]")),null!==h)for(g=[];null!==h;)g.push(h),/^[a-fA-F0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[a-fA-F0-9]"));else g=null;if(null!==g?(/^[hH]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[hH]")),null!==h?g=[g,h]:(g=null,F=d(n))):(g=null,F=d(n)),null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr(parseInt(D(d),16),"VALUE")}(m.offset,m.line,m.column,g[0])),null===g&&(F=d(m)),null===g){if(m=d(F),n=d(F),/^[0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[0-9]")),null!==h)for(g=[];null!==h;)g.push(h),/^[0-9]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[0-9]"));else g=null;null!==g?(/^[dD]/.test(b.charAt(F.offset))?(h=b.charAt(F.offset),e(F,1)):(h=null,0===G&&f("[dD]")),h=null!==h?h:"",null!==h?g=[g,h]:(g=null,F=d(n))):(g=null,F=d(n)),null!==g&&(g=function(a,b,c,d){return new Webvs.AstPrimaryExpr(parseInt(D(d),10),"VALUE")}(m.offset,m.line,m.column,g[0])),null===g&&(F=d(m))}}return J[a]={nextPos:d(F),result:g},g}function x(){var a="__@"+F.offset,b=J[a];if(b)return F=d(b.nextPos),b.result;var c,e;for(c=[],e=y(),null===e&&(e=z(),null===e&&(e=A()));null!==e;)c.push(e),e=y(),null===e&&(e=z(),null===e&&(e=A()));return J[a]={nextPos:d(F),result:c},c}function y(){var a="whiteSpace@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return/^[\t\x0B\f \xA0\uFEFF]/.test(b.charAt(F.offset))?(g=b.charAt(F.offset),e(F,1)):(g=null,0===G&&f("[\\t\\x0B\\f \\xA0\\uFEFF]")),J[a]={nextPos:d(F),result:g},g}function z(){var a="lineEnd@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g;return/^[\n\r\u2028\u2029]/.test(b.charAt(F.offset))?(g=b.charAt(F.offset),e(F,1)):(g=null,0===G&&f("[\\n\\r\\u2028\\u2029]")),J[a]={nextPos:d(F),result:g},g}function A(){var a="comment@"+F.offset,c=J[a];if(c)return F=d(c.nextPos),c.result;var g,h,i,j,k,l,m;if(k=d(F),"/*"===b.substr(F.offset,2)?(g="/*",e(F,2)):(g=null,0===G&&f('"/*"')),null!==g){for(h=[],l=d(F),m=d(F),G++,"*/"===b.substr(F.offset,2)?(i="*/",e(F,2)):(i=null,0===G&&f('"*/"')),G--,null===i?i="":(i=null,F=d(m)),null!==i?(b.length>F.offset?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("any character")),null!==j?i=[i,j]:(i=null,F=d(l))):(i=null,F=d(l));null!==i;)h.push(i),l=d(F),m=d(F),G++,"*/"===b.substr(F.offset,2)?(i="*/",e(F,2)):(i=null,0===G&&f('"*/"')),G--,null===i?i="":(i=null,F=d(m)),null!==i?(b.length>F.offset?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("any character")),null!==j?i=[i,j]:(i=null,F=d(l))):(i=null,F=d(l));null!==h?("*/"===b.substr(F.offset,2)?(i="*/",e(F,2)):(i=null,0===G&&f('"*/"')),null!==i?g=[g,h,i]:(g=null,F=d(k))):(g=null,F=d(k))}else g=null,F=d(k);if(null===g)if(k=d(F),"//"===b.substr(F.offset,2)?(g="//",e(F,2)):(g=null,0===G&&f('"//"')),null!==g){for(h=[],l=d(F),m=d(F),G++,i=z(),G--,null===i?i="":(i=null,F=d(m)),null!==i?(b.length>F.offset?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("any character")),null!==j?i=[i,j]:(i=null,F=d(l))):(i=null,F=d(l));null!==i;)h.push(i),l=d(F),m=d(F),G++,i=z(),G--,null===i?i="":(i=null,F=d(m)),null!==i?(b.length>F.offset?(j=b.charAt(F.offset),e(F,1)):(j=null,0===G&&f("any character")),null!==j?i=[i,j]:(i=null,F=d(l))):(i=null,F=d(l));null!==h?g=[g,h]:(g=null,F=d(k))}else g=null,F=d(k);return J[a]={nextPos:d(F),result:g},g}function B(a){a.sort();for(var b=null,c=[],d=0;dH.offset?F:H;throw new this.SyntaxError(B(I),M,L,N.line,N.column)}return K},toSource:function(){return this._source}};return b.SyntaxError=function(b,c,d,e,f){function g(b,c){var d,e;switch(b.length){case 0:d="end of input";break;case 1:d=b[0];break;default:d=b.slice(0,b.length-1).join(", ")+" or "+b[b.length-1]}return e=c?a(c):"end of input","Expected "+d+" but "+e+" found."}this.name="SyntaxError",this.expected=b,this.found=c,this.message=g(b,c),this.offset=d,this.line=e,this.column=f},b.SyntaxError.prototype=Error.prototype,b}(),function(a){function b(){}a.CodeInstance=a.defineClass(b,Object,{rand:function(a){return Math.floor(Math.random()*a)+1},gettime:function(a){switch(a){case 0:var b=(new Date).getTime();return(b-this._bootTime)/1e3;default:throw new Error("Invalid startTime mode for gettime call")}},getosc:function(a,b){for(var c=this._analyser.getWaveform(),d=Math.floor((a-b/2)*(c.length-1)),e=Math.floor((a+b/2)*(c.length-1)),f=0,g=d;e>=g;g++)f+=c[g];return f/(e-d+1)},bindUniforms:function(a){var b=this,c=_.difference(_.keys(this),this._treatAsNonUniform);if(_.each(c,function(c){var d=b[c];"number"==typeof d&&a.setUniform(c,"1f",d)}),_.each(this._registerUsages,function(b){a.setUniform(b,"1f",this._registerBank[b])}),this.hasRandom){var d=[Math.random()/100,Math.random()/100];a.setUniform("__randStep","2fv",d)}if(this.hasGettime){var e=((new Date).getTime()-this._bootTime)/1e3;a.setUniform("__gettime0","1f",e)}_.each(this._preCompute,function(b){var c=_.map(_.last(b,b.length-2),function(a){return _.isString(a)?"__REG"==a.substring(0,5)?this._registerBank[a]:this[a]:a}),d=this[b[0]].apply(this,c);a.setUniform(b[1],"1f",d)})},setup:function(a){this._registerBank=a.registerBank,this._bootTime=a.bootTime,this._analyser=a.analyser,this.w=a.canvas.width,this.h=a.canvas.height,_.each(this._registerUsages,function(b){_.has(a.registerBank,b)||(a.registerBank[b]=0)})}}),b.clone=function(a,b){a.cid=0;var c=[a];return b>1&&_.times(b-1,function(b){var d=_.clone(a);d.cid=b+1,c.push(d)}),c}}(Webvs),function(a){function b(a,b){this.codeSrc={};for(var c in a){var d=a[c];_.isArray(d)&&(d=d.join("\n")),d=d.trim(),""!==d&&(this.codeSrc[c]=d)}this.externalVars=_.union(b||[],["w","h","cid"]),this._parseSrc()}a.ExprCodeGenerator=a.defineClass(b,Object,{_parseSrc:function(){var b={},c=[],d={},e=[];for(var f in this.codeSrc)try{var g=this.codeSrc[f];b[f]=a.PegExprParser.parse(g);var h=[];this._getVars(b[f],c,h,e),d[f]=h}catch(i){throw new Error("Error parsing "+f+"("+i.line+":"+i.column+")"+" : "+i)}this.codeAst=b,this.funcUsages=d,this.instanceVars=_.uniq(this.externalVars.concat(c)),this.registerUsages=_.uniq(e)},generateJs:function(b){var c=new a.CodeInstance;_.each(this.instanceVars,function(a){c[a]=0});var d=_.intersection(_.keys(this.codeAst),b),e=_.difference(b,d);return _.each(d,function(a){var b=this.codeAst[a],d=this._generateJs(b);c[a]=new Function(d)},this),_.each(e,function(b){c[b]=a.noop}),c._registerUsages=this.registerUsages,c},generateGlsl:function(a,b,c){var d=[];b=b||[],_.each(this.instanceVars,function(a){var c="";_.contains(b,a)||(c="uniform "),d.push(c+"float "+a+";")});var e=_.intersection(_.keys(this.codeAst),a),f=_.difference(a,e),g=_.uniq(_.flatMap(e,function(a){return this.funcUsages[a]},this));_.each(g,function(a){var b=this.glslFuncCode[a];b&&d.push(b)},this);var h=[],i=[];return _.each(e,function(a){var b=this.codeAst[a],c=this._generateGlsl(b,h);i.push("void "+a+"() {"),i.push(c),i.push("}")},this),d=d.concat(_.map(h,function(a){return"uniform float "+a[1]+";"})),d=d.concat(i),_.each(f,function(a){d.push("void "+a+"() {}")}),c._preCompute=h,_.contains(e,"rand")&&(c.hasRandom=!0),_.contains(e,"gettime")&&(c.hasGettime=!0),c._treatAsNonUniform=b,d.join("\n")},funcArgLengths:{above:2,below:2,equal:2,pow:2,sqr:1,sqrt:1,invsqrt:1,floor:1,ceil:1,abs:1,"if":3,min:2,max:2,sin:1,cos:1,tan:1,asin:1,acos:1,atan:1,atan2:2,log:1,band:2,bor:2,bnot:1,rand:1,gettime:1,getosc:3,select:{min:2}},jsMathFuncs:["min","max","sin","cos","abs","tan","asin","acos","atan","log","pow","sqrt","floor","ceil"],glslFuncCode:{rand:["uniform vec2 __randStep;","vec2 __randSeed;","float rand(float max) {"," __randCur += __randStep;"," float val = fract(sin(dot(__randSeed.xy ,vec2(12.9898,78.233))) * 43758.5453);"," return (floor(val*max)+1);","}"].join("\n"),gettime:["uniform float __gettime0;","int gettime(int startTime) {"," int time = 0;"," if(startTime == 0) {"," time = __gettime0;"," }"," return time;","}"].join("\n")},_checkFunc:function(a){var b=this.funcArgLengths[a.funcName];if(void 0===b)throw Error("Unknown function "+a.funcName);if(_.isNumber(b)){if(a.args.length!=b)throw Error(a.funcName+" accepts "+b+" arguments")}else if(b.min&&a.args.length",this._generateGlsl(b.args[1],c),"?1.0:0.0)"].join("");case"below":return["(",this._generateGlsl(b.args[0],c),"<",this._generateGlsl(b.args[1],c),"?1.0:0.0)"].join("");case"equal":return["(",this._generateGlsl(b.args[0],c),"==",this._generateGlsl(b.args[1],c),"?1.0:0.0)"].join("");case"if":return["(",this._generateGlsl(b.args[0],c),"!=0.0?",this._generateGlsl(b.args[1],c),":",this._generateGlsl(b.args[2],c),")"].join("");case"select":var d=this._generateGlsl(b.args[0],c),e=this,f=function(a,b){return 1==a.length?e._generateGlsl(a[0],c):["(("+d+" === "+b+")?","("+e._generateGlsl(a[0],c)+"):","("+f(_.last(a,a.length-1),b+1)+"))"].join("")};return f(_.last(b.args,b.args.length-1),0);case"sqr":return"(pow(("+this._generateGlsl(b.args[0],c)+"), 2))";case"band":return"(float(("+this._generateGlsl(b.args[0],c)+")&&("+this._generateGlsl(b.args[1],c)+")))";case"bor":return"(float(("+this._generateGlsl(b.args[0],c)+")||("+this._generateGlsl(b.args[1],c)+")))";case"bnot":return"(float(!("+this._generateGlsl(b.args[0],c)+")))";case"invsqrt":return"(1/sqrt("+this._generateGlsl(b.args[0],c)+"))";case"atan2":return"(atan(("+this._generateGlsl(b.args[0],c)+"),("+this._generateGlsl(b.args[1],c)+"))";case"getosc":var g=_.every(b.args,function(b){return b instanceof a.AstPrimaryExpr});if(!g)throw new Error("Non Pre-Computable arguments for getosc in shader code, use variables or constants");var h="__PC_"+b.funcName+"_"+j,i=[b.funcName,h].concat(_.map(b.args,function(a){return a.value})),j=_.indexOf(c,i);return-1==j&&(c.push(i),j=c.length-1),h;default:var k=_.map(b.args,function(a){return this._generateGlsl(a,c)},this).join(","),l=b.funcName;return _.contains(this.varArgFuncs,b.funcName)&&(l+=b.args.length),"("+l+"("+k+"))"}if(b instanceof a.AstAssignment)return this._generateGlsl(b.lhs,c)+"="+this._generateGlsl(b.expr,c);if(b instanceof a.AstProgram){var m=_.map(b.statements,function(a){return this._generateGlsl(a,c)},this);return m.join(";\n")+";"}return b instanceof a.AstPrimaryExpr&&"VALUE"===b.type?a.glslFloatRepr(b.value):b instanceof a.AstPrimaryExpr&&"CONST"===b.type?this._translateConstants(b.value).toString():b instanceof a.AstPrimaryExpr&&("ID"===b.type||"REG"===b.type)?b.value:void 0},_generateJs:function(b){var c;if(b instanceof a.AstBinaryExpr)return"("+this._generateJs(b.leftOperand)+b.operator+this._generateJs(b.rightOperand)+")";if(b instanceof a.AstUnaryExpr)return"("+b.operator+this._generateJs(b.operand)+")";if(b instanceof a.AstFuncCall)switch(this._checkFunc(b),b.funcName){case"above":return["(",this._generateJs(b.args[0]),">",this._generateJs(b.args[1]),"?1:0)"].join("");case"below":return["(",this._generateJs(b.args[0]),"<",this._generateJs(b.args[1]),"?1:0)"].join("");case"equal":return["(",this._generateJs(b.args[0]),"==",this._generateJs(b.args[1]),"?1:0)"].join("");case"if":return["(",this._generateJs(b.args[0]),"!==0?",this._generateJs(b.args[1]),":",this._generateJs(b.args[2]),")"].join("");case"select":var d=["((function() {"];return d.push("switch("+this._generateJs(b.args[0])+") {"),_.each(_.last(b.args,b.args.length-1),function(a,b){d.push("case "+b+": return "+this._generateJs(a)+";")},this),d.push("default : throw new Error('Unknown selector value in select');"),d.push("}}).call(this))"),d.join("");case"sqr":return"(Math.pow(("+this._generateJs(b.args[0])+"),2))";case"band":return"((("+this._generateJs(b.args[0])+")&&("+this._generateJs(b.args[1])+"))?1:0)";case"bor":return"((("+this._generateJs(b.args[0])+")||("+this._generateJs(b.args[1])+"))?1:0)";case"bnot":return"((!("+this._generateJs(b.args[0])+"))?1:0)";case"invsqrt":return"(1/Math.sqrt("+this._generateJs(b.args[0])+"))";case"atan2":return"(Math.atan(("+this._generateJs(b.args[0])+")/("+this._generateJs(b.args[1])+")))";default:var e=_.map(b.args,function(a){return this._generateJs(a)},this).join(",");return c=_.contains(this.jsMathFuncs,b.funcName)?"Math.":"this.","("+c+b.funcName+"("+e+"))"}if(b instanceof a.AstAssignment)return this._generateJs(b.lhs)+"="+this._generateJs(b.expr);if(b instanceof a.AstProgram){var f=_.map(b.statements,function(a){return this._generateJs(a)},this);return f.join(";\n")}return b instanceof a.AstPrimaryExpr&&"VALUE"===b.type?b.value.toString():b instanceof a.AstPrimaryExpr&&"CONST"===b.type?this._translateConstants(b.value).toString():b instanceof a.AstPrimaryExpr&&"ID"===b.type?"this."+b.value:b instanceof a.AstPrimaryExpr&&"REG"===b.type?'this._registerBank["'+b.value+'"]':void 0 -},_getVars:function(b,c,d,e){b instanceof a.AstBinaryExpr?(this._getVars(b.leftOperand,c,d,e),this._getVars(b.rightOperand,c,d,e)):b instanceof a.AstUnaryExpr?this._getVars(b.operand,c,d,e):b instanceof a.AstFuncCall?(d.push(b.funcName),_.each(b.args,function(a){this._getVars(a,c,d,e)},this)):b instanceof a.AstAssignment?(this._getVars(b.lhs,c,d,e),this._getVars(b.expr,c,d,e)):b instanceof a.AstProgram?_.each(b.statements,function(a){this._getVars(a,c,d,e)},this):b instanceof a.AstPrimaryExpr&&"ID"===b.type?c.push(b.value):b instanceof a.AstPrimaryExpr&&"REG"===b.type&&e.push(b.value)},_translateConstants:function(a){switch(a){case"pi":return Math.PI;case"e":return Math.E;case"phi":return 1.6180339887;default:throw new Error("Unknown constant "+a)}}})}(Webvs),function(a){function b(c){a.checkRequiredOptions(c,["code"]);var d=new a.ExprCodeGenerator(c.code,["b"]);this.code=d.generateJs(["init","onBeat","perFrame"]),this.inited=!1,b.super.constructor.apply(this,arguments)}a.GlobalVar=a.defineClass(b,a.Component,{init:function(a,c,d){b.super.init.call(this,a,c,d),this.code.setup(c,this)},update:function(){var a=this.code;a.b=this.main.analyser.beat?1:0,this.inited||(a.init(),this.inited=!0),this.main.analyser.beat&&a.onBeat(),a.perFrame()}}),b.ui={disp:"Global Var",type:"GlobalVar",schema:{code:{type:"object",title:"Code","default":{},properties:{init:{type:"string",title:"Init"},onBeat:{type:"string",title:"On Beat"},perFrame:{type:"string",title:"Per Frame"}}}}}}(Webvs),function(a){function b(c){if(c=_.defaults(c,{action:"SAVE",bufferId:1,blendMode:"REPLACE"}),this.blendMode=a.blendModes[c.blendMode],this.action=this.actions[c.action],!this.action)throw new Error("Unknown BufferSave action "+c.action);this.action==this.actions.SAVERESTORE?this._nextAction=this.actions.SAVE:this.action==this.actions.RESTORESAVE&&(this._nextAction=this.actions.RESTORE),this._bufferId="__BUFFERSAVE_"+c.bufferId,b.super.constructor.apply(this,arguments)}a.BufferSave=a.defineClass(b,a.Component,{actions:{SAVE:1,RESTORE:2,SAVERESTORE:3,RESTORESAVE:4},init:function(c,d,e){if(b.super.init.call(this,c,d,e),!d.registerBank[this._bufferId]){var f=new a.FrameBufferManager(d.canvas.width,d.canvas.height,c,d.copier,!0,1);d.registerBank[this._bufferId]=f}},update:function(){this.gl;var a,b=this.main.registerBank[this._bufferId];switch(this.action==this.actions.SAVERESTORE||this.action==this.RESTORESAVE?(a=this._nextAction,this._nextAction=this._nextAction==this.actions.SAVE?this.actions.RESTORE:this.actions.SAVE):a=this.action,a){case this.actions.SAVE:b.setRenderTarget(),this.main.copier.run(null,null,this.parent.fm.getCurrentTexture()),b.restoreRenderTarget();break;case this.actions.RESTORE:this.main.copier.run(this.parent.fm,this.blendMode,b.getCurrentTexture())}},destroy:function(){b.super.destroy.call(this),this.main.registerBank[this._bufferId].destroy()}}),b.ui={disp:"Buffer Save",type:"BufferSave",schema:{action:{type:"string",title:"Buffer save action","enum":["SAVE","RESTORE","SAVERESTORE","RESTORESAVE"]},bufferId:{type:"number",title:"Buffer Id","enum":[1,2,3,4,5,6,7,8]},blendMode:{type:"string",title:"Blend mode","enum":_.keys(a.blendModes)}}}}(Webvs),function(a){function b(c){c=_.defaults(c,{speed:1,color:"#000000"}),this.color=a.parseColorNorm(c.color),this.frameCount=0,this.maxFrameCount=Math.floor(1/c.speed),this.program=new a.ClearScreenProgram(a.AVERAGE),b.super.constructor.apply(this,arguments)}a.FadeOut=a.defineClass(b,a.Component,{componentName:"FadeOut",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){this.gl,this.frameCount++,this.frameCount==this.maxFrameCount&&(this.frameCount=0,this.program.run(this.parent.fm,null,this.color))},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}}),b.ui={type:"FadeOut",disp:"Fade Out",schema:{speed:{type:"number",title:"Speed",maximum:0,minimum:1,"default":1},color:{type:"string",title:"Fadeout color",format:"color","default":"#FFFFFF"}},form:[{key:"speed",type:"range",step:"0.05"},"color"]}}(Webvs),function(a){function b(c){a.checkRequiredOptions(c,["kernel"]),c=_.defaults(c,{edgeMode:"EXTEND",bias:0});var d;if(c.kernel in b.kernels)d=b.kernels[c.kernel];else{if(!_.isArray(c.kernel)||1!==c.kernel.length%2)throw new Error("Invalid convolution kernel");d=c.kernel}var e=Math.floor(Math.sqrt(d.length));if(e*e!=d.length)throw new Error("Invalid convolution kernel");this.program=new a.ConvolutionProgram(d,e,c.edgeMode,c.scale,c.bias),b.super.constructor.apply(this,arguments)}function c(b,d,e,f,g){var h="";switch(e){case"WRAP":h="pos = vec2(pos.x<0?pos.x+1.0:pos.x%1, pos.y<0?pos.y+1.0:pos.y%1);";break;case"EXTEND":h="pos = clamp(pos, vec2(0,0), vec2(1,1));";break;default:throw new Error("Invalid edge mode")}var i,j,k=[],l=Math.floor(d/2);for(i=0;d>i;i++)for(j=0;d>j;j++){var m=b[i*d+j];0!==m&&(k.push("pos = v_position + texel * vec2("+(i-l)+","+(j-l)+");"),k.push(h),k.push("colorSum += texture2D(u_srcTexture, pos) * "+a.glslFloatRepr(m)+";"))}_.isUndefined(f)&&(f=_.reduce(b,function(a,b){return a+b},0)),c.super.constructor.call(this,{swapFrame:!0,fragmentShader:["void main() {"," vec2 texel = 1.0/(u_resolution-vec2(1,1));"," vec2 pos;"," vec4 colorSum = vec4(0,0,0,0);",k.join("\n")," setFragColor(vec4(((colorSum+"+a.glslFloatRepr(g)+") / "+a.glslFloatRepr(f)+").rgb, 1.0));","}"]})}a.Convolution=a.defineClass(b,a.Component,{componentName:"Convolution",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){this.program.run(this.parent.fm,null)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}}),b.kernels={normal:[0,0,0,0,1,0,0,0,0],gaussianBlur:[.045,.122,.045,.122,.332,.122,.045,.122,.045],unsharpen:[-1,-1,-1,-1,9,-1,-1,-1,-1],emboss:[-2,-1,0,-1,1,1,0,1,2],blur:[1,1,1,1,1,1,1,1,1]},a.ConvolutionProgram=a.defineClass(c,a.QuadBoxProgram)}(Webvs),function(a){function b(c){if(a.checkRequiredOptions(c,["maps"]),c=_.defaults(c,{key:"RED",output:"REPLACE",mapCycleMode:"SINGLE"}),this.maps=c.maps,this.currentMap=0,this.mapCycleMode=this.mapCycleModes[c.mapCycleMode],!this.mapCycleMode)throw new Error("Unknown mapCycleMode "+c.mapCycleMode);this.program=new a.ColorMapProgram(c.key,a.getBlendMode(c.output)),b.super.constructor.apply(this,arguments)}function c(a,b){var d="";switch(a){case"RED":d="srcColor.r";break;case"GREEN":d="srcColor.g";break;case"BLUE":d="srcColor.b";break;case"(R+G+B)/2":d="mod((srcColor.r+srcColor.g+srcColor.b)/2.0, 1.0)";break;case"(R+G+B)/3":d="(srcColor.r+srcColor.g+srcColor.b)/3.0";break;case"MAX":d="max(srcColor.r, max(srcColor.g, srcColor.b))";break;default:throw new Error("Unknown colormap key function "+options.key)}c.super.constructor.call(this,{outputBlendMode:b,swapFrame:!0,fragmentShader:["uniform sampler2D u_colorMap;","void main() {"," vec4 srcColor = getSrcColor();"," setFragColor(texture2D(u_colorMap, vec2(("+d+"), 0)));","}"]})}a.ColorMap=a.defineClass(b,a.Component,{mapCycleModes:{SINGLE:1,ONBEATRANDOM:2,ONBEATSEQUENTIAL:3},init:function(a,c,d){b.super.init.call(this,a,c,d),this.colorMaps=_.map(this.maps,function(a){return this._buildColorMap(a)},this),this.currentMap=0,this.program.init(a)},update:function(){if(this.main.analyser.beat)switch(this.mapCycleMode){case this.mapCycleModes.ONBEATRANDOM:this.currentMap=Math.floor(Math.random()*this.colorMaps.length);break;case this.mapCycleModes.ONBEATSEQUENTIAL:this.currentMap=(this.currentMap+1)%this.colorMaps.length}this.program.run(this.parent.fm,null,this.colorMaps[this.currentMap])},destroy:function(){b.super.destroy.call(this),this.program.cleanup()},_buildColorMap:function(b){var c=this.gl;b=_.sortBy(b,function(a){return a.index});var d=_.map(b,function(a){return a.index});if(_.uniq(d).length!=d.length)throw new Error("map cannot have repeated indices");b=_.map(b,function(b){var c=a.parseColor(b.color);return{color:c,index:b.index}});var e=_.first(b);0!==e.index&&b.splice(0,0,{color:e.color,index:0});var f=_.last(b);255!==f.index&&b.push({color:f.color,index:255});var g=new Uint8Array(768),h=0,i=_.zip(_.first(b,b.length-1),_.last(b,b.length-1));_.each(i,function(a){var b=a[0],c=a[1],d=c.index-b.index;_.times(d,function(a){g[h++]=Math.floor((b.color[0]*(255-a)+c.color[0]*a)/255),g[h++]=Math.floor((b.color[1]*(255-a)+c.color[1]*a)/255),g[h++]=Math.floor((b.color[2]*(255-a)+c.color[2]*a)/255)})}),g[h++]=f.color[0],g[h++]=f.color[1],g[h++]=f.color[2];var j=c.createTexture();return c.bindTexture(c.TEXTURE_2D,j),c.texImage2D(c.TEXTURE_2D,0,c.RGB,256,1,0,c.RGB,c.UNSIGNED_BYTE,g),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MAG_FILTER,c.NEAREST),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MIN_FILTER,c.NEAREST),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_S,c.CLAMP_TO_EDGE),c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_T,c.CLAMP_TO_EDGE),j}}),a.ColorMapProgram=a.defineClass(c,a.QuadBoxProgram,{draw:function(a){this.setUniform("u_colorMap","texture2D",a),c.super.draw.call(this)}}),b.ui={disp:"Color Map",type:"ColorMap",schema:{maps:{type:"array",items:{type:"array",title:"Map",items:{type:"object",properties:{color:{type:"string",title:"Color",format:"color","default":"#FFFFFF"},index:{type:"number",title:"Index",minimum:0,maximum:255}}}}},key:{type:"string",title:"Map key","enum":["RED","GREEN","BLUE","(R+G+B)/2","(R+G+B)/3","MAX"],"default":"RED"},mapCycleMode:{type:"string",title:"Map Cycle Mode","enum":["SINGLE","ONBEATRANDOM","ONBEATSEQUENTIAL"],"default":"SINGLE"},output:{type:"string",title:"Output blend mode","enum":_.keys(a.blendModes),"default":"REPLACE"}}}}(Webvs),function(a){function b(c){if(a.checkRequiredOptions(c,["mode","color","outColor"]),c=_.defaults(c,{mode:"BELOW",color:"#202020",outColor:"#202020",level:0}),this.mode=_.indexOf(this.modes,c.mode),-1==this.mode)throw new Error("ColorClip: invalid mode");this.color=a.parseColorNorm(c.color),this.outColor=a.parseColorNorm(c.outColor),this.level=c.level,this.program=new a.ColorClipProgram,b.super.constructor.apply(this,arguments)}function c(){c.super.constructor({swapFrame:!0,fragmentShader:["uniform int u_mode;","uniform vec3 u_color;","uniform vec3 u_outColor;","uniform float u_level;","void main() {"," vec4 inColor4 = getSrcColor();"," vec3 inColor = inColor4.rgb;"," bool clip = false;"," if(u_mode == 0) {"," clip = all(lessThanEqual(inColor, u_color));"," }"," if(u_mode == 1) {"," clip = all(greaterThanEqual(inColor, u_color));"," }"," if(u_mode == 2) {"," clip = (distance(inColor, u_color) <= u_level*0.5);"," }"," if(clip) {"," setFragColor(vec4(u_outColor, inColor4.a));"," } else {"," setFragColor(inColor4);"," }","}"]})}a.ColorClip=a.defineClass(b,a.Component,{modes:["BELOW","ABOVE","NEAR"],componentName:"ChannelShift",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){this.program.run(this.parent.fm,null,this.mode,this.color,this.outColor,this.level)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}}),a.ColorClipProgram=a.defineClass(c,a.QuadBoxProgram,{draw:function(a,b,d,e){this.setUniform("u_mode","1i",a),this.setUniform.apply(this,["u_color","3f"].concat(b)),this.setUniform.apply(this,["u_outColor","3f"].concat(d)),this.setUniform("u_level","1f",e),c.super.draw.call(this)}})}(Webvs),function(a){function b(c){a.checkRequiredOptions(c,["code"]),c=_.defaults(c,{gridW:16,gridH:16,noGrid:!1,bFilter:!0,compat:!1,coord:"POLAR"});var d;if(!_.isObject(c.code))throw new Error("Invalid Dynamic movement code");d=c.code;var e=new a.ExprCodeGenerator(d,["x","y","r","d","b"]);this.code=e.generateJs(["init","onBeat","perFrame"]);var f=e.generateGlsl(["perPixel"],["x","y","d","r"],this.code);this.inited=!1,this.noGrid=c.noGrid,this.gridW=c.gridW,this.gridH=c.gridH,this.coordMode=c.coord,this.bFilter=c.bFilter,this.compat=c.compat,this.program=this.noGrid?new a.DMovProgramNG(this.coordMode,this.bFilter,this.compat,this.code.hasRandom,f):new a.DMovProgram(this.coordMode,this.bFilter,this.compat,this.code.hasRandom,f),b.super.constructor.apply(this,arguments)}function c(a,b,d,e,f){var g=[f,this.glslFilter(b,d),"void main() {",e?"__randSeed = v_position;":""," x = v_position.x*2.0-1.0;"," y = -(v_position.y*2.0-1.0);",this.glslRectToPolar(a)," perPixel();",this.glslPolarToRect(a)," setFragColor(vec4(filter(vec2(x, -y)), 1));","}"];c.super.constructor.call(this,{fragmentShader:g,swapFrame:!0})}function d(a,b,c,e,f){var g=["attribute vec2 a_position;","varying vec2 v_newPoint;","uniform int u_coordMode;",f,"void main() {",e?"__randSeed = a_position;":""," x = a_position.x;"," y = -a_position.y;",this.glslRectToPolar(a)," perPixel();",this.glslPolarToRect(a)," v_newPoint = vec2(x,-y);"," setPosition(a_position);","}"],h=["varying vec2 v_newPoint;",this.glslFilter(b,c),"void main() {"," setFragColor(vec4(filter(v_newPoint), 1));","}"];d.super.constructor.call(this,{fragmentShader:h,vertexShader:g,swapFrame:!0})}a.DynamicMovement=a.defineClass(b,a.Component,{componentName:"DynamicMovement",init:function(c,d,e){if(b.super.init.call(this,c,d,e),this.program.init(c),this.code.setup(d,e),!this.noGrid){for(var f=a.clamp(this.gridW,1,this.main.canvas.width),g=a.clamp(this.gridH,1,this.main.canvas.height),h=2*(f/this.main.canvas.width),i=2*(g/this.main.canvas.height),j=Math.ceil(this.main.canvas.width/f),k=Math.ceil(this.main.canvas.height/g),l=new Float32Array(2*6*j*k),m=0,n=-1,o=-1,p=0;k>p;p++){for(var q=0;j>q;q++){var r=Math.min(n+h,1),s=Math.min(o+i,1);l[m++]=n,l[m++]=o,l[m++]=r,l[m++]=o,l[m++]=n,l[m++]=s,l[m++]=r,l[m++]=o,l[m++]=r,l[m++]=s,l[m++]=n,l[m++]=s,n+=h}n=-1,o+=i}this.gridVertices=l,this.gridVerticesSize=m/2}},update:function(){var a=this.code;this.inited||(a.init(),this.inited=!0);var b=this.main.analyser.beat;a.b=b?1:0,a.perFrame(),b&&a.onBeat(),this.noGrid?this.program.run(this.parent.fm,null,this.code):this.program.run(this.parent.fm,null,this.code,this.gridVertices,this.gridVerticesSize)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}});var e={glslRectToPolar:function(a){return"POLAR"===a?["float ar = u_resolution.x/u_resolution.y;","x=x*ar;","d = distance(vec2(x, y), vec2(0,0))/sqrt(2.0);","r = mod(atan(y, x)+PI*0.5, 2.0*PI);"].join("\n"):""},glslPolarToRect:function(a){return"POLAR"===a?["d = d*sqrt(2.0);","x = d*sin(r)/ar;","y = -d*cos(r);"].join("\n"):""},glslFilter:function(a,b){return a&&!b?["vec3 filter(vec2 point) {"," vec2 texel = 1.0/(u_resolution-vec2(1,1));"," vec2 coord = (point+1.0)/2.0;"," vec2 cornoff = fract(coord/texel);"," vec2 corn = floor(coord/texel)*texel;"," vec3 tl = getSrcColorAtPos(corn).rgb;"," vec3 tr = getSrcColorAtPos(corn + vec2(texel.x, 0)).rgb;"," vec3 bl = getSrcColorAtPos(corn + vec2(0, texel.y)).rgb;"," vec3 br = getSrcColorAtPos(corn + texel).rgb;"," vec3 pt = mix(tl, tr, cornoff.x);"," vec3 pb = mix(bl, br, cornoff.x);"," return mix(pt, pb, cornoff.y);","}"].join("\n"):a&&b?["vec3 filter(vec2 point) {"," vec2 texel = 1.0/(u_resolution-vec2(1,1));"," vec2 coord = (point+1.0)/2.0;"," vec2 corn = floor(coord/texel)*texel;"," ivec2 cornoff = (ivec2(fract(coord/texel)*255.0));"," ivec3 tl = ivec3(255.0 * getSrcColorAtPos(corn).rgb);"," ivec3 tr = ivec3(255.0 * getSrcColorAtPos(corn + vec2(texel.x, 0)).rgb);"," ivec3 bl = ivec3(255.0 * getSrcColorAtPos(corn + vec2(0, texel.y)).rgb);"," ivec3 br = ivec3(255.0 * getSrcColorAtPos(corn + texel).rgb);"," #define bt(i, j) int((float(i)/255.0)*float(j))"," int a1 = bt(255-cornoff.x,255-cornoff.y);"," int a2 = bt(cornoff.x ,255-cornoff.y);"," int a3 = bt(255-cornoff.x,cornoff.y);"," int a4 = bt(cornoff.x ,cornoff.y);"," float r = float(bt(a1,tl.r) + bt(a2,tr.r) + bt(a3,bl.r) + bt(a4,br.r))/255.0;"," float g = float(bt(a1,tl.g) + bt(a2,tr.g) + bt(a3,bl.g) + bt(a4,br.g))/255.0;"," float b = float(bt(a1,tl.b) + bt(a2,tr.b) + bt(a3,bl.b) + bt(a4,br.b))/255.0;"," return vec3(r,g,b);","}"].join("\n"):["vec3 filter(vec2 point) {"," return getSrcColorAtPos((point+1.0)/2.0).rgb;","}"].join("\n")}};a.DMovProgramNG=a.defineClass(c,a.QuadBoxProgram,e,{draw:function(a){a.bindUniforms(this),c.super.draw.call(this)}}),a.DMovProgram=a.defineClass(d,a.ShaderProgram,e,{draw:function(a,b,c){a.bindUniforms(this),this.setVertexAttribArray("a_position",b,2,this.gl.FLOAT,!1,0,0),this.gl.drawArrays(this.gl.TRIANGLES,0,c)}}),b.ui={type:"DynamicMovement",disp:"Dynamic Movement",schema:{code:{type:"object",title:"Code","default":{},properties:{init:{type:"string",title:"Init"},onBeat:{type:"string",title:"On Beat"},perFrame:{type:"string",title:"Per Frame"},perPixel:{type:"string",title:"Per Point"}}},gridW:{type:"number",title:"Grid Width","default":16},gridH:{type:"number",title:"Grid Height","default":16},coord:{type:"string",title:"Coordinate System","enum":["POLAR","RECT"],"default":"POLAR"}},form:[{key:"code.init",type:"textarea"},{key:"code.onBeat",type:"textarea"},{key:"code.perFrame",type:"textarea"},{key:"code.perPixel",type:"textarea"},"gridW","gridH","coord"]}}(Webvs),function(a){function b(a){a=_.defaults(a,{bFilter:!0,coord:"POLAR",compat:!1}),b.super.constructor.call(this,{noGrid:!0,bFilter:a.bFilter,compat:a.compat,coord:a.coord,code:a.code}),this.options=a}a.Movement=a.defineClass(b,a.DynamicMovement)}(Webvs),function(a){function b(a){if(a=_.defaults(a,{channel:"RGB",onBeatRandom:!1}),this.channel=d.indexOf(a.channel),-1==this.channel)throw new Error("Invalid Channel");this.onBeatRandom=a.onBeatRandom,this.program=new c,b.super.constructor.apply(this,arguments)}function c(){c.super.constructor.call(this,{swapFrame:!0,fragmentShader:["uniform int u_channel;","void main() {"," vec3 color = getSrcColor().rgb;",_.flatMap(d,function(a,b){return["if(u_channel == "+b+") {"," setFragColor(vec4(color."+a.toLowerCase()+",1));","}"]}).join("\n"),"}"]})}var d=["RGB","RBG","BRG","BGR","GBR","GRB"];a.ChannelShift=a.defineClass(b,a.Component,{componentName:"ChannelShift",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){this.onBeatRandom&&this.main.analyser.beat&&(this.channel=Math.floor(Math.random()*d.length)),this.program.run(this.parent.fm,null,this.channel)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}}),a.ChannelShiftProgram=a.defineClass(c,a.QuadBoxProgram,{draw:function(a){this.setUniform("u_channel","1i",a),c.super.draw.call(this)}}),b.ui={disp:"Channel Shift",type:"ChannelShift",schema:{channel:{type:"string",title:"Channel","enum":d},onBeatRandom:{type:"boolean",title:"On beat random"}}}}(Webvs),function(a){function b(b){b=_.defaults(b,{color:"#ffffff",invert:!1,blendMode:"REPLACE"}),this.tone=a.parseColorNorm(b.color),this.invert=b.invert,this.program=new c(a.getBlendMode(b.blendMode))}function c(a){c.super.constructor.call(this,{outputBlendMode:a,swapFrame:!0,fragmentShader:["uniform vec3 u_tone;","uniform bool u_invert;","void main() {"," vec4 srcColor = getSrcColor();"," float depth = max(srcColor.r, max(srcColor.g, srcColor.b));"," if(u_invert) {"," depth = 1.0-depth;"," }"," setFragColor(vec4(depth*u_tone, 1));","}"]})}a.UniqueTone=a.defineClass(b,a.Component,{init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){this.program.run(this.parent.fm,null,this.tone,this.invert)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()}}),a.UniqueToneProgram=a.defineClass(c,a.QuadBoxProgram,{draw:function(a,b){this.setUniform.apply(this,["u_tone","3f"].concat(a)),this.setUniform("u_invert","1f",b?1:0),c.super.draw.call(this)}})}(Webvs),function(a){function b(d){a.checkRequiredOptions(d,["code"]),d=_.defaults(d,{source:"SPECTRUM",drawMode:"LINES",colors:["#ffffff"]});var e;if(!_.isObject(d.code))throw new Error("Invalid superscope");e=d.code;var f=new a.ExprCodeGenerator(e,["n","v","i","x","y","b","red","green","blue"]);this.code=f.generateJs(["init","onBeat","perFrame","perPoint"]),this.code.n=100,this.clone=d.clone||1,this.spectrum="SPECTRUM"==d.source,this.dots="DOTS"==d.drawMode,this.colors=_.map(d.colors,a.parseColorNorm),this.currentColor=[],this.maxStep=100,this.step=this.maxStep,this.colorId=0,this.colorStep=[0,0,0],this.thickness=d.thickness?d.thickness:1,this.inited=!1,this.program=new c,b.super.constructor.apply(this,arguments)}function c(){c.super.constructor.call(this,{copyOnSwap:!0,vertexShader:["attribute vec2 a_position;","attribute vec3 a_color;","varying vec3 v_color;","uniform float u_pointSize;","void main() {"," gl_PointSize = u_pointSize;"," setPosition(clamp(a_position, vec2(-1,-1), vec2(1,1)));"," v_color = a_color;","}"],fragmentShader:["varying vec3 v_color;","void main() {"," setFragColor(vec4(v_color, 1));","}"]})}a.SuperScope=a.defineClass(b,a.Component,{componentName:"SuperScope",init:function(c,d,e){b.super.init.call(this,c,d,e),this.program.init(c),this.code.setup(d,this),this.code=a.CodeInstance.clone(this.code,this.clone)},update:function(){this._stepColor(),_.each(this.code,function(a){this.drawScope(a,!this.inited)},this),this.inited=!0},drawScope:function(a,b){this.gl,a.red=this.currentColor[0],a.green=this.currentColor[1],a.blue=this.currentColor[2],b&&a.init();var c=this.main.analyser.beat;a.b=c?1:0,a.perFrame(),c&&a.onBeat();for(var d=Math.floor(a.n),e=this.spectrum?this.main.analyser.getSpectrum():this.main.analyser.getWaveform(),f=e.length/d,g=0,h=0,i=new Float32Array(2*(this.dots?d:2*d-2)),j=new Float32Array(3*(this.dots?d:2*d-2)),k=0;d>k;k++){for(var l=0,m=0,n=Math.floor(k*f);(k+1)*f>n;n++,m++)l+=e[n];l/=m;var o=k/(d>1?d-1:1);a.i=o,a.v=l,a.perPoint(),i[g++]=a.x,i[g++]=-1*a.y,0===k||k==d-1||this.dots||(i[g++]=a.x,i[g++]=-1*a.y),this.dots?(j[h++]=a.red,j[h++]=a.green,j[h++]=a.blue):0!==k&&(j[h++]=a.red,j[h++]=a.green,j[h++]=a.blue,j[h++]=a.red,j[h++]=a.green,j[h++]=a.blue)}this.program.run(this.parent.fm,null,i,j,this.dots,this.thickness)},destroy:function(){b.super.destroy.call(this),this.program.cleanup()},_stepColor:function(){var a;if(this.colors.length>1)if(this.step==this.maxStep){var b=this.colors[this.colorId];this.colorId=(this.colorId+1)%this.colors.length;var c=this.colors[this.colorId];for(a=0;3>a;a++)this.colorStep[a]=(c[a]-b[a])/this.maxStep;for(this.step=0,a=0;3>a;a++)this.currentColor[a]=b[a]}else{for(a=0;3>a;a++)this.currentColor[a]+=this.colorStep[a];this.step++}else this.currentColor=this.colors[0]}}),a.SuperScopeShader=a.defineClass(c,a.ShaderProgram,{draw:function(a,b,c,d){var e=this.gl;this.setUniform("u_pointSize","1f",d),this.setVertexAttribArray("a_position",a,2,e.FLOAT,!1,0,0),this.setVertexAttribArray("a_color",b,3,e.FLOAT,!1,0,0);var f;c||(f=e.getParameter(e.LINE_WIDTH),e.lineWidth(d)),e.drawArrays(c?e.POINTS:e.LINES,0,a.length/2),c||e.lineWidth(f)}}),b.ui={disp:"SuperScope",type:"SuperScope",schema:{code:{type:"object",title:"Code","default":{},properties:{init:{type:"string",title:"Init"},onBeat:{type:"string",title:"On Beat"},perFrame:{type:"string",title:"Per Frame"},perPoint:{type:"string",title:"Per Point"}}},source:{type:"string",title:"Source","default":"WAVEFORM","enum":["WAVEFORM","SPECTRUM"]},drawMode:{type:"string",title:"Draw Mode","default":"LINES","enum":["DOTS","LINES"]},colors:{type:"array",title:"Cycle Colors",items:{type:"string",format:"color","default":"#FFFFFF"}}}}}(Webvs),function(a){function b(a){a=_.defaults(a,{drawMode:"SOLID",source:"WAVEFORM",align:"CENTER",colors:["#ffffff"]});var c={};"SOLID"!=a.drawMode?(c.init="n=w;",c.perPoint={TOP:"x=i*2-1; y=-v/2-0.5;",CENTER:"x=i*2-1; y=-v/2;",BOTTOM:"x=i*2-1; y=v/2+0.5;"}[a.align]):(c.init="n=w*2;",c.perFrame="c=0;",c.perPoint="SPECTRUM"==a.source?{TOP:"x=i*2-1; y=if(c%2,0,-v/2-0.5); c=c+1;",CENTER:"x=i*2-1; y=if(c%2,0.5,-v/2); c=c+1;",BOTTOM:"x=i*2-1; y=if(c%2,0,v/2+0.5); c=c+1;"}[a.align]:{TOP:"x=i*2-1; y=if(c%2,-0.5,-v/2-0.5); c=c+1;",CENTER:"x=i*2-1; y=if(c%2,0,-v/2); c=c+1;",BOTTOM:"x=i*2-1; y=if(c%2,0.5,v/2+0.5); c=c+1;"}[a.align]),b.super.constructor.call(this,{source:a.source,drawMode:"SOLID"==a.drawMode?"LINES":a.drawMode,colors:a.colors,code:c}),this.options=a}a.Simple=a.defineClass(b,a.SuperScope)}(Webvs),function(a){function b(d){a.checkRequiredOptions(d,["code","imageSrc"]),d=_.defaults(d,{source:"SPECTRUM",resizing:!1,wrapAround:!1,colorFiltering:!0}),this.resizing=d.resizing,this.colorFiltering=d.colorFiltering,this.wrapAround=d.wrapAround,this.imageSrc=d.imageSrc;var e=new a.ExprCodeGenerator(d.code,["n","v","i","x","y","b","sizex","sizey","red","green","blue"]);this.code=e.generateJs(["init","onBeat","perFrame","perPoint"]),this.code.n=100,this.spectrum="SPECTRUM"==d.source,this._inited=!1,this.program=new c,b.super.constructor.apply(this,arguments)}function c(){c.super.constructor.call(this,{vertexShader:["uniform bool u_colorFilter;","attribute vec2 a_texVertex;","attribute vec2 a_vertex;","attribute vec3 a_color;","varying vec2 v_texVertex;","varying vec3 v_color;","void main() {"," if(u_colorFilter) {"," v_color = a_color;"," }"," v_texVertex = a_texVertex;"," setPosition(a_vertex);","}"],fragmentShader:["uniform bool u_colorFilter;","uniform sampler2D u_image;","varying vec2 v_texVertex;","varying vec3 v_color;","void main() {"," vec3 outColor = texture2D(u_image, v_texVertex).rgb;"," if(u_colorFilter) {"," outColor = outColor*v_color;"," }"," setFragColor(vec4(outColor, 1));","}"]})}a.Texer=a.defineClass(b,a.Component,{componentName:"Texer",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a),this.code.setup(c,this);var e=new Image;e.src=c.getResource(this.imageSrc),this.imagewidth=e.width,this.imageHeight=e.height,this.texture=a.createTexture(),a.bindTexture(a.TEXTURE_2D,this.texture),a.texImage2D(a.TEXTURE_2D,0,a.RGBA,a.RGBA,a.UNSIGNED_BYTE,e),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.LINEAR),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE)},update:function(){function a(a,b,c,d,e,f,l){-1-c>a||a>1||-1-d>b||b>1||(g.push(a,b,a+c,b,a+c,b+d,a,b+d),h.push(0,0,1,0,1,1,0,1),j&&j.push(e,f,l,e,f,l,e,f,l,e,f,l),i.push(k+0,k+1,k+2,k+0,k+2,k+3),k+=4)}var b=this.code;this._inited||b.init();var c=this.main.analyser.beat;b.b=c?1:0,b.perFrame(),c&&b.onBeat();for(var d=Math.floor(b.n),e=this.spectrum?this.main.analyser.getSpectrum():this.main.analyser.getWaveform(),f=e.length/d,g=[],h=[],i=[],j=this.colorFiltering?[]:null,k=0,l=2*(this.imagewidth/this.parent.fm.width),m=2*(this.imageHeight/this.parent.fm.height),n=0;d>n;n++){for(var o=0,p=0,q=Math.floor(n*f);(n+1)*f>q;q++,p++)o+=e[q];o/=p;var r=n/(d-1);b.i=r,b.v=o,b.sizex=1,b.sizey=1,b.red=1,b.green=1,b.blue=1,b.perPoint();var s=l,t=m;this.resizing&&(s*=b.sizex,t*=b.sizey);var u=b.x-s/2,v=-b.y-t/2;if(a(u,v,s,t,b.red,b.green,b.blue),this.wrapAround){var w=-1>u?2:u>1-s?-2:0,x=-1>v?2:v>1-t?-2:0;w&&a(w+u,v,s,t,b.red,b.green,b.blue),x&&a(u,x+v,s,t,b.red,b.green,b.blue),w&&x&&a(w+u,x+v,s,t,b.red,b.green,b.blue)}}this.program.run(this.parent.fm,null,new Float32Array(g),new Float32Array(h),new Uint16Array(i),j?new Float32Array(j):null,this.texture)},destroy:function(){b.super.destroy.call(this),this.gl.deleteTexture(this.texture),this.program.cleanup()}}),a.TexerProgram=a.defineClass(c,a.ShaderProgram,{draw:function(a,b,c,d,e){this.setUniform("u_image","texture2D",e),this.setVertexAttribArray("a_vertex",a),this.setVertexAttribArray("a_texVertex",b),d?(this.setUniform("u_colorFilter","1f",1),this.setVertexAttribArray("a_color",d,3)):this.setUniform("u_colorFilter","1f",0),this.setElementArray(c),this.gl.drawElements(this.gl.TRIANGLES,c.length,this.gl.UNSIGNED_SHORT,0)}})}(Webvs),function(a){function b(c){c=_.defaults(c,{n:0,color:"#000000",blendMode:"REPLACE"}),this.n=c.n,this.color=a.parseColorNorm(c.color),this.outputBlendMode=a.blendModes[c.blendMode],this.prevBeat=!1,this.beatCount=0,this.program=new a.ClearScreenProgram(this.outputBlendMode),b.super.constructor.apply(this,arguments)}a.ClearScreen=a.defineClass(b,a.Component,{componentName:"ClearScreen",init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a)},update:function(){var a=!1;0===this.n?a=!0:(this.main.analyser.beat&&!this.prevBeat&&(this.beatCount++,this.beatCount==this.n&&(a=!0,this.beatCount=0)),this.prevBeat=this.main.analyser.beat),a&&this.program.run(this.parent.fm,null,this.color)},destroy:function(){this.program.cleanup()}}),b.ui={type:"ClearScreen",disp:"Clear Screen",schema:{n:{type:"number",title:"Clear on beat (0 = always clear)","default":0},color:{type:"string",title:"Clear color",format:"color","default":"#000000"},blendMode:{type:"string",title:"Blend Mode","enum":_.keys(a.blendModes)}}}}(Webvs),function(a){function b(c){a.checkRequiredOptions(c,["src","x","y"]),this.x=c.x,this.y=c.y,this.src=c.src,this.program=new a.PictureProgram,b.super.constructor.apply(this,arguments)}function c(){c.super.constructor.call(this,{copyOnSwap:!0,vertexShader:["attribute vec2 a_texVertex;","uniform vec2 u_pos;","uniform vec2 u_texRes;","varying vec2 v_texCoord;","void main() {"," v_texCoord = a_texVertex;"," setPosition(a_texVertex*(u_texRes/u_resolution)*vec2(2,-2)+u_pos);","}"],fragmentShader:["uniform sampler2D u_image;","varying vec2 v_texCoord;","void main() {"," setFragColor(texture2D(u_image, v_texCoord));","}"]})}a.Picture=a.defineClass(b,a.Component,{init:function(a,c,d){b.super.init.call(this,a,c,d),this.program.init(a);var e=new Image;e.src=c.getResource(this.src),this.width=e.width,this.height=e.height,this.texture=a.createTexture(),a.bindTexture(a.TEXTURE_2D,this.texture),a.texImage2D(a.TEXTURE_2D,0,a.RGBA,a.RGBA,a.UNSIGNED_BYTE,e),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.NEAREST),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.NEAREST),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE)},update:function(){this.program.run(this.parent.fm,null,this.x,this.y,this.texture,this.width,this.height)},destroy:function(){this.program.cleanup(),this.gl.deleteTexture(this.texture)}}),a.PictureProgram=a.defineClass(c,a.ShaderProgram,{draw:function(a,b,c,d,e){this.setUniform("u_pos","2f",a,-b),this.setUniform("u_texRes","2f",d,e),this.setUniform("u_image","texture2D",c),this.setVertexAttribArray("a_texVertex",new Float32Array([0,0,0,1,1,1,0,0,1,1,1,0])),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}})}(Webvs); \ No newline at end of file