├── script
├── engine
│ ├── title.js
│ ├── engine.js
│ ├── game.js
│ ├── scene.js
│ ├── assets.js
│ ├── application.js
│ ├── collection.js
│ └── loader.js
├── main.js
├── game.js
├── ant.js
└── vendor
│ ├── underscore-min.js
│ ├── eveline.js
│ └── canvasquery.js
└── index.html
/script/engine/title.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/script/engine/engine.js:
--------------------------------------------------------------------------------
1 | var ENGINE = {};
--------------------------------------------------------------------------------
/script/engine/game.js:
--------------------------------------------------------------------------------
1 | app.game = new ENGINE.Scene({
2 |
3 | onstep: function(delta) {
4 |
5 | },
6 |
7 | onrender: function(delta) {
8 |
9 | app.layer.clear("#111");
10 | }
11 | });
12 |
--------------------------------------------------------------------------------
/script/main.js:
--------------------------------------------------------------------------------
1 | /* global ENGINE */
2 |
3 | var app;
4 | window.app = app = new ENGINE.Application({
5 | width: window.innerWidth,
6 | height: window.innerHeight,
7 |
8 | oncreate: function() {
9 | console.log("oncreate");
10 | this.loader.foo(500);
11 | },
12 |
13 | onready: function() {
14 | this.selectScene(this.game);
15 | }
16 | });
--------------------------------------------------------------------------------
/script/engine/scene.js:
--------------------------------------------------------------------------------
1 | /* global ENGINE, _ */
2 | ENGINE.Scene = function(args) {
3 |
4 | _.extend(this, args);
5 |
6 | if (this.oncreate) {
7 | this.oncreate();
8 | }
9 | };
10 |
11 | ENGINE.Scene.prototype = {
12 |
13 | onenter: function() { },
14 | onleave: function() { },
15 | onrender: function() { },
16 | onstep: function() { }
17 |
18 | };
19 |
--------------------------------------------------------------------------------
/script/game.js:
--------------------------------------------------------------------------------
1 | /* global ENGINE,app */
2 |
3 | app.game = new ENGINE.Scene({
4 | oncreate: function() {
5 | this.entities = new ENGINE.Collection(this);
6 |
7 | for (var i = 0; i < 100; i++) {
8 | this.spawnAnt();
9 | }
10 | },
11 | spawnAnt: function() {
12 | this.entities.add(ENGINE.Ant, {
13 | x: app.width / 2,
14 | y: app.height / 2
15 | });
16 | },
17 | onstep: function(delta) {
18 | this.entities.step(delta);
19 |
20 | this.entities.call("step", delta);
21 | },
22 | onrender: function(delta) {
23 | app.layer.clear("#000");
24 |
25 | this.entities.call("render", delta);
26 | }
27 | });
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Whole lotta ants
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/script/engine/assets.js:
--------------------------------------------------------------------------------
1 | /* global ENGINE */
2 |
3 | ENGINE.Assets = function(loader, paths) {
4 | this.loader = loader;
5 |
6 | this.paths = paths || {
7 | images: "assets/images/"
8 | };
9 |
10 | this.data = {
11 | images: []
12 | };
13 | };
14 |
15 | ENGINE.Assets.prototype = {
16 | /* get image by key - key is created by removing extension from filename for example
17 | units/tank.png
18 | becomes units/tank
19 | */
20 | image: function(key) {
21 | return this.data.images[key];
22 | },
23 |
24 | /* Add multiple images */
25 | addImages: function(filenames) {
26 | for(var i = 0; i < filenames.length; i++) {
27 | this.addImage(filenames[i]);
28 | }
29 | },
30 |
31 | /* Add single image */
32 | addImage: function(filename) {
33 | var image = new Image();
34 |
35 | // Pass image to loader
36 | this.loader.image(image);
37 |
38 | // Rip off extension
39 | var key = filename.match(/(.*)\..*/)[1];
40 |
41 | // Add image to assets
42 | this.data.images[key] = image;
43 |
44 | // Search for image in defined path
45 | image.src = this.paths.images + filename;
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/script/engine/application.js:
--------------------------------------------------------------------------------
1 | /* global ENGINE,cq,eveline, _ */
2 |
3 | ENGINE.Application = function(args) {
4 | var app = this;
5 |
6 | _.extend(this, args);
7 |
8 | /* create a fullscreen canvas wrapper - we will draw on it */
9 | this.layer = cq();
10 |
11 | this.layer.appendTo("body");
12 |
13 | eveline(this);
14 |
15 | this.loader = new ENGINE.Loader();
16 | this.assets = new ENGINE.Assets(this.loader);
17 |
18 | this.oncreate();
19 |
20 | this.loader.ready(function() {
21 | app.onready();
22 | });
23 | };
24 |
25 | ENGINE.Application.prototype = {
26 | /* calls the method in current scene with given arguments
27 | for example
28 | this.dispatch("onmousemove", 32, 64);
29 | will trigger onmousemove method in current scene (if it has one)
30 | */
31 | dispatch: function(method) {
32 |
33 | if (this.scene && this.scene[arguments[0]]) {
34 | this.scene[arguments[0]].apply(this.scene, Array.prototype.slice.call(arguments, 1));
35 | }
36 | },
37 |
38 | selectScene: function(scene) {
39 | this.dispatch("onleave");
40 |
41 | this.scene = scene;
42 |
43 | this.dispatch("onenter");
44 | },
45 |
46 | /* game logic step (setInterval) */
47 | onstep: function(delta) {
48 | this.dispatch("onstep", delta);
49 | },
50 |
51 | /* rendering loop (requestAnimationFrame) */
52 | onrender: function(delta) {
53 | this.dispatch("onrender", delta);
54 | },
55 |
56 | /* the key gets translated to a string like shift, escape, a, b, c */
57 | onkeydown: function(key) {
58 | this.dispatch("onkeydown", key);
59 | }
60 |
61 | // TODO: fill out rest
62 | };
63 |
--------------------------------------------------------------------------------
/script/ant.js:
--------------------------------------------------------------------------------
1 | /* global ENGINE,app,_ */
2 |
3 | ENGINE.Ant = function(args) {
4 | /* extend the instancewith defaults and user data */
5 | _.extend(this, {
6 | // Direction ant is facing in radians
7 | direction: 0,
8 | // px's per sec
9 | speed: 6,
10 | // AI cooldown so movement is natural
11 | brainDelta: 0
12 | }, args);
13 | };
14 |
15 | ENGINE.Ant.prototype = {
16 | step: function(delta) {
17 | // Decrease brain cooldown
18 | this.brainDelta -= delta;
19 |
20 | //if cooldown goes below zero do some thinking
21 | if (this.brainDelta < 0) {
22 | this.direction = Math.random() * Math.PI * 2;
23 | this.brainDelta = Math.random() * 2000;
24 | }
25 |
26 | // increase speed with age
27 | this.speed += 8 * delta / 1000;
28 |
29 | //move ant
30 | this.x += Math.cos(this.direction) * this.speed * delta / 1000;
31 | this.y += Math.sin(this.direction) * this.speed * delta / 1000;
32 |
33 | //if off screen kill it
34 | if (this.x < 0 || this.y < 0
35 | || this.x > app.width || this.y > app.height) {
36 |
37 | this.remove();
38 | app.game.spawnAnt();
39 |
40 | }
41 | },
42 |
43 | render: function(delta) {
44 | app.layer
45 | .fillStyle("#ff0000")
46 | .fillRect(
47 | 5 * (this.x / 5 | 0),
48 | 5 * (this.y / 5 | 0),
49 | 2, 2
50 | );
51 |
52 | },
53 | remove: function() {
54 | // Mark for removal
55 | this._remove = true;
56 |
57 | // Tell collection it is dirty
58 | this.collection.dirty = true;
59 | }
60 | };
--------------------------------------------------------------------------------
/script/engine/collection.js:
--------------------------------------------------------------------------------
1 | /* global ENGINE, _ */
2 |
3 | ENGINE.Collection = function(parent) {
4 | // Object that manages the collection
5 | this.parent = parent;
6 |
7 | // Unique id for every entitiy
8 | this.index = 0;
9 |
10 | // If something inside dies, remove it
11 | this.dirty = false;
12 | };
13 |
14 | ENGINE.Collection.prototype = new Array;
15 |
16 | _.extend(ENGINE.Collection.prototype, {
17 |
18 | /* creates new object instance with given args and pushes it to the collection
19 | example:
20 | var entities = new ENGINE.Collection;
21 | entities.add(ENGINE.Soldier, { x: 32, y: 64});
22 | */
23 | add: function(constructor, args) {
24 | var entity = new constructor(_.extend({
25 | collection: this,
26 | index: this.index++
27 | }, args));
28 |
29 | // Use native Array prototype to push
30 | this.push(entity);
31 |
32 | return entity;
33 | },
34 |
35 | /* Remove dead bodies so they don't drain resources */
36 | clean: function() {
37 | for (var i = 0, len = this.length; i < len; i++) {
38 | if (this[i]._remove) {
39 | this.splice(i--, 1);
40 | len--;
41 | }
42 | }
43 | },
44 |
45 | /* Needs to be called in order to keep track of collections garbage */
46 | step: function(delta) {
47 | if (this.dirty) {
48 | // Collection has been cleaned
49 | this.dirty = false;
50 |
51 | this.clean();
52 |
53 | // Sort by z-index
54 | this.sort(function(a,b) {
55 | return (a.zIndex | 0) - (b.zIndex | 0);
56 | });
57 | }
58 | },
59 |
60 | /* Call some method on every entity */
61 | call: function(method) {
62 | var args = Array.prototype.slice.call(arguments, 1);
63 |
64 | for (var i = 0, len = this.length; i < len; i++) {
65 | if(this[i][method]) {
66 | this[i][method].apply(this[i], args);
67 | }
68 | }
69 | },
70 |
71 | /* Call some method on every entity **with args as array** */
72 | apply: function(method, args) {
73 | for (var i = 0, len = this.length; i < len; i++) {
74 | if(this[i][method]) {
75 | this[i][method].apply(this[i], args);
76 | }
77 | }
78 | }
79 | });
--------------------------------------------------------------------------------
/script/engine/loader.js:
--------------------------------------------------------------------------------
1 | /* global ENGINE */
2 |
3 | ENGINE.Loader = function() {
4 |
5 | /* all items to load */
6 | this.total = 0;
7 |
8 | /* items in queue */
9 | this.count = 0;
10 |
11 | /* convenient progress from 0 to 1 */
12 | this.progress = 0;
13 |
14 | /* all callbacks that should be fired when the loading is over */
15 | this.callbacks = [];
16 |
17 | this.loading = false;
18 | };
19 |
20 | ENGINE.Loader.prototype = {
21 | add: function() {
22 | this.loading = true;
23 | this.count++;
24 | this.total++;
25 | },
26 |
27 | image: function(image) {
28 | var loader = this;
29 |
30 | /* Listen to when the image is ready */
31 | image.addEventListener("load", function() {
32 | loader.onItemReady();
33 | });
34 |
35 | image.addEventListener("error", function() {
36 | loader.onItemError(this.src);
37 | });
38 |
39 | this.add();
40 | },
41 |
42 | /* sometimes it is convinient to simulate loading by using timeout
43 | usage:
44 | loader.foo(1000);
45 | this will simulate that some asset is being loaded for 1 second
46 | */
47 | foo: function(duration) {
48 | var loader = this;
49 |
50 | /* simulate loading using timeout */
51 | setTimeout(function() {
52 | loader.onItemReady();
53 | }, duration);
54 |
55 | /* increase items count */
56 | this.add();
57 | },
58 |
59 | /* Ready callback caller */
60 | ready: function(callback) {
61 | if (!this.loading) {
62 | callback();
63 | } else {
64 | this.callbacks.push(callback);
65 | }
66 | },
67 |
68 | /* Called when one item finished loading */
69 | onItemReady: function() {
70 | /* Reduce queue count */
71 | this.count--;
72 |
73 | /* Update progress - Can be used for progress bars */
74 | this.progress = (this.total - this.count) / this.total;
75 |
76 | if (this.count <= 0) {
77 | this.loading = false;
78 |
79 | // Run all callbacks
80 | for (var i = 0, len = this.callbacks.length; i < len; i++) {
81 | this.callbacks[i]();
82 | }
83 |
84 | // Cleanup callbacks
85 | this.callbacks = [];
86 | this.total = 0;
87 | this.count = 0;
88 | }
89 | },
90 |
91 | /* Called on error */
92 | onItemError: function(source) {
93 | console.log("unable to load ", source);
94 | }
95 | };
96 |
--------------------------------------------------------------------------------
/script/vendor/underscore-min.js:
--------------------------------------------------------------------------------
1 | (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.4";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2),e=w.isFunction(t);return w.map(n,function(n){return(e?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t,r){return w.isEmpty(t)?r?null:[]:w[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.findWhere=function(n,t){return w.where(n,t,!0)},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var k=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=k(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i},w.bind=function(n,t){if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));var r=o.call(arguments,2);return function(){return n.apply(t,r.concat(o.call(arguments)))}},w.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var I=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=I(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&I(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return I(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var M={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};M.unescape=w.invert(M.escape);var S={escape:RegExp("["+w.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(M.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(S[n],function(t){return M[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||T).source,(r.interpolate||T).source,(r.evaluate||T).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(B,function(n){return"\\"+q[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var D=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this);
--------------------------------------------------------------------------------
/script/vendor/eveline.js:
--------------------------------------------------------------------------------
1 | /* rezoner */
2 |
3 | (function(window, undefined) {
4 |
5 | var MOBILE = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
6 |
7 |
8 | window.requestAnimationFrame = (function() {
9 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
10 | window.setTimeout(callback, 1000 / 60);
11 | };
12 | })();
13 |
14 | eveline = function(events, context, element) {
15 |
16 | if (element === undefined) {
17 | element = window;
18 | } else if (typeof element === "string") {
19 | element = document.querySelector(element);
20 | }
21 |
22 | var tempContext = context || events;
23 |
24 | for (var name in events) {
25 | if (typeof eveline[name] === "function") eveline[name](element, events[name], tempContext);
26 | }
27 |
28 | /* apply scaling */
29 |
30 | tempContext.scale = 1;
31 | tempContext.iscale = 1;
32 |
33 | tempContext.preventMultitouch = tempContext.preventMultitouch || false;
34 |
35 | if (tempContext.autoscale) {
36 |
37 | window.addEventListener("resize", function() {
38 | eveline.fitToScreen(element, tempContext);
39 | });
40 |
41 | eveline.fitToScreen(element, tempContext);
42 | }
43 |
44 | requestAnimationFrame(eveline.step);
45 |
46 | }
47 |
48 | eveline.extend = function() {
49 | for (var i = 1; i < arguments.length; i++) {
50 | for (var j in arguments[i]) {
51 | arguments[0][j] = arguments[i][j];
52 | }
53 | }
54 |
55 | return arguments[0];
56 | };
57 |
58 | eveline.gamepadButtons = {
59 | 0: "1",
60 | 1: "2",
61 | 2: "3",
62 | 3: "4",
63 | 4: "l1",
64 | 5: "r1",
65 | 6: "l2",
66 | 7: "r2",
67 | 8: "select",
68 | 9: "start",
69 |
70 | 12: "up",
71 | 13: "down",
72 | 14: "left",
73 | 15: "right"
74 | };
75 |
76 | eveline.keycodes = {
77 | 37: "left",
78 | 38: "up",
79 | 39: "right",
80 | 40: "down",
81 | 45: "insert",
82 | 46: "delete",
83 | 8: "backspace",
84 | 9: "tab",
85 | 13: "enter",
86 | 16: "shift",
87 | 17: "ctrl",
88 | 18: "alt",
89 | 19: "pause",
90 | 20: "capslock",
91 | 27: "escape",
92 | 32: "space",
93 | 33: "pageup",
94 | 34: "pagedown",
95 | 35: "end",
96 | 112: "f1",
97 | 113: "f2",
98 | 114: "f3",
99 | 115: "f4",
100 | 116: "f5",
101 | 117: "f6",
102 | 118: "f7",
103 | 119: "f8",
104 | 120: "f9",
105 | 121: "f10",
106 | 122: "f11",
107 | 123: "f12",
108 | 144: "numlock",
109 | 145: "scrolllock",
110 | 186: "semicolon",
111 | 187: "equal",
112 | 188: "comma",
113 | 189: "dash",
114 | 190: "period",
115 | 191: "slash",
116 | 192: "graveaccent",
117 | 219: "openbracket",
118 | 220: "backslash",
119 | 221: "closebraket",
120 | 222: "singlequote"
121 | };
122 |
123 | eveline.extend(eveline, {
124 |
125 | gamepadState: [],
126 |
127 | eventsListeners: {},
128 |
129 | addEventListener: function(event, callback) {
130 | if (!this.eventsListeners[event]) this.eventsListeners[event] = [];
131 | this.eventsListeners[event].push(callback);
132 | },
133 |
134 | trigger: function(event) {
135 | var args = [];
136 |
137 | for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
138 |
139 | var listeners = this.eventsListeners[event];
140 |
141 | if (!listeners) return;
142 |
143 | for (var i = 0; i < listeners.length; i++) {
144 | listeners[i].apply(this, args);
145 | }
146 | },
147 |
148 | distance: function(x1, y1, x2, y2) {
149 | return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y2));
150 | },
151 |
152 | step: function() {
153 |
154 | if (navigator.webkitGetGamepads) {
155 |
156 | var gamepads = navigator.webkitGetGamepads();
157 |
158 |
159 | for (var i = 0; i < gamepads.length; i++) {
160 | var current = gamepads[i];
161 |
162 | if (!current) continue;
163 |
164 | if (!eveline.gamepadState[i]) eveline.gamepadState[i] = current;
165 |
166 | var previous = eveline.gamepadState[i];
167 |
168 | var buttons = [];
169 |
170 | if (previous.axes[0] !== current.axes[0] || previous.axes[1] !== current.axes[1]) {
171 | eveline.trigger("gamepadmove", current.axes[0], current.axes[1], i);
172 | }
173 |
174 | buttons[0] = current.axes[1] < 0 ? 1 : 0;
175 | buttons[1] = current.axes[1] > 0 ? 1 : 0;
176 | buttons[2] = current.axes[0] < 0 ? 1 : 0;
177 | buttons[3] = current.axes[0] > 0 ? 1 : 0;
178 |
179 | buttons = current.buttons.concat(buttons);
180 |
181 | for (var j = 0; j < buttons.length; j++) {
182 | if (previous.buttons[j] === 0 && buttons[j] === 1) eveline.trigger("gamepaddown", eveline.gamepadButtons[j], i);
183 | if (previous.buttons[j] === 1 && buttons[j] === 0) eveline.trigger("gamepadup", eveline.gamepadButtons[j], i);
184 | }
185 |
186 | eveline.gamepadState[i] = {
187 | buttons: buttons,
188 | axes: current.axes
189 | };
190 | }
191 |
192 | }
193 |
194 | requestAnimationFrame(eveline.step);
195 | },
196 |
197 | mousePosition: function(event, element) {
198 | var totalOffsetX = 0,
199 | totalOffsetY = 0,
200 | coordX = 0,
201 | coordY = 0,
202 | currentElement = element || event.target || event.srcElement,
203 | mouseX = 0,
204 | mouseY = 0;
205 |
206 | // Traversing the parents to get the total offset
207 | do {
208 | totalOffsetX += currentElement.offsetLeft;
209 | totalOffsetY += currentElement.offsetTop;
210 | }
211 | while ((currentElement = currentElement.offsetParent));
212 | // Set the event to first touch if using touch-input
213 | if (event.changedTouches && event.changedTouches[0] !== undefined) {
214 | event = event.changedTouches[0];
215 | }
216 | // Use pageX to get the mouse coordinates
217 | if (event.pageX || event.pageY) {
218 | mouseX = event.pageX;
219 | mouseY = event.pageY;
220 | }
221 | // IE8 and below doesn't support event.pageX
222 | else if (event.clientX || event.clientY) {
223 | mouseX = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
224 | mouseY = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
225 | }
226 | // Subtract the offset from the mouse coordinates
227 | coordX = mouseX - totalOffsetX;
228 | coordY = mouseY - totalOffsetY;
229 |
230 | return {
231 | x: coordX,
232 | y: coordY
233 | };
234 | },
235 |
236 | onstep: function(element, callback, context) {
237 | var lastTick = Date.now();
238 |
239 | this.timer = setInterval(function() {
240 | var delta = Date.now() - lastTick;
241 | lastTick = Date.now();
242 | callback.call(context, delta, lastTick);
243 | }, 25);
244 | },
245 |
246 | onrender: function(element, callback, context) {
247 | var lastTick = Date.now();
248 |
249 | function step() {
250 | var delta = Date.now() - lastTick;
251 | lastTick = Date.now();
252 | requestAnimationFrame(step)
253 | callback.call(context, delta, lastTick);
254 | };
255 |
256 | requestAnimationFrame(step);
257 | },
258 |
259 | onmousemove: function(element, callback, context) {
260 | element.addEventListener("mousemove", function(e) {
261 | var pos = eveline.mousePosition(e, element);
262 | callback.call(context, pos.x * context.iscale | 0, pos.y * context.iscale | 0);
263 | });
264 |
265 | return this;
266 | },
267 |
268 | onmousedown: function(element, callback, context) {
269 |
270 | element.addEventListener("mousedown", function(e) {
271 | var pos = eveline.mousePosition(e, element);
272 | context.mouseStart = {
273 | x: pos.x * context.iscale | 0,
274 | y: pos.y * context.iscale | 0
275 | };
276 | callback.call(context, pos.x * context.iscale | 0, pos.y * context.iscale | 0, e.button);
277 | });
278 |
279 | return this;
280 | },
281 |
282 | onmouseup: function(element, callback, context) {
283 |
284 | element.addEventListener("mouseup", function(e) {
285 | var pos = eveline.mousePosition(e, element);
286 | callback.call(context, pos.x * context.iscale | 0, pos.y * context.iscale | 0, e.button);
287 | });
288 |
289 | return this;
290 | },
291 |
292 | ontouchmove: function(element, callback, context) {
293 | element.addEventListener("touchmove", function(e) {
294 | if (context.preventMultitouch && e.targetTouches.length > 1) return;
295 | e.preventDefault();
296 | var pos = eveline.mousePosition(e, element);
297 | callback.call(context, pos.x * context.iscale | 0, pos.y * context.iscale | 0);
298 | });
299 | },
300 |
301 | ontouchstart: function(element, callback, context) {
302 | element.addEventListener("touchstart", function(e) {
303 | if (context.preventMultitouch && e.targetTouches.length > 0) return;
304 |
305 | var pos = eveline.mousePosition(e, element);
306 | context.mouseStart = {
307 | x: pos.x * context.iscale | 0,
308 | y: pos.y * context.iscale | 0
309 | };
310 | callback.call(context, pos.x * context.iscale | 0, pos.y * context.iscale | 0, e.button);
311 | });
312 | },
313 |
314 | ontouchend: function(element, callback, context) {
315 | element.addEventListener("touchend", function(e) {
316 | if (context.preventMultitouch && e.targetTouches.length > 0) return;
317 | var pos = eveline.mousePosition(e, element);
318 | callback.call(context, pos.x * context.iscale | 0, pos.y * context.iscale | 0, e.button);
319 | });
320 | },
321 |
322 | onclick: function(element, callback, context) {
323 | this.onmouseup(element, function(x, y) {
324 | if (eveline.distance(x, y, context.mouseStart.x, context.mouseStart.y) < 5) {
325 | callback.call(context, x * context.iscale | 0, y * context.iscale | 0);
326 | }
327 | }, context);
328 | },
329 |
330 | onkeydown: function(element, callback, context) {
331 |
332 | document.addEventListener("keydown", function(e) {
333 | if (e.which >= 48 && e.which <= 90) var keyName = String.fromCharCode(e.which).toLowerCase();
334 | else var keyName = eveline.keycodes[e.which];
335 | callback.call(context, keyName);
336 | });
337 | return this;
338 | },
339 |
340 | onkeyup: function(element, callback, context) {
341 |
342 | document.addEventListener("keyup", function(e) {
343 | if (e.which >= 48 && e.which <= 90) var keyName = String.fromCharCode(e.which).toLowerCase();
344 | else var keyName = eveline.keycodes[e.which];
345 | callback.call(context, keyName);
346 | });
347 | return this;
348 | },
349 |
350 | ongamepaddown: function(element, callback, context) {
351 |
352 | eveline.addEventListener("gamepaddown", function(button, gamepad) {
353 | callback.call(context, button, gamepad);
354 | });
355 |
356 | return this;
357 | },
358 |
359 | ongamepadup: function(element, callback, context) {
360 |
361 | eveline.addEventListener("gamepadup", function(button, gamepad) {
362 | callback.call(context, button, gamepad);
363 | });
364 |
365 | return this;
366 | },
367 |
368 | ongamepadmove: function(element, callback, context) {
369 |
370 | eveline.addEventListener("gamepadmove", function(x, y, gamepad) {
371 | callback.call(context, x, y, gamepad);
372 | });
373 |
374 | return this;
375 | },
376 |
377 |
378 | onresize: function(element, callback, context) {
379 | window.addEventListener("resize", function() {
380 | callback.call(context, window.innerWidth, window.innerHeight);
381 | });
382 |
383 | // callback.call(context, window.innerWidth, window.innerHeight);
384 |
385 | return this;
386 | },
387 |
388 | ondropimage: function(element, callback, context) {
389 |
390 | document.addEventListener('drop', function(e) {
391 | e.stopPropagation();
392 | e.preventDefault();
393 |
394 | var file = e.dataTransfer.files[0];
395 |
396 | if (!(/image/i).test(file.type)) return false;
397 | var reader = new FileReader();
398 |
399 | reader.onload = function(e) {
400 | var image = new Image;
401 |
402 | image.onload = function() {
403 | callback.call(context, this);
404 | };
405 |
406 | image.src = e.target.result;
407 | };
408 |
409 | reader.readAsDataURL(file);
410 |
411 | });
412 |
413 | document.addEventListener("dragover", function(e) {
414 | e.preventDefault();
415 | });
416 |
417 | return this;
418 | },
419 |
420 | fitToScreen: function(element, context) {
421 | var scale = window.innerWidth / context.width;
422 |
423 | if (context.height * scale > window.innerHeight) {
424 | scale = window.innerHeight / context.height;
425 | }
426 |
427 | // scale = Math.min(1, scale);
428 |
429 | context.scale = scale;
430 | context.iscale = 1 / scale;
431 |
432 | var scaleCSSText = "scale(" + scale + "," + scale + ")";
433 |
434 | eveline.extend(element.style, {
435 | "-webkit-transform-origin": "top left",
436 | "-webkit-transform": scaleCSSText,
437 | "-moz-transform-origin": "top left",
438 | "-moz-transform": scaleCSSText,
439 | "-ms-transform-origin": "top left",
440 | "-ms-transform": scaleCSSText,
441 | "transformOrigin": "top left",
442 | "transform": scaleCSSText,
443 | "left": ((window.innerWidth / 2 - (context.width * context.scale) / 2) | 0) + "px",
444 | "top": ((window.innerHeight / 2 - (context.height * context.scale) / 2) | 0) + "px"
445 | });
446 | },
447 |
448 | });
449 |
450 | if (typeof define === "function" && define.amd) {
451 | define([], function() {
452 | return eveline;
453 | });
454 | }
455 |
456 | })(window);
--------------------------------------------------------------------------------
/script/vendor/canvasquery.js:
--------------------------------------------------------------------------------
1 | /*
2 | Canvas Query 0.9.0
3 | http://canvasquery.com
4 | (c) 2012-2013 http://rezoner.net
5 | Canvas Query may be freely distributed under the MIT license.
6 | */
7 |
8 | (function(window, undefined) {
9 |
10 | var MOBILE = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
11 |
12 |
13 | window.requestAnimationFrame = (function() {
14 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
15 | window.setTimeout(callback, 1000 / 60);
16 | };
17 | })();
18 |
19 |
20 | var $ = function(selector) {
21 | if (arguments.length === 0) {
22 | var canvas = $.createCanvas(window.innerWidth, window.innerHeight);
23 | window.addEventListener("resize", function() {
24 | // canvas.width = window.innerWidth;
25 | // canvas.height = window.innerHeight;
26 | });
27 | } else if (typeof selector === "string") {
28 | var canvas = document.querySelector(selector);
29 | } else if (typeof selector === "number") {
30 | var canvas = $.createCanvas(arguments[0], arguments[1]);
31 | } else if (selector instanceof Image || selector instanceof HTMLImageElement) {
32 | var canvas = $.createCanvas(selector);
33 | } else if (selector instanceof $.Wrapper) {
34 | return selector;
35 | } else {
36 | var canvas = selector;
37 | }
38 |
39 | return new $.Wrapper(canvas);
40 | }
41 |
42 | $.extend = function() {
43 | for (var i = 1; i < arguments.length; i++) {
44 | for (var j in arguments[i]) {
45 | arguments[0][j] = arguments[i][j];
46 | }
47 | }
48 |
49 | return arguments[0];
50 | };
51 |
52 | $.augment = function() {
53 | for (var i = 1; i < arguments.length; i++) {
54 | _.extend(arguments[0], arguments[i]);
55 | arguments[i](arguments[0]);
56 | }
57 | };
58 |
59 | $.distance = function(x1, y1, x2, y2) {
60 | if (arguments.length > 2) {
61 | var dx = x1 - x2;
62 | var dy = y1 - y2;
63 |
64 | return Math.sqrt(dx * dx + dy * dy);
65 | } else {
66 | return Math.abs(x1 - y1);
67 | }
68 | };
69 |
70 | $.extend($, {
71 |
72 | keycodes: {
73 | 37: "left",
74 | 38: "up",
75 | 39: "right",
76 | 40: "down",
77 | 45: "insert",
78 | 46: "delete",
79 | 8: "backspace",
80 | 9: "tab",
81 | 13: "enter",
82 | 16: "shift",
83 | 17: "ctrl",
84 | 18: "alt",
85 | 19: "pause",
86 | 20: "capslock",
87 | 27: "escape",
88 | 32: "space",
89 | 33: "pageup",
90 | 34: "pagedown",
91 | 35: "end",
92 | 112: "f1",
93 | 113: "f2",
94 | 114: "f3",
95 | 115: "f4",
96 | 116: "f5",
97 | 117: "f6",
98 | 118: "f7",
99 | 119: "f8",
100 | 120: "f9",
101 | 121: "f10",
102 | 122: "f11",
103 | 123: "f12",
104 | 144: "numlock",
105 | 145: "scrolllock",
106 | 186: "semicolon",
107 | 187: "equal",
108 | 188: "comma",
109 | 189: "dash",
110 | 190: "period",
111 | 191: "slash",
112 | 192: "graveaccent",
113 | 219: "openbracket",
114 | 220: "backslash",
115 | 221: "closebraket",
116 | 222: "singlequote"
117 | },
118 |
119 | cleanArray: function(array, property) {
120 |
121 | var lastArgument = arguments[arguments.length - 1];
122 | var isLastArgumentFunction = typeof lastArgument === "function";
123 |
124 | for (var i = 0, len = array.length; i < len; i++) {
125 | if (array[i] === null || (property && array[i][property])) {
126 | if (isLastArgumentFunction) {
127 | lastArgument(array[i]);
128 | }
129 | array.splice(i--, 1);
130 | len--;
131 | }
132 | }
133 | },
134 |
135 | specialBlendFunctions: ["color", "value", "hue", "saturation"],
136 |
137 | blendFunctions: {
138 | normal: function(a, b) {
139 | return b;
140 | },
141 |
142 | overlay: function(a, b) {
143 | a /= 255;
144 | b /= 255;
145 | var result = 0;
146 |
147 | if (a < 0.5) result = 2 * a * b;
148 | else result = 1 - 2 * (1 - a) * (1 - b);
149 |
150 | return Math.min(255, Math.max(0, result * 255 | 0));
151 | },
152 |
153 | hardLight: function(a, b) {
154 | return $.blendFunctions.overlay(b, a);
155 | },
156 |
157 | softLight: function(a, b) {
158 | a /= 255;
159 | b /= 255;
160 |
161 | var v = (1 - 2 * b) * (a * a) + 2 * b * a;
162 | return $.limitValue(v * 255, 0, 255);
163 | },
164 |
165 | dodge: function(a, b) {
166 | return Math.min(256 * a / (255 - b + 1), 255);
167 | },
168 |
169 | burn: function(a, b) {
170 | return 255 - Math.min(256 * (255 - a) / (b + 1), 255);
171 | },
172 |
173 | multiply: function(a, b) {
174 | return b * a / 255;
175 | },
176 |
177 | divide: function(a, b) {
178 | return Math.min(256 * a / (b + 1), 255);
179 | },
180 |
181 | screen: function(a, b) {
182 | return 255 - (255 - b) * (255 - a) / 255;
183 | },
184 |
185 | grainExtract: function(a, b) {
186 | return $.limitValue(a - b + 128, 0, 255);
187 | },
188 |
189 | grainMerge: function(a, b) {
190 | return $.limitValue(a + b - 128, 0, 255);
191 | },
192 |
193 | difference: function(a, b) {
194 | return Math.abs(a - b);
195 | },
196 |
197 | addition: function(a, b) {
198 | return Math.min(a + b, 255);
199 | },
200 |
201 | substract: function(a, b) {
202 | return Math.max(a - b, 0);
203 | },
204 |
205 | darkenOnly: function(a, b) {
206 | return Math.min(a, b);
207 | },
208 |
209 | lightenOnly: function(a, b) {
210 | return Math.max(a, b);
211 | },
212 |
213 | color: function(a, b) {
214 | var aHSL = $.rgbToHsl(a);
215 | var bHSL = $.rgbToHsl(b);
216 |
217 | return $.hslToRgb(bHSL[0], bHSL[1], aHSL[2]);
218 | },
219 |
220 | hue: function(a, b) {
221 | var aHSV = $.rgbToHsv(a);
222 | var bHSV = $.rgbToHsv(b);
223 |
224 | if (!bHSV[1]) return $.hsvToRgb(aHSV[0], aHSV[1], aHSV[2]);
225 | else return $.hsvToRgb(bHSV[0], aHSV[1], aHSV[2]);
226 | },
227 |
228 | value: function(a, b) {
229 | var aHSV = $.rgbToHsv(a);
230 | var bHSV = $.rgbToHsv(b);
231 |
232 | return $.hsvToRgb(aHSV[0], aHSV[1], bHSV[2]);
233 | },
234 |
235 | saturation: function(a, b) {
236 | var aHSV = $.rgbToHsv(a);
237 | var bHSV = $.rgbToHsv(b);
238 |
239 | return $.hsvToRgb(aHSV[0], bHSV[1], aHSV[2]);
240 | }
241 | },
242 |
243 | blend: function(below, above, mode, mix) {
244 | if (typeof mix === "undefined") mix = 1;
245 |
246 | var below = $(below);
247 | var above = $(above);
248 |
249 | var belowCtx = below.context;
250 | var aboveCtx = above.context;
251 |
252 | var belowData = belowCtx.getImageData(0, 0, below.canvas.width, below.canvas.height);
253 | var aboveData = aboveCtx.getImageData(0, 0, above.canvas.width, above.canvas.height);
254 |
255 | var belowPixels = belowData.data;
256 | var abovePixels = aboveData.data;
257 |
258 | var imageData = this.createImageData(below.canvas.width, below.canvas.height);
259 | var pixels = imageData.data;
260 |
261 | var blendingFunction = $.blendFunctions[mode];
262 |
263 | if ($.specialBlendFunctions.indexOf(mode) !== -1) {
264 | for (var i = 0, len = belowPixels.length; i < len; i += 4) {
265 | var rgb = blendingFunction([belowPixels[i + 0], belowPixels[i + 1], belowPixels[i + 2]], [abovePixels[i + 0], abovePixels[i + 1], abovePixels[i + 2]]);
266 |
267 | pixels[i + 0] = belowPixels[i + 0] + (rgb[0] - belowPixels[i + 0]) * mix;
268 | pixels[i + 1] = belowPixels[i + 1] + (rgb[1] - belowPixels[i + 1]) * mix;
269 | pixels[i + 2] = belowPixels[i + 2] + (rgb[2] - belowPixels[i + 2]) * mix;
270 |
271 | pixels[i + 3] = belowPixels[i + 3];
272 | }
273 | } else {
274 |
275 | for (var i = 0, len = belowPixels.length; i < len; i += 4) {
276 | var r = blendingFunction(belowPixels[i + 0], abovePixels[i + 0]);
277 | var g = blendingFunction(belowPixels[i + 1], abovePixels[i + 1]);
278 | var b = blendingFunction(belowPixels[i + 2], abovePixels[i + 2]);
279 |
280 | pixels[i + 0] = belowPixels[i + 0] + (r - belowPixels[i + 0]) * mix;
281 | pixels[i + 1] = belowPixels[i + 1] + (g - belowPixels[i + 1]) * mix;
282 | pixels[i + 2] = belowPixels[i + 2] + (b - belowPixels[i + 2]) * mix;
283 |
284 | pixels[i + 3] = belowPixels[i + 3];
285 | }
286 | }
287 |
288 | below.context.putImageData(imageData, 0, 0);
289 |
290 | return below;
291 | },
292 |
293 | wrapValue: function(value, min, max) {
294 | var d = Math.abs(max - min);
295 | return min + (value - min) % d;
296 | },
297 |
298 | limitValue: function(value, min, max) {
299 | return value < min ? min : value > max ? max : value;
300 | },
301 |
302 | mix: function(a, b, ammount) {
303 | return a + (b - a) * ammount;
304 | },
305 |
306 | hexToRgb: function(hex) {
307 | if (hex.length === 7) return ['0x' + hex[1] + hex[2] | 0, '0x' + hex[3] + hex[4] | 0, '0x' + hex[5] + hex[6] | 0];
308 | else return ['0x' + hex[1] | 0, '0x' + hex[2], '0x' + hex[3] | 0];
309 | },
310 |
311 | rgbToHex: function(r, g, b) {
312 | return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1, 7);
313 | },
314 |
315 | /* author: http://mjijackson.com/ */
316 |
317 | rgbToHsl: function(r, g, b) {
318 |
319 | if (r instanceof Array) {
320 | b = r[2];
321 | g = r[1];
322 | r = r[0];
323 | }
324 |
325 | r /= 255, g /= 255, b /= 255;
326 | var max = Math.max(r, g, b),
327 | min = Math.min(r, g, b);
328 | var h, s, l = (max + min) / 2;
329 |
330 | if (max == min) {
331 | h = s = 0; // achromatic
332 | } else {
333 | var d = max - min;
334 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
335 | switch (max) {
336 | case r:
337 | h = (g - b) / d + (g < b ? 6 : 0);
338 | break;
339 | case g:
340 | h = (b - r) / d + 2;
341 | break;
342 | case b:
343 | h = (r - g) / d + 4;
344 | break;
345 | }
346 | h /= 6;
347 | }
348 |
349 | return [h, s, l];
350 | },
351 |
352 | /* author: http://mjijackson.com/ */
353 |
354 | hslToRgb: function(h, s, l) {
355 | var r, g, b;
356 |
357 | if (s == 0) {
358 | r = g = b = l; // achromatic
359 | } else {
360 | function hue2rgb(p, q, t) {
361 | if (t < 0) t += 1;
362 | if (t > 1) t -= 1;
363 | if (t < 1 / 6) return p + (q - p) * 6 * t;
364 | if (t < 1 / 2) return q;
365 | if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
366 | return p;
367 | }
368 |
369 | var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
370 | var p = 2 * l - q;
371 | r = hue2rgb(p, q, h + 1 / 3);
372 | g = hue2rgb(p, q, h);
373 | b = hue2rgb(p, q, h - 1 / 3);
374 | }
375 |
376 | return [r * 255 | 0, g * 255 | 0, b * 255 | 0];
377 | },
378 |
379 | rgbToHsv: function(r, g, b) {
380 | if (r instanceof Array) {
381 | b = r[2];
382 | g = r[1];
383 | r = r[0];
384 | }
385 |
386 | r = r / 255, g = g / 255, b = b / 255;
387 | var max = Math.max(r, g, b),
388 | min = Math.min(r, g, b);
389 | var h, s, v = max;
390 |
391 | var d = max - min;
392 | s = max == 0 ? 0 : d / max;
393 |
394 | if (max == min) {
395 | h = 0; // achromatic
396 | } else {
397 | switch (max) {
398 | case r:
399 | h = (g - b) / d + (g < b ? 6 : 0);
400 | break;
401 | case g:
402 | h = (b - r) / d + 2;
403 | break;
404 | case b:
405 | h = (r - g) / d + 4;
406 | break;
407 | }
408 | h /= 6;
409 | }
410 |
411 | return [h, s, v];
412 | },
413 |
414 | hsvToRgb: function(h, s, v) {
415 | var r, g, b;
416 |
417 | var i = Math.floor(h * 6);
418 | var f = h * 6 - i;
419 | var p = v * (1 - s);
420 | var q = v * (1 - f * s);
421 | var t = v * (1 - (1 - f) * s);
422 |
423 | switch (i % 6) {
424 | case 0:
425 | r = v, g = t, b = p;
426 | break;
427 | case 1:
428 | r = q, g = v, b = p;
429 | break;
430 | case 2:
431 | r = p, g = v, b = t;
432 | break;
433 | case 3:
434 | r = p, g = q, b = v;
435 | break;
436 | case 4:
437 | r = t, g = p, b = v;
438 | break;
439 | case 5:
440 | r = v, g = p, b = q;
441 | break;
442 | }
443 |
444 | return [r * 255 | 0, g * 255 | 0, b * 255 | 0];
445 | },
446 |
447 | color: function() {
448 | var result = new $.Color();
449 | result.parse(arguments[0], arguments[1]);
450 | return result;
451 | },
452 |
453 | createCanvas: function(width, height) {
454 | var result = document.createElement("canvas");
455 |
456 | if (arguments[0] instanceof Image || arguments[0] instanceof HTMLImageElement) {
457 | var image = arguments[0];
458 | result.width = image.width;
459 | result.height = image.height;
460 | result.getContext("2d").drawImage(image, 0, 0);
461 | } else {
462 | result.width = width;
463 | result.height = height;
464 | }
465 |
466 | return result;
467 | },
468 |
469 | createImageData: function(width, height) {
470 | return document.createElement("Canvas").getContext("2d").createImageData(width, height);
471 | },
472 |
473 |
474 | /* https://gist.github.com/3781251 */
475 |
476 | mousePosition: function(event) {
477 | var totalOffsetX = 0,
478 | totalOffsetY = 0,
479 | coordX = 0,
480 | coordY = 0,
481 | currentElement = event.target || event.srcElement,
482 | mouseX = 0,
483 | mouseY = 0;
484 |
485 | // Traversing the parents to get the total offset
486 | do {
487 | totalOffsetX += currentElement.offsetLeft;
488 | totalOffsetY += currentElement.offsetTop;
489 | }
490 | while ((currentElement = currentElement.offsetParent));
491 | // Set the event to first touch if using touch-input
492 | if (event.changedTouches && event.changedTouches[0] !== undefined) {
493 | event = event.changedTouches[0];
494 | }
495 | // Use pageX to get the mouse coordinates
496 | if (event.pageX || event.pageY) {
497 | mouseX = event.pageX;
498 | mouseY = event.pageY;
499 | }
500 | // IE8 and below doesn't support event.pageX
501 | else if (event.clientX || event.clientY) {
502 | mouseX = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
503 | mouseY = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
504 | }
505 | // Subtract the offset from the mouse coordinates
506 | coordX = mouseX - totalOffsetX;
507 | coordY = mouseY - totalOffsetY;
508 |
509 | return {
510 | x: coordX,
511 | y: coordY
512 | };
513 | }
514 | });
515 |
516 | $.Wrapper = function(canvas) {
517 | this.context = canvas.getContext("2d");
518 | this.canvas = canvas;
519 | }
520 |
521 | $.Wrapper.prototype = {
522 | appendTo: function(selector) {
523 | if (typeof selector === "object") {
524 | var element = selector;
525 | } else {
526 | var element = document.querySelector(selector);
527 | }
528 |
529 | element.appendChild(this.canvas);
530 |
531 | return this;
532 | },
533 |
534 | blendOn: function(what, mode, mix) {
535 | $.blend(what, this, mode, mix);
536 |
537 | return this;
538 | },
539 |
540 | blend: function(what, mode, mix) {
541 | if (typeof what === "string") {
542 | var color = what;
543 | what = $($.createCanvas(this.canvas.width, this.canvas.height));
544 | what.fillStyle(color).fillRect(0, 0, this.canvas.width, this.canvas.height);
545 | }
546 |
547 | $.blend(this, what, mode, mix);
548 |
549 | return this;
550 | },
551 |
552 | circle: function(x, y, r) {
553 | this.context.arc(x, y, r, 0, Math.PI * 2);
554 | return this;
555 | },
556 |
557 | crop: function(x, y, w, h) {
558 |
559 | var canvas = $.createCanvas(w, h);
560 | var context = canvas.getContext("2d");
561 |
562 | context.drawImage(this.canvas, x, y, w, h, 0, 0, w, h);
563 | this.canvas.width = w;
564 | this.canvas.height = h;
565 | this.clear();
566 | this.context.drawImage(canvas, 0, 0);
567 |
568 | return this;
569 | },
570 |
571 | set: function(properties) {
572 | $.extend(this.context, properties);
573 |
574 | /* for(var key in properties) {
575 | this[key](properties[key]);
576 | }
577 | */
578 |
579 | },
580 |
581 | resize: function(width, height) {
582 | var w = width,
583 | h = height;
584 |
585 | if (arguments.length === 1) {
586 | w = arguments[0] * this.canvas.width | 0;
587 | h = arguments[0] * this.canvas.height | 0;
588 | } else {
589 |
590 | if (height === null) {
591 | if (this.canvas.width > width) {
592 | h = this.canvas.height * (width / this.canvas.width) | 0;
593 | w = width;
594 | } else {
595 | w = this.canvas.width;
596 | h = this.canvas.height;
597 | }
598 | } else if (width === null) {
599 | if (this.canvas.width > width) {
600 | w = this.canvas.width * (height / this.canvas.height) | 0;
601 | h = height;
602 | } else {
603 | w = this.canvas.width;
604 | h = this.canvas.height;
605 | }
606 | }
607 | }
608 |
609 | var $resized = $(w, h).drawImage(this.canvas, 0, 0, this.canvas.width, this.canvas.height, 0, 0, w, h);
610 | this.canvas = $resized.canvas;
611 | this.context = $resized.context;
612 |
613 | return this;
614 | },
615 |
616 |
617 | trim: function(color, changes) {
618 | var transparent;
619 |
620 | if (color) {
621 | color = $.color(color).toArray();
622 | transparent = !color[3];
623 | } else transparent = true;
624 |
625 | var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
626 | var sourcePixels = sourceData.data;
627 |
628 | var bound = [this.canvas.width, this.canvas.height, 0, 0];
629 |
630 | for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
631 | if (transparent) {
632 | if (!sourcePixels[i + 3]) continue;
633 | } else if (sourcePixels[i + 0] === color[0] && sourcePixels[i + 1] === color[1] && sourcePixels[i + 2] === color[2]) continue;
634 | var x = (i / 4 | 0) % this.canvas.width | 0;
635 | var y = (i / 4 | 0) / this.canvas.width | 0;
636 |
637 | if (x < bound[0]) bound[0] = x;
638 | if (x > bound[2]) bound[2] = x;
639 |
640 | if (y < bound[1]) bound[1] = y;
641 | if (y > bound[3]) bound[3] = y;
642 | }
643 |
644 | if (bound[2] === 0 || bound[3] === 0) {
645 |
646 | } else {
647 | if (changes) {
648 | changes.left = bound[0];
649 | changes.top = bound[1];
650 | changes.width = bound[2] - bound[0];
651 | changes.height = bound[3] - bound[1];
652 | }
653 |
654 | this.crop(bound[0], bound[1], bound[2] - bound[0] + 1, bound[3] - bound[1] + 1);
655 | }
656 |
657 | return this;
658 | },
659 |
660 | resizePixel: function(pixelSize) {
661 |
662 | var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
663 | var sourcePixels = sourceData.data;
664 | var canvas = document.createElement("canvas");
665 | var context = canvas.context = canvas.getContext("2d");
666 |
667 | canvas.width = this.canvas.width * pixelSize | 0;
668 | canvas.height = this.canvas.height * pixelSize | 0;
669 |
670 | for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
671 | if (!sourcePixels[i + 3]) continue;
672 | context.fillStyle = $.rgbToHex(sourcePixels[i + 0], sourcePixels[i + 1], sourcePixels[i + 2]);
673 |
674 | var x = (i / 4) % this.canvas.width;
675 | var y = (i / 4) / this.canvas.width | 0;
676 |
677 | context.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
678 | }
679 |
680 | this.canvas.width = canvas.width;
681 | this.canvas.height = canvas.height;
682 | this.clear().drawImage(canvas, 0, 0);
683 |
684 | return this;
685 |
686 | /* this very clever method is working only under Chrome */
687 |
688 | var x = 0,
689 | y = 0;
690 |
691 | var canvas = document.createElement("canvas");
692 | var context = canvas.context = canvas.getContext("2d");
693 |
694 | canvas.width = this.canvas.width * pixelSize | 0;
695 | canvas.height = this.canvas.height * pixelSize | 0;
696 |
697 | while (x < this.canvas.width) {
698 | y = 0;
699 | while (y < this.canvas.height) {
700 | context.drawImage(this.canvas, x, y, 1, 1, x * pixelSize, y * pixelSize, pixelSize, pixelSize);
701 | y++;
702 | }
703 | x++;
704 | }
705 |
706 | this.canvas = canvas;
707 | this.context = context;
708 |
709 | return this;
710 | },
711 |
712 |
713 | matchPalette: function(palette) {
714 | var imgData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
715 |
716 | var rgbPalette = [];
717 | for (var i = 0; i < palette.length; i++) {
718 | rgbPalette.push($.color(palette[i]));
719 | }
720 |
721 |
722 | for (var i = 0; i < imgData.data.length; i += 4) {
723 | var difList = [];
724 | for (var j = 0; j < rgbPalette.length; j++) {
725 | var rgbVal = rgbPalette[j];
726 | var rDif = Math.abs(imgData.data[i] - rgbVal[0]),
727 | gDif = Math.abs(imgData.data[i + 1] - rgbVal[1]),
728 | bDif = Math.abs(imgData.data[i + 2] - rgbVal[2]);
729 | difList.push(rDif + gDif + bDif);
730 | }
731 |
732 | var closestMatch = 0;
733 | for (var j = 0; j < palette.length; j++) {
734 | if (difList[j] < difList[closestMatch]) {
735 | closestMatch = j;
736 | }
737 | }
738 |
739 | var paletteRgb = cq.hexToRgb(palette[closestMatch]);
740 | imgData.data[i] = paletteRgb[0];
741 | imgData.data[i + 1] = paletteRgb[1];
742 | imgData.data[i + 2] = paletteRgb[2];
743 | }
744 |
745 | this.context.putImageData(imgData, 0, 0);
746 |
747 | return this;
748 | },
749 |
750 | getPalette: function() {
751 | var palette = [];
752 | var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
753 | var sourcePixels = sourceData.data;
754 |
755 | for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
756 | if (sourcePixels[i + 3]) {
757 | var hex = $.rgbToHex(sourcePixels[i + 0], sourcePixels[i + 1], sourcePixels[i + 2]);
758 | if (palette.indexOf(hex) === -1) palette.push(hex);
759 | }
760 | }
761 |
762 | return palette;
763 | },
764 |
765 | pixelize: function(size) {
766 | if (!size) return this;
767 | size = size || 4;
768 |
769 | var mozImageSmoothingEnabled = this.context.mozImageSmoothingEnabled;
770 | var webkitImageSmoothingEnabled = this.context.webkitImageSmoothingEnabled;
771 |
772 | this.context.mozImageSmoothingEnabled = false;
773 | this.context.webkitImageSmoothingEnabled = false;
774 |
775 | var scale = (this.canvas.width / size) / this.canvas.width;
776 |
777 | var temp = cq(this.canvas.width, this.canvas.height);
778 |
779 | temp.drawImage(this.canvas, 0, 0, this.canvas.width, this.canvas.height, 0, 0, this.canvas.width * scale | 0, this.canvas.height * scale | 0);
780 | this.clear().drawImage(temp.canvas, 0, 0, this.canvas.width * scale | 0, this.canvas.height * scale | 0, 0, 0, this.canvas.width, this.canvas.height);
781 |
782 | this.context.mozImageSmoothingEnabled = mozImageSmoothingEnabled;
783 | this.context.webkitImageSmoothingEnabled = webkitImageSmoothingEnabled;
784 |
785 | return this;
786 | },
787 |
788 | colorToMask: function(color, inverted) {
789 | color = $.color(color).toArray();
790 | var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
791 | var sourcePixels = sourceData.data;
792 |
793 | var mask = [];
794 |
795 | for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
796 | if (sourcePixels[i + 0] == color[0] && sourcePixels[i + 1] == color[1] && sourcePixels[i + 2] == color[2]) mask.push(inverted || false);
797 | else mask.push(!inverted);
798 | }
799 |
800 | return mask;
801 | },
802 |
803 | grayscaleToMask: function(color) {
804 | color = $.color(color).toArray();
805 | var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
806 | var sourcePixels = sourceData.data;
807 |
808 | var mask = [];
809 |
810 | for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
811 | mask.push((sourcePixels[i + 0] + sourcePixels[i + 1] + sourcePixels[i + 2]) / 3 | 0);
812 | }
813 |
814 | return mask;
815 | },
816 |
817 | grayscaleToAlpha: function() {
818 | var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
819 | var sourcePixels = sourceData.data;
820 |
821 | var mask = [];
822 |
823 | for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
824 | sourcePixels[i + 3] = (sourcePixels[i + 0] + sourcePixels[i + 1] + sourcePixels[i + 2]) / 3 | 0;
825 |
826 | sourcePixels[i + 0] = sourcePixels[i + 1] = sourcePixels[i + 2] = 255;
827 | }
828 |
829 | this.context.putImageData(sourceData, 0, 0);
830 |
831 | return this;
832 | },
833 |
834 | applyMask: function(mask) {
835 | var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
836 | var sourcePixels = sourceData.data;
837 |
838 | var mode = typeof mask[0] === "boolean" ? "bool" : "byte";
839 |
840 | for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
841 | var value = mask[i / 4];
842 |
843 | if (mode === "bool") sourcePixels[i + 3] = 255 * value | 0;
844 | else {
845 | sourcePixels[i + 3] = value | 0;
846 | }
847 | }
848 |
849 |
850 | this.context.putImageData(sourceData, 0, 0);
851 | return this;
852 | },
853 |
854 | fillMask: function(mask) {
855 |
856 | var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
857 | var sourcePixels = sourceData.data;
858 |
859 | var maskType = typeof mask[0] === "boolean" ? "bool" : "byte";
860 | var colorMode = arguments.length === 2 ? "normal" : "gradient";
861 |
862 | var color = $.color(arguments[1]);
863 | if (colorMode === "gradient") colorB = $.color(arguments[2]);
864 |
865 | for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
866 | var value = mask[i / 4];
867 |
868 | if (maskType === "byte") value /= 255;
869 |
870 | if (colorMode === "normal") {
871 | if (value) {
872 | sourcePixels[i + 0] = color[0] | 0;
873 | sourcePixels[i + 1] = color[1] | 0;
874 | sourcePixels[i + 2] = color[2] | 0;
875 | sourcePixels[i + 3] = value * 255 | 0;
876 | }
877 | } else {
878 | sourcePixels[i + 0] = color[0] + (colorB[0] - color[0]) * value | 0;
879 | sourcePixels[i + 1] = color[1] + (colorB[1] - color[1]) * value | 0;
880 | sourcePixels[i + 2] = color[2] + (colorB[2] - color[2]) * value | 0;
881 | sourcePixels[i + 3] = 255;
882 | }
883 | }
884 |
885 | this.context.putImageData(sourceData, 0, 0);
886 | return this;
887 | },
888 |
889 | clear: function(color) {
890 | if (color) {
891 | this.context.fillStyle = color;
892 | this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
893 | } else {
894 | this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
895 | }
896 |
897 | return this;
898 | },
899 |
900 | clone: function() {
901 | var result = $.createCanvas(this.canvas.width, this.canvas.height);
902 | result.getContext("2d").drawImage(this.canvas, 0, 0);
903 | return $(result);
904 | },
905 |
906 | fillStyle: function(fillStyle) {
907 | this.context.fillStyle = fillStyle;
908 | return this;
909 | },
910 |
911 | strokeStyle: function(strokeStyle) {
912 | this.context.strokeStyle = strokeStyle;
913 | return this;
914 | },
915 |
916 | gradientText: function(text, x, y, maxWidth, gradient) {
917 |
918 | var words = text.split(" ");
919 |
920 | var h = this.font().match(/\d+/g)[0] * 2;
921 |
922 | var ox = 0;
923 | var oy = 0;
924 |
925 | if (maxWidth) {
926 | var line = 0;
927 | var lines = [""];
928 |
929 | for (var i = 0; i < words.length; i++) {
930 | var word = words[i] + " ";
931 | var wordWidth = this.context.measureText(word).width;
932 |
933 | if (ox + wordWidth > maxWidth) {
934 | lines[++line] = "";
935 | ox = 0;
936 | }
937 |
938 | lines[line] += word;
939 |
940 | ox += wordWidth;
941 | }
942 | } else var lines = [text];
943 |
944 | for (var i = 0; i < lines.length; i++) {
945 | var oy = y + i * h * 0.6 | 0;
946 | var lingrad = this.context.createLinearGradient(0, oy, 0, oy + h * 0.6 | 0);
947 |
948 | for (var j = 0; j < gradient.length; j += 2) {
949 | lingrad.addColorStop(gradient[j], gradient[j + 1]);
950 | }
951 |
952 | var text = lines[i];
953 |
954 | this.fillStyle(lingrad).fillText(text, x, oy);
955 | }
956 |
957 | return this;
958 | },
959 |
960 | setHsl: function() {
961 |
962 | if (arguments.length === 1) {
963 | var args = arguments[0];
964 | } else {
965 | var args = arguments;
966 | }
967 |
968 | var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
969 | var pixels = data.data;
970 | var r, g, b, a, h, s, l, hsl = [],
971 | newPixel = [];
972 |
973 | for (var i = 0, len = pixels.length; i < len; i += 4) {
974 | hsl = $.rgbToHsl(pixels[i + 0], pixels[i + 1], pixels[i + 2]);
975 |
976 | h = args[0] === null ? hsl[0] : $.limitValue(args[0], 0, 1);
977 | s = args[1] === null ? hsl[1] : $.limitValue(args[1], 0, 1);
978 | l = args[2] === null ? hsl[2] : $.limitValue(args[2], 0, 1);
979 |
980 | newPixel = $.hslToRgb(h, s, l);
981 |
982 | pixels[i + 0] = newPixel[0];
983 | pixels[i + 1] = newPixel[1];
984 | pixels[i + 2] = newPixel[2];
985 | }
986 |
987 | this.context.putImageData(data, 0, 0);
988 |
989 | return this;
990 | },
991 |
992 | shiftHsl: function() {
993 |
994 | if (arguments.length === 1) {
995 | var args = arguments[0];
996 | } else {
997 | var args = arguments;
998 | }
999 |
1000 | var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
1001 | var pixels = data.data;
1002 | var r, g, b, a, h, s, l, hsl = [],
1003 | newPixel = [];
1004 |
1005 | for (var i = 0, len = pixels.length; i < len; i += 4) {
1006 | hsl = $.rgbToHsl(pixels[i + 0], pixels[i + 1], pixels[i + 2]);
1007 |
1008 | h = args[0] === null ? hsl[0] : $.wrapValue(hsl[0] + args[0], 0, 1);
1009 | s = args[1] === null ? hsl[1] : $.limitValue(hsl[1] + args[1], 0, 1);
1010 | l = args[2] === null ? hsl[2] : $.limitValue(hsl[2] + args[2], 0, 1);
1011 |
1012 | newPixel = $.hslToRgb(h, s, l);
1013 |
1014 | pixels[i + 0] = newPixel[0];
1015 | pixels[i + 1] = newPixel[1];
1016 | pixels[i + 2] = newPixel[2];
1017 | }
1018 |
1019 |
1020 | this.context.putImageData(data, 0, 0);
1021 |
1022 | return this;
1023 | },
1024 |
1025 | replaceHue: function(src, dst) {
1026 |
1027 | var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
1028 | var pixels = data.data;
1029 | var r, g, b, a, h, s, l, hsl = [],
1030 | newPixel = [];
1031 |
1032 | for (var i = 0, len = pixels.length; i < len; i += 4) {
1033 | hsl = $.rgbToHsl(pixels[i + 0], pixels[i + 1], pixels[i + 2]);
1034 |
1035 | if (Math.abs(hsl[0] - src) < 0.05) h = $.wrapValue(dst, 0, 1);
1036 | else h = hsl[0];
1037 |
1038 | newPixel = $.hslToRgb(h, hsl[1], hsl[2]);
1039 |
1040 | pixels[i + 0] = newPixel[0];
1041 | pixels[i + 1] = newPixel[1];
1042 | pixels[i + 2] = newPixel[2];
1043 | }
1044 |
1045 | this.context.putImageData(data, 0, 0);
1046 |
1047 | return this;
1048 | },
1049 |
1050 | invert: function(src, dst) {
1051 |
1052 | var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
1053 | var pixels = data.data;
1054 | var r, g, b, a, h, s, l, hsl = [],
1055 | newPixel = [];
1056 |
1057 | for (var i = 0, len = pixels.length; i < len; i += 4) {
1058 | pixels[i + 0] = 255 - pixels[i + 0];
1059 | pixels[i + 1] = 255 - pixels[i + 1];
1060 | pixels[i + 2] = 255 - pixels[i + 2];
1061 | }
1062 |
1063 | this.context.putImageData(data, 0, 0);
1064 |
1065 | return this;
1066 | },
1067 |
1068 | roundRect: function(x, y, width, height, radius) {
1069 |
1070 | this.beginPath();
1071 | this.moveTo(x + radius, y);
1072 | this.lineTo(x + width - radius, y);
1073 | this.quadraticCurveTo(x + width, y, x + width, y + radius);
1074 | this.lineTo(x + width, y + height - radius);
1075 | this.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
1076 | this.lineTo(x + radius, y + height);
1077 | this.quadraticCurveTo(x, y + height, x, y + height - radius);
1078 | this.lineTo(x, y + radius);
1079 | this.quadraticCurveTo(x, y, x + radius, y);
1080 | this.closePath();
1081 |
1082 | return this;
1083 | },
1084 |
1085 | wrappedText: function(text, x, y, maxWidth, newlineCallback) {
1086 |
1087 | var words = text.split(" ");
1088 |
1089 | var h = this.font().match(/\d+/g)[0] * 2;
1090 |
1091 | var ox = 0;
1092 | var oy = 0;
1093 |
1094 | if (maxWidth) {
1095 | var line = 0;
1096 | var lines = [""];
1097 |
1098 | for (var i = 0; i < words.length; i++) {
1099 | var word = words[i] + " ";
1100 | var wordWidth = this.context.measureText(word).width;
1101 |
1102 | if (ox + wordWidth > maxWidth) {
1103 | lines[++line] = "";
1104 | ox = 0;
1105 | }
1106 |
1107 | lines[line] += word;
1108 |
1109 | ox += wordWidth;
1110 | }
1111 | } else {
1112 | var lines = [text];
1113 | }
1114 |
1115 | for (var i = 0; i < lines.length; i++) {
1116 | var oy = y + i * h * 0.6 | 0;
1117 |
1118 | var text = lines[i];
1119 |
1120 | if (newlineCallback) newlineCallback.call(this, x, y + oy);
1121 |
1122 | this.fillText(text, x, oy);
1123 | }
1124 |
1125 | return this;
1126 | },
1127 |
1128 | textBoundaries: function(text, maxWidth) {
1129 | var words = text.split(" ");
1130 |
1131 | var h = this.font().match(/\d+/g)[0] * 2;
1132 |
1133 | var ox = 0;
1134 | var oy = 0;
1135 |
1136 | if (maxWidth) {
1137 | var line = 0;
1138 | var lines = [""];
1139 |
1140 | for (var i = 0; i < words.length; i++) {
1141 | var word = words[i] + " ";
1142 | var wordWidth = this.context.measureText(word).width;
1143 |
1144 | if (ox + wordWidth > maxWidth) {
1145 | lines[++line] = "";
1146 | ox = 0;
1147 | }
1148 |
1149 | lines[line] += word;
1150 |
1151 | ox += wordWidth;
1152 | }
1153 | } else {
1154 | var lines = [text];
1155 | maxWidth = this.measureText(text).width;
1156 | }
1157 |
1158 | return {
1159 | height: lines.length * h * 0.6 | 0,
1160 | width: maxWidth
1161 | }
1162 | },
1163 |
1164 | paperBag: function(x, y, width, height, blowX, blowY) {
1165 | var lx, ly;
1166 | this.beginPath();
1167 | this.moveTo(x, y);
1168 | this.quadraticCurveTo(x + width / 2 | 0, y + height * blowY | 0, x + width, y);
1169 | this.quadraticCurveTo(x + width - width * blowX | 0, y + height / 2 | 0, x + width, y + height);
1170 | this.quadraticCurveTo(x + width / 2 | 0, y + height - height * blowY | 0, x, y + height);
1171 | this.quadraticCurveTo(x + width * blowX | 0, y + height / 2 | 0, x, y);
1172 | },
1173 |
1174 | borderImage: function(image, x, y, w, h, t, r, b, l, fill) {
1175 |
1176 | /* top */
1177 | this.drawImage(image, l, 0, image.width - l - r, t, x + l, y, w - l - r, t);
1178 |
1179 | /* bottom */
1180 | this.drawImage(image, l, image.height - b, image.width - l - r, b, x + l, y + h - b, w - l - r, b);
1181 |
1182 | /* left */
1183 | this.drawImage(image, 0, t, l, image.height - b - t, x, y + t, l, h - b - t);
1184 |
1185 | /* right */
1186 | this.drawImage(image, image.width - r, t, r, image.height - b - t, x + w - r, y + t, r, h - b - t);
1187 |
1188 | /* top-left */
1189 | this.drawImage(image, 0, 0, l, t, x, y, l, t);
1190 |
1191 | /* top-right */
1192 | this.drawImage(image, image.width - r, 0, r, t, x + w - r, y, r, t);
1193 |
1194 | /* bottom-right */
1195 | this.drawImage(image, image.width - r, image.height - b, r, b, x + w - r, y + h - b, r, b);
1196 |
1197 | /* bottom-left */
1198 | this.drawImage(image, 0, image.height - b, l, b, x, y + h - b, l, b);
1199 |
1200 | if (fill) {
1201 | if (typeof fill === "string") {
1202 | this.fillStyle(fill).fillRect(x + l, y + t, w - l - r, h - t - b);
1203 | } else {
1204 | this.drawImage(image, l, t, image.width - r - l, image.height - b - t, x + l, y + t, w - l - r, h - t - b);
1205 | }
1206 | }
1207 | },
1208 |
1209 | measureText: function() {
1210 | return this.context.measureText.apply(this.context, arguments);
1211 | },
1212 |
1213 | createRadialGradient: function() {
1214 | return this.context.createRadialGradient.apply(this.context, arguments);
1215 | },
1216 |
1217 | createLinearGradient: function() {
1218 | return this.context.createLinearGradient.apply(this.context, arguments);
1219 | },
1220 |
1221 | getImageData: function() {
1222 | return this.context.getImageData.apply(this.context, arguments);
1223 | }
1224 |
1225 | };
1226 |
1227 | /* extend wrapper with drawing context methods */
1228 |
1229 | var methods = ["arc", "arcTo", "beginPath", "bezierCurveTo", "clearRect", "clip", "closePath", "createImageData", "createLinearGradient", "createRadialGradient", "createPattern", "drawFocusRing", "drawImage", "fill", "fillRect", "fillText", "getImageData", "isPointInPath", "lineTo", "measureText", "moveTo", "putImageData", "quadraticCurveTo", "rect", "restore", "rotate", "save", "scale", "setTransform", "stroke", "strokeRect", "strokeText", "transform", "translate"];
1230 | for (var i = 0; i < methods.length; i++) {
1231 | var name = methods[i];
1232 | if (!$.Wrapper.prototype[name]) $.Wrapper.prototype[name] = Function("this.context." + name + ".apply(this.context, arguments); return this;");
1233 | };
1234 |
1235 | /* create setters and getters */
1236 |
1237 | var properties = ["canvas", "fillStyle", "font", "globalAlpha", "globalCompositeOperation", "lineCap", "lineJoin", "lineWidth", "miterLimit", "shadowOffsetX", "shadowOffsetY", "shadowBlur", "shadowColor", "strokeStyle", "textAlign", "textBaseline"];
1238 | for (var i = 0; i < properties.length; i++) {
1239 | var name = properties[i];
1240 | if (!$.Wrapper.prototype[name]) $.Wrapper.prototype[name] = Function("if(arguments.length) { this.context." + name + " = arguments[0]; return this; } else { return this.context." + name + "; }");
1241 | };
1242 |
1243 | /* color */
1244 |
1245 | $.Color = function(data, type) {
1246 | if (arguments.length) this.parse(data);
1247 | }
1248 |
1249 | $.Color.prototype = {
1250 | parse: function(args, type) {
1251 | if (args[0] instanceof $.Color) {
1252 | this[0] = args[0][0];
1253 | this[1] = args[0][1];
1254 | this[2] = args[0][2];
1255 | this[3] = args[0][3];
1256 | return;
1257 | }
1258 |
1259 | if (typeof args === "string") {
1260 | var match = null;
1261 |
1262 | if (args[0] === "#") {
1263 | var rgb = $.hexToRgb(args);
1264 | this[0] = rgb[0];
1265 | this[1] = rgb[1];
1266 | this[2] = rgb[2];
1267 | this[3] = 1.0;
1268 | } else if (match = args.match(/rgb\((.*),(.*),(.*)\)/)) {
1269 | this[0] = match[1] | 0;
1270 | this[1] = match[2] | 0;
1271 | this[2] = match[3] | 0;
1272 | this[3] = 1.0;
1273 | } else if (match = args.match(/rgba\((.*),(.*),(.*)\)/)) {
1274 | this[0] = match[1] | 0;
1275 | this[1] = match[2] | 0;
1276 | this[2] = match[3] | 0;
1277 | this[3] = match[4] | 0;
1278 | } else if (match = args.match(/hsl\((.*),(.*),(.*)\)/)) {
1279 | this.fromHsl(match[1], match[2], match[3]);
1280 | } else if (match = args.match(/hsv\((.*),(.*),(.*)\)/)) {
1281 | this.fromHsv(match[1], match[2], match[3]);
1282 | }
1283 | } else {
1284 | switch (type) {
1285 | case "hsl":
1286 | case "hsla":
1287 |
1288 | this.fromHsl(args[0], args[1], args[2], args[3]);
1289 | break;
1290 |
1291 | case "hsv":
1292 | case "hsva":
1293 |
1294 | this.fromHsv(args[0], args[1], args[2], args[3]);
1295 | break;
1296 |
1297 | default:
1298 | this[0] = args[0];
1299 | this[1] = args[1];
1300 | this[2] = args[2];
1301 | this[3] = typeof args[3] === "undefined" ? 1.0 : args[3];
1302 | break;
1303 | }
1304 | }
1305 | },
1306 |
1307 | fromHsl: function() {
1308 | var components = arguments[0] instanceof Array ? arguments[0] : arguments;
1309 | var color = $.hslToRgb(components[0], components[1], components[2]);
1310 |
1311 | this[0] = color[0];
1312 | this[1] = color[1];
1313 | this[2] = color[2];
1314 | this[3] = typeof arguments[3] === "undefined" ? 1.0 : arguments[3];
1315 | },
1316 |
1317 | fromHsv: function() {
1318 | var components = arguments[0] instanceof Array ? arguments[0] : arguments;
1319 | var color = $.hsvToRgb(components[0], components[1], components[2]);
1320 |
1321 | this[0] = color[0];
1322 | this[1] = color[1];
1323 | this[2] = color[2];
1324 | this[3] = typeof arguments[3] === "undefined" ? 1.0 : arguments[3];
1325 | },
1326 |
1327 | toArray: function() {
1328 | return [this[0], this[1], this[2], this[3]];
1329 | },
1330 |
1331 | toRgb: function() {
1332 | return "rgb(" + this[0] + ", " + this[1] + ", " + this[2] + ")";
1333 | },
1334 |
1335 | toRgba: function() {
1336 | return "rgba(" + this[0] + ", " + this[1] + ", " + this[2] + ", " + this[3] + ")";
1337 | },
1338 |
1339 | toHex: function() {
1340 | return $.rgbToHex(this[0], this[1], this[2]);
1341 | },
1342 |
1343 | toHsl: function() {
1344 | var c = $.rgbToHsl(this[0], this[1], this[2]);
1345 | c[3] = this[3];
1346 | return c;
1347 | },
1348 |
1349 | toHsv: function() {
1350 | var c = $.rgbToHsv(this[0], this[1], this[2]);
1351 | c[3] = this[3];
1352 | return c;
1353 | },
1354 |
1355 | gradient: function(target, steps) {
1356 | var targetColor = $.color(target);
1357 | },
1358 |
1359 | shiftHsl: function() {
1360 | var hsl = this.toHsl();
1361 |
1362 | var h = arguments[0] === null ? hsl[0] : $.wrapValue(hsl[0] + arguments[0], 0, 1);
1363 | var s = arguments[1] === null ? hsl[1] : $.limitValue(hsl[1] + arguments[1], 0, 1);
1364 | var l = arguments[2] === null ? hsl[2] : $.limitValue(hsl[2] + arguments[2], 0, 1);
1365 |
1366 | this.fromHsl(h, s, l);
1367 |
1368 | return this;
1369 | },
1370 |
1371 | setHsl: function() {
1372 | var hsl = this.toHsl();
1373 |
1374 | var h = arguments[0] === null ? hsl[0] : $.limitValue(arguments[0], 0, 1);
1375 | var s = arguments[1] === null ? hsl[1] : $.limitValue(arguments[1], 0, 1);
1376 | var l = arguments[2] === null ? hsl[2] : $.limitValue(arguments[2], 0, 1);
1377 |
1378 | this.fromHsl(h, s, l);
1379 |
1380 | return this;
1381 | }
1382 |
1383 | };
1384 |
1385 | window["cq"] = window["CanvasQuery"] = $;
1386 |
1387 | if (typeof define === "function" && define.amd) {
1388 | define([], function() {
1389 | return $;
1390 | });
1391 | }
1392 |
1393 | })(window);
--------------------------------------------------------------------------------