├── LICENSE ├── README.MD ├── example-1-1:tic-tac-toe └── index.html ├── example-1-2:tic-tac-toe - Path2D version └── index.html ├── example-2:brick └── index.html ├── example-3:picture-frame ├── index.html ├── picture-frame.jpg └── tfboys.jpg ├── example-4-1:loading bar └── index.html ├── example-4-2:loading ring └── index.html ├── example-5-1:balls ├── ball.class.js ├── baseball.png ├── basketball.png ├── football.png ├── index.html └── volleyball.png ├── example-5-2:control balls ├── ball.class.js ├── baseball.png ├── basketball.png ├── football.png ├── index.html └── volleyball.png ├── example-6:photo-edit ├── background.jpg ├── editor.js ├── index.html ├── main.js ├── photo.jpg ├── reset.png ├── style.css └── watermark.png ├── index.html ├── libs ├── flexbox.css └── utils.js ├── thirds ├── alloy_finger.js ├── bluebird.core.min.js ├── keymaster.js ├── normalize.css └── requestAnimationFrame.polyfill.js └── 思维导图 - canvas常用api.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Array Huang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # canvas-learning:分享一些本人学习canvas时用来练习的案例 2 | 3 | ## 案例1: 井字游戏 4 | 5 | ### [案例1-1][1] 6 | - 简单应用了canvas的一些基础API,如绘制基本图形以及路径。 7 | 8 | ### [案例1-2][2] 9 | - 练习使用`Path2D`配合`translate`来复用路径。 10 | 11 | ## [案例2:铺砖][3] 12 | - 练一下批量有规律地输出图形,灵感来自前公司楼下的地砖。 13 | 14 | ## [案例3:相框][4] 15 | - 主要是练习canvas图片相关的API。 16 | 17 | ## 案例4:进度条 18 | - 练习canvas文字相关的API。 19 | - 练习canvas动画。 20 | 21 | ### [案例4-1:长条进度条][5] 22 | 23 | ### [案例4-2:圆形进度条][6] 24 | 25 | ## 案例5:做自由落体运动的球 26 | 27 | ### [案例5-1][7] 28 | - 练习**自由落体运动**的动画。 29 | - 练习在动画里同时处理多个图形。 30 | 31 | ### [案例5-2:用户交互加强版][8] 32 | - 用户可以通过拖动球来改变其位置,若新位置不在画布底部,则开始做自由落体运动。 33 | - 练习canvas用户交互。 34 | 35 | ## [案例6:“过年就是这个味儿”活动页][9] 36 | - 此案例来自于我过往的一个项目,当时是用css3来做的,现在改成用canvas来实现。 37 | - 练习触控手势:pressmove / pinch / rotate。 38 | - 练习canvas的变形相关API:translate / scale / rotate。 39 | 40 | ## 附 **思维导图 - canvas常用api** 41 | ![思维导图 - canvas常用api][10] 42 | 43 | 44 | [1]: https://array-huang.github.io/canvas-learning/example-1-1%EF%BC%9Atic-tac-toe 45 | [2]: https://array-huang.github.io/canvas-learning/example-1-2%EF%BC%9Atic-tac-toe%20-%20Path2D%20version 46 | [3]: https://array-huang.github.io/canvas-learning/example-2%EF%BC%9Abrick 47 | [4]: https://array-huang.github.io/canvas-learning/example-3%EF%BC%9Apicture-frame 48 | [5]: https://array-huang.github.io/canvas-learning/example-4-1%EF%BC%9Aloading%20bar 49 | [6]: https://array-huang.github.io/canvas-learning/example-4-2%EF%BC%9Aloading%20ring 50 | [7]: https://array-huang.github.io/canvas-learning/example-5-1%EF%BC%9Aballs 51 | [8]: https://array-huang.github.io/canvas-learning/example-5-2%EF%BC%9Acontrol%20balls 52 | [9]: https://array-huang.github.io/canvas-learning/example-6%EF%BC%9Aphoto-edit 53 | [10]: https://array-huang.github.io/canvas-learning/%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE%20-%20canvas%E5%B8%B8%E7%94%A8api.png -------------------------------------------------------------------------------- /example-1-1:tic-tac-toe/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 案例1: 井字游戏 6 | 7 | 8 | 9 | 61 | 62 | -------------------------------------------------------------------------------- /example-1-2:tic-tac-toe - Path2D version/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 案例1: 井字游戏 6 | 7 | 8 | 9 | 100 | 101 | -------------------------------------------------------------------------------- /example-2:brick/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 案例2:铺砖 6 | 7 | 8 | 9 | 129 | 130 | -------------------------------------------------------------------------------- /example-3:picture-frame/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 案例3:相框 6 | 7 | 8 | 9 | 10 | 61 | 62 | -------------------------------------------------------------------------------- /example-3:picture-frame/picture-frame.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-3:picture-frame/picture-frame.jpg -------------------------------------------------------------------------------- /example-3:picture-frame/tfboys.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-3:picture-frame/tfboys.jpg -------------------------------------------------------------------------------- /example-4-1:loading bar/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 案例4-1:长条进度条 6 | 7 | 8 | 9 | 10 | 80 | 81 | -------------------------------------------------------------------------------- /example-4-2:loading ring/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 案例4-2:圆形进度条 6 | 7 | 8 | 9 | 10 | 85 | 86 | -------------------------------------------------------------------------------- /example-5-1:balls/ball.class.js: -------------------------------------------------------------------------------- 1 | var HIT_LOST_SPEED = 100; 2 | var g = 600 3 | 4 | var pf = { 5 | calculateCoor: function(ball, t1) { 6 | /* 计算离上次位移经过的时间 */ 7 | var deltaT = t1 - ball.t0; 8 | ball.t0 = t1; 9 | 10 | /* 计算速度 */ 11 | var newV = ball.v0 + g * deltaT; 12 | 13 | /* 计算位移 */ 14 | var s = ball.v0 * deltaT + g * Math.pow(deltaT, 2) / 2; 15 | var newY = ball.y - s; // 球离地面的距离 16 | if (newY < 0) { // 距离小于0表示球已触底 17 | ball.y = 0; // 方便起见,把距离重置为0 18 | /* 19 | 触底反弹,速度方向相反,而且会有一定的衰减,衰减由参数HIT_LOST_SPEED来决定 20 | 如果现时速度小于衰减的量,则直接重置速度为0,表明球已彻底停下 21 | */ 22 | ball.v0 = newV > HIT_LOST_SPEED ? -1 * (newV - HIT_LOST_SPEED) : 0; 23 | } else { 24 | ball.y = newY; // 记录好球的新位置 25 | ball.v0 = newV; // 记录好球的新速度 26 | } 27 | }, 28 | }; 29 | 30 | window.ball = function(params) { 31 | Object.assign(this, params); 32 | } 33 | 34 | window.ball.prototype = { 35 | /* 实例化ball类时需要传入的参数,这些参数是各个球独有的 */ 36 | // x: 0, // 绘制图片的基点,即图片的左下角 37 | // y: 300, // 绘制图片的基点,即图片的左下角 38 | // v0: 0, // 球当前的速度,为正数则表示方向向下,为负数则表示方向向上 39 | // t0: null, // 上次计算位移时记录的时间戳 40 | 41 | /* 设置球的坐标 */ 42 | setCoor: function(x, y) { 43 | this.x = x; 44 | this.y = y; 45 | }, 46 | 47 | /* 计算最新的坐标 */ 48 | getStatus: function(t1) { 49 | pf.calculateCoor(this, t1); 50 | return { 51 | x: this.x, 52 | y: this.y, 53 | v0: this.v0, 54 | }; 55 | }, 56 | }; 57 | -------------------------------------------------------------------------------- /example-5-1:balls/baseball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-5-1:balls/baseball.png -------------------------------------------------------------------------------- /example-5-1:balls/basketball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-5-1:balls/basketball.png -------------------------------------------------------------------------------- /example-5-1:balls/football.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-5-1:balls/football.png -------------------------------------------------------------------------------- /example-5-1:balls/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 案例5:做自由落体运动的球 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 122 | 123 | -------------------------------------------------------------------------------- /example-5-1:balls/volleyball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-5-1:balls/volleyball.png -------------------------------------------------------------------------------- /example-5-2:control balls/ball.class.js: -------------------------------------------------------------------------------- 1 | var HIT_LOST_SPEED = 50; 2 | var g = 600 3 | 4 | var pf = { 5 | calculateCoor: function(ball, t1) { 6 | /* 7 | 以下情况,只记录当前时间戳而不需要计算球坐标(即球静止不动) 8 | - 第一次运行 9 | - 球触底 10 | */ 11 | if (!ball.t0 || (ball.y === 0 && ball.v0 === 0)) { 12 | ball.t0 = t1; 13 | return; 14 | } 15 | 16 | /* 计算离上次位移经过的时间 */ 17 | var deltaT = t1 - ball.t0; 18 | ball.t0 = t1; 19 | 20 | if (ball.isControlled) { // 如果球被控制住了,则不改变它 21 | return; 22 | } 23 | 24 | /* 计算速度 */ 25 | var newV = ball.v0 + g * deltaT; 26 | 27 | /* 计算位移 */ 28 | var s = ball.v0 * deltaT + g * Math.pow(deltaT, 2) / 2; 29 | var newY = ball.y - s; // 球离地面的距离 30 | if (newY < 0) { // 距离小于0表示球已触底 31 | ball.y = 0; // 方便起见,把距离重置为0 32 | /* 33 | 触底反弹,速度方向相反,而且会有一定的衰减,衰减由参数HIT_LOST_SPEED来决定 34 | 如果现时速度小于衰减的量,则直接重置速度为0,表明球已彻底停下 35 | */ 36 | ball.v0 = newV > HIT_LOST_SPEED ? -1 * (newV - HIT_LOST_SPEED) : 0; 37 | } else { 38 | ball.y = newY; // 记录好球的新位置 39 | ball.v0 = newV; // 记录好球的新速度 40 | } 41 | }, 42 | 43 | moveBall: function(mouse, ball, canvas, BALL_SIZE) { 44 | // 需要注意的是,对于球来说,始终是以画布左下角作为坐标原点的 45 | return function() { 46 | /* 使球的中心与鼠标保持一致,以此来计算绘制球的基点(即左下角) */ 47 | ball.x = mouse.x - BALL_SIZE / 2; 48 | ball.x = ball.x < 0 ? 0 : ball.x; 49 | ball.y = (canvas.height - mouse.y) - BALL_SIZE / 2; // 由于鼠标的坐标是以左上角为坐标原点的,因此需要转换坐标 50 | ball.y = ball.y < 0 ? 0 : ball.y; 51 | }; 52 | }, 53 | }; 54 | 55 | window.ball = function(params) { 56 | Object.assign(this, params); 57 | } 58 | 59 | window.ball.prototype = { 60 | /* 实例化ball类时需要传入的参数,这些参数是各个球独有的 */ 61 | // x: 0, // 绘制图片的基点,即图片的左下角 62 | // y: 300, // 绘制图片的基点,即图片的左下角 63 | // v0: 0, // 球当前的速度,为正数则表示方向向下,为负数则表示方向向上 64 | // t0: null, // 上次计算位移时记录的时间戳 65 | // isControlled: false, // 是否被控制住了 66 | 67 | /* 设置球的坐标 */ 68 | setCoor: function(x, y) { 69 | this.x = x; 70 | this.y = y; 71 | }, 72 | 73 | /* 计算最新的坐标 */ 74 | getStatus: function(t1) { 75 | pf.calculateCoor(this, t1); 76 | return { 77 | x: this.x, 78 | y: this.y, 79 | v0: this.v0, 80 | }; 81 | }, 82 | 83 | /* 控制住球 */ 84 | control: function(element, mouse, BALL_SIZE) { 85 | var nowBall = this; 86 | this.isControlled = true; 87 | this.v0 = 0; // 因为被控制住了,速度归零 88 | this.t0 = null; // “上次计算坐标”的时间戳重置 89 | var mouseMoveCb = pf.moveBall(mouse, this, element, BALL_SIZE); 90 | element.addEventListener('mousemove', mouseMoveCb); // 鼠标移动时触发,用于保持球跟随鼠标移动 91 | var mouseUpCb = function() { // 解绑事件,解除控制状态 92 | element.removeEventListener('mousemove', mouseMoveCb); 93 | element.removeEventListener('mouseup', mouseUpCb); 94 | nowBall.isControlled = false; 95 | }; 96 | element.addEventListener('mouseup', mouseUpCb); // 鼠标按键弹起时触发,相当于结束控制 97 | }, 98 | }; 99 | -------------------------------------------------------------------------------- /example-5-2:control balls/baseball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-5-2:control balls/baseball.png -------------------------------------------------------------------------------- /example-5-2:control balls/basketball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-5-2:control balls/basketball.png -------------------------------------------------------------------------------- /example-5-2:control balls/football.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-5-2:control balls/football.png -------------------------------------------------------------------------------- /example-5-2:control balls/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 案例5-2:用户交互加强版 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

说明:各球可拖拽,松开鼠标自由落体

