From c0626029a8c433d21609fc3eded02580baf4cc80 Mon Sep 17 00:00:00 2001 From: Thomas Davis Date: Wed, 9 Jul 2014 15:11:00 -0400 Subject: [PATCH] Updated packages via auto-update.js --- ajax/libs/bacon.js/0.7.17/Bacon.js | 3086 ++++ ajax/libs/bacon.js/package.json | 2 +- ajax/libs/jsonld/0.2.15/jsonld.js | 6850 ++++++++ ajax/libs/jsonld/package.json | 2 +- ajax/libs/p2.js/0.6.0/p2.js | 13206 +++++++++++++++ ajax/libs/p2.js/0.6.0/p2.min.js | 28 + ajax/libs/p2.js/0.6.0/p2.renderer.js | 17804 +++++++++++++++++++++ ajax/libs/p2.js/0.6.0/p2.renderer.min.js | 7 + ajax/libs/p2.js/package.json | 2 +- 9 files changed, 40984 insertions(+), 3 deletions(-) create mode 100644 ajax/libs/bacon.js/0.7.17/Bacon.js create mode 100644 ajax/libs/jsonld/0.2.15/jsonld.js create mode 100644 ajax/libs/p2.js/0.6.0/p2.js create mode 100644 ajax/libs/p2.js/0.6.0/p2.min.js create mode 100644 ajax/libs/p2.js/0.6.0/p2.renderer.js create mode 100644 ajax/libs/p2.js/0.6.0/p2.renderer.min.js diff --git a/ajax/libs/bacon.js/0.7.17/Bacon.js b/ajax/libs/bacon.js/0.7.17/Bacon.js new file mode 100644 index 000000000..c35f99aa4 --- /dev/null +++ b/ajax/libs/bacon.js/0.7.17/Bacon.js @@ -0,0 +1,3086 @@ +(function() { + var Bacon, BufferingSource, Bus, CompositeUnsubscribe, ConsumingSource, DepCache, Desc, Dispatcher, End, Error, Event, EventStream, Initial, Next, None, Observable, Property, PropertyDispatcher, Some, Source, UpdateBarrier, addPropertyInitValueToStream, assert, assertArray, assertEventStream, assertFunction, assertNoArguments, assertString, cloneArray, compositeUnsubscribe, containsDuplicateDeps, convertArgsToFunction, describe, end, eventIdCounter, findDeps, flatMap_, former, idCounter, initial, isArray, isFieldKey, isFunction, isObservable, latterF, liftCallback, makeFunction, makeFunctionArgs, makeFunction_, makeObservable, makeSpawner, next, nop, partiallyApplied, recursionDepth, registerObs, spys, toCombinator, toEvent, toFieldExtractor, toFieldKey, toOption, toSimpleExtractor, withDescription, withMethodCallSupport, _, _ref, + __slice = [].slice, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + Bacon = { + toString: function() { + return "Bacon"; + } + }; + + Bacon.version = '0.7.17'; + + Bacon.fromBinder = function(binder, eventTransformer) { + if (eventTransformer == null) { + eventTransformer = _.id; + } + return new EventStream(describe(Bacon, "fromBinder", binder, eventTransformer), function(sink) { + var unbind, unbinder, unbound; + unbound = false; + unbind = function() { + if (typeof unbinder !== "undefined" && unbinder !== null) { + if (!unbound) { + unbinder(); + } + return unbound = true; + } + }; + unbinder = binder(function() { + var args, event, reply, value, _i, _len; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + value = eventTransformer.apply(null, args); + if (!(isArray(value) && _.last(value) instanceof Event)) { + value = [value]; + } + reply = Bacon.more; + for (_i = 0, _len = value.length; _i < _len; _i++) { + event = value[_i]; + reply = sink(event = toEvent(event)); + if (reply === Bacon.noMore || event.isEnd()) { + if (unbinder != null) { + unbind(); + } else { + Bacon.scheduler.setTimeout(unbind, 0); + } + return reply; + } + } + return reply; + }); + return unbind; + }); + }; + + Bacon.$ = { + asEventStream: function(eventName, selector, eventTransformer) { + var _ref; + if (isFunction(selector)) { + _ref = [selector, null], eventTransformer = _ref[0], selector = _ref[1]; + } + return withDescription(this.selector || this, "asEventStream", eventName, Bacon.fromBinder((function(_this) { + return function(handler) { + _this.on(eventName, selector, handler); + return function() { + return _this.off(eventName, selector, handler); + }; + }; + })(this), eventTransformer)); + } + }; + + if ((_ref = typeof jQuery !== "undefined" && jQuery !== null ? jQuery : typeof Zepto !== "undefined" && Zepto !== null ? Zepto : null) != null) { + _ref.fn.asEventStream = Bacon.$.asEventStream; + } + + Bacon.fromEventTarget = function(target, eventName, eventTransformer) { + var sub, unsub, _ref1, _ref2, _ref3, _ref4; + sub = (_ref1 = target.addEventListener) != null ? _ref1 : (_ref2 = target.addListener) != null ? _ref2 : target.bind; + unsub = (_ref3 = target.removeEventListener) != null ? _ref3 : (_ref4 = target.removeListener) != null ? _ref4 : target.unbind; + return withDescription(Bacon, "fromEventTarget", target, eventName, Bacon.fromBinder(function(handler) { + sub.call(target, eventName, handler); + return function() { + return unsub.call(target, eventName, handler); + }; + }, eventTransformer)); + }; + + Bacon.fromPromise = function(promise, abort) { + return withDescription(Bacon, "fromPromise", promise, Bacon.fromBinder(function(handler) { + promise.then(handler, function(e) { + return handler(new Error(e)); + }); + return function() { + if (abort) { + return typeof promise.abort === "function" ? promise.abort() : void 0; + } + }; + }, (function(value) { + return [value, end()]; + }))); + }; + + Bacon.noMore = [""]; + + Bacon.more = [""]; + + Bacon.later = function(delay, value) { + return withDescription(Bacon, "later", delay, value, Bacon.sequentially(delay, [value])); + }; + + Bacon.sequentially = function(delay, values) { + var index; + index = 0; + return withDescription(Bacon, "sequentially", delay, values, Bacon.fromPoll(delay, function() { + var value; + value = values[index++]; + if (index < values.length) { + return value; + } else if (index === values.length) { + return [value, end()]; + } else { + return end(); + } + })); + }; + + Bacon.repeatedly = function(delay, values) { + var index; + index = 0; + return withDescription(Bacon, "repeatedly", delay, values, Bacon.fromPoll(delay, function() { + return values[index++ % values.length]; + })); + }; + + Bacon.spy = function(spy) { + return spys.push(spy); + }; + + spys = []; + + registerObs = function(obs) { + var spy, _i, _len, _results; + if (spys.length) { + if (!registerObs.running) { + try { + registerObs.running = true; + _results = []; + for (_i = 0, _len = spys.length; _i < _len; _i++) { + spy = spys[_i]; + _results.push(spy(obs)); + } + return _results; + } finally { + delete registerObs.running; + } + } + } + }; + + withMethodCallSupport = function(wrapped) { + return function() { + var args, context, f, methodName; + f = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + if (typeof f === "object" && args.length) { + context = f; + methodName = args[0]; + f = function() { + return context[methodName].apply(context, arguments); + }; + args = args.slice(1); + } + return wrapped.apply(null, [f].concat(__slice.call(args))); + }; + }; + + liftCallback = function(desc, wrapped) { + return withMethodCallSupport(function() { + var args, f, stream; + f = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + stream = partiallyApplied(wrapped, [ + function(values, callback) { + return f.apply(null, __slice.call(values).concat([callback])); + } + ]); + return withDescription.apply(null, [Bacon, desc, f].concat(__slice.call(args), [Bacon.combineAsArray(args).flatMap(stream)])); + }); + }; + + Bacon.fromCallback = liftCallback("fromCallback", function() { + var args, f; + f = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + return Bacon.fromBinder(function(handler) { + makeFunction(f, args)(handler); + return nop; + }, (function(value) { + return [value, end()]; + })); + }); + + Bacon.fromNodeCallback = liftCallback("fromNodeCallback", function() { + var args, f; + f = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + return Bacon.fromBinder(function(handler) { + makeFunction(f, args)(handler); + return nop; + }, function(error, value) { + if (error) { + return [new Error(error), end()]; + } + return [value, end()]; + }); + }); + + Bacon.fromPoll = function(delay, poll) { + return withDescription(Bacon, "fromPoll", delay, poll, Bacon.fromBinder((function(handler) { + var id; + id = Bacon.scheduler.setInterval(handler, delay); + return function() { + return Bacon.scheduler.clearInterval(id); + }; + }), poll)); + }; + + Bacon.interval = function(delay, value) { + if (value == null) { + value = {}; + } + return withDescription(Bacon, "interval", delay, value, Bacon.fromPoll(delay, function() { + return next(value); + })); + }; + + Bacon.constant = function(value) { + return new Property(describe(Bacon, "constant", value), function(sink) { + sink(initial(value)); + sink(end()); + return nop; + }); + }; + + Bacon.never = function() { + return withDescription(Bacon, "never", Bacon.fromArray([])); + }; + + Bacon.once = function(value) { + return withDescription(Bacon, "once", value, Bacon.fromArray([value])); + }; + + Bacon.fromArray = function(values) { + assertArray(values); + values = cloneArray(values); + return new EventStream(describe(Bacon, "fromArray", values), function(sink) { + var reply, unsubd, value; + unsubd = false; + reply = Bacon.more; + while ((reply !== Bacon.noMore) && !unsubd) { + if (_.empty(values)) { + sink(end()); + reply = Bacon.noMore; + } else { + value = values.splice(0, 1)[0]; + reply = sink(toEvent(value)); + } + } + return function() { + return unsubd = true; + }; + }); + }; + + Bacon.mergeAll = function() { + var streams; + streams = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (isArray(streams[0])) { + streams = streams[0]; + } + if (streams.length) { + return new EventStream(describe.apply(null, [Bacon, "mergeAll"].concat(__slice.call(streams))), function(sink) { + var ends, sinks, smartSink; + ends = 0; + smartSink = function(obs) { + return function(unsubBoth) { + return obs.subscribeInternal(function(event) { + var reply; + if (event.isEnd()) { + ends++; + if (ends === streams.length) { + return sink(end()); + } else { + return Bacon.more; + } + } else { + reply = sink(event); + if (reply === Bacon.noMore) { + unsubBoth(); + } + return reply; + } + }); + }; + }; + sinks = _.map(smartSink, streams); + return compositeUnsubscribe.apply(null, sinks); + }); + } else { + return Bacon.never(); + } + }; + + Bacon.zipAsArray = function() { + var streams; + streams = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (isArray(streams[0])) { + streams = streams[0]; + } + return withDescription.apply(null, [Bacon, "zipAsArray"].concat(__slice.call(streams), [Bacon.zipWith(streams, function() { + var xs; + xs = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return xs; + })])); + }; + + Bacon.zipWith = function() { + var f, streams, _ref1; + f = arguments[0], streams = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + if (!isFunction(f)) { + _ref1 = [f, streams[0]], streams = _ref1[0], f = _ref1[1]; + } + streams = _.map((function(s) { + return s.toEventStream(); + }), streams); + return withDescription.apply(null, [Bacon, "zipWith", f].concat(__slice.call(streams), [Bacon.when(streams, f)])); + }; + + Bacon.groupSimultaneous = function() { + var s, sources, streams; + streams = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (streams.length === 1 && isArray(streams[0])) { + streams = streams[0]; + } + sources = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = streams.length; _i < _len; _i++) { + s = streams[_i]; + _results.push(new BufferingSource(s)); + } + return _results; + })(); + return withDescription.apply(null, [Bacon, "groupSimultaneous"].concat(__slice.call(streams), [Bacon.when(sources, (function() { + var xs; + xs = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return xs; + }))])); + }; + + Bacon.combineAsArray = function() { + var index, s, sources, stream, streams, _i, _len; + streams = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (streams.length === 1 && isArray(streams[0])) { + streams = streams[0]; + } + for (index = _i = 0, _len = streams.length; _i < _len; index = ++_i) { + stream = streams[index]; + if (!(isObservable(stream))) { + streams[index] = Bacon.constant(stream); + } + } + if (streams.length) { + sources = (function() { + var _j, _len1, _results; + _results = []; + for (_j = 0, _len1 = streams.length; _j < _len1; _j++) { + s = streams[_j]; + _results.push(new Source(s, true, s.subscribeInternal)); + } + return _results; + })(); + return withDescription.apply(null, [Bacon, "combineAsArray"].concat(__slice.call(streams), [Bacon.when(sources, (function() { + var xs; + xs = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return xs; + })).toProperty()])); + } else { + return Bacon.constant([]); + } + }; + + Bacon.onValues = function() { + var f, streams, _i; + streams = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), f = arguments[_i++]; + return Bacon.combineAsArray(streams).onValues(f); + }; + + Bacon.combineWith = function() { + var f, streams; + f = arguments[0], streams = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + return withDescription.apply(null, [Bacon, "combineWith", f].concat(__slice.call(streams), [Bacon.combineAsArray(streams).map(function(values) { + return f.apply(null, values); + })])); + }; + + Bacon.combineTemplate = function(template) { + var applyStreamValue, combinator, compile, compileTemplate, constantValue, current, funcs, mkContext, setValue, streams; + funcs = []; + streams = []; + current = function(ctxStack) { + return ctxStack[ctxStack.length - 1]; + }; + setValue = function(ctxStack, key, value) { + return current(ctxStack)[key] = value; + }; + applyStreamValue = function(key, index) { + return function(ctxStack, values) { + return setValue(ctxStack, key, values[index]); + }; + }; + constantValue = function(key, value) { + return function(ctxStack) { + return setValue(ctxStack, key, value); + }; + }; + mkContext = function(template) { + if (isArray(template)) { + return []; + } else { + return {}; + } + }; + compile = function(key, value) { + var popContext, pushContext; + if (isObservable(value)) { + streams.push(value); + return funcs.push(applyStreamValue(key, streams.length - 1)); + } else if (value === Object(value) && typeof value !== "function" && !(value instanceof RegExp) && !(value instanceof Date)) { + pushContext = function(key) { + return function(ctxStack) { + var newContext; + newContext = mkContext(value); + setValue(ctxStack, key, newContext); + return ctxStack.push(newContext); + }; + }; + popContext = function(ctxStack) { + return ctxStack.pop(); + }; + funcs.push(pushContext(key)); + compileTemplate(value); + return funcs.push(popContext); + } else { + return funcs.push(constantValue(key, value)); + } + }; + compileTemplate = function(template) { + return _.each(template, compile); + }; + compileTemplate(template); + combinator = function(values) { + var ctxStack, f, rootContext, _i, _len; + rootContext = mkContext(template); + ctxStack = [rootContext]; + for (_i = 0, _len = funcs.length; _i < _len; _i++) { + f = funcs[_i]; + f(ctxStack, values); + } + return rootContext; + }; + return withDescription(Bacon, "combineTemplate", template, Bacon.combineAsArray(streams).map(combinator)); + }; + + Bacon.retry = function(options) { + var delay, isRetryable, maxRetries, retries, retry, source; + if (!isFunction(options.source)) { + throw "'source' option has to be a function"; + } + source = options.source; + retries = options.retries || 0; + maxRetries = options.maxRetries || retries; + delay = options.delay || function() { + return 0; + }; + isRetryable = options.isRetryable || function() { + return true; + }; + retry = function(context) { + var delayedRetry, nextAttemptOptions; + nextAttemptOptions = { + source: source, + retries: retries - 1, + maxRetries: maxRetries, + delay: delay, + isRetryable: isRetryable + }; + delayedRetry = function() { + return Bacon.retry(nextAttemptOptions); + }; + return Bacon.later(delay(context)).filter(false).concat(Bacon.once().flatMap(delayedRetry)); + }; + return withDescription(Bacon, "retry", options, source().flatMapError(function(e) { + if (isRetryable(e) && retries > 0) { + return retry({ + error: e, + retriesDone: maxRetries - retries + }); + } else { + return Bacon.once(new Bacon.Error(e)); + } + })); + }; + + eventIdCounter = 0; + + Event = (function() { + function Event() { + this.id = ++eventIdCounter; + } + + Event.prototype.isEvent = function() { + return true; + }; + + Event.prototype.isEnd = function() { + return false; + }; + + Event.prototype.isInitial = function() { + return false; + }; + + Event.prototype.isNext = function() { + return false; + }; + + Event.prototype.isError = function() { + return false; + }; + + Event.prototype.hasValue = function() { + return false; + }; + + Event.prototype.filter = function() { + return true; + }; + + Event.prototype.inspect = function() { + return this.toString(); + }; + + Event.prototype.log = function() { + return this.toString(); + }; + + return Event; + + })(); + + Next = (function(_super) { + __extends(Next, _super); + + function Next(valueF) { + Next.__super__.constructor.call(this); + if (isFunction(valueF)) { + this.value = _.cached(valueF); + } else { + this.value = _.always(valueF); + } + } + + Next.prototype.isNext = function() { + return true; + }; + + Next.prototype.hasValue = function() { + return true; + }; + + Next.prototype.fmap = function(f) { + var value; + value = this.value; + return this.apply(function() { + return f(value()); + }); + }; + + Next.prototype.apply = function(value) { + return new Next(value); + }; + + Next.prototype.filter = function(f) { + return f(this.value()); + }; + + Next.prototype.toString = function() { + return _.toString(this.value()); + }; + + Next.prototype.log = function() { + return this.value(); + }; + + return Next; + + })(Event); + + Initial = (function(_super) { + __extends(Initial, _super); + + function Initial() { + return Initial.__super__.constructor.apply(this, arguments); + } + + Initial.prototype.isInitial = function() { + return true; + }; + + Initial.prototype.isNext = function() { + return false; + }; + + Initial.prototype.apply = function(value) { + return new Initial(value); + }; + + Initial.prototype.toNext = function() { + return new Next(this.value); + }; + + return Initial; + + })(Next); + + End = (function(_super) { + __extends(End, _super); + + function End() { + return End.__super__.constructor.apply(this, arguments); + } + + End.prototype.isEnd = function() { + return true; + }; + + End.prototype.fmap = function() { + return this; + }; + + End.prototype.apply = function() { + return this; + }; + + End.prototype.toString = function() { + return ""; + }; + + return End; + + })(Event); + + Error = (function(_super) { + __extends(Error, _super); + + function Error(error) { + this.error = error; + } + + Error.prototype.isError = function() { + return true; + }; + + Error.prototype.fmap = function() { + return this; + }; + + Error.prototype.apply = function() { + return this; + }; + + Error.prototype.toString = function() { + return " " + _.toString(this.error); + }; + + return Error; + + })(Event); + + idCounter = 0; + + Observable = (function() { + function Observable(desc) { + this.flatMapError = __bind(this.flatMapError, this); + this.id = ++idCounter; + withDescription(desc, this); + } + + Observable.prototype.onValue = function() { + var f; + f = makeFunctionArgs(arguments); + return this.subscribe(function(event) { + if (event.hasValue()) { + return f(event.value()); + } + }); + }; + + Observable.prototype.onValues = function(f) { + return this.onValue(function(args) { + return f.apply(null, args); + }); + }; + + Observable.prototype.onError = function() { + var f; + f = makeFunctionArgs(arguments); + return this.subscribe(function(event) { + if (event.isError()) { + return f(event.error); + } + }); + }; + + Observable.prototype.onEnd = function() { + var f; + f = makeFunctionArgs(arguments); + return this.subscribe(function(event) { + if (event.isEnd()) { + return f(); + } + }); + }; + + Observable.prototype.errors = function() { + return withDescription(this, "errors", this.filter(function() { + return false; + })); + }; + + Observable.prototype.filter = function() { + var args, f; + f = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + return convertArgsToFunction(this, f, args, function(f) { + return withDescription(this, "filter", f, this.withHandler(function(event) { + if (event.filter(f)) { + return this.push(event); + } else { + return Bacon.more; + } + })); + }); + }; + + Observable.prototype.takeWhile = function() { + var args, f; + f = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + return convertArgsToFunction(this, f, args, function(f) { + return withDescription(this, "takeWhile", f, this.withHandler(function(event) { + if (event.filter(f)) { + return this.push(event); + } else { + this.push(end()); + return Bacon.noMore; + } + })); + }); + }; + + Observable.prototype.endOnError = function() { + var args, f; + f = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + if (f == null) { + f = true; + } + return convertArgsToFunction(this, f, args, function(f) { + return withDescription(this, "endOnError", this.withHandler(function(event) { + if (event.isError() && f(event.error)) { + this.push(event); + return this.push(end()); + } else { + return this.push(event); + } + })); + }); + }; + + Observable.prototype.take = function(count) { + if (count <= 0) { + return Bacon.never(); + } + return withDescription(this, "take", count, this.withHandler(function(event) { + if (!event.hasValue()) { + return this.push(event); + } else { + count--; + if (count > 0) { + return this.push(event); + } else { + if (count === 0) { + this.push(event); + } + this.push(end()); + return Bacon.noMore; + } + } + })); + }; + + Observable.prototype.map = function() { + var args, p; + p = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + if (p instanceof Property) { + return p.sampledBy(this, former); + } else { + return convertArgsToFunction(this, p, args, function(f) { + return withDescription(this, "map", f, this.withHandler(function(event) { + return this.push(event.fmap(f)); + })); + }); + } + }; + + Observable.prototype.mapError = function() { + var f; + f = makeFunctionArgs(arguments); + return withDescription(this, "mapError", f, this.withHandler(function(event) { + if (event.isError()) { + return this.push(next(f(event.error))); + } else { + return this.push(event); + } + })); + }; + + Observable.prototype.mapEnd = function() { + var f; + f = makeFunctionArgs(arguments); + return withDescription(this, "mapEnd", f, this.withHandler(function(event) { + if (event.isEnd()) { + this.push(next(f(event))); + this.push(end()); + return Bacon.noMore; + } else { + return this.push(event); + } + })); + }; + + Observable.prototype.doAction = function() { + var f; + f = makeFunctionArgs(arguments); + return withDescription(this, "doAction", f, this.withHandler(function(event) { + if (event.hasValue()) { + f(event.value()); + } + return this.push(event); + })); + }; + + Observable.prototype.skip = function(count) { + return withDescription(this, "skip", count, this.withHandler(function(event) { + if (!event.hasValue()) { + return this.push(event); + } else if (count > 0) { + count--; + return Bacon.more; + } else { + return this.push(event); + } + })); + }; + + Observable.prototype.skipDuplicates = function(isEqual) { + if (isEqual == null) { + isEqual = function(a, b) { + return a === b; + }; + } + return withDescription(this, "skipDuplicates", this.withStateMachine(None, function(prev, event) { + if (!event.hasValue()) { + return [prev, [event]]; + } else if (event.isInitial() || prev === None || !isEqual(prev.get(), event.value())) { + return [new Some(event.value()), [event]]; + } else { + return [prev, []]; + } + })); + }; + + Observable.prototype.skipErrors = function() { + return withDescription(this, "skipErrors", this.withHandler(function(event) { + if (event.isError()) { + return Bacon.more; + } else { + return this.push(event); + } + })); + }; + + Observable.prototype.withStateMachine = function(initState, f) { + var state; + state = initState; + return withDescription(this, "withStateMachine", initState, f, this.withHandler(function(event) { + var fromF, newState, output, outputs, reply, _i, _len; + fromF = f(state, event); + newState = fromF[0], outputs = fromF[1]; + state = newState; + reply = Bacon.more; + for (_i = 0, _len = outputs.length; _i < _len; _i++) { + output = outputs[_i]; + reply = this.push(output); + if (reply === Bacon.noMore) { + return reply; + } + } + return reply; + })); + }; + + Observable.prototype.scan = function(seed, f, options) { + var acc, f_, resultProperty, subscribe; + if (options == null) { + options = {}; + } + f_ = toCombinator(f); + f = options.lazyF ? f_ : function(x, y) { + return f_(x(), y()); + }; + acc = toOption(seed).map(function(x) { + return _.always(x); + }); + subscribe = (function(_this) { + return function(sink) { + var initSent, reply, sendInit, unsub; + initSent = false; + unsub = nop; + reply = Bacon.more; + sendInit = function() { + if (!initSent) { + return acc.forEach(function(valueF) { + initSent = true; + reply = sink(new Initial(valueF)); + if (reply === Bacon.noMore) { + unsub(); + return unsub = nop; + } + }); + } + }; + unsub = _this.subscribeInternal(function(event) { + var next, prev; + if (event.hasValue()) { + if (initSent && event.isInitial()) { + return Bacon.more; + } else { + if (!event.isInitial()) { + sendInit(); + } + initSent = true; + prev = acc.getOrElse(function() { + return void 0; + }); + next = _.cached(function() { + return f(prev, event.value); + }); + acc = new Some(next); + if (options.eager) { + next(); + } + return sink(event.apply(next)); + } + } else { + if (event.isEnd()) { + reply = sendInit(); + } + if (reply !== Bacon.noMore) { + return sink(event); + } + } + }); + UpdateBarrier.whenDoneWith(resultProperty, sendInit); + return unsub; + }; + })(this); + return resultProperty = new Property(describe(this, "scan", seed, f), subscribe); + }; + + Observable.prototype.fold = function(seed, f, options) { + return withDescription(this, "fold", seed, f, this.scan(seed, f, options).sampledBy(this.filter(false).mapEnd().toProperty())); + }; + + Observable.prototype.zip = function(other, f) { + if (f == null) { + f = Array; + } + return withDescription(this, "zip", other, Bacon.zipWith([this, other], f)); + }; + + Observable.prototype.diff = function(start, f) { + f = toCombinator(f); + return withDescription(this, "diff", start, f, this.scan([start], function(prevTuple, next) { + return [next, f(prevTuple[0], next)]; + }).filter(function(tuple) { + return tuple.length === 2; + }).map(function(tuple) { + return tuple[1]; + })); + }; + + Observable.prototype.flatMap = function() { + return flatMap_(this, makeSpawner(arguments)); + }; + + Observable.prototype.flatMapFirst = function() { + return flatMap_(this, makeSpawner(arguments), true); + }; + + Observable.prototype.flatMapWithConcurrencyLimit = function() { + var args, limit; + limit = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + return withDescription.apply(null, [this, "flatMapWithConcurrencyLimit", limit].concat(__slice.call(args), [flatMap_(this, makeSpawner(args), false, limit)])); + }; + + Observable.prototype.flatMapLatest = function() { + var f, stream; + f = makeSpawner(arguments); + stream = this.toEventStream(); + return withDescription(this, "flatMapLatest", f, stream.flatMap(function(value) { + return makeObservable(f(value)).takeUntil(stream); + })); + }; + + Observable.prototype.flatMapError = function(fn) { + return withDescription(this, "flatMapError", fn, this.mapError(function(err) { + return new Bacon.Error(err); + }).flatMap(function(x) { + if (x instanceof Bacon.Error) { + return fn(x.error); + } else { + return Bacon.once(x); + } + })); + }; + + Observable.prototype.flatMapConcat = function() { + return withDescription.apply(null, [this, "flatMapConcat"].concat(__slice.call(arguments), [this.flatMapWithConcurrencyLimit.apply(this, [1].concat(__slice.call(arguments)))])); + }; + + Observable.prototype.bufferingThrottle = function(minimumInterval) { + return withDescription(this, "bufferingThrottle", minimumInterval, this.flatMapConcat(function(x) { + return Bacon.once(x).concat(Bacon.later(minimumInterval).filter(false)); + })); + }; + + Observable.prototype.not = function() { + return withDescription(this, "not", this.map(function(x) { + return !x; + })); + }; + + Observable.prototype.log = function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + this.subscribe(function(event) { + return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log.apply(console, __slice.call(args).concat([event.log()])) : void 0 : void 0; + }); + return this; + }; + + Observable.prototype.slidingWindow = function(n, minValues) { + if (minValues == null) { + minValues = 0; + } + return withDescription(this, "slidingWindow", n, minValues, this.scan([], (function(window, value) { + return window.concat([value]).slice(-n); + })).filter((function(values) { + return values.length >= minValues; + }))); + }; + + Observable.prototype.combine = function(other, f) { + var combinator; + combinator = toCombinator(f); + return withDescription(this, "combine", other, f, Bacon.combineAsArray(this, other).map(function(values) { + return combinator(values[0], values[1]); + })); + }; + + Observable.prototype.decode = function(cases) { + return withDescription(this, "decode", cases, this.combine(Bacon.combineTemplate(cases), function(key, values) { + return values[key]; + })); + }; + + Observable.prototype.awaiting = function(other) { + return withDescription(this, "awaiting", other, Bacon.groupSimultaneous(this, other).map(function(_arg) { + var myValues, otherValues; + myValues = _arg[0], otherValues = _arg[1]; + return otherValues.length === 0; + }).toProperty(false).skipDuplicates()); + }; + + Observable.prototype.name = function(name) { + this.toString = function() { + return name; + }; + return this; + }; + + Observable.prototype.withDescription = function() { + return describe.apply(null, arguments).apply(this); + }; + + return Observable; + + })(); + + Observable.prototype.reduce = Observable.prototype.fold; + + Observable.prototype.assign = Observable.prototype.onValue; + + flatMap_ = function(root, f, firstOnly, limit) { + var deps, result; + deps = [root]; + result = new EventStream(describe(root, "flatMap" + (firstOnly ? "First" : ""), f), function(sink) { + var checkEnd, checkQueue, composite, queue, spawn; + composite = new CompositeUnsubscribe(); + queue = []; + spawn = function(event) { + var child; + child = makeObservable(f(event.value())); + deps.push(child); + DepCache.invalidate(); + return composite.add(function(unsubAll, unsubMe) { + return child.subscribeInternal(function(event) { + var reply; + if (event.isEnd()) { + _.remove(child, deps); + DepCache.invalidate(); + checkQueue(); + checkEnd(unsubMe); + return Bacon.noMore; + } else { + if (event instanceof Initial) { + event = event.toNext(); + } + reply = sink(event); + if (reply === Bacon.noMore) { + unsubAll(); + } + return reply; + } + }); + }); + }; + checkQueue = function() { + var event; + event = queue.splice(0, 1)[0]; + if (event) { + return spawn(event); + } + }; + checkEnd = function(unsub) { + unsub(); + if (composite.empty()) { + return sink(end()); + } + }; + composite.add(function(__, unsubRoot) { + return root.subscribeInternal(function(event) { + if (event.isEnd()) { + return checkEnd(unsubRoot); + } else if (event.isError()) { + return sink(event); + } else if (firstOnly && composite.count() > 1) { + return Bacon.more; + } else { + if (composite.unsubscribed) { + return Bacon.noMore; + } + if (limit && composite.count() > limit) { + return queue.push(event); + } else { + return spawn(event); + } + } + }); + }); + return composite.unsubscribe; + }); + result.internalDeps = function() { + return deps; + }; + return result; + }; + + EventStream = (function(_super) { + __extends(EventStream, _super); + + function EventStream(desc, subscribe) { + var dispatcher; + if (isFunction(desc)) { + subscribe = desc; + desc = []; + } + EventStream.__super__.constructor.call(this, desc); + assertFunction(subscribe); + dispatcher = new Dispatcher(subscribe); + this.subscribeInternal = dispatcher.subscribe; + this.subscribe = UpdateBarrier.wrappedSubscribe(this); + this.hasSubscribers = dispatcher.hasSubscribers; + registerObs(this); + } + + EventStream.prototype.delay = function(delay) { + return withDescription(this, "delay", delay, this.flatMap(function(value) { + return Bacon.later(delay, value); + })); + }; + + EventStream.prototype.debounce = function(delay) { + return withDescription(this, "debounce", delay, this.flatMapLatest(function(value) { + return Bacon.later(delay, value); + })); + }; + + EventStream.prototype.debounceImmediate = function(delay) { + return withDescription(this, "debounceImmediate", delay, this.flatMapFirst(function(value) { + return Bacon.once(value).concat(Bacon.later(delay).filter(false)); + })); + }; + + EventStream.prototype.throttle = function(delay) { + return withDescription(this, "throttle", delay, this.bufferWithTime(delay).map(function(values) { + return values[values.length - 1]; + })); + }; + + EventStream.prototype.bufferWithTime = function(delay) { + return withDescription(this, "bufferWithTime", delay, this.bufferWithTimeOrCount(delay, Number.MAX_VALUE)); + }; + + EventStream.prototype.bufferWithCount = function(count) { + return withDescription(this, "bufferWithCount", count, this.bufferWithTimeOrCount(void 0, count)); + }; + + EventStream.prototype.bufferWithTimeOrCount = function(delay, count) { + var flushOrSchedule; + flushOrSchedule = function(buffer) { + if (buffer.values.length === count) { + return buffer.flush(); + } else if (delay !== void 0) { + return buffer.schedule(); + } + }; + return withDescription(this, "bufferWithTimeOrCount", delay, count, this.buffer(delay, flushOrSchedule, flushOrSchedule)); + }; + + EventStream.prototype.buffer = function(delay, onInput, onFlush) { + var buffer, delayMs, reply; + if (onInput == null) { + onInput = (function() {}); + } + if (onFlush == null) { + onFlush = (function() {}); + } + buffer = { + scheduled: false, + end: null, + values: [], + flush: function() { + var reply; + this.scheduled = false; + if (this.values.length > 0) { + reply = this.push(next(this.values)); + this.values = []; + if (this.end != null) { + return this.push(this.end); + } else if (reply !== Bacon.noMore) { + return onFlush(this); + } + } else { + if (this.end != null) { + return this.push(this.end); + } + } + }, + schedule: function() { + if (!this.scheduled) { + this.scheduled = true; + return delay((function(_this) { + return function() { + return _this.flush(); + }; + })(this)); + } + } + }; + reply = Bacon.more; + if (!isFunction(delay)) { + delayMs = delay; + delay = function(f) { + return Bacon.scheduler.setTimeout(f, delayMs); + }; + } + return withDescription(this, "buffer", this.withHandler(function(event) { + buffer.push = this.push; + if (event.isError()) { + reply = this.push(event); + } else if (event.isEnd()) { + buffer.end = event; + if (!buffer.scheduled) { + buffer.flush(); + } + } else { + buffer.values.push(event.value()); + onInput(buffer); + } + return reply; + })); + }; + + EventStream.prototype.merge = function(right) { + var left; + assertEventStream(right); + left = this; + return withDescription(left, "merge", right, Bacon.mergeAll(this, right)); + }; + + EventStream.prototype.toProperty = function(initValue) { + if (arguments.length === 0) { + initValue = None; + } + return withDescription(this, "toProperty", initValue, this.scan(initValue, latterF, { + lazyF: true + })); + }; + + EventStream.prototype.toEventStream = function() { + return this; + }; + + EventStream.prototype.sampledBy = function(sampler, combinator) { + return withDescription(this, "sampledBy", sampler, combinator, this.toProperty().sampledBy(sampler, combinator)); + }; + + EventStream.prototype.concat = function(right) { + var left; + left = this; + return new EventStream(describe(left, "concat", right), function(sink) { + var unsubLeft, unsubRight; + unsubRight = nop; + unsubLeft = left.subscribeInternal(function(e) { + if (e.isEnd()) { + return unsubRight = right.subscribeInternal(sink); + } else { + return sink(e); + } + }); + return function() { + unsubLeft(); + return unsubRight(); + }; + }); + }; + + EventStream.prototype.takeUntil = function(stopper) { + var endMarker; + endMarker = {}; + return withDescription(this, "takeUntil", stopper, Bacon.groupSimultaneous(this.mapEnd(endMarker), stopper.skipErrors()).withHandler(function(event) { + var data, reply, value, _i, _len, _ref1; + if (!event.hasValue()) { + return this.push(event); + } else { + _ref1 = event.value(), data = _ref1[0], stopper = _ref1[1]; + if (stopper.length) { + return this.push(end()); + } else { + reply = Bacon.more; + for (_i = 0, _len = data.length; _i < _len; _i++) { + value = data[_i]; + if (value === endMarker) { + reply = this.push(end()); + } else { + reply = this.push(next(value)); + } + } + return reply; + } + } + })); + }; + + EventStream.prototype.skipUntil = function(starter) { + var started; + started = starter.take(1).map(true).toProperty(false); + return withDescription(this, "skipUntil", starter, this.filter(started)); + }; + + EventStream.prototype.skipWhile = function() { + var args, f, ok; + f = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + ok = false; + return convertArgsToFunction(this, f, args, function(f) { + return withDescription(this, "skipWhile", f, this.withHandler(function(event) { + if (ok || !event.hasValue() || !f(event.value())) { + if (event.hasValue()) { + ok = true; + } + return this.push(event); + } else { + return Bacon.more; + } + })); + }); + }; + + EventStream.prototype.holdWhen = function(valve) { + var putToHold, releaseHold, valve_; + valve_ = valve.startWith(false); + releaseHold = valve_.filter(function(x) { + return !x; + }); + putToHold = valve_.filter(_.id); + return withDescription(this, "holdWhen", valve, this.filter(false).merge(valve_.flatMapConcat((function(_this) { + return function(shouldHold) { + if (!shouldHold) { + return _this.takeUntil(putToHold); + } else { + return _this.scan([], (function(xs, x) { + return xs.concat(x); + }), { + eager: true + }).sampledBy(releaseHold).take(1).flatMap(Bacon.fromArray); + } + }; + })(this)))); + }; + + EventStream.prototype.startWith = function(seed) { + return withDescription(this, "startWith", seed, Bacon.once(seed).concat(this)); + }; + + EventStream.prototype.withHandler = function(handler) { + var dispatcher; + dispatcher = new Dispatcher(this.subscribeInternal, handler); + return new EventStream(describe(this, "withHandler", handler), dispatcher.subscribe); + }; + + return EventStream; + + })(Observable); + + Property = (function(_super) { + __extends(Property, _super); + + function Property(desc, subscribe, handler) { + if (isFunction(desc)) { + handler = subscribe; + subscribe = desc; + desc = []; + } + Property.__super__.constructor.call(this, desc); + assertFunction(subscribe); + if (handler === true) { + this.subscribeInternal = subscribe; + } else { + this.subscribeInternal = new PropertyDispatcher(this, subscribe, handler).subscribe; + } + this.subscribe = UpdateBarrier.wrappedSubscribe(this); + registerObs(this); + } + + Property.prototype.sampledBy = function(sampler, combinator) { + var lazy, result, samplerSource, stream, thisSource; + if (combinator != null) { + combinator = toCombinator(combinator); + } else { + lazy = true; + combinator = function(f) { + return f(); + }; + } + thisSource = new Source(this, false, this.subscribeInternal, lazy); + samplerSource = new Source(sampler, true, sampler.subscribeInternal, lazy); + stream = Bacon.when([thisSource, samplerSource], combinator); + result = sampler instanceof Property ? stream.toProperty() : stream; + return withDescription(this, "sampledBy", sampler, combinator, result); + }; + + Property.prototype.sample = function(interval) { + return withDescription(this, "sample", interval, this.sampledBy(Bacon.interval(interval, {}))); + }; + + Property.prototype.changes = function() { + return new EventStream(describe(this, "changes"), (function(_this) { + return function(sink) { + return _this.subscribeInternal(function(event) { + if (!event.isInitial()) { + return sink(event); + } + }); + }; + })(this)); + }; + + Property.prototype.withHandler = function(handler) { + return new Property(describe(this, "withHandler", handler), this.subscribeInternal, handler); + }; + + Property.prototype.toProperty = function() { + assertNoArguments(arguments); + return this; + }; + + Property.prototype.toEventStream = function() { + return new EventStream(describe(this, "toEventStream"), (function(_this) { + return function(sink) { + return _this.subscribeInternal(function(event) { + if (event.isInitial()) { + event = event.toNext(); + } + return sink(event); + }); + }; + })(this)); + }; + + Property.prototype.and = function(other) { + return withDescription(this, "and", other, this.combine(other, function(x, y) { + return x && y; + })); + }; + + Property.prototype.or = function(other) { + return withDescription(this, "or", other, this.combine(other, function(x, y) { + return x || y; + })); + }; + + Property.prototype.delay = function(delay) { + return this.delayChanges("delay", delay, function(changes) { + return changes.delay(delay); + }); + }; + + Property.prototype.debounce = function(delay) { + return this.delayChanges("debounce", delay, function(changes) { + return changes.debounce(delay); + }); + }; + + Property.prototype.throttle = function(delay) { + return this.delayChanges("throttle", delay, function(changes) { + return changes.throttle(delay); + }); + }; + + Property.prototype.delayChanges = function() { + var desc, f, _i; + desc = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), f = arguments[_i++]; + return withDescription.apply(null, [this].concat(__slice.call(desc), [addPropertyInitValueToStream(this, f(this.changes()))])); + }; + + Property.prototype.takeUntil = function(stopper) { + var changes; + changes = this.changes().takeUntil(stopper); + return withDescription(this, "takeUntil", stopper, addPropertyInitValueToStream(this, changes)); + }; + + Property.prototype.startWith = function(value) { + return withDescription(this, "startWith", value, this.scan(value, function(prev, next) { + return next; + })); + }; + + Property.prototype.bufferingThrottle = function() { + var _ref1; + return (_ref1 = Property.__super__.bufferingThrottle.apply(this, arguments)).bufferingThrottle.apply(_ref1, arguments).toProperty(); + }; + + return Property; + + })(Observable); + + convertArgsToFunction = function(obs, f, args, method) { + var sampled; + if (f instanceof Property) { + sampled = f.sampledBy(obs, function(p, s) { + return [p, s]; + }); + return method.apply(sampled, [ + function(_arg) { + var p, s; + p = _arg[0], s = _arg[1]; + return p; + } + ]).map(function(_arg) { + var p, s; + p = _arg[0], s = _arg[1]; + return s; + }); + } else { + f = makeFunction(f, args); + return method.apply(obs, [f]); + } + }; + + addPropertyInitValueToStream = function(property, stream) { + var justInitValue; + justInitValue = new EventStream(describe(property, "justInitValue"), function(sink) { + var unsub, value; + value = null; + unsub = property.subscribeInternal(function(event) { + if (event.hasValue()) { + value = event; + } + return Bacon.noMore; + }); + UpdateBarrier.whenDoneWith(justInitValue, function() { + if (value != null) { + sink(value); + } + return sink(end()); + }); + return unsub; + }); + return justInitValue.concat(stream).toProperty(); + }; + + Dispatcher = (function() { + function Dispatcher(subscribe, handleEvent) { + var done, ended, prevError, pushIt, pushing, queue, removeSub, subscriptions, unsubscribeFromSource, waiters; + if (subscribe == null) { + subscribe = function() { + return nop; + }; + } + subscriptions = []; + queue = []; + pushing = false; + ended = false; + this.hasSubscribers = function() { + return subscriptions.length > 0; + }; + prevError = null; + unsubscribeFromSource = nop; + removeSub = function(subscription) { + return subscriptions = _.without(subscription, subscriptions); + }; + waiters = null; + done = function() { + var w, ws, _i, _len, _results; + if (waiters != null) { + ws = waiters; + waiters = null; + _results = []; + for (_i = 0, _len = ws.length; _i < _len; _i++) { + w = ws[_i]; + _results.push(w()); + } + return _results; + } + }; + pushIt = function(event) { + var reply, sub, success, tmp, _i, _len; + if (!pushing) { + if (event === prevError) { + return; + } + if (event.isError()) { + prevError = event; + } + success = false; + try { + pushing = true; + tmp = subscriptions; + for (_i = 0, _len = tmp.length; _i < _len; _i++) { + sub = tmp[_i]; + reply = sub.sink(event); + if (reply === Bacon.noMore || event.isEnd()) { + removeSub(sub); + } + } + success = true; + } finally { + pushing = false; + if (!success) { + queue = []; + } + } + success = true; + while (queue.length) { + event = queue.shift(); + this.push(event); + } + done(event); + if (this.hasSubscribers()) { + return Bacon.more; + } else { + unsubscribeFromSource(); + return Bacon.noMore; + } + } else { + queue.push(event); + return Bacon.more; + } + }; + this.push = (function(_this) { + return function(event) { + return UpdateBarrier.inTransaction(event, _this, pushIt, [event]); + }; + })(this); + if (handleEvent == null) { + handleEvent = function(event) { + return this.push(event); + }; + } + this.handleEvent = (function(_this) { + return function(event) { + if (event.isEnd()) { + ended = true; + } + return handleEvent.apply(_this, [event]); + }; + })(this); + this.subscribe = (function(_this) { + return function(sink) { + var subscription, unsubSrc; + if (ended) { + sink(end()); + return nop; + } else { + assertFunction(sink); + subscription = { + sink: sink + }; + subscriptions.push(subscription); + if (subscriptions.length === 1) { + unsubSrc = subscribe(_this.handleEvent); + unsubscribeFromSource = function() { + unsubSrc(); + return unsubscribeFromSource = nop; + }; + } + assertFunction(unsubscribeFromSource); + return function() { + removeSub(subscription); + if (!_this.hasSubscribers()) { + return unsubscribeFromSource(); + } + }; + } + }; + })(this); + } + + return Dispatcher; + + })(); + + PropertyDispatcher = (function(_super) { + __extends(PropertyDispatcher, _super); + + function PropertyDispatcher(p, subscribe, handleEvent) { + var current, currentValueRootId, ended, push; + PropertyDispatcher.__super__.constructor.call(this, subscribe, handleEvent); + current = None; + currentValueRootId = void 0; + push = this.push; + subscribe = this.subscribe; + ended = false; + this.push = (function(_this) { + return function(event) { + if (event.isEnd()) { + ended = true; + } + if (event.hasValue()) { + current = new Some(event); + currentValueRootId = UpdateBarrier.currentEventId(); + } + return push.apply(_this, [event]); + }; + })(this); + this.subscribe = (function(_this) { + return function(sink) { + var dispatchingId, initSent, maybeSubSource, reply, valId; + initSent = false; + reply = Bacon.more; + maybeSubSource = function() { + if (reply === Bacon.noMore) { + return nop; + } else if (ended) { + sink(end()); + return nop; + } else { + return subscribe.apply(this, [sink]); + } + }; + if (current.isDefined && (_this.hasSubscribers() || ended)) { + dispatchingId = UpdateBarrier.currentEventId(); + valId = currentValueRootId; + if (!ended && valId && dispatchingId && dispatchingId !== valId) { + UpdateBarrier.whenDoneWith(p, function() { + if (currentValueRootId === valId) { + return sink(initial(current.get().value())); + } + }); + return maybeSubSource(); + } else { + UpdateBarrier.inTransaction(void 0, _this, (function() { + return reply = sink(initial(current.get().value())); + }), []); + return maybeSubSource(); + } + } else { + return maybeSubSource(); + } + }; + })(this); + } + + return PropertyDispatcher; + + })(Dispatcher); + + Bus = (function(_super) { + __extends(Bus, _super); + + function Bus() { + var ended, guardedSink, sink, subscribeAll, subscribeInput, subscriptions, unsubAll, unsubscribeInput; + sink = void 0; + subscriptions = []; + ended = false; + guardedSink = (function(_this) { + return function(input) { + return function(event) { + if (event.isEnd()) { + unsubscribeInput(input); + return Bacon.noMore; + } else { + return sink(event); + } + }; + }; + })(this); + unsubAll = function() { + var sub, _i, _len, _results; + _results = []; + for (_i = 0, _len = subscriptions.length; _i < _len; _i++) { + sub = subscriptions[_i]; + _results.push(typeof sub.unsub === "function" ? sub.unsub() : void 0); + } + return _results; + }; + subscribeInput = function(subscription) { + return subscription.unsub = subscription.input.subscribeInternal(guardedSink(subscription.input)); + }; + unsubscribeInput = function(input) { + var i, sub, _i, _len; + for (i = _i = 0, _len = subscriptions.length; _i < _len; i = ++_i) { + sub = subscriptions[i]; + if (sub.input === input) { + if (typeof sub.unsub === "function") { + sub.unsub(); + } + subscriptions.splice(i, 1); + return; + } + } + }; + subscribeAll = (function(_this) { + return function(newSink) { + var subscription, _i, _len, _ref1; + sink = newSink; + _ref1 = cloneArray(subscriptions); + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + subscription = _ref1[_i]; + subscribeInput(subscription); + } + return unsubAll; + }; + })(this); + Bus.__super__.constructor.call(this, describe(Bacon, "Bus"), subscribeAll); + this.plug = (function(_this) { + return function(input) { + var sub; + if (ended) { + return; + } + sub = { + input: input + }; + subscriptions.push(sub); + if ((sink != null)) { + subscribeInput(sub); + } + return function() { + return unsubscribeInput(input); + }; + }; + })(this); + this.push = (function(_this) { + return function(value) { + return typeof sink === "function" ? sink(next(value)) : void 0; + }; + })(this); + this.error = (function(_this) { + return function(error) { + return typeof sink === "function" ? sink(new Error(error)) : void 0; + }; + })(this); + this.end = (function(_this) { + return function() { + ended = true; + unsubAll(); + return typeof sink === "function" ? sink(end()) : void 0; + }; + })(this); + } + + return Bus; + + })(EventStream); + + Source = (function() { + function Source(obs, sync, subscribe, lazy) { + this.obs = obs; + this.sync = sync; + this.subscribe = subscribe; + this.lazy = lazy != null ? lazy : false; + this.queue = []; + if (this.subscribe == null) { + this.subscribe = this.obs.subscribeInternal; + } + this.toString = this.obs.toString; + } + + Source.prototype.markEnded = function() { + return this.ended = true; + }; + + Source.prototype.consume = function() { + if (this.lazy) { + return _.always(this.queue[0]); + } else { + return this.queue[0]; + } + }; + + Source.prototype.push = function(x) { + return this.queue = [x]; + }; + + Source.prototype.mayHave = function() { + return true; + }; + + Source.prototype.hasAtLeast = function() { + return this.queue.length; + }; + + Source.prototype.flatten = true; + + return Source; + + })(); + + ConsumingSource = (function(_super) { + __extends(ConsumingSource, _super); + + function ConsumingSource() { + return ConsumingSource.__super__.constructor.apply(this, arguments); + } + + ConsumingSource.prototype.consume = function() { + return this.queue.shift(); + }; + + ConsumingSource.prototype.push = function(x) { + return this.queue.push(x); + }; + + ConsumingSource.prototype.mayHave = function(c) { + return !this.ended || this.queue.length >= c; + }; + + ConsumingSource.prototype.hasAtLeast = function(c) { + return this.queue.length >= c; + }; + + ConsumingSource.prototype.flatten = false; + + return ConsumingSource; + + })(Source); + + BufferingSource = (function(_super) { + __extends(BufferingSource, _super); + + function BufferingSource(obs) { + this.obs = obs; + BufferingSource.__super__.constructor.call(this, this.obs, true, this.obs.subscribeInternal); + } + + BufferingSource.prototype.consume = function() { + var values; + values = this.queue; + this.queue = []; + return function() { + return values; + }; + }; + + BufferingSource.prototype.push = function(x) { + return this.queue.push(x()); + }; + + BufferingSource.prototype.hasAtLeast = function() { + return true; + }; + + return BufferingSource; + + })(Source); + + Source.isTrigger = function(s) { + if (s instanceof Source) { + return s.sync; + } else { + return s instanceof EventStream; + } + }; + + Source.fromObservable = function(s) { + if (s instanceof Source) { + return s; + } else if (s instanceof Property) { + return new Source(s, false); + } else { + return new ConsumingSource(s, true); + } + }; + + describe = function() { + var args, context, method; + context = arguments[0], method = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + if ((context || method) instanceof Desc) { + return context || method; + } else { + return new Desc(context, method, args); + } + }; + + findDeps = function(x) { + if (isArray(x)) { + return _.flatMap(findDeps, x); + } else if (isObservable(x)) { + return [x]; + } else if (x instanceof Source) { + return [x.obs]; + } else { + return []; + } + }; + + Desc = (function() { + function Desc(context, method, args) { + this.context = context; + this.method = method; + this.args = args; + } + + Desc.prototype.apply = function(obs) { + var deps, that; + that = this; + deps = _.cached((function() { + return findDeps([that.context].concat(that.args)); + })); + obs.internalDeps = obs.internalDeps || deps; + obs.dependsOn = DepCache.dependsOn; + obs.deps = deps; + obs.toString = (function() { + return _.toString(that.context) + "." + _.toString(that.method) + "(" + _.map(_.toString, that.args) + ")"; + }); + obs.inspect = function() { + return obs.toString(); + }; + obs.desc = (function() { + return { + context: that.context, + method: that.method, + args: that.args + }; + }); + return obs; + }; + + return Desc; + + })(); + + withDescription = function() { + var desc, obs, _i; + desc = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), obs = arguments[_i++]; + return describe.apply(null, desc).apply(obs); + }; + + Bacon.when = function() { + var f, i, index, ix, len, needsBarrier, pat, patSources, pats, patterns, resultStream, s, sources, triggerFound, usage, _i, _j, _len, _len1, _ref1; + patterns = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (patterns.length === 0) { + return Bacon.never(); + } + len = patterns.length; + usage = "when: expecting arguments in the form (Observable+,function)+"; + assert(usage, len % 2 === 0); + sources = []; + pats = []; + i = 0; + while (i < len) { + patSources = _.toArray(patterns[i]); + f = patterns[i + 1]; + pat = { + f: (isFunction(f) ? f : (function() { + return f; + })), + ixs: [] + }; + triggerFound = false; + for (_i = 0, _len = patSources.length; _i < _len; _i++) { + s = patSources[_i]; + index = _.indexOf(sources, s); + if (!triggerFound) { + triggerFound = Source.isTrigger(s); + } + if (index < 0) { + sources.push(s); + index = sources.length - 1; + } + _ref1 = pat.ixs; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + ix = _ref1[_j]; + if (ix.index === index) { + ix.count++; + } + } + pat.ixs.push({ + index: index, + count: 1 + }); + } + assert("At least one EventStream required", triggerFound ||  (!patSources.length)); + if (patSources.length > 0) { + pats.push(pat); + } + i = i + 2; + } + if (!sources.length) { + return Bacon.never(); + } + sources = _.map(Source.fromObservable, sources); + needsBarrier = (_.any(sources, function(s) { + return s.flatten; + })) && (containsDuplicateDeps(_.map((function(s) { + return s.obs; + }), sources))); + return resultStream = new EventStream(describe.apply(null, [Bacon, "when"].concat(__slice.call(patterns))), function(sink) { + var cannotMatch, cannotSync, ends, match, nonFlattened, part, triggers; + triggers = []; + ends = false; + match = function(p) { + var _k, _len2, _ref2; + _ref2 = p.ixs; + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + i = _ref2[_k]; + if (!sources[i.index].hasAtLeast(i.count)) { + return false; + } + } + return true; + }; + cannotSync = function(source) { + return !source.sync || source.ended; + }; + cannotMatch = function(p) { + var _k, _len2, _ref2; + _ref2 = p.ixs; + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + i = _ref2[_k]; + if (!sources[i.index].mayHave(i.count)) { + return true; + } + } + }; + nonFlattened = function(trigger) { + return !trigger.source.flatten; + }; + part = function(source) { + return function(unsubAll) { + var flush, flushLater, flushWhileTriggers; + flushLater = function() { + return UpdateBarrier.whenDoneWith(resultStream, flush); + }; + flushWhileTriggers = function() { + var functions, p, reply, trigger, _k, _len2; + if (triggers.length > 0) { + reply = Bacon.more; + trigger = triggers.pop(); + for (_k = 0, _len2 = pats.length; _k < _len2; _k++) { + p = pats[_k]; + if (match(p)) { + functions = (function() { + var _l, _len3, _ref2, _results; + _ref2 = p.ixs; + _results = []; + for (_l = 0, _len3 = _ref2.length; _l < _len3; _l++) { + i = _ref2[_l]; + _results.push(sources[i.index].consume()); + } + return _results; + })(); + reply = sink(trigger.e.apply(function() { + var fun, values; + values = (function() { + var _l, _len3, _results; + _results = []; + for (_l = 0, _len3 = functions.length; _l < _len3; _l++) { + fun = functions[_l]; + _results.push(fun()); + } + return _results; + })(); + return p.f.apply(p, values); + })); + if (triggers.length && needsBarrier) { + triggers = _.filter(nonFlattened, triggers); + } + if (reply === Bacon.noMore) { + return reply; + } else { + return flushWhileTriggers(); + } + } + } + } else { + return Bacon.more; + } + }; + flush = function() { + var reply; + reply = flushWhileTriggers(); + if (ends) { + ends = false; + if (_.all(sources, cannotSync) || _.all(pats, cannotMatch)) { + reply = Bacon.noMore; + sink(end()); + } + } + if (reply === Bacon.noMore) { + unsubAll(); + } + return reply; + }; + return source.subscribe(function(e) { + var reply; + if (e.isEnd()) { + ends = true; + source.markEnded(); + flushLater(); + } else if (e.isError()) { + reply = sink(e); + } else { + source.push(e.value); + if (source.sync) { + triggers.push({ + source: source, + e: e + }); + if (needsBarrier || UpdateBarrier.hasWaiters()) { + flushLater(); + } else { + flush(); + } + } + } + if (reply === Bacon.noMore) { + unsubAll(); + } + return reply || Bacon.more; + }); + }; + }; + return compositeUnsubscribe.apply(null, (function() { + var _k, _len2, _results; + _results = []; + for (_k = 0, _len2 = sources.length; _k < _len2; _k++) { + s = sources[_k]; + _results.push(part(s)); + } + return _results; + })()); + }); + }; + + containsDuplicateDeps = function(observables, state) { + var checkObservable; + if (state == null) { + state = []; + } + checkObservable = function(obs) { + var deps; + if (Bacon._.contains(state, obs)) { + return true; + } else { + deps = obs.internalDeps(); + if (deps.length) { + state.push(obs); + return Bacon._.any(deps, checkObservable); + } else { + state.push(obs); + return false; + } + } + }; + return Bacon._.any(observables, checkObservable); + }; + + Bacon.update = function() { + var i, initial, lateBindFirst, patterns; + initial = arguments[0], patterns = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + lateBindFirst = function(f) { + return function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return function(i) { + return f.apply(null, [i].concat(args)); + }; + }; + }; + i = patterns.length - 1; + while (i > 0) { + if (!(patterns[i] instanceof Function)) { + patterns[i] = (function(x) { + return function() { + return x; + }; + })(patterns[i]); + } + patterns[i] = lateBindFirst(patterns[i]); + i = i - 2; + } + return withDescription.apply(null, [Bacon, "update", initial].concat(__slice.call(patterns), [Bacon.when.apply(Bacon, patterns).scan(initial, (function(x, f) { + return f(x); + }))])); + }; + + compositeUnsubscribe = function() { + var ss; + ss = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return new CompositeUnsubscribe(ss).unsubscribe; + }; + + CompositeUnsubscribe = (function() { + function CompositeUnsubscribe(ss) { + var s, _i, _len; + if (ss == null) { + ss = []; + } + this.unsubscribe = __bind(this.unsubscribe, this); + this.unsubscribed = false; + this.subscriptions = []; + this.starting = []; + for (_i = 0, _len = ss.length; _i < _len; _i++) { + s = ss[_i]; + this.add(s); + } + } + + CompositeUnsubscribe.prototype.add = function(subscription) { + var ended, unsub, unsubMe; + if (this.unsubscribed) { + return; + } + ended = false; + unsub = nop; + this.starting.push(subscription); + unsubMe = (function(_this) { + return function() { + if (_this.unsubscribed) { + return; + } + ended = true; + _this.remove(unsub); + return _.remove(subscription, _this.starting); + }; + })(this); + unsub = subscription(this.unsubscribe, unsubMe); + if (!(this.unsubscribed || ended)) { + this.subscriptions.push(unsub); + } + _.remove(subscription, this.starting); + return unsub; + }; + + CompositeUnsubscribe.prototype.remove = function(unsub) { + if (this.unsubscribed) { + return; + } + if ((_.remove(unsub, this.subscriptions)) !== void 0) { + return unsub(); + } + }; + + CompositeUnsubscribe.prototype.unsubscribe = function() { + var s, _i, _len, _ref1; + if (this.unsubscribed) { + return; + } + this.unsubscribed = true; + _ref1 = this.subscriptions; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + s = _ref1[_i]; + s(); + } + this.subscriptions = []; + return this.starting = []; + }; + + CompositeUnsubscribe.prototype.count = function() { + if (this.unsubscribed) { + return 0; + } + return this.subscriptions.length + this.starting.length; + }; + + CompositeUnsubscribe.prototype.empty = function() { + return this.count() === 0; + }; + + return CompositeUnsubscribe; + + })(); + + Bacon.CompositeUnsubscribe = CompositeUnsubscribe; + + Some = (function() { + function Some(value) { + this.value = value; + } + + Some.prototype.getOrElse = function() { + return this.value; + }; + + Some.prototype.get = function() { + return this.value; + }; + + Some.prototype.filter = function(f) { + if (f(this.value)) { + return new Some(this.value); + } else { + return None; + } + }; + + Some.prototype.map = function(f) { + return new Some(f(this.value)); + }; + + Some.prototype.forEach = function(f) { + return f(this.value); + }; + + Some.prototype.isDefined = true; + + Some.prototype.toArray = function() { + return [this.value]; + }; + + Some.prototype.inspect = function() { + return "Some(" + this.value + ")"; + }; + + Some.prototype.toString = function() { + return this.inspect(); + }; + + return Some; + + })(); + + None = { + getOrElse: function(value) { + return value; + }, + filter: function() { + return None; + }, + map: function() { + return None; + }, + forEach: function() {}, + isDefined: false, + toArray: function() { + return []; + }, + inspect: function() { + return "None"; + }, + toString: function() { + return this.inspect(); + } + }; + + DepCache = (function() { + var collectDeps, dependsOn, flatDeps, invalidate; + flatDeps = {}; + dependsOn = function(b) { + var myDeps; + myDeps = flatDeps[this.id]; + if (!myDeps) { + myDeps = flatDeps[this.id] = {}; + collectDeps(this, this); + } + return myDeps[b.id]; + }; + collectDeps = function(orig, o) { + var dep, _i, _len, _ref1, _results; + _ref1 = o.internalDeps(); + _results = []; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + dep = _ref1[_i]; + flatDeps[orig.id][dep.id] = true; + _results.push(collectDeps(orig, dep)); + } + return _results; + }; + invalidate = function() { + return flatDeps = {}; + }; + return { + invalidate: invalidate, + dependsOn: dependsOn + }; + })(); + + UpdateBarrier = (function() { + var afterTransaction, afters, currentEventId, findIndependent, flush, hasWaiters, inTransaction, independent, invalidateDeps, rootEvent, waiters, whenDoneWith, wrappedSubscribe; + rootEvent = void 0; + waiters = []; + afters = []; + afterTransaction = function(f) { + if (rootEvent) { + return afters.push(f); + } else { + return f(); + } + }; + independent = function(waiter) { + return !_.any(waiters, (function(other) { + return waiter.obs.dependsOn(other.obs); + })); + }; + whenDoneWith = function(obs, f) { + if (rootEvent) { + return waiters.push({ + obs: obs, + f: f + }); + } else { + return f(); + } + }; + findIndependent = function() { + while (!independent(waiters[0])) { + waiters.push(waiters.splice(0, 1)[0]); + } + return waiters.splice(0, 1)[0]; + }; + flush = function() { + var _results; + _results = []; + while (waiters.length) { + _results.push(findIndependent().f()); + } + return _results; + }; + invalidateDeps = DepCache.invalidate; + inTransaction = function(event, context, f, args) { + var result; + if (rootEvent) { + return f.apply(context, args); + } else { + rootEvent = event; + try { + result = f.apply(context, args); + flush(); + } finally { + rootEvent = void 0; + while (afters.length) { + f = afters.splice(0, 1)[0]; + f(); + } + invalidateDeps(); + } + return result; + } + }; + currentEventId = function() { + if (rootEvent) { + return rootEvent.id; + } else { + return void 0; + } + }; + wrappedSubscribe = function(obs) { + return function(sink) { + var doUnsub, unsub, unsubd; + unsubd = false; + doUnsub = function() {}; + unsub = function() { + unsubd = true; + return doUnsub(); + }; + doUnsub = obs.subscribeInternal(function(event) { + return afterTransaction(function() { + var reply; + if (!unsubd) { + reply = sink(event); + if (reply === Bacon.noMore) { + return unsub(); + } + } + }); + }); + return unsub; + }; + }; + hasWaiters = function() { + return waiters.length > 0; + }; + return { + whenDoneWith: whenDoneWith, + hasWaiters: hasWaiters, + inTransaction: inTransaction, + currentEventId: currentEventId, + wrappedSubscribe: wrappedSubscribe + }; + })(); + + Bacon.EventStream = EventStream; + + Bacon.Property = Property; + + Bacon.Observable = Observable; + + Bacon.Bus = Bus; + + Bacon.Initial = Initial; + + Bacon.Next = Next; + + Bacon.End = End; + + Bacon.Error = Error; + + nop = function() {}; + + latterF = function(_, x) { + return x(); + }; + + former = function(x, _) { + return x; + }; + + initial = function(value) { + return new Initial(_.always(value)); + }; + + next = function(value) { + return new Next(_.always(value)); + }; + + end = function() { + return new End(); + }; + + toEvent = function(x) { + if (x instanceof Event) { + return x; + } else { + return next(x); + } + }; + + cloneArray = function(xs) { + return xs.slice(0); + }; + + assert = function(message, condition) { + if (!condition) { + throw message; + } + }; + + assertEventStream = function(event) { + if (!(event instanceof EventStream)) { + throw "not an EventStream : " + event; + } + }; + + assertFunction = function(f) { + return assert("not a function : " + f, isFunction(f)); + }; + + isFunction = function(f) { + return typeof f === "function"; + }; + + isArray = function(xs) { + return xs instanceof Array; + }; + + isObservable = function(x) { + return x instanceof Observable; + }; + + assertArray = function(xs) { + if (!isArray(xs)) { + throw "not an array : " + xs; + } + }; + + assertNoArguments = function(args) { + return assert("no arguments supported", args.length === 0); + }; + + assertString = function(x) { + if (typeof x !== "string") { + throw "not a string : " + x; + } + }; + + partiallyApplied = function(f, applied) { + return function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return f.apply(null, applied.concat(args)); + }; + }; + + makeSpawner = function(args) { + if (args.length === 1 && isObservable(args[0])) { + return _.always(args[0]); + } else { + return makeFunctionArgs(args); + } + }; + + makeFunctionArgs = function(args) { + args = Array.prototype.slice.call(args); + return makeFunction_.apply(null, args); + }; + + makeFunction_ = withMethodCallSupport(function() { + var args, f; + f = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + if (isFunction(f)) { + if (args.length) { + return partiallyApplied(f, args); + } else { + return f; + } + } else if (isFieldKey(f)) { + return toFieldExtractor(f, args); + } else { + return _.always(f); + } + }); + + makeFunction = function(f, args) { + return makeFunction_.apply(null, [f].concat(__slice.call(args))); + }; + + makeObservable = function(x) { + if (isObservable(x)) { + return x; + } else { + return Bacon.once(x); + } + }; + + isFieldKey = function(f) { + return (typeof f === "string") && f.length > 1 && f.charAt(0) === "."; + }; + + Bacon.isFieldKey = isFieldKey; + + toFieldExtractor = function(f, args) { + var partFuncs, parts; + parts = f.slice(1).split("."); + partFuncs = _.map(toSimpleExtractor(args), parts); + return function(value) { + var _i, _len; + for (_i = 0, _len = partFuncs.length; _i < _len; _i++) { + f = partFuncs[_i]; + value = f(value); + } + return value; + }; + }; + + toSimpleExtractor = function(args) { + return function(key) { + return function(value) { + var fieldValue; + if (value == null) { + return void 0; + } else { + fieldValue = value[key]; + if (isFunction(fieldValue)) { + return fieldValue.apply(value, args); + } else { + return fieldValue; + } + } + }; + }; + }; + + toFieldKey = function(f) { + return f.slice(1); + }; + + toCombinator = function(f) { + var key; + if (isFunction(f)) { + return f; + } else if (isFieldKey(f)) { + key = toFieldKey(f); + return function(left, right) { + return left[key](right); + }; + } else { + return assert("not a function or a field key: " + f, false); + } + }; + + toOption = function(v) { + if (v instanceof Some || v === None) { + return v; + } else { + return new Some(v); + } + }; + + _ = { + indexOf: Array.prototype.indexOf ? function(xs, x) { + return xs.indexOf(x); + } : function(xs, x) { + var i, y, _i, _len; + for (i = _i = 0, _len = xs.length; _i < _len; i = ++_i) { + y = xs[i]; + if (x === y) { + return i; + } + } + return -1; + }, + indexWhere: function(xs, f) { + var i, y, _i, _len; + for (i = _i = 0, _len = xs.length; _i < _len; i = ++_i) { + y = xs[i]; + if (f(y)) { + return i; + } + } + return -1; + }, + head: function(xs) { + return xs[0]; + }, + always: function(x) { + return function() { + return x; + }; + }, + negate: function(f) { + return function(x) { + return !f(x); + }; + }, + empty: function(xs) { + return xs.length === 0; + }, + tail: function(xs) { + return xs.slice(1, xs.length); + }, + filter: function(f, xs) { + var filtered, x, _i, _len; + filtered = []; + for (_i = 0, _len = xs.length; _i < _len; _i++) { + x = xs[_i]; + if (f(x)) { + filtered.push(x); + } + } + return filtered; + }, + map: function(f, xs) { + var x, _i, _len, _results; + _results = []; + for (_i = 0, _len = xs.length; _i < _len; _i++) { + x = xs[_i]; + _results.push(f(x)); + } + return _results; + }, + each: function(xs, f) { + var key, value, _results; + _results = []; + for (key in xs) { + value = xs[key]; + _results.push(f(key, value)); + } + return _results; + }, + toArray: function(xs) { + if (isArray(xs)) { + return xs; + } else { + return [xs]; + } + }, + contains: function(xs, x) { + return _.indexOf(xs, x) !== -1; + }, + id: function(x) { + return x; + }, + last: function(xs) { + return xs[xs.length - 1]; + }, + all: function(xs, f) { + var x, _i, _len; + if (f == null) { + f = _.id; + } + for (_i = 0, _len = xs.length; _i < _len; _i++) { + x = xs[_i]; + if (!f(x)) { + return false; + } + } + return true; + }, + any: function(xs, f) { + var x, _i, _len; + if (f == null) { + f = _.id; + } + for (_i = 0, _len = xs.length; _i < _len; _i++) { + x = xs[_i]; + if (f(x)) { + return true; + } + } + return false; + }, + without: function(x, xs) { + return _.filter((function(y) { + return y !== x; + }), xs); + }, + remove: function(x, xs) { + var i; + i = _.indexOf(xs, x); + if (i >= 0) { + return xs.splice(i, 1); + } + }, + fold: function(xs, seed, f) { + var x, _i, _len; + for (_i = 0, _len = xs.length; _i < _len; _i++) { + x = xs[_i]; + seed = f(seed, x); + } + return seed; + }, + flatMap: function(f, xs) { + return _.fold(xs, [], (function(ys, x) { + return ys.concat(f(x)); + })); + }, + cached: function(f) { + var value; + value = None; + return function() { + if (value === None) { + value = f(); + f = null; + } + return value; + }; + }, + toString: function(obj) { + var ex, internals, key, value; + try { + recursionDepth++; + if (obj == null) { + return "undefined"; + } else if (isFunction(obj)) { + return "function"; + } else if (isArray(obj)) { + if (recursionDepth > 5) { + return "[..]"; + } + return "[" + _.map(_.toString, obj).toString() + "]"; + } else if (((obj != null ? obj.toString : void 0) != null) && obj.toString !== Object.prototype.toString) { + return obj.toString(); + } else if (typeof obj === "object") { + if (recursionDepth > 5) { + return "{..}"; + } + internals = (function() { + var _results; + _results = []; + for (key in obj) { + if (!__hasProp.call(obj, key)) continue; + value = (function() { + try { + return obj[key]; + } catch (_error) { + ex = _error; + return ex; + } + })(); + _results.push(_.toString(key) + ":" + _.toString(value)); + } + return _results; + })(); + return "{" + internals + "}"; + } else { + return obj; + } + } finally { + recursionDepth--; + } + } + }; + + recursionDepth = 0; + + Bacon._ = _; + + Bacon.scheduler = { + setTimeout: function(f, d) { + return setTimeout(f, d); + }, + setInterval: function(f, i) { + return setInterval(f, i); + }, + clearInterval: function(id) { + return clearInterval(id); + }, + now: function() { + return new Date().getTime(); + } + }; + + if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) { + define([], function() { + return Bacon; + }); + this.Bacon = Bacon; + } else if (typeof module !== "undefined" && module !== null) { + module.exports = Bacon; + Bacon.Bacon = Bacon; + } else { + this.Bacon = Bacon; + } + +}).call(this); diff --git a/ajax/libs/bacon.js/package.json b/ajax/libs/bacon.js/package.json index 7bc412dd4..7fafc678e 100644 --- a/ajax/libs/bacon.js/package.json +++ b/ajax/libs/bacon.js/package.json @@ -1,7 +1,7 @@ { "name": "bacon.js", "filename": "Bacon.js", - "version": "0.7.16", + "version": "0.7.17", "author": "Juha Paananen", "description": "A small functional reactive programming lib for JavaScript.", "homepage": "http://baconjs.blogspot.com", diff --git a/ajax/libs/jsonld/0.2.15/jsonld.js b/ajax/libs/jsonld/0.2.15/jsonld.js new file mode 100644 index 000000000..00e344412 --- /dev/null +++ b/ajax/libs/jsonld/0.2.15/jsonld.js @@ -0,0 +1,6850 @@ +/** + * A JavaScript implementation of the JSON-LD API. + * + * @author Dave Longley + * + * BSD 3-Clause License + * Copyright (c) 2011-2014 Digital Bazaar, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of the Digital Bazaar, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +(function() { + +// determine if in-browser or using node.js +var _nodejs = ( + typeof process !== 'undefined' && process.versions && process.versions.node); +var _browser = !_nodejs && + (typeof window !== 'undefined' || typeof self !== 'undefined'); +if(_browser) { + if(typeof global === 'undefined') { + if(typeof window !== 'undefined') { + global = window; + } else if(typeof self !== 'undefined') { + global = self; + } else if(typeof $ !== 'undefined') { + global = $; + } + } +} + +// attaches jsonld API to the given object +var wrapper = function(jsonld) { + +/* Core API */ + +/** + * Performs JSON-LD compaction. + * + * @param input the JSON-LD input to compact. + * @param ctx the context to compact with. + * @param [options] options to use: + * [base] the base IRI to use. + * [compactArrays] true to compact arrays to single values when + * appropriate, false not to (default: true). + * [graph] true to always output a top-level graph (default: false). + * [expandContext] a context to expand with. + * [skipExpansion] true to assume the input is expanded and skip + * expansion, false not to, defaults to false. + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, compacted, ctx) called once the operation completes. + */ +jsonld.compact = function(input, ctx, options, callback) { + if(arguments.length < 2) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not compact, too few arguments.')); + }); + } + + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + + if(ctx === null) { + return jsonld.nextTick(function() { + callback(new JsonLdError( + 'The compaction context must not be null.', + 'jsonld.CompactError', {code: 'invalid local context'})); + }); + } + + // nothing to compact + if(input === null) { + return jsonld.nextTick(function() { + callback(null, null); + }); + } + + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('compactArrays' in options)) { + options.compactArrays = true; + } + if(!('graph' in options)) { + options.graph = false; + } + if(!('skipExpansion' in options)) { + options.skipExpansion = false; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + + var expand = function(input, options, callback) { + jsonld.nextTick(function() { + if(options.skipExpansion) { + return callback(null, input); + } + jsonld.expand(input, options, callback); + }); + }; + + // expand input then do compaction + expand(input, options, function(err, expanded) { + if(err) { + return callback(new JsonLdError( + 'Could not expand input before compaction.', + 'jsonld.CompactError', {cause: err})); + } + + // process context + var activeCtx = _getInitialContext(options); + jsonld.processContext(activeCtx, ctx, options, function(err, activeCtx) { + if(err) { + return callback(new JsonLdError( + 'Could not process context before compaction.', + 'jsonld.CompactError', {cause: err})); + } + + var compacted; + try { + // do compaction + compacted = new Processor().compact( + activeCtx, null, expanded, options); + } catch(ex) { + return callback(ex); + } + + cleanup(null, compacted, activeCtx, options); + }); + }); + + // performs clean up after compaction + function cleanup(err, compacted, activeCtx, options) { + if(err) { + return callback(err); + } + + if(options.compactArrays && !options.graph && _isArray(compacted)) { + if(compacted.length === 1) { + // simplify to a single item + compacted = compacted[0]; + } else if(compacted.length === 0) { + // simplify to an empty object + compacted = {}; + } + } else if(options.graph && _isObject(compacted)) { + // always use array if graph option is on + compacted = [compacted]; + } + + // follow @context key + if(_isObject(ctx) && '@context' in ctx) { + ctx = ctx['@context']; + } + + // build output context + ctx = _clone(ctx); + if(!_isArray(ctx)) { + ctx = [ctx]; + } + // remove empty contexts + var tmp = ctx; + ctx = []; + for(var i = 0; i < tmp.length; ++i) { + if(!_isObject(tmp[i]) || Object.keys(tmp[i]).length > 0) { + ctx.push(tmp[i]); + } + } + + // remove array if only one context + var hasContext = (ctx.length > 0); + if(ctx.length === 1) { + ctx = ctx[0]; + } + + // add context and/or @graph + if(_isArray(compacted)) { + // use '@graph' keyword + var kwgraph = _compactIri(activeCtx, '@graph'); + var graph = compacted; + compacted = {}; + if(hasContext) { + compacted['@context'] = ctx; + } + compacted[kwgraph] = graph; + } else if(_isObject(compacted) && hasContext) { + // reorder keys so @context is first + var graph = compacted; + compacted = {'@context': ctx}; + for(var key in graph) { + compacted[key] = graph[key]; + } + } + + callback(null, compacted, activeCtx); + } +}; + +/** + * Performs JSON-LD expansion. + * + * @param input the JSON-LD input to expand. + * @param [options] the options to use: + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [keepFreeFloatingNodes] true to keep free-floating nodes, + * false not to, defaults to false. + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, expanded) called once the operation completes. + */ +jsonld.expand = function(input, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not expand, too few arguments.')); + }); + } + + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + + // set default options + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + if(!('keepFreeFloatingNodes' in options)) { + options.keepFreeFloatingNodes = false; + } + + jsonld.nextTick(function() { + // if input is a string, attempt to dereference remote document + if(typeof input === 'string') { + var done = function(err, remoteDoc) { + if(err) { + return callback(err); + } + try { + if(!remoteDoc.document) { + throw new JsonLdError( + 'No remote document found at the given URL.', + 'jsonld.NullRemoteDocument'); + } + if(typeof remoteDoc.document === 'string') { + remoteDoc.document = JSON.parse(remoteDoc.document); + } + } catch(ex) { + return callback(new JsonLdError( + 'Could not retrieve a JSON-LD document from the URL. URL ' + + 'dereferencing not implemented.', 'jsonld.LoadDocumentError', { + code: 'loading document failed', + cause: ex, + remoteDoc: remoteDoc + })); + } + expand(remoteDoc); + }; + var promise = options.documentLoader(input, done); + if(promise && 'then' in promise) { + promise.then(done.bind(null, null), done); + } + return; + } + // nothing to load + expand({contextUrl: null, documentUrl: null, document: input}); + }); + + function expand(remoteDoc) { + // set default base + if(!('base' in options)) { + options.base = remoteDoc.documentUrl || ''; + } + // build meta-object and retrieve all @context URLs + var input = { + document: _clone(remoteDoc.document), + remoteContext: {'@context': remoteDoc.contextUrl} + }; + if('expandContext' in options) { + var expandContext = _clone(options.expandContext); + if(typeof expandContext === 'object' && '@context' in expandContext) { + input.expandContext = expandContext; + } else { + input.expandContext = {'@context': expandContext}; + } + } + _retrieveContextUrls(input, options, function(err, input) { + if(err) { + return callback(err); + } + + var expanded; + try { + var processor = new Processor(); + var activeCtx = _getInitialContext(options); + var document = input.document; + var remoteContext = input.remoteContext['@context']; + + // process optional expandContext + if(input.expandContext) { + activeCtx = processor.processContext( + activeCtx, input.expandContext['@context'], options); + } + + // process remote context from HTTP Link Header + if(remoteContext) { + activeCtx = processor.processContext( + activeCtx, remoteContext, options); + } + + // expand document + expanded = processor.expand( + activeCtx, null, document, options, false); + + // optimize away @graph with no other properties + if(_isObject(expanded) && ('@graph' in expanded) && + Object.keys(expanded).length === 1) { + expanded = expanded['@graph']; + } else if(expanded === null) { + expanded = []; + } + + // normalize to an array + if(!_isArray(expanded)) { + expanded = [expanded]; + } + } catch(ex) { + return callback(ex); + } + callback(null, expanded); + }); + } +}; + +/** + * Performs JSON-LD flattening. + * + * @param input the JSON-LD to flatten. + * @param ctx the context to use to compact the flattened output, or null. + * @param [options] the options to use: + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, flattened) called once the operation completes. + */ +jsonld.flatten = function(input, ctx, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not flatten, too few arguments.')); + }); + } + + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } else if(typeof ctx === 'function') { + callback = ctx; + ctx = null; + options = {}; + } + options = options || {}; + + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + + // expand input + jsonld.expand(input, options, function(err, _input) { + if(err) { + return callback(new JsonLdError( + 'Could not expand input before flattening.', + 'jsonld.FlattenError', {cause: err})); + } + + var flattened; + try { + // do flattening + flattened = new Processor().flatten(_input); + } catch(ex) { + return callback(ex); + } + + if(ctx === null) { + return callback(null, flattened); + } + + // compact result (force @graph option to true, skip expansion) + options.graph = true; + options.skipExpansion = true; + jsonld.compact(flattened, ctx, options, function(err, compacted) { + if(err) { + return callback(new JsonLdError( + 'Could not compact flattened output.', + 'jsonld.FlattenError', {cause: err})); + } + callback(null, compacted); + }); + }); +}; + +/** + * Performs JSON-LD framing. + * + * @param input the JSON-LD input to frame. + * @param frame the JSON-LD frame to use. + * @param [options] the framing options. + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [embed] default @embed flag (default: true). + * [explicit] default @explicit flag (default: false). + * [omitDefault] default @omitDefault flag (default: false). + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, framed) called once the operation completes. + */ +jsonld.frame = function(input, frame, options, callback) { + if(arguments.length < 2) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not frame, too few arguments.')); + }); + } + + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + if(!('embed' in options)) { + options.embed = true; + } + options.explicit = options.explicit || false; + options.omitDefault = options.omitDefault || false; + + jsonld.nextTick(function() { + // if frame is a string, attempt to dereference remote document + if(typeof frame === 'string') { + var done = function(err, remoteDoc) { + if(err) { + return callback(err); + } + try { + if(!remoteDoc.document) { + throw new JsonLdError( + 'No remote document found at the given URL.', + 'jsonld.NullRemoteDocument'); + } + if(typeof remoteDoc.document === 'string') { + remoteDoc.document = JSON.parse(remoteDoc.document); + } + } catch(ex) { + return callback(new JsonLdError( + 'Could not retrieve a JSON-LD document from the URL. URL ' + + 'dereferencing not implemented.', 'jsonld.LoadDocumentError', { + code: 'loading document failed', + cause: ex, + remoteDoc: remoteDoc + })); + } + doFrame(remoteDoc); + }; + var promise = options.documentLoader(frame, done); + if(promise && 'then' in promise) { + promise.then(done.bind(null, null), done); + } + return; + } + // nothing to load + doFrame({contextUrl: null, documentUrl: null, document: frame}); + }); + + function doFrame(remoteFrame) { + // preserve frame context and add any Link header context + var frame = remoteFrame.document; + var ctx; + if(frame) { + ctx = frame['@context']; + if(remoteFrame.contextUrl) { + if(!ctx) { + ctx = remoteFrame.contextUrl; + } else if(_isArray(ctx)) { + ctx.push(remoteFrame.contextUrl); + } else { + ctx = [ctx, remoteFrame.contextUrl]; + } + frame['@context'] = ctx; + } else { + ctx = ctx || {}; + } + } else { + ctx = {}; + } + + // expand input + jsonld.expand(input, options, function(err, expanded) { + if(err) { + return callback(new JsonLdError( + 'Could not expand input before framing.', + 'jsonld.FrameError', {cause: err})); + } + + // expand frame + var opts = _clone(options); + opts.isFrame = true; + opts.keepFreeFloatingNodes = true; + jsonld.expand(frame, opts, function(err, expandedFrame) { + if(err) { + return callback(new JsonLdError( + 'Could not expand frame before framing.', + 'jsonld.FrameError', {cause: err})); + } + + var framed; + try { + // do framing + framed = new Processor().frame(expanded, expandedFrame, opts); + } catch(ex) { + return callback(ex); + } + + // compact result (force @graph option to true, skip expansion) + opts.graph = true; + opts.skipExpansion = true; + jsonld.compact(framed, ctx, opts, function(err, compacted, ctx) { + if(err) { + return callback(new JsonLdError( + 'Could not compact framed output.', + 'jsonld.FrameError', {cause: err})); + } + // get graph alias + var graph = _compactIri(ctx, '@graph'); + // remove @preserve from results + compacted[graph] = _removePreserve(ctx, compacted[graph], opts); + callback(null, compacted); + }); + }); + }); + } +}; + +/** + * Performs JSON-LD objectification. + * + * @param input the JSON-LD input to objectify. + * @param ctx the JSON-LD context to apply. + * @param [options] the framing options. + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, objectified) called once the operation completes. + */ +jsonld.objectify = function(input, ctx, options, callback) { + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + + // expand input + jsonld.expand(input, options, function(err, _input) { + if(err) { + return callback(new JsonLdError( + 'Could not expand input before framing.', + 'jsonld.FrameError', {cause: err})); + } + + var flattened; + try { + // flatten the graph + flattened = new Processor().flatten(_input); + } catch(ex) { + return callback(ex); + } + + // compact result (force @graph option to true, skip expansion) + options.graph = true; + options.skipExpansion = true; + jsonld.compact(flattened, ctx, options, function(err, compacted, ctx) { + if(err) { + return callback(new JsonLdError( + 'Could not compact flattened output.', + 'jsonld.FrameError', {cause: err})); + } + // get graph alias + var graph = _compactIri(ctx, '@graph'); + // remove @preserve from results (named graphs?) + compacted[graph] = _removePreserve(ctx, compacted[graph], options); + + var top = compacted[graph][0]; + + var recurse = function(subject) { + // can't replace just a string + if(!_isObject(subject) && !_isArray(subject)) { + return; + } + + // bottom out recursion on re-visit + if(_isObject(subject)) { + if(recurse.visited[subject['@id']]) { + return; + } + recurse.visited[subject['@id']] = true; + } + + // each array element *or* object key + for(var k in subject) { + var obj = subject[k]; + var isid = (jsonld.getContextValue(ctx, k, '@type') === '@id'); + + // can't replace a non-object or non-array unless it's an @id + if(!_isArray(obj) && !_isObject(obj) && !isid) { + continue; + } + + if(_isString(obj) && isid) { + subject[k] = obj = top[obj]; + recurse(obj); + } else if(_isArray(obj)) { + for(var i = 0; i < obj.length; ++i) { + if(_isString(obj[i]) && isid) { + obj[i] = top[obj[i]]; + } else if(_isObject(obj[i]) && '@id' in obj[i]) { + obj[i] = top[obj[i]['@id']]; + } + recurse(obj[i]); + } + } else if(_isObject(obj)) { + var sid = obj['@id']; + subject[k] = obj = top[sid]; + recurse(obj); + } + } + }; + recurse.visited = {}; + recurse(top); + + compacted.of_type = {}; + for(var s in top) { + if(!('@type' in top[s])) { + continue; + } + var types = top[s]['@type']; + if(!_isArray(types)) { + types = [types]; + } + for(var t in types) { + if(!(types[t] in compacted.of_type)) { + compacted.of_type[types[t]] = []; + } + compacted.of_type[types[t]].push(top[s]); + } + } + callback(null, compacted); + }); + }); +}; + +/** + * Performs RDF dataset normalization on the given JSON-LD input. The output + * is an RDF dataset unless the 'format' option is used. + * + * @param input the JSON-LD input to normalize. + * @param [options] the options to use: + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [format] the format if output is a string: + * 'application/nquads' for N-Quads. + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, normalized) called once the operation completes. + */ +jsonld.normalize = function(input, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not normalize, too few arguments.')); + }); + } + + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + + // convert to RDF dataset then do normalization + var opts = _clone(options); + delete opts.format; + opts.produceGeneralizedRdf = false; + jsonld.toRDF(input, opts, function(err, dataset) { + if(err) { + return callback(new JsonLdError( + 'Could not convert input to RDF dataset before normalization.', + 'jsonld.NormalizeError', {cause: err})); + } + + // do normalization + new Processor().normalize(dataset, options, callback); + }); +}; + +/** + * Converts an RDF dataset to JSON-LD. + * + * @param dataset a serialized string of RDF in a format specified by the + * format option or an RDF dataset to convert. + * @param [options] the options to use: + * [format] the format if dataset param must first be parsed: + * 'application/nquads' for N-Quads (default). + * [rdfParser] a custom RDF-parser to use to parse the dataset. + * [useRdfType] true to use rdf:type, false to use @type + * (default: false). + * [useNativeTypes] true to convert XSD types into native types + * (boolean, integer, double), false not to (default: false). + * @param callback(err, output) called once the operation completes. + */ +jsonld.fromRDF = function(dataset, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not convert from RDF, too few arguments.')); + }); + } + + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + + // set default options + if(!('useRdfType' in options)) { + options.useRdfType = false; + } + if(!('useNativeTypes' in options)) { + options.useNativeTypes = false; + } + + if(!('format' in options) && _isString(dataset)) { + // set default format to nquads + if(!('format' in options)) { + options.format = 'application/nquads'; + } + } + + jsonld.nextTick(function() { + // handle special format + var rdfParser; + if(options.format) { + // check supported formats + rdfParser = options.rdfParser || _rdfParsers[options.format]; + if(!rdfParser) { + return callback(new JsonLdError( + 'Unknown input format.', + 'jsonld.UnknownFormat', {format: options.format})); + } + } else { + // no-op parser, assume dataset already parsed + rdfParser = function() { + return dataset; + }; + } + + // rdf parser may be async or sync, always pass callback + dataset = rdfParser(dataset, function(err, dataset) { + if(err) { + return callback(err); + } + fromRDF(dataset, options, callback); + }); + // handle synchronous or promise-based parser + if(dataset) { + // if dataset is actually a promise + if('then' in dataset) { + return dataset.then(function(dataset) { + fromRDF(dataset, options, callback); + }, callback); + } + // parser is synchronous + fromRDF(dataset, options, callback); + } + + function fromRDF(dataset, options, callback) { + // convert from RDF + new Processor().fromRDF(dataset, options, callback); + } + }); +}; + +/** + * Outputs the RDF dataset found in the given JSON-LD object. + * + * @param input the JSON-LD input. + * @param [options] the options to use: + * [base] the base IRI to use. + * [expandContext] a context to expand with. + * [format] the format to use to output a string: + * 'application/nquads' for N-Quads. + * [produceGeneralizedRdf] true to output generalized RDF, false + * to produce only standard RDF (default: false). + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, dataset) called once the operation completes. + */ +jsonld.toRDF = function(input, options, callback) { + if(arguments.length < 1) { + return jsonld.nextTick(function() { + callback(new TypeError('Could not convert to RDF, too few arguments.')); + }); + } + + // get arguments + if(typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + + // set default options + if(!('base' in options)) { + options.base = (typeof input === 'string') ? input : ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + + // expand input + jsonld.expand(input, options, function(err, expanded) { + if(err) { + return callback(new JsonLdError( + 'Could not expand input before serialization to RDF.', + 'jsonld.RdfError', {cause: err})); + } + + var dataset; + try { + // output RDF dataset + dataset = Processor.prototype.toRDF(expanded, options); + if(options.format) { + if(options.format === 'application/nquads') { + return callback(null, _toNQuads(dataset)); + } + throw new JsonLdError( + 'Unknown output format.', + 'jsonld.UnknownFormat', {format: options.format}); + } + } catch(ex) { + return callback(ex); + } + callback(null, dataset); + }); +}; + +/** + * Relabels all blank nodes in the given JSON-LD input. + * + * @param input the JSON-LD input. + */ +jsonld.relabelBlankNodes = function(input) { + _labelBlankNodes(new UniqueNamer('_:b', input)); +}; + +/** + * The default document loader for external documents. If the environment + * is node.js, a callback-continuation-style document loader is used; otherwise, + * a promises-style document loader is used. + * + * @param url the URL to load. + * @param callback(err, remoteDoc) called once the operation completes, + * if using a non-promises API. + * + * @return a promise, if using a promises API. + */ +jsonld.documentLoader = function(url, callback) { + var err = new JsonLdError( + 'Could not retrieve a JSON-LD document from the URL. URL ' + + 'dereferencing not implemented.', 'jsonld.LoadDocumentError', + {code: 'loading document failed'}); + if(_nodejs) { + return callback(err, {contextUrl: null, documentUrl: url, document: null}); + } + return jsonld.promisify(function(callback) { + callback(err); + }); +}; + +/** + * Deprecated default document loader. Use or override jsonld.documentLoader + * instead. + */ +jsonld.loadDocument = function(url, callback) { + var promise = jsonld.documentLoader(url, callback); + if(promise && 'then' in promise) { + promise.then(callback.bind(null, null), callback); + } +}; + +/* Promises API */ + +jsonld.promises = function() { + try { + jsonld.Promise = global.Promise || require('es6-promise').Promise; + } catch(e) { + throw new Error('Unable to find a Promise implementation.'); + } + var slice = Array.prototype.slice; + var promisify = jsonld.promisify; + + var api = {}; + api.expand = function(input) { + if(arguments.length < 1) { + throw new TypeError('Could not expand, too few arguments.'); + } + return promisify.apply(null, [jsonld.expand].concat(slice.call(arguments))); + }; + api.compact = function(input, ctx) { + if(arguments.length < 2) { + throw new TypeError('Could not compact, too few arguments.'); + } + var compact = function(input, ctx, options, callback) { + // ensure only one value is returned in callback + jsonld.compact(input, ctx, options, function(err, compacted) { + callback(err, compacted); + }); + }; + return promisify.apply(null, [compact].concat(slice.call(arguments))); + }; + api.flatten = function(input) { + if(arguments.length < 1) { + throw new TypeError('Could not flatten, too few arguments.'); + } + return promisify.apply( + null, [jsonld.flatten].concat(slice.call(arguments))); + }; + api.frame = function(input, frame) { + if(arguments.length < 2) { + throw new TypeError('Could not frame, too few arguments.'); + } + return promisify.apply(null, [jsonld.frame].concat(slice.call(arguments))); + }; + api.fromRDF = function(dataset) { + if(arguments.length < 1) { + throw new TypeError('Could not convert from RDF, too few arguments.'); + } + return promisify.apply( + null, [jsonld.fromRDF].concat(slice.call(arguments))); + }; + api.toRDF = function(input) { + if(arguments.length < 1) { + throw new TypeError('Could not convert to RDF, too few arguments.'); + } + return promisify.apply(null, [jsonld.toRDF].concat(slice.call(arguments))); + }; + api.normalize = function(input) { + if(arguments.length < 1) { + throw new TypeError('Could not normalize, too few arguments.'); + } + return promisify.apply( + null, [jsonld.normalize].concat(slice.call(arguments))); + }; + return api; +}; + +/** + * Converts a node.js async op into a promise w/boxed resolved value(s). + * + * @param op the operation to convert. + * + * @return the promise. + */ +jsonld.promisify = function(op) { + if(!jsonld.Promise) { + try { + jsonld.Promise = global.Promise || require('es6-promise').Promise; + } catch(e) { + throw new Error('Unable to find a Promise implementation.'); + } + } + var args = Array.prototype.slice.call(arguments, 1); + return new jsonld.Promise(function(resolve, reject) { + op.apply(null, args.concat(function(err, value) { + if(!err) { + resolve(value); + } else { + reject(err); + } + })); + }); +}; + +/* WebIDL API */ + +function JsonLdProcessor() {} +JsonLdProcessor.prototype = jsonld.promises(); +JsonLdProcessor.prototype.toString = function() { + if(this instanceof JsonLdProcessor) { + return '[object JsonLdProcessor]'; + } + return '[object JsonLdProcessorPrototype]'; +}; +jsonld.JsonLdProcessor = JsonLdProcessor; + +// IE8 has Object.defineProperty but it only +// works on DOM nodes -- so feature detection +// requires try/catch :-( +var canDefineProperty = !!Object.defineProperty; +if(canDefineProperty) { + try { + Object.defineProperty({}, 'x', {}); + } catch(e) { + canDefineProperty = false; + } +} + +if(canDefineProperty) { + Object.defineProperty(JsonLdProcessor, 'prototype', { + writable: false, + enumerable: false + }); + Object.defineProperty(JsonLdProcessor.prototype, 'constructor', { + writable: true, + enumerable: false, + configurable: true, + value: JsonLdProcessor + }); +} + +// setup browser global JsonLdProcessor +if(_browser && typeof global.JsonLdProcessor === 'undefined') { + if(canDefineProperty) { + Object.defineProperty(global, 'JsonLdProcessor', { + writable: true, + enumerable: false, + configurable: true, + value: JsonLdProcessor + }); + } else { + global.JsonLdProcessor = JsonLdProcessor; + } +} + +/* Utility API */ + +// define setImmediate and nextTick +if(typeof process === 'undefined' || !process.nextTick) { + if(typeof setImmediate === 'function') { + jsonld.setImmediate = jsonld.nextTick = function(callback) { + return setImmediate(callback); + }; + } else { + jsonld.setImmediate = function(callback) { + setTimeout(callback, 0); + }; + jsonld.nextTick = jsonld.setImmediate; + } +} else { + jsonld.nextTick = process.nextTick; + if(typeof setImmediate === 'function') { + jsonld.setImmediate = setImmediate; + } else { + jsonld.setImmediate = jsonld.nextTick; + } +} + +/** + * Parses a link header. The results will be key'd by the value of "rel". + * + * Link: ; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json" + * + * Parses as: { + * 'http://www.w3.org/ns/json-ld#context': { + * target: http://json-ld.org/contexts/person.jsonld, + * type: 'application/ld+json' + * } + * } + * + * If there is more than one "rel" with the same IRI, then entries in the + * resulting map for that "rel" will be arrays. + * + * @param header the link header to parse. + */ +jsonld.parseLinkHeader = function(header) { + var rval = {}; + // split on unbracketed/unquoted commas + var entries = header.match(/(?:<[^>]*?>|"[^"]*?"|[^,])+/g); + var rLinkHeader = /\s*<([^>]*?)>\s*(?:;\s*(.*))?/; + for(var i = 0; i < entries.length; ++i) { + var match = entries[i].match(rLinkHeader); + if(!match) { + continue; + } + var result = {target: match[1]}; + var params = match[2]; + var rParams = /(.*?)=(?:(?:"([^"]*?)")|([^"]*?))\s*(?:(?:;\s*)|$)/g; + while(match = rParams.exec(params)) { + result[match[1]] = (match[2] === undefined) ? match[3] : match[2]; + } + var rel = result['rel'] || ''; + if(_isArray(rval[rel])) { + rval[rel].push(result); + } else if(rel in rval) { + rval[rel] = [rval[rel], result]; + } else { + rval[rel] = result; + } + } + return rval; +}; + +/** + * Creates a simple document cache that retains documents for a short + * period of time. + * + * FIXME: Implement simple HTTP caching instead. + * + * @param size the maximum size of the cache. + */ +jsonld.DocumentCache = function(size) { + this.order = []; + this.cache = {}; + this.size = size || 50; + this.expires = 30*1000; +}; +jsonld.DocumentCache.prototype.get = function(url) { + if(url in this.cache) { + var entry = this.cache[url]; + if(entry.expires >= +new Date()) { + return entry.ctx; + } + delete this.cache[url]; + this.order.splice(this.order.indexOf(url), 1); + } + return null; +}; +jsonld.DocumentCache.prototype.set = function(url, ctx) { + if(this.order.length === this.size) { + delete this.cache[this.order.shift()]; + } + this.order.push(url); + this.cache[url] = {ctx: ctx, expires: (+new Date() + this.expires)}; +}; + +/** + * Creates an active context cache. + * + * @param size the maximum size of the cache. + */ +jsonld.ActiveContextCache = function(size) { + this.order = []; + this.cache = {}; + this.size = size || 100; +}; +jsonld.ActiveContextCache.prototype.get = function(activeCtx, localCtx) { + var key1 = JSON.stringify(activeCtx); + var key2 = JSON.stringify(localCtx); + var level1 = this.cache[key1]; + if(level1 && key2 in level1) { + return level1[key2]; + } + return null; +}; +jsonld.ActiveContextCache.prototype.set = function( + activeCtx, localCtx, result) { + if(this.order.length === this.size) { + var entry = this.order.shift(); + delete this.cache[entry.activeCtx][entry.localCtx]; + } + var key1 = JSON.stringify(activeCtx); + var key2 = JSON.stringify(localCtx); + this.order.push({activeCtx: key1, localCtx: key2}); + if(!(key1 in this.cache)) { + this.cache[key1] = {}; + } + this.cache[key1][key2] = _clone(result); +}; + +/** + * Default JSON-LD cache. + */ +jsonld.cache = { + activeCtx: new jsonld.ActiveContextCache() +}; + +/** + * Document loaders. + */ +jsonld.documentLoaders = {}; + +/** + * Creates a built-in jquery document loader. + * + * @param $ the jquery instance to use. + * @param options the options to use: + * secure: require all URLs to use HTTPS. + * usePromise: true to use a promises API, false for a + * callback-continuation-style API; defaults to true if Promise + * is globally defined, false if not. + * + * @return the jquery document loader. + */ +jsonld.documentLoaders.jquery = function($, options) { + options = options || {}; + var cache = new jsonld.DocumentCache(); + var loader = function(url, callback) { + if(options.secure && url.indexOf('https') !== 0) { + return callback(new JsonLdError( + 'URL could not be dereferenced; secure mode is enabled and ' + + 'the URL\'s scheme is not "https".', + 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), + {contextUrl: null, documentUrl: url, document: null}); + } + var doc = cache.get(url); + if(doc !== null) { + return callback(null, doc); + } + $.ajax({ + url: url, + accepts: { + json: 'application/ld+json, application/json' + }, + // ensure Accept header is very specific for JSON-LD/JSON + headers: { + 'Accept': 'application/ld+json, application/json' + }, + dataType: 'json', + crossDomain: true, + success: function(data, textStatus, jqXHR) { + var doc = {contextUrl: null, documentUrl: url, document: data}; + + // handle Link Header + var contentType = jqXHR.getResponseHeader('Content-Type'); + var linkHeader = jqXHR.getResponseHeader('Link'); + if(linkHeader && contentType !== 'application/ld+json') { + // only 1 related link header permitted + linkHeader = jsonld.parseLinkHeader(linkHeader)[LINK_HEADER_REL]; + if(_isArray(linkHeader)) { + return callback(new JsonLdError( + 'URL could not be dereferenced, it has more than one ' + + 'associated HTTP Link Header.', + 'jsonld.InvalidUrl', + {code: 'multiple context link headers', url: url}), doc); + } + if(linkHeader) { + doc.contextUrl = linkHeader.target; + } + } + + cache.set(url, doc); + callback(null, doc); + }, + error: function(jqXHR, textStatus, err) { + callback(new JsonLdError( + 'URL could not be dereferenced, an error occurred.', + 'jsonld.LoadDocumentError', + {code: 'loading document failed', url: url, cause: err}), + {contextUrl: null, documentUrl: url, document: null}); + } + }); + }; + + var usePromise = (typeof Promise !== 'undefined'); + if('usePromise' in options) { + usePromise = options.usePromise; + } + if(usePromise) { + return function(url) { + return jsonld.promisify(loader, url); + }; + } + return loader; +}; + +/** + * Creates a built-in node document loader. + * + * @param options the options to use: + * secure: require all URLs to use HTTPS. + * strictSSL: true to require SSL certificates to be valid, + * false not to (default: true). + * maxRedirects: the maximum number of redirects to permit, none by + * default. + * usePromise: true to use a promises API, false for a + * callback-continuation-style API; false by default. + * + * @return the node document loader. + */ +jsonld.documentLoaders.node = function(options) { + options = options || {}; + var strictSSL = ('strictSSL' in options) ? options.strictSSL : true; + var maxRedirects = ('maxRedirects' in options) ? options.maxRedirects : -1; + var request = require('request'); + var http = require('http'); + var cache = new jsonld.DocumentCache(); + function loadDocument(url, redirects, callback) { + if(options.secure && url.indexOf('https') !== 0) { + return callback(new JsonLdError( + 'URL could not be dereferenced; secure mode is enabled and ' + + 'the URL\'s scheme is not "https".', + 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), + {contextUrl: null, documentUrl: url, document: null}); + } + var doc = cache.get(url); + if(doc !== null) { + return callback(null, doc); + } + request({ + url: url, + headers: { + 'Accept': 'application/ld+json, application/json' + }, + strictSSL: strictSSL, + followRedirect: false + }, function(err, res, body) { + doc = {contextUrl: null, documentUrl: url, document: body || null}; + + // handle error + if(err) { + return callback(new JsonLdError( + 'URL could not be dereferenced, an error occurred.', + 'jsonld.LoadDocumentError', + {code: 'loading document failed', url: url, cause: err}), doc); + } + var statusText = http.STATUS_CODES[res.statusCode]; + if(res.statusCode >= 400) { + return callback(new JsonLdError( + 'URL could not be dereferenced: ' + statusText, + 'jsonld.InvalidUrl', { + code: 'loading document failed', + url: url, + httpStatusCode: res.statusCode + }), doc); + } + + // handle Link Header + if(res.headers.link && + res.headers['content-type'] !== 'application/ld+json') { + // only 1 related link header permitted + var linkHeader = jsonld.parseLinkHeader( + res.headers.link)[LINK_HEADER_REL]; + if(_isArray(linkHeader)) { + return callback(new JsonLdError( + 'URL could not be dereferenced, it has more than one associated ' + + 'HTTP Link Header.', + 'jsonld.InvalidUrl', + {code: 'multiple context link headers', url: url}), doc); + } + if(linkHeader) { + doc.contextUrl = linkHeader.target; + } + } + + // handle redirect + if(res.statusCode >= 300 && res.statusCode < 400 && + res.headers.location) { + if(redirects.length === maxRedirects) { + return callback(new JsonLdError( + 'URL could not be dereferenced; there were too many redirects.', + 'jsonld.TooManyRedirects', { + code: 'loading document failed', + url: url, + httpStatusCode: res.statusCode, + redirects: redirects + }), doc); + } + if(redirects.indexOf(url) !== -1) { + return callback(new JsonLdError( + 'URL could not be dereferenced; infinite redirection was detected.', + 'jsonld.InfiniteRedirectDetected', { + code: 'recursive context inclusion', + url: url, + httpStatusCode: res.statusCode, + redirects: redirects + }), doc); + } + redirects.push(url); + return loadDocument(res.headers.location, redirects, callback); + } + // cache for each redirected URL + redirects.push(url); + for(var i = 0; i < redirects.length; ++i) { + cache.set( + redirects[i], + {contextUrl: null, documentUrl: redirects[i], document: body}); + } + callback(err, doc); + }); + } + + var loader = function(url, callback) { + loadDocument(url, [], callback); + }; + if(options.usePromise) { + return function(url) { + return jsonld.promisify(loader, url); + }; + } + return loader; +}; + +/** + * Creates a built-in XMLHttpRequest document loader. + * + * @param options the options to use: + * secure: require all URLs to use HTTPS. + * usePromise: true to use a promises API, false for a + * callback-continuation-style API; defaults to true if Promise + * is globally defined, false if not. + * [xhr]: the XMLHttpRequest API to use. + * + * @return the XMLHttpRequest document loader. + */ +jsonld.documentLoaders.xhr = function(options) { + var rlink = /(^|(\r\n))link:/i; + options = options || {}; + var cache = new jsonld.DocumentCache(); + var loader = function(url, callback) { + if(options.secure && url.indexOf('https') !== 0) { + return callback(new JsonLdError( + 'URL could not be dereferenced; secure mode is enabled and ' + + 'the URL\'s scheme is not "https".', + 'jsonld.InvalidUrl', {code: 'loading document failed', url: url}), + {contextUrl: null, documentUrl: url, document: null}); + } + var doc = cache.get(url); + if(doc !== null) { + return callback(null, doc); + } + var xhr = options.xhr || XMLHttpRequest; + var req = new xhr(); + req.onload = function(e) { + if(req.status >= 400) { + return callback(new JsonLdError( + 'URL could not be dereferenced: ' + req.statusText, + 'jsonld.LoadDocumentError', { + code: 'loading document failed', + url: url, + httpStatusCode: req.status + }), {contextUrl: null, documentUrl: url, document: null}); + } + + var doc = {contextUrl: null, documentUrl: url, document: req.response}; + + // handle Link Header (avoid unsafe header warning by existence testing) + var contentType = req.getResponseHeader('Content-Type'); + var linkHeader; + if(rlink.test(req.getAllResponseHeaders())) { + linkHeader = req.getResponseHeader('Link'); + } + if(linkHeader && contentType !== 'application/ld+json') { + // only 1 related link header permitted + linkHeader = jsonld.parseLinkHeader(linkHeader)[LINK_HEADER_REL]; + if(_isArray(linkHeader)) { + return callback(new JsonLdError( + 'URL could not be dereferenced, it has more than one ' + + 'associated HTTP Link Header.', + 'jsonld.InvalidUrl', + {code: 'multiple context link headers', url: url}), doc); + } + if(linkHeader) { + doc.contextUrl = linkHeader.target; + } + } + + cache.set(url, doc); + callback(null, doc); + }; + req.onerror = function() { + callback(new JsonLdError( + 'URL could not be dereferenced, an error occurred.', + 'jsonld.LoadDocumentError', + {code: 'loading document failed', url: url}), + {contextUrl: null, documentUrl: url, document: null}); + }; + req.open('GET', url, true); + req.setRequestHeader('Accept', 'application/ld+json, application/json'); + req.send(); + }; + + var usePromise = (typeof Promise !== 'undefined'); + if('usePromise' in options) { + usePromise = options.usePromise; + } + if(usePromise) { + return function(url) { + return jsonld.promisify(loader, url); + }; + } + return loader; +}; + +/** + * Assigns the default document loader for external document URLs to a built-in + * default. Supported types currently include: 'jquery' and 'node'. + * + * To use the jquery document loader, the first parameter must be a reference + * to the main jquery object. + * + * @param type the type to set. + * @param [params] the parameters required to use the document loader. + */ +jsonld.useDocumentLoader = function(type) { + if(!(type in jsonld.documentLoaders)) { + throw new JsonLdError( + 'Unknown document loader type: "' + type + '"', + 'jsonld.UnknownDocumentLoader', + {type: type}); + } + + // set document loader + jsonld.documentLoader = jsonld.documentLoaders[type].apply( + jsonld, Array.prototype.slice.call(arguments, 1)); +}; + +/** + * Processes a local context, resolving any URLs as necessary, and returns a + * new active context in its callback. + * + * @param activeCtx the current active context. + * @param localCtx the local context to process. + * @param [options] the options to use: + * [documentLoader(url, callback(err, remoteDoc))] the document loader. + * @param callback(err, ctx) called once the operation completes. + */ +jsonld.processContext = function(activeCtx, localCtx) { + // get arguments + var options = {}; + var callbackArg = 2; + if(arguments.length > 3) { + options = arguments[2] || {}; + callbackArg += 1; + } + var callback = arguments[callbackArg]; + + // set default options + if(!('base' in options)) { + options.base = ''; + } + if(!('documentLoader' in options)) { + options.documentLoader = jsonld.loadDocument; + } + + // return initial context early for null context + if(localCtx === null) { + return callback(null, _getInitialContext(options)); + } + + // retrieve URLs in localCtx + localCtx = _clone(localCtx); + if(!(_isObject(localCtx) && '@context' in localCtx)) { + localCtx = {'@context': localCtx}; + } + _retrieveContextUrls(localCtx, options, function(err, ctx) { + if(err) { + return callback(err); + } + try { + // process context + ctx = new Processor().processContext(activeCtx, ctx, options); + } catch(ex) { + return callback(ex); + } + callback(null, ctx); + }); +}; + +/** + * Returns true if the given subject has the given property. + * + * @param subject the subject to check. + * @param property the property to look for. + * + * @return true if the subject has the given property, false if not. + */ +jsonld.hasProperty = function(subject, property) { + var rval = false; + if(property in subject) { + var value = subject[property]; + rval = (!_isArray(value) || value.length > 0); + } + return rval; +}; + +/** + * Determines if the given value is a property of the given subject. + * + * @param subject the subject to check. + * @param property the property to check. + * @param value the value to check. + * + * @return true if the value exists, false if not. + */ +jsonld.hasValue = function(subject, property, value) { + var rval = false; + if(jsonld.hasProperty(subject, property)) { + var val = subject[property]; + var isList = _isList(val); + if(_isArray(val) || isList) { + if(isList) { + val = val['@list']; + } + for(var i = 0; i < val.length; ++i) { + if(jsonld.compareValues(value, val[i])) { + rval = true; + break; + } + } + } else if(!_isArray(value)) { + // avoid matching the set of values with an array value parameter + rval = jsonld.compareValues(value, val); + } + } + return rval; +}; + +/** + * Adds a value to a subject. If the value is an array, all values in the + * array will be added. + * + * @param subject the subject to add the value to. + * @param property the property that relates the value to the subject. + * @param value the value to add. + * @param [options] the options to use: + * [propertyIsArray] true if the property is always an array, false + * if not (default: false). + * [allowDuplicate] true to allow duplicates, false not to (uses a + * simple shallow comparison of subject ID or value) (default: true). + */ +jsonld.addValue = function(subject, property, value, options) { + options = options || {}; + if(!('propertyIsArray' in options)) { + options.propertyIsArray = false; + } + if(!('allowDuplicate' in options)) { + options.allowDuplicate = true; + } + + if(_isArray(value)) { + if(value.length === 0 && options.propertyIsArray && + !(property in subject)) { + subject[property] = []; + } + for(var i = 0; i < value.length; ++i) { + jsonld.addValue(subject, property, value[i], options); + } + } else if(property in subject) { + // check if subject already has value if duplicates not allowed + var hasValue = (!options.allowDuplicate && + jsonld.hasValue(subject, property, value)); + + // make property an array if value not present or always an array + if(!_isArray(subject[property]) && + (!hasValue || options.propertyIsArray)) { + subject[property] = [subject[property]]; + } + + // add new value + if(!hasValue) { + subject[property].push(value); + } + } else { + // add new value as set or single value + subject[property] = options.propertyIsArray ? [value] : value; + } +}; + +/** + * Gets all of the values for a subject's property as an array. + * + * @param subject the subject. + * @param property the property. + * + * @return all of the values for a subject's property as an array. + */ +jsonld.getValues = function(subject, property) { + var rval = subject[property] || []; + if(!_isArray(rval)) { + rval = [rval]; + } + return rval; +}; + +/** + * Removes a property from a subject. + * + * @param subject the subject. + * @param property the property. + */ +jsonld.removeProperty = function(subject, property) { + delete subject[property]; +}; + +/** + * Removes a value from a subject. + * + * @param subject the subject. + * @param property the property that relates the value to the subject. + * @param value the value to remove. + * @param [options] the options to use: + * [propertyIsArray] true if the property is always an array, false + * if not (default: false). + */ +jsonld.removeValue = function(subject, property, value, options) { + options = options || {}; + if(!('propertyIsArray' in options)) { + options.propertyIsArray = false; + } + + // filter out value + var values = jsonld.getValues(subject, property).filter(function(e) { + return !jsonld.compareValues(e, value); + }); + + if(values.length === 0) { + jsonld.removeProperty(subject, property); + } else if(values.length === 1 && !options.propertyIsArray) { + subject[property] = values[0]; + } else { + subject[property] = values; + } +}; + +/** + * Compares two JSON-LD values for equality. Two JSON-LD values will be + * considered equal if: + * + * 1. They are both primitives of the same type and value. + * 2. They are both @values with the same @value, @type, @language, + * and @index, OR + * 3. They both have @ids they are the same. + * + * @param v1 the first value. + * @param v2 the second value. + * + * @return true if v1 and v2 are considered equal, false if not. + */ +jsonld.compareValues = function(v1, v2) { + // 1. equal primitives + if(v1 === v2) { + return true; + } + + // 2. equal @values + if(_isValue(v1) && _isValue(v2) && + v1['@value'] === v2['@value'] && + v1['@type'] === v2['@type'] && + v1['@language'] === v2['@language'] && + v1['@index'] === v2['@index']) { + return true; + } + + // 3. equal @ids + if(_isObject(v1) && ('@id' in v1) && _isObject(v2) && ('@id' in v2)) { + return v1['@id'] === v2['@id']; + } + + return false; +}; + +/** + * Gets the value for the given active context key and type, null if none is + * set. + * + * @param ctx the active context. + * @param key the context key. + * @param [type] the type of value to get (eg: '@id', '@type'), if not + * specified gets the entire entry for a key, null if not found. + * + * @return the value. + */ +jsonld.getContextValue = function(ctx, key, type) { + var rval = null; + + // return null for invalid key + if(key === null) { + return rval; + } + + // get default language + if(type === '@language' && (type in ctx)) { + rval = ctx[type]; + } + + // get specific entry information + if(ctx.mappings[key]) { + var entry = ctx.mappings[key]; + + if(_isUndefined(type)) { + // return whole entry + rval = entry; + } else if(type in entry) { + // return entry value for type + rval = entry[type]; + } + } + + return rval; +}; + +/** Registered RDF dataset parsers hashed by content-type. */ +var _rdfParsers = {}; + +/** + * Registers an RDF dataset parser by content-type, for use with + * jsonld.fromRDF. An RDF dataset parser will always be given two parameters, + * a string of input and a callback. An RDF dataset parser can be synchronous + * or asynchronous. + * + * If the parser function returns undefined or null then it will be assumed to + * be asynchronous w/a continuation-passing style and the callback parameter + * given to the parser MUST be invoked. + * + * If it returns a Promise, then it will be assumed to be asynchronous, but the + * callback parameter MUST NOT be invoked. It should instead be ignored. + * + * If it returns an RDF dataset, it will be assumed to be synchronous and the + * callback parameter MUST NOT be invoked. It should instead be ignored. + * + * @param contentType the content-type for the parser. + * @param parser(input, callback(err, dataset)) the parser function (takes a + * string as a parameter and either returns null/undefined and uses + * the given callback, returns a Promise, or returns an RDF dataset). + */ +jsonld.registerRDFParser = function(contentType, parser) { + _rdfParsers[contentType] = parser; +}; + +/** + * Unregisters an RDF dataset parser by content-type. + * + * @param contentType the content-type for the parser. + */ +jsonld.unregisterRDFParser = function(contentType) { + delete _rdfParsers[contentType]; +}; + +if(_nodejs) { + // needed for serialization of XML literals + if(typeof XMLSerializer === 'undefined') { + var XMLSerializer = null; + } + if(typeof Node === 'undefined') { + var Node = { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE:12 + }; + } +} + +// constants +var XSD_BOOLEAN = 'http://www.w3.org/2001/XMLSchema#boolean'; +var XSD_DOUBLE = 'http://www.w3.org/2001/XMLSchema#double'; +var XSD_INTEGER = 'http://www.w3.org/2001/XMLSchema#integer'; +var XSD_STRING = 'http://www.w3.org/2001/XMLSchema#string'; + +var RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; +var RDF_LIST = RDF + 'List'; +var RDF_FIRST = RDF + 'first'; +var RDF_REST = RDF + 'rest'; +var RDF_NIL = RDF + 'nil'; +var RDF_TYPE = RDF + 'type'; +var RDF_PLAIN_LITERAL = RDF + 'PlainLiteral'; +var RDF_XML_LITERAL = RDF + 'XMLLiteral'; +var RDF_OBJECT = RDF + 'object'; +var RDF_LANGSTRING = RDF + 'langString'; + +var LINK_HEADER_REL = 'http://www.w3.org/ns/json-ld#context'; +var MAX_CONTEXT_URLS = 10; + +/** + * A JSON-LD Error. + * + * @param msg the error message. + * @param type the error type. + * @param details the error details. + */ +var JsonLdError = function(msg, type, details) { + if(_nodejs) { + Error.call(this); + Error.captureStackTrace(this, this.constructor); + } + this.name = type || 'jsonld.Error'; + this.message = msg || 'An unspecified JSON-LD error occurred.'; + this.details = details || {}; +}; +if(_nodejs) { + require('util').inherits(JsonLdError, Error); +} + +/** + * Constructs a new JSON-LD Processor. + */ +var Processor = function() {}; + +/** + * Recursively compacts an element using the given active context. All values + * must be in expanded form before this method is called. + * + * @param activeCtx the active context to use. + * @param activeProperty the compacted property associated with the element + * to compact, null for none. + * @param element the element to compact. + * @param options the compaction options. + * + * @return the compacted value. + */ +Processor.prototype.compact = function( + activeCtx, activeProperty, element, options) { + // recursively compact array + if(_isArray(element)) { + var rval = []; + for(var i = 0; i < element.length; ++i) { + // compact, dropping any null values + var compacted = this.compact( + activeCtx, activeProperty, element[i], options); + if(compacted !== null) { + rval.push(compacted); + } + } + if(options.compactArrays && rval.length === 1) { + // use single element if no container is specified + var container = jsonld.getContextValue( + activeCtx, activeProperty, '@container'); + if(container === null) { + rval = rval[0]; + } + } + return rval; + } + + // recursively compact object + if(_isObject(element)) { + // do value compaction on @values and subject references + if(_isValue(element) || _isSubjectReference(element)) { + return _compactValue(activeCtx, activeProperty, element); + } + + // FIXME: avoid misuse of active property as an expanded property? + var insideReverse = (activeProperty === '@reverse'); + + // process element keys in order + var keys = Object.keys(element).sort(); + var rval = {}; + for(var ki = 0; ki < keys.length; ++ki) { + var expandedProperty = keys[ki]; + var expandedValue = element[expandedProperty]; + + // compact @id and @type(s) + if(expandedProperty === '@id' || expandedProperty === '@type') { + var compactedValue; + + // compact single @id + if(_isString(expandedValue)) { + compactedValue = _compactIri( + activeCtx, expandedValue, null, + {vocab: (expandedProperty === '@type')}); + } else { + // expanded value must be a @type array + compactedValue = []; + for(var vi = 0; vi < expandedValue.length; ++vi) { + compactedValue.push(_compactIri( + activeCtx, expandedValue[vi], null, {vocab: true})); + } + } + + // use keyword alias and add value + var alias = _compactIri(activeCtx, expandedProperty); + var isArray = (_isArray(compactedValue) && expandedValue.length === 0); + jsonld.addValue( + rval, alias, compactedValue, {propertyIsArray: isArray}); + continue; + } + + // handle @reverse + if(expandedProperty === '@reverse') { + // recursively compact expanded value + var compactedValue = this.compact( + activeCtx, '@reverse', expandedValue, options); + + // handle double-reversed properties + for(var compactedProperty in compactedValue) { + if(activeCtx.mappings[compactedProperty] && + activeCtx.mappings[compactedProperty].reverse) { + var value = compactedValue[compactedProperty]; + var container = jsonld.getContextValue( + activeCtx, compactedProperty, '@container'); + var useArray = (container === '@set' || !options.compactArrays); + jsonld.addValue( + rval, compactedProperty, value, {propertyIsArray: useArray}); + delete compactedValue[compactedProperty]; + } + } + + if(Object.keys(compactedValue).length > 0) { + // use keyword alias and add value + var alias = _compactIri(activeCtx, expandedProperty); + jsonld.addValue(rval, alias, compactedValue); + } + + continue; + } + + // handle @index property + if(expandedProperty === '@index') { + // drop @index if inside an @index container + var container = jsonld.getContextValue( + activeCtx, activeProperty, '@container'); + if(container === '@index') { + continue; + } + + // use keyword alias and add value + var alias = _compactIri(activeCtx, expandedProperty); + jsonld.addValue(rval, alias, expandedValue); + continue; + } + + // Note: expanded value must be an array due to expansion algorithm. + + // preserve empty arrays + if(expandedValue.length === 0) { + var itemActiveProperty = _compactIri( + activeCtx, expandedProperty, expandedValue, {vocab: true}, + insideReverse); + jsonld.addValue( + rval, itemActiveProperty, expandedValue, {propertyIsArray: true}); + } + + // recusively process array values + for(var vi = 0; vi < expandedValue.length; ++vi) { + var expandedItem = expandedValue[vi]; + + // compact property and get container type + var itemActiveProperty = _compactIri( + activeCtx, expandedProperty, expandedItem, {vocab: true}, + insideReverse); + var container = jsonld.getContextValue( + activeCtx, itemActiveProperty, '@container'); + + // get @list value if appropriate + var isList = _isList(expandedItem); + var list = null; + if(isList) { + list = expandedItem['@list']; + } + + // recursively compact expanded item + var compactedItem = this.compact( + activeCtx, itemActiveProperty, isList ? list : expandedItem, options); + + // handle @list + if(isList) { + // ensure @list value is an array + if(!_isArray(compactedItem)) { + compactedItem = [compactedItem]; + } + + if(container !== '@list') { + // wrap using @list alias + var wrapper = {}; + wrapper[_compactIri(activeCtx, '@list')] = compactedItem; + compactedItem = wrapper; + + // include @index from expanded @list, if any + if('@index' in expandedItem) { + compactedItem[_compactIri(activeCtx, '@index')] = + expandedItem['@index']; + } + } else if(itemActiveProperty in rval) { + // can't use @list container for more than 1 list + throw new JsonLdError( + 'JSON-LD compact error; property has a "@list" @container ' + + 'rule but there is more than a single @list that matches ' + + 'the compacted term in the document. Compaction might mix ' + + 'unwanted items into the list.', + 'jsonld.SyntaxError', {code: 'compaction to list of lists'}); + } + } + + // handle language and index maps + if(container === '@language' || container === '@index') { + // get or create the map object + var mapObject; + if(itemActiveProperty in rval) { + mapObject = rval[itemActiveProperty]; + } else { + rval[itemActiveProperty] = mapObject = {}; + } + + // if container is a language map, simplify compacted value to + // a simple string + if(container === '@language' && _isValue(compactedItem)) { + compactedItem = compactedItem['@value']; + } + + // add compact value to map object using key from expanded value + // based on the container type + jsonld.addValue(mapObject, expandedItem[container], compactedItem); + } else { + // use an array if: compactArrays flag is false, + // @container is @set or @list , value is an empty + // array, or key is @graph + var isArray = (!options.compactArrays || container === '@set' || + container === '@list' || + (_isArray(compactedItem) && compactedItem.length === 0) || + expandedProperty === '@list' || expandedProperty === '@graph'); + + // add compact value + jsonld.addValue( + rval, itemActiveProperty, compactedItem, + {propertyIsArray: isArray}); + } + } + } + + return rval; + } + + // only primitives remain which are already compact + return element; +}; + +/** + * Recursively expands an element using the given context. Any context in + * the element will be removed. All context URLs must have been retrieved + * before calling this method. + * + * @param activeCtx the context to use. + * @param activeProperty the property for the element, null for none. + * @param element the element to expand. + * @param options the expansion options. + * @param insideList true if the element is a list, false if not. + * + * @return the expanded value. + */ +Processor.prototype.expand = function( + activeCtx, activeProperty, element, options, insideList) { + var self = this; + + // nothing to expand + if(element === null || element === undefined) { + return null; + } + + if(!_isArray(element) && !_isObject(element)) { + // drop free-floating scalars that are not in lists + if(!insideList && (activeProperty === null || + _expandIri(activeCtx, activeProperty, {vocab: true}) === '@graph')) { + return null; + } + + // expand element according to value expansion rules + return _expandValue(activeCtx, activeProperty, element); + } + + // recursively expand array + if(_isArray(element)) { + var rval = []; + var container = jsonld.getContextValue( + activeCtx, activeProperty, '@container'); + insideList = insideList || container === '@list'; + for(var i = 0; i < element.length; ++i) { + // expand element + var e = self.expand(activeCtx, activeProperty, element[i], options); + if(insideList && (_isArray(e) || _isList(e))) { + // lists of lists are illegal + throw new JsonLdError( + 'Invalid JSON-LD syntax; lists of lists are not permitted.', + 'jsonld.SyntaxError', {code: 'list of lists'}); + } + // drop null values + if(e !== null) { + if(_isArray(e)) { + rval = rval.concat(e); + } else { + rval.push(e); + } + } + } + return rval; + } + + // recursively expand object: + + // if element has a context, process it + if('@context' in element) { + activeCtx = self.processContext(activeCtx, element['@context'], options); + } + + // expand the active property + var expandedActiveProperty = _expandIri( + activeCtx, activeProperty, {vocab: true}); + + var rval = {}; + var keys = Object.keys(element).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var key = keys[ki]; + var value = element[key]; + var expandedValue; + + // skip @context + if(key === '@context') { + continue; + } + + // expand property + var expandedProperty = _expandIri(activeCtx, key, {vocab: true}); + + // drop non-absolute IRI keys that aren't keywords + if(expandedProperty === null || + !(_isAbsoluteIri(expandedProperty) || _isKeyword(expandedProperty))) { + continue; + } + + if(_isKeyword(expandedProperty)) { + if(expandedActiveProperty === '@reverse') { + throw new JsonLdError( + 'Invalid JSON-LD syntax; a keyword cannot be used as a @reverse ' + + 'property.', 'jsonld.SyntaxError', + {code: 'invalid reverse property map', value: value}); + } + if(expandedProperty in rval) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; colliding keywords detected.', + 'jsonld.SyntaxError', + {code: 'colliding keywords', keyword: expandedProperty}); + } + } + + // syntax error if @id is not a string + if(expandedProperty === '@id' && !_isString(value)) { + if(!options.isFrame) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@id" value must a string.', + 'jsonld.SyntaxError', {code: 'invalid @id value', value: value}); + } + if(!_isObject(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@id" value must be a string or an ' + + 'object.', 'jsonld.SyntaxError', + {code: 'invalid @id value', value: value}); + } + } + + if(expandedProperty === '@type') { + _validateTypeValue(value); + } + + // @graph must be an array or an object + if(expandedProperty === '@graph' && + !(_isObject(value) || _isArray(value))) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@graph" value must not be an ' + + 'object or an array.', + 'jsonld.SyntaxError', {code: 'invalid @graph value', value: value}); + } + + // @value must not be an object or an array + if(expandedProperty === '@value' && + (_isObject(value) || _isArray(value))) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@value" value must not be an ' + + 'object or an array.', + 'jsonld.SyntaxError', + {code: 'invalid value object value', value: value}); + } + + // @language must be a string + if(expandedProperty === '@language') { + if(!_isString(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@language" value must be a string.', + 'jsonld.SyntaxError', + {code: 'invalid language-tagged string', value: value}); + } + // ensure language value is lowercase + value = value.toLowerCase(); + } + + // @index must be a string + if(expandedProperty === '@index') { + if(!_isString(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@index" value must be a string.', + 'jsonld.SyntaxError', + {code: 'invalid @index value', value: value}); + } + } + + // @reverse must be an object + if(expandedProperty === '@reverse') { + if(!_isObject(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@reverse" value must be an object.', + 'jsonld.SyntaxError', {code: 'invalid @reverse value', value: value}); + } + + expandedValue = self.expand(activeCtx, '@reverse', value, options); + + // properties double-reversed + if('@reverse' in expandedValue) { + for(var property in expandedValue['@reverse']) { + jsonld.addValue( + rval, property, expandedValue['@reverse'][property], + {propertyIsArray: true}); + } + } + + // FIXME: can this be merged with code below to simplify? + // merge in all reversed properties + var reverseMap = rval['@reverse'] || null; + for(var property in expandedValue) { + if(property === '@reverse') { + continue; + } + if(reverseMap === null) { + reverseMap = rval['@reverse'] = {}; + } + jsonld.addValue(reverseMap, property, [], {propertyIsArray: true}); + var items = expandedValue[property]; + for(var ii = 0; ii < items.length; ++ii) { + var item = items[ii]; + if(_isValue(item) || _isList(item)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@reverse" value must not be a ' + + '@value or an @list.', 'jsonld.SyntaxError', + {code: 'invalid reverse property value', value: expandedValue}); + } + jsonld.addValue( + reverseMap, property, item, {propertyIsArray: true}); + } + } + + continue; + } + + var container = jsonld.getContextValue(activeCtx, key, '@container'); + + if(container === '@language' && _isObject(value)) { + // handle language map container (skip if value is not an object) + expandedValue = _expandLanguageMap(value); + } else if(container === '@index' && _isObject(value)) { + // handle index container (skip if value is not an object) + expandedValue = (function _expandIndexMap(activeProperty) { + var rval = []; + var keys = Object.keys(value).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var key = keys[ki]; + var val = value[key]; + if(!_isArray(val)) { + val = [val]; + } + val = self.expand(activeCtx, activeProperty, val, options, false); + for(var vi = 0; vi < val.length; ++vi) { + var item = val[vi]; + if(!('@index' in item)) { + item['@index'] = key; + } + rval.push(item); + } + } + return rval; + })(key); + } else { + // recurse into @list or @set + var isList = (expandedProperty === '@list'); + if(isList || expandedProperty === '@set') { + var nextActiveProperty = activeProperty; + if(isList && expandedActiveProperty === '@graph') { + nextActiveProperty = null; + } + expandedValue = self.expand( + activeCtx, nextActiveProperty, value, options, isList); + if(isList && _isList(expandedValue)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; lists of lists are not permitted.', + 'jsonld.SyntaxError', {code: 'list of lists'}); + } + } else { + // recursively expand value with key as new active property + expandedValue = self.expand(activeCtx, key, value, options, false); + } + } + + // drop null values if property is not @value + if(expandedValue === null && expandedProperty !== '@value') { + continue; + } + + // convert expanded value to @list if container specifies it + if(expandedProperty !== '@list' && !_isList(expandedValue) && + container === '@list') { + // ensure expanded value is an array + expandedValue = (_isArray(expandedValue) ? + expandedValue : [expandedValue]); + expandedValue = {'@list': expandedValue}; + } + + // FIXME: can this be merged with code above to simplify? + // merge in reverse properties + if(activeCtx.mappings[key] && activeCtx.mappings[key].reverse) { + var reverseMap = rval['@reverse'] = rval['@reverse'] || {}; + if(!_isArray(expandedValue)) { + expandedValue = [expandedValue]; + } + for(var ii = 0; ii < expandedValue.length; ++ii) { + var item = expandedValue[ii]; + if(_isValue(item) || _isList(item)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@reverse" value must not be a ' + + '@value or an @list.', 'jsonld.SyntaxError', + {code: 'invalid reverse property value', value: expandedValue}); + } + jsonld.addValue( + reverseMap, expandedProperty, item, {propertyIsArray: true}); + } + continue; + } + + // add value for property + // use an array except for certain keywords + var useArray = + ['@index', '@id', '@type', '@value', '@language'].indexOf( + expandedProperty) === -1; + jsonld.addValue( + rval, expandedProperty, expandedValue, {propertyIsArray: useArray}); + } + + // get property count on expanded output + keys = Object.keys(rval); + var count = keys.length; + + if('@value' in rval) { + // @value must only have @language or @type + if('@type' in rval && '@language' in rval) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; an element containing "@value" may not ' + + 'contain both "@type" and "@language".', + 'jsonld.SyntaxError', {code: 'invalid value object', element: rval}); + } + var validCount = count - 1; + if('@type' in rval) { + validCount -= 1; + } + if('@index' in rval) { + validCount -= 1; + } + if('@language' in rval) { + validCount -= 1; + } + if(validCount !== 0) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; an element containing "@value" may only ' + + 'have an "@index" property and at most one other property ' + + 'which can be "@type" or "@language".', + 'jsonld.SyntaxError', {code: 'invalid value object', element: rval}); + } + // drop null @values + if(rval['@value'] === null) { + rval = null; + } else if('@language' in rval && !_isString(rval['@value'])) { + // if @language is present, @value must be a string + throw new JsonLdError( + 'Invalid JSON-LD syntax; only strings may be language-tagged.', + 'jsonld.SyntaxError', + {code: 'invalid language-tagged value', element: rval}); + } else if('@type' in rval && (!_isAbsoluteIri(rval['@type']) || + rval['@type'].indexOf('_:') === 0)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; an element containing "@value" and "@type" ' + + 'must have an absolute IRI for the value of "@type".', + 'jsonld.SyntaxError', {code: 'invalid typed value', element: rval}); + } + } else if('@type' in rval && !_isArray(rval['@type'])) { + // convert @type to an array + rval['@type'] = [rval['@type']]; + } else if('@set' in rval || '@list' in rval) { + // handle @set and @list + if(count > 1 && !(count === 2 && '@index' in rval)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; if an element has the property "@set" ' + + 'or "@list", then it can have at most one other property that is ' + + '"@index".', 'jsonld.SyntaxError', + {code: 'invalid set or list object', element: rval}); + } + // optimize away @set + if('@set' in rval) { + rval = rval['@set']; + keys = Object.keys(rval); + count = keys.length; + } + } else if(count === 1 && '@language' in rval) { + // drop objects with only @language + rval = null; + } + + // drop certain top-level objects that do not occur in lists + if(_isObject(rval) && + !options.keepFreeFloatingNodes && !insideList && + (activeProperty === null || expandedActiveProperty === '@graph')) { + // drop empty object, top-level @value/@list, or object with only @id + if(count === 0 || '@value' in rval || '@list' in rval || + (count === 1 && '@id' in rval)) { + rval = null; + } + } + + return rval; +}; + +/** + * Performs JSON-LD flattening. + * + * @param input the expanded JSON-LD to flatten. + * + * @return the flattened output. + */ +Processor.prototype.flatten = function(input) { + // produce a map of all subjects and name each bnode + var namer = new UniqueNamer('_:b'); + var graphs = {'@default': {}}; + _createNodeMap(input, graphs, '@default', namer); + + // add all non-default graphs to default graph + var defaultGraph = graphs['@default']; + var graphNames = Object.keys(graphs).sort(); + for(var i = 0; i < graphNames.length; ++i) { + var graphName = graphNames[i]; + if(graphName === '@default') { + continue; + } + var nodeMap = graphs[graphName]; + var subject = defaultGraph[graphName]; + if(!subject) { + defaultGraph[graphName] = subject = { + '@id': graphName, + '@graph': [] + }; + } else if(!('@graph' in subject)) { + subject['@graph'] = []; + } + var graph = subject['@graph']; + var ids = Object.keys(nodeMap).sort(); + for(var ii = 0; ii < ids.length; ++ii) { + var node = nodeMap[ids[ii]]; + // only add full subjects + if(!_isSubjectReference(node)) { + graph.push(node); + } + } + } + + // produce flattened output + var flattened = []; + var keys = Object.keys(defaultGraph).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var node = defaultGraph[keys[ki]]; + // only add full subjects to top-level + if(!_isSubjectReference(node)) { + flattened.push(node); + } + } + return flattened; +}; + +/** + * Performs JSON-LD framing. + * + * @param input the expanded JSON-LD to frame. + * @param frame the expanded JSON-LD frame to use. + * @param options the framing options. + * + * @return the framed output. + */ +Processor.prototype.frame = function(input, frame, options) { + // create framing state + var state = { + options: options, + graphs: {'@default': {}, '@merged': {}} + }; + + // produce a map of all graphs and name each bnode + // FIXME: currently uses subjects from @merged graph only + var namer = new UniqueNamer('_:b'); + _createNodeMap(input, state.graphs, '@merged', namer); + state.subjects = state.graphs['@merged']; + + // frame the subjects + var framed = []; + _frame(state, Object.keys(state.subjects).sort(), frame, framed, null); + return framed; +}; + +/** + * Performs normalization on the given RDF dataset. + * + * @param dataset the RDF dataset to normalize. + * @param options the normalization options. + * @param callback(err, normalized) called once the operation completes. + */ +Processor.prototype.normalize = function(dataset, options, callback) { + // create quads and map bnodes to their associated quads + var quads = []; + var bnodes = {}; + for(var graphName in dataset) { + var triples = dataset[graphName]; + if(graphName === '@default') { + graphName = null; + } + for(var ti = 0; ti < triples.length; ++ti) { + var quad = triples[ti]; + if(graphName !== null) { + if(graphName.indexOf('_:') === 0) { + quad.name = {type: 'blank node', value: graphName}; + } else { + quad.name = {type: 'IRI', value: graphName}; + } + } + quads.push(quad); + + var attrs = ['subject', 'object', 'name']; + for(var ai = 0; ai < attrs.length; ++ai) { + var attr = attrs[ai]; + if(quad[attr] && quad[attr].type === 'blank node') { + var id = quad[attr].value; + if(id in bnodes) { + bnodes[id].quads.push(quad); + } else { + bnodes[id] = {quads: [quad]}; + } + } + } + } + } + + // mapping complete, start canonical naming + var namer = new UniqueNamer('_:c14n'); + return hashBlankNodes(Object.keys(bnodes)); + + // generates unique and duplicate hashes for bnodes + function hashBlankNodes(unnamed) { + var nextUnnamed = []; + var duplicates = {}; + var unique = {}; + + // hash quads for each unnamed bnode + jsonld.setImmediate(function() {hashUnnamed(0);}); + function hashUnnamed(i) { + if(i === unnamed.length) { + // done, name blank nodes + return nameBlankNodes(unique, duplicates, nextUnnamed); + } + + // hash unnamed bnode + var bnode = unnamed[i]; + var hash = _hashQuads(bnode, bnodes, namer); + + // store hash as unique or a duplicate + if(hash in duplicates) { + duplicates[hash].push(bnode); + nextUnnamed.push(bnode); + } else if(hash in unique) { + duplicates[hash] = [unique[hash], bnode]; + nextUnnamed.push(unique[hash]); + nextUnnamed.push(bnode); + delete unique[hash]; + } else { + unique[hash] = bnode; + } + + // hash next unnamed bnode + jsonld.setImmediate(function() {hashUnnamed(i + 1);}); + } + } + + // names unique hash bnodes + function nameBlankNodes(unique, duplicates, unnamed) { + // name unique bnodes in sorted hash order + var named = false; + var hashes = Object.keys(unique).sort(); + for(var i = 0; i < hashes.length; ++i) { + var bnode = unique[hashes[i]]; + namer.getName(bnode); + named = true; + } + + if(named) { + // continue to hash bnodes if a bnode was assigned a name + hashBlankNodes(unnamed); + } else { + // name the duplicate hash bnodes + nameDuplicates(duplicates); + } + } + + // names duplicate hash bnodes + function nameDuplicates(duplicates) { + // enumerate duplicate hash groups in sorted order + var hashes = Object.keys(duplicates).sort(); + + // process each group + processGroup(0); + function processGroup(i) { + if(i === hashes.length) { + // done, create JSON-LD array + return createArray(); + } + + // name each group member + var group = duplicates[hashes[i]]; + var results = []; + nameGroupMember(group, 0); + function nameGroupMember(group, n) { + if(n === group.length) { + // name bnodes in hash order + results.sort(function(a, b) { + a = a.hash; + b = b.hash; + return (a < b) ? -1 : ((a > b) ? 1 : 0); + }); + for(var r in results) { + // name all bnodes in path namer in key-entry order + // Note: key-order is preserved in javascript + for(var key in results[r].pathNamer.existing) { + namer.getName(key); + } + } + return processGroup(i + 1); + } + + // skip already-named bnodes + var bnode = group[n]; + if(namer.isNamed(bnode)) { + return nameGroupMember(group, n + 1); + } + + // hash bnode paths + var pathNamer = new UniqueNamer('_:b'); + pathNamer.getName(bnode); + _hashPaths(bnode, bnodes, namer, pathNamer, + function(err, result) { + if(err) { + return callback(err); + } + results.push(result); + nameGroupMember(group, n + 1); + }); + } + } + } + + // creates the sorted array of RDF quads + function createArray() { + var normalized = []; + + /* Note: At this point all bnodes in the set of RDF quads have been + assigned canonical names, which have been stored in the 'namer' object. + Here each quad is updated by assigning each of its bnodes its new name + via the 'namer' object. */ + + // update bnode names in each quad and serialize + for(var i = 0; i < quads.length; ++i) { + var quad = quads[i]; + var attrs = ['subject', 'object', 'name']; + for(var ai = 0; ai < attrs.length; ++ai) { + var attr = attrs[ai]; + if(quad[attr] && quad[attr].type === 'blank node' && + quad[attr].value.indexOf('_:c14n') !== 0) { + quad[attr].value = namer.getName(quad[attr].value); + } + } + normalized.push(_toNQuad(quad, quad.name ? quad.name.value : null)); + } + + // sort normalized output + normalized.sort(); + + // handle output format + if(options.format) { + if(options.format === 'application/nquads') { + return callback(null, normalized.join('')); + } + return callback(new JsonLdError( + 'Unknown output format.', + 'jsonld.UnknownFormat', {format: options.format})); + } + + // output RDF dataset + callback(null, _parseNQuads(normalized.join(''))); + } +}; + +/** + * Converts an RDF dataset to JSON-LD. + * + * @param dataset the RDF dataset. + * @param options the RDF serialization options. + * @param callback(err, output) called once the operation completes. + */ +Processor.prototype.fromRDF = function(dataset, options, callback) { + var defaultGraph = {}; + var graphMap = {'@default': defaultGraph}; + + for(var name in dataset) { + var graph = dataset[name]; + if(!(name in graphMap)) { + graphMap[name] = {}; + } + if(name !== '@default' && !(name in defaultGraph)) { + defaultGraph[name] = {'@id': name}; + } + var nodeMap = graphMap[name]; + for(var ti = 0; ti < graph.length; ++ti) { + var triple = graph[ti]; + + // get subject, predicate, object + var s = triple.subject.value; + var p = triple.predicate.value; + var o = triple.object; + + if(!(s in nodeMap)) { + nodeMap[s] = {'@id': s}; + } + var node = nodeMap[s]; + + var objectIsId = (o.type === 'IRI' || o.type === 'blank node'); + if(objectIsId && !(o.value in nodeMap)) { + nodeMap[o.value] = {'@id': o.value}; + } + + if(p === RDF_TYPE && !options.useRdfType && objectIsId) { + jsonld.addValue(node, '@type', o.value, {propertyIsArray: true}); + continue; + } + + var value = _RDFToObject(o, options.useNativeTypes); + jsonld.addValue(node, p, value, {propertyIsArray: true}); + + // object may be an RDF list/partial list node but we can't know easily + // until all triples are read + if(objectIsId) { + var object = nodeMap[o.value]; + if(!('usages' in object)) { + object.usages = []; + } + object.usages.push({ + node: node, + property: p, + value: value + }); + } + } + } + + // convert linked lists to @list arrays + for(var name in graphMap) { + var graphObject = graphMap[name]; + + // no @lists to be converted, continue + if(!(RDF_NIL in graphObject)) { + continue; + } + + // iterate backwards through each RDF list + var nil = graphObject[RDF_NIL]; + for(var i = 0; i < nil.usages.length; ++i) { + var usage = nil.usages[i]; + var node = usage.node; + var property = usage.property; + var head = usage.value; + var list = []; + var listNodes = []; + + // ensure node is a well-formed list node; it must: + // 1. Be used only once in a list. + // 2. Have an array for rdf:first that has 1 item. + // 3. Have an array for rdf:rest that has 1 item. + // 4. Have no keys other than: @id, usages, rdf:first, rdf:rest, and, + // optionally, @type where the value is rdf:List. + var nodeKeyCount = Object.keys(node).length; + while(property === RDF_REST && node.usages.length === 1 && + _isArray(node[RDF_FIRST]) && node[RDF_FIRST].length === 1 && + _isArray(node[RDF_REST]) && node[RDF_REST].length === 1 && + (nodeKeyCount === 4 || (nodeKeyCount === 5 && _isArray(node['@type']) && + node['@type'].length === 1 && node['@type'][0] === RDF_LIST))) { + list.push(node[RDF_FIRST][0]); + listNodes.push(node['@id']); + + // get next node, moving backwards through list + usage = node.usages[0]; + node = usage.node; + property = usage.property; + head = usage.value; + nodeKeyCount = Object.keys(node).length; + + // if node is not a blank node, then list head found + if(node['@id'].indexOf('_:') !== 0) { + break; + } + } + + // the list is nested in another list + if(property === RDF_FIRST) { + // empty list + if(node['@id'] === RDF_NIL) { + // can't convert rdf:nil to a @list object because it would + // result in a list of lists which isn't supported + continue; + } + + // preserve list head + head = graphObject[head['@id']][RDF_REST][0]; + list.pop(); + listNodes.pop(); + } + + // transform list into @list object + delete head['@id']; + head['@list'] = list.reverse(); + for(var j = 0; j < listNodes.length; ++j) { + delete graphObject[listNodes[j]]; + } + } + } + + var result = []; + var subjects = Object.keys(defaultGraph).sort(); + for(var i = 0; i < subjects.length; ++i) { + var subject = subjects[i]; + var node = defaultGraph[subject]; + if(subject in graphMap) { + var graph = node['@graph'] = []; + var graphObject = graphMap[subject]; + var subjects_ = Object.keys(graphObject).sort(); + for(var si = 0; si < subjects_.length; ++si) { + var node_ = graphObject[subjects_[si]]; + delete node_.usages; + // only add full subjects to top-level + if(!_isSubjectReference(node_)) { + graph.push(node_); + } + } + } + delete node.usages; + // only add full subjects to top-level + if(!_isSubjectReference(node)) { + result.push(node); + } + } + + callback(null, result); +}; + +/** + * Outputs an RDF dataset for the expanded JSON-LD input. + * + * @param input the expanded JSON-LD input. + * @param options the RDF serialization options. + * + * @return the RDF dataset. + */ +Processor.prototype.toRDF = function(input, options) { + // create node map for default graph (and any named graphs) + var namer = new UniqueNamer('_:b'); + var nodeMap = {'@default': {}}; + _createNodeMap(input, nodeMap, '@default', namer); + + var dataset = {}; + var graphNames = Object.keys(nodeMap).sort(); + for(var i = 0; i < graphNames.length; ++i) { + var graphName = graphNames[i]; + // skip relative IRIs + if(graphName === '@default' || _isAbsoluteIri(graphName)) { + dataset[graphName] = _graphToRDF(nodeMap[graphName], namer, options); + } + } + return dataset; +}; + +/** + * Processes a local context and returns a new active context. + * + * @param activeCtx the current active context. + * @param localCtx the local context to process. + * @param options the context processing options. + * + * @return the new active context. + */ +Processor.prototype.processContext = function(activeCtx, localCtx, options) { + // normalize local context to an array of @context objects + if(_isObject(localCtx) && '@context' in localCtx && + _isArray(localCtx['@context'])) { + localCtx = localCtx['@context']; + } + var ctxs = _isArray(localCtx) ? localCtx : [localCtx]; + + // no contexts in array, clone existing context + if(ctxs.length === 0) { + return activeCtx.clone(); + } + + // process each context in order, update active context + // on each iteration to ensure proper caching + var rval = activeCtx; + for(var i = 0; i < ctxs.length; ++i) { + var ctx = ctxs[i]; + + // reset to initial context + if(ctx === null) { + rval = activeCtx = _getInitialContext(options); + continue; + } + + // dereference @context key if present + if(_isObject(ctx) && '@context' in ctx) { + ctx = ctx['@context']; + } + + // context must be an object by now, all URLs retrieved before this call + if(!_isObject(ctx)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; @context must be an object.', + 'jsonld.SyntaxError', {code: 'invalid local context', context: ctx}); + } + + // get context from cache if available + if(jsonld.cache.activeCtx) { + var cached = jsonld.cache.activeCtx.get(activeCtx, ctx); + if(cached) { + rval = activeCtx = cached; + continue; + } + } + + // update active context and clone new one before updating + activeCtx = rval; + rval = rval.clone(); + + // define context mappings for keys in local context + var defined = {}; + + // handle @base + if('@base' in ctx) { + var base = ctx['@base']; + + // clear base + if(base === null) { + base = null; + } else if(!_isString(base)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; the value of "@base" in a ' + + '@context must be a string or null.', + 'jsonld.SyntaxError', {code: 'invalid base IRI', context: ctx}); + } else if(base !== '' && !_isAbsoluteIri(base)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; the value of "@base" in a ' + + '@context must be an absolute IRI or the empty string.', + 'jsonld.SyntaxError', {code: 'invalid base IRI', context: ctx}); + } + + if(base !== null) { + base = jsonld.url.parse(base || ''); + } + rval['@base'] = base; + defined['@base'] = true; + } + + // handle @vocab + if('@vocab' in ctx) { + var value = ctx['@vocab']; + if(value === null) { + delete rval['@vocab']; + } else if(!_isString(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; the value of "@vocab" in a ' + + '@context must be a string or null.', + 'jsonld.SyntaxError', {code: 'invalid vocab mapping', context: ctx}); + } else if(!_isAbsoluteIri(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; the value of "@vocab" in a ' + + '@context must be an absolute IRI.', + 'jsonld.SyntaxError', {code: 'invalid vocab mapping', context: ctx}); + } else { + rval['@vocab'] = value; + } + defined['@vocab'] = true; + } + + // handle @language + if('@language' in ctx) { + var value = ctx['@language']; + if(value === null) { + delete rval['@language']; + } else if(!_isString(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; the value of "@language" in a ' + + '@context must be a string or null.', + 'jsonld.SyntaxError', + {code: 'invalid default language', context: ctx}); + } else { + rval['@language'] = value.toLowerCase(); + } + defined['@language'] = true; + } + + // process all other keys + for(var key in ctx) { + _createTermDefinition(rval, ctx, key, defined); + } + + // cache result + if(jsonld.cache.activeCtx) { + jsonld.cache.activeCtx.set(activeCtx, ctx, rval); + } + } + + return rval; +}; + +/** + * Expands a language map. + * + * @param languageMap the language map to expand. + * + * @return the expanded language map. + */ +function _expandLanguageMap(languageMap) { + var rval = []; + var keys = Object.keys(languageMap).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var key = keys[ki]; + var val = languageMap[key]; + if(!_isArray(val)) { + val = [val]; + } + for(var vi = 0; vi < val.length; ++vi) { + var item = val[vi]; + if(!_isString(item)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; language map values must be strings.', + 'jsonld.SyntaxError', + {code: 'invalid language map value', languageMap: languageMap}); + } + rval.push({ + '@value': item, + '@language': key.toLowerCase() + }); + } + } + return rval; +} + +/** + * Labels the blank nodes in the given value using the given UniqueNamer. + * + * @param namer the UniqueNamer to use. + * @param element the element with blank nodes to rename. + * + * @return the element. + */ +function _labelBlankNodes(namer, element) { + if(_isArray(element)) { + for(var i = 0; i < element.length; ++i) { + element[i] = _labelBlankNodes(namer, element[i]); + } + } else if(_isList(element)) { + element['@list'] = _labelBlankNodes(namer, element['@list']); + } else if(_isObject(element)) { + // rename blank node + if(_isBlankNode(element)) { + element['@id'] = namer.getName(element['@id']); + } + + // recursively apply to all keys + var keys = Object.keys(element).sort(); + for(var ki = 0; ki < keys.length; ++ki) { + var key = keys[ki]; + if(key !== '@id') { + element[key] = _labelBlankNodes(namer, element[key]); + } + } + } + + return element; +} + +/** + * Expands the given value by using the coercion and keyword rules in the + * given context. + * + * @param activeCtx the active context to use. + * @param activeProperty the active property the value is associated with. + * @param value the value to expand. + * + * @return the expanded value. + */ +function _expandValue(activeCtx, activeProperty, value) { + // nothing to expand + if(value === null) { + return null; + } + + // special-case expand @id and @type (skips '@id' expansion) + var expandedProperty = _expandIri(activeCtx, activeProperty, {vocab: true}); + if(expandedProperty === '@id') { + return _expandIri(activeCtx, value, {base: true}); + } else if(expandedProperty === '@type') { + return _expandIri(activeCtx, value, {vocab: true, base: true}); + } + + // get type definition from context + var type = jsonld.getContextValue(activeCtx, activeProperty, '@type'); + + // do @id expansion (automatic for @graph) + if(type === '@id' || (expandedProperty === '@graph' && _isString(value))) { + return {'@id': _expandIri(activeCtx, value, {base: true})}; + } + // do @id expansion w/vocab + if(type === '@vocab') { + return {'@id': _expandIri(activeCtx, value, {vocab: true, base: true})}; + } + + // do not expand keyword values + if(_isKeyword(expandedProperty)) { + return value; + } + + var rval = {}; + + if(type !== null) { + // other type + rval['@type'] = type; + } else if(_isString(value)) { + // check for language tagging for strings + var language = jsonld.getContextValue( + activeCtx, activeProperty, '@language'); + if(language !== null) { + rval['@language'] = language; + } + } + rval['@value'] = value; + + return rval; +} + +/** + * Creates an array of RDF triples for the given graph. + * + * @param graph the graph to create RDF triples for. + * @param namer a UniqueNamer for assigning blank node names. + * @param options the RDF serialization options. + * + * @return the array of RDF triples for the given graph. + */ +function _graphToRDF(graph, namer, options) { + var rval = []; + + var ids = Object.keys(graph).sort(); + for(var i = 0; i < ids.length; ++i) { + var id = ids[i]; + var node = graph[id]; + var properties = Object.keys(node).sort(); + for(var pi = 0; pi < properties.length; ++pi) { + var property = properties[pi]; + var items = node[property]; + if(property === '@type') { + property = RDF_TYPE; + } else if(_isKeyword(property)) { + continue; + } + + for(var ii = 0; ii < items.length; ++ii) { + var item = items[ii]; + + // RDF subject + var subject = {}; + subject.type = (id.indexOf('_:') === 0) ? 'blank node' : 'IRI'; + subject.value = id; + + // skip relative IRI subjects + if(!_isAbsoluteIri(id)) { + continue; + } + + // RDF predicate + var predicate = {}; + predicate.type = (property.indexOf('_:') === 0) ? 'blank node' : 'IRI'; + predicate.value = property; + + // skip relative IRI predicates + if(!_isAbsoluteIri(property)) { + continue; + } + + // skip blank node predicates unless producing generalized RDF + if(predicate.type === 'blank node' && !options.produceGeneralizedRdf) { + continue; + } + + // convert @list to triples + if(_isList(item)) { + _listToRDF(item['@list'], namer, subject, predicate, rval); + } else { + // convert value or node object to triple + var object = _objectToRDF(item); + // skip null objects (they are relative IRIs) + if(object) { + rval.push({subject: subject, predicate: predicate, object: object}); + } + } + } + } + } + + return rval; +} + +/** + * Converts a @list value into linked list of blank node RDF triples + * (an RDF collection). + * + * @param list the @list value. + * @param namer a UniqueNamer for assigning blank node names. + * @param subject the subject for the head of the list. + * @param predicate the predicate for the head of the list. + * @param triples the array of triples to append to. + */ +function _listToRDF(list, namer, subject, predicate, triples) { + var first = {type: 'IRI', value: RDF_FIRST}; + var rest = {type: 'IRI', value: RDF_REST}; + var nil = {type: 'IRI', value: RDF_NIL}; + + for(var i = 0; i < list.length; ++i) { + var item = list[i]; + + var blankNode = {type: 'blank node', value: namer.getName()}; + triples.push({subject: subject, predicate: predicate, object: blankNode}); + + subject = blankNode; + predicate = first; + var object = _objectToRDF(item); + + // skip null objects (they are relative IRIs) + if(object) { + triples.push({subject: subject, predicate: predicate, object: object}); + } + + predicate = rest; + } + + triples.push({subject: subject, predicate: predicate, object: nil}); +} + +/** + * Converts a JSON-LD value object to an RDF literal or a JSON-LD string or + * node object to an RDF resource. + * + * @param item the JSON-LD value or node object. + * + * @return the RDF literal or RDF resource. + */ +function _objectToRDF(item) { + var object = {}; + + // convert value object to RDF + if(_isValue(item)) { + object.type = 'literal'; + var value = item['@value']; + var datatype = item['@type'] || null; + + // convert to XSD datatypes as appropriate + if(_isBoolean(value)) { + object.value = value.toString(); + object.datatype = datatype || XSD_BOOLEAN; + } else if(_isDouble(value) || datatype === XSD_DOUBLE) { + // canonical double representation + object.value = value.toExponential(15).replace(/(\d)0*e\+?/, '$1E'); + object.datatype = datatype || XSD_DOUBLE; + } else if(_isNumber(value)) { + object.value = value.toFixed(0); + object.datatype = datatype || XSD_INTEGER; + } else if('@language' in item) { + object.value = value; + object.datatype = datatype || RDF_LANGSTRING; + object.language = item['@language']; + } else { + object.value = value; + object.datatype = datatype || XSD_STRING; + } + } else { + // convert string/node object to RDF + var id = _isObject(item) ? item['@id'] : item; + object.type = (id.indexOf('_:') === 0) ? 'blank node' : 'IRI'; + object.value = id; + } + + // skip relative IRIs + if(object.type === 'IRI' && !_isAbsoluteIri(object.value)) { + return null; + } + + return object; +} + +/** + * Converts an RDF triple object to a JSON-LD object. + * + * @param o the RDF triple object to convert. + * @param useNativeTypes true to output native types, false not to. + * + * @return the JSON-LD object. + */ +function _RDFToObject(o, useNativeTypes) { + // convert IRI/blank node object to JSON-LD + if(o.type === 'IRI' || o.type === 'blank node') { + return {'@id': o.value}; + } + + // convert literal to JSON-LD + var rval = {'@value': o.value}; + + // add language + if(o['language']) { + rval['@language'] = o.language; + } else { + var type = o.datatype; + if(!type) { + type = XSD_STRING; + } + // use native types for certain xsd types + if(useNativeTypes) { + if(type === XSD_BOOLEAN) { + if(rval['@value'] === 'true') { + rval['@value'] = true; + } else if(rval['@value'] === 'false') { + rval['@value'] = false; + } + } else if(_isNumeric(rval['@value'])) { + if(type === XSD_INTEGER) { + var i = parseInt(rval['@value']); + if(i.toFixed(0) === rval['@value']) { + rval['@value'] = i; + } + } else if(type === XSD_DOUBLE) { + rval['@value'] = parseFloat(rval['@value']); + } + } + // do not add native type + if([XSD_BOOLEAN, XSD_INTEGER, XSD_DOUBLE, XSD_STRING] + .indexOf(type) === -1) { + rval['@type'] = type; + } + } else if(type !== XSD_STRING) { + rval['@type'] = type; + } + } + + return rval; +} + +/** + * Compares two RDF triples for equality. + * + * @param t1 the first triple. + * @param t2 the second triple. + * + * @return true if the triples are the same, false if not. + */ +function _compareRDFTriples(t1, t2) { + var attrs = ['subject', 'predicate', 'object']; + for(var i = 0; i < attrs.length; ++i) { + var attr = attrs[i]; + if(t1[attr].type !== t2[attr].type || t1[attr].value !== t2[attr].value) { + return false; + } + } + if(t1.object.language !== t2.object.language) { + return false; + } + if(t1.object.datatype !== t2.object.datatype) { + return false; + } + return true; +} + +/** + * Hashes all of the quads about a blank node. + * + * @param id the ID of the bnode to hash quads for. + * @param bnodes the mapping of bnodes to quads. + * @param namer the canonical bnode namer. + * + * @return the new hash. + */ +function _hashQuads(id, bnodes, namer) { + // return cached hash + if('hash' in bnodes[id]) { + return bnodes[id].hash; + } + + // serialize all of bnode's quads + var quads = bnodes[id].quads; + var nquads = []; + for(var i = 0; i < quads.length; ++i) { + nquads.push(_toNQuad( + quads[i], quads[i].name ? quads[i].name.value : null, id)); + } + // sort serialized quads + nquads.sort(); + // return hashed quads + var hash = bnodes[id].hash = sha1.hash(nquads); + return hash; +} + +/** + * Produces a hash for the paths of adjacent bnodes for a bnode, + * incorporating all information about its subgraph of bnodes. This + * method will recursively pick adjacent bnode permutations that produce the + * lexicographically-least 'path' serializations. + * + * @param id the ID of the bnode to hash paths for. + * @param bnodes the map of bnode quads. + * @param namer the canonical bnode namer. + * @param pathNamer the namer used to assign names to adjacent bnodes. + * @param callback(err, result) called once the operation completes. + */ +function _hashPaths(id, bnodes, namer, pathNamer, callback) { + // create SHA-1 digest + var md = sha1.create(); + + // group adjacent bnodes by hash, keep properties and references separate + var groups = {}; + var groupHashes; + var quads = bnodes[id].quads; + jsonld.setImmediate(function() {groupNodes(0);}); + function groupNodes(i) { + if(i === quads.length) { + // done, hash groups + groupHashes = Object.keys(groups).sort(); + return hashGroup(0); + } + + // get adjacent bnode + var quad = quads[i]; + var bnode = _getAdjacentBlankNodeName(quad.subject, id); + var direction = null; + if(bnode !== null) { + // normal property + direction = 'p'; + } else { + bnode = _getAdjacentBlankNodeName(quad.object, id); + if(bnode !== null) { + // reverse property + direction = 'r'; + } + } + + if(bnode !== null) { + // get bnode name (try canonical, path, then hash) + var name; + if(namer.isNamed(bnode)) { + name = namer.getName(bnode); + } else if(pathNamer.isNamed(bnode)) { + name = pathNamer.getName(bnode); + } else { + name = _hashQuads(bnode, bnodes, namer); + } + + // hash direction, property, and bnode name/hash + var md = sha1.create(); + md.update(direction); + md.update(quad.predicate.value); + md.update(name); + var groupHash = md.digest(); + + // add bnode to hash group + if(groupHash in groups) { + groups[groupHash].push(bnode); + } else { + groups[groupHash] = [bnode]; + } + } + + jsonld.setImmediate(function() {groupNodes(i + 1);}); + } + + // hashes a group of adjacent bnodes + function hashGroup(i) { + if(i === groupHashes.length) { + // done, return SHA-1 digest and path namer + return callback(null, {hash: md.digest(), pathNamer: pathNamer}); + } + + // digest group hash + var groupHash = groupHashes[i]; + md.update(groupHash); + + // choose a path and namer from the permutations + var chosenPath = null; + var chosenNamer = null; + var permutator = new Permutator(groups[groupHash]); + jsonld.setImmediate(function() {permutate();}); + function permutate() { + var permutation = permutator.next(); + var pathNamerCopy = pathNamer.clone(); + + // build adjacent path + var path = ''; + var recurse = []; + for(var n in permutation) { + var bnode = permutation[n]; + + // use canonical name if available + if(namer.isNamed(bnode)) { + path += namer.getName(bnode); + } else { + // recurse if bnode isn't named in the path yet + if(!pathNamerCopy.isNamed(bnode)) { + recurse.push(bnode); + } + path += pathNamerCopy.getName(bnode); + } + + // skip permutation if path is already >= chosen path + if(chosenPath !== null && path.length >= chosenPath.length && + path > chosenPath) { + return nextPermutation(true); + } + } + + // does the next recursion + nextRecursion(0); + function nextRecursion(n) { + if(n === recurse.length) { + // done, do next permutation + return nextPermutation(false); + } + + // do recursion + var bnode = recurse[n]; + _hashPaths(bnode, bnodes, namer, pathNamerCopy, + function(err, result) { + if(err) { + return callback(err); + } + path += pathNamerCopy.getName(bnode) + '<' + result.hash + '>'; + pathNamerCopy = result.pathNamer; + + // skip permutation if path is already >= chosen path + if(chosenPath !== null && path.length >= chosenPath.length && + path > chosenPath) { + return nextPermutation(true); + } + + // do next recursion + nextRecursion(n + 1); + }); + } + + // stores the results of this permutation and runs the next + function nextPermutation(skipped) { + if(!skipped && (chosenPath === null || path < chosenPath)) { + chosenPath = path; + chosenNamer = pathNamerCopy; + } + + // do next permutation + if(permutator.hasNext()) { + jsonld.setImmediate(function() {permutate();}); + } else { + // digest chosen path and update namer + md.update(chosenPath); + pathNamer = chosenNamer; + + // hash the next group + hashGroup(i + 1); + } + } + } + } +} + +/** + * A helper function that gets the blank node name from an RDF quad node + * (subject or object). If the node is a blank node and its value + * does not match the given blank node ID, it will be returned. + * + * @param node the RDF quad node. + * @param id the ID of the blank node to look next to. + * + * @return the adjacent blank node name or null if none was found. + */ +function _getAdjacentBlankNodeName(node, id) { + return (node.type === 'blank node' && node.value !== id ? node.value : null); +} + +/** + * Recursively flattens the subjects in the given JSON-LD expanded input + * into a node map. + * + * @param input the JSON-LD expanded input. + * @param graphs a map of graph name to subject map. + * @param graph the name of the current graph. + * @param namer the blank node namer. + * @param name the name assigned to the current input if it is a bnode. + * @param list the list to append to, null for none. + */ +function _createNodeMap(input, graphs, graph, namer, name, list) { + // recurse through array + if(_isArray(input)) { + for(var i = 0; i < input.length; ++i) { + _createNodeMap(input[i], graphs, graph, namer, undefined, list); + } + return; + } + + // add non-object to list + if(!_isObject(input)) { + if(list) { + list.push(input); + } + return; + } + + // add values to list + if(_isValue(input)) { + if('@type' in input) { + var type = input['@type']; + // rename @type blank node + if(type.indexOf('_:') === 0) { + input['@type'] = type = namer.getName(type); + } + } + if(list) { + list.push(input); + } + return; + } + + // Note: At this point, input must be a subject. + + // spec requires @type to be named first, so assign names early + if('@type' in input) { + var types = input['@type']; + for(var i = 0; i < types.length; ++i) { + var type = types[i]; + if(type.indexOf('_:') === 0) { + namer.getName(type); + } + } + } + + // get name for subject + if(_isUndefined(name)) { + name = _isBlankNode(input) ? namer.getName(input['@id']) : input['@id']; + } + + // add subject reference to list + if(list) { + list.push({'@id': name}); + } + + // create new subject or merge into existing one + var subjects = graphs[graph]; + var subject = subjects[name] = subjects[name] || {}; + subject['@id'] = name; + var properties = Object.keys(input).sort(); + for(var pi = 0; pi < properties.length; ++pi) { + var property = properties[pi]; + + // skip @id + if(property === '@id') { + continue; + } + + // handle reverse properties + if(property === '@reverse') { + var referencedNode = {'@id': name}; + var reverseMap = input['@reverse']; + for(var reverseProperty in reverseMap) { + var items = reverseMap[reverseProperty]; + for(var ii = 0; ii < items.length; ++ii) { + var item = items[ii]; + var itemName = item['@id']; + if(_isBlankNode(item)) { + itemName = namer.getName(itemName); + } + _createNodeMap(item, graphs, graph, namer, itemName); + jsonld.addValue( + subjects[itemName], reverseProperty, referencedNode, + {propertyIsArray: true, allowDuplicate: false}); + } + } + continue; + } + + // recurse into graph + if(property === '@graph') { + // add graph subjects map entry + if(!(name in graphs)) { + graphs[name] = {}; + } + var g = (graph === '@merged') ? graph : name; + _createNodeMap(input[property], graphs, g, namer); + continue; + } + + // copy non-@type keywords + if(property !== '@type' && _isKeyword(property)) { + if(property === '@index' && '@index' in subject) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; conflicting @index property detected.', + 'jsonld.SyntaxError', + {code: 'conflicting indexes', subject: subject}); + } + subject[property] = input[property]; + continue; + } + + // iterate over objects + var objects = input[property]; + + // if property is a bnode, assign it a new id + if(property.indexOf('_:') === 0) { + property = namer.getName(property); + } + + // ensure property is added for empty arrays + if(objects.length === 0) { + jsonld.addValue(subject, property, [], {propertyIsArray: true}); + continue; + } + for(var oi = 0; oi < objects.length; ++oi) { + var o = objects[oi]; + + if(property === '@type') { + // rename @type blank nodes + o = (o.indexOf('_:') === 0) ? namer.getName(o) : o; + } + + // handle embedded subject or subject reference + if(_isSubject(o) || _isSubjectReference(o)) { + // rename blank node @id + var id = _isBlankNode(o) ? namer.getName(o['@id']) : o['@id']; + + // add reference and recurse + jsonld.addValue( + subject, property, {'@id': id}, + {propertyIsArray: true, allowDuplicate: false}); + _createNodeMap(o, graphs, graph, namer, id); + } else if(_isList(o)) { + // handle @list + var _list = []; + _createNodeMap(o['@list'], graphs, graph, namer, name, _list); + o = {'@list': _list}; + jsonld.addValue( + subject, property, o, + {propertyIsArray: true, allowDuplicate: false}); + } else { + // handle @value + _createNodeMap(o, graphs, graph, namer, name); + jsonld.addValue( + subject, property, o, {propertyIsArray: true, allowDuplicate: false}); + } + } + } +} + +/** + * Frames subjects according to the given frame. + * + * @param state the current framing state. + * @param subjects the subjects to filter. + * @param frame the frame. + * @param parent the parent subject or top-level array. + * @param property the parent property, initialized to null. + */ +function _frame(state, subjects, frame, parent, property) { + // validate the frame + _validateFrame(state, frame); + frame = frame[0]; + + // filter out subjects that match the frame + var matches = _filterSubjects(state, subjects, frame); + + // get flags for current frame + var options = state.options; + var embedOn = _getFrameFlag(frame, options, 'embed'); + var explicitOn = _getFrameFlag(frame, options, 'explicit'); + + // add matches to output + var ids = Object.keys(matches).sort(); + for(var idx in ids) { + var id = ids[idx]; + + /* Note: In order to treat each top-level match as a compartmentalized + result, create an independent copy of the embedded subjects map when the + property is null, which only occurs at the top-level. */ + if(property === null) { + state.embeds = {}; + } + + // start output + var output = {}; + output['@id'] = id; + + // prepare embed meta info + var embed = {parent: parent, property: property}; + + // if embed is on and there is an existing embed + if(embedOn && (id in state.embeds)) { + // only overwrite an existing embed if it has already been added to its + // parent -- otherwise its parent is somewhere up the tree from this + // embed and the embed would occur twice once the tree is added + embedOn = false; + + // existing embed's parent is an array + var existing = state.embeds[id]; + if(_isArray(existing.parent)) { + for(var i = 0; i < existing.parent.length; ++i) { + if(jsonld.compareValues(output, existing.parent[i])) { + embedOn = true; + break; + } + } + } else if(jsonld.hasValue(existing.parent, existing.property, output)) { + // existing embed's parent is an object + embedOn = true; + } + + // existing embed has already been added, so allow an overwrite + if(embedOn) { + _removeEmbed(state, id); + } + } + + // not embedding, add output without any other properties + if(!embedOn) { + _addFrameOutput(state, parent, property, output); + } else { + // add embed meta info + state.embeds[id] = embed; + + // iterate over subject properties + var subject = matches[id]; + var props = Object.keys(subject).sort(); + for(var i = 0; i < props.length; i++) { + var prop = props[i]; + + // copy keywords to output + if(_isKeyword(prop)) { + output[prop] = _clone(subject[prop]); + continue; + } + + // if property isn't in the frame + if(!(prop in frame)) { + // if explicit is off, embed values + if(!explicitOn) { + _embedValues(state, subject, prop, output); + } + continue; + } + + // add objects + var objects = subject[prop]; + for(var oi = 0; oi < objects.length; ++oi) { + var o = objects[oi]; + + // recurse into list + if(_isList(o)) { + // add empty list + var list = {'@list': []}; + _addFrameOutput(state, output, prop, list); + + // add list objects + var src = o['@list']; + for(var n in src) { + o = src[n]; + if(_isSubjectReference(o)) { + // recurse into subject reference + _frame(state, [o['@id']], frame[prop][0]['@list'], + list, '@list'); + } else { + // include other values automatically + _addFrameOutput(state, list, '@list', _clone(o)); + } + } + continue; + } + + if(_isSubjectReference(o)) { + // recurse into subject reference + _frame(state, [o['@id']], frame[prop], output, prop); + } else { + // include other values automatically + _addFrameOutput(state, output, prop, _clone(o)); + } + } + } + + // handle defaults + var props = Object.keys(frame).sort(); + for(var i = 0; i < props.length; ++i) { + var prop = props[i]; + + // skip keywords + if(_isKeyword(prop)) { + continue; + } + + // if omit default is off, then include default values for properties + // that appear in the next frame but are not in the matching subject + var next = frame[prop][0]; + var omitDefaultOn = _getFrameFlag(next, options, 'omitDefault'); + if(!omitDefaultOn && !(prop in output)) { + var preserve = '@null'; + if('@default' in next) { + preserve = _clone(next['@default']); + } + if(!_isArray(preserve)) { + preserve = [preserve]; + } + output[prop] = [{'@preserve': preserve}]; + } + } + + // add output to parent + _addFrameOutput(state, parent, property, output); + } + } +} + +/** + * Gets the frame flag value for the given flag name. + * + * @param frame the frame. + * @param options the framing options. + * @param name the flag name. + * + * @return the flag value. + */ +function _getFrameFlag(frame, options, name) { + var flag = '@' + name; + return (flag in frame) ? frame[flag][0] : options[name]; +} + +/** + * Validates a JSON-LD frame, throwing an exception if the frame is invalid. + * + * @param state the current frame state. + * @param frame the frame to validate. + */ +function _validateFrame(state, frame) { + if(!_isArray(frame) || frame.length !== 1 || !_isObject(frame[0])) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; a JSON-LD frame must be a single object.', + 'jsonld.SyntaxError', {frame: frame}); + } +} + +/** + * Returns a map of all of the subjects that match a parsed frame. + * + * @param state the current framing state. + * @param subjects the set of subjects to filter. + * @param frame the parsed frame. + * + * @return all of the matched subjects. + */ +function _filterSubjects(state, subjects, frame) { + // filter subjects in @id order + var rval = {}; + for(var i = 0; i < subjects.length; ++i) { + var id = subjects[i]; + var subject = state.subjects[id]; + if(_filterSubject(subject, frame)) { + rval[id] = subject; + } + } + return rval; +} + +/** + * Returns true if the given subject matches the given frame. + * + * @param subject the subject to check. + * @param frame the frame to check. + * + * @return true if the subject matches, false if not. + */ +function _filterSubject(subject, frame) { + // check @type (object value means 'any' type, fall through to ducktyping) + if('@type' in frame && + !(frame['@type'].length === 1 && _isObject(frame['@type'][0]))) { + var types = frame['@type']; + for(var i = 0; i < types.length; ++i) { + // any matching @type is a match + if(jsonld.hasValue(subject, '@type', types[i])) { + return true; + } + } + return false; + } + + // check ducktype + for(var key in frame) { + // only not a duck if @id or non-keyword isn't in subject + if((key === '@id' || !_isKeyword(key)) && !(key in subject)) { + return false; + } + } + return true; +} + +/** + * Embeds values for the given subject and property into the given output + * during the framing algorithm. + * + * @param state the current framing state. + * @param subject the subject. + * @param property the property. + * @param output the output. + */ +function _embedValues(state, subject, property, output) { + // embed subject properties in output + var objects = subject[property]; + for(var i = 0; i < objects.length; ++i) { + var o = objects[i]; + + // recurse into @list + if(_isList(o)) { + var list = {'@list': []}; + _addFrameOutput(state, output, property, list); + return _embedValues(state, o, '@list', list['@list']); + } + + // handle subject reference + if(_isSubjectReference(o)) { + var id = o['@id']; + + // embed full subject if isn't already embedded + if(!(id in state.embeds)) { + // add embed + var embed = {parent: output, property: property}; + state.embeds[id] = embed; + + // recurse into subject + o = {}; + var s = state.subjects[id]; + for(var prop in s) { + // copy keywords + if(_isKeyword(prop)) { + o[prop] = _clone(s[prop]); + continue; + } + _embedValues(state, s, prop, o); + } + } + _addFrameOutput(state, output, property, o); + } else { + // copy non-subject value + _addFrameOutput(state, output, property, _clone(o)); + } + } +} + +/** + * Removes an existing embed. + * + * @param state the current framing state. + * @param id the @id of the embed to remove. + */ +function _removeEmbed(state, id) { + // get existing embed + var embeds = state.embeds; + var embed = embeds[id]; + var parent = embed.parent; + var property = embed.property; + + // create reference to replace embed + var subject = {'@id': id}; + + // remove existing embed + if(_isArray(parent)) { + // replace subject with reference + for(var i = 0; i < parent.length; ++i) { + if(jsonld.compareValues(parent[i], subject)) { + parent[i] = subject; + break; + } + } + } else { + // replace subject with reference + var useArray = _isArray(parent[property]); + jsonld.removeValue(parent, property, subject, {propertyIsArray: useArray}); + jsonld.addValue(parent, property, subject, {propertyIsArray: useArray}); + } + + // recursively remove dependent dangling embeds + var removeDependents = function(id) { + // get embed keys as a separate array to enable deleting keys in map + var ids = Object.keys(embeds); + for(var i = 0; i < ids.length; ++i) { + var next = ids[i]; + if(next in embeds && _isObject(embeds[next].parent) && + embeds[next].parent['@id'] === id) { + delete embeds[next]; + removeDependents(next); + } + } + }; + removeDependents(id); +} + +/** + * Adds framing output to the given parent. + * + * @param state the current framing state. + * @param parent the parent to add to. + * @param property the parent property. + * @param output the output to add. + */ +function _addFrameOutput(state, parent, property, output) { + if(_isObject(parent)) { + jsonld.addValue(parent, property, output, {propertyIsArray: true}); + } else { + parent.push(output); + } +} + +/** + * Removes the @preserve keywords as the last step of the framing algorithm. + * + * @param ctx the active context used to compact the input. + * @param input the framed, compacted output. + * @param options the compaction options used. + * + * @return the resulting output. + */ +function _removePreserve(ctx, input, options) { + // recurse through arrays + if(_isArray(input)) { + var output = []; + for(var i = 0; i < input.length; ++i) { + var result = _removePreserve(ctx, input[i], options); + // drop nulls from arrays + if(result !== null) { + output.push(result); + } + } + input = output; + } else if(_isObject(input)) { + // remove @preserve + if('@preserve' in input) { + if(input['@preserve'] === '@null') { + return null; + } + return input['@preserve']; + } + + // skip @values + if(_isValue(input)) { + return input; + } + + // recurse through @lists + if(_isList(input)) { + input['@list'] = _removePreserve(ctx, input['@list'], options); + return input; + } + + // recurse through properties + for(var prop in input) { + var result = _removePreserve(ctx, input[prop], options); + var container = jsonld.getContextValue(ctx, prop, '@container'); + if(options.compactArrays && _isArray(result) && result.length === 1 && + container === null) { + result = result[0]; + } + input[prop] = result; + } + } + return input; +} + +/** + * Compares two strings first based on length and then lexicographically. + * + * @param a the first string. + * @param b the second string. + * + * @return -1 if a < b, 1 if a > b, 0 if a == b. + */ +function _compareShortestLeast(a, b) { + if(a.length < b.length) { + return -1; + } + if(b.length < a.length) { + return 1; + } + if(a === b) { + return 0; + } + return (a < b) ? -1 : 1; +} + +/** + * Picks the preferred compaction term from the given inverse context entry. + * + * @param activeCtx the active context. + * @param iri the IRI to pick the term for. + * @param value the value to pick the term for. + * @param containers the preferred containers. + * @param typeOrLanguage either '@type' or '@language'. + * @param typeOrLanguageValue the preferred value for '@type' or '@language'. + * + * @return the preferred term. + */ +function _selectTerm( + activeCtx, iri, value, containers, typeOrLanguage, typeOrLanguageValue) { + if(typeOrLanguageValue === null) { + typeOrLanguageValue = '@null'; + } + + // preferences for the value of @type or @language + var prefs = []; + + // determine prefs for @id based on whether or not value compacts to a term + if((typeOrLanguageValue === '@id' || typeOrLanguageValue === '@reverse') && + _isSubjectReference(value)) { + // prefer @reverse first + if(typeOrLanguageValue === '@reverse') { + prefs.push('@reverse'); + } + // try to compact value to a term + var term = _compactIri(activeCtx, value['@id'], null, {vocab: true}); + if(term in activeCtx.mappings && + activeCtx.mappings[term] && + activeCtx.mappings[term]['@id'] === value['@id']) { + // prefer @vocab + prefs.push.apply(prefs, ['@vocab', '@id']); + } else { + // prefer @id + prefs.push.apply(prefs, ['@id', '@vocab']); + } + } else { + prefs.push(typeOrLanguageValue); + } + prefs.push('@none'); + + var containerMap = activeCtx.inverse[iri]; + for(var ci = 0; ci < containers.length; ++ci) { + // if container not available in the map, continue + var container = containers[ci]; + if(!(container in containerMap)) { + continue; + } + + var typeOrLanguageValueMap = containerMap[container][typeOrLanguage]; + for(var pi = 0; pi < prefs.length; ++pi) { + // if type/language option not available in the map, continue + var pref = prefs[pi]; + if(!(pref in typeOrLanguageValueMap)) { + continue; + } + + // select term + return typeOrLanguageValueMap[pref]; + } + } + + return null; +} + +/** + * Compacts an IRI or keyword into a term or prefix if it can be. If the + * IRI has an associated value it may be passed. + * + * @param activeCtx the active context to use. + * @param iri the IRI to compact. + * @param value the value to check or null. + * @param relativeTo options for how to compact IRIs: + * vocab: true to split after @vocab, false not to. + * @param reverse true if a reverse property is being compacted, false if not. + * + * @return the compacted term, prefix, keyword alias, or the original IRI. + */ +function _compactIri(activeCtx, iri, value, relativeTo, reverse) { + // can't compact null + if(iri === null) { + return iri; + } + + // default value and parent to null + if(_isUndefined(value)) { + value = null; + } + // default reverse to false + if(_isUndefined(reverse)) { + reverse = false; + } + relativeTo = relativeTo || {}; + + // if term is a keyword, default vocab to true + if(_isKeyword(iri)) { + relativeTo.vocab = true; + } + + // use inverse context to pick a term if iri is relative to vocab + if(relativeTo.vocab && iri in activeCtx.getInverse()) { + var defaultLanguage = activeCtx['@language'] || '@none'; + + // prefer @index if available in value + var containers = []; + if(_isObject(value) && '@index' in value) { + containers.push('@index'); + } + + // defaults for term selection based on type/language + var typeOrLanguage = '@language'; + var typeOrLanguageValue = '@null'; + + if(reverse) { + typeOrLanguage = '@type'; + typeOrLanguageValue = '@reverse'; + containers.push('@set'); + } else if(_isList(value)) { + // choose the most specific term that works for all elements in @list + // only select @list containers if @index is NOT in value + if(!('@index' in value)) { + containers.push('@list'); + } + var list = value['@list']; + var commonLanguage = (list.length === 0) ? defaultLanguage : null; + var commonType = null; + for(var i = 0; i < list.length; ++i) { + var item = list[i]; + var itemLanguage = '@none'; + var itemType = '@none'; + if(_isValue(item)) { + if('@language' in item) { + itemLanguage = item['@language']; + } else if('@type' in item) { + itemType = item['@type']; + } else { + // plain literal + itemLanguage = '@null'; + } + } else { + itemType = '@id'; + } + if(commonLanguage === null) { + commonLanguage = itemLanguage; + } else if(itemLanguage !== commonLanguage && _isValue(item)) { + commonLanguage = '@none'; + } + if(commonType === null) { + commonType = itemType; + } else if(itemType !== commonType) { + commonType = '@none'; + } + // there are different languages and types in the list, so choose + // the most generic term, no need to keep iterating the list + if(commonLanguage === '@none' && commonType === '@none') { + break; + } + } + commonLanguage = commonLanguage || '@none'; + commonType = commonType || '@none'; + if(commonType !== '@none') { + typeOrLanguage = '@type'; + typeOrLanguageValue = commonType; + } else { + typeOrLanguageValue = commonLanguage; + } + } else { + if(_isValue(value)) { + if('@language' in value && !('@index' in value)) { + containers.push('@language'); + typeOrLanguageValue = value['@language']; + } else if('@type' in value) { + typeOrLanguage = '@type'; + typeOrLanguageValue = value['@type']; + } + } else { + typeOrLanguage = '@type'; + typeOrLanguageValue = '@id'; + } + containers.push('@set'); + } + + // do term selection + containers.push('@none'); + var term = _selectTerm( + activeCtx, iri, value, containers, typeOrLanguage, typeOrLanguageValue); + if(term !== null) { + return term; + } + } + + // no term match, use @vocab if available + if(relativeTo.vocab) { + if('@vocab' in activeCtx) { + // determine if vocab is a prefix of the iri + var vocab = activeCtx['@vocab']; + if(iri.indexOf(vocab) === 0 && iri !== vocab) { + // use suffix as relative iri if it is not a term in the active context + var suffix = iri.substr(vocab.length); + if(!(suffix in activeCtx.mappings)) { + return suffix; + } + } + } + } + + // no term or @vocab match, check for possible CURIEs + var choice = null; + for(var term in activeCtx.mappings) { + // skip terms with colons, they can't be prefixes + if(term.indexOf(':') !== -1) { + continue; + } + // skip entries with @ids that are not partial matches + var definition = activeCtx.mappings[term]; + if(!definition || + definition['@id'] === iri || iri.indexOf(definition['@id']) !== 0) { + continue; + } + + // a CURIE is usable if: + // 1. it has no mapping, OR + // 2. value is null, which means we're not compacting an @value, AND + // the mapping matches the IRI) + var curie = term + ':' + iri.substr(definition['@id'].length); + var isUsableCurie = (!(curie in activeCtx.mappings) || + (value === null && activeCtx.mappings[curie] && + activeCtx.mappings[curie]['@id'] === iri)); + + // select curie if it is shorter or the same length but lexicographically + // less than the current choice + if(isUsableCurie && (choice === null || + _compareShortestLeast(curie, choice) < 0)) { + choice = curie; + } + } + + // return chosen curie + if(choice !== null) { + return choice; + } + + // compact IRI relative to base + if(!relativeTo.vocab) { + return _removeBase(activeCtx['@base'], iri); + } + + // return IRI as is + return iri; +} + +/** + * Performs value compaction on an object with '@value' or '@id' as the only + * property. + * + * @param activeCtx the active context. + * @param activeProperty the active property that points to the value. + * @param value the value to compact. + * + * @return the compaction result. + */ +function _compactValue(activeCtx, activeProperty, value) { + // value is a @value + if(_isValue(value)) { + // get context rules + var type = jsonld.getContextValue(activeCtx, activeProperty, '@type'); + var language = jsonld.getContextValue( + activeCtx, activeProperty, '@language'); + var container = jsonld.getContextValue( + activeCtx, activeProperty, '@container'); + + // whether or not the value has an @index that must be preserved + var preserveIndex = (('@index' in value) && + container !== '@index'); + + // if there's no @index to preserve ... + if(!preserveIndex) { + // matching @type or @language specified in context, compact value + if(value['@type'] === type || value['@language'] === language) { + return value['@value']; + } + } + + // return just the value of @value if all are true: + // 1. @value is the only key or @index isn't being preserved + // 2. there is no default language or @value is not a string or + // the key has a mapping with a null @language + var keyCount = Object.keys(value).length; + var isValueOnlyKey = (keyCount === 1 || + (keyCount === 2 && ('@index' in value) && !preserveIndex)); + var hasDefaultLanguage = ('@language' in activeCtx); + var isValueString = _isString(value['@value']); + var hasNullMapping = (activeCtx.mappings[activeProperty] && + activeCtx.mappings[activeProperty]['@language'] === null); + if(isValueOnlyKey && + (!hasDefaultLanguage || !isValueString || hasNullMapping)) { + return value['@value']; + } + + var rval = {}; + + // preserve @index + if(preserveIndex) { + rval[_compactIri(activeCtx, '@index')] = value['@index']; + } + + if('@type' in value) { + // compact @type IRI + rval[_compactIri(activeCtx, '@type')] = _compactIri( + activeCtx, value['@type'], null, {vocab: true}); + } else if('@language' in value) { + // alias @language + rval[_compactIri(activeCtx, '@language')] = value['@language']; + } + + // alias @value + rval[_compactIri(activeCtx, '@value')] = value['@value']; + + return rval; + } + + // value is a subject reference + var expandedProperty = _expandIri(activeCtx, activeProperty, {vocab: true}); + var type = jsonld.getContextValue(activeCtx, activeProperty, '@type'); + var compacted = _compactIri( + activeCtx, value['@id'], null, {vocab: type === '@vocab'}); + + // compact to scalar + if(type === '@id' || type === '@vocab' || expandedProperty === '@graph') { + return compacted; + } + + var rval = {}; + rval[_compactIri(activeCtx, '@id')] = compacted; + return rval; +} + +/** + * Creates a term definition during context processing. + * + * @param activeCtx the current active context. + * @param localCtx the local context being processed. + * @param term the term in the local context to define the mapping for. + * @param defined a map of defining/defined keys to detect cycles and prevent + * double definitions. + */ +function _createTermDefinition(activeCtx, localCtx, term, defined) { + if(term in defined) { + // term already defined + if(defined[term]) { + return; + } + // cycle detected + throw new JsonLdError( + 'Cyclical context definition detected.', + 'jsonld.CyclicalContext', + {code: 'cyclic IRI mapping', context: localCtx, term: term}); + } + + // now defining term + defined[term] = false; + + if(_isKeyword(term)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; keywords cannot be overridden.', + 'jsonld.SyntaxError', + {code: 'keyword redefinition', context: localCtx, term: term}); + } + + if(term === '') { + throw new JsonLdError( + 'Invalid JSON-LD syntax; a term cannot be an empty string.', + 'jsonld.SyntaxError', + {code: 'invalid term definition', context: localCtx}); + } + + // remove old mapping + if(activeCtx.mappings[term]) { + delete activeCtx.mappings[term]; + } + + // get context term value + var value = localCtx[term]; + + // clear context entry + if(value === null || (_isObject(value) && value['@id'] === null)) { + activeCtx.mappings[term] = null; + defined[term] = true; + return; + } + + // convert short-hand value to object w/@id + if(_isString(value)) { + value = {'@id': value}; + } + + if(!_isObject(value)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; @context property values must be ' + + 'strings or objects.', + 'jsonld.SyntaxError', + {code: 'invalid term definition', context: localCtx}); + } + + // create new mapping + var mapping = activeCtx.mappings[term] = {}; + mapping.reverse = false; + + if('@reverse' in value) { + if('@id' in value) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; a @reverse term definition must not ' + + 'contain @id.', 'jsonld.SyntaxError', + {code: 'invalid reverse property', context: localCtx}); + } + var reverse = value['@reverse']; + if(!_isString(reverse)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; a @context @reverse value must be a string.', + 'jsonld.SyntaxError', {code: 'invalid IRI mapping', context: localCtx}); + } + + // expand and add @id mapping + var id = _expandIri( + activeCtx, reverse, {vocab: true, base: false}, localCtx, defined); + if(!_isAbsoluteIri(id)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; a @context @reverse value must be an ' + + 'absolute IRI or a blank node identifier.', + 'jsonld.SyntaxError', {code: 'invalid IRI mapping', context: localCtx}); + } + mapping['@id'] = id; + mapping.reverse = true; + } else if('@id' in value) { + var id = value['@id']; + if(!_isString(id)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; a @context @id value must be an array ' + + 'of strings or a string.', + 'jsonld.SyntaxError', {code: 'invalid IRI mapping', context: localCtx}); + } + if(id !== term) { + // expand and add @id mapping + id = _expandIri( + activeCtx, id, {vocab: true, base: false}, localCtx, defined); + if(!_isAbsoluteIri(id) && !_isKeyword(id)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; a @context @id value must be an ' + + 'absolute IRI, a blank node identifier, or a keyword.', + 'jsonld.SyntaxError', + {code: 'invalid IRI mapping', context: localCtx}); + } + mapping['@id'] = id; + } + } + + if(!('@id' in mapping)) { + // see if the term has a prefix + var colon = term.indexOf(':'); + if(colon !== -1) { + var prefix = term.substr(0, colon); + if(prefix in localCtx) { + // define parent prefix + _createTermDefinition(activeCtx, localCtx, prefix, defined); + } + + if(activeCtx.mappings[prefix]) { + // set @id based on prefix parent + var suffix = term.substr(colon + 1); + mapping['@id'] = activeCtx.mappings[prefix]['@id'] + suffix; + } else { + // term is an absolute IRI + mapping['@id'] = term; + } + } else { + // non-IRIs *must* define @ids if @vocab is not available + if(!('@vocab' in activeCtx)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; @context terms must define an @id.', + 'jsonld.SyntaxError', + {code: 'invalid IRI mapping', context: localCtx, term: term}); + } + // prepend vocab to term + mapping['@id'] = activeCtx['@vocab'] + term; + } + } + + // IRI mapping now defined + defined[term] = true; + + if('@type' in value) { + var type = value['@type']; + if(!_isString(type)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; an @context @type values must be a string.', + 'jsonld.SyntaxError', + {code: 'invalid type mapping', context: localCtx}); + } + + if(type !== '@id' && type !== '@vocab') { + // expand @type to full IRI + type = _expandIri( + activeCtx, type, {vocab: true, base: false}, localCtx, defined); + if(!_isAbsoluteIri(type)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; an @context @type value must be an ' + + 'absolute IRI.', + 'jsonld.SyntaxError', + {code: 'invalid type mapping', context: localCtx}); + } + if(type.indexOf('_:') === 0) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; an @context @type values must be an IRI, ' + + 'not a blank node identifier.', + 'jsonld.SyntaxError', + {code: 'invalid type mapping', context: localCtx}); + } + } + + // add @type to mapping + mapping['@type'] = type; + } + + if('@container' in value) { + var container = value['@container']; + if(container !== '@list' && container !== '@set' && + container !== '@index' && container !== '@language') { + throw new JsonLdError( + 'Invalid JSON-LD syntax; @context @container value must be ' + + 'one of the following: @list, @set, @index, or @language.', + 'jsonld.SyntaxError', + {code: 'invalid container mapping', context: localCtx}); + } + if(mapping.reverse && container !== '@index' && container !== '@set' && + container !== null) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; @context @container value for a @reverse ' + + 'type definition must be @index or @set.', 'jsonld.SyntaxError', + {code: 'invalid reverse property', context: localCtx}); + } + + // add @container to mapping + mapping['@container'] = container; + } + + if('@language' in value && !('@type' in value)) { + var language = value['@language']; + if(language !== null && !_isString(language)) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; @context @language value must be ' + + 'a string or null.', 'jsonld.SyntaxError', + {code: 'invalid language mapping', context: localCtx}); + } + + // add @language to mapping + if(language !== null) { + language = language.toLowerCase(); + } + mapping['@language'] = language; + } + + // disallow aliasing @context and @preserve + var id = mapping['@id']; + if(id === '@context' || id === '@preserve') { + throw new JsonLdError( + 'Invalid JSON-LD syntax; @context and @preserve cannot be aliased.', + 'jsonld.SyntaxError', {code: 'invalid keyword alias', context: localCtx}); + } +} + +/** + * Expands a string to a full IRI. The string may be a term, a prefix, a + * relative IRI, or an absolute IRI. The associated absolute IRI will be + * returned. + * + * @param activeCtx the current active context. + * @param value the string to expand. + * @param relativeTo options for how to resolve relative IRIs: + * base: true to resolve against the base IRI, false not to. + * vocab: true to concatenate after @vocab, false not to. + * @param localCtx the local context being processed (only given if called + * during context processing). + * @param defined a map for tracking cycles in context definitions (only given + * if called during context processing). + * + * @return the expanded value. + */ +function _expandIri(activeCtx, value, relativeTo, localCtx, defined) { + // already expanded + if(value === null || _isKeyword(value)) { + return value; + } + + // define term dependency if not defined + if(localCtx && value in localCtx && defined[value] !== true) { + _createTermDefinition(activeCtx, localCtx, value, defined); + } + + relativeTo = relativeTo || {}; + if(relativeTo.vocab) { + var mapping = activeCtx.mappings[value]; + + // value is explicitly ignored with a null mapping + if(mapping === null) { + return null; + } + + if(mapping) { + // value is a term + return mapping['@id']; + } + } + + // split value into prefix:suffix + var colon = value.indexOf(':'); + if(colon !== -1) { + var prefix = value.substr(0, colon); + var suffix = value.substr(colon + 1); + + // do not expand blank nodes (prefix of '_') or already-absolute + // IRIs (suffix of '//') + if(prefix === '_' || suffix.indexOf('//') === 0) { + return value; + } + + // prefix dependency not defined, define it + if(localCtx && prefix in localCtx) { + _createTermDefinition(activeCtx, localCtx, prefix, defined); + } + + // use mapping if prefix is defined + var mapping = activeCtx.mappings[prefix]; + if(mapping) { + return mapping['@id'] + suffix; + } + + // already absolute IRI + return value; + } + + // prepend vocab + if(relativeTo.vocab && '@vocab' in activeCtx) { + return activeCtx['@vocab'] + value; + } + + // prepend base + var rval = value; + if(relativeTo.base) { + rval = _prependBase(activeCtx['@base'], rval); + } + + return rval; +} + +/** + * Prepends a base IRI to the given relative IRI. + * + * @param base the base IRI. + * @param iri the relative IRI. + * + * @return the absolute IRI. + */ +function _prependBase(base, iri) { + // skip IRI processing + if(base === null) { + return iri; + } + // already an absolute IRI + if(iri.indexOf(':') !== -1) { + return iri; + } + + // parse base if it is a string + if(_isString(base)) { + base = jsonld.url.parse(base || ''); + } + + // parse given IRI + var rel = jsonld.url.parse(iri); + + // start hierarchical part + var hierPart = (base.protocol || ''); + if(rel.authority) { + hierPart += '//' + rel.authority; + } else if(base.href !== '') { + hierPart += '//' + base.authority; + } + + // per RFC3986 normalize + var path; + + // IRI represents an absolute path + if(rel.pathname.indexOf('/') === 0) { + path = rel.pathname; + } else { + path = base.pathname; + + // append relative path to the end of the last directory from base + if(rel.pathname !== '') { + path = path.substr(0, path.lastIndexOf('/') + 1); + if(path.length > 0 && path.substr(-1) !== '/') { + path += '/'; + } + path += rel.pathname; + } + } + + // remove slashes and dots in path + path = _removeDotSegments(path, hierPart !== ''); + + // add query and hash + if(rel.query) { + path += '?' + rel.query; + } + if(rel.hash) { + path += rel.hash; + } + + var rval = hierPart + path; + + if(rval === '') { + rval = './'; + } + + return rval; +} + +/** + * Removes a base IRI from the given absolute IRI. + * + * @param base the base IRI. + * @param iri the absolute IRI. + * + * @return the relative IRI if relative to base, otherwise the absolute IRI. + */ +function _removeBase(base, iri) { + // skip IRI processing + if(base === null) { + return iri; + } + + if(_isString(base)) { + base = jsonld.url.parse(base || ''); + } + + // establish base root + var root = ''; + if(base.href !== '') { + root += (base.protocol || '') + '//' + base.authority; + } else if(iri.indexOf('//')) { + // support network-path reference with empty base + root += '//'; + } + + // IRI not relative to base + if(iri.indexOf(root) !== 0) { + return iri; + } + + // remove root from IRI and parse remainder + var rel = jsonld.url.parse(iri.substr(root.length)); + + // remove path segments that match (do not remove last segment unless there + // is a hash or query) + var baseSegments = base.normalizedPath.split('/'); + var iriSegments = rel.normalizedPath.split('/'); + var last = (rel.hash || rel.query) ? 0 : 1; + while(baseSegments.length > 0 && iriSegments.length > last) { + if(baseSegments[0] !== iriSegments[0]) { + break; + } + baseSegments.shift(); + iriSegments.shift(); + } + + // use '../' for each non-matching base segment + var rval = ''; + if(baseSegments.length > 0) { + // don't count the last segment (if it ends with '/' last path doesn't + // count and if it doesn't end with '/' it isn't a path) + baseSegments.pop(); + for(var i = 0; i < baseSegments.length; ++i) { + rval += '../'; + } + } + + // prepend remaining segments + rval += iriSegments.join('/'); + + // add query and hash + if(rel.query) { + rval += '?' + rel.query; + } + if(rel.hash) { + rval += rel.hash; + } + + if(rval === '') { + rval = './'; + } + + return rval; +} + +/** + * Gets the initial context. + * + * @param options the options to use. + * base the document base IRI. + * + * @return the initial context. + */ +function _getInitialContext(options) { + var base = jsonld.url.parse(options.base || ''); + return { + '@base': base, + mappings: {}, + inverse: null, + getInverse: _createInverseContext, + clone: _cloneActiveContext + }; + + /** + * Generates an inverse context for use in the compaction algorithm, if + * not already generated for the given active context. + * + * @return the inverse context. + */ + function _createInverseContext() { + var activeCtx = this; + + // lazily create inverse + if(activeCtx.inverse) { + return activeCtx.inverse; + } + var inverse = activeCtx.inverse = {}; + + // handle default language + var defaultLanguage = activeCtx['@language'] || '@none'; + + // create term selections for each mapping in the context, ordered by + // shortest and then lexicographically least + var mappings = activeCtx.mappings; + var terms = Object.keys(mappings).sort(_compareShortestLeast); + for(var i = 0; i < terms.length; ++i) { + var term = terms[i]; + var mapping = mappings[term]; + if(mapping === null) { + continue; + } + + var container = mapping['@container'] || '@none'; + + // iterate over every IRI in the mapping + var ids = mapping['@id']; + if(!_isArray(ids)) { + ids = [ids]; + } + for(var ii = 0; ii < ids.length; ++ii) { + var iri = ids[ii]; + var entry = inverse[iri]; + + // initialize entry + if(!entry) { + inverse[iri] = entry = {}; + } + + // add new entry + if(!entry[container]) { + entry[container] = { + '@language': {}, + '@type': {} + }; + } + entry = entry[container]; + + if(mapping.reverse) { + // term is preferred for values using @reverse + _addPreferredTerm(mapping, term, entry['@type'], '@reverse'); + } else if('@type' in mapping) { + // term is preferred for values using specific type + _addPreferredTerm(mapping, term, entry['@type'], mapping['@type']); + } else if('@language' in mapping) { + // term is preferred for values using specific language + var language = mapping['@language'] || '@null'; + _addPreferredTerm(mapping, term, entry['@language'], language); + } else { + // term is preferred for values w/default language or no type and + // no language + // add an entry for the default language + _addPreferredTerm(mapping, term, entry['@language'], defaultLanguage); + + // add entries for no type and no language + _addPreferredTerm(mapping, term, entry['@type'], '@none'); + _addPreferredTerm(mapping, term, entry['@language'], '@none'); + } + } + } + + return inverse; + } + + /** + * Adds the term for the given entry if not already added. + * + * @param mapping the term mapping. + * @param term the term to add. + * @param entry the inverse context typeOrLanguage entry to add to. + * @param typeOrLanguageValue the key in the entry to add to. + */ + function _addPreferredTerm(mapping, term, entry, typeOrLanguageValue) { + if(!(typeOrLanguageValue in entry)) { + entry[typeOrLanguageValue] = term; + } + } + + /** + * Clones an active context, creating a child active context. + * + * @return a clone (child) of the active context. + */ + function _cloneActiveContext() { + var child = {}; + child['@base'] = this['@base']; + child.mappings = _clone(this.mappings); + child.clone = this.clone; + child.inverse = null; + child.getInverse = this.getInverse; + if('@language' in this) { + child['@language'] = this['@language']; + } + if('@vocab' in this) { + child['@vocab'] = this['@vocab']; + } + return child; + } +} + +/** + * Returns whether or not the given value is a keyword. + * + * @param v the value to check. + * + * @return true if the value is a keyword, false if not. + */ +function _isKeyword(v) { + if(!_isString(v)) { + return false; + } + switch(v) { + case '@base': + case '@context': + case '@container': + case '@default': + case '@embed': + case '@explicit': + case '@graph': + case '@id': + case '@index': + case '@language': + case '@list': + case '@omitDefault': + case '@preserve': + case '@reverse': + case '@set': + case '@type': + case '@value': + case '@vocab': + return true; + } + return false; +} + +/** + * Returns true if the given value is an Object. + * + * @param v the value to check. + * + * @return true if the value is an Object, false if not. + */ +function _isObject(v) { + return (Object.prototype.toString.call(v) === '[object Object]'); +} + +/** + * Returns true if the given value is an empty Object. + * + * @param v the value to check. + * + * @return true if the value is an empty Object, false if not. + */ +function _isEmptyObject(v) { + return _isObject(v) && Object.keys(v).length === 0; +} + +/** + * Returns true if the given value is an Array. + * + * @param v the value to check. + * + * @return true if the value is an Array, false if not. + */ +function _isArray(v) { + return Array.isArray(v); +} + +/** + * Throws an exception if the given value is not a valid @type value. + * + * @param v the value to check. + */ +function _validateTypeValue(v) { + // can be a string or an empty object + if(_isString(v) || _isEmptyObject(v)) { + return; + } + + // must be an array + var isValid = false; + if(_isArray(v)) { + // must contain only strings + isValid = true; + for(var i = 0; i < v.length; ++i) { + if(!(_isString(v[i]))) { + isValid = false; + break; + } + } + } + + if(!isValid) { + throw new JsonLdError( + 'Invalid JSON-LD syntax; "@type" value must a string, an array of ' + + 'strings, or an empty object.', 'jsonld.SyntaxError', + {code: 'invalid type value', value: v}); + } +} + +/** + * Returns true if the given value is a String. + * + * @param v the value to check. + * + * @return true if the value is a String, false if not. + */ +function _isString(v) { + return (typeof v === 'string' || + Object.prototype.toString.call(v) === '[object String]'); +} + +/** + * Returns true if the given value is a Number. + * + * @param v the value to check. + * + * @return true if the value is a Number, false if not. + */ +function _isNumber(v) { + return (typeof v === 'number' || + Object.prototype.toString.call(v) === '[object Number]'); +} + +/** + * Returns true if the given value is a double. + * + * @param v the value to check. + * + * @return true if the value is a double, false if not. + */ +function _isDouble(v) { + return _isNumber(v) && String(v).indexOf('.') !== -1; +} + +/** + * Returns true if the given value is numeric. + * + * @param v the value to check. + * + * @return true if the value is numeric, false if not. + */ +function _isNumeric(v) { + return !isNaN(parseFloat(v)) && isFinite(v); +} + +/** + * Returns true if the given value is a Boolean. + * + * @param v the value to check. + * + * @return true if the value is a Boolean, false if not. + */ +function _isBoolean(v) { + return (typeof v === 'boolean' || + Object.prototype.toString.call(v) === '[object Boolean]'); +} + +/** + * Returns true if the given value is undefined. + * + * @param v the value to check. + * + * @return true if the value is undefined, false if not. + */ +function _isUndefined(v) { + return (typeof v === 'undefined'); +} + +/** + * Returns true if the given value is a subject with properties. + * + * @param v the value to check. + * + * @return true if the value is a subject with properties, false if not. + */ +function _isSubject(v) { + // Note: A value is a subject if all of these hold true: + // 1. It is an Object. + // 2. It is not a @value, @set, or @list. + // 3. It has more than 1 key OR any existing key is not @id. + var rval = false; + if(_isObject(v) && + !(('@value' in v) || ('@set' in v) || ('@list' in v))) { + var keyCount = Object.keys(v).length; + rval = (keyCount > 1 || !('@id' in v)); + } + return rval; +} + +/** + * Returns true if the given value is a subject reference. + * + * @param v the value to check. + * + * @return true if the value is a subject reference, false if not. + */ +function _isSubjectReference(v) { + // Note: A value is a subject reference if all of these hold true: + // 1. It is an Object. + // 2. It has a single key: @id. + return (_isObject(v) && Object.keys(v).length === 1 && ('@id' in v)); +} + +/** + * Returns true if the given value is a @value. + * + * @param v the value to check. + * + * @return true if the value is a @value, false if not. + */ +function _isValue(v) { + // Note: A value is a @value if all of these hold true: + // 1. It is an Object. + // 2. It has the @value property. + return _isObject(v) && ('@value' in v); +} + +/** + * Returns true if the given value is a @list. + * + * @param v the value to check. + * + * @return true if the value is a @list, false if not. + */ +function _isList(v) { + // Note: A value is a @list if all of these hold true: + // 1. It is an Object. + // 2. It has the @list property. + return _isObject(v) && ('@list' in v); +} + +/** + * Returns true if the given value is a blank node. + * + * @param v the value to check. + * + * @return true if the value is a blank node, false if not. + */ +function _isBlankNode(v) { + // Note: A value is a blank node if all of these hold true: + // 1. It is an Object. + // 2. If it has an @id key its value begins with '_:'. + // 3. It has no keys OR is not a @value, @set, or @list. + var rval = false; + if(_isObject(v)) { + if('@id' in v) { + rval = (v['@id'].indexOf('_:') === 0); + } else { + rval = (Object.keys(v).length === 0 || + !(('@value' in v) || ('@set' in v) || ('@list' in v))); + } + } + return rval; +} + +/** + * Returns true if the given value is an absolute IRI, false if not. + * + * @param v the value to check. + * + * @return true if the value is an absolute IRI, false if not. + */ +function _isAbsoluteIri(v) { + return _isString(v) && v.indexOf(':') !== -1; +} + +/** + * Clones an object, array, or string/number. + * + * @param value the value to clone. + * + * @return the cloned value. + */ +function _clone(value) { + if(value && typeof value === 'object') { + var rval; + if(_isArray(value)) { + rval = []; + for(var i = 0; i < value.length; ++i) { + rval[i] = _clone(value[i]); + } + } else { + rval = {}; + for(var key in value) { + rval[key] = _clone(value[key]); + } + } + return rval; + } + return value; +} + +/** + * Finds all @context URLs in the given JSON-LD input. + * + * @param input the JSON-LD input. + * @param urls a map of URLs (url => false/@contexts). + * @param replace true to replace the URLs in the given input with the + * @contexts from the urls map, false not to. + * @param base the base IRI to use to resolve relative IRIs. + * + * @return true if new URLs to retrieve were found, false if not. + */ +function _findContextUrls(input, urls, replace, base) { + var count = Object.keys(urls).length; + if(_isArray(input)) { + for(var i = 0; i < input.length; ++i) { + _findContextUrls(input[i], urls, replace, base); + } + return (count < Object.keys(urls).length); + } else if(_isObject(input)) { + for(var key in input) { + if(key !== '@context') { + _findContextUrls(input[key], urls, replace, base); + continue; + } + + // get @context + var ctx = input[key]; + + // array @context + if(_isArray(ctx)) { + var length = ctx.length; + for(var i = 0; i < length; ++i) { + var _ctx = ctx[i]; + if(_isString(_ctx)) { + _ctx = _prependBase(base, _ctx); + // replace w/@context if requested + if(replace) { + _ctx = urls[_ctx]; + if(_isArray(_ctx)) { + // add flattened context + Array.prototype.splice.apply(ctx, [i, 1].concat(_ctx)); + i += _ctx.length; + length += _ctx.length; + } else { + ctx[i] = _ctx; + } + } else if(!(_ctx in urls)) { + // @context URL found + urls[_ctx] = false; + } + } + } + } else if(_isString(ctx)) { + // string @context + ctx = _prependBase(base, ctx); + // replace w/@context if requested + if(replace) { + input[key] = urls[ctx]; + } else if(!(ctx in urls)) { + // @context URL found + urls[ctx] = false; + } + } + } + return (count < Object.keys(urls).length); + } + return false; +} + +/** + * Retrieves external @context URLs using the given document loader. Every + * instance of @context in the input that refers to a URL will be replaced + * with the JSON @context found at that URL. + * + * @param input the JSON-LD input with possible contexts. + * @param options the options to use: + * documentLoader(url, callback(err, remoteDoc)) the document loader. + * @param callback(err, input) called once the operation completes. + */ +function _retrieveContextUrls(input, options, callback) { + // if any error occurs during URL resolution, quit + var error = null; + var regex = /(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/; + + // recursive document loader + var documentLoader = options.documentLoader; + var retrieve = function(input, cycles, documentLoader, base, callback) { + if(Object.keys(cycles).length > MAX_CONTEXT_URLS) { + error = new JsonLdError( + 'Maximum number of @context URLs exceeded.', + 'jsonld.ContextUrlError', + {code: 'loading remote context failed', max: MAX_CONTEXT_URLS}); + return callback(error); + } + + // for tracking the URLs to retrieve + var urls = {}; + + // finished will be called once the URL queue is empty + var finished = function() { + // replace all URLs in the input + _findContextUrls(input, urls, true, base); + callback(null, input); + }; + + // find all URLs in the given input + if(!_findContextUrls(input, urls, false, base)) { + // no new URLs in input + finished(); + } + + // queue all unretrieved URLs + var queue = []; + for(var url in urls) { + if(urls[url] === false) { + // validate URL + if(!regex.test(url)) { + error = new JsonLdError( + 'Malformed URL.', 'jsonld.InvalidUrl', + {code: 'loading remote context failed', url: url}); + return callback(error); + } + queue.push(url); + } + } + + // retrieve URLs in queue + var count = queue.length; + for(var i = 0; i < queue.length; ++i) { + (function(url) { + // check for context URL cycle + if(url in cycles) { + error = new JsonLdError( + 'Cyclical @context URLs detected.', + 'jsonld.ContextUrlError', + {code: 'recursive context inclusion', url: url}); + return callback(error); + } + var _cycles = _clone(cycles); + _cycles[url] = true; + var done = function(err, remoteDoc) { + // short-circuit if there was an error with another URL + if(error) { + return; + } + + var ctx = remoteDoc ? remoteDoc.document : null; + + // parse string context as JSON + if(!err && _isString(ctx)) { + try { + ctx = JSON.parse(ctx); + } catch(ex) { + err = ex; + } + } + + // ensure ctx is an object + if(err) { + err = new JsonLdError( + 'Dereferencing a URL did not result in a valid JSON-LD object. ' + + 'Possible causes are an inaccessible URL perhaps due to ' + + 'a same-origin policy (ensure the server uses CORS if you are ' + + 'using client-side JavaScript), too many redirects, a ' + + 'non-JSON response, or more than one HTTP Link Header was ' + + 'provided for a remote context.', + 'jsonld.InvalidUrl', + {code: 'loading remote context failed', url: url, cause: err}); + } else if(!_isObject(ctx)) { + err = new JsonLdError( + 'Dereferencing a URL did not result in a JSON object. The ' + + 'response was valid JSON, but it was not a JSON object.', + 'jsonld.InvalidUrl', + {code: 'invalid remote context', url: url, cause: err}); + } + if(err) { + error = err; + return callback(error); + } + + // use empty context if no @context key is present + if(!('@context' in ctx)) { + ctx = {'@context': {}}; + } else { + ctx = {'@context': ctx['@context']}; + } + + // append context URL to context if given + if(remoteDoc.contextUrl) { + if(!_isArray(ctx['@context'])) { + ctx['@context'] = [ctx['@context']]; + } + ctx['@context'].push(remoteDoc.contextUrl); + } + + // recurse + retrieve(ctx, _cycles, documentLoader, url, function(err, ctx) { + if(err) { + return callback(err); + } + urls[url] = ctx['@context']; + count -= 1; + if(count === 0) { + finished(); + } + }); + }; + var promise = documentLoader(url, done); + if(promise && 'then' in promise) { + promise.then(done.bind(null, null), done); + } + }(queue[i])); + } + }; + retrieve(input, {}, documentLoader, options.base, callback); +} + +// define js 1.8.5 Object.keys method if not present +if(!Object.keys) { + Object.keys = function(o) { + if(o !== Object(o)) { + throw new TypeError('Object.keys called on non-object'); + } + var rval = []; + for(var p in o) { + if(Object.prototype.hasOwnProperty.call(o, p)) { + rval.push(p); + } + } + return rval; + }; +} + +/** + * Parses RDF in the form of N-Quads. + * + * @param input the N-Quads input to parse. + * + * @return an RDF dataset. + */ +function _parseNQuads(input) { + // define partial regexes + var iri = '(?:<([^:]+:[^>]*)>)'; + var bnode = '(_:(?:[A-Za-z0-9]+))'; + var plain = '"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"'; + var datatype = '(?:\\^\\^' + iri + ')'; + var language = '(?:@([a-z]+(?:-[a-z0-9]+)*))'; + var literal = '(?:' + plain + '(?:' + datatype + '|' + language + ')?)'; + var ws = '[ \\t]+'; + var wso = '[ \\t]*'; + var eoln = /(?:\r\n)|(?:\n)|(?:\r)/g; + var empty = new RegExp('^' + wso + '$'); + + // define quad part regexes + var subject = '(?:' + iri + '|' + bnode + ')' + ws; + var property = iri + ws; + var object = '(?:' + iri + '|' + bnode + '|' + literal + ')' + wso; + var graphName = '(?:\\.|(?:(?:' + iri + '|' + bnode + ')' + wso + '\\.))'; + + // full quad regex + var quad = new RegExp( + '^' + wso + subject + property + object + graphName + wso + '$'); + + // build RDF dataset + var dataset = {}; + + // split N-Quad input into lines + var lines = input.split(eoln); + var lineNumber = 0; + for(var li = 0; li < lines.length; ++li) { + var line = lines[li]; + lineNumber++; + + // skip empty lines + if(empty.test(line)) { + continue; + } + + // parse quad + var match = line.match(quad); + if(match === null) { + throw new JsonLdError( + 'Error while parsing N-Quads; invalid quad.', + 'jsonld.ParseError', {line: lineNumber}); + } + + // create RDF triple + var triple = {}; + + // get subject + if(!_isUndefined(match[1])) { + triple.subject = {type: 'IRI', value: match[1]}; + } else { + triple.subject = {type: 'blank node', value: match[2]}; + } + + // get predicate + triple.predicate = {type: 'IRI', value: match[3]}; + + // get object + if(!_isUndefined(match[4])) { + triple.object = {type: 'IRI', value: match[4]}; + } else if(!_isUndefined(match[5])) { + triple.object = {type: 'blank node', value: match[5]}; + } else { + triple.object = {type: 'literal'}; + if(!_isUndefined(match[7])) { + triple.object.datatype = match[7]; + } else if(!_isUndefined(match[8])) { + triple.object.datatype = RDF_LANGSTRING; + triple.object.language = match[8]; + } else { + triple.object.datatype = XSD_STRING; + } + var unescaped = match[6] + .replace(/\\"/g, '"') + .replace(/\\t/g, '\t') + .replace(/\\n/g, '\n') + .replace(/\\r/g, '\r') + .replace(/\\\\/g, '\\'); + triple.object.value = unescaped; + } + + // get graph name ('@default' is used for the default graph) + var name = '@default'; + if(!_isUndefined(match[9])) { + name = match[9]; + } else if(!_isUndefined(match[10])) { + name = match[10]; + } + + // initialize graph in dataset + if(!(name in dataset)) { + dataset[name] = [triple]; + } else { + // add triple if unique to its graph + var unique = true; + var triples = dataset[name]; + for(var ti = 0; unique && ti < triples.length; ++ti) { + if(_compareRDFTriples(triples[ti], triple)) { + unique = false; + } + } + if(unique) { + triples.push(triple); + } + } + } + + return dataset; +} + +// register the N-Quads RDF parser +jsonld.registerRDFParser('application/nquads', _parseNQuads); + +/** + * Converts an RDF dataset to N-Quads. + * + * @param dataset the RDF dataset to convert. + * + * @return the N-Quads string. + */ +function _toNQuads(dataset) { + var quads = []; + for(var graphName in dataset) { + var triples = dataset[graphName]; + for(var ti = 0; ti < triples.length; ++ti) { + var triple = triples[ti]; + if(graphName === '@default') { + graphName = null; + } + quads.push(_toNQuad(triple, graphName)); + } + } + quads.sort(); + return quads.join(''); +} + +/** + * Converts an RDF triple and graph name to an N-Quad string (a single quad). + * + * @param triple the RDF triple to convert. + * @param graphName the name of the graph containing the triple, null for + * the default graph. + * @param bnode the bnode the quad is mapped to (optional, for use + * during normalization only). + * + * @return the N-Quad string. + */ +function _toNQuad(triple, graphName, bnode) { + var s = triple.subject; + var p = triple.predicate; + var o = triple.object; + var g = graphName; + + var quad = ''; + + // subject is an IRI + if(s.type === 'IRI') { + quad += '<' + s.value + '>'; + } else if(bnode) { + // bnode normalization mode + quad += (s.value === bnode) ? '_:a' : '_:z'; + } else { + // bnode normal mode + quad += s.value; + } + quad += ' '; + + // predicate is an IRI + if(p.type === 'IRI') { + quad += '<' + p.value + '>'; + } else if(bnode) { + // FIXME: TBD what to do with bnode predicates during normalization + // bnode normalization mode + quad += '_:p'; + } else { + // bnode normal mode + quad += p.value; + } + quad += ' '; + + // object is IRI, bnode, or literal + if(o.type === 'IRI') { + quad += '<' + o.value + '>'; + } else if(o.type === 'blank node') { + // normalization mode + if(bnode) { + quad += (o.value === bnode) ? '_:a' : '_:z'; + } else { + // normal mode + quad += o.value; + } + } else { + var escaped = o.value + .replace(/\\/g, '\\\\') + .replace(/\t/g, '\\t') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\"/g, '\\"'); + quad += '"' + escaped + '"'; + if(o.datatype === RDF_LANGSTRING) { + if(o.language) { + quad += '@' + o.language; + } + } else if(o.datatype !== XSD_STRING) { + quad += '^^<' + o.datatype + '>'; + } + } + + // graph + if(g !== null) { + if(g.indexOf('_:') !== 0) { + quad += ' <' + g + '>'; + } else if(bnode) { + quad += ' _:g'; + } else { + quad += ' ' + g; + } + } + + quad += ' .\n'; + return quad; +} + +/** + * Parses the RDF dataset found via the data object from the RDFa API. + * + * @param data the RDFa API data object. + * + * @return the RDF dataset. + */ +function _parseRdfaApiData(data) { + var dataset = {}; + dataset['@default'] = []; + + var subjects = data.getSubjects(); + for(var si = 0; si < subjects.length; ++si) { + var subject = subjects[si]; + if(subject === null) { + continue; + } + + // get all related triples + var triples = data.getSubjectTriples(subject); + if(triples === null) { + continue; + } + var predicates = triples.predicates; + for(var predicate in predicates) { + // iterate over objects + var objects = predicates[predicate].objects; + for(var oi = 0; oi < objects.length; ++oi) { + var object = objects[oi]; + + // create RDF triple + var triple = {}; + + // add subject + if(subject.indexOf('_:') === 0) { + triple.subject = {type: 'blank node', value: subject}; + } else { + triple.subject = {type: 'IRI', value: subject}; + } + + // add predicate + if(predicate.indexOf('_:') === 0) { + triple.predicate = {type: 'blank node', value: predicate}; + } else { + triple.predicate = {type: 'IRI', value: predicate}; + } + + // serialize XML literal + var value = object.value; + if(object.type === RDF_XML_LITERAL) { + // initialize XMLSerializer + if(!XMLSerializer) { + _defineXMLSerializer(); + } + var serializer = new XMLSerializer(); + value = ''; + for(var x = 0; x < object.value.length; x++) { + if(object.value[x].nodeType === Node.ELEMENT_NODE) { + value += serializer.serializeToString(object.value[x]); + } else if(object.value[x].nodeType === Node.TEXT_NODE) { + value += object.value[x].nodeValue; + } + } + } + + // add object + triple.object = {}; + + // object is an IRI + if(object.type === RDF_OBJECT) { + if(object.value.indexOf('_:') === 0) { + triple.object.type = 'blank node'; + } else { + triple.object.type = 'IRI'; + } + } else { + // object is a literal + triple.object.type = 'literal'; + if(object.type === RDF_PLAIN_LITERAL) { + if(object.language) { + triple.object.datatype = RDF_LANGSTRING; + triple.object.language = object.language; + } else { + triple.object.datatype = XSD_STRING; + } + } else { + triple.object.datatype = object.type; + } + } + triple.object.value = value; + + // add triple to dataset in default graph + dataset['@default'].push(triple); + } + } + } + + return dataset; +} + +// register the RDFa API RDF parser +jsonld.registerRDFParser('rdfa-api', _parseRdfaApiData); + +/** + * Creates a new UniqueNamer. A UniqueNamer issues unique names, keeping + * track of any previously issued names. + * + * @param prefix the prefix to use (''). + */ +function UniqueNamer(prefix) { + this.prefix = prefix; + this.counter = 0; + this.existing = {}; +} + +/** + * Copies this UniqueNamer. + * + * @return a copy of this UniqueNamer. + */ +UniqueNamer.prototype.clone = function() { + var copy = new UniqueNamer(this.prefix); + copy.counter = this.counter; + copy.existing = _clone(this.existing); + return copy; +}; + +/** + * Gets the new name for the given old name, where if no old name is given + * a new name will be generated. + * + * @param [oldName] the old name to get the new name for. + * + * @return the new name. + */ +UniqueNamer.prototype.getName = function(oldName) { + // return existing old name + if(oldName && oldName in this.existing) { + return this.existing[oldName]; + } + + // get next name + var name = this.prefix + this.counter; + this.counter += 1; + + // save mapping + if(oldName) { + this.existing[oldName] = name; + } + + return name; +}; + +/** + * Returns true if the given oldName has already been assigned a new name. + * + * @param oldName the oldName to check. + * + * @return true if the oldName has been assigned a new name, false if not. + */ +UniqueNamer.prototype.isNamed = function(oldName) { + return (oldName in this.existing); +}; + +/** + * A Permutator iterates over all possible permutations of the given array + * of elements. + * + * @param list the array of elements to iterate over. + */ +var Permutator = function(list) { + // original array + this.list = list.sort(); + // indicates whether there are more permutations + this.done = false; + // directional info for permutation algorithm + this.left = {}; + for(var i = 0; i < list.length; ++i) { + this.left[list[i]] = true; + } +}; + +/** + * Returns true if there is another permutation. + * + * @return true if there is another permutation, false if not. + */ +Permutator.prototype.hasNext = function() { + return !this.done; +}; + +/** + * Gets the next permutation. Call hasNext() to ensure there is another one + * first. + * + * @return the next permutation. + */ +Permutator.prototype.next = function() { + // copy current permutation + var rval = this.list.slice(); + + /* Calculate the next permutation using the Steinhaus-Johnson-Trotter + permutation algorithm. */ + + // get largest mobile element k + // (mobile: element is greater than the one it is looking at) + var k = null; + var pos = 0; + var length = this.list.length; + for(var i = 0; i < length; ++i) { + var element = this.list[i]; + var left = this.left[element]; + if((k === null || element > k) && + ((left && i > 0 && element > this.list[i - 1]) || + (!left && i < (length - 1) && element > this.list[i + 1]))) { + k = element; + pos = i; + } + } + + // no more permutations + if(k === null) { + this.done = true; + } else { + // swap k and the element it is looking at + var swap = this.left[k] ? pos - 1 : pos + 1; + this.list[pos] = this.list[swap]; + this.list[swap] = k; + + // reverse the direction of all elements larger than k + for(var i = 0; i < length; ++i) { + if(this.list[i] > k) { + this.left[this.list[i]] = !this.left[this.list[i]]; + } + } + } + + return rval; +}; + +// SHA-1 API +var sha1 = jsonld.sha1 = {}; + +if(_nodejs) { + var crypto = require('crypto'); + sha1.create = function() { + var md = crypto.createHash('sha1'); + return { + update: function(data) { + md.update(data, 'utf8'); + }, + digest: function() { + return md.digest('hex'); + } + }; + }; +} else { + sha1.create = function() { + return new sha1.MessageDigest(); + }; +} + +/** + * Hashes the given array of quads and returns its hexadecimal SHA-1 message + * digest. + * + * @param nquads the list of serialized quads to hash. + * + * @return the hexadecimal SHA-1 message digest. + */ +sha1.hash = function(nquads) { + var md = sha1.create(); + for(var i = 0; i < nquads.length; ++i) { + md.update(nquads[i]); + } + return md.digest(); +}; + +// only define sha1 MessageDigest for non-nodejs +if(!_nodejs) { + +/** + * Creates a simple byte buffer for message digest operations. + */ +sha1.Buffer = function() { + this.data = ''; + this.read = 0; +}; + +/** + * Puts a 32-bit integer into this buffer in big-endian order. + * + * @param i the 32-bit integer. + */ +sha1.Buffer.prototype.putInt32 = function(i) { + this.data += ( + String.fromCharCode(i >> 24 & 0xFF) + + String.fromCharCode(i >> 16 & 0xFF) + + String.fromCharCode(i >> 8 & 0xFF) + + String.fromCharCode(i & 0xFF)); +}; + +/** + * Gets a 32-bit integer from this buffer in big-endian order and + * advances the read pointer by 4. + * + * @return the word. + */ +sha1.Buffer.prototype.getInt32 = function() { + var rval = ( + this.data.charCodeAt(this.read) << 24 ^ + this.data.charCodeAt(this.read + 1) << 16 ^ + this.data.charCodeAt(this.read + 2) << 8 ^ + this.data.charCodeAt(this.read + 3)); + this.read += 4; + return rval; +}; + +/** + * Gets the bytes in this buffer. + * + * @return a string full of UTF-8 encoded characters. + */ +sha1.Buffer.prototype.bytes = function() { + return this.data.slice(this.read); +}; + +/** + * Gets the number of bytes in this buffer. + * + * @return the number of bytes in this buffer. + */ +sha1.Buffer.prototype.length = function() { + return this.data.length - this.read; +}; + +/** + * Compacts this buffer. + */ +sha1.Buffer.prototype.compact = function() { + this.data = this.data.slice(this.read); + this.read = 0; +}; + +/** + * Converts this buffer to a hexadecimal string. + * + * @return a hexadecimal string. + */ +sha1.Buffer.prototype.toHex = function() { + var rval = ''; + for(var i = this.read; i < this.data.length; ++i) { + var b = this.data.charCodeAt(i); + if(b < 16) { + rval += '0'; + } + rval += b.toString(16); + } + return rval; +}; + +/** + * Creates a SHA-1 message digest object. + * + * @return a message digest object. + */ +sha1.MessageDigest = function() { + // do initialization as necessary + if(!_sha1.initialized) { + _sha1.init(); + } + + this.blockLength = 64; + this.digestLength = 20; + // length of message so far (does not including padding) + this.messageLength = 0; + + // input buffer + this.input = new sha1.Buffer(); + + // for storing words in the SHA-1 algorithm + this.words = new Array(80); + + // SHA-1 state contains five 32-bit integers + this.state = { + h0: 0x67452301, + h1: 0xEFCDAB89, + h2: 0x98BADCFE, + h3: 0x10325476, + h4: 0xC3D2E1F0 + }; +}; + +/** + * Updates the digest with the given string input. + * + * @param msg the message input to update with. + */ +sha1.MessageDigest.prototype.update = function(msg) { + // UTF-8 encode message + msg = unescape(encodeURIComponent(msg)); + + // update message length and input buffer + this.messageLength += msg.length; + this.input.data += msg; + + // process input + _sha1.update(this.state, this.words, this.input); + + // compact input buffer every 2K or if empty + if(this.input.read > 2048 || this.input.length() === 0) { + this.input.compact(); + } +}; + +/** + * Produces the digest. + * + * @return the digest as a hexadecimal string. + */ +sha1.MessageDigest.prototype.digest = function() { + /* Determine the number of bytes that must be added to the message + to ensure its length is congruent to 448 mod 512. In other words, + a 64-bit integer that gives the length of the message will be + appended to the message and whatever the length of the message is + plus 64 bits must be a multiple of 512. So the length of the + message must be congruent to 448 mod 512 because 512 - 64 = 448. + + In order to fill up the message length it must be filled with + padding that begins with 1 bit followed by all 0 bits. Padding + must *always* be present, so if the message length is already + congruent to 448 mod 512, then 512 padding bits must be added. */ + + // 512 bits == 64 bytes, 448 bits == 56 bytes, 64 bits = 8 bytes + // _padding starts with 1 byte with first bit is set in it which + // is byte value 128, then there may be up to 63 other pad bytes + var len = this.messageLength; + var padBytes = new sha1.Buffer(); + padBytes.data += this.input.bytes(); + padBytes.data += _sha1.padding.substr(0, 64 - ((len + 8) % 64)); + + /* Now append length of the message. The length is appended in bits + as a 64-bit number in big-endian order. Since we store the length + in bytes, we must multiply it by 8 (or left shift by 3). So here + store the high 3 bits in the low end of the first 32-bits of the + 64-bit number and the lower 5 bits in the high end of the second + 32-bits. */ + padBytes.putInt32((len >>> 29) & 0xFF); + padBytes.putInt32((len << 3) & 0xFFFFFFFF); + _sha1.update(this.state, this.words, padBytes); + var rval = new sha1.Buffer(); + rval.putInt32(this.state.h0); + rval.putInt32(this.state.h1); + rval.putInt32(this.state.h2); + rval.putInt32(this.state.h3); + rval.putInt32(this.state.h4); + return rval.toHex(); +}; + +// private SHA-1 data +var _sha1 = { + padding: null, + initialized: false +}; + +/** + * Initializes the constant tables. + */ +_sha1.init = function() { + // create padding + _sha1.padding = String.fromCharCode(128); + var c = String.fromCharCode(0x00); + var n = 64; + while(n > 0) { + if(n & 1) { + _sha1.padding += c; + } + n >>>= 1; + if(n > 0) { + c += c; + } + } + + // now initialized + _sha1.initialized = true; +}; + +/** + * Updates a SHA-1 state with the given byte buffer. + * + * @param s the SHA-1 state to update. + * @param w the array to use to store words. + * @param input the input byte buffer. + */ +_sha1.update = function(s, w, input) { + // consume 512 bit (64 byte) chunks + var t, a, b, c, d, e, f, i; + var len = input.length(); + while(len >= 64) { + // the w array will be populated with sixteen 32-bit big-endian words + // and then extended into 80 32-bit words according to SHA-1 algorithm + // and for 32-79 using Max Locktyukhin's optimization + + // initialize hash value for this chunk + a = s.h0; + b = s.h1; + c = s.h2; + d = s.h3; + e = s.h4; + + // round 1 + for(i = 0; i < 16; ++i) { + t = input.getInt32(); + w[i] = t; + f = d ^ (b & (c ^ d)); + t = ((a << 5) | (a >>> 27)) + f + e + 0x5A827999 + t; + e = d; + d = c; + c = (b << 30) | (b >>> 2); + b = a; + a = t; + } + for(; i < 20; ++i) { + t = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]); + t = (t << 1) | (t >>> 31); + w[i] = t; + f = d ^ (b & (c ^ d)); + t = ((a << 5) | (a >>> 27)) + f + e + 0x5A827999 + t; + e = d; + d = c; + c = (b << 30) | (b >>> 2); + b = a; + a = t; + } + // round 2 + for(; i < 32; ++i) { + t = (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]); + t = (t << 1) | (t >>> 31); + w[i] = t; + f = b ^ c ^ d; + t = ((a << 5) | (a >>> 27)) + f + e + 0x6ED9EBA1 + t; + e = d; + d = c; + c = (b << 30) | (b >>> 2); + b = a; + a = t; + } + for(; i < 40; ++i) { + t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]); + t = (t << 2) | (t >>> 30); + w[i] = t; + f = b ^ c ^ d; + t = ((a << 5) | (a >>> 27)) + f + e + 0x6ED9EBA1 + t; + e = d; + d = c; + c = (b << 30) | (b >>> 2); + b = a; + a = t; + } + // round 3 + for(; i < 60; ++i) { + t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]); + t = (t << 2) | (t >>> 30); + w[i] = t; + f = (b & c) | (d & (b ^ c)); + t = ((a << 5) | (a >>> 27)) + f + e + 0x8F1BBCDC + t; + e = d; + d = c; + c = (b << 30) | (b >>> 2); + b = a; + a = t; + } + // round 4 + for(; i < 80; ++i) { + t = (w[i - 6] ^ w[i - 16] ^ w[i - 28] ^ w[i - 32]); + t = (t << 2) | (t >>> 30); + w[i] = t; + f = b ^ c ^ d; + t = ((a << 5) | (a >>> 27)) + f + e + 0xCA62C1D6 + t; + e = d; + d = c; + c = (b << 30) | (b >>> 2); + b = a; + a = t; + } + + // update hash state + s.h0 += a; + s.h1 += b; + s.h2 += c; + s.h3 += d; + s.h4 += e; + + len -= 64; + } +}; + +} // end non-nodejs + +if(!XMLSerializer) { + +var _defineXMLSerializer = function() { + XMLSerializer = require('xmldom').XMLSerializer; +}; + +} // end _defineXMLSerializer + +// define URL parser +jsonld.url = {}; +if(_nodejs) { + var parse = require('url').parse; + jsonld.url.parse = function(url) { + var parsed = parse(url); + parsed.pathname = parsed.pathname || ''; + _parseAuthority(parsed); + parsed.normalizedPath = _removeDotSegments( + parsed.pathname, parsed.authority !== ''); + return parsed; + }; +} else { + // parseUri 1.2.2 + // (c) Steven Levithan + // MIT License + var parseUri = {}; + parseUri.options = { + key: ['href','protocol','host','auth','user','password','hostname','port','relative','path','directory','file','query','hash'], + parser: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ + }; + jsonld.url.parse = function(str) { + var o = parseUri.options; + var m = o.parser.exec(str); + var uri = {}; + var i = 14; + while(i--) { + uri[o.key[i]] = m[i] || ''; + } + // normalize to node.js API + if(uri.host && uri.path === '') { + uri.path = '/'; + } + uri.pathname = uri.path || ''; + _parseAuthority(uri); + uri.normalizedPath = _removeDotSegments(uri.pathname, uri.authority !== ''); + if(uri.query) { + uri.path = uri.path + '?' + uri.query; + } + if(uri.protocol) { + uri.protocol += ':'; + } + if(uri.hash) { + uri.hash = '#' + uri.hash; + } + return uri; + }; +} + +/** + * Parses the authority for the pre-parsed given URL. + * + * @param parsed the pre-parsed URL. + */ +function _parseAuthority(parsed) { + // parse authority for unparsed relative network-path reference + if(parsed.href.indexOf(':') === -1 && parsed.href.indexOf('//') === 0 && + !parsed.host) { + // must parse authority from pathname + parsed.pathname = parsed.pathname.substr(2); + var idx = parsed.pathname.indexOf('/'); + if(idx === -1) { + parsed.authority = parsed.pathname; + parsed.pathname = ''; + } else { + parsed.authority = parsed.pathname.substr(0, idx); + parsed.pathname = parsed.pathname.substr(idx); + } + } else { + // construct authority + parsed.authority = parsed.host || ''; + if(parsed.auth) { + parsed.authority = parsed.auth + '@' + parsed.authority; + } + } +} + +/** + * Removes dot segments from a URL path. + * + * @param path the path to remove dot segments from. + * @param hasAuthority true if the URL has an authority, false if not. + */ +function _removeDotSegments(path, hasAuthority) { + var rval = ''; + + if(path.indexOf('/') === 0) { + rval = '/'; + } + + // RFC 3986 5.2.4 (reworked) + var input = path.split('/'); + var output = []; + while(input.length > 0) { + if(input[0] === '.' || (input[0] === '' && input.length > 1)) { + input.shift(); + continue; + } + if(input[0] === '..') { + input.shift(); + if(hasAuthority || + (output.length > 0 && output[output.length - 1] !== '..')) { + output.pop(); + } else { + // leading relative URL '..' + output.push('..'); + } + continue; + } + output.push(input.shift()); + } + + return rval + output.join('/'); +} + +if(_nodejs) { + // use node document loader by default + jsonld.useDocumentLoader('node'); +} else if(typeof XMLHttpRequest !== 'undefined') { + // use xhr document loader by default + jsonld.useDocumentLoader('xhr'); +} + +if(_nodejs) { + jsonld.use = function(extension) { + switch(extension) { + case 'request': + // use node JSON-LD request extension + jsonld.request = require('./request'); + break; + default: + throw new JsonLdError( + 'Unknown extension.', + 'jsonld.UnknownExtension', {extension: extension}); + } + }; +} + +// end of jsonld API factory +return jsonld; +}; + +// external APIs: + +// used to generate a new jsonld API instance +var factory = function() { + return wrapper(function() { + return factory(); + }); +}; +// the shared global jsonld API instance +wrapper(factory); + +if(_nodejs) { + // export nodejs API + module.exports = factory; +} else if(typeof define === 'function' && define.amd) { + // export AMD API + define([], function() { + return factory; + }); +} else if(_browser) { + // export simple browser API + if(typeof jsonld === 'undefined') { + jsonld = jsonldjs = factory; + } else { + jsonldjs = factory; + } +} + +})(); diff --git a/ajax/libs/jsonld/package.json b/ajax/libs/jsonld/package.json index 7959d9fe9..303197072 100644 --- a/ajax/libs/jsonld/package.json +++ b/ajax/libs/jsonld/package.json @@ -1,6 +1,6 @@ { "name": "jsonld", - "version": "0.2.14", + "version": "0.2.15", "filename": "jsonld.js", "description": "A JSON-LD Processor and API implementation in JavaScript.", "homepage": "http://github.com/digitalbazaar/jsonld.js", diff --git a/ajax/libs/p2.js/0.6.0/p2.js b/ajax/libs/p2.js/0.6.0/p2.js new file mode 100644 index 000000000..610db2372 --- /dev/null +++ b/ajax/libs/p2.js/0.6.0/p2.js @@ -0,0 +1,13206 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013 p2.js authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.p2=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o + * License: MIT + * + * `npm install buffer` + */ + +var base64 = _dereq_('base64-js') +var ieee754 = _dereq_('ieee754') + +exports.Buffer = Buffer +exports.SlowBuffer = Buffer +exports.INSPECT_MAX_BYTES = 50 +Buffer.poolSize = 8192 + +/** + * If `Buffer._useTypedArrays`: + * === true Use Uint8Array implementation (fastest) + * === false Use Object implementation (compatible down to IE6) + */ +Buffer._useTypedArrays = (function () { + // Detect if browser supports Typed Arrays. Supported browsers are IE 10+, + // Firefox 4+, Chrome 7+, Safari 5.1+, Opera 11.6+, iOS 4.2+. + if (typeof Uint8Array !== 'function' || typeof ArrayBuffer !== 'function') + return false + + // Does the browser support adding properties to `Uint8Array` instances? If + // not, then that's the same as no `Uint8Array` support. We need to be able to + // add all the node Buffer API methods. + // Bug in Firefox 4-29, now fixed: https://bugzilla.mozilla.org/show_bug.cgi?id=695438 + try { + var arr = new Uint8Array(0) + arr.foo = function () { return 42 } + return 42 === arr.foo() && + typeof arr.subarray === 'function' // Chrome 9-10 lack `subarray` + } catch (e) { + return false + } +})() + +/** + * Class: Buffer + * ============= + * + * The Buffer constructor returns instances of `Uint8Array` that are augmented + * with function properties for all the node `Buffer` API functions. We use + * `Uint8Array` so that square bracket notation works as expected -- it returns + * a single octet. + * + * By augmenting the instances, we can avoid modifying the `Uint8Array` + * prototype. + */ +function Buffer (subject, encoding, noZero) { + if (!(this instanceof Buffer)) + return new Buffer(subject, encoding, noZero) + + var type = typeof subject + + // Workaround: node's base64 implementation allows for non-padded strings + // while base64-js does not. + if (encoding === 'base64' && type === 'string') { + subject = stringtrim(subject) + while (subject.length % 4 !== 0) { + subject = subject + '=' + } + } + + // Find the length + var length + if (type === 'number') + length = coerce(subject) + else if (type === 'string') + length = Buffer.byteLength(subject, encoding) + else if (type === 'object') + length = coerce(subject.length) // Assume object is an array + else + throw new Error('First argument needs to be a number, array or string.') + + var buf + if (Buffer._useTypedArrays) { + // Preferred: Return an augmented `Uint8Array` instance for best performance + buf = augment(new Uint8Array(length)) + } else { + // Fallback: Return THIS instance of Buffer (created by `new`) + buf = this + buf.length = length + buf._isBuffer = true + } + + var i + if (Buffer._useTypedArrays && typeof Uint8Array === 'function' && + subject instanceof Uint8Array) { + // Speed optimization -- use set if we're copying from a Uint8Array + buf._set(subject) + } else if (isArrayish(subject)) { + // Treat array-ish objects as a byte array + for (i = 0; i < length; i++) { + if (Buffer.isBuffer(subject)) + buf[i] = subject.readUInt8(i) + else + buf[i] = subject[i] + } + } else if (type === 'string') { + buf.write(subject, 0, encoding) + } else if (type === 'number' && !Buffer._useTypedArrays && !noZero) { + for (i = 0; i < length; i++) { + buf[i] = 0 + } + } + + return buf +} + +// STATIC METHODS +// ============== + +Buffer.isEncoding = function (encoding) { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'binary': + case 'base64': + case 'raw': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true + default: + return false + } +} + +Buffer.isBuffer = function (b) { + return !!(b !== null && b !== undefined && b._isBuffer) +} + +Buffer.byteLength = function (str, encoding) { + var ret + str = str + '' + switch (encoding || 'utf8') { + case 'hex': + ret = str.length / 2 + break + case 'utf8': + case 'utf-8': + ret = utf8ToBytes(str).length + break + case 'ascii': + case 'binary': + case 'raw': + ret = str.length + break + case 'base64': + ret = base64ToBytes(str).length + break + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + ret = str.length * 2 + break + default: + throw new Error('Unknown encoding') + } + return ret +} + +Buffer.concat = function (list, totalLength) { + assert(isArray(list), 'Usage: Buffer.concat(list, [totalLength])\n' + + 'list should be an Array.') + + if (list.length === 0) { + return new Buffer(0) + } else if (list.length === 1) { + return list[0] + } + + var i + if (typeof totalLength !== 'number') { + totalLength = 0 + for (i = 0; i < list.length; i++) { + totalLength += list[i].length + } + } + + var buf = new Buffer(totalLength) + var pos = 0 + for (i = 0; i < list.length; i++) { + var item = list[i] + item.copy(buf, pos) + pos += item.length + } + return buf +} + +// BUFFER INSTANCE METHODS +// ======================= + +function _hexWrite (buf, string, offset, length) { + offset = Number(offset) || 0 + var remaining = buf.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + + // must be an even number of digits + var strLen = string.length + assert(strLen % 2 === 0, 'Invalid hex string') + + if (length > strLen / 2) { + length = strLen / 2 + } + for (var i = 0; i < length; i++) { + var byte = parseInt(string.substr(i * 2, 2), 16) + assert(!isNaN(byte), 'Invalid hex string') + buf[offset + i] = byte + } + Buffer._charsWritten = i * 2 + return i +} + +function _utf8Write (buf, string, offset, length) { + var charsWritten = Buffer._charsWritten = + blitBuffer(utf8ToBytes(string), buf, offset, length) + return charsWritten +} + +function _asciiWrite (buf, string, offset, length) { + var charsWritten = Buffer._charsWritten = + blitBuffer(asciiToBytes(string), buf, offset, length) + return charsWritten +} + +function _binaryWrite (buf, string, offset, length) { + return _asciiWrite(buf, string, offset, length) +} + +function _base64Write (buf, string, offset, length) { + var charsWritten = Buffer._charsWritten = + blitBuffer(base64ToBytes(string), buf, offset, length) + return charsWritten +} + +function _utf16leWrite (buf, string, offset, length) { + var charsWritten = Buffer._charsWritten = + blitBuffer(utf16leToBytes(string), buf, offset, length) + return charsWritten +} + +Buffer.prototype.write = function (string, offset, length, encoding) { + // Support both (string, offset, length, encoding) + // and the legacy (string, encoding, offset, length) + if (isFinite(offset)) { + if (!isFinite(length)) { + encoding = length + length = undefined + } + } else { // legacy + var swap = encoding + encoding = offset + offset = length + length = swap + } + + offset = Number(offset) || 0 + var remaining = this.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + encoding = String(encoding || 'utf8').toLowerCase() + + var ret + switch (encoding) { + case 'hex': + ret = _hexWrite(this, string, offset, length) + break + case 'utf8': + case 'utf-8': + ret = _utf8Write(this, string, offset, length) + break + case 'ascii': + ret = _asciiWrite(this, string, offset, length) + break + case 'binary': + ret = _binaryWrite(this, string, offset, length) + break + case 'base64': + ret = _base64Write(this, string, offset, length) + break + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + ret = _utf16leWrite(this, string, offset, length) + break + default: + throw new Error('Unknown encoding') + } + return ret +} + +Buffer.prototype.toString = function (encoding, start, end) { + var self = this + + encoding = String(encoding || 'utf8').toLowerCase() + start = Number(start) || 0 + end = (end !== undefined) + ? Number(end) + : end = self.length + + // Fastpath empty strings + if (end === start) + return '' + + var ret + switch (encoding) { + case 'hex': + ret = _hexSlice(self, start, end) + break + case 'utf8': + case 'utf-8': + ret = _utf8Slice(self, start, end) + break + case 'ascii': + ret = _asciiSlice(self, start, end) + break + case 'binary': + ret = _binarySlice(self, start, end) + break + case 'base64': + ret = _base64Slice(self, start, end) + break + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + ret = _utf16leSlice(self, start, end) + break + default: + throw new Error('Unknown encoding') + } + return ret +} + +Buffer.prototype.toJSON = function () { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + } +} + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function (target, target_start, start, end) { + var source = this + + if (!start) start = 0 + if (!end && end !== 0) end = this.length + if (!target_start) target_start = 0 + + // Copy 0 bytes; we're done + if (end === start) return + if (target.length === 0 || source.length === 0) return + + // Fatal error conditions + assert(end >= start, 'sourceEnd < sourceStart') + assert(target_start >= 0 && target_start < target.length, + 'targetStart out of bounds') + assert(start >= 0 && start < source.length, 'sourceStart out of bounds') + assert(end >= 0 && end <= source.length, 'sourceEnd out of bounds') + + // Are we oob? + if (end > this.length) + end = this.length + if (target.length - target_start < end - start) + end = target.length - target_start + start + + // copy! + for (var i = 0; i < end - start; i++) + target[i + target_start] = this[i + start] +} + +function _base64Slice (buf, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf) + } else { + return base64.fromByteArray(buf.slice(start, end)) + } +} + +function _utf8Slice (buf, start, end) { + var res = '' + var tmp = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; i++) { + if (buf[i] <= 0x7F) { + res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]) + tmp = '' + } else { + tmp += '%' + buf[i].toString(16) + } + } + + return res + decodeUtf8Char(tmp) +} + +function _asciiSlice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; i++) + ret += String.fromCharCode(buf[i]) + return ret +} + +function _binarySlice (buf, start, end) { + return _asciiSlice(buf, start, end) +} + +function _hexSlice (buf, start, end) { + var len = buf.length + + if (!start || start < 0) start = 0 + if (!end || end < 0 || end > len) end = len + + var out = '' + for (var i = start; i < end; i++) { + out += toHex(buf[i]) + } + return out +} + +function _utf16leSlice (buf, start, end) { + var bytes = buf.slice(start, end) + var res = '' + for (var i = 0; i < bytes.length; i += 2) { + res += String.fromCharCode(bytes[i] + bytes[i+1] * 256) + } + return res +} + +Buffer.prototype.slice = function (start, end) { + var len = this.length + start = clamp(start, len, 0) + end = clamp(end, len, len) + + if (Buffer._useTypedArrays) { + return augment(this.subarray(start, end)) + } else { + var sliceLen = end - start + var newBuf = new Buffer(sliceLen, undefined, true) + for (var i = 0; i < sliceLen; i++) { + newBuf[i] = this[i + start] + } + return newBuf + } +} + +// `get` will be removed in Node 0.13+ +Buffer.prototype.get = function (offset) { + console.log('.get() is deprecated. Access using array indexes instead.') + return this.readUInt8(offset) +} + +// `set` will be removed in Node 0.13+ +Buffer.prototype.set = function (v, offset) { + console.log('.set() is deprecated. Access using array indexes instead.') + return this.writeUInt8(v, offset) +} + +Buffer.prototype.readUInt8 = function (offset, noAssert) { + if (!noAssert) { + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset < this.length, 'Trying to read beyond buffer length') + } + + if (offset >= this.length) + return + + return this[offset] +} + +function _readUInt16 (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 1 < buf.length, 'Trying to read beyond buffer length') + } + + var len = buf.length + if (offset >= len) + return + + var val + if (littleEndian) { + val = buf[offset] + if (offset + 1 < len) + val |= buf[offset + 1] << 8 + } else { + val = buf[offset] << 8 + if (offset + 1 < len) + val |= buf[offset + 1] + } + return val +} + +Buffer.prototype.readUInt16LE = function (offset, noAssert) { + return _readUInt16(this, offset, true, noAssert) +} + +Buffer.prototype.readUInt16BE = function (offset, noAssert) { + return _readUInt16(this, offset, false, noAssert) +} + +function _readUInt32 (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') + } + + var len = buf.length + if (offset >= len) + return + + var val + if (littleEndian) { + if (offset + 2 < len) + val = buf[offset + 2] << 16 + if (offset + 1 < len) + val |= buf[offset + 1] << 8 + val |= buf[offset] + if (offset + 3 < len) + val = val + (buf[offset + 3] << 24 >>> 0) + } else { + if (offset + 1 < len) + val = buf[offset + 1] << 16 + if (offset + 2 < len) + val |= buf[offset + 2] << 8 + if (offset + 3 < len) + val |= buf[offset + 3] + val = val + (buf[offset] << 24 >>> 0) + } + return val +} + +Buffer.prototype.readUInt32LE = function (offset, noAssert) { + return _readUInt32(this, offset, true, noAssert) +} + +Buffer.prototype.readUInt32BE = function (offset, noAssert) { + return _readUInt32(this, offset, false, noAssert) +} + +Buffer.prototype.readInt8 = function (offset, noAssert) { + if (!noAssert) { + assert(offset !== undefined && offset !== null, + 'missing offset') + assert(offset < this.length, 'Trying to read beyond buffer length') + } + + if (offset >= this.length) + return + + var neg = this[offset] & 0x80 + if (neg) + return (0xff - this[offset] + 1) * -1 + else + return this[offset] +} + +function _readInt16 (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 1 < buf.length, 'Trying to read beyond buffer length') + } + + var len = buf.length + if (offset >= len) + return + + var val = _readUInt16(buf, offset, littleEndian, true) + var neg = val & 0x8000 + if (neg) + return (0xffff - val + 1) * -1 + else + return val +} + +Buffer.prototype.readInt16LE = function (offset, noAssert) { + return _readInt16(this, offset, true, noAssert) +} + +Buffer.prototype.readInt16BE = function (offset, noAssert) { + return _readInt16(this, offset, false, noAssert) +} + +function _readInt32 (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') + } + + var len = buf.length + if (offset >= len) + return + + var val = _readUInt32(buf, offset, littleEndian, true) + var neg = val & 0x80000000 + if (neg) + return (0xffffffff - val + 1) * -1 + else + return val +} + +Buffer.prototype.readInt32LE = function (offset, noAssert) { + return _readInt32(this, offset, true, noAssert) +} + +Buffer.prototype.readInt32BE = function (offset, noAssert) { + return _readInt32(this, offset, false, noAssert) +} + +function _readFloat (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') + } + + return ieee754.read(buf, offset, littleEndian, 23, 4) +} + +Buffer.prototype.readFloatLE = function (offset, noAssert) { + return _readFloat(this, offset, true, noAssert) +} + +Buffer.prototype.readFloatBE = function (offset, noAssert) { + return _readFloat(this, offset, false, noAssert) +} + +function _readDouble (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset + 7 < buf.length, 'Trying to read beyond buffer length') + } + + return ieee754.read(buf, offset, littleEndian, 52, 8) +} + +Buffer.prototype.readDoubleLE = function (offset, noAssert) { + return _readDouble(this, offset, true, noAssert) +} + +Buffer.prototype.readDoubleBE = function (offset, noAssert) { + return _readDouble(this, offset, false, noAssert) +} + +Buffer.prototype.writeUInt8 = function (value, offset, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset < this.length, 'trying to write beyond buffer length') + verifuint(value, 0xff) + } + + if (offset >= this.length) return + + this[offset] = value +} + +function _writeUInt16 (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 1 < buf.length, 'trying to write beyond buffer length') + verifuint(value, 0xffff) + } + + var len = buf.length + if (offset >= len) + return + + for (var i = 0, j = Math.min(len - offset, 2); i < j; i++) { + buf[offset + i] = + (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> + (littleEndian ? i : 1 - i) * 8 + } +} + +Buffer.prototype.writeUInt16LE = function (value, offset, noAssert) { + _writeUInt16(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeUInt16BE = function (value, offset, noAssert) { + _writeUInt16(this, value, offset, false, noAssert) +} + +function _writeUInt32 (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 3 < buf.length, 'trying to write beyond buffer length') + verifuint(value, 0xffffffff) + } + + var len = buf.length + if (offset >= len) + return + + for (var i = 0, j = Math.min(len - offset, 4); i < j; i++) { + buf[offset + i] = + (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff + } +} + +Buffer.prototype.writeUInt32LE = function (value, offset, noAssert) { + _writeUInt32(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeUInt32BE = function (value, offset, noAssert) { + _writeUInt32(this, value, offset, false, noAssert) +} + +Buffer.prototype.writeInt8 = function (value, offset, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset < this.length, 'Trying to write beyond buffer length') + verifsint(value, 0x7f, -0x80) + } + + if (offset >= this.length) + return + + if (value >= 0) + this.writeUInt8(value, offset, noAssert) + else + this.writeUInt8(0xff + value + 1, offset, noAssert) +} + +function _writeInt16 (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 1 < buf.length, 'Trying to write beyond buffer length') + verifsint(value, 0x7fff, -0x8000) + } + + var len = buf.length + if (offset >= len) + return + + if (value >= 0) + _writeUInt16(buf, value, offset, littleEndian, noAssert) + else + _writeUInt16(buf, 0xffff + value + 1, offset, littleEndian, noAssert) +} + +Buffer.prototype.writeInt16LE = function (value, offset, noAssert) { + _writeInt16(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeInt16BE = function (value, offset, noAssert) { + _writeInt16(this, value, offset, false, noAssert) +} + +function _writeInt32 (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 3 < buf.length, 'Trying to write beyond buffer length') + verifsint(value, 0x7fffffff, -0x80000000) + } + + var len = buf.length + if (offset >= len) + return + + if (value >= 0) + _writeUInt32(buf, value, offset, littleEndian, noAssert) + else + _writeUInt32(buf, 0xffffffff + value + 1, offset, littleEndian, noAssert) +} + +Buffer.prototype.writeInt32LE = function (value, offset, noAssert) { + _writeInt32(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeInt32BE = function (value, offset, noAssert) { + _writeInt32(this, value, offset, false, noAssert) +} + +function _writeFloat (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 3 < buf.length, 'Trying to write beyond buffer length') + verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38) + } + + var len = buf.length + if (offset >= len) + return + + ieee754.write(buf, value, offset, littleEndian, 23, 4) +} + +Buffer.prototype.writeFloatLE = function (value, offset, noAssert) { + _writeFloat(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeFloatBE = function (value, offset, noAssert) { + _writeFloat(this, value, offset, false, noAssert) +} + +function _writeDouble (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 7 < buf.length, + 'Trying to write beyond buffer length') + verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308) + } + + var len = buf.length + if (offset >= len) + return + + ieee754.write(buf, value, offset, littleEndian, 52, 8) +} + +Buffer.prototype.writeDoubleLE = function (value, offset, noAssert) { + _writeDouble(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeDoubleBE = function (value, offset, noAssert) { + _writeDouble(this, value, offset, false, noAssert) +} + +// fill(value, start=0, end=buffer.length) +Buffer.prototype.fill = function (value, start, end) { + if (!value) value = 0 + if (!start) start = 0 + if (!end) end = this.length + + if (typeof value === 'string') { + value = value.charCodeAt(0) + } + + assert(typeof value === 'number' && !isNaN(value), 'value is not a number') + assert(end >= start, 'end < start') + + // Fill 0 bytes; we're done + if (end === start) return + if (this.length === 0) return + + assert(start >= 0 && start < this.length, 'start out of bounds') + assert(end >= 0 && end <= this.length, 'end out of bounds') + + for (var i = start; i < end; i++) { + this[i] = value + } +} + +Buffer.prototype.inspect = function () { + var out = [] + var len = this.length + for (var i = 0; i < len; i++) { + out[i] = toHex(this[i]) + if (i === exports.INSPECT_MAX_BYTES) { + out[i + 1] = '...' + break + } + } + return '' +} + +/** + * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance. + * Added in Node 0.12. Only available in browsers that support ArrayBuffer. + */ +Buffer.prototype.toArrayBuffer = function () { + if (typeof Uint8Array === 'function') { + if (Buffer._useTypedArrays) { + return (new Buffer(this)).buffer + } else { + var buf = new Uint8Array(this.length) + for (var i = 0, len = buf.length; i < len; i += 1) + buf[i] = this[i] + return buf.buffer + } + } else { + throw new Error('Buffer.toArrayBuffer not supported in this browser') + } +} + +// HELPER FUNCTIONS +// ================ + +function stringtrim (str) { + if (str.trim) return str.trim() + return str.replace(/^\s+|\s+$/g, '') +} + +var BP = Buffer.prototype + +/** + * Augment the Uint8Array *instance* (not the class!) with Buffer methods + */ +function augment (arr) { + arr._isBuffer = true + + // save reference to original Uint8Array get/set methods before overwriting + arr._get = arr.get + arr._set = arr.set + + // deprecated, will be removed in node 0.13+ + arr.get = BP.get + arr.set = BP.set + + arr.write = BP.write + arr.toString = BP.toString + arr.toLocaleString = BP.toString + arr.toJSON = BP.toJSON + arr.copy = BP.copy + arr.slice = BP.slice + arr.readUInt8 = BP.readUInt8 + arr.readUInt16LE = BP.readUInt16LE + arr.readUInt16BE = BP.readUInt16BE + arr.readUInt32LE = BP.readUInt32LE + arr.readUInt32BE = BP.readUInt32BE + arr.readInt8 = BP.readInt8 + arr.readInt16LE = BP.readInt16LE + arr.readInt16BE = BP.readInt16BE + arr.readInt32LE = BP.readInt32LE + arr.readInt32BE = BP.readInt32BE + arr.readFloatLE = BP.readFloatLE + arr.readFloatBE = BP.readFloatBE + arr.readDoubleLE = BP.readDoubleLE + arr.readDoubleBE = BP.readDoubleBE + arr.writeUInt8 = BP.writeUInt8 + arr.writeUInt16LE = BP.writeUInt16LE + arr.writeUInt16BE = BP.writeUInt16BE + arr.writeUInt32LE = BP.writeUInt32LE + arr.writeUInt32BE = BP.writeUInt32BE + arr.writeInt8 = BP.writeInt8 + arr.writeInt16LE = BP.writeInt16LE + arr.writeInt16BE = BP.writeInt16BE + arr.writeInt32LE = BP.writeInt32LE + arr.writeInt32BE = BP.writeInt32BE + arr.writeFloatLE = BP.writeFloatLE + arr.writeFloatBE = BP.writeFloatBE + arr.writeDoubleLE = BP.writeDoubleLE + arr.writeDoubleBE = BP.writeDoubleBE + arr.fill = BP.fill + arr.inspect = BP.inspect + arr.toArrayBuffer = BP.toArrayBuffer + + return arr +} + +// slice(start, end) +function clamp (index, len, defaultValue) { + if (typeof index !== 'number') return defaultValue + index = ~~index; // Coerce to integer. + if (index >= len) return len + if (index >= 0) return index + index += len + if (index >= 0) return index + return 0 +} + +function coerce (length) { + // Coerce length to a number (possibly NaN), round up + // in case it's fractional (e.g. 123.456) then do a + // double negate to coerce a NaN to 0. Easy, right? + length = ~~Math.ceil(+length) + return length < 0 ? 0 : length +} + +function isArray (subject) { + return (Array.isArray || function (subject) { + return Object.prototype.toString.call(subject) === '[object Array]' + })(subject) +} + +function isArrayish (subject) { + return isArray(subject) || Buffer.isBuffer(subject) || + subject && typeof subject === 'object' && + typeof subject.length === 'number' +} + +function toHex (n) { + if (n < 16) return '0' + n.toString(16) + return n.toString(16) +} + +function utf8ToBytes (str) { + var byteArray = [] + for (var i = 0; i < str.length; i++) { + var b = str.charCodeAt(i) + if (b <= 0x7F) + byteArray.push(str.charCodeAt(i)) + else { + var start = i + if (b >= 0xD800 && b <= 0xDFFF) i++ + var h = encodeURIComponent(str.slice(start, i+1)).substr(1).split('%') + for (var j = 0; j < h.length; j++) + byteArray.push(parseInt(h[j], 16)) + } + } + return byteArray +} + +function asciiToBytes (str) { + var byteArray = [] + for (var i = 0; i < str.length; i++) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xFF) + } + return byteArray +} + +function utf16leToBytes (str) { + var c, hi, lo + var byteArray = [] + for (var i = 0; i < str.length; i++) { + c = str.charCodeAt(i) + hi = c >> 8 + lo = c % 256 + byteArray.push(lo) + byteArray.push(hi) + } + + return byteArray +} + +function base64ToBytes (str) { + return base64.toByteArray(str) +} + +function blitBuffer (src, dst, offset, length) { + var pos + for (var i = 0; i < length; i++) { + if ((i + offset >= dst.length) || (i >= src.length)) + break + dst[i + offset] = src[i] + } + return i +} + +function decodeUtf8Char (str) { + try { + return decodeURIComponent(str) + } catch (err) { + return String.fromCharCode(0xFFFD) // UTF 8 invalid char + } +} + +/* + * We have to make sure that the value is a valid integer. This means that it + * is non-negative. It has no fractional component and that it does not + * exceed the maximum allowed value. + */ +function verifuint (value, max) { + assert(typeof value === 'number', 'cannot write a non-number as a number') + assert(value >= 0, 'specified a negative value for writing an unsigned value') + assert(value <= max, 'value is larger than maximum value for type') + assert(Math.floor(value) === value, 'value has a fractional component') +} + +function verifsint (value, max, min) { + assert(typeof value === 'number', 'cannot write a non-number as a number') + assert(value <= max, 'value larger than maximum allowed value') + assert(value >= min, 'value smaller than minimum allowed value') + assert(Math.floor(value) === value, 'value has a fractional component') +} + +function verifIEEE754 (value, max, min) { + assert(typeof value === 'number', 'cannot write a non-number as a number') + assert(value <= max, 'value larger than maximum allowed value') + assert(value >= min, 'value smaller than minimum allowed value') +} + +function assert (test, message) { + if (!test) throw new Error(message || 'Failed assertion') +} + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer/index.js","/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer") +},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"base64-js":2,"buffer":1,"ieee754":3}],2:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +;(function (exports) { + 'use strict'; + + var Arr = (typeof Uint8Array !== 'undefined') + ? Uint8Array + : Array + + var ZERO = '0'.charCodeAt(0) + var PLUS = '+'.charCodeAt(0) + var SLASH = '/'.charCodeAt(0) + var NUMBER = '0'.charCodeAt(0) + var LOWER = 'a'.charCodeAt(0) + var UPPER = 'A'.charCodeAt(0) + + function decode (elt) { + var code = elt.charCodeAt(0) + if (code === PLUS) + return 62 // '+' + if (code === SLASH) + return 63 // '/' + if (code < NUMBER) + return -1 //no match + if (code < NUMBER + 10) + return code - NUMBER + 26 + 26 + if (code < UPPER + 26) + return code - UPPER + if (code < LOWER + 26) + return code - LOWER + 26 + } + + function b64ToByteArray (b64) { + var i, j, l, tmp, placeHolders, arr + + if (b64.length % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // the number of equal signs (place holders) + // if there are two placeholders, than the two characters before it + // represent one byte + // if there is only one, then the three characters before it represent 2 bytes + // this is just a cheap hack to not do indexOf twice + var len = b64.length + placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0 + + // base64 is 4/3 + up to two characters of the original data + arr = new Arr(b64.length * 3 / 4 - placeHolders) + + // if there are placeholders, only get up to the last complete 4 chars + l = placeHolders > 0 ? b64.length - 4 : b64.length + + var L = 0 + + function push (v) { + arr[L++] = v + } + + for (i = 0, j = 0; i < l; i += 4, j += 3) { + tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) + push((tmp & 0xFF0000) >> 16) + push((tmp & 0xFF00) >> 8) + push(tmp & 0xFF) + } + + if (placeHolders === 2) { + tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) + push(tmp & 0xFF) + } else if (placeHolders === 1) { + tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) + push((tmp >> 8) & 0xFF) + push(tmp & 0xFF) + } + + return arr + } + + function uint8ToBase64 (uint8) { + var i, + extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes + output = "", + temp, length + + function encode (num) { + return lookup.charAt(num) + } + + function tripletToBase64 (num) { + return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) + } + + // go through the array every three bytes, we'll deal with trailing stuff later + for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { + temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) + output += tripletToBase64(temp) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + switch (extraBytes) { + case 1: + temp = uint8[uint8.length - 1] + output += encode(temp >> 2) + output += encode((temp << 4) & 0x3F) + output += '==' + break + case 2: + temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) + output += encode(temp >> 10) + output += encode((temp >> 4) & 0x3F) + output += encode((temp << 2) & 0x3F) + output += '=' + break + } + + return output + } + + module.exports.toByteArray = b64ToByteArray + module.exports.fromByteArray = uint8ToBase64 +}()) + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer/node_modules/base64-js/lib/b64.js","/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer/node_modules/base64-js/lib") +},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],3:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +exports.read = function(buffer, offset, isLE, mLen, nBytes) { + var e, m, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + nBits = -7, + i = isLE ? (nBytes - 1) : 0, + d = isLE ? -1 : 1, + s = buffer[offset + i]; + + i += d; + + e = s & ((1 << (-nBits)) - 1); + s >>= (-nBits); + nBits += eLen; + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); + + m = e & ((1 << (-nBits)) - 1); + e >>= (-nBits); + nBits += mLen; + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); + + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity); + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); +}; + +exports.write = function(buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), + i = isLE ? 0 : (nBytes - 1), + d = isLE ? 1 : -1, + s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; + + value = Math.abs(value); + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + if (e + eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + if (value * c >= 2) { + e++; + c /= 2; + } + + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); + + e = (e << mLen) | m; + eLen += mLen; + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); + + buffer[offset + i - d] |= s * 128; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer/node_modules/ieee754/index.js","/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer/node_modules/ieee754") +},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],4:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +// shim for using process in browser + +var process = module.exports = {}; + +process.nextTick = (function () { + var canSetImmediate = typeof window !== 'undefined' + && window.setImmediate; + var canPost = typeof window !== 'undefined' + && window.postMessage && window.addEventListener + ; + + if (canSetImmediate) { + return function (f) { return window.setImmediate(f) }; + } + + if (canPost) { + var queue = []; + window.addEventListener('message', function (ev) { + var source = ev.source; + if ((source === window || source === null) && ev.data === 'process-tick') { + ev.stopPropagation(); + if (queue.length > 0) { + var fn = queue.shift(); + fn(); + } + } + }, true); + + return function nextTick(fn) { + queue.push(fn); + window.postMessage('process-tick', '*'); + }; + } + + return function nextTick(fn) { + setTimeout(fn, 0); + }; +})(); + +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; + +function noop() {} + +process.on = noop; +process.once = noop; +process.off = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +} + +// TODO(shtylman) +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js","/../node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process") +},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],5:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Scalar = _dereq_('./Scalar'); + +module.exports = Line; + +/** + * Container for line-related functions + * @class Line + */ +function Line(){}; + +/** + * Compute the intersection between two lines. + * @static + * @method lineInt + * @param {Array} l1 Line vector 1 + * @param {Array} l2 Line vector 2 + * @param {Number} precision Precision to use when checking if the lines are parallel + * @return {Array} The intersection point. + */ +Line.lineInt = function(l1,l2,precision){ + precision = precision || 0; + var i = [0,0]; // point + var a1, b1, c1, a2, b2, c2, det; // scalars + a1 = l1[1][1] - l1[0][1]; + b1 = l1[0][0] - l1[1][0]; + c1 = a1 * l1[0][0] + b1 * l1[0][1]; + a2 = l2[1][1] - l2[0][1]; + b2 = l2[0][0] - l2[1][0]; + c2 = a2 * l2[0][0] + b2 * l2[0][1]; + det = a1 * b2 - a2*b1; + if (!Scalar.eq(det, 0, precision)) { // lines are not parallel + i[0] = (b2 * c1 - b1 * c2) / det; + i[1] = (a1 * c2 - a2 * c1) / det; + } + return i; +}; + +/** + * Checks if two line segments intersects. + * @method segmentsIntersect + * @param {Array} p1 The start vertex of the first line segment. + * @param {Array} p2 The end vertex of the first line segment. + * @param {Array} q1 The start vertex of the second line segment. + * @param {Array} q2 The end vertex of the second line segment. + * @return {Boolean} True if the two line segments intersect + */ +Line.segmentsIntersect = function(p1, p2, q1, q2){ + var dx = p2[0] - p1[0]; + var dy = p2[1] - p1[1]; + var da = q2[0] - q1[0]; + var db = q2[1] - q1[1]; + + // segments are parallel + if(da*dy - db*dx == 0) + return false; + + var s = (dx * (q1[1] - p1[1]) + dy * (p1[0] - q1[0])) / (da * dy - db * dx) + var t = (da * (p1[1] - q1[1]) + db * (q1[0] - p1[0])) / (db * dx - da * dy) + + return (s>=0 && s<=1 && t>=0 && t<=1); +}; + + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/poly-decomp/src/Line.js","/../node_modules/poly-decomp/src") +},{"./Scalar":8,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],6:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +module.exports = Point; + +/** + * Point related functions + * @class Point + */ +function Point(){}; + +/** + * Get the area of a triangle spanned by the three given points. Note that the area will be negative if the points are not given in counter-clockwise order. + * @static + * @method area + * @param {Array} a + * @param {Array} b + * @param {Array} c + * @return {Number} + */ +Point.area = function(a,b,c){ + return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1]))); +}; + +Point.left = function(a,b,c){ + return Point.area(a,b,c) > 0; +}; + +Point.leftOn = function(a,b,c) { + return Point.area(a, b, c) >= 0; +}; + +Point.right = function(a,b,c) { + return Point.area(a, b, c) < 0; +}; + +Point.rightOn = function(a,b,c) { + return Point.area(a, b, c) <= 0; +}; + +var tmpPoint1 = [], + tmpPoint2 = []; + +/** + * Check if three points are collinear + * @method collinear + * @param {Array} a + * @param {Array} b + * @param {Array} c + * @param {Number} [thresholdAngle=0] Threshold angle to use when comparing the vectors. The function will return true if the angle between the resulting vectors is less than this value. Use zero for max precision. + * @return {Boolean} + */ +Point.collinear = function(a,b,c,thresholdAngle) { + if(!thresholdAngle) + return Point.area(a, b, c) == 0; + else { + var ab = tmpPoint1, + bc = tmpPoint2; + + ab[0] = b[0]-a[0]; + ab[1] = b[1]-a[1]; + bc[0] = c[0]-b[0]; + bc[1] = c[1]-b[1]; + + var dot = ab[0]*bc[0] + ab[1]*bc[1], + magA = Math.sqrt(ab[0]*ab[0] + ab[1]*ab[1]), + magB = Math.sqrt(bc[0]*bc[0] + bc[1]*bc[1]), + angle = Math.acos(dot/(magA*magB)); + return angle < thresholdAngle; + } +}; + +Point.sqdist = function(a,b){ + var dx = b[0] - a[0]; + var dy = b[1] - a[1]; + return dx * dx + dy * dy; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/poly-decomp/src/Point.js","/../node_modules/poly-decomp/src") +},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],7:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Line = _dereq_("./Line") +, Point = _dereq_("./Point") +, Scalar = _dereq_("./Scalar") + +module.exports = Polygon; + +/** + * Polygon class. + * @class Polygon + * @constructor + */ +function Polygon(){ + + /** + * Vertices that this polygon consists of. An array of array of numbers, example: [[0,0],[1,0],..] + * @property vertices + * @type {Array} + */ + this.vertices = []; +} + +/** + * Get a vertex at position i. It does not matter if i is out of bounds, this function will just cycle. + * @method at + * @param {Number} i + * @return {Array} + */ +Polygon.prototype.at = function(i){ + var v = this.vertices, + s = v.length; + return v[i < 0 ? i % s + s : i % s]; +}; + +/** + * Get first vertex + * @method first + * @return {Array} + */ +Polygon.prototype.first = function(){ + return this.vertices[0]; +}; + +/** + * Get last vertex + * @method last + * @return {Array} + */ +Polygon.prototype.last = function(){ + return this.vertices[this.vertices.length-1]; +}; + +/** + * Clear the polygon data + * @method clear + * @return {Array} + */ +Polygon.prototype.clear = function(){ + this.vertices.length = 0; +}; + +/** + * Append points "from" to "to"-1 from an other polygon "poly" onto this one. + * @method append + * @param {Polygon} poly The polygon to get points from. + * @param {Number} from The vertex index in "poly". + * @param {Number} to The end vertex index in "poly". Note that this vertex is NOT included when appending. + * @return {Array} + */ +Polygon.prototype.append = function(poly,from,to){ + if(typeof(from) == "undefined") throw new Error("From is not given!"); + if(typeof(to) == "undefined") throw new Error("To is not given!"); + + if(to-1 < from) throw new Error("lol1"); + if(to > poly.vertices.length) throw new Error("lol2"); + if(from < 0) throw new Error("lol3"); + + for(var i=from; i v[br][0])) { + br = i; + } + } + + // reverse poly if clockwise + if (!Point.left(this.at(br - 1), this.at(br), this.at(br + 1))) { + this.reverse(); + } +}; + +/** + * Reverse the vertices in the polygon + * @method reverse + */ +Polygon.prototype.reverse = function(){ + var tmp = []; + for(var i=0, N=this.vertices.length; i!==N; i++){ + tmp.push(this.vertices.pop()); + } + this.vertices = tmp; +}; + +/** + * Check if a point in the polygon is a reflex point + * @method isReflex + * @param {Number} i + * @return {Boolean} + */ +Polygon.prototype.isReflex = function(i){ + return Point.right(this.at(i - 1), this.at(i), this.at(i + 1)); +}; + +var tmpLine1=[], + tmpLine2=[]; + +/** + * Check if two vertices in the polygon can see each other + * @method canSee + * @param {Number} a Vertex index 1 + * @param {Number} b Vertex index 2 + * @return {Boolean} + */ +Polygon.prototype.canSee = function(a,b) { + var p, dist, l1=tmpLine1, l2=tmpLine2; + + if (Point.leftOn(this.at(a + 1), this.at(a), this.at(b)) && Point.rightOn(this.at(a - 1), this.at(a), this.at(b))) { + return false; + } + dist = Point.sqdist(this.at(a), this.at(b)); + for (var i = 0; i !== this.vertices.length; ++i) { // for each edge + if ((i + 1) % this.vertices.length === a || i === a) // ignore incident edges + continue; + if (Point.leftOn(this.at(a), this.at(b), this.at(i + 1)) && Point.rightOn(this.at(a), this.at(b), this.at(i))) { // if diag intersects an edge + l1[0] = this.at(a); + l1[1] = this.at(b); + l2[0] = this.at(i); + l2[1] = this.at(i + 1); + p = Line.lineInt(l1,l2); + if (Point.sqdist(this.at(a), p) < dist) { // if edge is blocking visibility to b + return false; + } + } + } + + return true; +}; + +/** + * Copy the polygon from vertex i to vertex j. + * @method copy + * @param {Number} i + * @param {Number} j + * @param {Polygon} [targetPoly] Optional target polygon to save in. + * @return {Polygon} The resulting copy. + */ +Polygon.prototype.copy = function(i,j,targetPoly){ + var p = targetPoly || new Polygon(); + p.clear(); + if (i < j) { + // Insert all vertices from i to j + for(var k=i; k<=j; k++) + p.vertices.push(this.vertices[k]); + + } else { + + // Insert vertices 0 to j + for(var k=0; k<=j; k++) + p.vertices.push(this.vertices[k]); + + // Insert vertices i to end + for(var k=i; k 0) + return this.slice(edges); + else + return [this]; +}; + +/** + * Slices the polygon given one or more cut edges. If given one, this function will return two polygons (false on failure). If many, an array of polygons. + * @method slice + * @param {Array} cutEdges A list of edges, as returned by .getCutEdges() + * @return {Array} + */ +Polygon.prototype.slice = function(cutEdges){ + if(cutEdges.length == 0) return [this]; + if(cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length==2 && cutEdges[0][0] instanceof Array){ + + var polys = [this]; + + for(var i=0; i maxlevel){ + console.warn("quickDecomp: max level ("+maxlevel+") reached."); + return result; + } + + for (var i = 0; i < this.vertices.length; ++i) { + if (poly.isReflex(i)) { + reflexVertices.push(poly.vertices[i]); + upperDist = lowerDist = Number.MAX_VALUE; + + + for (var j = 0; j < this.vertices.length; ++j) { + if (Point.left(poly.at(i - 1), poly.at(i), poly.at(j)) + && Point.rightOn(poly.at(i - 1), poly.at(i), poly.at(j - 1))) { // if line intersects with an edge + p = getIntersectionPoint(poly.at(i - 1), poly.at(i), poly.at(j), poly.at(j - 1)); // find the point of intersection + if (Point.right(poly.at(i + 1), poly.at(i), p)) { // make sure it's inside the poly + d = Point.sqdist(poly.vertices[i], p); + if (d < lowerDist) { // keep only the closest intersection + lowerDist = d; + lowerInt = p; + lowerIndex = j; + } + } + } + if (Point.left(poly.at(i + 1), poly.at(i), poly.at(j + 1)) + && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) { + p = getIntersectionPoint(poly.at(i + 1), poly.at(i), poly.at(j), poly.at(j + 1)); + if (Point.left(poly.at(i - 1), poly.at(i), p)) { + d = Point.sqdist(poly.vertices[i], p); + if (d < upperDist) { + upperDist = d; + upperInt = p; + upperIndex = j; + } + } + } + } + + // if there are no vertices to connect to, choose a point in the middle + if (lowerIndex == (upperIndex + 1) % this.vertices.length) { + //console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+this.vertices.length+")"); + p[0] = (lowerInt[0] + upperInt[0]) / 2; + p[1] = (lowerInt[1] + upperInt[1]) / 2; + steinerPoints.push(p); + + if (i < upperIndex) { + //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1); + lowerPoly.append(poly, i, upperIndex+1); + lowerPoly.vertices.push(p); + upperPoly.vertices.push(p); + if (lowerIndex != 0){ + //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end()); + upperPoly.append(poly,lowerIndex,poly.vertices.length); + } + //upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1); + upperPoly.append(poly,0,i+1); + } else { + if (i != 0){ + //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end()); + lowerPoly.append(poly,i,poly.vertices.length); + } + //lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1); + lowerPoly.append(poly,0,upperIndex+1); + lowerPoly.vertices.push(p); + upperPoly.vertices.push(p); + //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1); + upperPoly.append(poly,lowerIndex,i+1); + } + } else { + // connect to the closest point within the triangle + //console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+this.vertices.length+")\n"); + + if (lowerIndex > upperIndex) { + upperIndex += this.vertices.length; + } + closestDist = Number.MAX_VALUE; + + if(upperIndex < lowerIndex){ + return result; + } + + for (var j = lowerIndex; j <= upperIndex; ++j) { + if (Point.leftOn(poly.at(i - 1), poly.at(i), poly.at(j)) + && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) { + d = Point.sqdist(poly.at(i), poly.at(j)); + if (d < closestDist) { + closestDist = d; + closestIndex = j % this.vertices.length; + } + } + } + + if (i < closestIndex) { + lowerPoly.append(poly,i,closestIndex+1); + if (closestIndex != 0){ + upperPoly.append(poly,closestIndex,v.length); + } + upperPoly.append(poly,0,i+1); + } else { + if (i != 0){ + lowerPoly.append(poly,i,v.length); + } + lowerPoly.append(poly,0,closestIndex+1); + upperPoly.append(poly,closestIndex,i+1); + } + } + + // solve smallest poly first + if (lowerPoly.vertices.length < upperPoly.vertices.length) { + lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); + upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); + } else { + upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); + lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level); + } + + return result; + } + } + result.push(this); + + return result; +}; + +/** + * Remove collinear points in the polygon. + * @method removeCollinearPoints + * @param {Number} [precision] The threshold angle to use when determining whether two edges are collinear. Use zero for finest precision. + * @return {Number} The number of points removed + */ +Polygon.prototype.removeCollinearPoints = function(precision){ + var num = 0; + for(var i=this.vertices.length-1; this.vertices.length>3 && i>=0; --i){ + if(Point.collinear(this.at(i-1),this.at(i),this.at(i+1),precision)){ + // Remove the middle point + this.vertices.splice(i%this.vertices.length,1); + i--; // Jump one point forward. Otherwise we may get a chain removal + num++; + } + } + return num; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/poly-decomp/src/Polygon.js","/../node_modules/poly-decomp/src") +},{"./Line":5,"./Point":6,"./Scalar":8,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],8:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +module.exports = Scalar; + +/** + * Scalar functions + * @class Scalar + */ +function Scalar(){} + +/** + * Check if two scalars are equal + * @static + * @method eq + * @param {Number} a + * @param {Number} b + * @param {Number} [precision] + * @return {Boolean} + */ +Scalar.eq = function(a,b,precision){ + precision = precision || 0; + return Math.abs(a-b) < precision; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/poly-decomp/src/Scalar.js","/../node_modules/poly-decomp/src") +},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],9:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +module.exports = { + Polygon : _dereq_("./Polygon"), + Point : _dereq_("./Point"), +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/poly-decomp/src/index.js","/../node_modules/poly-decomp/src") +},{"./Point":6,"./Polygon":7,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],10:[function(_dereq_,module,exports){ +module.exports={ + "name": "p2", + "version": "0.6.0", + "description": "A JavaScript 2D physics engine.", + "author": "Stefan Hedman (http://steffe.se)", + "keywords": [ + "p2.js", + "p2", + "physics", + "engine", + "2d" + ], + "main": "./src/p2.js", + "engines": { + "node": "*" + }, + "repository": { + "type": "git", + "url": "https://github.com/schteppe/p2.js.git" + }, + "bugs": { + "url": "https://github.com/schteppe/p2.js/issues" + }, + "licenses": [ + { + "type": "MIT" + } + ], + "devDependencies": { + "grunt": "~0.4.0", + "grunt-contrib-jshint": "~0.9.2", + "grunt-contrib-nodeunit": "~0.1.2", + "grunt-contrib-uglify": "~0.4.0", + "grunt-contrib-watch": "~0.5.0", + "grunt-browserify": "~2.0.1", + "grunt-contrib-concat": "^0.4.0" + }, + "dependencies": { + "poly-decomp": "0.1.0" + } +} + +},{}],11:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var vec2 = _dereq_('../math/vec2') +, Utils = _dereq_('../utils/Utils'); + +module.exports = AABB; + +/** + * Axis aligned bounding box class. + * @class AABB + * @constructor + * @param {Object} [options] + * @param {Array} [options.upperBound] + * @param {Array} [options.lowerBound] + */ +function AABB(options){ + + /** + * The lower bound of the bounding box. + * @property lowerBound + * @type {Array} + */ + this.lowerBound = vec2.create(); + if(options && options.lowerBound){ + vec2.copy(this.lowerBound, options.lowerBound); + } + + /** + * The upper bound of the bounding box. + * @property upperBound + * @type {Array} + */ + this.upperBound = vec2.create(); + if(options && options.upperBound){ + vec2.copy(this.upperBound, options.upperBound); + } +} + +var tmp = vec2.create(); + +/** + * Set the AABB bounds from a set of points. + * @method setFromPoints + * @param {Array} points An array of vec2's. + */ +AABB.prototype.setFromPoints = function(points, position, angle, skinSize){ + var l = this.lowerBound, + u = this.upperBound; + + if(typeof(angle) !== "number"){ + angle = 0; + } + + // Set to the first point + if(angle !== 0){ + vec2.rotate(l, points[0], angle); + } else { + vec2.copy(l, points[0]); + } + vec2.copy(u, l); + + // Compute cosines and sines just once + var cosAngle = Math.cos(angle), + sinAngle = Math.sin(angle); + for(var i = 1; i u[j]){ + u[j] = p[j]; + } + if(p[j] < l[j]){ + l[j] = p[j]; + } + } + } + + // Add offset + if(position){ + vec2.add(this.lowerBound, this.lowerBound, position); + vec2.add(this.upperBound, this.upperBound, position); + } + + if(skinSize){ + this.lowerBound[0] -= skinSize; + this.lowerBound[1] -= skinSize; + this.upperBound[0] += skinSize; + this.upperBound[1] += skinSize; + } +}; + +/** + * Copy bounds from an AABB to this AABB + * @method copy + * @param {AABB} aabb + */ +AABB.prototype.copy = function(aabb){ + vec2.copy(this.lowerBound, aabb.lowerBound); + vec2.copy(this.upperBound, aabb.upperBound); +}; + +/** + * Extend this AABB so that it covers the given AABB too. + * @method extend + * @param {AABB} aabb + */ +AABB.prototype.extend = function(aabb){ + // Loop over x and y + var i = 2; + while(i--){ + // Extend lower bound + var l = aabb.lowerBound[i]; + if(this.lowerBound[i] > l){ + this.lowerBound[i] = l; + } + + // Upper + var u = aabb.upperBound[i]; + if(this.upperBound[i] < u){ + this.upperBound[i] = u; + } + } +}; + +/** + * Returns true if the given AABB overlaps this AABB. + * @method overlaps + * @param {AABB} aabb + * @return {Boolean} + */ +AABB.prototype.overlaps = function(aabb){ + var l1 = this.lowerBound, + u1 = this.upperBound, + l2 = aabb.lowerBound, + u2 = aabb.upperBound; + + // l2 u2 + // |---------| + // |--------| + // l1 u1 + + return ((l2[0] <= u1[0] && u1[0] <= u2[0]) || (l1[0] <= u2[0] && u2[0] <= u1[0])) && + ((l2[1] <= u1[1] && u1[1] <= u2[1]) || (l1[1] <= u2[1] && u2[1] <= u1[1])); +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/collision/AABB.js","/collision") +},{"../math/vec2":33,"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],12:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var vec2 = _dereq_('../math/vec2'); +var Body = _dereq_('../objects/Body'); + +module.exports = Broadphase; + +/** + * Base class for broadphase implementations. + * @class Broadphase + * @constructor + */ +function Broadphase(type){ + + this.type = type; + + /** + * The resulting overlapping pairs. Will be filled with results during .getCollisionPairs(). + * @property result + * @type {Array} + */ + this.result = []; + + /** + * The world to search for collision pairs in. To change it, use .setWorld() + * @property world + * @type {World} + * @readOnly + */ + this.world = null; + + /** + * The bounding volume type to use in the broadphase algorithms. + * @property {Number} boundingVolumeType + */ + this.boundingVolumeType = Broadphase.AABB; +} + +/** + * Axis aligned bounding box type. + * @static + * @property {Number} AABB + */ +Broadphase.AABB = 1; + +/** + * Bounding circle type. + * @static + * @property {Number} BOUNDING_CIRCLE + */ +Broadphase.BOUNDING_CIRCLE = 2; + +/** + * Set the world that we are searching for collision pairs in + * @method setWorld + * @param {World} world + */ +Broadphase.prototype.setWorld = function(world){ + this.world = world; +}; + +/** + * Get all potential intersecting body pairs. + * @method getCollisionPairs + * @param {World} world The world to search in. + * @return {Array} An array of the bodies, ordered in pairs. Example: A result of [a,b,c,d] means that the potential pairs are: (a,b), (c,d). + */ +Broadphase.prototype.getCollisionPairs = function(world){ + throw new Error("getCollisionPairs must be implemented in a subclass!"); +}; + +var dist = vec2.create(); + +/** + * Check whether the bounding radius of two bodies overlap. + * @method boundingRadiusCheck + * @param {Body} bodyA + * @param {Body} bodyB + * @return {Boolean} + */ +Broadphase.boundingRadiusCheck = function(bodyA, bodyB){ + vec2.sub(dist, bodyA.position, bodyB.position); + var d2 = vec2.squaredLength(dist), + r = bodyA.boundingRadius + bodyB.boundingRadius; + return d2 <= r*r; +}; + +/** + * Check whether the bounding radius of two bodies overlap. + * @method boundingRadiusCheck + * @param {Body} bodyA + * @param {Body} bodyB + * @return {Boolean} + */ +Broadphase.aabbCheck = function(bodyA, bodyB){ + if(bodyA.aabbNeedsUpdate){ + bodyA.updateAABB(); + } + if(bodyB.aabbNeedsUpdate){ + bodyB.updateAABB(); + } + return bodyA.aabb.overlaps(bodyB.aabb); +}; + +/** + * Check whether the bounding radius of two bodies overlap. + * @method boundingRadiusCheck + * @param {Body} bodyA + * @param {Body} bodyB + * @return {Boolean} + */ +Broadphase.prototype.boundingVolumeCheck = function(bodyA, bodyB){ + var result; + + switch(this.boundingVolumeType){ + case Broadphase.BOUNDING_CIRCLE: + result = Broadphase.boundingRadiusCheck(bodyA,bodyB); + break; + case Broadphase.AABB: + result = Broadphase.aabbCheck(bodyA,bodyB); + break; + default: + throw new Error('Bounding volume type not recognized: '+this.boundingVolumeType); + } + return result; +}; + +/** + * Check whether two bodies are allowed to collide at all. + * @method canCollide + * @param {Body} bodyA + * @param {Body} bodyB + * @return {Boolean} + */ +Broadphase.canCollide = function(bodyA, bodyB){ + + // Cannot collide static bodies + if(bodyA.type === Body.STATIC && bodyB.type === Body.STATIC){ + return false; + } + + // Cannot collide static vs kinematic bodies + if( (bodyA.type === Body.KINEMATIC && bodyB.type === Body.STATIC) || + (bodyA.type === Body.STATIC && bodyB.type === Body.KINEMATIC)){ + return false; + } + + // Cannot collide kinematic vs kinematic + if(bodyA.type === Body.KINEMATIC && bodyB.type === Body.KINEMATIC){ + return false; + } + + // Cannot collide both sleeping bodies + if(bodyA.sleepState === Body.SLEEPING && bodyB.sleepState === Body.SLEEPING){ + return false; + } + + // Cannot collide if one is static and the other is sleeping + if( (bodyA.sleepState === Body.SLEEPING && bodyB.type === Body.STATIC) || + (bodyB.sleepState === Body.SLEEPING && bodyA.type === Body.STATIC)){ + return false; + } + + return true; +}; + +Broadphase.NAIVE = 1; +Broadphase.SAP = 2; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/collision/Broadphase.js","/collision") +},{"../math/vec2":33,"../objects/Body":34,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],13:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Circle = _dereq_('../shapes/Circle') +, Plane = _dereq_('../shapes/Plane') +, Particle = _dereq_('../shapes/Particle') +, Broadphase = _dereq_('../collision/Broadphase') +, vec2 = _dereq_('../math/vec2') +, Utils = _dereq_('../utils/Utils'); + +module.exports = GridBroadphase; + +/** + * Broadphase that uses axis-aligned bins. + * @class GridBroadphase + * @constructor + * @extends Broadphase + * @param {object} [options] + * @param {number} [options.xmin] Lower x bound of the grid + * @param {number} [options.xmax] Upper x bound + * @param {number} [options.ymin] Lower y bound + * @param {number} [options.ymax] Upper y bound + * @param {number} [options.nx] Number of bins along x axis + * @param {number} [options.ny] Number of bins along y axis + * @todo Should have an option for dynamic scene size + */ +function GridBroadphase(options){ + Broadphase.apply(this); + + options = Utils.defaults(options,{ + xmin: -100, + xmax: 100, + ymin: -100, + ymax: 100, + nx: 10, + ny: 10 + }); + + this.xmin = options.xmin; + this.ymin = options.ymin; + this.xmax = options.xmax; + this.ymax = options.ymax; + this.nx = options.nx; + this.ny = options.ny; + + this.binsizeX = (this.xmax-this.xmin) / this.nx; + this.binsizeY = (this.ymax-this.ymin) / this.ny; +} +GridBroadphase.prototype = new Broadphase(); + +/** + * Get collision pairs. + * @method getCollisionPairs + * @param {World} world + * @return {Array} + */ +GridBroadphase.prototype.getCollisionPairs = function(world){ + var result = [], + bodies = world.bodies, + Ncolliding = bodies.length, + binsizeX = this.binsizeX, + binsizeY = this.binsizeY, + nx = this.nx, + ny = this.ny, + xmin = this.xmin, + ymin = this.ymin, + xmax = this.xmax, + ymax = this.ymax; + + // Todo: make garbage free + var bins=[], Nbins=nx*ny; + for(var i=0; i= 0 && idx < Nbins){ + bins[ idx ].push(bi); + } + } + } + } + + // Check each bin + for(var i=0; i!==Nbins; i++){ + var bin = bins[i]; + + for(var j=0, NbodiesInBin=bin.length; j!==NbodiesInBin; j++){ + var bi = bin[j]; + for(var k=0; k!==j; k++){ + var bj = bin[k]; + if(Broadphase.canCollide(bi,bj) && this.boundingVolumeCheck(bi,bj)){ + result.push(bi,bj); + } + } + } + } + return result; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/collision/GridBroadphase.js","/collision") +},{"../collision/Broadphase":12,"../math/vec2":33,"../shapes/Circle":40,"../shapes/Particle":44,"../shapes/Plane":45,"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],14:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Circle = _dereq_('../shapes/Circle'), + Plane = _dereq_('../shapes/Plane'), + Shape = _dereq_('../shapes/Shape'), + Particle = _dereq_('../shapes/Particle'), + Broadphase = _dereq_('../collision/Broadphase'), + vec2 = _dereq_('../math/vec2'); + +module.exports = NaiveBroadphase; + +/** + * Naive broadphase implementation. Does N^2 tests. + * + * @class NaiveBroadphase + * @constructor + * @extends Broadphase + */ +function NaiveBroadphase(){ + Broadphase.call(this, Broadphase.NAIVE); +} +NaiveBroadphase.prototype = new Broadphase(); + +/** + * Get the colliding pairs + * @method getCollisionPairs + * @param {World} world + * @return {Array} + */ +NaiveBroadphase.prototype.getCollisionPairs = function(world){ + var bodies = world.bodies, + result = this.result; + + result.length = 0; + + for(var i=0, Ncolliding=bodies.length; i!==Ncolliding; i++){ + var bi = bodies[i]; + + for(var j=0; j pos0 && pos < pos1){ + // We got contact! + + if(justTest){ + return true; + } + + var c = this.createContactEquation(circleBody,lineBody,circleShape,lineShape); + + vec2.scale(c.normalA, orthoDist, -1); + vec2.normalize(c.normalA, c.normalA); + + vec2.scale( c.contactPointA, c.normalA, circleRadius); + add(c.contactPointA, c.contactPointA, circleOffset); + sub(c.contactPointA, c.contactPointA, circleBody.position); + + sub(c.contactPointB, projectedPoint, lineOffset); + add(c.contactPointB, c.contactPointB, lineOffset); + sub(c.contactPointB, c.contactPointB, lineBody.position); + + this.contactEquations.push(c); + + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + + return 1; + } + } + + // Add corner + verts[0] = worldVertex0; + verts[1] = worldVertex1; + + for(var i=0; i 0){ + for(var i=0; i Math.pow(r,2)){ + return 0; + } + + if(justTest){ + return true; + } + + var c = this.createContactEquation(bodyA,bodyB,shapeA,shapeB); + sub(c.normalA, offsetB, offsetA); + vec2.normalize(c.normalA,c.normalA); + + vec2.scale( c.contactPointA, c.normalA, radiusA); + vec2.scale( c.contactPointB, c.normalA, -radiusB); + + add(c.contactPointA, c.contactPointA, offsetA); + sub(c.contactPointA, c.contactPointA, bodyA.position); + + add(c.contactPointB, c.contactPointB, offsetB); + sub(c.contactPointB, c.contactPointB, bodyB.position); + + this.contactEquations.push(c); + + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + return 1; +}; + +/** + * Plane/Convex Narrowphase + * @method planeConvex + * @param {Body} planeBody + * @param {Plane} planeShape + * @param {Array} planeOffset + * @param {Number} planeAngle + * @param {Body} convexBody + * @param {Convex} convexShape + * @param {Array} convexOffset + * @param {Number} convexAngle + * @param {Boolean} justTest + */ +Narrowphase.prototype[Shape.PLANE | Shape.CONVEX] = +Narrowphase.prototype[Shape.PLANE | Shape.RECTANGLE] = +Narrowphase.prototype.planeConvex = function( + planeBody, + planeShape, + planeOffset, + planeAngle, + convexBody, + convexShape, + convexOffset, + convexAngle, + justTest +){ + var worldVertex = tmp1, + worldNormal = tmp2, + dist = tmp3; + + var numReported = 0; + vec2.rotate(worldNormal, yAxis, planeAngle); + + for(var i=0; i!==convexShape.vertices.length; i++){ + var v = convexShape.vertices[i]; + vec2.rotate(worldVertex, v, convexAngle); + add(worldVertex, worldVertex, convexOffset); + + sub(dist, worldVertex, planeOffset); + + if(dot(dist,worldNormal) <= 0){ + + if(justTest){ + return true; + } + + // Found vertex + numReported++; + + var c = this.createContactEquation(planeBody,convexBody,planeShape,convexShape); + + sub(dist, worldVertex, planeOffset); + + vec2.copy(c.normalA, worldNormal); + + var d = dot(dist, c.normalA); + vec2.scale(dist, c.normalA, d); + + // rj is from convex center to contact + sub(c.contactPointB, worldVertex, convexBody.position); + + + // ri is from plane center to contact + sub( c.contactPointA, worldVertex, dist); + sub( c.contactPointA, c.contactPointA, planeBody.position); + + this.contactEquations.push(c); + + if(!this.enableFrictionReduction){ + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + } + } + } + + if(this.enableFrictionReduction){ + if(this.enableFriction && numReported){ + this.frictionEquations.push(this.createFrictionFromAverage(numReported)); + } + } + + return numReported; +}; + +/** + * Narrowphase for particle vs plane + * @method particlePlane + * @param {Body} particleBody + * @param {Particle} particleShape + * @param {Array} particleOffset + * @param {Number} particleAngle + * @param {Body} planeBody + * @param {Plane} planeShape + * @param {Array} planeOffset + * @param {Number} planeAngle + * @param {Boolean} justTest + */ +Narrowphase.prototype[Shape.PARTICLE | Shape.PLANE] = +Narrowphase.prototype.particlePlane = function( + particleBody, + particleShape, + particleOffset, + particleAngle, + planeBody, + planeShape, + planeOffset, + planeAngle, + justTest +){ + var dist = tmp1, + worldNormal = tmp2; + + planeAngle = planeAngle || 0; + + sub(dist, particleOffset, planeOffset); + vec2.rotate(worldNormal, yAxis, planeAngle); + + var d = dot(dist, worldNormal); + + if(d > 0){ + return 0; + } + if(justTest){ + return true; + } + + var c = this.createContactEquation(planeBody,particleBody,planeShape,particleShape); + + vec2.copy(c.normalA, worldNormal); + vec2.scale( dist, c.normalA, d ); + // dist is now the distance vector in the normal direction + + // ri is the particle position projected down onto the plane, from the plane center + sub( c.contactPointA, particleOffset, dist); + sub( c.contactPointA, c.contactPointA, planeBody.position); + + // rj is from the body center to the particle center + sub( c.contactPointB, particleOffset, particleBody.position ); + + this.contactEquations.push(c); + + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + return 1; +}; + +/** + * Circle/Particle Narrowphase + * @method circleParticle + * @param {Body} circleBody + * @param {Circle} circleShape + * @param {Array} circleOffset + * @param {Number} circleAngle + * @param {Body} particleBody + * @param {Particle} particleShape + * @param {Array} particleOffset + * @param {Number} particleAngle + * @param {Boolean} justTest + */ +Narrowphase.prototype[Shape.CIRCLE | Shape.PARTICLE] = +Narrowphase.prototype.circleParticle = function( + circleBody, + circleShape, + circleOffset, + circleAngle, + particleBody, + particleShape, + particleOffset, + particleAngle, + justTest +){ + var dist = tmp1; + + sub(dist, particleOffset, circleOffset); + if(vec2.squaredLength(dist) > Math.pow(circleShape.radius, 2)){ + return 0; + } + if(justTest){ + return true; + } + + var c = this.createContactEquation(circleBody,particleBody,circleShape,particleShape); + vec2.copy(c.normalA, dist); + vec2.normalize(c.normalA,c.normalA); + + // Vector from circle to contact point is the normal times the circle radius + vec2.scale(c.contactPointA, c.normalA, circleShape.radius); + add(c.contactPointA, c.contactPointA, circleOffset); + sub(c.contactPointA, c.contactPointA, circleBody.position); + + // Vector from particle center to contact point is zero + sub(c.contactPointB, particleOffset, particleBody.position); + + this.contactEquations.push(c); + + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + + return 1; +}; + +var planeCapsule_tmpCircle = new Circle(1), + planeCapsule_tmp1 = vec2.create(), + planeCapsule_tmp2 = vec2.create(), + planeCapsule_tmp3 = vec2.create(); + +/** + * @method planeCapsule + * @param {Body} planeBody + * @param {Circle} planeShape + * @param {Array} planeOffset + * @param {Number} planeAngle + * @param {Body} capsuleBody + * @param {Particle} capsuleShape + * @param {Array} capsuleOffset + * @param {Number} capsuleAngle + * @param {Boolean} justTest + */ +Narrowphase.prototype[Shape.PLANE | Shape.CAPSULE] = +Narrowphase.prototype.planeCapsule = function( + planeBody, + planeShape, + planeOffset, + planeAngle, + capsuleBody, + capsuleShape, + capsuleOffset, + capsuleAngle, + justTest +){ + var end1 = planeCapsule_tmp1, + end2 = planeCapsule_tmp2, + circle = planeCapsule_tmpCircle, + dst = planeCapsule_tmp3; + + // Compute world end positions + vec2.set(end1, -capsuleShape.length/2, 0); + vec2.rotate(end1,end1,capsuleAngle); + add(end1,end1,capsuleOffset); + + vec2.set(end2, capsuleShape.length/2, 0); + vec2.rotate(end2,end2,capsuleAngle); + add(end2,end2,capsuleOffset); + + circle.radius = capsuleShape.radius; + + var enableFrictionBefore; + + // Temporarily turn off friction + if(this.enableFrictionReduction){ + enableFrictionBefore = this.enableFriction; + this.enableFriction = false; + } + + // Do Narrowphase as two circles + var numContacts1 = this.circlePlane(capsuleBody,circle,end1,0, planeBody,planeShape,planeOffset,planeAngle, justTest), + numContacts2 = this.circlePlane(capsuleBody,circle,end2,0, planeBody,planeShape,planeOffset,planeAngle, justTest); + + // Restore friction + if(this.enableFrictionReduction){ + this.enableFriction = enableFrictionBefore; + } + + if(justTest){ + return numContacts1 || numContacts2; + } else { + var numTotal = numContacts1 + numContacts2; + if(this.enableFrictionReduction){ + if(numTotal){ + this.frictionEquations.push(this.createFrictionFromAverage(numTotal)); + } + } + return numTotal; + } +}; + +/** + * Creates ContactEquations and FrictionEquations for a collision. + * @method circlePlane + * @param {Body} bi The first body that should be connected to the equations. + * @param {Circle} si The circle shape participating in the collision. + * @param {Array} xi Extra offset to take into account for the Shape, in addition to the one in circleBody.position. Will *not* be rotated by circleBody.angle (maybe it should, for sake of homogenity?). Set to null if none. + * @param {Body} bj The second body that should be connected to the equations. + * @param {Plane} sj The Plane shape that is participating + * @param {Array} xj Extra offset for the plane shape. + * @param {Number} aj Extra angle to apply to the plane + */ +Narrowphase.prototype[Shape.CIRCLE | Shape.PLANE] = +Narrowphase.prototype.circlePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){ + var circleBody = bi, + circleShape = si, + circleOffset = xi, // Offset from body center, rotated! + planeBody = bj, + shapeB = sj, + planeOffset = xj, + planeAngle = aj; + + planeAngle = planeAngle || 0; + + // Vector from plane to circle + var planeToCircle = tmp1, + worldNormal = tmp2, + temp = tmp3; + + sub(planeToCircle, circleOffset, planeOffset); + + // World plane normal + vec2.rotate(worldNormal, yAxis, planeAngle); + + // Normal direction distance + var d = dot(worldNormal, planeToCircle); + + if(d > circleShape.radius){ + return 0; // No overlap. Abort. + } + + if(justTest){ + return true; + } + + // Create contact + var contact = this.createContactEquation(planeBody,circleBody,sj,si); + + // ni is the plane world normal + vec2.copy(contact.normalA, worldNormal); + + // rj is the vector from circle center to the contact point + vec2.scale(contact.contactPointB, contact.normalA, -circleShape.radius); + add(contact.contactPointB, contact.contactPointB, circleOffset); + sub(contact.contactPointB, contact.contactPointB, circleBody.position); + + // ri is the distance from plane center to contact. + vec2.scale(temp, contact.normalA, d); + sub(contact.contactPointA, planeToCircle, temp ); // Subtract normal distance vector from the distance vector + add(contact.contactPointA, contact.contactPointA, planeOffset); + sub(contact.contactPointA, contact.contactPointA, planeBody.position); + + this.contactEquations.push(contact); + + if(this.enableFriction){ + this.frictionEquations.push( this.createFrictionFromContact(contact) ); + } + + return 1; +}; + +/** + * Convex/convex Narrowphase.See this article for more info. + * @method convexConvex + * @param {Body} bi + * @param {Convex} si + * @param {Array} xi + * @param {Number} ai + * @param {Body} bj + * @param {Convex} sj + * @param {Array} xj + * @param {Number} aj + */ +Narrowphase.prototype[Shape.CONVEX] = +Narrowphase.prototype[Shape.CONVEX | Shape.RECTANGLE] = +Narrowphase.prototype[Shape.RECTANGLE] = +Narrowphase.prototype.convexConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTest, precision ){ + var sepAxis = tmp1, + worldPoint = tmp2, + worldPoint0 = tmp3, + worldPoint1 = tmp4, + worldEdge = tmp5, + projected = tmp6, + penetrationVec = tmp7, + dist = tmp8, + worldNormal = tmp9, + numContacts = 0, + precision = typeof(precision) === 'number' ? precision : 0; + + var found = Narrowphase.findSeparatingAxis(si,xi,ai,sj,xj,aj,sepAxis); + if(!found){ + return 0; + } + + // Make sure the separating axis is directed from shape i to shape j + sub(dist,xj,xi); + if(dot(sepAxis,dist) > 0){ + vec2.scale(sepAxis,sepAxis,-1); + } + + // Find edges with normals closest to the separating axis + var closestEdge1 = Narrowphase.getClosestEdge(si,ai,sepAxis,true), // Flipped axis + closestEdge2 = Narrowphase.getClosestEdge(sj,aj,sepAxis); + + if(closestEdge1 === -1 || closestEdge2 === -1){ + return 0; + } + + // Loop over the shapes + for(var k=0; k<2; k++){ + + var closestEdgeA = closestEdge1, + closestEdgeB = closestEdge2, + shapeA = si, shapeB = sj, + offsetA = xi, offsetB = xj, + angleA = ai, angleB = aj, + bodyA = bi, bodyB = bj; + + if(k === 0){ + // Swap! + var tmp; + tmp = closestEdgeA; + closestEdgeA = closestEdgeB; + closestEdgeB = tmp; + + tmp = shapeA; + shapeA = shapeB; + shapeB = tmp; + + tmp = offsetA; + offsetA = offsetB; + offsetB = tmp; + + tmp = angleA; + angleA = angleB; + angleB = tmp; + + tmp = bodyA; + bodyA = bodyB; + bodyB = tmp; + } + + // Loop over 2 points in convex B + for(var j=closestEdgeB; j= 3){ + + if(justTest){ + return true; + } + + // worldPoint was on the "inside" side of each of the 3 checked edges. + // Project it to the center edge and use the projection direction as normal + + // Create contact + var c = this.createContactEquation(bodyA,bodyB,shapeA,shapeB); + numContacts++; + + // Get center edge from body A + var v0 = shapeA.vertices[(closestEdgeA) % shapeA.vertices.length], + v1 = shapeA.vertices[(closestEdgeA+1) % shapeA.vertices.length]; + + // Construct the edge + vec2.rotate(worldPoint0, v0, angleA); + vec2.rotate(worldPoint1, v1, angleA); + add(worldPoint0, worldPoint0, offsetA); + add(worldPoint1, worldPoint1, offsetA); + + sub(worldEdge, worldPoint1, worldPoint0); + + vec2.rotate90cw(c.normalA, worldEdge); // Normal points out of convex A + vec2.normalize(c.normalA,c.normalA); + + sub(dist, worldPoint, worldPoint0); // From edge point to the penetrating point + var d = dot(c.normalA,dist); // Penetration + vec2.scale(penetrationVec, c.normalA, d); // Vector penetration + + sub(c.contactPointA, worldPoint, offsetA); + sub(c.contactPointA, c.contactPointA, penetrationVec); + add(c.contactPointA, c.contactPointA, offsetA); + sub(c.contactPointA, c.contactPointA, bodyA.position); + + sub(c.contactPointB, worldPoint, offsetB); + add(c.contactPointB, c.contactPointB, offsetB); + sub(c.contactPointB, c.contactPointB, bodyB.position); + + this.contactEquations.push(c); + + // Todo reduce to 1 friction equation if we have 2 contact points + if(!this.enableFrictionReduction){ + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + } + } + } + } + + if(this.enableFrictionReduction){ + if(this.enableFriction && numContacts){ + this.frictionEquations.push(this.createFrictionFromAverage(numContacts)); + } + } + + return numContacts; +}; + +// .projectConvex is called by other functions, need local tmp vectors +var pcoa_tmp1 = vec2.fromValues(0,0); + +/** + * Project a Convex onto a world-oriented axis + * @method projectConvexOntoAxis + * @static + * @param {Convex} convexShape + * @param {Array} convexOffset + * @param {Number} convexAngle + * @param {Array} worldAxis + * @param {Array} result + */ +Narrowphase.projectConvexOntoAxis = function(convexShape, convexOffset, convexAngle, worldAxis, result){ + var max=null, + min=null, + v, + value, + localAxis = pcoa_tmp1; + + // Convert the axis to local coords of the body + vec2.rotate(localAxis, worldAxis, -convexAngle); + + // Get projected position of all vertices + for(var i=0; i max){ + max = value; + } + if(min === null || value < min){ + min = value; + } + } + + if(min > max){ + var t = min; + min = max; + max = t; + } + + // Project the position of the body onto the axis - need to add this to the result + var offset = dot(convexOffset, worldAxis); + + vec2.set( result, min + offset, max + offset); +}; + +// .findSeparatingAxis is called by other functions, need local tmp vectors +var fsa_tmp1 = vec2.fromValues(0,0) +, fsa_tmp2 = vec2.fromValues(0,0) +, fsa_tmp3 = vec2.fromValues(0,0) +, fsa_tmp4 = vec2.fromValues(0,0) +, fsa_tmp5 = vec2.fromValues(0,0) +, fsa_tmp6 = vec2.fromValues(0,0); + +/** + * Find a separating axis between the shapes, that maximizes the separating distance between them. + * @method findSeparatingAxis + * @static + * @param {Convex} c1 + * @param {Array} offset1 + * @param {Number} angle1 + * @param {Convex} c2 + * @param {Array} offset2 + * @param {Number} angle2 + * @param {Array} sepAxis The resulting axis + * @return {Boolean} Whether the axis could be found. + */ +Narrowphase.findSeparatingAxis = function(c1,offset1,angle1,c2,offset2,angle2,sepAxis){ + var maxDist = null, + overlap = false, + found = false, + edge = fsa_tmp1, + worldPoint0 = fsa_tmp2, + worldPoint1 = fsa_tmp3, + normal = fsa_tmp4, + span1 = fsa_tmp5, + span2 = fsa_tmp6; + + if(c1 instanceof Rectangle && c2 instanceof Rectangle){ + + for(var j=0; j!==2; j++){ + var c = c1, + angle = angle1; + if(j===1){ + c = c2; + angle = angle2; + } + + for(var i=0; i!==2; i++){ + + // Get the world edge + if(i === 0){ + vec2.set(normal, 0, 1); + } else if(i === 1) { + vec2.set(normal, 1, 0); + } + if(angle !== 0){ + vec2.rotate(normal, normal, angle); + } + + // Project hulls onto that normal + Narrowphase.projectConvexOntoAxis(c1,offset1,angle1,normal,span1); + Narrowphase.projectConvexOntoAxis(c2,offset2,angle2,normal,span2); + + // Order by span position + var a=span1, + b=span2, + swapped = false; + if(span1[0] > span2[0]){ + b=span1; + a=span2; + swapped = true; + } + + // Get separating distance + var dist = b[0] - a[1]; + overlap = (dist <= 0); + + if(maxDist===null || dist > maxDist){ + vec2.copy(sepAxis, normal); + maxDist = dist; + found = overlap; + } + } + } + + } else { + + for(var j=0; j!==2; j++){ + var c = c1, + angle = angle1; + if(j===1){ + c = c2; + angle = angle2; + } + + for(var i=0; i!==c.vertices.length; i++){ + // Get the world edge + vec2.rotate(worldPoint0, c.vertices[i], angle); + vec2.rotate(worldPoint1, c.vertices[(i+1)%c.vertices.length], angle); + + sub(edge, worldPoint1, worldPoint0); + + // Get normal - just rotate 90 degrees since vertices are given in CCW + vec2.rotate90cw(normal, edge); + vec2.normalize(normal,normal); + + // Project hulls onto that normal + Narrowphase.projectConvexOntoAxis(c1,offset1,angle1,normal,span1); + Narrowphase.projectConvexOntoAxis(c2,offset2,angle2,normal,span2); + + // Order by span position + var a=span1, + b=span2, + swapped = false; + if(span1[0] > span2[0]){ + b=span1; + a=span2; + swapped = true; + } + + // Get separating distance + var dist = b[0] - a[1]; + overlap = (dist <= 0); + + if(maxDist===null || dist > maxDist){ + vec2.copy(sepAxis, normal); + maxDist = dist; + found = overlap; + } + } + } + } + + + /* + // Needs to be tested some more + for(var j=0; j!==2; j++){ + var c = c1, + angle = angle1; + if(j===1){ + c = c2; + angle = angle2; + } + + for(var i=0; i!==c.axes.length; i++){ + + var normal = c.axes[i]; + + // Project hulls onto that normal + Narrowphase.projectConvexOntoAxis(c1, offset1, angle1, normal, span1); + Narrowphase.projectConvexOntoAxis(c2, offset2, angle2, normal, span2); + + // Order by span position + var a=span1, + b=span2, + swapped = false; + if(span1[0] > span2[0]){ + b=span1; + a=span2; + swapped = true; + } + + // Get separating distance + var dist = b[0] - a[1]; + overlap = (dist <= Narrowphase.convexPrecision); + + if(maxDist===null || dist > maxDist){ + vec2.copy(sepAxis, normal); + maxDist = dist; + found = overlap; + } + } + } + */ + + return found; +}; + +// .getClosestEdge is called by other functions, need local tmp vectors +var gce_tmp1 = vec2.fromValues(0,0) +, gce_tmp2 = vec2.fromValues(0,0) +, gce_tmp3 = vec2.fromValues(0,0); + +/** + * Get the edge that has a normal closest to an axis. + * @method getClosestEdge + * @static + * @param {Convex} c + * @param {Number} angle + * @param {Array} axis + * @param {Boolean} flip + * @return {Number} Index of the edge that is closest. This index and the next spans the resulting edge. Returns -1 if failed. + */ +Narrowphase.getClosestEdge = function(c,angle,axis,flip){ + var localAxis = gce_tmp1, + edge = gce_tmp2, + normal = gce_tmp3; + + // Convert the axis to local coords of the body + vec2.rotate(localAxis, axis, -angle); + if(flip){ + vec2.scale(localAxis,localAxis,-1); + } + + var closestEdge = -1, + N = c.vertices.length, + maxDot = -1; + for(var i=0; i!==N; i++){ + // Get the edge + sub(edge, c.vertices[(i+1)%N], c.vertices[i%N]); + + // Get normal - just rotate 90 degrees since vertices are given in CCW + vec2.rotate90cw(normal, edge); + vec2.normalize(normal,normal); + + var d = dot(normal,localAxis); + if(closestEdge === -1 || d > maxDot){ + closestEdge = i % N; + maxDot = d; + } + } + + return closestEdge; +}; + +var circleHeightfield_candidate = vec2.create(), + circleHeightfield_dist = vec2.create(), + circleHeightfield_v0 = vec2.create(), + circleHeightfield_v1 = vec2.create(), + circleHeightfield_minCandidate = vec2.create(), + circleHeightfield_worldNormal = vec2.create(), + circleHeightfield_minCandidateNormal = vec2.create(); + +/** + * @method circleHeightfield + * @param {Body} bi + * @param {Circle} si + * @param {Array} xi + * @param {Body} bj + * @param {Heightfield} sj + * @param {Array} xj + * @param {Number} aj + */ +Narrowphase.prototype[Shape.CIRCLE | Shape.HEIGHTFIELD] = +Narrowphase.prototype.circleHeightfield = function( circleBody,circleShape,circlePos,circleAngle, + hfBody,hfShape,hfPos,hfAngle, justTest, radius ){ + var data = hfShape.data, + radius = radius || circleShape.radius, + w = hfShape.elementWidth, + dist = circleHeightfield_dist, + candidate = circleHeightfield_candidate, + minCandidate = circleHeightfield_minCandidate, + minCandidateNormal = circleHeightfield_minCandidateNormal, + worldNormal = circleHeightfield_worldNormal, + v0 = circleHeightfield_v0, + v1 = circleHeightfield_v1; + + // Get the index of the points to test against + var idxA = Math.floor( (circlePos[0] - radius - hfPos[0]) / w ), + idxB = Math.ceil( (circlePos[0] + radius - hfPos[0]) / w ); + + /*if(idxB < 0 || idxA >= data.length) + return justTest ? false : 0;*/ + + if(idxA < 0){ + idxA = 0; + } + if(idxB >= data.length){ + idxB = data.length-1; + } + + // Get max and min + var max = data[idxA], + min = data[idxB]; + for(var i=idxA; i max){ + max = data[i]; + } + } + + if(circlePos[1]-radius > max){ + return justTest ? false : 0; + } + + if(circlePos[1]+radius < min){ + // Below the minimum point... We can just guess. + // TODO + } + + // 1. Check so center of circle is not inside the field. If it is, this wont work... + // 2. For each edge + // 2. 1. Get point on circle that is closest to the edge (scale normal with -radius) + // 2. 2. Check if point is inside. + + var found = false; + + // Check all edges first + for(var i=idxA; i= v0[0] && candidate[0] < v1[0] && d <= 0){ + + if(justTest){ + return true; + } + + found = true; + + // Store the candidate point, projected to the edge + vec2.scale(dist,worldNormal,-d); + vec2.add(minCandidate,candidate,dist); + vec2.copy(minCandidateNormal,worldNormal); + + var c = this.createContactEquation(hfBody,circleBody,hfShape,circleShape); + + // Normal is out of the heightfield + vec2.copy(c.normalA, minCandidateNormal); + + // Vector from circle to heightfield + vec2.scale(c.contactPointB, c.normalA, -radius); + add(c.contactPointB, c.contactPointB, circlePos); + sub(c.contactPointB, c.contactPointB, circleBody.position); + + vec2.copy(c.contactPointA, minCandidate); + vec2.sub(c.contactPointA, c.contactPointA, hfBody.position); + + this.contactEquations.push(c); + + if(this.enableFriction){ + this.frictionEquations.push( this.createFrictionFromContact(c) ); + } + } + } + + // Check all vertices + found = false; + if(radius > 0){ + for(var i=idxA; i<=idxB; i++){ + + // Get point + vec2.set(v0, i*w, data[i]); + vec2.add(v0,v0,hfPos); + + vec2.sub(dist, circlePos, v0); + + if(vec2.squaredLength(dist) < Math.pow(radius, 2)){ + + if(justTest){ + return true; + } + + found = true; + + var c = this.createContactEquation(hfBody,circleBody,hfShape,circleShape); + + // Construct normal - out of heightfield + vec2.copy(c.normalA, dist); + vec2.normalize(c.normalA,c.normalA); + + vec2.scale(c.contactPointB, c.normalA, -radius); + add(c.contactPointB, c.contactPointB, circlePos); + sub(c.contactPointB, c.contactPointB, circleBody.position); + + sub(c.contactPointA, v0, hfPos); + add(c.contactPointA, c.contactPointA, hfPos); + sub(c.contactPointA, c.contactPointA, hfBody.position); + + this.contactEquations.push(c); + + if(this.enableFriction){ + this.frictionEquations.push(this.createFrictionFromContact(c)); + } + } + } + } + + if(found){ + return 1; + } + + return 0; + +}; + +var convexHeightfield_v0 = vec2.create(), + convexHeightfield_v1 = vec2.create(), + convexHeightfield_tilePos = vec2.create(), + convexHeightfield_tempConvexShape = new Convex([vec2.create(),vec2.create(),vec2.create(),vec2.create()]); +/** + * @method circleHeightfield + * @param {Body} bi + * @param {Circle} si + * @param {Array} xi + * @param {Body} bj + * @param {Heightfield} sj + * @param {Array} xj + * @param {Number} aj + */ +Narrowphase.prototype[Shape.RECTANGLE | Shape.HEIGHTFIELD] = +Narrowphase.prototype[Shape.CONVEX | Shape.HEIGHTFIELD] = +Narrowphase.prototype.convexHeightfield = function( convexBody,convexShape,convexPos,convexAngle, + hfBody,hfShape,hfPos,hfAngle, justTest ){ + var data = hfShape.data, + w = hfShape.elementWidth, + v0 = convexHeightfield_v0, + v1 = convexHeightfield_v1, + tilePos = convexHeightfield_tilePos, + tileConvex = convexHeightfield_tempConvexShape; + + // Get the index of the points to test against + var idxA = Math.floor( (convexBody.aabb.lowerBound[0] - hfPos[0]) / w ), + idxB = Math.ceil( (convexBody.aabb.upperBound[0] - hfPos[0]) / w ); + + if(idxA < 0){ + idxA = 0; + } + if(idxB >= data.length){ + idxB = data.length-1; + } + + // Get max and min + var max = data[idxA], + min = data[idxB]; + for(var i=idxA; i max){ + max = data[i]; + } + } + + if(convexBody.aabb.lowerBound[1] > max){ + return justTest ? false : 0; + } + + var found = false; + var numContacts = 0; + + // Loop over all edges + // TODO: If possible, construct a convex from several data points (need o check if the points make a convex shape) + for(var i=idxA; i=0;j--) { + if(a[j].aabb.lowerBound[axisIndex] <= v.aabb.lowerBound[axisIndex]){ + break; + } + a[j+1] = a[j]; + } + a[j+1] = v; + } + return a; +}; + +/** + * Get the colliding pairs + * @method getCollisionPairs + * @param {World} world + * @return {Array} + */ +SAPBroadphase.prototype.getCollisionPairs = function(world){ + var bodies = this.axisList, + result = this.result, + axisIndex = this.axisIndex; + + result.length = 0; + + // Update all AABBs if needed + var l = bodies.length; + while(l--){ + var b = bodies[l]; + if(b.aabbNeedsUpdate){ + b.updateAABB(); + } + } + + // Sort the lists + SAPBroadphase.sortAxisList(bodies, axisIndex); + + // Look through the X list + for(var i=0, N=bodies.length|0; i!==N; i++){ + var bi = bodies[i]; + + for(var j=i+1; j G = [-n -rixn n rjxn] + + var r = vec2.create(); + var ri = vec2.create(); // worldAnchorA + var rj = vec2.create(); // worldAnchorB + var that = this; + normal.computeGq = function(){ + var bodyA = this.bodyA, + bodyB = this.bodyB, + xi = bodyA.position, + xj = bodyB.position; + + // Transform local anchors to world + vec2.rotate(ri, localAnchorA, bodyA.angle); + vec2.rotate(rj, localAnchorB, bodyB.angle); + + vec2.add(r, xj, rj); + vec2.sub(r, r, ri); + vec2.sub(r, r, xi); + + //vec2.sub(r, bodyB.position, bodyA.position); + return vec2.length(r) - that.distance; + }; + + // Make the contact constraint bilateral + this.setMaxForce(maxForce); + + /** + * If the upper limit is enabled or not. + * @property {Boolean} upperLimitEnabled + */ + this.upperLimitEnabled = false; + + /** + * The upper constraint limit. + * @property {number} upperLimit + */ + this.upperLimit = 1; + + /** + * If the lower limit is enabled or not. + * @property {Boolean} lowerLimitEnabled + */ + this.lowerLimitEnabled = false; + + /** + * The lower constraint limit. + * @property {number} lowerLimit + */ + this.lowerLimit = 0; + + /** + * Current constraint position. This is equal to the current distance between the world anchor points. + * @property {number} position + */ + this.position = 0; +} +DistanceConstraint.prototype = new Constraint(); + +/** + * Update the constraint equations. Should be done if any of the bodies changed position, before solving. + * @method update + */ +var n = vec2.create(); +var ri = vec2.create(); // worldAnchorA +var rj = vec2.create(); // worldAnchorB +DistanceConstraint.prototype.update = function(){ + var normal = this.equations[0], + bodyA = this.bodyA, + bodyB = this.bodyB, + distance = this.distance, + xi = bodyA.position, + xj = bodyB.position, + normalEquation = this.equations[0], + G = normal.G; + + // Transform local anchors to world + vec2.rotate(ri, this.localAnchorA, bodyA.angle); + vec2.rotate(rj, this.localAnchorB, bodyB.angle); + + // Get world anchor points and normal + vec2.add(n, xj, rj); + vec2.sub(n, n, ri); + vec2.sub(n, n, xi); + this.position = vec2.length(n); + + var violating = false; + if(this.upperLimitEnabled){ + if(this.position > this.upperLimit){ + normalEquation.maxForce = 0; + normalEquation.minForce = -this.maxForce; + this.distance = this.upperLimit; + violating = true; + } + } + + if(this.lowerLimitEnabled){ + if(this.position < this.lowerLimit){ + normalEquation.maxForce = this.maxForce; + normalEquation.minForce = 0; + this.distance = this.lowerLimit; + violating = true; + } + } + + if((this.lowerLimitEnabled || this.upperLimitEnabled) && !violating){ + // No constraint needed. + normalEquation.enabled = false; + return; + } + + normalEquation.enabled = true; + + vec2.normalize(n,n); + + // Caluclate cross products + var rixn = vec2.crossLength(ri, n), + rjxn = vec2.crossLength(rj, n); + + // G = [-n -rixn n rjxn] + G[0] = -n[0]; + G[1] = -n[1]; + G[2] = -rixn; + G[3] = n[0]; + G[4] = n[1]; + G[5] = rjxn; +}; + +/** + * Set the max force to be used + * @method setMaxForce + * @param {Number} f + */ +DistanceConstraint.prototype.setMaxForce = function(f){ + var normal = this.equations[0]; + normal.minForce = -f; + normal.maxForce = f; +}; + +/** + * Get the max force + * @method getMaxForce + * @return {Number} + */ +DistanceConstraint.prototype.getMaxForce = function(f){ + var normal = this.equations[0]; + return normal.maxForce; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/constraints/DistanceConstraint.js","/constraints") +},{"../equations/Equation":25,"../math/vec2":33,"../utils/Utils":52,"./Constraint":17,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],19:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Constraint = _dereq_('./Constraint') +, Equation = _dereq_('../equations/Equation') +, AngleLockEquation = _dereq_('../equations/AngleLockEquation') +, vec2 = _dereq_('../math/vec2'); + +module.exports = GearConstraint; + +/** + * Connects two bodies at given offset points, letting them rotate relative to each other around this point. + * @class GearConstraint + * @constructor + * @author schteppe + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Object} [options] + * @param {Number} [options.angle=0] Relative angle between the bodies. Will be set to the current angle between the bodies (the gear ratio is accounted for). + * @param {Number} [options.ratio=1] Gear ratio. + * @param {Number} [options.maxTorque] Maximum torque to apply. + * @extends Constraint + * @todo Ability to specify world points + */ +function GearConstraint(bodyA, bodyB, options){ + options = options || {}; + + Constraint.call(this, bodyA, bodyB, Constraint.GEAR, options); + + /** + * The gear ratio. + * @property ratio + * @type {Number} + */ + this.ratio = typeof(options.ratio) === "number" ? options.ratio : 1; + + /** + * The relative angle + * @property angle + * @type {Number} + */ + this.angle = typeof(options.angle) === "number" ? options.angle : bodyB.angle - this.ratio * bodyA.angle; + + // Send same parameters to the equation + options.angle = this.angle; + options.ratio = this.ratio; + + this.equations = [ + new AngleLockEquation(bodyA,bodyB,options), + ]; + + // Set max torque + if(typeof(options.maxTorque) === "number"){ + this.setMaxTorque(options.maxTorque); + } +} +GearConstraint.prototype = new Constraint(); + +GearConstraint.prototype.update = function(){ + var eq = this.equations[0]; + if(eq.ratio !== this.ratio){ + eq.setRatio(this.ratio); + } + eq.angle = this.angle; +}; + +/** + * Set the max torque for the constraint. + * @method setMaxTorque + * @param {Number} torque + */ +GearConstraint.prototype.setMaxTorque = function(torque){ + this.equations[0].setMaxTorque(torque); +}; + +/** + * Get the max torque for the constraint. + * @method getMaxTorque + * @return {Number} + */ +GearConstraint.prototype.getMaxTorque = function(torque){ + return this.equations[0].maxForce; +}; +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/constraints/GearConstraint.js","/constraints") +},{"../equations/AngleLockEquation":23,"../equations/Equation":25,"../math/vec2":33,"./Constraint":17,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],20:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Constraint = _dereq_('./Constraint') +, vec2 = _dereq_('../math/vec2') +, Equation = _dereq_('../equations/Equation'); + +module.exports = LockConstraint; + +/** + * Locks the relative position between two bodies. + * + * @class LockConstraint + * @constructor + * @author schteppe + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Object} [options] + * @param {Array} [options.localOffsetB] The offset of bodyB in bodyA's frame. If not given the offset is computed from current positions. + * @param {number} [options.localAngleB] The angle of bodyB in bodyA's frame. If not given, the angle is computed from current angles. + * @param {number} [options.maxForce] + * @extends Constraint + */ +function LockConstraint(bodyA, bodyB, options){ + options = options || {}; + + Constraint.call(this,bodyA,bodyB,Constraint.LOCK,options); + + var maxForce = ( typeof(options.maxForce)==="undefined" ? Number.MAX_VALUE : options.maxForce ); + + var localAngleB = options.localAngleB || 0; + + // Use 3 equations: + // gx = (xj - xi - l) * xhat = 0 + // gy = (xj - xi - l) * yhat = 0 + // gr = (xi - xj + r) * that = 0 + // + // ...where: + // l is the localOffsetB vector rotated to world in bodyA frame + // r is the same vector but reversed and rotated from bodyB frame + // xhat, yhat are world axis vectors + // that is the tangent of r + // + // For the first two constraints, we get + // G*W = (vj - vi - ldot ) * xhat + // = (vj - vi - wi x l) * xhat + // + // Since (wi x l) * xhat = (l x xhat) * wi, we get + // G*W = [ -1 0 (-l x xhat) 1 0 0] * [vi wi vj wj] + // + // The last constraint gives + // GW = (vi - vj + wj x r) * that + // = [ that 0 -that (r x t) ] + + var x = new Equation(bodyA,bodyB,-maxForce,maxForce), + y = new Equation(bodyA,bodyB,-maxForce,maxForce), + rot = new Equation(bodyA,bodyB,-maxForce,maxForce); + + var l = vec2.create(), + g = vec2.create(), + that = this; + x.computeGq = function(){ + vec2.rotate(l, that.localOffsetB, bodyA.angle); + vec2.sub(g, bodyB.position, bodyA.position); + vec2.sub(g, g, l); + return g[0]; + }; + y.computeGq = function(){ + vec2.rotate(l, that.localOffsetB, bodyA.angle); + vec2.sub(g, bodyB.position, bodyA.position); + vec2.sub(g, g, l); + return g[1]; + }; + var r = vec2.create(), + t = vec2.create(); + rot.computeGq = function(){ + vec2.rotate(r, that.localOffsetB, bodyB.angle - that.localAngleB); + vec2.scale(r,r,-1); + vec2.sub(g,bodyA.position,bodyB.position); + vec2.add(g,g,r); + vec2.rotate(t,r,-Math.PI/2); + vec2.normalize(t,t); + return vec2.dot(g,t); + }; + + /** + * The offset of bodyB in bodyA's frame. + * @property {Array} localOffsetB + */ + this.localOffsetB = vec2.create(); + if(options.localOffsetB){ + vec2.copy(this.localOffsetB, options.localOffsetB); + } else { + // Construct from current positions + vec2.sub(this.localOffsetB, bodyB.position, bodyA.position); + vec2.rotate(this.localOffsetB, this.localOffsetB, -bodyA.angle); + } + + /** + * The offset angle of bodyB in bodyA's frame. + * @property {Number} localAngleB + */ + this.localAngleB = 0; + if(typeof(options.localAngleB) === 'number'){ + this.localAngleB = options.localAngleB; + } else { + // Construct + this.localAngleB = bodyB.angle - bodyA.angle; + } + + this.equations.push(x, y, rot); + this.setMaxForce(maxForce); +} +LockConstraint.prototype = new Constraint(); + +/** + * Set the maximum force to be applied. + * @method setMaxForce + * @param {Number} force + */ +LockConstraint.prototype.setMaxForce = function(force){ + var eqs = this.equations; + for(var i=0; ithis tutorial. + * + * @class PrismaticConstraint + * @constructor + * @extends Constraint + * @author schteppe + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Object} [options] + * @param {Number} [options.maxForce] Max force to be applied by the constraint + * @param {Array} [options.localAnchorA] Body A's anchor point, defined in its own local frame. + * @param {Array} [options.localAnchorB] Body B's anchor point, defined in its own local frame. + * @param {Array} [options.localAxisA] An axis, defined in body A frame, that body B's anchor point may slide along. + * @param {Boolean} [options.disableRotationalLock] If set to true, bodyB will be free to rotate around its anchor point. + * @param {Number} [options.upperLimit] + * @param {Number} [options.lowerLimit] + * @todo Ability to create using only a point and a worldAxis + */ +function PrismaticConstraint(bodyA, bodyB, options){ + options = options || {}; + Constraint.call(this,bodyA,bodyB,Constraint.PRISMATIC,options); + + // Get anchors + var localAnchorA = vec2.fromValues(0,0), + localAxisA = vec2.fromValues(1,0), + localAnchorB = vec2.fromValues(0,0); + if(options.localAnchorA){ vec2.copy(localAnchorA, options.localAnchorA); } + if(options.localAxisA){ vec2.copy(localAxisA, options.localAxisA); } + if(options.localAnchorB){ vec2.copy(localAnchorB, options.localAnchorB); } + + /** + * @property localAnchorA + * @type {Array} + */ + this.localAnchorA = localAnchorA; + + /** + * @property localAnchorB + * @type {Array} + */ + this.localAnchorB = localAnchorB; + + /** + * @property localAxisA + * @type {Array} + */ + this.localAxisA = localAxisA; + + /* + + The constraint violation for the common axis point is + + g = ( xj + rj - xi - ri ) * t := gg*t + + where r are body-local anchor points, and t is a tangent to the constraint axis defined in body i frame. + + gdot = ( vj + wj x rj - vi - wi x ri ) * t + ( xj + rj - xi - ri ) * ( wi x t ) + + Note the use of the chain rule. Now we identify the jacobian + + G*W = [ -t -ri x t + t x gg t rj x t ] * [vi wi vj wj] + + The rotational part is just a rotation lock. + + */ + + var maxForce = this.maxForce = typeof(options.maxForce)!=="undefined" ? options.maxForce : Number.MAX_VALUE; + + // Translational part + var trans = new Equation(bodyA,bodyB,-maxForce,maxForce); + var ri = new vec2.create(), + rj = new vec2.create(), + gg = new vec2.create(), + t = new vec2.create(); + trans.computeGq = function(){ + // g = ( xj + rj - xi - ri ) * t + return vec2.dot(gg,t); + }; + trans.updateJacobian = function(){ + var G = this.G, + xi = bodyA.position, + xj = bodyB.position; + vec2.rotate(ri,localAnchorA,bodyA.angle); + vec2.rotate(rj,localAnchorB,bodyB.angle); + vec2.add(gg,xj,rj); + vec2.sub(gg,gg,xi); + vec2.sub(gg,gg,ri); + vec2.rotate(t,localAxisA,bodyA.angle+Math.PI/2); + + G[0] = -t[0]; + G[1] = -t[1]; + G[2] = -vec2.crossLength(ri,t) + vec2.crossLength(t,gg); + G[3] = t[0]; + G[4] = t[1]; + G[5] = vec2.crossLength(rj,t); + }; + this.equations.push(trans); + + // Rotational part + if(!options.disableRotationalLock){ + var rot = new RotationalLockEquation(bodyA,bodyB,-maxForce,maxForce); + this.equations.push(rot); + } + + /** + * The position of anchor A relative to anchor B, along the constraint axis. + * @property position + * @type {Number} + */ + this.position = 0; + + this.velocity = 0; + + /** + * Set to true to enable lower limit. + * @property lowerLimitEnabled + * @type {Boolean} + */ + this.lowerLimitEnabled = typeof(options.lowerLimit)!=="undefined" ? true : false; + + /** + * Set to true to enable upper limit. + * @property upperLimitEnabled + * @type {Boolean} + */ + this.upperLimitEnabled = typeof(options.upperLimit)!=="undefined" ? true : false; + + /** + * Lower constraint limit. The constraint position is forced to be larger than this value. + * @property lowerLimit + * @type {Number} + */ + this.lowerLimit = typeof(options.lowerLimit)!=="undefined" ? options.lowerLimit : 0; + + /** + * Upper constraint limit. The constraint position is forced to be smaller than this value. + * @property upperLimit + * @type {Number} + */ + this.upperLimit = typeof(options.upperLimit)!=="undefined" ? options.upperLimit : 1; + + // Equations used for limits + this.upperLimitEquation = new ContactEquation(bodyA,bodyB); + this.lowerLimitEquation = new ContactEquation(bodyA,bodyB); + + // Set max/min forces + this.upperLimitEquation.minForce = this.lowerLimitEquation.minForce = 0; + this.upperLimitEquation.maxForce = this.lowerLimitEquation.maxForce = maxForce; + + /** + * Equation used for the motor. + * @property motorEquation + * @type {Equation} + */ + this.motorEquation = new Equation(bodyA,bodyB); + + /** + * The current motor state. Enable or disable the motor using .enableMotor + * @property motorEnabled + * @type {Boolean} + */ + this.motorEnabled = false; + + /** + * Set the target speed for the motor. + * @property motorSpeed + * @type {Number} + */ + this.motorSpeed = 0; + + var that = this; + var motorEquation = this.motorEquation; + var old = motorEquation.computeGW; + motorEquation.computeGq = function(){ return 0; }; + motorEquation.computeGW = function(){ + var G = this.G, + bi = this.bodyA, + bj = this.bodyB, + vi = bi.velocity, + vj = bj.velocity, + wi = bi.angularVelocity, + wj = bj.angularVelocity; + return this.gmult(G,vi,wi,vj,wj) + that.motorSpeed; + }; +} + +PrismaticConstraint.prototype = new Constraint(); + +var worldAxisA = vec2.create(), + worldAnchorA = vec2.create(), + worldAnchorB = vec2.create(), + orientedAnchorA = vec2.create(), + orientedAnchorB = vec2.create(), + tmp = vec2.create(); + +/** + * Update the constraint equations. Should be done if any of the bodies changed position, before solving. + * @method update + */ +PrismaticConstraint.prototype.update = function(){ + var eqs = this.equations, + trans = eqs[0], + upperLimit = this.upperLimit, + lowerLimit = this.lowerLimit, + upperLimitEquation = this.upperLimitEquation, + lowerLimitEquation = this.lowerLimitEquation, + bodyA = this.bodyA, + bodyB = this.bodyB, + localAxisA = this.localAxisA, + localAnchorA = this.localAnchorA, + localAnchorB = this.localAnchorB; + + trans.updateJacobian(); + + // Transform local things to world + vec2.rotate(worldAxisA, localAxisA, bodyA.angle); + vec2.rotate(orientedAnchorA, localAnchorA, bodyA.angle); + vec2.add(worldAnchorA, orientedAnchorA, bodyA.position); + vec2.rotate(orientedAnchorB, localAnchorB, bodyB.angle); + vec2.add(worldAnchorB, orientedAnchorB, bodyB.position); + + var relPosition = this.position = vec2.dot(worldAnchorB,worldAxisA) - vec2.dot(worldAnchorA,worldAxisA); + + // Motor + if(this.motorEnabled){ + // G = [ a a x ri -a -a x rj ] + var G = this.motorEquation.G; + G[0] = worldAxisA[0]; + G[1] = worldAxisA[1]; + G[2] = vec2.crossLength(worldAxisA,orientedAnchorB); + G[3] = -worldAxisA[0]; + G[4] = -worldAxisA[1]; + G[5] = -vec2.crossLength(worldAxisA,orientedAnchorA); + } + + /* + Limits strategy: + Add contact equation, with normal along the constraint axis. + min/maxForce is set so the constraint is repulsive in the correct direction. + Some offset is added to either equation.contactPointA or .contactPointB to get the correct upper/lower limit. + + ^ + | + upperLimit x + | ------ + anchorB x<---| B | + | | | + ------ | ------ + | | | + | A |-->x anchorA + ------ | + x lowerLimit + | + axis + */ + + + if(this.upperLimitEnabled && relPosition > upperLimit){ + // Update contact constraint normal, etc + vec2.scale(upperLimitEquation.normalA, worldAxisA, -1); + vec2.sub(upperLimitEquation.contactPointA, worldAnchorA, bodyA.position); + vec2.sub(upperLimitEquation.contactPointB, worldAnchorB, bodyB.position); + vec2.scale(tmp,worldAxisA,upperLimit); + vec2.add(upperLimitEquation.contactPointA,upperLimitEquation.contactPointA,tmp); + if(eqs.indexOf(upperLimitEquation) === -1){ + eqs.push(upperLimitEquation); + } + } else { + var idx = eqs.indexOf(upperLimitEquation); + if(idx !== -1){ + eqs.splice(idx,1); + } + } + + if(this.lowerLimitEnabled && relPosition < lowerLimit){ + // Update contact constraint normal, etc + vec2.scale(lowerLimitEquation.normalA, worldAxisA, 1); + vec2.sub(lowerLimitEquation.contactPointA, worldAnchorA, bodyA.position); + vec2.sub(lowerLimitEquation.contactPointB, worldAnchorB, bodyB.position); + vec2.scale(tmp,worldAxisA,lowerLimit); + vec2.sub(lowerLimitEquation.contactPointB,lowerLimitEquation.contactPointB,tmp); + if(eqs.indexOf(lowerLimitEquation) === -1){ + eqs.push(lowerLimitEquation); + } + } else { + var idx = eqs.indexOf(lowerLimitEquation); + if(idx !== -1){ + eqs.splice(idx,1); + } + } +}; + +/** + * Enable the motor + * @method enableMotor + */ +PrismaticConstraint.prototype.enableMotor = function(){ + if(this.motorEnabled){ + return; + } + this.equations.push(this.motorEquation); + this.motorEnabled = true; +}; + +/** + * Disable the rotational motor + * @method disableMotor + */ +PrismaticConstraint.prototype.disableMotor = function(){ + if(!this.motorEnabled){ + return; + } + var i = this.equations.indexOf(this.motorEquation); + this.equations.splice(i,1); + this.motorEnabled = false; +}; + +/** + * Set the constraint limits. + * @method setLimits + * @param {number} lower Lower limit. + * @param {number} upper Upper limit. + */ +PrismaticConstraint.prototype.setLimits = function (lower, upper) { + if(typeof(lower) === 'number'){ + this.lowerLimit = lower; + this.lowerLimitEnabled = true; + } else { + this.lowerLimit = lower; + this.lowerLimitEnabled = false; + } + + if(typeof(upper) === 'number'){ + this.upperLimit = upper; + this.upperLimitEnabled = true; + } else { + this.upperLimit = upper; + this.upperLimitEnabled = false; + } +}; + + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/constraints/PrismaticConstraint.js","/constraints") +},{"../equations/ContactEquation":24,"../equations/Equation":25,"../equations/RotationalLockEquation":27,"../math/vec2":33,"./Constraint":17,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],22:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Constraint = _dereq_('./Constraint') +, Equation = _dereq_('../equations/Equation') +, RotationalVelocityEquation = _dereq_('../equations/RotationalVelocityEquation') +, RotationalLockEquation = _dereq_('../equations/RotationalLockEquation') +, vec2 = _dereq_('../math/vec2'); + +module.exports = RevoluteConstraint; + +var worldPivotA = vec2.create(), + worldPivotB = vec2.create(), + xAxis = vec2.fromValues(1,0), + yAxis = vec2.fromValues(0,1), + g = vec2.create(); + +/** + * Connects two bodies at given offset points, letting them rotate relative to each other around this point. + * @class RevoluteConstraint + * @constructor + * @author schteppe + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Object} [options] + * @param {Array} [options.worldPivot] A pivot point given in world coordinates. If specified, localPivotA and localPivotB are automatically computed from this value. + * @param {Array} [options.localPivotA] The point relative to the center of mass of bodyA which bodyA is constrained to. + * @param {Array} [options.localPivotB] See localPivotA. + * @param {Number} [options.maxForce] The maximum force that should be applied to constrain the bodies. + * @extends Constraint + * + * @example + * // This will create a revolute constraint between two bodies with pivot point in between them. + * var bodyA = new Body({ mass: 1, position: [-1, 0] }); + * var bodyB = new Body({ mass: 1, position: [1, 0] }); + * var constraint = new RevoluteConstraint(bodyA, bodyB, { + * worldPivot: [0, 0] + * }); + * world.addConstraint(constraint); + * + * // Using body-local pivot points, the constraint could have been constructed like this: + * var constraint = new RevoluteConstraint(bodyA, bodyB, { + * localPivotA: [1, 0], + * localPivotB: [-1, 0] + * }); + */ +function RevoluteConstraint(bodyA, bodyB, options){ + options = options || {}; + Constraint.call(this,bodyA,bodyB,Constraint.REVOLUTE,options); + + var maxForce = this.maxForce = typeof(options.maxForce) !== "undefined" ? options.maxForce : Number.MAX_VALUE; + + /** + * @property {Array} pivotA + */ + this.pivotA = vec2.create(); + + /** + * @property {Array} pivotB + */ + this.pivotB = vec2.create(); + + if(options.worldPivot){ + // Compute pivotA and pivotB + vec2.sub(this.pivotA, options.worldPivot, bodyA.position); + vec2.sub(this.pivotB, options.worldPivot, bodyB.position); + // Rotate to local coordinate system + vec2.rotate(this.pivotA, this.pivotA, -bodyA.angle); + vec2.rotate(this.pivotB, this.pivotB, -bodyB.angle); + } else { + // Get pivotA and pivotB + vec2.copy(this.pivotA, options.localPivotA); + vec2.copy(this.pivotB, options.localPivotB); + } + + // Equations to be fed to the solver + var eqs = this.equations = [ + new Equation(bodyA,bodyB,-maxForce,maxForce), + new Equation(bodyA,bodyB,-maxForce,maxForce), + ]; + + var x = eqs[0]; + var y = eqs[1]; + var that = this; + + x.computeGq = function(){ + vec2.rotate(worldPivotA, that.pivotA, bodyA.angle); + vec2.rotate(worldPivotB, that.pivotB, bodyB.angle); + vec2.add(g, bodyB.position, worldPivotB); + vec2.sub(g, g, bodyA.position); + vec2.sub(g, g, worldPivotA); + return vec2.dot(g,xAxis); + }; + + y.computeGq = function(){ + vec2.rotate(worldPivotA, that.pivotA, bodyA.angle); + vec2.rotate(worldPivotB, that.pivotB, bodyB.angle); + vec2.add(g, bodyB.position, worldPivotB); + vec2.sub(g, g, bodyA.position); + vec2.sub(g, g, worldPivotA); + return vec2.dot(g,yAxis); + }; + + y.minForce = x.minForce = -maxForce; + y.maxForce = x.maxForce = maxForce; + + this.motorEquation = new RotationalVelocityEquation(bodyA,bodyB); + + /** + * Indicates whether the motor is enabled. Use .enableMotor() to enable the constraint motor. + * @property {Boolean} motorEnabled + * @readOnly + */ + this.motorEnabled = false; + + /** + * The constraint position. + * @property angle + * @type {Number} + * @readOnly + */ + this.angle = 0; + + /** + * Set to true to enable lower limit + * @property lowerLimitEnabled + * @type {Boolean} + */ + this.lowerLimitEnabled = false; + + /** + * Set to true to enable upper limit + * @property upperLimitEnabled + * @type {Boolean} + */ + this.upperLimitEnabled = false; + + /** + * The lower limit on the constraint angle. + * @property lowerLimit + * @type {Boolean} + */ + this.lowerLimit = 0; + + /** + * The upper limit on the constraint angle. + * @property upperLimit + * @type {Boolean} + */ + this.upperLimit = 0; + + this.upperLimitEquation = new RotationalLockEquation(bodyA,bodyB); + this.lowerLimitEquation = new RotationalLockEquation(bodyA,bodyB); + this.upperLimitEquation.minForce = 0; + this.lowerLimitEquation.maxForce = 0; +} +RevoluteConstraint.prototype = new Constraint(); + +/** + * Set the constraint angle limits. + * @method setLimits + * @param {number} lower Lower angle limit. + * @param {number} upper Upper angle limit. + */ +RevoluteConstraint.prototype.setLimits = function (lower, upper) { + if(typeof(lower) === 'number'){ + this.lowerLimit = lower; + this.lowerLimitEnabled = true; + } else { + this.lowerLimit = lower; + this.lowerLimitEnabled = false; + } + + if(typeof(upper) === 'number'){ + this.upperLimit = upper; + this.upperLimitEnabled = true; + } else { + this.upperLimit = upper; + this.upperLimitEnabled = false; + } +}; + +RevoluteConstraint.prototype.update = function(){ + var bodyA = this.bodyA, + bodyB = this.bodyB, + pivotA = this.pivotA, + pivotB = this.pivotB, + eqs = this.equations, + normal = eqs[0], + tangent= eqs[1], + x = eqs[0], + y = eqs[1], + upperLimit = this.upperLimit, + lowerLimit = this.lowerLimit, + upperLimitEquation = this.upperLimitEquation, + lowerLimitEquation = this.lowerLimitEquation; + + var relAngle = this.angle = bodyB.angle - bodyA.angle; + + if(this.upperLimitEnabled && relAngle > upperLimit){ + upperLimitEquation.angle = upperLimit; + if(eqs.indexOf(upperLimitEquation) === -1){ + eqs.push(upperLimitEquation); + } + } else { + var idx = eqs.indexOf(upperLimitEquation); + if(idx !== -1){ + eqs.splice(idx,1); + } + } + + if(this.lowerLimitEnabled && relAngle < lowerLimit){ + lowerLimitEquation.angle = lowerLimit; + if(eqs.indexOf(lowerLimitEquation) === -1){ + eqs.push(lowerLimitEquation); + } + } else { + var idx = eqs.indexOf(lowerLimitEquation); + if(idx !== -1){ + eqs.splice(idx,1); + } + } + + /* + + The constraint violation is + + g = xj + rj - xi - ri + + ...where xi and xj are the body positions and ri and rj world-oriented offset vectors. Differentiate: + + gdot = vj + wj x rj - vi - wi x ri + + We split this into x and y directions. (let x and y be unit vectors along the respective axes) + + gdot * x = ( vj + wj x rj - vi - wi x ri ) * x + = ( vj*x + (wj x rj)*x -vi*x -(wi x ri)*x + = ( vj*x + (rj x x)*wj -vi*x -(ri x x)*wi + = [ -x -(ri x x) x (rj x x)] * [vi wi vj wj] + = G*W + + ...and similar for y. We have then identified the jacobian entries for x and y directions: + + Gx = [ x (rj x x) -x -(ri x x)] + Gy = [ y (rj x y) -y -(ri x y)] + + */ + + vec2.rotate(worldPivotA, pivotA, bodyA.angle); + vec2.rotate(worldPivotB, pivotB, bodyB.angle); + + // todo: these are a bit sparse. We could save some computations on making custom eq.computeGW functions, etc + + x.G[0] = -1; + x.G[1] = 0; + x.G[2] = -vec2.crossLength(worldPivotA,xAxis); + x.G[3] = 1; + x.G[4] = 0; + x.G[5] = vec2.crossLength(worldPivotB,xAxis); + + y.G[0] = 0; + y.G[1] = -1; + y.G[2] = -vec2.crossLength(worldPivotA,yAxis); + y.G[3] = 0; + y.G[4] = 1; + y.G[5] = vec2.crossLength(worldPivotB,yAxis); +}; + +/** + * Enable the rotational motor + * @method enableMotor + */ +RevoluteConstraint.prototype.enableMotor = function(){ + if(this.motorEnabled){ + return; + } + this.equations.push(this.motorEquation); + this.motorEnabled = true; +}; + +/** + * Disable the rotational motor + * @method disableMotor + */ +RevoluteConstraint.prototype.disableMotor = function(){ + if(!this.motorEnabled){ + return; + } + var i = this.equations.indexOf(this.motorEquation); + this.equations.splice(i,1); + this.motorEnabled = false; +}; + +/** + * Check if the motor is enabled. + * @method motorIsEnabled + * @return {Boolean} + */ +RevoluteConstraint.prototype.motorIsEnabled = function(){ + return !!this.motorEnabled; +}; + +/** + * Set the speed of the rotational constraint motor + * @method setMotorSpeed + * @param {Number} speed + */ +RevoluteConstraint.prototype.setMotorSpeed = function(speed){ + if(!this.motorEnabled){ + return; + } + var i = this.equations.indexOf(this.motorEquation); + this.equations[i].relativeVelocity = speed; +}; + +/** + * Get the speed of the rotational constraint motor + * @method getMotorSpeed + * @return {Number} The current speed, or false if the motor is not enabled. + */ +RevoluteConstraint.prototype.getMotorSpeed = function(){ + if(!this.motorEnabled){ + return false; + } + return this.motorEquation.relativeVelocity; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/constraints/RevoluteConstraint.js","/constraints") +},{"../equations/Equation":25,"../equations/RotationalLockEquation":27,"../equations/RotationalVelocityEquation":28,"../math/vec2":33,"./Constraint":17,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],23:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Equation = _dereq_("./Equation"), + vec2 = _dereq_('../math/vec2'); + +module.exports = AngleLockEquation; + +/** + * Locks the relative angle between two bodies. The constraint tries to keep the dot product between two vectors, local in each body, to zero. The local angle in body i is a parameter. + * + * @class AngleLockEquation + * @constructor + * @extends Equation + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Object} [options] + * @param {Number} [options.angle] Angle to add to the local vector in body A. + * @param {Number} [options.ratio] Gear ratio + */ +function AngleLockEquation(bodyA, bodyB, options){ + options = options || {}; + Equation.call(this,bodyA,bodyB,-Number.MAX_VALUE,Number.MAX_VALUE); + this.angle = options.angle || 0; + + /** + * The gear ratio. + * @property {Number} ratio + * @private + * @see setRatio + */ + this.ratio = typeof(options.ratio)==="number" ? options.ratio : 1; + + this.setRatio(this.ratio); +} +AngleLockEquation.prototype = new Equation(); +AngleLockEquation.prototype.constructor = AngleLockEquation; + +AngleLockEquation.prototype.computeGq = function(){ + return this.ratio * this.bodyA.angle - this.bodyB.angle + this.angle; +}; + +/** + * Set the gear ratio for this equation + * @method setRatio + * @param {Number} ratio + */ +AngleLockEquation.prototype.setRatio = function(ratio){ + var G = this.G; + G[2] = ratio; + G[5] = -1; + this.ratio = ratio; +}; + +/** + * Set the max force for the equation. + * @method setMaxTorque + * @param {Number} torque + */ +AngleLockEquation.prototype.setMaxTorque = function(torque){ + this.maxForce = torque; + this.minForce = -torque; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/AngleLockEquation.js","/equations") +},{"../math/vec2":33,"./Equation":25,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],24:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Equation = _dereq_("./Equation"), + vec2 = _dereq_('../math/vec2'); + +module.exports = ContactEquation; + +/** + * Non-penetration constraint equation. Tries to make the contactPointA and contactPointB vectors coincide, while keeping the applied force repulsive. + * + * @class ContactEquation + * @constructor + * @extends Equation + * @param {Body} bodyA + * @param {Body} bodyB + */ +function ContactEquation(bodyA, bodyB){ + Equation.call(this, bodyA, bodyB, 0, Number.MAX_VALUE); + + /** + * Vector from body i center of mass to the contact point. + * @property contactPointA + * @type {Array} + */ + this.contactPointA = vec2.create(); + this.penetrationVec = vec2.create(); + + /** + * World-oriented vector from body A center of mass to the contact point. + * @property contactPointB + * @type {Array} + */ + this.contactPointB = vec2.create(); + + /** + * The normal vector, pointing out of body i + * @property normalA + * @type {Array} + */ + this.normalA = vec2.create(); + + /** + * The restitution to use (0=no bounciness, 1=max bounciness). + * @property restitution + * @type {Number} + */ + this.restitution = 0; + + /** + * This property is set to true if this is the first impact between the bodies (not persistant contact). + * @property firstImpact + * @type {Boolean} + * @readOnly + */ + this.firstImpact = false; + + /** + * The shape in body i that triggered this contact. + * @property shapeA + * @type {Shape} + */ + this.shapeA = null; + + /** + * The shape in body j that triggered this contact. + * @property shapeB + * @type {Shape} + */ + this.shapeB = null; +} +ContactEquation.prototype = new Equation(); +ContactEquation.prototype.constructor = ContactEquation; +ContactEquation.prototype.computeB = function(a,b,h){ + var bi = this.bodyA, + bj = this.bodyB, + ri = this.contactPointA, + rj = this.contactPointB, + xi = bi.position, + xj = bj.position; + + var penetrationVec = this.penetrationVec, + n = this.normalA, + G = this.G; + + // Caluclate cross products + var rixn = vec2.crossLength(ri,n), + rjxn = vec2.crossLength(rj,n); + + // G = [-n -rixn n rjxn] + G[0] = -n[0]; + G[1] = -n[1]; + G[2] = -rixn; + G[3] = n[0]; + G[4] = n[1]; + G[5] = rjxn; + + // Calculate q = xj+rj -(xi+ri) i.e. the penetration vector + vec2.add(penetrationVec,xj,rj); + vec2.sub(penetrationVec,penetrationVec,xi); + vec2.sub(penetrationVec,penetrationVec,ri); + + // Compute iteration + var GW, Gq; + if(this.firstImpact && this.restitution !== 0){ + Gq = 0; + GW = (1/b)*(1+this.restitution) * this.computeGW(); + } else { + Gq = vec2.dot(n,penetrationVec) + this.offset; + GW = this.computeGW(); + } + + var GiMf = this.computeGiMf(); + var B = - Gq * a - GW * b - h*GiMf; + + return B; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/ContactEquation.js","/equations") +},{"../math/vec2":33,"./Equation":25,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],25:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +module.exports = Equation; + +var vec2 = _dereq_('../math/vec2'), + Utils = _dereq_('../utils/Utils'), + Body = _dereq_('../objects/Body'); + +/** + * Base class for constraint equations. + * @class Equation + * @constructor + * @param {Body} bodyA First body participating in the equation + * @param {Body} bodyB Second body participating in the equation + * @param {number} minForce Minimum force to apply. Default: -Number.MAX_VALUE + * @param {number} maxForce Maximum force to apply. Default: Number.MAX_VALUE + */ +function Equation(bodyA, bodyB, minForce, maxForce){ + + /** + * Minimum force to apply when solving. + * @property minForce + * @type {Number} + */ + this.minForce = typeof(minForce)==="undefined" ? -Number.MAX_VALUE : minForce; + + /** + * Max force to apply when solving. + * @property maxForce + * @type {Number} + */ + this.maxForce = typeof(maxForce)==="undefined" ? Number.MAX_VALUE : maxForce; + + /** + * First body participating in the constraint + * @property bodyA + * @type {Body} + */ + this.bodyA = bodyA; + + /** + * Second body participating in the constraint + * @property bodyB + * @type {Body} + */ + this.bodyB = bodyB; + + /** + * The stiffness of this equation. Typically chosen to a large number (~1e7), but can be chosen somewhat freely to get a stable simulation. + * @property stiffness + * @type {Number} + */ + this.stiffness = Equation.DEFAULT_STIFFNESS; + + /** + * The number of time steps needed to stabilize the constraint equation. Typically between 3 and 5 time steps. + * @property relaxation + * @type {Number} + */ + this.relaxation = Equation.DEFAULT_RELAXATION; + + /** + * The Jacobian entry of this equation. 6 numbers, 3 per body (x,y,angle). + * @property G + * @type {Array} + */ + this.G = new Utils.ARRAY_TYPE(6); + for(var i=0; i<6; i++){ + this.G[i]=0; + } + + this.offset = 0; + + this.a = 0; + this.b = 0; + this.epsilon = 0; + this.timeStep = 1/60; + + /** + * Indicates if stiffness or relaxation was changed. + * @property {Boolean} needsUpdate + */ + this.needsUpdate = true; + + /** + * The resulting constraint multiplier from the last solve. This is mostly equivalent to the force produced by the constraint. + * @property multiplier + * @type {Number} + */ + this.multiplier = 0; + + /** + * Relative velocity. + * @property {Number} relativeVelocity + */ + this.relativeVelocity = 0; + + /** + * Whether this equation is enabled or not. If true, it will be added to the solver. + * @property {Boolean} enabled + */ + this.enabled = true; +} +Equation.prototype.constructor = Equation; + +/** + * The default stiffness when creating a new Equation. + * @static + * @property {Number} DEFAULT_STIFFNESS + * @default 1e6 + */ +Equation.DEFAULT_STIFFNESS = 1e6; + +/** + * The default relaxation when creating a new Equation. + * @static + * @property {Number} DEFAULT_RELAXATION + * @default 4 + */ +Equation.DEFAULT_RELAXATION = 4; + +/** + * Compute SPOOK parameters .a, .b and .epsilon according to the current parameters. See equations 9, 10 and 11 in the SPOOK notes. + * @method update + */ +Equation.prototype.update = function(){ + var k = this.stiffness, + d = this.relaxation, + h = this.timeStep; + + this.a = 4.0 / (h * (1 + 4 * d)); + this.b = (4.0 * d) / (1 + 4 * d); + this.epsilon = 4.0 / (h * h * k * (1 + 4 * d)); + + this.needsUpdate = false; +}; + +/** + * Multiply a jacobian entry with corresponding positions or velocities + * @method gmult + * @return {Number} + */ +Equation.prototype.gmult = function(G,vi,wi,vj,wj){ + return G[0] * vi[0] + + G[1] * vi[1] + + G[2] * wi + + G[3] * vj[0] + + G[4] * vj[1] + + G[5] * wj; +}; + +/** + * Computes the RHS of the SPOOK equation + * @method computeB + * @return {Number} + */ +Equation.prototype.computeB = function(a,b,h){ + var GW = this.computeGW(); + var Gq = this.computeGq(); + var GiMf = this.computeGiMf(); + return - Gq * a - GW * b - GiMf*h; +}; + +/** + * Computes G\*q, where q are the generalized body coordinates + * @method computeGq + * @return {Number} + */ +var qi = vec2.create(), + qj = vec2.create(); +Equation.prototype.computeGq = function(){ + var G = this.G, + bi = this.bodyA, + bj = this.bodyB, + xi = bi.position, + xj = bj.position, + ai = bi.angle, + aj = bj.angle; + + return this.gmult(G, qi, ai, qj, aj) + this.offset; +}; + +/** + * Computes G\*W, where W are the body velocities + * @method computeGW + * @return {Number} + */ +Equation.prototype.computeGW = function(){ + var G = this.G, + bi = this.bodyA, + bj = this.bodyB, + vi = bi.velocity, + vj = bj.velocity, + wi = bi.angularVelocity, + wj = bj.angularVelocity; + return this.gmult(G,vi,wi,vj,wj) + this.relativeVelocity; +}; + +/** + * Computes G\*Wlambda, where W are the body velocities + * @method computeGWlambda + * @return {Number} + */ +Equation.prototype.computeGWlambda = function(){ + var G = this.G, + bi = this.bodyA, + bj = this.bodyB, + vi = bi.vlambda, + vj = bj.vlambda, + wi = bi.wlambda, + wj = bj.wlambda; + return this.gmult(G,vi,wi,vj,wj); +}; + +/** + * Computes G\*inv(M)\*f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies. + * @method computeGiMf + * @return {Number} + */ +var iMfi = vec2.create(), + iMfj = vec2.create(); +Equation.prototype.computeGiMf = function(){ + var bi = this.bodyA, + bj = this.bodyB, + fi = bi.force, + ti = bi.angularForce, + fj = bj.force, + tj = bj.angularForce, + invMassi = bi.invMassSolve, + invMassj = bj.invMassSolve, + invIi = bi.invInertiaSolve, + invIj = bj.invInertiaSolve, + G = this.G; + + vec2.scale(iMfi, fi,invMassi); + vec2.scale(iMfj, fj,invMassj); + + return this.gmult(G,iMfi,ti*invIi,iMfj,tj*invIj); +}; + +/** + * Computes G\*inv(M)\*G' + * @method computeGiMGt + * @return {Number} + */ +Equation.prototype.computeGiMGt = function(){ + var bi = this.bodyA, + bj = this.bodyB, + invMassi = bi.invMassSolve, + invMassj = bj.invMassSolve, + invIi = bi.invInertiaSolve, + invIj = bj.invInertiaSolve, + G = this.G; + + return G[0] * G[0] * invMassi + + G[1] * G[1] * invMassi + + G[2] * G[2] * invIi + + G[3] * G[3] * invMassj + + G[4] * G[4] * invMassj + + G[5] * G[5] * invIj; +}; + +var addToWlambda_temp = vec2.create(), + addToWlambda_Gi = vec2.create(), + addToWlambda_Gj = vec2.create(), + addToWlambda_ri = vec2.create(), + addToWlambda_rj = vec2.create(), + addToWlambda_Mdiag = vec2.create(); + +/** + * Add constraint velocity to the bodies. + * @method addToWlambda + * @param {Number} deltalambda + */ +Equation.prototype.addToWlambda = function(deltalambda){ + var bi = this.bodyA, + bj = this.bodyB, + temp = addToWlambda_temp, + Gi = addToWlambda_Gi, + Gj = addToWlambda_Gj, + ri = addToWlambda_ri, + rj = addToWlambda_rj, + invMassi = bi.invMassSolve, + invMassj = bj.invMassSolve, + invIi = bi.invInertiaSolve, + invIj = bj.invInertiaSolve, + Mdiag = addToWlambda_Mdiag, + G = this.G; + + Gi[0] = G[0]; + Gi[1] = G[1]; + Gj[0] = G[3]; + Gj[1] = G[4]; + + // Add to linear velocity + // v_lambda += inv(M) * delta_lamba * G + vec2.scale(temp, Gi, invMassi*deltalambda); + vec2.add( bi.vlambda, bi.vlambda, temp); + // This impulse is in the offset frame + // Also add contribution to angular + //bi.wlambda -= vec2.crossLength(temp,ri); + bi.wlambda += invIi * G[2] * deltalambda; + + + vec2.scale(temp, Gj, invMassj*deltalambda); + vec2.add( bj.vlambda, bj.vlambda, temp); + //bj.wlambda -= vec2.crossLength(temp,rj); + bj.wlambda += invIj * G[5] * deltalambda; +}; + +/** + * Compute the denominator part of the SPOOK equation: C = G\*inv(M)\*G' + eps + * @method computeInvC + * @param {Number} eps + * @return {Number} + */ +Equation.prototype.computeInvC = function(eps){ + return 1.0 / (this.computeGiMGt() + eps); +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/Equation.js","/equations") +},{"../math/vec2":33,"../objects/Body":34,"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],26:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var vec2 = _dereq_('../math/vec2') +, Equation = _dereq_('./Equation') +, Utils = _dereq_('../utils/Utils'); + +module.exports = FrictionEquation; + +/** + * Constrains the slipping in a contact along a tangent + * + * @class FrictionEquation + * @constructor + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Number} slipForce + * @extends Equation + */ +function FrictionEquation(bodyA, bodyB, slipForce){ + Equation.call(this, bodyA, bodyB, -slipForce, slipForce); + + /** + * Relative vector from center of body A to the contact point, world oriented. + * @property contactPointA + * @type {Array} + */ + this.contactPointA = vec2.create(); + + /** + * Relative vector from center of body B to the contact point, world oriented. + * @property contactPointB + * @type {Array} + */ + this.contactPointB = vec2.create(); + + /** + * Tangent vector that the friction force will act along. World oriented. + * @property t + * @type {Array} + */ + this.t = vec2.create(); + + /** + * A ContactEquation connected to this friction. The contact equations can be used to rescale the max force for the friction. If more than one contact equation is given, then the max force can be set to the average. + * @property contactEquations + * @type {ContactEquation} + */ + this.contactEquations = []; + + /** + * The shape in body i that triggered this friction. + * @property shapeA + * @type {Shape} + * @todo Needed? The shape can be looked up via contactEquation.shapeA... + */ + this.shapeA = null; + + /** + * The shape in body j that triggered this friction. + * @property shapeB + * @type {Shape} + * @todo Needed? The shape can be looked up via contactEquation.shapeB... + */ + this.shapeB = null; + + /** + * The friction coefficient to use. + * @property frictionCoefficient + * @type {Number} + */ + this.frictionCoefficient = 0.3; +} +FrictionEquation.prototype = new Equation(); +FrictionEquation.prototype.constructor = FrictionEquation; + +/** + * Set the slipping condition for the constraint. The friction force cannot be + * larger than this value. + * @method setSlipForce + * @param {Number} slipForce + */ +FrictionEquation.prototype.setSlipForce = function(slipForce){ + this.maxForce = slipForce; + this.minForce = -slipForce; +}; + +/** + * Get the max force for the constraint. + * @method getSlipForce + * @return {Number} + */ +FrictionEquation.prototype.getSlipForce = function(){ + return this.maxForce; +}; + +FrictionEquation.prototype.computeB = function(a,b,h){ + var bi = this.bodyA, + bj = this.bodyB, + ri = this.contactPointA, + rj = this.contactPointB, + t = this.t, + G = this.G; + + // G = [-t -rixt t rjxt] + // And remember, this is a pure velocity constraint, g is always zero! + G[0] = -t[0]; + G[1] = -t[1]; + G[2] = -vec2.crossLength(ri,t); + G[3] = t[0]; + G[4] = t[1]; + G[5] = vec2.crossLength(rj,t); + + var GW = this.computeGW(), + GiMf = this.computeGiMf(); + + var B = /* - g * a */ - GW * b - h*GiMf; + + return B; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/FrictionEquation.js","/equations") +},{"../math/vec2":33,"../utils/Utils":52,"./Equation":25,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],27:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Equation = _dereq_("./Equation"), + vec2 = _dereq_('../math/vec2'); + +module.exports = RotationalLockEquation; + +/** + * Locks the relative angle between two bodies. The constraint tries to keep the dot product between two vectors, local in each body, to zero. The local angle in body i is a parameter. + * + * @class RotationalLockEquation + * @constructor + * @extends Equation + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Object} [options] + * @param {Number} [options.angle] Angle to add to the local vector in bodyA. + */ +function RotationalLockEquation(bodyA, bodyB, options){ + options = options || {}; + Equation.call(this, bodyA, bodyB, -Number.MAX_VALUE, Number.MAX_VALUE); + + /** + * @property {number} angle + */ + this.angle = options.angle || 0; + + var G = this.G; + G[2] = 1; + G[5] = -1; +} +RotationalLockEquation.prototype = new Equation(); +RotationalLockEquation.prototype.constructor = RotationalLockEquation; + +var worldVectorA = vec2.create(), + worldVectorB = vec2.create(), + xAxis = vec2.fromValues(1,0), + yAxis = vec2.fromValues(0,1); +RotationalLockEquation.prototype.computeGq = function(){ + vec2.rotate(worldVectorA,xAxis,this.bodyA.angle+this.angle); + vec2.rotate(worldVectorB,yAxis,this.bodyB.angle); + return vec2.dot(worldVectorA,worldVectorB); +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/RotationalLockEquation.js","/equations") +},{"../math/vec2":33,"./Equation":25,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],28:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Equation = _dereq_("./Equation"), + vec2 = _dereq_('../math/vec2'); + +module.exports = RotationalVelocityEquation; + +/** + * Syncs rotational velocity of two bodies, or sets a relative velocity (motor). + * + * @class RotationalVelocityEquation + * @constructor + * @extends Equation + * @param {Body} bodyA + * @param {Body} bodyB + */ +function RotationalVelocityEquation(bodyA, bodyB){ + Equation.call(this, bodyA, bodyB, -Number.MAX_VALUE, Number.MAX_VALUE); + this.relativeVelocity = 1; + this.ratio = 1; +} +RotationalVelocityEquation.prototype = new Equation(); +RotationalVelocityEquation.prototype.constructor = RotationalVelocityEquation; +RotationalVelocityEquation.prototype.computeB = function(a,b,h){ + var G = this.G; + G[2] = -1; + G[5] = this.ratio; + + var GiMf = this.computeGiMf(); + var GW = this.computeGW(); + var B = - GW * b - h*GiMf; + + return B; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/RotationalVelocityEquation.js","/equations") +},{"../math/vec2":33,"./Equation":25,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],29:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +/** + * Base class for objects that dispatches events. + * @class EventEmitter + * @constructor + */ +var EventEmitter = function () {}; + +module.exports = EventEmitter; + +EventEmitter.prototype = { + constructor: EventEmitter, + + /** + * Add an event listener + * @method on + * @param {String} type + * @param {Function} listener + * @return {EventEmitter} The self object, for chainability. + */ + on: function ( type, listener, context ) { + listener.context = context || this; + if ( this._listeners === undefined ){ + this._listeners = {}; + } + var listeners = this._listeners; + if ( listeners[ type ] === undefined ) { + listeners[ type ] = []; + } + if ( listeners[ type ].indexOf( listener ) === - 1 ) { + listeners[ type ].push( listener ); + } + return this; + }, + + /** + * Check if an event listener is added + * @method has + * @param {String} type + * @param {Function} listener + * @return {Boolean} + */ + has: function ( type, listener ) { + if ( this._listeners === undefined ){ + return false; + } + var listeners = this._listeners; + if(listener){ + if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) { + return true; + } + } else { + if ( listeners[ type ] !== undefined ) { + return true; + } + } + + return false; + }, + + /** + * Remove an event listener + * @method off + * @param {String} type + * @param {Function} listener + * @return {EventEmitter} The self object, for chainability. + */ + off: function ( type, listener ) { + if ( this._listeners === undefined ){ + return this; + } + var listeners = this._listeners; + var index = listeners[ type ].indexOf( listener ); + if ( index !== - 1 ) { + listeners[ type ].splice( index, 1 ); + } + return this; + }, + + /** + * Emit an event. + * @method emit + * @param {Object} event + * @param {String} event.type + * @return {EventEmitter} The self object, for chainability. + */ + emit: function ( event ) { + if ( this._listeners === undefined ){ + return this; + } + var listeners = this._listeners; + var listenerArray = listeners[ event.type ]; + if ( listenerArray !== undefined ) { + event.target = this; + for ( var i = 0, l = listenerArray.length; i < l; i ++ ) { + var listener = listenerArray[ i ]; + listener.call( listener.context, event ); + } + } + return this; + } +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/events/EventEmitter.js","/events") +},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],30:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Material = _dereq_('./Material'); +var Equation = _dereq_('../equations/Equation'); + +module.exports = ContactMaterial; + +/** + * Defines what happens when two materials meet, such as what friction coefficient to use. You can also set other things such as restitution, surface velocity and constraint parameters. + * @class ContactMaterial + * @constructor + * @param {Material} materialA + * @param {Material} materialB + * @param {Object} [options] + * @param {Number} [options.friction=0.3] Friction coefficient. + * @param {Number} [options.restitution=0] Restitution coefficient aka "bounciness". + * @param {Number} [options.stiffness] ContactEquation stiffness. + * @param {Number} [options.relaxation] ContactEquation relaxation. + * @param {Number} [options.frictionStiffness] FrictionEquation stiffness. + * @param {Number} [options.frictionRelaxation] FrictionEquation relaxation. + * @param {Number} [options.surfaceVelocity=0] Surface velocity. + * @author schteppe + */ +function ContactMaterial(materialA, materialB, options){ + options = options || {}; + + if(!(materialA instanceof Material) || !(materialB instanceof Material)){ + throw new Error("First two arguments must be Material instances."); + } + + /** + * The contact material identifier + * @property id + * @type {Number} + */ + this.id = ContactMaterial.idCounter++; + + /** + * First material participating in the contact material + * @property materialA + * @type {Material} + */ + this.materialA = materialA; + + /** + * Second material participating in the contact material + * @property materialB + * @type {Material} + */ + this.materialB = materialB; + + /** + * Friction to use in the contact of these two materials + * @property friction + * @type {Number} + */ + this.friction = typeof(options.friction) !== "undefined" ? Number(options.friction) : 0.3; + + /** + * Restitution to use in the contact of these two materials + * @property restitution + * @type {Number} + */ + this.restitution = typeof(options.restitution) !== "undefined" ? Number(options.restitution) : 0.0; + + /** + * Stiffness of the resulting ContactEquation that this ContactMaterial generate + * @property stiffness + * @type {Number} + */ + this.stiffness = typeof(options.stiffness) !== "undefined" ? Number(options.stiffness) : Equation.DEFAULT_STIFFNESS; + + /** + * Relaxation of the resulting ContactEquation that this ContactMaterial generate + * @property relaxation + * @type {Number} + */ + this.relaxation = typeof(options.relaxation) !== "undefined" ? Number(options.relaxation) : Equation.DEFAULT_RELAXATION; + + /** + * Stiffness of the resulting FrictionEquation that this ContactMaterial generate + * @property frictionStiffness + * @type {Number} + */ + this.frictionStiffness = typeof(options.frictionStiffness) !== "undefined" ? Number(options.frictionStiffness) : Equation.DEFAULT_STIFFNESS; + + /** + * Relaxation of the resulting FrictionEquation that this ContactMaterial generate + * @property frictionRelaxation + * @type {Number} + */ + this.frictionRelaxation = typeof(options.frictionRelaxation) !== "undefined" ? Number(options.frictionRelaxation) : Equation.DEFAULT_RELAXATION; + + /** + * Will add surface velocity to this material. If bodyA rests on top if bodyB, and the surface velocity is positive, bodyA will slide to the right. + * @property {Number} surfaceVelocity + */ + this.surfaceVelocity = typeof(options.surfaceVelocity) !== "undefined" ? Number(options.surfaceVelocity) : 0; + + /** + * Offset to be set on ContactEquations. A positive value will make the bodies penetrate more into each other. Can be useful in scenes where contacts need to be more persistent, for example when stacking. Aka "cure for nervous contacts". + * @property contactSkinSize + * @type {Number} + */ + this.contactSkinSize = 0.005; +} + +ContactMaterial.idCounter = 0; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/material/ContactMaterial.js","/material") +},{"../equations/Equation":25,"./Material":31,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],31:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +module.exports = Material; + +/** + * Defines a physics material. + * @class Material + * @constructor + * @param {number} id Material identifier + * @author schteppe + */ +function Material(id){ + /** + * The material identifier + * @property id + * @type {Number} + */ + this.id = id || Material.idCounter++; +} + +Material.idCounter = 0; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/material/Material.js","/material") +},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],32:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ + + /* + PolyK library + url: http://polyk.ivank.net + Released under MIT licence. + + Copyright (c) 2012 Ivan Kuckir + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ + + var PolyK = {}; + + /* + Is Polygon self-intersecting? + + O(n^2) + */ + /* + PolyK.IsSimple = function(p) + { + var n = p.length>>1; + if(n<4) return true; + var a1 = new PolyK._P(), a2 = new PolyK._P(); + var b1 = new PolyK._P(), b2 = new PolyK._P(); + var c = new PolyK._P(); + + for(var i=0; i>1; + if(n<3) return []; + var tgs = []; + var avl = []; + for(var i=0; i 3) + { + var i0 = avl[(i+0)%al]; + var i1 = avl[(i+1)%al]; + var i2 = avl[(i+2)%al]; + + var ax = p[2*i0], ay = p[2*i0+1]; + var bx = p[2*i1], by = p[2*i1+1]; + var cx = p[2*i2], cy = p[2*i2+1]; + + var earFound = false; + if(PolyK._convex(ax, ay, bx, by, cx, cy)) + { + earFound = true; + for(var j=0; j 3*al) break; // no convex angles :( + } + tgs.push(avl[0], avl[1], avl[2]); + return tgs; + } + /* + PolyK.ContainsPoint = function(p, px, py) + { + var n = p.length>>1; + var ax, ay, bx = p[2*n-2]-px, by = p[2*n-1]-py; + var depth = 0; + for(var i=0; i=0 && by>=0) continue; // both "up" or both "donw" + if(ax< 0 && bx< 0) continue; + + var lx = ax + (bx-ax)*(-ay)/(by-ay); + if(lx>0) depth++; + } + return (depth & 1) == 1; + } + + PolyK.Slice = function(p, ax, ay, bx, by) + { + if(PolyK.ContainsPoint(p, ax, ay) || PolyK.ContainsPoint(p, bx, by)) return [p.slice(0)]; + + var a = new PolyK._P(ax, ay); + var b = new PolyK._P(bx, by); + var iscs = []; // intersections + var ps = []; // points + for(var i=0; i 0) + { + var n = ps.length; + var i0 = iscs[0]; + var i1 = iscs[1]; + var ind0 = ps.indexOf(i0); + var ind1 = ps.indexOf(i1); + var solved = false; + + if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true; + else + { + i0 = iscs[1]; + i1 = iscs[0]; + ind0 = ps.indexOf(i0); + ind1 = ps.indexOf(i1); + if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true; + } + if(solved) + { + dir--; + var pgn = PolyK._getPoints(ps, ind0, ind1); + pgs.push(pgn); + ps = PolyK._getPoints(ps, ind1, ind0); + i0.flag = i1.flag = false; + iscs.splice(0,2); + if(iscs.length == 0) pgs.push(ps); + } + else { dir++; iscs.reverse(); } + if(dir>1) break; + } + var result = []; + for(var i=0; i>1, isc); + } + b1.x = b2.x; b1.y = b2.y; + b2.x = p[0]; b2.y = p[1]; + PolyK._pointLineDist(a1, b1, b2, l>>1, isc); + + var idst = 1/isc.dist; + isc.norm.x = (x-isc.point.x)*idst; + isc.norm.y = (y-isc.point.y)*idst; + return isc; + } + + PolyK._pointLineDist = function(p, a, b, edge, isc) + { + var x = p.x, y = p.y, x1 = a.x, y1 = a.y, x2 = b.x, y2 = b.y; + + var A = x - x1; + var B = y - y1; + var C = x2 - x1; + var D = y2 - y1; + + var dot = A * C + B * D; + var len_sq = C * C + D * D; + var param = dot / len_sq; + + var xx, yy; + + if (param < 0 || (x1 == x2 && y1 == y2)) { + xx = x1; + yy = y1; + } + else if (param > 1) { + xx = x2; + yy = y2; + } + else { + xx = x1 + param * C; + yy = y1 + param * D; + } + + var dx = x - xx; + var dy = y - yy; + var dst = Math.sqrt(dx * dx + dy * dy); + if(dst= 0) && (v >= 0) && (u + v < 1); + } + /* + PolyK._RayLineIntersection = function(a1, a2, b1, b2, c) + { + var dax = (a1.x-a2.x), dbx = (b1.x-b2.x); + var day = (a1.y-a2.y), dby = (b1.y-b2.y); + + var Den = dax*dby - day*dbx; + if (Den == 0) return null; // parallel + + var A = (a1.x * a2.y - a1.y * a2.x); + var B = (b1.x * b2.y - b1.y * b2.x); + + var I = c; + var iDen = 1/Den; + I.x = ( A*dbx - dax*B ) * iDen; + I.y = ( A*dby - day*B ) * iDen; + + if(!PolyK._InRect(I, b1, b2)) return null; + if((day>0 && I.y>a1.y) || (day<0 && I.y0 && I.x>a1.x) || (dax<0 && I.x=Math.min(b.y, c.y) && a.y<=Math.max(b.y, c.y)); + if (b.y == c.y) return (a.x>=Math.min(b.x, c.x) && a.x<=Math.max(b.x, c.x)); + + if(a.x >= Math.min(b.x, c.x) && a.x <= Math.max(b.x, c.x) + && a.y >= Math.min(b.y, c.y) && a.y <= Math.max(b.y, c.y)) + return true; + return false; + } + */ + PolyK._convex = function(ax, ay, bx, by, cx, cy) + { + return (ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0; + } + /* + PolyK._P = function(x,y) + { + this.x = x; + this.y = y; + this.flag = false; + } + PolyK._P.prototype.toString = function() + { + return "Point ["+this.x+", "+this.y+"]"; + } + PolyK._P.dist = function(a,b) + { + var dx = b.x-a.x; + var dy = b.y-a.y; + return Math.sqrt(dx*dx + dy*dy); + } + + PolyK._tp = []; + for(var i=0; i<10; i++) PolyK._tp.push(new PolyK._P(0,0)); + */ + +module.exports = PolyK; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/math/polyk.js","/math") +},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],33:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/** + * The vec2 object from glMatrix, with some extensions and some removed methods. See http://glmatrix.net. + * @class vec2 + */ + +var vec2 = module.exports = {}; + +var Utils = _dereq_('../utils/Utils'); + +/** + * Make a cross product and only return the z component + * @method crossLength + * @static + * @param {Array} a + * @param {Array} b + * @return {Number} + */ +vec2.crossLength = function(a,b){ + return a[0] * b[1] - a[1] * b[0]; +}; + +/** + * Cross product between a vector and the Z component of a vector + * @method crossVZ + * @static + * @param {Array} out + * @param {Array} vec + * @param {Number} zcomp + * @return {Number} + */ +vec2.crossVZ = function(out, vec, zcomp){ + vec2.rotate(out,vec,-Math.PI/2);// Rotate according to the right hand rule + vec2.scale(out,out,zcomp); // Scale with z + return out; +}; + +/** + * Cross product between a vector and the Z component of a vector + * @method crossZV + * @static + * @param {Array} out + * @param {Number} zcomp + * @param {Array} vec + * @return {Number} + */ +vec2.crossZV = function(out, zcomp, vec){ + vec2.rotate(out,vec,Math.PI/2); // Rotate according to the right hand rule + vec2.scale(out,out,zcomp); // Scale with z + return out; +}; + +/** + * Rotate a vector by an angle + * @method rotate + * @static + * @param {Array} out + * @param {Array} a + * @param {Number} angle + */ +vec2.rotate = function(out,a,angle){ + if(angle !== 0){ + var c = Math.cos(angle), + s = Math.sin(angle), + x = a[0], + y = a[1]; + out[0] = c*x -s*y; + out[1] = s*x +c*y; + } else { + out[0] = a[0]; + out[1] = a[1]; + } +}; + +/** + * Rotate a vector 90 degrees clockwise + * @method rotate90cw + * @static + * @param {Array} out + * @param {Array} a + * @param {Number} angle + */ +vec2.rotate90cw = function(out, a) { + var x = a[0]; + var y = a[1]; + out[0] = y; + out[1] = -x; +}; + +/** + * Transform a point position to local frame. + * @method toLocalFrame + * @param {Array} out + * @param {Array} worldPoint + * @param {Array} framePosition + * @param {Number} frameAngle + */ +vec2.toLocalFrame = function(out, worldPoint, framePosition, frameAngle){ + vec2.copy(out, worldPoint); + vec2.sub(out, out, framePosition); + vec2.rotate(out, out, -frameAngle); +}; + +/** + * Transform a point position to global frame. + * @method toGlobalFrame + * @param {Array} out + * @param {Array} localPoint + * @param {Array} framePosition + * @param {Number} frameAngle + */ +vec2.toGlobalFrame = function(out, localPoint, framePosition, frameAngle){ + vec2.copy(out, localPoint); + vec2.rotate(out, out, frameAngle); + vec2.add(out, out, framePosition); +}; + +/** + * Compute centroid of a triangle spanned by vectors a,b,c. See http://easycalculation.com/analytical/learn-centroid.php + * @method centroid + * @static + * @param {Array} out + * @param {Array} a + * @param {Array} b + * @param {Array} c + * @return {Array} The out object + */ +vec2.centroid = function(out, a, b, c){ + vec2.add(out, a, b); + vec2.add(out, out, c); + vec2.scale(out, out, 1/3); + return out; +}; + +/** + * Creates a new, empty vec2 + * @static + * @method create + * @returns {Array} a new 2D vector + */ +vec2.create = function() { + var out = new Utils.ARRAY_TYPE(2); + out[0] = 0; + out[1] = 0; + return out; +}; + +/** + * Creates a new vec2 initialized with values from an existing vector + * @static + * @method clone + * @param {Array} a vector to clone + * @returns {Array} a new 2D vector + */ +vec2.clone = function(a) { + var out = new Utils.ARRAY_TYPE(2); + out[0] = a[0]; + out[1] = a[1]; + return out; +}; + +/** + * Creates a new vec2 initialized with the given values + * @static + * @method fromValues + * @param {Number} x X component + * @param {Number} y Y component + * @returns {Array} a new 2D vector + */ +vec2.fromValues = function(x, y) { + var out = new Utils.ARRAY_TYPE(2); + out[0] = x; + out[1] = y; + return out; +}; + +/** + * Copy the values from one vec2 to another + * @static + * @method copy + * @param {Array} out the receiving vector + * @param {Array} a the source vector + * @returns {Array} out + */ +vec2.copy = function(out, a) { + out[0] = a[0]; + out[1] = a[1]; + return out; +}; + +/** + * Set the components of a vec2 to the given values + * @static + * @method set + * @param {Array} out the receiving vector + * @param {Number} x X component + * @param {Number} y Y component + * @returns {Array} out + */ +vec2.set = function(out, x, y) { + out[0] = x; + out[1] = y; + return out; +}; + +/** + * Adds two vec2's + * @static + * @method add + * @param {Array} out the receiving vector + * @param {Array} a the first operand + * @param {Array} b the second operand + * @returns {Array} out + */ +vec2.add = function(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + return out; +}; + +/** + * Subtracts two vec2's + * @static + * @method subtract + * @param {Array} out the receiving vector + * @param {Array} a the first operand + * @param {Array} b the second operand + * @returns {Array} out + */ +vec2.subtract = function(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + return out; +}; + +/** + * Alias for vec2.subtract + * @static + * @method sub + */ +vec2.sub = vec2.subtract; + +/** + * Multiplies two vec2's + * @static + * @method multiply + * @param {Array} out the receiving vector + * @param {Array} a the first operand + * @param {Array} b the second operand + * @returns {Array} out + */ +vec2.multiply = function(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + return out; +}; + +/** + * Alias for vec2.multiply + * @static + * @method mul + */ +vec2.mul = vec2.multiply; + +/** + * Divides two vec2's + * @static + * @method divide + * @param {Array} out the receiving vector + * @param {Array} a the first operand + * @param {Array} b the second operand + * @returns {Array} out + */ +vec2.divide = function(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + return out; +}; + +/** + * Alias for vec2.divide + * @static + * @method div + */ +vec2.div = vec2.divide; + +/** + * Scales a vec2 by a scalar number + * @static + * @method scale + * @param {Array} out the receiving vector + * @param {Array} a the vector to scale + * @param {Number} b amount to scale the vector by + * @returns {Array} out + */ +vec2.scale = function(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + return out; +}; + +/** + * Calculates the euclidian distance between two vec2's + * @static + * @method distance + * @param {Array} a the first operand + * @param {Array} b the second operand + * @returns {Number} distance between a and b + */ +vec2.distance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return Math.sqrt(x*x + y*y); +}; + +/** + * Alias for vec2.distance + * @static + * @method dist + */ +vec2.dist = vec2.distance; + +/** + * Calculates the squared euclidian distance between two vec2's + * @static + * @method squaredDistance + * @param {Array} a the first operand + * @param {Array} b the second operand + * @returns {Number} squared distance between a and b + */ +vec2.squaredDistance = function(a, b) { + var x = b[0] - a[0], + y = b[1] - a[1]; + return x*x + y*y; +}; + +/** + * Alias for vec2.squaredDistance + * @static + * @method sqrDist + */ +vec2.sqrDist = vec2.squaredDistance; + +/** + * Calculates the length of a vec2 + * @static + * @method length + * @param {Array} a vector to calculate length of + * @returns {Number} length of a + */ +vec2.length = function (a) { + var x = a[0], + y = a[1]; + return Math.sqrt(x*x + y*y); +}; + +/** + * Alias for vec2.length + * @method len + * @static + */ +vec2.len = vec2.length; + +/** + * Calculates the squared length of a vec2 + * @static + * @method squaredLength + * @param {Array} a vector to calculate squared length of + * @returns {Number} squared length of a + */ +vec2.squaredLength = function (a) { + var x = a[0], + y = a[1]; + return x*x + y*y; +}; + +/** + * Alias for vec2.squaredLength + * @static + * @method sqrLen + */ +vec2.sqrLen = vec2.squaredLength; + +/** + * Negates the components of a vec2 + * @static + * @method negate + * @param {Array} out the receiving vector + * @param {Array} a vector to negate + * @returns {Array} out + */ +vec2.negate = function(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + return out; +}; + +/** + * Normalize a vec2 + * @static + * @method normalize + * @param {Array} out the receiving vector + * @param {Array} a vector to normalize + * @returns {Array} out + */ +vec2.normalize = function(out, a) { + var x = a[0], + y = a[1]; + var len = x*x + y*y; + if (len > 0) { + //TODO: evaluate use of glm_invsqrt here? + len = 1 / Math.sqrt(len); + out[0] = a[0] * len; + out[1] = a[1] * len; + } + return out; +}; + +/** + * Calculates the dot product of two vec2's + * @static + * @method dot + * @param {Array} a the first operand + * @param {Array} b the second operand + * @returns {Number} dot product of a and b + */ +vec2.dot = function (a, b) { + return a[0] * b[0] + a[1] * b[1]; +}; + +/** + * Returns a string representation of a vector + * @static + * @method str + * @param {Array} vec vector to represent as a string + * @returns {String} string representation of the vector + */ +vec2.str = function (a) { + return 'vec2(' + a[0] + ', ' + a[1] + ')'; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/math/vec2.js","/math") +},{"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],34:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var vec2 = _dereq_('../math/vec2') +, decomp = _dereq_('poly-decomp') +, Convex = _dereq_('../shapes/Convex') +, AABB = _dereq_('../collision/AABB') +, EventEmitter = _dereq_('../events/EventEmitter'); + +module.exports = Body; + +/** + * A rigid body. Has got a center of mass, position, velocity and a number of + * shapes that are used for collisions. + * + * @class Body + * @constructor + * @extends EventEmitter + * @param {Object} [options] + * @param {Number} [options.mass=0] A number >= 0. If zero, the .type will be set to Body.STATIC. + * @param {Array} [options.position] + * @param {Array} [options.velocity] + * @param {Number} [options.angle=0] + * @param {Number} [options.angularVelocity=0] + * @param {Array} [options.force] + * @param {Number} [options.angularForce=0] + * @param {Number} [options.fixedRotation=false] + * + * @example + * // Create a typical dynamic body + * var body = new Body({ + * mass: 1, + * position: [0, 0], + * angle: 0, + * velocity: [0, 0], + * angularVelocity: 0 + * }); + * + * // Add a circular shape to the body + * body.addShape(new Circle(1)); + * + * // Add the body to the world + * world.addBody(body); + */ +function Body(options){ + options = options || {}; + + EventEmitter.call(this); + + /** + * The body identifyer + * @property id + * @type {Number} + */ + this.id = ++Body._idCounter; + + /** + * The world that this body is added to. This property is set to NULL if the body is not added to any world. + * @property world + * @type {World} + */ + this.world = null; + + /** + * The shapes of the body. The local transform of the shape in .shapes[i] is + * defined by .shapeOffsets[i] and .shapeAngles[i]. + * + * @property shapes + * @type {Array} + */ + this.shapes = []; + + /** + * The local shape offsets, relative to the body center of mass. This is an + * array of Array. + * @property shapeOffsets + * @type {Array} + */ + this.shapeOffsets = []; + + /** + * The body-local shape angle transforms. This is an array of numbers (angles). + * @property shapeAngles + * @type {Array} + */ + this.shapeAngles = []; + + /** + * The mass of the body. + * @property mass + * @type {number} + */ + this.mass = options.mass || 0; + + /** + * The inverse mass of the body. + * @property invMass + * @type {number} + */ + this.invMass = 0; + + /** + * The inertia of the body around the Z axis. + * @property inertia + * @type {number} + */ + this.inertia = 0; + + /** + * The inverse inertia of the body. + * @property invInertia + * @type {number} + */ + this.invInertia = 0; + + this.invMassSolve = 0; + this.invInertiaSolve = 0; + + /** + * Set to true if you want to fix the rotation of the body. + * @property fixedRotation + * @type {Boolean} + */ + this.fixedRotation = !!options.fixedRotation || false; + + /** + * The position of the body + * @property position + * @type {Array} + */ + this.position = vec2.fromValues(0,0); + if(options.position){ + vec2.copy(this.position, options.position); + } + + /** + * The interpolated position of the body. + * @property interpolatedPosition + * @type {Array} + */ + this.interpolatedPosition = vec2.fromValues(0,0); + + /** + * The interpolated angle of the body. + * @property interpolatedAngle + * @type {Number} + */ + this.interpolatedAngle = 0; + + /** + * The previous position of the body. + * @property previousPosition + * @type {Array} + */ + this.previousPosition = vec2.fromValues(0,0); + + /** + * The previous angle of the body. + * @property previousAngle + * @type {Number} + */ + this.previousAngle = 0; + + /** + * The velocity of the body + * @property velocity + * @type {Array} + */ + this.velocity = vec2.fromValues(0,0); + if(options.velocity){ + vec2.copy(this.velocity, options.velocity); + } + + /** + * Constraint velocity that was added to the body during the last step. + * @property vlambda + * @type {Array} + */ + this.vlambda = vec2.fromValues(0,0); + + /** + * Angular constraint velocity that was added to the body during last step. + * @property wlambda + * @type {Array} + */ + this.wlambda = 0; + + /** + * The angle of the body, in radians. + * @property angle + * @type {number} + * @example + * // The angle property is not normalized to the interval 0 to 2*pi, it can be any value. + * // If you need a value between 0 and 2*pi, use the following function to normalize it. + * function normalizeAngle(angle){ + * angle = angle % (2*Math.PI); + * if(angle < 0){ + * angle += (2*Math.PI); + * } + * return angle; + * } + */ + this.angle = options.angle || 0; + + /** + * The angular velocity of the body, in radians per second. + * @property angularVelocity + * @type {number} + */ + this.angularVelocity = options.angularVelocity || 0; + + /** + * The force acting on the body. Since the body force (and {{#crossLink "Body/angularForce:property"}}{{/crossLink}}) will be zeroed after each step, so you need to set the force before each step. + * @property force + * @type {Array} + * + * @example + * // This produces a forcefield of 1 Newton in the positive x direction. + * for(var i=0; i radius){ + radius = offset + r; + } + } + + this.boundingRadius = radius; +}; + +/** + * Add a shape to the body. You can pass a local transform when adding a shape, + * so that the shape gets an offset and angle relative to the body center of mass. + * Will automatically update the mass properties and bounding radius. + * + * @method addShape + * @param {Shape} shape + * @param {Array} [offset] Local body offset of the shape. + * @param {Number} [angle] Local body angle. + * + * @example + * var body = new Body(), + * shape = new Circle(); + * + * // Add the shape to the body, positioned in the center + * body.addShape(shape); + * + * // Add another shape to the body, positioned 1 unit length from the body center of mass along the local x-axis. + * body.addShape(shape,[1,0]); + * + * // Add another shape to the body, positioned 1 unit length from the body center of mass along the local y-axis, and rotated 90 degrees CCW. + * body.addShape(shape,[0,1],Math.PI/2); + */ +Body.prototype.addShape = function(shape,offset,angle){ + angle = angle || 0.0; + + // Copy the offset vector + if(offset){ + offset = vec2.fromValues(offset[0],offset[1]); + } else { + offset = vec2.fromValues(0,0); + } + + this.shapes .push(shape); + this.shapeOffsets.push(offset); + this.shapeAngles .push(angle); + this.updateMassProperties(); + this.updateBoundingRadius(); + + this.aabbNeedsUpdate = true; +}; + +/** + * Remove a shape + * @method removeShape + * @param {Shape} shape + * @return {Boolean} True if the shape was found and removed, else false. + */ +Body.prototype.removeShape = function(shape){ + var idx = this.shapes.indexOf(shape); + + if(idx !== -1){ + this.shapes.splice(idx,1); + this.shapeOffsets.splice(idx,1); + this.shapeAngles.splice(idx,1); + this.aabbNeedsUpdate = true; + return true; + } else { + return false; + } +}; + +/** + * Updates .inertia, .invMass, .invInertia for this Body. Should be called when + * changing the structure or mass of the Body. + * + * @method updateMassProperties + * + * @example + * body.mass += 1; + * body.updateMassProperties(); + */ +Body.prototype.updateMassProperties = function(){ + if(this.type === Body.STATIC || this.type === Body.KINEMATIC){ + + this.mass = Number.MAX_VALUE; + this.invMass = 0; + this.inertia = Number.MAX_VALUE; + this.invInertia = 0; + + } else { + + var shapes = this.shapes, + N = shapes.length, + m = this.mass / N, + I = 0; + + if(!this.fixedRotation){ + for(var i=0; i0 ? 1/I : 0; + + } else { + this.inertia = Number.MAX_VALUE; + this.invInertia = 0; + } + + // Inverse mass properties are easy + this.invMass = 1/this.mass;// > 0 ? 1/this.mass : 0; + } +}; + +var Body_applyForce_r = vec2.create(); + +/** + * Apply force to a world point. This could for example be a point on the RigidBody surface. Applying force this way will add to Body.force and Body.angularForce. + * @method applyForce + * @param {Array} force The force to add. + * @param {Array} worldPoint A world point to apply the force on. + */ +Body.prototype.applyForce = function(force,worldPoint){ + // Compute point position relative to the body center + var r = Body_applyForce_r; + vec2.sub(r,worldPoint,this.position); + + // Add linear force + vec2.add(this.force,this.force,force); + + // Compute produced rotational force + var rotForce = vec2.crossLength(r,force); + + // Add rotational force + this.angularForce += rotForce; +}; + +/** + * Transform a world point to local body frame. + * @method toLocalFrame + * @param {Array} out The vector to store the result in + * @param {Array} worldPoint The input world vector + */ +Body.prototype.toLocalFrame = function(out, worldPoint){ + vec2.toLocalFrame(out, worldPoint, this.position, this.angle); +}; + +/** + * Transform a local point to world frame. + * @method toWorldFrame + * @param {Array} out The vector to store the result in + * @param {Array} localPoint The input local vector + */ +Body.prototype.toWorldFrame = function(out, localPoint){ + vec2.toGlobalFrame(out, localPoint, this.position, this.angle); +}; + +/** + * Reads a polygon shape path, and assembles convex shapes from that and puts them at proper offset points. + * @method fromPolygon + * @param {Array} path An array of 2d vectors, e.g. [[0,0],[0,1],...] that resembles a concave or convex polygon. The shape must be simple and without holes. + * @param {Object} [options] + * @param {Boolean} [options.optimalDecomp=false] Set to true if you need optimal decomposition. Warning: very slow for polygons with more than 10 vertices. + * @param {Boolean} [options.skipSimpleCheck=false] Set to true if you already know that the path is not intersecting itself. + * @param {Boolean|Number} [options.removeCollinearPoints=false] Set to a number (angle threshold value) to remove collinear points, or false to keep all points. + * @return {Boolean} True on success, else false. + */ +Body.prototype.fromPolygon = function(path,options){ + options = options || {}; + + // Remove all shapes + for(var i=this.shapes.length; i>=0; --i){ + this.removeShape(this.shapes[i]); + } + + var p = new decomp.Polygon(); + p.vertices = path; + + // Make it counter-clockwise + p.makeCCW(); + + if(typeof(options.removeCollinearPoints) === "number"){ + p.removeCollinearPoints(options.removeCollinearPoints); + } + + // Check if any line segment intersects the path itself + if(typeof(options.skipSimpleCheck) === "undefined"){ + if(!p.isSimple()){ + return false; + } + } + + // Save this path for later + this.concavePath = p.vertices.slice(0); + for(var i=0; ithis for details. + * @method applyDamping + * @param {number} dt Current time step + */ +Body.prototype.applyDamping = function(dt){ + if(this.type === Body.DYNAMIC){ // Only for dynamic bodies + + // Since Math.pow generates garbage we check if we can reuse the scaling coefficient from last step + if(dt !== this.lastDampingTimeStep){ + this.lastDampingScale = Math.pow(1.0 - this.damping,dt); + this.lastAngularDampingScale = Math.pow(1.0 - this.angularDamping,dt); + this.lastDampingTimeStep = dt; + } + + var v = this.velocity; + vec2.scale(v,v,this.lastDampingScale); + this.angularVelocity *= this.lastAngularDampingScale; + } +}; + +/** + * Wake the body up. Normally you should not need this, as the body is automatically awoken at events such as collisions. + * Sets the sleepState to {{#crossLink "Body/AWAKE:property"}}Body.AWAKE{{/crossLink}} and emits the wakeUp event if the body wasn't awake before. + * @method wakeUp + */ +Body.prototype.wakeUp = function(){ + var s = this.sleepState; + this.sleepState = Body.AWAKE; + this.idleTime = 0; + if(s !== Body.AWAKE){ + this.emit(Body.wakeUpEvent); + } +}; + +/** + * Force body sleep + * @method sleep + */ +Body.prototype.sleep = function(){ + this.sleepState = Body.SLEEPING; + this.angularVelocity = 0; + this.angularForce = 0; + vec2.set(this.velocity,0,0); + vec2.set(this.force,0,0); + this.emit(Body.sleepEvent); +}; + +//var velo2 = vec2.create(); + +/** + * @method sleepTick + * @param float time The world time in seconds + * @brief Called every timestep to update internal sleep timer and change sleep state if needed. + */ +Body.prototype.sleepTick = function(time, dontSleep, dt){ + if(!this.allowSleep || this.type === Body.SLEEPING){ + return; + } + + this.wantsToSleep = false; + + var sleepState = this.sleepState, + speedSquared = vec2.squaredLength(this.velocity) + Math.pow(this.angularVelocity,2), + speedLimitSquared = Math.pow(this.sleepSpeedLimit,2); + + // Add to idle time + if(speedSquared >= speedLimitSquared){ + this.idleTime = 0; + this.sleepState = Body.AWAKE; + } else { + this.idleTime += dt; + this.sleepState = Body.SLEEPY; + } + if(this.idleTime > this.sleepTimeLimit){ + if(!dontSleep){ + this.sleep(); + } else { + this.wantsToSleep = true; + } + } + + /* + if(sleepState===Body.AWAKE && speedSquared < speedLimitSquared){ + this.sleepState = Body.SLEEPY; // Sleepy + this.timeLastSleepy = time; + this.emit(Body.sleepyEvent); + } else if(sleepState===Body.SLEEPY && speedSquared >= speedLimitSquared){ + this.wakeUp(); // Wake up + } else if(sleepState===Body.SLEEPY && (time - this.timeLastSleepy ) > this.sleepTimeLimit){ + this.wantsToSleep = true; + if(!dontSleep){ + this.sleep(); + } + } + */ +}; + +Body.prototype.getVelocityFromPosition = function(store, timeStep){ + store = store || vec2.create(); + vec2.sub(store, this.position, this.previousPosition); + vec2.scale(store, store, 1/timeStep); + return store; +}; +Body.prototype.getAngularVelocityFromPosition = function(timeStep){ + return (this.angle - this.previousAngle) / timeStep; +}; + +/** + * Check if the body is overlapping another body. Note that this method only works if the body was added to a World and if at least one step was taken. + * @method overlaps + * @param {Body} body + * @return {boolean} + */ +Body.prototype.overlaps = function(body){ + return this.world.overlapKeeper.bodiesAreOverlapping(this, body); +}; + +/** + * @event sleepy + */ +Body.sleepyEvent = { + type: "sleepy" +}; + +/** + * @event sleep + */ +Body.sleepEvent = { + type: "sleep" +}; + +/** + * @event wakeup + */ +Body.wakeUpEvent = { + type: "wakeup" +}; + +/** + * Dynamic body. + * @property DYNAMIC + * @type {Number} + * @static + */ +Body.DYNAMIC = 1; + +/** + * Static body. + * @property STATIC + * @type {Number} + * @static + */ +Body.STATIC = 2; + +/** + * Kinematic body. + * @property KINEMATIC + * @type {Number} + * @static + */ +Body.KINEMATIC = 4; + +/** + * @property AWAKE + * @type {Number} + * @static + */ +Body.AWAKE = 0; + +/** + * @property SLEEPY + * @type {Number} + * @static + */ +Body.SLEEPY = 1; + +/** + * @property SLEEPING + * @type {Number} + * @static + */ +Body.SLEEPING = 2; + + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/objects/Body.js","/objects") +},{"../collision/AABB":11,"../events/EventEmitter":29,"../math/vec2":33,"../shapes/Convex":41,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1,"poly-decomp":9}],35:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var vec2 = _dereq_('../math/vec2'); +var Spring = _dereq_('./Spring'); +var Utils = _dereq_('../utils/Utils'); + +module.exports = LinearSpring; + +/** + * A spring, connecting two bodies. + * + * The Spring explicitly adds force and angularForce to the bodies. + * + * @class LinearSpring + * @extends Spring + * @constructor + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Object} [options] + * @param {number} [options.restLength] A number > 0. Default is the current distance between the world anchor points. + * @param {number} [options.stiffness=100] Spring constant (see Hookes Law). A number >= 0. + * @param {number} [options.damping=1] A number >= 0. Default: 1 + * @param {Array} [options.worldAnchorA] Where to hook the spring to body A, in world coordinates. Overrides the option "localAnchorA" if given. + * @param {Array} [options.worldAnchorB] + * @param {Array} [options.localAnchorA] Where to hook the spring to body A, in local body coordinates. Defaults to the body center. + * @param {Array} [options.localAnchorB] + */ +function LinearSpring(bodyA,bodyB,options){ + options = options || {}; + + Spring.call(this, bodyA, bodyB, options); + + /** + * Anchor for bodyA in local bodyA coordinates. + * @property localAnchorA + * @type {Array} + */ + this.localAnchorA = vec2.fromValues(0,0); + + /** + * Anchor for bodyB in local bodyB coordinates. + * @property localAnchorB + * @type {Array} + */ + this.localAnchorB = vec2.fromValues(0,0); + + if(options.localAnchorA){ vec2.copy(this.localAnchorA, options.localAnchorA); } + if(options.localAnchorB){ vec2.copy(this.localAnchorB, options.localAnchorB); } + if(options.worldAnchorA){ this.setWorldAnchorA(options.worldAnchorA); } + if(options.worldAnchorB){ this.setWorldAnchorB(options.worldAnchorB); } + + var worldAnchorA = vec2.create(); + var worldAnchorB = vec2.create(); + this.getWorldAnchorA(worldAnchorA); + this.getWorldAnchorB(worldAnchorB); + var worldDistance = vec2.distance(worldAnchorA, worldAnchorB); + + /** + * Rest length of the spring. + * @property restLength + * @type {number} + */ + this.restLength = typeof(options.restLength) === "number" ? options.restLength : worldDistance; +} +LinearSpring.prototype = new Spring(); + +/** + * Set the anchor point on body A, using world coordinates. + * @method setWorldAnchorA + * @param {Array} worldAnchorA + */ +LinearSpring.prototype.setWorldAnchorA = function(worldAnchorA){ + this.bodyA.toLocalFrame(this.localAnchorA, worldAnchorA); +}; + +/** + * Set the anchor point on body B, using world coordinates. + * @method setWorldAnchorB + * @param {Array} worldAnchorB + */ +LinearSpring.prototype.setWorldAnchorB = function(worldAnchorB){ + this.bodyB.toLocalFrame(this.localAnchorB, worldAnchorB); +}; + +/** + * Get the anchor point on body A, in world coordinates. + * @method getWorldAnchorA + * @param {Array} result The vector to store the result in. + */ +LinearSpring.prototype.getWorldAnchorA = function(result){ + this.bodyA.toWorldFrame(result, this.localAnchorA); +}; + +/** + * Get the anchor point on body B, in world coordinates. + * @method getWorldAnchorB + * @param {Array} result The vector to store the result in. + */ +LinearSpring.prototype.getWorldAnchorB = function(result){ + this.bodyB.toWorldFrame(result, this.localAnchorB); +}; + +var applyForce_r = vec2.create(), + applyForce_r_unit = vec2.create(), + applyForce_u = vec2.create(), + applyForce_f = vec2.create(), + applyForce_worldAnchorA = vec2.create(), + applyForce_worldAnchorB = vec2.create(), + applyForce_ri = vec2.create(), + applyForce_rj = vec2.create(), + applyForce_tmp = vec2.create(); + +/** + * Apply the spring force to the connected bodies. + * @method applyForce + */ +LinearSpring.prototype.applyForce = function(){ + var k = this.stiffness, + d = this.damping, + l = this.restLength, + bodyA = this.bodyA, + bodyB = this.bodyB, + r = applyForce_r, + r_unit = applyForce_r_unit, + u = applyForce_u, + f = applyForce_f, + tmp = applyForce_tmp; + + var worldAnchorA = applyForce_worldAnchorA, + worldAnchorB = applyForce_worldAnchorB, + ri = applyForce_ri, + rj = applyForce_rj; + + // Get world anchors + this.getWorldAnchorA(worldAnchorA); + this.getWorldAnchorB(worldAnchorB); + + // Get offset points + vec2.sub(ri, worldAnchorA, bodyA.position); + vec2.sub(rj, worldAnchorB, bodyB.position); + + // Compute distance vector between world anchor points + vec2.sub(r, worldAnchorB, worldAnchorA); + var rlen = vec2.len(r); + vec2.normalize(r_unit,r); + + //console.log(rlen) + //console.log("A",vec2.str(worldAnchorA),"B",vec2.str(worldAnchorB)) + + // Compute relative velocity of the anchor points, u + vec2.sub(u, bodyB.velocity, bodyA.velocity); + vec2.crossZV(tmp, bodyB.angularVelocity, rj); + vec2.add(u, u, tmp); + vec2.crossZV(tmp, bodyA.angularVelocity, ri); + vec2.sub(u, u, tmp); + + // F = - k * ( x - L ) - D * ( u ) + vec2.scale(f, r_unit, -k*(rlen-l) - d*vec2.dot(u,r_unit)); + + // Add forces to bodies + vec2.sub( bodyA.force, bodyA.force, f); + vec2.add( bodyB.force, bodyB.force, f); + + // Angular force + var ri_x_f = vec2.crossLength(ri, f); + var rj_x_f = vec2.crossLength(rj, f); + bodyA.angularForce -= ri_x_f; + bodyB.angularForce += rj_x_f; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/objects/LinearSpring.js","/objects") +},{"../math/vec2":33,"../utils/Utils":52,"./Spring":37,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],36:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var vec2 = _dereq_('../math/vec2'); +var Spring = _dereq_('./Spring'); + +module.exports = RotationalSpring; + +/** + * A rotational spring, connecting two bodies rotation. This spring explicitly adds angularForce (torque) to the bodies. + * + * The spring can be combined with a {{#crossLink "RevoluteConstraint"}}{{/crossLink}} to make, for example, a mouse trap. + * + * @class RotationalSpring + * @extends Spring + * @constructor + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Object} [options] + * @param {number} [options.restAngle] The relative angle of bodies at which the spring is at rest. If not given, it's set to the current relative angle between the bodies. + * @param {number} [options.stiffness=100] Spring constant (see Hookes Law). A number >= 0. + * @param {number} [options.damping=1] A number >= 0. + */ +function RotationalSpring(bodyA, bodyB, options){ + options = options || {}; + + Spring.call(this, bodyA, bodyB, options); + + /** + * Rest angle of the spring. + * @property restAngle + * @type {number} + */ + this.restAngle = typeof(options.restAngle) === "number" ? options.restAngle : bodyB.angle - bodyA.angle; +} +RotationalSpring.prototype = new Spring(); + +/** + * Apply the spring force to the connected bodies. + * @method applyForce + */ +RotationalSpring.prototype.applyForce = function(){ + var k = this.stiffness, + d = this.damping, + l = this.restAngle, + bodyA = this.bodyA, + bodyB = this.bodyB, + x = bodyB.angle - bodyA.angle, + u = bodyB.angularVelocity - bodyA.angularVelocity; + + var torque = - k * (x - l) - d * u * 0; + + bodyA.angularForce -= torque; + bodyB.angularForce += torque; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/objects/RotationalSpring.js","/objects") +},{"../math/vec2":33,"./Spring":37,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],37:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var vec2 = _dereq_('../math/vec2'); +var Utils = _dereq_('../utils/Utils'); + +module.exports = Spring; + +/** + * A spring, connecting two bodies. The Spring explicitly adds force and angularForce to the bodies and does therefore not put load on the constraint solver. + * + * @class Spring + * @constructor + * @param {Body} bodyA + * @param {Body} bodyB + * @param {Object} [options] + * @param {number} [options.stiffness=100] Spring constant (see Hookes Law). A number >= 0. + * @param {number} [options.damping=1] A number >= 0. Default: 1 + * @param {Array} [options.localAnchorA] Where to hook the spring to body A, in local body coordinates. Defaults to the body center. + * @param {Array} [options.localAnchorB] + * @param {Array} [options.worldAnchorA] Where to hook the spring to body A, in world coordinates. Overrides the option "localAnchorA" if given. + * @param {Array} [options.worldAnchorB] + */ +function Spring(bodyA, bodyB, options){ + options = Utils.defaults(options,{ + stiffness: 100, + damping: 1, + }); + + /** + * Stiffness of the spring. + * @property stiffness + * @type {number} + */ + this.stiffness = options.stiffness; + + /** + * Damping of the spring. + * @property damping + * @type {number} + */ + this.damping = options.damping; + + /** + * First connected body. + * @property bodyA + * @type {Body} + */ + this.bodyA = bodyA; + + /** + * Second connected body. + * @property bodyB + * @type {Body} + */ + this.bodyB = bodyB; +} + +/** + * Apply the spring force to the connected bodies. + * @method applyForce + */ +Spring.prototype.applyForce = function(){ + // To be implemented by subclasses +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/objects/Spring.js","/objects") +},{"../math/vec2":33,"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],38:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +// Export p2 classes +module.exports = { + AABB : _dereq_('./collision/AABB'), + AngleLockEquation : _dereq_('./equations/AngleLockEquation'), + Body : _dereq_('./objects/Body'), + Broadphase : _dereq_('./collision/Broadphase'), + Capsule : _dereq_('./shapes/Capsule'), + Circle : _dereq_('./shapes/Circle'), + Constraint : _dereq_('./constraints/Constraint'), + ContactEquation : _dereq_('./equations/ContactEquation'), + ContactMaterial : _dereq_('./material/ContactMaterial'), + Convex : _dereq_('./shapes/Convex'), + DistanceConstraint : _dereq_('./constraints/DistanceConstraint'), + Equation : _dereq_('./equations/Equation'), + EventEmitter : _dereq_('./events/EventEmitter'), + FrictionEquation : _dereq_('./equations/FrictionEquation'), + GearConstraint : _dereq_('./constraints/GearConstraint'), + GridBroadphase : _dereq_('./collision/GridBroadphase'), + GSSolver : _dereq_('./solver/GSSolver'), + Heightfield : _dereq_('./shapes/Heightfield'), + Line : _dereq_('./shapes/Line'), + LockConstraint : _dereq_('./constraints/LockConstraint'), + Material : _dereq_('./material/Material'), + Narrowphase : _dereq_('./collision/Narrowphase'), + NaiveBroadphase : _dereq_('./collision/NaiveBroadphase'), + Particle : _dereq_('./shapes/Particle'), + Plane : _dereq_('./shapes/Plane'), + RevoluteConstraint : _dereq_('./constraints/RevoluteConstraint'), + PrismaticConstraint : _dereq_('./constraints/PrismaticConstraint'), + Rectangle : _dereq_('./shapes/Rectangle'), + RotationalVelocityEquation : _dereq_('./equations/RotationalVelocityEquation'), + SAPBroadphase : _dereq_('./collision/SAPBroadphase'), + Shape : _dereq_('./shapes/Shape'), + Solver : _dereq_('./solver/Solver'), + Spring : _dereq_('./objects/Spring'), + LinearSpring : _dereq_('./objects/LinearSpring'), + RotationalSpring : _dereq_('./objects/RotationalSpring'), + Utils : _dereq_('./utils/Utils'), + World : _dereq_('./world/World'), + vec2 : _dereq_('./math/vec2'), + version : _dereq_('../package.json').version, +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/p2.js","/") +},{"../package.json":10,"./collision/AABB":11,"./collision/Broadphase":12,"./collision/GridBroadphase":13,"./collision/NaiveBroadphase":14,"./collision/Narrowphase":15,"./collision/SAPBroadphase":16,"./constraints/Constraint":17,"./constraints/DistanceConstraint":18,"./constraints/GearConstraint":19,"./constraints/LockConstraint":20,"./constraints/PrismaticConstraint":21,"./constraints/RevoluteConstraint":22,"./equations/AngleLockEquation":23,"./equations/ContactEquation":24,"./equations/Equation":25,"./equations/FrictionEquation":26,"./equations/RotationalVelocityEquation":28,"./events/EventEmitter":29,"./material/ContactMaterial":30,"./material/Material":31,"./math/vec2":33,"./objects/Body":34,"./objects/LinearSpring":35,"./objects/RotationalSpring":36,"./objects/Spring":37,"./shapes/Capsule":39,"./shapes/Circle":40,"./shapes/Convex":41,"./shapes/Heightfield":42,"./shapes/Line":43,"./shapes/Particle":44,"./shapes/Plane":45,"./shapes/Rectangle":46,"./shapes/Shape":47,"./solver/GSSolver":48,"./solver/Solver":49,"./utils/Utils":52,"./world/World":56,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],39:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Shape = _dereq_('./Shape') +, vec2 = _dereq_('../math/vec2'); + +module.exports = Capsule; + +/** + * Capsule shape class. + * @class Capsule + * @constructor + * @extends Shape + * @param {Number} [length] The distance between the end points + * @param {Number} [radius] Radius of the capsule + */ +function Capsule(length, radius){ + + /** + * The distance between the end points. + * @property {Number} length + */ + this.length = length || 1; + + /** + * The radius of the capsule. + * @property {Number} radius + */ + this.radius = radius || 1; + + Shape.call(this,Shape.CAPSULE); +} +Capsule.prototype = new Shape(); + +/** + * Compute the mass moment of inertia of the Capsule. + * @method conputeMomentOfInertia + * @param {Number} mass + * @return {Number} + * @todo + */ +Capsule.prototype.computeMomentOfInertia = function(mass){ + // Approximate with rectangle + var r = this.radius, + w = this.length + r, // 2*r is too much, 0 is too little + h = r*2; + return mass * (h*h + w*w) / 12; +}; + +/** + * @method updateBoundingRadius + */ +Capsule.prototype.updateBoundingRadius = function(){ + this.boundingRadius = this.radius + this.length/2; +}; + +/** + * @method updateArea + */ +Capsule.prototype.updateArea = function(){ + this.area = Math.PI * this.radius * this.radius + this.radius * 2 * this.length; +}; + +var r = vec2.create(); + +/** + * @method computeAABB + * @param {AABB} out The resulting AABB. + * @param {Array} position + * @param {Number} angle + */ +Capsule.prototype.computeAABB = function(out, position, angle){ + var radius = this.radius; + + // Compute center position of one of the the circles, world oriented, but with local offset + vec2.set(r,this.length / 2,0); + if(angle !== 0){ + vec2.rotate(r,r,angle); + } + + // Get bounds + vec2.set(out.upperBound, Math.max(r[0]+radius, -r[0]+radius), + Math.max(r[1]+radius, -r[1]+radius)); + vec2.set(out.lowerBound, Math.min(r[0]-radius, -r[0]-radius), + Math.min(r[1]-radius, -r[1]-radius)); + + // Add offset + vec2.add(out.lowerBound, out.lowerBound, position); + vec2.add(out.upperBound, out.upperBound, position); +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/shapes/Capsule.js","/shapes") +},{"../math/vec2":33,"./Shape":47,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],40:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Shape = _dereq_('./Shape') +, vec2 = _dereq_('../math/vec2'); + +module.exports = Circle; + +/** + * Circle shape class. + * @class Circle + * @extends Shape + * @constructor + * @param {number} [radius=1] The radius of this circle + */ +function Circle(radius){ + + /** + * The radius of the circle. + * @property radius + * @type {number} + */ + this.radius = radius || 1; + + Shape.call(this,Shape.CIRCLE); +} +Circle.prototype = new Shape(); + +/** + * @method computeMomentOfInertia + * @param {Number} mass + * @return {Number} + */ +Circle.prototype.computeMomentOfInertia = function(mass){ + var r = this.radius; + return mass * r * r / 2; +}; + +/** + * @method updateBoundingRadius + * @return {Number} + */ +Circle.prototype.updateBoundingRadius = function(){ + this.boundingRadius = this.radius; +}; + +/** + * @method updateArea + * @return {Number} + */ +Circle.prototype.updateArea = function(){ + this.area = Math.PI * this.radius * this.radius; +}; + +/** + * @method computeAABB + * @param {AABB} out The resulting AABB. + * @param {Array} position + * @param {Number} angle + */ +Circle.prototype.computeAABB = function(out, position, angle){ + var r = this.radius; + vec2.set(out.upperBound, r, r); + vec2.set(out.lowerBound, -r, -r); + if(position){ + vec2.add(out.lowerBound, out.lowerBound, position); + vec2.add(out.upperBound, out.upperBound, position); + } +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/shapes/Circle.js","/shapes") +},{"../math/vec2":33,"./Shape":47,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],41:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Shape = _dereq_('./Shape') +, vec2 = _dereq_('../math/vec2') +, polyk = _dereq_('../math/polyk') +, decomp = _dereq_('poly-decomp'); + +module.exports = Convex; + +/** + * Convex shape class. + * @class Convex + * @constructor + * @extends Shape + * @param {Array} vertices An array of vertices that span this shape. Vertices are given in counter-clockwise (CCW) direction. + * @param {Array} axes An array of unit length vectors + */ +function Convex(vertices, axes){ + + /** + * Vertices defined in the local frame. + * @property vertices + * @type {Array} + */ + this.vertices = []; + + /** + * Axes defined in the local frame. + * @property axes + * @type {Array} + */ + this.axes = []; + + // Copy the verts + for(var i=0; i max){ + max = value; + } + if(min === null || value < min){ + min = value; + } + } + + if(min > max){ + var t = min; + min = max; + max = t; + } + + vec2.set(result, min, max); +}; + +Convex.prototype.projectOntoWorldAxis = function(localAxis, shapeOffset, shapeAngle, result){ + var worldAxis = tmpVec2; + + this.projectOntoLocalAxis(localAxis, result); + + // Project the position of the body onto the axis - need to add this to the result + if(shapeAngle !== 0){ + vec2.rotate(worldAxis, localAxis, shapeAngle); + } else { + worldAxis = localAxis; + } + var offset = vec2.dot(shapeOffset, worldAxis); + + vec2.set(result, result[0] + offset, result[1] + offset); +}; + + +/** + * Update the .triangles property + * @method updateTriangles + */ +Convex.prototype.updateTriangles = function(){ + + this.triangles.length = 0; + + // Rewrite on polyk notation, array of numbers + var polykVerts = []; + for(var i=0; i r2){ + r2 = l2; + } + } + + this.boundingRadius = Math.sqrt(r2); +}; + +/** + * Get the area of the triangle spanned by the three points a, b, c. The area is positive if the points are given in counter-clockwise order, otherwise negative. + * @static + * @method triangleArea + * @param {Array} a + * @param {Array} b + * @param {Array} c + * @return {Number} + */ +Convex.triangleArea = function(a,b,c){ + return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1]))) * 0.5; +}; + +/** + * Update the .area + * @method updateArea + */ +Convex.prototype.updateArea = function(){ + this.updateTriangles(); + this.area = 0; + + var triangles = this.triangles, + verts = this.vertices; + for(var i=0; i!==triangles.length; i++){ + var t = triangles[i], + a = verts[t[0]], + b = verts[t[1]], + c = verts[t[2]]; + + // Get mass for the triangle (density=1 in this case) + var m = Convex.triangleArea(a,b,c); + this.area += m; + } +}; + +/** + * @method computeAABB + * @param {AABB} out + * @param {Array} position + * @param {Number} angle + */ +Convex.prototype.computeAABB = function(out, position, angle){ + out.setFromPoints(this.vertices, position, angle, 0); +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/shapes/Convex.js","/shapes") +},{"../math/polyk":32,"../math/vec2":33,"./Shape":47,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1,"poly-decomp":9}],42:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Shape = _dereq_('./Shape') +, vec2 = _dereq_('../math/vec2') +, Utils = _dereq_('../utils/Utils'); + +module.exports = Heightfield; + +/** + * Heightfield shape class. Height data is given as an array. These data points are spread out evenly with a distance "elementWidth". + * @class Heightfield + * @extends Shape + * @constructor + * @param {Array} data An array of Y values that will be used to construct the terrain. + * @param {object} options + * @param {Number} [options.minValue] Minimum value of the data points in the data array. Will be computed automatically if not given. + * @param {Number} [options.maxValue] Maximum value. + * @param {Number} [options.elementWidth=0.1] World spacing between the data points in X direction. + * @todo Should be possible to use along all axes, not just y + * + * @example + * // Generate some height data (y-values). + * var data = []; + * for(var i = 0; i < 1000; i++){ + * var y = 0.5 * Math.cos(0.2 * i); + * data.push(y); + * } + * + * // Create the heightfield shape + * var heightfieldShape = new Heightfield(data, { + * elementWidth: 1 // Distance between the data points in X direction + * }); + * var heightfieldBody = new Body(); + * heightfieldBody.addShape(heightfieldShape); + * world.addBody(heightfieldBody); + */ +function Heightfield(data, options){ + options = Utils.defaults(options, { + maxValue : null, + minValue : null, + elementWidth : 0.1 + }); + + if(options.minValue === null || options.maxValue === null){ + options.maxValue = data[0]; + options.minValue = data[0]; + for(var i=0; i !== data.length; i++){ + var v = data[i]; + if(v > options.maxValue){ + options.maxValue = v; + } + if(v < options.minValue){ + options.minValue = v; + } + } + } + + /** + * An array of numbers, or height values, that are spread out along the x axis. + * @property {array} data + */ + this.data = data; + + /** + * Max value of the data + * @property {number} maxValue + */ + this.maxValue = options.maxValue; + + /** + * Max value of the data + * @property {number} minValue + */ + this.minValue = options.minValue; + + /** + * The width of each element + * @property {number} elementWidth + */ + this.elementWidth = options.elementWidth; + + Shape.call(this,Shape.HEIGHTFIELD); +} +Heightfield.prototype = new Shape(); + +/** + * @method computeMomentOfInertia + * @param {Number} mass + * @return {Number} + */ +Heightfield.prototype.computeMomentOfInertia = function(mass){ + return Number.MAX_VALUE; +}; + +Heightfield.prototype.updateBoundingRadius = function(){ + this.boundingRadius = Number.MAX_VALUE; +}; + +Heightfield.prototype.updateArea = function(){ + var data = this.data, + area = 0; + for(var i=0; ithis tutorial. + * @property collisionGroup + * @type {Number} + * @example + * // Setup bits for each available group + * var PLAYER = Math.pow(2,0), + * ENEMY = Math.pow(2,1), + * GROUND = Math.pow(2,2) + * + * // Put shapes into their groups + * player1Shape.collisionGroup = PLAYER; + * player2Shape.collisionGroup = PLAYER; + * enemyShape .collisionGroup = ENEMY; + * groundShape .collisionGroup = GROUND; + * + * // Assign groups that each shape collide with. + * // Note that the players can collide with ground and enemies, but not with other players. + * player1Shape.collisionMask = ENEMY | GROUND; + * player2Shape.collisionMask = ENEMY | GROUND; + * enemyShape .collisionMask = PLAYER | GROUND; + * groundShape .collisionMask = PLAYER | ENEMY; + * + * @example + * // How collision check is done + * if(shapeA.collisionGroup & shapeB.collisionMask)!=0 && (shapeB.collisionGroup & shapeA.collisionMask)!=0){ + * // The shapes will collide + * } + */ + this.collisionGroup = 1; + + /** + * Collision mask of this shape. See .collisionGroup. + * @property collisionMask + * @type {Number} + */ + this.collisionMask = 1; + if(type){ + this.updateBoundingRadius(); + } + + /** + * Material to use in collisions for this Shape. If this is set to null, the world will use default material properties instead. + * @property material + * @type {Material} + */ + this.material = null; + + /** + * Area of this shape. + * @property area + * @type {Number} + */ + this.area = 0; + + /** + * Set to true if you want this shape to be a sensor. A sensor does not generate contacts, but it still reports contact events. This is good if you want to know if a shape is overlapping another shape, without them generating contacts. + * @property {Boolean} sensor + */ + this.sensor = false; + + this.updateArea(); +} + +Shape.idCounter = 0; + +/** + * @static + * @property {Number} CIRCLE + */ +Shape.CIRCLE = 1; + +/** + * @static + * @property {Number} PARTICLE + */ +Shape.PARTICLE = 2; + +/** + * @static + * @property {Number} PLANE + */ +Shape.PLANE = 4; + +/** + * @static + * @property {Number} CONVEX + */ +Shape.CONVEX = 8; + +/** + * @static + * @property {Number} LINE + */ +Shape.LINE = 16; + +/** + * @static + * @property {Number} RECTANGLE + */ +Shape.RECTANGLE = 32; + +/** + * @static + * @property {Number} CAPSULE + */ +Shape.CAPSULE = 64; + +/** + * @static + * @property {Number} HEIGHTFIELD + */ +Shape.HEIGHTFIELD = 128; + +/** + * Should return the moment of inertia around the Z axis of the body given the total mass. See Wikipedia's list of moments of inertia. + * @method computeMomentOfInertia + * @param {Number} mass + * @return {Number} If the inertia is infinity or if the object simply isn't possible to rotate, return 0. + */ +Shape.prototype.computeMomentOfInertia = function(mass){ + throw new Error("Shape.computeMomentOfInertia is not implemented in this Shape..."); +}; + +/** + * Returns the bounding circle radius of this shape. + * @method updateBoundingRadius + * @return {Number} + */ +Shape.prototype.updateBoundingRadius = function(){ + throw new Error("Shape.updateBoundingRadius is not implemented in this Shape..."); +}; + +/** + * Update the .area property of the shape. + * @method updateArea + */ +Shape.prototype.updateArea = function(){ + // To be implemented in all subclasses +}; + +/** + * Compute the world axis-aligned bounding box (AABB) of this shape. + * @method computeAABB + * @param {AABB} out The resulting AABB. + * @param {Array} position + * @param {Number} angle + */ +Shape.prototype.computeAABB = function(out, position, angle){ + // To be implemented in each subclass +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/shapes/Shape.js","/shapes") +},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],48:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var vec2 = _dereq_('../math/vec2') +, Solver = _dereq_('./Solver') +, Utils = _dereq_('../utils/Utils') +, FrictionEquation = _dereq_('../equations/FrictionEquation'); + +module.exports = GSSolver; + +/** + * Iterative Gauss-Seidel constraint equation solver. + * + * @class GSSolver + * @constructor + * @extends Solver + * @param {Object} [options] + * @param {Number} [options.iterations=10] + * @param {Number} [options.tolerance=0] + */ +function GSSolver(options){ + Solver.call(this,options,Solver.GS); + options = options || {}; + + /** + * The number of iterations to do when solving. More gives better results, but is more expensive. + * @property iterations + * @type {Number} + */ + this.iterations = options.iterations || 10; + + /** + * The error tolerance, per constraint. If the total error is below this limit, the solver will stop iterating. Set to zero for as good solution as possible, but to something larger than zero to make computations faster. + * @property tolerance + * @type {Number} + */ + this.tolerance = options.tolerance || 1e-10; + + this.arrayStep = 30; + this.lambda = new Utils.ARRAY_TYPE(this.arrayStep); + this.Bs = new Utils.ARRAY_TYPE(this.arrayStep); + this.invCs = new Utils.ARRAY_TYPE(this.arrayStep); + + /** + * Set to true to set all right hand side terms to zero when solving. Can be handy for a few applications. + * @property useZeroRHS + * @type {Boolean} + */ + this.useZeroRHS = false; + + /** + * Number of solver iterations that are done to approximate normal forces. When these iterations are done, friction force will be computed from the contact normal forces. These friction forces will override any other friction forces set from the World for example. + * The solver will use less iterations if the solution is below the .tolerance. + * @property frictionIterations + * @type {Number} + */ + this.frictionIterations = 0; + + /** + * The number of iterations that were made during the last solve. If .tolerance is zero, this value will always be equal to .iterations, but if .tolerance is larger than zero, and the solver can quit early, then this number will be somewhere between 1 and .iterations. + * @property {Number} usedIterations + */ + this.usedIterations = 0; +} +GSSolver.prototype = new Solver(); + +function setArrayZero(array){ + var l = array.length; + while(l--){ + array[l] = +0.0; + } +} + +/** + * Solve the system of equations + * @method solve + * @param {Number} h Time step + * @param {World} world World to solve + */ +GSSolver.prototype.solve = function(h, world){ + + this.sortEquations(); + + var iter = 0, + maxIter = this.iterations, + maxFrictionIter = this.frictionIterations, + equations = this.equations, + Neq = equations.length, + tolSquared = Math.pow(this.tolerance*Neq, 2), + bodies = world.bodies, + Nbodies = world.bodies.length, + add = vec2.add, + set = vec2.set, + useZeroRHS = this.useZeroRHS, + lambda = this.lambda; + + this.usedIterations = 0; + + if(Neq){ + for(var i=0; i!==Nbodies; i++){ + var b = bodies[i]; + + // Update solve mass + b.updateSolveMassProperties(); + } + } + + // Things that does not change during iteration can be computed once + if(lambda.length < Neq){ + lambda = this.lambda = new Utils.ARRAY_TYPE(Neq + this.arrayStep); + this.Bs = new Utils.ARRAY_TYPE(Neq + this.arrayStep); + this.invCs = new Utils.ARRAY_TYPE(Neq + this.arrayStep); + } + setArrayZero(lambda); + var invCs = this.invCs, + Bs = this.Bs, + lambda = this.lambda; + + for(var i=0; i!==equations.length; i++){ + var c = equations[i]; + if(c.timeStep !== h || c.needsUpdate){ + c.timeStep = h; + c.update(); + } + Bs[i] = c.computeB(c.a,c.b,h); + invCs[i] = c.computeInvC(c.epsilon); + } + + var q, B, c, deltalambdaTot,i,j; + + if(Neq !== 0){ + + for(i=0; i!==Nbodies; i++){ + var b = bodies[i]; + + // Reset vlambda + b.resetConstraintVelocity(); + } + + if(maxFrictionIter){ + // Iterate over contact equations to get normal forces + for(iter=0; iter!==maxFrictionIter; iter++){ + + // Accumulate the total error for each iteration. + deltalambdaTot = 0.0; + + for(j=0; j!==Neq; j++){ + c = equations[j]; + + var deltalambda = GSSolver.iterateEquation(j,c,c.epsilon,Bs,invCs,lambda,useZeroRHS,h,iter); + deltalambdaTot += Math.abs(deltalambda); + } + + this.usedIterations++; + + // If the total error is small enough - stop iterate + if(deltalambdaTot*deltalambdaTot <= tolSquared){ + break; + } + } + + GSSolver.updateMultipliers(equations, lambda, 1/h); + + // Set computed friction force + for(j=0; j!==Neq; j++){ + var eq = equations[j]; + if(eq instanceof FrictionEquation){ + var f = 0.0; + for(var k=0; k!==eq.contactEquations.length; k++){ + f += eq.contactEquations[k].multiplier; + } + f *= eq.frictionCoefficient / eq.contactEquations.length; + eq.maxForce = f; + eq.minForce = -f; + } + } + } + + // Iterate over all equations + for(iter=0; iter!==maxIter; iter++){ + + // Accumulate the total error for each iteration. + deltalambdaTot = 0.0; + + for(j=0; j!==Neq; j++){ + c = equations[j]; + + var deltalambda = GSSolver.iterateEquation(j,c,c.epsilon,Bs,invCs,lambda,useZeroRHS,h,iter); + deltalambdaTot += Math.abs(deltalambda); + } + + this.usedIterations++; + + // If the total error is small enough - stop iterate + if(deltalambdaTot*deltalambdaTot <= tolSquared){ + break; + } + } + + // Add result to velocity + for(i=0; i!==Nbodies; i++){ + bodies[i].addConstraintVelocity(); + } + + GSSolver.updateMultipliers(equations, lambda, 1/h); + } +}; + +// Sets the .multiplier property of each equation +GSSolver.updateMultipliers = function(equations, lambda, invDt){ + // Set the .multiplier property of each equation + var l = equations.length; + while(l--){ + equations[l].multiplier = lambda[l] * invDt; + } +}; + +GSSolver.iterateEquation = function(j,eq,eps,Bs,invCs,lambda,useZeroRHS,dt,iter){ + // Compute iteration + var B = Bs[j], + invC = invCs[j], + lambdaj = lambda[j], + GWlambda = eq.computeGWlambda(); + + var maxForce = eq.maxForce, + minForce = eq.minForce; + + if(useZeroRHS){ + B = 0; + } + + var deltalambda = invC * ( B - GWlambda - eps * lambdaj ); + + // Clamp if we are not within the min/max interval + var lambdaj_plus_deltalambda = lambdaj + deltalambda; + if(lambdaj_plus_deltalambda < minForce*dt){ + deltalambda = minForce*dt - lambdaj; + } else if(lambdaj_plus_deltalambda > maxForce*dt){ + deltalambda = maxForce*dt - lambdaj; + } + lambda[j] += deltalambda; + eq.addToWlambda(deltalambda); + + return deltalambda; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/solver/GSSolver.js","/solver") +},{"../equations/FrictionEquation":26,"../math/vec2":33,"../utils/Utils":52,"./Solver":49,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],49:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Utils = _dereq_('../utils/Utils') +, EventEmitter = _dereq_('../events/EventEmitter'); + +module.exports = Solver; + +/** + * Base class for constraint solvers. + * @class Solver + * @constructor + * @extends EventEmitter + */ +function Solver(options,type){ + options = options || {}; + + EventEmitter.call(this); + + this.type = type; + + /** + * Current equations in the solver. + * + * @property equations + * @type {Array} + */ + this.equations = []; + + /** + * Function that is used to sort all equations before each solve. + * @property equationSortFunction + * @type {function|boolean} + */ + this.equationSortFunction = options.equationSortFunction || false; +} +Solver.prototype = new EventEmitter(); + +/** + * Method to be implemented in each subclass + * @method solve + * @param {Number} dt + * @param {World} world + */ +Solver.prototype.solve = function(dt,world){ + throw new Error("Solver.solve should be implemented by subclasses!"); +}; + +var mockWorld = {bodies:[]}; + +/** + * Solves all constraints in an island. + * @method solveIsland + * @param {Number} dt + * @param {Island} island + */ +Solver.prototype.solveIsland = function(dt,island){ + + this.removeAllEquations(); + + if(island.equations.length){ + // Add equations to solver + this.addEquations(island.equations); + mockWorld.bodies.length = 0; + island.getBodies(mockWorld.bodies); + + // Solve + if(mockWorld.bodies.length){ + this.solve(dt,mockWorld); + } + } +}; + +/** + * Sort all equations using the .equationSortFunction. Should be called by subclasses before solving. + * @method sortEquations + */ +Solver.prototype.sortEquations = function(){ + if(this.equationSortFunction){ + this.equations.sort(this.equationSortFunction); + } +}; + +/** + * Add an equation to be solved. + * + * @method addEquation + * @param {Equation} eq + */ +Solver.prototype.addEquation = function(eq){ + if(eq.enabled){ + this.equations.push(eq); + } +}; + +/** + * Add equations. Same as .addEquation, but this time the argument is an array of Equations + * + * @method addEquations + * @param {Array} eqs + */ +Solver.prototype.addEquations = function(eqs){ + //Utils.appendArray(this.equations,eqs); + for(var i=0, N=eqs.length; i!==N; i++){ + var eq = eqs[i]; + if(eq.enabled){ + this.equations.push(eq); + } + } +}; + +/** + * Remove an equation. + * + * @method removeEquation + * @param {Equation} eq + */ +Solver.prototype.removeEquation = function(eq){ + var i = this.equations.indexOf(eq); + if(i !== -1){ + this.equations.splice(i,1); + } +}; + +/** + * Remove all currently added equations. + * + * @method removeAllEquations + */ +Solver.prototype.removeAllEquations = function(){ + this.equations.length=0; +}; + +Solver.GS = 1; +Solver.ISLAND = 2; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/solver/Solver.js","/solver") +},{"../events/EventEmitter":29,"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],50:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var TupleDictionary = _dereq_('./TupleDictionary'); +var Utils = _dereq_('./Utils'); + +module.exports = OverlapKeeper; + +/** + * Keeps track of overlaps in the current state and the last step state. + * @class OverlapKeeper + * @constructor + */ +function OverlapKeeper() { + this.overlappingShapesLastState = new TupleDictionary(); + this.overlappingShapesCurrentState = new TupleDictionary(); + this.recordPool = []; + this.tmpDict = new TupleDictionary(); + this.tmpArray1 = []; +} + +/** + * Ticks one step forward in time. This will move the current overlap state to the "old" overlap state, and create a new one as current. + * @method tick + */ +OverlapKeeper.prototype.tick = function() { + var last = this.overlappingShapesLastState; + var current = this.overlappingShapesCurrentState; + + // Save old objects into pool + var l = last.keys.length; + while(l--){ + var key = last.keys[l]; + var lastObject = last.getByKey(key); + var currentObject = current.getByKey(key); + if(lastObject && !currentObject){ + // The record is only used in the "last" dict, and will be removed. We might as well pool it. + this.recordPool.push(lastObject); + } + } + + // Clear last object + last.reset(); + + // Transfer from new object to old + last.copy(current); + + // Clear current object + current.reset(); +}; + +/** + * @method setOverlapping + * @param {Body} bodyA + * @param {Body} shapeA + * @param {Body} bodyB + * @param {Body} shapeB + */ +OverlapKeeper.prototype.setOverlapping = function(bodyA, shapeA, bodyB, shapeB) { + var last = this.overlappingShapesLastState; + var current = this.overlappingShapesCurrentState; + + // Store current contact state + if(!current.get(shapeA.id, shapeB.id)){ + + var data; + if(this.recordPool.length){ + data = this.recordPool.pop(); + data.set(bodyA, shapeA, bodyB, shapeB); + } else { + data = new OverlapKeeperRecord(bodyA, shapeA, bodyB, shapeB); + } + + current.set(shapeA.id, shapeB.id, data); + } +}; + +OverlapKeeper.prototype.getNewOverlaps = function(result){ + return this.getDiff(this.overlappingShapesLastState, this.overlappingShapesCurrentState, result); +}; + +OverlapKeeper.prototype.getEndOverlaps = function(result){ + return this.getDiff(this.overlappingShapesCurrentState, this.overlappingShapesLastState, result); +}; + +/** + * Checks if two bodies are currently overlapping. + * @method bodiesAreOverlapping + * @param {Body} bodyA + * @param {Body} bodyB + * @return {boolean} + */ +OverlapKeeper.prototype.bodiesAreOverlapping = function(bodyA, bodyB){ + var current = this.overlappingShapesCurrentState; + var l = current.keys.length; + while(l--){ + var key = current.keys[l]; + var data = current.data[key]; + if((data.bodyA === bodyA && data.bodyB === bodyB) || data.bodyA === bodyB && data.bodyB === bodyA){ + return true; + } + } + return false; +}; + +OverlapKeeper.prototype.getDiff = function(dictA, dictB, result){ + var result = result || []; + var last = dictA; + var current = dictB; + + result.length = 0; + + var l = current.keys.length; + while(l--){ + var key = current.keys[l]; + var data = current.data[key]; + + if(!data){ + throw new Error('Key '+key+' had no data!'); + } + + var lastData = last.data[key]; + if(!lastData){ + // Not overlapping in last state, but in current. + result.push(data); + } + } + + return result; +}; + +OverlapKeeper.prototype.isNewOverlap = function(shapeA, shapeB){ + var idA = shapeA.id|0, + idB = shapeB.id|0; + var last = this.overlappingShapesLastState; + var current = this.overlappingShapesCurrentState; + // Not in last but in new + return !!!last.get(idA, idB) && !!current.get(idA, idB); +}; + +OverlapKeeper.prototype.getNewBodyOverlaps = function(result){ + this.tmpArray1.length = 0; + var overlaps = this.getNewOverlaps(this.tmpArray1); + return this.getBodyDiff(overlaps, result); +}; + +OverlapKeeper.prototype.getEndBodyOverlaps = function(result){ + this.tmpArray1.length = 0; + var overlaps = this.getEndOverlaps(this.tmpArray1); + return this.getBodyDiff(overlaps, result); +}; + +OverlapKeeper.prototype.getBodyDiff = function(overlaps, result){ + result = result || []; + var accumulator = this.tmpDict; + + var l = overlaps.length; + + while(l--){ + var data = overlaps[l]; + + // Since we use body id's for the accumulator, these will be a subset of the original one + accumulator.set(data.bodyA.id|0, data.bodyB.id|0, data); + } + + l = accumulator.keys.length; + while(l--){ + var data = accumulator.getByKey(accumulator.keys[l]); + if(data){ + result.push(data.bodyA, data.bodyB); + } + } + + accumulator.reset(); + + return result; +}; + +/** + * Overlap data container for the OverlapKeeper + * @class OverlapKeeperRecord + * @constructor + * @param {Body} bodyA + * @param {Shape} shapeA + * @param {Body} bodyB + * @param {Shape} shapeB + */ +function OverlapKeeperRecord(bodyA, shapeA, bodyB, shapeB){ + /** + * @property {Shape} shapeA + */ + this.shapeA = shapeA; + /** + * @property {Shape} shapeB + */ + this.shapeB = shapeB; + /** + * @property {Body} bodyA + */ + this.bodyA = bodyA; + /** + * @property {Body} bodyB + */ + this.bodyB = bodyB; +} + +/** + * Set the data for the record + * @method set + * @param {Body} bodyA + * @param {Shape} shapeA + * @param {Body} bodyB + * @param {Shape} shapeB + */ +OverlapKeeperRecord.prototype.set = function(bodyA, shapeA, bodyB, shapeB){ + OverlapKeeperRecord.call(this, bodyA, shapeA, bodyB, shapeB); +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/utils/OverlapKeeper.js","/utils") +},{"./TupleDictionary":51,"./Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],51:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Utils = _dereq_('./Utils'); + +module.exports = TupleDictionary; + +/** + * @class TupleDictionary + * @constructor + */ +function TupleDictionary() { + + /** + * The data storage + * @property data + * @type {Object} + */ + this.data = {}; + + /** + * Keys that are currently used. + * @property {Array} keys + */ + this.keys = []; +} + +/** + * Generate a key given two integers + * @method getKey + * @param {number} i + * @param {number} j + * @return {string} + */ +TupleDictionary.prototype.getKey = function(id1, id2) { + id1 = id1|0; + id2 = id2|0; + + if ( (id1|0) === (id2|0) ){ + return -1; + } + + // valid for values < 2^16 + return ((id1|0) > (id2|0) ? + (id1 << 16) | (id2 & 0xFFFF) : + (id2 << 16) | (id1 & 0xFFFF))|0 + ; +}; + +/** + * @method getByKey + * @param {Number} key + * @return {Object} + */ +TupleDictionary.prototype.getByKey = function(key) { + key = key|0; + return this.data[key]; +}; + +/** + * @method get + * @param {Number} i + * @param {Number} j + * @return {Number} + */ +TupleDictionary.prototype.get = function(i, j) { + return this.data[this.getKey(i, j)]; +}; + +/** + * Set a value. + * @method set + * @param {Number} i + * @param {Number} j + * @param {Number} value + */ +TupleDictionary.prototype.set = function(i, j, value) { + if(!value){ + throw new Error("No data!"); + } + + var key = this.getKey(i, j); + + // Check if key already exists + if(!this.data[key]){ + this.keys.push(key); + } + + this.data[key] = value; + + return key; +}; + +/** + * Remove all data. + * @method reset + */ +TupleDictionary.prototype.reset = function() { + var data = this.data, + keys = this.keys; + + var l = keys.length; + while(l--) { + delete data[keys[l]]; + } + + keys.length = 0; +}; + +/** + * Copy another TupleDictionary. Note that all data in this dictionary will be removed. + * @method copy + * @param {TupleDictionary} dict The TupleDictionary to copy into this one. + */ +TupleDictionary.prototype.copy = function(dict) { + this.reset(); + Utils.appendArray(this.keys, dict.keys); + var l = dict.keys.length; + while(l--){ + var key = dict.keys[l]; + this.data[key] = dict.data[key]; + } +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/utils/TupleDictionary.js","/utils") +},{"./Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],52:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +/* global P2_ARRAY_TYPE */ + +module.exports = Utils; + +/** + * Misc utility functions + * @class Utils + * @constructor + */ +function Utils(){} + +/** + * Append the values in array b to the array a. See this for an explanation. + * @method appendArray + * @static + * @param {Array} a + * @param {Array} b + */ +Utils.appendArray = function(a,b){ + if (b.length < 150000) { + a.push.apply(a, b); + } else { + for (var i = 0, len = b.length; i !== len; ++i) { + a.push(b[i]); + } + } +}; + +/** + * Garbage free Array.splice(). Does not allocate a new array. + * @method splice + * @static + * @param {Array} array + * @param {Number} index + * @param {Number} howmany + */ +Utils.splice = function(array,index,howmany){ + howmany = howmany || 1; + for (var i=index, len=array.length-howmany; i < len; i++){ + array[i] = array[i + howmany]; + } + array.length = len; +}; + +/** + * The array type to use for internal numeric computations throughout the library. Float32Array is used if it is available, but falls back on Array. If you want to set array type manually, inject it via the global variable P2_ARRAY_TYPE. See example below. + * @static + * @property {function} ARRAY_TYPE + * @example + * + * + */ +if(typeof P2_ARRAY_TYPE !== 'undefined') { + Utils.ARRAY_TYPE = P2_ARRAY_TYPE; +} else if (typeof Float32Array !== 'undefined'){ + Utils.ARRAY_TYPE = Float32Array; +} else { + Utils.ARRAY_TYPE = Array; +} + +/** + * Extend an object with the properties of another + * @static + * @method extend + * @param {object} a + * @param {object} b + */ +Utils.extend = function(a,b){ + for(var key in b){ + a[key] = b[key]; + } +}; + +/** + * Extend an options object with default values. + * @static + * @method defaults + * @param {object} options The options object. May be falsy: in this case, a new object is created and returned. + * @param {object} defaults An object containing default values. + * @return {object} The modified options object. + */ +Utils.defaults = function(options, defaults){ + options = options || {}; + for(var key in defaults){ + if(!(key in options)){ + options[key] = defaults[key]; + } + } + return options; +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/utils/Utils.js","/utils") +},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}],53:[function(_dereq_,module,exports){ +(function (process,global,Buffer,__argument0,__argument1,__argument2,__argument3,__filename,__dirname){ +var Body = _dereq_('../objects/Body'); + +module.exports = Island; + +/** + * An island of bodies connected with equations. + * @class Island + * @constructor + */ +function Island(){ + + /** + * Current equations in this island. + * @property equations + * @type {Array} + */ + this.equations = []; + + /** + * Current bodies in this island. + * @property bodies + * @type {Array} + */ + this.bodies = []; +} + +/** + * Clean this island from bodies and equations. + * @method reset + */ +Island.prototype.reset = function(){ + this.equations.length = this.bodies.length = 0; +}; + +var bodyIds = []; + +/** + * Get all unique bodies in this island. + * @method getBodies + * @return {Array} An array of Body + */ +Island.prototype.getBodies = function(result){ + var bodies = result || [], + eqs = this.equations; + bodyIds.length = 0; + for(var i=0; i!==eqs.length; i++){ + var eq = eqs[i]; + if(bodyIds.indexOf(eq.bodyA.id)===-1){ + bodies.push(eq.bodyA); + bodyIds.push(eq.bodyA.id); + } + if(bodyIds.indexOf(eq.bodyB.id)===-1){ + bodies.push(eq.bodyB); + bodyIds.push(eq.bodyB.id); + } + } + return bodies; +}; + +/** + * Check if the entire island wants to sleep. + * @method wantsToSleep + * @return {Boolean} + */ +Island.prototype.wantsToSleep = function(){ + for(var i=0; i dt*1000){ + // We are slower than real-time. Better bail out. + break; + } + } + + // Increment internal clock + this.time += timeSinceLastCalled; + + // Compute "Left over" time step + var h = this.time % dt; + var h_div_dt = h/dt; + + for(var j=0; j!==this.bodies.length; j++){ + var b = this.bodies[j]; + if(b.type !== Body.STATIC && b.sleepState !== Body.SLEEPING){ + // Interpolate + vec2.sub(interpvelo, b.position, b.previousPosition); + vec2.scale(interpvelo, interpvelo, h_div_dt); + vec2.add(b.interpolatedPosition, b.position, interpvelo); + + b.interpolatedAngle = b.angle + (b.angle - b.previousAngle) * h_div_dt; + } else { + // For static bodies, just copy. Who else will do it? + vec2.copy(b.interpolatedPosition, b.position); + b.interpolatedAngle = b.angle; + } + } + } +}; + +var endOverlaps = []; + +/** + * Make a fixed step. + * @method internalStep + * @param {number} dt + * @private + */ +World.prototype.internalStep = function(dt){ + this.stepping = true; + + var that = this, + doProfiling = this.doProfiling, + Nsprings = this.springs.length, + springs = this.springs, + bodies = this.bodies, + g = this.gravity, + solver = this.solver, + Nbodies = this.bodies.length, + broadphase = this.broadphase, + np = this.narrowphase, + constraints = this.constraints, + t0, t1, + fhMinv = step_fhMinv, + velodt = step_velodt, + mg = step_mg, + scale = vec2.scale, + add = vec2.add, + rotate = vec2.rotate, + islandManager = this.islandManager; + + this.overlapKeeper.tick(); + + this.lastTimeStep = dt; + + if(doProfiling){ + t0 = performance.now(); + } + + // Update approximate friction gravity. + if(this.useWorldGravityAsFrictionGravity){ + var gravityLen = vec2.length(this.gravity); + if(gravityLen === 0 && this.useFrictionGravityOnZeroGravity){ + // Leave friction gravity as it is. + } else { + // Nonzero gravity. Use it. + this.frictionGravity = gravityLen; + } + } + + // Add gravity to bodies + if(this.applyGravity){ + for(var i=0; i!==Nbodies; i++){ + var b = bodies[i], + fi = b.force; + if(b.type !== Body.DYNAMIC || b.sleepState === Body.SLEEPING){ + continue; + } + vec2.scale(mg,g,b.mass*b.gravityScale); // F=m*g + add(fi,fi,mg); + } + } + + // Add spring forces + if(this.applySpringForces){ + for(var i=0; i!==Nsprings; i++){ + var s = springs[i]; + s.applyForce(); + } + } + + if(this.applyDamping){ + for(var i=0; i!==Nbodies; i++){ + var b = bodies[i]; + if(b.type === Body.DYNAMIC){ + b.applyDamping(dt); + } + } + } + + // Broadphase + var result = broadphase.getCollisionPairs(this); + + // Remove ignored collision pairs + var ignoredPairs = this.disabledBodyCollisionPairs; + for(var i=ignoredPairs.length-2; i>=0; i-=2){ + for(var j=result.length-2; j>=0; j-=2){ + if( (ignoredPairs[i] === result[j] && ignoredPairs[i+1] === result[j+1]) || + (ignoredPairs[i+1] === result[j] && ignoredPairs[i] === result[j+1])){ + result.splice(j,2); + } + } + } + + // Remove constrained pairs with collideConnected == false + var Nconstraints = constraints.length; + for(i=0; i!==Nconstraints; i++){ + var c = constraints[i]; + if(!c.collideConnected){ + for(var j=result.length-2; j>=0; j-=2){ + if( (c.bodyA === result[j] && c.bodyB === result[j+1]) || + (c.bodyB === result[j] && c.bodyA === result[j+1])){ + result.splice(j,2); + } + } + } + } + + // postBroadphase event + this.postBroadphaseEvent.pairs = result; + this.emit(this.postBroadphaseEvent); + + // Narrowphase + np.reset(this); + for(var i=0, Nresults=result.length; i!==Nresults; i+=2){ + var bi = result[i], + bj = result[i+1]; + + // Loop over all shapes of body i + for(var k=0, Nshapesi=bi.shapes.length; k!==Nshapesi; k++){ + var si = bi.shapes[k], + xi = bi.shapeOffsets[k], + ai = bi.shapeAngles[k]; + + // All shapes of body j + for(var l=0, Nshapesj=bj.shapes.length; l!==Nshapesj; l++){ + var sj = bj.shapes[l], + xj = bj.shapeOffsets[l], + aj = bj.shapeAngles[l]; + + var cm = this.defaultContactMaterial; + if(si.material && sj.material){ + var tmp = this.getContactMaterial(si.material,sj.material); + if(tmp){ + cm = tmp; + } + } + + this.runNarrowphase(np,bi,si,xi,ai,bj,sj,xj,aj,cm,this.frictionGravity); + } + } + } + + // Wake up bodies + for(var i=0; i!==Nbodies; i++){ + var body = bodies[i]; + if(body._wakeUpAfterNarrowphase){ + body.wakeUp(); + body._wakeUpAfterNarrowphase = false; + } + } + + // Emit end overlap events + if(this.has('endContact')){ + this.overlapKeeper.getEndOverlaps(endOverlaps); + var e = this.endContactEvent; + var l = endOverlaps.length; + while(l--){ + var data = endOverlaps[l]; + e.shapeA = data.shapeA; + e.shapeB = data.shapeB; + e.bodyA = data.bodyA; + e.bodyB = data.bodyA; + this.emit(e); + } + } + + var preSolveEvent = this.preSolveEvent; + preSolveEvent.contactEquations = np.contactEquations; + preSolveEvent.frictionEquations = np.frictionEquations; + this.emit(preSolveEvent); + + // update constraint equations + var Nconstraints = constraints.length; + for(i=0; i!==Nconstraints; i++){ + constraints[i].update(); + } + + if(np.contactEquations.length || np.frictionEquations.length || constraints.length){ + if(this.islandSplit){ + // Split into islands + islandManager.equations.length = 0; + Utils.appendArray(islandManager.equations, np.contactEquations); + Utils.appendArray(islandManager.equations, np.frictionEquations); + for(i=0; i!==Nconstraints; i++){ + Utils.appendArray(islandManager.equations, constraints[i].equations); + } + islandManager.split(this); + + for(var i=0; i!==islandManager.islands.length; i++){ + var island = islandManager.islands[i]; + if(island.equations.length){ + solver.solveIsland(dt,island); + } + } + + } else { + + // Add contact equations to solver + solver.addEquations(np.contactEquations); + solver.addEquations(np.frictionEquations); + + // Add user-defined constraint equations + for(i=0; i!==Nconstraints; i++){ + solver.addEquations(constraints[i].equations); + } + + if(this.solveConstraints){ + solver.solve(dt,this); + } + + solver.removeAllEquations(); + } + } + + // Step forward + for(var i=0; i!==Nbodies; i++){ + var body = bodies[i]; + + if(body.sleepState !== Body.SLEEPING && body.type !== Body.STATIC){ + World.integrateBody(body,dt); + } + } + + // Reset force + for(var i=0; i!==Nbodies; i++){ + bodies[i].setZeroForce(); + } + + if(doProfiling){ + t1 = performance.now(); + that.lastStepTime = t1-t0; + } + + // Emit impact event + if(this.emitImpactEvent && this.has('impact')){ + var ev = this.impactEvent; + for(var i=0; i!==np.contactEquations.length; i++){ + var eq = np.contactEquations[i]; + if(eq.firstImpact){ + ev.bodyA = eq.bodyA; + ev.bodyB = eq.bodyB; + ev.shapeA = eq.shapeA; + ev.shapeB = eq.shapeB; + ev.contactEquation = eq; + this.emit(ev); + } + } + } + + // Sleeping update + if(this.sleepMode === World.BODY_SLEEPING){ + for(i=0; i!==Nbodies; i++){ + bodies[i].sleepTick(this.time, false, dt); + } + } else if(this.sleepMode === World.ISLAND_SLEEPING && this.islandSplit){ + + // Tell all bodies to sleep tick but dont sleep yet + for(i=0; i!==Nbodies; i++){ + bodies[i].sleepTick(this.time, true, dt); + } + + // Sleep islands + for(var i=0; i 0; + np.frictionCoefficient = cm.friction; + var reducedMass; + if(bi.type === Body.STATIC || bi.type === Body.KINEMATIC){ + reducedMass = bj.mass; + } else if(bj.type === Body.STATIC || bj.type === Body.KINEMATIC){ + reducedMass = bi.mass; + } else { + reducedMass = (bi.mass*bj.mass)/(bi.mass+bj.mass); + } + np.slipForce = cm.friction*glen*reducedMass; + np.restitution = cm.restitution; + np.surfaceVelocity = cm.surfaceVelocity; + np.frictionStiffness = cm.frictionStiffness; + np.frictionRelaxation = cm.frictionRelaxation; + np.stiffness = cm.stiffness; + np.relaxation = cm.relaxation; + np.contactSkinSize = cm.contactSkinSize; + + var resolver = np[si.type | sj.type], + numContacts = 0; + if (resolver) { + var sensor = si.sensor || sj.sensor; + var numFrictionBefore = np.frictionEquations.length; + if (si.type < sj.type) { + numContacts = resolver.call(np, bi,si,xiw,aiw, bj,sj,xjw,ajw, sensor); + } else { + numContacts = resolver.call(np, bj,sj,xjw,ajw, bi,si,xiw,aiw, sensor); + } + var numFrictionEquations = np.frictionEquations.length - numFrictionBefore; + + if(numContacts){ + + if( bi.allowSleep && + bi.type === Body.DYNAMIC && + bi.sleepState === Body.SLEEPING && + bj.sleepState === Body.AWAKE && + bj.type !== Body.STATIC + ){ + var speedSquaredB = vec2.squaredLength(bj.velocity) + Math.pow(bj.angularVelocity,2); + var speedLimitSquaredB = Math.pow(bj.sleepSpeedLimit,2); + if(speedSquaredB >= speedLimitSquaredB*2){ + bi._wakeUpAfterNarrowphase = true; + } + } + + if( bj.allowSleep && + bj.type === Body.DYNAMIC && + bj.sleepState === Body.SLEEPING && + bi.sleepState === Body.AWAKE && + bi.type !== Body.STATIC + ){ + var speedSquaredA = vec2.squaredLength(bi.velocity) + Math.pow(bi.angularVelocity,2); + var speedLimitSquaredA = Math.pow(bi.sleepSpeedLimit,2); + if(speedSquaredA >= speedLimitSquaredA*2){ + bj._wakeUpAfterNarrowphase = true; + } + } + + this.overlapKeeper.setOverlapping(bi, si, bj, sj); + if(this.has('beginContact') && this.overlapKeeper.isNewOverlap(si, sj)){ + + // Report new shape overlap + var e = this.beginContactEvent; + e.shapeA = si; + e.shapeB = sj; + e.bodyA = bi; + e.bodyB = bj; + + // Reset contact equations + e.contactEquations.length = 0; + + if(typeof(numContacts)==="number"){ + for(var i=np.contactEquations.length-numContacts; i 1){ // Why divide by 1? + for(var i=np.frictionEquations.length-numFrictionEquations; i=0; i--){ + this.removeConstraint(cs[i]); + } + + // Remove all bodies + var bodies = this.bodies; + for(var i=bodies.length-1; i>=0; i--){ + this.removeBody(bodies[i]); + } + + // Remove all springs + var springs = this.springs; + for(var i=springs.length-1; i>=0; i--){ + this.removeSpring(springs[i]); + } + + // Remove all contact materials + var cms = this.contactMaterials; + for(var i=cms.length-1; i>=0; i--){ + this.removeContactMaterial(cms[i]); + } + + World.apply(this); +}; + +/** + * Get a copy of this World instance + * @method clone + * @return {World} + */ +World.prototype.clone = function(){ + var world = new World(); + world.fromJSON(this.toJSON()); + return world; +}; + +var hitTest_tmp1 = vec2.create(), + hitTest_zero = vec2.fromValues(0,0), + hitTest_tmp2 = vec2.fromValues(0,0); + +/** + * Test if a world point overlaps bodies + * @method hitTest + * @param {Array} worldPoint Point to use for intersection tests + * @param {Array} bodies A list of objects to check for intersection + * @param {Number} precision Used for matching against particles and lines. Adds some margin to these infinitesimal objects. + * @return {Array} Array of bodies that overlap the point + */ +World.prototype.hitTest = function(worldPoint,bodies,precision){ + precision = precision || 0; + + // Create a dummy particle body with a particle shape to test against the bodies + var pb = new Body({ position:worldPoint }), + ps = new Particle(), + px = worldPoint, + pa = 0, + x = hitTest_tmp1, + zero = hitTest_zero, + tmp = hitTest_tmp2; + pb.addShape(ps); + + var n = this.narrowphase, + result = []; + + // Check bodies + for(var i=0, N=bodies.length; i!==N; i++){ + var b = bodies[i]; + for(var j=0, NS=b.shapes.length; j!==NS; j++){ + var s = b.shapes[j], + offset = b.shapeOffsets[j] || zero, + angle = b.shapeAngles[j] || 0.0; + + // Get shape world position + angle + vec2.rotate(x, offset, b.angle); + vec2.add(x, x, b.position); + var a = angle + b.angle; + + if( (s instanceof Circle && n.circleParticle (b,s,x,a, pb,ps,px,pa, true)) || + (s instanceof Convex && n.particleConvex (pb,ps,px,pa, b,s,x,a, true)) || + (s instanceof Plane && n.particlePlane (pb,ps,px,pa, b,s,x,a, true)) || + (s instanceof Capsule && n.particleCapsule (pb,ps,px,pa, b,s,x,a, true)) || + (s instanceof Particle && vec2.squaredLength(vec2.sub(tmp,x,worldPoint)) < precision*precision) + ){ + result.push(b); + } + } + } + + return result; +}; + +/** + * Sets the Equation parameters for all constraints and contact materials. + * @method setGlobalEquationParameters + * @param {object} [parameters] + * @param {Number} [parameters.relaxation] + * @param {Number} [parameters.stiffness] + */ +World.prototype.setGlobalEquationParameters = function(parameters){ + parameters = parameters || {}; + + // Set for all constraints + for(var i=0; i !== this.constraints.length; i++){ + var c = this.constraints[i]; + for(var j=0; j !== c.equations.length; j++){ + var eq = c.equations[j]; + if(typeof(parameters.stiffness) !== "undefined"){ + eq.stiffness = parameters.stiffness; + } + if(typeof(parameters.relaxation) !== "undefined"){ + eq.relaxation = parameters.relaxation; + } + eq.needsUpdate = true; + } + } + + // Set for all contact materials + for(var i=0; i !== this.contactMaterials.length; i++){ + var c = this.contactMaterials[i]; + if(typeof(parameters.stiffness) !== "undefined"){ + c.stiffness = parameters.stiffness; + c.frictionStiffness = parameters.stiffness; + } + if(typeof(parameters.relaxation) !== "undefined"){ + c.relaxation = parameters.relaxation; + c.frictionRelaxation = parameters.relaxation; + } + } + + // Set for default contact material + var c = this.defaultContactMaterial; + if(typeof(parameters.stiffness) !== "undefined"){ + c.stiffness = parameters.stiffness; + c.frictionStiffness = parameters.stiffness; + } + if(typeof(parameters.relaxation) !== "undefined"){ + c.relaxation = parameters.relaxation; + c.frictionRelaxation = parameters.relaxation; + } +}; + +/** + * Set the stiffness for all equations and contact materials. + * @method setGlobalStiffness + * @param {Number} stiffness + */ +World.prototype.setGlobalStiffness = function(stiffness){ + this.setGlobalEquationParameters({ + stiffness: stiffness + }); +}; + +/** + * Set the relaxation for all equations and contact materials. + * @method setGlobalRelaxation + * @param {Number} relaxation + */ +World.prototype.setGlobalRelaxation = function(relaxation){ + this.setGlobalEquationParameters({ + relaxation: relaxation + }); +}; + +}).call(this,_dereq_("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},_dereq_("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/world/World.js","/world") +},{"../../package.json":10,"../collision/Broadphase":12,"../collision/NaiveBroadphase":14,"../collision/Narrowphase":15,"../collision/SAPBroadphase":16,"../constraints/Constraint":17,"../constraints/DistanceConstraint":18,"../constraints/GearConstraint":19,"../constraints/LockConstraint":20,"../constraints/PrismaticConstraint":21,"../constraints/RevoluteConstraint":22,"../events/EventEmitter":29,"../material/ContactMaterial":30,"../material/Material":31,"../math/vec2":33,"../objects/Body":34,"../objects/LinearSpring":35,"../objects/RotationalSpring":36,"../shapes/Capsule":39,"../shapes/Circle":40,"../shapes/Convex":41,"../shapes/Line":43,"../shapes/Particle":44,"../shapes/Plane":45,"../shapes/Rectangle":46,"../shapes/Shape":47,"../solver/GSSolver":48,"../solver/Solver":49,"../utils/OverlapKeeper":50,"../utils/Utils":52,"./IslandManager":54,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"buffer":1}]},{},[38]) +(38) +});; \ No newline at end of file diff --git a/ajax/libs/p2.js/0.6.0/p2.min.js b/ajax/libs/p2.js/0.6.0/p2.min.js new file mode 100644 index 000000000..1f1b21073 --- /dev/null +++ b/ajax/libs/p2.js/0.6.0/p2.min.js @@ -0,0 +1,28 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013 p2.js authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +!function(a){if("object"==typeof exports)module.exports=a();else if("function"==typeof define&&define.amd)define(a);else{var b;"undefined"!=typeof window?b=window:"undefined"!=typeof global?b=global:"undefined"!=typeof self&&(b=self),b.p2=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gh;h++)g[h]=e.isBuffer(a)?a.readUInt8(h):a[h];else if("string"===d)g.write(a,0,b);else if("number"===d&&!e._useTypedArrays&&!c)for(h=0;f>h;h++)g[h]=0;return g}function f(a,b,c,d){c=Number(c)||0;var f=a.length-c;d?(d=Number(d),d>f&&(d=f)):d=f;var g=b.length;T(g%2===0,"Invalid hex string"),d>g/2&&(d=g/2);for(var h=0;d>h;h++){var i=parseInt(b.substr(2*h,2),16);T(!isNaN(i),"Invalid hex string"),a[c+h]=i}return e._charsWritten=2*h,h}function g(a,b,c,d){var f=e._charsWritten=O(K(b),a,c,d);return f}function h(a,b,c,d){var f=e._charsWritten=O(L(b),a,c,d);return f}function i(a,b,c,d){return h(a,b,c,d)}function j(a,b,c,d){var f=e._charsWritten=O(N(b),a,c,d);return f}function k(a,b,c,d){var f=e._charsWritten=O(M(b),a,c,d);return f}function l(a,b,c){return U.fromByteArray(0===b&&c===a.length?a:a.slice(b,c))}function m(a,b,c){var d="",e="";c=Math.min(a.length,c);for(var f=b;c>f;f++)a[f]<=127?(d+=P(e)+String.fromCharCode(a[f]),e=""):e+="%"+a[f].toString(16);return d+P(e)}function n(a,b,c){var d="";c=Math.min(a.length,c);for(var e=b;c>e;e++)d+=String.fromCharCode(a[e]);return d}function o(a,b,c){return n(a,b,c)}function p(a,b,c){var d=a.length;(!b||0>b)&&(b=0),(!c||0>c||c>d)&&(c=d);for(var e="",f=b;c>f;f++)e+=J(a[f]);return e}function q(a,b,c){for(var d=a.slice(b,c),e="",f=0;f=e)){var f;return c?(f=a[b],e>b+1&&(f|=a[b+1]<<8)):(f=a[b]<<8,e>b+1&&(f|=a[b+1])),f}}function s(a,b,c,d){d||(T("boolean"==typeof c,"missing or invalid endian"),T(void 0!==b&&null!==b,"missing offset"),T(b+3=e)){var f;return c?(e>b+2&&(f=a[b+2]<<16),e>b+1&&(f|=a[b+1]<<8),f|=a[b],e>b+3&&(f+=a[b+3]<<24>>>0)):(e>b+1&&(f=a[b+1]<<16),e>b+2&&(f|=a[b+2]<<8),e>b+3&&(f|=a[b+3]),f+=a[b]<<24>>>0),f}}function t(a,b,c,d){d||(T("boolean"==typeof c,"missing or invalid endian"),T(void 0!==b&&null!==b,"missing offset"),T(b+1=e)){var f=r(a,b,c,!0),g=32768&f;return g?-1*(65535-f+1):f}}function u(a,b,c,d){d||(T("boolean"==typeof c,"missing or invalid endian"),T(void 0!==b&&null!==b,"missing offset"),T(b+3=e)){var f=s(a,b,c,!0),g=2147483648&f;return g?-1*(4294967295-f+1):f}}function v(a,b,c,d){return d||(T("boolean"==typeof c,"missing or invalid endian"),T(b+3=f))for(var g=0,h=Math.min(f-c,2);h>g;g++)a[c+g]=(b&255<<8*(d?g:1-g))>>>8*(d?g:1-g)}function y(a,b,c,d,e){e||(T(void 0!==b&&null!==b,"missing value"),T("boolean"==typeof d,"missing or invalid endian"),T(void 0!==c&&null!==c,"missing offset"),T(c+3=f))for(var g=0,h=Math.min(f-c,4);h>g;g++)a[c+g]=b>>>8*(d?g:3-g)&255}function z(a,b,c,d,e){e||(T(void 0!==b&&null!==b,"missing value"),T("boolean"==typeof d,"missing or invalid endian"),T(void 0!==c&&null!==c,"missing offset"),T(c+1=f||(b>=0?x(a,b,c,d,e):x(a,65535+b+1,c,d,e))}function A(a,b,c,d,e){e||(T(void 0!==b&&null!==b,"missing value"),T("boolean"==typeof d,"missing or invalid endian"),T(void 0!==c&&null!==c,"missing offset"),T(c+3=f||(b>=0?y(a,b,c,d,e):y(a,4294967295+b+1,c,d,e))}function B(a,b,c,d,e){e||(T(void 0!==b&&null!==b,"missing value"),T("boolean"==typeof d,"missing or invalid endian"),T(void 0!==c&&null!==c,"missing offset"),T(c+3=f||V.write(a,b,c,d,23,4)}function C(a,b,c,d,e){e||(T(void 0!==b&&null!==b,"missing value"),T("boolean"==typeof d,"missing or invalid endian"),T(void 0!==c&&null!==c,"missing offset"),T(c+7=f||V.write(a,b,c,d,52,8)}function D(a){return a.trim?a.trim():a.replace(/^\s+|\s+$/g,"")}function E(a){return a._isBuffer=!0,a._get=a.get,a._set=a.set,a.get=W.get,a.set=W.set,a.write=W.write,a.toString=W.toString,a.toLocaleString=W.toString,a.toJSON=W.toJSON,a.copy=W.copy,a.slice=W.slice,a.readUInt8=W.readUInt8,a.readUInt16LE=W.readUInt16LE,a.readUInt16BE=W.readUInt16BE,a.readUInt32LE=W.readUInt32LE,a.readUInt32BE=W.readUInt32BE,a.readInt8=W.readInt8,a.readInt16LE=W.readInt16LE,a.readInt16BE=W.readInt16BE,a.readInt32LE=W.readInt32LE,a.readInt32BE=W.readInt32BE,a.readFloatLE=W.readFloatLE,a.readFloatBE=W.readFloatBE,a.readDoubleLE=W.readDoubleLE,a.readDoubleBE=W.readDoubleBE,a.writeUInt8=W.writeUInt8,a.writeUInt16LE=W.writeUInt16LE,a.writeUInt16BE=W.writeUInt16BE,a.writeUInt32LE=W.writeUInt32LE,a.writeUInt32BE=W.writeUInt32BE,a.writeInt8=W.writeInt8,a.writeInt16LE=W.writeInt16LE,a.writeInt16BE=W.writeInt16BE,a.writeInt32LE=W.writeInt32LE,a.writeInt32BE=W.writeInt32BE,a.writeFloatLE=W.writeFloatLE,a.writeFloatBE=W.writeFloatBE,a.writeDoubleLE=W.writeDoubleLE,a.writeDoubleBE=W.writeDoubleBE,a.fill=W.fill,a.inspect=W.inspect,a.toArrayBuffer=W.toArrayBuffer,a}function F(a,b,c){return"number"!=typeof a?c:(a=~~a,a>=b?b:a>=0?a:(a+=b,a>=0?a:0))}function G(a){return a=~~Math.ceil(+a),0>a?0:a}function H(a){return(Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)})(a)}function I(a){return H(a)||e.isBuffer(a)||a&&"object"==typeof a&&"number"==typeof a.length}function J(a){return 16>a?"0"+a.toString(16):a.toString(16)}function K(a){for(var b=[],c=0;c=d)b.push(a.charCodeAt(c));else{var e=c;d>=55296&&57343>=d&&c++;for(var f=encodeURIComponent(a.slice(e,c+1)).substr(1).split("%"),g=0;g>8,d=b%256,e.push(d),e.push(c);return e}function N(a){return U.toByteArray(a)}function O(a,b,c,d){for(var e=0;d>e&&!(e+c>=b.length||e>=a.length);e++)b[e+c]=a[e];return e}function P(a){try{return decodeURIComponent(a)}catch(b){return String.fromCharCode(65533)}}function Q(a,b){T("number"==typeof a,"cannot write a non-number as a number"),T(a>=0,"specified a negative value for writing an unsigned value"),T(b>=a,"value is larger than maximum value for type"),T(Math.floor(a)===a,"value has a fractional component")}function R(a,b,c){T("number"==typeof a,"cannot write a non-number as a number"),T(b>=a,"value larger than maximum allowed value"),T(a>=c,"value smaller than minimum allowed value"),T(Math.floor(a)===a,"value has a fractional component")}function S(a,b,c){T("number"==typeof a,"cannot write a non-number as a number"),T(b>=a,"value larger than maximum allowed value"),T(a>=c,"value smaller than minimum allowed value")}function T(a,b){if(!a)throw new Error(b||"Failed assertion")}var U=a("base64-js"),V=a("ieee754");c.Buffer=e,c.SlowBuffer=e,c.INSPECT_MAX_BYTES=50,e.poolSize=8192,e._useTypedArrays=function(){if("function"!=typeof Uint8Array||"function"!=typeof ArrayBuffer)return!1;try{var a=new Uint8Array(0);return a.foo=function(){return 42},42===a.foo()&&"function"==typeof a.subarray}catch(b){return!1}}(),e.isEncoding=function(a){switch(String(a).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"raw":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},e.isBuffer=function(a){return!(null===a||void 0===a||!a._isBuffer)},e.byteLength=function(a,b){var c;switch(a+="",b||"utf8"){case"hex":c=a.length/2;break;case"utf8":case"utf-8":c=K(a).length;break;case"ascii":case"binary":case"raw":c=a.length;break;case"base64":c=N(a).length;break;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":c=2*a.length;break;default:throw new Error("Unknown encoding")}return c},e.concat=function(a,b){if(T(H(a),"Usage: Buffer.concat(list, [totalLength])\nlist should be an Array."),0===a.length)return new e(0);if(1===a.length)return a[0];var c;if("number"!=typeof b)for(b=0,c=0;cl&&(c=l)):c=l,d=String(d||"utf8").toLowerCase();var m;switch(d){case"hex":m=f(this,a,b,c);break;case"utf8":case"utf-8":m=g(this,a,b,c);break;case"ascii":m=h(this,a,b,c);break;case"binary":m=i(this,a,b,c);break;case"base64":m=j(this,a,b,c);break;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":m=k(this,a,b,c);break;default:throw new Error("Unknown encoding")}return m},e.prototype.toString=function(a,b,c){var d=this;if(a=String(a||"utf8").toLowerCase(),b=Number(b)||0,c=void 0!==c?Number(c):c=d.length,c===b)return"";var e;switch(a){case"hex":e=p(d,b,c);break;case"utf8":case"utf-8":e=m(d,b,c);break;case"ascii":e=n(d,b,c);break;case"binary":e=o(d,b,c);break;case"base64":e=l(d,b,c);break;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":e=q(d,b,c);break;default:throw new Error("Unknown encoding")}return e},e.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}},e.prototype.copy=function(a,b,c,d){var e=this;if(c||(c=0),d||0===d||(d=this.length),b||(b=0),d!==c&&0!==a.length&&0!==e.length){T(d>=c,"sourceEnd < sourceStart"),T(b>=0&&b=0&&c=0&&d<=e.length,"sourceEnd out of bounds"),d>this.length&&(d=this.length),a.length-bf;f++)a[f+b]=this[f+c]}},e.prototype.slice=function(a,b){var c=this.length;if(a=F(a,c,0),b=F(b,c,c),e._useTypedArrays)return E(this.subarray(a,b));for(var d=b-a,f=new e(d,void 0,!0),g=0;d>g;g++)f[g]=this[g+a];return f},e.prototype.get=function(a){return console.log(".get() is deprecated. Access using array indexes instead."),this.readUInt8(a)},e.prototype.set=function(a,b){return console.log(".set() is deprecated. Access using array indexes instead."),this.writeUInt8(a,b)},e.prototype.readUInt8=function(a,b){return b||(T(void 0!==a&&null!==a,"missing offset"),T(a=this.length?void 0:this[a]},e.prototype.readUInt16LE=function(a,b){return r(this,a,!0,b)},e.prototype.readUInt16BE=function(a,b){return r(this,a,!1,b)},e.prototype.readUInt32LE=function(a,b){return s(this,a,!0,b)},e.prototype.readUInt32BE=function(a,b){return s(this,a,!1,b)},e.prototype.readInt8=function(a,b){if(b||(T(void 0!==a&&null!==a,"missing offset"),T(a=this.length)){var c=128&this[a];return c?-1*(255-this[a]+1):this[a]}},e.prototype.readInt16LE=function(a,b){return t(this,a,!0,b)},e.prototype.readInt16BE=function(a,b){return t(this,a,!1,b)},e.prototype.readInt32LE=function(a,b){return u(this,a,!0,b)},e.prototype.readInt32BE=function(a,b){return u(this,a,!1,b)},e.prototype.readFloatLE=function(a,b){return v(this,a,!0,b)},e.prototype.readFloatBE=function(a,b){return v(this,a,!1,b)},e.prototype.readDoubleLE=function(a,b){return w(this,a,!0,b)},e.prototype.readDoubleBE=function(a,b){return w(this,a,!1,b)},e.prototype.writeUInt8=function(a,b,c){c||(T(void 0!==a&&null!==a,"missing value"),T(void 0!==b&&null!==b,"missing offset"),T(b=this.length||(this[b]=a)},e.prototype.writeUInt16LE=function(a,b,c){x(this,a,b,!0,c)},e.prototype.writeUInt16BE=function(a,b,c){x(this,a,b,!1,c)},e.prototype.writeUInt32LE=function(a,b,c){y(this,a,b,!0,c)},e.prototype.writeUInt32BE=function(a,b,c){y(this,a,b,!1,c)},e.prototype.writeInt8=function(a,b,c){c||(T(void 0!==a&&null!==a,"missing value"),T(void 0!==b&&null!==b,"missing offset"),T(b=this.length||(a>=0?this.writeUInt8(a,b,c):this.writeUInt8(255+a+1,b,c))},e.prototype.writeInt16LE=function(a,b,c){z(this,a,b,!0,c)},e.prototype.writeInt16BE=function(a,b,c){z(this,a,b,!1,c)},e.prototype.writeInt32LE=function(a,b,c){A(this,a,b,!0,c)},e.prototype.writeInt32BE=function(a,b,c){A(this,a,b,!1,c)},e.prototype.writeFloatLE=function(a,b,c){B(this,a,b,!0,c)},e.prototype.writeFloatBE=function(a,b,c){B(this,a,b,!1,c)},e.prototype.writeDoubleLE=function(a,b,c){C(this,a,b,!0,c)},e.prototype.writeDoubleBE=function(a,b,c){C(this,a,b,!1,c)},e.prototype.fill=function(a,b,c){if(a||(a=0),b||(b=0),c||(c=this.length),"string"==typeof a&&(a=a.charCodeAt(0)),T("number"==typeof a&&!isNaN(a),"value is not a number"),T(c>=b,"end < start"),c!==b&&0!==this.length){T(b>=0&&b=0&&c<=this.length,"end out of bounds");for(var d=b;c>d;d++)this[d]=a}},e.prototype.inspect=function(){for(var a=[],b=this.length,d=0;b>d;d++)if(a[d]=J(this[d]),d===c.INSPECT_MAX_BYTES){a[d+1]="...";break}return""},e.prototype.toArrayBuffer=function(){if("function"==typeof Uint8Array){if(e._useTypedArrays)return new e(this).buffer;for(var a=new Uint8Array(this.length),b=0,c=a.length;c>b;b+=1)a[b]=this[b];return a.buffer}throw new Error("Buffer.toArrayBuffer not supported in this browser")};var W=e.prototype}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer/index.js","/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer")},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,"base64-js":2,buffer:1,ieee754:3}],2:[function(a,b){(function(){var a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";!function(){"use strict";function c(a){var b=a.charCodeAt(0);return b===g?62:b===h?63:i>b?-1:i+10>b?b-i+26+26:k+26>b?b-k:j+26>b?b-j+26:void 0}function d(a){function b(a){j[l++]=a}var d,e,g,h,i,j;if(a.length%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var k=a.length;i="="===a.charAt(k-2)?2:"="===a.charAt(k-1)?1:0,j=new f(3*a.length/4-i),g=i>0?a.length-4:a.length;var l=0;for(d=0,e=0;g>d;d+=4,e+=3)h=c(a.charAt(d))<<18|c(a.charAt(d+1))<<12|c(a.charAt(d+2))<<6|c(a.charAt(d+3)),b((16711680&h)>>16),b((65280&h)>>8),b(255&h);return 2===i?(h=c(a.charAt(d))<<2|c(a.charAt(d+1))>>4,b(255&h)):1===i&&(h=c(a.charAt(d))<<10|c(a.charAt(d+1))<<4|c(a.charAt(d+2))>>2,b(h>>8&255),b(255&h)),j}function e(b){function c(b){return a.charAt(b)}function d(a){return c(a>>18&63)+c(a>>12&63)+c(a>>6&63)+c(63&a)}var e,f,g,h=b.length%3,i="";for(e=0,g=b.length-h;g>e;e+=3)f=(b[e]<<16)+(b[e+1]<<8)+b[e+2],i+=d(f);switch(h){case 1:f=b[b.length-1],i+=c(f>>2),i+=c(f<<4&63),i+="==";break;case 2:f=(b[b.length-2]<<8)+b[b.length-1],i+=c(f>>10),i+=c(f>>4&63),i+=c(f<<2&63),i+="="}return i}var f="undefined"!=typeof Uint8Array?Uint8Array:Array,g=("0".charCodeAt(0),"+".charCodeAt(0)),h="/".charCodeAt(0),i="0".charCodeAt(0),j="a".charCodeAt(0),k="A".charCodeAt(0);b.exports.toByteArray=d,b.exports.fromByteArray=e}()}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer/node_modules/base64-js/lib/b64.js","/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer/node_modules/base64-js/lib")},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],3:[function(a,b,c){(function(){c.read=function(a,b,c,d,e){var f,g,h=8*e-d-1,i=(1<>1,k=-7,l=c?e-1:0,m=c?-1:1,n=a[b+l];for(l+=m,f=n&(1<<-k)-1,n>>=-k,k+=h;k>0;f=256*f+a[b+l],l+=m,k-=8);for(g=f&(1<<-k)-1,f>>=-k,k+=d;k>0;g=256*g+a[b+l],l+=m,k-=8);if(0===f)f=1-j;else{if(f===i)return g?0/0:1/0*(n?-1:1);g+=Math.pow(2,d),f-=j}return(n?-1:1)*g*Math.pow(2,f-d)},c.write=function(a,b,c,d,e,f){var g,h,i,j=8*f-e-1,k=(1<>1,m=23===e?Math.pow(2,-24)-Math.pow(2,-77):0,n=d?0:f-1,o=d?1:-1,p=0>b||0===b&&0>1/b?1:0;for(b=Math.abs(b),isNaN(b)||1/0===b?(h=isNaN(b)?1:0,g=k):(g=Math.floor(Math.log(b)/Math.LN2),b*(i=Math.pow(2,-g))<1&&(g--,i*=2),b+=g+l>=1?m/i:m*Math.pow(2,1-l),b*i>=2&&(g++,i/=2),g+l>=k?(h=0,g=k):g+l>=1?(h=(b*i-1)*Math.pow(2,e),g+=l):(h=b*Math.pow(2,l-1)*Math.pow(2,e),g=0));e>=8;a[c+n]=255&h,n+=o,h/=256,e-=8);for(g=g<0;a[c+n]=255&g,n+=o,g/=256,j-=8);a[c+n-o]|=128*p}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer/node_modules/ieee754/index.js","/../node_modules/grunt-browserify/node_modules/browserify/node_modules/buffer/node_modules/ieee754")},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],4:[function(a,b){(function(a){function c(){}var a=b.exports={};a.nextTick=function(){var a="undefined"!=typeof window&&window.setImmediate,b="undefined"!=typeof window&&window.postMessage&&window.addEventListener;if(a)return function(a){return window.setImmediate(a)};if(b){var c=[];return window.addEventListener("message",function(a){var b=a.source;if((b===window||null===b)&&"process-tick"===a.data&&(a.stopPropagation(),c.length>0)){var d=c.shift();d()}},!0),function(a){c.push(a),window.postMessage("process-tick","*")}}return function(a){setTimeout(a,0)}}(),a.title="browser",a.browser=!0,a.env={},a.argv=[],a.on=c,a.once=c,a.off=c,a.emit=c,a.binding=function(){throw new Error("process.binding is not supported")},a.cwd=function(){return"/"},a.chdir=function(){throw new Error("process.chdir is not supported")}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js","/../node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process")},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],5:[function(a,b){(function(){function c(){}var d=a("./Scalar");b.exports=c,c.lineInt=function(a,b,c){c=c||0;var e,f,g,h,i,j,k,l=[0,0];return e=a[1][1]-a[0][1],f=a[0][0]-a[1][0],g=e*a[0][0]+f*a[0][1],h=b[1][1]-b[0][1],i=b[0][0]-b[1][0],j=h*b[0][0]+i*b[0][1],k=e*i-h*f,d.eq(k,0,c)||(l[0]=(i*g-f*j)/k,l[1]=(e*j-h*g)/k),l},c.segmentsIntersect=function(a,b,c,d){var e=b[0]-a[0],f=b[1]-a[1],g=d[0]-c[0],h=d[1]-c[1];if(g*f-h*e==0)return!1;var i=(e*(c[1]-a[1])+f*(a[0]-c[0]))/(g*f-h*e),j=(g*(a[1]-c[1])+h*(c[0]-a[0]))/(h*e-g*f);return i>=0&&1>=i&&j>=0&&1>=j}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/poly-decomp/src/Line.js","/../node_modules/poly-decomp/src")},{"./Scalar":8,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],6:[function(a,b){(function(){function a(){}b.exports=a,a.area=function(a,b,c){return(b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1])},a.left=function(b,c,d){return a.area(b,c,d)>0},a.leftOn=function(b,c,d){return a.area(b,c,d)>=0},a.right=function(b,c,d){return a.area(b,c,d)<0},a.rightOn=function(b,c,d){return a.area(b,c,d)<=0};var c=[],d=[];a.collinear=function(b,e,f,g){if(g){var h=c,i=d;h[0]=e[0]-b[0],h[1]=e[1]-b[1],i[0]=f[0]-e[0],i[1]=f[1]-e[1];var j=h[0]*i[0]+h[1]*i[1],k=Math.sqrt(h[0]*h[0]+h[1]*h[1]),l=Math.sqrt(i[0]*i[0]+i[1]*i[1]),m=Math.acos(j/(k*l));return g>m}return 0==a.area(b,e,f)},a.sqdist=function(a,b){var c=b[0]-a[0],d=b[1]-a[1];return c*c+d*d}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/poly-decomp/src/Point.js","/../node_modules/poly-decomp/src")},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],7:[function(a,b){(function(){function c(){this.vertices=[]}function d(a,b,c,d,e){e=e||0;var f=b[1]-a[1],h=a[0]-b[0],i=f*a[0]+h*a[1],j=d[1]-c[1],k=c[0]-d[0],l=j*c[0]+k*c[1],m=f*k-j*h;return g.eq(m,0,e)?[0,0]:[(k*i-h*l)/m,(f*l-j*i)/m]}var e=a("./Line"),f=a("./Point"),g=a("./Scalar");b.exports=c,c.prototype.at=function(a){var b=this.vertices,c=b.length;return b[0>a?a%c+c:a%c]},c.prototype.first=function(){return this.vertices[0]},c.prototype.last=function(){return this.vertices[this.vertices.length-1]},c.prototype.clear=function(){this.vertices.length=0},c.prototype.append=function(a,b,c){if("undefined"==typeof b)throw new Error("From is not given!");if("undefined"==typeof c)throw new Error("To is not given!");if(b>c-1)throw new Error("lol1");if(c>a.vertices.length)throw new Error("lol2");if(0>b)throw new Error("lol3");for(var d=b;c>d;d++)this.vertices.push(a.vertices[d])},c.prototype.makeCCW=function(){for(var a=0,b=this.vertices,c=1;cb[a][0])&&(a=c);f.left(this.at(a-1),this.at(a),this.at(a+1))||this.reverse()},c.prototype.reverse=function(){for(var a=[],b=0,c=this.vertices.length;b!==c;b++)a.push(this.vertices.pop());this.vertices=a},c.prototype.isReflex=function(a){return f.right(this.at(a-1),this.at(a),this.at(a+1))};var h=[],i=[];c.prototype.canSee=function(a,b){var c,d,g=h,j=i;if(f.leftOn(this.at(a+1),this.at(a),this.at(b))&&f.rightOn(this.at(a-1),this.at(a),this.at(b)))return!1;d=f.sqdist(this.at(a),this.at(b));for(var k=0;k!==this.vertices.length;++k)if((k+1)%this.vertices.length!==a&&k!==a&&f.leftOn(this.at(a),this.at(b),this.at(k+1))&&f.rightOn(this.at(a),this.at(b),this.at(k))&&(g[0]=this.at(a),g[1]=this.at(b),j[0]=this.at(k),j[1]=this.at(k+1),c=e.lineInt(g,j),f.sqdist(this.at(a),c)a)for(var f=a;b>=f;f++)e.vertices.push(this.vertices[f]);else{for(var f=0;b>=f;f++)e.vertices.push(this.vertices[f]);for(var f=a;f0?this.slice(a):[this]},c.prototype.slice=function(a){if(0==a.length)return[this];if(a instanceof Array&&a.length&&a[0]instanceof Array&&2==a[0].length&&a[0][0]instanceof Array){for(var b=[this],c=0;cc;c++)if(e.segmentsIntersect(a[b],a[b+1],a[c],a[c+1]))return!1;for(var b=1;bh)return console.warn("quickDecomp: max level ("+h+") reached."),a;for(var x=0;xo&&(n=o,k=l,r=y))),f.left(v.at(x+1),v.at(x),v.at(y+1))&&f.rightOn(v.at(x+1),v.at(x),v.at(y))&&(l=d(v.at(x+1),v.at(x),v.at(y),v.at(y+1)),f.left(v.at(x-1),v.at(x),l)&&(o=f.sqdist(v.vertices[x],l),m>o&&(m=o,j=l,q=y)));if(r==(q+1)%this.vertices.length)l[0]=(k[0]+j[0])/2,l[1]=(k[1]+j[1])/2,e.push(l),q>x?(t.append(v,x,q+1),t.vertices.push(l),u.vertices.push(l),0!=r&&u.append(v,r,v.vertices.length),u.append(v,0,x+1)):(0!=x&&t.append(v,x,v.vertices.length),t.append(v,0,q+1),t.vertices.push(l),u.vertices.push(l),u.append(v,r,x+1));else{if(r>q&&(q+=this.vertices.length),p=Number.MAX_VALUE,r>q)return a;for(var y=r;q>=y;++y)f.leftOn(v.at(x-1),v.at(x),v.at(y))&&f.rightOn(v.at(x+1),v.at(x),v.at(y))&&(o=f.sqdist(v.at(x),v.at(y)),p>o&&(p=o,s=y%this.vertices.length));s>x?(t.append(v,x,s+1),0!=s&&u.append(v,s,w.length),u.append(v,0,x+1)):(0!=x&&t.append(v,x,w.length),t.append(v,0,s+1),u.append(v,s,x+1))}return t.vertices.length3&&c>=0;--c)f.collinear(this.at(c-1),this.at(c),this.at(c+1),a)&&(this.vertices.splice(c%this.vertices.length,1),c--,b++);return b}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/../node_modules/poly-decomp/src/Polygon.js","/../node_modules/poly-decomp/src")},{"./Line":5,"./Point":6,"./Scalar":8,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],8:[function(a,b){(function(){function a(){}b.exports=a,a.eq=function(a,b,c){return c=c||0,Math.abs(a-b) (http://steffe.se)",keywords:["p2.js","p2","physics","engine","2d"],main:"./src/p2.js",engines:{node:"*"},repository:{type:"git",url:"https://github.com/schteppe/p2.js.git"},bugs:{url:"https://github.com/schteppe/p2.js/issues"},licenses:[{type:"MIT"}],devDependencies:{grunt:"~0.4.0","grunt-contrib-jshint":"~0.9.2","grunt-contrib-nodeunit":"~0.1.2","grunt-contrib-uglify":"~0.4.0","grunt-contrib-watch":"~0.5.0","grunt-browserify":"~2.0.1","grunt-contrib-concat":"^0.4.0"},dependencies:{"poly-decomp":"0.1.0"}}},{}],11:[function(a,b){(function(){function c(a){this.lowerBound=d.create(),a&&a.lowerBound&&d.copy(this.lowerBound,a.lowerBound),this.upperBound=d.create(),a&&a.upperBound&&d.copy(this.upperBound,a.upperBound)}{var d=a("../math/vec2");a("../utils/Utils")}b.exports=c;var e=d.create();c.prototype.setFromPoints=function(a,b,c,f){var g=this.lowerBound,h=this.upperBound;"number"!=typeof c&&(c=0),0!==c?d.rotate(g,a[0],c):d.copy(g,a[0]),d.copy(h,g);for(var i=Math.cos(c),j=Math.sin(c),k=1;ko;o++)l[o]>h[o]&&(h[o]=l[o]),l[o]c&&(this.lowerBound[b]=c);var d=a.upperBound[b];this.upperBound[b]=c},c.aabbCheck=function(a,b){return a.aabbNeedsUpdate&&a.updateAABB(),b.aabbNeedsUpdate&&b.updateAABB(),a.aabb.overlaps(b.aabb)},c.prototype.boundingVolumeCheck=function(a,b){var d;switch(this.boundingVolumeType){case c.BOUNDING_CIRCLE:d=c.boundingRadiusCheck(a,b);break;case c.AABB:d=c.aabbCheck(a,b);break;default:throw new Error("Bounding volume type not recognized: "+this.boundingVolumeType)}return d},c.canCollide=function(a,b){return a.type===e.STATIC&&b.type===e.STATIC?!1:a.type===e.KINEMATIC&&b.type===e.STATIC||a.type===e.STATIC&&b.type===e.KINEMATIC?!1:a.type===e.KINEMATIC&&b.type===e.KINEMATIC?!1:a.sleepState===e.SLEEPING&&b.sleepState===e.SLEEPING?!1:a.sleepState===e.SLEEPING&&b.type===e.STATIC||b.sleepState===e.SLEEPING&&a.type===e.STATIC?!1:!0},c.NAIVE=1,c.SAP=2}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/collision/Broadphase.js","/collision")},{"../math/vec2":33,"../objects/Body":34,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],13:[function(a,b){(function(){function c(a){d.apply(this),a=e.defaults(a,{xmin:-100,xmax:100,ymin:-100,ymax:100,nx:10,ny:10}),this.xmin=a.xmin,this.ymin=a.ymin,this.xmax=a.xmax,this.ymax=a.ymax,this.nx=a.nx,this.ny=a.ny,this.binsizeX=(this.xmax-this.xmin)/this.nx,this.binsizeY=(this.ymax-this.ymin)/this.ny}var d=(a("../shapes/Circle"),a("../shapes/Plane"),a("../shapes/Particle"),a("../collision/Broadphase")),e=(a("../math/vec2"),a("../utils/Utils"));b.exports=c,c.prototype=new d,c.prototype.getCollisionPairs=function(a){for(var b=[],c=a.bodies,e=c.length,f=(this.binsizeX,this.binsizeY,this.nx),g=this.ny,h=this.xmin,i=this.ymin,j=this.xmax,k=this.ymax,l=[],m=f*g,n=0;m>n;n++)l.push([]);for(var o=f/(j-h),p=g/(k-i),n=0;n!==e;n++)for(var q=c[n],r=q.aabb,s=Math.max(r.lowerBound[0],h),t=Math.max(r.lowerBound[1],i),u=Math.min(r.upperBound[0],j),v=Math.min(r.upperBound[1],k),w=Math.floor(o*(s-h)),x=Math.floor(p*(t-i)),y=Math.floor(o*(u-h)),z=Math.floor(p*(v-i)),A=w;y>=A;A++)for(var B=x;z>=B;B++){var C=A,D=B,E=C*(g-1)+D;E>=0&&m>E&&l[E].push(q)}for(var n=0;n!==m;n++)for(var F=l[n],A=0,G=F.length;A!==G;A++)for(var q=F[A],B=0;B!==A;B++){var H=F[B];d.canCollide(q,H)&&this.boundingVolumeCheck(q,H)&&b.push(q,H)}return b}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/collision/GridBroadphase.js","/collision")},{"../collision/Broadphase":12,"../math/vec2":33,"../shapes/Circle":40,"../shapes/Particle":44,"../shapes/Plane":45,"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],14:[function(a,b){(function(){function c(){d.call(this,d.NAIVE)}{var d=(a("../shapes/Circle"),a("../shapes/Plane"),a("../shapes/Shape"),a("../shapes/Particle"),a("../collision/Broadphase"));a("../math/vec2")}b.exports=c,c.prototype=new d,c.prototype.getCollisionPairs=function(a){var b=a.bodies,c=this.result;c.length=0;for(var e=0,f=b.length;e!==f;e++)for(var g=b[e],h=0;e>h;h++){var i=b[h];d.canCollide(g,i)&&this.boundingVolumeCheck(g,i)&&c.push(g,i)}return c}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/collision/NaiveBroadphase.js","/collision")},{"../collision/Broadphase":12,"../math/vec2":33,"../shapes/Circle":40,"../shapes/Particle":44,"../shapes/Plane":45,"../shapes/Shape":47,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],15:[function(a,b){(function(){function c(){this.contactEquations=[],this.frictionEquations=[],this.enableFriction=!0,this.slipForce=10,this.frictionCoefficient=.3,this.surfaceVelocity=0,this.reuseObjects=!0,this.reusableContactEquations=[],this.reusableFrictionEquations=[],this.restitution=0,this.stiffness=l.DEFAULT_STIFFNESS,this.relaxation=l.DEFAULT_RELAXATION,this.frictionStiffness=l.DEFAULT_STIFFNESS,this.frictionRelaxation=l.DEFAULT_RELAXATION,this.enableFrictionReduction=!0,this.collidingBodiesLastStep=new k,this.contactSkinSize=.01}function d(a,b){f.set(a.vertices[0],.5*-b.length,-b.radius),f.set(a.vertices[1],.5*b.length,-b.radius),f.set(a.vertices[2],.5*b.length,b.radius),f.set(a.vertices[3],.5*-b.length,b.radius)}function e(a,b,c,d){for(var e=R,i=S,j=T,k=U,l=a,m=b.vertices,n=null,o=0;o!==m.length+1;o++){var p=m[o%m.length],q=m[(o+1)%m.length];f.rotate(e,p,d),f.rotate(i,q,d),h(e,e,c),h(i,i,c),g(j,e,l),g(k,i,l);var r=f.crossLength(j,k);if(null===n&&(n=r),0>=r*n)return!1;n=r}return!0}var f=a("../math/vec2"),g=f.sub,h=f.add,i=f.dot,j=a("../utils/Utils"),k=a("../utils/TupleDictionary"),l=a("../equations/Equation"),m=a("../equations/ContactEquation"),n=a("../equations/FrictionEquation"),o=a("../shapes/Circle"),p=a("../shapes/Convex"),q=a("../shapes/Shape"),r=(a("../objects/Body"),a("../shapes/Rectangle"));b.exports=c;var s=f.fromValues(0,1),t=f.fromValues(0,0),u=f.fromValues(0,0),v=f.fromValues(0,0),w=f.fromValues(0,0),x=f.fromValues(0,0),y=f.fromValues(0,0),z=f.fromValues(0,0),A=f.fromValues(0,0),B=f.fromValues(0,0),C=f.fromValues(0,0),D=f.fromValues(0,0),E=f.fromValues(0,0),F=f.fromValues(0,0),G=f.fromValues(0,0),H=f.fromValues(0,0),I=f.fromValues(0,0),J=f.fromValues(0,0),K=f.fromValues(0,0),L=[];c.prototype.collidedLastStep=function(a,b){var c=0|a.id,d=0|b.id;return!!this.collidingBodiesLastStep.get(c,d)},c.prototype.reset=function(){this.collidingBodiesLastStep.reset();for(var a=this.contactEquations,b=a.length;b--;){var c=a[b],d=c.bodyA.id,e=c.bodyB.id;this.collidingBodiesLastStep.set(d,e,!0)}if(this.reuseObjects){var f=this.contactEquations,g=this.frictionEquations,h=this.reusableFrictionEquations,i=this.reusableContactEquations;j.appendArray(i,f),j.appendArray(h,g)}this.contactEquations.length=this.frictionEquations.length=0},c.prototype.createContactEquation=function(a,b,c,d){var e=this.reusableContactEquations.length?this.reusableContactEquations.pop():new m(a,b);return e.bodyA=a,e.bodyB=b,e.shapeA=c,e.shapeB=d,e.restitution=this.restitution,e.firstImpact=!this.collidedLastStep(a,b),e.stiffness=this.stiffness,e.relaxation=this.relaxation,e.needsUpdate=!0,e.enabled=!0,e.offset=this.contactSkinSize,e},c.prototype.createFrictionEquation=function(a,b,c,d){var e=this.reusableFrictionEquations.length?this.reusableFrictionEquations.pop():new n(a,b);return e.bodyA=a,e.bodyB=b,e.shapeA=c,e.shapeB=d,e.setSlipForce(this.slipForce),e.frictionCoefficient=this.frictionCoefficient,e.relativeVelocity=this.surfaceVelocity,e.enabled=!0,e.needsUpdate=!0,e.stiffness=this.frictionStiffness,e.relaxation=this.frictionRelaxation,e.contactEquations.length=0,e},c.prototype.createFrictionFromContact=function(a){var b=this.createFrictionEquation(a.bodyA,a.bodyB,a.shapeA,a.shapeB);return f.copy(b.contactPointA,a.contactPointA),f.copy(b.contactPointB,a.contactPointB),f.rotate90cw(b.t,a.normalA),b.contactEquations.push(a),b},c.prototype.createFrictionFromAverage=function(a){if(!a)throw new Error("numContacts == 0!");{var b=this.contactEquations[this.contactEquations.length-1],c=this.createFrictionEquation(b.bodyA,b.bodyB,b.shapeA,b.shapeB),d=b.bodyA;b.bodyB}f.set(c.contactPointA,0,0),f.set(c.contactPointB,0,0),f.set(c.t,0,0);for(var e=0;e!==a;e++)b=this.contactEquations[this.contactEquations.length-1-e],b.bodyA===d?(f.add(c.t,c.t,b.normalA),f.add(c.contactPointA,c.contactPointA,b.contactPointA),f.add(c.contactPointB,c.contactPointB,b.contactPointB)):(f.sub(c.t,c.t,b.normalA),f.add(c.contactPointA,c.contactPointA,b.contactPointB),f.add(c.contactPointB,c.contactPointB,b.contactPointA)),c.contactEquations.push(b);var g=1/a;return f.scale(c.contactPointA,c.contactPointA,g),f.scale(c.contactPointB,c.contactPointB,g),f.normalize(c.t,c.t),f.rotate90cw(c.t,c.t),c},c.prototype[q.LINE|q.CONVEX]=c.prototype.convexLine=function(a,b,c,d,e,f,g,h,i){return i?!1:0},c.prototype[q.LINE|q.RECTANGLE]=c.prototype.lineRectangle=function(a,b,c,d,e,f,g,h,i){return i?!1:0};var M=new r(1,1),N=f.create();c.prototype[q.CAPSULE|q.CONVEX]=c.prototype[q.CAPSULE|q.RECTANGLE]=c.prototype.convexCapsule=function(a,b,c,e,g,h,i,j,k){var l=N;f.set(l,h.length/2,0),f.rotate(l,l,j),f.add(l,l,i);var m=this.circleConvex(g,h,l,j,a,b,c,e,k,h.radius);f.set(l,-h.length/2,0),f.rotate(l,l,j),f.add(l,l,i);var n=this.circleConvex(g,h,l,j,a,b,c,e,k,h.radius);if(k&&(m||n))return!0;var o=M;d(o,h);var p=this.convexConvex(a,b,c,e,g,o,i,j,k);return p+m+n},c.prototype[q.CAPSULE|q.LINE]=c.prototype.lineCapsule=function(a,b,c,d,e,f,g,h,i){return i?!1:0};var O=f.create(),P=f.create(),Q=new r(1,1);c.prototype[q.CAPSULE|q.CAPSULE]=c.prototype.capsuleCapsule=function(a,b,c,e,g,h,i,j,k){for(var l,m=O,n=P,o=0,p=0;2>p;p++){f.set(m,(0===p?-1:1)*b.length/2,0),f.rotate(m,m,e),f.add(m,m,c);for(var q=0;2>q;q++){f.set(n,(0===q?-1:1)*h.length/2,0),f.rotate(n,n,j),f.add(n,n,i),this.enableFrictionReduction&&(l=this.enableFriction,this.enableFriction=!1);var r=this.circleCircle(a,b,m,e,g,h,n,j,k,b.radius,h.radius);if(this.enableFrictionReduction&&(this.enableFriction=l),k&&r)return!0;o+=r}}this.enableFrictionReduction&&(l=this.enableFriction,this.enableFriction=!1);var s=Q;d(s,b);var t=this.convexCapsule(a,s,c,e,g,h,i,j,k);if(this.enableFrictionReduction&&(this.enableFriction=l),k&&t)return!0;if(o+=t,this.enableFrictionReduction){var l=this.enableFriction;this.enableFriction=!1}d(s,h);var u=this.convexCapsule(g,s,i,j,a,b,c,e,k);return this.enableFrictionReduction&&(this.enableFriction=l),k&&u?!0:(o+=u,this.enableFrictionReduction&&o&&this.enableFriction&&this.frictionEquations.push(this.createFrictionFromAverage(o)),o)},c.prototype[q.LINE|q.LINE]=c.prototype.lineLine=function(a,b,c,d,e,f,g,h,i){return i?!1:0},c.prototype[q.PLANE|q.LINE]=c.prototype.planeLine=function(a,b,c,d,e,j,k,l,m){var n=t,o=u,p=v,q=w,r=x,C=y,D=z,E=A,F=B,G=L,H=0;f.set(n,-j.length/2,0),f.set(o,j.length/2,0),f.rotate(p,n,l),f.rotate(q,o,l),h(p,p,k),h(q,q,k),f.copy(n,p),f.copy(o,q),g(r,o,n),f.normalize(C,r),f.rotate90cw(F,C),f.rotate(E,s,d),G[0]=n,G[1]=o;for(var I=0;IK){if(m)return!0;var M=this.createContactEquation(a,e,b,j);H++,f.copy(M.normalA,E),f.normalize(M.normalA,M.normalA),f.scale(D,E,K),g(M.contactPointA,J,D),g(M.contactPointA,M.contactPointA,a.position),g(M.contactPointB,J,k),h(M.contactPointB,M.contactPointB,k),g(M.contactPointB,M.contactPointB,e.position),this.contactEquations.push(M),this.enableFrictionReduction||this.enableFriction&&this.frictionEquations.push(this.createFrictionFromContact(M))}}return m?!1:(this.enableFrictionReduction||H&&this.enableFriction&&this.frictionEquations.push(this.createFrictionFromAverage(H)),H)},c.prototype[q.PARTICLE|q.CAPSULE]=c.prototype.particleCapsule=function(a,b,c,d,e,f,g,h,i){return this.circleLine(a,b,c,d,e,f,g,h,i,f.radius,0)},c.prototype[q.CIRCLE|q.LINE]=c.prototype.circleLine=function(a,b,c,d,e,j,k,l,m,n,o){var n=n||0,o="undefined"!=typeof o?o:b.radius,p=t,q=u,r=v,s=w,H=x,I=y,J=z,K=A,M=B,N=C,O=D,P=E,Q=F,R=G,S=L;f.set(K,-j.length/2,0),f.set(M,j.length/2,0),f.rotate(N,K,l),f.rotate(O,M,l),h(N,N,k),h(O,O,k),f.copy(K,N),f.copy(M,O),g(I,M,K),f.normalize(J,I),f.rotate90cw(H,J),g(P,c,K);var T=i(P,H);g(s,K,k),g(Q,c,k);var U=o+n;if(Math.abs(T)W&&X>V){if(m)return!0;var Y=this.createContactEquation(a,e,b,j);return f.scale(Y.normalA,p,-1),f.normalize(Y.normalA,Y.normalA),f.scale(Y.contactPointA,Y.normalA,o),h(Y.contactPointA,Y.contactPointA,c),g(Y.contactPointA,Y.contactPointA,a.position),g(Y.contactPointB,r,k),h(Y.contactPointB,Y.contactPointB,k),g(Y.contactPointB,Y.contactPointB,e.position),this.contactEquations.push(Y),this.enableFriction&&this.frictionEquations.push(this.createFrictionFromContact(Y)),1}}S[0]=K,S[1]=M;for(var Z=0;ZQ&&(f.copy(J,B),L=Q,f.scale(A,s,Q),f.add(A,A,B),K=!0)}}if(K){if(m)return!0;var R=this.createContactEquation(a,i,b,j);return f.sub(R.normalA,J,c),f.normalize(R.normalA,R.normalA),f.scale(R.contactPointA,R.normalA,n),h(R.contactPointA,R.contactPointA,c),g(R.contactPointA,R.contactPointA,a.position),g(R.contactPointB,A,k),h(R.contactPointB,R.contactPointB,k),g(R.contactPointB,R.contactPointB,i.position),this.contactEquations.push(R),this.enableFriction&&this.frictionEquations.push(this.createFrictionFromContact(R)),1}if(n>0)for(var N=0;NQ&&(I=Q,f.scale(E,s,Q),f.add(E,E,c),f.copy(H,s),L=!0)}if(L){var R=this.createContactEquation(a,j,b,k);return f.scale(R.normalA,H,-1),f.normalize(R.normalA,R.normalA),f.set(R.contactPointA,0,0),h(R.contactPointA,R.contactPointA,c),g(R.contactPointA,R.contactPointA,a.position),g(R.contactPointB,E,l),h(R.contactPointB,R.contactPointB,l),g(R.contactPointB,R.contactPointB,j.position),this.contactEquations.push(R),this.enableFriction&&this.frictionEquations.push(this.createFrictionFromContact(R)),1}return 0},c.prototype[q.CIRCLE]=c.prototype.circleCircle=function(a,b,c,d,e,i,j,k,l,m,n){var o=t,m=m||b.radius,n=n||i.radius;g(o,c,j);var p=m+n;if(f.squaredLength(o)>Math.pow(p,2))return 0;if(l)return!0;var q=this.createContactEquation(a,e,b,i);return g(q.normalA,j,c),f.normalize(q.normalA,q.normalA),f.scale(q.contactPointA,q.normalA,m),f.scale(q.contactPointB,q.normalA,-n),h(q.contactPointA,q.contactPointA,c),g(q.contactPointA,q.contactPointA,a.position),h(q.contactPointB,q.contactPointB,j),g(q.contactPointB,q.contactPointB,e.position),this.contactEquations.push(q),this.enableFriction&&this.frictionEquations.push(this.createFrictionFromContact(q)),1},c.prototype[q.PLANE|q.CONVEX]=c.prototype[q.PLANE|q.RECTANGLE]=c.prototype.planeConvex=function(a,b,c,d,e,j,k,l,m){var n=t,o=u,p=v,q=0;f.rotate(o,s,d);for(var r=0;r!==j.vertices.length;r++){var w=j.vertices[r];if(f.rotate(n,w,l),h(n,n,k),g(p,n,c),i(p,o)<=0){if(m)return!0;q++;var x=this.createContactEquation(a,e,b,j);g(p,n,c),f.copy(x.normalA,o);var y=i(p,x.normalA);f.scale(p,x.normalA,y),g(x.contactPointB,n,e.position),g(x.contactPointA,n,p),g(x.contactPointA,x.contactPointA,a.position),this.contactEquations.push(x),this.enableFrictionReduction||this.enableFriction&&this.frictionEquations.push(this.createFrictionFromContact(x))}}return this.enableFrictionReduction&&this.enableFriction&&q&&this.frictionEquations.push(this.createFrictionFromAverage(q)),q},c.prototype[q.PARTICLE|q.PLANE]=c.prototype.particlePlane=function(a,b,c,d,e,h,j,k,l){var m=t,n=u;k=k||0,g(m,c,j),f.rotate(n,s,k);var o=i(m,n);if(o>0)return 0;if(l)return!0;var p=this.createContactEquation(e,a,h,b);return f.copy(p.normalA,n),f.scale(m,p.normalA,o),g(p.contactPointA,c,m),g(p.contactPointA,p.contactPointA,e.position),g(p.contactPointB,c,a.position),this.contactEquations.push(p),this.enableFriction&&this.frictionEquations.push(this.createFrictionFromContact(p)),1},c.prototype[q.CIRCLE|q.PARTICLE]=c.prototype.circleParticle=function(a,b,c,d,e,i,j,k,l){var m=t;if(g(m,j,c),f.squaredLength(m)>Math.pow(b.radius,2))return 0;if(l)return!0;var n=this.createContactEquation(a,e,b,i);return f.copy(n.normalA,m),f.normalize(n.normalA,n.normalA),f.scale(n.contactPointA,n.normalA,b.radius),h(n.contactPointA,n.contactPointA,c),g(n.contactPointA,n.contactPointA,a.position),g(n.contactPointB,j,e.position),this.contactEquations.push(n),this.enableFriction&&this.frictionEquations.push(this.createFrictionFromContact(n)),1};{var V=new o(1),W=f.create(),X=f.create();f.create()}c.prototype[q.PLANE|q.CAPSULE]=c.prototype.planeCapsule=function(a,b,c,d,e,g,i,j,k){var l=W,m=X,n=V;f.set(l,-g.length/2,0),f.rotate(l,l,j),h(l,l,i),f.set(m,g.length/2,0),f.rotate(m,m,j),h(m,m,i),n.radius=g.radius;var o;this.enableFrictionReduction&&(o=this.enableFriction,this.enableFriction=!1);var p=this.circlePlane(e,n,l,0,a,b,c,d,k),q=this.circlePlane(e,n,m,0,a,b,c,d,k);if(this.enableFrictionReduction&&(this.enableFriction=o),k)return p||q;var r=p+q;return this.enableFrictionReduction&&r&&this.frictionEquations.push(this.createFrictionFromAverage(r)),r},c.prototype[q.CIRCLE|q.PLANE]=c.prototype.circlePlane=function(a,b,c,d,e,j,k,l,m){var n=a,o=b,p=c,q=e,r=k,w=l;w=w||0;var x=t,y=u,z=v;g(x,p,r),f.rotate(y,s,w);var A=i(y,x);if(A>o.radius)return 0;if(m)return!0;var B=this.createContactEquation(q,n,j,b);return f.copy(B.normalA,y),f.scale(B.contactPointB,B.normalA,-o.radius),h(B.contactPointB,B.contactPointB,p),g(B.contactPointB,B.contactPointB,n.position),f.scale(z,B.normalA,A),g(B.contactPointA,x,z),h(B.contactPointA,B.contactPointA,r),g(B.contactPointA,B.contactPointA,q.position),this.contactEquations.push(B),this.enableFriction&&this.frictionEquations.push(this.createFrictionFromContact(B)),1},c.prototype[q.CONVEX]=c.prototype[q.CONVEX|q.RECTANGLE]=c.prototype[q.RECTANGLE]=c.prototype.convexConvex=function(a,b,d,e,j,k,l,m,n,o){var p=t,q=u,r=v,s=w,y=x,C=z,D=A,E=B,F=0,o="number"==typeof o?o:0,G=c.findSeparatingAxis(b,d,e,k,l,m,p);if(!G)return 0;g(D,l,d),i(p,D)>0&&f.scale(p,p,-1);var H=c.getClosestEdge(b,e,p,!0),I=c.getClosestEdge(k,m,p);if(-1===H||-1===I)return 0;for(var J=0;2>J;J++){var K=H,L=I,M=b,N=k,O=d,P=l,Q=e,R=m,S=a,T=j;if(0===J){var U;U=K,K=L,L=U,U=M,M=N,N=U,U=O,O=P,P=U,U=Q,Q=R,R=U,U=S,S=T,T=U}for(var V=L;L+2>V;V++){var W=N.vertices[(V+N.vertices.length)%N.vertices.length];f.rotate(q,W,R),h(q,q,P);for(var X=0,Y=K-1;K+2>Y;Y++){var Z=M.vertices[(Y+M.vertices.length)%M.vertices.length],$=M.vertices[(Y+1+M.vertices.length)%M.vertices.length];f.rotate(r,Z,Q),f.rotate(s,$,Q),h(r,r,O),h(s,s,O),g(y,s,r),f.rotate90cw(E,y),f.normalize(E,E),g(D,q,r);var _=i(E,D);(Y===K&&o>=_||Y!==K&&0>=_)&&X++}if(X>=3){if(n)return!0;var ab=this.createContactEquation(S,T,M,N);F++;var Z=M.vertices[K%M.vertices.length],$=M.vertices[(K+1)%M.vertices.length];f.rotate(r,Z,Q),f.rotate(s,$,Q),h(r,r,O),h(s,s,O),g(y,s,r),f.rotate90cw(ab.normalA,y),f.normalize(ab.normalA,ab.normalA),g(D,q,r);var _=i(ab.normalA,D);f.scale(C,ab.normalA,_),g(ab.contactPointA,q,O),g(ab.contactPointA,ab.contactPointA,C),h(ab.contactPointA,ab.contactPointA,O),g(ab.contactPointA,ab.contactPointA,S.position),g(ab.contactPointB,q,P),h(ab.contactPointB,ab.contactPointB,P),g(ab.contactPointB,ab.contactPointB,T.position),this.contactEquations.push(ab),this.enableFrictionReduction||this.enableFriction&&this.frictionEquations.push(this.createFrictionFromContact(ab))}}}return this.enableFrictionReduction&&this.enableFriction&&F&&this.frictionEquations.push(this.createFrictionFromAverage(F)),F};var Y=f.fromValues(0,0);c.projectConvexOntoAxis=function(a,b,c,d,e){var g,h,j=null,k=null,l=Y;f.rotate(l,d,-c);for(var m=0;mj)&&(j=h),(null===k||k>h)&&(k=h);if(k>j){var n=k;k=j,j=n}var o=i(b,d);f.set(e,k+o,j+o)};var Z=f.fromValues(0,0),$=f.fromValues(0,0),_=f.fromValues(0,0),ab=f.fromValues(0,0),bb=f.fromValues(0,0),cb=f.fromValues(0,0);c.findSeparatingAxis=function(a,b,d,e,h,i,j){var k=null,l=!1,m=!1,n=Z,o=$,p=_,q=ab,s=bb,t=cb;if(a instanceof r&&e instanceof r)for(var u=0;2!==u;u++){var v=a,w=d;1===u&&(v=e,w=i);for(var x=0;2!==x;x++){0===x?f.set(q,0,1):1===x&&f.set(q,1,0),0!==w&&f.rotate(q,q,w),c.projectConvexOntoAxis(a,b,d,q,s),c.projectConvexOntoAxis(e,h,i,q,t);var y=s,z=t,A=!1;s[0]>t[0]&&(z=s,y=t,A=!0);var B=z[0]-y[1];l=0>=B,(null===k||B>k)&&(f.copy(j,q),k=B,m=l)}}else for(var u=0;2!==u;u++){var v=a,w=d;1===u&&(v=e,w=i);for(var x=0;x!==v.vertices.length;x++){f.rotate(o,v.vertices[x],w),f.rotate(p,v.vertices[(x+1)%v.vertices.length],w),g(n,p,o),f.rotate90cw(q,n),f.normalize(q,q),c.projectConvexOntoAxis(a,b,d,q,s),c.projectConvexOntoAxis(e,h,i,q,t);var y=s,z=t,A=!1;s[0]>t[0]&&(z=s,y=t,A=!0);var B=z[0]-y[1];l=0>=B,(null===k||B>k)&&(f.copy(j,q),k=B,m=l)}}return m};var db=f.fromValues(0,0),eb=f.fromValues(0,0),fb=f.fromValues(0,0);c.getClosestEdge=function(a,b,c,d){var e=db,h=eb,j=fb;f.rotate(e,c,-b),d&&f.scale(e,e,-1);for(var k=-1,l=a.vertices.length,m=-1,n=0;n!==l;n++){g(h,a.vertices[(n+1)%l],a.vertices[n%l]),f.rotate90cw(j,h),f.normalize(j,j);var o=i(j,e);(-1===k||o>m)&&(k=n%l,m=o)}return k};var gb=f.create(),hb=f.create(),ib=f.create(),jb=f.create(),kb=f.create(),lb=f.create(),mb=f.create();c.prototype[q.CIRCLE|q.HEIGHTFIELD]=c.prototype.circleHeightfield=function(a,b,c,d,e,i,j,k,l,m){var n=i.data,m=m||b.radius,o=i.elementWidth,p=hb,q=gb,r=kb,s=mb,t=lb,u=ib,v=jb,w=Math.floor((c[0]-m-j[0])/o),x=Math.ceil((c[0]+m-j[0])/o);0>w&&(w=0),x>=n.length&&(x=n.length-1);for(var y=n[w],z=n[x],A=w;x>A;A++)n[A]y&&(y=n[A]);if(c[1]-m>y)return l?!1:0;c[1]+mA;A++){f.set(u,A*o,n[A]),f.set(v,(A+1)*o,n[A+1]),f.add(u,u,j),f.add(v,v,j),f.sub(t,v,u),f.rotate(t,t,Math.PI/2),f.normalize(t,t),f.scale(q,t,-m),f.add(q,q,c),f.sub(p,q,u);var C=f.dot(p,t);if(q[0]>=u[0]&&q[0]=C){if(l)return!0;B=!0,f.scale(p,t,-C),f.add(r,q,p),f.copy(s,t);var D=this.createContactEquation(e,a,i,b);f.copy(D.normalA,s),f.scale(D.contactPointB,D.normalA,-m),h(D.contactPointB,D.contactPointB,c),g(D.contactPointB,D.contactPointB,a.position),f.copy(D.contactPointA,r),f.sub(D.contactPointA,D.contactPointA,e.position),this.contactEquations.push(D),this.enableFriction&&this.frictionEquations.push(this.createFrictionFromContact(D))}}if(B=!1,m>0)for(var A=w;x>=A;A++)if(f.set(u,A*o,n[A]),f.add(u,u,j),f.sub(p,c,u),f.squaredLength(p)q&&(q=0),r>=k.length&&(r=k.length-1);for(var s=k[q],t=k[r],u=q;r>u;u++)k[u]s&&(s=k[u]);if(a.aabb.lowerBound[1]>s)return j?!1:0;for(var v=0,u=q;r>u;u++){f.set(m,u*l,k[u]),f.set(n,(u+1)*l,k[u+1]),f.add(m,m,h),f.add(n,n,h);var w=100;f.set(o,.5*(n[0]+m[0]),.5*(n[1]+m[1]-w)),f.sub(p.vertices[0],n,o),f.sub(p.vertices[1],m,o),f.copy(p.vertices[2],p.vertices[1]),f.copy(p.vertices[3],p.vertices[0]),p.vertices[2][1]-=w,p.vertices[3][1]-=w,v+=this.convexConvex(a,b,c,d,e,p,o,0,j)}return v}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/collision/Narrowphase.js","/collision")},{"../equations/ContactEquation":24,"../equations/Equation":25,"../equations/FrictionEquation":26,"../math/vec2":33,"../objects/Body":34,"../shapes/Circle":40,"../shapes/Convex":41,"../shapes/Rectangle":46,"../shapes/Shape":47,"../utils/TupleDictionary":51,"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],16:[function(a,b){(function(){function c(){e.call(this,e.SAP),this.axisList=[],this.world=null,this.axisIndex=0;var a=this.axisList;this._addBodyHandler=function(b){a.push(b.body)},this._removeBodyHandler=function(b){var c=a.indexOf(b.body);-1!==c&&a.splice(c,1)}}var d=a("../utils/Utils"),e=a("../collision/Broadphase");b.exports=c,c.prototype=new e,c.prototype.setWorld=function(a){this.axisList.length=0,d.appendArray(this.axisList,a.bodies),a.off("addBody",this._addBodyHandler).off("removeBody",this._removeBodyHandler),a.on("addBody",this._addBodyHandler).on("removeBody",this._removeBodyHandler),this.world=a},c.sortAxisList=function(a,b){b=0|b;for(var c=1,d=a.length;d>c;c++){for(var e=a[c],f=c-1;f>=0&&!(a[f].aabb.lowerBound[b]<=e.aabb.lowerBound[b]);f--)a[f+1]=a[f];a[f+1]=e}return a},c.prototype.getCollisionPairs=function(){var a=this.axisList,b=this.result,d=this.axisIndex;b.length=0;for(var f=a.length;f--;){var g=a[f];g.aabbNeedsUpdate&&g.updateAABB()}c.sortAxisList(a,d);for(var h=0,i=0|a.length;h!==i;h++)for(var j=a[h],k=h+1;i>k;k++){var l=a[k],m=l.aabb.lowerBound[d]<=j.aabb.upperBound[d];if(!m)break;e.canCollide(j,l)&&this.boundingVolumeCheck(j,l)&&b.push(j,l)}return b}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/collision/SAPBroadphase.js","/collision")},{"../collision/Broadphase":12,"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],17:[function(a,b){(function(){function c(a,b,c,e){this.type=c,e=d.defaults(e,{collideConnected:!0,wakeUpBodies:!0}),this.equations=[],this.bodyA=a,this.bodyB=b,this.collideConnected=e.collideConnected,e.wakeUpBodies&&(a&&a.wakeUp(),b&&b.wakeUp())}b.exports=c;var d=a("../utils/Utils");c.prototype.update=function(){throw new Error("method update() not implmemented in this Constraint subclass!")},c.DISTANCE=1,c.GEAR=2,c.LOCK=3,c.PRISMATIC=4,c.REVOLUTE=5,c.prototype.setStiffness=function(a){for(var b=this.equations,c=0;c!==b.length;c++){var d=b[c];d.stiffness=a,d.needsUpdate=!0}},c.prototype.setRelaxation=function(a){for(var b=this.equations,c=0;c!==b.length;c++){var d=b[c];d.relaxation=a,d.needsUpdate=!0}}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/constraints/Constraint.js","/constraints")},{"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],18:[function(a,b){(function(){function c(a,b,c){c=g.defaults(c,{localAnchorA:[0,0],localAnchorB:[0,0]}),d.call(this,a,b,d.DISTANCE,c),this.localAnchorA=f.fromValues(c.localAnchorA[0],c.localAnchorA[1]),this.localAnchorB=f.fromValues(c.localAnchorB[0],c.localAnchorB[1]);var h=this.localAnchorA,i=this.localAnchorB;if(this.distance=0,"number"==typeof c.distance)this.distance=c.distance;else{var j=f.create(),k=f.create(),l=f.create();f.rotate(j,h,a.angle),f.rotate(k,i,b.angle),f.add(l,b.position,k),f.sub(l,l,j),f.sub(l,l,a.position),this.distance=f.length(l)}var m;m="undefined"==typeof c.maxForce?Number.MAX_VALUE:c.maxForce;var n=new e(a,b,-m,m);this.equations=[n],this.maxForce=m;var l=f.create(),o=f.create(),p=f.create(),q=this;n.computeGq=function(){var a=this.bodyA,b=this.bodyB,c=a.position,d=b.position;return f.rotate(o,h,a.angle),f.rotate(p,i,b.angle),f.add(l,d,p),f.sub(l,l,o),f.sub(l,l,c),f.length(l)-q.distance},this.setMaxForce(m),this.upperLimitEnabled=!1,this.upperLimit=1,this.lowerLimitEnabled=!1,this.lowerLimit=0,this.position=0}var d=a("./Constraint"),e=a("../equations/Equation"),f=a("../math/vec2"),g=a("../utils/Utils"); +b.exports=c,c.prototype=new d;var h=f.create(),i=f.create(),j=f.create();c.prototype.update=function(){var a=this.equations[0],b=this.bodyA,c=this.bodyB,d=(this.distance,b.position),e=c.position,g=this.equations[0],k=a.G;f.rotate(i,this.localAnchorA,b.angle),f.rotate(j,this.localAnchorB,c.angle),f.add(h,e,j),f.sub(h,h,i),f.sub(h,h,d),this.position=f.length(h);var l=!1;if(this.upperLimitEnabled&&this.position>this.upperLimit&&(g.maxForce=0,g.minForce=-this.maxForce,this.distance=this.upperLimit,l=!0),this.lowerLimitEnabled&&this.positionc)g.scale(e.normalA,i,-1),g.sub(e.contactPointA,j,h.position),g.sub(e.contactPointB,k,o.position),g.scale(n,i,c),g.add(e.contactPointA,e.contactPointA,n),-1===a.indexOf(e)&&a.push(e);else{var u=a.indexOf(e);-1!==u&&a.splice(u,1)}if(this.lowerLimitEnabled&&d>s)g.scale(f.normalA,i,1),g.sub(f.contactPointA,j,h.position),g.sub(f.contactPointB,k,o.position),g.scale(n,i,d),g.sub(f.contactPointB,f.contactPointB,n),-1===a.indexOf(f)&&a.push(f);else{var u=a.indexOf(f);-1!==u&&a.splice(u,1)}},c.prototype.enableMotor=function(){this.motorEnabled||(this.equations.push(this.motorEquation),this.motorEnabled=!0)},c.prototype.disableMotor=function(){if(this.motorEnabled){var a=this.equations.indexOf(this.motorEquation);this.equations.splice(a,1),this.motorEnabled=!1}},c.prototype.setLimits=function(a,b){"number"==typeof a?(this.lowerLimit=a,this.lowerLimitEnabled=!0):(this.lowerLimit=a,this.lowerLimitEnabled=!1),"number"==typeof b?(this.upperLimit=b,this.upperLimitEnabled=!0):(this.upperLimit=b,this.upperLimitEnabled=!1)}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/constraints/PrismaticConstraint.js","/constraints")},{"../equations/ContactEquation":24,"../equations/Equation":25,"../equations/RotationalLockEquation":27,"../math/vec2":33,"./Constraint":17,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],22:[function(a,b){(function(){function c(a,b,c){c=c||{},d.call(this,a,b,d.REVOLUTE,c);var n=this.maxForce="undefined"!=typeof c.maxForce?c.maxForce:Number.MAX_VALUE;this.pivotA=h.create(),this.pivotB=h.create(),c.worldPivot?(h.sub(this.pivotA,c.worldPivot,a.position),h.sub(this.pivotB,c.worldPivot,b.position),h.rotate(this.pivotA,this.pivotA,-a.angle),h.rotate(this.pivotB,this.pivotB,-b.angle)):(h.copy(this.pivotA,c.localPivotA),h.copy(this.pivotB,c.localPivotB));var o=this.equations=[new e(a,b,-n,n),new e(a,b,-n,n)],p=o[0],q=o[1],r=this;p.computeGq=function(){return h.rotate(i,r.pivotA,a.angle),h.rotate(j,r.pivotB,b.angle),h.add(m,b.position,j),h.sub(m,m,a.position),h.sub(m,m,i),h.dot(m,k)},q.computeGq=function(){return h.rotate(i,r.pivotA,a.angle),h.rotate(j,r.pivotB,b.angle),h.add(m,b.position,j),h.sub(m,m,a.position),h.sub(m,m,i),h.dot(m,l)},q.minForce=p.minForce=-n,q.maxForce=p.maxForce=n,this.motorEquation=new f(a,b),this.motorEnabled=!1,this.angle=0,this.lowerLimitEnabled=!1,this.upperLimitEnabled=!1,this.lowerLimit=0,this.upperLimit=0,this.upperLimitEquation=new g(a,b),this.lowerLimitEquation=new g(a,b),this.upperLimitEquation.minForce=0,this.lowerLimitEquation.maxForce=0}var d=a("./Constraint"),e=a("../equations/Equation"),f=a("../equations/RotationalVelocityEquation"),g=a("../equations/RotationalLockEquation"),h=a("../math/vec2");b.exports=c;var i=h.create(),j=h.create(),k=h.fromValues(1,0),l=h.fromValues(0,1),m=h.create();c.prototype=new d,c.prototype.setLimits=function(a,b){"number"==typeof a?(this.lowerLimit=a,this.lowerLimitEnabled=!0):(this.lowerLimit=a,this.lowerLimitEnabled=!1),"number"==typeof b?(this.upperLimit=b,this.upperLimitEnabled=!0):(this.upperLimit=b,this.upperLimitEnabled=!1)},c.prototype.update=function(){var a=this.bodyA,b=this.bodyB,c=this.pivotA,d=this.pivotB,e=this.equations,f=(e[0],e[1],e[0]),g=e[1],m=this.upperLimit,n=this.lowerLimit,o=this.upperLimitEquation,p=this.lowerLimitEquation,q=this.angle=b.angle-a.angle;if(this.upperLimitEnabled&&q>m)o.angle=m,-1===e.indexOf(o)&&e.push(o);else{var r=e.indexOf(o);-1!==r&&e.splice(r,1)}if(this.lowerLimitEnabled&&n>q)p.angle=n,-1===e.indexOf(p)&&e.push(p);else{var r=e.indexOf(p);-1!==r&&e.splice(r,1)}h.rotate(i,c,a.angle),h.rotate(j,d,b.angle),f.G[0]=-1,f.G[1]=0,f.G[2]=-h.crossLength(i,k),f.G[3]=1,f.G[4]=0,f.G[5]=h.crossLength(j,k),g.G[0]=0,g.G[1]=-1,g.G[2]=-h.crossLength(i,l),g.G[3]=0,g.G[4]=1,g.G[5]=h.crossLength(j,l)},c.prototype.enableMotor=function(){this.motorEnabled||(this.equations.push(this.motorEquation),this.motorEnabled=!0)},c.prototype.disableMotor=function(){if(this.motorEnabled){var a=this.equations.indexOf(this.motorEquation);this.equations.splice(a,1),this.motorEnabled=!1}},c.prototype.motorIsEnabled=function(){return!!this.motorEnabled},c.prototype.setMotorSpeed=function(a){if(this.motorEnabled){var b=this.equations.indexOf(this.motorEquation);this.equations[b].relativeVelocity=a}},c.prototype.getMotorSpeed=function(){return this.motorEnabled?this.motorEquation.relativeVelocity:!1}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/constraints/RevoluteConstraint.js","/constraints")},{"../equations/Equation":25,"../equations/RotationalLockEquation":27,"../equations/RotationalVelocityEquation":28,"../math/vec2":33,"./Constraint":17,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],23:[function(a,b){(function(){function c(a,b,c){c=c||{},d.call(this,a,b,-Number.MAX_VALUE,Number.MAX_VALUE),this.angle=c.angle||0,this.ratio="number"==typeof c.ratio?c.ratio:1,this.setRatio(this.ratio)}{var d=a("./Equation");a("../math/vec2")}b.exports=c,c.prototype=new d,c.prototype.constructor=c,c.prototype.computeGq=function(){return this.ratio*this.bodyA.angle-this.bodyB.angle+this.angle},c.prototype.setRatio=function(a){var b=this.G;b[2]=a,b[5]=-1,this.ratio=a},c.prototype.setMaxTorque=function(a){this.maxForce=a,this.minForce=-a}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/AngleLockEquation.js","/equations")},{"../math/vec2":33,"./Equation":25,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],24:[function(a,b){(function(){function c(a,b){d.call(this,a,b,0,Number.MAX_VALUE),this.contactPointA=e.create(),this.penetrationVec=e.create(),this.contactPointB=e.create(),this.normalA=e.create(),this.restitution=0,this.firstImpact=!1,this.shapeA=null,this.shapeB=null}var d=a("./Equation"),e=a("../math/vec2");b.exports=c,c.prototype=new d,c.prototype.constructor=c,c.prototype.computeB=function(a,b,c){var d=this.bodyA,f=this.bodyB,g=this.contactPointA,h=this.contactPointB,i=d.position,j=f.position,k=this.penetrationVec,l=this.normalA,m=this.G,n=e.crossLength(g,l),o=e.crossLength(h,l);m[0]=-l[0],m[1]=-l[1],m[2]=-n,m[3]=l[0],m[4]=l[1],m[5]=o,e.add(k,j,h),e.sub(k,k,i),e.sub(k,k,g);var p,q;this.firstImpact&&0!==this.restitution?(q=0,p=1/b*(1+this.restitution)*this.computeGW()):(q=e.dot(l,k)+this.offset,p=this.computeGW());var r=this.computeGiMf(),s=-q*a-p*b-c*r;return s}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/ContactEquation.js","/equations")},{"../math/vec2":33,"./Equation":25,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],25:[function(a,b){(function(){function c(a,b,d,f){this.minForce="undefined"==typeof d?-Number.MAX_VALUE:d,this.maxForce="undefined"==typeof f?Number.MAX_VALUE:f,this.bodyA=a,this.bodyB=b,this.stiffness=c.DEFAULT_STIFFNESS,this.relaxation=c.DEFAULT_RELAXATION,this.G=new e.ARRAY_TYPE(6);for(var g=0;6>g;g++)this.G[g]=0;this.offset=0,this.a=0,this.b=0,this.epsilon=0,this.timeStep=1/60,this.needsUpdate=!0,this.multiplier=0,this.relativeVelocity=0,this.enabled=!0}b.exports=c;{var d=a("../math/vec2"),e=a("../utils/Utils");a("../objects/Body")}c.prototype.constructor=c,c.DEFAULT_STIFFNESS=1e6,c.DEFAULT_RELAXATION=4,c.prototype.update=function(){var a=this.stiffness,b=this.relaxation,c=this.timeStep;this.a=4/(c*(1+4*b)),this.b=4*b/(1+4*b),this.epsilon=4/(c*c*a*(1+4*b)),this.needsUpdate=!1},c.prototype.gmult=function(a,b,c,d,e){return a[0]*b[0]+a[1]*b[1]+a[2]*c+a[3]*d[0]+a[4]*d[1]+a[5]*e},c.prototype.computeB=function(a,b,c){var d=this.computeGW(),e=this.computeGq(),f=this.computeGiMf();return-e*a-d*b-f*c};var f=d.create(),g=d.create();c.prototype.computeGq=function(){var a=this.G,b=this.bodyA,c=this.bodyB,d=(b.position,c.position,b.angle),e=c.angle;return this.gmult(a,f,d,g,e)+this.offset},c.prototype.computeGW=function(){var a=this.G,b=this.bodyA,c=this.bodyB,d=b.velocity,e=c.velocity,f=b.angularVelocity,g=c.angularVelocity;return this.gmult(a,d,f,e,g)+this.relativeVelocity},c.prototype.computeGWlambda=function(){var a=this.G,b=this.bodyA,c=this.bodyB,d=b.vlambda,e=c.vlambda,f=b.wlambda,g=c.wlambda;return this.gmult(a,d,f,e,g)};var h=d.create(),i=d.create();c.prototype.computeGiMf=function(){var a=this.bodyA,b=this.bodyB,c=a.force,e=a.angularForce,f=b.force,g=b.angularForce,j=a.invMassSolve,k=b.invMassSolve,l=a.invInertiaSolve,m=b.invInertiaSolve,n=this.G;return d.scale(h,c,j),d.scale(i,f,k),this.gmult(n,h,e*l,i,g*m)},c.prototype.computeGiMGt=function(){var a=this.bodyA,b=this.bodyB,c=a.invMassSolve,d=b.invMassSolve,e=a.invInertiaSolve,f=b.invInertiaSolve,g=this.G;return g[0]*g[0]*c+g[1]*g[1]*c+g[2]*g[2]*e+g[3]*g[3]*d+g[4]*g[4]*d+g[5]*g[5]*f};{var j=d.create(),k=d.create(),l=d.create();d.create(),d.create(),d.create()}c.prototype.addToWlambda=function(a){var b=this.bodyA,c=this.bodyB,e=j,f=k,g=l,h=b.invMassSolve,i=c.invMassSolve,m=b.invInertiaSolve,n=c.invInertiaSolve,o=this.G;f[0]=o[0],f[1]=o[1],g[0]=o[3],g[1]=o[4],d.scale(e,f,h*a),d.add(b.vlambda,b.vlambda,e),b.wlambda+=m*o[2]*a,d.scale(e,g,i*a),d.add(c.vlambda,c.vlambda,e),c.wlambda+=n*o[5]*a},c.prototype.computeInvC=function(a){return 1/(this.computeGiMGt()+a)}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/Equation.js","/equations")},{"../math/vec2":33,"../objects/Body":34,"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],26:[function(a,b){(function(){function c(a,b,c){e.call(this,a,b,-c,c),this.contactPointA=d.create(),this.contactPointB=d.create(),this.t=d.create(),this.contactEquations=[],this.shapeA=null,this.shapeB=null,this.frictionCoefficient=.3}{var d=a("../math/vec2"),e=a("./Equation");a("../utils/Utils")}b.exports=c,c.prototype=new e,c.prototype.constructor=c,c.prototype.setSlipForce=function(a){this.maxForce=a,this.minForce=-a},c.prototype.getSlipForce=function(){return this.maxForce},c.prototype.computeB=function(a,b,c){var e=(this.bodyA,this.bodyB,this.contactPointA),f=this.contactPointB,g=this.t,h=this.G;h[0]=-g[0],h[1]=-g[1],h[2]=-d.crossLength(e,g),h[3]=g[0],h[4]=g[1],h[5]=d.crossLength(f,g);var i=this.computeGW(),j=this.computeGiMf(),k=-i*b-c*j;return k}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/FrictionEquation.js","/equations")},{"../math/vec2":33,"../utils/Utils":52,"./Equation":25,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],27:[function(a,b){(function(){function c(a,b,c){c=c||{},d.call(this,a,b,-Number.MAX_VALUE,Number.MAX_VALUE),this.angle=c.angle||0;var e=this.G;e[2]=1,e[5]=-1}var d=a("./Equation"),e=a("../math/vec2");b.exports=c,c.prototype=new d,c.prototype.constructor=c;var f=e.create(),g=e.create(),h=e.fromValues(1,0),i=e.fromValues(0,1);c.prototype.computeGq=function(){return e.rotate(f,h,this.bodyA.angle+this.angle),e.rotate(g,i,this.bodyB.angle),e.dot(f,g)}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/RotationalLockEquation.js","/equations")},{"../math/vec2":33,"./Equation":25,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],28:[function(a,b){(function(){function c(a,b){d.call(this,a,b,-Number.MAX_VALUE,Number.MAX_VALUE),this.relativeVelocity=1,this.ratio=1}{var d=a("./Equation");a("../math/vec2")}b.exports=c,c.prototype=new d,c.prototype.constructor=c,c.prototype.computeB=function(a,b,c){var d=this.G;d[2]=-1,d[5]=this.ratio;var e=this.computeGiMf(),f=this.computeGW(),g=-f*b-c*e;return g}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/equations/RotationalVelocityEquation.js","/equations")},{"../math/vec2":33,"./Equation":25,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],29:[function(a,b){(function(){var a=function(){};b.exports=a,a.prototype={constructor:a,on:function(a,b,c){b.context=c||this,void 0===this._listeners&&(this._listeners={});var d=this._listeners;return void 0===d[a]&&(d[a]=[]),-1===d[a].indexOf(b)&&d[a].push(b),this},has:function(a,b){if(void 0===this._listeners)return!1;var c=this._listeners;if(b){if(void 0!==c[a]&&-1!==c[a].indexOf(b))return!0}else if(void 0!==c[a])return!0;return!1},off:function(a,b){if(void 0===this._listeners)return this;var c=this._listeners,d=c[a].indexOf(b);return-1!==d&&c[a].splice(d,1),this},emit:function(a){if(void 0===this._listeners)return this;var b=this._listeners,c=b[a.type];if(void 0!==c){a.target=this;for(var d=0,e=c.length;e>d;d++){var f=c[d];f.call(f.context,a)}}return this}}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/events/EventEmitter.js","/events")},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],30:[function(a,b){(function(){function c(a,b,f){if(f=f||{},!(a instanceof d&&b instanceof d))throw new Error("First two arguments must be Material instances.");this.id=c.idCounter++,this.materialA=a,this.materialB=b,this.friction="undefined"!=typeof f.friction?Number(f.friction):.3,this.restitution="undefined"!=typeof f.restitution?Number(f.restitution):0,this.stiffness="undefined"!=typeof f.stiffness?Number(f.stiffness):e.DEFAULT_STIFFNESS,this.relaxation="undefined"!=typeof f.relaxation?Number(f.relaxation):e.DEFAULT_RELAXATION,this.frictionStiffness="undefined"!=typeof f.frictionStiffness?Number(f.frictionStiffness):e.DEFAULT_STIFFNESS,this.frictionRelaxation="undefined"!=typeof f.frictionRelaxation?Number(f.frictionRelaxation):e.DEFAULT_RELAXATION,this.surfaceVelocity="undefined"!=typeof f.surfaceVelocity?Number(f.surfaceVelocity):0,this.contactSkinSize=.005}var d=a("./Material"),e=a("../equations/Equation");b.exports=c,c.idCounter=0}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/material/ContactMaterial.js","/material")},{"../equations/Equation":25,"./Material":31,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],31:[function(a,b){(function(){function a(b){this.id=b||a.idCounter++}b.exports=a,a.idCounter=0}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/material/Material.js","/material")},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],32:[function(a,b){(function(){var a={};a.GetArea=function(a){if(a.length<6)return 0;for(var b=a.length-2,c=0,d=0;b>d;d+=2)c+=(a[d+2]-a[d])*(a[d+1]+a[d+3]);return c+=(a[0]-a[b])*(a[b+1]+a[1]),.5*-c},a.Triangulate=function(b){var c=b.length>>1;if(3>c)return[];for(var d=[],e=[],f=0;c>f;f++)e.push(f);for(var f=0,g=c;g>3;){var h=e[(f+0)%g],i=e[(f+1)%g],j=e[(f+2)%g],k=b[2*h],l=b[2*h+1],m=b[2*i],n=b[2*i+1],o=b[2*j],p=b[2*j+1],q=!1;if(a._convex(k,l,m,n,o,p)){q=!0;for(var r=0;g>r;r++){var s=e[r];if(s!=h&&s!=i&&s!=j&&a._PointInTriangle(b[2*s],b[2*s+1],k,l,m,n,o,p)){q=!1;break}}}if(q)d.push(h,i,j),e.splice((f+1)%g,1),g--,f=0;else if(f++>3*g)break}return d.push(e[0],e[1],e[2]),d},a._PointInTriangle=function(a,b,c,d,e,f,g,h){var i=g-c,j=h-d,k=e-c,l=f-d,m=a-c,n=b-d,o=i*i+j*j,p=i*k+j*l,q=i*m+j*n,r=k*k+l*l,s=k*m+l*n,t=1/(o*r-p*p),u=(r*q-p*s)*t,v=(o*s-p*q)*t;return u>=0&&v>=0&&1>u+v},a._convex=function(a,b,c,d,e,f){return(b-d)*(e-c)+(c-a)*(f-d)>=0},b.exports=a}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/math/polyk.js","/math")},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],33:[function(a,b){(function(){var c=b.exports={},d=a("../utils/Utils");c.crossLength=function(a,b){return a[0]*b[1]-a[1]*b[0]},c.crossVZ=function(a,b,d){return c.rotate(a,b,-Math.PI/2),c.scale(a,a,d),a},c.crossZV=function(a,b,d){return c.rotate(a,d,Math.PI/2),c.scale(a,a,b),a},c.rotate=function(a,b,c){if(0!==c){var d=Math.cos(c),e=Math.sin(c),f=b[0],g=b[1];a[0]=d*f-e*g,a[1]=e*f+d*g}else a[0]=b[0],a[1]=b[1]},c.rotate90cw=function(a,b){var c=b[0],d=b[1];a[0]=d,a[1]=-c},c.toLocalFrame=function(a,b,d,e){c.copy(a,b),c.sub(a,a,d),c.rotate(a,a,-e)},c.toGlobalFrame=function(a,b,d,e){c.copy(a,b),c.rotate(a,a,e),c.add(a,a,d)},c.centroid=function(a,b,d,e){return c.add(a,b,d),c.add(a,a,e),c.scale(a,a,1/3),a},c.create=function(){var a=new d.ARRAY_TYPE(2);return a[0]=0,a[1]=0,a},c.clone=function(a){var b=new d.ARRAY_TYPE(2);return b[0]=a[0],b[1]=a[1],b},c.fromValues=function(a,b){var c=new d.ARRAY_TYPE(2);return c[0]=a,c[1]=b,c},c.copy=function(a,b){return a[0]=b[0],a[1]=b[1],a},c.set=function(a,b,c){return a[0]=b,a[1]=c,a},c.add=function(a,b,c){return a[0]=b[0]+c[0],a[1]=b[1]+c[1],a},c.subtract=function(a,b,c){return a[0]=b[0]-c[0],a[1]=b[1]-c[1],a},c.sub=c.subtract,c.multiply=function(a,b,c){return a[0]=b[0]*c[0],a[1]=b[1]*c[1],a},c.mul=c.multiply,c.divide=function(a,b,c){return a[0]=b[0]/c[0],a[1]=b[1]/c[1],a},c.div=c.divide,c.scale=function(a,b,c){return a[0]=b[0]*c,a[1]=b[1]*c,a},c.distance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1];return Math.sqrt(c*c+d*d)},c.dist=c.distance,c.squaredDistance=function(a,b){var c=b[0]-a[0],d=b[1]-a[1];return c*c+d*d},c.sqrDist=c.squaredDistance,c.length=function(a){var b=a[0],c=a[1];return Math.sqrt(b*b+c*c)},c.len=c.length,c.squaredLength=function(a){var b=a[0],c=a[1];return b*b+c*c},c.sqrLen=c.squaredLength,c.negate=function(a,b){return a[0]=-b[0],a[1]=-b[1],a},c.normalize=function(a,b){var c=b[0],d=b[1],e=c*c+d*d;return e>0&&(e=1/Math.sqrt(e),a[0]=b[0]*e,a[1]=b[1]*e),a},c.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]},c.str=function(a){return"vec2("+a[0]+", "+a[1]+")"}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/math/vec2.js","/math")},{"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],34:[function(a,b){(function(){function c(a){a=a||{},h.call(this),this.id=++c._idCounter,this.world=null,this.shapes=[],this.shapeOffsets=[],this.shapeAngles=[],this.mass=a.mass||0,this.invMass=0,this.inertia=0,this.invInertia=0,this.invMassSolve=0,this.invInertiaSolve=0,this.fixedRotation=!!a.fixedRotation||!1,this.position=d.fromValues(0,0),a.position&&d.copy(this.position,a.position),this.interpolatedPosition=d.fromValues(0,0),this.interpolatedAngle=0,this.previousPosition=d.fromValues(0,0),this.previousAngle=0,this.velocity=d.fromValues(0,0),a.velocity&&d.copy(this.velocity,a.velocity),this.vlambda=d.fromValues(0,0),this.wlambda=0,this.angle=a.angle||0,this.angularVelocity=a.angularVelocity||0,this.force=d.create(),a.force&&d.copy(this.force,a.force),this.angularForce=a.angularForce||0,this.damping="number"==typeof a.damping?a.damping:.1,this.angularDamping="number"==typeof a.angularDamping?a.angularDamping:.1,this.type=c.STATIC,this.type="undefined"!=typeof a.type?a.type:a.mass?c.DYNAMIC:c.STATIC,this.boundingRadius=0,this.aabb=new g,this.aabbNeedsUpdate=!0,this.allowSleep=!0,this.wantsToSleep=!1,this.sleepState=c.AWAKE,this.sleepSpeedLimit=.2,this.sleepTimeLimit=1,this.gravityScale=1,this.timeLastSleepy=0,this.concavePath=null,this.lastDampingScale=1,this.lastAngularDampingScale=1,this.lastDampingTimeStep=-1,this._wakeUpAfterNarrowphase=!1,this.updateMassProperties()}var d=a("../math/vec2"),e=a("poly-decomp"),f=a("../shapes/Convex"),g=a("../collision/AABB"),h=a("../events/EventEmitter");b.exports=c,c.prototype=new h,c._idCounter=0,c.prototype.updateSolveMassProperties=function(){this.sleepState===c.SLEEPING||this.type===c.KINEMATIC?(this.invMassSolve=0,this.invInertiaSolve=0):(this.invMassSolve=this.invMass,this.invInertiaSolve=this.invInertia)},c.prototype.setDensity=function(a){var b=this.getArea();this.mass=b*a,this.updateMassProperties()},c.prototype.getArea=function(){for(var a=0,b=0;be&&(e=h+i)}this.boundingRadius=e},c.prototype.addShape=function(a,b,c){c=c||0,b=b?d.fromValues(b[0],b[1]):d.fromValues(0,0),this.shapes.push(a),this.shapeOffsets.push(b),this.shapeAngles.push(c),this.updateMassProperties(),this.updateBoundingRadius(),this.aabbNeedsUpdate=!0},c.prototype.removeShape=function(a){var b=this.shapes.indexOf(a);return-1!==b?(this.shapes.splice(b,1),this.shapeOffsets.splice(b,1),this.shapeAngles.splice(b,1),this.aabbNeedsUpdate=!0,!0):!1},c.prototype.updateMassProperties=function(){if(this.type===c.STATIC||this.type===c.KINEMATIC)this.mass=Number.MAX_VALUE,this.invMass=0,this.inertia=Number.MAX_VALUE,this.invInertia=0;else{var a=this.shapes,b=a.length,e=this.mass/b,f=0;if(this.fixedRotation)this.inertia=Number.MAX_VALUE,this.invInertia=0;else{for(var g=0;b>g;g++){var h=a[g],i=d.squaredLength(this.shapeOffsets[g]),j=h.computeMomentOfInertia(e);f+=j+e*i}this.inertia=f,this.invInertia=f>0?1/f:0}this.invMass=1/this.mass}};var k=d.create();c.prototype.applyForce=function(a,b){var c=k;d.sub(c,b,this.position),d.add(this.force,this.force,a);var e=d.crossLength(c,a);this.angularForce+=e},c.prototype.toLocalFrame=function(a,b){d.toLocalFrame(a,b,this.position,this.angle)},c.prototype.toWorldFrame=function(a,b){d.toGlobalFrame(a,b,this.position,this.angle)},c.prototype.fromPolygon=function(a,b){b=b||{};for(var c=this.shapes.length;c>=0;--c)this.removeShape(this.shapes[c]);var g=new e.Polygon;if(g.vertices=a,g.makeCCW(),"number"==typeof b.removeCollinearPoints&&g.removeCollinearPoints(b.removeCollinearPoints),"undefined"==typeof b.skipSimpleCheck&&!g.isSimple())return!1;this.concavePath=g.vertices.slice(0);for(var c=0;c=g?(this.idleTime=0,this.sleepState=c.AWAKE):(this.idleTime+=e,this.sleepState=c.SLEEPY),this.idleTime>this.sleepTimeLimit&&(b?this.wantsToSleep=!0:this.sleep())}},c.prototype.getVelocityFromPosition=function(a,b){return a=a||d.create(),d.sub(a,this.position,this.previousPosition),d.scale(a,a,1/b),a},c.prototype.getAngularVelocityFromPosition=function(a){return(this.angle-this.previousAngle)/a},c.prototype.overlaps=function(a){return this.world.overlapKeeper.bodiesAreOverlapping(this,a)},c.sleepyEvent={type:"sleepy"},c.sleepEvent={type:"sleep"},c.wakeUpEvent={type:"wakeup"},c.DYNAMIC=1,c.STATIC=2,c.KINEMATIC=4,c.AWAKE=0,c.SLEEPY=1,c.SLEEPING=2}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/objects/Body.js","/objects")},{"../collision/AABB":11,"../events/EventEmitter":29,"../math/vec2":33,"../shapes/Convex":41,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1,"poly-decomp":9}],35:[function(a,b){(function(){function c(a,b,c){c=c||{},e.call(this,a,b,c),this.localAnchorA=d.fromValues(0,0),this.localAnchorB=d.fromValues(0,0),c.localAnchorA&&d.copy(this.localAnchorA,c.localAnchorA),c.localAnchorB&&d.copy(this.localAnchorB,c.localAnchorB),c.worldAnchorA&&this.setWorldAnchorA(c.worldAnchorA),c.worldAnchorB&&this.setWorldAnchorB(c.worldAnchorB);var f=d.create(),g=d.create();this.getWorldAnchorA(f),this.getWorldAnchorB(g);var h=d.distance(f,g);this.restLength="number"==typeof c.restLength?c.restLength:h}{var d=a("../math/vec2"),e=a("./Spring");a("../utils/Utils")}b.exports=c,c.prototype=new e,c.prototype.setWorldAnchorA=function(a){this.bodyA.toLocalFrame(this.localAnchorA,a)},c.prototype.setWorldAnchorB=function(a){this.bodyB.toLocalFrame(this.localAnchorB,a)},c.prototype.getWorldAnchorA=function(a){this.bodyA.toWorldFrame(a,this.localAnchorA)},c.prototype.getWorldAnchorB=function(a){this.bodyB.toWorldFrame(a,this.localAnchorB)};var f=d.create(),g=d.create(),h=d.create(),i=d.create(),j=d.create(),k=d.create(),l=d.create(),m=d.create(),n=d.create();c.prototype.applyForce=function(){var a=this.stiffness,b=this.damping,c=this.restLength,e=this.bodyA,o=this.bodyB,p=f,q=g,r=h,s=i,t=n,u=j,v=k,w=l,x=m;this.getWorldAnchorA(u),this.getWorldAnchorB(v),d.sub(w,u,e.position),d.sub(x,v,o.position),d.sub(p,v,u);var y=d.len(p);d.normalize(q,p),d.sub(r,o.velocity,e.velocity),d.crossZV(t,o.angularVelocity,x),d.add(r,r,t),d.crossZV(t,e.angularVelocity,w),d.sub(r,r,t),d.scale(s,q,-a*(y-c)-b*d.dot(r,q)),d.sub(e.force,e.force,s),d.add(o.force,o.force,s);var z=d.crossLength(w,s),A=d.crossLength(x,s);e.angularForce-=z,o.angularForce+=A}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/objects/LinearSpring.js","/objects")},{"../math/vec2":33,"../utils/Utils":52,"./Spring":37,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],36:[function(a,b){(function(){function c(a,b,c){c=c||{},d.call(this,a,b,c),this.restAngle="number"==typeof c.restAngle?c.restAngle:b.angle-a.angle}var d=(a("../math/vec2"),a("./Spring"));b.exports=c,c.prototype=new d,c.prototype.applyForce=function(){var a=this.stiffness,b=this.damping,c=this.restAngle,d=this.bodyA,e=this.bodyB,f=e.angle-d.angle,g=e.angularVelocity-d.angularVelocity,h=-a*(f-c)-b*g*0;d.angularForce-=h,e.angularForce+=h}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/objects/RotationalSpring.js","/objects")},{"../math/vec2":33,"./Spring":37,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],37:[function(a,b){(function(){function c(a,b,c){c=d.defaults(c,{stiffness:100,damping:1}),this.stiffness=c.stiffness,this.damping=c.damping,this.bodyA=a,this.bodyB=b}var d=(a("../math/vec2"),a("../utils/Utils"));b.exports=c,c.prototype.applyForce=function(){}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/objects/Spring.js","/objects")},{"../math/vec2":33,"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],38:[function(a,b){(function(){b.exports={AABB:a("./collision/AABB"),AngleLockEquation:a("./equations/AngleLockEquation"),Body:a("./objects/Body"),Broadphase:a("./collision/Broadphase"),Capsule:a("./shapes/Capsule"),Circle:a("./shapes/Circle"),Constraint:a("./constraints/Constraint"),ContactEquation:a("./equations/ContactEquation"),ContactMaterial:a("./material/ContactMaterial"),Convex:a("./shapes/Convex"),DistanceConstraint:a("./constraints/DistanceConstraint"),Equation:a("./equations/Equation"),EventEmitter:a("./events/EventEmitter"),FrictionEquation:a("./equations/FrictionEquation"),GearConstraint:a("./constraints/GearConstraint"),GridBroadphase:a("./collision/GridBroadphase"),GSSolver:a("./solver/GSSolver"),Heightfield:a("./shapes/Heightfield"),Line:a("./shapes/Line"),LockConstraint:a("./constraints/LockConstraint"),Material:a("./material/Material"),Narrowphase:a("./collision/Narrowphase"),NaiveBroadphase:a("./collision/NaiveBroadphase"),Particle:a("./shapes/Particle"),Plane:a("./shapes/Plane"),RevoluteConstraint:a("./constraints/RevoluteConstraint"),PrismaticConstraint:a("./constraints/PrismaticConstraint"),Rectangle:a("./shapes/Rectangle"),RotationalVelocityEquation:a("./equations/RotationalVelocityEquation"),SAPBroadphase:a("./collision/SAPBroadphase"),Shape:a("./shapes/Shape"),Solver:a("./solver/Solver"),Spring:a("./objects/Spring"),LinearSpring:a("./objects/LinearSpring"),RotationalSpring:a("./objects/RotationalSpring"),Utils:a("./utils/Utils"),World:a("./world/World"),vec2:a("./math/vec2"),version:a("../package.json").version}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/p2.js","/")},{"../package.json":10,"./collision/AABB":11,"./collision/Broadphase":12,"./collision/GridBroadphase":13,"./collision/NaiveBroadphase":14,"./collision/Narrowphase":15,"./collision/SAPBroadphase":16,"./constraints/Constraint":17,"./constraints/DistanceConstraint":18,"./constraints/GearConstraint":19,"./constraints/LockConstraint":20,"./constraints/PrismaticConstraint":21,"./constraints/RevoluteConstraint":22,"./equations/AngleLockEquation":23,"./equations/ContactEquation":24,"./equations/Equation":25,"./equations/FrictionEquation":26,"./equations/RotationalVelocityEquation":28,"./events/EventEmitter":29,"./material/ContactMaterial":30,"./material/Material":31,"./math/vec2":33,"./objects/Body":34,"./objects/LinearSpring":35,"./objects/RotationalSpring":36,"./objects/Spring":37,"./shapes/Capsule":39,"./shapes/Circle":40,"./shapes/Convex":41,"./shapes/Heightfield":42,"./shapes/Line":43,"./shapes/Particle":44,"./shapes/Plane":45,"./shapes/Rectangle":46,"./shapes/Shape":47,"./solver/GSSolver":48,"./solver/Solver":49,"./utils/Utils":52,"./world/World":56,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],39:[function(a,b){(function(){function c(a,b){this.length=a||1,this.radius=b||1,d.call(this,d.CAPSULE)}var d=a("./Shape"),e=a("../math/vec2");b.exports=c,c.prototype=new d,c.prototype.computeMomentOfInertia=function(a){var b=this.radius,c=this.length+b,d=2*b;return a*(d*d+c*c)/12},c.prototype.updateBoundingRadius=function(){this.boundingRadius=this.radius+this.length/2},c.prototype.updateArea=function(){this.area=Math.PI*this.radius*this.radius+2*this.radius*this.length};var f=e.create();c.prototype.computeAABB=function(a,b,c){var d=this.radius;e.set(f,this.length/2,0),0!==c&&e.rotate(f,f,c),e.set(a.upperBound,Math.max(f[0]+d,-f[0]+d),Math.max(f[1]+d,-f[1]+d)),e.set(a.lowerBound,Math.min(f[0]-d,-f[0]-d),Math.min(f[1]-d,-f[1]-d)),e.add(a.lowerBound,a.lowerBound,b),e.add(a.upperBound,a.upperBound,b)}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/shapes/Capsule.js","/shapes")},{"../math/vec2":33,"./Shape":47,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],40:[function(a,b){(function(){function c(a){this.radius=a||1,d.call(this,d.CIRCLE)}var d=a("./Shape"),e=a("../math/vec2");b.exports=c,c.prototype=new d,c.prototype.computeMomentOfInertia=function(a){var b=this.radius;return a*b*b/2},c.prototype.updateBoundingRadius=function(){this.boundingRadius=this.radius},c.prototype.updateArea=function(){this.area=Math.PI*this.radius*this.radius},c.prototype.computeAABB=function(a,b){var c=this.radius;e.set(a.upperBound,c,c),e.set(a.lowerBound,-c,-c),b&&(e.add(a.lowerBound,a.lowerBound,b),e.add(a.upperBound,a.upperBound,b))}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/shapes/Circle.js","/shapes")},{"../math/vec2":33,"./Shape":47,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],41:[function(a,b){(function(){function c(a,b){this.vertices=[],this.axes=[];for(var c=0;cf)&&(f=d),(null===h||h>d)&&(h=d);if(h>f){var j=h;h=f,f=j}e.set(b,h,f)},c.prototype.projectOntoWorldAxis=function(a,b,c,d){var f=h;this.projectOntoLocalAxis(a,d),0!==c?e.rotate(f,a,c):f=a;var g=e.dot(b,f);e.set(d,d[0]+g,d[1]+g)},c.prototype.updateTriangles=function(){this.triangles.length=0;for(var a=[],b=0;bg;f=g,g++){var h=this.vertices[f],i=this.vertices[g],j=Math.abs(e.crossLength(h,i)),k=e.dot(i,i)+e.dot(i,h)+e.dot(h,h);b+=j*k,c+=j}return a/6*(b/c)},c.prototype.updateBoundingRadius=function(){for(var a=this.vertices,b=0,c=0;c!==a.length;c++){var d=e.squaredLength(a[c]);d>b&&(b=d)}this.boundingRadius=Math.sqrt(b)},c.triangleArea=function(a,b,c){return.5*((b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1]))},c.prototype.updateArea=function(){this.updateTriangles(),this.area=0;for(var a=this.triangles,b=this.vertices,d=0;d!==a.length;d++){var e=a[d],f=b[e[0]],g=b[e[1]],h=b[e[2]],i=c.triangleArea(f,g,h);this.area+=i}},c.prototype.computeAABB=function(a,b,c){a.setFromPoints(this.vertices,b,c,0)}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/shapes/Convex.js","/shapes")},{"../math/polyk":32,"../math/vec2":33,"./Shape":47,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1,"poly-decomp":9}],42:[function(a,b){(function(){function c(a,b){if(b=e.defaults(b,{maxValue:null,minValue:null,elementWidth:.1}),null===b.minValue||null===b.maxValue){b.maxValue=a[0],b.minValue=a[0];for(var c=0;c!==a.length;c++){var f=a[c];f>b.maxValue&&(b.maxValue=f),f=w*w)break}for(c.updateMultipliers(k,q,1/a),x=0;x!==l;x++){var z=k[x];if(z instanceof h){for(var A=0,B=0;B!==z.contactEquations.length;B++)A+=z.contactEquations[B].multiplier;A*=z.frictionCoefficient/z.contactEquations.length,z.maxForce=A,z.minForce=-A}}}for(f=0;f!==i;f++){for(w=0,x=0;x!==l;x++){v=k[x];var y=c.iterateEquation(x,v,v.epsilon,u,t,q,p,a,f);w+=Math.abs(y)}if(this.usedIterations++,m>=w*w)break}for(r=0;r!==o;r++)n[r].addConstraintVelocity();c.updateMultipliers(k,q,1/a)}},c.updateMultipliers=function(a,b,c){for(var d=a.length;d--;)a[d].multiplier=b[d]*c},c.iterateEquation=function(a,b,c,d,e,f,g,h){var i=d[a],j=e[a],k=f[a],l=b.computeGWlambda(),m=b.maxForce,n=b.minForce;g&&(i=0);var o=j*(i-l-c*k),p=k+o;return n*h>p?o=n*h-k:p>m*h&&(o=m*h-k),f[a]+=o,b.addToWlambda(o),o}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/solver/GSSolver.js","/solver")},{"../equations/FrictionEquation":26,"../math/vec2":33,"../utils/Utils":52,"./Solver":49,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],49:[function(a,b){(function(){function c(a,b){a=a||{},d.call(this),this.type=b,this.equations=[],this.equationSortFunction=a.equationSortFunction||!1}var d=(a("../utils/Utils"),a("../events/EventEmitter"));b.exports=c,c.prototype=new d,c.prototype.solve=function(){throw new Error("Solver.solve should be implemented by subclasses!")};var e={bodies:[]};c.prototype.solveIsland=function(a,b){this.removeAllEquations(),b.equations.length&&(this.addEquations(b.equations),e.bodies.length=0,b.getBodies(e.bodies),e.bodies.length&&this.solve(a,e))},c.prototype.sortEquations=function(){this.equationSortFunction&&this.equations.sort(this.equationSortFunction)},c.prototype.addEquation=function(a){a.enabled&&this.equations.push(a)},c.prototype.addEquations=function(a){for(var b=0,c=a.length;b!==c;b++){var d=a[b];d.enabled&&this.equations.push(d)}},c.prototype.removeEquation=function(a){var b=this.equations.indexOf(a);-1!==b&&this.equations.splice(b,1)},c.prototype.removeAllEquations=function(){this.equations.length=0},c.GS=1,c.ISLAND=2}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/solver/Solver.js","/solver")},{"../events/EventEmitter":29,"../utils/Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],50:[function(a,b){(function(){function c(){this.overlappingShapesLastState=new e,this.overlappingShapesCurrentState=new e,this.recordPool=[],this.tmpDict=new e,this.tmpArray1=[]}function d(a,b,c,d){this.shapeA=b,this.shapeB=d,this.bodyA=a,this.bodyB=c}{var e=a("./TupleDictionary");a("./Utils")}b.exports=c,c.prototype.tick=function(){for(var a=this.overlappingShapesLastState,b=this.overlappingShapesCurrentState,c=a.keys.length;c--;){var d=a.keys[c],e=a.getByKey(d),f=b.getByKey(d);e&&!f&&this.recordPool.push(e)}a.reset(),a.copy(b),b.reset()},c.prototype.setOverlapping=function(a,b,c,e){var f=(this.overlappingShapesLastState,this.overlappingShapesCurrentState);if(!f.get(b.id,e.id)){var g;this.recordPool.length?(g=this.recordPool.pop(),g.set(a,b,c,e)):g=new d(a,b,c,e),f.set(b.id,e.id,g)}},c.prototype.getNewOverlaps=function(a){return this.getDiff(this.overlappingShapesLastState,this.overlappingShapesCurrentState,a)},c.prototype.getEndOverlaps=function(a){return this.getDiff(this.overlappingShapesCurrentState,this.overlappingShapesLastState,a)},c.prototype.bodiesAreOverlapping=function(a,b){for(var c=this.overlappingShapesCurrentState,d=c.keys.length;d--;){var e=c.keys[d],f=c.data[e];if(f.bodyA===a&&f.bodyB===b||f.bodyA===b&&f.bodyB===a)return!0}return!1},c.prototype.getDiff=function(a,b,c){var c=c||[],d=a,e=b;c.length=0;for(var f=e.keys.length;f--;){var g=e.keys[f],h=e.data[g];if(!h)throw new Error("Key "+g+" had no data!");var i=d.data[g];i||c.push(h)}return c},c.prototype.isNewOverlap=function(a,b){var c=0|a.id,d=0|b.id,e=this.overlappingShapesLastState,f=this.overlappingShapesCurrentState;return!e.get(c,d)&&!!f.get(c,d)},c.prototype.getNewBodyOverlaps=function(a){this.tmpArray1.length=0;var b=this.getNewOverlaps(this.tmpArray1);return this.getBodyDiff(b,a)},c.prototype.getEndBodyOverlaps=function(a){this.tmpArray1.length=0;var b=this.getEndOverlaps(this.tmpArray1);return this.getBodyDiff(b,a)},c.prototype.getBodyDiff=function(a,b){b=b||[];for(var c=this.tmpDict,d=a.length;d--;){var e=a[d]; +c.set(0|e.bodyA.id,0|e.bodyB.id,e)}for(d=c.keys.length;d--;){var e=c.getByKey(c.keys[d]);e&&b.push(e.bodyA,e.bodyB)}return c.reset(),b},d.prototype.set=function(a,b,c,e){d.call(this,a,b,c,e)}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/utils/OverlapKeeper.js","/utils")},{"./TupleDictionary":51,"./Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],51:[function(a,b){(function(){function c(){this.data={},this.keys=[]}var d=a("./Utils");b.exports=c,c.prototype.getKey=function(a,b){return a=0|a,b=0|b,(0|a)===(0|b)?-1:0|((0|a)>(0|b)?a<<16|65535&b:b<<16|65535&a)},c.prototype.getByKey=function(a){return a=0|a,this.data[a]},c.prototype.get=function(a,b){return this.data[this.getKey(a,b)]},c.prototype.set=function(a,b,c){if(!c)throw new Error("No data!");var d=this.getKey(a,b);return this.data[d]||this.keys.push(d),this.data[d]=c,d},c.prototype.reset=function(){for(var a=this.data,b=this.keys,c=b.length;c--;)delete a[b[c]];b.length=0},c.prototype.copy=function(a){this.reset(),d.appendArray(this.keys,a.keys);for(var b=a.keys.length;b--;){var c=a.keys[b];this.data[c]=a.data[c]}}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/utils/TupleDictionary.js","/utils")},{"./Utils":52,"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],52:[function(a,b){(function(){function a(){}b.exports=a,a.appendArray=function(a,b){if(b.length<15e4)a.push.apply(a,b);else for(var c=0,d=b.length;c!==d;++c)a.push(b[c])},a.splice=function(a,b,c){c=c||1;for(var d=b,e=a.length-c;e>d;d++)a[d]=a[d+c];a.length=e},a.ARRAY_TYPE="undefined"!=typeof P2_ARRAY_TYPE?P2_ARRAY_TYPE:"undefined"!=typeof Float32Array?Float32Array:Array,a.extend=function(a,b){for(var c in b)a[c]=b[c]},a.defaults=function(a,b){a=a||{};for(var c in b)c in a||(a[c]=b[c]);return a}}).call(this,a("/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),"undefined"!=typeof self?self:"undefined"!=typeof window?window:{},a("buffer").Buffer,arguments[3],arguments[4],arguments[5],arguments[6],"/utils/Utils.js","/utils")},{"/Users/schteppe/git/p2.js/node_modules/grunt-browserify/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":4,buffer:1}],53:[function(a,b){(function(){function c(){this.equations=[],this.bodies=[]}var d=a("../objects/Body");b.exports=c,c.prototype.reset=function(){this.equations.length=this.bodies.length=0};var e=[];c.prototype.getBodies=function(a){var b=a||[],c=this.equations;e.length=0;for(var d=0;d!==c.length;d++){var f=c[d];-1===e.indexOf(f.bodyA.id)&&(b.push(f.bodyA),e.push(f.bodyA.id)),-1===e.indexOf(f.bodyB.id)&&(b.push(f.bodyB),e.push(f.bodyB.id))}return b},c.prototype.wantsToSleep=function(){for(var a=0;a1e3*a));g++);this.time+=b;for(var h=this.time%a,i=h/a,j=0;j!==this.bodies.length;j++){var k=this.bodies[j];k.type!==m.STATIC&&k.sleepState!==m.SLEEPING?(f.sub(x,k.position,k.previousPosition),f.scale(x,x,i),f.add(k.interpolatedPosition,k.position,x),k.interpolatedAngle=k.angle+(k.angle-k.previousAngle)*i):(f.copy(k.interpolatedPosition,k.position),k.interpolatedAngle=k.angle)}}};var y=[];c.prototype.internalStep=function(a){this.stepping=!0;var b,d,e=this,g=this.doProfiling,h=this.springs.length,i=this.springs,j=this.bodies,k=this.gravity,l=this.solver,n=this.bodies.length,o=this.broadphase,p=this.narrowphase,r=this.constraints,s=u,t=(f.scale,f.add),v=(f.rotate,this.islandManager);if(this.overlapKeeper.tick(),this.lastTimeStep=a,g&&(b=performance.now()),this.useWorldGravityAsFrictionGravity){var w=f.length(this.gravity);0===w&&this.useFrictionGravityOnZeroGravity||(this.frictionGravity=w)}if(this.applyGravity)for(var x=0;x!==n;x++){var z=j[x],A=z.force;z.type===m.DYNAMIC&&z.sleepState!==m.SLEEPING&&(f.scale(s,k,z.mass*z.gravityScale),t(A,A,s))}if(this.applySpringForces)for(var x=0;x!==h;x++){var B=i[x];B.applyForce()}if(this.applyDamping)for(var x=0;x!==n;x++){var z=j[x];z.type===m.DYNAMIC&&z.applyDamping(a)}for(var C=o.getCollisionPairs(this),D=this.disabledBodyCollisionPairs,x=D.length-2;x>=0;x-=2)for(var E=C.length-2;E>=0;E-=2)(D[x]===C[E]&&D[x+1]===C[E+1]||D[x+1]===C[E]&&D[x]===C[E+1])&&C.splice(E,2);var F=r.length;for(x=0;x!==F;x++){var G=r[x];if(!G.collideConnected)for(var E=C.length-2;E>=0;E-=2)(G.bodyA===C[E]&&G.bodyB===C[E+1]||G.bodyB===C[E]&&G.bodyA===C[E+1])&&C.splice(E,2)}this.postBroadphaseEvent.pairs=C,this.emit(this.postBroadphaseEvent),p.reset(this);for(var x=0,H=C.length;x!==H;x+=2)for(var I=C[x],J=C[x+1],K=0,L=I.shapes.length;K!==L;K++)for(var M=I.shapes[K],N=I.shapeOffsets[K],O=I.shapeAngles[K],P=0,Q=J.shapes.length;P!==Q;P++){var R=J.shapes[P],S=J.shapeOffsets[P],T=J.shapeAngles[P],U=this.defaultContactMaterial;if(M.material&&R.material){var V=this.getContactMaterial(M.material,R.material);V&&(U=V)}this.runNarrowphase(p,I,M,N,O,J,R,S,T,U,this.frictionGravity)}for(var x=0;x!==n;x++){var W=j[x];W._wakeUpAfterNarrowphase&&(W.wakeUp(),W._wakeUpAfterNarrowphase=!1)}if(this.has("endContact")){this.overlapKeeper.getEndOverlaps(y);for(var X=this.endContactEvent,P=y.length;P--;){var Y=y[P];X.shapeA=Y.shapeA,X.shapeB=Y.shapeB,X.bodyA=Y.bodyA,X.bodyB=Y.bodyA,this.emit(X)}}var Z=this.preSolveEvent;Z.contactEquations=p.contactEquations,Z.frictionEquations=p.frictionEquations,this.emit(Z);var F=r.length;for(x=0;x!==F;x++)r[x].update();if(p.contactEquations.length||p.frictionEquations.length||r.length)if(this.islandSplit){for(v.equations.length=0,q.appendArray(v.equations,p.contactEquations),q.appendArray(v.equations,p.frictionEquations),x=0;x!==F;x++)q.appendArray(v.equations,r[x].equations);v.split(this);for(var x=0;x!==v.islands.length;x++){var $=v.islands[x];$.equations.length&&l.solveIsland(a,$)}}else{for(l.addEquations(p.contactEquations),l.addEquations(p.frictionEquations),x=0;x!==F;x++)l.addEquations(r[x].equations);this.solveConstraints&&l.solve(a,this),l.removeAllEquations()}for(var x=0;x!==n;x++){var W=j[x];W.sleepState!==m.SLEEPING&&W.type!==m.STATIC&&c.integrateBody(W,a)}for(var x=0;x!==n;x++)j[x].setZeroForce();if(g&&(d=performance.now(),e.lastStepTime=d-b),this.emitImpactEvent&&this.has("impact"))for(var _=this.impactEvent,x=0;x!==p.contactEquations.length;x++){var ab=p.contactEquations[x];ab.firstImpact&&(_.bodyA=ab.bodyA,_.bodyB=ab.bodyB,_.shapeA=ab.shapeA,_.shapeB=ab.shapeB,_.contactEquation=ab,this.emit(_))}if(this.sleepMode===c.BODY_SLEEPING)for(x=0;x!==n;x++)j[x].sleepTick(this.time,!1,a);else if(this.sleepMode===c.ISLAND_SLEEPING&&this.islandSplit){for(x=0;x!==n;x++)j[x].sleepTick(this.time,!0,a);for(var x=0;x0,a.frictionCoefficient=k.friction;var p;p=b.type===m.STATIC||b.type===m.KINEMATIC?g.mass:g.type===m.STATIC||g.type===m.KINEMATIC?b.mass:b.mass*g.mass/(b.mass+g.mass),a.slipForce=k.friction*l*p,a.restitution=k.restitution,a.surfaceVelocity=k.surfaceVelocity,a.frictionStiffness=k.frictionStiffness,a.frictionRelaxation=k.frictionRelaxation,a.stiffness=k.stiffness,a.relaxation=k.relaxation,a.contactSkinSize=k.contactSkinSize;var q=a[c.type|h.type],r=0;if(q){var s=c.sensor||h.sensor,t=a.frictionEquations.length;r=c.type=2*y&&(b._wakeUpAfterNarrowphase=!0)}if(g.allowSleep&&g.type===m.DYNAMIC&&g.sleepState===m.SLEEPING&&b.sleepState===m.AWAKE&&b.type!==m.STATIC){var z=f.squaredLength(b.velocity)+Math.pow(b.angularVelocity,2),A=Math.pow(b.sleepSpeedLimit,2);z>=2*A&&(g._wakeUpAfterNarrowphase=!0)}if(this.overlapKeeper.setOverlapping(b,c,g,h),this.has("beginContact")&&this.overlapKeeper.isNewOverlap(c,h)){var B=this.beginContactEvent;if(B.shapeA=c,B.shapeB=h,B.bodyA=b,B.bodyB=g,B.contactEquations.length=0,"number"==typeof r)for(var C=a.contactEquations.length-r;C1)for(var C=a.frictionEquations.length-u;C=0;b--)this.removeConstraint(a[b]);for(var d=this.bodies,b=d.length-1;b>=0;b--)this.removeBody(d[b]);for(var e=this.springs,b=e.length-1;b>=0;b--)this.removeSpring(e[b]);for(var f=this.contactMaterials,b=f.length-1;b>=0;b--)this.removeContactMaterial(f[b]);c.apply(this)},c.prototype.clone=function(){var a=new c;return a.fromJSON(this.toJSON()),a};var B=f.create(),C=f.fromValues(0,0),D=f.fromValues(0,0);c.prototype.hitTest=function(a,b,c){c=c||0;var d=new m({position:a}),e=new k,l=a,n=0,o=B,p=C,q=D;d.addShape(e);for(var r=this.narrowphase,s=[],t=0,u=b.length;t!==u;t++)for(var v=b[t],w=0,x=v.shapes.length;w!==x;w++){var y=v.shapes[w],z=v.shapeOffsets[w]||p,A=v.shapeAngles[w]||0;f.rotate(o,z,v.angle),f.add(o,o,v.position);var E=A+v.angle;(y instanceof g&&r.circleParticle(v,y,o,E,d,e,l,n,!0)||y instanceof h&&r.particleConvex(d,e,l,n,v,y,o,E,!0)||y instanceof i&&r.particlePlane(d,e,l,n,v,y,o,E,!0)||y instanceof j&&r.particleCapsule(d,e,l,n,v,y,o,E,!0)||y instanceof k&&f.squaredLength(f.sub(q,o,a))= x1 && x <= x1 + this.width) + { + var y1 = this.y; + + if(y >= y1 && y <= y1 + this.height) + { + return true; + } + } + + return false; +}; + +// constructor +PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; + + +/** + * @author Adrien Brault + */ + +/** + * @class Polygon + * @constructor + * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon, + * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be + * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the + * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are + * Numbers. + */ +PIXI.Polygon = function(points) +{ + //if points isn't an array, use arguments as the array + if(!(points instanceof Array)) + points = Array.prototype.slice.call(arguments); + + //if this is a flat array of numbers, convert it to points + if(typeof points[0] === 'number') { + var p = []; + for(var i = 0, il = points.length; i < il; i+=2) { + p.push( + new PIXI.Point(points[i], points[i + 1]) + ); + } + + points = p; + } + + this.points = points; +}; + +/** + * Creates a clone of this polygon + * + * @method clone + * @return {Polygon} a copy of the polygon + */ +PIXI.Polygon.prototype.clone = function() +{ + var points = []; + for (var i=0; i y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); + + if(intersect) inside = !inside; + } + + return inside; +}; + +// constructor +PIXI.Polygon.prototype.constructor = PIXI.Polygon; + +/** + * @author Chad Engler + */ + +/** + * The Circle object can be used to specify a hit area for displayobjects + * + * @class Circle + * @constructor + * @param x {Number} The X coord of the upper-left corner of the framing rectangle of this circle + * @param y {Number} The Y coord of the upper-left corner of the framing rectangle of this circle + * @param radius {Number} The radius of the circle + */ +PIXI.Circle = function(x, y, radius) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property radius + * @type Number + * @default 0 + */ + this.radius = radius || 0; +}; + +/** + * Creates a clone of this Circle instance + * + * @method clone + * @return {Circle} a copy of the polygon + */ +PIXI.Circle.prototype.clone = function() +{ + return new PIXI.Circle(this.x, this.y, this.radius); +}; + +/** + * Checks if the x, and y coords passed to this function are contained within this circle + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this polygon + */ +PIXI.Circle.prototype.contains = function(x, y) +{ + if(this.radius <= 0) + return false; + + var dx = (this.x - x), + dy = (this.y - y), + r2 = this.radius * this.radius; + + dx *= dx; + dy *= dy; + + return (dx + dy <= r2); +}; + +// constructor +PIXI.Circle.prototype.constructor = PIXI.Circle; + + +/** + * @author Chad Engler + */ + +/** + * The Ellipse object can be used to specify a hit area for displayobjects + * + * @class Ellipse + * @constructor + * @param x {Number} The X coord of the upper-left corner of the framing rectangle of this ellipse + * @param y {Number} The Y coord of the upper-left corner of the framing rectangle of this ellipse + * @param width {Number} The overall width of this ellipse + * @param height {Number} The overall height of this ellipse + */ +PIXI.Ellipse = function(x, y, width, height) +{ + /** + * @property x + * @type Number + * @default 0 + */ + this.x = x || 0; + + /** + * @property y + * @type Number + * @default 0 + */ + this.y = y || 0; + + /** + * @property width + * @type Number + * @default 0 + */ + this.width = width || 0; + + /** + * @property height + * @type Number + * @default 0 + */ + this.height = height || 0; +}; + +/** + * Creates a clone of this Ellipse instance + * + * @method clone + * @return {Ellipse} a copy of the ellipse + */ +PIXI.Ellipse.prototype.clone = function() +{ + return new PIXI.Ellipse(this.x, this.y, this.width, this.height); +}; + +/** + * Checks if the x, and y coords passed to this function are contained within this ellipse + * + * @method contains + * @param x {Number} The X coord of the point to test + * @param y {Number} The Y coord of the point to test + * @return {Boolean} if the x/y coords are within this ellipse + */ +PIXI.Ellipse.prototype.contains = function(x, y) +{ + if(this.width <= 0 || this.height <= 0) + return false; + + //normalize the coords to an ellipse with center 0,0 + //and a radius of 0.5 + var normx = ((x - this.x) / this.width) - 0.5, + normy = ((y - this.y) / this.height) - 0.5; + + normx *= normx; + normy *= normy; + + return (normx + normy < 0.25); +}; + +PIXI.Ellipse.prototype.getBounds = function() +{ + return new PIXI.Rectangle(this.x, this.y, this.width, this.height); +}; + +// constructor +PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; + + + +/* + * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV + * you both rock! + */ + +function determineMatrixArrayType() { + PIXI.Matrix = (typeof Float32Array !== 'undefined') ? Float32Array : Array; + return PIXI.Matrix; +} + +determineMatrixArrayType(); + +PIXI.mat3 = {}; + +PIXI.mat3.create = function() +{ + var matrix = new PIXI.Matrix(9); + + matrix[0] = 1; + matrix[1] = 0; + matrix[2] = 0; + matrix[3] = 0; + matrix[4] = 1; + matrix[5] = 0; + matrix[6] = 0; + matrix[7] = 0; + matrix[8] = 1; + + return matrix; +}; + + +PIXI.mat3.identity = function(matrix) +{ + matrix[0] = 1; + matrix[1] = 0; + matrix[2] = 0; + matrix[3] = 0; + matrix[4] = 1; + matrix[5] = 0; + matrix[6] = 0; + matrix[7] = 0; + matrix[8] = 1; + + return matrix; +}; + + +PIXI.mat4 = {}; + +PIXI.mat4.create = function() +{ + var matrix = new PIXI.Matrix(16); + + matrix[0] = 1; + matrix[1] = 0; + matrix[2] = 0; + matrix[3] = 0; + matrix[4] = 0; + matrix[5] = 1; + matrix[6] = 0; + matrix[7] = 0; + matrix[8] = 0; + matrix[9] = 0; + matrix[10] = 1; + matrix[11] = 0; + matrix[12] = 0; + matrix[13] = 0; + matrix[14] = 0; + matrix[15] = 1; + + return matrix; +}; + +PIXI.mat3.multiply = function (mat, mat2, dest) +{ + if (!dest) { dest = mat; } + + // Cache the matrix values (makes for huge speed increases!) + var a00 = mat[0], a01 = mat[1], a02 = mat[2], + a10 = mat[3], a11 = mat[4], a12 = mat[5], + a20 = mat[6], a21 = mat[7], a22 = mat[8], + + b00 = mat2[0], b01 = mat2[1], b02 = mat2[2], + b10 = mat2[3], b11 = mat2[4], b12 = mat2[5], + b20 = mat2[6], b21 = mat2[7], b22 = mat2[8]; + + dest[0] = b00 * a00 + b01 * a10 + b02 * a20; + dest[1] = b00 * a01 + b01 * a11 + b02 * a21; + dest[2] = b00 * a02 + b01 * a12 + b02 * a22; + + dest[3] = b10 * a00 + b11 * a10 + b12 * a20; + dest[4] = b10 * a01 + b11 * a11 + b12 * a21; + dest[5] = b10 * a02 + b11 * a12 + b12 * a22; + + dest[6] = b20 * a00 + b21 * a10 + b22 * a20; + dest[7] = b20 * a01 + b21 * a11 + b22 * a21; + dest[8] = b20 * a02 + b21 * a12 + b22 * a22; + + return dest; +}; + +PIXI.mat3.clone = function(mat) +{ + var matrix = new PIXI.Matrix(9); + + matrix[0] = mat[0]; + matrix[1] = mat[1]; + matrix[2] = mat[2]; + matrix[3] = mat[3]; + matrix[4] = mat[4]; + matrix[5] = mat[5]; + matrix[6] = mat[6]; + matrix[7] = mat[7]; + matrix[8] = mat[8]; + + return matrix; +}; + +PIXI.mat3.transpose = function (mat, dest) +{ + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (!dest || mat === dest) { + var a01 = mat[1], a02 = mat[2], + a12 = mat[5]; + + mat[1] = mat[3]; + mat[2] = mat[6]; + mat[3] = a01; + mat[5] = mat[7]; + mat[6] = a02; + mat[7] = a12; + return mat; + } + + dest[0] = mat[0]; + dest[1] = mat[3]; + dest[2] = mat[6]; + dest[3] = mat[1]; + dest[4] = mat[4]; + dest[5] = mat[7]; + dest[6] = mat[2]; + dest[7] = mat[5]; + dest[8] = mat[8]; + return dest; +}; + +PIXI.mat3.toMat4 = function (mat, dest) +{ + if (!dest) { dest = PIXI.mat4.create(); } + + dest[15] = 1; + dest[14] = 0; + dest[13] = 0; + dest[12] = 0; + + dest[11] = 0; + dest[10] = mat[8]; + dest[9] = mat[7]; + dest[8] = mat[6]; + + dest[7] = 0; + dest[6] = mat[5]; + dest[5] = mat[4]; + dest[4] = mat[3]; + + dest[3] = 0; + dest[2] = mat[2]; + dest[1] = mat[1]; + dest[0] = mat[0]; + + return dest; +}; + + +///// + + +PIXI.mat4.create = function() +{ + var matrix = new PIXI.Matrix(16); + + matrix[0] = 1; + matrix[1] = 0; + matrix[2] = 0; + matrix[3] = 0; + matrix[4] = 0; + matrix[5] = 1; + matrix[6] = 0; + matrix[7] = 0; + matrix[8] = 0; + matrix[9] = 0; + matrix[10] = 1; + matrix[11] = 0; + matrix[12] = 0; + matrix[13] = 0; + matrix[14] = 0; + matrix[15] = 1; + + return matrix; +}; + +PIXI.mat4.transpose = function (mat, dest) +{ + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (!dest || mat === dest) + { + var a01 = mat[1], a02 = mat[2], a03 = mat[3], + a12 = mat[6], a13 = mat[7], + a23 = mat[11]; + + mat[1] = mat[4]; + mat[2] = mat[8]; + mat[3] = mat[12]; + mat[4] = a01; + mat[6] = mat[9]; + mat[7] = mat[13]; + mat[8] = a02; + mat[9] = a12; + mat[11] = mat[14]; + mat[12] = a03; + mat[13] = a13; + mat[14] = a23; + return mat; + } + + dest[0] = mat[0]; + dest[1] = mat[4]; + dest[2] = mat[8]; + dest[3] = mat[12]; + dest[4] = mat[1]; + dest[5] = mat[5]; + dest[6] = mat[9]; + dest[7] = mat[13]; + dest[8] = mat[2]; + dest[9] = mat[6]; + dest[10] = mat[10]; + dest[11] = mat[14]; + dest[12] = mat[3]; + dest[13] = mat[7]; + dest[14] = mat[11]; + dest[15] = mat[15]; + return dest; +}; + +PIXI.mat4.multiply = function (mat, mat2, dest) +{ + if (!dest) { dest = mat; } + + // Cache the matrix values (makes for huge speed increases!) + var a00 = mat[ 0], a01 = mat[ 1], a02 = mat[ 2], a03 = mat[3]; + var a10 = mat[ 4], a11 = mat[ 5], a12 = mat[ 6], a13 = mat[7]; + var a20 = mat[ 8], a21 = mat[ 9], a22 = mat[10], a23 = mat[11]; + var a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15]; + + // Cache only the current line of the second matrix + var b0 = mat2[0], b1 = mat2[1], b2 = mat2[2], b3 = mat2[3]; + dest[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + dest[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + dest[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + dest[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = mat2[4]; + b1 = mat2[5]; + b2 = mat2[6]; + b3 = mat2[7]; + dest[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + dest[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + dest[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + dest[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = mat2[8]; + b1 = mat2[9]; + b2 = mat2[10]; + b3 = mat2[11]; + dest[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + dest[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + dest[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + dest[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = mat2[12]; + b1 = mat2[13]; + b2 = mat2[14]; + b3 = mat2[15]; + dest[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + dest[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + dest[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + dest[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + return dest; +}; + +PIXI.identityMatrix = PIXI.mat3.create(); + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The base class for all objects that are rendered on the screen. + * + * @class DisplayObject + * @constructor + */ +PIXI.DisplayObject = function() +{ + this.last = this; + this.first = this; + /** + * The coordinate of the object relative to the local coordinates of the parent. + * + * @property position + * @type Point + */ + this.position = new PIXI.Point(); + + /** + * The scale factor of the object. + * + * @property scale + * @type Point + */ + this.scale = new PIXI.Point(1,1);//{x:1, y:1}; + + /** + * The pivot point of the displayObject that it rotates around + * + * @property pivot + * @type Point + */ + this.pivot = new PIXI.Point(0,0); + + /** + * The rotation of the object in radians. + * + * @property rotation + * @type Number + */ + this.rotation = 0; + + /** + * The opacity of the object. + * + * @property alpha + * @type Number + */ + this.alpha = 1; + + /** + * The visibility of the object. + * + * @property visible + * @type Boolean + */ + this.visible = true; + + /** + * This is the defined area that will pick up mouse / touch events. It is null by default. + * Setting it is a neat way of optimising the hitTest function that the interactionManager will use (as it will not need to hit test all the children) + * + * @property hitArea + * @type Rectangle|Circle|Ellipse|Polygon + */ + this.hitArea = null; + + /** + * This is used to indicate if the displayObject should display a mouse hand cursor on rollover + * + * @property buttonMode + * @type Boolean + */ + this.buttonMode = false; + + /** + * Can this object be rendered + * + * @property renderable + * @type Boolean + */ + this.renderable = false; + + /** + * [read-only] The display object container that contains this display object. + * + * @property parent + * @type DisplayObjectContainer + * @readOnly + */ + this.parent = null; + + /** + * [read-only] The stage the display object is connected to, or undefined if it is not connected to the stage. + * + * @property stage + * @type Stage + * @readOnly + */ + this.stage = null; + + /** + * [read-only] The multiplied alpha of the displayobject + * + * @property worldAlpha + * @type Number + * @readOnly + */ + this.worldAlpha = 1; + + /** + * [read-only] Whether or not the object is interactive, do not toggle directly! use the `interactive` property + * + * @property _interactive + * @type Boolean + * @readOnly + * @private + */ + this._interactive = false; + + /** + * This is the curser that will be used when the mouse is over this object. To enable this the element must have interaction = true and buttonMode = true + * + * @property defaultCursor + * @type String + * + */ + this.defaultCursor = 'pointer'; + + /** + * [read-only] Current transform of the object based on world (parent) factors + * + * @property worldTransform + * @type Mat3 + * @readOnly + * @private + */ + this.worldTransform = PIXI.mat3.create(); //mat3.identity(); + + /** + * [read-only] Current transform of the object locally + * + * @property localTransform + * @type Mat3 + * @readOnly + * @private + */ + this.localTransform = PIXI.mat3.create(); //mat3.identity(); + + /** + * [NYI] Unkown + * + * @property color + * @type Array<> + * @private + */ + this.color = []; + + /** + * [NYI] Holds whether or not this object is dynamic, for rendering optimization + * + * @property dynamic + * @type Boolean + * @private + */ + this.dynamic = true; + + // chach that puppy! + this._sr = 0; + this._cr = 1; + + + this.filterArea = new PIXI.Rectangle(0,0,1,1); + + + /** + * + * + * + */ + this._bounds = new PIXI.Rectangle(0, 0, 1, 1); + this._currentBounds = null; + this._mask = null; + + /* + * MOUSE Callbacks + */ + + /** + * A callback that is used when the users clicks on the displayObject with their mouse + * @method click + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user clicks the mouse down over the sprite + * @method mousedown + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse that was over the displayObject + * for this callback to be fired the mouse must have been pressed down over the displayObject + * @method mouseup + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the mouse that was over the displayObject but is no longer over the displayObject + * for this callback to be fired, The touch must have started over the displayObject + * @method mouseupoutside + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the users mouse rolls over the displayObject + * @method mouseover + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the users mouse leaves the displayObject + * @method mouseout + * @param interactionData {InteractionData} + */ + + + /* + * TOUCH Callbacks + */ + + /** + * A callback that is used when the users taps on the sprite with their finger + * basically a touch version of click + * @method tap + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user touch's over the displayObject + * @method touchstart + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases a touch over the displayObject + * @method touchend + * @param interactionData {InteractionData} + */ + + /** + * A callback that is used when the user releases the touch that was over the displayObject + * for this callback to be fired, The touch must have started over the sprite + * @method touchendoutside + * @param interactionData {InteractionData} + */ +}; + +// constructor +PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; + +/** + * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default + * Instead of using this function you can now simply set the interactive property to true or false + * + * @method setInteractive + * @param interactive {Boolean} + * @deprecated Simply set the `interactive` property directly + */ +PIXI.DisplayObject.prototype.setInteractive = function(interactive) +{ + this.interactive = interactive; +}; + +/** + * Indicates if the sprite will have touch and mouse interactivity. It is false by default + * + * @property interactive + * @type Boolean + * @default false + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', { + get: function() { + return this._interactive; + }, + set: function(value) { + this._interactive = value; + + // TODO more to be done here.. + // need to sort out a re-crawl! + if(this.stage)this.stage.dirty = true; + } +}); + +/** + * [read-only] Indicates if the sprite is globaly visible. + * + * @property worldVisible + * @type Boolean + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'worldVisible', { + get: function() { + var item = this; + + do + { + if(!item.visible)return false; + item = item.parent; + } + while(item); + + return true; + } +}); + +/** + * Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it. + * In PIXI a regular mask must be a PIXI.Ggraphics object. This allows for much faster masking in canvas as it utilises shape clipping. + * To remove a mask, set this property to null. + * + * @property mask + * @type Graphics + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { + get: function() { + return this._mask; + }, + set: function(value) { + + if(this._mask)this._mask.isMask = false; + this._mask = value; + if(this._mask)this._mask.isMask = true; + } +}); + +/** + * Sets the filters for the displayObject. + * * IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer. + * To remove filters simply set this property to 'null' + * @property filters + * @type Array An array of filters + */ +Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { + get: function() { + return this._filters; + }, + set: function(value) { + + if(value) + { + // now put all the passes in one place.. + var passes = []; + for (var i = 0; i < value.length; i++) + { + var filterPasses = value[i].passes; + for (var j = 0; j < filterPasses.length; j++) + { + passes.push(filterPasses[j]); + } + } + + // TODO change this as it is legacy + this._filterBlock = {target:this, filterPasses:passes}; + } + + this._filters = value; + } +}); + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.DisplayObject.prototype.updateTransform = function() +{ + // TODO OPTIMIZE THIS!! with dirty + if(this.rotation !== this.rotationCache) + { + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); + } + + var localTransform = this.localTransform; + var parentTransform = this.parent.worldTransform; + var worldTransform = this.worldTransform; + //console.log(localTransform) + localTransform[0] = this._cr * this.scale.x; + localTransform[1] = -this._sr * this.scale.y; + localTransform[3] = this._sr * this.scale.x; + localTransform[4] = this._cr * this.scale.y; + + // TODO --> do we even need a local matrix??? + + var px = this.pivot.x; + var py = this.pivot.y; + + // Cache the matrix values (makes for huge speed increases!) + var a00 = localTransform[0], a01 = localTransform[1], a02 = this.position.x - localTransform[0] * px - py * localTransform[1], + a10 = localTransform[3], a11 = localTransform[4], a12 = this.position.y - localTransform[4] * py - px * localTransform[3], + + b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], + b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; + + localTransform[2] = a02; + localTransform[5] = a12; + + worldTransform[0] = b00 * a00 + b01 * a10; + worldTransform[1] = b00 * a01 + b01 * a11; + worldTransform[2] = b00 * a02 + b01 * a12 + b02; + + worldTransform[3] = b10 * a00 + b11 * a10; + worldTransform[4] = b10 * a01 + b11 * a11; + worldTransform[5] = b10 * a02 + b11 * a12 + b12; + + // because we are using affine transformation, we can optimise the matrix concatenation process.. wooo! + // mat3.multiply(this.localTransform, this.parent.worldTransform, this.worldTransform); + this.worldAlpha = this.alpha * this.parent.worldAlpha; +}; + +PIXI.DisplayObject.prototype.getBounds = function() +{ + return PIXI.EmptyRectangle; +}; + +PIXI.DisplayObject.prototype.getLocalBounds = function() +{ + var matrixCache = this.worldTransform; + + this.worldTransform = PIXI.identityMatrix; + + this.updateTransform(); + + var bounds = this.getBounds(); + + this.worldTransform = matrixCache; + + return bounds; +}; + +PIXI.DisplayObject.prototype.setStageReference = function(stage) +{ + this.stage = stage; + if(this._interactive)this.stage.dirty = true; +}; + +PIXI.DisplayObject.prototype._renderWebGL = function(renderSession) +{ + // OVERWRITE; + // this line is just here to pass jshinting :) + renderSession = renderSession; +}; + +PIXI.DisplayObject.prototype._renderCanvas = function(renderSession) +{ + // OVERWRITE; + // this line is just here to pass jshinting :) + renderSession = renderSession; +}; + +PIXI.EmptyRectangle = new PIXI.Rectangle(0,0,0,0); + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * A DisplayObjectContainer represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class DisplayObjectContainer + * @extends DisplayObject + * @constructor + */ +PIXI.DisplayObjectContainer = function() +{ + PIXI.DisplayObject.call( this ); + + /** + * [read-only] The of children of this container. + * + * @property children + * @type Array + * @readOnly + */ + this.children = []; +}; + +// constructor +PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); +PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; + +/** + * The width of the displayObjectContainer, setting this will actually modify the scale to acheive the value set + * + * @property width + * @type Number + */ + +/* +Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', { + get: function() { + return this.scale.x * this.getLocalBounds().width; + }, + set: function(value) { + this.scale.x = value / (this.getLocalBounds().width/this.scale.x); + this._width = value; + } +}); +*/ + +/** + * The height of the displayObjectContainer, setting this will actually modify the scale to acheive the value set + * + * @property height + * @type Number + */ + + /* +Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', { + get: function() { + return this.scale.y * this.getLocalBounds().height; + }, + set: function(value) { + this.scale.y = value / (this.getLocalBounds().height/this.scale.y); + this._height = value; + } +}); +*/ + +/** + * Adds a child to the container. + * + * @method addChild + * @param child {DisplayObject} The DisplayObject to add to the container + */ +PIXI.DisplayObjectContainer.prototype.addChild = function(child) +{ + if(child.parent && child !== this) + { + //// COULD BE THIS??? + child.parent.removeChild(child); + // return; + } + + child.parent = this; + + this.children.push(child); + + // update the stage refference.. + + if(this.stage)child.setStageReference(this.stage); + +}; + +/** + * Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown + * + * @method addChildAt + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + */ +PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) +{ + if(index >= 0 && index <= this.children.length) + { + if(child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + if(this.stage)child.setStageReference(this.stage); + } + else + { + throw new Error(child + ' The index '+ index +' supplied is out of bounds ' + this.children.length); + } +}; + +/** + * [NYI] Swaps the depth of 2 displayObjects + * + * @method swapChildren + * @param child {DisplayObject} + * @param child2 {DisplayObject} + * @private + */ +PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) +{ + if(child === child2) { + return; + } + + var index1 = this.children.indexOf(child); + var index2 = this.children.indexOf(child2); + + if(index1 < 0 || index2 < 0) { + throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.'); + } + + this.children[index1] = child2; + this.children[index2] = child; + +}; + +/** + * Returns the Child at the specified index + * + * @method getChildAt + * @param index {Number} The index to get the child from + */ +PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) +{ + if(index >= 0 && index < this.children.length) + { + return this.children[index]; + } + else + { + throw new Error('The supplied DisplayObjects must be a child of the caller ' + this); + } +}; + +/** + * Removes a child from the container. + * + * @method removeChild + * @param child {DisplayObject} The DisplayObject to remove + */ +PIXI.DisplayObjectContainer.prototype.removeChild = function(child) +{ + var index = this.children.indexOf( child ); + if ( index !== -1 ) + { + // update the stage reference.. + if(this.stage)child.removeStageReference(); + + child.parent = undefined; + this.children.splice( index, 1 ); + } + else + { + throw new Error(child + ' The supplied DisplayObject must be a child of the caller ' + this); + } +}; + +/* + * Updates the container's children's transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.DisplayObjectContainer.prototype.updateTransform = function() +{ + //this._currentBounds = null; + + if(!this.visible)return; + + PIXI.DisplayObject.prototype.updateTransform.call( this ); + + for(var i=0,j=this.children.length; i childMaxX ? maxX : childMaxX; + maxY = maxY > childMaxY ? maxY : childMaxY; + } + + var bounds = this._bounds; + + bounds.x = minX; + bounds.y = minY; + bounds.width = maxX - minX; + bounds.height = maxY - minY; + + // TODO: store a refferance so that if this function gets called again in the render cycle we do not have to recacalculate + //this._currentBounds = bounds; + + return bounds; +}; + +PIXI.DisplayObjectContainer.prototype.setStageReference = function(stage) +{ + this.stage = stage; + if(this._interactive)this.stage.dirty = true; + + for(var i=0,j=this.children.length; i maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + // store a refferance so that if this function gets called again in the render cycle we do not have to recacalculate + this._currentBounds = bounds; + + return bounds; +}; + + +PIXI.Sprite.prototype._renderWebGL = function(renderSession) +{ + // if the sprite is not visible or the alpha is 0 then no need to render this element + if(this.visible === false || this.alpha === 0)return; + + var i,j; + + // do a quick check to see if this element has a mask or a filter. + if(this._mask || this._filters) + { + var spriteBatch = renderSession.spriteBatch; + + if(this._mask) + { + spriteBatch.stop(); + renderSession.maskManager.pushMask(this.mask, renderSession); + spriteBatch.start(); + } + + if(this._filters) + { + spriteBatch.flush(); + renderSession.filterManager.pushFilter(this._filterBlock); + } + + // add this sprite to the batch + spriteBatch.render(this); + + // now loop through the children and make sure they get rendered + for(i=0,j=this.children.length; i} an array of {Texture} objects that make up the animation + */ +PIXI.MovieClip = function(textures) +{ + PIXI.Sprite.call(this, textures[0]); + + /** + * The array of textures that make up the animation + * + * @property textures + * @type Array + */ + this.textures = textures; + + /** + * The speed that the MovieClip will play at. Higher is faster, lower is slower + * + * @property animationSpeed + * @type Number + * @default 1 + */ + this.animationSpeed = 1; + + /** + * Whether or not the movie clip repeats after playing. + * + * @property loop + * @type Boolean + * @default true + */ + this.loop = true; + + /** + * Function to call when a MovieClip finishes playing + * + * @property onComplete + * @type Function + */ + this.onComplete = null; + + /** + * [read-only] The index MovieClips current frame (this may not have to be a whole number) + * + * @property currentFrame + * @type Number + * @default 0 + * @readOnly + */ + this.currentFrame = 0; + + /** + * [read-only] Indicates if the MovieClip is currently playing + * + * @property playing + * @type Boolean + * @readOnly + */ + this.playing = false; +}; + +// constructor +PIXI.MovieClip.prototype = Object.create( PIXI.Sprite.prototype ); +PIXI.MovieClip.prototype.constructor = PIXI.MovieClip; + +/** +* [read-only] totalFrames is the total number of frames in the MovieClip. This is the same as number of textures +* assigned to the MovieClip. +* +* @property totalFrames +* @type Number +* @default 0 +* @readOnly +*/ +Object.defineProperty( PIXI.MovieClip.prototype, 'totalFrames', { + get: function() { + + return this.textures.length; + } +}); + + +/** + * Stops the MovieClip + * + * @method stop + */ +PIXI.MovieClip.prototype.stop = function() +{ + this.playing = false; +}; + +/** + * Plays the MovieClip + * + * @method play + */ +PIXI.MovieClip.prototype.play = function() +{ + this.playing = true; +}; + +/** + * Stops the MovieClip and goes to a specific frame + * + * @method gotoAndStop + * @param frameNumber {Number} frame index to stop at + */ +PIXI.MovieClip.prototype.gotoAndStop = function(frameNumber) +{ + this.playing = false; + this.currentFrame = frameNumber; + var round = (this.currentFrame + 0.5) | 0; + this.setTexture(this.textures[round % this.textures.length]); +}; + +/** + * Goes to a specific frame and begins playing the MovieClip + * + * @method gotoAndPlay + * @param frameNumber {Number} frame index to start at + */ +PIXI.MovieClip.prototype.gotoAndPlay = function(frameNumber) +{ + this.currentFrame = frameNumber; + this.playing = true; +}; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.MovieClip.prototype.updateTransform = function() +{ + PIXI.Sprite.prototype.updateTransform.call(this); + + if(!this.playing)return; + + this.currentFrame += this.animationSpeed; + + var round = (this.currentFrame + 0.5) | 0; + + if(this.loop || round < this.textures.length) + { + this.setTexture(this.textures[round % this.textures.length]); + } + else if(round >= this.textures.length) + { + this.gotoAndStop(this.textures.length - 1); + if(this.onComplete) + { + this.onComplete(); + } + } +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + + +PIXI.FilterBlock = function() +{ + this.visible = true; + this.renderable = true; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Text Object will create a line(s) of text to split a line you can use '\n' + * + * @class Text + * @extends Sprite + * @constructor + * @param text {String} The copy that you would like the text to display + * @param [style] {Object} The style parameters + * @param [style.font] {String} default 'bold 20pt Arial' The style and size of the font + * @param [style.fill='black'] {Object} A canvas fillstyle that will be used on the text eg 'red', '#00FF00' + * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right') + * @param [style.stroke] {String} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00' + * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) + * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used + * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap + */ +PIXI.Text = function(text, style) +{ + this.canvas = document.createElement('canvas'); + this.context = this.canvas.getContext('2d'); + PIXI.Sprite.call(this, PIXI.Texture.fromCanvas(this.canvas)); + + this.setText(text); + this.setStyle(style); + + this.updateText(); + this.dirty = false; +}; + +// constructor +PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); +PIXI.Text.prototype.constructor = PIXI.Text; + +/** + * Set the style of the text + * + * @method setStyle + * @param [style] {Object} The style parameters + * @param [style.font='bold 20pt Arial'] {String} The style and size of the font + * @param [style.fill='black'] {Object} A canvas fillstyle that will be used on the text eg 'red', '#00FF00' + * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right') + * @param [style.stroke='black'] {String} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00' + * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) + * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used + * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap + */ +PIXI.Text.prototype.setStyle = function(style) +{ + style = style || {}; + style.font = style.font || 'bold 20pt Arial'; + style.fill = style.fill || 'black'; + style.align = style.align || 'left'; + style.stroke = style.stroke || 'black'; //provide a default, see: https://github.com/GoodBoyDigital/pixi.js/issues/136 + style.strokeThickness = style.strokeThickness || 0; + style.wordWrap = style.wordWrap || false; + style.wordWrapWidth = style.wordWrapWidth || 100; + this.style = style; + this.dirty = true; +}; + +/** + * Set the copy for the text object. To split a line you can use '\n' + * + * @method setText + * @param {String} text The copy that you would like the text to display + */ +PIXI.Text.prototype.setText = function(text) +{ + this.text = text.toString() || ' '; + this.dirty = true; + +}; + +/** + * Renders text + * + * @method updateText + * @private + */ +PIXI.Text.prototype.updateText = function() +{ + this.context.font = this.style.font; + + var outputText = this.text; + + // word wrap + // preserve original text + if(this.style.wordWrap)outputText = this.wordWrap(this.text); + + //split text into lines + var lines = outputText.split(/(?:\r\n|\r|\n)/); + + //calculate text width + var lineWidths = []; + var maxLineWidth = 0; + for (var i = 0; i < lines.length; i++) + { + var lineWidth = this.context.measureText(lines[i]).width; + lineWidths[i] = lineWidth; + maxLineWidth = Math.max(maxLineWidth, lineWidth); + } + this.canvas.width = maxLineWidth + this.style.strokeThickness; + + //calculate text height + var lineHeight = this.determineFontHeight('font: ' + this.style.font + ';') + this.style.strokeThickness; + this.canvas.height = lineHeight * lines.length; + + if(navigator.isCocoonJS) this.context.clearRect(0,0,this.canvas.width,this.canvas.height); + + //set canvas text styles + this.context.fillStyle = this.style.fill; + this.context.font = this.style.font; + + this.context.strokeStyle = this.style.stroke; + this.context.lineWidth = this.style.strokeThickness; + + this.context.textBaseline = 'top'; + + //draw lines line by line + for (i = 0; i < lines.length; i++) + { + var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight); + + if(this.style.align === 'right') + { + linePosition.x += maxLineWidth - lineWidths[i]; + } + else if(this.style.align === 'center') + { + linePosition.x += (maxLineWidth - lineWidths[i]) / 2; + } + + if(this.style.stroke && this.style.strokeThickness) + { + this.context.strokeText(lines[i], linePosition.x, linePosition.y); + } + + if(this.style.fill) + { + this.context.fillText(lines[i], linePosition.x, linePosition.y); + } + } + + this.updateTexture(); +}; + +/** + * Updates texture size based on canvas size + * + * @method updateTexture + * @private + */ +PIXI.Text.prototype.updateTexture = function() +{ + this.texture.baseTexture.width = this.canvas.width; + this.texture.baseTexture.height = this.canvas.height; + this.texture.frame.width = this.canvas.width; + this.texture.frame.height = this.canvas.height; + + this._width = this.canvas.width; + this._height = this.canvas.height; + + this.requiresUpdate = true; +}; + +PIXI.Text.prototype._renderWebGL = function(renderSession) +{ + if(this.requiresUpdate) + { + this.requiresUpdate = false; + PIXI.updateWebGLTexture(this.texture.baseTexture, renderSession.gl); + } + + PIXI.Sprite.prototype._renderWebGL.call(this, renderSession); +}; + +/** + * Updates the transfor of this object + * + * @method updateTransform + * @private + */ +PIXI.Text.prototype.updateTransform = function() +{ + if(this.dirty) + { + this.updateText(); + this.dirty = false; + } + + PIXI.Sprite.prototype.updateTransform.call(this); +}; + +/* + * http://stackoverflow.com/users/34441/ellisbben + * great solution to the problem! + * + * @method determineFontHeight + * @param fontStyle {Object} + * @private + */ +PIXI.Text.prototype.determineFontHeight = function(fontStyle) +{ + // build a little reference dictionary so if the font style has been used return a + // cached version... + var result = PIXI.Text.heightCache[fontStyle]; + + if(!result) + { + var body = document.getElementsByTagName('body')[0]; + var dummy = document.createElement('div'); + var dummyText = document.createTextNode('M'); + dummy.appendChild(dummyText); + dummy.setAttribute('style', fontStyle + ';position:absolute;top:0;left:0'); + body.appendChild(dummy); + + result = dummy.offsetHeight; + PIXI.Text.heightCache[fontStyle] = result; + + body.removeChild(dummy); + } + + return result; +}; + +/** + * Applies newlines to a string to have it optimally fit into the horizontal + * bounds set by the Text object's wordWrapWidth property. + * + * @method wordWrap + * @param text {String} + * @private + */ +PIXI.Text.prototype.wordWrap = function(text) +{ + // Greedy wrapping algorithm that will wrap words as the line grows longer + // than its horizontal bounds. + var result = ''; + var lines = text.split('\n'); + for (var i = 0; i < lines.length; i++) + { + var spaceLeft = this.style.wordWrapWidth; + var words = lines[i].split(' '); + for (var j = 0; j < words.length; j++) + { + var wordWidth = this.context.measureText(words[j]).width; + var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; + if(wordWidthWithSpace > spaceLeft) + { + // Skip printing the newline if it's the first word of the line that is + // greater than the word wrap width. + if(j > 0) + { + result += '\n'; + } + result += words[j] + ' '; + spaceLeft = this.style.wordWrapWidth - wordWidth; + } + else + { + spaceLeft -= wordWidthWithSpace; + result += words[j] + ' '; + } + } + result += '\n'; + } + return result; +}; + +/** + * Destroys this text object + * + * @method destroy + * @param destroyTexture {Boolean} + */ +PIXI.Text.prototype.destroy = function(destroyTexture) +{ + if(destroyTexture) + { + this.texture.destroy(); + } + +}; + +PIXI.Text.heightCache = {}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Text Object will create a line(s) of text using bitmap font. To split a line you can use '\n', '\r' or '\r\n' + * You can generate the fnt files using + * http://www.angelcode.com/products/bmfont/ for windows or + * http://www.bmglyph.com/ for mac. + * + * @class BitmapText + * @extends DisplayObjectContainer + * @constructor + * @param text {String} The copy that you would like the text to display + * @param style {Object} The style parameters + * @param style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) + * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right') + */ +PIXI.BitmapText = function(text, style) +{ + PIXI.DisplayObjectContainer.call(this); + + this.setText(text); + this.setStyle(style); + this.updateText(); + this.dirty = false; +}; + +// constructor +PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); +PIXI.BitmapText.prototype.constructor = PIXI.BitmapText; + +/** + * Set the copy for the text object + * + * @method setText + * @param text {String} The copy that you would like the text to display + */ +PIXI.BitmapText.prototype.setText = function(text) +{ + this.text = text || ' '; + this.dirty = true; +}; + +/** + * Set the style of the text + * + * @method setStyle + * @param style {Object} The style parameters + * @param style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) + * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right') + */ +PIXI.BitmapText.prototype.setStyle = function(style) +{ + style = style || {}; + style.align = style.align || 'left'; + this.style = style; + + var font = style.font.split(' '); + this.fontName = font[font.length - 1]; + this.fontSize = font.length >= 2 ? parseInt(font[font.length - 2], 10) : PIXI.BitmapText.fonts[this.fontName].size; + + this.dirty = true; +}; + +/** + * Renders text + * + * @method updateText + * @private + */ +PIXI.BitmapText.prototype.updateText = function() +{ + var data = PIXI.BitmapText.fonts[this.fontName]; + var pos = new PIXI.Point(); + var prevCharCode = null; + var chars = []; + var maxLineWidth = 0; + var lineWidths = []; + var line = 0; + var scale = this.fontSize / data.size; + for(var i = 0; i < this.text.length; i++) + { + var charCode = this.text.charCodeAt(i); + if(/(?:\r\n|\r|\n)/.test(this.text.charAt(i))) + { + lineWidths.push(pos.x); + maxLineWidth = Math.max(maxLineWidth, pos.x); + line++; + + pos.x = 0; + pos.y += data.lineHeight; + prevCharCode = null; + continue; + } + + var charData = data.chars[charCode]; + if(!charData) continue; + + if(prevCharCode && charData[prevCharCode]) + { + pos.x += charData.kerning[prevCharCode]; + } + chars.push({texture:charData.texture, line: line, charCode: charCode, position: new PIXI.Point(pos.x + charData.xOffset, pos.y + charData.yOffset)}); + pos.x += charData.xAdvance; + + prevCharCode = charCode; + } + + lineWidths.push(pos.x); + maxLineWidth = Math.max(maxLineWidth, pos.x); + + var lineAlignOffsets = []; + for(i = 0; i <= line; i++) + { + var alignOffset = 0; + if(this.style.align === 'right') + { + alignOffset = maxLineWidth - lineWidths[i]; + } + else if(this.style.align === 'center') + { + alignOffset = (maxLineWidth - lineWidths[i]) / 2; + } + lineAlignOffsets.push(alignOffset); + } + + for(i = 0; i < chars.length; i++) + { + var c = new PIXI.Sprite(chars[i].texture); //PIXI.Sprite.fromFrame(chars[i].charCode); + c.position.x = (chars[i].position.x + lineAlignOffsets[chars[i].line]) * scale; + c.position.y = chars[i].position.y * scale; + c.scale.x = c.scale.y = scale; + this.addChild(c); + } + + this.width = maxLineWidth * scale; + this.height = (pos.y + data.lineHeight) * scale; +}; + +/** + * Updates the transfor of this object + * + * @method updateTransform + * @private + */ +PIXI.BitmapText.prototype.updateTransform = function() +{ + if(this.dirty) + { + while(this.children.length > 0) + { + this.removeChild(this.getChildAt(0)); + } + this.updateText(); + + this.dirty = false; + } + + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); +}; + +PIXI.BitmapText.fonts = {}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + /** + * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive + * This manager also supports multitouch. + * + * @class InteractionManager + * @constructor + * @param stage {Stage} The stage to handle interactions + */ +PIXI.InteractionManager = function(stage) +{ + /** + * a refference to the stage + * + * @property stage + * @type Stage + */ + this.stage = stage; + + /** + * the mouse data + * + * @property mouse + * @type InteractionData + */ + this.mouse = new PIXI.InteractionData(); + + /** + * an object that stores current touches (InteractionData) by id reference + * + * @property touchs + * @type Object + */ + this.touchs = {}; + + // helpers + this.tempPoint = new PIXI.Point(); + //this.tempMatrix = mat3.create(); + + this.mouseoverEnabled = true; + + //tiny little interactiveData pool! + this.pool = []; + + this.interactiveItems = []; + this.interactionDOMElement = null; + + //this will make it so that you dont have to call bind all the time + this.onMouseMove = this.onMouseMove.bind( this ); + this.onMouseDown = this.onMouseDown.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseUp = this.onMouseUp.bind(this); + + this.onTouchStart = this.onTouchStart.bind(this); + this.onTouchEnd = this.onTouchEnd.bind(this); + this.onTouchMove = this.onTouchMove.bind(this); + this.last = 0; + + this.currentCursor = 'inherit'; +}; + +// constructor +PIXI.InteractionManager.prototype.constructor = PIXI.InteractionManager; + +/** + * Collects an interactive sprite recursively to have their interactions managed + * + * @method collectInteractiveSprite + * @param displayObject {DisplayObject} the displayObject to collect + * @param iParent {DisplayObject} + * @private + */ +PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) +{ + var children = displayObject.children; + var length = children.length; + + /// make an interaction tree... {item.__interactiveParent} + for (var i = length-1; i >= 0; i--) + { + var child = children[i]; + +// if(child.visible) { + // push all interactive bits + if(child.interactive) + { + iParent.interactiveChildren = true; + //child.__iParent = iParent; + this.interactiveItems.push(child); + + if(child.children.length > 0) + { + this.collectInteractiveSprite(child, child); + } + } + else + { + child.__iParent = null; + + if(child.children.length > 0) + { + this.collectInteractiveSprite(child, iParent); + } + } +// } + } +}; + +/** + * Sets the target for event delegation + * + * @method setTarget + * @param target {WebGLRenderer|CanvasRenderer} the renderer to bind events to + * @private + */ +PIXI.InteractionManager.prototype.setTarget = function(target) +{ + this.target = target; + + //check if the dom element has been set. If it has don't do anything + if( this.interactionDOMElement === null ) { + + this.setTargetDomElement( target.view ); + } + + document.body.addEventListener('mouseup', this.onMouseUp, true); +}; + + +/** + * Sets the dom element which will receive mouse/touch events. This is useful for when you have other DOM + * elements ontop of the renderers Canvas element. With this you'll be able to delegate another dom element + * to receive those events + * + * @method setTargetDomElement + * @param domElement {DOMElement} the dom element which will receive mouse and touch events + * @private + */ +PIXI.InteractionManager.prototype.setTargetDomElement = function(domElement) +{ + //remove previouse listeners + if( this.interactionDOMElement !== null ) + { + this.interactionDOMElement.style['-ms-content-zooming'] = ''; + this.interactionDOMElement.style['-ms-touch-action'] = ''; + + this.interactionDOMElement.removeEventListener('mousemove', this.onMouseMove, true); + this.interactionDOMElement.removeEventListener('mousedown', this.onMouseDown, true); + this.interactionDOMElement.removeEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + this.interactionDOMElement.removeEventListener('touchstart', this.onTouchStart, true); + this.interactionDOMElement.removeEventListener('touchend', this.onTouchEnd, true); + this.interactionDOMElement.removeEventListener('touchmove', this.onTouchMove, true); + } + + + if (window.navigator.msPointerEnabled) + { + // time to remove some of that zoom in ja.. + domElement.style['-ms-content-zooming'] = 'none'; + domElement.style['-ms-touch-action'] = 'none'; + + // DO some window specific touch! + } + + this.interactionDOMElement = domElement; + + domElement.addEventListener('mousemove', this.onMouseMove, true); + domElement.addEventListener('mousedown', this.onMouseDown, true); + domElement.addEventListener('mouseout', this.onMouseOut, true); + + // aint no multi touch just yet! + domElement.addEventListener('touchstart', this.onTouchStart, true); + domElement.addEventListener('touchend', this.onTouchEnd, true); + domElement.addEventListener('touchmove', this.onTouchMove, true); +}; + +/** + * updates the state of interactive objects + * + * @method update + * @private + */ +PIXI.InteractionManager.prototype.update = function() +{ + if(!this.target)return; + + // frequency of 30fps?? + var now = Date.now(); + var diff = now - this.last; + diff = (diff * 30) / 1000; + if(diff < 1)return; + this.last = now; + // + + var i = 0; + + // ok.. so mouse events?? + // yes for now :) + // OPTIMSE - how often to check?? + if(this.dirty) + { + this.dirty = false; + + var len = this.interactiveItems.length; + + for (i = 0; i < len; i++) { + this.interactiveItems[i].interactiveChildren = false; + } + + this.interactiveItems = []; + + if(this.stage.interactive)this.interactiveItems.push(this.stage); + // go through and collect all the objects that are interactive.. + this.collectInteractiveSprite(this.stage, this.stage); + } + + // loop through interactive objects! + var length = this.interactiveItems.length; + + // return; + + for (i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + + //if(!item.visible)continue; + + // OPTIMISATION - only calculate every time if the mousemove function exists.. + // OK so.. does the object have any other interactive functions? + // hit-test the clip! + + + if(item.mouseover || item.mouseout || item.buttonMode) + { + // ok so there are some functions so lets hit test it.. + item.__hit = this.hitTest(item, this.mouse); + this.mouse.target = item; + // ok so deal with interactions.. + // loks like there was a hit! + if(item.__hit) + { + if(item.buttonMode) this.currentCursor = item.defaultCursor; + + if(!item.__isOver) + { + + if(item.mouseover)item.mouseover(this.mouse); + item.__isOver = true; + } + } + else + { + if(item.__isOver) + { + // roll out! + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } + } + // ---> + } + + + if(this.currentCursor !== this.interactionDOMElement.style.cursor) + { + this.interactionDOMElement.style.cursor = this.currentCursor; + } +}; + +/** + * Is called when the mouse moves accross the renderer element + * + * @method onMouseMove + * @param event {Event} The DOM event of the mouse moving + * @private + */ +PIXI.InteractionManager.prototype.onMouseMove = function(event) +{ + this.mouse.originalEvent = event || window.event; //IE uses window.event + // TODO optimize by not check EVERY TIME! maybe half as often? // + var rect = this.interactionDOMElement.getBoundingClientRect(); + + this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width); + this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height); + + var length = this.interactiveItems.length; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.mousemove) + { + //call the function! + item.mousemove(this.mouse); + } + } +}; + +/** + * Is called when the mouse button is pressed down on the renderer element + * + * @method onMouseDown + * @param event {Event} The DOM event of a mouse button being pressed down + * @private + */ +PIXI.InteractionManager.prototype.onMouseDown = function(event) +{ + this.mouse.originalEvent = event || window.event; //IE uses window.event + + // loop through inteaction tree... + // hit test each item! -> + // get interactive items under point?? + //stage.__i + var length = this.interactiveItems.length; + + // while + // hit test + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.mousedown || item.click) + { + item.__mouseIsDown = true; + item.__hit = this.hitTest(item, this.mouse); + + if(item.__hit) + { + //call the function! + if(item.mousedown)item.mousedown(this.mouse); + item.__isDown = true; + + // just the one! + if(!item.interactiveChildren)break; + } + } + } +}; + + +PIXI.InteractionManager.prototype.onMouseOut = function() +{ + var length = this.interactiveItems.length; + + this.currentCursor = 'inherit'; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.__isOver) + { + this.mouse.target = item; + if(item.mouseout)item.mouseout(this.mouse); + item.__isOver = false; + } + } +}; + +/** + * Is called when the mouse button is released on the renderer element + * + * @method onMouseUp + * @param event {Event} The DOM event of a mouse button being released + * @private + */ +PIXI.InteractionManager.prototype.onMouseUp = function(event) +{ + this.mouse.originalEvent = event || window.event; //IE uses window.event + + var length = this.interactiveItems.length; + var up = false; + + for (var i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + + if(item.mouseup || item.mouseupoutside || item.click) + { + item.__hit = this.hitTest(item, this.mouse); + + if(item.__hit && !up) + { + //call the function! + if(item.mouseup) + { + item.mouseup(this.mouse); + } + if(item.__isDown) + { + if(item.click)item.click(this.mouse); + } + + if(!item.interactiveChildren)up = true; + } + else + { + if(item.__isDown) + { + if(item.mouseupoutside)item.mouseupoutside(this.mouse); + } + } + + item.__isDown = false; + } + } +}; + +/** + * Tests if the current mouse coords hit a sprite + * + * @method hitTest + * @param item {DisplayObject} The displayObject to test for a hit + * @param interactionData {InteractionData} The interactiondata object to update in the case of a hit + * @private + */ +PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) +{ + var global = interactionData.global; + + if( !item.worldVisible )return false; + + // temp fix for if the element is in a non visible + + var isSprite = (item instanceof PIXI.Sprite), + worldTransform = item.worldTransform, + a00 = worldTransform[0], a01 = worldTransform[1], a02 = worldTransform[2], + a10 = worldTransform[3], a11 = worldTransform[4], a12 = worldTransform[5], + id = 1 / (a00 * a11 + a01 * -a10), + x = a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id, + y = a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id; + + interactionData.target = item; + + //a sprite or display object with a hit area defined + if(item.hitArea && item.hitArea.contains) { + if(item.hitArea.contains(x, y)) { + //if(isSprite) + interactionData.target = item; + + return true; + } + + return false; + } + // a sprite with no hitarea defined + else if(isSprite) + { + var width = item.texture.frame.width, + height = item.texture.frame.height, + x1 = -width * item.anchor.x, + y1; + + if(x > x1 && x < x1 + width) + { + y1 = -height * item.anchor.y; + + if(y > y1 && y < y1 + height) + { + // set the target property if a hit is true! + interactionData.target = item; + return true; + } + } + } + + var length = item.children.length; + + for (var i = 0; i < length; i++) + { + var tempItem = item.children[i]; + var hit = this.hitTest(tempItem, interactionData); + if(hit) + { + // hmm.. TODO SET CORRECT TARGET? + interactionData.target = item; + return true; + } + } + + return false; +}; + +/** + * Is called when a touch is moved accross the renderer element + * + * @method onTouchMove + * @param event {Event} The DOM event of a touch moving accross the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchMove = function(event) +{ + var rect = this.interactionDOMElement.getBoundingClientRect(); + var changedTouches = event.changedTouches; + var touchData; + var i = 0; + + for (i = 0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + touchData = this.touchs[touchEvent.identifier]; + touchData.originalEvent = event || window.event; + + // update the touch position + touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); + touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); + if(navigator.isCocoonJS) { + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } + } + + var length = this.interactiveItems.length; + for (i = 0; i < length; i++) + { + var item = this.interactiveItems[i]; + if(item.touchmove) + item.touchmove(touchData); + } +}; + +/** + * Is called when a touch is started on the renderer element + * + * @method onTouchStart + * @param event {Event} The DOM event of a touch starting on the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchStart = function(event) +{ + var rect = this.interactionDOMElement.getBoundingClientRect(); + + var changedTouches = event.changedTouches; + for (var i=0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + + var touchData = this.pool.pop(); + if(!touchData)touchData = new PIXI.InteractionData(); + + touchData.originalEvent = event || window.event; + + this.touchs[touchEvent.identifier] = touchData; + touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); + touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); + if(navigator.isCocoonJS) { + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } + + var length = this.interactiveItems.length; + + for (var j = 0; j < length; j++) + { + var item = this.interactiveItems[j]; + + if(item.touchstart || item.tap) + { + item.__hit = this.hitTest(item, touchData); + + if(item.__hit) + { + //call the function! + if(item.touchstart)item.touchstart(touchData); + item.__isDown = true; + item.__touchData = touchData; + + if(!item.interactiveChildren)break; + } + } + } + } +}; + +/** + * Is called when a touch is ended on the renderer element + * + * @method onTouchEnd + * @param event {Event} The DOM event of a touch ending on the renderer view + * @private + */ +PIXI.InteractionManager.prototype.onTouchEnd = function(event) +{ + //this.mouse.originalEvent = event || window.event; //IE uses window.event + var rect = this.interactionDOMElement.getBoundingClientRect(); + var changedTouches = event.changedTouches; + + for (var i=0; i < changedTouches.length; i++) + { + var touchEvent = changedTouches[i]; + var touchData = this.touchs[touchEvent.identifier]; + var up = false; + touchData.global.x = (touchEvent.clientX - rect.left) * (this.target.width / rect.width); + touchData.global.y = (touchEvent.clientY - rect.top) * (this.target.height / rect.height); + if(navigator.isCocoonJS) { + touchData.global.x = touchEvent.clientX; + touchData.global.y = touchEvent.clientY; + } + + var length = this.interactiveItems.length; + for (var j = 0; j < length; j++) + { + var item = this.interactiveItems[j]; + var itemTouchData = item.__touchData; // <-- Here! + item.__hit = this.hitTest(item, touchData); + + if(itemTouchData === touchData) + { + // so this one WAS down... + touchData.originalEvent = event || window.event; + // hitTest?? + + if(item.touchend || item.tap) + { + if(item.__hit && !up) + { + if(item.touchend)item.touchend(touchData); + if(item.__isDown) + { + if(item.tap)item.tap(touchData); + } + + if(!item.interactiveChildren)up = true; + } + else + { + if(item.__isDown) + { + if(item.touchendoutside)item.touchendoutside(touchData); + } + } + + item.__isDown = false; + } + + item.__touchData = null; + + } + /* + else + { + + } + */ + } + // remove the touch.. + this.pool.push(touchData); + this.touchs[touchEvent.identifier] = null; + } +}; + +/** + * Holds all information related to an Interaction event + * + * @class InteractionData + * @constructor + */ +PIXI.InteractionData = function() +{ + /** + * This point stores the global coords of where the touch/mouse event happened + * + * @property global + * @type Point + */ + this.global = new PIXI.Point(); + + // this is here for legacy... but will remove + this.local = new PIXI.Point(); + + /** + * The target Sprite that was interacted with + * + * @property target + * @type Sprite + */ + this.target = null; + + /** + * When passed to an event handler, this will be the original DOM Event that was captured + * + * @property originalEvent + * @type Event + */ + this.originalEvent = null; +}; + +/** + * This will return the local coords of the specified displayObject for this InteractionData + * + * @method getLocalPosition + * @param displayObject {DisplayObject} The DisplayObject that you would like the local coords off + * @return {Point} A point containing the coords of the InteractionData position relative to the DisplayObject + */ +PIXI.InteractionData.prototype.getLocalPosition = function(displayObject) +{ + var worldTransform = displayObject.worldTransform; + var global = this.global; + + // do a cheeky transform to get the mouse coords; + var a00 = worldTransform[0], a01 = worldTransform[1], a02 = worldTransform[2], + a10 = worldTransform[3], a11 = worldTransform[4], a12 = worldTransform[5], + id = 1 / (a00 * a11 + a01 * -a10); + // set the mouse coords... + return new PIXI.Point(a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id, + a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id); +}; + +// constructor +PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A Stage represents the root of the display tree. Everything connected to the stage is rendered + * + * @class Stage + * @extends DisplayObjectContainer + * @constructor + * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format + * like: 0xFFFFFF for white + */ +PIXI.Stage = function(backgroundColor) +{ + PIXI.DisplayObjectContainer.call( this ); + + /** + * [read-only] Current transform of the object based on world (parent) factors + * + * @property worldTransform + * @type Mat3 + * @readOnly + * @private + */ + this.worldTransform = PIXI.mat3.create(); + + /** + * Whether or not the stage is interactive + * + * @property interactive + * @type Boolean + */ + this.interactive = true; + + /** + * The interaction manage for this stage, manages all interactive activity on the stage + * + * @property interactive + * @type InteractionManager + */ + this.interactionManager = new PIXI.InteractionManager(this); + + /** + * Whether the stage is dirty and needs to have interactions updated + * + * @property dirty + * @type Boolean + * @private + */ + this.dirty = true; + + //the stage is it's own stage + this.stage = this; + + //optimize hit detection a bit + this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000); + + this.setBackgroundColor(backgroundColor); +}; + +// constructor +PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Stage.prototype.constructor = PIXI.Stage; + +/** + * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. + * This is useful for when you have other DOM elements ontop of the Canvas element. + * + * @method setInteractionDelegate + * @param domElement {DOMElement} This new domElement which will receive mouse/touch events + */ +PIXI.Stage.prototype.setInteractionDelegate = function(domElement) +{ + this.interactionManager.setTargetDomElement( domElement ); +}; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Stage.prototype.updateTransform = function() +{ + this.worldAlpha = 1; + + for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; +}; + + +PIXI.rgb2hex = function(rgb) { + return ((rgb[0]*255 << 16) + (rgb[1]*255 << 8) + rgb[2]*255); +}; + +/** + * A polyfill for Function.prototype.bind + * + * @method bind + */ +if (typeof Function.prototype.bind !== 'function') { + Function.prototype.bind = (function () { + var slice = Array.prototype.slice; + return function (thisArg) { + var target = this, boundArgs = slice.call(arguments, 1); + + if (typeof target !== 'function') throw new TypeError(); + + function bound() { + var args = boundArgs.concat(slice.call(arguments)); + target.apply(this instanceof bound ? this : thisArg, args); + } + + bound.prototype = (function F(proto) { + if (proto) F.prototype = proto; + if (!(this instanceof F)) return new F(); + })(target.prototype); + + return bound; + }; + })(); +} + +/** + * A wrapper for ajax requests to be handled cross browser + * + * @class AjaxRequest + * @constructor + */ +PIXI.AjaxRequest = function AjaxRequest() +{ + var activexmodes = ['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.3.0', 'Microsoft.XMLHTTP']; //activeX versions to check for in IE + + if (window.ActiveXObject) + { //Test for support for ActiveXObject in IE first (as XMLHttpRequest in IE7 is broken) + for (var i=0; i 0 && (number & (number - 1)) === 0) // see: http://goo.gl/D9kPj + return number; + else + { + var result = 1; + while (result < number) result <<= 1; + return result; + } +}; + + + +/** + * https://github.com/mrdoob/eventtarget.js/ + * THankS mr DOob! + */ + +/** + * Adds event emitter functionality to a class + * + * @class EventTarget + * @example + * function MyEmitter() { + * PIXI.EventTarget.call(this); //mixes in event target stuff + * } + * + * var em = new MyEmitter(); + * em.emit({ type: 'eventName', data: 'some data' }); + */ +PIXI.EventTarget = function () { + + var listeners = {}; + + this.addEventListener = this.on = function ( type, listener ) { + + + if ( listeners[ type ] === undefined ) { + + listeners[ type ] = []; + + } + + if ( listeners[ type ].indexOf( listener ) === - 1 ) { + + listeners[ type ].push( listener ); + } + + }; + + this.dispatchEvent = this.emit = function ( event ) { + + if ( !listeners[ event.type ] || !listeners[ event.type ].length ) { + + return; + + } + + for(var i = 0, l = listeners[ event.type ].length; i < l; i++) { + + listeners[ event.type ][ i ]( event ); + + } + + }; + + this.removeEventListener = this.off = function ( type, listener ) { + + var index = listeners[ type ].indexOf( listener ); + + if ( index !== - 1 ) { + + listeners[ type ].splice( index, 1 ); + + } + + }; + + this.removeAllEventListeners = function( type ) { + var a = listeners[type]; + if (a) + a.length = 0; + }; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot fastest. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @method autoDetectRenderer + * @static + * @param width {Number} the width of the renderers view + * @param height {Number} the height of the renderers view + * @param view {Canvas} the canvas to use as a view, optional + * @param transparent=false {Boolean} the transparency of the render view, default false + * @param antialias=false {Boolean} sets antialias (only applicable in webGL chrome at the moment) + * + * antialias + */ +PIXI.autoDetectRenderer = function(width, height, view, transparent, antialias) +{ + if(!width)width = 800; + if(!height)height = 600; + + // BORROWED from Mr Doob (mrdoob.com) + var webgl = ( function () { try { + var canvas = document.createElement( 'canvas' ); + return !! window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ); + } catch( e ) { + return false; + } + } )(); + + // used to detect ie 11 - no longer required + /* if(webgl) + { + var ie = (navigator.userAgent.toLowerCase().indexOf('trident') !== -1); + webgl = !ie; + } + */ + + + if( webgl ) + { + return new PIXI.WebGLRenderer(width, height, view, transparent, antialias); + } + + return new PIXI.CanvasRenderer(width, height, view, transparent); +}; + +/* + PolyK library + url: http://polyk.ivank.net + Released under MIT licence. + + Copyright (c) 2012 Ivan Kuckir + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + This is an amazing lib! + + slightly modified by mat groves (matgroves.com); +*/ + +PIXI.PolyK = {}; + +/** + * Triangulates shapes for webGL graphic fills + * + * @method Triangulate + * @namespace PolyK + * @constructor + */ +PIXI.PolyK.Triangulate = function(p) +{ + var sign = true; + + var n = p.length >> 1; + if(n < 3) return []; + + var tgs = []; + var avl = []; + for(var i = 0; i < n; i++) avl.push(i); + + i = 0; + var al = n; + while(al > 3) + { + var i0 = avl[(i+0)%al]; + var i1 = avl[(i+1)%al]; + var i2 = avl[(i+2)%al]; + + var ax = p[2*i0], ay = p[2*i0+1]; + var bx = p[2*i1], by = p[2*i1+1]; + var cx = p[2*i2], cy = p[2*i2+1]; + + var earFound = false; + if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) + { + earFound = true; + for(var j = 0; j < al; j++) + { + var vi = avl[j]; + if(vi === i0 || vi === i1 || vi === i2) continue; + + if(PIXI.PolyK._PointInTriangle(p[2*vi], p[2*vi+1], ax, ay, bx, by, cx, cy)) { + earFound = false; + break; + } + } + } + + if(earFound) + { + tgs.push(i0, i1, i2); + avl.splice((i+1)%al, 1); + al--; + i = 0; + } + else if(i++ > 3*al) + { + // need to flip flip reverse it! + // reset! + if(sign) + { + tgs = []; + avl = []; + for(i = 0; i < n; i++) avl.push(i); + + i = 0; + al = n; + + sign = false; + } + else + { + window.console.log("PIXI Warning: shape too complex to fill"); + return []; + } + } + } + + tgs.push(avl[0], avl[1], avl[2]); + return tgs; +}; + +/** + * Checks if a point is within a triangle + * + * @class _PointInTriangle + * @namespace PolyK + * @private + */ +PIXI.PolyK._PointInTriangle = function(px, py, ax, ay, bx, by, cx, cy) +{ + var v0x = cx-ax; + var v0y = cy-ay; + var v1x = bx-ax; + var v1y = by-ay; + var v2x = px-ax; + var v2y = py-ay; + + var dot00 = v0x*v0x+v0y*v0y; + var dot01 = v0x*v1x+v0y*v1y; + var dot02 = v0x*v2x+v0y*v2y; + var dot11 = v1x*v1x+v1y*v1y; + var dot12 = v1x*v2x+v1y*v2y; + + var invDenom = 1 / (dot00 * dot11 - dot01 * dot01); + var u = (dot11 * dot02 - dot01 * dot12) * invDenom; + var v = (dot00 * dot12 - dot01 * dot02) * invDenom; + + // Check if point is in triangle + return (u >= 0) && (v >= 0) && (u + v < 1); +}; + +/** + * Checks if a shape is convex + * + * @class _convex + * @namespace PolyK + * @private + */ +PIXI.PolyK._convex = function(ax, ay, bx, by, cx, cy, sign) +{ + return ((ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0) === sign; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.initDefaultShaders = function() +{ + + // PIXI.stripShader = new PIXI.StripShader(); +// PIXI.stripShader.init(); + +}; + +PIXI.CompileVertexShader = function(gl, shaderSrc) +{ + return PIXI._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); +}; + +PIXI.CompileFragmentShader = function(gl, shaderSrc) +{ + return PIXI._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); +}; + +PIXI._CompileShader = function(gl, shaderSrc, shaderType) +{ + var src = shaderSrc.join("\n"); + var shader = gl.createShader(shaderType); + gl.shaderSource(shader, src); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + window.console.log(gl.getShaderInfoLog(shader)); + return null; + } + + return shader; +}; + +PIXI.compileProgram = function(gl, vertexSrc, fragmentSrc) +{ + //var gl = PIXI.gl; + var fragmentShader = PIXI.CompileFragmentShader(gl, fragmentSrc); + var vertexShader = PIXI.CompileVertexShader(gl, vertexSrc); + + var shaderProgram = gl.createProgram(); + + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + window.console.log("Could not initialise shaders"); + } + + return shaderProgram; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * @author Richard Davey http://www.photonstorm.com @photonstorm + */ + +/** +* @class PIXI.PixiShader +* @constructor +*/ +PIXI.PixiShader = function(gl) +{ + this.gl = gl; + + /** + * @property {any} program - The WebGL program. + */ + this.program = null; + + /** + * @property {array} fragmentSrc - The fragment shader. + */ + this.fragmentSrc = [ + 'precision lowp float;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ]; + + + /** + * @property {number} textureCount - A local texture counter for multi-texture shaders. + */ + this.textureCount = 0; + + + this.init(); +}; + +/** +* @method PIXI.PixiShader#init +*/ +PIXI.PixiShader.prototype.init = function() +{ + + var gl = this.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc); + + gl.useProgram(program); + + // get and store the uniforms for the shader + this.uSampler = gl.getUniformLocation(program, 'uSampler'); + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.dimensions = gl.getUniformLocation(program, 'dimensions'); + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + + + // Begin worst hack eva // + + // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? + // maybe its somthing to do with the current state of the gl context. + // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel + // If theres any webGL people that know why could happen please help :) + if(this.colorAttribute === -1) + { + this.colorAttribute = 2; + } + + // End worst hack eva // + + // add those custom shaders! + for (var key in this.uniforms) + { + // get the uniform locations.. + this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); + } + + this.initUniforms(); + + this.program = program; +}; + +/** +* Initialises the shader uniform values. +* Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ +* http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf +* +* @method PIXI.PixiShader#initUniforms +*/ +PIXI.PixiShader.prototype.initUniforms = function() +{ + this.textureCount = 1; + var gl = this.gl; + var uniform; + + for (var key in this.uniforms) + { + uniform = this.uniforms[key]; + + var type = uniform.type; + + if (type === 'sampler2D') + { + uniform._init = false; + + if (uniform.value !== null) + { + this.initSampler2D(uniform); + } + } + else if (type === 'mat2' || type === 'mat3' || type === 'mat4') + { + // These require special handling + uniform.glMatrix = true; + uniform.glValueLength = 1; + + if (type === 'mat2') + { + uniform.glFunc = gl.uniformMatrix2fv; + } + else if (type === 'mat3') + { + uniform.glFunc = gl.uniformMatrix3fv; + } + else if (type === 'mat4') + { + uniform.glFunc = gl.uniformMatrix4fv; + } + } + else + { + // GL function reference + uniform.glFunc = gl['uniform' + type]; + + if (type === '2f' || type === '2i') + { + uniform.glValueLength = 2; + } + else if (type === '3f' || type === '3i') + { + uniform.glValueLength = 3; + } + else if (type === '4f' || type === '4i') + { + uniform.glValueLength = 4; + } + else + { + uniform.glValueLength = 1; + } + } + } + +}; + +/** +* Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture is has loaded) +* +* @method PIXI.PixiShader#initSampler2D +*/ +PIXI.PixiShader.prototype.initSampler2D = function(uniform) +{ + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + return; + } + + var gl = this.gl; + + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTexture); + + // Extended texture data + if (uniform.textureData) + { + var data = uniform.textureData; + + // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); + // GLTextureLinear = mag/min linear, wrap clamp + // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat + // GLTextureNearest = mag/min nearest, wrap clamp + // AudioTexture = whatever + luminance + width 512, height 2, border 0 + // KeyTexture = whatever + luminance + width 256, height 2, border 0 + + // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST + // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT + + var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; + var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; + var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; + var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; + var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; + + if (data.repeat) + { + wrapS = gl.REPEAT; + wrapT = gl.REPEAT; + } + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + + if (data.width) + { + var width = (data.width) ? data.width : 512; + var height = (data.height) ? data.height : 2; + var border = (data.border) ? data.border : 0; + + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); + } + else + { + // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); + gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); + } + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); + } + + gl.uniform1i(uniform.uniformLocation, this.textureCount); + + uniform._init = true; + + this.textureCount++; + +}; + +/** +* Updates the shader uniform values. +* +* @method PIXI.PixiShader#syncUniforms +*/ +PIXI.PixiShader.prototype.syncUniforms = function() +{ + this.textureCount = 1; + var uniform; + var gl = this.gl; + + // This would probably be faster in an array and it would guarantee key order + for (var key in this.uniforms) + { + + uniform = this.uniforms[key]; + + if (uniform.glValueLength === 1) + { + if (uniform.glMatrix === true) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); + } + else + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); + } + } + else if (uniform.glValueLength === 2) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); + } + else if (uniform.glValueLength === 3) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); + } + else if (uniform.glValueLength === 4) + { + uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); + } + else if (uniform.type === 'sampler2D') + { + if (uniform._init) + { + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || PIXI.createWebGLTexture( uniform.value.baseTexture, gl)); + gl.uniform1i(uniform.uniformLocation, this.textureCount); + this.textureCount++; + } + else + { + this.initSampler2D(uniform); + } + } + } + +}; + +PIXI.PixiShader.defaultVertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec2 aColor;', + + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void) {', + ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', + ' vColor = vec4(color * aColor.x, aColor.x);', + '}' +]; + + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.StripShader = function() +{ + // the webGL program.. + this.program = null; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec2 vTextureCoord;', + 'varying float vColor;', + 'uniform float alpha;', + 'uniform sampler2D uSampler;', + + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y));', + ' gl_FragColor = gl_FragColor * alpha;', + '}' + ]; + + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute float aColor;', + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'varying vec2 vTextureCoord;', + 'uniform vec2 offsetVector;', + 'varying float vColor;', + + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition, 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / projectionVector.y + 1.0 , 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = aColor;', + '}' + ]; +}; + +PIXI.StripShader.prototype.init = function() +{ + + var gl = PIXI.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); + gl.useProgram(program); + + // get and store the uniforms for the shader + this.uSampler = gl.getUniformLocation(program, 'uSampler'); + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); + + this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); + this.alpha = gl.getUniformLocation(program, 'alpha'); + + this.program = program; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.PrimitiveShader = function(gl) +{ + this.gl = gl; + + // the webGL program.. + this.program = null; + + this.fragmentSrc = [ + 'precision mediump float;', + 'varying vec4 vColor;', + + 'void main(void) {', + ' gl_FragColor = vColor;', + '}' + ]; + + this.vertexSrc = [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + 'uniform mat3 translationMatrix;', + 'uniform vec2 projectionVector;', + 'uniform vec2 offsetVector;', + 'uniform float alpha;', + 'uniform vec3 tint;', + 'varying vec4 vColor;', + + 'void main(void) {', + ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', + ' v -= offsetVector.xyx;', + ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ]; + + this.init(); +}; + +PIXI.PrimitiveShader.prototype.init = function() +{ + + var gl = this.gl; + + var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); + gl.useProgram(program); + + // get and store the uniforms for the shader + this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); + this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); + this.tintColor = gl.getUniformLocation(program, 'tint'); + + + // get and store the attributes + this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); + this.colorAttribute = gl.getAttribLocation(program, 'aColor'); + + this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); + this.alpha = gl.getUniformLocation(program, 'alpha'); + + this.program = program; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * A set of functions used by the webGL renderer to draw the primitive graphics data + * + * @class CanvasGraphics + */ +PIXI.WebGLGraphics = function() +{ + +}; + +/** + * Renders the graphics object + * + * @static + * @private + * @method renderGraphics + * @param graphics {Graphics} + * @param projection {Object} + */ +PIXI.WebGLGraphics.renderGraphics = function(graphics, renderSession)//projection, offset) +{ + var gl = renderSession.gl; + var projection = renderSession.projection, + offset = renderSession.offset, + shader = renderSession.shaderManager.primitiveShader; + + if(!graphics._webGL[gl.id])graphics._webGL[gl.id] = {points:[], indices:[], lastIndex:0, + buffer:gl.createBuffer(), + indexBuffer:gl.createBuffer()}; + + var webGL = graphics._webGL[gl.id]; + + if(graphics.dirty) + { + graphics.dirty = false; + + if(graphics.clearDirty) + { + graphics.clearDirty = false; + + webGL.lastIndex = 0; + webGL.points = []; + webGL.indices = []; + + } + + PIXI.WebGLGraphics.updateGraphics(graphics, gl); + } + + renderSession.shaderManager.activatePrimitiveShader(); + + // This could be speeded up fo sure! + var m = PIXI.mat3.clone(graphics.worldTransform); + + PIXI.mat3.transpose(m); + + // set the matrix transform for the + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + + gl.uniformMatrix3fv(shader.translationMatrix, false, m); + + gl.uniform2f(shader.projectionVector, projection.x, -projection.y); + gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); + + gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); + + gl.uniform1f(shader.alpha, graphics.worldAlpha); + gl.bindBuffer(gl.ARRAY_BUFFER, webGL.buffer); + + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); + gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); + + // set the index buffer! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGL.indexBuffer); + + gl.drawElements(gl.TRIANGLE_STRIP, webGL.indices.length, gl.UNSIGNED_SHORT, 0 ); + + renderSession.shaderManager.deactivatePrimitiveShader(); + + // return to default shader... +// PIXI.activateShader(PIXI.defaultShader); +}; + +/** + * Updates the graphics object + * + * @static + * @private + * @method updateGraphics + * @param graphics {Graphics} + */ +PIXI.WebGLGraphics.updateGraphics = function(graphics, gl) +{ + var webGL = graphics._webGL[gl.id]; + + for (var i = webGL.lastIndex; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + if(data.type === PIXI.Graphics.POLY) + { + if(data.fill) + { + if(data.points.length>3) + PIXI.WebGLGraphics.buildPoly(data, webGL); + } + + if(data.lineWidth > 0) + { + PIXI.WebGLGraphics.buildLine(data, webGL); + } + } + else if(data.type === PIXI.Graphics.RECT) + { + PIXI.WebGLGraphics.buildRectangle(data, webGL); + } + else if(data.type === PIXI.Graphics.CIRC || data.type === PIXI.Graphics.ELIP) + { + PIXI.WebGLGraphics.buildCircle(data, webGL); + } + } + + webGL.lastIndex = graphics.graphicsData.length; + + + + webGL.glPoints = new Float32Array(webGL.points); + + gl.bindBuffer(gl.ARRAY_BUFFER, webGL.buffer); + gl.bufferData(gl.ARRAY_BUFFER, webGL.glPoints, gl.STATIC_DRAW); + + webGL.glIndicies = new Uint16Array(webGL.indices); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGL.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, webGL.glIndicies, gl.STATIC_DRAW); +}; + +/** + * Builds a rectangle to draw + * + * @static + * @private + * @method buildRectangle + * @param graphics {Graphics} + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) +{ + // --- // + // need to convert points to a nice regular data + // + var rectData = graphicsData.points; + var x = rectData[0]; + var y = rectData[1]; + var width = rectData[2]; + var height = rectData[3]; + + + if(graphicsData.fill) + { + var color = PIXI.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vertPos = verts.length/6; + + // start + verts.push(x, y); + verts.push(r, g, b, alpha); + + verts.push(x + width, y); + verts.push(r, g, b, alpha); + + verts.push(x , y + height); + verts.push(r, g, b, alpha); + + verts.push(x + width, y + height); + verts.push(r, g, b, alpha); + + // insert 2 dead triangles.. + indices.push(vertPos, vertPos, vertPos+1, vertPos+2, vertPos+3, vertPos+3); + } + + if(graphicsData.lineWidth) + { + var tempPoints = graphicsData.points; + + graphicsData.points = [x, y, + x + width, y, + x + width, y + height, + x, y + height, + x, y]; + + + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; + } +}; + +/** + * Builds a circle to draw + * + * @static + * @private + * @method buildCircle + * @param graphics {Graphics} + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) +{ + // --- // + // need to convert points to a nice regular data + // + var rectData = graphicsData.points; + var x = rectData[0]; + var y = rectData[1]; + var width = rectData[2]; + var height = rectData[3]; + + var totalSegs = 40; + var seg = (Math.PI * 2) / totalSegs ; + + var i = 0; + + if(graphicsData.fill) + { + var color = PIXI.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var verts = webGLData.points; + var indices = webGLData.indices; + + var vecPos = verts.length/6; + + indices.push(vecPos); + + for (i = 0; i < totalSegs + 1 ; i++) + { + verts.push(x,y, r, g, b, alpha); + + verts.push(x + Math.sin(seg * i) * width, + y + Math.cos(seg * i) * height, + r, g, b, alpha); + + indices.push(vecPos++, vecPos++); + } + + indices.push(vecPos-1); + } + + if(graphicsData.lineWidth) + { + var tempPoints = graphicsData.points; + + graphicsData.points = []; + + for (i = 0; i < totalSegs + 1; i++) + { + graphicsData.points.push(x + Math.sin(seg * i) * width, + y + Math.cos(seg * i) * height); + } + + PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); + + graphicsData.points = tempPoints; + } +}; + +/** + * Builds a line to draw + * + * @static + * @private + * @method buildLine + * @param graphics {Graphics} + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) +{ + // TODO OPTIMISE! + var i = 0; + + var points = graphicsData.points; + if(points.length === 0)return; + + /* + // if the line width is an odd number add 0.5 to align to a whole pixel + if(graphicsData.lineWidth%2) + { + for (i = 0; i < points.length; i++) { + points[i] += 0.5; + } + } + */ + + // get first and last point.. figure out the middle! + var firstPoint = new PIXI.Point( points[0], points[1] ); + var lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); + + // if the first point is the last point - goona have issues :) + if(firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) + { + points.pop(); + points.pop(); + + lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); + + var midPointX = lastPoint.x + (firstPoint.x - lastPoint.x) *0.5; + var midPointY = lastPoint.y + (firstPoint.y - lastPoint.y) *0.5; + + points.unshift(midPointX, midPointY); + points.push(midPointX, midPointY); + } + + var verts = webGLData.points; + var indices = webGLData.indices; + var length = points.length / 2; + var indexCount = points.length; + var indexStart = verts.length/6; + + // DRAW the Line + var width = graphicsData.lineWidth / 2; + + // sort color + var color = PIXI.hex2rgb(graphicsData.lineColor); + var alpha = graphicsData.lineAlpha; + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var px, py, p1x, p1y, p2x, p2y, p3x, p3y; + var perpx, perpy, perp2x, perp2y, perp3x, perp3y; + var a1, b1, c1, a2, b2, c2; + var denom, pdist, dist; + + p1x = points[0]; + p1y = points[1]; + + p2x = points[2]; + p2y = points[3]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + // start + verts.push(p1x - perpx , p1y - perpy, + r, g, b, alpha); + + verts.push(p1x + perpx , p1y + perpy, + r, g, b, alpha); + + for (i = 1; i < length-1; i++) + { + p1x = points[(i-1)*2]; + p1y = points[(i-1)*2 + 1]; + + p2x = points[(i)*2]; + p2y = points[(i)*2 + 1]; + + p3x = points[(i+1)*2]; + p3y = points[(i+1)*2 + 1]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + perp2x = -(p2y - p3y); + perp2y = p2x - p3x; + + dist = Math.sqrt(perp2x*perp2x + perp2y*perp2y); + perp2x /= dist; + perp2y /= dist; + perp2x *= width; + perp2y *= width; + + a1 = (-perpy + p1y) - (-perpy + p2y); + b1 = (-perpx + p2x) - (-perpx + p1x); + c1 = (-perpx + p1x) * (-perpy + p2y) - (-perpx + p2x) * (-perpy + p1y); + a2 = (-perp2y + p3y) - (-perp2y + p2y); + b2 = (-perp2x + p2x) - (-perp2x + p3x); + c2 = (-perp2x + p3x) * (-perp2y + p2y) - (-perp2x + p2x) * (-perp2y + p3y); + + denom = a1*b2 - a2*b1; + + /* + if(Math.abs(denom) < 0.1 ) + { + + denom+=10.1; + verts.push(p2x - perpx , p2y - perpy, + r, g, b, alpha); + + verts.push(p2x + perpx , p2y + perpy, + r, g, b, alpha); + + continue; + } + */ + + px = (b1*c2 - b2*c1)/denom; + py = (a2*c1 - a1*c2)/denom; + + + pdist = (px -p2x) * (px -p2x) + (py -p2y) + (py -p2y); + + + if(pdist > 140 * 140) + { + perp3x = perpx - perp2x; + perp3y = perpy - perp2y; + + dist = Math.sqrt(perp3x*perp3x + perp3y*perp3y); + perp3x /= dist; + perp3y /= dist; + perp3x *= width; + perp3y *= width; + + verts.push(p2x - perp3x, p2y -perp3y); + verts.push(r, g, b, alpha); + + verts.push(p2x + perp3x, p2y +perp3y); + verts.push(r, g, b, alpha); + + verts.push(p2x - perp3x, p2y -perp3y); + verts.push(r, g, b, alpha); + + indexCount++; + } + else + { + + verts.push(px , py); + verts.push(r, g, b, alpha); + + verts.push(p2x - (px-p2x), p2y - (py - p2y)); + verts.push(r, g, b, alpha); + } + } + + p1x = points[(length-2)*2]; + p1y = points[(length-2)*2 + 1]; + + p2x = points[(length-1)*2]; + p2y = points[(length-1)*2 + 1]; + + perpx = -(p1y - p2y); + perpy = p1x - p2x; + + dist = Math.sqrt(perpx*perpx + perpy*perpy); + perpx /= dist; + perpy /= dist; + perpx *= width; + perpy *= width; + + verts.push(p2x - perpx , p2y - perpy); + verts.push(r, g, b, alpha); + + verts.push(p2x + perpx , p2y + perpy); + verts.push(r, g, b, alpha); + + indices.push(indexStart); + + for (i = 0; i < indexCount; i++) + { + indices.push(indexStart++); + } + + indices.push(indexStart-1); +}; + +/** + * Builds a polygon to draw + * + * @static + * @private + * @method buildPoly + * @param graphics {Graphics} + * @param webGLData {Object} + */ +PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) +{ + var points = graphicsData.points; + if(points.length < 6)return; + + // get first and last point.. figure out the middle! + var verts = webGLData.points; + var indices = webGLData.indices; + + var length = points.length / 2; + + // sort color + var color = PIXI.hex2rgb(graphicsData.fillColor); + var alpha = graphicsData.fillAlpha; + var r = color[0] * alpha; + var g = color[1] * alpha; + var b = color[2] * alpha; + + var triangles = PIXI.PolyK.Triangulate(points); + + var vertPos = verts.length / 6; + + var i = 0; + + for (i = 0; i < triangles.length; i+=3) + { + indices.push(triangles[i] + vertPos); + indices.push(triangles[i] + vertPos); + indices.push(triangles[i+1] + vertPos); + indices.push(triangles[i+2] +vertPos); + indices.push(triangles[i+2] + vertPos); + } + + for (i = 0; i < length; i++) + { + verts.push(points[i * 2], points[i * 2 + 1], + r, g, b, alpha); + } +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.glContexts = []; // this is where we store the webGL contexts for easy access. + +/** + * the WebGLRenderer is draws the stage and all its content onto a webGL enabled canvas. This renderer + * should be used for browsers support webGL. This Render works by automatically managing webGLBatchs. + * So no need for Sprite Batch's or Sprite Cloud's + * Dont forget to add the view to your DOM or you will not see anything :) + * + * @class WebGLRenderer + * @constructor + * @param width=0 {Number} the width of the canvas view + * @param height=0 {Number} the height of the canvas view + * @param view {Canvas} the canvas to use as a view, optional + * @param transparent=false {Boolean} the transparency of the render view, default false + * @param antialias=false {Boolean} sets antialias (only applicable in chrome at the moment) + * + */ +PIXI.WebGLRenderer = function(width, height, view, transparent, antialias) +{ + if(!PIXI.defaultRenderer)PIXI.defaultRenderer = this; + + this.type = PIXI.WEBGL_RENDERER; + + // do a catch.. only 1 webGL renderer.. + this.transparent = !!transparent; + + this.width = width || 800; + this.height = height || 600; + + this.view = view || document.createElement( 'canvas' ); + this.view.width = this.width; + this.view.height = this.height; + + // deal with losing context.. + var scope = this; + this.view.addEventListener('webglcontextlost', function(event) { scope.handleContextLost(event); }, false); + this.view.addEventListener('webglcontextrestored', function(event) { scope.handleContextRestored(event); }, false); + + this.options = { + alpha: this.transparent, + antialias:!!antialias, // SPEED UP?? + premultipliedAlpha:false, + stencil:true + }; + + //try 'experimental-webgl' + try { + this.gl = this.view.getContext('experimental-webgl', this.options); + } catch (e) { + //try 'webgl' + try { + this.gl = this.view.getContext('webgl', this.options); + } catch (e2) { + // fail, not able to get a context + throw new Error(' This browser does not support webGL. Try using the canvas renderer' + this); + } + } + + var gl = this.gl; + this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId ++; + + PIXI.glContexts[this.glContextId] = gl; + + if(!PIXI.blendModesWebGL) + { + PIXI.blendModesWebGL = []; + + PIXI.blendModesWebGL[PIXI.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; + PIXI.blendModesWebGL[PIXI.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; + } + + + + + this.projection = new PIXI.Point(); + this.projection.x = this.width/2; + this.projection.y = -this.height/2; + + this.offset = new PIXI.Point(0, 0); + + this.resize(this.width, this.height); + this.contextLost = false; + + // time to create the render managers! each one focuses on managine a state in webGL + this.shaderManager = new PIXI.WebGLShaderManager(gl); // deals with managing the shader programs and their attribs + this.spriteBatch = new PIXI.WebGLSpriteBatch(gl); // manages the rendering of sprites + this.maskManager = new PIXI.WebGLMaskManager(gl); // manages the masks using the stencil buffer + this.filterManager = new PIXI.WebGLFilterManager(gl, this.transparent); // manages the filters + + // + this.renderSession = {}; + this.renderSession.gl = this.gl; + this.renderSession.drawCount = 0; + this.renderSession.shaderManager = this.shaderManager; + this.renderSession.maskManager = this.maskManager; + this.renderSession.filterManager = this.filterManager; + this.renderSession.spriteBatch = this.spriteBatch; + + + gl.useProgram(this.shaderManager.defaultShader.program); + + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + + gl.enable(gl.BLEND); + gl.colorMask(true, true, true, this.transparent); +}; + +// constructor +PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; + +/** + * Renders the stage to its webGL view + * + * @method render + * @param stage {Stage} the Stage element to be rendered + */ +PIXI.WebGLRenderer.prototype.render = function(stage) +{ + if(this.contextLost)return; + + + // if rendering a new stage clear the batchs.. + if(this.__stage !== stage) + { + // TODO make this work + // dont think this is needed any more? + this.__stage = stage; + } + + // update any textures this includes uvs and uploading them to the gpu + PIXI.WebGLRenderer.updateTextures(); + + // update the scene graph + stage.updateTransform(); + + var gl = this.gl; + + // -- Does this need to be set every frame? -- // + gl.colorMask(true, true, true, this.transparent); + gl.viewport(0, 0, this.width, this.height); + + // make sure we are bound to the main frame buffer + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent); + gl.clear(gl.COLOR_BUFFER_BIT); + + // this.projection.x = this.width/2; + //this.projection.y = -this.height/2; + + this.renderDisplayObject( stage, this.projection ); + + // interaction + if(stage.interactive) + { + //need to add some events! + if(!stage._interactiveEventsAdded) + { + stage._interactiveEventsAdded = true; + stage.interactionManager.setTarget(this); + } + } + + /* + //can simulate context loss in Chrome like so: + this.view.onmousedown = function(ev) { + console.dir(this.gl.getSupportedExtensions()); + var ext = ( + gl.getExtension("WEBGL_scompressed_texture_s3tc") + // gl.getExtension("WEBGL_compressed_texture_s3tc") || + // gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") || + // gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc") + ); + console.dir(ext); + var loseCtx = this.gl.getExtension("WEBGL_lose_context"); + console.log("killing context"); + loseCtx.loseContext(); + setTimeout(function() { + console.log("restoring context..."); + loseCtx.restoreContext(); + }.bind(this), 1000); + }.bind(this); + */ +}; + +PIXI.WebGLRenderer.prototype.renderDisplayObject = function(displayObject, projection) +{ + // reset the render session data.. + this.renderSession.drawCount = 0; + this.renderSession.currentBlendMode = 9999; + + this.renderSession.projection = projection; + this.renderSession.offset = this.offset; + + // start the sprite batch + this.spriteBatch.begin(this.renderSession); + + // start the filter manager + this.filterManager.begin(this.renderSession, null); + + // render the scene! + displayObject._renderWebGL(this.renderSession); + + // finish the sprite batch + this.spriteBatch.end(); +}; + +/** + * Updates the textures loaded into this webgl renderer + * + * @static + * @method updateTextures + * @private + */ +PIXI.WebGLRenderer.updateTextures = function() +{ + var i = 0; + + //TODO break this out into a texture manager... + //for (i = 0; i < PIXI.texturesToUpdate.length; i++) + // PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]); + + + for (i=0; i < PIXI.Texture.frameUpdates.length; i++) + PIXI.WebGLRenderer.updateTextureFrame(PIXI.Texture.frameUpdates[i]); + + for (i = 0; i < PIXI.texturesToDestroy.length; i++) + PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]); + + PIXI.texturesToUpdate.length = 0; + PIXI.texturesToDestroy.length = 0; + PIXI.Texture.frameUpdates.length = 0; +}; + +/** + * Destroys a loaded webgl texture + * + * @method destroyTexture + * @param texture {Texture} The texture to update + * @private + */ +PIXI.WebGLRenderer.destroyTexture = function(texture) +{ + //TODO break this out into a texture manager... + + for (var i = texture._glTextures.length - 1; i >= 0; i--) + { + var glTexture = texture._glTextures[i]; + var gl = PIXI.glContexts[i]; + + if(gl && glTexture) + { + gl.deleteTexture(glTexture); + } + } + + texture._glTextures.length = 0; +}; + +PIXI.WebGLRenderer.updateTextureFrame = function(texture) +{ + texture.updateFrame = false; + + // now set the uvs. Figured that the uv data sits with a texture rather than a sprite. + // so uv data is stored on the texture itself + texture._updateWebGLuvs(); +}; + +/** + * resizes the webGL view to the specified width and height + * + * @method resize + * @param width {Number} the new width of the webGL view + * @param height {Number} the new height of the webGL view + */ +PIXI.WebGLRenderer.prototype.resize = function(width, height) +{ + this.width = width; + this.height = height; + + this.view.width = width; + this.view.height = height; + + this.gl.viewport(0, 0, this.width, this.height); + + this.projection.x = this.width/2; + this.projection.y = -this.height/2; +}; + +PIXI.createWebGLTexture = function(texture, gl) +{ + + + if(texture.hasLoaded) + { + texture._glTextures[gl.id] = gl.createTexture(); + + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR ? gl.LINEAR : gl.NEAREST); + + // reguler... + + if(!texture._powerOf2) + { + 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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + } + + gl.bindTexture(gl.TEXTURE_2D, null); + } + + return texture._glTextures[gl.id]; +}; + +PIXI.updateWebGLTexture = function(texture, gl) +{ + if( texture._glTextures[gl.id] ) + { + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR ? gl.LINEAR : gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR ? gl.LINEAR : gl.NEAREST); + + // reguler... + + if(!texture._powerOf2) + { + 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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + } + + gl.bindTexture(gl.TEXTURE_2D, null); + } + +}; + +/** + * Handles a lost webgl context + * + * @method handleContextLost + * @param event {Event} + * @private + */ +PIXI.WebGLRenderer.prototype.handleContextLost = function(event) +{ + event.preventDefault(); + this.contextLost = true; +}; + +/** + * Handles a restored webgl context + * + * @method handleContextRestored + * @param event {Event} + * @private + */ +PIXI.WebGLRenderer.prototype.handleContextRestored = function() +{ + + //try 'experimental-webgl' + try { + this.gl = this.view.getContext('experimental-webgl', this.options); + } catch (e) { + //try 'webgl' + try { + this.gl = this.view.getContext('webgl', this.options); + } catch (e2) { + // fail, not able to get a context + throw new Error(' This browser does not support webGL. Try using the canvas renderer' + this); + } + } + + var gl = this.gl; + gl.id = PIXI.WebGLRenderer.glContextId ++; + + + + // need to set the context... + this.shaderManager.setContext(gl); + this.spriteBatch.setContext(gl); + this.maskManager.setContext(gl); + this.filterManager.setContext(gl); + + + this.renderSession.gl = this.gl; + + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + + gl.enable(gl.BLEND); + gl.colorMask(true, true, true, this.transparent); + + this.gl.viewport(0, 0, this.width, this.height); + + for(var key in PIXI.TextureCache) + { + var texture = PIXI.TextureCache[key].baseTexture; + texture._glTextures = []; + } + + this.contextLost = false; + +}; + +PIXI.WebGLRenderer.glContextId = 0; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.WebGLMaskManager = function(gl) +{ + this.maskStack = []; + this.maskPosition = 0; + + this.setContext(gl); +}; + +PIXI.WebGLMaskManager.prototype.setContext = function(gl) +{ + this.gl = gl; +}; + +PIXI.WebGLMaskManager.prototype.pushMask = function(maskData, renderSession) +{ + var gl = this.gl; + + if(this.maskStack.length === 0) + { + gl.enable(gl.STENCIL_TEST); + gl.stencilFunc(gl.ALWAYS,1,1); + } + + // maskData.visible = false; + + this.maskStack.push(maskData); + + gl.colorMask(false, false, false, true); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); + + PIXI.WebGLGraphics.renderGraphics(maskData, renderSession); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL,0, this.maskStack.length); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); +}; + +PIXI.WebGLMaskManager.prototype.popMask = function(renderSession) +{ + var gl = this.gl; + + var maskData = this.maskStack.pop(); + + if(maskData) + { + gl.colorMask(false, false, false, false); + + //gl.stencilFunc(gl.ALWAYS,1,1); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); + + PIXI.WebGLGraphics.renderGraphics(maskData, renderSession); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL,0,this.maskStack.length); + gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); + } + + if(this.maskStack.length === 0)gl.disable(gl.STENCIL_TEST); +}; +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.WebGLShaderManager = function(gl) +{ + this.setContext(gl); + + // the final one is used for the rendering strips + //this.stripShader = new PIXI.StripShader(gl); +}; + +PIXI.WebGLShaderManager.prototype.setContext = function(gl) +{ + this.gl = gl; + + // the next one is used for rendering primatives + this.primitiveShader = new PIXI.PrimitiveShader(gl); + + // this shader is used for the default sprite rendering + this.defaultShader = new PIXI.PixiShader(gl); + + var shaderProgram = this.defaultShader.program; + + gl.useProgram(shaderProgram); + + gl.enableVertexAttribArray(this.defaultShader.aVertexPosition); + gl.enableVertexAttribArray(this.defaultShader.colorAttribute); + gl.enableVertexAttribArray(this.defaultShader.aTextureCoord); + + +}; + +PIXI.WebGLShaderManager.prototype.activatePrimitiveShader = function() +{ + var gl = this.gl; + + gl.useProgram(this.primitiveShader.program); + + gl.disableVertexAttribArray(this.defaultShader.aVertexPosition); + gl.disableVertexAttribArray(this.defaultShader.colorAttribute); + gl.disableVertexAttribArray(this.defaultShader.aTextureCoord); + + gl.enableVertexAttribArray(this.primitiveShader.aVertexPosition); + gl.enableVertexAttribArray(this.primitiveShader.colorAttribute); +}; + +PIXI.WebGLShaderManager.prototype.deactivatePrimitiveShader = function() +{ + var gl = this.gl; + + gl.useProgram(this.defaultShader.program); + + gl.disableVertexAttribArray(this.primitiveShader.aVertexPosition); + gl.disableVertexAttribArray(this.primitiveShader.colorAttribute); + + gl.enableVertexAttribArray(this.defaultShader.aVertexPosition); + gl.enableVertexAttribArray(this.defaultShader.colorAttribute); + gl.enableVertexAttribArray(this.defaultShader.aTextureCoord); +}; +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * + * Heavily inspired by LibGDX's WebGLSpriteBatch: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java + */ + +PIXI.WebGLSpriteBatch = function(gl) +{ + + + this.size = 2000; + this.vertSize = 6; + + //the total number of floats in our batch + var numVerts = this.size * 4 * this.vertSize; + //the total number of indices in our batch + var numIndices = this.size * 6; + + //vertex data + this.vertices = new Float32Array(numVerts); + //index data + this.indices = new Uint16Array(numIndices); + + this.lastIndexCount = 0; + + for (var i=0, j=0; i < numIndices; i += 6, j += 4) + { + this.indices[i + 0] = j + 0; + this.indices[i + 1] = j + 1; + this.indices[i + 2] = j + 2; + this.indices[i + 3] = j + 0; + this.indices[i + 4] = j + 2; + this.indices[i + 5] = j + 3; + } + + + this.drawing = false; + this.currentBatchSize = 0; + this.currentBaseTexture = null; + + this.setContext(gl); +}; + +PIXI.WebGLSpriteBatch.prototype.setContext = function(gl) +{ + this.gl = gl; + + // create a couple of buffers + this.vertexBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + // 65535 is max index, so 65535 / 6 = 10922. + + + //upload the index data + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); + + this.currentBlendMode = 99999; +}; + +PIXI.WebGLSpriteBatch.prototype.begin = function(renderSession) +{ + this.renderSession = renderSession; + this.shader = this.renderSession.shaderManager.defaultShader; + + this.start(); +}; + +PIXI.WebGLSpriteBatch.prototype.end = function() +{ + this.flush(); +}; + + +PIXI.WebGLSpriteBatch.prototype.render = function(sprite) +{ + // check texture.. + if(sprite.texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size) + { + this.flush(); + this.currentBaseTexture = sprite.texture.baseTexture; + } + + + // check blend mode + if(sprite.blendMode !== this.currentBlendMode) + { + this.setBlendMode(sprite.blendMode); + } + + // get the uvs for the texture + var uvs = sprite._uvs || sprite.texture._uvs; + // if the uvs have not updated then no point rendering just yet! + if(!uvs)return; + + // get the sprites current alpha + var alpha = sprite.worldAlpha; + var tint = sprite.tint; + + var verticies = this.vertices; + + var width = sprite.texture.frame.width; + var height = sprite.texture.frame.height; + + // TODO trim?? + var aX = sprite.anchor.x; + var aY = sprite.anchor.y; + + var w0, w1, h0, h1; + + if (sprite.texture.trimmed) + { + // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. + var trim = sprite.texture.trim; + + w1 = trim.x - aX * trim.realWidth; + w0 = w1 + width; + + h1 = trim.y - aY * trim.realHeight; + h0 = h1 + height; + } + else + { + w0 = (width ) * (1-aX); + w1 = (width ) * -aX; + + h0 = height * (1-aY); + h1 = height * -aY; + } + + var index = this.currentBatchSize * 4 * this.vertSize; + + var worldTransform = sprite.worldTransform; + + var a = worldTransform[0]; + var b = worldTransform[3]; + var c = worldTransform[1]; + var d = worldTransform[4]; + var tx = worldTransform[2]; + var ty = worldTransform[5]; + + // xy + verticies[index++] = a * w1 + c * h1 + tx; + verticies[index++] = d * h1 + b * w1 + ty; + // uv + verticies[index++] = uvs[0]; + verticies[index++] = uvs[1]; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h1 + tx; + verticies[index++] = d * h1 + b * w0 + ty; + // uv + verticies[index++] = uvs[2]; + verticies[index++] = uvs[3]; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h0 + tx; + verticies[index++] = d * h0 + b * w0 + ty; + // uv + verticies[index++] = uvs[4]; + verticies[index++] = uvs[5]; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w1 + c * h0 + tx; + verticies[index++] = d * h0 + b * w1 + ty; + // uv + verticies[index++] = uvs[6]; + verticies[index++] = uvs[7]; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // increment the batchs + this.currentBatchSize++; + + +}; + +PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite) +{ + var texture = tilingSprite.tilingTexture; + + if(texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size) + { + this.flush(); + this.currentBaseTexture = texture.baseTexture; + } + + // check blend mode + if(tilingSprite.blendMode !== this.currentBlendMode) + { + this.setBlendMode(tilingSprite.blendMode); + } + + // set the textures uvs temporarily + // TODO create a seperate texture so that we can tile part of a texture + + if(!tilingSprite._uvs)tilingSprite._uvs = new Float32Array(8); + + var uvs = tilingSprite._uvs; + + var offsetX = tilingSprite.tilePosition.x/texture.baseTexture.width; + var offsetY = tilingSprite.tilePosition.y/texture.baseTexture.height; + + var scaleX = (tilingSprite.width / texture.baseTexture.width) / (tilingSprite.tileScale.x * tilingSprite.tileScaleOffset.x); + var scaleY = (tilingSprite.height / texture.baseTexture.height) / (tilingSprite.tileScale.y * tilingSprite.tileScaleOffset.y); + + uvs[0] = 0 - offsetX; + uvs[1] = 0 - offsetY; + + uvs[2] = (1 * scaleX) - offsetX; + uvs[3] = 0 - offsetY; + + uvs[4] = (1 * scaleX) - offsetX; + uvs[5] = (1 * scaleY) - offsetY; + + uvs[6] = 0 - offsetX; + uvs[7] = (1 *scaleY) - offsetY; + + + // get the tilingSprites current alpha + var alpha = tilingSprite.worldAlpha; + var tint = tilingSprite.tint; + + var verticies = this.vertices; + + var width = tilingSprite.width; + var height = tilingSprite.height; + + // TODO trim?? + var aX = tilingSprite.anchor.x; // - tilingSprite.texture.trim.x + var aY = tilingSprite.anchor.y; //- tilingSprite.texture.trim.y + var w0 = width * (1-aX); + var w1 = width * -aX; + + var h0 = height * (1-aY); + var h1 = height * -aY; + + var index = this.currentBatchSize * 4 * this.vertSize; + + var worldTransform = tilingSprite.worldTransform; + + var a = worldTransform[0]; + var b = worldTransform[3]; + var c = worldTransform[1]; + var d = worldTransform[4]; + var tx = worldTransform[2]; + var ty = worldTransform[5]; + + // xy + verticies[index++] = a * w1 + c * h1 + tx; + verticies[index++] = d * h1 + b * w1 + ty; + // uv + verticies[index++] = uvs[0]; + verticies[index++] = uvs[1]; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h1 + tx; + verticies[index++] = d * h1 + b * w0 + ty; + // uv + verticies[index++] = uvs[2]; + verticies[index++] = uvs[3]; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w0 + c * h0 + tx; + verticies[index++] = d * h0 + b * w0 + ty; + // uv + verticies[index++] = uvs[4]; + verticies[index++] = uvs[5]; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // xy + verticies[index++] = a * w1 + c * h0 + tx; + verticies[index++] = d * h0 + b * w1 + ty; + // uv + verticies[index++] = uvs[6]; + verticies[index++] = uvs[7]; + // color + verticies[index++] = alpha; + verticies[index++] = tint; + + // increment the batchs + this.currentBatchSize++; +}; + +PIXI.WebGLSpriteBatch.prototype.flush = function() +{ + // If the batch is length 0 then return as there is nothing to draw + if (this.currentBatchSize===0)return; + + var gl = this.gl; + + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id] || PIXI.createWebGLTexture(this.currentBaseTexture, gl)); + + // upload the verts to the buffer + var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); + + // now draw those suckas! + gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0); + + // then reset the batch! + this.currentBatchSize = 0; + + // increment the draw count + this.renderSession.drawCount++; +}; + + +PIXI.WebGLSpriteBatch.prototype.stop = function() +{ + this.flush(); +}; + +PIXI.WebGLSpriteBatch.prototype.start = function() +{ + var gl = this.gl; + + // bind the main texture + gl.activeTexture(gl.TEXTURE0); + + // bind the buffers + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // set the projection + var projection = this.renderSession.projection; + gl.uniform2f(this.shader.projectionVector, projection.x, projection.y); + + // set the pointers + var stride = this.vertSize * 4; + gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); + gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4); + gl.vertexAttribPointer(this.shader.colorAttribute, 2, gl.FLOAT, false, stride, 4 * 4); + + // set the blend mode.. + if(this.currentBlendMode !== PIXI.blendModes.NORMAL) + { + this.setBlendMode(PIXI.blendModes.NORMAL); + } +}; + +PIXI.WebGLSpriteBatch.prototype.setBlendMode = function(blendMode) +{ + this.flush(); + + this.currentBlendMode = blendMode; + + var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; + this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); +}; + + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +PIXI.WebGLFilterManager = function(gl, transparent) +{ + this.transparent = transparent; + + this.filterStack = []; + + this.offsetX = 0; + this.offsetY = 0; + + this.setContext(gl); +}; + +// API + +PIXI.WebGLFilterManager.prototype.setContext = function(gl) +{ + this.gl = gl; + this.texturePool = []; + + this.initShaderBuffers(); +}; + +PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer) +{ + this.renderSession = renderSession; + this.defaultShader = renderSession.shaderManager.defaultShader; + + var projection = this.renderSession.projection; + + this.width = projection.x * 2; + this.height = -projection.y * 2; + this.buffer = buffer; +}; + +PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) +{ + var gl = this.gl; + + var projection = this.renderSession.projection; + var offset = this.renderSession.offset; + + + // filter program + // OPTIMISATION - the first filter is free if its a simple color change? + this.filterStack.push(filterBlock); + + var filter = filterBlock.filterPasses[0]; + + this.offsetX += filterBlock.target.filterArea.x; + this.offsetY += filterBlock.target.filterArea.y; + + var texture = this.texturePool.pop(); + if(!texture) + { + texture = new PIXI.FilterTexture(this.gl, this.width, this.height); + } + else + { + texture.resize(this.width, this.height); + } + + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + +// this.getBounds(filterBlock.target); + + filterBlock.target.filterArea = filterBlock.target.getBounds(); + // console.log(filterBlock.target.filterArea) + // console.log(filterBlock.target.filterArea); + // addpadding? + //displayObject.filterArea.x + + var filterArea = filterBlock.target.filterArea; + + var padidng = filter.padding; + filterArea.x -= padidng; + filterArea.y -= padidng; + filterArea.width += padidng * 2; + filterArea.height += padidng * 2; + + // cap filter to screen size.. + if(filterArea.x < 0)filterArea.x = 0; + if(filterArea.width > this.width)filterArea.width = this.width; + if(filterArea.y < 0)filterArea.y = 0; + if(filterArea.height > this.height)filterArea.height = this.height; + + //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); + + //console.log(filterArea) + // set view port + gl.viewport(0, 0, filterArea.width, filterArea.height); + + projection.x = filterArea.width/2; + projection.y = -filterArea.height/2; + + offset.x = -filterArea.x; + offset.y = -filterArea.y; + + //console.log(PIXI.defaultShader.projectionVector) + // update projection + gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); + gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); + //PIXI.primitiveProgram + + gl.colorMask(true, true, true, true); + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + + //filter.texture = texture; + filterBlock._glFilterTexture = texture; + + //console.log("PUSH") +}; + + +PIXI.WebGLFilterManager.prototype.popFilter = function() +{ + var gl = this.gl; + var filterBlock = this.filterStack.pop(); + var filterArea = filterBlock.target.filterArea; + var texture = filterBlock._glFilterTexture; + var projection = this.renderSession.projection; + var offset = this.renderSession.offset; + + if(filterBlock.filterPasses.length > 1) + { + gl.viewport(0, 0, filterArea.width, filterArea.height); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = 0; + this.vertexArray[1] = filterArea.height; + + this.vertexArray[2] = filterArea.width; + this.vertexArray[3] = filterArea.height; + + this.vertexArray[4] = 0; + this.vertexArray[5] = 0; + + this.vertexArray[6] = filterArea.width; + this.vertexArray[7] = 0; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + // nnow set the uvs.. + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + var inputTexture = texture; + var outputTexture = this.texturePool.pop(); + if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.gl, this.width, this.height); + + // need to clear this FBO as it may have some left over elements from a prvious filter. + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.disable(gl.BLEND); + + for (var i = 0; i < filterBlock.filterPasses.length-1; i++) + { + var filterPass = filterBlock.filterPasses[i]; + + gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); + + // draw texture.. + //filterPass.applyFilterPass(filterArea.width, filterArea.height); + this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); + + // swap the textures.. + var temp = inputTexture; + inputTexture = outputTexture; + outputTexture = temp; + } + + gl.enable(gl.BLEND); + + texture = inputTexture; + this.texturePool.push(outputTexture); + } + + var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; + + this.offsetX -= filterArea.x; + this.offsetY -= filterArea.y; + + + var sizeX = this.width; + var sizeY = this.height; + + var offsetX = 0; + var offsetY = 0; + + var buffer = this.buffer; + + // time to render the filters texture to the previous scene + if(this.filterStack.length === 0) + { + gl.colorMask(true, true, true, this.transparent); + } + else + { + var currentFilter = this.filterStack[this.filterStack.length-1]; + filterArea = currentFilter.target.filterArea; + + sizeX = filterArea.width; + sizeY = filterArea.height; + + offsetX = filterArea.x; + offsetY = filterArea.y; + + buffer = currentFilter._glFilterTexture.frameBuffer; + } + + + + // TODO need toremove thease global elements.. + projection.x = sizeX/2; + projection.y = -sizeY/2; + + offset.x = offsetX; + offset.y = offsetY; + + filterArea = filterBlock.target.filterArea; + + var x = filterArea.x-offsetX; + var y = filterArea.y-offsetY; + + // update the buffers.. + // make sure to flip the y! + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + + this.vertexArray[0] = x; + this.vertexArray[1] = y + filterArea.height; + + this.vertexArray[2] = x + filterArea.width; + this.vertexArray[3] = y + filterArea.height; + + this.vertexArray[4] = x; + this.vertexArray[5] = y; + + this.vertexArray[6] = x + filterArea.width; + this.vertexArray[7] = y; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + + this.uvArray[2] = filterArea.width/this.width; + this.uvArray[5] = filterArea.height/this.height; + this.uvArray[6] = filterArea.width/this.width; + this.uvArray[7] = filterArea.height/this.height; + + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); + + gl.viewport(0, 0, sizeX, sizeY); + // bind the buffer + gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); + + // set the blend mode! + //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) + + // set texture + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, texture.texture); + + // apply! + //filter.applyFilterPass(sizeX, sizeY); + this.applyFilterPass(filter, filterArea, sizeX, sizeY); + + // now restore the regular shader.. + gl.useProgram(this.defaultShader.program); + gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); + gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); + + // return the texture to the pool + this.texturePool.push(texture); + filterBlock._glFilterTexture = null; +}; + +PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height) +{ + // use program + var gl = this.gl; + var shader = filter.shaders[gl.id]; + + if(!shader) + { + shader = new PIXI.PixiShader(gl); + + shader.fragmentSrc = filter.fragmentSrc; + shader.uniforms = filter.uniforms; + shader.init(); + + filter.shaders[gl.id] = shader; + } + + // set the shader + gl.useProgram(shader.program); + + gl.uniform2f(shader.projectionVector, width/2, -height/2); + gl.uniform2f(shader.offsetVector, 0,0); + + if(filter.uniforms.dimensions) + { + //console.log(filter.uniforms.dimensions) + filter.uniforms.dimensions.value[0] = this.width;//width; + filter.uniforms.dimensions.value[1] = this.height;//height; + filter.uniforms.dimensions.value[2] = this.vertexArray[0]; + filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; + // console.log(this.vertexArray[5]) + } + + shader.syncUniforms(); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.vertexAttribPointer(shader.colorAttribute, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + + // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + this.renderSession.drawCount++; +}; + +PIXI.WebGLFilterManager.prototype.initShaderBuffers = function() +{ + var gl = this.gl; + + // create some buffers + this.vertexBuffer = gl.createBuffer(); + this.uvBuffer = gl.createBuffer(); + this.colorBuffer = gl.createBuffer(); + this.indexBuffer = gl.createBuffer(); + + + // bind and upload the vertexs.. + // keep a refferance to the vertexFloatData.. + this.vertexArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + this.vertexArray, + gl.STATIC_DRAW); + + + // bind and upload the uv buffer + this.uvArray = new Float32Array([0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + this.uvArray, + gl.STATIC_DRAW); + + this.colorArray = new Float32Array([1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF, + 1.0, 0xFFFFFF]); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + this.colorArray, + gl.STATIC_DRAW); + + // bind and upload the index + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); + gl.bufferData( + gl.ELEMENT_ARRAY_BUFFER, + new Uint16Array([0, 1, 2, 1, 3, 2]), + gl.STATIC_DRAW); +}; + +PIXI.FilterTexture = function(gl, width, height) +{ + this.gl = gl; + + // next time to create a frame buffer and texture + this.frameBuffer = gl.createFramebuffer(); + 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); + gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer ); + + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0); + + this.resize(width, height); +}; + +PIXI.FilterTexture.prototype.clear = function() +{ + var gl = this.gl; + + gl.clearColor(0,0,0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); +}; + +PIXI.FilterTexture.prototype.resize = function(width, height) +{ + if(this.width === width && this.height === height) return; + + this.width = width; + this.height = height; + + var gl = this.gl; + + gl.bindTexture(gl.TEXTURE_2D, this.texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + +}; + +/** + * @author Mat Groves + * + * + */ + +PIXI.CanvasMaskManager = function() +{ + +}; + +PIXI.CanvasMaskManager.prototype.pushMask = function(maskData, context) +{ + context.save(); + + //maskData.visible = false; + // maskData.alpha = 0; + + var cacheAlpha = maskData.alpha; + var transform = maskData.worldTransform; + + context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); + + PIXI.CanvasGraphics.renderGraphicsMask(maskData, context); + + context.clip(); + + maskData.worldAlpha = cacheAlpha; +}; + +PIXI.CanvasMaskManager.prototype.popMask = function(context) +{ + context.restore(); +}; + +/** + * @author Mat Groves + * + * + */ + +PIXI.CanvasTinter = function() +{ + /// this.textureCach +}; + +//PIXI.CanvasTinter.cachTint = true; + + +PIXI.CanvasTinter.getTintedTexture = function(sprite, color) +{ + // + // cach on sprite + // cach on texture + // no cache + + var texture = sprite.texture; + + color = PIXI.CanvasTinter.roundColor(color); + + var stringColor = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + + texture.tintCache = texture.tintCache || {}; + + if(texture.tintCache[stringColor]) return texture.tintCache[stringColor]; + + // clone texture.. + var canvas = PIXI.CanvasTinter.canvas || document.createElement("canvas"); + + //PIXI.CanvasTinter.tintWithPerPixel(texture, stringColor, canvas); + + + PIXI.CanvasTinter.tintMethod(texture, color, canvas); + + if(PIXI.CanvasTinter.convertTintToImage) + { + // is this better? + var tintImage = new Image(); + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the refferance to the canvas + PIXI.CanvasTinter.canvas = null; + + } + + return canvas; +}; + +PIXI.CanvasTinter.tintWithMultiply = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var frame = texture.frame; + + canvas.width = frame.width; + canvas.height = frame.height; + + context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + + context.fillRect(0, 0, frame.width, frame.height); + + context.globalCompositeOperation = "multiply"; + + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); + + context.globalCompositeOperation = "destination-atop"; + + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); +}; + +PIXI.CanvasTinter.tintWithOverlay = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var frame = texture.frame; + + canvas.width = frame.width; + canvas.height = frame.height; + + + + context.globalCompositeOperation = "copy"; + context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); + context.fillRect(0, 0, frame.width, frame.height); + + context.globalCompositeOperation = "destination-atop"; + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); + + + //context.globalCompositeOperation = "copy"; + +}; + + +PIXI.CanvasTinter.tintWithPerPixel = function(texture, color, canvas) +{ + var context = canvas.getContext( "2d" ); + + var frame = texture.frame; + + canvas.width = frame.width; + canvas.height = frame.height; + + context.globalCompositeOperation = "copy"; + context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height); + + var rgbValues = PIXI.hex2rgb(color); + var r = rgbValues[0], g = rgbValues[1], b = rgbValues[2]; + + var pixelData = context.getImageData(0, 0, frame.width, frame.height); + + var pixels = pixelData.data; + + for (var i = 0; i < pixels.length; i += 4) + { + pixels[i+0] *= r; + pixels[i+1] *= g; + pixels[i+2] *= b; + } + + context.putImageData(pixelData, 0, 0); +}; + +PIXI.CanvasTinter.roundColor = function(color) +{ + var step = PIXI.CanvasTinter.cacheStepsPerColorChannel; + + var rgbValues = PIXI.hex2rgb(color); + + rgbValues[0] = Math.min(255, Math.round(rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, Math.round(rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, Math.round(rgbValues[2] / step) * step); + + return PIXI.rgb2hex(rgbValues); +}; + +PIXI.CanvasTinter.cacheStepsPerColorChannel = 8; +PIXI.CanvasTinter.convertTintToImage = false; + +PIXI.CanvasTinter.canUseMultiply = PIXI.canUseNewCanvasBlendModes(); + +PIXI.CanvasTinter.tintMethod = PIXI.CanvasTinter.canUseMultiply ? PIXI.CanvasTinter.tintWithMultiply : PIXI.CanvasTinter.tintWithPerPixel; + + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * the CanvasRenderer draws the stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. + * Dont forget to add the view to your DOM or you will not see anything :) + * + * @class CanvasRenderer + * @constructor + * @param width=0 {Number} the width of the canvas view + * @param height=0 {Number} the height of the canvas view + * @param view {Canvas} the canvas to use as a view, optional + * @param transparent=false {Boolean} the transparency of the render view, default false + */ +PIXI.CanvasRenderer = function(width, height, view, transparent) +{ + PIXI.defaultRenderer = PIXI.defaultRenderer || this; + + this.type = PIXI.CANVAS_RENDERER; + + this.transparent = transparent; + + if(!PIXI.blendModesCanvas) + { + PIXI.blendModesCanvas = []; + + if(PIXI.canUseNewCanvasBlendModes()) + { + PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK??? + PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "multiply"; + PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "screen"; + } + else + { + // this means that the browser does not support the cool new blend modes in canvas "cough" ie "cough" + PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK??? + PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "source-over"; + PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "source-over"; + } + } + + /** + * The width of the canvas view + * + * @property width + * @type Number + * @default 800 + */ + this.width = width || 800; + + /** + * The height of the canvas view + * + * @property height + * @type Number + * @default 600 + */ + this.height = height || 600; + + /** + * The canvas element that the everything is drawn to + * + * @property view + * @type Canvas + */ + this.view = view || document.createElement( "canvas" ); + + /** + * The canvas context that the everything is drawn to + * @property context + * @type Canvas 2d Context + */ + this.context = this.view.getContext( "2d" ); + + //some filter variables + this.smoothProperty = null; + + if("imageSmoothingEnabled" in this.context) + this.smoothProperty = "imageSmoothingEnabled"; + else if("webkitImageSmoothingEnabled" in this.context) + this.smoothProperty = "webkitImageSmoothingEnabled"; + else if("mozImageSmoothingEnabled" in this.context) + this.smoothProperty = "mozImageSmoothingEnabled"; + else if("oImageSmoothingEnabled" in this.context) + this.smoothProperty = "oImageSmoothingEnabled"; + + this.scaleMode = null; + + this.refresh = true; + // hack to enable some hardware acceleration! + //this.view.style["transform"] = "translatez(0)"; + + this.view.width = this.width; + this.view.height = this.height; + this.count = 0; + + this.maskManager = new PIXI.CanvasMaskManager(); + + this.renderSession = {}; + this.renderSession.context = this.context; + this.renderSession.maskManager = this.maskManager; + +}; + +// constructor +PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; + +/** + * Renders the stage to its canvas view + * + * @method render + * @param stage {Stage} the Stage element to be rendered + */ +PIXI.CanvasRenderer.prototype.render = function(stage) +{ + //stage.__childrenAdded = []; + //stage.__childrenRemoved = []; + + // update textures if need be + PIXI.texturesToUpdate.length = 0; + PIXI.texturesToDestroy.length = 0; + + stage.updateTransform(); + + // update the background color + if(this.view.style.backgroundColor !== stage.backgroundColorString && !this.transparent) + this.view.style.backgroundColor = stage.backgroundColorString; + + this.context.setTransform(1,0,0,1,0,0); + this.context.clearRect(0, 0, this.width, this.height); + this.renderDisplayObject(stage); + //as + + // run interaction! + if(stage.interactive) + { + //need to add some events! + if(!stage._interactiveEventsAdded) + { + stage._interactiveEventsAdded = true; + stage.interactionManager.setTarget(this); + } + } + + // remove frame updates.. + if(PIXI.Texture.frameUpdates.length > 0) + { + PIXI.Texture.frameUpdates.length = 0; + } +}; + +/** + * resizes the canvas view to the specified width and height + * + * @method resize + * @param width {Number} the new width of the canvas view + * @param height {Number} the new height of the canvas view + */ +PIXI.CanvasRenderer.prototype.resize = function(width, height) +{ + this.width = width; + this.height = height; + + this.view.width = width; + this.view.height = height; +}; + +/** + * Renders a display object + * + * @method renderDisplayObject + * @param displayObject {DisplayObject} The displayObject to render + * @private + */ +PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context) +{ + // no loger recurrsive! + //var transform; + //var context = this.context; + + this.renderSession.context = context || this.context; + displayObject._renderCanvas(this.renderSession); +}; + +/** + * Renders a flat strip + * + * @method renderStripFlat + * @param strip {Strip} The Strip to render + * @private + */ +PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip) +{ + var context = this.context; + var verticies = strip.verticies; + + var length = verticies.length/2; + this.count++; + + context.beginPath(); + for (var i=1; i < length-2; i++) + { + // draw some triangles! + var index = i*2; + + var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4]; + var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5]; + + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + } + + context.fillStyle = "#FF0000"; + context.fill(); + context.closePath(); +}; + +/** + * Renders a strip + * + * @method renderStrip + * @param strip {Strip} The Strip to render + * @private + */ +PIXI.CanvasRenderer.prototype.renderStrip = function(strip) +{ + var context = this.context; + + // draw triangles!! + var verticies = strip.verticies; + var uvs = strip.uvs; + + var length = verticies.length/2; + this.count++; + + for (var i = 1; i < length-2; i++) + { + // draw some triangles! + var index = i*2; + + var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4]; + var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5]; + + var u0 = uvs[index] * strip.texture.width, u1 = uvs[index+2] * strip.texture.width, u2 = uvs[index+4]* strip.texture.width; + var v0 = uvs[index+1]* strip.texture.height, v1 = uvs[index+3] * strip.texture.height, v2 = uvs[index+5]* strip.texture.height; + + context.save(); + context.beginPath(); + context.moveTo(x0, y0); + context.lineTo(x1, y1); + context.lineTo(x2, y2); + context.closePath(); + + context.clip(); + + // Compute matrix transform + var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2; + var deltaA = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2; + var deltaB = u0*x1 + x0*u2 + u1*x2 - x1*u2 - x0*u1 - u0*x2; + var deltaC = u0*v1*x2 + v0*x1*u2 + x0*u1*v2 - x0*v1*u2 - v0*u1*x2 - u0*x1*v2; + var deltaD = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2; + var deltaE = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2; + var deltaF = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2 - v0*u1*y2 - u0*y1*v2; + + context.transform(deltaA / delta, deltaD / delta, + deltaB / delta, deltaE / delta, + deltaC / delta, deltaF / delta); + + context.drawImage(strip.texture.baseTexture.source, 0, 0); + context.restore(); + } +}; + +PIXI.CanvasBuffer = function(width, height) +{ + this.width = width; + this.height = height; + + this.canvas = document.createElement( "canvas" ); + this.context = this.canvas.getContext( "2d" ); + +// this.context.f + this.canvas.width = width; + this.canvas.height = height; +}; + +PIXI.CanvasBuffer.prototype.clear = function() +{ + this.context.clearRect(0,0, this.width, this.height); +}; + +PIXI.CanvasBuffer.prototype.resize = function(width, height) +{ + this.width = this.canvas.width = width; + this.height = this.canvas.height = height; +}; + + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * A set of functions used by the canvas renderer to draw the primitive graphics data + * + * @class CanvasGraphics + */ +PIXI.CanvasGraphics = function() +{ + +}; + + +/* + * Renders the graphics object + * + * @static + * @private + * @method renderGraphics + * @param graphics {Graphics} + * @param context {Context2D} + */ +PIXI.CanvasGraphics.renderGraphics = function(graphics, context) +{ + var worldAlpha = graphics.worldAlpha; + var color = ''; + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + var points = data.points; + + context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6); + + context.lineWidth = data.lineWidth; + + if(data.type === PIXI.Graphics.POLY) + { + context.beginPath(); + + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + // if the first and last point are the same close the path - much neater :) + if(points[0] === points[points.length-2] && points[1] === points[points.length-1]) + { + context.closePath(); + } + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.stroke(); + } + } + else if(data.type === PIXI.Graphics.RECT) + { + + if(data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fillRect(points[0], points[1], points[2], points[3]); + + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeRect(points[0], points[1], points[2], points[3]); + } + + } + else if(data.type === PIXI.Graphics.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(points[0], points[1], points[2],0,2*Math.PI); + context.closePath(); + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.stroke(); + } + } + else if(data.type === PIXI.Graphics.ELIP) + { + + // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + + var ellipseData = data.points; + + var w = ellipseData[2] * 2; + var h = ellipseData[3] * 2; + + var x = ellipseData[0] - w/2; + var y = ellipseData[1] - h/2; + + context.beginPath(); + + var kappa = 0.5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + + context.closePath(); + + if(data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if(data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.stroke(); + } + } + } +}; + +/* + * Renders a graphics mask + * + * @static + * @private + * @method renderGraphicsMask + * @param graphics {Graphics} + * @param context {Context2D} + */ +PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) +{ + var len = graphics.graphicsData.length; + + if(len === 0) return; + + if(len > 1) + { + len = 1; + window.console.log('Pixi.js warning: masks in canvas can only mask using the first path in the graphics object'); + } + + for (var i = 0; i < 1; i++) + { + var data = graphics.graphicsData[i]; + var points = data.points; + + if(data.type === PIXI.Graphics.POLY) + { + context.beginPath(); + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + // if the first and last point are the same close the path - much neater :) + if(points[0] === points[points.length-2] && points[1] === points[points.length-1]) + { + context.closePath(); + } + + } + else if(data.type === PIXI.Graphics.RECT) + { + context.beginPath(); + context.rect(points[0], points[1], points[2], points[3]); + context.closePath(); + } + else if(data.type === PIXI.Graphics.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(points[0], points[1], points[2],0,2*Math.PI); + context.closePath(); + } + else if(data.type === PIXI.Graphics.ELIP) + { + + // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + var ellipseData = data.points; + + var w = ellipseData[2] * 2; + var h = ellipseData[3] * 2; + + var x = ellipseData[0] - w/2; + var y = ellipseData[1] - h/2; + + context.beginPath(); + + var kappa = 0.5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + context.closePath(); + } + } +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + + +/** + * The Graphics class contains a set of methods that you can use to create primitive shapes and lines. + * It is important to know that with the webGL renderer only simple polys can be filled at this stage + * Complex polys will not be filled. Heres an example of a complex poly: http://www.goodboydigital.com/wp-content/uploads/2013/06/complexPolygon.png + * + * @class Graphics + * @extends DisplayObjectContainer + * @constructor + */ +PIXI.Graphics = function() +{ + PIXI.DisplayObjectContainer.call( this ); + + this.renderable = true; + + /** + * The alpha of the fill of this graphics object + * + * @property fillAlpha + * @type Number + */ + this.fillAlpha = 1; + + /** + * The width of any lines drawn + * + * @property lineWidth + * @type Number + */ + this.lineWidth = 0; + + /** + * The color of any lines drawn + * + * @property lineColor + * @type String + */ + this.lineColor = "black"; + + /** + * Graphics data + * + * @property graphicsData + * @type Array + * @private + */ + this.graphicsData = []; + + this.tint = 0xFFFFFF;// * Math.random(); + + this.blendMode = PIXI.blendModes.NORMAL; + + /** + * Current path + * + * @property currentPath + * @type Object + * @private + */ + this.currentPath = {points:[]}; + + this._webGL = []; + + this.isMask = false; + + this.bounds = null; + + this.boundsPadding = 10; +}; + +// constructor +PIXI.Graphics.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Graphics.prototype.constructor = PIXI.Graphics; + +/** + * If cacheAsBitmap is true the graphics object will then be rendered as if it was a sprite. + * This is useful if your graphics element does not change often as it will speed up the rendering of the object + * It is also usful as the graphics object will always be aliased because it will be rendered using canvas + * Not recommended if you are conastanly redrawing the graphics element. + * + * @property cacheAsBitmap + * @default false + * @type Boolean + * @private + */ +Object.defineProperty(PIXI.Graphics.prototype, "cacheAsBitmap", { + get: function() { + return this._cacheAsBitmap; + }, + set: function(value) { + this._cacheAsBitmap = value; + + if(this._cacheAsBitmap) + { + this._generateCachedSprite(); + } + else + { + this.destroyCachedSprite(); + this.dirty = true; + } + + } +}); + + +/** + * Specifies a line style used for subsequent calls to Graphics methods such as the lineTo() method or the drawCircle() method. + * + * @method lineStyle + * @param lineWidth {Number} width of the line to draw, will update the object's stored style + * @param color {Number} color of the line to draw, will update the object's stored style + * @param alpha {Number} alpha of the line to draw, will update the object's stored style + */ +PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) +{ + if (!this.currentPath.points.length) this.graphicsData.pop(); + + this.lineWidth = lineWidth || 0; + this.lineColor = color || 0; + this.lineAlpha = (arguments.length < 3) ? 1 : alpha; + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; + + this.graphicsData.push(this.currentPath); +}; + +/** + * Moves the current drawing position to (x, y). + * + * @method moveTo + * @param x {Number} the X coord to move to + * @param y {Number} the Y coord to move to + */ +PIXI.Graphics.prototype.moveTo = function(x, y) +{ + if (!this.currentPath.points.length) this.graphicsData.pop(); + + this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY}; + + this.currentPath.points.push(x, y); + + this.graphicsData.push(this.currentPath); +}; + +/** + * Draws a line using the current line style from the current drawing position to (x, y); + * the current drawing position is then set to (x, y). + * + * @method lineTo + * @param x {Number} the X coord to draw to + * @param y {Number} the Y coord to draw to + */ +PIXI.Graphics.prototype.lineTo = function(x, y) +{ + this.currentPath.points.push(x, y); + this.dirty = true; +}; + +/** + * Specifies a simple one-color fill that subsequent calls to other Graphics methods + * (such as lineTo() or drawCircle()) use when drawing. + * + * @method beginFill + * @param color {uint} the color of the fill + * @param alpha {Number} the alpha + */ +PIXI.Graphics.prototype.beginFill = function(color, alpha) +{ + + this.filling = true; + this.fillColor = color || 0; + this.fillAlpha = (arguments.length < 2) ? 1 : alpha; +}; + +/** + * Applies a fill to the lines and shapes that were added since the last call to the beginFill() method. + * + * @method endFill + */ +PIXI.Graphics.prototype.endFill = function() +{ + this.filling = false; + this.fillColor = null; + this.fillAlpha = 1; +}; + +/** + * @method drawRect + * + * @param x {Number} The X coord of the top-left of the rectangle + * @param y {Number} The Y coord of the top-left of the rectangle + * @param width {Number} The width of the rectangle + * @param height {Number} The height of the rectangle + */ +PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) +{ + if (!this.currentPath.points.length) this.graphicsData.pop(); + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + points:[x, y, width, height], type:PIXI.Graphics.RECT}; + + this.graphicsData.push(this.currentPath); + this.dirty = true; +}; + +/** + * Draws a circle. + * + * @method drawCircle + * @param x {Number} The X coord of the center of the circle + * @param y {Number} The Y coord of the center of the circle + * @param radius {Number} The radius of the circle + */ +PIXI.Graphics.prototype.drawCircle = function( x, y, radius) +{ + + if (!this.currentPath.points.length) this.graphicsData.pop(); + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + points:[x, y, radius, radius], type:PIXI.Graphics.CIRC}; + + this.graphicsData.push(this.currentPath); + this.dirty = true; +}; + +/** + * Draws an ellipse. + * + * @method drawEllipse + * @param x {Number} + * @param y {Number} + * @param width {Number} + * @param height {Number} + */ +PIXI.Graphics.prototype.drawEllipse = function( x, y, width, height) +{ + + if (!this.currentPath.points.length) this.graphicsData.pop(); + + this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha, + fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, + points:[x, y, width, height], type:PIXI.Graphics.ELIP}; + + this.graphicsData.push(this.currentPath); + this.dirty = true; +}; + +/** + * Clears the graphics that were drawn to this Graphics object, and resets fill and line style settings. + * + * @method clear + */ +PIXI.Graphics.prototype.clear = function() +{ + this.lineWidth = 0; + this.filling = false; + + this.dirty = true; + this.clearDirty = true; + this.graphicsData = []; + + this.bounds = null; //new PIXI.Rectangle(); +}; + +/** + * Useful function that returns a texture of the graphics object that can then be used to create sprites + * This can be quite useful if your geometry is complicated and needs to be reused multiple times. + * + * @method generateTexture + * @return {Texture} a texture of the graphics object + */ +PIXI.Graphics.prototype.generateTexture = function() +{ + var bounds = this.getBounds(); + + var canvasBuffer = new PIXI.CanvasBuffer(bounds.width, bounds.height); + var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + + canvasBuffer.context.translate(-bounds.x,-bounds.y); + + PIXI.CanvasGraphics.renderGraphics(this, canvasBuffer.context); + + return texture; +}; + +PIXI.Graphics.prototype._renderWebGL = function(renderSession) +{ + // if the sprite is not visible or the alpha is 0 then no need to render this element + if(this.visible === false || this.alpha === 0 || this.isMask === true)return; + + if(this._cacheAsBitmap) + { + + if(this.dirty) + { + this._generateCachedSprite(); + // we will also need to update the texture on the gpu too! + PIXI.updateWebGLTexture(this._cachedSprite.texture.baseTexture, renderSession.gl); + + this.dirty = false; + } + + PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); + + return; + } + else + { + renderSession.spriteBatch.stop(); + + if(this._mask)renderSession.maskManager.pushMask(this.mask, renderSession); + if(this._filters)renderSession.filterManager.pushFilter(this._filterBlock); + + // check blend mode + if(this.blendMode !== renderSession.spriteBatch.currentBlendMode) + { + this.spriteBatch.currentBlendMode = this.blendMode; + var blendModeWebGL = PIXI.blendModesWebGL[renderSession.spriteBatch.currentBlendMode]; + this.spriteBatch.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); + } + + PIXI.WebGLGraphics.renderGraphics(this, renderSession); + + // only rende rif it has children! + if(this.children.length) + { + renderSession.spriteBatch.start(); + + // simple render children! + for(var i=0, j=this.children.length; i maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + return bounds; +}; + +PIXI.Graphics.prototype.updateBounds = function() +{ + + var minX = Infinity; + var maxX = -Infinity; + + var minY = Infinity; + var maxY = -Infinity; + + var points, x, y; + + for (var i = 0; i < this.graphicsData.length; i++) { + var data = this.graphicsData[i]; + var type = data.type; + var lineWidth = data.lineWidth; + + points = data.points; + + if(type === PIXI.Graphics.RECT) + { + x = points.x - lineWidth/2; + y = points.y - lineWidth/2; + var width = points.width + lineWidth; + var height = points.height + lineWidth; + + minX = x < minX ? x : minX; + maxX = x + width > maxX ? x + width : maxX; + + minY = y < minY ? x : minY; + maxY = y + height > maxY ? y + height : maxY; + } + else if(type === PIXI.Graphics.CIRC || type === PIXI.Graphics.ELIP) + { + x = points.x; + y = points.y; + var radius = points.radius + lineWidth/2; + + minX = x - radius < minX ? x - radius : minX; + maxX = x + radius > maxX ? x + radius : maxX; + + minY = y - radius < minY ? y - radius : minY; + maxY = y + radius > maxY ? y + radius : maxY; + } + else + { + // POLY + for (var j = 0; j < points.length; j+=2) + { + + x = points[j]; + y = points[j+1]; + minX = x-lineWidth < minX ? x-lineWidth : minX; + maxX = x+lineWidth > maxX ? x+lineWidth : maxX; + + minY = y-lineWidth < minY ? y-lineWidth : minY; + maxY = y+lineWidth > maxY ? y+lineWidth : maxY; + } + } + } + + var padding = this.boundsPadding; + this.bounds = new PIXI.Rectangle(minX - padding, minY - padding, (maxX - minX) + padding * 2, (maxY - minY) + padding * 2); +}; + +PIXI.Graphics.prototype._generateCachedSprite = function() +{ + var bounds = this.getBounds(); + + if(!this._cachedSprite) + { + var canvasBuffer = new PIXI.CanvasBuffer(bounds.width, bounds.height); + var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + + this._cachedSprite = new PIXI.Sprite(texture); + this._cachedSprite.buffer = canvasBuffer; + + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.buffer.resize(bounds.width, bounds.height); + } + + // leverage the anchor to account for the offest of the element + this._cachedSprite.anchor.x = -( bounds.x / bounds.width ); + this._cachedSprite.anchor.y = -( bounds.y / bounds.height ); + + // this._cachedSprite.buffer.context.save(); + this._cachedSprite.buffer.context.translate(-bounds.x,-bounds.y); + + PIXI.CanvasGraphics.renderGraphics(this, this._cachedSprite.buffer.context); + // this._cachedSprite.buffer.context.restore(); +}; + +PIXI.Graphics.prototype.destroyCachedSprite = function() +{ + this._cachedSprite.texture.destroy(true); + + // let the gc collect the unused sprite + // TODO could be object pooled! + this._cachedSprite = null; +}; + + +// SOME TYPES: +PIXI.Graphics.POLY = 0; +PIXI.Graphics.RECT = 1; +PIXI.Graphics.CIRC = 2; +PIXI.Graphics.ELIP = 3; + +/** + * @author Mat Groves http://matgroves.com/ + */ + +PIXI.Strip = function(texture, width, height) +{ + PIXI.DisplayObjectContainer.call( this ); + this.texture = texture; + this.blendMode = PIXI.blendModes.NORMAL; + + try + { + this.uvs = new Float32Array([0, 1, + 1, 1, + 1, 0, 0,1]); + + this.verticies = new Float32Array([0, 0, + 0,0, + 0,0, 0, + 0, 0]); + + this.colors = new Float32Array([1, 1, 1, 1]); + + this.indices = new Uint16Array([0, 1, 2, 3]); + } + catch(error) + { + this.uvs = [0, 1, + 1, 1, + 1, 0, 0,1]; + + this.verticies = [0, 0, + 0,0, + 0,0, 0, + 0, 0]; + + this.colors = [1, 1, 1, 1]; + + this.indices = [0, 1, 2, 3]; + } + + + /* + this.uvs = new Float32Array() + this.verticies = new Float32Array() + this.colors = new Float32Array() + this.indices = new Uint16Array() + */ + this.width = width; + this.height = height; + + // load the texture! + if(texture.baseTexture.hasLoaded) + { + this.width = this.texture.frame.width; + this.height = this.texture.frame.height; + this.updateFrame = true; + } + else + { + this.onTextureUpdateBind = this.onTextureUpdate.bind(this); + this.texture.addEventListener( 'update', this.onTextureUpdateBind ); + } + + this.renderable = true; +}; + +// constructor +PIXI.Strip.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); +PIXI.Strip.prototype.constructor = PIXI.Strip; + +PIXI.Strip.prototype.setTexture = function(texture) +{ + //TODO SET THE TEXTURES + //TODO VISIBILITY + + // stop current texture + this.texture = texture; + this.width = texture.frame.width; + this.height = texture.frame.height; + this.updateFrame = true; +}; + +PIXI.Strip.prototype.onTextureUpdate = function() +{ + this.updateFrame = true; +}; +// some helper functions.. + +/** + * @author Mat Groves http://matgroves.com/ + */ + +PIXI.Rope = function(texture, points) +{ + PIXI.Strip.call( this, texture ); + this.points = points; + + try + { + this.verticies = new Float32Array(points.length * 4); + this.uvs = new Float32Array(points.length * 4); + this.colors = new Float32Array(points.length * 2); + this.indices = new Uint16Array(points.length * 2); + } + catch(error) + { + this.verticies = new Array(points.length * 4); + this.uvs = new Array(points.length * 4); + this.colors = new Array(points.length * 2); + this.indices = new Array(points.length * 2); + } + + this.refresh(); +}; + + +// constructor +PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); +PIXI.Rope.prototype.constructor = PIXI.Rope; + +PIXI.Rope.prototype.refresh = function() +{ + var points = this.points; + if(points.length < 1) return; + + var uvs = this.uvs; + + var lastPoint = points[0]; + var indices = this.indices; + var colors = this.colors; + + this.count-=0.2; + + + uvs[0] = 0; + uvs[1] = 1; + uvs[2] = 0; + uvs[3] = 1; + + colors[0] = 1; + colors[1] = 1; + + indices[0] = 0; + indices[1] = 1; + + var total = points.length, + point, index, amount; + + for (var i = 1; i < total; i++) + { + + point = points[i]; + index = i * 4; + // time to do some smart drawing! + amount = i / (total-1); + + if(i%2) + { + uvs[index] = amount; + uvs[index+1] = 0; + + uvs[index+2] = amount; + uvs[index+3] = 1; + + } + else + { + uvs[index] = amount; + uvs[index+1] = 0; + + uvs[index+2] = amount; + uvs[index+3] = 1; + } + + index = i * 2; + colors[index] = 1; + colors[index+1] = 1; + + index = i * 2; + indices[index] = index; + indices[index + 1] = index + 1; + + lastPoint = point; + } +}; + +PIXI.Rope.prototype.updateTransform = function() +{ + + var points = this.points; + if(points.length < 1)return; + + var lastPoint = points[0]; + var nextPoint; + var perp = {x:0, y:0}; + + this.count-=0.2; + + var verticies = this.verticies; + verticies[0] = lastPoint.x + perp.x; + verticies[1] = lastPoint.y + perp.y; //+ 200 + verticies[2] = lastPoint.x - perp.x; + verticies[3] = lastPoint.y - perp.y;//+200 + // time to do some smart drawing! + + var total = points.length, + point, index, ratio, perpLength, num; + + for (var i = 1; i < total; i++) + { + point = points[i]; + index = i * 4; + + if(i < points.length-1) + { + nextPoint = points[i+1]; + } + else + { + nextPoint = point; + } + + perp.y = -(nextPoint.x - lastPoint.x); + perp.x = nextPoint.y - lastPoint.y; + + ratio = (1 - (i / (total-1))) * 10; + + if(ratio > 1) ratio = 1; + + perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); + num = this.texture.height / 2; //(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; + perp.x /= perpLength; + perp.y /= perpLength; + + perp.x *= num; + perp.y *= num; + + verticies[index] = point.x + perp.x; + verticies[index+1] = point.y + perp.y; + verticies[index+2] = point.x - perp.x; + verticies[index+3] = point.y - perp.y; + + lastPoint = point; + } + + PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); +}; + +PIXI.Rope.prototype.setTexture = function(texture) +{ + // stop current texture + this.texture = texture; + this.updateFrame = true; +}; + +/** + * @author Mat Groves http://matgroves.com/ + */ + +/** + * A tiling sprite is a fast way of rendering a tiling image + * + * @class TilingSprite + * @extends DisplayObjectContainer + * @constructor + * @param texture {Texture} the texture of the tiling sprite + * @param width {Number} the width of the tiling sprite + * @param height {Number} the height of the tiling sprite + */ +PIXI.TilingSprite = function(texture, width, height) +{ + PIXI.Sprite.call( this, texture); + + this.width = width || 100; + this.height = height || 100; + + /** + * The scaling of the image that is being tiled + * + * @property tileScale + * @type Point + */ + this.tileScale = new PIXI.Point(1,1); + + + this.tileScaleOffset = new PIXI.Point(1,1); + + /** + * The offset position of the image that is being tiled + * + * @property tilePosition + * @type Point + */ + this.tilePosition = new PIXI.Point(0,0); + + this.renderable = true; + + this.tint = 0xFFFFFF; + this.blendMode = PIXI.blendModes.NORMAL; +}; + +// constructor +PIXI.TilingSprite.prototype = Object.create( PIXI.Sprite.prototype ); +PIXI.TilingSprite.prototype.constructor = PIXI.TilingSprite; + + +/** + * The width of the sprite, setting this will actually modify the scale to acheive the value set + * + * @property width + * @type Number + */ +Object.defineProperty(PIXI.TilingSprite.prototype, 'width', { + get: function() { + return this._width; + }, + set: function(value) { + + this._width = value; + } +}); + +/** + * The height of the TilingSprite, setting this will actually modify the scale to acheive the value set + * + * @property height + * @type Number + */ +Object.defineProperty(PIXI.TilingSprite.prototype, 'height', { + get: function() { + return this._height; + }, + set: function(value) { + this._height = value; + } +}); + +PIXI.TilingSprite.prototype.onTextureUpdate = function() +{ + // so if _width is 0 then width was not set.. + //console.log("HI MUM") + + + this.updateFrame = true; +}; + +PIXI.TilingSprite.prototype._renderWebGL = function(renderSession) +{ + + if(this.visible === false || this.alpha === 0)return; + + var i,j; + + if(this.mask || this.filters) + { + if(this.mask) + { + renderSession.spriteBatch.stop(); + renderSession.maskManager.pushMask(this.mask, renderSession.projection); + renderSession.spriteBatch.start(); + } + + if(this.filters) + { + renderSession.spriteBatch.flush(); + renderSession.filterManager.pushFilter(this._filterBlock); + } + + if(!this.tilingTexture)this.generateTilingTexture(true); + else renderSession.spriteBatch.renderTilingSprite(this); + + // simple render children! + for(i=0,j=this.children.length; i maxX ? x1 : maxX; + maxX = x2 > maxX ? x2 : maxX; + maxX = x3 > maxX ? x3 : maxX; + maxX = x4 > maxX ? x4 : maxX; + + maxY = y1 > maxY ? y1 : maxY; + maxY = y2 > maxY ? y2 : maxY; + maxY = y3 > maxY ? y3 : maxY; + maxY = y4 > maxY ? y4 : maxY; + + var bounds = this._bounds; + + bounds.x = minX; + bounds.width = maxX - minX; + + bounds.y = minY; + bounds.height = maxY - minY; + + // store a refferance so that if this function gets called again in the render cycle we do not have to recacalculate + this._currentBounds = bounds; + + return bounds; +}; + + +PIXI.TilingSprite.prototype.generateTilingTexture = function(forcePowerOfTwo) +{ + var texture = this.texture; + + if(!texture.baseTexture.hasLoaded)return; + + var baseTexture = texture.baseTexture; + var frame = texture.frame; + + var targetWidth, targetHeight; + + // check that the frame is the same size as the base texture. + + var isFrame = frame.width !== baseTexture.width || frame.height !== baseTexture.height; + + this.tilingTexture = texture; + + var newTextureRequired = false; + + if(!forcePowerOfTwo) + { + if(isFrame) + { + targetWidth = frame.width; + targetHeight = frame.height; + + newTextureRequired = true; + } + } + else + { + targetWidth = PIXI.getNextPowerOfTwo(texture.frame.width); + targetHeight = PIXI.getNextPowerOfTwo(texture.frame.height); + + if(frame.width !== targetWidth && frame.height !== targetHeight)newTextureRequired = true; + } + + if(newTextureRequired) + { + var canvasBuffer = new PIXI.CanvasBuffer(targetWidth, targetHeight); + + canvasBuffer.context.drawImage(texture.baseTexture.source, + frame.x, + frame.y, + frame.width, + frame.height, + 0, + 0, + targetWidth, + targetHeight); + + this.tilingTexture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); + + this.tileScaleOffset.x = frame.width / targetWidth; + this.tileScaleOffset.y = frame.height / targetHeight; + } + + + this.tilingTexture.baseTexture._powerOf2 = true; +}; +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + * based on pixi impact spine implementation made by Eemeli Kelokorpi (@ekelokorpi) https://github.com/ekelokorpi + * + * Awesome JS run time provided by EsotericSoftware + * https://github.com/EsotericSoftware/spine-runtimes + * + */ + +/* + * Awesome JS run time provided by EsotericSoftware + * + * https://github.com/EsotericSoftware/spine-runtimes + * + */ + +var spine = {}; + +spine.BoneData = function (name, parent) { + this.name = name; + this.parent = parent; +}; +spine.BoneData.prototype = { + length: 0, + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1 +}; + +spine.SlotData = function (name, boneData) { + this.name = name; + this.boneData = boneData; +}; +spine.SlotData.prototype = { + r: 1, g: 1, b: 1, a: 1, + attachmentName: null +}; + +spine.Bone = function (boneData, parent) { + this.data = boneData; + this.parent = parent; + this.setToSetupPose(); +}; +spine.Bone.yDown = false; +spine.Bone.prototype = { + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1, + m00: 0, m01: 0, worldX: 0, // a b x + m10: 0, m11: 0, worldY: 0, // c d y + worldRotation: 0, + worldScaleX: 1, worldScaleY: 1, + updateWorldTransform: function (flipX, flipY) { + var parent = this.parent; + if (parent != null) { + this.worldX = this.x * parent.m00 + this.y * parent.m01 + parent.worldX; + this.worldY = this.x * parent.m10 + this.y * parent.m11 + parent.worldY; + this.worldScaleX = parent.worldScaleX * this.scaleX; + this.worldScaleY = parent.worldScaleY * this.scaleY; + this.worldRotation = parent.worldRotation + this.rotation; + } else { + this.worldX = this.x; + this.worldY = this.y; + this.worldScaleX = this.scaleX; + this.worldScaleY = this.scaleY; + this.worldRotation = this.rotation; + } + var radians = this.worldRotation * Math.PI / 180; + var cos = Math.cos(radians); + var sin = Math.sin(radians); + this.m00 = cos * this.worldScaleX; + this.m10 = sin * this.worldScaleX; + this.m01 = -sin * this.worldScaleY; + this.m11 = cos * this.worldScaleY; + if (flipX) { + this.m00 = -this.m00; + this.m01 = -this.m01; + } + if (flipY) { + this.m10 = -this.m10; + this.m11 = -this.m11; + } + if (spine.Bone.yDown) { + this.m10 = -this.m10; + this.m11 = -this.m11; + } + }, + setToSetupPose: function () { + var data = this.data; + this.x = data.x; + this.y = data.y; + this.rotation = data.rotation; + this.scaleX = data.scaleX; + this.scaleY = data.scaleY; + } +}; + +spine.Slot = function (slotData, skeleton, bone) { + this.data = slotData; + this.skeleton = skeleton; + this.bone = bone; + this.setToSetupPose(); +}; +spine.Slot.prototype = { + r: 1, g: 1, b: 1, a: 1, + _attachmentTime: 0, + attachment: null, + setAttachment: function (attachment) { + this.attachment = attachment; + this._attachmentTime = this.skeleton.time; + }, + setAttachmentTime: function (time) { + this._attachmentTime = this.skeleton.time - time; + }, + getAttachmentTime: function () { + return this.skeleton.time - this._attachmentTime; + }, + setToSetupPose: function () { + var data = this.data; + this.r = data.r; + this.g = data.g; + this.b = data.b; + this.a = data.a; + + var slotDatas = this.skeleton.data.slots; + for (var i = 0, n = slotDatas.length; i < n; i++) { + if (slotDatas[i] == data) { + this.setAttachment(!data.attachmentName ? null : this.skeleton.getAttachmentBySlotIndex(i, data.attachmentName)); + break; + } + } + } +}; + +spine.Skin = function (name) { + this.name = name; + this.attachments = {}; +}; +spine.Skin.prototype = { + addAttachment: function (slotIndex, name, attachment) { + this.attachments[slotIndex + ":" + name] = attachment; + }, + getAttachment: function (slotIndex, name) { + return this.attachments[slotIndex + ":" + name]; + }, + _attachAll: function (skeleton, oldSkin) { + for (var key in oldSkin.attachments) { + var colon = key.indexOf(":"); + var slotIndex = parseInt(key.substring(0, colon), 10); + var name = key.substring(colon + 1); + var slot = skeleton.slots[slotIndex]; + if (slot.attachment && slot.attachment.name == name) { + var attachment = this.getAttachment(slotIndex, name); + if (attachment) slot.setAttachment(attachment); + } + } + } +}; + +spine.Animation = function (name, timelines, duration) { + this.name = name; + this.timelines = timelines; + this.duration = duration; +}; +spine.Animation.prototype = { + apply: function (skeleton, time, loop) { + if (loop && this.duration) time %= this.duration; + var timelines = this.timelines; + for (var i = 0, n = timelines.length; i < n; i++) + timelines[i].apply(skeleton, time, 1); + }, + mix: function (skeleton, time, loop, alpha) { + if (loop && this.duration) time %= this.duration; + var timelines = this.timelines; + for (var i = 0, n = timelines.length; i < n; i++) + timelines[i].apply(skeleton, time, alpha); + } +}; + +spine.binarySearch = function (values, target, step) { + var low = 0; + var high = Math.floor(values.length / step) - 2; + if (!high) return step; + var current = high >>> 1; + while (true) { + if (values[(current + 1) * step] <= target) + low = current + 1; + else + high = current; + if (low == high) return (low + 1) * step; + current = (low + high) >>> 1; + } +}; +spine.linearSearch = function (values, target, step) { + for (var i = 0, last = values.length - step; i <= last; i += step) + if (values[i] > target) return i; + return -1; +}; + +spine.Curves = function (frameCount) { + this.curves = []; // dfx, dfy, ddfx, ddfy, dddfx, dddfy, ... + this.curves.length = (frameCount - 1) * 6; +}; +spine.Curves.prototype = { + setLinear: function (frameIndex) { + this.curves[frameIndex * 6] = 0/*LINEAR*/; + }, + setStepped: function (frameIndex) { + this.curves[frameIndex * 6] = -1/*STEPPED*/; + }, + /** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. + * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of + * the difference between the keyframe's values. */ + setCurve: function (frameIndex, cx1, cy1, cx2, cy2) { + var subdiv_step = 1 / 10/*BEZIER_SEGMENTS*/; + var subdiv_step2 = subdiv_step * subdiv_step; + var subdiv_step3 = subdiv_step2 * subdiv_step; + var pre1 = 3 * subdiv_step; + var pre2 = 3 * subdiv_step2; + var pre4 = 6 * subdiv_step2; + var pre5 = 6 * subdiv_step3; + var tmp1x = -cx1 * 2 + cx2; + var tmp1y = -cy1 * 2 + cy2; + var tmp2x = (cx1 - cx2) * 3 + 1; + var tmp2y = (cy1 - cy2) * 3 + 1; + var i = frameIndex * 6; + var curves = this.curves; + curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3; + curves[i + 1] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3; + curves[i + 2] = tmp1x * pre4 + tmp2x * pre5; + curves[i + 3] = tmp1y * pre4 + tmp2y * pre5; + curves[i + 4] = tmp2x * pre5; + curves[i + 5] = tmp2y * pre5; + }, + getCurvePercent: function (frameIndex, percent) { + percent = percent < 0 ? 0 : (percent > 1 ? 1 : percent); + var curveIndex = frameIndex * 6; + var curves = this.curves; + var dfx = curves[curveIndex]; + if (!dfx/*LINEAR*/) return percent; + if (dfx == -1/*STEPPED*/) return 0; + var dfy = curves[curveIndex + 1]; + var ddfx = curves[curveIndex + 2]; + var ddfy = curves[curveIndex + 3]; + var dddfx = curves[curveIndex + 4]; + var dddfy = curves[curveIndex + 5]; + var x = dfx, y = dfy; + var i = 10/*BEZIER_SEGMENTS*/ - 2; + while (true) { + if (x >= percent) { + var lastX = x - dfx; + var lastY = y - dfy; + return lastY + (y - lastY) * (percent - lastX) / (x - lastX); + } + if (!i) break; + i--; + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + x += dfx; + y += dfy; + } + return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. + } +}; + +spine.RotateTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, angle, ... + this.frames.length = frameCount * 2; +}; +spine.RotateTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, angle) { + frameIndex *= 2; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = angle; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames, + amount; + + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 2]) { // Time is after last frame. + amount = bone.data.rotation + frames[frames.length - 1] - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 2); + var lastFrameValue = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 2/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 2 - 1, percent); + + amount = frames[frameIndex + 1/*FRAME_VALUE*/] - lastFrameValue; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + amount = bone.data.rotation + (lastFrameValue + amount * percent) - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + } +}; + +spine.TranslateTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, x, y, ... + this.frames.length = frameCount * 3; +}; +spine.TranslateTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = x; + this.frames[frameIndex + 2] = y; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.x += (bone.data.x + frames[frames.length - 2] - bone.x) * alpha; + bone.y += (bone.data.y + frames[frames.length - 1] - bone.y) * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 3); + var lastFrameX = frames[frameIndex - 2]; + var lastFrameY = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.x) * alpha; + bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.y) * alpha; + } +}; + +spine.ScaleTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, x, y, ... + this.frames.length = frameCount * 3; +}; +spine.ScaleTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = x; + this.frames[frameIndex + 2] = y; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 3); + var lastFrameX = frames[frameIndex - 2]; + var lastFrameY = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + bone.scaleX += (bone.data.scaleX - 1 + lastFrameX + (frames[frameIndex + 1/*FRAME_X*/] - lastFrameX) * percent - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY - 1 + lastFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - lastFrameY) * percent - bone.scaleY) * alpha; + } +}; + +spine.ColorTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, r, g, b, a, ... + this.frames.length = frameCount * 5; +}; +spine.ColorTimeline.prototype = { + slotIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 5; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = r; + this.frames[frameIndex + 2] = g; + this.frames[frameIndex + 3] = b; + this.frames[frameIndex + 4] = a; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var slot = skeleton.slots[this.slotIndex]; + + if (time >= frames[frames.length - 5]) { // Time is after last frame. + var i = frames.length - 1; + slot.r = frames[i - 3]; + slot.g = frames[i - 2]; + slot.b = frames[i - 1]; + slot.a = frames[i]; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex = spine.binarySearch(frames, time, 5); + var lastFrameR = frames[frameIndex - 4]; + var lastFrameG = frames[frameIndex - 3]; + var lastFrameB = frames[frameIndex - 2]; + var lastFrameA = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 5/*LAST_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 5 - 1, percent); + + var r = lastFrameR + (frames[frameIndex + 1/*FRAME_R*/] - lastFrameR) * percent; + var g = lastFrameG + (frames[frameIndex + 2/*FRAME_G*/] - lastFrameG) * percent; + var b = lastFrameB + (frames[frameIndex + 3/*FRAME_B*/] - lastFrameB) * percent; + var a = lastFrameA + (frames[frameIndex + 4/*FRAME_A*/] - lastFrameA) * percent; + if (alpha < 1) { + slot.r += (r - slot.r) * alpha; + slot.g += (g - slot.g) * alpha; + slot.b += (b - slot.b) * alpha; + slot.a += (a - slot.a) * alpha; + } else { + slot.r = r; + slot.g = g; + slot.b = b; + slot.a = a; + } + } +}; + +spine.AttachmentTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, ... + this.frames.length = frameCount; + this.attachmentNames = []; // time, ... + this.attachmentNames.length = frameCount; +}; +spine.AttachmentTimeline.prototype = { + slotIndex: 0, + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, attachmentName) { + this.frames[frameIndex] = time; + this.attachmentNames[frameIndex] = attachmentName; + }, + apply: function (skeleton, time, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var frameIndex; + if (time >= frames[frames.length - 1]) // Time is after last frame. + frameIndex = frames.length - 1; + else + frameIndex = spine.binarySearch(frames, time, 1) - 1; + + var attachmentName = this.attachmentNames[frameIndex]; + skeleton.slots[this.slotIndex].setAttachment(!attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); + } +}; + +spine.SkeletonData = function () { + this.bones = []; + this.slots = []; + this.skins = []; + this.animations = []; +}; +spine.SkeletonData.prototype = { + defaultSkin: null, + /** @return May be null. */ + findBone: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].name == boneName) return bones[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findBoneIndex: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].name == boneName) return i; + return -1; + }, + /** @return May be null. */ + findSlot: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + if (slots[i].name == slotName) return slot[i]; + } + return null; + }, + /** @return -1 if the bone was not found. */ + findSlotIndex: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].name == slotName) return i; + return -1; + }, + /** @return May be null. */ + findSkin: function (skinName) { + var skins = this.skins; + for (var i = 0, n = skins.length; i < n; i++) + if (skins[i].name == skinName) return skins[i]; + return null; + }, + /** @return May be null. */ + findAnimation: function (animationName) { + var animations = this.animations; + for (var i = 0, n = animations.length; i < n; i++) + if (animations[i].name == animationName) return animations[i]; + return null; + } +}; + +spine.Skeleton = function (skeletonData) { + this.data = skeletonData; + + this.bones = []; + for (var i = 0, n = skeletonData.bones.length; i < n; i++) { + var boneData = skeletonData.bones[i]; + var parent = !boneData.parent ? null : this.bones[skeletonData.bones.indexOf(boneData.parent)]; + this.bones.push(new spine.Bone(boneData, parent)); + } + + this.slots = []; + this.drawOrder = []; + for (i = 0, n = skeletonData.slots.length; i < n; i++) { + var slotData = skeletonData.slots[i]; + var bone = this.bones[skeletonData.bones.indexOf(slotData.boneData)]; + var slot = new spine.Slot(slotData, this, bone); + this.slots.push(slot); + this.drawOrder.push(slot); + } +}; +spine.Skeleton.prototype = { + x: 0, y: 0, + skin: null, + r: 1, g: 1, b: 1, a: 1, + time: 0, + flipX: false, flipY: false, + /** Updates the world transform for each bone. */ + updateWorldTransform: function () { + var flipX = this.flipX; + var flipY = this.flipY; + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + bones[i].updateWorldTransform(flipX, flipY); + }, + /** Sets the bones and slots to their setup pose values. */ + setToSetupPose: function () { + this.setBonesToSetupPose(); + this.setSlotsToSetupPose(); + }, + setBonesToSetupPose: function () { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + bones[i].setToSetupPose(); + }, + setSlotsToSetupPose: function () { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + slots[i].setToSetupPose(i); + }, + /** @return May return null. */ + getRootBone: function () { + return this.bones.length ? this.bones[0] : null; + }, + /** @return May be null. */ + findBone: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].data.name == boneName) return bones[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findBoneIndex: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].data.name == boneName) return i; + return -1; + }, + /** @return May be null. */ + findSlot: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].data.name == slotName) return slots[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findSlotIndex: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].data.name == slotName) return i; + return -1; + }, + setSkinByName: function (skinName) { + var skin = this.data.findSkin(skinName); + if (!skin) throw "Skin not found: " + skinName; + this.setSkin(skin); + }, + /** Sets the skin used to look up attachments not found in the {@link SkeletonData#getDefaultSkin() default skin}. Attachments + * from the new skin are attached if the corresponding attachment from the old skin was attached. + * @param newSkin May be null. */ + setSkin: function (newSkin) { + if (this.skin && newSkin) newSkin._attachAll(this, this.skin); + this.skin = newSkin; + }, + /** @return May be null. */ + getAttachmentBySlotName: function (slotName, attachmentName) { + return this.getAttachmentBySlotIndex(this.data.findSlotIndex(slotName), attachmentName); + }, + /** @return May be null. */ + getAttachmentBySlotIndex: function (slotIndex, attachmentName) { + if (this.skin) { + var attachment = this.skin.getAttachment(slotIndex, attachmentName); + if (attachment) return attachment; + } + if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); + return null; + }, + /** @param attachmentName May be null. */ + setAttachment: function (slotName, attachmentName) { + var slots = this.slots; + for (var i = 0, n = slots.size; i < n; i++) { + var slot = slots[i]; + if (slot.data.name == slotName) { + var attachment = null; + if (attachmentName) { + attachment = this.getAttachment(i, attachmentName); + if (attachment == null) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; + } + slot.setAttachment(attachment); + return; + } + } + throw "Slot not found: " + slotName; + }, + update: function (delta) { + time += delta; + } +}; + +spine.AttachmentType = { + region: 0 +}; + +spine.RegionAttachment = function () { + this.offset = []; + this.offset.length = 8; + this.uvs = []; + this.uvs.length = 8; +}; +spine.RegionAttachment.prototype = { + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1, + width: 0, height: 0, + rendererObject: null, + regionOffsetX: 0, regionOffsetY: 0, + regionWidth: 0, regionHeight: 0, + regionOriginalWidth: 0, regionOriginalHeight: 0, + setUVs: function (u, v, u2, v2, rotate) { + var uvs = this.uvs; + if (rotate) { + uvs[2/*X2*/] = u; + uvs[3/*Y2*/] = v2; + uvs[4/*X3*/] = u; + uvs[5/*Y3*/] = v; + uvs[6/*X4*/] = u2; + uvs[7/*Y4*/] = v; + uvs[0/*X1*/] = u2; + uvs[1/*Y1*/] = v2; + } else { + uvs[0/*X1*/] = u; + uvs[1/*Y1*/] = v2; + uvs[2/*X2*/] = u; + uvs[3/*Y2*/] = v; + uvs[4/*X3*/] = u2; + uvs[5/*Y3*/] = v; + uvs[6/*X4*/] = u2; + uvs[7/*Y4*/] = v2; + } + }, + updateOffset: function () { + var regionScaleX = this.width / this.regionOriginalWidth * this.scaleX; + var regionScaleY = this.height / this.regionOriginalHeight * this.scaleY; + var localX = -this.width / 2 * this.scaleX + this.regionOffsetX * regionScaleX; + var localY = -this.height / 2 * this.scaleY + this.regionOffsetY * regionScaleY; + var localX2 = localX + this.regionWidth * regionScaleX; + var localY2 = localY + this.regionHeight * regionScaleY; + var radians = this.rotation * Math.PI / 180; + var cos = Math.cos(radians); + var sin = Math.sin(radians); + var localXCos = localX * cos + this.x; + var localXSin = localX * sin; + var localYCos = localY * cos + this.y; + var localYSin = localY * sin; + var localX2Cos = localX2 * cos + this.x; + var localX2Sin = localX2 * sin; + var localY2Cos = localY2 * cos + this.y; + var localY2Sin = localY2 * sin; + var offset = this.offset; + offset[0/*X1*/] = localXCos - localYSin; + offset[1/*Y1*/] = localYCos + localXSin; + offset[2/*X2*/] = localXCos - localY2Sin; + offset[3/*Y2*/] = localY2Cos + localXSin; + offset[4/*X3*/] = localX2Cos - localY2Sin; + offset[5/*Y3*/] = localY2Cos + localX2Sin; + offset[6/*X4*/] = localX2Cos - localYSin; + offset[7/*Y4*/] = localYCos + localX2Sin; + }, + computeVertices: function (x, y, bone, vertices) { + x += bone.worldX; + y += bone.worldY; + var m00 = bone.m00; + var m01 = bone.m01; + var m10 = bone.m10; + var m11 = bone.m11; + var offset = this.offset; + vertices[0/*X1*/] = offset[0/*X1*/] * m00 + offset[1/*Y1*/] * m01 + x; + vertices[1/*Y1*/] = offset[0/*X1*/] * m10 + offset[1/*Y1*/] * m11 + y; + vertices[2/*X2*/] = offset[2/*X2*/] * m00 + offset[3/*Y2*/] * m01 + x; + vertices[3/*Y2*/] = offset[2/*X2*/] * m10 + offset[3/*Y2*/] * m11 + y; + vertices[4/*X3*/] = offset[4/*X3*/] * m00 + offset[5/*X3*/] * m01 + x; + vertices[5/*X3*/] = offset[4/*X3*/] * m10 + offset[5/*X3*/] * m11 + y; + vertices[6/*X4*/] = offset[6/*X4*/] * m00 + offset[7/*Y4*/] * m01 + x; + vertices[7/*Y4*/] = offset[6/*X4*/] * m10 + offset[7/*Y4*/] * m11 + y; + } +} + +spine.AnimationStateData = function (skeletonData) { + this.skeletonData = skeletonData; + this.animationToMixTime = {}; +}; +spine.AnimationStateData.prototype = { + defaultMix: 0, + setMixByName: function (fromName, toName, duration) { + var from = this.skeletonData.findAnimation(fromName); + if (!from) throw "Animation not found: " + fromName; + var to = this.skeletonData.findAnimation(toName); + if (!to) throw "Animation not found: " + toName; + this.setMix(from, to, duration); + }, + setMix: function (from, to, duration) { + this.animationToMixTime[from.name + ":" + to.name] = duration; + }, + getMix: function (from, to) { + var time = this.animationToMixTime[from.name + ":" + to.name]; + return time ? time : this.defaultMix; + } +}; + +spine.AnimationState = function (stateData) { + this.data = stateData; + this.queue = []; +}; +spine.AnimationState.prototype = { + current: null, + previous: null, + currentTime: 0, + previousTime: 0, + currentLoop: false, + previousLoop: false, + mixTime: 0, + mixDuration: 0, + update: function (delta) { + this.currentTime += delta; + this.previousTime += delta; + this.mixTime += delta; + + if (this.queue.length > 0) { + var entry = this.queue[0]; + if (this.currentTime >= entry.delay) { + this._setAnimation(entry.animation, entry.loop); + this.queue.shift(); + } + } + }, + apply: function (skeleton) { + if (!this.current) return; + if (this.previous) { + this.previous.apply(skeleton, this.previousTime, this.previousLoop); + var alpha = this.mixTime / this.mixDuration; + if (alpha >= 1) { + alpha = 1; + this.previous = null; + } + this.current.mix(skeleton, this.currentTime, this.currentLoop, alpha); + } else + this.current.apply(skeleton, this.currentTime, this.currentLoop); + }, + clearAnimation: function () { + this.previous = null; + this.current = null; + this.queue.length = 0; + }, + _setAnimation: function (animation, loop) { + this.previous = null; + if (animation && this.current) { + this.mixDuration = this.data.getMix(this.current, animation); + if (this.mixDuration > 0) { + this.mixTime = 0; + this.previous = this.current; + this.previousTime = this.currentTime; + this.previousLoop = this.currentLoop; + } + } + this.current = animation; + this.currentLoop = loop; + this.currentTime = 0; + }, + /** @see #setAnimation(Animation, Boolean) */ + setAnimationByName: function (animationName, loop) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (!animation) throw "Animation not found: " + animationName; + this.setAnimation(animation, loop); + }, + /** Set the current animation. Any queued animations are cleared and the current animation time is set to 0. + * @param animation May be null. */ + setAnimation: function (animation, loop) { + this.queue.length = 0; + this._setAnimation(animation, loop); + }, + /** @see #addAnimation(Animation, Boolean, Number) */ + addAnimationByName: function (animationName, loop, delay) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (!animation) throw "Animation not found: " + animationName; + this.addAnimation(animation, loop, delay); + }, + /** Adds an animation to be played delay seconds after the current or last queued animation. + * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ + addAnimation: function (animation, loop, delay) { + var entry = {}; + entry.animation = animation; + entry.loop = loop; + + if (!delay || delay <= 0) { + var previousAnimation = this.queue.length ? this.queue[this.queue.length - 1].animation : this.current; + if (previousAnimation != null) + delay = previousAnimation.duration - this.data.getMix(previousAnimation, animation) + (delay || 0); + else + delay = 0; + } + entry.delay = delay; + + this.queue.push(entry); + }, + /** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */ + isComplete: function () { + return !this.current || this.currentTime >= this.current.duration; + } +}; + +spine.SkeletonJson = function (attachmentLoader) { + this.attachmentLoader = attachmentLoader; +}; +spine.SkeletonJson.prototype = { + scale: 1, + readSkeletonData: function (root) { + /*jshint -W069*/ + var skeletonData = new spine.SkeletonData(), + boneData; + + // Bones. + var bones = root["bones"]; + for (var i = 0, n = bones.length; i < n; i++) { + var boneMap = bones[i]; + var parent = null; + if (boneMap["parent"]) { + parent = skeletonData.findBone(boneMap["parent"]); + if (!parent) throw "Parent bone not found: " + boneMap["parent"]; + } + boneData = new spine.BoneData(boneMap["name"], parent); + boneData.length = (boneMap["length"] || 0) * this.scale; + boneData.x = (boneMap["x"] || 0) * this.scale; + boneData.y = (boneMap["y"] || 0) * this.scale; + boneData.rotation = (boneMap["rotation"] || 0); + boneData.scaleX = boneMap["scaleX"] || 1; + boneData.scaleY = boneMap["scaleY"] || 1; + skeletonData.bones.push(boneData); + } + + // Slots. + var slots = root["slots"]; + for (i = 0, n = slots.length; i < n; i++) { + var slotMap = slots[i]; + boneData = skeletonData.findBone(slotMap["bone"]); + if (!boneData) throw "Slot bone not found: " + slotMap["bone"]; + var slotData = new spine.SlotData(slotMap["name"], boneData); + + var color = slotMap["color"]; + if (color) { + slotData.r = spine.SkeletonJson.toColor(color, 0); + slotData.g = spine.SkeletonJson.toColor(color, 1); + slotData.b = spine.SkeletonJson.toColor(color, 2); + slotData.a = spine.SkeletonJson.toColor(color, 3); + } + + slotData.attachmentName = slotMap["attachment"]; + + skeletonData.slots.push(slotData); + } + + // Skins. + var skins = root["skins"]; + for (var skinName in skins) { + if (!skins.hasOwnProperty(skinName)) continue; + var skinMap = skins[skinName]; + var skin = new spine.Skin(skinName); + for (var slotName in skinMap) { + if (!skinMap.hasOwnProperty(slotName)) continue; + var slotIndex = skeletonData.findSlotIndex(slotName); + var slotEntry = skinMap[slotName]; + for (var attachmentName in slotEntry) { + if (!slotEntry.hasOwnProperty(attachmentName)) continue; + var attachment = this.readAttachment(skin, attachmentName, slotEntry[attachmentName]); + if (attachment != null) skin.addAttachment(slotIndex, attachmentName, attachment); + } + } + skeletonData.skins.push(skin); + if (skin.name == "default") skeletonData.defaultSkin = skin; + } + + // Animations. + var animations = root["animations"]; + for (var animationName in animations) { + if (!animations.hasOwnProperty(animationName)) continue; + this.readAnimation(animationName, animations[animationName], skeletonData); + } + + return skeletonData; + }, + readAttachment: function (skin, name, map) { + /*jshint -W069*/ + name = map["name"] || name; + + var type = spine.AttachmentType[map["type"] || "region"]; + + if (type == spine.AttachmentType.region) { + var attachment = new spine.RegionAttachment(); + attachment.x = (map["x"] || 0) * this.scale; + attachment.y = (map["y"] || 0) * this.scale; + attachment.scaleX = map["scaleX"] || 1; + attachment.scaleY = map["scaleY"] || 1; + attachment.rotation = map["rotation"] || 0; + attachment.width = (map["width"] || 32) * this.scale; + attachment.height = (map["height"] || 32) * this.scale; + attachment.updateOffset(); + + attachment.rendererObject = {}; + attachment.rendererObject.name = name; + attachment.rendererObject.scale = {}; + attachment.rendererObject.scale.x = attachment.scaleX; + attachment.rendererObject.scale.y = attachment.scaleY; + attachment.rendererObject.rotation = -attachment.rotation * Math.PI / 180; + return attachment; + } + + throw "Unknown attachment type: " + type; + }, + + readAnimation: function (name, map, skeletonData) { + /*jshint -W069*/ + var timelines = []; + var duration = 0; + var frameIndex, timeline, timelineName, valueMap, values, + i, n; + + var bones = map["bones"]; + for (var boneName in bones) { + if (!bones.hasOwnProperty(boneName)) continue; + var boneIndex = skeletonData.findBoneIndex(boneName); + if (boneIndex == -1) throw "Bone not found: " + boneName; + var boneMap = bones[boneName]; + + for (timelineName in boneMap) { + if (!boneMap.hasOwnProperty(timelineName)) continue; + values = boneMap[timelineName]; + if (timelineName == "rotate") { + timeline = new spine.RotateTimeline(values.length); + timeline.boneIndex = boneIndex; + + frameIndex = 0; + for (i = 0, n = values.length; i < n; i++) { + valueMap = values[i]; + timeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]); + spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]); + + } else if (timelineName == "translate" || timelineName == "scale") { + var timelineScale = 1; + if (timelineName == "scale") + timeline = new spine.ScaleTimeline(values.length); + else { + timeline = new spine.TranslateTimeline(values.length); + timelineScale = this.scale; + } + timeline.boneIndex = boneIndex; + + frameIndex = 0; + for (i = 0, n = values.length; i < n; i++) { + valueMap = values[i]; + var x = (valueMap["x"] || 0) * timelineScale; + var y = (valueMap["y"] || 0) * timelineScale; + timeline.setFrame(frameIndex, valueMap["time"], x, y); + spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); + + } else + throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; + } + } + var slots = map["slots"]; + for (var slotName in slots) { + if (!slots.hasOwnProperty(slotName)) continue; + var slotMap = slots[slotName]; + var slotIndex = skeletonData.findSlotIndex(slotName); + + for (timelineName in slotMap) { + if (!slotMap.hasOwnProperty(timelineName)) continue; + values = slotMap[timelineName]; + if (timelineName == "color") { + timeline = new spine.ColorTimeline(values.length); + timeline.slotIndex = slotIndex; + + frameIndex = 0; + for (i = 0, n = values.length; i < n; i++) { + valueMap = values[i]; + var color = valueMap["color"]; + var r = spine.SkeletonJson.toColor(color, 0); + var g = spine.SkeletonJson.toColor(color, 1); + var b = spine.SkeletonJson.toColor(color, 2); + var a = spine.SkeletonJson.toColor(color, 3); + timeline.setFrame(frameIndex, valueMap["time"], r, g, b, a); + spine.SkeletonJson.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 5 - 5]); + + } else if (timelineName == "attachment") { + timeline = new spine.AttachmentTimeline(values.length); + timeline.slotIndex = slotIndex; + + frameIndex = 0; + for (i = 0, n = values.length; i < n; i++) { + valueMap = values[i]; + timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + + } else + throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; + } + } + skeletonData.animations.push(new spine.Animation(name, timelines, duration)); + } +}; +spine.SkeletonJson.readCurve = function (timeline, frameIndex, valueMap) { + /*jshint -W069*/ + var curve = valueMap["curve"]; + if (!curve) return; + if (curve == "stepped") + timeline.curves.setStepped(frameIndex); + else if (curve instanceof Array) + timeline.curves.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); +}; +spine.SkeletonJson.toColor = function (hexString, colorIndex) { + if (hexString.length != 8) throw "Color hexidecimal length must be 8, recieved: " + hexString; + return parseInt(hexString.substring(colorIndex * 2, 2), 16) / 255; +}; + +spine.Atlas = function (atlasText, textureLoader) { + this.textureLoader = textureLoader; + this.pages = []; + this.regions = []; + + var reader = new spine.AtlasReader(atlasText); + var tuple = []; + tuple.length = 4; + var page = null; + while (true) { + var line = reader.readLine(); + if (line == null) break; + line = reader.trim(line); + if (!line.length) + page = null; + else if (!page) { + page = new spine.AtlasPage(); + page.name = line; + + page.format = spine.Atlas.Format[reader.readValue()]; + + reader.readTuple(tuple); + page.minFilter = spine.Atlas.TextureFilter[tuple[0]]; + page.magFilter = spine.Atlas.TextureFilter[tuple[1]]; + + var direction = reader.readValue(); + page.uWrap = spine.Atlas.TextureWrap.clampToEdge; + page.vWrap = spine.Atlas.TextureWrap.clampToEdge; + if (direction == "x") + page.uWrap = spine.Atlas.TextureWrap.repeat; + else if (direction == "y") + page.vWrap = spine.Atlas.TextureWrap.repeat; + else if (direction == "xy") + page.uWrap = page.vWrap = spine.Atlas.TextureWrap.repeat; + + textureLoader.load(page, line); + + this.pages.push(page); + + } else { + var region = new spine.AtlasRegion(); + region.name = line; + region.page = page; + + region.rotate = reader.readValue() == "true"; + + reader.readTuple(tuple); + var x = parseInt(tuple[0], 10); + var y = parseInt(tuple[1], 10); + + reader.readTuple(tuple); + var width = parseInt(tuple[0], 10); + var height = parseInt(tuple[1], 10); + + region.u = x / page.width; + region.v = y / page.height; + if (region.rotate) { + region.u2 = (x + height) / page.width; + region.v2 = (y + width) / page.height; + } else { + region.u2 = (x + width) / page.width; + region.v2 = (y + height) / page.height; + } + region.x = x; + region.y = y; + region.width = Math.abs(width); + region.height = Math.abs(height); + + if (reader.readTuple(tuple) == 4) { // split is optional + region.splits = [parseInt(tuple[0], 10), parseInt(tuple[1], 10), parseInt(tuple[2], 10), parseInt(tuple[3], 10)]; + + if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits + region.pads = [parseInt(tuple[0], 10), parseInt(tuple[1], 10), parseInt(tuple[2], 10), parseInt(tuple[3], 10)]; + + reader.readTuple(tuple); + } + } + + region.originalWidth = parseInt(tuple[0], 10); + region.originalHeight = parseInt(tuple[1], 10); + + reader.readTuple(tuple); + region.offsetX = parseInt(tuple[0], 10); + region.offsetY = parseInt(tuple[1], 10); + + region.index = parseInt(reader.readValue(), 10); + + this.regions.push(region); + } + } +}; +spine.Atlas.prototype = { + findRegion: function (name) { + var regions = this.regions; + for (var i = 0, n = regions.length; i < n; i++) + if (regions[i].name == name) return regions[i]; + return null; + }, + dispose: function () { + var pages = this.pages; + for (var i = 0, n = pages.length; i < n; i++) + this.textureLoader.unload(pages[i].rendererObject); + }, + updateUVs: function (page) { + var regions = this.regions; + for (var i = 0, n = regions.length; i < n; i++) { + var region = regions[i]; + if (region.page != page) continue; + region.u = region.x / page.width; + region.v = region.y / page.height; + if (region.rotate) { + region.u2 = (region.x + region.height) / page.width; + region.v2 = (region.y + region.width) / page.height; + } else { + region.u2 = (region.x + region.width) / page.width; + region.v2 = (region.y + region.height) / page.height; + } + } + } +}; + +spine.Atlas.Format = { + alpha: 0, + intensity: 1, + luminanceAlpha: 2, + rgb565: 3, + rgba4444: 4, + rgb888: 5, + rgba8888: 6 +}; + +spine.Atlas.TextureFilter = { + nearest: 0, + linear: 1, + mipMap: 2, + mipMapNearestNearest: 3, + mipMapLinearNearest: 4, + mipMapNearestLinear: 5, + mipMapLinearLinear: 6 +}; + +spine.Atlas.TextureWrap = { + mirroredRepeat: 0, + clampToEdge: 1, + repeat: 2 +}; + +spine.AtlasPage = function () {}; +spine.AtlasPage.prototype = { + name: null, + format: null, + minFilter: null, + magFilter: null, + uWrap: null, + vWrap: null, + rendererObject: null, + width: 0, + height: 0 +}; + +spine.AtlasRegion = function () {}; +spine.AtlasRegion.prototype = { + page: null, + name: null, + x: 0, y: 0, + width: 0, height: 0, + u: 0, v: 0, u2: 0, v2: 0, + offsetX: 0, offsetY: 0, + originalWidth: 0, originalHeight: 0, + index: 0, + rotate: false, + splits: null, + pads: null, +}; + +spine.AtlasReader = function (text) { + this.lines = text.split(/\r\n|\r|\n/); +}; +spine.AtlasReader.prototype = { + index: 0, + trim: function (value) { + return value.replace(/^\s+|\s+$/g, ""); + }, + readLine: function () { + if (this.index >= this.lines.length) return null; + return this.lines[this.index++]; + }, + readValue: function () { + var line = this.readLine(); + var colon = line.indexOf(":"); + if (colon == -1) throw "Invalid line: " + line; + return this.trim(line.substring(colon + 1)); + }, + /** Returns the number of tuple values read (2 or 4). */ + readTuple: function (tuple) { + var line = this.readLine(); + var colon = line.indexOf(":"); + if (colon == -1) throw "Invalid line: " + line; + var i = 0, lastMatch= colon + 1; + for (; i < 3; i++) { + var comma = line.indexOf(",", lastMatch); + if (comma == -1) { + if (!i) throw "Invalid line: " + line; + break; + } + tuple[i] = this.trim(line.substr(lastMatch, comma - lastMatch)); + lastMatch = comma + 1; + } + tuple[i] = this.trim(line.substring(lastMatch)); + return i + 1; + } +} + +spine.AtlasAttachmentLoader = function (atlas) { + this.atlas = atlas; +} +spine.AtlasAttachmentLoader.prototype = { + newAttachment: function (skin, type, name) { + switch (type) { + case spine.AttachmentType.region: + var region = this.atlas.findRegion(name); + if (!region) throw "Region not found in atlas: " + name + " (" + type + ")"; + var attachment = new spine.RegionAttachment(name); + attachment.rendererObject = region; + attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate); + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + } + throw "Unknown attachment type: " + type; + } +} + +spine.Bone.yDown = true; +PIXI.AnimCache = {}; + +/** + * A class that enables the you to import and run your spine animations in pixi. + * Spine animation data needs to be loaded using the PIXI.AssetLoader or PIXI.SpineLoader before it can be used by this class + * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source + * + * @class Spine + * @extends DisplayObjectContainer + * @constructor + * @param url {String} The url of the spine anim file to be used + */ +PIXI.Spine = function (url) { + PIXI.DisplayObjectContainer.call(this); + + this.spineData = PIXI.AnimCache[url]; + + if (!this.spineData) { + throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: " + url); + } + + this.skeleton = new spine.Skeleton(this.spineData); + this.skeleton.updateWorldTransform(); + + this.stateData = new spine.AnimationStateData(this.spineData); + this.state = new spine.AnimationState(this.stateData); + + this.slotContainers = []; + + for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { + var slot = this.skeleton.drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = new PIXI.DisplayObjectContainer(); + this.slotContainers.push(slotContainer); + this.addChild(slotContainer); + if (!(attachment instanceof spine.RegionAttachment)) { + continue; + } + var spriteName = attachment.rendererObject.name; + var sprite = this.createSprite(slot, attachment.rendererObject); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotContainer.addChild(sprite); + } +}; + +PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); +PIXI.Spine.prototype.constructor = PIXI.Spine; + +/* + * Updates the object transform for rendering + * + * @method updateTransform + * @private + */ +PIXI.Spine.prototype.updateTransform = function () { + this.lastTime = this.lastTime || Date.now(); + var timeDelta = (Date.now() - this.lastTime) * 0.001; + this.lastTime = Date.now(); + this.state.update(timeDelta); + this.state.apply(this.skeleton); + this.skeleton.updateWorldTransform(); + + var drawOrder = this.skeleton.drawOrder; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var attachment = slot.attachment; + var slotContainer = this.slotContainers[i]; + if (!(attachment instanceof spine.RegionAttachment)) { + slotContainer.visible = false; + continue; + } + + if (attachment.rendererObject) { + if (!slot.currentSpriteName || slot.currentSpriteName != attachment.name) { + var spriteName = attachment.rendererObject.name; + if (slot.currentSprite !== undefined) { + slot.currentSprite.visible = false; + } + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) { + slot.sprites[spriteName].visible = true; + } else { + var sprite = this.createSprite(slot, attachment.rendererObject); + slotContainer.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; + } + } + slotContainer.visible = true; + + var bone = slot.bone; + + slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; + slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; + slotContainer.scale.x = bone.worldScaleX; + slotContainer.scale.y = bone.worldScaleY; + + slotContainer.rotation = -(slot.bone.worldRotation * Math.PI / 180); + } + + PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); +}; + + +PIXI.Spine.prototype.createSprite = function (slot, descriptor) { + var name = PIXI.TextureCache[descriptor.name] ? descriptor.name : descriptor.name + ".png"; + var sprite = new PIXI.Sprite(PIXI.Texture.fromFrame(name)); + sprite.scale = descriptor.scale; + sprite.rotation = descriptor.rotation; + sprite.anchor.x = sprite.anchor.y = 0.5; + + slot.sprites = slot.sprites || {}; + slot.sprites[descriptor.name] = sprite; + return sprite; +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.BaseTextureCache = {}; +PIXI.texturesToUpdate = []; +PIXI.texturesToDestroy = []; + +/** + * A texture stores the information that represents an image. All textures have a base texture + * + * @class BaseTexture + * @uses EventTarget + * @constructor + * @param source {String} the source object (image or canvas) + */ +PIXI.BaseTexture = function(source, scaleMode) +{ + PIXI.EventTarget.call( this ); + + /** + * [read-only] The width of the base texture set when the image has loaded + * + * @property width + * @type Number + * @readOnly + */ + this.width = 100; + + /** + * [read-only] The height of the base texture set when the image has loaded + * + * @property height + * @type Number + * @readOnly + */ + this.height = 100; + + /** + * The scale mode to apply when scaling this texture + * @property scaleMode + * @type PIXI.BaseTexture.SCALE_MODE + * @default PIXI.BaseTexture.SCALE_MODE.LINEAR + */ + this.scaleMode = scaleMode || PIXI.BaseTexture.SCALE_MODE.DEFAULT; + + /** + * [read-only] Describes if the base texture has loaded or not + * + * @property hasLoaded + * @type Boolean + * @readOnly + */ + this.hasLoaded = false; + + /** + * The source that is loaded to create the texture + * + * @property source + * @type Image + */ + this.source = source; + + if(!source)return; + + if(this.source instanceof Image || this.source instanceof HTMLImageElement) + { + if(this.source.complete) + { + this.hasLoaded = true; + this.width = this.source.width; + this.height = this.source.height; + + PIXI.texturesToUpdate.push(this); + } + else + { + + var scope = this; + this.source.onload = function() { + + scope.hasLoaded = true; + scope.width = scope.source.width; + scope.height = scope.source.height; + + // add it to somewhere... + PIXI.texturesToUpdate.push(scope); + scope.dispatchEvent( { type: 'loaded', content: scope } ); + }; + //this.image.src = imageUrl; + } + } + else + { + this.hasLoaded = true; + this.width = this.source.width; + this.height = this.source.height; + + PIXI.texturesToUpdate.push(this); + } + + this.imageUrl = null; + this._powerOf2 = false; + + + // used for webGL + this._glTextures = []; + +}; + +PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; + +/** + * Destroys this base texture + * + * @method destroy + */ +PIXI.BaseTexture.prototype.destroy = function() +{ + if(this.source instanceof Image) + { + if (this.imageUrl in PIXI.BaseTextureCache) + delete PIXI.BaseTextureCache[this.imageUrl]; + this.imageUrl = null; + this.source.src = null; + } + this.source = null; + PIXI.texturesToDestroy.push(this); +}; + +/** + * + * + * @method destroy + */ + +PIXI.BaseTexture.prototype.updateSourceImage = function(newSrc) +{ + this.hasLoaded = false; + this.source.src = null; + this.source.src = newSrc; +}; + +/** + * Helper function that returns a base texture based on an image url + * If the image is not in the base texture cache it will be created and loaded + * + * @static + * @method fromImage + * @param imageUrl {String} The image url of the texture + * @return BaseTexture + */ +PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin, scaleMode) +{ + var baseTexture = PIXI.BaseTextureCache[imageUrl]; + if(!baseTexture) + { + // new Image() breaks tex loading in some versions of Chrome. + // See https://code.google.com/p/chromium/issues/detail?id=238071 + var image = new Image();//document.createElement('img'); + if (crossorigin) + { + image.crossOrigin = ''; + } + image.src = imageUrl; + baseTexture = new PIXI.BaseTexture(image, scaleMode); + baseTexture.imageUrl = imageUrl; + PIXI.BaseTextureCache[imageUrl] = baseTexture; + } + + return baseTexture; +}; + +PIXI.BaseTexture.SCALE_MODE = { + DEFAULT: 0, //default to LINEAR + LINEAR: 0, + NEAREST: 1 +}; +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +PIXI.TextureCache = {}; +PIXI.FrameCache = {}; + +/** + * A texture stores the information that represents an image or part of an image. It cannot be added + * to the display list directly. To do this use PIXI.Sprite. If no frame is provided then the whole image is used + * + * @class Texture + * @uses EventTarget + * @constructor + * @param baseTexture {BaseTexture} The base texture source to create the texture from + * @param frame {Rectangle} The rectangle frame of the texture to show + */ +PIXI.Texture = function(baseTexture, frame) +{ + PIXI.EventTarget.call( this ); + + if(!frame) + { + this.noFrame = true; + frame = new PIXI.Rectangle(0,0,1,1); + } + + if(baseTexture instanceof PIXI.Texture) + baseTexture = baseTexture.baseTexture; + + /** + * The base texture of this texture + * + * @property baseTexture + * @type BaseTexture + */ + this.baseTexture = baseTexture; + + /** + * The frame specifies the region of the base texture that this texture uses + * + * @property frame + * @type Rectangle + */ + this.frame = frame; + + /** + * The trim point + * + * @property trim + * @type Point + */ + this.trim = new PIXI.Point(); + + this.scope = this; + + if(baseTexture.hasLoaded) + { + if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); + + this.setFrame(frame); + } + else + { + var scope = this; + baseTexture.addEventListener('loaded', function(){ scope.onBaseTextureLoaded(); }); + } +}; + +PIXI.Texture.prototype.constructor = PIXI.Texture; + +/** + * Called when the base texture is loaded + * + * @method onBaseTextureLoaded + * @param event + * @private + */ +PIXI.Texture.prototype.onBaseTextureLoaded = function() +{ + var baseTexture = this.baseTexture; + baseTexture.removeEventListener( 'loaded', this.onLoaded ); + + if(this.noFrame)this.frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height); + + this.setFrame(this.frame); + + this.scope.dispatchEvent( { type: 'update', content: this } ); +}; + +/** + * Destroys this texture + * + * @method destroy + * @param destroyBase {Boolean} Whether to destroy the base texture as well + */ +PIXI.Texture.prototype.destroy = function(destroyBase) +{ + if(destroyBase) this.baseTexture.destroy(); +}; + +/** + * Specifies the rectangle region of the baseTexture + * + * @method setFrame + * @param frame {Rectangle} The frame of the texture to set it to + */ +PIXI.Texture.prototype.setFrame = function(frame) +{ + this.frame = frame; + this.width = frame.width; + this.height = frame.height; + + if(frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height) + { + throw new Error('Texture Error: frame does not fit inside the base Texture dimensions ' + this); + } + + this.updateFrame = true; + + PIXI.Texture.frameUpdates.push(this); + //this.dispatchEvent( { type: 'update', content: this } ); +}; + +PIXI.Texture.prototype._updateWebGLuvs = function() +{ + if(!this._uvs)this._uvs = new Float32Array(8); + + var frame = this.frame; + var tw = this.baseTexture.width; + var th = this.baseTexture.height; + + this._uvs[0] = frame.x / tw; + this._uvs[1] = frame.y / th; + + this._uvs[2] = (frame.x + frame.width) / tw; + this._uvs[3] = frame.y / th; + + this._uvs[4] = (frame.x + frame.width) / tw; + this._uvs[5] = (frame.y + frame.height) / th; + + this._uvs[6] = frame.x / tw; + this._uvs[7] = (frame.y + frame.height) / th; +}; + +/** + * Helper function that returns a texture based on an image url + * If the image is not in the texture cache it will be created and loaded + * + * @static + * @method fromImage + * @param imageUrl {String} The image url of the texture + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + * @return Texture + */ +PIXI.Texture.fromImage = function(imageUrl, crossorigin, scaleMode) +{ + var texture = PIXI.TextureCache[imageUrl]; + + if(!texture) + { + texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin, scaleMode)); + PIXI.TextureCache[imageUrl] = texture; + } + + return texture; +}; + +/** + * Helper function that returns a texture based on a frame id + * If the frame id is not in the texture cache an error will be thrown + * + * @static + * @method fromFrame + * @param frameId {String} The frame id of the texture + * @return Texture + */ +PIXI.Texture.fromFrame = function(frameId) +{ + var texture = PIXI.TextureCache[frameId]; + if(!texture) throw new Error('The frameId "' + frameId + '" does not exist in the texture cache ' + this); + return texture; +}; + +/** + * Helper function that returns a texture based on a canvas element + * If the canvas is not in the texture cache it will be created and loaded + * + * @static + * @method fromCanvas + * @param canvas {Canvas} The canvas element source of the texture + * @return Texture + */ +PIXI.Texture.fromCanvas = function(canvas, scaleMode) +{ + var baseTexture = new PIXI.BaseTexture(canvas, scaleMode); + return new PIXI.Texture(baseTexture); +}; + + +/** + * Adds a texture to the textureCache. + * + * @static + * @method addTextureToCache + * @param texture {Texture} + * @param id {String} the id that the texture will be stored against. + */ +PIXI.Texture.addTextureToCache = function(texture, id) +{ + PIXI.TextureCache[id] = texture; +}; + +/** + * Remove a texture from the textureCache. + * + * @static + * @method removeTextureFromCache + * @param id {String} the id of the texture to be removed + * @return {Texture} the texture that was removed + */ +PIXI.Texture.removeTextureFromCache = function(id) +{ + var texture = PIXI.TextureCache[id]; + PIXI.TextureCache[id] = null; + return texture; +}; + +// this is more for webGL.. it contains updated frames.. +PIXI.Texture.frameUpdates = []; + +PIXI.Texture.SCALE_MODE = PIXI.BaseTexture.SCALE_MODE; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + A RenderTexture is a special texture that allows any pixi displayObject to be rendered to it. + + __Hint__: All DisplayObjects (exmpl. Sprites) that renders on RenderTexture should be preloaded. + Otherwise black rectangles will be drawn instead. + + RenderTexture takes snapshot of DisplayObject passed to render method. If DisplayObject is passed to render method, position and rotation of it will be ignored. For example: + + var renderTexture = new PIXI.RenderTexture(800, 600); + var sprite = PIXI.Sprite.fromImage("spinObj_01.png"); + sprite.position.x = 800/2; + sprite.position.y = 600/2; + sprite.anchor.x = 0.5; + sprite.anchor.y = 0.5; + renderTexture.render(sprite); + + Sprite in this case will be rendered to 0,0 position. To render this sprite at center DisplayObjectContainer should be used: + + var doc = new PIXI.DisplayObjectContainer(); + doc.addChild(sprite); + renderTexture.render(doc); // Renders to center of renderTexture + + @class RenderTexture + @extends Texture + @constructor + @param width {Number} The width of the render texture + @param height {Number} The height of the render texture + */ +PIXI.RenderTexture = function(width, height, renderer) +{ + PIXI.EventTarget.call( this ); + + this.width = width || 100; + this.height = height || 100; + + this.indetityMatrix = PIXI.mat3.create(); + + this.frame = new PIXI.Rectangle(0, 0, this.width, this.height); + + this.baseTexture = new PIXI.BaseTexture(); + this.baseTexture.width = this.width; + this.baseTexture.height = this.height; + this.baseTexture._glTextures = []; + + this.baseTexture.hasLoaded = true; + + // each render texture can only belong to one renderer at the moment if its webGL + this.renderer = renderer || PIXI.defaultRenderer; + + if(this.renderer.type === PIXI.WEBGL_RENDERER) + { + var gl = this.renderer.gl; + + this.textureBuffer = new PIXI.FilterTexture(gl, this.width, this.height); + this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; + + this.render = this.renderWebGL; + this.projection = new PIXI.Point(this.width/2 , -this.height/2); + } + else + { + this.render = this.renderCanvas; + this.textureBuffer = new PIXI.CanvasBuffer(this.width, this.height); + this.baseTexture.source = this.textureBuffer.canvas; + } + + PIXI.Texture.frameUpdates.push(this); +}; + +PIXI.RenderTexture.prototype = Object.create( PIXI.Texture.prototype ); +PIXI.RenderTexture.prototype.constructor = PIXI.RenderTexture; + +PIXI.RenderTexture.prototype.resize = function(width, height) +{ + this.width = width; + this.height = height; + + this.frame.width = this.width; + this.frame.height = this.height; + + if(this.renderer.type === PIXI.WEBGL_RENDERER) + { + this.projection.x = this.width / 2; + this.projection.y = -this.height / 2; + + var gl = this.gl; + gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTextures[gl.id]); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + else + { + this.textureBuffer.resize(this.width, this.height); + } + + PIXI.Texture.frameUpdates.push(this); +}; + +/** + * This function will draw the display object to the texture. + * + * @method renderWebGL + * @param displayObject {DisplayObject} The display object to render this texture on + * @param clear {Boolean} If true the texture will be cleared before the displayObject is drawn + * @private + */ +PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, clear) +{ + var gl = this.renderer.gl; + + gl.colorMask(true, true, true, true); + + gl.viewport(0, 0, this.width, this.height); + + gl.bindFramebuffer(gl.FRAMEBUFFER, this.textureBuffer.frameBuffer ); + + if(clear)this.textureBuffer.clear(); + + // THIS WILL MESS WITH HIT TESTING! + var children = displayObject.children; + + //TODO -? create a new one??? dont think so! + var originalWorldTransform = displayObject.worldTransform; + displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix; + // modify to flip... + displayObject.worldTransform[4] = -1; + displayObject.worldTransform[5] = this.projection.y * -2; + + if(position) + { + displayObject.worldTransform[2] = position.x; + displayObject.worldTransform[5] -= position.y; + } + + for(var i=0,j=children.length; i} assetURLs an array of image/sprite sheet urls that you would like loaded + * supported. Supported image formats include 'jpeg', 'jpg', 'png', 'gif'. Supported + * sprite sheet data formats only include 'JSON' at this time. Supported bitmap font + * data formats include 'xml' and 'fnt'. + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.AssetLoader = function(assetURLs, crossorigin) +{ + PIXI.EventTarget.call(this); + + /** + * The array of asset URLs that are going to be loaded + * + * @property assetURLs + * @type Array + */ + this.assetURLs = assetURLs; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * Maps file extension to loader types + * + * @property loadersByType + * @type Object + */ + this.loadersByType = { + 'jpg': PIXI.ImageLoader, + 'jpeg': PIXI.ImageLoader, + 'png': PIXI.ImageLoader, + 'gif': PIXI.ImageLoader, + 'json': PIXI.JsonLoader, + 'atlas': PIXI.AtlasLoader, + 'anim': PIXI.SpineLoader, + 'xml': PIXI.BitmapFontLoader, + 'fnt': PIXI.BitmapFontLoader + }; +}; + +/** + * Fired when an item has loaded + * @event onProgress + */ + +/** + * Fired when all the assets have loaded + * @event onComplete + */ + +// constructor +PIXI.AssetLoader.prototype.constructor = PIXI.AssetLoader; + + +PIXI.AssetLoader.prototype._getDataType = function(str) +{ + var test = 'data:'; + //starts with 'data:' + var start = str.slice(0, test.length).toLowerCase(); + if (start === test) { + var data = str.slice(test.length); + + var sepIdx = data.indexOf(','); + if (sepIdx === -1) //malformed data URI scheme + return null; + + //e.g. 'image/gif;base64' => 'image/gif' + var info = data.slice(0, sepIdx).split(';')[0]; + + //We might need to handle some special cases here... + //standardize text/plain to 'txt' file extension + if (!info || info.toLowerCase() === 'text/plain') + return 'txt'; + + //User specified mime type, try splitting it by '/' + return info.split('/').pop().toLowerCase(); + } + + return null; +}; + +/** + * Starts loading the assets sequentially + * + * @method load + */ +PIXI.AssetLoader.prototype.load = function() +{ + var scope = this; + + function onLoad() { + scope.onAssetLoaded(); + } + + this.loadCount = this.assetURLs.length; + + for (var i=0; i < this.assetURLs.length; i++) + { + var fileName = this.assetURLs[i]; + //first see if we have a data URI scheme.. + var fileType = this._getDataType(fileName); + + //if not, assume it's a file URI + if (!fileType) + fileType = fileName.split('?').shift().split('.').pop().toLowerCase(); + + var Constructor = this.loadersByType[fileType]; + if(!Constructor) + throw new Error(fileType + ' is an unsupported file type'); + + var loader = new Constructor(fileName, this.crossorigin); + + loader.addEventListener('loaded', onLoad); + loader.load(); + } +}; + +/** + * Invoked after each file is loaded + * + * @method onAssetLoaded + * @private + */ +PIXI.AssetLoader.prototype.onAssetLoaded = function() +{ + this.loadCount--; + this.dispatchEvent({type: 'onProgress', content: this}); + if (this.onProgress) this.onProgress(); + + if (!this.loadCount) + { + this.dispatchEvent({type: 'onComplete', content: this}); + if(this.onComplete) this.onComplete(); + } +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The json file loader is used to load in JSON data and parsing it + * When loaded this class will dispatch a 'loaded' event + * If load failed this class will dispatch a 'error' event + * + * @class JsonLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.JsonLoader = function (url, crossorigin) { + PIXI.EventTarget.call(this); + + /** + * The url of the bitmap font data + * + * @property url + * @type String + */ + this.url = url; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * [read-only] The base url of the bitmap font data + * + * @property baseUrl + * @type String + * @readOnly + */ + this.baseUrl = url.replace(/[^\/]*$/, ''); + + /** + * [read-only] Whether the data has loaded yet + * + * @property loaded + * @type Boolean + * @readOnly + */ + this.loaded = false; + +}; + +// constructor +PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; + +/** + * Loads the JSON data + * + * @method load + */ +PIXI.JsonLoader.prototype.load = function () { + this.ajaxRequest = new PIXI.AjaxRequest(); + var scope = this; + this.ajaxRequest.onreadystatechange = function () { + scope.onJSONLoaded(); + }; + + this.ajaxRequest.open('GET', this.url, true); + if (this.ajaxRequest.overrideMimeType) this.ajaxRequest.overrideMimeType('application/json'); + this.ajaxRequest.send(null); +}; + +/** + * Invoke when JSON file is loaded + * + * @method onJSONLoaded + * @private + */ +PIXI.JsonLoader.prototype.onJSONLoaded = function () { + if (this.ajaxRequest.readyState === 4) { + if (this.ajaxRequest.status === 200 || window.location.protocol.indexOf('http') === -1) { + this.json = JSON.parse(this.ajaxRequest.responseText); + + if(this.json.frames) + { + // sprite sheet + var scope = this; + var textureUrl = this.baseUrl + this.json.meta.image; + var image = new PIXI.ImageLoader(textureUrl, this.crossorigin); + var frameData = this.json.frames; + + this.texture = image.texture.baseTexture; + image.addEventListener('loaded', function() { + scope.onLoaded(); + }); + + for (var i in frameData) { + var rect = frameData[i].frame; + if (rect) { + PIXI.TextureCache[i] = new PIXI.Texture(this.texture, { + x: rect.x, + y: rect.y, + width: rect.w, + height: rect.h + }); + + // check to see ifthe sprite ha been trimmed.. + if (frameData[i].trimmed) { + + var texture = PIXI.TextureCache[i]; + + texture.trimmed = true; + + var actualSize = frameData[i].sourceSize; + var realSize = frameData[i].spriteSourceSize; + + texture.trim.x = realSize.x; + texture.trim.y = realSize.y; + texture.trim.realWidth = actualSize.w; + texture.trim.realHeight = actualSize.h; + } + } + } + + image.load(); + + } + else if(this.json.bones) + { + // spine animation + var spineJsonParser = new spine.SkeletonJson(); + var skeletonData = spineJsonParser.readSkeletonData(this.json); + PIXI.AnimCache[this.url] = skeletonData; + this.onLoaded(); + } + else + { + this.onLoaded(); + } + } + else + { + this.onError(); + } + } +}; + +/** + * Invoke when json file loaded + * + * @method onLoaded + * @private + */ +PIXI.JsonLoader.prototype.onLoaded = function () { + this.loaded = true; + this.dispatchEvent({ + type: 'loaded', + content: this + }); +}; + +/** + * Invoke when error occured + * + * @method onError + * @private + */ +PIXI.JsonLoader.prototype.onError = function () { + this.dispatchEvent({ + type: 'error', + content: this + }); +}; +/** + * @author Martin Kelm http://mkelm.github.com + */ + +/** + * The atlas file loader is used to load in Atlas data and parsing it + * When loaded this class will dispatch a 'loaded' event + * If load failed this class will dispatch a 'error' event + * @class AtlasLoader + * @extends EventTarget + * @constructor + * @param {String} url the url of the JSON file + * @param {Boolean} crossorigin + */ + +PIXI.AtlasLoader = function (url, crossorigin) { + PIXI.EventTarget.call(this); + this.url = url; + this.baseUrl = url.replace(/[^\/]*$/, ''); + this.crossorigin = crossorigin; + this.loaded = false; + +}; + +// constructor +PIXI.AtlasLoader.constructor = PIXI.AtlasLoader; + +/** + * This will begin loading the JSON file + */ +PIXI.AtlasLoader.prototype.load = function () { + this.ajaxRequest = new PIXI.AjaxRequest(); + this.ajaxRequest.onreadystatechange = this.onAtlasLoaded.bind(this); + + this.ajaxRequest.open('GET', this.url, true); + if (this.ajaxRequest.overrideMimeType) this.ajaxRequest.overrideMimeType('application/json'); + this.ajaxRequest.send(null); +}; + +/** + * Invoke when JSON file is loaded + * @private + */ +PIXI.AtlasLoader.prototype.onAtlasLoaded = function () { + if (this.ajaxRequest.readyState === 4) { + if (this.ajaxRequest.status === 200 || window.location.href.indexOf('http') === -1) { + this.atlas = { + meta : { + image : [] + }, + frames : [] + }; + var result = this.ajaxRequest.responseText.split(/\r?\n/); + var lineCount = -3; + + var currentImageId = 0; + var currentFrame = null; + var nameInNextLine = false; + + var i = 0, + j = 0, + selfOnLoaded = this.onLoaded.bind(this); + + // parser without rotation support yet! + for (i = 0; i < result.length; i++) { + result[i] = result[i].replace(/^\s+|\s+$/g, ''); + if (result[i] === '') { + nameInNextLine = i+1; + } + if (result[i].length > 0) { + if (nameInNextLine === i) { + this.atlas.meta.image.push(result[i]); + currentImageId = this.atlas.meta.image.length - 1; + this.atlas.frames.push({}); + lineCount = -3; + } else if (lineCount > 0) { + if (lineCount % 7 === 1) { // frame name + if (currentFrame != null) { //jshint ignore:line + this.atlas.frames[currentImageId][currentFrame.name] = currentFrame; + } + currentFrame = { name: result[i], frame : {} }; + } else { + var text = result[i].split(' '); + if (lineCount % 7 === 3) { // position + currentFrame.frame.x = Number(text[1].replace(',', '')); + currentFrame.frame.y = Number(text[2]); + } else if (lineCount % 7 === 4) { // size + currentFrame.frame.w = Number(text[1].replace(',', '')); + currentFrame.frame.h = Number(text[2]); + } else if (lineCount % 7 === 5) { // real size + var realSize = { + x : 0, + y : 0, + w : Number(text[1].replace(',', '')), + h : Number(text[2]) + }; + + if (realSize.w > currentFrame.frame.w || realSize.h > currentFrame.frame.h) { + currentFrame.trimmed = true; + currentFrame.realSize = realSize; + } else { + currentFrame.trimmed = false; + } + } + } + } + lineCount++; + } + } + + if (currentFrame != null) { //jshint ignore:line + this.atlas.frames[currentImageId][currentFrame.name] = currentFrame; + } + + if (this.atlas.meta.image.length > 0) { + this.images = []; + for (j = 0; j < this.atlas.meta.image.length; j++) { + // sprite sheet + var textureUrl = this.baseUrl + this.atlas.meta.image[j]; + var frameData = this.atlas.frames[j]; + this.images.push(new PIXI.ImageLoader(textureUrl, this.crossorigin)); + + for (i in frameData) { + var rect = frameData[i].frame; + if (rect) { + PIXI.TextureCache[i] = new PIXI.Texture(this.images[j].texture.baseTexture, { + x: rect.x, + y: rect.y, + width: rect.w, + height: rect.h + }); + if (frameData[i].trimmed) { + PIXI.TextureCache[i].realSize = frameData[i].realSize; + // trim in pixi not supported yet, todo update trim properties if it is done ... + PIXI.TextureCache[i].trim.x = 0; + PIXI.TextureCache[i].trim.y = 0; + } + } + } + } + + this.currentImageId = 0; + for (j = 0; j < this.images.length; j++) { + this.images[j].addEventListener('loaded', selfOnLoaded); + } + this.images[this.currentImageId].load(); + + } else { + this.onLoaded(); + } + + } else { + this.onError(); + } + } +}; + +/** + * Invoke when json file loaded + * @private + */ +PIXI.AtlasLoader.prototype.onLoaded = function () { + if (this.images.length - 1 > this.currentImageId) { + this.currentImageId++; + this.images[this.currentImageId].load(); + } else { + this.loaded = true; + this.dispatchEvent({ + type: 'loaded', + content: this + }); + } +}; + +/** + * Invoke when error occured + * @private + */ +PIXI.AtlasLoader.prototype.onError = function () { + this.dispatchEvent({ + type: 'error', + content: this + }); +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The sprite sheet loader is used to load in JSON sprite sheet data + * To generate the data you can use http://www.codeandweb.com/texturepacker and publish the 'JSON' format + * There is a free version so thats nice, although the paid version is great value for money. + * It is highly recommended to use Sprite sheets (also know as texture atlas') as it means sprite's can be batched and drawn together for highly increased rendering speed. + * Once the data has been loaded the frames are stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFromeId() + * This loader will also load the image file that the Spritesheet points to as well as the data. + * When loaded this class will dispatch a 'loaded' event + * + * @class SpriteSheetLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the sprite sheet JSON file + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.SpriteSheetLoader = function (url, crossorigin) { + /* + * i use texture packer to load the assets.. + * http://www.codeandweb.com/texturepacker + * make sure to set the format as 'JSON' + */ + PIXI.EventTarget.call(this); + + /** + * The url of the bitmap font data + * + * @property url + * @type String + */ + this.url = url; + + /** + * Whether the requests should be treated as cross origin + * + * @property crossorigin + * @type Boolean + */ + this.crossorigin = crossorigin; + + /** + * [read-only] The base url of the bitmap font data + * + * @property baseUrl + * @type String + * @readOnly + */ + this.baseUrl = url.replace(/[^\/]*$/, ''); + + /** + * The texture being loaded + * + * @property texture + * @type Texture + */ + this.texture = null; + + /** + * The frames of the sprite sheet + * + * @property frames + * @type Object + */ + this.frames = {}; +}; + +// constructor +PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; + +/** + * This will begin loading the JSON file + * + * @method load + */ +PIXI.SpriteSheetLoader.prototype.load = function () { + var scope = this; + var jsonLoader = new PIXI.JsonLoader(this.url, this.crossorigin); + jsonLoader.addEventListener('loaded', function (event) { + scope.json = event.content.json; + scope.onLoaded(); + }); + jsonLoader.load(); +}; + +/** + * Invoke when all files are loaded (json and texture) + * + * @method onLoaded + * @private + */ +PIXI.SpriteSheetLoader.prototype.onLoaded = function () { + this.dispatchEvent({ + type: 'loaded', + content: this + }); +}; + +/** + * @author Mat Groves http://matgroves.com/ @Doormat23 + */ + +/** + * The image loader class is responsible for loading images file formats ('jpeg', 'jpg', 'png' and 'gif') + * Once the image has been loaded it is stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFromeId() + * When loaded this class will dispatch a 'loaded' event + * + * @class ImageLoader + * @uses EventTarget + * @constructor + * @param url {String} The url of the image + * @param crossorigin {Boolean} Whether requests should be treated as crossorigin + */ +PIXI.ImageLoader = function(url, crossorigin) +{ + PIXI.EventTarget.call(this); + + /** + * The texture being loaded + * + * @property texture + * @type Texture + */ + this.texture = PIXI.Texture.fromImage(url, crossorigin); + + /** + * if the image is loaded with loadFramedSpriteSheet + * frames will contain the sprite sheet frames + * + */ + this.frames = []; +}; + +// constructor +PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; + +/** + * Loads image or takes it from cache + * + * @method load + */ +PIXI.ImageLoader.prototype.load = function() +{ + if(!this.texture.baseTexture.hasLoaded) + { + var scope = this; + this.texture.baseTexture.addEventListener('loaded', function() + { + scope.onLoaded(); + }); + } + else + { + this.onLoaded(); + } +}; + +/** + * Invoked when image file is loaded or it is already cached and ready to use + * + * @method onLoaded + * @private + */ +PIXI.ImageLoader.prototype.onLoaded = function() +{ + this.dispatchEvent({type: 'loaded', content: this}); +}; + +/** + * Loads image and split it to uniform sized frames + * + * + * @method loadFramedSpriteSheet + * @param frameWidth {Number} with of each frame + * @param frameHeight {Number} height of each frame + * @param textureName {String} if given, the frames will be cached in - format + */ +PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) +{ + this.frames = []; + var cols = Math.floor(this.texture.width / frameWidth); + var rows = Math.floor(this.texture.height / frameHeight); + + var i=0; + for (var y=0; y= 0; i--) { + args = [toCall[i].apply(this, args)]; + } + return args[0]; + } + }, + + each: function(obj, itr, scope) { + + if (!obj) return; + + if (ARR_EACH && obj.forEach && obj.forEach === ARR_EACH) { + + obj.forEach(itr, scope); + + } else if (obj.length === obj.length + 0) { // Is number but not NaN + + for (var key = 0, l = obj.length; key < l; key++) + if (key in obj && itr.call(scope, obj[key], key) === this.BREAK) + return; + + } else { + + for (var key in obj) + if (itr.call(scope, obj[key], key) === this.BREAK) + return; + + } + + }, + + defer: function(fnc) { + setTimeout(fnc, 0); + }, + + toArray: function(obj) { + if (obj.toArray) return obj.toArray(); + return ARR_SLICE.call(obj); + }, + + isUndefined: function(obj) { + return obj === undefined; + }, + + isNull: function(obj) { + return obj === null; + }, + + isNaN: function(obj) { + return obj !== obj; + }, + + isArray: Array.isArray || function(obj) { + return obj.constructor === Array; + }, + + isObject: function(obj) { + return obj === Object(obj); + }, + + isNumber: function(obj) { + return obj === obj+0; + }, + + isString: function(obj) { + return obj === obj+''; + }, + + isBoolean: function(obj) { + return obj === false || obj === true; + }, + + isFunction: function(obj) { + return Object.prototype.toString.call(obj) === '[object Function]'; + } + + }; + +})(); + + +dat.controllers.Controller = (function (common) { + + /** + * @class An "abstract" class that represents a given property of an object. + * + * @param {Object} object The object to be manipulated + * @param {string} property The name of the property to be manipulated + * + * @member dat.controllers + */ + var Controller = function(object, property) { + + this.initialValue = object[property]; + + /** + * Those who extend this class will put their DOM elements in here. + * @type {DOMElement} + */ + this.domElement = document.createElement('div'); + + /** + * The object to manipulate + * @type {Object} + */ + this.object = object; + + /** + * The name of the property to manipulate + * @type {String} + */ + this.property = property; + + /** + * The function to be called on change. + * @type {Function} + * @ignore + */ + this.__onChange = undefined; + + /** + * The function to be called on finishing change. + * @type {Function} + * @ignore + */ + this.__onFinishChange = undefined; + + }; + + common.extend( + + Controller.prototype, + + /** @lends dat.controllers.Controller.prototype */ + { + + /** + * Specify that a function fire every time someone changes the value with + * this Controller. + * + * @param {Function} fnc This function will be called whenever the value + * is modified via this Controller. + * @returns {dat.controllers.Controller} this + */ + onChange: function(fnc) { + this.__onChange = fnc; + return this; + }, + + /** + * Specify that a function fire every time someone "finishes" changing + * the value wih this Controller. Useful for values that change + * incrementally like numbers or strings. + * + * @param {Function} fnc This function will be called whenever + * someone "finishes" changing the value via this Controller. + * @returns {dat.controllers.Controller} this + */ + onFinishChange: function(fnc) { + this.__onFinishChange = fnc; + return this; + }, + + /** + * Change the value of object[property] + * + * @param {Object} newValue The new value of object[property] + */ + setValue: function(newValue) { + this.object[this.property] = newValue; + if (this.__onChange) { + this.__onChange.call(this, newValue); + } + this.updateDisplay(); + return this; + }, + + /** + * Gets the value of object[property] + * + * @returns {Object} The current value of object[property] + */ + getValue: function() { + return this.object[this.property]; + }, + + /** + * Refreshes the visual display of a Controller in order to keep sync + * with the object's current value. + * @returns {dat.controllers.Controller} this + */ + updateDisplay: function() { + return this; + }, + + /** + * @returns {Boolean} true if the value has deviated from initialValue + */ + isModified: function() { + return this.initialValue !== this.getValue() + } + + } + + ); + + return Controller; + + +})(dat.utils.common); + + +dat.dom.dom = (function (common) { + + var EVENT_MAP = { + 'HTMLEvents': ['change'], + 'MouseEvents': ['click','mousemove','mousedown','mouseup', 'mouseover'], + 'KeyboardEvents': ['keydown'] + }; + + var EVENT_MAP_INV = {}; + common.each(EVENT_MAP, function(v, k) { + common.each(v, function(e) { + EVENT_MAP_INV[e] = k; + }); + }); + + var CSS_VALUE_PIXELS = /(\d+(\.\d+)?)px/; + + function cssValueToPixels(val) { + + if (val === '0' || common.isUndefined(val)) return 0; + + var match = val.match(CSS_VALUE_PIXELS); + + if (!common.isNull(match)) { + return parseFloat(match[1]); + } + + // TODO ...ems? %? + + return 0; + + } + + /** + * @namespace + * @member dat.dom + */ + var dom = { + + /** + * + * @param elem + * @param selectable + */ + makeSelectable: function(elem, selectable) { + + if (elem === undefined || elem.style === undefined) return; + + elem.onselectstart = selectable ? function() { + return false; + } : function() { + }; + + elem.style.MozUserSelect = selectable ? 'auto' : 'none'; + elem.style.KhtmlUserSelect = selectable ? 'auto' : 'none'; + elem.unselectable = selectable ? 'on' : 'off'; + + }, + + /** + * + * @param elem + * @param horizontal + * @param vertical + */ + makeFullscreen: function(elem, horizontal, vertical) { + + if (common.isUndefined(horizontal)) horizontal = true; + if (common.isUndefined(vertical)) vertical = true; + + elem.style.position = 'absolute'; + + if (horizontal) { + elem.style.left = 0; + elem.style.right = 0; + } + if (vertical) { + elem.style.top = 0; + elem.style.bottom = 0; + } + + }, + + /** + * + * @param elem + * @param eventType + * @param params + */ + fakeEvent: function(elem, eventType, params, aux) { + params = params || {}; + var className = EVENT_MAP_INV[eventType]; + if (!className) { + throw new Error('Event type ' + eventType + ' not supported.'); + } + var evt = document.createEvent(className); + switch (className) { + case 'MouseEvents': + var clientX = params.x || params.clientX || 0; + var clientY = params.y || params.clientY || 0; + evt.initMouseEvent(eventType, params.bubbles || false, + params.cancelable || true, window, params.clickCount || 1, + 0, //screen X + 0, //screen Y + clientX, //client X + clientY, //client Y + false, false, false, false, 0, null); + break; + case 'KeyboardEvents': + var init = evt.initKeyboardEvent || evt.initKeyEvent; // webkit || moz + common.defaults(params, { + cancelable: true, + ctrlKey: false, + altKey: false, + shiftKey: false, + metaKey: false, + keyCode: undefined, + charCode: undefined + }); + init(eventType, params.bubbles || false, + params.cancelable, window, + params.ctrlKey, params.altKey, + params.shiftKey, params.metaKey, + params.keyCode, params.charCode); + break; + default: + evt.initEvent(eventType, params.bubbles || false, + params.cancelable || true); + break; + } + common.defaults(evt, aux); + elem.dispatchEvent(evt); + }, + + /** + * + * @param elem + * @param event + * @param func + * @param bool + */ + bind: function(elem, event, func, bool) { + bool = bool || false; + if (elem.addEventListener) + elem.addEventListener(event, func, bool); + else if (elem.attachEvent) + elem.attachEvent('on' + event, func); + return dom; + }, + + /** + * + * @param elem + * @param event + * @param func + * @param bool + */ + unbind: function(elem, event, func, bool) { + bool = bool || false; + if (elem.removeEventListener) + elem.removeEventListener(event, func, bool); + else if (elem.detachEvent) + elem.detachEvent('on' + event, func); + return dom; + }, + + /** + * + * @param elem + * @param className + */ + addClass: function(elem, className) { + if (elem.className === undefined) { + elem.className = className; + } else if (elem.className !== className) { + var classes = elem.className.split(/ +/); + if (classes.indexOf(className) == -1) { + classes.push(className); + elem.className = classes.join(' ').replace(/^\s+/, '').replace(/\s+$/, ''); + } + } + return dom; + }, + + /** + * + * @param elem + * @param className + */ + removeClass: function(elem, className) { + if (className) { + if (elem.className === undefined) { + // elem.className = className; + } else if (elem.className === className) { + elem.removeAttribute('class'); + } else { + var classes = elem.className.split(/ +/); + var index = classes.indexOf(className); + if (index != -1) { + classes.splice(index, 1); + elem.className = classes.join(' '); + } + } + } else { + elem.className = undefined; + } + return dom; + }, + + hasClass: function(elem, className) { + return new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)').test(elem.className) || false; + }, + + /** + * + * @param elem + */ + getWidth: function(elem) { + + var style = getComputedStyle(elem); + + return cssValueToPixels(style['border-left-width']) + + cssValueToPixels(style['border-right-width']) + + cssValueToPixels(style['padding-left']) + + cssValueToPixels(style['padding-right']) + + cssValueToPixels(style['width']); + }, + + /** + * + * @param elem + */ + getHeight: function(elem) { + + var style = getComputedStyle(elem); + + return cssValueToPixels(style['border-top-width']) + + cssValueToPixels(style['border-bottom-width']) + + cssValueToPixels(style['padding-top']) + + cssValueToPixels(style['padding-bottom']) + + cssValueToPixels(style['height']); + }, + + /** + * + * @param elem + */ + getOffset: function(elem) { + var offset = {left: 0, top:0}; + if (elem.offsetParent) { + do { + offset.left += elem.offsetLeft; + offset.top += elem.offsetTop; + } while (elem = elem.offsetParent); + } + return offset; + }, + + // http://stackoverflow.com/posts/2684561/revisions + /** + * + * @param elem + */ + isActive: function(elem) { + return elem === document.activeElement && ( elem.type || elem.href ); + } + + }; + + return dom; + +})(dat.utils.common); + + +dat.controllers.OptionController = (function (Controller, dom, common) { + + /** + * @class Provides a select input to alter the property of an object, using a + * list of accepted values. + * + * @extends dat.controllers.Controller + * + * @param {Object} object The object to be manipulated + * @param {string} property The name of the property to be manipulated + * @param {Object|string[]} options A map of labels to acceptable values, or + * a list of acceptable string values. + * + * @member dat.controllers + */ + var OptionController = function(object, property, options) { + + OptionController.superclass.call(this, object, property); + + var _this = this; + + /** + * The drop down menu + * @ignore + */ + this.__select = document.createElement('select'); + + if (common.isArray(options)) { + var map = {}; + common.each(options, function(element) { + map[element] = element; + }); + options = map; + } + + common.each(options, function(value, key) { + + var opt = document.createElement('option'); + opt.innerHTML = key; + opt.setAttribute('value', value); + _this.__select.appendChild(opt); + + }); + + // Acknowledge original value + this.updateDisplay(); + + dom.bind(this.__select, 'change', function() { + var desiredValue = this.options[this.selectedIndex].value; + _this.setValue(desiredValue); + }); + + this.domElement.appendChild(this.__select); + + }; + + OptionController.superclass = Controller; + + common.extend( + + OptionController.prototype, + Controller.prototype, + + { + + setValue: function(v) { + var toReturn = OptionController.superclass.prototype.setValue.call(this, v); + if (this.__onFinishChange) { + this.__onFinishChange.call(this, this.getValue()); + } + return toReturn; + }, + + updateDisplay: function() { + this.__select.value = this.getValue(); + return OptionController.superclass.prototype.updateDisplay.call(this); + } + + } + + ); + + return OptionController; + +})(dat.controllers.Controller, +dat.dom.dom, +dat.utils.common); + + +dat.controllers.NumberController = (function (Controller, common) { + + /** + * @class Represents a given property of an object that is a number. + * + * @extends dat.controllers.Controller + * + * @param {Object} object The object to be manipulated + * @param {string} property The name of the property to be manipulated + * @param {Object} [params] Optional parameters + * @param {Number} [params.min] Minimum allowed value + * @param {Number} [params.max] Maximum allowed value + * @param {Number} [params.step] Increment by which to change value + * + * @member dat.controllers + */ + var NumberController = function(object, property, params) { + + NumberController.superclass.call(this, object, property); + + params = params || {}; + + this.__min = params.min; + this.__max = params.max; + this.__step = params.step; + + if (common.isUndefined(this.__step)) { + + if (this.initialValue == 0) { + this.__impliedStep = 1; // What are we, psychics? + } else { + // Hey Doug, check this out. + this.__impliedStep = Math.pow(10, Math.floor(Math.log(this.initialValue)/Math.LN10))/10; + } + + } else { + + this.__impliedStep = this.__step; + + } + + this.__precision = numDecimals(this.__impliedStep); + + + }; + + NumberController.superclass = Controller; + + common.extend( + + NumberController.prototype, + Controller.prototype, + + /** @lends dat.controllers.NumberController.prototype */ + { + + setValue: function(v) { + + if (this.__min !== undefined && v < this.__min) { + v = this.__min; + } else if (this.__max !== undefined && v > this.__max) { + v = this.__max; + } + + if (this.__step !== undefined && v % this.__step != 0) { + v = Math.round(v / this.__step) * this.__step; + } + + return NumberController.superclass.prototype.setValue.call(this, v); + + }, + + /** + * Specify a minimum value for object[property]. + * + * @param {Number} minValue The minimum value for + * object[property] + * @returns {dat.controllers.NumberController} this + */ + min: function(v) { + this.__min = v; + return this; + }, + + /** + * Specify a maximum value for object[property]. + * + * @param {Number} maxValue The maximum value for + * object[property] + * @returns {dat.controllers.NumberController} this + */ + max: function(v) { + this.__max = v; + return this; + }, + + /** + * Specify a step value that dat.controllers.NumberController + * increments by. + * + * @param {Number} stepValue The step value for + * dat.controllers.NumberController + * @default if minimum and maximum specified increment is 1% of the + * difference otherwise stepValue is 1 + * @returns {dat.controllers.NumberController} this + */ + step: function(v) { + this.__step = v; + this.__impliedStep = v; + this.__precision = numDecimals(v); + return this; + } + + } + + ); + + function numDecimals(x) { + x = x.toString(); + if (x.indexOf('.') > -1) { + return x.length - x.indexOf('.') - 1; + } else { + return 0; + } + } + + return NumberController; + +})(dat.controllers.Controller, +dat.utils.common); + + +dat.controllers.NumberControllerBox = (function (NumberController, dom, common) { + + /** + * @class Represents a given property of an object that is a number and + * provides an input element with which to manipulate it. + * + * @extends dat.controllers.Controller + * @extends dat.controllers.NumberController + * + * @param {Object} object The object to be manipulated + * @param {string} property The name of the property to be manipulated + * @param {Object} [params] Optional parameters + * @param {Number} [params.min] Minimum allowed value + * @param {Number} [params.max] Maximum allowed value + * @param {Number} [params.step] Increment by which to change value + * + * @member dat.controllers + */ + var NumberControllerBox = function(object, property, params) { + + this.__truncationSuspended = false; + + NumberControllerBox.superclass.call(this, object, property, params); + + var _this = this; + + /** + * {Number} Previous mouse y position + * @ignore + */ + var prev_y; + + this.__input = document.createElement('input'); + this.__input.setAttribute('type', 'text'); + + // Makes it so manually specified values are not truncated. + + dom.bind(this.__input, 'change', onChange); + dom.bind(this.__input, 'blur', onBlur); + dom.bind(this.__input, 'mousedown', onMouseDown); + dom.bind(this.__input, 'keydown', function(e) { + + // When pressing entire, you can be as precise as you want. + if (e.keyCode === 13) { + _this.__truncationSuspended = true; + this.blur(); + _this.__truncationSuspended = false; + } + + }); + + function onChange() { + var attempted = parseFloat(_this.__input.value); + if (!common.isNaN(attempted)) _this.setValue(attempted); + } + + function onBlur() { + onChange(); + if (_this.__onFinishChange) { + _this.__onFinishChange.call(_this, _this.getValue()); + } + } + + function onMouseDown(e) { + dom.bind(window, 'mousemove', onMouseDrag); + dom.bind(window, 'mouseup', onMouseUp); + prev_y = e.clientY; + } + + function onMouseDrag(e) { + + var diff = prev_y - e.clientY; + _this.setValue(_this.getValue() + diff * _this.__impliedStep); + + prev_y = e.clientY; + + } + + function onMouseUp() { + dom.unbind(window, 'mousemove', onMouseDrag); + dom.unbind(window, 'mouseup', onMouseUp); + } + + this.updateDisplay(); + + this.domElement.appendChild(this.__input); + + }; + + NumberControllerBox.superclass = NumberController; + + common.extend( + + NumberControllerBox.prototype, + NumberController.prototype, + + { + + updateDisplay: function() { + + this.__input.value = this.__truncationSuspended ? this.getValue() : roundToDecimal(this.getValue(), this.__precision); + return NumberControllerBox.superclass.prototype.updateDisplay.call(this); + } + + } + + ); + + function roundToDecimal(value, decimals) { + var tenTo = Math.pow(10, decimals); + return Math.round(value * tenTo) / tenTo; + } + + return NumberControllerBox; + +})(dat.controllers.NumberController, +dat.dom.dom, +dat.utils.common); + + +dat.controllers.NumberControllerSlider = (function (NumberController, dom, css, common, styleSheet) { + + /** + * @class Represents a given property of an object that is a number, contains + * a minimum and maximum, and provides a slider element with which to + * manipulate it. It should be noted that the slider element is made up of + * <div> tags, not the html5 + * <slider> element. + * + * @extends dat.controllers.Controller + * @extends dat.controllers.NumberController + * + * @param {Object} object The object to be manipulated + * @param {string} property The name of the property to be manipulated + * @param {Number} minValue Minimum allowed value + * @param {Number} maxValue Maximum allowed value + * @param {Number} stepValue Increment by which to change value + * + * @member dat.controllers + */ + var NumberControllerSlider = function(object, property, min, max, step) { + + NumberControllerSlider.superclass.call(this, object, property, { min: min, max: max, step: step }); + + var _this = this; + + this.__background = document.createElement('div'); + this.__foreground = document.createElement('div'); + + + + dom.bind(this.__background, 'mousedown', onMouseDown); + + dom.addClass(this.__background, 'slider'); + dom.addClass(this.__foreground, 'slider-fg'); + + function onMouseDown(e) { + + dom.bind(window, 'mousemove', onMouseDrag); + dom.bind(window, 'mouseup', onMouseUp); + + onMouseDrag(e); + } + + function onMouseDrag(e) { + + e.preventDefault(); + + var offset = dom.getOffset(_this.__background); + var width = dom.getWidth(_this.__background); + + _this.setValue( + map(e.clientX, offset.left, offset.left + width, _this.__min, _this.__max) + ); + + return false; + + } + + function onMouseUp() { + dom.unbind(window, 'mousemove', onMouseDrag); + dom.unbind(window, 'mouseup', onMouseUp); + if (_this.__onFinishChange) { + _this.__onFinishChange.call(_this, _this.getValue()); + } + } + + this.updateDisplay(); + + this.__background.appendChild(this.__foreground); + this.domElement.appendChild(this.__background); + + }; + + NumberControllerSlider.superclass = NumberController; + + /** + * Injects default stylesheet for slider elements. + */ + NumberControllerSlider.useDefaultStyles = function() { + css.inject(styleSheet); + }; + + common.extend( + + NumberControllerSlider.prototype, + NumberController.prototype, + + { + + updateDisplay: function() { + var pct = (this.getValue() - this.__min)/(this.__max - this.__min); + this.__foreground.style.width = pct*100+'%'; + return NumberControllerSlider.superclass.prototype.updateDisplay.call(this); + } + + } + + + + ); + + function map(v, i1, i2, o1, o2) { + return o1 + (o2 - o1) * ((v - i1) / (i2 - i1)); + } + + return NumberControllerSlider; + +})(dat.controllers.NumberController, +dat.dom.dom, +dat.utils.css, +dat.utils.common, +"/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Google Creative Lab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}"); + + +dat.controllers.FunctionController = (function (Controller, dom, common) { + + /** + * @class Provides a GUI interface to fire a specified method, a property of an object. + * + * @extends dat.controllers.Controller + * + * @param {Object} object The object to be manipulated + * @param {string} property The name of the property to be manipulated + * + * @member dat.controllers + */ + var FunctionController = function(object, property, text) { + + FunctionController.superclass.call(this, object, property); + + var _this = this; + + this.__button = document.createElement('div'); + this.__button.innerHTML = text === undefined ? 'Fire' : text; + dom.bind(this.__button, 'click', function(e) { + e.preventDefault(); + _this.fire(); + return false; + }); + + dom.addClass(this.__button, 'button'); + + this.domElement.appendChild(this.__button); + + + }; + + FunctionController.superclass = Controller; + + common.extend( + + FunctionController.prototype, + Controller.prototype, + { + + fire: function() { + if (this.__onChange) { + this.__onChange.call(this); + } + if (this.__onFinishChange) { + this.__onFinishChange.call(this, this.getValue()); + } + this.getValue().call(this.object); + } + } + + ); + + return FunctionController; + +})(dat.controllers.Controller, +dat.dom.dom, +dat.utils.common); + + +dat.controllers.BooleanController = (function (Controller, dom, common) { + + /** + * @class Provides a checkbox input to alter the boolean property of an object. + * @extends dat.controllers.Controller + * + * @param {Object} object The object to be manipulated + * @param {string} property The name of the property to be manipulated + * + * @member dat.controllers + */ + var BooleanController = function(object, property) { + + BooleanController.superclass.call(this, object, property); + + var _this = this; + this.__prev = this.getValue(); + + this.__checkbox = document.createElement('input'); + this.__checkbox.setAttribute('type', 'checkbox'); + + + dom.bind(this.__checkbox, 'change', onChange, false); + + this.domElement.appendChild(this.__checkbox); + + // Match original value + this.updateDisplay(); + + function onChange() { + _this.setValue(!_this.__prev); + } + + }; + + BooleanController.superclass = Controller; + + common.extend( + + BooleanController.prototype, + Controller.prototype, + + { + + setValue: function(v) { + var toReturn = BooleanController.superclass.prototype.setValue.call(this, v); + if (this.__onFinishChange) { + this.__onFinishChange.call(this, this.getValue()); + } + this.__prev = this.getValue(); + return toReturn; + }, + + updateDisplay: function() { + + if (this.getValue() === true) { + this.__checkbox.setAttribute('checked', 'checked'); + this.__checkbox.checked = true; + } else { + this.__checkbox.checked = false; + } + + return BooleanController.superclass.prototype.updateDisplay.call(this); + + } + + + } + + ); + + return BooleanController; + +})(dat.controllers.Controller, +dat.dom.dom, +dat.utils.common); + + +dat.color.toString = (function (common) { + + return function(color) { + + if (color.a == 1 || common.isUndefined(color.a)) { + + var s = color.hex.toString(16); + while (s.length < 6) { + s = '0' + s; + } + + return '#' + s; + + } else { + + return 'rgba(' + Math.round(color.r) + ',' + Math.round(color.g) + ',' + Math.round(color.b) + ',' + color.a + ')'; + + } + + } + +})(dat.utils.common); + + +dat.color.interpret = (function (toString, common) { + + var result, toReturn; + + var interpret = function() { + + toReturn = false; + + var original = arguments.length > 1 ? common.toArray(arguments) : arguments[0]; + + common.each(INTERPRETATIONS, function(family) { + + if (family.litmus(original)) { + + common.each(family.conversions, function(conversion, conversionName) { + + result = conversion.read(original); + + if (toReturn === false && result !== false) { + toReturn = result; + result.conversionName = conversionName; + result.conversion = conversion; + return common.BREAK; + + } + + }); + + return common.BREAK; + + } + + }); + + return toReturn; + + }; + + var INTERPRETATIONS = [ + + // Strings + { + + litmus: common.isString, + + conversions: { + + THREE_CHAR_HEX: { + + read: function(original) { + + var test = original.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i); + if (test === null) return false; + + return { + space: 'HEX', + hex: parseInt( + '0x' + + test[1].toString() + test[1].toString() + + test[2].toString() + test[2].toString() + + test[3].toString() + test[3].toString()) + }; + + }, + + write: toString + + }, + + SIX_CHAR_HEX: { + + read: function(original) { + + var test = original.match(/^#([A-F0-9]{6})$/i); + if (test === null) return false; + + return { + space: 'HEX', + hex: parseInt('0x' + test[1].toString()) + }; + + }, + + write: toString + + }, + + CSS_RGB: { + + read: function(original) { + + var test = original.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); + if (test === null) return false; + + return { + space: 'RGB', + r: parseFloat(test[1]), + g: parseFloat(test[2]), + b: parseFloat(test[3]) + }; + + }, + + write: toString + + }, + + CSS_RGBA: { + + read: function(original) { + + var test = original.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/); + if (test === null) return false; + + return { + space: 'RGB', + r: parseFloat(test[1]), + g: parseFloat(test[2]), + b: parseFloat(test[3]), + a: parseFloat(test[4]) + }; + + }, + + write: toString + + } + + } + + }, + + // Numbers + { + + litmus: common.isNumber, + + conversions: { + + HEX: { + read: function(original) { + return { + space: 'HEX', + hex: original, + conversionName: 'HEX' + } + }, + + write: function(color) { + return color.hex; + } + } + + } + + }, + + // Arrays + { + + litmus: common.isArray, + + conversions: { + + RGB_ARRAY: { + read: function(original) { + if (original.length != 3) return false; + return { + space: 'RGB', + r: original[0], + g: original[1], + b: original[2] + }; + }, + + write: function(color) { + return [color.r, color.g, color.b]; + } + + }, + + RGBA_ARRAY: { + read: function(original) { + if (original.length != 4) return false; + return { + space: 'RGB', + r: original[0], + g: original[1], + b: original[2], + a: original[3] + }; + }, + + write: function(color) { + return [color.r, color.g, color.b, color.a]; + } + + } + + } + + }, + + // Objects + { + + litmus: common.isObject, + + conversions: { + + RGBA_OBJ: { + read: function(original) { + if (common.isNumber(original.r) && + common.isNumber(original.g) && + common.isNumber(original.b) && + common.isNumber(original.a)) { + return { + space: 'RGB', + r: original.r, + g: original.g, + b: original.b, + a: original.a + } + } + return false; + }, + + write: function(color) { + return { + r: color.r, + g: color.g, + b: color.b, + a: color.a + } + } + }, + + RGB_OBJ: { + read: function(original) { + if (common.isNumber(original.r) && + common.isNumber(original.g) && + common.isNumber(original.b)) { + return { + space: 'RGB', + r: original.r, + g: original.g, + b: original.b + } + } + return false; + }, + + write: function(color) { + return { + r: color.r, + g: color.g, + b: color.b + } + } + }, + + HSVA_OBJ: { + read: function(original) { + if (common.isNumber(original.h) && + common.isNumber(original.s) && + common.isNumber(original.v) && + common.isNumber(original.a)) { + return { + space: 'HSV', + h: original.h, + s: original.s, + v: original.v, + a: original.a + } + } + return false; + }, + + write: function(color) { + return { + h: color.h, + s: color.s, + v: color.v, + a: color.a + } + } + }, + + HSV_OBJ: { + read: function(original) { + if (common.isNumber(original.h) && + common.isNumber(original.s) && + common.isNumber(original.v)) { + return { + space: 'HSV', + h: original.h, + s: original.s, + v: original.v + } + } + return false; + }, + + write: function(color) { + return { + h: color.h, + s: color.s, + v: color.v + } + } + + } + + } + + } + + + ]; + + return interpret; + + +})(dat.color.toString, +dat.utils.common); + + +dat.GUI = dat.gui.GUI = (function (css, saveDialogueContents, styleSheet, controllerFactory, Controller, BooleanController, FunctionController, NumberControllerBox, NumberControllerSlider, OptionController, ColorController, requestAnimationFrame, CenteredDiv, dom, common) { + + css.inject(styleSheet); + + /** Outer-most className for GUI's */ + var CSS_NAMESPACE = 'dg'; + + var HIDE_KEY_CODE = 72; + + /** The only value shared between the JS and SCSS. Use caution. */ + var CLOSE_BUTTON_HEIGHT = 20; + + var DEFAULT_DEFAULT_PRESET_NAME = 'Default'; + + var SUPPORTS_LOCAL_STORAGE = (function() { + try { + return 'localStorage' in window && window['localStorage'] !== null; + } catch (e) { + return false; + } + })(); + + var SAVE_DIALOGUE; + + /** Have we yet to create an autoPlace GUI? */ + var auto_place_virgin = true; + + /** Fixed position div that auto place GUI's go inside */ + var auto_place_container; + + /** Are we hiding the GUI's ? */ + var hide = false; + + /** GUI's which should be hidden */ + var hideable_guis = []; + + /** + * A lightweight controller library for JavaScript. It allows you to easily + * manipulate variables and fire functions on the fly. + * @class + * + * @member dat.gui + * + * @param {Object} [params] + * @param {String} [params.name] The name of this GUI. + * @param {Object} [params.load] JSON object representing the saved state of + * this GUI. + * @param {Boolean} [params.auto=true] + * @param {dat.gui.GUI} [params.parent] The GUI I'm nested in. + * @param {Boolean} [params.closed] If true, starts closed + */ + var GUI = function(params) { + + var _this = this; + + /** + * Outermost DOM Element + * @type DOMElement + */ + this.domElement = document.createElement('div'); + this.__ul = document.createElement('ul'); + this.domElement.appendChild(this.__ul); + + dom.addClass(this.domElement, CSS_NAMESPACE); + + /** + * Nested GUI's by name + * @ignore + */ + this.__folders = {}; + + this.__controllers = []; + + /** + * List of objects I'm remembering for save, only used in top level GUI + * @ignore + */ + this.__rememberedObjects = []; + + /** + * Maps the index of remembered objects to a map of controllers, only used + * in top level GUI. + * + * @private + * @ignore + * + * @example + * [ + * { + * propertyName: Controller, + * anotherPropertyName: Controller + * }, + * { + * propertyName: Controller + * } + * ] + */ + this.__rememberedObjectIndecesToControllers = []; + + this.__listening = []; + + params = params || {}; + + // Default parameters + params = common.defaults(params, { + autoPlace: true, + width: GUI.DEFAULT_WIDTH + }); + + params = common.defaults(params, { + resizable: params.autoPlace, + hideable: params.autoPlace + }); + + + if (!common.isUndefined(params.load)) { + + // Explicit preset + if (params.preset) params.load.preset = params.preset; + + } else { + + params.load = { preset: DEFAULT_DEFAULT_PRESET_NAME }; + + } + + if (common.isUndefined(params.parent) && params.hideable) { + hideable_guis.push(this); + } + + // Only root level GUI's are resizable. + params.resizable = common.isUndefined(params.parent) && params.resizable; + + + if (params.autoPlace && common.isUndefined(params.scrollable)) { + params.scrollable = true; + } +// params.scrollable = common.isUndefined(params.parent) && params.scrollable === true; + + // Not part of params because I don't want people passing this in via + // constructor. Should be a 'remembered' value. + var use_local_storage = + SUPPORTS_LOCAL_STORAGE && + localStorage.getItem(getLocalStorageHash(this, 'isLocal')) === 'true'; + + var saveToLocalStorage; + + Object.defineProperties(this, + + /** @lends dat.gui.GUI.prototype */ + { + + /** + * The parent GUI + * @type dat.gui.GUI + */ + parent: { + get: function() { + return params.parent; + } + }, + + scrollable: { + get: function() { + return params.scrollable; + } + }, + + /** + * Handles GUI's element placement for you + * @type Boolean + */ + autoPlace: { + get: function() { + return params.autoPlace; + } + }, + + /** + * The identifier for a set of saved values + * @type String + */ + preset: { + + get: function() { + if (_this.parent) { + return _this.getRoot().preset; + } else { + return params.load.preset; + } + }, + + set: function(v) { + if (_this.parent) { + _this.getRoot().preset = v; + } else { + params.load.preset = v; + } + setPresetSelectIndex(this); + _this.revert(); + } + + }, + + /** + * The width of GUI element + * @type Number + */ + width: { + get: function() { + return params.width; + }, + set: function(v) { + params.width = v; + setWidth(_this, v); + } + }, + + /** + * The name of GUI. Used for folders. i.e + * a folder's name + * @type String + */ + name: { + get: function() { + return params.name; + }, + set: function(v) { + // TODO Check for collisions among sibling folders + params.name = v; + if (title_row_name) { + title_row_name.innerHTML = params.name; + } + } + }, + + /** + * Whether the GUI is collapsed or not + * @type Boolean + */ + closed: { + get: function() { + return params.closed; + }, + set: function(v) { + params.closed = v; + if (params.closed) { + dom.addClass(_this.__ul, GUI.CLASS_CLOSED); + } else { + dom.removeClass(_this.__ul, GUI.CLASS_CLOSED); + } + // For browsers that aren't going to respect the CSS transition, + // Lets just check our height against the window height right off + // the bat. + this.onResize(); + + if (_this.__closeButton) { + _this.__closeButton.innerHTML = v ? GUI.TEXT_OPEN : GUI.TEXT_CLOSED; + } + } + }, + + /** + * Contains all presets + * @type Object + */ + load: { + get: function() { + return params.load; + } + }, + + /** + * Determines whether or not to use localStorage as the means for + * remembering + * @type Boolean + */ + useLocalStorage: { + + get: function() { + return use_local_storage; + }, + set: function(bool) { + if (SUPPORTS_LOCAL_STORAGE) { + use_local_storage = bool; + if (bool) { + dom.bind(window, 'unload', saveToLocalStorage); + } else { + dom.unbind(window, 'unload', saveToLocalStorage); + } + localStorage.setItem(getLocalStorageHash(_this, 'isLocal'), bool); + } + } + + } + + }); + + // Are we a root level GUI? + if (common.isUndefined(params.parent)) { + + params.closed = false; + + dom.addClass(this.domElement, GUI.CLASS_MAIN); + dom.makeSelectable(this.domElement, false); + + // Are we supposed to be loading locally? + if (SUPPORTS_LOCAL_STORAGE) { + + if (use_local_storage) { + + _this.useLocalStorage = true; + + var saved_gui = localStorage.getItem(getLocalStorageHash(this, 'gui')); + + if (saved_gui) { + params.load = JSON.parse(saved_gui); + } + + } + + } + + this.__closeButton = document.createElement('div'); + this.__closeButton.innerHTML = GUI.TEXT_CLOSED; + dom.addClass(this.__closeButton, GUI.CLASS_CLOSE_BUTTON); + this.domElement.appendChild(this.__closeButton); + + dom.bind(this.__closeButton, 'click', function() { + + _this.closed = !_this.closed; + + + }); + + + // Oh, you're a nested GUI! + } else { + + if (params.closed === undefined) { + params.closed = true; + } + + var title_row_name = document.createTextNode(params.name); + dom.addClass(title_row_name, 'controller-name'); + + var title_row = addRow(_this, title_row_name); + + var on_click_title = function(e) { + e.preventDefault(); + _this.closed = !_this.closed; + return false; + }; + + dom.addClass(this.__ul, GUI.CLASS_CLOSED); + + dom.addClass(title_row, 'title'); + dom.bind(title_row, 'click', on_click_title); + + if (!params.closed) { + this.closed = false; + } + + } + + if (params.autoPlace) { + + if (common.isUndefined(params.parent)) { + + if (auto_place_virgin) { + auto_place_container = document.createElement('div'); + dom.addClass(auto_place_container, CSS_NAMESPACE); + dom.addClass(auto_place_container, GUI.CLASS_AUTO_PLACE_CONTAINER); + document.body.appendChild(auto_place_container); + auto_place_virgin = false; + } + + // Put it in the dom for you. + auto_place_container.appendChild(this.domElement); + + // Apply the auto styles + dom.addClass(this.domElement, GUI.CLASS_AUTO_PLACE); + + } + + + // Make it not elastic. + if (!this.parent) setWidth(_this, params.width); + + } + + dom.bind(window, 'resize', function() { _this.onResize() }); + dom.bind(this.__ul, 'webkitTransitionEnd', function() { _this.onResize(); }); + dom.bind(this.__ul, 'transitionend', function() { _this.onResize() }); + dom.bind(this.__ul, 'oTransitionEnd', function() { _this.onResize() }); + this.onResize(); + + + if (params.resizable) { + addResizeHandle(this); + } + + saveToLocalStorage = function () { + if (SUPPORTS_LOCAL_STORAGE && localStorage.getItem(getLocalStorageHash(_this, 'isLocal')) === 'true') { + localStorage.setItem(getLocalStorageHash(_this, 'gui'), JSON.stringify(_this.getSaveObject())); + } + } + + // expose this method publicly + this.saveToLocalStorageIfPossible = saveToLocalStorage; + + var root = _this.getRoot(); + function resetWidth() { + var root = _this.getRoot(); + root.width += 1; + common.defer(function() { + root.width -= 1; + }); + } + + if (!params.parent) { + resetWidth(); + } + + }; + + GUI.toggleHide = function() { + + hide = !hide; + common.each(hideable_guis, function(gui) { + gui.domElement.style.zIndex = hide ? -999 : 999; + gui.domElement.style.opacity = hide ? 0 : 1; + }); + }; + + GUI.CLASS_AUTO_PLACE = 'a'; + GUI.CLASS_AUTO_PLACE_CONTAINER = 'ac'; + GUI.CLASS_MAIN = 'main'; + GUI.CLASS_CONTROLLER_ROW = 'cr'; + GUI.CLASS_TOO_TALL = 'taller-than-window'; + GUI.CLASS_CLOSED = 'closed'; + GUI.CLASS_CLOSE_BUTTON = 'close-button'; + GUI.CLASS_DRAG = 'drag'; + + GUI.DEFAULT_WIDTH = 245; + GUI.TEXT_CLOSED = 'Close Controls'; + GUI.TEXT_OPEN = 'Open Controls'; + + dom.bind(window, 'keydown', function(e) { + + if (document.activeElement.type !== 'text' && + (e.which === HIDE_KEY_CODE || e.keyCode == HIDE_KEY_CODE)) { + GUI.toggleHide(); + } + + }, false); + + common.extend( + + GUI.prototype, + + /** @lends dat.gui.GUI */ + { + + /** + * @param object + * @param property + * @returns {dat.controllers.Controller} The new controller that was added. + * @instance + */ + add: function(object, property) { + + return add( + this, + object, + property, + { + factoryArgs: Array.prototype.slice.call(arguments, 2) + } + ); + + }, + + /** + * @param object + * @param property + * @returns {dat.controllers.ColorController} The new controller that was added. + * @instance + */ + addColor: function(object, property) { + + return add( + this, + object, + property, + { + color: true + } + ); + + }, + + /** + * @param controller + * @instance + */ + remove: function(controller) { + + // TODO listening? + this.__ul.removeChild(controller.__li); + this.__controllers.slice(this.__controllers.indexOf(controller), 1); + var _this = this; + common.defer(function() { + _this.onResize(); + }); + + }, + + destroy: function() { + + if (this.autoPlace) { + auto_place_container.removeChild(this.domElement); + } + + }, + + /** + * @param name + * @returns {dat.gui.GUI} The new folder. + * @throws {Error} if this GUI already has a folder by the specified + * name + * @instance + */ + addFolder: function(name) { + + // We have to prevent collisions on names in order to have a key + // by which to remember saved values + if (this.__folders[name] !== undefined) { + throw new Error('You already have a folder in this GUI by the' + + ' name "' + name + '"'); + } + + var new_gui_params = { name: name, parent: this }; + + // We need to pass down the autoPlace trait so that we can + // attach event listeners to open/close folder actions to + // ensure that a scrollbar appears if the window is too short. + new_gui_params.autoPlace = this.autoPlace; + + // Do we have saved appearance data for this folder? + + if (this.load && // Anything loaded? + this.load.folders && // Was my parent a dead-end? + this.load.folders[name]) { // Did daddy remember me? + + // Start me closed if I was closed + new_gui_params.closed = this.load.folders[name].closed; + + // Pass down the loaded data + new_gui_params.load = this.load.folders[name]; + + } + + var gui = new GUI(new_gui_params); + this.__folders[name] = gui; + + var li = addRow(this, gui.domElement); + dom.addClass(li, 'folder'); + return gui; + + }, + + open: function() { + this.closed = false; + }, + + close: function() { + this.closed = true; + }, + + onResize: function() { + + var root = this.getRoot(); + + if (root.scrollable) { + + var top = dom.getOffset(root.__ul).top; + var h = 0; + + common.each(root.__ul.childNodes, function(node) { + if (! (root.autoPlace && node === root.__save_row)) + h += dom.getHeight(node); + }); + + if (window.innerHeight - top - CLOSE_BUTTON_HEIGHT < h) { + dom.addClass(root.domElement, GUI.CLASS_TOO_TALL); + root.__ul.style.height = window.innerHeight - top - CLOSE_BUTTON_HEIGHT + 'px'; + } else { + dom.removeClass(root.domElement, GUI.CLASS_TOO_TALL); + root.__ul.style.height = 'auto'; + } + + } + + if (root.__resize_handle) { + common.defer(function() { + root.__resize_handle.style.height = root.__ul.offsetHeight + 'px'; + }); + } + + if (root.__closeButton) { + root.__closeButton.style.width = root.width + 'px'; + } + + }, + + /** + * Mark objects for saving. The order of these objects cannot change as + * the GUI grows. When remembering new objects, append them to the end + * of the list. + * + * @param {Object...} objects + * @throws {Error} if not called on a top level GUI. + * @instance + */ + remember: function() { + + if (common.isUndefined(SAVE_DIALOGUE)) { + SAVE_DIALOGUE = new CenteredDiv(); + SAVE_DIALOGUE.domElement.innerHTML = saveDialogueContents; + } + + if (this.parent) { + throw new Error("You can only call remember on a top level GUI."); + } + + var _this = this; + + common.each(Array.prototype.slice.call(arguments), function(object) { + if (_this.__rememberedObjects.length == 0) { + addSaveMenu(_this); + } + if (_this.__rememberedObjects.indexOf(object) == -1) { + _this.__rememberedObjects.push(object); + } + }); + + if (this.autoPlace) { + // Set save row width + setWidth(this, this.width); + } + + }, + + /** + * @returns {dat.gui.GUI} the topmost parent GUI of a nested GUI. + * @instance + */ + getRoot: function() { + var gui = this; + while (gui.parent) { + gui = gui.parent; + } + return gui; + }, + + /** + * @returns {Object} a JSON object representing the current state of + * this GUI as well as its remembered properties. + * @instance + */ + getSaveObject: function() { + + var toReturn = this.load; + + toReturn.closed = this.closed; + + // Am I remembering any values? + if (this.__rememberedObjects.length > 0) { + + toReturn.preset = this.preset; + + if (!toReturn.remembered) { + toReturn.remembered = {}; + } + + toReturn.remembered[this.preset] = getCurrentPreset(this); + + } + + toReturn.folders = {}; + common.each(this.__folders, function(element, key) { + toReturn.folders[key] = element.getSaveObject(); + }); + + return toReturn; + + }, + + save: function() { + + if (!this.load.remembered) { + this.load.remembered = {}; + } + + this.load.remembered[this.preset] = getCurrentPreset(this); + markPresetModified(this, false); + this.saveToLocalStorageIfPossible(); + + }, + + saveAs: function(presetName) { + + if (!this.load.remembered) { + + // Retain default values upon first save + this.load.remembered = {}; + this.load.remembered[DEFAULT_DEFAULT_PRESET_NAME] = getCurrentPreset(this, true); + + } + + this.load.remembered[presetName] = getCurrentPreset(this); + this.preset = presetName; + addPresetOption(this, presetName, true); + this.saveToLocalStorageIfPossible(); + + }, + + revert: function(gui) { + + common.each(this.__controllers, function(controller) { + // Make revert work on Default. + if (!this.getRoot().load.remembered) { + controller.setValue(controller.initialValue); + } else { + recallSavedValue(gui || this.getRoot(), controller); + } + }, this); + + common.each(this.__folders, function(folder) { + folder.revert(folder); + }); + + if (!gui) { + markPresetModified(this.getRoot(), false); + } + + + }, + + listen: function(controller) { + + var init = this.__listening.length == 0; + this.__listening.push(controller); + if (init) updateDisplays(this.__listening); + + } + + } + + ); + + function add(gui, object, property, params) { + + if (object[property] === undefined) { + throw new Error("Object " + object + " has no property \"" + property + "\""); + } + + var controller; + + if (params.color) { + + controller = new ColorController(object, property); + + } else { + + var factoryArgs = [object,property].concat(params.factoryArgs); + controller = controllerFactory.apply(gui, factoryArgs); + + } + + if (params.before instanceof Controller) { + params.before = params.before.__li; + } + + recallSavedValue(gui, controller); + + dom.addClass(controller.domElement, 'c'); + + var name = document.createElement('span'); + dom.addClass(name, 'property-name'); + name.innerHTML = controller.property; + + var container = document.createElement('div'); + container.appendChild(name); + container.appendChild(controller.domElement); + + var li = addRow(gui, container, params.before); + + dom.addClass(li, GUI.CLASS_CONTROLLER_ROW); + dom.addClass(li, typeof controller.getValue()); + + augmentController(gui, li, controller); + + gui.__controllers.push(controller); + + return controller; + + } + + /** + * Add a row to the end of the GUI or before another row. + * + * @param gui + * @param [dom] If specified, inserts the dom content in the new row + * @param [liBefore] If specified, places the new row before another row + */ + function addRow(gui, dom, liBefore) { + var li = document.createElement('li'); + if (dom) li.appendChild(dom); + if (liBefore) { + gui.__ul.insertBefore(li, params.before); + } else { + gui.__ul.appendChild(li); + } + gui.onResize(); + return li; + } + + function augmentController(gui, li, controller) { + + controller.__li = li; + controller.__gui = gui; + + common.extend(controller, { + + options: function(options) { + + if (arguments.length > 1) { + controller.remove(); + + return add( + gui, + controller.object, + controller.property, + { + before: controller.__li.nextElementSibling, + factoryArgs: [common.toArray(arguments)] + } + ); + + } + + if (common.isArray(options) || common.isObject(options)) { + controller.remove(); + + return add( + gui, + controller.object, + controller.property, + { + before: controller.__li.nextElementSibling, + factoryArgs: [options] + } + ); + + } + + }, + + name: function(v) { + controller.__li.firstElementChild.firstElementChild.innerHTML = v; + return controller; + }, + + listen: function() { + controller.__gui.listen(controller); + return controller; + }, + + remove: function() { + controller.__gui.remove(controller); + return controller; + } + + }); + + // All sliders should be accompanied by a box. + if (controller instanceof NumberControllerSlider) { + + var box = new NumberControllerBox(controller.object, controller.property, + { min: controller.__min, max: controller.__max, step: controller.__step }); + + common.each(['updateDisplay', 'onChange', 'onFinishChange'], function(method) { + var pc = controller[method]; + var pb = box[method]; + controller[method] = box[method] = function() { + var args = Array.prototype.slice.call(arguments); + pc.apply(controller, args); + return pb.apply(box, args); + } + }); + + dom.addClass(li, 'has-slider'); + controller.domElement.insertBefore(box.domElement, controller.domElement.firstElementChild); + + } + else if (controller instanceof NumberControllerBox) { + + var r = function(returned) { + + // Have we defined both boundaries? + if (common.isNumber(controller.__min) && common.isNumber(controller.__max)) { + + // Well, then lets just replace this with a slider. + controller.remove(); + return add( + gui, + controller.object, + controller.property, + { + before: controller.__li.nextElementSibling, + factoryArgs: [controller.__min, controller.__max, controller.__step] + }); + + } + + return returned; + + }; + + controller.min = common.compose(r, controller.min); + controller.max = common.compose(r, controller.max); + + } + else if (controller instanceof BooleanController) { + + dom.bind(li, 'click', function() { + dom.fakeEvent(controller.__checkbox, 'click'); + }); + + dom.bind(controller.__checkbox, 'click', function(e) { + e.stopPropagation(); // Prevents double-toggle + }) + + } + else if (controller instanceof FunctionController) { + + dom.bind(li, 'click', function() { + dom.fakeEvent(controller.__button, 'click'); + }); + + dom.bind(li, 'mouseover', function() { + dom.addClass(controller.__button, 'hover'); + }); + + dom.bind(li, 'mouseout', function() { + dom.removeClass(controller.__button, 'hover'); + }); + + } + else if (controller instanceof ColorController) { + + dom.addClass(li, 'color'); + controller.updateDisplay = common.compose(function(r) { + li.style.borderLeftColor = controller.__color.toString(); + return r; + }, controller.updateDisplay); + + controller.updateDisplay(); + + } + + controller.setValue = common.compose(function(r) { + if (gui.getRoot().__preset_select && controller.isModified()) { + markPresetModified(gui.getRoot(), true); + } + return r; + }, controller.setValue); + + } + + function recallSavedValue(gui, controller) { + + // Find the topmost GUI, that's where remembered objects live. + var root = gui.getRoot(); + + // Does the object we're controlling match anything we've been told to + // remember? + var matched_index = root.__rememberedObjects.indexOf(controller.object); + + // Why yes, it does! + if (matched_index != -1) { + + // Let me fetch a map of controllers for thcommon.isObject. + var controller_map = + root.__rememberedObjectIndecesToControllers[matched_index]; + + // Ohp, I believe this is the first controller we've created for this + // object. Lets make the map fresh. + if (controller_map === undefined) { + controller_map = {}; + root.__rememberedObjectIndecesToControllers[matched_index] = + controller_map; + } + + // Keep track of this controller + controller_map[controller.property] = controller; + + // Okay, now have we saved any values for this controller? + if (root.load && root.load.remembered) { + + var preset_map = root.load.remembered; + + // Which preset are we trying to load? + var preset; + + if (preset_map[gui.preset]) { + + preset = preset_map[gui.preset]; + + } else if (preset_map[DEFAULT_DEFAULT_PRESET_NAME]) { + + // Uhh, you can have the default instead? + preset = preset_map[DEFAULT_DEFAULT_PRESET_NAME]; + + } else { + + // Nada. + + return; + + } + + + // Did the loaded object remember thcommon.isObject? + if (preset[matched_index] && + + // Did we remember this particular property? + preset[matched_index][controller.property] !== undefined) { + + // We did remember something for this guy ... + var value = preset[matched_index][controller.property]; + + // And that's what it is. + controller.initialValue = value; + controller.setValue(value); + + } + + } + + } + + } + + function getLocalStorageHash(gui, key) { + // TODO how does this deal with multiple GUI's? + return document.location.href + '.' + key; + + } + + function addSaveMenu(gui) { + + var div = gui.__save_row = document.createElement('li'); + + dom.addClass(gui.domElement, 'has-save'); + + gui.__ul.insertBefore(div, gui.__ul.firstChild); + + dom.addClass(div, 'save-row'); + + var gears = document.createElement('span'); + gears.innerHTML = ' '; + dom.addClass(gears, 'button gears'); + + // TODO replace with FunctionController + var button = document.createElement('span'); + button.innerHTML = 'Save'; + dom.addClass(button, 'button'); + dom.addClass(button, 'save'); + + var button2 = document.createElement('span'); + button2.innerHTML = 'New'; + dom.addClass(button2, 'button'); + dom.addClass(button2, 'save-as'); + + var button3 = document.createElement('span'); + button3.innerHTML = 'Revert'; + dom.addClass(button3, 'button'); + dom.addClass(button3, 'revert'); + + var select = gui.__preset_select = document.createElement('select'); + + if (gui.load && gui.load.remembered) { + + common.each(gui.load.remembered, function(value, key) { + addPresetOption(gui, key, key == gui.preset); + }); + + } else { + addPresetOption(gui, DEFAULT_DEFAULT_PRESET_NAME, false); + } + + dom.bind(select, 'change', function() { + + + for (var index = 0; index < gui.__preset_select.length; index++) { + gui.__preset_select[index].innerHTML = gui.__preset_select[index].value; + } + + gui.preset = this.value; + + }); + + div.appendChild(select); + div.appendChild(gears); + div.appendChild(button); + div.appendChild(button2); + div.appendChild(button3); + + if (SUPPORTS_LOCAL_STORAGE) { + + var saveLocally = document.getElementById('dg-save-locally'); + var explain = document.getElementById('dg-local-explain'); + + saveLocally.style.display = 'block'; + + var localStorageCheckBox = document.getElementById('dg-local-storage'); + + if (localStorage.getItem(getLocalStorageHash(gui, 'isLocal')) === 'true') { + localStorageCheckBox.setAttribute('checked', 'checked'); + } + + function showHideExplain() { + explain.style.display = gui.useLocalStorage ? 'block' : 'none'; + } + + showHideExplain(); + + // TODO: Use a boolean controller, fool! + dom.bind(localStorageCheckBox, 'change', function() { + gui.useLocalStorage = !gui.useLocalStorage; + showHideExplain(); + }); + + } + + var newConstructorTextArea = document.getElementById('dg-new-constructor'); + + dom.bind(newConstructorTextArea, 'keydown', function(e) { + if (e.metaKey && (e.which === 67 || e.keyCode == 67)) { + SAVE_DIALOGUE.hide(); + } + }); + + dom.bind(gears, 'click', function() { + newConstructorTextArea.innerHTML = JSON.stringify(gui.getSaveObject(), undefined, 2); + SAVE_DIALOGUE.show(); + newConstructorTextArea.focus(); + newConstructorTextArea.select(); + }); + + dom.bind(button, 'click', function() { + gui.save(); + }); + + dom.bind(button2, 'click', function() { + var presetName = prompt('Enter a new preset name.'); + if (presetName) gui.saveAs(presetName); + }); + + dom.bind(button3, 'click', function() { + gui.revert(); + }); + +// div.appendChild(button2); + + } + + function addResizeHandle(gui) { + + gui.__resize_handle = document.createElement('div'); + + common.extend(gui.__resize_handle.style, { + + width: '6px', + marginLeft: '-3px', + height: '200px', + cursor: 'ew-resize', + position: 'absolute' +// border: '1px solid blue' + + }); + + var pmouseX; + + dom.bind(gui.__resize_handle, 'mousedown', dragStart); + dom.bind(gui.__closeButton, 'mousedown', dragStart); + + gui.domElement.insertBefore(gui.__resize_handle, gui.domElement.firstElementChild); + + function dragStart(e) { + + e.preventDefault(); + + pmouseX = e.clientX; + + dom.addClass(gui.__closeButton, GUI.CLASS_DRAG); + dom.bind(window, 'mousemove', drag); + dom.bind(window, 'mouseup', dragStop); + + return false; + + } + + function drag(e) { + + e.preventDefault(); + + gui.width += pmouseX - e.clientX; + gui.onResize(); + pmouseX = e.clientX; + + return false; + + } + + function dragStop() { + + dom.removeClass(gui.__closeButton, GUI.CLASS_DRAG); + dom.unbind(window, 'mousemove', drag); + dom.unbind(window, 'mouseup', dragStop); + + } + + } + + function setWidth(gui, w) { + gui.domElement.style.width = w + 'px'; + // Auto placed save-rows are position fixed, so we have to + // set the width manually if we want it to bleed to the edge + if (gui.__save_row && gui.autoPlace) { + gui.__save_row.style.width = w + 'px'; + }if (gui.__closeButton) { + gui.__closeButton.style.width = w + 'px'; + } + } + + function getCurrentPreset(gui, useInitialValues) { + + var toReturn = {}; + + // For each object I'm remembering + common.each(gui.__rememberedObjects, function(val, index) { + + var saved_values = {}; + + // The controllers I've made for thcommon.isObject by property + var controller_map = + gui.__rememberedObjectIndecesToControllers[index]; + + // Remember each value for each property + common.each(controller_map, function(controller, property) { + saved_values[property] = useInitialValues ? controller.initialValue : controller.getValue(); + }); + + // Save the values for thcommon.isObject + toReturn[index] = saved_values; + + }); + + return toReturn; + + } + + function addPresetOption(gui, name, setSelected) { + var opt = document.createElement('option'); + opt.innerHTML = name; + opt.value = name; + gui.__preset_select.appendChild(opt); + if (setSelected) { + gui.__preset_select.selectedIndex = gui.__preset_select.length - 1; + } + } + + function setPresetSelectIndex(gui) { + for (var index = 0; index < gui.__preset_select.length; index++) { + if (gui.__preset_select[index].value == gui.preset) { + gui.__preset_select.selectedIndex = index; + } + } + } + + function markPresetModified(gui, modified) { + var opt = gui.__preset_select[gui.__preset_select.selectedIndex]; +// console.log('mark', modified, opt); + if (modified) { + opt.innerHTML = opt.value + "*"; + } else { + opt.innerHTML = opt.value; + } + } + + function updateDisplays(controllerArray) { + + + if (controllerArray.length != 0) { + + requestAnimationFrame(function() { + updateDisplays(controllerArray); + }); + + } + + common.each(controllerArray, function(c) { + c.updateDisplay(); + }); + + } + + return GUI; + +})(dat.utils.css, +"
\n\n Here's the new load parameter for your GUI's constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI's constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n \n
\n \n
\n\n
", +".dg {\n /** Clear list styles */\n /* Auto-place container */\n /* Auto-placed GUI's */\n /* Line items that don't contain folders. */\n /** Folder names */\n /** Hides closed items */\n /** Controller row */\n /** Name-half (left) */\n /** Controller-half (right) */\n /** Controller placement */\n /** Shorter number boxes when slider is present. */\n /** Ensure the entire boolean and function row shows a hand */ }\n .dg ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n clear: both; }\n .dg.ac {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 0;\n z-index: 0; }\n .dg:not(.ac) .main {\n /** Exclude mains in ac so that we don't hide close button */\n overflow: hidden; }\n .dg.main {\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear; }\n .dg.main.taller-than-window {\n overflow-y: auto; }\n .dg.main.taller-than-window .close-button {\n opacity: 1;\n /* TODO, these are style notes */\n margin-top: -1px;\n border-top: 1px solid #2c2c2c; }\n .dg.main ul.closed .close-button {\n opacity: 1 !important; }\n .dg.main:hover .close-button,\n .dg.main .close-button.drag {\n opacity: 1; }\n .dg.main .close-button {\n /*opacity: 0;*/\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear;\n border: 0;\n position: absolute;\n line-height: 19px;\n height: 20px;\n /* TODO, these are style notes */\n cursor: pointer;\n text-align: center;\n background-color: #000; }\n .dg.main .close-button:hover {\n background-color: #111; }\n .dg.a {\n float: right;\n margin-right: 15px;\n overflow-x: hidden; }\n .dg.a.has-save > ul {\n margin-top: 27px; }\n .dg.a.has-save > ul.closed {\n margin-top: 0; }\n .dg.a .save-row {\n position: fixed;\n top: 0;\n z-index: 1002; }\n .dg li {\n -webkit-transition: height 0.1s ease-out;\n -o-transition: height 0.1s ease-out;\n -moz-transition: height 0.1s ease-out;\n transition: height 0.1s ease-out; }\n .dg li:not(.folder) {\n cursor: auto;\n height: 27px;\n line-height: 27px;\n overflow: hidden;\n padding: 0 4px 0 5px; }\n .dg li.folder {\n padding: 0;\n border-left: 4px solid rgba(0, 0, 0, 0); }\n .dg li.title {\n cursor: pointer;\n margin-left: -4px; }\n .dg .closed li:not(.title),\n .dg .closed ul li,\n .dg .closed ul li > * {\n height: 0;\n overflow: hidden;\n border: 0; }\n .dg .cr {\n clear: both;\n padding-left: 3px;\n height: 27px; }\n .dg .property-name {\n cursor: default;\n float: left;\n clear: left;\n width: 40%;\n overflow: hidden;\n text-overflow: ellipsis; }\n .dg .c {\n float: left;\n width: 60%; }\n .dg .c input[type=text] {\n border: 0;\n margin-top: 4px;\n padding: 3px;\n width: 100%;\n float: right; }\n .dg .has-slider input[type=text] {\n width: 30%;\n /*display: none;*/\n margin-left: 0; }\n .dg .slider {\n float: left;\n width: 66%;\n margin-left: -5px;\n margin-right: 0;\n height: 19px;\n margin-top: 4px; }\n .dg .slider-fg {\n height: 100%; }\n .dg .c input[type=checkbox] {\n margin-top: 9px; }\n .dg .c select {\n margin-top: 5px; }\n .dg .cr.function,\n .dg .cr.function .property-name,\n .dg .cr.function *,\n .dg .cr.boolean,\n .dg .cr.boolean * {\n cursor: pointer; }\n .dg .selector {\n display: none;\n position: absolute;\n margin-left: -9px;\n margin-top: 23px;\n z-index: 10; }\n .dg .c:hover .selector,\n .dg .selector.drag {\n display: block; }\n .dg li.save-row {\n padding: 0; }\n .dg li.save-row .button {\n display: inline-block;\n padding: 0px 6px; }\n .dg.dialogue {\n background-color: #222;\n width: 460px;\n padding: 15px;\n font-size: 13px;\n line-height: 15px; }\n\n/* TODO Separate style and structure */\n#dg-new-constructor {\n padding: 10px;\n color: #222;\n font-family: Monaco, monospace;\n font-size: 10px;\n border: 0;\n resize: none;\n box-shadow: inset 1px 1px 1px #888;\n word-wrap: break-word;\n margin: 12px 0;\n display: block;\n width: 440px;\n overflow-y: scroll;\n height: 100px;\n position: relative; }\n\n#dg-local-explain {\n display: none;\n font-size: 11px;\n line-height: 17px;\n border-radius: 3px;\n background-color: #333;\n padding: 8px;\n margin-top: 10px; }\n #dg-local-explain code {\n font-size: 10px; }\n\n#dat-gui-save-locally {\n display: none; }\n\n/** Main type */\n.dg {\n color: #eee;\n font: 11px 'Lucida Grande', sans-serif;\n text-shadow: 0 -1px 0 #111;\n /** Auto place */\n /* Controller row,
  • */\n /** Controllers */ }\n .dg.main {\n /** Scrollbar */ }\n .dg.main::-webkit-scrollbar {\n width: 5px;\n background: #1a1a1a; }\n .dg.main::-webkit-scrollbar-corner {\n height: 0;\n display: none; }\n .dg.main::-webkit-scrollbar-thumb {\n border-radius: 5px;\n background: #676767; }\n .dg li:not(.folder) {\n background: #1a1a1a;\n border-bottom: 1px solid #2c2c2c; }\n .dg li.save-row {\n line-height: 25px;\n background: #dad5cb;\n border: 0; }\n .dg li.save-row select {\n margin-left: 5px;\n width: 108px; }\n .dg li.save-row .button {\n margin-left: 5px;\n margin-top: 1px;\n border-radius: 2px;\n font-size: 9px;\n line-height: 7px;\n padding: 4px 4px 5px 4px;\n background: #c5bdad;\n color: #fff;\n text-shadow: 0 1px 0 #b0a58f;\n box-shadow: 0 -1px 0 #b0a58f;\n cursor: pointer; }\n .dg li.save-row .button.gears {\n background: #c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;\n height: 7px;\n width: 8px; }\n .dg li.save-row .button:hover {\n background-color: #bab19e;\n box-shadow: 0 -1px 0 #b0a58f; }\n .dg li.folder {\n border-bottom: 0; }\n .dg li.title {\n padding-left: 16px;\n background: black url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;\n cursor: pointer;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2); }\n .dg .closed li.title {\n background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==); }\n .dg .cr.boolean {\n border-left: 3px solid #806787; }\n .dg .cr.function {\n border-left: 3px solid #e61d5f; }\n .dg .cr.number {\n border-left: 3px solid #2fa1d6; }\n .dg .cr.number input[type=text] {\n color: #2fa1d6; }\n .dg .cr.string {\n border-left: 3px solid #1ed36f; }\n .dg .cr.string input[type=text] {\n color: #1ed36f; }\n .dg .cr.function:hover, .dg .cr.boolean:hover {\n background: #111; }\n .dg .c input[type=text] {\n background: #303030;\n outline: none; }\n .dg .c input[type=text]:hover {\n background: #3c3c3c; }\n .dg .c input[type=text]:focus {\n background: #494949;\n color: #fff; }\n .dg .c .slider {\n background: #303030;\n cursor: ew-resize; }\n .dg .c .slider-fg {\n background: #2fa1d6; }\n .dg .c .slider:hover {\n background: #3c3c3c; }\n .dg .c .slider:hover .slider-fg {\n background: #44abda; }\n", +dat.controllers.factory = (function (OptionController, NumberControllerBox, NumberControllerSlider, StringController, FunctionController, BooleanController, common) { + + return function(object, property) { + + var initialValue = object[property]; + + // Providing options? + if (common.isArray(arguments[2]) || common.isObject(arguments[2])) { + return new OptionController(object, property, arguments[2]); + } + + // Providing a map? + + if (common.isNumber(initialValue)) { + + if (common.isNumber(arguments[2]) && common.isNumber(arguments[3])) { + + // Has min and max. + return new NumberControllerSlider(object, property, arguments[2], arguments[3]); + + } else { + + return new NumberControllerBox(object, property, { min: arguments[2], max: arguments[3] }); + + } + + } + + if (common.isString(initialValue)) { + return new StringController(object, property); + } + + if (common.isFunction(initialValue)) { + return new FunctionController(object, property, ''); + } + + if (common.isBoolean(initialValue)) { + return new BooleanController(object, property); + } + + } + + })(dat.controllers.OptionController, +dat.controllers.NumberControllerBox, +dat.controllers.NumberControllerSlider, +dat.controllers.StringController = (function (Controller, dom, common) { + + /** + * @class Provides a text input to alter the string property of an object. + * + * @extends dat.controllers.Controller + * + * @param {Object} object The object to be manipulated + * @param {string} property The name of the property to be manipulated + * + * @member dat.controllers + */ + var StringController = function(object, property) { + + StringController.superclass.call(this, object, property); + + var _this = this; + + this.__input = document.createElement('input'); + this.__input.setAttribute('type', 'text'); + + dom.bind(this.__input, 'keyup', onChange); + dom.bind(this.__input, 'change', onChange); + dom.bind(this.__input, 'blur', onBlur); + dom.bind(this.__input, 'keydown', function(e) { + if (e.keyCode === 13) { + this.blur(); + } + }); + + + function onChange() { + _this.setValue(_this.__input.value); + } + + function onBlur() { + if (_this.__onFinishChange) { + _this.__onFinishChange.call(_this, _this.getValue()); + } + } + + this.updateDisplay(); + + this.domElement.appendChild(this.__input); + + }; + + StringController.superclass = Controller; + + common.extend( + + StringController.prototype, + Controller.prototype, + + { + + updateDisplay: function() { + // Stops the caret from moving on account of: + // keyup -> setValue -> updateDisplay + if (!dom.isActive(this.__input)) { + this.__input.value = this.getValue(); + } + return StringController.superclass.prototype.updateDisplay.call(this); + } + + } + + ); + + return StringController; + +})(dat.controllers.Controller, +dat.dom.dom, +dat.utils.common), +dat.controllers.FunctionController, +dat.controllers.BooleanController, +dat.utils.common), +dat.controllers.Controller, +dat.controllers.BooleanController, +dat.controllers.FunctionController, +dat.controllers.NumberControllerBox, +dat.controllers.NumberControllerSlider, +dat.controllers.OptionController, +dat.controllers.ColorController = (function (Controller, dom, Color, interpret, common) { + + var ColorController = function(object, property) { + + ColorController.superclass.call(this, object, property); + + this.__color = new Color(this.getValue()); + this.__temp = new Color(0); + + var _this = this; + + this.domElement = document.createElement('div'); + + dom.makeSelectable(this.domElement, false); + + this.__selector = document.createElement('div'); + this.__selector.className = 'selector'; + + this.__saturation_field = document.createElement('div'); + this.__saturation_field.className = 'saturation-field'; + + this.__field_knob = document.createElement('div'); + this.__field_knob.className = 'field-knob'; + this.__field_knob_border = '2px solid '; + + this.__hue_knob = document.createElement('div'); + this.__hue_knob.className = 'hue-knob'; + + this.__hue_field = document.createElement('div'); + this.__hue_field.className = 'hue-field'; + + this.__input = document.createElement('input'); + this.__input.type = 'text'; + this.__input_textShadow = '0 1px 1px '; + + dom.bind(this.__input, 'keydown', function(e) { + if (e.keyCode === 13) { // on enter + onBlur.call(this); + } + }); + + dom.bind(this.__input, 'blur', onBlur); + + dom.bind(this.__selector, 'mousedown', function(e) { + + dom + .addClass(this, 'drag') + .bind(window, 'mouseup', function(e) { + dom.removeClass(_this.__selector, 'drag'); + }); + + }); + + var value_field = document.createElement('div'); + + common.extend(this.__selector.style, { + width: '122px', + height: '102px', + padding: '3px', + backgroundColor: '#222', + boxShadow: '0px 1px 3px rgba(0,0,0,0.3)' + }); + + common.extend(this.__field_knob.style, { + position: 'absolute', + width: '12px', + height: '12px', + border: this.__field_knob_border + (this.__color.v < .5 ? '#fff' : '#000'), + boxShadow: '0px 1px 3px rgba(0,0,0,0.5)', + borderRadius: '12px', + zIndex: 1 + }); + + common.extend(this.__hue_knob.style, { + position: 'absolute', + width: '15px', + height: '2px', + borderRight: '4px solid #fff', + zIndex: 1 + }); + + common.extend(this.__saturation_field.style, { + width: '100px', + height: '100px', + border: '1px solid #555', + marginRight: '3px', + display: 'inline-block', + cursor: 'pointer' + }); + + common.extend(value_field.style, { + width: '100%', + height: '100%', + background: 'none' + }); + + linearGradient(value_field, 'top', 'rgba(0,0,0,0)', '#000'); + + common.extend(this.__hue_field.style, { + width: '15px', + height: '100px', + display: 'inline-block', + border: '1px solid #555', + cursor: 'ns-resize' + }); + + hueGradient(this.__hue_field); + + common.extend(this.__input.style, { + outline: 'none', +// width: '120px', + textAlign: 'center', +// padding: '4px', +// marginBottom: '6px', + color: '#fff', + border: 0, + fontWeight: 'bold', + textShadow: this.__input_textShadow + 'rgba(0,0,0,0.7)' + }); + + dom.bind(this.__saturation_field, 'mousedown', fieldDown); + dom.bind(this.__field_knob, 'mousedown', fieldDown); + + dom.bind(this.__hue_field, 'mousedown', function(e) { + setH(e); + dom.bind(window, 'mousemove', setH); + dom.bind(window, 'mouseup', unbindH); + }); + + function fieldDown(e) { + setSV(e); + // document.body.style.cursor = 'none'; + dom.bind(window, 'mousemove', setSV); + dom.bind(window, 'mouseup', unbindSV); + } + + function unbindSV() { + dom.unbind(window, 'mousemove', setSV); + dom.unbind(window, 'mouseup', unbindSV); + // document.body.style.cursor = 'default'; + } + + function onBlur() { + var i = interpret(this.value); + if (i !== false) { + _this.__color.__state = i; + _this.setValue(_this.__color.toOriginal()); + } else { + this.value = _this.__color.toString(); + } + } + + function unbindH() { + dom.unbind(window, 'mousemove', setH); + dom.unbind(window, 'mouseup', unbindH); + } + + this.__saturation_field.appendChild(value_field); + this.__selector.appendChild(this.__field_knob); + this.__selector.appendChild(this.__saturation_field); + this.__selector.appendChild(this.__hue_field); + this.__hue_field.appendChild(this.__hue_knob); + + this.domElement.appendChild(this.__input); + this.domElement.appendChild(this.__selector); + + this.updateDisplay(); + + function setSV(e) { + + e.preventDefault(); + + var w = dom.getWidth(_this.__saturation_field); + var o = dom.getOffset(_this.__saturation_field); + var s = (e.clientX - o.left + document.body.scrollLeft) / w; + var v = 1 - (e.clientY - o.top + document.body.scrollTop) / w; + + if (v > 1) v = 1; + else if (v < 0) v = 0; + + if (s > 1) s = 1; + else if (s < 0) s = 0; + + _this.__color.v = v; + _this.__color.s = s; + + _this.setValue(_this.__color.toOriginal()); + + + return false; + + } + + function setH(e) { + + e.preventDefault(); + + var s = dom.getHeight(_this.__hue_field); + var o = dom.getOffset(_this.__hue_field); + var h = 1 - (e.clientY - o.top + document.body.scrollTop) / s; + + if (h > 1) h = 1; + else if (h < 0) h = 0; + + _this.__color.h = h * 360; + + _this.setValue(_this.__color.toOriginal()); + + return false; + + } + + }; + + ColorController.superclass = Controller; + + common.extend( + + ColorController.prototype, + Controller.prototype, + + { + + updateDisplay: function() { + + var i = interpret(this.getValue()); + + if (i !== false) { + + var mismatch = false; + + // Check for mismatch on the interpreted value. + + common.each(Color.COMPONENTS, function(component) { + if (!common.isUndefined(i[component]) && + !common.isUndefined(this.__color.__state[component]) && + i[component] !== this.__color.__state[component]) { + mismatch = true; + return {}; // break + } + }, this); + + // If nothing diverges, we keep our previous values + // for statefulness, otherwise we recalculate fresh + if (mismatch) { + common.extend(this.__color.__state, i); + } + + } + + common.extend(this.__temp.__state, this.__color.__state); + + this.__temp.a = 1; + + var flip = (this.__color.v < .5 || this.__color.s > .5) ? 255 : 0; + var _flip = 255 - flip; + + common.extend(this.__field_knob.style, { + marginLeft: 100 * this.__color.s - 7 + 'px', + marginTop: 100 * (1 - this.__color.v) - 7 + 'px', + backgroundColor: this.__temp.toString(), + border: this.__field_knob_border + 'rgb(' + flip + ',' + flip + ',' + flip +')' + }); + + this.__hue_knob.style.marginTop = (1 - this.__color.h / 360) * 100 + 'px' + + this.__temp.s = 1; + this.__temp.v = 1; + + linearGradient(this.__saturation_field, 'left', '#fff', this.__temp.toString()); + + common.extend(this.__input.style, { + backgroundColor: this.__input.value = this.__color.toString(), + color: 'rgb(' + flip + ',' + flip + ',' + flip +')', + textShadow: this.__input_textShadow + 'rgba(' + _flip + ',' + _flip + ',' + _flip +',.7)' + }); + + } + + } + + ); + + var vendors = ['-moz-','-o-','-webkit-','-ms-','']; + + function linearGradient(elem, x, a, b) { + elem.style.background = ''; + common.each(vendors, function(vendor) { + elem.style.cssText += 'background: ' + vendor + 'linear-gradient('+x+', '+a+' 0%, ' + b + ' 100%); '; + }); + } + + function hueGradient(elem) { + elem.style.background = ''; + elem.style.cssText += 'background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);' + elem.style.cssText += 'background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' + elem.style.cssText += 'background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' + elem.style.cssText += 'background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' + elem.style.cssText += 'background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' + } + + + return ColorController; + +})(dat.controllers.Controller, +dat.dom.dom, +dat.color.Color = (function (interpret, math, toString, common) { + + var Color = function() { + + this.__state = interpret.apply(this, arguments); + + if (this.__state === false) { + throw 'Failed to interpret color arguments'; + } + + this.__state.a = this.__state.a || 1; + + + }; + + Color.COMPONENTS = ['r','g','b','h','s','v','hex','a']; + + common.extend(Color.prototype, { + + toString: function() { + return toString(this); + }, + + toOriginal: function() { + return this.__state.conversion.write(this); + } + + }); + + defineRGBComponent(Color.prototype, 'r', 2); + defineRGBComponent(Color.prototype, 'g', 1); + defineRGBComponent(Color.prototype, 'b', 0); + + defineHSVComponent(Color.prototype, 'h'); + defineHSVComponent(Color.prototype, 's'); + defineHSVComponent(Color.prototype, 'v'); + + Object.defineProperty(Color.prototype, 'a', { + + get: function() { + return this.__state.a; + }, + + set: function(v) { + this.__state.a = v; + } + + }); + + Object.defineProperty(Color.prototype, 'hex', { + + get: function() { + + if (!this.__state.space !== 'HEX') { + this.__state.hex = math.rgb_to_hex(this.r, this.g, this.b); + } + + return this.__state.hex; + + }, + + set: function(v) { + + this.__state.space = 'HEX'; + this.__state.hex = v; + + } + + }); + + function defineRGBComponent(target, component, componentHexIndex) { + + Object.defineProperty(target, component, { + + get: function() { + + if (this.__state.space === 'RGB') { + return this.__state[component]; + } + + recalculateRGB(this, component, componentHexIndex); + + return this.__state[component]; + + }, + + set: function(v) { + + if (this.__state.space !== 'RGB') { + recalculateRGB(this, component, componentHexIndex); + this.__state.space = 'RGB'; + } + + this.__state[component] = v; + + } + + }); + + } + + function defineHSVComponent(target, component) { + + Object.defineProperty(target, component, { + + get: function() { + + if (this.__state.space === 'HSV') + return this.__state[component]; + + recalculateHSV(this); + + return this.__state[component]; + + }, + + set: function(v) { + + if (this.__state.space !== 'HSV') { + recalculateHSV(this); + this.__state.space = 'HSV'; + } + + this.__state[component] = v; + + } + + }); + + } + + function recalculateRGB(color, component, componentHexIndex) { + + if (color.__state.space === 'HEX') { + + color.__state[component] = math.component_from_hex(color.__state.hex, componentHexIndex); + + } else if (color.__state.space === 'HSV') { + + common.extend(color.__state, math.hsv_to_rgb(color.__state.h, color.__state.s, color.__state.v)); + + } else { + + throw 'Corrupted color state'; + + } + + } + + function recalculateHSV(color) { + + var result = math.rgb_to_hsv(color.r, color.g, color.b); + + common.extend(color.__state, + { + s: result.s, + v: result.v + } + ); + + if (!common.isNaN(result.h)) { + color.__state.h = result.h; + } else if (common.isUndefined(color.__state.h)) { + color.__state.h = 0; + } + + } + + return Color; + +})(dat.color.interpret, +dat.color.math = (function () { + + var tmpComponent; + + return { + + hsv_to_rgb: function(h, s, v) { + + var hi = Math.floor(h / 60) % 6; + + var f = h / 60 - Math.floor(h / 60); + var p = v * (1.0 - s); + var q = v * (1.0 - (f * s)); + var t = v * (1.0 - ((1.0 - f) * s)); + var c = [ + [v, t, p], + [q, v, p], + [p, v, t], + [p, q, v], + [t, p, v], + [v, p, q] + ][hi]; + + return { + r: c[0] * 255, + g: c[1] * 255, + b: c[2] * 255 + }; + + }, + + rgb_to_hsv: function(r, g, b) { + + var min = Math.min(r, g, b), + max = Math.max(r, g, b), + delta = max - min, + h, s; + + if (max != 0) { + s = delta / max; + } else { + return { + h: NaN, + s: 0, + v: 0 + }; + } + + if (r == max) { + h = (g - b) / delta; + } else if (g == max) { + h = 2 + (b - r) / delta; + } else { + h = 4 + (r - g) / delta; + } + h /= 6; + if (h < 0) { + h += 1; + } + + return { + h: h * 360, + s: s, + v: max / 255 + }; + }, + + rgb_to_hex: function(r, g, b) { + var hex = this.hex_with_component(0, 2, r); + hex = this.hex_with_component(hex, 1, g); + hex = this.hex_with_component(hex, 0, b); + return hex; + }, + + component_from_hex: function(hex, componentIndex) { + return (hex >> (componentIndex * 8)) & 0xFF; + }, + + hex_with_component: function(hex, componentIndex, value) { + return value << (tmpComponent = componentIndex * 8) | (hex & ~ (0xFF << tmpComponent)); + } + + } + +})(), +dat.color.toString, +dat.utils.common), +dat.color.interpret, +dat.utils.common), +dat.utils.requestAnimationFrame = (function () { + + /** + * requirejs version of Paul Irish's RequestAnimationFrame + * http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + */ + + return window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback, element) { + + window.setTimeout(callback, 1000 / 60); + + }; +})(), +dat.dom.CenteredDiv = (function (dom, common) { + + + var CenteredDiv = function() { + + this.backgroundElement = document.createElement('div'); + common.extend(this.backgroundElement.style, { + backgroundColor: 'rgba(0,0,0,0.8)', + top: 0, + left: 0, + display: 'none', + zIndex: '1000', + opacity: 0, + WebkitTransition: 'opacity 0.2s linear' + }); + + dom.makeFullscreen(this.backgroundElement); + this.backgroundElement.style.position = 'fixed'; + + this.domElement = document.createElement('div'); + common.extend(this.domElement.style, { + position: 'fixed', + display: 'none', + zIndex: '1001', + opacity: 0, + WebkitTransition: '-webkit-transform 0.2s ease-out, opacity 0.2s linear' + }); + + + document.body.appendChild(this.backgroundElement); + document.body.appendChild(this.domElement); + + var _this = this; + dom.bind(this.backgroundElement, 'click', function() { + _this.hide(); + }); + + + }; + + CenteredDiv.prototype.show = function() { + + var _this = this; + + + + this.backgroundElement.style.display = 'block'; + + this.domElement.style.display = 'block'; + this.domElement.style.opacity = 0; +// this.domElement.style.top = '52%'; + this.domElement.style.webkitTransform = 'scale(1.1)'; + + this.layout(); + + common.defer(function() { + _this.backgroundElement.style.opacity = 1; + _this.domElement.style.opacity = 1; + _this.domElement.style.webkitTransform = 'scale(1)'; + }); + + }; + + CenteredDiv.prototype.hide = function() { + + var _this = this; + + var hide = function() { + + _this.domElement.style.display = 'none'; + _this.backgroundElement.style.display = 'none'; + + dom.unbind(_this.domElement, 'webkitTransitionEnd', hide); + dom.unbind(_this.domElement, 'transitionend', hide); + dom.unbind(_this.domElement, 'oTransitionEnd', hide); + + }; + + dom.bind(this.domElement, 'webkitTransitionEnd', hide); + dom.bind(this.domElement, 'transitionend', hide); + dom.bind(this.domElement, 'oTransitionEnd', hide); + + this.backgroundElement.style.opacity = 0; +// this.domElement.style.top = '48%'; + this.domElement.style.opacity = 0; + this.domElement.style.webkitTransform = 'scale(1.1)'; + + }; + + CenteredDiv.prototype.layout = function() { + this.domElement.style.left = window.innerWidth/2 - dom.getWidth(this.domElement) / 2 + 'px'; + this.domElement.style.top = window.innerHeight/2 - dom.getHeight(this.domElement) / 2 + 'px'; + }; + + function lockScroll(e) { + console.log(e); + } + + return CenteredDiv; + +})(dat.dom.dom, +dat.utils.common), +dat.dom.dom, +dat.utils.common); +/* global dat,p2 */ + +(function(p2){ + +// shim layer with setTimeout fallback +var requestAnimFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function( callback ){ + window.setTimeout(callback, 1000 / 60); + }; + +var disableSelectionCSS = [ + "-ms-user-select: none", + "-moz-user-select: -moz-none", + "-khtml-user-select: none", + "-webkit-user-select: none", + "user-select: none" +]; + +p2.Renderer = Renderer; + + +/** + * Base class for rendering a p2 physics scene. + * @class Renderer + * @constructor + * @param {object} scenes One or more scene definitions. See setScene. + */ +function Renderer(scenes){ + p2.EventEmitter.call(this); + + // Expose globally + window.app = this; + + var that = this; + + if(scenes.setup){ + // Only one scene given, without name + scenes = { + 'default': scenes + }; + } else if(typeof(scenes)==='function'){ + scenes = { + 'default': { + setup: scenes + } + }; + } + + this.scenes = scenes; + + this.state = Renderer.DEFAULT; + + // Bodies to draw + this.bodies=[]; + this.springs=[]; + this.timeStep = 1/60; + this.relaxation = p2.Equation.DEFAULT_RELAXATION; + this.stiffness = p2.Equation.DEFAULT_STIFFNESS; + + this.mouseConstraint = null; + this.nullBody = new p2.Body(); + this.pickPrecision = 5; + + this.useInterpolatedPositions = false; + + this.drawPoints = []; + this.drawPointsChangeEvent = { type : "drawPointsChange" }; + this.drawCircleCenter = p2.vec2.create(); + this.drawCirclePoint = p2.vec2.create(); + this.drawCircleChangeEvent = { type : "drawCircleChange" }; + this.drawRectangleChangeEvent = { type : "drawRectangleChange" }; + this.drawRectStart = p2.vec2.create(); + this.drawRectEnd = p2.vec2.create(); + + this.stateChangeEvent = { type : "stateChange", state:null }; + + + // Default collision masks for new shapes + this.newShapeCollisionMask = 1; + this.newShapeCollisionGroup = 1; + + // If constraints should be drawn + this.drawConstraints = false; + + this.stats_sum = 0; + this.stats_N = 100; + this.stats_Nsummed = 0; + this.stats_average = -1; + + this.addedGlobals = []; + + this.settings = { + tool: Renderer.DEFAULT, + fullscreen: function(){ + var el = document.body; + var requestFullscreen = el.requestFullscreen || el.msRequestFullscreen || el.mozRequestFullScreen || el.webkitRequestFullscreen; + if(requestFullscreen){ + requestFullscreen.call(el); + } + }, + + 'paused [p]': false, + 'manualStep [s]': function(){ that.world.step(that.world.lastTimeStep); }, + fps: 60, + maxSubSteps: 3, + gravityX: 0, + gravityY: -10, + sleepMode: p2.World.NO_SLEEPING, + + 'drawContacts [c]': false, + 'drawAABBs [t]': false, + drawConstraints: false, + + iterations: 10, + stiffness: 1000000, + relaxation: 4, + tolerance: 0.0001, + }; + + this.init(); + this.resizeToFit(); + this.render(); + this.createStats(); + this.addLogo(); + this.centerCamera(0, 0); + + window.onresize = function(){ + that.resizeToFit(); + }; + + this.setUpKeyboard(); + this.setupGUI(); + + this.printConsoleMessage(); + + // Set first scene + this.setSceneByIndex(0); + + this.startRenderingLoop(); + +} +Renderer.prototype = new p2.EventEmitter(); + +Renderer.DEFAULT = 1; +Renderer.PANNING = 2; +Renderer.DRAGGING = 3; +Renderer.DRAWPOLYGON = 4; +Renderer.DRAWINGPOLYGON = 5; +Renderer.DRAWCIRCLE = 6; +Renderer.DRAWINGCIRCLE = 7; +Renderer.DRAWRECTANGLE = 8; +Renderer.DRAWINGRECTANGLE = 9; + +Renderer.toolStateMap = { + 'pick/pan [q]': Renderer.DEFAULT, + 'polygon [d]': Renderer.DRAWPOLYGON, + 'circle [a]': Renderer.DRAWCIRCLE, + 'rectangle [f]': Renderer.DRAWRECTANGLE +}; +Renderer.stateToolMap = {}; +for(var key in Renderer.toolStateMap){ + Renderer.stateToolMap[Renderer.toolStateMap[key]] = key; +} + +Renderer.keydownEvent = { + type:"keydown", + originalEvent : null, + keyCode : 0, +}; +Renderer.keyupEvent = { + type:"keyup", + originalEvent : null, + keyCode : 0, +}; + +Object.defineProperty(Renderer.prototype, 'drawContacts', { + get: function() { + return this.settings['drawContacts [c]']; + }, + set: function(value) { + this.settings['drawContacts [c]'] = value; + this.updateGUI(); + } +}); + +Object.defineProperty(Renderer.prototype, 'drawAABBs', { + get: function() { + return this.settings['drawAABBs [t]']; + }, + set: function(value) { + this.settings['drawAABBs [t]'] = value; + this.updateGUI(); + } +}); + +Object.defineProperty(Renderer.prototype, 'paused', { + get: function() { + return this.settings['paused [p]']; + }, + set: function(value) { + this.settings['paused [p]'] = value; + this.updateGUI(); + } +}); + +Renderer.prototype.getDevicePixelRatio = function() { + return window.devicePixelRatio || 1; +}; + +Renderer.prototype.printConsoleMessage = function(){ + console.log([ + '=== p2.js v' + p2.version + ' ===', + 'Welcome to the p2.js debugging environment!', + 'Did you know you can interact with the physics here in the console? Try executing the following:', + '', + ' world.gravity[1] = 10;', + '' + ].join('\n')); +}; + +Renderer.prototype.resizeToFit = function(){ + var dpr = this.getDevicePixelRatio(); + var rect = this.elementContainer.getBoundingClientRect(); + var w = rect.width * dpr; + var h = rect.height * dpr; + this.resize(w, h); +} + +/** + * Sets up dat.gui + */ +Renderer.prototype.setupGUI = function() { + if(typeof(dat) === 'undefined'){ + return; + } + + var that = this; + + var gui = this.gui = new dat.GUI(); + gui.domElement.setAttribute('style',disableSelectionCSS.join(';')); + + var settings = this.settings; + + gui.add(settings, 'tool', Renderer.toolStateMap).onChange(function(state){ + that.setState(parseInt(state)); + }); + gui.add(settings, 'fullscreen'); + + // World folder + var worldFolder = gui.addFolder('World'); + worldFolder.open(); + worldFolder.add(settings, 'paused [p]').onChange(function(p){ + that.paused = p; + }); + worldFolder.add(settings, 'manualStep [s]'); + worldFolder.add(settings, 'fps', 60, 60*10).step(60).onChange(function(freq){ + that.timeStep = 1 / freq; + }); + worldFolder.add(settings, 'maxSubSteps', 0, 10).step(1); + var maxg = 100; + + function changeGravity(){ + if(!isNaN(settings.gravityX) && !isNaN(settings.gravityY)){ + p2.vec2.set(that.world.gravity, settings.gravityX, settings.gravityY); + } + } + worldFolder.add(settings, 'gravityX', -maxg, maxg).onChange(changeGravity); + worldFolder.add(settings, 'gravityY', -maxg, maxg).onChange(changeGravity); + worldFolder.add(settings, 'sleepMode', { + NO_SLEEPING: p2.World.NO_SLEEPING, + BODY_SLEEPING: p2.World.BODY_SLEEPING, + ISLAND_SLEEPING: p2.World.ISLAND_SLEEPING, + }).onChange(function(mode){ + that.world.sleepMode = parseInt(mode); + }); + + // Rendering + var renderingFolder = gui.addFolder('Rendering'); + renderingFolder.open(); + renderingFolder.add(settings,'drawContacts [c]').onChange(function(draw){ + that.drawContacts = draw; + }); + renderingFolder.add(settings,'drawAABBs [t]').onChange(function(draw){ + that.drawAABBs = draw; + }); + + // Solver + var solverFolder = gui.addFolder('Solver'); + solverFolder.open(); + solverFolder.add(settings, 'iterations', 1, 100).step(1).onChange(function(it){ + that.world.solver.iterations = it; + }); + solverFolder.add(settings, 'stiffness', 10).onChange(function(k){ + that.setEquationParameters(); + }); + solverFolder.add(settings, 'relaxation', 0, 20).step(0.1).onChange(function(d){ + that.setEquationParameters(); + }); + solverFolder.add(settings, 'tolerance', 0, 10).step(0.01).onChange(function(t){ + that.world.solver.tolerance = t; + }); + + // Scene picker + var sceneFolder = gui.addFolder('Scenes'); + sceneFolder.open(); + + // Add scenes + var i = 1; + for(var sceneName in this.scenes){ + var guiLabel = sceneName + ' [' + (i++) + ']'; + this.settings[guiLabel] = function(){ + that.setScene(that.scenes[sceneName]); + }; + sceneFolder.add(settings, guiLabel); + } +}; + +/** + * Updates dat.gui. Call whenever you change demo.settings. + */ +Renderer.prototype.updateGUI = function() { + if(!this.gui){ + return; + } + function updateControllers(folder){ + // First level + for (var i in folder.__controllers){ + folder.__controllers[i].updateDisplay(); + } + + // Second level + for (var f in folder.__folders){ + updateControllers(folder.__folders[f]); + } + } + updateControllers(this.gui); +}; + +Renderer.prototype.setWorld = function(world){ + this.world = world; + + window.world = world; // For debugging. + + var that = this; + + world.on("postStep",function(e){ + that.updateStats(); + }).on("addBody",function(e){ + that.addVisual(e.body); + }).on("removeBody",function(e){ + that.removeVisual(e.body); + }).on("addSpring",function(e){ + that.addVisual(e.spring); + }).on("removeSpring",function(e){ + that.removeVisual(e.spring); + }); +}; + +/** + * Sets the current scene to the scene definition given. + * @param {object} sceneDefinition + * @param {function} sceneDefinition.setup + * @param {function} [sceneDefinition.teardown] + */ +Renderer.prototype.setScene = function(sceneDefinition){ + if(typeof(sceneDefinition) === 'string'){ + sceneDefinition = this.scenes[sceneDefinition]; + } + + this.removeAllVisuals(); + if(this.currentScene && this.currentScene.teardown){ + this.currentScene.teardown(); + } + if(this.world){ + this.world.clear(); + } + + for(var i=0; i 0){ + b = result.shift(); + if(b.type === p2.Body.STATIC){ + b = null; + } else { + break; + } + } + + if(b){ + b.wakeUp(); + this.setState(Renderer.DRAGGING); + // Add mouse joint to the body + var localPoint = p2.vec2.create(); + b.toLocalFrame(localPoint,physicsPosition); + this.world.addBody(this.nullBody); + this.mouseConstraint = new p2.RevoluteConstraint(this.nullBody, b, { + localPivotA: physicsPosition, + localPivotB: localPoint + }); + this.world.addConstraint(this.mouseConstraint); + } else { + this.setState(Renderer.PANNING); + } + break; + + case Renderer.DRAWPOLYGON: + // Start drawing a polygon + this.setState(Renderer.DRAWINGPOLYGON); + this.drawPoints = []; + var copy = p2.vec2.create(); + p2.vec2.copy(copy,physicsPosition); + this.drawPoints.push(copy); + this.emit(this.drawPointsChangeEvent); + break; + + case Renderer.DRAWCIRCLE: + // Start drawing a circle + this.setState(Renderer.DRAWINGCIRCLE); + p2.vec2.copy(this.drawCircleCenter,physicsPosition); + p2.vec2.copy(this.drawCirclePoint, physicsPosition); + this.emit(this.drawCircleChangeEvent); + break; + + case Renderer.DRAWRECTANGLE: + // Start drawing a circle + this.setState(Renderer.DRAWINGRECTANGLE); + p2.vec2.copy(this.drawRectStart,physicsPosition); + p2.vec2.copy(this.drawRectEnd, physicsPosition); + this.emit(this.drawRectangleChangeEvent); + break; + } +}; + +/** + * Should be called by subclasses whenever there's a mousedown event + */ +Renderer.prototype.handleMouseMove = function(physicsPosition){ + var sampling = 0.4; + switch(this.state){ + case Renderer.DEFAULT: + case Renderer.DRAGGING: + if(this.mouseConstraint){ + p2.vec2.copy(this.mouseConstraint.pivotA, physicsPosition); + this.mouseConstraint.bodyA.wakeUp(); + this.mouseConstraint.bodyB.wakeUp(); + } + break; + + case Renderer.DRAWINGPOLYGON: + // drawing a polygon - add new point + var sqdist = p2.vec2.dist(physicsPosition,this.drawPoints[this.drawPoints.length-1]); + if(sqdist > sampling*sampling){ + var copy = [0,0]; + p2.vec2.copy(copy,physicsPosition); + this.drawPoints.push(copy); + this.emit(this.drawPointsChangeEvent); + } + break; + + case Renderer.DRAWINGCIRCLE: + // drawing a circle - change the circle radius point to current + p2.vec2.copy(this.drawCirclePoint, physicsPosition); + this.emit(this.drawCircleChangeEvent); + break; + + case Renderer.DRAWINGRECTANGLE: + // drawing a rectangle - change the end point to current + p2.vec2.copy(this.drawRectEnd, physicsPosition); + this.emit(this.drawRectangleChangeEvent); + break; + } +}; + +/** + * Should be called by subclasses whenever there's a mouseup event + */ +Renderer.prototype.handleMouseUp = function(physicsPosition){ + + var b; + + switch(this.state){ + + case Renderer.DEFAULT: + break; + + case Renderer.DRAGGING: + // Drop constraint + this.world.removeConstraint(this.mouseConstraint); + this.mouseConstraint = null; + this.world.removeBody(this.nullBody); + this.setState(Renderer.DEFAULT); + break; + + case Renderer.PANNING: + this.setState(Renderer.DEFAULT); + break; + + case Renderer.DRAWINGPOLYGON: + // End this drawing state + this.setState(Renderer.DRAWPOLYGON); + if(this.drawPoints.length > 3){ + // Create polygon + b = new p2.Body({ mass : 1 }); + if(b.fromPolygon(this.drawPoints,{ + removeCollinearPoints : 0.01, + })){ + this.world.addBody(b); + } + } + this.drawPoints = []; + this.emit(this.drawPointsChangeEvent); + break; + + case Renderer.DRAWINGCIRCLE: + // End this drawing state + this.setState(Renderer.DRAWCIRCLE); + var R = p2.vec2.dist(this.drawCircleCenter,this.drawCirclePoint); + if(R > 0){ + // Create circle + b = new p2.Body({ mass : 1, position : this.drawCircleCenter }); + var circle = new p2.Circle(R); + b.addShape(circle); + this.world.addBody(b); + } + p2.vec2.copy(this.drawCircleCenter,this.drawCirclePoint); + this.emit(this.drawCircleChangeEvent); + break; + + case Renderer.DRAWINGRECTANGLE: + // End this drawing state + this.setState(Renderer.DRAWRECTANGLE); + // Make sure first point is upper left + var start = this.drawRectStart; + var end = this.drawRectEnd; + for(var i=0; i<2; i++){ + if(start[i] > end[i]){ + var tmp = end[i]; + end[i] = start[i]; + start[i] = tmp; + } + } + var width = Math.abs(start[0] - end[0]); + var height = Math.abs(start[1] - end[1]); + if(width > 0 && height > 0){ + // Create box + b = new p2.Body({ + mass : 1, + position : [this.drawRectStart[0] + width*0.5, this.drawRectStart[1] + height*0.5] + }); + var rectangleShape = new p2.Rectangle(width, height); + b.addShape(rectangleShape); + this.world.addBody(b); + } + p2.vec2.copy(this.drawRectEnd,this.drawRectStart); + this.emit(this.drawRectangleChangeEvent); + break; + } + + if(b){ + b.wakeUp(); + for(var i=0; i", + "

    p2.js

    ", + "

    Physics Engine

    ", + '', + "" + ].join(""); + this.elementContainer.appendChild(div); + + // Twitter button script + !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs'); +}; + +Renderer.zoomInEvent = { + type:"zoomin" +}; +Renderer.zoomOutEvent = { + type:"zoomout" +}; + +Renderer.prototype.setEquationParameters = function(){ + this.world.setGlobalEquationParameters({ + stiffness: this.settings.stiffness, + relaxation: this.settings.relaxation + }); +}; + +})(p2); +/* global PIXI,Renderer */ + +(function(p2){ + +p2.WebGLRenderer = WebGLRenderer; + +var Renderer = p2.Renderer; + +/** + * Renderer using Pixi.js + * @class WebGLRenderer + * @constructor + * @extends Renderer + * @param {World} world The world to render. + * @param {Object} [options] + * @param {Number} options.lineWidth + * @param {Number} options.scrollFactor + * @param {Number} options.width Num pixels in horizontal direction + * @param {Number} options.height Num pixels in vertical direction + */ +function WebGLRenderer(world,options){ + options = options || {}; + + var that = this; + + var settings = { + lineWidth : 0.01, + scrollFactor : 0.1, + width : 1280, // Pixi screen resolution + height : 720, + useDeviceAspect : false, + sleepOpacity : 0.2, + }; + for(var key in options){ + settings[key] = options[key]; + } + + if(settings.useDeviceAspect){ + settings.height = window.innerHeight / window.innerWidth * settings.width; + } + + //this.settings = settings; + this.lineWidth = settings.lineWidth; + this.scrollFactor = settings.scrollFactor; + this.sleepOpacity = settings.sleepOpacity; + + this.sprites = []; + this.springSprites = []; + this.debugPolygons = false; + + Renderer.call(this,world); + + for(var key in settings){ + this.settings[key] = settings[key]; + } + + this.pickPrecision = 0.1; + + // Update "ghost draw line" + this.on("drawPointsChange",function(e){ + var g = that.drawShapeGraphics; + var path = that.drawPoints; + + g.clear(); + + var path2 = []; + for(var j=0; j 1 + d = d < 1 ? d < -1 ? (-Math.pow(d, 2) - n1) / n : d : (Math.pow(d, 2) + n1) / n; + // Delta *should* not be greater than 2... + var delta = Math.min(Math.max(d / 2, -1), 1); + + var out = delta >= 0; + if(typeof lastMoveX !== 'undefined'){ + that.zoom(lastMoveX, lastMoveY, out, undefined, undefined, delta); + } + } + + if (el.addEventListener) { + el.addEventListener("mousewheel", MouseWheelHandler, false); // IE9, Chrome, Safari, Opera + el.addEventListener("DOMMouseScroll", MouseWheelHandler, false); // Firefox + } else { + el.attachEvent("onmousewheel", MouseWheelHandler); // IE 6/7/8 + } + + this.centerCamera(0, 0); +}; + +WebGLRenderer.prototype.zoom = function(x, y, zoomOut, actualScaleX, actualScaleY, multiplier){ + var scrollFactor = this.scrollFactor, + stage = this.stage; + + if(typeof actualScaleX === 'undefined'){ + + if(!zoomOut){ + scrollFactor *= -1; + } + + scrollFactor *= Math.abs(multiplier); + + stage.scale.x *= (1 + scrollFactor); + stage.scale.y *= (1 + scrollFactor); + stage.position.x += (scrollFactor) * (stage.position.x - x); + stage.position.y += (scrollFactor) * (stage.position.y - y); + } else { + stage.scale.x *= actualScaleX; + stage.scale.y *= actualScaleY; + stage.position.x += (actualScaleX - 1) * (stage.position.x - x); + stage.position.y += (actualScaleY - 1) * (stage.position.y - y); + } + + stage.updateTransform(); +}; + +WebGLRenderer.prototype.centerCamera = function(x, y){ + this.stage.position.x = this.renderer.width / 2 - this.stage.scale.x * x; + this.stage.position.y = this.renderer.height / 2 - this.stage.scale.y * y; +}; + +/** + * Make sure that a rectangle is visible in the canvas. + * @param {number} centerX + * @param {number} centerY + * @param {number} width + * @param {number} height + */ +WebGLRenderer.prototype.frame = function(centerX, centerY, width, height){ + var ratio = this.renderer.width / this.renderer.height; + if(ratio < width / height){ + this.stage.scale.x = this.renderer.width / width; + this.stage.scale.y = -this.stage.scale.x; + } else { + this.stage.scale.y = -this.renderer.height / height; + this.stage.scale.x = -this.stage.scale.y; + } + this.centerCamera(centerX, centerY); +}; + +/** + * Draw a circle onto a graphics object + * @method drawCircle + * @static + * @param {PIXI.Graphics} g + * @param {Number} x + * @param {Number} y + * @param {Number} radius + * @param {Number} color + * @param {Number} lineWidth + */ +WebGLRenderer.prototype.drawCircle = function(g,x,y,angle,radius,color,lineWidth,isSleeping){ + lineWidth = typeof(lineWidth)==="number" ? lineWidth : 1; + color = typeof(color)==="number" ? color : 0xffffff; + g.lineStyle(lineWidth, 0x000000, 1); + g.beginFill(color, isSleeping ? this.sleepOpacity : 1.0); + g.drawCircle(x, y, radius); + g.endFill(); + + // line from center to edge + g.moveTo(x,y); + g.lineTo( x + radius*Math.cos(angle), + y + radius*Math.sin(angle) ); +}; + +WebGLRenderer.drawSpring = function(g,restLength,color,lineWidth){ + lineWidth = typeof(lineWidth)==="number" ? lineWidth : 1; + color = typeof(color)==="undefined" ? 0xffffff : color; + g.lineStyle(lineWidth, color, 1); + if(restLength < lineWidth*10){ + restLength = lineWidth*10; + } + var M = 12; + var dx = restLength/M; + g.moveTo(-restLength/2,0); + for(var i=1; i=M-1 ){ + // Do nothing + } else if(i % 2 === 0){ + y -= 0.1*restLength; + } else { + y += 0.1*restLength; + } + g.lineTo(x,y); + } + g.lineTo(restLength/2,0); +}; + +/** + * Draw a finite plane onto a PIXI.Graphics. + * @method drawPlane + * @param {PIXI.Graphics} g + * @param {Number} x0 + * @param {Number} x1 + * @param {Number} color + * @param {Number} lineWidth + * @param {Number} diagMargin + * @param {Number} diagSize + * @todo Should consider an angle + */ +WebGLRenderer.drawPlane = function(g, x0, x1, color, lineColor, lineWidth, diagMargin, diagSize, maxLength){ + lineWidth = typeof(lineWidth)==="number" ? lineWidth : 1; + color = typeof(color)==="undefined" ? 0xffffff : color; + g.lineStyle(lineWidth, lineColor, 1); + + // Draw a fill color + g.lineStyle(0,0,0); + g.beginFill(color); + var max = maxLength; + g.moveTo(-max,0); + g.lineTo(max,0); + g.lineTo(max,-max); + g.lineTo(-max,-max); + g.endFill(); + + // Draw the actual plane + g.lineStyle(lineWidth,lineColor); + g.moveTo(-max,0); + g.lineTo(max,0); +}; + + +WebGLRenderer.drawLine = function(g, offset, angle, len, color, lineWidth){ + lineWidth = typeof(lineWidth)==="number" ? lineWidth : 1; + color = typeof(color)==="undefined" ? 0x000000 : color; + g.lineStyle(lineWidth, color, 1); + + var startPoint = p2.vec2.fromValues(-len/2,0); + var endPoint = p2.vec2.fromValues(len/2,0); + + p2.vec2.rotate(startPoint, startPoint, angle); + p2.vec2.rotate(endPoint, endPoint, angle); + + p2.vec2.add(startPoint, startPoint, offset); + p2.vec2.add(endPoint, endPoint, offset); + + g.moveTo(startPoint[0], startPoint[1]); + g.lineTo(endPoint[0], endPoint[1]); +}; + +WebGLRenderer.prototype.drawCapsule = function(g, x, y, angle, len, radius, color, fillColor, lineWidth, isSleeping){ + lineWidth = typeof(lineWidth)==="number" ? lineWidth : 1; + color = typeof(color)==="undefined" ? 0x000000 : color; + g.lineStyle(lineWidth, color, 1); + + // Draw circles at ends + var c = Math.cos(angle); + var s = Math.sin(angle); + g.beginFill(fillColor, isSleeping ? this.sleepOpacity : 1.0); + g.drawCircle(-len/2*c + x, -len/2*s + y, radius); + g.drawCircle( len/2*c + x, len/2*s + y, radius); + g.endFill(); + + // Draw rectangle + g.lineStyle(lineWidth, color, 0); + g.beginFill(fillColor, isSleeping ? this.sleepOpacity : 1.0); + g.moveTo(-len/2*c + radius*s + x, -len/2*s + radius*c + y); + g.lineTo( len/2*c + radius*s + x, len/2*s + radius*c + y); + g.lineTo( len/2*c - radius*s + x, len/2*s - radius*c + y); + g.lineTo(-len/2*c - radius*s + x, -len/2*s - radius*c + y); + g.endFill(); + + // Draw lines in between + g.lineStyle(lineWidth, color, 1); + g.moveTo(-len/2*c + radius*s + x, -len/2*s + radius*c + y); + g.lineTo( len/2*c + radius*s + x, len/2*s + radius*c + y); + g.moveTo(-len/2*c - radius*s + x, -len/2*s - radius*c + y); + g.lineTo( len/2*c - radius*s + x, len/2*s - radius*c + y); + +}; + +// Todo angle +WebGLRenderer.prototype.drawRectangle = function(g,x,y,angle,w,h,color,fillColor,lineWidth,isSleeping){ + lineWidth = typeof(lineWidth)==="number" ? lineWidth : 1; + color = typeof(color)==="number" ? color : 0xffffff; + fillColor = typeof(fillColor)==="number" ? fillColor : 0xffffff; + g.lineStyle(lineWidth); + g.beginFill(fillColor, isSleeping ? this.sleepOpacity : 1.0); + g.drawRect(x-w/2,y-h/2,w,h); +}; + +WebGLRenderer.prototype.drawConvex = function(g,verts,triangles,color,fillColor,lineWidth,debug,offset,isSleeping){ + lineWidth = typeof(lineWidth)==="number" ? lineWidth : 1; + color = typeof(color)==="undefined" ? 0x000000 : color; + if(!debug){ + g.lineStyle(lineWidth, color, 1); + g.beginFill(fillColor, isSleeping ? this.sleepOpacity : 1.0); + for(var i=0; i!==verts.length; i++){ + var v = verts[i], + x = v[0], + y = v[1]; + if(i===0){ + g.moveTo(x,y); + } else { + g.lineTo(x,y); + } + } + g.endFill(); + if(verts.length>2){ + g.moveTo(verts[verts.length-1][0],verts[verts.length-1][1]); + g.lineTo(verts[0][0],verts[0][1]); + } + } else { + var colors = [0xff0000,0x00ff00,0x0000ff]; + for(var i=0; i!==verts.length+1; i++){ + var v0 = verts[i%verts.length], + v1 = verts[(i+1)%verts.length], + x0 = v0[0], + y0 = v0[1], + x1 = v1[0], + y1 = v1[1]; + g.lineStyle(lineWidth, colors[i%colors.length], 1); + g.moveTo(x0,y0); + g.lineTo(x1,y1); + g.drawCircle(x0,y0,lineWidth*2); + } + + g.lineStyle(lineWidth, 0x000000, 1); + g.drawCircle(offset[0],offset[1],lineWidth*2); + } +}; + +WebGLRenderer.prototype.drawPath = function(g,path,color,fillColor,lineWidth,isSleeping){ + lineWidth = typeof(lineWidth)==="number" ? lineWidth : 1; + color = typeof(color)==="undefined" ? 0x000000 : color; + g.lineStyle(lineWidth, color, 1); + if(typeof(fillColor)==="number"){ + g.beginFill(fillColor, isSleeping ? this.sleepOpacity : 1.0); + } + var lastx = null, + lasty = null; + for(var i=0; i2 && typeof(fillColor)==="number"){ + g.moveTo(path[path.length-1][0],path[path.length-1][1]); + g.lineTo(path[0][0],path[0][1]); + } +}; + +WebGLRenderer.prototype.updateSpriteTransform = function(sprite,body){ + if(this.useInterpolatedPositions){ + sprite.position.x = body.interpolatedPosition[0]; + sprite.position.y = body.interpolatedPosition[1]; + sprite.rotation = body.interpolatedAngle; + } else { + sprite.position.x = body.position[0]; + sprite.position.y = body.position[1]; + sprite.rotation = body.angle; + } +}; + +var X = p2.vec2.fromValues(1,0), + distVec = p2.vec2.fromValues(0,0), + worldAnchorA = p2.vec2.fromValues(0,0), + worldAnchorB = p2.vec2.fromValues(0,0); +WebGLRenderer.prototype.render = function(){ + var w = this.renderer.width, + h = this.renderer.height, + springSprites = this.springSprites, + sprites = this.sprites; + + // Update body transforms + for(var i=0; i!==this.bodies.length; i++){ + this.updateSpriteTransform(this.sprites[i],this.bodies[i]); + } + + // Update graphics if the body changed sleepState + for(var i=0; i!==this.bodies.length; i++){ + var isSleeping = (this.bodies[i].sleepState===p2.Body.SLEEPING); + var sprite = this.sprites[i]; + var body = this.bodies[i]; + if(sprite.drawnSleeping !== isSleeping){ + sprite.clear(); + this.drawRenderable(body, sprite, sprite.drawnColor, sprite.drawnLineColor); + } + } + + // Update spring transforms + for(var i=0; i!==this.springs.length; i++){ + var s = this.springs[i], + sprite = springSprites[i], + bA = s.bodyA, + bB = s.bodyB; + + if(this.useInterpolatedPositions){ + p2.vec2.toGlobalFrame(worldAnchorA, s.localAnchorA, bA.interpolatedPosition, bA.interpolatedAngle); + p2.vec2.toGlobalFrame(worldAnchorB, s.localAnchorB, bB.interpolatedPosition, bB.interpolatedAngle); + } else { + s.getWorldAnchorA(worldAnchorA); + s.getWorldAnchorB(worldAnchorB); + } + + sprite.scale.y = 1; + if(worldAnchorA[1] < worldAnchorB[1]){ + var tmp = worldAnchorA; + worldAnchorA = worldAnchorB; + worldAnchorB = tmp; + sprite.scale.y = -1; + } + + var sxA = worldAnchorA[0], + syA = worldAnchorA[1], + sxB = worldAnchorB[0], + syB = worldAnchorB[1]; + + // Spring position is the mean point between the anchors + sprite.position.x = ( sxA + sxB ) / 2; + sprite.position.y = ( syA + syB ) / 2; + + // Compute distance vector between anchors, in screen coords + distVec[0] = sxA - sxB; + distVec[1] = syA - syB; + + // Compute angle + sprite.rotation = Math.acos( p2.vec2.dot(X, distVec) / p2.vec2.length(distVec) ); + + // And scale + sprite.scale.x = p2.vec2.length(distVec) / s.restLength; + } + + // Clear contacts + if(this.drawContacts){ + this.contactGraphics.clear(); + this.stage.removeChild(this.contactGraphics); + this.stage.addChild(this.contactGraphics); + + var g = this.contactGraphics; + g.lineStyle(this.lineWidth,0x000000,1); + for(var i=0; i!==this.world.narrowphase.contactEquations.length; i++){ + var eq = this.world.narrowphase.contactEquations[i], + bi = eq.bodyA, + bj = eq.bodyB, + ri = eq.contactPointA, + rj = eq.contactPointB, + xi = bi.position[0], + yi = bi.position[1], + xj = bj.position[0], + yj = bj.position[1]; + + g.moveTo(xi,yi); + g.lineTo(xi+ri[0], yi+ri[1]); + + g.moveTo(xj,yj); + g.lineTo(xj+rj[0], yj+rj[1]); + + } + this.contactGraphics.cleared = false; + } else if(!this.contactGraphics.cleared){ + this.contactGraphics.clear(); + this.contactGraphics.cleared = true; + } + + // Draw AABBs + if(this.drawAABBs){ + this.aabbGraphics.clear(); + this.stage.removeChild(this.aabbGraphics); + this.stage.addChild(this.aabbGraphics); + var g = this.aabbGraphics; + g.lineStyle(this.lineWidth,0x000000,1); + + for(var i=0; i!==this.world.bodies.length; i++){ + var aabb = this.world.bodies[i].getAABB(); + g.drawRect(aabb.lowerBound[0], aabb.lowerBound[1], aabb.upperBound[0] - aabb.lowerBound[0], aabb.upperBound[1] - aabb.lowerBound[1]); + } + this.aabbGraphics.cleared = false; + } else if(!this.aabbGraphics.cleared){ + this.aabbGraphics.clear(); + this.aabbGraphics.cleared = true; + } + + this.renderer.render(this.container); +}; + +//http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb +function componentToHex(c) { + var hex = c.toString(16); + return hex.length === 1 ? "0" + hex : hex; +} +function rgbToHex(r, g, b) { + return componentToHex(r) + componentToHex(g) + componentToHex(b); +} +//http://stackoverflow.com/questions/43044/algorithm-to-randomly-generate-an-aesthetically-pleasing-color-palette +function randomPastelHex(){ + var mix = [255,255,255]; + var red = Math.floor(Math.random()*256); + var green = Math.floor(Math.random()*256); + var blue = Math.floor(Math.random()*256); + + // mix the color + red = Math.floor((red + 3*mix[0]) / 4); + green = Math.floor((green + 3*mix[1]) / 4); + blue = Math.floor((blue + 3*mix[2]) / 4); + + return rgbToHex(red,green,blue); +} + +WebGLRenderer.prototype.drawRenderable = function(obj, graphics, color, lineColor){ + var lw = this.lineWidth; + + var zero = [0,0]; + graphics.drawnSleeping = false; + graphics.drawnColor = color; + graphics.drawnLineColor = lineColor; + + if(obj instanceof p2.Body && obj.shapes.length){ + + var isSleeping = (obj.sleepState === p2.Body.SLEEPING); + graphics.drawnSleeping = isSleeping; + + if(obj.concavePath && !this.debugPolygons){ + var path = []; + for(var j=0; j!==obj.concavePath.length; j++){ + var v = obj.concavePath[j]; + path.push([v[0], v[1]]); + } + this.drawPath(graphics, path, lineColor, color, lw, isSleeping); + } else { + for(var i=0; i=c&&a<=c+this.width){var d=this.y;if(b>=d&&b<=d+this.height)return!0}return!1},e.Rectangle.prototype.constructor=e.Rectangle,e.Polygon=function(a){if(a instanceof Array||(a=Array.prototype.slice.call(arguments)),"number"==typeof a[0]){for(var b=[],c=0,d=a.length;d>c;c+=2)b.push(new e.Point(a[c],a[c+1]));a=b}this.points=a},e.Polygon.prototype.clone=function(){for(var a=[],b=0;bb!=i>b&&(h-f)*(b-g)/(i-g)+f>a;j&&(c=!c)}return c},e.Polygon.prototype.constructor=e.Polygon,e.Circle=function(a,b,c){this.x=a||0,this.y=b||0,this.radius=c||0},e.Circle.prototype.clone=function(){return new e.Circle(this.x,this.y,this.radius)},e.Circle.prototype.contains=function(a,b){if(this.radius<=0)return!1;var c=this.x-a,d=this.y-b,e=this.radius*this.radius;return c*=c,d*=d,e>=c+d},e.Circle.prototype.constructor=e.Circle,e.Ellipse=function(a,b,c,d){this.x=a||0,this.y=b||0,this.width=c||0,this.height=d||0},e.Ellipse.prototype.clone=function(){return new e.Ellipse(this.x,this.y,this.width,this.height)},e.Ellipse.prototype.contains=function(a,b){if(this.width<=0||this.height<=0)return!1;var c=(a-this.x)/this.width-.5,d=(b-this.y)/this.height-.5;return c*=c,d*=d,.25>c+d},e.Ellipse.prototype.getBounds=function(){return new e.Rectangle(this.x,this.y,this.width,this.height)},e.Ellipse.prototype.constructor=e.Ellipse,c(),e.mat3={},e.mat3.create=function(){var a=new e.Matrix(9);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=1,a[5]=0,a[6]=0,a[7]=0,a[8]=1,a},e.mat3.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=1,a[5]=0,a[6]=0,a[7]=0,a[8]=1,a},e.mat4={},e.mat4.create=function(){var a=new e.Matrix(16);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=0,a[5]=1,a[6]=0,a[7]=0,a[8]=0,a[9]=0,a[10]=1,a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1,a},e.mat3.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],f=a[2],g=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],m=b[0],n=b[1],o=b[2],p=b[3],q=b[4],r=b[5],s=b[6],t=b[7],u=b[8];return c[0]=m*d+n*g+o*j,c[1]=m*e+n*h+o*k,c[2]=m*f+n*i+o*l,c[3]=p*d+q*g+r*j,c[4]=p*e+q*h+r*k,c[5]=p*f+q*i+r*l,c[6]=s*d+t*g+u*j,c[7]=s*e+t*h+u*k,c[8]=s*f+t*i+u*l,c},e.mat3.clone=function(a){var b=new e.Matrix(9);return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b},e.mat3.transpose=function(a,b){if(!b||a===b){var c=a[1],d=a[2],e=a[5];return a[1]=a[3],a[2]=a[6],a[3]=c,a[5]=a[7],a[6]=d,a[7]=e,a}return b[0]=a[0],b[1]=a[3],b[2]=a[6],b[3]=a[1],b[4]=a[4],b[5]=a[7],b[6]=a[2],b[7]=a[5],b[8]=a[8],b},e.mat3.toMat4=function(a,b){return b||(b=e.mat4.create()),b[15]=1,b[14]=0,b[13]=0,b[12]=0,b[11]=0,b[10]=a[8],b[9]=a[7],b[8]=a[6],b[7]=0,b[6]=a[5],b[5]=a[4],b[4]=a[3],b[3]=0,b[2]=a[2],b[1]=a[1],b[0]=a[0],b},e.mat4.create=function(){var a=new e.Matrix(16);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=0,a[5]=1,a[6]=0,a[7]=0,a[8]=0,a[9]=0,a[10]=1,a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1,a},e.mat4.transpose=function(a,b){if(!b||a===b){var c=a[1],d=a[2],e=a[3],f=a[6],g=a[7],h=a[11];return a[1]=a[4],a[2]=a[8],a[3]=a[12],a[4]=c,a[6]=a[9],a[7]=a[13],a[8]=d,a[9]=f,a[11]=a[14],a[12]=e,a[13]=g,a[14]=h,a}return b[0]=a[0],b[1]=a[4],b[2]=a[8],b[3]=a[12],b[4]=a[1],b[5]=a[5],b[6]=a[9],b[7]=a[13],b[8]=a[2],b[9]=a[6],b[10]=a[10],b[11]=a[14],b[12]=a[3],b[13]=a[7],b[14]=a[11],b[15]=a[15],b},e.mat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],f=a[2],g=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],m=a[9],n=a[10],o=a[11],p=a[12],q=a[13],r=a[14],s=a[15],t=b[0],u=b[1],v=b[2],w=b[3];return c[0]=t*d+u*h+v*l+w*p,c[1]=t*e+u*i+v*m+w*q,c[2]=t*f+u*j+v*n+w*r,c[3]=t*g+u*k+v*o+w*s,t=b[4],u=b[5],v=b[6],w=b[7],c[4]=t*d+u*h+v*l+w*p,c[5]=t*e+u*i+v*m+w*q,c[6]=t*f+u*j+v*n+w*r,c[7]=t*g+u*k+v*o+w*s,t=b[8],u=b[9],v=b[10],w=b[11],c[8]=t*d+u*h+v*l+w*p,c[9]=t*e+u*i+v*m+w*q,c[10]=t*f+u*j+v*n+w*r,c[11]=t*g+u*k+v*o+w*s,t=b[12],u=b[13],v=b[14],w=b[15],c[12]=t*d+u*h+v*l+w*p,c[13]=t*e+u*i+v*m+w*q,c[14]=t*f+u*j+v*n+w*r,c[15]=t*g+u*k+v*o+w*s,c},e.identityMatrix=e.mat3.create(),e.DisplayObject=function(){this.last=this,this.first=this,this.position=new e.Point,this.scale=new e.Point(1,1),this.pivot=new e.Point(0,0),this.rotation=0,this.alpha=1,this.visible=!0,this.hitArea=null,this.buttonMode=!1,this.renderable=!1,this.parent=null,this.stage=null,this.worldAlpha=1,this._interactive=!1,this.defaultCursor="pointer",this.worldTransform=e.mat3.create(),this.localTransform=e.mat3.create(),this.color=[],this.dynamic=!0,this._sr=0,this._cr=1,this.filterArea=new e.Rectangle(0,0,1,1),this._bounds=new e.Rectangle(0,0,1,1),this._currentBounds=null,this._mask=null},e.DisplayObject.prototype.constructor=e.DisplayObject,e.DisplayObject.prototype.setInteractive=function(a){this.interactive=a},Object.defineProperty(e.DisplayObject.prototype,"interactive",{get:function(){return this._interactive},set:function(a){this._interactive=a,this.stage&&(this.stage.dirty=!0)}}),Object.defineProperty(e.DisplayObject.prototype,"worldVisible",{get:function(){var a=this;do{if(!a.visible)return!1;a=a.parent}while(a);return!0}}),Object.defineProperty(e.DisplayObject.prototype,"mask",{get:function(){return this._mask},set:function(a){this._mask&&(this._mask.isMask=!1),this._mask=a,this._mask&&(this._mask.isMask=!0)}}),Object.defineProperty(e.DisplayObject.prototype,"filters",{get:function(){return this._filters},set:function(a){if(a){for(var b=[],c=0;c=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);a.parent&&a.parent.removeChild(a),a.parent=this,this.children.splice(b,0,a),this.stage&&a.setStageReference(this.stage)},e.DisplayObjectContainer.prototype.swapChildren=function(a,b){if(a!==b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(0>c||0>d)throw new Error("swapChildren: Both the supplied DisplayObjects must be a child of the caller.");this.children[c]=b,this.children[d]=a}},e.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&aa;a++)this.children[a].updateTransform()}},e.DisplayObjectContainer.prototype.getBounds=function(){if(0===this.children.length)return e.EmptyRectangle;for(var a,b,c,d=1/0,f=1/0,g=-1/0,h=-1/0,i=0,j=this.children.length;j>i;i++){var k=this.children[i];k.visible&&(a=this.children[i].getBounds(),d=db?g:b,h=h>c?h:c)}var l=this._bounds;return l.x=d,l.y=f,l.width=g-d,l.height=h-f,l},e.DisplayObjectContainer.prototype.setStageReference=function(a){this.stage=a,this._interactive&&(this.stage.dirty=!0);for(var b=0,c=this.children.length;c>b;b++){var d=this.children[b];d.setStageReference(a)}},e.DisplayObjectContainer.prototype.removeStageReference=function(){for(var a=0,b=this.children.length;b>a;a++){var c=this.children[a];c.removeStageReference()}this._interactive&&(this.stage.dirty=!0),this.stage=null},e.DisplayObjectContainer.prototype._renderWebGL=function(a){if(this.visible!==!1&&0!==this.alpha){var b,c;if(this._mask||this._filters){for(this._mask&&(a.spriteBatch.stop(),a.maskManager.pushMask(this.mask,a),a.spriteBatch.start()),this._filters&&(a.spriteBatch.flush(),a.filterManager.pushFilter(this._filterBlock)),b=0,c=this.children.length;c>b;b++)this.children[b]._renderWebGL(a);a.spriteBatch.stop(),this._filters&&a.filterManager.popFilter(),this._mask&&a.maskManager.popMask(a),a.spriteBatch.start()}else for(b=0,c=this.children.length;c>b;b++)this.children[b]._renderWebGL(a)}},e.DisplayObjectContainer.prototype._renderCanvas=function(a){if(this.visible!==!1&&0!==this.alpha){this._mask&&a.maskManager.pushMask(this._mask,a.context);for(var b=0,c=this.children.length;c>b;b++){var d=this.children[b];d._renderCanvas(a)}this._mask&&a.maskManager.popMask(a.context)}},e.blendModes={},e.blendModes.NORMAL=0,e.blendModes.ADD=1,e.blendModes.MULTIPLY=2,e.blendModes.SCREEN=3,e.Sprite=function(a){e.DisplayObjectContainer.call(this),this.anchor=new e.Point,this.texture=a,this._width=0,this._height=0,this.tint=16777215,this.blendMode=e.blendModes.NORMAL,a.baseTexture.hasLoaded?this.onTextureUpdate():(this.onTextureUpdateBind=this.onTextureUpdate.bind(this),this.texture.addEventListener("update",this.onTextureUpdateBind)),this.renderable=!0},e.Sprite.prototype=Object.create(e.DisplayObjectContainer.prototype),e.Sprite.prototype.constructor=e.Sprite,Object.defineProperty(e.Sprite.prototype,"width",{get:function(){return this.scale.x*this.texture.frame.width},set:function(a){this.scale.x=a/this.texture.frame.width,this._width=a}}),Object.defineProperty(e.Sprite.prototype,"height",{get:function(){return this.scale.y*this.texture.frame.height},set:function(a){this.scale.y=a/this.texture.frame.height,this._height=a}}),e.Sprite.prototype.setTexture=function(a){this.texture.baseTexture!==a.baseTexture?(this.textureChange=!0,this.texture=a):this.texture=a,this.cachedTint=16777215,this.updateFrame=!0},e.Sprite.prototype.onTextureUpdate=function(){this._width&&(this.scale.x=this._width/this.texture.frame.width),this._height&&(this.scale.y=this._height/this.texture.frame.height),this.updateFrame=!0},e.Sprite.prototype.getBounds=function(){var a=this.texture.frame.width,b=this.texture.frame.height,c=a*(1-this.anchor.x),d=a*-this.anchor.x,e=b*(1-this.anchor.y),f=b*-this.anchor.y,g=this.worldTransform,h=g[0],i=g[3],j=g[1],k=g[4],l=g[2],m=g[5],n=h*d+j*f+l,o=k*f+i*d+m,p=h*c+j*f+l,q=k*f+i*c+m,r=h*c+j*e+l,s=k*e+i*c+m,t=h*d+j*e+l,u=k*e+i*d+m,v=-1/0,w=-1/0,x=1/0,y=1/0;x=x>n?n:x,x=x>p?p:x,x=x>r?r:x,x=x>t?t:x,y=y>o?o:y,y=y>q?q:y,y=y>s?s:y,y=y>u?u:y,v=n>v?n:v,v=p>v?p:v,v=r>v?r:v,v=t>v?t:v,w=o>w?o:w,w=q>w?q:w,w=s>w?s:w,w=u>w?u:w;var z=this._bounds;return z.x=x,z.width=v-x,z.y=y,z.height=w-y,this._currentBounds=z,z},e.Sprite.prototype._renderWebGL=function(a){if(this.visible!==!1&&0!==this.alpha){var b,c;if(this._mask||this._filters){var d=a.spriteBatch;for(this._mask&&(d.stop(),a.maskManager.pushMask(this.mask,a),d.start()),this._filters&&(d.flush(),a.filterManager.pushFilter(this._filterBlock)),d.render(this),b=0,c=this.children.length;c>b;b++)this.children[b]._renderWebGL(a);d.stop(),this._filters&&a.filterManager.popFilter(),this._mask&&a.maskManager.popMask(a),d.start()}else for(a.spriteBatch.render(this),b=0,c=this.children.length;c>b;b++)this.children[b]._renderWebGL(a)}},e.Sprite.prototype._renderCanvas=function(a){if(this.visible!==!1&&0!==this.alpha){this._mask&&a.maskManager.pushMask(this._mask,a.context);var b=this.texture.frame,c=a.context,d=this.texture;if(b&&b.width&&b.height&&d.baseTexture.source){c.globalAlpha=this.worldAlpha;var f=this.worldTransform;if(c.setTransform(f[0],f[3],f[1],f[4],f[2],f[5]),this.blendMode!==a.currentBlendMode&&(a.currentBlendMode=this.blendMode,c.globalCompositeOperation=e.blendModesCanvas[a.currentBlendMode]),16777215!==this.tint){if(this.cachedTint!==this.tint){if(!d.baseTexture.hasLoaded)return;this.cachedTint=this.tint,this.tintedTexture=e.CanvasTinter.getTintedTexture(this,this.tint)}c.drawImage(this.tintedTexture,0,0,b.width,b.height,this.anchor.x*-b.width,this.anchor.y*-b.height,b.width,b.height)}else if(d.trimmed){var g=d.trim;c.drawImage(this.texture.baseTexture.source,b.x,b.y,b.width,b.height,g.x-this.anchor.x*g.realWidth,g.y-this.anchor.y*g.realHeight,b.width,b.height)}else c.drawImage(this.texture.baseTexture.source,b.x,b.y,b.width,b.height,this.anchor.x*-b.width,this.anchor.y*-b.height,b.width,b.height)}for(var h=0,i=this.children.length;i>h;h++){var j=this.children[h];j._renderCanvas(a)}this._mask&&a.maskManager.popMask(a.context)}},e.Sprite.fromFrame=function(a){var b=e.TextureCache[a];if(!b)throw new Error('The frameId "'+a+'" does not exist in the texture cache'+this);return new e.Sprite(b)},e.Sprite.fromImage=function(a){var b=e.Texture.fromImage(a);return new e.Sprite(b)},e.MovieClip=function(a){e.Sprite.call(this,a[0]),this.textures=a,this.animationSpeed=1,this.loop=!0,this.onComplete=null,this.currentFrame=0,this.playing=!1},e.MovieClip.prototype=Object.create(e.Sprite.prototype),e.MovieClip.prototype.constructor=e.MovieClip,Object.defineProperty(e.MovieClip.prototype,"totalFrames",{get:function(){return this.textures.length}}),e.MovieClip.prototype.stop=function(){this.playing=!1},e.MovieClip.prototype.play=function(){this.playing=!0},e.MovieClip.prototype.gotoAndStop=function(a){this.playing=!1,this.currentFrame=a;var b=this.currentFrame+.5|0;this.setTexture(this.textures[b%this.textures.length])},e.MovieClip.prototype.gotoAndPlay=function(a){this.currentFrame=a,this.playing=!0},e.MovieClip.prototype.updateTransform=function(){if(e.Sprite.prototype.updateTransform.call(this),this.playing){this.currentFrame+=this.animationSpeed;var a=this.currentFrame+.5|0;this.loop||a=this.textures.length&&(this.gotoAndStop(this.textures.length-1),this.onComplete&&this.onComplete())}},e.FilterBlock=function(){this.visible=!0,this.renderable=!0},e.Text=function(a,b){this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),e.Sprite.call(this,e.Texture.fromCanvas(this.canvas)),this.setText(a),this.setStyle(b),this.updateText(),this.dirty=!1},e.Text.prototype=Object.create(e.Sprite.prototype),e.Text.prototype.constructor=e.Text,e.Text.prototype.setStyle=function(a){a=a||{},a.font=a.font||"bold 20pt Arial",a.fill=a.fill||"black",a.align=a.align||"left",a.stroke=a.stroke||"black",a.strokeThickness=a.strokeThickness||0,a.wordWrap=a.wordWrap||!1,a.wordWrapWidth=a.wordWrapWidth||100,this.style=a,this.dirty=!0},e.Text.prototype.setText=function(a){this.text=a.toString()||" ",this.dirty=!0},e.Text.prototype.updateText=function(){this.context.font=this.style.font;var a=this.text;this.style.wordWrap&&(a=this.wordWrap(this.text));for(var b=a.split(/(?:\r\n|\r|\n)/),c=[],d=0,f=0;fe?(g>0&&(b+="\n"),b+=f[g]+" ",e=this.style.wordWrapWidth-h):(e-=i,b+=f[g]+" ")}b+="\n"}return b},e.Text.prototype.destroy=function(a){a&&this.texture.destroy()},e.Text.heightCache={},e.BitmapText=function(a,b){e.DisplayObjectContainer.call(this),this.setText(a),this.setStyle(b),this.updateText(),this.dirty=!1},e.BitmapText.prototype=Object.create(e.DisplayObjectContainer.prototype),e.BitmapText.prototype.constructor=e.BitmapText,e.BitmapText.prototype.setText=function(a){this.text=a||" ",this.dirty=!0},e.BitmapText.prototype.setStyle=function(a){a=a||{},a.align=a.align||"left",this.style=a;var b=a.font.split(" ");this.fontName=b[b.length-1],this.fontSize=b.length>=2?parseInt(b[b.length-2],10):e.BitmapText.fonts[this.fontName].size,this.dirty=!0},e.BitmapText.prototype.updateText=function(){for(var a=e.BitmapText.fonts[this.fontName],b=new e.Point,c=null,d=[],f=0,g=[],h=0,i=this.fontSize/a.size,j=0;j=j;j++){var n=0;"right"===this.style.align?n=f-g[j]:"center"===this.style.align&&(n=(f-g[j])/2),m.push(n)}for(j=0;j0;)this.removeChild(this.getChildAt(0));this.updateText(),this.dirty=!1}e.DisplayObjectContainer.prototype.updateTransform.call(this)},e.BitmapText.fonts={},e.InteractionManager=function(a){this.stage=a,this.mouse=new e.InteractionData,this.touchs={},this.tempPoint=new e.Point,this.mouseoverEnabled=!0,this.pool=[],this.interactiveItems=[],this.interactionDOMElement=null,this.onMouseMove=this.onMouseMove.bind(this),this.onMouseDown=this.onMouseDown.bind(this),this.onMouseOut=this.onMouseOut.bind(this),this.onMouseUp=this.onMouseUp.bind(this),this.onTouchStart=this.onTouchStart.bind(this),this.onTouchEnd=this.onTouchEnd.bind(this),this.onTouchMove=this.onTouchMove.bind(this),this.last=0,this.currentCursor="inherit"},e.InteractionManager.prototype.constructor=e.InteractionManager,e.InteractionManager.prototype.collectInteractiveSprite=function(a,b){for(var c=a.children,d=c.length,e=d-1;e>=0;e--){var f=c[e];f.interactive?(b.interactiveChildren=!0,this.interactiveItems.push(f),f.children.length>0&&this.collectInteractiveSprite(f,f)):(f.__iParent=null,f.children.length>0&&this.collectInteractiveSprite(f,b))}},e.InteractionManager.prototype.setTarget=function(a){this.target=a,null===this.interactionDOMElement&&this.setTargetDomElement(a.view),document.body.addEventListener("mouseup",this.onMouseUp,!0)},e.InteractionManager.prototype.setTargetDomElement=function(a){null!==this.interactionDOMElement&&(this.interactionDOMElement.style["-ms-content-zooming"]="",this.interactionDOMElement.style["-ms-touch-action"]="",this.interactionDOMElement.removeEventListener("mousemove",this.onMouseMove,!0),this.interactionDOMElement.removeEventListener("mousedown",this.onMouseDown,!0),this.interactionDOMElement.removeEventListener("mouseout",this.onMouseOut,!0),this.interactionDOMElement.removeEventListener("touchstart",this.onTouchStart,!0),this.interactionDOMElement.removeEventListener("touchend",this.onTouchEnd,!0),this.interactionDOMElement.removeEventListener("touchmove",this.onTouchMove,!0)),window.navigator.msPointerEnabled&&(a.style["-ms-content-zooming"]="none",a.style["-ms-touch-action"]="none"),this.interactionDOMElement=a,a.addEventListener("mousemove",this.onMouseMove,!0),a.addEventListener("mousedown",this.onMouseDown,!0),a.addEventListener("mouseout",this.onMouseOut,!0),a.addEventListener("touchstart",this.onTouchStart,!0),a.addEventListener("touchend",this.onTouchEnd,!0),a.addEventListener("touchmove",this.onTouchMove,!0)},e.InteractionManager.prototype.update=function(){if(this.target){var a=Date.now(),b=a-this.last;if(b=30*b/1e3,!(1>b)){this.last=a;var c=0;if(this.dirty){this.dirty=!1;var d=this.interactiveItems.length;for(c=0;d>c;c++)this.interactiveItems[c].interactiveChildren=!1;this.interactiveItems=[],this.stage.interactive&&this.interactiveItems.push(this.stage),this.collectInteractiveSprite(this.stage,this.stage)}var e=this.interactiveItems.length;for(c=0;e>c;c++){var f=this.interactiveItems[c];(f.mouseover||f.mouseout||f.buttonMode)&&(f.__hit=this.hitTest(f,this.mouse),this.mouse.target=f,f.__hit?(f.buttonMode&&(this.currentCursor=f.defaultCursor),f.__isOver||(f.mouseover&&f.mouseover(this.mouse),f.__isOver=!0)):f.__isOver&&(f.mouseout&&f.mouseout(this.mouse),f.__isOver=!1))}this.currentCursor!==this.interactionDOMElement.style.cursor&&(this.interactionDOMElement.style.cursor=this.currentCursor)}}},e.InteractionManager.prototype.onMouseMove=function(a){this.mouse.originalEvent=a||window.event;var b=this.interactionDOMElement.getBoundingClientRect();this.mouse.global.x=(a.clientX-b.left)*(this.target.width/b.width),this.mouse.global.y=(a.clientY-b.top)*(this.target.height/b.height);for(var c=this.interactiveItems.length,d=0;c>d;d++){var e=this.interactiveItems[d];e.mousemove&&e.mousemove(this.mouse)}},e.InteractionManager.prototype.onMouseDown=function(a){this.mouse.originalEvent=a||window.event;for(var b=this.interactiveItems.length,c=0;b>c;c++){var d=this.interactiveItems[c];if((d.mousedown||d.click)&&(d.__mouseIsDown=!0,d.__hit=this.hitTest(d,this.mouse),d.__hit&&(d.mousedown&&d.mousedown(this.mouse),d.__isDown=!0,!d.interactiveChildren)))break}},e.InteractionManager.prototype.onMouseOut=function(){var a=this.interactiveItems.length;this.currentCursor="inherit";for(var b=0;a>b;b++){var c=this.interactiveItems[b];c.__isOver&&(this.mouse.target=c,c.mouseout&&c.mouseout(this.mouse),c.__isOver=!1)}},e.InteractionManager.prototype.onMouseUp=function(a){this.mouse.originalEvent=a||window.event;for(var b=this.interactiveItems.length,c=!1,d=0;b>d;d++){var e=this.interactiveItems[d];(e.mouseup||e.mouseupoutside||e.click)&&(e.__hit=this.hitTest(e,this.mouse),e.__hit&&!c?(e.mouseup&&e.mouseup(this.mouse),e.__isDown&&e.click&&e.click(this.mouse),e.interactiveChildren||(c=!0)):e.__isDown&&e.mouseupoutside&&e.mouseupoutside(this.mouse),e.__isDown=!1)}},e.InteractionManager.prototype.hitTest=function(a,b){var c=b.global;if(!a.worldVisible)return!1;var d=a instanceof e.Sprite,f=a.worldTransform,g=f[0],h=f[1],i=f[2],j=f[3],k=f[4],l=f[5],m=1/(g*k+h*-j),n=k*m*c.x+-h*m*c.y+(l*h-i*k)*m,o=g*m*c.y+-j*m*c.x+(-l*g+i*j)*m;if(b.target=a,a.hitArea&&a.hitArea.contains)return a.hitArea.contains(n,o)?(b.target=a,!0):!1;if(d){var p,q=a.texture.frame.width,r=a.texture.frame.height,s=-q*a.anchor.x;if(n>s&&s+q>n&&(p=-r*a.anchor.y,o>p&&p+r>o))return b.target=a,!0}for(var t=a.children.length,u=0;t>u;u++){var v=a.children[u],w=this.hitTest(v,b);if(w)return b.target=a,!0}return!1},e.InteractionManager.prototype.onTouchMove=function(a){var b,c=this.interactionDOMElement.getBoundingClientRect(),d=a.changedTouches,e=0;for(e=0;ee;e++){var h=this.interactiveItems[e];h.touchmove&&h.touchmove(b)}},e.InteractionManager.prototype.onTouchStart=function(a){for(var b=this.interactionDOMElement.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i];if((j.touchstart||j.tap)&&(j.__hit=this.hitTest(j,g),j.__hit&&(j.touchstart&&j.touchstart(g),j.__isDown=!0,j.__touchData=g,!j.interactiveChildren)))break}}},e.InteractionManager.prototype.onTouchEnd=function(a){for(var b=this.interactionDOMElement.getBoundingClientRect(),c=a.changedTouches,d=0;di;i++){var j=this.interactiveItems[i],k=j.__touchData;j.__hit=this.hitTest(j,f),k===f&&(f.originalEvent=a||window.event,(j.touchend||j.tap)&&(j.__hit&&!g?(j.touchend&&j.touchend(f),j.__isDown&&j.tap&&j.tap(f),j.interactiveChildren||(g=!0)):j.__isDown&&j.touchendoutside&&j.touchendoutside(f),j.__isDown=!1),j.__touchData=null)}this.pool.push(f),this.touchs[e.identifier]=null}},e.InteractionData=function(){this.global=new e.Point,this.local=new e.Point,this.target=null,this.originalEvent=null},e.InteractionData.prototype.getLocalPosition=function(a){var b=a.worldTransform,c=this.global,d=b[0],f=b[1],g=b[2],h=b[3],i=b[4],j=b[5],k=1/(d*i+f*-h);return new e.Point(i*k*c.x+-f*k*c.y+(j*f-g*i)*k,d*k*c.y+-h*k*c.x+(-j*d+g*h)*k)},e.InteractionData.prototype.constructor=e.InteractionData,e.Stage=function(a){e.DisplayObjectContainer.call(this),this.worldTransform=e.mat3.create(),this.interactive=!0,this.interactionManager=new e.InteractionManager(this),this.dirty=!0,this.stage=this,this.stage.hitArea=new e.Rectangle(0,0,1e5,1e5),this.setBackgroundColor(a)},e.Stage.prototype=Object.create(e.DisplayObjectContainer.prototype),e.Stage.prototype.constructor=e.Stage,e.Stage.prototype.setInteractionDelegate=function(a){this.interactionManager.setTargetDomElement(a)},e.Stage.prototype.updateTransform=function(){this.worldAlpha=1;for(var a=0,b=this.children.length;b>a;a++)this.children[a].updateTransform();this.dirty&&(this.dirty=!1,this.interactionManager.dirty=!0),this.interactive&&this.interactionManager.update()},e.Stage.prototype.setBackgroundColor=function(a){this.backgroundColor=a||0,this.backgroundColorSplit=e.hex2rgb(this.backgroundColor);var b=this.backgroundColor.toString(16);b="000000".substr(0,6-b.length)+b,this.backgroundColorString="#"+b},e.Stage.prototype.getMousePosition=function(){return this.interactionManager.mouse.global};for(var f=0,h=["ms","moz","webkit","o"],i=0;i>16&255)/255,(a>>8&255)/255,(255&a)/255]},e.rgb2hex=function(a){return(255*a[0]<<16)+(255*a[1]<<8)+255*a[2]},"function"!=typeof Function.prototype.bind&&(Function.prototype.bind=function(){var a=Array.prototype.slice;return function(b){function c(){var f=e.concat(a.call(arguments));d.apply(this instanceof c?this:b,f)}var d=this,e=a.call(arguments,1);if("function"!=typeof d)throw new TypeError;return c.prototype=function f(a){return a&&(f.prototype=a),this instanceof f?void 0:new f}(d.prototype),c}}()),e.AjaxRequest=function(){var a=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.3.0","Microsoft.XMLHTTP"];if(!window.ActiveXObject)return window.XMLHttpRequest?new window.XMLHttpRequest:!1;for(var b=0;b0&&0===(a&a-1))return a;for(var b=1;a>b;)b<<=1;return b},e.EventTarget=function(){var a={};this.addEventListener=this.on=function(b,c){void 0===a[b]&&(a[b]=[]),-1===a[b].indexOf(c)&&a[b].push(c)},this.dispatchEvent=this.emit=function(b){if(a[b.type]&&a[b.type].length)for(var c=0,d=a[b.type].length;d>c;c++)a[b.type][c](b)},this.removeEventListener=this.off=function(b,c){var d=a[b].indexOf(c);-1!==d&&a[b].splice(d,1)},this.removeAllEventListeners=function(b){var c=a[b];c&&(c.length=0)}},e.autoDetectRenderer=function(a,b,c,d,f){a||(a=800),b||(b=600);var g=function(){try{var a=document.createElement("canvas");return!!window.WebGLRenderingContext&&(a.getContext("webgl")||a.getContext("experimental-webgl")) +}catch(b){return!1}}();return g?new e.WebGLRenderer(a,b,c,d,f):new e.CanvasRenderer(a,b,c,d)},e.PolyK={},e.PolyK.Triangulate=function(a){var b=!0,c=a.length>>1;if(3>c)return[];for(var d=[],f=[],g=0;c>g;g++)f.push(g);g=0;for(var h=c;h>3;){var i=f[(g+0)%h],j=f[(g+1)%h],k=f[(g+2)%h],l=a[2*i],m=a[2*i+1],n=a[2*j],o=a[2*j+1],p=a[2*k],q=a[2*k+1],r=!1;if(e.PolyK._convex(l,m,n,o,p,q,b)){r=!0;for(var s=0;h>s;s++){var t=f[s];if(t!==i&&t!==j&&t!==k&&e.PolyK._PointInTriangle(a[2*t],a[2*t+1],l,m,n,o,p,q)){r=!1;break}}}if(r)d.push(i,j,k),f.splice((g+1)%h,1),h--,g=0;else if(g++>3*h){if(!b)return window.console.log("PIXI Warning: shape too complex to fill"),[];for(d=[],f=[],g=0;c>g;g++)f.push(g);g=0,h=c,b=!1}}return d.push(f[0],f[1],f[2]),d},e.PolyK._PointInTriangle=function(a,b,c,d,e,f,g,h){var i=g-c,j=h-d,k=e-c,l=f-d,m=a-c,n=b-d,o=i*i+j*j,p=i*k+j*l,q=i*m+j*n,r=k*k+l*l,s=k*m+l*n,t=1/(o*r-p*p),u=(r*q-p*s)*t,v=(o*s-p*q)*t;return u>=0&&v>=0&&1>u+v},e.PolyK._convex=function(a,b,c,d,e,f,g){return(b-d)*(e-c)+(c-a)*(f-d)>=0===g},e.initDefaultShaders=function(){},e.CompileVertexShader=function(a,b){return e._CompileShader(a,b,a.VERTEX_SHADER)},e.CompileFragmentShader=function(a,b){return e._CompileShader(a,b,a.FRAGMENT_SHADER)},e._CompileShader=function(a,b,c){var d=b.join("\n"),e=a.createShader(c);return a.shaderSource(e,d),a.compileShader(e),a.getShaderParameter(e,a.COMPILE_STATUS)?e:(window.console.log(a.getShaderInfoLog(e)),null)},e.compileProgram=function(a,b,c){var d=e.CompileFragmentShader(a,c),f=e.CompileVertexShader(a,b),g=a.createProgram();return a.attachShader(g,f),a.attachShader(g,d),a.linkProgram(g),a.getProgramParameter(g,a.LINK_STATUS)||window.console.log("Could not initialise shaders"),g},e.PixiShader=function(a){this.gl=a,this.program=null,this.fragmentSrc=["precision lowp float;","varying vec2 vTextureCoord;","varying vec4 vColor;","uniform sampler2D uSampler;","void main(void) {"," gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;","}"],this.textureCount=0,this.init()},e.PixiShader.prototype.init=function(){var a=this.gl,b=e.compileProgram(a,this.vertexSrc||e.PixiShader.defaultVertexSrc,this.fragmentSrc);a.useProgram(b),this.uSampler=a.getUniformLocation(b,"uSampler"),this.projectionVector=a.getUniformLocation(b,"projectionVector"),this.offsetVector=a.getUniformLocation(b,"offsetVector"),this.dimensions=a.getUniformLocation(b,"dimensions"),this.aVertexPosition=a.getAttribLocation(b,"aVertexPosition"),this.aTextureCoord=a.getAttribLocation(b,"aTextureCoord"),this.colorAttribute=a.getAttribLocation(b,"aColor"),-1===this.colorAttribute&&(this.colorAttribute=2);for(var c in this.uniforms)this.uniforms[c].uniformLocation=a.getUniformLocation(b,c);this.initUniforms(),this.program=b},e.PixiShader.prototype.initUniforms=function(){this.textureCount=1;var a,b=this.gl;for(var c in this.uniforms){a=this.uniforms[c];var d=a.type;"sampler2D"===d?(a._init=!1,null!==a.value&&this.initSampler2D(a)):"mat2"===d||"mat3"===d||"mat4"===d?(a.glMatrix=!0,a.glValueLength=1,"mat2"===d?a.glFunc=b.uniformMatrix2fv:"mat3"===d?a.glFunc=b.uniformMatrix3fv:"mat4"===d&&(a.glFunc=b.uniformMatrix4fv)):(a.glFunc=b["uniform"+d],a.glValueLength="2f"===d||"2i"===d?2:"3f"===d||"3i"===d?3:"4f"===d||"4i"===d?4:1)}},e.PixiShader.prototype.initSampler2D=function(a){if(a.value&&a.value.baseTexture&&a.value.baseTexture.hasLoaded){var b=this.gl;if(b.activeTexture(b["TEXTURE"+this.textureCount]),b.bindTexture(b.TEXTURE_2D,a.value.baseTexture._glTexture),a.textureData){var c=a.textureData,d=c.magFilter?c.magFilter:b.LINEAR,e=c.minFilter?c.minFilter:b.LINEAR,f=c.wrapS?c.wrapS:b.CLAMP_TO_EDGE,g=c.wrapT?c.wrapT:b.CLAMP_TO_EDGE,h=c.luminance?b.LUMINANCE:b.RGBA;if(c.repeat&&(f=b.REPEAT,g=b.REPEAT),b.pixelStorei(b.UNPACK_FLIP_Y_WEBGL,!1),c.width){var i=c.width?c.width:512,j=c.height?c.height:2,k=c.border?c.border:0;b.texImage2D(b.TEXTURE_2D,0,h,i,j,k,h,b.UNSIGNED_BYTE,null)}else b.texImage2D(b.TEXTURE_2D,0,h,b.RGBA,b.UNSIGNED_BYTE,a.value.baseTexture.source);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,d),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,e),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,f),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,g)}b.uniform1i(a.uniformLocation,this.textureCount),a._init=!0,this.textureCount++}},e.PixiShader.prototype.syncUniforms=function(){this.textureCount=1;var a,b=this.gl;for(var c in this.uniforms)a=this.uniforms[c],1===a.glValueLength?a.glMatrix===!0?a.glFunc.call(b,a.uniformLocation,a.transpose,a.value):a.glFunc.call(b,a.uniformLocation,a.value):2===a.glValueLength?a.glFunc.call(b,a.uniformLocation,a.value.x,a.value.y):3===a.glValueLength?a.glFunc.call(b,a.uniformLocation,a.value.x,a.value.y,a.value.z):4===a.glValueLength?a.glFunc.call(b,a.uniformLocation,a.value.x,a.value.y,a.value.z,a.value.w):"sampler2D"===a.type&&(a._init?(b.activeTexture(b["TEXTURE"+this.textureCount]),b.bindTexture(b.TEXTURE_2D,a.value.baseTexture._glTextures[b.id]||e.createWebGLTexture(a.value.baseTexture,b)),b.uniform1i(a.uniformLocation,this.textureCount),this.textureCount++):this.initSampler2D(a))},e.PixiShader.defaultVertexSrc=["attribute vec2 aVertexPosition;","attribute vec2 aTextureCoord;","attribute vec2 aColor;","uniform vec2 projectionVector;","uniform vec2 offsetVector;","varying vec2 vTextureCoord;","varying vec4 vColor;","const vec2 center = vec2(-1.0, 1.0);","void main(void) {"," gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);"," vTextureCoord = aTextureCoord;"," vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;"," vColor = vec4(color * aColor.x, aColor.x);","}"],e.StripShader=function(){this.program=null,this.fragmentSrc=["precision mediump float;","varying vec2 vTextureCoord;","varying float vColor;","uniform float alpha;","uniform sampler2D uSampler;","void main(void) {"," gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y));"," gl_FragColor = gl_FragColor * alpha;","}"],this.vertexSrc=["attribute vec2 aVertexPosition;","attribute vec2 aTextureCoord;","attribute float aColor;","uniform mat3 translationMatrix;","uniform vec2 projectionVector;","varying vec2 vTextureCoord;","uniform vec2 offsetVector;","varying float vColor;","void main(void) {"," vec3 v = translationMatrix * vec3(aVertexPosition, 1.0);"," v -= offsetVector.xyx;"," gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / projectionVector.y + 1.0 , 0.0, 1.0);"," vTextureCoord = aTextureCoord;"," vColor = aColor;","}"]},e.StripShader.prototype.init=function(){var a=e.gl,b=e.compileProgram(a,this.vertexSrc,this.fragmentSrc);a.useProgram(b),this.uSampler=a.getUniformLocation(b,"uSampler"),this.projectionVector=a.getUniformLocation(b,"projectionVector"),this.offsetVector=a.getUniformLocation(b,"offsetVector"),this.colorAttribute=a.getAttribLocation(b,"aColor"),this.aVertexPosition=a.getAttribLocation(b,"aVertexPosition"),this.aTextureCoord=a.getAttribLocation(b,"aTextureCoord"),this.translationMatrix=a.getUniformLocation(b,"translationMatrix"),this.alpha=a.getUniformLocation(b,"alpha"),this.program=b},e.PrimitiveShader=function(a){this.gl=a,this.program=null,this.fragmentSrc=["precision mediump float;","varying vec4 vColor;","void main(void) {"," gl_FragColor = vColor;","}"],this.vertexSrc=["attribute vec2 aVertexPosition;","attribute vec4 aColor;","uniform mat3 translationMatrix;","uniform vec2 projectionVector;","uniform vec2 offsetVector;","uniform float alpha;","uniform vec3 tint;","varying vec4 vColor;","void main(void) {"," vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);"," v -= offsetVector.xyx;"," gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);"," vColor = aColor * vec4(tint * alpha, alpha);","}"],this.init()},e.PrimitiveShader.prototype.init=function(){var a=this.gl,b=e.compileProgram(a,this.vertexSrc,this.fragmentSrc);a.useProgram(b),this.projectionVector=a.getUniformLocation(b,"projectionVector"),this.offsetVector=a.getUniformLocation(b,"offsetVector"),this.tintColor=a.getUniformLocation(b,"tint"),this.aVertexPosition=a.getAttribLocation(b,"aVertexPosition"),this.colorAttribute=a.getAttribLocation(b,"aColor"),this.translationMatrix=a.getUniformLocation(b,"translationMatrix"),this.alpha=a.getUniformLocation(b,"alpha"),this.program=b},e.WebGLGraphics=function(){},e.WebGLGraphics.renderGraphics=function(a,b){var c=b.gl,d=b.projection,f=b.offset,g=b.shaderManager.primitiveShader;a._webGL[c.id]||(a._webGL[c.id]={points:[],indices:[],lastIndex:0,buffer:c.createBuffer(),indexBuffer:c.createBuffer()});var h=a._webGL[c.id];a.dirty&&(a.dirty=!1,a.clearDirty&&(a.clearDirty=!1,h.lastIndex=0,h.points=[],h.indices=[]),e.WebGLGraphics.updateGraphics(a,c)),b.shaderManager.activatePrimitiveShader();var i=e.mat3.clone(a.worldTransform);e.mat3.transpose(i),c.blendFunc(c.ONE,c.ONE_MINUS_SRC_ALPHA),c.uniformMatrix3fv(g.translationMatrix,!1,i),c.uniform2f(g.projectionVector,d.x,-d.y),c.uniform2f(g.offsetVector,-f.x,-f.y),c.uniform3fv(g.tintColor,e.hex2rgb(a.tint)),c.uniform1f(g.alpha,a.worldAlpha),c.bindBuffer(c.ARRAY_BUFFER,h.buffer),c.vertexAttribPointer(g.aVertexPosition,2,c.FLOAT,!1,24,0),c.vertexAttribPointer(g.colorAttribute,4,c.FLOAT,!1,24,8),c.bindBuffer(c.ELEMENT_ARRAY_BUFFER,h.indexBuffer),c.drawElements(c.TRIANGLE_STRIP,h.indices.length,c.UNSIGNED_SHORT,0),b.shaderManager.deactivatePrimitiveShader()},e.WebGLGraphics.updateGraphics=function(a,b){for(var c=a._webGL[b.id],d=c.lastIndex;d3&&e.WebGLGraphics.buildPoly(f,c),f.lineWidth>0&&e.WebGLGraphics.buildLine(f,c)):f.type===e.Graphics.RECT?e.WebGLGraphics.buildRectangle(f,c):(f.type===e.Graphics.CIRC||f.type===e.Graphics.ELIP)&&e.WebGLGraphics.buildCircle(f,c)}c.lastIndex=a.graphicsData.length,c.glPoints=new Float32Array(c.points),b.bindBuffer(b.ARRAY_BUFFER,c.buffer),b.bufferData(b.ARRAY_BUFFER,c.glPoints,b.STATIC_DRAW),c.glIndicies=new Uint16Array(c.indices),b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,c.indexBuffer),b.bufferData(b.ELEMENT_ARRAY_BUFFER,c.glIndicies,b.STATIC_DRAW)},e.WebGLGraphics.buildRectangle=function(a,b){var c=a.points,d=c[0],f=c[1],g=c[2],h=c[3];if(a.fill){var i=e.hex2rgb(a.fillColor),j=a.fillAlpha,k=i[0]*j,l=i[1]*j,m=i[2]*j,n=b.points,o=b.indices,p=n.length/6;n.push(d,f),n.push(k,l,m,j),n.push(d+g,f),n.push(k,l,m,j),n.push(d,f+h),n.push(k,l,m,j),n.push(d+g,f+h),n.push(k,l,m,j),o.push(p,p,p+1,p+2,p+3,p+3)}if(a.lineWidth){var q=a.points;a.points=[d,f,d+g,f,d+g,f+h,d,f+h,d,f],e.WebGLGraphics.buildLine(a,b),a.points=q}},e.WebGLGraphics.buildCircle=function(a,b){var c=a.points,d=c[0],f=c[1],g=c[2],h=c[3],i=40,j=2*Math.PI/i,k=0;if(a.fill){var l=e.hex2rgb(a.fillColor),m=a.fillAlpha,n=l[0]*m,o=l[1]*m,p=l[2]*m,q=b.points,r=b.indices,s=q.length/6;for(r.push(s),k=0;i+1>k;k++)q.push(d,f,n,o,p,m),q.push(d+Math.sin(j*k)*g,f+Math.cos(j*k)*h,n,o,p,m),r.push(s++,s++);r.push(s-1)}if(a.lineWidth){var t=a.points;for(a.points=[],k=0;i+1>k;k++)a.points.push(d+Math.sin(j*k)*g,f+Math.cos(j*k)*h);e.WebGLGraphics.buildLine(a,b),a.points=t}},e.WebGLGraphics.buildLine=function(a,b){var c=0,d=a.points;if(0!==d.length){var f=new e.Point(d[0],d[1]),g=new e.Point(d[d.length-2],d[d.length-1]);if(f.x===g.x&&f.y===g.y){d.pop(),d.pop(),g=new e.Point(d[d.length-2],d[d.length-1]);var h=g.x+.5*(f.x-g.x),i=g.y+.5*(f.y-g.y);d.unshift(h,i),d.push(h,i)}var j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G=b.points,H=b.indices,I=d.length/2,J=d.length,K=G.length/6,L=a.lineWidth/2,M=e.hex2rgb(a.lineColor),N=a.lineAlpha,O=M[0]*N,P=M[1]*N,Q=M[2]*N;for(l=d[0],m=d[1],n=d[2],o=d[3],r=-(m-o),s=l-n,F=Math.sqrt(r*r+s*s),r/=F,s/=F,r*=L,s*=L,G.push(l-r,m-s,O,P,Q,N),G.push(l+r,m+s,O,P,Q,N),c=1;I-1>c;c++)l=d[2*(c-1)],m=d[2*(c-1)+1],n=d[2*c],o=d[2*c+1],p=d[2*(c+1)],q=d[2*(c+1)+1],r=-(m-o),s=l-n,F=Math.sqrt(r*r+s*s),r/=F,s/=F,r*=L,s*=L,t=-(o-q),u=n-p,F=Math.sqrt(t*t+u*u),t/=F,u/=F,t*=L,u*=L,x=-s+m-(-s+o),y=-r+n-(-r+l),z=(-r+l)*(-s+o)-(-r+n)*(-s+m),A=-u+q-(-u+o),B=-t+n-(-t+p),C=(-t+p)*(-u+o)-(-t+n)*(-u+q),D=x*B-A*y,j=(y*C-B*z)/D,k=(A*z-x*C)/D,E=(j-n)*(j-n)+(k-o)+(k-o),E>19600?(v=r-t,w=s-u,F=Math.sqrt(v*v+w*w),v/=F,w/=F,v*=L,w*=L,G.push(n-v,o-w),G.push(O,P,Q,N),G.push(n+v,o+w),G.push(O,P,Q,N),G.push(n-v,o-w),G.push(O,P,Q,N),J++):(G.push(j,k),G.push(O,P,Q,N),G.push(n-(j-n),o-(k-o)),G.push(O,P,Q,N));for(l=d[2*(I-2)],m=d[2*(I-2)+1],n=d[2*(I-1)],o=d[2*(I-1)+1],r=-(m-o),s=l-n,F=Math.sqrt(r*r+s*s),r/=F,s/=F,r*=L,s*=L,G.push(n-r,o-s),G.push(O,P,Q,N),G.push(n+r,o+s),G.push(O,P,Q,N),H.push(K),c=0;J>c;c++)H.push(K++);H.push(K-1)}},e.WebGLGraphics.buildPoly=function(a,b){var c=a.points;if(!(c.length<6)){var d=b.points,f=b.indices,g=c.length/2,h=e.hex2rgb(a.fillColor),i=a.fillAlpha,j=h[0]*i,k=h[1]*i,l=h[2]*i,m=e.PolyK.Triangulate(c),n=d.length/6,o=0;for(o=0;oo;o++)d.push(c[2*o],c[2*o+1],j,k,l,i)}},e.glContexts=[],e.WebGLRenderer=function(a,b,c,d,f){e.defaultRenderer||(e.defaultRenderer=this),this.type=e.WEBGL_RENDERER,this.transparent=!!d,this.width=a||800,this.height=b||600,this.view=c||document.createElement("canvas"),this.view.width=this.width,this.view.height=this.height;var g=this;this.view.addEventListener("webglcontextlost",function(a){g.handleContextLost(a)},!1),this.view.addEventListener("webglcontextrestored",function(a){g.handleContextRestored(a)},!1),this.options={alpha:this.transparent,antialias:!!f,premultipliedAlpha:!1,stencil:!0};try{this.gl=this.view.getContext("experimental-webgl",this.options)}catch(h){try{this.gl=this.view.getContext("webgl",this.options)}catch(i){throw new Error(" This browser does not support webGL. Try using the canvas renderer"+this)}}var j=this.gl;this.glContextId=j.id=e.WebGLRenderer.glContextId++,e.glContexts[this.glContextId]=j,e.blendModesWebGL||(e.blendModesWebGL=[],e.blendModesWebGL[e.blendModes.NORMAL]=[j.ONE,j.ONE_MINUS_SRC_ALPHA],e.blendModesWebGL[e.blendModes.ADD]=[j.SRC_ALPHA,j.DST_ALPHA],e.blendModesWebGL[e.blendModes.MULTIPLY]=[j.DST_COLOR,j.ONE_MINUS_SRC_ALPHA],e.blendModesWebGL[e.blendModes.SCREEN]=[j.SRC_ALPHA,j.ONE]),this.projection=new e.Point,this.projection.x=this.width/2,this.projection.y=-this.height/2,this.offset=new e.Point(0,0),this.resize(this.width,this.height),this.contextLost=!1,this.shaderManager=new e.WebGLShaderManager(j),this.spriteBatch=new e.WebGLSpriteBatch(j),this.maskManager=new e.WebGLMaskManager(j),this.filterManager=new e.WebGLFilterManager(j,this.transparent),this.renderSession={},this.renderSession.gl=this.gl,this.renderSession.drawCount=0,this.renderSession.shaderManager=this.shaderManager,this.renderSession.maskManager=this.maskManager,this.renderSession.filterManager=this.filterManager,this.renderSession.spriteBatch=this.spriteBatch,j.useProgram(this.shaderManager.defaultShader.program),j.disable(j.DEPTH_TEST),j.disable(j.CULL_FACE),j.enable(j.BLEND),j.colorMask(!0,!0,!0,this.transparent)},e.WebGLRenderer.prototype.constructor=e.WebGLRenderer,e.WebGLRenderer.prototype.render=function(a){if(!this.contextLost){this.__stage!==a&&(this.__stage=a),e.WebGLRenderer.updateTextures(),a.updateTransform();var b=this.gl;b.colorMask(!0,!0,!0,this.transparent),b.viewport(0,0,this.width,this.height),b.bindFramebuffer(b.FRAMEBUFFER,null),b.clearColor(a.backgroundColorSplit[0],a.backgroundColorSplit[1],a.backgroundColorSplit[2],!this.transparent),b.clear(b.COLOR_BUFFER_BIT),this.renderDisplayObject(a,this.projection),a.interactive&&(a._interactiveEventsAdded||(a._interactiveEventsAdded=!0,a.interactionManager.setTarget(this)))}},e.WebGLRenderer.prototype.renderDisplayObject=function(a,b){this.renderSession.drawCount=0,this.renderSession.currentBlendMode=9999,this.renderSession.projection=b,this.renderSession.offset=this.offset,this.spriteBatch.begin(this.renderSession),this.filterManager.begin(this.renderSession,null),a._renderWebGL(this.renderSession),this.spriteBatch.end()},e.WebGLRenderer.updateTextures=function(){var a=0;for(a=0;a=0;b--){var c=a._glTextures[b],d=e.glContexts[b];d&&c&&d.deleteTexture(c)}a._glTextures.length=0},e.WebGLRenderer.updateTextureFrame=function(a){a.updateFrame=!1,a._updateWebGLuvs()},e.WebGLRenderer.prototype.resize=function(a,b){this.width=a,this.height=b,this.view.width=a,this.view.height=b,this.gl.viewport(0,0,this.width,this.height),this.projection.x=this.width/2,this.projection.y=-this.height/2},e.createWebGLTexture=function(a,b){return a.hasLoaded&&(a._glTextures[b.id]=b.createTexture(),b.bindTexture(b.TEXTURE_2D,a._glTextures[b.id]),b.pixelStorei(b.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0),b.texImage2D(b.TEXTURE_2D,0,b.RGBA,b.RGBA,b.UNSIGNED_BYTE,a.source),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,a.scaleMode===e.BaseTexture.SCALE_MODE.LINEAR?b.LINEAR:b.NEAREST),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,a.scaleMode===e.BaseTexture.SCALE_MODE.LINEAR?b.LINEAR:b.NEAREST),a._powerOf2?(b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.REPEAT),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.REPEAT)):(b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE)),b.bindTexture(b.TEXTURE_2D,null)),a._glTextures[b.id]},e.updateWebGLTexture=function(a,b){a._glTextures[b.id]&&(b.bindTexture(b.TEXTURE_2D,a._glTextures[b.id]),b.pixelStorei(b.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0),b.texImage2D(b.TEXTURE_2D,0,b.RGBA,b.RGBA,b.UNSIGNED_BYTE,a.source),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,a.scaleMode===e.BaseTexture.SCALE_MODE.LINEAR?b.LINEAR:b.NEAREST),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,a.scaleMode===e.BaseTexture.SCALE_MODE.LINEAR?b.LINEAR:b.NEAREST),a._powerOf2?(b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.REPEAT),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.REPEAT)):(b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE)),b.bindTexture(b.TEXTURE_2D,null))},e.WebGLRenderer.prototype.handleContextLost=function(a){a.preventDefault(),this.contextLost=!0},e.WebGLRenderer.prototype.handleContextRestored=function(){try{this.gl=this.view.getContext("experimental-webgl",this.options)}catch(a){try{this.gl=this.view.getContext("webgl",this.options)}catch(b){throw new Error(" This browser does not support webGL. Try using the canvas renderer"+this)}}var c=this.gl;c.id=e.WebGLRenderer.glContextId++,this.shaderManager.setContext(c),this.spriteBatch.setContext(c),this.maskManager.setContext(c),this.filterManager.setContext(c),this.renderSession.gl=this.gl,c.disable(c.DEPTH_TEST),c.disable(c.CULL_FACE),c.enable(c.BLEND),c.colorMask(!0,!0,!0,this.transparent),this.gl.viewport(0,0,this.width,this.height);for(var d in e.TextureCache){var f=e.TextureCache[d].baseTexture;f._glTextures=[]}this.contextLost=!1},e.WebGLRenderer.glContextId=0,e.WebGLMaskManager=function(a){this.maskStack=[],this.maskPosition=0,this.setContext(a)},e.WebGLMaskManager.prototype.setContext=function(a){this.gl=a},e.WebGLMaskManager.prototype.pushMask=function(a,b){var c=this.gl;0===this.maskStack.length&&(c.enable(c.STENCIL_TEST),c.stencilFunc(c.ALWAYS,1,1)),this.maskStack.push(a),c.colorMask(!1,!1,!1,!0),c.stencilOp(c.KEEP,c.KEEP,c.INCR),e.WebGLGraphics.renderGraphics(a,b),c.colorMask(!0,!0,!0,!0),c.stencilFunc(c.NOTEQUAL,0,this.maskStack.length),c.stencilOp(c.KEEP,c.KEEP,c.KEEP)},e.WebGLMaskManager.prototype.popMask=function(a){var b=this.gl,c=this.maskStack.pop();c&&(b.colorMask(!1,!1,!1,!1),b.stencilOp(b.KEEP,b.KEEP,b.DECR),e.WebGLGraphics.renderGraphics(c,a),b.colorMask(!0,!0,!0,!0),b.stencilFunc(b.NOTEQUAL,0,this.maskStack.length),b.stencilOp(b.KEEP,b.KEEP,b.KEEP)),0===this.maskStack.length&&b.disable(b.STENCIL_TEST)},e.WebGLShaderManager=function(a){this.setContext(a)},e.WebGLShaderManager.prototype.setContext=function(a){this.gl=a,this.primitiveShader=new e.PrimitiveShader(a),this.defaultShader=new e.PixiShader(a);var b=this.defaultShader.program;a.useProgram(b),a.enableVertexAttribArray(this.defaultShader.aVertexPosition),a.enableVertexAttribArray(this.defaultShader.colorAttribute),a.enableVertexAttribArray(this.defaultShader.aTextureCoord)},e.WebGLShaderManager.prototype.activatePrimitiveShader=function(){var a=this.gl;a.useProgram(this.primitiveShader.program),a.disableVertexAttribArray(this.defaultShader.aVertexPosition),a.disableVertexAttribArray(this.defaultShader.colorAttribute),a.disableVertexAttribArray(this.defaultShader.aTextureCoord),a.enableVertexAttribArray(this.primitiveShader.aVertexPosition),a.enableVertexAttribArray(this.primitiveShader.colorAttribute)},e.WebGLShaderManager.prototype.deactivatePrimitiveShader=function(){var a=this.gl;a.useProgram(this.defaultShader.program),a.disableVertexAttribArray(this.primitiveShader.aVertexPosition),a.disableVertexAttribArray(this.primitiveShader.colorAttribute),a.enableVertexAttribArray(this.defaultShader.aVertexPosition),a.enableVertexAttribArray(this.defaultShader.colorAttribute),a.enableVertexAttribArray(this.defaultShader.aTextureCoord)},e.WebGLSpriteBatch=function(a){this.size=2e3,this.vertSize=6;var b=4*this.size*this.vertSize,c=6*this.size;this.vertices=new Float32Array(b),this.indices=new Uint16Array(c),this.lastIndexCount=0;for(var d=0,e=0;c>d;d+=6,e+=4)this.indices[d+0]=e+0,this.indices[d+1]=e+1,this.indices[d+2]=e+2,this.indices[d+3]=e+0,this.indices[d+4]=e+2,this.indices[d+5]=e+3;this.drawing=!1,this.currentBatchSize=0,this.currentBaseTexture=null,this.setContext(a)},e.WebGLSpriteBatch.prototype.setContext=function(a){this.gl=a,this.vertexBuffer=a.createBuffer(),this.indexBuffer=a.createBuffer(),a.bindBuffer(a.ELEMENT_ARRAY_BUFFER,this.indexBuffer),a.bufferData(a.ELEMENT_ARRAY_BUFFER,this.indices,a.STATIC_DRAW),a.bindBuffer(a.ARRAY_BUFFER,this.vertexBuffer),a.bufferData(a.ARRAY_BUFFER,this.vertices,a.DYNAMIC_DRAW),this.currentBlendMode=99999},e.WebGLSpriteBatch.prototype.begin=function(a){this.renderSession=a,this.shader=this.renderSession.shaderManager.defaultShader,this.start()},e.WebGLSpriteBatch.prototype.end=function(){this.flush()},e.WebGLSpriteBatch.prototype.render=function(a){(a.texture.baseTexture!==this.currentBaseTexture||this.currentBatchSize>=this.size)&&(this.flush(),this.currentBaseTexture=a.texture.baseTexture),a.blendMode!==this.currentBlendMode&&this.setBlendMode(a.blendMode);var b=a._uvs||a.texture._uvs;if(b){var c,d,e,f,g=a.worldAlpha,h=a.tint,i=this.vertices,j=a.texture.frame.width,k=a.texture.frame.height,l=a.anchor.x,m=a.anchor.y;if(a.texture.trimmed){var n=a.texture.trim;d=n.x-l*n.realWidth,c=d+j,f=n.y-m*n.realHeight,e=f+k}else c=j*(1-l),d=j*-l,e=k*(1-m),f=k*-m;var o=4*this.currentBatchSize*this.vertSize,p=a.worldTransform,q=p[0],r=p[3],s=p[1],t=p[4],u=p[2],v=p[5];i[o++]=q*d+s*f+u,i[o++]=t*f+r*d+v,i[o++]=b[0],i[o++]=b[1],i[o++]=g,i[o++]=h,i[o++]=q*c+s*f+u,i[o++]=t*f+r*c+v,i[o++]=b[2],i[o++]=b[3],i[o++]=g,i[o++]=h,i[o++]=q*c+s*e+u,i[o++]=t*e+r*c+v,i[o++]=b[4],i[o++]=b[5],i[o++]=g,i[o++]=h,i[o++]=q*d+s*e+u,i[o++]=t*e+r*d+v,i[o++]=b[6],i[o++]=b[7],i[o++]=g,i[o++]=h,this.currentBatchSize++}},e.WebGLSpriteBatch.prototype.renderTilingSprite=function(a){var b=a.tilingTexture;(b.baseTexture!==this.currentBaseTexture||this.currentBatchSize>=this.size)&&(this.flush(),this.currentBaseTexture=b.baseTexture),a.blendMode!==this.currentBlendMode&&this.setBlendMode(a.blendMode),a._uvs||(a._uvs=new Float32Array(8));var c=a._uvs,d=a.tilePosition.x/b.baseTexture.width,e=a.tilePosition.y/b.baseTexture.height,f=a.width/b.baseTexture.width/(a.tileScale.x*a.tileScaleOffset.x),g=a.height/b.baseTexture.height/(a.tileScale.y*a.tileScaleOffset.y);c[0]=0-d,c[1]=0-e,c[2]=1*f-d,c[3]=0-e,c[4]=1*f-d,c[5]=1*g-e,c[6]=0-d,c[7]=1*g-e;var h=a.worldAlpha,i=a.tint,j=this.vertices,k=a.width,l=a.height,m=a.anchor.x,n=a.anchor.y,o=k*(1-m),p=k*-m,q=l*(1-n),r=l*-n,s=4*this.currentBatchSize*this.vertSize,t=a.worldTransform,u=t[0],v=t[3],w=t[1],x=t[4],y=t[2],z=t[5];j[s++]=u*p+w*r+y,j[s++]=x*r+v*p+z,j[s++]=c[0],j[s++]=c[1],j[s++]=h,j[s++]=i,j[s++]=u*o+w*r+y,j[s++]=x*r+v*o+z,j[s++]=c[2],j[s++]=c[3],j[s++]=h,j[s++]=i,j[s++]=u*o+w*q+y,j[s++]=x*q+v*o+z,j[s++]=c[4],j[s++]=c[5],j[s++]=h,j[s++]=i,j[s++]=u*p+w*q+y,j[s++]=x*q+v*p+z,j[s++]=c[6],j[s++]=c[7],j[s++]=h,j[s++]=i,this.currentBatchSize++},e.WebGLSpriteBatch.prototype.flush=function(){if(0!==this.currentBatchSize){var a=this.gl;a.bindTexture(a.TEXTURE_2D,this.currentBaseTexture._glTextures[a.id]||e.createWebGLTexture(this.currentBaseTexture,a));var b=this.vertices.subarray(0,4*this.currentBatchSize*this.vertSize);a.bufferSubData(a.ARRAY_BUFFER,0,b),a.drawElements(a.TRIANGLES,6*this.currentBatchSize,a.UNSIGNED_SHORT,0),this.currentBatchSize=0,this.renderSession.drawCount++}},e.WebGLSpriteBatch.prototype.stop=function(){this.flush()},e.WebGLSpriteBatch.prototype.start=function(){var a=this.gl;a.activeTexture(a.TEXTURE0),a.bindBuffer(a.ARRAY_BUFFER,this.vertexBuffer),a.bindBuffer(a.ELEMENT_ARRAY_BUFFER,this.indexBuffer);var b=this.renderSession.projection;a.uniform2f(this.shader.projectionVector,b.x,b.y);var c=4*this.vertSize;a.vertexAttribPointer(this.shader.aVertexPosition,2,a.FLOAT,!1,c,0),a.vertexAttribPointer(this.shader.aTextureCoord,2,a.FLOAT,!1,c,8),a.vertexAttribPointer(this.shader.colorAttribute,2,a.FLOAT,!1,c,16),this.currentBlendMode!==e.blendModes.NORMAL&&this.setBlendMode(e.blendModes.NORMAL)},e.WebGLSpriteBatch.prototype.setBlendMode=function(a){this.flush(),this.currentBlendMode=a;var b=e.blendModesWebGL[this.currentBlendMode];this.gl.blendFunc(b[0],b[1])},e.WebGLFilterManager=function(a,b){this.transparent=b,this.filterStack=[],this.offsetX=0,this.offsetY=0,this.setContext(a)},e.WebGLFilterManager.prototype.setContext=function(a){this.gl=a,this.texturePool=[],this.initShaderBuffers()},e.WebGLFilterManager.prototype.begin=function(a,b){this.renderSession=a,this.defaultShader=a.shaderManager.defaultShader;var c=this.renderSession.projection;this.width=2*c.x,this.height=2*-c.y,this.buffer=b},e.WebGLFilterManager.prototype.pushFilter=function(a){var b=this.gl,c=this.renderSession.projection,d=this.renderSession.offset;this.filterStack.push(a);var f=a.filterPasses[0];this.offsetX+=a.target.filterArea.x,this.offsetY+=a.target.filterArea.y;var g=this.texturePool.pop();g?g.resize(this.width,this.height):g=new e.FilterTexture(this.gl,this.width,this.height),b.bindTexture(b.TEXTURE_2D,g.texture),a.target.filterArea=a.target.getBounds();var h=a.target.filterArea,i=f.padding;h.x-=i,h.y-=i,h.width+=2*i,h.height+=2*i,h.x<0&&(h.x=0),h.width>this.width&&(h.width=this.width),h.y<0&&(h.y=0),h.height>this.height&&(h.height=this.height),b.bindFramebuffer(b.FRAMEBUFFER,g.frameBuffer),b.viewport(0,0,h.width,h.height),c.x=h.width/2,c.y=-h.height/2,d.x=-h.x,d.y=-h.y,b.uniform2f(this.defaultShader.projectionVector,h.width/2,-h.height/2),b.uniform2f(this.defaultShader.offsetVector,-h.x,-h.y),b.colorMask(!0,!0,!0,!0),b.clearColor(0,0,0,0),b.clear(b.COLOR_BUFFER_BIT),a._glFilterTexture=g},e.WebGLFilterManager.prototype.popFilter=function(){var a=this.gl,b=this.filterStack.pop(),c=b.target.filterArea,d=b._glFilterTexture,f=this.renderSession.projection,g=this.renderSession.offset;if(b.filterPasses.length>1){a.viewport(0,0,c.width,c.height),a.bindBuffer(a.ARRAY_BUFFER,this.vertexBuffer),this.vertexArray[0]=0,this.vertexArray[1]=c.height,this.vertexArray[2]=c.width,this.vertexArray[3]=c.height,this.vertexArray[4]=0,this.vertexArray[5]=0,this.vertexArray[6]=c.width,this.vertexArray[7]=0,a.bufferSubData(a.ARRAY_BUFFER,0,this.vertexArray),a.bindBuffer(a.ARRAY_BUFFER,this.uvBuffer),this.uvArray[2]=c.width/this.width,this.uvArray[5]=c.height/this.height,this.uvArray[6]=c.width/this.width,this.uvArray[7]=c.height/this.height,a.bufferSubData(a.ARRAY_BUFFER,0,this.uvArray);var h=d,i=this.texturePool.pop();i||(i=new e.FilterTexture(this.gl,this.width,this.height)),a.bindFramebuffer(a.FRAMEBUFFER,i.frameBuffer),a.clear(a.COLOR_BUFFER_BIT),a.disable(a.BLEND);for(var j=0;j0&&(e.Texture.frameUpdates.length=0)},e.CanvasRenderer.prototype.resize=function(a,b){this.width=a,this.height=b,this.view.width=a,this.view.height=b},e.CanvasRenderer.prototype.renderDisplayObject=function(a,b){this.renderSession.context=b||this.context,a._renderCanvas(this.renderSession)},e.CanvasRenderer.prototype.renderStripFlat=function(a){var b=this.context,c=a.verticies,d=c.length/2;this.count++,b.beginPath();for(var e=1;d-2>e;e++){var f=2*e,g=c[f],h=c[f+2],i=c[f+4],j=c[f+1],k=c[f+3],l=c[f+5];b.moveTo(g,j),b.lineTo(h,k),b.lineTo(i,l)}b.fillStyle="#FF0000",b.fill(),b.closePath()},e.CanvasRenderer.prototype.renderStrip=function(a){var b=this.context,c=a.verticies,d=a.uvs,e=c.length/2;this.count++;for(var f=1;e-2>f;f++){var g=2*f,h=c[g],i=c[g+2],j=c[g+4],k=c[g+1],l=c[g+3],m=c[g+5],n=d[g]*a.texture.width,o=d[g+2]*a.texture.width,p=d[g+4]*a.texture.width,q=d[g+1]*a.texture.height,r=d[g+3]*a.texture.height,s=d[g+5]*a.texture.height;b.save(),b.beginPath(),b.moveTo(h,k),b.lineTo(i,l),b.lineTo(j,m),b.closePath(),b.clip();var t=n*r+q*p+o*s-r*p-q*o-n*s,u=h*r+q*j+i*s-r*j-q*i-h*s,v=n*i+h*p+o*j-i*p-h*o-n*j,w=n*r*j+q*i*p+h*o*s-h*r*p-q*o*j-n*i*s,x=k*r+q*m+l*s-r*m-q*l-k*s,y=n*l+k*p+o*m-l*p-k*o-n*m,z=n*r*m+q*l*p+k*o*s-k*r*p-q*o*m-n*l*s;b.transform(u/t,x/t,v/t,y/t,w/t,z/t),b.drawImage(a.texture.baseTexture.source,0,0),b.restore()}},e.CanvasBuffer=function(a,b){this.width=a,this.height=b,this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),this.canvas.width=a,this.canvas.height=b},e.CanvasBuffer.prototype.clear=function(){this.context.clearRect(0,0,this.width,this.height)},e.CanvasBuffer.prototype.resize=function(a,b){this.width=this.canvas.width=a,this.height=this.canvas.height=b},e.CanvasGraphics=function(){},e.CanvasGraphics.renderGraphics=function(a,b){for(var c=a.worldAlpha,d="",f=0;f1&&(c=1,window.console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object"));for(var d=0;1>d;d++){var f=a.graphicsData[d],g=f.points;if(f.type===e.Graphics.POLY){b.beginPath(),b.moveTo(g[0],g[1]);for(var h=1;hc;c++)this.children[c]._renderWebGL(a);a.spriteBatch.stop()}this._filters&&a.filterManager.popFilter(),this._mask&&a.maskManager.popMask(a),a.drawCount++,a.spriteBatch.start()}},e.Graphics.prototype._renderCanvas=function(a){if(this.visible!==!1&&0!==this.alpha&&this.isMask!==!0){var b=a.context,c=this.worldTransform;this.blendMode!==a.currentBlendMode&&(a.currentBlendMode=this.blendMode,b.globalCompositeOperation=e.blendModesCanvas[a.currentBlendMode]),b.setTransform(c[0],c[3],c[1],c[4],c[2],c[5]),e.CanvasGraphics.renderGraphics(this,b);for(var d=0,f=this.children.length;f>d;d++)this.children[d]._renderCanvas(a)}},e.Graphics.prototype.getBounds=function(){this.bounds||this.updateBounds();var a=this.bounds.x,b=this.bounds.width+this.bounds.x,c=this.bounds.y,d=this.bounds.height+this.bounds.y,e=this.worldTransform,f=e[0],g=e[3],h=e[1],i=e[4],j=e[2],k=e[5],l=f*b+h*d+j,m=i*d+g*b+k,n=f*a+h*d+j,o=i*d+g*a+k,p=f*a+h*c+j,q=i*c+g*a+k,r=f*b+h*c+j,s=i*c+g*b+k,t=-1/0,u=-1/0,v=1/0,w=1/0;v=v>l?l:v,v=v>n?n:v,v=v>p?p:v,v=v>r?r:v,w=w>m?m:w,w=w>o?o:w,w=w>q?q:w,w=w>s?s:w,t=l>t?l:t,t=n>t?n:t,t=p>t?p:t,t=r>t?r:t,u=m>u?m:u,u=o>u?o:u,u=q>u?q:u,u=s>u?s:u;var x=this._bounds;return x.x=v,x.width=t-v,x.y=w,x.height=u-w,x},e.Graphics.prototype.updateBounds=function(){for(var a,b,c,d=1/0,f=-1/0,g=1/0,h=-1/0,i=0;ib?b:d,f=b+m>f?b+m:f,g=g>c?b:g,h=c+n>h?c+n:h}else if(k===e.Graphics.CIRC||k===e.Graphics.ELIP){b=a.x,c=a.y;var o=a.radius+l/2;d=d>b-o?b-o:d,f=b+o>f?b+o:f,g=g>c-o?c-o:g,h=c+o>h?c+o:h}else for(var p=0;pb-l?b-l:d,f=b+l>f?b+l:f,g=g>c-l?c-l:g,h=c+l>h?c+l:h}var q=this.boundsPadding;this.bounds=new e.Rectangle(d-q,g-q,f-d+2*q,h-g+2*q)},e.Graphics.prototype._generateCachedSprite=function(){var a=this.getBounds();if(this._cachedSprite)this._cachedSprite.buffer.resize(a.width,a.height);else{var b=new e.CanvasBuffer(a.width,a.height),c=e.Texture.fromCanvas(b.canvas);this._cachedSprite=new e.Sprite(c),this._cachedSprite.buffer=b,this._cachedSprite.worldTransform=this.worldTransform}this._cachedSprite.anchor.x=-(a.x/a.width),this._cachedSprite.anchor.y=-(a.y/a.height),this._cachedSprite.buffer.context.translate(-a.x,-a.y),e.CanvasGraphics.renderGraphics(this,this._cachedSprite.buffer.context)},e.Graphics.prototype.destroyCachedSprite=function(){this._cachedSprite.texture.destroy(!0),this._cachedSprite=null},e.Graphics.POLY=0,e.Graphics.RECT=1,e.Graphics.CIRC=2,e.Graphics.ELIP=3,e.Strip=function(a,b,c){e.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=e.blendModes.NORMAL;try{this.uvs=new Float32Array([0,1,1,1,1,0,0,1]),this.verticies=new Float32Array([0,0,0,0,0,0,0,0,0]),this.colors=new Float32Array([1,1,1,1]),this.indices=new Uint16Array([0,1,2,3])}catch(d){this.uvs=[0,1,1,1,1,0,0,1],this.verticies=[0,0,0,0,0,0,0,0,0],this.colors=[1,1,1,1],this.indices=[0,1,2,3]}this.width=b,this.height=c,a.baseTexture.hasLoaded?(this.width=this.texture.frame.width,this.height=this.texture.frame.height,this.updateFrame=!0):(this.onTextureUpdateBind=this.onTextureUpdate.bind(this),this.texture.addEventListener("update",this.onTextureUpdateBind)),this.renderable=!0},e.Strip.prototype=Object.create(e.DisplayObjectContainer.prototype),e.Strip.prototype.constructor=e.Strip,e.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},e.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},e.Rope=function(a,b){e.Strip.call(this,a),this.points=b;try{this.verticies=new Float32Array(4*b.length),this.uvs=new Float32Array(4*b.length),this.colors=new Float32Array(2*b.length),this.indices=new Uint16Array(2*b.length)}catch(c){this.verticies=new Array(4*b.length),this.uvs=new Array(4*b.length),this.colors=new Array(2*b.length),this.indices=new Array(2*b.length)}this.refresh()},e.Rope.prototype=Object.create(e.Strip.prototype),e.Rope.prototype.constructor=e.Rope,e.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=a[0],d=this.indices,e=this.colors;this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,e[0]=1,e[1]=1,d[0]=0,d[1]=1;for(var f,g,h,i=a.length,j=1;i>j;j++)f=a[j],g=4*j,h=j/(i-1),j%2?(b[g]=h,b[g+1]=0,b[g+2]=h,b[g+3]=1):(b[g]=h,b[g+1]=0,b[g+2]=h,b[g+3]=1),g=2*j,e[g]=1,e[g+1]=1,g=2*j,d[g]=g,d[g+1]=g+1,c=f}},e.Rope.prototype.updateTransform=function(){var a=this.points;if(!(a.length<1)){var b,c=a[0],d={x:0,y:0};this.count-=.2;var f=this.verticies;f[0]=c.x+d.x,f[1]=c.y+d.y,f[2]=c.x-d.x,f[3]=c.y-d.y;for(var g,h,i,j,k,l=a.length,m=1;l>m;m++)g=a[m],h=4*m,b=m1&&(i=1),j=Math.sqrt(d.x*d.x+d.y*d.y),k=this.texture.height/2,d.x/=j,d.y/=j,d.x*=k,d.y*=k,f[h]=g.x+d.x,f[h+1]=g.y+d.y,f[h+2]=g.x-d.x,f[h+3]=g.y-d.y,c=g;e.DisplayObjectContainer.prototype.updateTransform.call(this)}},e.Rope.prototype.setTexture=function(a){this.texture=a,this.updateFrame=!0},e.TilingSprite=function(a,b,c){e.Sprite.call(this,a),this.width=b||100,this.height=c||100,this.tileScale=new e.Point(1,1),this.tileScaleOffset=new e.Point(1,1),this.tilePosition=new e.Point(0,0),this.renderable=!0,this.tint=16777215,this.blendMode=e.blendModes.NORMAL},e.TilingSprite.prototype=Object.create(e.Sprite.prototype),e.TilingSprite.prototype.constructor=e.TilingSprite,Object.defineProperty(e.TilingSprite.prototype,"width",{get:function(){return this._width},set:function(a){this._width=a}}),Object.defineProperty(e.TilingSprite.prototype,"height",{get:function(){return this._height},set:function(a){this._height=a}}),e.TilingSprite.prototype.onTextureUpdate=function(){this.updateFrame=!0},e.TilingSprite.prototype._renderWebGL=function(a){if(this.visible!==!1&&0!==this.alpha){var b,c;if(this.mask||this.filters){for(this.mask&&(a.spriteBatch.stop(),a.maskManager.pushMask(this.mask,a.projection),a.spriteBatch.start()),this.filters&&(a.spriteBatch.flush(),a.filterManager.pushFilter(this._filterBlock)),this.tilingTexture?a.spriteBatch.renderTilingSprite(this):this.generateTilingTexture(!0),b=0,c=this.children.length;c>b;b++)this.children[b]._renderWebGL(a);a.spriteBatch.stop(),this.filters&&a.filterManager.popFilter(),this.mask&&a.maskManager.popMask(a.projection),a.spriteBatch.start()}else for(this.tilingTexture?a.spriteBatch.renderTilingSprite(this):this.generateTilingTexture(!0),b=0,c=this.children.length;c>b;b++)this.children[b]._renderWebGL(a)}},e.TilingSprite.prototype._renderCanvas=function(a){if(this.visible!==!1&&0!==this.alpha){var b=a.context;b.globalAlpha=this.worldAlpha;var c=this.worldTransform;b.setTransform(c[0],c[3],c[1],c[4],c[2],c[5]),this.__tilePattern||(this.generateTilingTexture(!1),this.tilingTexture&&(this.__tilePattern=b.createPattern(this.tilingTexture.baseTexture.source,"repeat"))),this.blendMode!==a.currentBlendMode&&(a.currentBlendMode=this.blendMode,b.globalCompositeOperation=e.blendModesCanvas[a.currentBlendMode]),b.beginPath();var d=this.tilePosition.x%this.tilingTexture.width,f=this.tilePosition.y%this.tilingTexture.height,g=this.tileScale;b.scale(g.x,g.y),b.translate(d,f),b.fillStyle=this.__tilePattern,b.fillRect(-d,-f,this.width/g.x,this.height/g.y),b.scale(1/g.x,1/g.y),b.translate(-d,-f),b.closePath()}},e.TilingSprite.prototype.getBounds=function(){var a=this._width,b=this._height,c=a*(1-this.anchor.x),d=a*-this.anchor.x,e=b*(1-this.anchor.y),f=b*-this.anchor.y,g=this.worldTransform,h=g[0],i=g[3],j=g[1],k=g[4],l=g[2],m=g[5],n=h*d+j*f+l,o=k*f+i*d+m,p=h*c+j*f+l,q=k*f+i*c+m,r=h*c+j*e+l,s=k*e+i*c+m,t=h*d+j*e+l,u=k*e+i*d+m,v=-1/0,w=-1/0,x=1/0,y=1/0;x=x>n?n:x,x=x>p?p:x,x=x>r?r:x,x=x>t?t:x,y=y>o?o:y,y=y>q?q:y,y=y>s?s:y,y=y>u?u:y,v=n>v?n:v,v=p>v?p:v,v=r>v?r:v,v=t>v?t:v,w=o>w?o:w,w=q>w?q:w,w=s>w?s:w,w=u>w?u:w;var z=this._bounds;return z.x=x,z.width=v-x,z.y=y,z.height=w-y,this._currentBounds=z,z},e.TilingSprite.prototype.generateTilingTexture=function(a){var b=this.texture;if(b.baseTexture.hasLoaded){var c,d,f=b.baseTexture,g=b.frame,h=g.width!==f.width||g.height!==f.height;this.tilingTexture=b;var i=!1;if(a?(c=e.getNextPowerOfTwo(b.frame.width),d=e.getNextPowerOfTwo(b.frame.height),g.width!==c&&g.height!==d&&(i=!0)):h&&(c=g.width,d=g.height,i=!0),i){var j=new e.CanvasBuffer(c,d);j.context.drawImage(b.baseTexture.source,g.x,g.y,g.width,g.height,0,0,c,d),this.tilingTexture=e.Texture.fromCanvas(j.canvas),this.tileScaleOffset.x=g.width/c,this.tileScaleOffset.y=g.height/d}this.tilingTexture.baseTexture._powerOf2=!0}};var j={};j.BoneData=function(a,b){this.name=a,this.parent=b},j.BoneData.prototype={length:0,x:0,y:0,rotation:0,scaleX:1,scaleY:1},j.SlotData=function(a,b){this.name=a,this.boneData=b},j.SlotData.prototype={r:1,g:1,b:1,a:1,attachmentName:null},j.Bone=function(a,b){this.data=a,this.parent=b,this.setToSetupPose()},j.Bone.yDown=!1,j.Bone.prototype={x:0,y:0,rotation:0,scaleX:1,scaleY:1,m00:0,m01:0,worldX:0,m10:0,m11:0,worldY:0,worldRotation:0,worldScaleX:1,worldScaleY:1,updateWorldTransform:function(a,b){var c=this.parent;null!=c?(this.worldX=this.x*c.m00+this.y*c.m01+c.worldX,this.worldY=this.x*c.m10+this.y*c.m11+c.worldY,this.worldScaleX=c.worldScaleX*this.scaleX,this.worldScaleY=c.worldScaleY*this.scaleY,this.worldRotation=c.worldRotation+this.rotation):(this.worldX=this.x,this.worldY=this.y,this.worldScaleX=this.scaleX,this.worldScaleY=this.scaleY,this.worldRotation=this.rotation);var d=this.worldRotation*Math.PI/180,e=Math.cos(d),f=Math.sin(d);this.m00=e*this.worldScaleX,this.m10=f*this.worldScaleX,this.m01=-f*this.worldScaleY,this.m11=e*this.worldScaleY,a&&(this.m00=-this.m00,this.m01=-this.m01),b&&(this.m10=-this.m10,this.m11=-this.m11),j.Bone.yDown&&(this.m10=-this.m10,this.m11=-this.m11)},setToSetupPose:function(){var a=this.data;this.x=a.x,this.y=a.y,this.rotation=a.rotation,this.scaleX=a.scaleX,this.scaleY=a.scaleY}},j.Slot=function(a,b,c){this.data=a,this.skeleton=b,this.bone=c,this.setToSetupPose()},j.Slot.prototype={r:1,g:1,b:1,a:1,_attachmentTime:0,attachment:null,setAttachment:function(a){this.attachment=a,this._attachmentTime=this.skeleton.time},setAttachmentTime:function(a){this._attachmentTime=this.skeleton.time-a},getAttachmentTime:function(){return this.skeleton.time-this._attachmentTime},setToSetupPose:function(){var a=this.data;this.r=a.r,this.g=a.g,this.b=a.b,this.a=a.a;for(var b=this.skeleton.data.slots,c=0,d=b.length;d>c;c++)if(b[c]==a){this.setAttachment(a.attachmentName?this.skeleton.getAttachmentBySlotIndex(c,a.attachmentName):null);break}}},j.Skin=function(a){this.name=a,this.attachments={}},j.Skin.prototype={addAttachment:function(a,b,c){this.attachments[a+":"+b]=c},getAttachment:function(a,b){return this.attachments[a+":"+b]},_attachAll:function(a,b){for(var c in b.attachments){var d=c.indexOf(":"),e=parseInt(c.substring(0,d),10),f=c.substring(d+1),g=a.slots[e];if(g.attachment&&g.attachment.name==f){var h=this.getAttachment(e,f);h&&g.setAttachment(h)}}}},j.Animation=function(a,b,c){this.name=a,this.timelines=b,this.duration=c},j.Animation.prototype={apply:function(a,b,c){c&&this.duration&&(b%=this.duration);for(var d=this.timelines,e=0,f=d.length;f>e;e++)d[e].apply(a,b,1)},mix:function(a,b,c,d){c&&this.duration&&(b%=this.duration);for(var e=this.timelines,f=0,g=e.length;g>f;f++)e[f].apply(a,b,d)}},j.binarySearch=function(a,b,c){var d=0,e=Math.floor(a.length/c)-2;if(!e)return c;for(var f=e>>>1;;){if(a[(f+1)*c]<=b?d=f+1:e=f,d==e)return(d+1)*c;f=d+e>>>1}},j.linearSearch=function(a,b,c){for(var d=0,e=a.length-c;e>=d;d+=c)if(a[d]>b)return d;return-1},j.Curves=function(a){this.curves=[],this.curves.length=6*(a-1)},j.Curves.prototype={setLinear:function(a){this.curves[6*a]=0},setStepped:function(a){this.curves[6*a]=-1},setCurve:function(a,b,c,d,e){var f=.1,g=f*f,h=g*f,i=3*f,j=3*g,k=6*g,l=6*h,m=2*-b+d,n=2*-c+e,o=3*(b-d)+1,p=3*(c-e)+1,q=6*a,r=this.curves;r[q]=b*i+m*j+o*h,r[q+1]=c*i+n*j+p*h,r[q+2]=m*k+o*l,r[q+3]=n*k+p*l,r[q+4]=o*l,r[q+5]=p*l},getCurvePercent:function(a,b){b=0>b?0:b>1?1:b;var c=6*a,d=this.curves,e=d[c];if(!e)return b;if(-1==e)return 0;for(var f=d[c+1],g=d[c+2],h=d[c+3],i=d[c+4],j=d[c+5],k=e,l=f,m=8;;){if(k>=b){var n=k-e,o=l-f;return o+(l-o)*(b-n)/(k-n)}if(!m)break;m--,e+=g,f+=h,g+=i,h+=j,k+=e,l+=f}return l+(1-l)*(b-k)/(1-k)}},j.RotateTimeline=function(a){this.curves=new j.Curves(a),this.frames=[],this.frames.length=2*a},j.RotateTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/2},setFrame:function(a,b,c){a*=2,this.frames[a]=b,this.frames[a+1]=c},apply:function(a,b,c){var d,e=this.frames;if(!(b=e[e.length-2]){for(d=f.data.rotation+e[e.length-1]-f.rotation;d>180;)d-=360;for(;-180>d;)d+=360;return void(f.rotation+=d*c)}var g=j.binarySearch(e,b,2),h=e[g-1],i=e[g],k=1-(b-i)/(e[g-2]-i);for(k=this.curves.getCurvePercent(g/2-1,k),d=e[g+1]-h;d>180;)d-=360;for(;-180>d;)d+=360;for(d=f.data.rotation+(h+d*k)-f.rotation;d>180;)d-=360;for(;-180>d;)d+=360;f.rotation+=d*c}}},j.TranslateTimeline=function(a){this.curves=new j.Curves(a),this.frames=[],this.frames.length=3*a},j.TranslateTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/3},setFrame:function(a,b,c,d){a*=3,this.frames[a]=b,this.frames[a+1]=c,this.frames[a+2]=d},apply:function(a,b,c){var d=this.frames;if(!(b=d[d.length-3])return e.x+=(e.data.x+d[d.length-2]-e.x)*c,void(e.y+=(e.data.y+d[d.length-1]-e.y)*c);var f=j.binarySearch(d,b,3),g=d[f-2],h=d[f-1],i=d[f],k=1-(b-i)/(d[f+-3]-i);k=this.curves.getCurvePercent(f/3-1,k),e.x+=(e.data.x+g+(d[f+1]-g)*k-e.x)*c,e.y+=(e.data.y+h+(d[f+2]-h)*k-e.y)*c}}},j.ScaleTimeline=function(a){this.curves=new j.Curves(a),this.frames=[],this.frames.length=3*a},j.ScaleTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/3},setFrame:function(a,b,c,d){a*=3,this.frames[a]=b,this.frames[a+1]=c,this.frames[a+2]=d},apply:function(a,b,c){var d=this.frames;if(!(b=d[d.length-3])return e.scaleX+=(e.data.scaleX-1+d[d.length-2]-e.scaleX)*c,void(e.scaleY+=(e.data.scaleY-1+d[d.length-1]-e.scaleY)*c);var f=j.binarySearch(d,b,3),g=d[f-2],h=d[f-1],i=d[f],k=1-(b-i)/(d[f+-3]-i);k=this.curves.getCurvePercent(f/3-1,k),e.scaleX+=(e.data.scaleX-1+g+(d[f+1]-g)*k-e.scaleX)*c,e.scaleY+=(e.data.scaleY-1+h+(d[f+2]-h)*k-e.scaleY)*c}}},j.ColorTimeline=function(a){this.curves=new j.Curves(a),this.frames=[],this.frames.length=5*a},j.ColorTimeline.prototype={slotIndex:0,getFrameCount:function(){return this.frames.length/2},setFrame:function(c,d){c*=5,this.frames[c]=d,this.frames[c+1]=r,this.frames[c+2]=g,this.frames[c+3]=b,this.frames[c+4]=a},apply:function(a,b,c){var d=this.frames;if(!(b=d[d.length-5]){var f=d.length-1;return e.r=d[f-3],e.g=d[f-2],e.b=d[f-1],void(e.a=d[f])}var g=j.binarySearch(d,b,5),h=d[g-4],i=d[g-3],k=d[g-2],l=d[g-1],m=d[g],n=1-(b-m)/(d[g-5]-m);n=this.curves.getCurvePercent(g/5-1,n);var o=h+(d[g+1]-h)*n,p=i+(d[g+2]-i)*n,q=k+(d[g+3]-k)*n,r=l+(d[g+4]-l)*n;1>c?(e.r+=(o-e.r)*c,e.g+=(p-e.g)*c,e.b+=(q-e.b)*c,e.a+=(r-e.a)*c):(e.r=o,e.g=p,e.b=q,e.a=r)}}},j.AttachmentTimeline=function(a){this.curves=new j.Curves(a),this.frames=[],this.frames.length=a,this.attachmentNames=[],this.attachmentNames.length=a},j.AttachmentTimeline.prototype={slotIndex:0,getFrameCount:function(){return this.frames.length},setFrame:function(a,b,c){this.frames[a]=b,this.attachmentNames[a]=c},apply:function(a,b){var c=this.frames;if(!(b=c[c.length-1]?c.length-1:j.binarySearch(c,b,1)-1;var e=this.attachmentNames[d];a.slots[this.slotIndex].setAttachment(e?a.getAttachmentBySlotIndex(this.slotIndex,e):null)}}},j.SkeletonData=function(){this.bones=[],this.slots=[],this.skins=[],this.animations=[]},j.SkeletonData.prototype={defaultSkin:null,findBone:function(a){for(var b=this.bones,c=0,d=b.length;d>c;c++)if(b[c].name==a)return b[c];return null},findBoneIndex:function(a){for(var b=this.bones,c=0,d=b.length;d>c;c++)if(b[c].name==a)return c;return-1},findSlot:function(a){for(var b=this.slots,c=0,d=b.length;d>c;c++)if(b[c].name==a)return slot[c];return null},findSlotIndex:function(a){for(var b=this.slots,c=0,d=b.length;d>c;c++)if(b[c].name==a)return c;return-1},findSkin:function(a){for(var b=this.skins,c=0,d=b.length;d>c;c++)if(b[c].name==a)return b[c];return null},findAnimation:function(a){for(var b=this.animations,c=0,d=b.length;d>c;c++)if(b[c].name==a)return b[c];return null}},j.Skeleton=function(a){this.data=a,this.bones=[];for(var b=0,c=a.bones.length;c>b;b++){var d=a.bones[b],e=d.parent?this.bones[a.bones.indexOf(d.parent)]:null;this.bones.push(new j.Bone(d,e))}for(this.slots=[],this.drawOrder=[],b=0,c=a.slots.length;c>b;b++){var f=a.slots[b],g=this.bones[a.bones.indexOf(f.boneData)],h=new j.Slot(f,this,g);this.slots.push(h),this.drawOrder.push(h)}},j.Skeleton.prototype={x:0,y:0,skin:null,r:1,g:1,b:1,a:1,time:0,flipX:!1,flipY:!1,updateWorldTransform:function(){for(var a=this.flipX,b=this.flipY,c=this.bones,d=0,e=c.length;e>d;d++)c[d].updateWorldTransform(a,b)},setToSetupPose:function(){this.setBonesToSetupPose(),this.setSlotsToSetupPose()},setBonesToSetupPose:function(){for(var a=this.bones,b=0,c=a.length;c>b;b++)a[b].setToSetupPose()},setSlotsToSetupPose:function(){for(var a=this.slots,b=0,c=a.length;c>b;b++)a[b].setToSetupPose(b)},getRootBone:function(){return this.bones.length?this.bones[0]:null},findBone:function(a){for(var b=this.bones,c=0,d=b.length;d>c;c++)if(b[c].data.name==a)return b[c];return null},findBoneIndex:function(a){for(var b=this.bones,c=0,d=b.length;d>c;c++)if(b[c].data.name==a)return c;return-1},findSlot:function(a){for(var b=this.slots,c=0,d=b.length;d>c;c++)if(b[c].data.name==a)return b[c];return null},findSlotIndex:function(a){for(var b=this.slots,c=0,d=b.length;d>c;c++)if(b[c].data.name==a)return c;return-1},setSkinByName:function(a){var b=this.data.findSkin(a);if(!b)throw"Skin not found: "+a;this.setSkin(b)},setSkin:function(a){this.skin&&a&&a._attachAll(this,this.skin),this.skin=a},getAttachmentBySlotName:function(a,b){return this.getAttachmentBySlotIndex(this.data.findSlotIndex(a),b)},getAttachmentBySlotIndex:function(a,b){if(this.skin){var c=this.skin.getAttachment(a,b);if(c)return c}return this.data.defaultSkin?this.data.defaultSkin.getAttachment(a,b):null},setAttachment:function(a,b){for(var c=this.slots,d=0,e=c.size;e>d;d++){var f=c[d];if(f.data.name==a){var g=null;if(b&&(g=this.getAttachment(d,b),null==g))throw"Attachment not found: "+b+", for slot: "+a;return void f.setAttachment(g)}}throw"Slot not found: "+a},update:function(a){time+=a}},j.AttachmentType={region:0},j.RegionAttachment=function(){this.offset=[],this.offset.length=8,this.uvs=[],this.uvs.length=8},j.RegionAttachment.prototype={x:0,y:0,rotation:0,scaleX:1,scaleY:1,width:0,height:0,rendererObject:null,regionOffsetX:0,regionOffsetY:0,regionWidth:0,regionHeight:0,regionOriginalWidth:0,regionOriginalHeight:0,setUVs:function(a,b,c,d,e){var f=this.uvs; +e?(f[2]=a,f[3]=d,f[4]=a,f[5]=b,f[6]=c,f[7]=b,f[0]=c,f[1]=d):(f[0]=a,f[1]=d,f[2]=a,f[3]=b,f[4]=c,f[5]=b,f[6]=c,f[7]=d)},updateOffset:function(){var a=this.width/this.regionOriginalWidth*this.scaleX,b=this.height/this.regionOriginalHeight*this.scaleY,c=-this.width/2*this.scaleX+this.regionOffsetX*a,d=-this.height/2*this.scaleY+this.regionOffsetY*b,e=c+this.regionWidth*a,f=d+this.regionHeight*b,g=this.rotation*Math.PI/180,h=Math.cos(g),i=Math.sin(g),j=c*h+this.x,k=c*i,l=d*h+this.y,m=d*i,n=e*h+this.x,o=e*i,p=f*h+this.y,q=f*i,r=this.offset;r[0]=j-m,r[1]=l+k,r[2]=j-q,r[3]=p+k,r[4]=n-q,r[5]=p+o,r[6]=n-m,r[7]=l+o},computeVertices:function(a,b,c,d){a+=c.worldX,b+=c.worldY;var e=c.m00,f=c.m01,g=c.m10,h=c.m11,i=this.offset;d[0]=i[0]*e+i[1]*f+a,d[1]=i[0]*g+i[1]*h+b,d[2]=i[2]*e+i[3]*f+a,d[3]=i[2]*g+i[3]*h+b,d[4]=i[4]*e+i[5]*f+a,d[5]=i[4]*g+i[5]*h+b,d[6]=i[6]*e+i[7]*f+a,d[7]=i[6]*g+i[7]*h+b}},j.AnimationStateData=function(a){this.skeletonData=a,this.animationToMixTime={}},j.AnimationStateData.prototype={defaultMix:0,setMixByName:function(a,b,c){var d=this.skeletonData.findAnimation(a);if(!d)throw"Animation not found: "+a;var e=this.skeletonData.findAnimation(b);if(!e)throw"Animation not found: "+b;this.setMix(d,e,c)},setMix:function(a,b,c){this.animationToMixTime[a.name+":"+b.name]=c},getMix:function(a,b){var c=this.animationToMixTime[a.name+":"+b.name];return c?c:this.defaultMix}},j.AnimationState=function(a){this.data=a,this.queue=[]},j.AnimationState.prototype={current:null,previous:null,currentTime:0,previousTime:0,currentLoop:!1,previousLoop:!1,mixTime:0,mixDuration:0,update:function(a){if(this.currentTime+=a,this.previousTime+=a,this.mixTime+=a,this.queue.length>0){var b=this.queue[0];this.currentTime>=b.delay&&(this._setAnimation(b.animation,b.loop),this.queue.shift())}},apply:function(a){if(this.current)if(this.previous){this.previous.apply(a,this.previousTime,this.previousLoop);var b=this.mixTime/this.mixDuration;b>=1&&(b=1,this.previous=null),this.current.mix(a,this.currentTime,this.currentLoop,b)}else this.current.apply(a,this.currentTime,this.currentLoop)},clearAnimation:function(){this.previous=null,this.current=null,this.queue.length=0},_setAnimation:function(a,b){this.previous=null,a&&this.current&&(this.mixDuration=this.data.getMix(this.current,a),this.mixDuration>0&&(this.mixTime=0,this.previous=this.current,this.previousTime=this.currentTime,this.previousLoop=this.currentLoop)),this.current=a,this.currentLoop=b,this.currentTime=0},setAnimationByName:function(a,b){var c=this.data.skeletonData.findAnimation(a);if(!c)throw"Animation not found: "+a;this.setAnimation(c,b)},setAnimation:function(a,b){this.queue.length=0,this._setAnimation(a,b)},addAnimationByName:function(a,b,c){var d=this.data.skeletonData.findAnimation(a);if(!d)throw"Animation not found: "+a;this.addAnimation(d,b,c)},addAnimation:function(a,b,c){var d={};if(d.animation=a,d.loop=b,!c||0>=c){var e=this.queue.length?this.queue[this.queue.length-1].animation:this.current;c=null!=e?e.duration-this.data.getMix(e,a)+(c||0):0}d.delay=c,this.queue.push(d)},isComplete:function(){return!this.current||this.currentTime>=this.current.duration}},j.SkeletonJson=function(a){this.attachmentLoader=a},j.SkeletonJson.prototype={scale:1,readSkeletonData:function(a){for(var b,c=new j.SkeletonData,d=a.bones,e=0,f=d.length;f>e;e++){var g=d[e],h=null;if(g.parent&&(h=c.findBone(g.parent),!h))throw"Parent bone not found: "+g.parent;b=new j.BoneData(g.name,h),b.length=(g.length||0)*this.scale,b.x=(g.x||0)*this.scale,b.y=(g.y||0)*this.scale,b.rotation=g.rotation||0,b.scaleX=g.scaleX||1,b.scaleY=g.scaleY||1,c.bones.push(b)}var i=a.slots;for(e=0,f=i.length;f>e;e++){var k=i[e];if(b=c.findBone(k.bone),!b)throw"Slot bone not found: "+k.bone;var l=new j.SlotData(k.name,b),m=k.color;m&&(l.r=j.SkeletonJson.toColor(m,0),l.g=j.SkeletonJson.toColor(m,1),l.b=j.SkeletonJson.toColor(m,2),l.a=j.SkeletonJson.toColor(m,3)),l.attachmentName=k.attachment,c.slots.push(l)}var n=a.skins;for(var o in n)if(n.hasOwnProperty(o)){var p=n[o],q=new j.Skin(o);for(var r in p)if(p.hasOwnProperty(r)){var s=c.findSlotIndex(r),t=p[r];for(var u in t)if(t.hasOwnProperty(u)){var v=this.readAttachment(q,u,t[u]);null!=v&&q.addAttachment(s,u,v)}}c.skins.push(q),"default"==q.name&&(c.defaultSkin=q)}var w=a.animations;for(var x in w)w.hasOwnProperty(x)&&this.readAnimation(x,w[x],c);return c},readAttachment:function(a,b,c){b=c.name||b;var d=j.AttachmentType[c.type||"region"];if(d==j.AttachmentType.region){var e=new j.RegionAttachment;return e.x=(c.x||0)*this.scale,e.y=(c.y||0)*this.scale,e.scaleX=c.scaleX||1,e.scaleY=c.scaleY||1,e.rotation=c.rotation||0,e.width=(c.width||32)*this.scale,e.height=(c.height||32)*this.scale,e.updateOffset(),e.rendererObject={},e.rendererObject.name=b,e.rendererObject.scale={},e.rendererObject.scale.x=e.scaleX,e.rendererObject.scale.y=e.scaleY,e.rendererObject.rotation=-e.rotation*Math.PI/180,e}throw"Unknown attachment type: "+d},readAnimation:function(a,b,c){var d,e,f,g,h,i,k,l=[],m=0,n=b.bones;for(var o in n)if(n.hasOwnProperty(o)){var p=c.findBoneIndex(o);if(-1==p)throw"Bone not found: "+o;var q=n[o];for(f in q)if(q.hasOwnProperty(f))if(h=q[f],"rotate"==f){for(e=new j.RotateTimeline(h.length),e.boneIndex=p,d=0,i=0,k=h.length;k>i;i++)g=h[i],e.setFrame(d,g.time,g.angle),j.SkeletonJson.readCurve(e,d,g),d++;l.push(e),m=Math.max(m,e.frames[2*e.getFrameCount()-2])}else{if("translate"!=f&&"scale"!=f)throw"Invalid timeline type for a bone: "+f+" ("+o+")";var r=1;for("scale"==f?e=new j.ScaleTimeline(h.length):(e=new j.TranslateTimeline(h.length),r=this.scale),e.boneIndex=p,d=0,i=0,k=h.length;k>i;i++){g=h[i];var s=(g.x||0)*r,t=(g.y||0)*r;e.setFrame(d,g.time,s,t),j.SkeletonJson.readCurve(e,d,g),d++}l.push(e),m=Math.max(m,e.frames[3*e.getFrameCount()-3])}}var u=b.slots;for(var v in u)if(u.hasOwnProperty(v)){var w=u[v],x=c.findSlotIndex(v);for(f in w)if(w.hasOwnProperty(f))if(h=w[f],"color"==f){for(e=new j.ColorTimeline(h.length),e.slotIndex=x,d=0,i=0,k=h.length;k>i;i++){g=h[i];var y=g.color,z=j.SkeletonJson.toColor(y,0),A=j.SkeletonJson.toColor(y,1),B=j.SkeletonJson.toColor(y,2),C=j.SkeletonJson.toColor(y,3);e.setFrame(d,g.time,z,A,B,C),j.SkeletonJson.readCurve(e,d,g),d++}l.push(e),m=Math.max(m,e.frames[5*e.getFrameCount()-5])}else{if("attachment"!=f)throw"Invalid timeline type for a slot: "+f+" ("+v+")";for(e=new j.AttachmentTimeline(h.length),e.slotIndex=x,d=0,i=0,k=h.length;k>i;i++)g=h[i],e.setFrame(d++,g.time,g.name);l.push(e),m=Math.max(m,e.frames[e.getFrameCount()-1])}}c.animations.push(new j.Animation(a,l,m))}},j.SkeletonJson.readCurve=function(a,b,c){var d=c.curve;d&&("stepped"==d?a.curves.setStepped(b):d instanceof Array&&a.curves.setCurve(b,d[0],d[1],d[2],d[3]))},j.SkeletonJson.toColor=function(a,b){if(8!=a.length)throw"Color hexidecimal length must be 8, recieved: "+a;return parseInt(a.substring(2*b,2),16)/255},j.Atlas=function(a,b){this.textureLoader=b,this.pages=[],this.regions=[];var c=new j.AtlasReader(a),d=[];d.length=4;for(var e=null;;){var f=c.readLine();if(null==f)break;if(f=c.trim(f),f.length)if(e){var g=new j.AtlasRegion;g.name=f,g.page=e,g.rotate="true"==c.readValue(),c.readTuple(d);var h=parseInt(d[0],10),i=parseInt(d[1],10);c.readTuple(d);var k=parseInt(d[0],10),l=parseInt(d[1],10);g.u=h/e.width,g.v=i/e.height,g.rotate?(g.u2=(h+l)/e.width,g.v2=(i+k)/e.height):(g.u2=(h+k)/e.width,g.v2=(i+l)/e.height),g.x=h,g.y=i,g.width=Math.abs(k),g.height=Math.abs(l),4==c.readTuple(d)&&(g.splits=[parseInt(d[0],10),parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10)],4==c.readTuple(d)&&(g.pads=[parseInt(d[0],10),parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10)],c.readTuple(d))),g.originalWidth=parseInt(d[0],10),g.originalHeight=parseInt(d[1],10),c.readTuple(d),g.offsetX=parseInt(d[0],10),g.offsetY=parseInt(d[1],10),g.index=parseInt(c.readValue(),10),this.regions.push(g)}else{e=new j.AtlasPage,e.name=f,e.format=j.Atlas.Format[c.readValue()],c.readTuple(d),e.minFilter=j.Atlas.TextureFilter[d[0]],e.magFilter=j.Atlas.TextureFilter[d[1]];var m=c.readValue();e.uWrap=j.Atlas.TextureWrap.clampToEdge,e.vWrap=j.Atlas.TextureWrap.clampToEdge,"x"==m?e.uWrap=j.Atlas.TextureWrap.repeat:"y"==m?e.vWrap=j.Atlas.TextureWrap.repeat:"xy"==m&&(e.uWrap=e.vWrap=j.Atlas.TextureWrap.repeat),b.load(e,f),this.pages.push(e)}else e=null}},j.Atlas.prototype={findRegion:function(a){for(var b=this.regions,c=0,d=b.length;d>c;c++)if(b[c].name==a)return b[c];return null},dispose:function(){for(var a=this.pages,b=0,c=a.length;c>b;b++)this.textureLoader.unload(a[b].rendererObject)},updateUVs:function(a){for(var b=this.regions,c=0,d=b.length;d>c;c++){var e=b[c];e.page==a&&(e.u=e.x/a.width,e.v=e.y/a.height,e.rotate?(e.u2=(e.x+e.height)/a.width,e.v2=(e.y+e.width)/a.height):(e.u2=(e.x+e.width)/a.width,e.v2=(e.y+e.height)/a.height))}}},j.Atlas.Format={alpha:0,intensity:1,luminanceAlpha:2,rgb565:3,rgba4444:4,rgb888:5,rgba8888:6},j.Atlas.TextureFilter={nearest:0,linear:1,mipMap:2,mipMapNearestNearest:3,mipMapLinearNearest:4,mipMapNearestLinear:5,mipMapLinearLinear:6},j.Atlas.TextureWrap={mirroredRepeat:0,clampToEdge:1,repeat:2},j.AtlasPage=function(){},j.AtlasPage.prototype={name:null,format:null,minFilter:null,magFilter:null,uWrap:null,vWrap:null,rendererObject:null,width:0,height:0},j.AtlasRegion=function(){},j.AtlasRegion.prototype={page:null,name:null,x:0,y:0,width:0,height:0,u:0,v:0,u2:0,v2:0,offsetX:0,offsetY:0,originalWidth:0,originalHeight:0,index:0,rotate:!1,splits:null,pads:null},j.AtlasReader=function(a){this.lines=a.split(/\r\n|\r|\n/)},j.AtlasReader.prototype={index:0,trim:function(a){return a.replace(/^\s+|\s+$/g,"")},readLine:function(){return this.index>=this.lines.length?null:this.lines[this.index++]},readValue:function(){var a=this.readLine(),b=a.indexOf(":");if(-1==b)throw"Invalid line: "+a;return this.trim(a.substring(b+1))},readTuple:function(a){var b=this.readLine(),c=b.indexOf(":");if(-1==c)throw"Invalid line: "+b;for(var d=0,e=c+1;3>d;d++){var f=b.indexOf(",",e);if(-1==f){if(!d)throw"Invalid line: "+b;break}a[d]=this.trim(b.substr(e,f-e)),e=f+1}return a[d]=this.trim(b.substring(e)),d+1}},j.AtlasAttachmentLoader=function(a){this.atlas=a},j.AtlasAttachmentLoader.prototype={newAttachment:function(a,b,c){switch(b){case j.AttachmentType.region:var d=this.atlas.findRegion(c);if(!d)throw"Region not found in atlas: "+c+" ("+b+")";var e=new j.RegionAttachment(c);return e.rendererObject=d,e.setUVs(d.u,d.v,d.u2,d.v2,d.rotate),e.regionOffsetX=d.offsetX,e.regionOffsetY=d.offsetY,e.regionWidth=d.width,e.regionHeight=d.height,e.regionOriginalWidth=d.originalWidth,e.regionOriginalHeight=d.originalHeight,e}throw"Unknown attachment type: "+b}},j.Bone.yDown=!0,e.AnimCache={},e.Spine=function(a){if(e.DisplayObjectContainer.call(this),this.spineData=e.AnimCache[a],!this.spineData)throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: "+a);this.skeleton=new j.Skeleton(this.spineData),this.skeleton.updateWorldTransform(),this.stateData=new j.AnimationStateData(this.spineData),this.state=new j.AnimationState(this.stateData),this.slotContainers=[];for(var b=0,c=this.skeleton.drawOrder.length;c>b;b++){var d=this.skeleton.drawOrder[b],f=d.attachment,g=new e.DisplayObjectContainer;if(this.slotContainers.push(g),this.addChild(g),f instanceof j.RegionAttachment){var h=f.rendererObject.name,i=this.createSprite(d,f.rendererObject);d.currentSprite=i,d.currentSpriteName=h,g.addChild(i)}}},e.Spine.prototype=Object.create(e.DisplayObjectContainer.prototype),e.Spine.prototype.constructor=e.Spine,e.Spine.prototype.updateTransform=function(){this.lastTime=this.lastTime||Date.now();var a=.001*(Date.now()-this.lastTime);this.lastTime=Date.now(),this.state.update(a),this.state.apply(this.skeleton),this.skeleton.updateWorldTransform();for(var b=this.skeleton.drawOrder,c=0,d=b.length;d>c;c++){var f=b[c],g=f.attachment,h=this.slotContainers[c];if(g instanceof j.RegionAttachment){if(g.rendererObject&&(!f.currentSpriteName||f.currentSpriteName!=g.name)){var i=g.rendererObject.name;if(void 0!==f.currentSprite&&(f.currentSprite.visible=!1),f.sprites=f.sprites||{},void 0!==f.sprites[i])f.sprites[i].visible=!0;else{var k=this.createSprite(f,g.rendererObject);h.addChild(k)}f.currentSprite=f.sprites[i],f.currentSpriteName=i}h.visible=!0;var l=f.bone;h.position.x=l.worldX+g.x*l.m00+g.y*l.m01,h.position.y=l.worldY+g.x*l.m10+g.y*l.m11,h.scale.x=l.worldScaleX,h.scale.y=l.worldScaleY,h.rotation=-(f.bone.worldRotation*Math.PI/180)}else h.visible=!1}e.DisplayObjectContainer.prototype.updateTransform.call(this)},e.Spine.prototype.createSprite=function(a,b){var c=e.TextureCache[b.name]?b.name:b.name+".png",d=new e.Sprite(e.Texture.fromFrame(c));return d.scale=b.scale,d.rotation=b.rotation,d.anchor.x=d.anchor.y=.5,a.sprites=a.sprites||{},a.sprites[b.name]=d,d},e.BaseTextureCache={},e.texturesToUpdate=[],e.texturesToDestroy=[],e.BaseTexture=function(a,b){if(e.EventTarget.call(this),this.width=100,this.height=100,this.scaleMode=b||e.BaseTexture.SCALE_MODE.DEFAULT,this.hasLoaded=!1,this.source=a,a){if(this.source instanceof Image||this.source instanceof HTMLImageElement)if(this.source.complete)this.hasLoaded=!0,this.width=this.source.width,this.height=this.source.height,e.texturesToUpdate.push(this);else{var c=this;this.source.onload=function(){c.hasLoaded=!0,c.width=c.source.width,c.height=c.source.height,e.texturesToUpdate.push(c),c.dispatchEvent({type:"loaded",content:c})}}else this.hasLoaded=!0,this.width=this.source.width,this.height=this.source.height,e.texturesToUpdate.push(this);this.imageUrl=null,this._powerOf2=!1,this._glTextures=[]}},e.BaseTexture.prototype.constructor=e.BaseTexture,e.BaseTexture.prototype.destroy=function(){this.source instanceof Image&&(this.imageUrl in e.BaseTextureCache&&delete e.BaseTextureCache[this.imageUrl],this.imageUrl=null,this.source.src=null),this.source=null,e.texturesToDestroy.push(this)},e.BaseTexture.prototype.updateSourceImage=function(a){this.hasLoaded=!1,this.source.src=null,this.source.src=a},e.BaseTexture.fromImage=function(a,b,c){var d=e.BaseTextureCache[a];if(!d){var f=new Image;b&&(f.crossOrigin=""),f.src=a,d=new e.BaseTexture(f,c),d.imageUrl=a,e.BaseTextureCache[a]=d}return d},e.BaseTexture.SCALE_MODE={DEFAULT:0,LINEAR:0,NEAREST:1},e.TextureCache={},e.FrameCache={},e.Texture=function(a,b){if(e.EventTarget.call(this),b||(this.noFrame=!0,b=new e.Rectangle(0,0,1,1)),a instanceof e.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=b,this.trim=new e.Point,this.scope=this,a.hasLoaded)this.noFrame&&(b=new e.Rectangle(0,0,a.width,a.height)),this.setFrame(b);else{var c=this;a.addEventListener("loaded",function(){c.onBaseTextureLoaded()})}},e.Texture.prototype.constructor=e.Texture,e.Texture.prototype.onBaseTextureLoaded=function(){var a=this.baseTexture;a.removeEventListener("loaded",this.onLoaded),this.noFrame&&(this.frame=new e.Rectangle(0,0,a.width,a.height)),this.setFrame(this.frame),this.scope.dispatchEvent({type:"update",content:this})},e.Texture.prototype.destroy=function(a){a&&this.baseTexture.destroy()},e.Texture.prototype.setFrame=function(a){if(this.frame=a,this.width=a.width,this.height=a.height,a.x+a.width>this.baseTexture.width||a.y+a.height>this.baseTexture.height)throw new Error("Texture Error: frame does not fit inside the base Texture dimensions "+this);this.updateFrame=!0,e.Texture.frameUpdates.push(this)},e.Texture.prototype._updateWebGLuvs=function(){this._uvs||(this._uvs=new Float32Array(8));var a=this.frame,b=this.baseTexture.width,c=this.baseTexture.height;this._uvs[0]=a.x/b,this._uvs[1]=a.y/c,this._uvs[2]=(a.x+a.width)/b,this._uvs[3]=a.y/c,this._uvs[4]=(a.x+a.width)/b,this._uvs[5]=(a.y+a.height)/c,this._uvs[6]=a.x/b,this._uvs[7]=(a.y+a.height)/c},e.Texture.fromImage=function(a,b,c){var d=e.TextureCache[a];return d||(d=new e.Texture(e.BaseTexture.fromImage(a,b,c)),e.TextureCache[a]=d),d},e.Texture.fromFrame=function(a){var b=e.TextureCache[a];if(!b)throw new Error('The frameId "'+a+'" does not exist in the texture cache '+this);return b},e.Texture.fromCanvas=function(a,b){var c=new e.BaseTexture(a,b);return new e.Texture(c)},e.Texture.addTextureToCache=function(a,b){e.TextureCache[b]=a},e.Texture.removeTextureFromCache=function(a){var b=e.TextureCache[a];return e.TextureCache[a]=null,b},e.Texture.frameUpdates=[],e.Texture.SCALE_MODE=e.BaseTexture.SCALE_MODE,e.RenderTexture=function(a,b,c){if(e.EventTarget.call(this),this.width=a||100,this.height=b||100,this.indetityMatrix=e.mat3.create(),this.frame=new e.Rectangle(0,0,this.width,this.height),this.baseTexture=new e.BaseTexture,this.baseTexture.width=this.width,this.baseTexture.height=this.height,this.baseTexture._glTextures=[],this.baseTexture.hasLoaded=!0,this.renderer=c||e.defaultRenderer,this.renderer.type===e.WEBGL_RENDERER){var d=this.renderer.gl;this.textureBuffer=new e.FilterTexture(d,this.width,this.height),this.baseTexture._glTextures[d.id]=this.textureBuffer.texture,this.render=this.renderWebGL,this.projection=new e.Point(this.width/2,-this.height/2)}else this.render=this.renderCanvas,this.textureBuffer=new e.CanvasBuffer(this.width,this.height),this.baseTexture.source=this.textureBuffer.canvas;e.Texture.frameUpdates.push(this)},e.RenderTexture.prototype=Object.create(e.Texture.prototype),e.RenderTexture.prototype.constructor=e.RenderTexture,e.RenderTexture.prototype.resize=function(a,b){if(this.width=a,this.height=b,this.frame.width=this.width,this.frame.height=this.height,this.renderer.type===e.WEBGL_RENDERER){this.projection.x=this.width/2,this.projection.y=-this.height/2;var c=this.gl;c.bindTexture(c.TEXTURE_2D,this.baseTexture._glTextures[c.id]),c.texImage2D(c.TEXTURE_2D,0,c.RGBA,this.width,this.height,0,c.RGBA,c.UNSIGNED_BYTE,null)}else this.textureBuffer.resize(this.width,this.height);e.Texture.frameUpdates.push(this)},e.RenderTexture.prototype.renderWebGL=function(a,b,c){var d=this.renderer.gl;d.colorMask(!0,!0,!0,!0),d.viewport(0,0,this.width,this.height),d.bindFramebuffer(d.FRAMEBUFFER,this.textureBuffer.frameBuffer),c&&this.textureBuffer.clear();var f=a.children,g=a.worldTransform;a.worldTransform=e.mat3.create(),a.worldTransform[4]=-1,a.worldTransform[5]=-2*this.projection.y,b&&(a.worldTransform[2]=b.x,a.worldTransform[5]-=b.y);for(var h=0,i=f.length;i>h;h++)f[h].updateTransform();this.renderer.renderDisplayObject(a,this.projection),a.worldTransform=g},e.RenderTexture.prototype.renderCanvas=function(a,b,c){var d=a.children;a.worldTransform=e.mat3.create(),b&&(a.worldTransform[2]=b.x,a.worldTransform[5]=b.y);for(var f=0,g=d.length;g>f;f++)d[f].updateTransform();c&&this.textureBuffer.clear();var h=this.textureBuffer.context;this.renderer.renderDisplayObject(a,h),h.setTransform(1,0,0,1,0,0)},e.AssetLoader=function(a,b){e.EventTarget.call(this),this.assetURLs=a,this.crossorigin=b,this.loadersByType={jpg:e.ImageLoader,jpeg:e.ImageLoader,png:e.ImageLoader,gif:e.ImageLoader,json:e.JsonLoader,atlas:e.AtlasLoader,anim:e.SpineLoader,xml:e.BitmapFontLoader,fnt:e.BitmapFontLoader}},e.AssetLoader.prototype.constructor=e.AssetLoader,e.AssetLoader.prototype._getDataType=function(a){var b="data:",c=a.slice(0,b.length).toLowerCase();if(c===b){var d=a.slice(b.length),e=d.indexOf(",");if(-1===e)return null;var f=d.slice(0,e).split(";")[0];return f&&"text/plain"!==f.toLowerCase()?f.split("/").pop().toLowerCase():"txt"}return null},e.AssetLoader.prototype.load=function(){function a(){b.onAssetLoaded()}var b=this;this.loadCount=this.assetURLs.length;for(var c=0;c0){if(f===g)this.atlas.meta.image.push(a[g]),c=this.atlas.meta.image.length-1,this.atlas.frames.push({}),b=-3;else if(b>0)if(b%7===1)null!=d&&(this.atlas.frames[c][d.name]=d),d={name:a[g],frame:{}};else{var j=a[g].split(" ");if(b%7===3)d.frame.x=Number(j[1].replace(",","")),d.frame.y=Number(j[2]);else if(b%7===4)d.frame.w=Number(j[1].replace(",","")),d.frame.h=Number(j[2]);else if(b%7===5){var k={x:0,y:0,w:Number(j[1].replace(",","")),h:Number(j[2])};k.w>d.frame.w||k.h>d.frame.h?(d.trimmed=!0,d.realSize=k):d.trimmed=!1}}b++}if(null!=d&&(this.atlas.frames[c][d.name]=d),this.atlas.meta.image.length>0){for(this.images=[],h=0;hthis.currentImageId?(this.currentImageId++,this.images[this.currentImageId].load()):(this.loaded=!0,this.dispatchEvent({type:"loaded",content:this}))},e.AtlasLoader.prototype.onError=function(){this.dispatchEvent({type:"error",content:this})},e.SpriteSheetLoader=function(a,b){e.EventTarget.call(this),this.url=a,this.crossorigin=b,this.baseUrl=a.replace(/[^\/]*$/,""),this.texture=null,this.frames={}},e.SpriteSheetLoader.prototype.constructor=e.SpriteSheetLoader,e.SpriteSheetLoader.prototype.load=function(){var a=this,b=new e.JsonLoader(this.url,this.crossorigin);b.addEventListener("loaded",function(b){a.json=b.content.json,a.onLoaded()}),b.load()},e.SpriteSheetLoader.prototype.onLoaded=function(){this.dispatchEvent({type:"loaded",content:this})},e.ImageLoader=function(a,b){e.EventTarget.call(this),this.texture=e.Texture.fromImage(a,b),this.frames=[]},e.ImageLoader.prototype.constructor=e.ImageLoader,e.ImageLoader.prototype.load=function(){if(this.texture.baseTexture.hasLoaded)this.onLoaded();else{var a=this;this.texture.baseTexture.addEventListener("loaded",function(){a.onLoaded()})}},e.ImageLoader.prototype.onLoaded=function(){this.dispatchEvent({type:"loaded",content:this})},e.ImageLoader.prototype.loadFramedSpriteSheet=function(a,b,c){this.frames=[];for(var d=Math.floor(this.texture.width/a),f=Math.floor(this.texture.height/b),g=0,h=0;f>h;h++)for(var i=0;d>i;i++,g++){var j=new e.Texture(this.texture,{x:i*a,y:h*b,width:a,height:b});this.frames.push(j),c&&(e.TextureCache[c+"-"+g]=j)}if(this.texture.baseTexture.hasLoaded)this.onLoaded();else{var k=this;this.texture.baseTexture.addEventListener("loaded",function(){k.onLoaded()})}},e.BitmapFontLoader=function(a,b){e.EventTarget.call(this),this.url=a,this.crossorigin=b,this.baseUrl=a.replace(/[^\/]*$/,""),this.texture=null},e.BitmapFontLoader.prototype.constructor=e.BitmapFontLoader,e.BitmapFontLoader.prototype.load=function(){this.ajaxRequest=new XMLHttpRequest;var a=this;this.ajaxRequest.onreadystatechange=function(){a.onXMLLoaded()},this.ajaxRequest.open("GET",this.url,!0),this.ajaxRequest.overrideMimeType&&this.ajaxRequest.overrideMimeType("application/xml"),this.ajaxRequest.send(null)},e.BitmapFontLoader.prototype.onXMLLoaded=function(){if(4===this.ajaxRequest.readyState&&(200===this.ajaxRequest.status||-1===window.location.protocol.indexOf("http"))){var a=this.ajaxRequest.responseXML;if(!a||/MSIE 9/i.test(navigator.userAgent)||navigator.isCocoonJS)if("function"==typeof window.DOMParser){var b=new DOMParser;a=b.parseFromString(this.ajaxRequest.responseText,"text/xml")}else{var c=document.createElement("div");c.innerHTML=this.ajaxRequest.responseText,a=c}var d=this.baseUrl+a.getElementsByTagName("page")[0].getAttribute("file"),f=new e.ImageLoader(d,this.crossorigin);this.texture=f.texture.baseTexture;var g={},h=a.getElementsByTagName("info")[0],i=a.getElementsByTagName("common")[0];g.font=h.getAttribute("face"),g.size=parseInt(h.getAttribute("size"),10),g.lineHeight=parseInt(i.getAttribute("lineHeight"),10),g.chars={};for(var j=a.getElementsByTagName("char"),k=0;k=0;d--)c=[a[d].apply(this,c)];return c[0]}},each:function(b,c,d){if(b)if(a&&b.forEach&&b.forEach===a)b.forEach(c,d);else if(b.length===b.length+0){for(var e=0,f=b.length;f>e;e++)if(e in b&&c.call(d,b[e],e)===this.BREAK)return}else for(var e in b)if(c.call(d,b[e],e)===this.BREAK)return},defer:function(a){setTimeout(a,0)},toArray:function(a){return a.toArray?a.toArray():b.call(a)},isUndefined:function(a){return void 0===a},isNull:function(a){return null===a},isNaN:function(a){return a!==a},isArray:Array.isArray||function(a){return a.constructor===Array},isObject:function(a){return a===Object(a)},isNumber:function(a){return a===a+0},isString:function(a){return a===a+""},isBoolean:function(a){return a===!1||a===!0},isFunction:function(a){return"[object Function]"===Object.prototype.toString.call(a)}}}(),dat.controllers.Controller=function(a){var b=function(a,b){this.initialValue=a[b],this.domElement=document.createElement("div"),this.object=a,this.property=b,this.__onChange=void 0,this.__onFinishChange=void 0};return a.extend(b.prototype,{onChange:function(a){return this.__onChange=a,this},onFinishChange:function(a){return this.__onFinishChange=a,this},setValue:function(a){return this.object[this.property]=a,this.__onChange&&this.__onChange.call(this,a),this.updateDisplay(),this},getValue:function(){return this.object[this.property]},updateDisplay:function(){return this},isModified:function(){return this.initialValue!==this.getValue()}}),b}(dat.utils.common),dat.dom.dom=function(a){function b(b){if("0"===b||a.isUndefined(b))return 0;var c=b.match(e);return a.isNull(c)?0:parseFloat(c[1])}var c={HTMLEvents:["change"],MouseEvents:["click","mousemove","mousedown","mouseup","mouseover"],KeyboardEvents:["keydown"]},d={};a.each(c,function(b,c){a.each(b,function(a){d[a]=c})});var e=/(\d+(\.\d+)?)px/,f={makeSelectable:function(a,b){void 0!==a&&void 0!==a.style&&(a.onselectstart=b?function(){return!1}:function(){},a.style.MozUserSelect=b?"auto":"none",a.style.KhtmlUserSelect=b?"auto":"none",a.unselectable=b?"on":"off")},makeFullscreen:function(b,c,d){a.isUndefined(c)&&(c=!0),a.isUndefined(d)&&(d=!0),b.style.position="absolute",c&&(b.style.left=0,b.style.right=0),d&&(b.style.top=0,b.style.bottom=0)},fakeEvent:function(b,c,e,f){e=e||{};var g=d[c];if(!g)throw new Error("Event type "+c+" not supported.");var h=document.createEvent(g);switch(g){case"MouseEvents":var i=e.x||e.clientX||0,j=e.y||e.clientY||0;h.initMouseEvent(c,e.bubbles||!1,e.cancelable||!0,window,e.clickCount||1,0,0,i,j,!1,!1,!1,!1,0,null);break;case"KeyboardEvents":var k=h.initKeyboardEvent||h.initKeyEvent;a.defaults(e,{cancelable:!0,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,keyCode:void 0,charCode:void 0}),k(c,e.bubbles||!1,e.cancelable,window,e.ctrlKey,e.altKey,e.shiftKey,e.metaKey,e.keyCode,e.charCode);break;default:h.initEvent(c,e.bubbles||!1,e.cancelable||!0)}a.defaults(h,f),b.dispatchEvent(h)},bind:function(a,b,c,d){return d=d||!1,a.addEventListener?a.addEventListener(b,c,d):a.attachEvent&&a.attachEvent("on"+b,c),f},unbind:function(a,b,c,d){return d=d||!1,a.removeEventListener?a.removeEventListener(b,c,d):a.detachEvent&&a.detachEvent("on"+b,c),f},addClass:function(a,b){if(void 0===a.className)a.className=b;else if(a.className!==b){var c=a.className.split(/ +/);-1==c.indexOf(b)&&(c.push(b),a.className=c.join(" ").replace(/^\s+/,"").replace(/\s+$/,""))}return f},removeClass:function(a,b){if(b)if(void 0===a.className);else if(a.className===b)a.removeAttribute("class");else{var c=a.className.split(/ +/),d=c.indexOf(b);-1!=d&&(c.splice(d,1),a.className=c.join(" "))}else a.className=void 0;return f},hasClass:function(a,b){return new RegExp("(?:^|\\s+)"+b+"(?:\\s+|$)").test(a.className)||!1},getWidth:function(a){var c=getComputedStyle(a);return b(c["border-left-width"])+b(c["border-right-width"])+b(c["padding-left"])+b(c["padding-right"])+b(c.width)},getHeight:function(a){var c=getComputedStyle(a);return b(c["border-top-width"])+b(c["border-bottom-width"])+b(c["padding-top"])+b(c["padding-bottom"])+b(c.height)},getOffset:function(a){var b={left:0,top:0};if(a.offsetParent)do b.left+=a.offsetLeft,b.top+=a.offsetTop;while(a=a.offsetParent);return b},isActive:function(a){return a===document.activeElement&&(a.type||a.href)}};return f}(dat.utils.common),dat.controllers.OptionController=function(a,b,c){var d=function(a,e,f){d.superclass.call(this,a,e);var g=this;if(this.__select=document.createElement("select"),c.isArray(f)){var h={};c.each(f,function(a){h[a]=a}),f=h}c.each(f,function(a,b){var c=document.createElement("option");c.innerHTML=b,c.setAttribute("value",a),g.__select.appendChild(c)}),this.updateDisplay(),b.bind(this.__select,"change",function(){var a=this.options[this.selectedIndex].value;g.setValue(a)}),this.domElement.appendChild(this.__select)};return d.superclass=a,c.extend(d.prototype,a.prototype,{setValue:function(a){var b=d.superclass.prototype.setValue.call(this,a);return this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue()),b},updateDisplay:function(){return this.__select.value=this.getValue(),d.superclass.prototype.updateDisplay.call(this)}}),d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.NumberController=function(a,b){function c(a){return a=a.toString(),a.indexOf(".")>-1?a.length-a.indexOf(".")-1:0}var d=function(a,e,f){d.superclass.call(this,a,e),f=f||{},this.__min=f.min,this.__max=f.max,this.__step=f.step,this.__impliedStep=b.isUndefined(this.__step)?0==this.initialValue?1:Math.pow(10,Math.floor(Math.log(this.initialValue)/Math.LN10))/10:this.__step,this.__precision=c(this.__impliedStep)};return d.superclass=a,b.extend(d.prototype,a.prototype,{setValue:function(a){return void 0!==this.__min&&athis.__max&&(a=this.__max),void 0!==this.__step&&a%this.__step!=0&&(a=Math.round(a/this.__step)*this.__step),d.superclass.prototype.setValue.call(this,a)},min:function(a){return this.__min=a,this},max:function(a){return this.__max=a,this},step:function(a){return this.__step=a,this.__impliedStep=a,this.__precision=c(a),this}}),d}(dat.controllers.Controller,dat.utils.common),dat.controllers.NumberControllerBox=function(a,b,c){function d(a,b){var c=Math.pow(10,b);return Math.round(a*c)/c}var e=function(a,d,f){function g(){var a=parseFloat(m.__input.value);c.isNaN(a)||m.setValue(a)}function h(){g(),m.__onFinishChange&&m.__onFinishChange.call(m,m.getValue())}function i(a){b.bind(window,"mousemove",j),b.bind(window,"mouseup",k),l=a.clientY}function j(a){var b=l-a.clientY;m.setValue(m.getValue()+b*m.__impliedStep),l=a.clientY}function k(){b.unbind(window,"mousemove",j),b.unbind(window,"mouseup",k)}this.__truncationSuspended=!1,e.superclass.call(this,a,d,f);var l,m=this;this.__input=document.createElement("input"),this.__input.setAttribute("type","text"),b.bind(this.__input,"change",g),b.bind(this.__input,"blur",h),b.bind(this.__input,"mousedown",i),b.bind(this.__input,"keydown",function(a){13===a.keyCode&&(m.__truncationSuspended=!0,this.blur(),m.__truncationSuspended=!1)}),this.updateDisplay(),this.domElement.appendChild(this.__input)};return e.superclass=a,c.extend(e.prototype,a.prototype,{updateDisplay:function(){return this.__input.value=this.__truncationSuspended?this.getValue():d(this.getValue(),this.__precision),e.superclass.prototype.updateDisplay.call(this)}}),e}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common),dat.controllers.NumberControllerSlider=function(a,b,c,d,e){function f(a,b,c,d,e){return d+(e-d)*((a-b)/(c-b))}var g=function(a,c,d,e,h){function i(a){b.bind(window,"mousemove",j),b.bind(window,"mouseup",k),j(a)}function j(a){a.preventDefault();var c=b.getOffset(l.__background),d=b.getWidth(l.__background);return l.setValue(f(a.clientX,c.left,c.left+d,l.__min,l.__max)),!1}function k(){b.unbind(window,"mousemove",j),b.unbind(window,"mouseup",k),l.__onFinishChange&&l.__onFinishChange.call(l,l.getValue())}g.superclass.call(this,a,c,{min:d,max:e,step:h});var l=this;this.__background=document.createElement("div"),this.__foreground=document.createElement("div"),b.bind(this.__background,"mousedown",i),b.addClass(this.__background,"slider"),b.addClass(this.__foreground,"slider-fg"),this.updateDisplay(),this.__background.appendChild(this.__foreground),this.domElement.appendChild(this.__background)};return g.superclass=a,g.useDefaultStyles=function(){c.inject(e)},d.extend(g.prototype,a.prototype,{updateDisplay:function(){var a=(this.getValue()-this.__min)/(this.__max-this.__min);return this.__foreground.style.width=100*a+"%",g.superclass.prototype.updateDisplay.call(this)}}),g}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,"/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Google Creative Lab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}"),dat.controllers.FunctionController=function(a,b,c){var d=function(a,c,e){d.superclass.call(this,a,c);var f=this;this.__button=document.createElement("div"),this.__button.innerHTML=void 0===e?"Fire":e,b.bind(this.__button,"click",function(a){return a.preventDefault(),f.fire(),!1}),b.addClass(this.__button,"button"),this.domElement.appendChild(this.__button)};return d.superclass=a,c.extend(d.prototype,a.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this),this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue()),this.getValue().call(this.object)}}),d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.BooleanController=function(a,b,c){var d=function(a,c){function e(){f.setValue(!f.__prev)}d.superclass.call(this,a,c);var f=this;this.__prev=this.getValue(),this.__checkbox=document.createElement("input"),this.__checkbox.setAttribute("type","checkbox"),b.bind(this.__checkbox,"change",e,!1),this.domElement.appendChild(this.__checkbox),this.updateDisplay()};return d.superclass=a,c.extend(d.prototype,a.prototype,{setValue:function(a){var b=d.superclass.prototype.setValue.call(this,a);return this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue()),this.__prev=this.getValue(),b},updateDisplay:function(){return this.getValue()===!0?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=!0):this.__checkbox.checked=!1,d.superclass.prototype.updateDisplay.call(this)}}),d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.color.toString=function(a){return function(b){if(1==b.a||a.isUndefined(b.a)){for(var c=b.hex.toString(16);c.length<6;)c="0"+c;return"#"+c}return"rgba("+Math.round(b.r)+","+Math.round(b.g)+","+Math.round(b.b)+","+b.a+")"}}(dat.utils.common),dat.color.interpret=function(a,b){var c,d,e=function(){d=!1;var a=arguments.length>1?b.toArray(arguments):arguments[0];return b.each(f,function(e){return e.litmus(a)?(b.each(e.conversions,function(e,f){return c=e.read(a),d===!1&&c!==!1?(d=c,c.conversionName=f,c.conversion=e,b.BREAK):void 0}),b.BREAK):void 0}),d},f=[{litmus:b.isString,conversions:{THREE_CHAR_HEX:{read:function(a){var b=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return null===b?!1:{space:"HEX",hex:parseInt("0x"+b[1].toString()+b[1].toString()+b[2].toString()+b[2].toString()+b[3].toString()+b[3].toString())}},write:a},SIX_CHAR_HEX:{read:function(a){var b=a.match(/^#([A-F0-9]{6})$/i);return null===b?!1:{space:"HEX",hex:parseInt("0x"+b[1].toString())}},write:a},CSS_RGB:{read:function(a){var b=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/);return null===b?!1:{space:"RGB",r:parseFloat(b[1]),g:parseFloat(b[2]),b:parseFloat(b[3])}},write:a},CSS_RGBA:{read:function(a){var b=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return null===b?!1:{space:"RGB",r:parseFloat(b[1]),g:parseFloat(b[2]),b:parseFloat(b[3]),a:parseFloat(b[4])}},write:a}}},{litmus:b.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:b.isArray,conversions:{RGB_ARRAY:{read:function(a){return 3!=a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return 4!=a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:b.isObject,conversions:{RGBA_OBJ:{read:function(a){return b.isNumber(a.r)&&b.isNumber(a.g)&&b.isNumber(a.b)&&b.isNumber(a.a)?{space:"RGB",r:a.r,g:a.g,b:a.b,a:a.a}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(a){return b.isNumber(a.r)&&b.isNumber(a.g)&&b.isNumber(a.b)?{space:"RGB",r:a.r,g:a.g,b:a.b}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(a){return b.isNumber(a.h)&&b.isNumber(a.s)&&b.isNumber(a.v)&&b.isNumber(a.a)?{space:"HSV",h:a.h,s:a.s,v:a.v,a:a.a}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(a){return b.isNumber(a.h)&&b.isNumber(a.s)&&b.isNumber(a.v)?{space:"HSV",h:a.h,s:a.s,v:a.v}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return e}(dat.color.toString,dat.utils.common),dat.GUI=dat.gui.GUI=function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){function p(a,b,c,f){if(void 0===b[c])throw new Error("Object "+b+' has no property "'+c+'"');var g;if(f.color)g=new k(b,c);else{var h=[b,c].concat(f.factoryArgs);g=d.apply(a,h)}f.before instanceof e&&(f.before=f.before.__li),s(a,g),n.addClass(g.domElement,"c");var i=document.createElement("span");n.addClass(i,"property-name"),i.innerHTML=g.property;var j=document.createElement("div");j.appendChild(i),j.appendChild(g.domElement);var l=q(a,j,f.before);return n.addClass(l,M.CLASS_CONTROLLER_ROW),n.addClass(l,typeof g.getValue()),r(a,l,g),a.__controllers.push(g),g}function q(a,b,c){var d=document.createElement("li");return b&&d.appendChild(b),c?a.__ul.insertBefore(d,params.before):a.__ul.appendChild(d),a.onResize(),d}function r(a,b,c){if(c.__li=b,c.__gui=a,o.extend(c,{options:function(b){return arguments.length>1?(c.remove(),p(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[o.toArray(arguments)]})):o.isArray(b)||o.isObject(b)?(c.remove(),p(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[b]})):void 0},name:function(a){return c.__li.firstElementChild.firstElementChild.innerHTML=a,c},listen:function(){return c.__gui.listen(c),c},remove:function(){return c.__gui.remove(c),c}}),c instanceof i){var d=new h(c.object,c.property,{min:c.__min,max:c.__max,step:c.__step});o.each(["updateDisplay","onChange","onFinishChange"],function(a){var b=c[a],e=d[a];c[a]=d[a]=function(){var a=Array.prototype.slice.call(arguments);return b.apply(c,a),e.apply(d,a)}}),n.addClass(b,"has-slider"),c.domElement.insertBefore(d.domElement,c.domElement.firstElementChild)}else if(c instanceof h){var e=function(b){return o.isNumber(c.__min)&&o.isNumber(c.__max)?(c.remove(),p(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[c.__min,c.__max,c.__step]})):b};c.min=o.compose(e,c.min),c.max=o.compose(e,c.max)}else c instanceof f?(n.bind(b,"click",function(){n.fakeEvent(c.__checkbox,"click")}),n.bind(c.__checkbox,"click",function(a){a.stopPropagation()})):c instanceof g?(n.bind(b,"click",function(){n.fakeEvent(c.__button,"click")}),n.bind(b,"mouseover",function(){n.addClass(c.__button,"hover")}),n.bind(b,"mouseout",function(){n.removeClass(c.__button,"hover")})):c instanceof k&&(n.addClass(b,"color"),c.updateDisplay=o.compose(function(a){return b.style.borderLeftColor=c.__color.toString(),a},c.updateDisplay),c.updateDisplay());c.setValue=o.compose(function(b){return a.getRoot().__preset_select&&c.isModified()&&A(a.getRoot(),!0),b},c.setValue)}function s(a,b){var c=a.getRoot(),d=c.__rememberedObjects.indexOf(b.object);if(-1!=d){var e=c.__rememberedObjectIndecesToControllers[d];if(void 0===e&&(e={},c.__rememberedObjectIndecesToControllers[d]=e),e[b.property]=b,c.load&&c.load.remembered){var f,g=c.load.remembered;if(g[a.preset])f=g[a.preset];else{if(!g[H])return;f=g[H]}if(f[d]&&void 0!==f[d][b.property]){var h=f[d][b.property];b.initialValue=h,b.setValue(h)}}}}function t(a,b){return document.location.href+"."+b}function u(a){function b(){j.style.display=a.useLocalStorage?"block":"none"}var c=a.__save_row=document.createElement("li");n.addClass(a.domElement,"has-save"),a.__ul.insertBefore(c,a.__ul.firstChild),n.addClass(c,"save-row");var d=document.createElement("span");d.innerHTML=" ",n.addClass(d,"button gears");var e=document.createElement("span");e.innerHTML="Save",n.addClass(e,"button"),n.addClass(e,"save");var f=document.createElement("span");f.innerHTML="New",n.addClass(f,"button"),n.addClass(f,"save-as");var g=document.createElement("span");g.innerHTML="Revert",n.addClass(g,"button"),n.addClass(g,"revert");var h=a.__preset_select=document.createElement("select");if(a.load&&a.load.remembered?o.each(a.load.remembered,function(b,c){y(a,c,c==a.preset)}):y(a,H,!1),n.bind(h,"change",function(){for(var b=0;b0&&(a.preset=this.preset,a.remembered||(a.remembered={}),a.remembered[this.preset]=x(this)),a.folders={},o.each(this.__folders,function(b,c){a.folders[c]=b.getSaveObject()}),a},save:function(){this.load.remembered||(this.load.remembered={}),this.load.remembered[this.preset]=x(this),A(this,!1),this.saveToLocalStorageIfPossible()},saveAs:function(a){this.load.remembered||(this.load.remembered={},this.load.remembered[H]=x(this,!0)),this.load.remembered[a]=x(this),this.preset=a,y(this,a,!0),this.saveToLocalStorageIfPossible()},revert:function(a){o.each(this.__controllers,function(b){this.getRoot().load.remembered?s(a||this.getRoot(),b):b.setValue(b.initialValue)},this),o.each(this.__folders,function(a){a.revert(a)}),a||A(this.getRoot(),!1)},listen:function(a){var b=0==this.__listening.length;this.__listening.push(a),b&&B(this.__listening)}}),M}(dat.utils.css,'
    \n\n Here\'s the new load parameter for your GUI\'s constructor:\n\n \n\n
    \n\n Automatically save\n values to localStorage on exit.\n\n
    The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n \n
    \n \n
    \n\n
    ',".dg {\n /** Clear list styles */\n /* Auto-place container */\n /* Auto-placed GUI's */\n /* Line items that don't contain folders. */\n /** Folder names */\n /** Hides closed items */\n /** Controller row */\n /** Name-half (left) */\n /** Controller-half (right) */\n /** Controller placement */\n /** Shorter number boxes when slider is present. */\n /** Ensure the entire boolean and function row shows a hand */ }\n .dg ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n clear: both; }\n .dg.ac {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 0;\n z-index: 0; }\n .dg:not(.ac) .main {\n /** Exclude mains in ac so that we don't hide close button */\n overflow: hidden; }\n .dg.main {\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear; }\n .dg.main.taller-than-window {\n overflow-y: auto; }\n .dg.main.taller-than-window .close-button {\n opacity: 1;\n /* TODO, these are style notes */\n margin-top: -1px;\n border-top: 1px solid #2c2c2c; }\n .dg.main ul.closed .close-button {\n opacity: 1 !important; }\n .dg.main:hover .close-button,\n .dg.main .close-button.drag {\n opacity: 1; }\n .dg.main .close-button {\n /*opacity: 0;*/\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear;\n border: 0;\n position: absolute;\n line-height: 19px;\n height: 20px;\n /* TODO, these are style notes */\n cursor: pointer;\n text-align: center;\n background-color: #000; }\n .dg.main .close-button:hover {\n background-color: #111; }\n .dg.a {\n float: right;\n margin-right: 15px;\n overflow-x: hidden; }\n .dg.a.has-save > ul {\n margin-top: 27px; }\n .dg.a.has-save > ul.closed {\n margin-top: 0; }\n .dg.a .save-row {\n position: fixed;\n top: 0;\n z-index: 1002; }\n .dg li {\n -webkit-transition: height 0.1s ease-out;\n -o-transition: height 0.1s ease-out;\n -moz-transition: height 0.1s ease-out;\n transition: height 0.1s ease-out; }\n .dg li:not(.folder) {\n cursor: auto;\n height: 27px;\n line-height: 27px;\n overflow: hidden;\n padding: 0 4px 0 5px; }\n .dg li.folder {\n padding: 0;\n border-left: 4px solid rgba(0, 0, 0, 0); }\n .dg li.title {\n cursor: pointer;\n margin-left: -4px; }\n .dg .closed li:not(.title),\n .dg .closed ul li,\n .dg .closed ul li > * {\n height: 0;\n overflow: hidden;\n border: 0; }\n .dg .cr {\n clear: both;\n padding-left: 3px;\n height: 27px; }\n .dg .property-name {\n cursor: default;\n float: left;\n clear: left;\n width: 40%;\n overflow: hidden;\n text-overflow: ellipsis; }\n .dg .c {\n float: left;\n width: 60%; }\n .dg .c input[type=text] {\n border: 0;\n margin-top: 4px;\n padding: 3px;\n width: 100%;\n float: right; }\n .dg .has-slider input[type=text] {\n width: 30%;\n /*display: none;*/\n margin-left: 0; }\n .dg .slider {\n float: left;\n width: 66%;\n margin-left: -5px;\n margin-right: 0;\n height: 19px;\n margin-top: 4px; }\n .dg .slider-fg {\n height: 100%; }\n .dg .c input[type=checkbox] {\n margin-top: 9px; }\n .dg .c select {\n margin-top: 5px; }\n .dg .cr.function,\n .dg .cr.function .property-name,\n .dg .cr.function *,\n .dg .cr.boolean,\n .dg .cr.boolean * {\n cursor: pointer; }\n .dg .selector {\n display: none;\n position: absolute;\n margin-left: -9px;\n margin-top: 23px;\n z-index: 10; }\n .dg .c:hover .selector,\n .dg .selector.drag {\n display: block; }\n .dg li.save-row {\n padding: 0; }\n .dg li.save-row .button {\n display: inline-block;\n padding: 0px 6px; }\n .dg.dialogue {\n background-color: #222;\n width: 460px;\n padding: 15px;\n font-size: 13px;\n line-height: 15px; }\n\n/* TODO Separate style and structure */\n#dg-new-constructor {\n padding: 10px;\n color: #222;\n font-family: Monaco, monospace;\n font-size: 10px;\n border: 0;\n resize: none;\n box-shadow: inset 1px 1px 1px #888;\n word-wrap: break-word;\n margin: 12px 0;\n display: block;\n width: 440px;\n overflow-y: scroll;\n height: 100px;\n position: relative; }\n\n#dg-local-explain {\n display: none;\n font-size: 11px;\n line-height: 17px;\n border-radius: 3px;\n background-color: #333;\n padding: 8px;\n margin-top: 10px; }\n #dg-local-explain code {\n font-size: 10px; }\n\n#dat-gui-save-locally {\n display: none; }\n\n/** Main type */\n.dg {\n color: #eee;\n font: 11px 'Lucida Grande', sans-serif;\n text-shadow: 0 -1px 0 #111;\n /** Auto place */\n /* Controller row,
  • */\n /** Controllers */ }\n .dg.main {\n /** Scrollbar */ }\n .dg.main::-webkit-scrollbar {\n width: 5px;\n background: #1a1a1a; }\n .dg.main::-webkit-scrollbar-corner {\n height: 0;\n display: none; }\n .dg.main::-webkit-scrollbar-thumb {\n border-radius: 5px;\n background: #676767; }\n .dg li:not(.folder) {\n background: #1a1a1a;\n border-bottom: 1px solid #2c2c2c; }\n .dg li.save-row {\n line-height: 25px;\n background: #dad5cb;\n border: 0; }\n .dg li.save-row select {\n margin-left: 5px;\n width: 108px; }\n .dg li.save-row .button {\n margin-left: 5px;\n margin-top: 1px;\n border-radius: 2px;\n font-size: 9px;\n line-height: 7px;\n padding: 4px 4px 5px 4px;\n background: #c5bdad;\n color: #fff;\n text-shadow: 0 1px 0 #b0a58f;\n box-shadow: 0 -1px 0 #b0a58f;\n cursor: pointer; }\n .dg li.save-row .button.gears {\n background: #c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;\n height: 7px;\n width: 8px; }\n .dg li.save-row .button:hover {\n background-color: #bab19e;\n box-shadow: 0 -1px 0 #b0a58f; }\n .dg li.folder {\n border-bottom: 0; }\n .dg li.title {\n padding-left: 16px;\n background: black url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;\n cursor: pointer;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2); }\n .dg .closed li.title {\n background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==); }\n .dg .cr.boolean {\n border-left: 3px solid #806787; }\n .dg .cr.function {\n border-left: 3px solid #e61d5f; }\n .dg .cr.number {\n border-left: 3px solid #2fa1d6; }\n .dg .cr.number input[type=text] {\n color: #2fa1d6; }\n .dg .cr.string {\n border-left: 3px solid #1ed36f; }\n .dg .cr.string input[type=text] {\n color: #1ed36f; }\n .dg .cr.function:hover, .dg .cr.boolean:hover {\n background: #111; }\n .dg .c input[type=text] {\n background: #303030;\n outline: none; }\n .dg .c input[type=text]:hover {\n background: #3c3c3c; }\n .dg .c input[type=text]:focus {\n background: #494949;\n color: #fff; }\n .dg .c .slider {\n background: #303030;\n cursor: ew-resize; }\n .dg .c .slider-fg {\n background: #2fa1d6; }\n .dg .c .slider:hover {\n background: #3c3c3c; }\n .dg .c .slider:hover .slider-fg {\n background: #44abda; }\n",dat.controllers.factory=function(a,b,c,d,e,f,g){return function(h,i){var j=h[i];return g.isArray(arguments[2])||g.isObject(arguments[2])?new a(h,i,arguments[2]):g.isNumber(j)?g.isNumber(arguments[2])&&g.isNumber(arguments[3])?new c(h,i,arguments[2],arguments[3]):new b(h,i,{min:arguments[2],max:arguments[3]}):g.isString(j)?new d(h,i):g.isFunction(j)?new e(h,i,""):g.isBoolean(j)?new f(h,i):void 0}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(a,b,c){var d=function(a,c){function e(){g.setValue(g.__input.value)}function f(){g.__onFinishChange&&g.__onFinishChange.call(g,g.getValue())}d.superclass.call(this,a,c);var g=this;this.__input=document.createElement("input"),this.__input.setAttribute("type","text"),b.bind(this.__input,"keyup",e),b.bind(this.__input,"change",e),b.bind(this.__input,"blur",f),b.bind(this.__input,"keydown",function(a){13===a.keyCode&&this.blur()}),this.updateDisplay(),this.domElement.appendChild(this.__input)};return d.superclass=a,c.extend(d.prototype,a.prototype,{updateDisplay:function(){return b.isActive(this.__input)||(this.__input.value=this.getValue()),d.superclass.prototype.updateDisplay.call(this)}}),d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController,dat.controllers.ColorController=function(a,b,c,d,e){function f(a,b,c,d){a.style.background="",e.each(i,function(e){a.style.cssText+="background: "+e+"linear-gradient("+b+", "+c+" 0%, "+d+" 100%); "})}function g(a){a.style.background="",a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);",a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var h=function(a,i){function j(a){n(a),b.bind(window,"mousemove",n),b.bind(window,"mouseup",k)}function k(){b.unbind(window,"mousemove",n),b.unbind(window,"mouseup",k)}function l(){var a=d(this.value);a!==!1?(p.__color.__state=a,p.setValue(p.__color.toOriginal())):this.value=p.__color.toString()}function m(){b.unbind(window,"mousemove",o),b.unbind(window,"mouseup",m)}function n(a){a.preventDefault();var c=b.getWidth(p.__saturation_field),d=b.getOffset(p.__saturation_field),e=(a.clientX-d.left+document.body.scrollLeft)/c,f=1-(a.clientY-d.top+document.body.scrollTop)/c;return f>1?f=1:0>f&&(f=0),e>1?e=1:0>e&&(e=0),p.__color.v=f,p.__color.s=e,p.setValue(p.__color.toOriginal()),!1}function o(a){a.preventDefault();var c=b.getHeight(p.__hue_field),d=b.getOffset(p.__hue_field),e=1-(a.clientY-d.top+document.body.scrollTop)/c;return e>1?e=1:0>e&&(e=0),p.__color.h=360*e,p.setValue(p.__color.toOriginal()),!1}h.superclass.call(this,a,i),this.__color=new c(this.getValue()),this.__temp=new c(0);var p=this;this.domElement=document.createElement("div"),b.makeSelectable(this.domElement,!1),this.__selector=document.createElement("div"),this.__selector.className="selector",this.__saturation_field=document.createElement("div"),this.__saturation_field.className="saturation-field",this.__field_knob=document.createElement("div"),this.__field_knob.className="field-knob",this.__field_knob_border="2px solid ",this.__hue_knob=document.createElement("div"),this.__hue_knob.className="hue-knob",this.__hue_field=document.createElement("div"),this.__hue_field.className="hue-field",this.__input=document.createElement("input"),this.__input.type="text",this.__input_textShadow="0 1px 1px ",b.bind(this.__input,"keydown",function(a){13===a.keyCode&&l.call(this)}),b.bind(this.__input,"blur",l),b.bind(this.__selector,"mousedown",function(){b.addClass(this,"drag").bind(window,"mouseup",function(){b.removeClass(p.__selector,"drag")})});var q=document.createElement("div");e.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"}),e.extend(this.__field_knob.style,{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(this.__color.v<.5?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1}),e.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1}),e.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"}),e.extend(q.style,{width:"100%",height:"100%",background:"none"}),f(q,"top","rgba(0,0,0,0)","#000"),e.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"}),g(this.__hue_field),e.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"}),b.bind(this.__saturation_field,"mousedown",j),b.bind(this.__field_knob,"mousedown",j),b.bind(this.__hue_field,"mousedown",function(a){o(a),b.bind(window,"mousemove",o),b.bind(window,"mouseup",m)}),this.__saturation_field.appendChild(q),this.__selector.appendChild(this.__field_knob),this.__selector.appendChild(this.__saturation_field),this.__selector.appendChild(this.__hue_field),this.__hue_field.appendChild(this.__hue_knob),this.domElement.appendChild(this.__input),this.domElement.appendChild(this.__selector),this.updateDisplay()};h.superclass=a,e.extend(h.prototype,a.prototype,{updateDisplay:function(){var a=d(this.getValue());if(a!==!1){var b=!1;e.each(c.COMPONENTS,function(c){return e.isUndefined(a[c])||e.isUndefined(this.__color.__state[c])||a[c]===this.__color.__state[c]?void 0:(b=!0,{})},this),b&&e.extend(this.__color.__state,a)}e.extend(this.__temp.__state,this.__color.__state),this.__temp.a=1;var g=this.__color.v<.5||this.__color.s>.5?255:0,h=255-g;e.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toString(),border:this.__field_knob_border+"rgb("+g+","+g+","+g+")"}),this.__hue_knob.style.marginTop=100*(1-this.__color.h/360)+"px",this.__temp.s=1,this.__temp.v=1,f(this.__saturation_field,"left","#fff",this.__temp.toString()),e.extend(this.__input.style,{backgroundColor:this.__input.value=this.__color.toString(),color:"rgb("+g+","+g+","+g+")",textShadow:this.__input_textShadow+"rgba("+h+","+h+","+h+",.7)"})}});var i=["-moz-","-o-","-webkit-","-ms-",""];return h}(dat.controllers.Controller,dat.dom.dom,dat.color.Color=function(a,b,c,d){function e(a,b,c){Object.defineProperty(a,b,{get:function(){return"RGB"===this.__state.space?this.__state[b]:(g(this,b,c),this.__state[b])},set:function(a){"RGB"!==this.__state.space&&(g(this,b,c),this.__state.space="RGB"),this.__state[b]=a}})}function f(a,b){Object.defineProperty(a,b,{get:function(){return"HSV"===this.__state.space?this.__state[b]:(h(this),this.__state[b])},set:function(a){"HSV"!==this.__state.space&&(h(this),this.__state.space="HSV"),this.__state[b]=a}})}function g(a,c,e){if("HEX"===a.__state.space)a.__state[c]=b.component_from_hex(a.__state.hex,e);else{if("HSV"!==a.__state.space)throw"Corrupted color state";d.extend(a.__state,b.hsv_to_rgb(a.__state.h,a.__state.s,a.__state.v))}}function h(a){var c=b.rgb_to_hsv(a.r,a.g,a.b);d.extend(a.__state,{s:c.s,v:c.v}),d.isNaN(c.h)?d.isUndefined(a.__state.h)&&(a.__state.h=0):a.__state.h=c.h}var i=function(){if(this.__state=a.apply(this,arguments),this.__state===!1)throw"Failed to interpret color arguments";this.__state.a=this.__state.a||1};return i.COMPONENTS=["r","g","b","h","s","v","hex","a"],d.extend(i.prototype,{toString:function(){return c(this)},toOriginal:function(){return this.__state.conversion.write(this)}}),e(i.prototype,"r",2),e(i.prototype,"g",1),e(i.prototype,"b",0),f(i.prototype,"h"),f(i.prototype,"s"),f(i.prototype,"v"),Object.defineProperty(i.prototype,"a",{get:function(){return this.__state.a},set:function(a){this.__state.a=a}}),Object.defineProperty(i.prototype,"hex",{get:function(){return"HEX"!==!this.__state.space&&(this.__state.hex=b.rgb_to_hex(this.r,this.g,this.b)),this.__state.hex},set:function(a){this.__state.space="HEX",this.__state.hex=a}}),i}(dat.color.interpret,dat.color.math=function(){var a;return{hsv_to_rgb:function(a,b,c){var d=Math.floor(a/60)%6,e=a/60-Math.floor(a/60),f=c*(1-b),g=c*(1-e*b),h=c*(1-(1-e)*b),i=[[c,h,f],[g,c,f],[f,c,h],[f,g,c],[h,f,c],[c,f,g]][d];return{r:255*i[0],g:255*i[1],b:255*i[2]}},rgb_to_hsv:function(a,b,c){var d,e,f=Math.min(a,b,c),g=Math.max(a,b,c),h=g-f;return 0==g?{h:0/0,s:0,v:0}:(e=h/g,d=a==g?(b-c)/h:b==g?2+(c-a)/h:4+(a-b)/h,d/=6,0>d&&(d+=1),{h:360*d,s:e,v:g/255})},rgb_to_hex:function(a,b,c){var d=this.hex_with_component(0,2,a);return d=this.hex_with_component(d,1,b),d=this.hex_with_component(d,0,c)},component_from_hex:function(a,b){return a>>8*b&255},hex_with_component:function(b,c,d){return d<<(a=8*c)|b&~(255<0&&(d=e.shift(),d.type===a.Body.STATIC);)d=null;if(d){d.wakeUp(),this.setState(b.DRAGGING);var f=a.vec2.create();d.toLocalFrame(f,c),this.world.addBody(this.nullBody),this.mouseConstraint=new a.RevoluteConstraint(this.nullBody,d,{localPivotA:c,localPivotB:f}),this.world.addConstraint(this.mouseConstraint)}else this.setState(b.PANNING);break;case b.DRAWPOLYGON:this.setState(b.DRAWINGPOLYGON),this.drawPoints=[];var g=a.vec2.create();a.vec2.copy(g,c),this.drawPoints.push(g),this.emit(this.drawPointsChangeEvent);break;case b.DRAWCIRCLE:this.setState(b.DRAWINGCIRCLE),a.vec2.copy(this.drawCircleCenter,c),a.vec2.copy(this.drawCirclePoint,c),this.emit(this.drawCircleChangeEvent);break;case b.DRAWRECTANGLE:this.setState(b.DRAWINGRECTANGLE),a.vec2.copy(this.drawRectStart,c),a.vec2.copy(this.drawRectEnd,c),this.emit(this.drawRectangleChangeEvent)}},b.prototype.handleMouseMove=function(c){var d=.4;switch(this.state){case b.DEFAULT:case b.DRAGGING:this.mouseConstraint&&(a.vec2.copy(this.mouseConstraint.pivotA,c),this.mouseConstraint.bodyA.wakeUp(),this.mouseConstraint.bodyB.wakeUp());break;case b.DRAWINGPOLYGON:var e=a.vec2.dist(c,this.drawPoints[this.drawPoints.length-1]);if(e>d*d){var f=[0,0];a.vec2.copy(f,c),this.drawPoints.push(f),this.emit(this.drawPointsChangeEvent)}break;case b.DRAWINGCIRCLE:a.vec2.copy(this.drawCirclePoint,c),this.emit(this.drawCircleChangeEvent);break;case b.DRAWINGRECTANGLE:a.vec2.copy(this.drawRectEnd,c),this.emit(this.drawRectangleChangeEvent)}},b.prototype.handleMouseUp=function(){var c;switch(this.state){case b.DEFAULT:break;case b.DRAGGING:this.world.removeConstraint(this.mouseConstraint),this.mouseConstraint=null,this.world.removeBody(this.nullBody),this.setState(b.DEFAULT);break;case b.PANNING:this.setState(b.DEFAULT);break;case b.DRAWINGPOLYGON:this.setState(b.DRAWPOLYGON),this.drawPoints.length>3&&(c=new a.Body({mass:1}),c.fromPolygon(this.drawPoints,{removeCollinearPoints:.01})&&this.world.addBody(c)),this.drawPoints=[],this.emit(this.drawPointsChangeEvent);break;case b.DRAWINGCIRCLE:this.setState(b.DRAWCIRCLE);var d=a.vec2.dist(this.drawCircleCenter,this.drawCirclePoint);if(d>0){c=new a.Body({mass:1,position:this.drawCircleCenter});var e=new a.Circle(d);c.addShape(e),this.world.addBody(c)}a.vec2.copy(this.drawCircleCenter,this.drawCirclePoint),this.emit(this.drawCircleChangeEvent);break;case b.DRAWINGRECTANGLE:this.setState(b.DRAWRECTANGLE);for(var f=this.drawRectStart,g=this.drawRectEnd,h=0;2>h;h++)if(f[h]>g[h]){var i=g[h];g[h]=f[h],f[h]=i}var j=Math.abs(f[0]-g[0]),k=Math.abs(f[1]-g[1]);if(j>0&&k>0){c=new a.Body({mass:1,position:[this.drawRectStart[0]+.5*j,this.drawRectStart[1]+.5*k]});var l=new a.Rectangle(j,k);c.addShape(l),this.world.addBody(c)}a.vec2.copy(this.drawRectEnd,this.drawRectStart),this.emit(this.drawRectangleChangeEvent)}if(c){c.wakeUp();for(var h=0;h","

    p2.js

    ","

    Physics Engine

    ",'',""].join(""),this.elementContainer.appendChild(b),!function(a,b,c){var d,e=a.getElementsByTagName(b)[0],f=/^http:/.test(a.location)?"http":"https";a.getElementById(c)||(d=a.createElement(b),d.id=c,d.src=f+"://platform.twitter.com/widgets.js",e.parentNode.insertBefore(d,e))}(document,"script","twitter-wjs")},b.zoomInEvent={type:"zoomin"},b.zoomOutEvent={type:"zoomout"},b.prototype.setEquationParameters=function(){this.world.setGlobalEquationParameters({stiffness:this.settings.stiffness,relaxation:this.settings.relaxation})}}(p2),function(a){function b(b,c){c=c||{};var d=this,e={lineWidth:.01,scrollFactor:.1,width:1280,height:720,useDeviceAspect:!1,sleepOpacity:.2};for(var g in c)e[g]=c[g];e.useDeviceAspect&&(e.height=window.innerHeight/window.innerWidth*e.width),this.lineWidth=e.lineWidth,this.scrollFactor=e.scrollFactor,this.sleepOpacity=e.sleepOpacity,this.sprites=[],this.springSprites=[],this.debugPolygons=!1,f.call(this,b);for(var g in e)this.settings[g]=e[g];this.pickPrecision=.1,this.on("drawPointsChange",function(){var a=d.drawShapeGraphics,b=d.drawPoints;a.clear();for(var c=[],e=0;ee?-1>e?(-Math.pow(e,2)-h)/g:e:(Math.pow(e,2)+h)/g;var i=Math.min(Math.max(e/2,-1),1),j=i>=0;"undefined"!=typeof n&&d.zoom(n,o,j,void 0,void 0,i)}var c=(this.w,this.h,this.settings),d=this,e=(this.renderer=PIXI.autoDetectRenderer(c.width,c.height,null,null,!0),this.stage=new PIXI.DisplayObjectContainer),i=this.container=new PIXI.Stage(16777215,!0),j=this.element=this.renderer.view;j.tabIndex=1,j.classList.add(f.elementClass),j.setAttribute("style","width:100%;");var k=this.elementContainer=document.createElement("div");k.classList.add(f.containerClass),k.setAttribute("style","width:100%; height:100%"),k.appendChild(j),document.body.appendChild(k),j.focus(),j.oncontextmenu=function(){return!1},this.container.addChild(e),this.drawShapeGraphics=new PIXI.Graphics,e.addChild(this.drawShapeGraphics),this.contactGraphics=new PIXI.Graphics,e.addChild(this.contactGraphics),this.aabbGraphics=new PIXI.Graphics,e.addChild(this.aabbGraphics),e.scale.x=200,e.scale.y=-200;var l,m,n,o,p,q,r=!1,s=a.vec2.create(),t=a.vec2.create(),u=a.vec2.create(),v=0,w=1,x=1,y=0;i.mousedown=i.touchstart=function(b){if(n=b.global.x,o=b.global.y,b.originalEvent.touches&&(y=b.originalEvent.touches.length),b.originalEvent.touches&&2===b.originalEvent.touches.length){var c=d.container.interactionManager.touchs[0],f=d.container.interactionManager.touchs[1],i=c.getLocalPosition(e);a.vec2.set(u,i.x,i.y),d.stagePositionToPhysics(s,u);var i=f.getLocalPosition(e);a.vec2.set(u,i.x,i.y),d.stagePositionToPhysics(t,u),v=a.vec2.dist(s,t);{e.scale.x,e.scale.y}}else{l=b.global.x,m=b.global.y,p=e.position.x,q=e.position.y,r=!0,d.lastMousePos=b.global;var i=b.getLocalPosition(e);a.vec2.set(g,i.x,i.y),d.stagePositionToPhysics(h,g),d.handleMouseDown(h)}},i.mousemove=i.touchmove=function(b){if(b.originalEvent.touches&&(y!==b.originalEvent.touches.length&&(l=b.global.x,m=b.global.y,p=e.position.x,q=e.position.y),y=b.originalEvent.touches.length),n=b.global.x,o=b.global.y,b.originalEvent.touches&&2===b.originalEvent.touches.length){var c=d.container.interactionManager.touchs[0],i=d.container.interactionManager.touchs[1],j=c.getLocalPosition(e);a.vec2.set(u,j.x,j.y),d.stagePositionToPhysics(s,u);var j=i.getLocalPosition(e);a.vec2.set(u,j.x,j.y),d.stagePositionToPhysics(t,u);var k=a.vec2.dist(s,t);return a.vec2.add(s,s,t),a.vec2.scale(s,s,.5),void d.zoom(.5*(c.global.x+i.global.x),.5*(c.global.y+i.global.y),null,k/v*w,k/v*x)}r&&d.state===f.PANNING&&(e.position.x=b.global.x-l+p,e.position.y=b.global.y-m+q),d.lastMousePos=b.global;var j=b.getLocalPosition(e);a.vec2.set(g,j.x,j.y),d.stagePositionToPhysics(h,g),d.handleMouseMove(h)},i.mouseup=i.touchend=function(b){b.originalEvent.touches&&(y=b.originalEvent.touches.length),r=!1,n=b.global.x,o=b.global.y,d.lastMousePos=b.global;var c=b.getLocalPosition(e);a.vec2.set(g,c.x,c.y),d.stagePositionToPhysics(h,g),d.handleMouseUp(h)},this.element.ontouchmove=function(a){a.preventDefault()},j.addEventListener?(j.addEventListener("mousewheel",b,!1),j.addEventListener("DOMMouseScroll",b,!1)):j.attachEvent("onmousewheel",b),this.centerCamera(0,0)},b.prototype.zoom=function(a,b,c,d,e,f){var g=this.scrollFactor,h=this.stage;"undefined"==typeof d?(c||(g*=-1),g*=Math.abs(f),h.scale.x*=1+g,h.scale.y*=1+g,h.position.x+=g*(h.position.x-a),h.position.y+=g*(h.position.y-b)):(h.scale.x*=d,h.scale.y*=e,h.position.x+=(d-1)*(h.position.x-a),h.position.y+=(e-1)*(h.position.y-b)),h.updateTransform()},b.prototype.centerCamera=function(a,b){this.stage.position.x=this.renderer.width/2-this.stage.scale.x*a,this.stage.position.y=this.renderer.height/2-this.stage.scale.y*b},b.prototype.frame=function(a,b,c,d){var e=this.renderer.width/this.renderer.height;c/d>e?(this.stage.scale.x=this.renderer.width/c,this.stage.scale.y=-this.stage.scale.x):(this.stage.scale.y=-this.renderer.height/d,this.stage.scale.x=-this.stage.scale.y),this.centerCamera(a,b)},b.prototype.drawCircle=function(a,b,c,d,e,f,g,h){g="number"==typeof g?g:1,f="number"==typeof f?f:16777215,a.lineStyle(g,0,1),a.beginFill(f,h?this.sleepOpacity:1),a.drawCircle(b,c,e),a.endFill(),a.moveTo(b,c),a.lineTo(b+e*Math.cos(d),c+e*Math.sin(d))},b.drawSpring=function(a,b,c,d){d="number"==typeof d?d:1,c="undefined"==typeof c?16777215:c,a.lineStyle(d,c,1),10*d>b&&(b=10*d);var e=12,f=b/e;a.moveTo(-b/2,0);for(var g=1;e>g;g++){var h=-b/2+f*g,i=0;1>=g||g>=e-1||(g%2===0?i-=.1*b:i+=.1*b),a.lineTo(h,i)}a.lineTo(b/2,0)},b.drawPlane=function(a,b,c,d,e,f,g,h,i){f="number"==typeof f?f:1,d="undefined"==typeof d?16777215:d,a.lineStyle(f,e,1),a.lineStyle(0,0,0),a.beginFill(d);var j=i;a.moveTo(-j,0),a.lineTo(j,0),a.lineTo(j,-j),a.lineTo(-j,-j),a.endFill(),a.lineStyle(f,e),a.moveTo(-j,0),a.lineTo(j,0)},b.drawLine=function(b,c,d,e,f,g){g="number"==typeof g?g:1,f="undefined"==typeof f?0:f,b.lineStyle(g,f,1);var h=a.vec2.fromValues(-e/2,0),i=a.vec2.fromValues(e/2,0);a.vec2.rotate(h,h,d),a.vec2.rotate(i,i,d),a.vec2.add(h,h,c),a.vec2.add(i,i,c),b.moveTo(h[0],h[1]),b.lineTo(i[0],i[1])},b.prototype.drawCapsule=function(a,b,c,d,e,f,g,h,i,j){i="number"==typeof i?i:1,g="undefined"==typeof g?0:g,a.lineStyle(i,g,1);var k=Math.cos(d),l=Math.sin(d);a.beginFill(h,j?this.sleepOpacity:1),a.drawCircle(-e/2*k+b,-e/2*l+c,f),a.drawCircle(e/2*k+b,e/2*l+c,f),a.endFill(),a.lineStyle(i,g,0),a.beginFill(h,j?this.sleepOpacity:1),a.moveTo(-e/2*k+f*l+b,-e/2*l+f*k+c),a.lineTo(e/2*k+f*l+b,e/2*l+f*k+c),a.lineTo(e/2*k-f*l+b,e/2*l-f*k+c),a.lineTo(-e/2*k-f*l+b,-e/2*l-f*k+c),a.endFill(),a.lineStyle(i,g,1),a.moveTo(-e/2*k+f*l+b,-e/2*l+f*k+c),a.lineTo(e/2*k+f*l+b,e/2*l+f*k+c),a.moveTo(-e/2*k-f*l+b,-e/2*l-f*k+c),a.lineTo(e/2*k-f*l+b,e/2*l-f*k+c)},b.prototype.drawRectangle=function(a,b,c,d,e,f,g,h,i,j){i="number"==typeof i?i:1,g="number"==typeof g?g:16777215,h="number"==typeof h?h:16777215,a.lineStyle(i),a.beginFill(h,j?this.sleepOpacity:1),a.drawRect(b-e/2,c-f/2,e,f)},b.prototype.drawConvex=function(a,b,c,d,e,f,g,h,i){if(f="number"==typeof f?f:1,d="undefined"==typeof d?0:d,g){for(var j=[16711680,65280,255],k=0;k!==b.length+1;k++){var l=b[k%b.length],m=b[(k+1)%b.length],n=l[0],o=l[1],p=m[0],q=m[1];a.lineStyle(f,j[k%j.length],1),a.moveTo(n,o),a.lineTo(p,q),a.drawCircle(n,o,2*f)}a.lineStyle(f,0,1),a.drawCircle(h[0],h[1],2*f)}else{a.lineStyle(f,d,1),a.beginFill(e,i?this.sleepOpacity:1);for(var k=0;k!==b.length;k++){var r=b[k],s=r[0],t=r[1];0===k?a.moveTo(s,t):a.lineTo(s,t)}a.endFill(),b.length>2&&(a.moveTo(b[b.length-1][0],b[b.length-1][1]),a.lineTo(b[0][0],b[0][1]))}},b.prototype.drawPath=function(a,b,c,d,e,f){e="number"==typeof e?e:1,c="undefined"==typeof c?0:c,a.lineStyle(e,c,1),"number"==typeof d&&a.beginFill(d,f?this.sleepOpacity:1);for(var g=null,h=null,i=0;i2&&"number"==typeof d&&(a.moveTo(b[b.length-1][0],b[b.length-1][1]),a.lineTo(b[0][0],b[0][1]))},b.prototype.updateSpriteTransform=function(a,b){this.useInterpolatedPositions?(a.position.x=b.interpolatedPosition[0],a.position.y=b.interpolatedPosition[1],a.rotation=b.interpolatedAngle):(a.position.x=b.position[0],a.position.y=b.position[1],a.rotation=b.angle)};var i=a.vec2.fromValues(1,0),j=a.vec2.fromValues(0,0),k=a.vec2.fromValues(0,0),l=a.vec2.fromValues(0,0);b.prototype.render=function(){for(var b=(this.renderer.width,this.renderer.height,this.springSprites),c=(this.sprites,0);c!==this.bodies.length;c++)this.updateSpriteTransform(this.sprites[c],this.bodies[c]);for(var c=0;c!==this.bodies.length;c++){var d=this.bodies[c].sleepState===a.Body.SLEEPING,e=this.sprites[c],f=this.bodies[c];e.drawnSleeping!==d&&(e.clear(),this.drawRenderable(f,e,e.drawnColor,e.drawnLineColor))}for(var c=0;c!==this.springs.length;c++){var g=this.springs[c],e=b[c],h=g.bodyA,m=g.bodyB;if(this.useInterpolatedPositions?(a.vec2.toGlobalFrame(k,g.localAnchorA,h.interpolatedPosition,h.interpolatedAngle),a.vec2.toGlobalFrame(l,g.localAnchorB,m.interpolatedPosition,m.interpolatedAngle)):(g.getWorldAnchorA(k),g.getWorldAnchorB(l)),e.scale.y=1,k[1] (http://steffe.se)", "keywords": [