14 | 143 | 144 | -------------------------------------------------------------------------------- /example-5-2:control balls/volleyball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-5-2:control balls/volleyball.png -------------------------------------------------------------------------------- /example-6:photo-edit/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-6:photo-edit/background.jpg -------------------------------------------------------------------------------- /example-6:photo-edit/editor.js: -------------------------------------------------------------------------------- 1 | /* 照片编辑器的单例 */ 2 | window.editorObject = { 3 | canvas: null, // 存放canvas的element对象 4 | context: null, // 存放canvas的context 5 | canvasSize: { // 的宽高 6 | w: 0, 7 | h: 0, 8 | }, 9 | 10 | watermark: { // 水印相关信息 11 | img: null, // 水印img的element对象 12 | coor: { // 绘制水印的基点 13 | x: 0, 14 | y: 0, 15 | }, 16 | size: { // 绘制水印的大小,注意不一定等于水印图片的实际大小 17 | width: 0, 18 | height: 0, 19 | } 20 | }, 21 | 22 | photo: { // 照片的相关信息 23 | img: null, // 照片img的element对象 24 | translate: { // 绘制照片时,canvas坐标轴移动的横距离和纵距离 25 | x: 0, 26 | y: 0, 27 | }, 28 | rotate: 0, // 绘制图片时,canvas坐标轴旋转的弧度 29 | initScale: 1, // 图片缩放动作开始时的原始scale 30 | scale: 1, // 图片缩放动作结束时,正式绘制的scale 31 | }, 32 | 33 | init: function(params) { // 初始化,主要是赋初值以及绑定各种事件 34 | this.canvas = params.canvas; 35 | this.context = this.canvas.getContext('2d'); 36 | this.canvasSize = { 37 | w: this.canvas.width, 38 | h: this.canvas.height, 39 | }; 40 | this.watermark = params.watermark; 41 | this.photo.img = params.photo.img; 42 | this.photo.translate = { // canvas坐标轴的原点设在画布的正中心,也是图片本身的正中心 43 | x: this.canvasSize.w / 2, 44 | y: this.canvasSize.h / 2, 45 | }; 46 | 47 | this.context.save(); // 保存canvas的原始状态 48 | 49 | this._render(); // 根据初始参数绘制水印和图片 50 | 51 | /* 绑定手势事件 */ 52 | var that = this; 53 | new window.AlloyFinger(this.canvas, { 54 | multipointStart: function () { 55 | that.photo.initScale = that.photo.scale; // 记录图片缩放动作开始时的原始scale 56 | }, 57 | rotate: function (evt) { 58 | that._rotate(evt.angle * Math.PI / 180); // 这里的evt.angle是度数,而canvas使用的是弧度,需要先进行单位转换 59 | }, 60 | pinch: function (evt) { 61 | that._scale(evt.scale); 62 | }, 63 | pressMove: function (evt) { 64 | that._translate(evt.deltaX, evt.deltaY); 65 | evt.preventDefault(); 66 | }, 67 | }) 68 | 69 | return this; 70 | }, 71 | 72 | changeWatermarkCoor: function(coor) { // 切换水印位置 73 | this.watermark.coor = coor; 74 | this._render(); 75 | }, 76 | 77 | _translate: function(deltaX, deltaY) { // 通过调整canvas的坐标原点来调整图片的位置 78 | this.photo.translate.x += deltaX; 79 | this.photo.translate.y += deltaY; 80 | this._render(); 81 | }, 82 | 83 | _rotate: function(angle) { // 通过调整canvas坐标轴的旋转弧度来调整图片的旋转弧度 84 | this.photo.rotate += angle; 85 | this._render(); 86 | }, 87 | 88 | _scale: function(scale) { // 通过调整canvas坐标轴的scale来调整图片的scale 89 | this.photo.scale = this.photo.initScale * scale; // 根据缩放动作前的scale乘以这次缩放的scale来确定最终的scale(即相对于原始scale) 90 | this._render(); 91 | }, 92 | 93 | reset: function() { // 重置图片的所有参数 94 | /* 恢复移动距离 */ 95 | this.photo.translate = { 96 | x: this.canvasSize.w / 2, 97 | y: this.canvasSize.h / 2, 98 | }; 99 | /* 恢复旋转角度 */ 100 | this.photo.rotate = 0; 101 | /* 恢复缩放 */ 102 | this.photo.scale = 1; 103 | 104 | this._render(); 105 | 106 | return this; 107 | }, 108 | 109 | output: function() { // 将canvas输出为DataUrl 110 | return this.canvas.toDataURL('image/jpeg', 0.8); 111 | }, 112 | 113 | _resetCanvas: function() { // 恢复到canvas的原始状态 114 | this.context.restore(); 115 | this.context.save(); // 记得restore后要save一次,否则下次就无法恢复到原始状态了 116 | return this; 117 | }, 118 | 119 | _clean: function() { // 清空canvas 120 | this.context.clearRect(0, 0, this.canvasSize.w, this.canvasSize.h); 121 | return this; 122 | }, 123 | 124 | _render: function() { 125 | this._clean(); // 绘制前先清空 126 | 127 | (function _renderPhoto(that) { // 根据图片参数来绘制图片 128 | that._resetCanvas(); 129 | that.context.translate(that.photo.translate.x, that.photo.translate.y); 130 | that.context.rotate(that.photo.rotate); 131 | that.context.scale(that.photo.scale, that.photo.scale); 132 | that.context.drawImage(that.photo.img, -that.canvasSize.w / 2, -that.canvasSize.h / 2, that.canvasSize.w, that.canvasSize.h); 133 | })(this); 134 | 135 | (function _renderWatermark(that) { // 根据水印参数来绘制水印 136 | that._resetCanvas(); 137 | that.context.drawImage(that.watermark.img, that.watermark.coor.x, that.watermark.coor.y, that.watermark.size.width, that.watermark.size.height); 138 | })(this); 139 | 140 | }, 141 | }; -------------------------------------------------------------------------------- /example-6:photo-edit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 案例6:“过年就是这个味儿”活动页 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 |
生成图片
26 |
27 | 28 |
29 | 30 |
再玩一遍
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example-6:photo-edit/main.js: -------------------------------------------------------------------------------- 1 | var WATERMARK_LOCATION = [ // 水印的几个可选位置 2 | { x: 5, y: 0}, { x: 5, y: 223}, { x: 50, y: 100}, { x: 100, y: 0}, { x: 100, y: 223}, 3 | ]; 4 | var watermarkSamples = getSamplesElement(WATERMARK_LOCATION); 5 | var WATERMARK_SIZE = { 6 | width: 100, 7 | height: 77, 8 | }; 9 | 10 | /* 用于在水印可选位置示例中绘制水印 */ 11 | function drawWatermark(canvas, img, coor) { 12 | var context = canvas.getContext('2d'); 13 | context.drawImage(img, coor.x, coor.y, WATERMARK_SIZE.width, WATERMARK_SIZE.height); 14 | } 15 | /* 整理一下水印可选位置示例的数据 */ 16 | function getSamplesElement(WATERMARK_LOCATION) { 17 | var samples = []; 18 | var elements = document.querySelectorAll('.js-sample'); 19 | for (var i = 0; i < 5; i ++) { 20 | samples.push({ 21 | element: elements[i], 22 | coor: WATERMARK_LOCATION[i], 23 | }); 24 | } 25 | return samples; 26 | } 27 | 28 | (function() { 29 | /* 获取图片的promise */ 30 | var photoPromise = new Promise(function(resolve) { 31 | var img = new Image(); 32 | img.src = './photo.jpg'; 33 | img.onload = function() { 34 | resolve(img); 35 | }; 36 | }); 37 | /* 获取水印的promise */ 38 | var watermarkPromise = new Promise(function(resolve) { 39 | var img = new Image(); 40 | img.src = './watermark.png'; 41 | img.onload = function() { 42 | resolve(img); 43 | }; 44 | }); 45 | /* 成功获取图片和水印后再开始执行 */ 46 | Promise.all([photoPromise, watermarkPromise]).then(function(imgs) { 47 | var editor = window.editorObject.init({ // 传入参数初始化图片编辑器 48 | canvas: document.getElementById('editor'), 49 | watermark: { 50 | img: imgs[1], 51 | coor: watermarkSamples[0].coor, 52 | size: WATERMARK_SIZE, 53 | }, 54 | photo: { 55 | img: imgs[0], 56 | }, 57 | }); 58 | 59 | /* 重置按钮 */ 60 | document.getElementById('reset-btn').addEventListener('click', function() { 61 | editor.reset(); 62 | }); 63 | 64 | /* 生成图片的按钮 */ 65 | document.getElementById('submit-btn').addEventListener('click', function() { 66 | document.getElementById('final-product').src = editor.output(); 67 | document.getElementById('editor-container').style.display = 'none'; 68 | document.getElementById('display-container').style.display = 'block'; 69 | }); 70 | 71 | /* 再玩一遍的按钮 */ 72 | document.getElementById('again-btn').addEventListener('click', function() { 73 | document.getElementById('display-container').style.display = 'none'; 74 | document.getElementById('editor-container').style.display = 'block'; 75 | }); 76 | 77 | for (var i = 0; i < 5; i ++) { 78 | watermarkSamples[i].element.addEventListener('click', function(evt) { // 点击水印可选位置示例后更新图片编辑器的水印位置 79 | editor.changeWatermarkCoor(watermarkSamples[evt.target.dataset.index].coor); 80 | }); 81 | drawWatermark(watermarkSamples[i].element, imgs[1], watermarkSamples[i].coor); // 绘制水印可选位置示例 82 | } 83 | }); 84 | })(); -------------------------------------------------------------------------------- /example-6:photo-edit/photo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-6:photo-edit/photo.jpg -------------------------------------------------------------------------------- /example-6:photo-edit/reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-6:photo-edit/reset.png -------------------------------------------------------------------------------- /example-6:photo-edit/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | body { 7 | background: url(./background.jpg); 8 | background-size: 100% 100%; 9 | -moz-background-size: 100% 100%; 10 | background-repeat: no-repeat; 11 | } 12 | 13 | #editor-container { 14 | padding-top: 50px; 15 | } 16 | 17 | canvas { 18 | background-color: #000; 19 | } 20 | 21 | #editor { 22 | display: block; 23 | margin: 0 auto 5px; 24 | } 25 | 26 | #watermark-samples-container { 27 | width: 200px; 28 | margin: 0 auto; 29 | } 30 | 31 | #watermark-samples-container canvas { 32 | width: 0%; 33 | height: 77px; 34 | margin-left: 2px; 35 | } 36 | 37 | #watermark-samples-container canvas:first-child { 38 | margin-left: 0; 39 | } 40 | 41 | #reset-btn { 42 | width: 35px; 43 | position: absolute; 44 | right: 0; 45 | top: 200px; 46 | } 47 | 48 | #submit-btn, #again-btn { 49 | margin: 15px auto 0 auto; 50 | width: 200px; 51 | height: 30px; 52 | box-sizing: border-box; 53 | border: 1px solid #eee; 54 | text-align: center; 55 | line-height: 28px; 56 | color: #eee; 57 | } 58 | 59 | #submit-btn:active, #again-btn:active { 60 | color: yellow; 61 | border: 1px solid yellow; 62 | } 63 | 64 | #display-container { 65 | display: none; 66 | padding-top: 50px; 67 | } 68 | 69 | #final-product { 70 | display: block; 71 | margin: 0 auto; 72 | } -------------------------------------------------------------------------------- /example-6:photo-edit/watermark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/example-6:photo-edit/watermark.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | canvas-learning:分享一些本人学习canvas时用来练习的案例 3 | 4 | 5 | 6 | 7 | 8 | 804 | 805 |
806 | 807 |

canvas-learning:分享一些本人学习canvas时用来练习的案例

808 |

案例1: 井字游戏

809 |

案例1-1

810 |
    811 |
  • 简单应用了canvas的一些基础API,如绘制基本图形以及路径。

  • 812 |
813 |

案例1-2

814 |
    815 |
  • 练习使用Path2D配合translate来复用路径。

  • 816 |
817 |

案例2:铺砖

818 |
    819 |
  • 练一下批量有规律地输出图形,灵感来自前公司楼下的地砖。

  • 820 |
821 |

案例3:相框

822 |
    823 |
  • 主要是练习canvas图片相关的API。

  • 824 |
825 |

案例4:进度条

826 |
    827 |
  • 练习canvas文字相关的API。

  • 828 |
  • 练习canvas动画。

  • 829 |
830 |

案例4-1:长条进度条

831 |

案例4-2:圆形进度条

832 |

案例5:做自由落体运动的球

833 |

案例5-1

834 |
    835 |
  • 练习自由落体运动的动画。

  • 836 |
  • 练习在动画里同时处理多个图形。

  • 837 |
838 |

案例5-2:用户交互加强版

839 |
    840 |
  • 用户可以通过拖动球来改变其位置,若新位置不在画布底部,则开始做自由落体运动。

  • 841 |
  • 练习canvas用户交互。

  • 842 |
843 |

案例6:“过年就是这个味儿”活动页

844 |
    845 |
  • 此案例来自于我过往的一个项目,当时是用css3来做的,现在改成用canvas来实现。

  • 846 |
  • 练习触控手势:pressmove / pinch / rotate。

  • 847 |
  • 练习canvas的变形相关API:translate / scale / rotate。

  • 848 |
849 |

思维导图 - canvas常用api

850 |

思维导图 - canvas常用api

思维导图 - canvas常用api

851 |
852 | 853 |
854 | 855 | 856 | -------------------------------------------------------------------------------- /libs/flexbox.css: -------------------------------------------------------------------------------- 1 | /* 源自https://gist.github.com/HeGanjie/0289b5b16e2ee4b16030 */ 2 | 3 | /* 给父元素设置为flex布局 */ 4 | .flex { 5 | display: -webkit-box; 6 | display: -moz-box; 7 | display: -ms-flexbox; 8 | display: -webkit-flex; 9 | display: flex; 10 | } 11 | 12 | /* 13 | * 垂直排列的flex布局,需配合.flex使用 14 | * 注意有可能有兼容性问题 15 | * 理论上来说应该是兼容ios 4+、android 2.3+、winphone8+ 16 | * 但实测海马玩模拟器异常,可以给子元素添上 'display: block;' 来兼容 17 | */ 18 | .flex--column { 19 | -webkit-box-orient: vertical; 20 | -moz-box-orient: vertical; 21 | -webkit-box-direction: normal; 22 | -moz-box-direction: normal; 23 | -webkit-flex-direction: column; 24 | -ms-flex-direction: column; 25 | flex-direction: column; 26 | } 27 | 28 | /* 子元素在父元素内部水平方向 - 两端对齐,项目之间的间隔都相等 */ 29 | .flex--justify-content--space-between { 30 | -webkit-box-pack: justify; 31 | -moz-box-pack: justify; 32 | -ms-flex-pack: justify; 33 | -webkit-justify-content: space-between; 34 | justify-content: space-between; 35 | } 36 | 37 | /* 子元素在父元素内部水平方向 - 每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。 */ 38 | .flex--justify-content--space-around { 39 | -webkit-box-pack: justify; // 相当于 space-between 40 | -moz-box-pack: justify; // 相当于 space-between 41 | -ms-flex-pack: distribute; 42 | -webkit-justify-content: space-around; 43 | justify-content: space-around; 44 | } 45 | 46 | /* 子元素在父元素内部水平方向 - 居中 */ 47 | .flex--justify-content--center { 48 | -webkit-box-pack: center; 49 | -moz-box-pack: center; 50 | -ms-flex-pack: center; 51 | -webkit-justify-content: center; 52 | justify-content: center; 53 | } 54 | 55 | /* 子元素在父元素内部水平方向 - 左对齐 */ 56 | .flex--justify-content--start { 57 | -webkit-box-pack: start; 58 | -moz-box-pack: start; 59 | -ms-flex-pack: start; 60 | -webkit-justify-content: flex-start; 61 | justify-content: flex-start; 62 | } 63 | 64 | /* 子元素在父元素内部水平方向 - 右对齐 */ 65 | .flex--justify-content--end { 66 | -webkit-box-pack: end; 67 | -moz-box-pack: end; 68 | -ms-flex-pack: end; 69 | -webkit-justify-content: flex-end; 70 | justify-content: flex-end; 71 | } 72 | 73 | /* 子元素在父元素内部垂直方向 - 顶部对齐 */ 74 | .flex--align-items--start { 75 | -webkit-box-align: start; 76 | -moz-box-align: start; 77 | -ms-flex-align: start; 78 | -webkit-align-items: flex-start; 79 | align-items: flex-start; 80 | } 81 | 82 | /* 子元素在父元素内部垂直方向 - 中间对齐 */ 83 | .flex--align-items--center { 84 | -webkit-box-align: center; 85 | -moz-box-align: center; 86 | -ms-flex-align: center; 87 | -webkit-align-items: center; 88 | align-items: center; 89 | } 90 | 91 | /* 子元素在父元素内部剩余空间的分配比例 */ 92 | .flex--box-flex-1 { 93 | -webkit-box-flex: 1; 94 | -moz-box-flex: 1; 95 | -webkit-flex-grow: 1; 96 | -ms-flex-grow: 1; 97 | flex-grow: 1; 98 | // 在旧版的规范中,使用比例伸缩布局时, 99 | // 子元素的内容长短不同会导致无法“等分”, 100 | // 这个时候,我们需要给子元素设置一个'width: 0%;' 101 | width: 0%; 102 | } -------------------------------------------------------------------------------- /libs/utils.js: -------------------------------------------------------------------------------- 1 | /* 计算鼠标或触摸在某元素内部的坐标 */ 2 | var calculateCoorInElement = function(originCoor, element) { 3 | var x, y; // 整个文档的坐标 4 | if (originCoor.pageX || originCoor.pageY) { 5 | x = originCoor.pageX; 6 | y = originCoor.pageY; 7 | } else { // 兼容IE系列浏览器 8 | x = originCoor.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 9 | y = originCoor.clientY + document.body.scrollTop + document.documentElement.scrollTop; 10 | } 11 | 12 | return { 13 | x: x - element.offsetLeft, 14 | y: y - element.offsetTop, 15 | }; 16 | } 17 | 18 | var utils = { 19 | mouseInited: false, 20 | mouse: { 21 | x: null, 22 | y: null, 23 | isPressed: false, 24 | }, 25 | 26 | /* 获取鼠标在画布内的坐标 */ 27 | captureMouse: function(element) { 28 | if (utils.mouseInited) { // 单例模式 29 | return utils.mouse; 30 | } 31 | 32 | element.addEventListener('mousedown', function() { 33 | utils.mouse.isPressed = true; 34 | }); 35 | 36 | element.addEventListener('mouseup', function() { 37 | utils.mouse.isPressed = false; 38 | }); 39 | 40 | element.addEventListener('mousemove', function(evt) { 41 | var coor = calculateCoorInElement(evt, evt.target); 42 | 43 | utils.mouse.x = coor.x; 44 | utils.mouse.y = coor.y; 45 | }); 46 | 47 | utils.mouseInited = true; 48 | 49 | return utils.mouse; 50 | }, 51 | 52 | touchInited: false, 53 | touch: { 54 | x: null, 55 | y: null, 56 | isPressed: false, 57 | }, 58 | 59 | /* 获取触摸相关的属性 */ 60 | captureTouch: function(element) { 61 | if (utils.touchInited) { // 单例模式 62 | return utils.touch; 63 | } 64 | 65 | element.addEventListener('touchstart', function(evt) { 66 | utils.touch.isPressed = true; 67 | }); 68 | 69 | element.addEventListener('touchend', function(evt) { 70 | utils.touch.x = null; 71 | utils.touch.y = null; 72 | utils.touch.isPressed = false; 73 | }); 74 | 75 | element.addEventListener('touchmove', function(evt) { 76 | var touch = evt.touches[0]; // 只考虑单点触控,因此取数组第一个元素 77 | var coor = calculateCoorInElement(touch, evt.target); 78 | utils.touch.x = coor.x; 79 | utils.touch.y = coor.y; 80 | }); 81 | 82 | utils.touchInited = true; 83 | 84 | return utils.touch; 85 | }, 86 | }; 87 | 88 | window.utils = utils; 89 | -------------------------------------------------------------------------------- /thirds/alloy_finger.js: -------------------------------------------------------------------------------- 1 | /* AlloyFinger v0.1.2 2 | * By dntzhang 3 | * Github: https://github.com/AlloyTeam/AlloyFinger 4 | */ 5 | ;(function() { 6 | function getLen(v) { 7 | return Math.sqrt(v.x * v.x + v.y * v.y); 8 | } 9 | 10 | function dot(v1, v2) { 11 | return v1.x * v2.x + v1.y * v2.y; 12 | } 13 | 14 | function getAngle(v1, v2) { 15 | var mr = getLen(v1) * getLen(v2); 16 | if (mr === 0) return 0; 17 | var r = dot(v1, v2) / mr; 18 | if (r > 1) r = 1; 19 | return Math.acos(r); 20 | } 21 | 22 | function cross(v1, v2) { 23 | return v1.x * v2.y - v2.x * v1.y; 24 | } 25 | 26 | function getRotateAngle(v1, v2) { 27 | var angle = getAngle(v1, v2); 28 | if (cross(v1, v2) > 0) { 29 | angle *= -1; 30 | } 31 | 32 | return angle * 180 / Math.PI; 33 | } 34 | var AlloyFinger = function (el, option) { 35 | 36 | el.addEventListener("touchstart", this.start.bind(this), false); 37 | el.addEventListener("touchmove", this.move.bind(this), false); 38 | el.addEventListener("touchend", this.end.bind(this), false); 39 | el.addEventListener("touchcancel",this.cancel.bind(this),false); 40 | 41 | this.preV = { x: null, y: null }; 42 | this.pinchStartLen = null; 43 | this.scale = 1; 44 | this.isDoubleTap = false; 45 | this.rotate = option.rotate || function () { }; 46 | this.touchStart = option.touchStart || function () { }; 47 | this.multipointStart = option.multipointStart || function () { }; 48 | this.multipointEnd=option.multipointEnd||function(){}; 49 | this.pinch = option.pinch || function () { }; 50 | this.swipe = option.swipe || function () { }; 51 | this.tap = option.tap || function () { }; 52 | this.doubleTap = option.doubleTap || function () { }; 53 | this.longTap = option.longTap || function () { }; 54 | this.singleTap = option.singleTap || function () { }; 55 | this.pressMove = option.pressMove || function () { }; 56 | this.touchMove = option.touchMove || function () { }; 57 | this.touchEnd = option.touchEnd || function () { }; 58 | this.touchCancel = option.touchCancel || function () { }; 59 | 60 | this.delta = null; 61 | this.last = null; 62 | this.now = null; 63 | this.tapTimeout = null; 64 | this.touchTimeout = null; 65 | this.longTapTimeout = null; 66 | this.swipeTimeout=null; 67 | this.x1 = this.x2 = this.y1 = this.y2 = null; 68 | this.preTapPosition={x:null,y:null}; 69 | }; 70 | 71 | AlloyFinger.prototype = { 72 | start: function (evt) { 73 | if(!evt.touches)return; 74 | this.now = Date.now(); 75 | this.x1 = evt.touches[0].pageX; 76 | this.y1 = evt.touches[0].pageY; 77 | this.delta = this.now - (this.last || this.now); 78 | this.touchStart(evt); 79 | if(this.preTapPosition.x!==null){ 80 | this.isDoubleTap = (this.delta > 0 && this.delta <= 250&&Math.abs(this.preTapPosition.x-this.x1)<30&&Math.abs(this.preTapPosition.y-this.y1)<30); 81 | } 82 | this.preTapPosition.x=this.x1; 83 | this.preTapPosition.y=this.y1; 84 | this.last = this.now; 85 | var preV = this.preV, 86 | len = evt.touches.length; 87 | if (len > 1) { 88 | this._cancelLongTap(); 89 | var v = { x: evt.touches[1].pageX - this.x1, y: evt.touches[1].pageY - this.y1 }; 90 | preV.x = v.x; 91 | preV.y = v.y; 92 | this.pinchStartLen = getLen(preV); 93 | this.multipointStart(evt); 94 | } 95 | this.longTapTimeout = setTimeout(function(){ 96 | this.longTap(evt); 97 | }.bind(this), 750); 98 | }, 99 | move: function (evt) { 100 | if(!evt.touches)return; 101 | var preV = this.preV, 102 | len = evt.touches.length, 103 | currentX = evt.touches[0].pageX, 104 | currentY = evt.touches[0].pageY; 105 | this.isDoubleTap=false; 106 | if (len > 1) { 107 | var v = { x: evt.touches[1].pageX - currentX, y: evt.touches[1].pageY - currentY }; 108 | 109 | if (preV.x !== null) { 110 | if (this.pinchStartLen > 0) { 111 | evt.scale = getLen(v) / this.pinchStartLen; 112 | this.pinch(evt); 113 | } 114 | 115 | evt.angle = getRotateAngle(v, preV); 116 | this.rotate(evt); 117 | } 118 | preV.x = v.x; 119 | preV.y = v.y; 120 | } else { 121 | if (this.x2 !== null) { 122 | evt.deltaX = currentX - this.x2; 123 | evt.deltaY = currentY - this.y2; 124 | 125 | }else{ 126 | evt.deltaX = 0; 127 | evt.deltaY = 0; 128 | } 129 | this.pressMove(evt); 130 | } 131 | 132 | this.touchMove(evt); 133 | 134 | this._cancelLongTap(); 135 | this.x2 = currentX; 136 | this.y2 = currentY; 137 | if (evt.touches.length > 1) { 138 | this._cancelLongTap(); 139 | evt.preventDefault(); 140 | } 141 | }, 142 | end: function (evt) { 143 | if(!evt.changedTouches)return; 144 | this._cancelLongTap(); 145 | var self = this; 146 | if( evt.touches.length<2){ 147 | this.multipointEnd(evt); 148 | } 149 | this.touchEnd(evt); 150 | //swipe 151 | if ((this.x2 && Math.abs(this.x1 - this.x2) > 30) || 152 | (this.y2 && Math.abs(this.preV.y - this.y2) > 30)) { 153 | evt.direction = this._swipeDirection(this.x1, this.x2, this.y1, this.y2); 154 | this.swipeTimeout = setTimeout(function () { 155 | self.swipe(evt); 156 | 157 | }, 0) 158 | } else { 159 | this.tapTimeout = setTimeout(function () { 160 | self.tap(evt); 161 | // trigger double tap immediately 162 | if (self.isDoubleTap) { 163 | self.doubleTap(evt); 164 | clearTimeout(self.touchTimeout); 165 | self.isDoubleTap = false; 166 | }else{ 167 | self.touchTimeout=setTimeout(function(){ 168 | self.singleTap(evt); 169 | },250); 170 | } 171 | }, 0) 172 | } 173 | 174 | this.preV.x = 0; 175 | this.preV.y = 0; 176 | this.scale = 1; 177 | this.pinchStartLen = null; 178 | this.x1 = this.x2 = this.y1 = this.y2 = null; 179 | }, 180 | cancel:function(evt){ 181 | clearTimeout(this.touchTimeout); 182 | clearTimeout(this.tapTimeout); 183 | clearTimeout(this.longTapTimeout); 184 | clearTimeout(this.swipeTimeout); 185 | this.touchCancel(evt); 186 | }, 187 | _cancelLongTap: function () { 188 | clearTimeout(this.longTapTimeout); 189 | }, 190 | _swipeDirection: function (x1, x2, y1, y2) { 191 | return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down') 192 | } 193 | 194 | 195 | }; 196 | 197 | if (typeof module !== 'undefined' && typeof exports === 'object') { 198 | module.exports = AlloyFinger; 199 | }else { 200 | window.AlloyFinger = AlloyFinger; 201 | } 202 | })(); 203 | -------------------------------------------------------------------------------- /thirds/bluebird.core.min.js: -------------------------------------------------------------------------------- 1 | /* @preserve 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2013-2015 Petka Antonov 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | /** 26 | * bluebird build version 3.4.6 27 | * Features enabled: core 28 | * Features disabled: race, call_get, generators, map, nodeify, promisify, props, reduce, settle, some, using, timers, filter, any, each 29 | */ 30 | !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;"undefined"!=typeof window?e=window:"undefined"!=typeof global?e=global:"undefined"!=typeof self&&(e=self),e.Promise=t()}}(function(){var t,e,n;return function r(t,e,n){function i(a,s){if(!e[a]){if(!t[a]){var c="function"==typeof _dereq_&&_dereq_;if(!s&&c)return c(a,!0);if(o)return o(a,!0);var l=new Error("Cannot find module '"+a+"'");throw l.code="MODULE_NOT_FOUND",l}var u=e[a]={exports:{}};t[a][0].call(u.exports,function(e){var n=t[a][1][e];return i(n?n:e)},u,u.exports,r,t,e,n)}return e[a].exports}for(var o="function"==typeof _dereq_&&_dereq_,a=0;a0;){var e=t.shift();if("function"==typeof e){var n=t.shift(),r=t.shift();e.call(n,r)}else e._settlePromises()}},r.prototype._drainQueues=function(){this._drainQueue(this._normalQueue),this._reset(),this._haveDrainedQueues=!0,this._drainQueue(this._lateQueue)},r.prototype._queueTick=function(){this._isTickUsed||(this._isTickUsed=!0,this._schedule(this.drainQueues))},r.prototype._reset=function(){this._isTickUsed=!1},e.exports=r,e.exports.firstLineError=s},{"./queue":17,"./schedule":18,"./util":21}],2:[function(t,e,n){"use strict";e.exports=function(t,e,n,r){var i=!1,o=function(t,e){this._reject(e)},a=function(t,e){e.promiseRejectionQueued=!0,e.bindingPromise._then(o,o,null,this,t)},s=function(t,e){0===(50397184&this._bitField)&&this._resolveCallback(e.target)},c=function(t,e){e.promiseRejectionQueued||this._reject(t)};t.prototype.bind=function(o){i||(i=!0,t.prototype._propagateFrom=r.propagateFromFunction(),t.prototype._boundValue=r.boundValueFunction());var l=n(o),u=new t(e);u._propagateFrom(this,1);var p=this._target();if(u._setBoundTo(l),l instanceof t){var f={promiseRejectionQueued:!1,promise:u,target:p,bindingPromise:l};p._then(e,a,void 0,u,f),l._then(s,c,void 0,u,f),u._setOnCancel(l)}else u._resolveCallback(p);return u},t.prototype._setBoundTo=function(t){void 0!==t?(this._bitField=2097152|this._bitField,this._boundTo=t):this._bitField=-2097153&this._bitField},t.prototype._isBound=function(){return 2097152===(2097152&this._bitField)},t.bind=function(e,n){return t.resolve(n).bind(e)}}},{}],3:[function(t,e,n){"use strict";function r(){try{Promise===o&&(Promise=i)}catch(t){}return o}var i;"undefined"!=typeof Promise&&(i=Promise);var o=t("./promise")();o.noConflict=r,e.exports=o},{"./promise":15}],4:[function(t,e,n){"use strict";e.exports=function(e,n,r,i){var o=t("./util"),a=o.tryCatch,s=o.errorObj,c=e._async;e.prototype["break"]=e.prototype.cancel=function(){if(!i.cancellation())return this._warn("cancellation is disabled");for(var t=this,e=t;t._isCancellable();){if(!t._cancelBy(e)){e._isFollowing()?e._followee().cancel():e._cancelBranched();break}var n=t._cancellationParent;if(null==n||!n._isCancellable()){t._isFollowing()?t._followee().cancel():t._cancelBranched();break}t._isFollowing()&&t._followee().cancel(),t._setWillBeCancelled(),e=t,t=n}},e.prototype._branchHasCancelled=function(){this._branchesRemainingToCancel--},e.prototype._enoughBranchesHaveCancelled=function(){return void 0===this._branchesRemainingToCancel||this._branchesRemainingToCancel<=0},e.prototype._cancelBy=function(t){return t===this?(this._branchesRemainingToCancel=0,this._invokeOnCancel(),!0):(this._branchHasCancelled(),this._enoughBranchesHaveCancelled()?(this._invokeOnCancel(),!0):!1)},e.prototype._cancelBranched=function(){this._enoughBranchesHaveCancelled()&&this._cancel()},e.prototype._cancel=function(){this._isCancellable()&&(this._setCancelled(),c.invoke(this._cancelPromises,this,void 0))},e.prototype._cancelPromises=function(){this._length()>0&&this._settlePromises()},e.prototype._unsetOnCancel=function(){this._onCancelField=void 0},e.prototype._isCancellable=function(){return this.isPending()&&!this._isCancelled()},e.prototype.isCancellable=function(){return this.isPending()&&!this.isCancelled()},e.prototype._doInvokeOnCancel=function(t,e){if(o.isArray(t))for(var n=0;n=0?o[t]:void 0}var i=!1,o=[];return t.prototype._promiseCreated=function(){},t.prototype._pushContext=function(){},t.prototype._popContext=function(){return null},t._peekContext=t.prototype._peekContext=function(){},e.prototype._pushContext=function(){void 0!==this._trace&&(this._trace._promiseCreated=null,o.push(this._trace))},e.prototype._popContext=function(){if(void 0!==this._trace){var t=o.pop(),e=t._promiseCreated;return t._promiseCreated=null,e}return null},e.CapturedTrace=null,e.create=n,e.deactivateLongStackTraces=function(){},e.activateLongStackTraces=function(){var n=t.prototype._pushContext,o=t.prototype._popContext,a=t._peekContext,s=t.prototype._peekContext,c=t.prototype._promiseCreated;e.deactivateLongStackTraces=function(){t.prototype._pushContext=n,t.prototype._popContext=o,t._peekContext=a,t.prototype._peekContext=s,t.prototype._promiseCreated=c,i=!1},i=!0,t.prototype._pushContext=e.prototype._pushContext,t.prototype._popContext=e.prototype._popContext,t._peekContext=t.prototype._peekContext=r,t.prototype._promiseCreated=function(){var t=this._peekContext();t&&null==t._promiseCreated&&(t._promiseCreated=this)}},e}},{}],7:[function(t,e,n){"use strict";e.exports=function(e,n){function r(t,e){return{promise:e}}function i(){return!1}function o(t,e,n){var r=this;try{t(e,n,function(t){if("function"!=typeof t)throw new TypeError("onCancel must be a function, got: "+I.toString(t));r._attachCancellationCallback(t)})}catch(i){return i}}function a(t){if(!this._isCancellable())return this;var e=this._onCancel();void 0!==e?I.isArray(e)?e.push(t):this._setOnCancel([e,t]):this._setOnCancel(t)}function s(){return this._onCancelField}function c(t){this._onCancelField=t}function l(){this._cancellationParent=void 0,this._onCancelField=void 0}function u(t,e){if(0!==(1&e)){this._cancellationParent=t;var n=t._branchesRemainingToCancel;void 0===n&&(n=0),t._branchesRemainingToCancel=n+1}0!==(2&e)&&t._isBound()&&this._setBoundTo(t._boundTo)}function p(t,e){0!==(2&e)&&t._isBound()&&this._setBoundTo(t._boundTo)}function f(){var t=this._boundTo;return void 0!==t&&t instanceof e?t.isFulfilled()?t.value():void 0:t}function h(){this._trace=new S(this._peekContext())}function _(t,e){if(H(t)){var n=this._trace;if(void 0!==n&&e&&(n=n._parent),void 0!==n)n.attachExtraTrace(t);else if(!t.__stackCleaned__){var r=k(t);I.notEnumerableProp(t,"stack",r.message+"\n"+r.stack.join("\n")),I.notEnumerableProp(t,"__stackCleaned__",!0)}}}function d(t,e,n,r,i){if(void 0===t&&null!==e&&X){if(void 0!==i&&i._returnedNonUndefined())return;if(0===(65535&r._bitField))return;n&&(n+=" ");var o="",a="";if(e._trace){for(var s=e._trace.stack.split("\n"),c=C(s),l=c.length-1;l>=0;--l){var u=c[l];if(!V.test(u)){var p=u.match(Q);p&&(o="at "+p[1]+":"+p[2]+":"+p[3]+" ");break}}if(c.length>0)for(var f=c[0],l=0;l0&&(a="\n"+s[l-1]);break}}var h="a promise was created in a "+n+"handler "+o+"but was not returned from it, see http://goo.gl/rRqMUw"+a;r._warn(h,!0,e)}}function v(t,e){var n=t+" is deprecated and will be removed in a future version.";return e&&(n+=" Use "+e+" instead."),y(n)}function y(t,n,r){if(ot.warnings){var i,o=new U(t);if(n)r._attachExtraTrace(o);else if(ot.longStackTraces&&(i=e._peekContext()))i.attachExtraTrace(o);else{var a=k(o);o.stack=a.message+"\n"+a.stack.join("\n")}tt("warning",o)||E(o,"",!0)}}function g(t,e){for(var n=0;n=0;--s)if(r[s]===o){a=s;break}for(var s=a;s>=0;--s){var c=r[s];if(e[i]!==c)break;e.pop(),i--}e=r}}function C(t){for(var e=[],n=0;n0&&(e=e.slice(n)),e}function k(t){var e=t.stack,n=t.toString();return e="string"==typeof e&&e.length>0?w(t):[" (No stack trace)"],{message:n,stack:C(e)}}function E(t,e,n){if("undefined"!=typeof console){var r;if(I.isObject(t)){var i=t.stack;r=e+G(i,t)}else r=e+String(t);"function"==typeof L?L(r,n):("function"==typeof console.log||"object"==typeof console.log)&&console.log(r)}}function j(t,e,n,r){var i=!1;try{"function"==typeof e&&(i=!0,"rejectionHandled"===t?e(r):e(n,r))}catch(o){B.throwLater(o)}"unhandledRejection"===t?tt(t,n,r)||i||E(n,"Unhandled rejection "):tt(t,r)}function F(t){var e;if("function"==typeof t)e="[function "+(t.name||"anonymous")+"]";else{e=t&&"function"==typeof t.toString?t.toString():I.toString(t);var n=/\[object [a-zA-Z0-9$_]+\]/;if(n.test(e))try{var r=JSON.stringify(t);e=r}catch(i){}0===e.length&&(e="(empty array)")}return"(<"+T(e)+">, no stack trace)"}function T(t){var e=41;return t.lengtha||0>s||!n||!r||n!==r||a>=s||(nt=function(t){if(D.test(t))return!0;var e=P(t);return e&&e.fileName===n&&a<=e.line&&e.line<=s?!0:!1})}}function S(t){this._parent=t,this._promisesCreated=0;var e=this._length=1+(void 0===t?0:t._length);it(this,S),e>32&&this.uncycle()}var x,A,L,N=e._getDomain,B=e._async,U=t("./errors").Warning,I=t("./util"),H=I.canAttachTrace,D=/[\\\/]bluebird[\\\/]js[\\\/](release|debug|instrumented)/,V=/\((?:timers\.js):\d+:\d+\)/,Q=/[\/<\(](.+?):(\d+):(\d+)\)?\s*$/,q=null,G=null,M=!1,W=!(0==I.env("BLUEBIRD_DEBUG")||!I.env("BLUEBIRD_DEBUG")&&"development"!==I.env("NODE_ENV")),$=!(0==I.env("BLUEBIRD_WARNINGS")||!W&&!I.env("BLUEBIRD_WARNINGS")),z=!(0==I.env("BLUEBIRD_LONG_STACK_TRACES")||!W&&!I.env("BLUEBIRD_LONG_STACK_TRACES")),X=0!=I.env("BLUEBIRD_W_FORGOTTEN_RETURN")&&($||!!I.env("BLUEBIRD_W_FORGOTTEN_RETURN"));e.prototype.suppressUnhandledRejections=function(){var t=this._target();t._bitField=-1048577&t._bitField|524288},e.prototype._ensurePossibleRejectionHandled=function(){0===(524288&this._bitField)&&(this._setRejectionIsUnhandled(),B.invokeLater(this._notifyUnhandledRejection,this,void 0))},e.prototype._notifyUnhandledRejectionIsHandled=function(){j("rejectionHandled",x,void 0,this)},e.prototype._setReturnedNonUndefined=function(){this._bitField=268435456|this._bitField},e.prototype._returnedNonUndefined=function(){return 0!==(268435456&this._bitField)},e.prototype._notifyUnhandledRejection=function(){if(this._isRejectionUnhandled()){var t=this._settledValue();this._setUnhandledRejectionIsNotified(),j("unhandledRejection",A,t,this)}},e.prototype._setUnhandledRejectionIsNotified=function(){this._bitField=262144|this._bitField},e.prototype._unsetUnhandledRejectionIsNotified=function(){this._bitField=-262145&this._bitField},e.prototype._isUnhandledRejectionNotified=function(){return(262144&this._bitField)>0},e.prototype._setRejectionIsUnhandled=function(){this._bitField=1048576|this._bitField},e.prototype._unsetRejectionIsUnhandled=function(){this._bitField=-1048577&this._bitField,this._isUnhandledRejectionNotified()&&(this._unsetUnhandledRejectionIsNotified(),this._notifyUnhandledRejectionIsHandled())},e.prototype._isRejectionUnhandled=function(){return(1048576&this._bitField)>0},e.prototype._warn=function(t,e,n){return y(t,e,n||this)},e.onPossiblyUnhandledRejection=function(t){var e=N();A="function"==typeof t?null===e?t:I.domainBind(e,t):void 0},e.onUnhandledRejectionHandled=function(t){var e=N();x="function"==typeof t?null===e?t:I.domainBind(e,t):void 0};var K=function(){};e.longStackTraces=function(){if(B.haveItemsQueued()&&!ot.longStackTraces)throw new Error("cannot enable long stack traces after promises have been created\n\n See http://goo.gl/MqrFmX\n");if(!ot.longStackTraces&&O()){var t=e.prototype._captureStackTrace,r=e.prototype._attachExtraTrace;ot.longStackTraces=!0,K=function(){if(B.haveItemsQueued()&&!ot.longStackTraces)throw new Error("cannot enable long stack traces after promises have been created\n\n See http://goo.gl/MqrFmX\n");e.prototype._captureStackTrace=t,e.prototype._attachExtraTrace=r,n.deactivateLongStackTraces(),B.enableTrampoline(),ot.longStackTraces=!1},e.prototype._captureStackTrace=h,e.prototype._attachExtraTrace=_,n.activateLongStackTraces(),B.disableTrampolineIfNecessary()}},e.hasLongStackTraces=function(){return ot.longStackTraces&&O()};var J=function(){try{if("function"==typeof CustomEvent){var t=new CustomEvent("CustomEvent");return I.global.dispatchEvent(t),function(t,e){var n=new CustomEvent(t.toLowerCase(),{detail:e,cancelable:!0});return!I.global.dispatchEvent(n)}}if("function"==typeof Event){var t=new Event("CustomEvent");return I.global.dispatchEvent(t),function(t,e){var n=new Event(t.toLowerCase(),{cancelable:!0});return n.detail=e,!I.global.dispatchEvent(n)}}var t=document.createEvent("CustomEvent");return t.initCustomEvent("testingtheevent",!1,!0,{}),I.global.dispatchEvent(t),function(t,e){var n=document.createEvent("CustomEvent");return n.initCustomEvent(t.toLowerCase(),!1,!0,e),!I.global.dispatchEvent(n)}}catch(e){}return function(){return!1}}(),Y=function(){return I.isNode?function(){return process.emit.apply(process,arguments)}:I.global?function(t){var e="on"+t.toLowerCase(),n=I.global[e];return n?(n.apply(I.global,[].slice.call(arguments,1)),!0):!1}:function(){return!1}}(),Z={promiseCreated:r,promiseFulfilled:r,promiseRejected:r,promiseResolved:r,promiseCancelled:r,promiseChained:function(t,e,n){return{promise:e,child:n}},warning:function(t,e){return{warning:e}},unhandledRejection:function(t,e,n){return{reason:e,promise:n}},rejectionHandled:r},tt=function(t){var e=!1;try{e=Y.apply(null,arguments)}catch(n){B.throwLater(n),e=!0}var r=!1;try{r=J(t,Z[t].apply(null,arguments))}catch(n){B.throwLater(n),r=!0}return r||e};e.config=function(t){if(t=Object(t),"longStackTraces"in t&&(t.longStackTraces?e.longStackTraces():!t.longStackTraces&&e.hasLongStackTraces()&&K()),"warnings"in t){var n=t.warnings;ot.warnings=!!n,X=ot.warnings,I.isObject(n)&&"wForgottenReturn"in n&&(X=!!n.wForgottenReturn)}if("cancellation"in t&&t.cancellation&&!ot.cancellation){if(B.haveItemsQueued())throw new Error("cannot enable cancellation after promises are in use");e.prototype._clearCancellationData=l,e.prototype._propagateFrom=u,e.prototype._onCancel=s,e.prototype._setOnCancel=c,e.prototype._attachCancellationCallback=a,e.prototype._execute=o,et=u,ot.cancellation=!0}"monitoring"in t&&(t.monitoring&&!ot.monitoring?(ot.monitoring=!0,e.prototype._fireEvent=tt):!t.monitoring&&ot.monitoring&&(ot.monitoring=!1,e.prototype._fireEvent=i))},e.prototype._fireEvent=i,e.prototype._execute=function(t,e,n){try{t(e,n)}catch(r){return r}},e.prototype._onCancel=function(){},e.prototype._setOnCancel=function(t){},e.prototype._attachCancellationCallback=function(t){},e.prototype._captureStackTrace=function(){},e.prototype._attachExtraTrace=function(){},e.prototype._clearCancellationData=function(){},e.prototype._propagateFrom=function(t,e){};var et=p,nt=function(){return!1},rt=/[\/<\(]([^:\/]+):(\d+):(?:\d+)\)?\s*$/;I.inherits(S,Error),n.CapturedTrace=S,S.prototype.uncycle=function(){var t=this._length;if(!(2>t)){for(var e=[],n={},r=0,i=this;void 0!==i;++r)e.push(i),i=i._parent;t=this._length=r;for(var r=t-1;r>=0;--r){var o=e[r].stack;void 0===n[o]&&(n[o]=r)}for(var r=0;t>r;++r){var a=e[r].stack,s=n[a];if(void 0!==s&&s!==r){s>0&&(e[s-1]._parent=void 0,e[s-1]._length=1),e[r]._parent=void 0,e[r]._length=1;var c=r>0?e[r-1]:this;t-1>s?(c._parent=e[s+1],c._parent.uncycle(),c._length=c._parent._length+1):(c._parent=void 0,c._length=1);for(var l=c._length+1,u=r-2;u>=0;--u)e[u]._length=l,l++;return}}}},S.prototype.attachExtraTrace=function(t){if(!t.__stackCleaned__){this.uncycle();for(var e=k(t),n=e.message,r=[e.stack],i=this;void 0!==i;)r.push(C(i.stack.split("\n"))),i=i._parent;b(r),m(r),I.notEnumerableProp(t,"stack",g(n,r)),I.notEnumerableProp(t,"__stackCleaned__",!0)}};var it=function(){var t=/^\s*at\s*/,e=function(t,e){return"string"==typeof t?t:void 0!==e.name&&void 0!==e.message?e.toString():F(e)};if("number"==typeof Error.stackTraceLimit&&"function"==typeof Error.captureStackTrace){Error.stackTraceLimit+=6,q=t,G=e;var n=Error.captureStackTrace;return nt=function(t){return D.test(t)},function(t,e){Error.stackTraceLimit+=6,n(t,e),Error.stackTraceLimit-=6}}var r=new Error;if("string"==typeof r.stack&&r.stack.split("\n")[0].indexOf("stackDetection@")>=0)return q=/@/,G=e,M=!0,function(t){t.stack=(new Error).stack};var i;try{throw new Error}catch(o){i="stack"in o}return"stack"in r||!i||"number"!=typeof Error.stackTraceLimit?(G=function(t,e){return"string"==typeof t?t:"object"!=typeof e&&"function"!=typeof e||void 0===e.name||void 0===e.message?F(e):e.toString()},null):(q=t,G=e,function(t){Error.stackTraceLimit+=6;try{throw new Error}catch(e){t.stack=e.stack}Error.stackTraceLimit-=6})}([]);"undefined"!=typeof console&&"undefined"!=typeof console.warn&&(L=function(t){console.warn(t)},I.isNode&&process.stderr.isTTY?L=function(t,e){var n=e?"":"";console.warn(n+t+"\n")}:I.isNode||"string"!=typeof(new Error).stack||(L=function(t,e){console.warn("%c"+t,e?"color: darkorange":"color: red")}));var ot={warnings:$,longStackTraces:!1,cancellation:!1,monitoring:!1};return z&&e.longStackTraces(),{longStackTraces:function(){return ot.longStackTraces},warnings:function(){return ot.warnings},cancellation:function(){return ot.cancellation},monitoring:function(){return ot.monitoring},propagateFromFunction:function(){return et},boundValueFunction:function(){return f},checkForgottenReturns:d,setBounds:R,warn:y,deprecated:v,CapturedTrace:S,fireDomEvent:J,fireGlobalEvent:Y}}},{"./errors":9,"./util":21}],8:[function(t,e,n){"use strict";e.exports=function(t){function e(){return this.value}function n(){throw this.reason}t.prototype["return"]=t.prototype.thenReturn=function(n){return n instanceof t&&n.suppressUnhandledRejections(),this._then(e,void 0,void 0,{value:n},void 0)},t.prototype["throw"]=t.prototype.thenThrow=function(t){return this._then(n,void 0,void 0,{reason:t},void 0)},t.prototype.catchThrow=function(t){if(arguments.length<=1)return this._then(void 0,n,void 0,{reason:t},void 0);var e=arguments[1],r=function(){throw e};return this.caught(t,r)},t.prototype.catchReturn=function(n){if(arguments.length<=1)return n instanceof t&&n.suppressUnhandledRejections(),this._then(void 0,e,void 0,{value:n},void 0);var r=arguments[1];r instanceof t&&r.suppressUnhandledRejections();var i=function(){return r};return this.caught(n,i)}}},{}],9:[function(t,e,n){"use strict";function r(t,e){function n(r){return this instanceof n?(p(this,"message","string"==typeof r?r:e),p(this,"name",t),void(Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):Error.call(this))):new n(r)}return u(n,Error),n}function i(t){return this instanceof i?(p(this,"name","OperationalError"),p(this,"message",t),this.cause=t,this.isOperational=!0,void(t instanceof Error?(p(this,"message",t.message),p(this,"stack",t.stack)):Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor))):new i(t)}var o,a,s=t("./es5"),c=s.freeze,l=t("./util"),u=l.inherits,p=l.notEnumerableProp,f=r("Warning","warning"),h=r("CancellationError","cancellation error"),_=r("TimeoutError","timeout error"),d=r("AggregateError","aggregate error");try{o=TypeError,a=RangeError}catch(v){o=r("TypeError","type error"),a=r("RangeError","range error")}for(var y="join pop push shift unshift slice filter forEach some every map indexOf lastIndexOf reduce reduceRight sort reverse".split(" "),g=0;g1?t.cancelPromise._reject(e):t.cancelPromise._cancel(),t.cancelPromise=null,!0):!1}function a(){return c.call(this,this.promise._target()._settledValue())}function s(t){return o(this,t)?void 0:(p.e=t,p)}function c(t){var r=this.promise,c=this.handler;if(!this.called){this.called=!0;var l=this.isFinallyHandler()?c.call(r._boundValue()):c.call(r._boundValue(),t);if(void 0!==l){r._setReturnedNonUndefined();var f=n(l,r);if(f instanceof e){if(null!=this.cancelPromise){if(f._isCancelled()){var h=new u("late cancellation observer");return r._attachExtraTrace(h),p.e=h,p}f.isPending()&&f._attachCancellationCallback(new i(this))}return f._then(a,s,void 0,this,void 0)}}}return r.isRejected()?(o(this),p.e=t,p):(o(this),t)}var l=t("./util"),u=e.CancellationError,p=l.errorObj;return r.prototype.isFinallyHandler=function(){return 0===this.type},i.prototype._resultCancelled=function(){o(this.finallyHandler)},e.prototype._passThrough=function(t,e,n,i){return"function"!=typeof t?this.then():this._then(n,i,void 0,new r(this,e,t),void 0)},e.prototype.lastly=e.prototype["finally"]=function(t){return this._passThrough(t,0,c,c)},e.prototype.tap=function(t){return this._passThrough(t,1,c)},r}},{"./util":21}],12:[function(t,e,n){"use strict";e.exports=function(e,n,r,i,o,a){var s=t("./util");s.canEvaluate,s.tryCatch,s.errorObj;e.join=function(){var t,e=arguments.length-1;if(e>0&&"function"==typeof arguments[e]){t=arguments[e];var r}var i=[].slice.call(arguments);t&&i.pop();var r=new n(i).promise();return void 0!==t?r.spread(t):r}}},{"./util":21}],13:[function(t,e,n){"use strict";e.exports=function(e,n,r,i,o){var a=t("./util"),s=a.tryCatch;e.method=function(t){if("function"!=typeof t)throw new e.TypeError("expecting a function but got "+a.classString(t));return function(){var r=new e(n);r._captureStackTrace(),r._pushContext();var i=s(t).apply(this,arguments),a=r._popContext();return o.checkForgottenReturns(i,a,"Promise.method",r),r._resolveFromSyncValue(i),r}},e.attempt=e["try"]=function(t){if("function"!=typeof t)return i("expecting a function but got "+a.classString(t));var r=new e(n);r._captureStackTrace(),r._pushContext();var c;if(arguments.length>1){o.deprecated("calling Promise.try with more than 1 argument");var l=arguments[1],u=arguments[2];c=a.isArray(l)?s(t).apply(u,l):s(t).call(u,l)}else c=s(t)();var p=r._popContext();return o.checkForgottenReturns(c,p,"Promise.try",r),r._resolveFromSyncValue(c),r},e.prototype._resolveFromSyncValue=function(t){t===a.errorObj?this._rejectCallback(t.e,!1):this._resolveCallback(t,!0)}}},{"./util":21}],14:[function(t,e,n){"use strict";function r(t){return t instanceof Error&&u.getPrototypeOf(t)===Error.prototype}function i(t){var e;if(r(t)){e=new l(t),e.name=t.name,e.message=t.message,e.stack=t.stack;for(var n=u.keys(t),i=0;i1){var n,r=new Array(e-1),i=0;for(n=0;e-1>n;++n){var o=arguments[n];if(!h.isObject(o))return p("expecting an object but got A catch statement predicate "+h.classString(o));r[i++]=o}return r.length=i,t=arguments[n],this.then(void 0,O(r,t,this))}return this.then(void 0,t)},i.prototype.reflect=function(){return this._then(u,u,void 0,this,void 0)},i.prototype.then=function(t,e){if(F.warnings()&&arguments.length>0&&"function"!=typeof t&&"function"!=typeof e){var n=".then() only accepts functions but was passed: "+h.classString(t);arguments.length>1&&(n+=", "+h.classString(e)),this._warn(n)}return this._then(t,e,void 0,void 0,void 0)},i.prototype.done=function(t,e){var n=this._then(t,e,void 0,void 0,void 0);n._setIsFinal()},i.prototype.spread=function(t){return"function"!=typeof t?p("expecting a function but got "+h.classString(t)):this.all()._then(t,void 0,void 0,C,void 0)},i.prototype.toJSON=function(){var t={isFulfilled:!1,isRejected:!1,fulfillmentValue:void 0,rejectionReason:void 0};return this.isFulfilled()?(t.fulfillmentValue=this.value(),t.isFulfilled=!0):this.isRejected()&&(t.rejectionReason=this.reason(),t.isRejected=!0),t},i.prototype.all=function(){return arguments.length>0&&this._warn(".all() was passed arguments but it does not take any"), 31 | new E(this).promise()},i.prototype.error=function(t){return this.caught(h.originatesFromRejection,t)},i.getNewLibraryCopy=e.exports,i.is=function(t){return t instanceof i},i.fromNode=i.fromCallback=function(t){var e=new i(b);e._captureStackTrace();var n=arguments.length>1?!!Object(arguments[1]).multiArgs:!1,r=S(t)(P(e,n));return r===R&&e._rejectCallback(r.e,!0),e._isFateSealed()||e._setAsyncGuaranteed(),e},i.all=function(t){return new E(t).promise()},i.cast=function(t){var e=k(t);return e instanceof i||(e=new i(b),e._captureStackTrace(),e._setFulfilled(),e._rejectionHandler0=t),e},i.resolve=i.fulfilled=i.cast,i.reject=i.rejected=function(t){var e=new i(b);return e._captureStackTrace(),e._rejectCallback(t,!0),e},i.setScheduler=function(t){if("function"!=typeof t)throw new g("expecting a function but got "+h.classString(t));return v.setScheduler(t)},i.prototype._then=function(t,e,n,r,o){var a=void 0!==o,s=a?o:new i(b),l=this._target(),u=l._bitField;a||(s._propagateFrom(this,3),s._captureStackTrace(),void 0===r&&0!==(2097152&this._bitField)&&(r=0!==(50397184&u)?this._boundValue():l===this?void 0:this._boundTo),this._fireEvent("promiseChained",this,s));var p=c();if(0!==(50397184&u)){var f,_,d=l._settlePromiseCtx;0!==(33554432&u)?(_=l._rejectionHandler0,f=t):0!==(16777216&u)?(_=l._fulfillmentHandler0,f=e,l._unsetRejectionIsUnhandled()):(d=l._settlePromiseLateCancellationObserver,_=new m("late cancellation observer"),l._attachExtraTrace(_),f=e),v.invoke(d,l,{handler:null===p?f:"function"==typeof f&&h.domainBind(p,f),promise:s,receiver:r,value:_})}else l._addCallbacks(t,e,s,r,p);return s},i.prototype._length=function(){return 65535&this._bitField},i.prototype._isFateSealed=function(){return 0!==(117506048&this._bitField)},i.prototype._isFollowing=function(){return 67108864===(67108864&this._bitField)},i.prototype._setLength=function(t){this._bitField=-65536&this._bitField|65535&t},i.prototype._setFulfilled=function(){this._bitField=33554432|this._bitField,this._fireEvent("promiseFulfilled",this)},i.prototype._setRejected=function(){this._bitField=16777216|this._bitField,this._fireEvent("promiseRejected",this)},i.prototype._setFollowing=function(){this._bitField=67108864|this._bitField,this._fireEvent("promiseResolved",this)},i.prototype._setIsFinal=function(){this._bitField=4194304|this._bitField},i.prototype._isFinal=function(){return(4194304&this._bitField)>0},i.prototype._unsetCancelled=function(){this._bitField=-65537&this._bitField},i.prototype._setCancelled=function(){this._bitField=65536|this._bitField,this._fireEvent("promiseCancelled",this)},i.prototype._setWillBeCancelled=function(){this._bitField=8388608|this._bitField},i.prototype._setAsyncGuaranteed=function(){v.hasCustomScheduler()||(this._bitField=134217728|this._bitField)},i.prototype._receiverAt=function(t){var e=0===t?this._receiver0:this[4*t-4+3];return e===f?void 0:void 0===e&&this._isBound()?this._boundValue():e},i.prototype._promiseAt=function(t){return this[4*t-4+2]},i.prototype._fulfillmentHandlerAt=function(t){return this[4*t-4+0]},i.prototype._rejectionHandlerAt=function(t){return this[4*t-4+1]},i.prototype._boundValue=function(){},i.prototype._migrateCallback0=function(t){var e=(t._bitField,t._fulfillmentHandler0),n=t._rejectionHandler0,r=t._promise0,i=t._receiverAt(0);void 0===i&&(i=f),this._addCallbacks(e,n,r,i,null)},i.prototype._migrateCallbackAt=function(t,e){var n=t._fulfillmentHandlerAt(e),r=t._rejectionHandlerAt(e),i=t._promiseAt(e),o=t._receiverAt(e);void 0===o&&(o=f),this._addCallbacks(n,r,i,o,null)},i.prototype._addCallbacks=function(t,e,n,r,i){var o=this._length();if(o>=65531&&(o=0,this._setLength(0)),0===o)this._promise0=n,this._receiver0=r,"function"==typeof t&&(this._fulfillmentHandler0=null===i?t:h.domainBind(i,t)),"function"==typeof e&&(this._rejectionHandler0=null===i?e:h.domainBind(i,e));else{var a=4*o-4;this[a+2]=n,this[a+3]=r,"function"==typeof t&&(this[a+0]=null===i?t:h.domainBind(i,t)),"function"==typeof e&&(this[a+1]=null===i?e:h.domainBind(i,e))}return this._setLength(o+1),o},i.prototype._proxy=function(t,e){this._addCallbacks(void 0,void 0,e,t,null)},i.prototype._resolveCallback=function(t,e){if(0===(117506048&this._bitField)){if(t===this)return this._rejectCallback(l(),!1);var n=k(t,this);if(!(n instanceof i))return this._fulfill(t);e&&this._propagateFrom(n,2);var r=n._target();if(r===this)return void this._reject(l());var o=r._bitField;if(0===(50397184&o)){var a=this._length();a>0&&r._migrateCallback0(this);for(var s=1;a>s;++s)r._migrateCallbackAt(this,s);this._setFollowing(),this._setLength(0),this._setFollowee(r)}else if(0!==(33554432&o))this._fulfill(r._value());else if(0!==(16777216&o))this._reject(r._reason());else{var c=new m("late cancellation observer");r._attachExtraTrace(c),this._reject(c)}}},i.prototype._rejectCallback=function(t,e,n){var r=h.ensureErrorObject(t),i=r===t;if(!i&&!n&&F.warnings()){var o="a promise was rejected with a non-error: "+h.classString(t);this._warn(o,!0)}this._attachExtraTrace(r,e?i:!1),this._reject(t)},i.prototype._resolveFromExecutor=function(t){var e=this;this._captureStackTrace(),this._pushContext();var n=!0,r=this._execute(t,function(t){e._resolveCallback(t)},function(t){e._rejectCallback(t,n)});n=!1,this._popContext(),void 0!==r&&e._rejectCallback(r,!0)},i.prototype._settlePromiseFromHandler=function(t,e,n,r){var i=r._bitField;if(0===(65536&i)){r._pushContext();var o;e===C?n&&"number"==typeof n.length?o=S(t).apply(this._boundValue(),n):(o=R,o.e=new g("cannot .spread() a non-array: "+h.classString(n))):o=S(t).call(e,n);var a=r._popContext();i=r._bitField,0===(65536&i)&&(o===w?r._reject(n):o===R?r._rejectCallback(o.e,!1):(F.checkForgottenReturns(o,a,"",r,this),r._resolveCallback(o)))}},i.prototype._target=function(){for(var t=this;t._isFollowing();)t=t._followee();return t},i.prototype._followee=function(){return this._rejectionHandler0},i.prototype._setFollowee=function(t){this._rejectionHandler0=t},i.prototype._settlePromise=function(t,e,r,o){var a=t instanceof i,s=this._bitField,c=0!==(134217728&s);0!==(65536&s)?(a&&t._invokeInternalOnCancel(),r instanceof T&&r.isFinallyHandler()?(r.cancelPromise=t,S(e).call(r,o)===R&&t._reject(R.e)):e===u?t._fulfill(u.call(r)):r instanceof n?r._promiseCancelled(t):a||t instanceof E?t._cancel():r.cancel()):"function"==typeof e?a?(c&&t._setAsyncGuaranteed(),this._settlePromiseFromHandler(e,r,o,t)):e.call(r,o,t):r instanceof n?r._isResolved()||(0!==(33554432&s)?r._promiseFulfilled(o,t):r._promiseRejected(o,t)):a&&(c&&t._setAsyncGuaranteed(),0!==(33554432&s)?t._fulfill(o):t._reject(o))},i.prototype._settlePromiseLateCancellationObserver=function(t){var e=t.handler,n=t.promise,r=t.receiver,o=t.value;"function"==typeof e?n instanceof i?this._settlePromiseFromHandler(e,r,o,n):e.call(r,o,n):n instanceof i&&n._reject(o)},i.prototype._settlePromiseCtx=function(t){this._settlePromise(t.promise,t.handler,t.receiver,t.value)},i.prototype._settlePromise0=function(t,e,n){var r=this._promise0,i=this._receiverAt(0);this._promise0=void 0,this._receiver0=void 0,this._settlePromise(r,t,i,e)},i.prototype._clearCallbackDataAtIndex=function(t){var e=4*t-4;this[e+2]=this[e+3]=this[e+0]=this[e+1]=void 0},i.prototype._fulfill=function(t){var e=this._bitField;if(!((117506048&e)>>>16)){if(t===this){var n=l();return this._attachExtraTrace(n),this._reject(n)}this._setFulfilled(),this._rejectionHandler0=t,(65535&e)>0&&(0!==(134217728&e)?this._settlePromises():v.settlePromises(this))}},i.prototype._reject=function(t){var e=this._bitField;if(!((117506048&e)>>>16))return this._setRejected(),this._fulfillmentHandler0=t,this._isFinal()?v.fatalError(t,h.isNode):void((65535&e)>0?v.settlePromises(this):this._ensurePossibleRejectionHandled())},i.prototype._fulfillPromises=function(t,e){for(var n=1;t>n;n++){var r=this._fulfillmentHandlerAt(n),i=this._promiseAt(n),o=this._receiverAt(n);this._clearCallbackDataAtIndex(n),this._settlePromise(i,r,o,e)}},i.prototype._rejectPromises=function(t,e){for(var n=1;t>n;n++){var r=this._rejectionHandlerAt(n),i=this._promiseAt(n),o=this._receiverAt(n);this._clearCallbackDataAtIndex(n),this._settlePromise(i,r,o,e)}},i.prototype._settlePromises=function(){var t=this._bitField,e=65535&t;if(e>0){if(0!==(16842752&t)){var n=this._fulfillmentHandler0;this._settlePromise0(this._rejectionHandler0,n,t),this._rejectPromises(e,n)}else{var r=this._rejectionHandler0;this._settlePromise0(this._fulfillmentHandler0,r,t),this._fulfillPromises(e,r)}this._setLength(0)}this._clearCancellationData()},i.prototype._settledValue=function(){var t=this._bitField;return 0!==(33554432&t)?this._rejectionHandler0:0!==(16777216&t)?this._fulfillmentHandler0:void 0},i.defer=i.pending=function(){F.deprecated("Promise.defer","new Promise");var t=new i(b);return{promise:t,resolve:o,reject:a}},h.notEnumerableProp(i,"_makeSelfResolutionError",l),t("./method")(i,b,k,p,F),t("./bind")(i,b,k,F),t("./cancel")(i,E,p,F),t("./direct_resolve")(i),t("./synchronous_inspection")(i),t("./join")(i,E,k,b,v,c),i.Promise=i,i.version="3.4.6",h.toFastProperties(i),h.toFastProperties(i.prototype),s({a:1}),s({b:2}),s({c:3}),s(1),s(function(){}),s(void 0),s(!1),s(new i(b)),F.setBounds(d.firstLineError,h.lastLineError),i}},{"./async":1,"./bind":2,"./cancel":4,"./catch_filter":5,"./context":6,"./debuggability":7,"./direct_resolve":8,"./errors":9,"./es5":10,"./finally":11,"./join":12,"./method":13,"./nodeback":14,"./promise_array":16,"./synchronous_inspection":19,"./thenables":20,"./util":21}],16:[function(t,e,n){"use strict";e.exports=function(e,n,r,i,o){function a(t){switch(t){case-2:return[];case-3:return{}}}function s(t){var r=this._promise=new e(n);t instanceof e&&r._propagateFrom(t,3),r._setOnCancel(this),this._values=t,this._length=0,this._totalResolved=0,this._init(void 0,-2)}var c=t("./util");c.isArray;return c.inherits(s,o),s.prototype.length=function(){return this._length},s.prototype.promise=function(){return this._promise},s.prototype._init=function l(t,n){var o=r(this._values,this._promise);if(o instanceof e){o=o._target();var s=o._bitField;if(this._values=o,0===(50397184&s))return this._promise._setAsyncGuaranteed(),o._then(l,this._reject,void 0,this,n);if(0===(33554432&s))return 0!==(16777216&s)?this._reject(o._reason()):this._cancel();o=o._value()}if(o=c.asArray(o),null===o){var u=i("expecting an array or an iterable object but got "+c.classString(o)).reason();return void this._promise._rejectCallback(u,!1)}return 0===o.length?void(-5===n?this._resolveEmptyArray():this._resolve(a(n))):void this._iterate(o)},s.prototype._iterate=function(t){var n=this.getActualLength(t.length);this._length=n,this._values=this.shouldCopyValues()?new Array(n):this._values;for(var i=this._promise,o=!1,a=null,s=0;n>s;++s){var c=r(t[s],i);c instanceof e?(c=c._target(),a=c._bitField):a=null,o?null!==a&&c.suppressUnhandledRejections():null!==a?0===(50397184&a)?(c._proxy(this,s),this._values[s]=c):o=0!==(33554432&a)?this._promiseFulfilled(c._value(),s):0!==(16777216&a)?this._promiseRejected(c._reason(),s):this._promiseCancelled(s):o=this._promiseFulfilled(c,s)}o||i._setAsyncGuaranteed()},s.prototype._isResolved=function(){return null===this._values},s.prototype._resolve=function(t){this._values=null,this._promise._fulfill(t)},s.prototype._cancel=function(){!this._isResolved()&&this._promise._isCancellable()&&(this._values=null,this._promise._cancel())},s.prototype._reject=function(t){this._values=null,this._promise._rejectCallback(t,!1)},s.prototype._promiseFulfilled=function(t,e){this._values[e]=t;var n=++this._totalResolved;return n>=this._length?(this._resolve(this._values),!0):!1},s.prototype._promiseCancelled=function(){return this._cancel(),!0},s.prototype._promiseRejected=function(t){return this._totalResolved++,this._reject(t),!0},s.prototype._resultCancelled=function(){if(!this._isResolved()){var t=this._values;if(this._cancel(),t instanceof e)t.cancel();else for(var n=0;no;++o)n[o+r]=t[o+e],t[o+e]=void 0}function i(t){this._capacity=t,this._length=0,this._front=0}i.prototype._willBeOverCapacity=function(t){return this._capacityn;++n)i[n]=t[n];return i[n]=e,i}function l(t,e,n){if(!F.isES5)return{}.hasOwnProperty.call(t,e)?t[e]:void 0;var r=Object.getOwnPropertyDescriptor(t,e);return null!=r?null==r.get&&null==r.set?r.value:n:void 0}function u(t,e,n){if(o(t))return t;var r={value:n,configurable:!0,enumerable:!1,writable:!0};return F.defineProperty(t,e,r),t}function p(t){throw t}function f(t){try{if("function"==typeof t){var e=F.names(t.prototype),n=F.isES5&&e.length>1,r=e.length>0&&!(1===e.length&&"constructor"===e[0]),i=A.test(t+"")&&F.names(t).length>0;if(n||r||i)return!0}return!1}catch(o){return!1}}function h(t){function e(){}e.prototype=t;for(var n=8;n--;)new e;return t}function _(t){return L.test(t)}function d(t,e,n){for(var r=new Array(t),i=0;t>i;++i)r[i]=e+i+n;return r}function v(t){try{return t+""}catch(e){return"[no string representation]"}}function y(t){return null!==t&&"object"==typeof t&&"string"==typeof t.message&&"string"==typeof t.name}function g(t){try{u(t,"isOperational",!0)}catch(e){}}function m(t){return null==t?!1:t instanceof Error.__BluebirdErrorTypes__.OperationalError||t.isOperational===!0}function b(t){return y(t)&&F.propertyIsWritable(t,"stack")}function C(t){return{}.toString.call(t)}function w(t,e,n){for(var r=F.names(t),i=0;i10||t[0]>0}(),H.isNode&&H.toFastProperties(process);try{throw new Error}catch(D){H.lastLineError=D}e.exports=H},{"./es5":10}]},{},[3])(3)}),"undefined"!=typeof window&&null!==window?window.P=window.Promise:"undefined"!=typeof self&&null!==self&&(self.P=self.Promise); -------------------------------------------------------------------------------- /thirds/keymaster.js: -------------------------------------------------------------------------------- 1 | // keymaster.js 2 | // (c) 2011-2013 Thomas Fuchs 3 | // keymaster.js may be freely distributed under the MIT license. 4 | 5 | ;(function(global){ 6 | var k, 7 | _handlers = {}, 8 | _mods = { 16: false, 18: false, 17: false, 91: false }, 9 | _scope = 'all', 10 | // modifier keys 11 | _MODIFIERS = { 12 | '⇧': 16, shift: 16, 13 | '⌥': 18, alt: 18, option: 18, 14 | '⌃': 17, ctrl: 17, control: 17, 15 | '⌘': 91, command: 91 16 | }, 17 | // special keys 18 | _MAP = { 19 | backspace: 8, tab: 9, clear: 12, 20 | enter: 13, 'return': 13, 21 | esc: 27, escape: 27, space: 32, 22 | left: 37, up: 38, 23 | right: 39, down: 40, 24 | del: 46, 'delete': 46, 25 | home: 36, end: 35, 26 | pageup: 33, pagedown: 34, 27 | ',': 188, '.': 190, '/': 191, 28 | '`': 192, '-': 189, '=': 187, 29 | ';': 186, '\'': 222, 30 | '[': 219, ']': 221, '\\': 220 31 | }, 32 | code = function(x){ 33 | return _MAP[x] || x.toUpperCase().charCodeAt(0); 34 | }, 35 | _downKeys = []; 36 | 37 | for(k=1;k<20;k++) _MAP['f'+k] = 111+k; 38 | 39 | // IE doesn't support Array#indexOf, so have a simple replacement 40 | function index(array, item){ 41 | var i = array.length; 42 | while(i--) if(array[i]===item) return i; 43 | return -1; 44 | } 45 | 46 | // for comparing mods before unassignment 47 | function compareArray(a1, a2) { 48 | if (a1.length != a2.length) return false; 49 | for (var i = 0; i < a1.length; i++) { 50 | if (a1[i] !== a2[i]) return false; 51 | } 52 | return true; 53 | } 54 | 55 | var modifierMap = { 56 | 16:'shiftKey', 57 | 18:'altKey', 58 | 17:'ctrlKey', 59 | 91:'metaKey' 60 | }; 61 | function updateModifierKey(event) { 62 | for(k in _mods) _mods[k] = event[modifierMap[k]]; 63 | }; 64 | 65 | // handle keydown event 66 | function dispatch(event) { 67 | var key, handler, k, i, modifiersMatch, scope; 68 | key = event.keyCode; 69 | 70 | if (index(_downKeys, key) == -1) { 71 | _downKeys.push(key); 72 | } 73 | 74 | // if a modifier key, set the key. property to true and return 75 | if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko 76 | if(key in _mods) { 77 | _mods[key] = true; 78 | // 'assignKey' from inside this closure is exported to window.key 79 | for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true; 80 | return; 81 | } 82 | updateModifierKey(event); 83 | 84 | // see if we need to ignore the keypress (filter() can can be overridden) 85 | // by default ignore key presses if a select, textarea, or input is focused 86 | if(!assignKey.filter.call(this, event)) return; 87 | 88 | // abort if no potentially matching shortcuts found 89 | if (!(key in _handlers)) return; 90 | 91 | scope = getScope(); 92 | 93 | // for each potential shortcut 94 | for (i = 0; i < _handlers[key].length; i++) { 95 | handler = _handlers[key][i]; 96 | 97 | // see if it's in the current scope 98 | if(handler.scope == scope || handler.scope == 'all'){ 99 | // check if modifiers match if any 100 | modifiersMatch = handler.mods.length > 0; 101 | for(k in _mods) 102 | if((!_mods[k] && index(handler.mods, +k) > -1) || 103 | (_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false; 104 | // call the handler and stop the event if neccessary 105 | if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){ 106 | if(handler.method(event, handler)===false){ 107 | if(event.preventDefault) event.preventDefault(); 108 | else event.returnValue = false; 109 | if(event.stopPropagation) event.stopPropagation(); 110 | if(event.cancelBubble) event.cancelBubble = true; 111 | } 112 | } 113 | } 114 | } 115 | }; 116 | 117 | // unset modifier keys on keyup 118 | function clearModifier(event){ 119 | var key = event.keyCode, k, 120 | i = index(_downKeys, key); 121 | 122 | // remove key from _downKeys 123 | if (i >= 0) { 124 | _downKeys.splice(i, 1); 125 | } 126 | 127 | if(key == 93 || key == 224) key = 91; 128 | if(key in _mods) { 129 | _mods[key] = false; 130 | for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false; 131 | } 132 | }; 133 | 134 | function resetModifiers() { 135 | for(k in _mods) _mods[k] = false; 136 | for(k in _MODIFIERS) assignKey[k] = false; 137 | }; 138 | 139 | // parse and assign shortcut 140 | function assignKey(key, scope, method){ 141 | var keys, mods; 142 | keys = getKeys(key); 143 | if (method === undefined) { 144 | method = scope; 145 | scope = 'all'; 146 | } 147 | 148 | // for each shortcut 149 | for (var i = 0; i < keys.length; i++) { 150 | // set modifier keys if any 151 | mods = []; 152 | key = keys[i].split('+'); 153 | if (key.length > 1){ 154 | mods = getMods(key); 155 | key = [key[key.length-1]]; 156 | } 157 | // convert to keycode and... 158 | key = key[0] 159 | key = code(key); 160 | // ...store handler 161 | if (!(key in _handlers)) _handlers[key] = []; 162 | _handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods }); 163 | } 164 | }; 165 | 166 | // unbind all handlers for given key in current scope 167 | function unbindKey(key, scope) { 168 | var multipleKeys, keys, 169 | mods = [], 170 | i, j, obj; 171 | 172 | multipleKeys = getKeys(key); 173 | 174 | for (j = 0; j < multipleKeys.length; j++) { 175 | keys = multipleKeys[j].split('+'); 176 | 177 | if (keys.length > 1) { 178 | mods = getMods(keys); 179 | } 180 | 181 | key = keys[keys.length - 1]; 182 | key = code(key); 183 | 184 | if (scope === undefined) { 185 | scope = getScope(); 186 | } 187 | if (!_handlers[key]) { 188 | return; 189 | } 190 | for (i = 0; i < _handlers[key].length; i++) { 191 | obj = _handlers[key][i]; 192 | // only clear handlers if correct scope and mods match 193 | if (obj.scope === scope && compareArray(obj.mods, mods)) { 194 | _handlers[key][i] = {}; 195 | } 196 | } 197 | } 198 | }; 199 | 200 | // Returns true if the key with code 'keyCode' is currently down 201 | // Converts strings into key codes. 202 | function isPressed(keyCode) { 203 | if (typeof(keyCode)=='string') { 204 | keyCode = code(keyCode); 205 | } 206 | return index(_downKeys, keyCode) != -1; 207 | } 208 | 209 | function getPressedKeyCodes() { 210 | return _downKeys.slice(0); 211 | } 212 | 213 | function filter(event){ 214 | var tagName = (event.target || event.srcElement).tagName; 215 | // ignore keypressed in any elements that support keyboard data input 216 | return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA'); 217 | } 218 | 219 | // initialize key. to false 220 | for(k in _MODIFIERS) assignKey[k] = false; 221 | 222 | // set current scope (default 'all') 223 | function setScope(scope){ _scope = scope || 'all' }; 224 | function getScope(){ return _scope || 'all' }; 225 | 226 | // delete all handlers for a given scope 227 | function deleteScope(scope){ 228 | var key, handlers, i; 229 | 230 | for (key in _handlers) { 231 | handlers = _handlers[key]; 232 | for (i = 0; i < handlers.length; ) { 233 | if (handlers[i].scope === scope) handlers.splice(i, 1); 234 | else i++; 235 | } 236 | } 237 | }; 238 | 239 | // abstract key logic for assign and unassign 240 | function getKeys(key) { 241 | var keys; 242 | key = key.replace(/\s/g, ''); 243 | keys = key.split(','); 244 | if ((keys[keys.length - 1]) == '') { 245 | keys[keys.length - 2] += ','; 246 | } 247 | return keys; 248 | } 249 | 250 | // abstract mods logic for assign and unassign 251 | function getMods(key) { 252 | var mods = key.slice(0, key.length - 1); 253 | for (var mi = 0; mi < mods.length; mi++) 254 | mods[mi] = _MODIFIERS[mods[mi]]; 255 | return mods; 256 | } 257 | 258 | // cross-browser events 259 | function addEvent(object, event, method) { 260 | if (object.addEventListener) 261 | object.addEventListener(event, method, false); 262 | else if(object.attachEvent) 263 | object.attachEvent('on'+event, function(){ method(window.event) }); 264 | }; 265 | 266 | // set the handlers globally on document 267 | addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48 268 | addEvent(document, 'keyup', clearModifier); 269 | 270 | // reset modifiers to false whenever the window is (re)focused. 271 | addEvent(window, 'focus', resetModifiers); 272 | 273 | // store previously defined key 274 | var previousKey = global.key; 275 | 276 | // restore previously defined key and return reference to our key object 277 | function noConflict() { 278 | var k = global.key; 279 | global.key = previousKey; 280 | return k; 281 | } 282 | 283 | // set window.key and window.key.set/get/deleteScope, and the default filter 284 | global.key = assignKey; 285 | global.key.setScope = setScope; 286 | global.key.getScope = getScope; 287 | global.key.deleteScope = deleteScope; 288 | global.key.filter = filter; 289 | global.key.isPressed = isPressed; 290 | global.key.getPressedKeyCodes = getPressedKeyCodes; 291 | global.key.noConflict = noConflict; 292 | global.key.unbind = unbindKey; 293 | 294 | if(typeof module !== 'undefined') module.exports = assignKey; 295 | 296 | })(this); 297 | -------------------------------------------------------------------------------- /thirds/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Change the default font family in all browsers (opinionated). 5 | * 2. Correct the line height in all browsers. 6 | * 3. Prevent adjustments of font size after orientation changes in 7 | * IE on Windows Phone and in iOS. 8 | */ 9 | 10 | /* Document 11 | ========================================================================== */ 12 | 13 | html { 14 | font-family: sans-serif; /* 1 */ 15 | line-height: 1.15; /* 2 */ 16 | -ms-text-size-adjust: 100%; /* 3 */ 17 | -webkit-text-size-adjust: 100%; /* 3 */ 18 | } 19 | 20 | /* Sections 21 | ========================================================================== */ 22 | 23 | /** 24 | * Remove the margin in all browsers (opinionated). 25 | */ 26 | 27 | body { 28 | margin: 0; 29 | } 30 | 31 | /** 32 | * Add the correct display in IE 9-. 33 | */ 34 | 35 | article, 36 | aside, 37 | footer, 38 | header, 39 | nav, 40 | section { 41 | display: block; 42 | } 43 | 44 | /** 45 | * Correct the font size and margin on `h1` elements within `section` and 46 | * `article` contexts in Chrome, Firefox, and Safari. 47 | */ 48 | 49 | h1 { 50 | font-size: 2em; 51 | margin: 0.67em 0; 52 | } 53 | 54 | /* Grouping content 55 | ========================================================================== */ 56 | 57 | /** 58 | * Add the correct display in IE 9-. 59 | * 1. Add the correct display in IE. 60 | */ 61 | 62 | figcaption, 63 | figure, 64 | main { /* 1 */ 65 | display: block; 66 | } 67 | 68 | /** 69 | * Add the correct margin in IE 8. 70 | */ 71 | 72 | figure { 73 | margin: 1em 40px; 74 | } 75 | 76 | /** 77 | * 1. Add the correct box sizing in Firefox. 78 | * 2. Show the overflow in Edge and IE. 79 | */ 80 | 81 | hr { 82 | box-sizing: content-box; /* 1 */ 83 | height: 0; /* 1 */ 84 | overflow: visible; /* 2 */ 85 | } 86 | 87 | /** 88 | * 1. Correct the inheritance and scaling of font size in all browsers. 89 | * 2. Correct the odd `em` font sizing in all browsers. 90 | */ 91 | 92 | pre { 93 | font-family: monospace, monospace; /* 1 */ 94 | font-size: 1em; /* 2 */ 95 | } 96 | 97 | /* Text-level semantics 98 | ========================================================================== */ 99 | 100 | /** 101 | * 1. Remove the gray background on active links in IE 10. 102 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 103 | */ 104 | 105 | a { 106 | background-color: transparent; /* 1 */ 107 | -webkit-text-decoration-skip: objects; /* 2 */ 108 | } 109 | 110 | /** 111 | * Remove the outline on focused links when they are also active or hovered 112 | * in all browsers (opinionated). 113 | */ 114 | 115 | a:active, 116 | a:hover { 117 | outline-width: 0; 118 | } 119 | 120 | /** 121 | * 1. Remove the bottom border in Firefox 39-. 122 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 123 | */ 124 | 125 | abbr[title] { 126 | border-bottom: none; /* 1 */ 127 | text-decoration: underline; /* 2 */ 128 | text-decoration: underline dotted; /* 2 */ 129 | } 130 | 131 | /** 132 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 133 | */ 134 | 135 | b, 136 | strong { 137 | font-weight: inherit; 138 | } 139 | 140 | /** 141 | * Add the correct font weight in Chrome, Edge, and Safari. 142 | */ 143 | 144 | b, 145 | strong { 146 | font-weight: bolder; 147 | } 148 | 149 | /** 150 | * 1. Correct the inheritance and scaling of font size in all browsers. 151 | * 2. Correct the odd `em` font sizing in all browsers. 152 | */ 153 | 154 | code, 155 | kbd, 156 | samp { 157 | font-family: monospace, monospace; /* 1 */ 158 | font-size: 1em; /* 2 */ 159 | } 160 | 161 | /** 162 | * Add the correct font style in Android 4.3-. 163 | */ 164 | 165 | dfn { 166 | font-style: italic; 167 | } 168 | 169 | /** 170 | * Add the correct background and color in IE 9-. 171 | */ 172 | 173 | mark { 174 | background-color: #ff0; 175 | color: #000; 176 | } 177 | 178 | /** 179 | * Add the correct font size in all browsers. 180 | */ 181 | 182 | small { 183 | font-size: 80%; 184 | } 185 | 186 | /** 187 | * Prevent `sub` and `sup` elements from affecting the line height in 188 | * all browsers. 189 | */ 190 | 191 | sub, 192 | sup { 193 | font-size: 75%; 194 | line-height: 0; 195 | position: relative; 196 | vertical-align: baseline; 197 | } 198 | 199 | sub { 200 | bottom: -0.25em; 201 | } 202 | 203 | sup { 204 | top: -0.5em; 205 | } 206 | 207 | /* Embedded content 208 | ========================================================================== */ 209 | 210 | /** 211 | * Add the correct display in IE 9-. 212 | */ 213 | 214 | audio, 215 | video { 216 | display: inline-block; 217 | } 218 | 219 | /** 220 | * Add the correct display in iOS 4-7. 221 | */ 222 | 223 | audio:not([controls]) { 224 | display: none; 225 | height: 0; 226 | } 227 | 228 | /** 229 | * Remove the border on images inside links in IE 10-. 230 | */ 231 | 232 | img { 233 | border-style: none; 234 | } 235 | 236 | /** 237 | * Hide the overflow in IE. 238 | */ 239 | 240 | svg:not(:root) { 241 | overflow: hidden; 242 | } 243 | 244 | /* Forms 245 | ========================================================================== */ 246 | 247 | /** 248 | * 1. Change the font styles in all browsers (opinionated). 249 | * 2. Remove the margin in Firefox and Safari. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | font-family: sans-serif; /* 1 */ 258 | font-size: 100%; /* 1 */ 259 | line-height: 1.15; /* 1 */ 260 | margin: 0; /* 2 */ 261 | } 262 | 263 | /** 264 | * Show the overflow in IE. 265 | * 1. Show the overflow in Edge. 266 | */ 267 | 268 | button, 269 | input { /* 1 */ 270 | overflow: visible; 271 | } 272 | 273 | /** 274 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 275 | * 1. Remove the inheritance of text transform in Firefox. 276 | */ 277 | 278 | button, 279 | select { /* 1 */ 280 | text-transform: none; 281 | } 282 | 283 | /** 284 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 285 | * controls in Android 4. 286 | * 2. Correct the inability to style clickable types in iOS and Safari. 287 | */ 288 | 289 | button, 290 | html [type="button"], /* 1 */ 291 | [type="reset"], 292 | [type="submit"] { 293 | -webkit-appearance: button; /* 2 */ 294 | } 295 | 296 | /** 297 | * Remove the inner border and padding in Firefox. 298 | */ 299 | 300 | button::-moz-focus-inner, 301 | [type="button"]::-moz-focus-inner, 302 | [type="reset"]::-moz-focus-inner, 303 | [type="submit"]::-moz-focus-inner { 304 | border-style: none; 305 | padding: 0; 306 | } 307 | 308 | /** 309 | * Restore the focus styles unset by the previous rule. 310 | */ 311 | 312 | button:-moz-focusring, 313 | [type="button"]:-moz-focusring, 314 | [type="reset"]:-moz-focusring, 315 | [type="submit"]:-moz-focusring { 316 | outline: 1px dotted ButtonText; 317 | } 318 | 319 | /** 320 | * Change the border, margin, and padding in all browsers (opinionated). 321 | */ 322 | 323 | fieldset { 324 | border: 1px solid #c0c0c0; 325 | margin: 0 2px; 326 | padding: 0.35em 0.625em 0.75em; 327 | } 328 | 329 | /** 330 | * 1. Correct the text wrapping in Edge and IE. 331 | * 2. Correct the color inheritance from `fieldset` elements in IE. 332 | * 3. Remove the padding so developers are not caught out when they zero out 333 | * `fieldset` elements in all browsers. 334 | */ 335 | 336 | legend { 337 | box-sizing: border-box; /* 1 */ 338 | color: inherit; /* 2 */ 339 | display: table; /* 1 */ 340 | max-width: 100%; /* 1 */ 341 | padding: 0; /* 3 */ 342 | white-space: normal; /* 1 */ 343 | } 344 | 345 | /** 346 | * 1. Add the correct display in IE 9-. 347 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. 348 | */ 349 | 350 | progress { 351 | display: inline-block; /* 1 */ 352 | vertical-align: baseline; /* 2 */ 353 | } 354 | 355 | /** 356 | * Remove the default vertical scrollbar in IE. 357 | */ 358 | 359 | textarea { 360 | overflow: auto; 361 | } 362 | 363 | /** 364 | * 1. Add the correct box sizing in IE 10-. 365 | * 2. Remove the padding in IE 10-. 366 | */ 367 | 368 | [type="checkbox"], 369 | [type="radio"] { 370 | box-sizing: border-box; /* 1 */ 371 | padding: 0; /* 2 */ 372 | } 373 | 374 | /** 375 | * Correct the cursor style of increment and decrement buttons in Chrome. 376 | */ 377 | 378 | [type="number"]::-webkit-inner-spin-button, 379 | [type="number"]::-webkit-outer-spin-button { 380 | height: auto; 381 | } 382 | 383 | /** 384 | * 1. Correct the odd appearance in Chrome and Safari. 385 | * 2. Correct the outline style in Safari. 386 | */ 387 | 388 | [type="search"] { 389 | -webkit-appearance: textfield; /* 1 */ 390 | outline-offset: -2px; /* 2 */ 391 | } 392 | 393 | /** 394 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. 395 | */ 396 | 397 | [type="search"]::-webkit-search-cancel-button, 398 | [type="search"]::-webkit-search-decoration { 399 | -webkit-appearance: none; 400 | } 401 | 402 | /** 403 | * 1. Correct the inability to style clickable types in iOS and Safari. 404 | * 2. Change font properties to `inherit` in Safari. 405 | */ 406 | 407 | ::-webkit-file-upload-button { 408 | -webkit-appearance: button; /* 1 */ 409 | font: inherit; /* 2 */ 410 | } 411 | 412 | /* Interactive 413 | ========================================================================== */ 414 | 415 | /* 416 | * Add the correct display in IE 9-. 417 | * 1. Add the correct display in Edge, IE, and Firefox. 418 | */ 419 | 420 | details, /* 1 */ 421 | menu { 422 | display: block; 423 | } 424 | 425 | /* 426 | * Add the correct display in all browsers. 427 | */ 428 | 429 | summary { 430 | display: list-item; 431 | } 432 | 433 | /* Scripting 434 | ========================================================================== */ 435 | 436 | /** 437 | * Add the correct display in IE 9-. 438 | */ 439 | 440 | canvas { 441 | display: inline-block; 442 | } 443 | 444 | /** 445 | * Add the correct display in IE. 446 | */ 447 | 448 | template { 449 | display: none; 450 | } 451 | 452 | /* Hidden 453 | ========================================================================== */ 454 | 455 | /** 456 | * Add the correct display in IE 10-. 457 | */ 458 | 459 | [hidden] { 460 | display: none; 461 | } -------------------------------------------------------------------------------- /thirds/requestAnimationFrame.polyfill.js: -------------------------------------------------------------------------------- 1 | // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 2 | // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating 3 | // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel 4 | // MIT license 5 | (function() { 6 | var lastTime = 0; 7 | var vendors = ['ms', 'moz', 'webkit', 'o']; 8 | for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 9 | window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; 10 | window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame']; 11 | } 12 | if (!window.requestAnimationFrame) window.requestAnimationFrame = function(callback, element) { 13 | var currTime = new Date().getTime(); 14 | var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 15 | var id = window.setTimeout(function() { 16 | callback(currTime + timeToCall); 17 | }, timeToCall); 18 | lastTime = currTime + timeToCall; 19 | return id; 20 | }; 21 | if (!window.cancelAnimationFrame) window.cancelAnimationFrame = function(id) { 22 | clearTimeout(id); 23 | }; 24 | }()); -------------------------------------------------------------------------------- /思维导图 - canvas常用api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Array-Huang/canvas-learning/e65a372d23c044c41b836a062dfec3910c6d1971/思维导图 - canvas常用api.png --------------------------------------------------------------------------------