diff --git a/inc/Base64.js b/inc/Base64.js deleted file mode 100644 index 7c1ef07..0000000 --- a/inc/Base64.js +++ /dev/null @@ -1,67 +0,0 @@ -// http://ntt.cc/2008/01/19/base64-encoder-decoder-with-javascript.html - -// window.atob and window.btoa - -(function (window) { - - var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - - window.btoa || (window.btoa = function encode64(input) { - input = escape(input); - var output = ""; - var chr1, chr2, chr3 = ""; - var enc1, enc2, enc3, enc4 = ""; - var i = 0; - do { - chr1 = input.charCodeAt(i++); - chr2 = input.charCodeAt(i++); - chr3 = input.charCodeAt(i++); - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } - output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); - chr1 = chr2 = chr3 = ""; - enc1 = enc2 = enc3 = enc4 = ""; - } while (i < input.length); - return output; - }); - - window.atob || (window.atob = function(input) { - var output = ""; - var chr1, chr2, chr3 = ""; - var enc1, enc2, enc3, enc4 = ""; - var i = 0; - // remove all characters that are not A-Z, a-z, 0-9, +, /, or = - var base64test = /[^A-Za-z0-9\+\/\=]/g; - if (base64test.exec(input)) { - alert("There were invalid base64 characters in the input text.\n" + "Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" + "Expect errors in decoding."); - } - input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); - do { - enc1 = keyStr.indexOf(input.charAt(i++)); - enc2 = keyStr.indexOf(input.charAt(i++)); - enc3 = keyStr.indexOf(input.charAt(i++)); - enc4 = keyStr.indexOf(input.charAt(i++)); - chr1 = (enc1 << 2) | (enc2 >> 4); - chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - chr3 = ((enc3 & 3) << 6) | enc4; - output = output + String.fromCharCode(chr1); - if (enc3 != 64) { - output = output + String.fromCharCode(chr2); - } - if (enc4 != 64) { - output = output + String.fromCharCode(chr3); - } - chr1 = chr2 = chr3 = ""; - enc1 = enc2 = enc3 = enc4 = ""; - } while (i < input.length); - return unescape(output); - }); - -}(this)); \ No newline at end of file diff --git a/inc/base64binary.js b/inc/base64binary.js deleted file mode 100644 index 6e1e558..0000000 --- a/inc/base64binary.js +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright (c) 2011, Daniel Guerrero -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Daniel Guerrero nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL DANIEL GUERRERO BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -var Base64Binary = { - _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", - - /* will return a Uint8Array type */ - decodeArrayBuffer: function(input) { - var bytes = Math.ceil( (3*input.length) / 4.0); - var ab = new ArrayBuffer(bytes); - this.decode(input, ab); - - return ab; - }, - - decode: function(input, arrayBuffer) { - //get last chars to see if are valid - var lkey1 = this._keyStr.indexOf(input.charAt(input.length-1)); - var lkey2 = this._keyStr.indexOf(input.charAt(input.length-1)); - - var bytes = Math.ceil( (3*input.length) / 4.0); - if (lkey1 == 64) bytes--; //padding chars, so skip - if (lkey2 == 64) bytes--; //padding chars, so skip - - var uarray; - var chr1, chr2, chr3; - var enc1, enc2, enc3, enc4; - var i = 0; - var j = 0; - - if (arrayBuffer) - uarray = new Uint8Array(arrayBuffer); - else - uarray = new Uint8Array(bytes); - - input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); - - for (i=0; i> 4); - chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - chr3 = ((enc3 & 3) << 6) | enc4; - - uarray[i] = chr1; - if (enc3 != 64) uarray[i+1] = chr2; - if (enc4 != 64) uarray[i+2] = chr3; - } - - return uarray; - } -}; \ No newline at end of file diff --git a/inc/colorspace.js b/inc/colorspace.js new file mode 100644 index 0000000..409becc --- /dev/null +++ b/inc/colorspace.js @@ -0,0 +1,448 @@ +/* + ---------------------------------------------------------- + Color Space : 1.2 : 2012.11.06 + ---------------------------------------------------------- + https://github.com/mudcube/Color.Space.js + ---------------------------------------------------------- + RGBA <-> HSLA <-> W3 + RGBA <-> HSVA + RGBA <-> CMY <-> CMYK + RGBA <-> HEX24 <-> W3 + RGBA <-> HEX32 + RGBA <-> W3 + ---------------------------------------------------------- + Examples + ---------------------------------------------------------- + Color.Space(0x99ff0000, "HEX32>RGBA>HSLA>W3"); // outputs "hsla(60,100%,17%,0.6)" + Color.Space(0xFF0000, "HEX24>RGB>HSL"); // convert hex24 to HSL object. + ---------------------------------------------------------- + W3 values + ---------------------------------------------------------- + rgb(255,0,0) + rgba(255,0,0,1) + rgb(100%,0%,0%) + rgba(100%,0%,0%,1) + hsl(120, 100%, 50%) + hsla(120, 100%, 50%, 1) + #000000 + ---------------------------------------------------------- +*/ + +if (typeof(Color) === "undefined") var Color = {}; +if (typeof(Color.Space) === "undefined") Color.Space = {}; + +(function () { "use strict"; + +var useEval = false; // caches functions for quicker access. + +var functions = { + // holds generated cached conversion functions. +}; + +var shortcuts = { + "HEX24>HSL": "HEX24>RGB>HSL", + "HEX32>HSLA": "HEX32>RGBA>HSLA", + "HEX24>CMYK": "HEX24>RGB>CMY>CMYK", + "RGB>CMYK": "RGB>CMY>CMYK" +}; + +var root = Color.Space = function(color, route) { + if (shortcuts[route]) { // shortcut available + route = shortcuts[route]; + } + var r = route.split(">"); + // check whether color is an [], if so, convert to {} + if (typeof(color) === "object" && color[0] >= 0) { // array + var type = r[0]; + var tmp = {}; + for(var i = 0; i < type.length; i ++) { + var str = type.substr(i, 1); + tmp[str] = color[i]; + } + color = tmp; + } + if (functions[route]) { // cached function available + return functions[route](color); + } + var f = "color"; + for (var pos = 1, key = r[0]; pos < r.length; pos ++) { + if (pos > 1) { // recycle previous + key = key.substr(key.indexOf("_") + 1); + } + key += (pos === 0 ? "" : "_") + r[pos]; + color = root[key](color); + if (useEval) { + f = "Color.Space."+key+"("+f+")"; + } + } + if (useEval) { + functions[route] = eval("(function(color) { return "+f+" })"); + } + return color; +}; + +// W3C - RGB + RGBA + +root.RGB_W3 = function(o) { + return "rgb(" + (o.R >> 0) + "," + (o.G >> 0) + "," + (o.B >> 0) + ")"; +}; + +root.RGBA_W3 = function(o) { + var alpha = typeof(o.A) === "number" ? o.A / 255 : 1; + return "rgba(" + (o.R >> 0) + "," + (o.G >> 0) + "," + (o.B >> 0) + "," + alpha + ")"; +}; + +root.W3_RGB = function(o) { + var o = o.substr(4, o.length - 5).split(","); + return { + R: parseInt(o[0]), + G: parseInt(o[1]), + B: parseInt(o[2]) + } +}; + +root.W3_RGBA = function(o) { + var o = o.substr(5, o.length - 6).split(","); + return { + R: parseInt(o[0]), + G: parseInt(o[1]), + B: parseInt(o[2]), + A: parseFloat(o[3]) * 255 + } +}; + +// W3C - HSL + HSLA + +root.HSL_W3 = function(o) { + return "hsl(" + ((o.H + 0.5) >> 0) + "," + ((o.S + 0.5) >> 0) + "%," + ((o.L + 0.5) >> 0) + "%)"; +}; + +root.HSLA_W3 = function(o) { + var alpha = typeof(o.A) === "number" ? o.A / 255 : 1; + return "hsla(" + ((o.H + 0.5) >> 0) + "," + ((o.S + 0.5) >> 0) + "%," + ((o.L + 0.5) >> 0) + "%," + alpha + ")"; +}; + +root.W3_HSL = function(o) { + var o = o.substr(4, o.length - 5).split(","); + return { + H: parseInt(o[0]), + S: parseInt(o[1]), + L: parseInt(o[2]) + } +}; + +root.W3_HSLA = function(o) { + var o = o.substr(5, o.length - 6).split(","); + return { + H: parseInt(o[0]), + S: parseInt(o[1]), + L: parseInt(o[2]), + A: parseFloat(o[3]) * 255 + } +}; + +// W3 HEX = "FFFFFF" | "FFFFFFFF" + +root.W3_HEX = +root.W3_HEX24 = function (o) { + if (o.substr(0, 1) === "#") o = o.substr(1); + if (o.length === 3) o = o[0] + o[0] + o[1] + o[1] + o[2] + o[2]; + return parseInt("0x" + o); +}; + +root.W3_HEX32 = function (o) { + if (o.substr(0, 1) === "#") o = o.substr(1); + if (o.length === 6) { + return parseInt("0xFF" + o); + } else { + return parseInt("0x" + o); + } +}; + +// HEX = 0x000000 -> 0xFFFFFF + +root.HEX_W3 = +root.HEX24_W3 = function (o, maxLength) { + if (!maxLength) maxLength = 6; + if (!o) o = 0; + var z = o.toString(16); + // when string is lesser than maxLength + var n = z.length; + while (n < maxLength) { + z = "0" + z; + n++; + } + // when string is greater than maxLength + var n = z.length; + while (n > maxLength) { + z = z.substr(1); + n--; + } + return "#" + z; +}; + +root.HEX32_W3 = function(o) { + return root.HEX_W3(o, 8); +}; + +root.HEX_RGB = +root.HEX24_RGB = function (o) { + return { + R: (o >> 16), + G: (o >> 8) & 0xFF, + B: o & 0xFF + }; +}; + +// HEX32 = 0x00000000 -> 0xFFFFFFFF + +root.HEX32_RGBA = function (o) { + return { + R: o >>> 16 & 0xFF, + G: o >>> 8 & 0xFF, + B: o & 0xFF, + A: o >>> 24 + }; +}; + +// RGBA = R: Red / G: Green / B: Blue / A: Alpha + +root.RGBA_HEX32 = function (o) { + return (o.A << 24 | o.R << 16 | o.G << 8 | o.B) >>> 0; +}; + +// RGB = R: Red / G: Green / B: Blue + +root.RGB_HEX24 = +root.RGB_HEX = function (o) { + if (o.R < 0) o.R = 0; + if (o.G < 0) o.G = 0; + if (o.B < 0) o.B = 0; + if (o.R > 255) o.R = 255; + if (o.G > 255) o.G = 255; + if (o.B > 255) o.B = 255; + return o.R << 16 | o.G << 8 | o.B; +}; + +root.RGB_CMY = function (o) { + return { + C: 1 - (o.R / 255), + M: 1 - (o.G / 255), + Y: 1 - (o.B / 255) + }; +}; + +root.RGBA_HSLA = +root.RGB_HSL = function (o) { // RGB from 0 to 1 + var _R = o.R / 255, + _G = o.G / 255, + _B = o.B / 255, + min = Math.min(_R, _G, _B), + max = Math.max(_R, _G, _B), + D = max - min, + H, + S, + L = (max + min) / 2; + if (D === 0) { // No chroma + H = 0; + S = 0; + } else { // Chromatic data + if (L < 0.5) S = D / (max + min); + else S = D / (2 - max - min); + var DR = (((max - _R) / 6) + (D / 2)) / D; + var DG = (((max - _G) / 6) + (D / 2)) / D; + var DB = (((max - _B) / 6) + (D / 2)) / D; + if (_R === max) H = DB - DG; + else if (_G === max) H = (1 / 3) + DR - DB; + else if (_B === max) H = (2 / 3) + DG - DR; + if (H < 0) H += 1; + if (H > 1) H -= 1; + } + return { + H: H * 360, + S: S * 100, + L: L * 100, + A: o.A + }; +}; + +root.RGBA_HSVA = +root.RGB_HSV = function (o) { //- RGB from 0 to 255 + var _R = o.R / 255, + _G = o.G / 255, + _B = o.B / 255, + min = Math.min(_R, _G, _B), + max = Math.max(_R, _G, _B), + D = max - min, + H, + S, + V = max; + if (D === 0) { // No chroma + H = 0; + S = 0; + } else { // Chromatic data + S = D / max; + var DR = (((max - _R) / 6) + (D / 2)) / D; + var DG = (((max - _G) / 6) + (D / 2)) / D; + var DB = (((max - _B) / 6) + (D / 2)) / D; + if (_R === max) H = DB - DG; + else if (_G === max) H = (1 / 3) + DR - DB; + else if (_B === max) H = (2 / 3) + DG - DR; + if (H < 0) H += 1; + if (H > 1) H -= 1; + } + return { + H: H * 360, + S: S * 100, + V: V * 100, + A: o.A + }; +}; + +// CMY = C: Cyan / M: Magenta / Y: Yellow + +root.CMY_RGB = function (o) { + return { + R: Math.max(0, (1 - o.C) * 255), + G: Math.max(0, (1 - o.M) * 255), + B: Math.max(0, (1 - o.Y) * 255) + }; +}; + +root.CMY_CMYK = function (o) { + var C = o.C; + var M = o.M; + var Y = o.Y; + var K = Math.min(Y, Math.min(M, Math.min(C, 1))); + C = Math.round((C - K) / (1 - K) * 100); + M = Math.round((M - K) / (1 - K) * 100); + Y = Math.round((Y - K) / (1 - K) * 100); + K = Math.round(K * 100); + return { + C: C, + M: M, + Y: Y, + K: K + }; +}; + +// CMYK = C: Cyan / M: Magenta / Y: Yellow / K: Key (black) + +root.CMYK_CMY = function (o) { + return { + C: (o.C * (1 - o.K) + o.K), + M: (o.M * (1 - o.K) + o.K), + Y: (o.Y * (1 - o.K) + o.K) + }; +}; + +// HSL (1978) = H: Hue / S: Saturation / L: Lightess +// en.wikipedia.org/wiki/HSL_and_HSV + +root.HSLA_RGBA = +root.HSL_RGB = function (o) { + var H = o.H / 360; + var S = o.S / 100; + var L = o.L / 100; + var R, G, B; + var temp1, temp2, temp3; + if (S === 0) { + R = G = B = L; + } else { + if (L < 0.5) temp2 = L * (1 + S); + else temp2 = (L + S) - (S * L); + temp1 = 2 * L - temp2; + // calculate red + temp3 = H + (1 / 3); + if (temp3 < 0) temp3 += 1; + if (temp3 > 1) temp3 -= 1; + if ((6 * temp3) < 1) R = temp1 + (temp2 - temp1) * 6 * temp3; + else if ((2 * temp3) < 1) R = temp2; + else if ((3 * temp3) < 2) R = temp1 + (temp2 - temp1) * ((2 / 3) - temp3) * 6; + else R = temp1; + // calculate green + temp3 = H; + if (temp3 < 0) temp3 += 1; + if (temp3 > 1) temp3 -= 1; + if ((6 * temp3) < 1) G = temp1 + (temp2 - temp1) * 6 * temp3; + else if ((2 * temp3) < 1) G = temp2; + else if ((3 * temp3) < 2) G = temp1 + (temp2 - temp1) * ((2 / 3) - temp3) * 6; + else G = temp1; + // calculate blue + temp3 = H - (1 / 3); + if (temp3 < 0) temp3 += 1; + if (temp3 > 1) temp3 -= 1; + if ((6 * temp3) < 1) B = temp1 + (temp2 - temp1) * 6 * temp3; + else if ((2 * temp3) < 1) B = temp2; + else if ((3 * temp3) < 2) B = temp1 + (temp2 - temp1) * ((2 / 3) - temp3) * 6; + else B = temp1; + } + return { + R: R * 255, + G: G * 255, + B: B * 255, + A: o.A + }; +}; + +// HSV (1978) = H: Hue / S: Saturation / V: Value +// en.wikipedia.org/wiki/HSL_and_HSV + +root.HSVA_RGBA = +root.HSV_RGB = function (o) { + var H = o.H / 360; + var S = o.S / 100; + var V = o.V / 100; + var R, G, B, D, A, C; + if (S === 0) { + R = G = B = Math.round(V * 255); + } else { + if (H >= 1) H = 0; + H = 6 * H; + D = H - Math.floor(H); + A = Math.round(255 * V * (1 - S)); + B = Math.round(255 * V * (1 - (S * D))); + C = Math.round(255 * V * (1 - (S * (1 - D)))); + V = Math.round(255 * V); + switch (Math.floor(H)) { + case 0: + R = V; + G = C; + B = A; + break; + case 1: + R = B; + G = V; + B = A; + break; + case 2: + R = A; + G = V; + B = C; + break; + case 3: + R = A; + G = B; + B = V; + break; + case 4: + R = C; + G = A; + B = V; + break; + case 5: + R = V; + G = A; + B = B; + break; + } + } + return { + R: R, + G: G, + B: B, + A: o.A + }; +}; + +})(); \ No newline at end of file diff --git a/inc/event.js b/inc/event.js new file mode 100644 index 0000000..db9215b --- /dev/null +++ b/inc/event.js @@ -0,0 +1,1901 @@ +/*: + ---------------------------------------------------- + event.js : 1.1.5 : 2013/12/12 : MIT License + ---------------------------------------------------- + https://github.com/mudcube/Event.js + ---------------------------------------------------- + 1 : click, dblclick, dbltap + 1+ : tap, longpress, drag, swipe + 2+ : pinch, rotate + : mousewheel, devicemotion, shake + ---------------------------------------------------- + Ideas for the future + ---------------------------------------------------- + * GamePad, and other input abstractions. + * Event batching - i.e. for every x fingers down a new gesture is created. + ---------------------------------------------------- + http://www.w3.org/TR/2011/WD-touch-events-20110505/ + ---------------------------------------------------- +*/ + +if (typeof(eventjs) === "undefined") var eventjs = {}; + +(function(root) { "use strict"; + +// Add custom *EventListener commands to HTMLElements (set false to prevent funkiness). +root.modifyEventListener = false; + +// Add bulk *EventListener commands on NodeLists from querySelectorAll and others (set false to prevent funkiness). +root.modifySelectors = false; + +// Event maintenance. +root.add = function(target, type, listener, configure) { + return eventManager(target, type, listener, configure, "add"); +}; + +root.remove = function(target, type, listener, configure) { + return eventManager(target, type, listener, configure, "remove"); +}; + +root.returnFalse = function(event) { + return false; +}; + +root.stop = function(event) { + if (!event) return; + if (event.stopPropagation) event.stopPropagation(); + event.cancelBubble = true; // <= IE8 + event.cancelBubbleCount = 0; +}; + +root.prevent = function(event) { + if (!event) return; + if (event.preventDefault) { + event.preventDefault(); + } else if (event.preventManipulation) { + event.preventManipulation(); // MS + } else { + event.returnValue = false; // <= IE8 + } +}; + +root.cancel = function(event) { + root.stop(event); + root.prevent(event); +}; + +root.blur = function() { // Blurs the focused element. Useful when using eventjs.cancel as canceling will prevent focused elements from being blurred. + var node = document.activeElement; + if (!node) return; + var nodeName = document.activeElement.nodeName; + if (nodeName === "INPUT" || nodeName === "TEXTAREA" || node.contentEditable === "true") { + if (node.blur) node.blur(); + } +}; + +// Check whether event is natively supported (via @kangax) +root.getEventSupport = function (target, type) { + if (typeof(target) === "string") { + type = target; + target = window; + } + type = "on" + type; + if (type in target) return true; + if (!target.setAttribute) target = document.createElement("div"); + if (target.setAttribute && target.removeAttribute) { + target.setAttribute(type, ""); + var isSupported = typeof target[type] === "function"; + if (typeof target[type] !== "undefined") target[type] = null; + target.removeAttribute(type); + return isSupported; + } +}; + +var clone = function (obj) { + if (!obj || typeof (obj) !== 'object') return obj; + var temp = new obj.constructor(); + for (var key in obj) { + if (!obj[key] || typeof (obj[key]) !== 'object') { + temp[key] = obj[key]; + } else { // clone sub-object + temp[key] = clone(obj[key]); + } + } + return temp; +}; + +/// Handle custom *EventListener commands. +var eventManager = function(target, type, listener, configure, trigger, fromOverwrite) { + configure = configure || {}; + // Check whether target is a configuration variable; + if (String(target) === "[object Object]") { + var data = target; + target = data.target; delete data.target; + /// + if (data.type && data.listener) { + type = data.type; delete data.type; + listener = data.listener; delete data.listener; + for (var key in data) { + configure[key] = data[key]; + } + } else { // specialness + for (var param in data) { + var value = data[param]; + if (typeof(value) === "function") continue; + configure[param] = value; + } + /// + var ret = {}; + for (var key in data) { + var param = key.split(","); + var o = data[key]; + var conf = {}; + for (var k in configure) { // clone base configuration + conf[k] = configure[k]; + } + /// + if (typeof(o) === "function") { // without configuration + var listener = o; + } else if (typeof(o.listener) === "function") { // with configuration + var listener = o.listener; + for (var k in o) { // merge configure into base configuration + if (typeof(o[k]) === "function") continue; + conf[k] = o[k]; + } + } else { // not a listener + continue; + } + /// + for (var n = 0; n < param.length; n ++) { + ret[key] = eventjs.add(target, param[n], listener, conf, trigger); + } + } + return ret; + } + } + /// + if (!target || !type || !listener) return; + // Check for element to load on interval (before onload). + if (typeof(target) === "string" && type === "ready") { + if (window.eventjs_stallOnReady) { /// force stall for scripts to load + type = "load"; + target = window; + } else { // + var time = (new Date()).getTime(); + var timeout = configure.timeout; + var ms = configure.interval || 1000 / 60; + var interval = window.setInterval(function() { + if ((new Date()).getTime() - time > timeout) { + window.clearInterval(interval); + } + if (document.querySelector(target)) { + window.clearInterval(interval); + setTimeout(listener, 1); + } + }, ms); + return; + } + } + // Get DOM element from Query Selector. + if (typeof(target) === "string") { + target = document.querySelectorAll(target); + if (target.length === 0) return createError("Missing target on listener!", arguments); // No results. + if (target.length === 1) { // Single target. + target = target[0]; + } + } + + /// Handle multiple targets. + var event; + var events = {}; + if (target.length > 0 && target !== window) { + for (var n0 = 0, length0 = target.length; n0 < length0; n0 ++) { + event = eventManager(target[n0], type, listener, clone(configure), trigger); + if (event) events[n0] = event; + } + return createBatchCommands(events); + } + + /// Check for multiple events in one string. + if (typeof(type) === "string") { + type = type.toLowerCase(); + if (type.indexOf(" ") !== -1) { + type = type.split(" "); + } else if (type.indexOf(",") !== -1) { + type = type.split(","); + } + } + + /// Attach or remove multiple events associated with a target. + if (typeof(type) !== "string") { // Has multiple events. + if (typeof(type.length) === "number") { // Handle multiple listeners glued together. + for (var n1 = 0, length1 = type.length; n1 < length1; n1 ++) { // Array [type] + event = eventManager(target, type[n1], listener, clone(configure), trigger); + if (event) events[type[n1]] = event; + } + } else { // Handle multiple listeners. + for (var key in type) { // Object {type} + if (typeof(type[key]) === "function") { // without configuration. + event = eventManager(target, key, type[key], clone(configure), trigger); + } else { // with configuration. + event = eventManager(target, key, type[key].listener, clone(type[key]), trigger); + } + if (event) events[key] = event; + } + } + return createBatchCommands(events); + } else if (type.indexOf("on") === 0) { // to support things like "onclick" instead of "click" + type = type.substr(2); + } + + // Ensure listener is a function. + if (typeof(target) !== "object") return createError("Target is not defined!", arguments); + if (typeof(listener) !== "function") return createError("Listener is not a function!", arguments); + + // Generate a unique wrapper identifier. + var useCapture = configure.useCapture || false; + var id = getID(target) + "." + getID(listener) + "." + (useCapture ? 1 : 0); + // Handle the event. + if (root.Gesture && root.Gesture._gestureHandlers[type]) { // Fire custom event. + id = type + id; + if (trigger === "remove") { // Remove event listener. + if (!wrappers[id]) return; // Already removed. + wrappers[id].remove(); + delete wrappers[id]; + } else if (trigger === "add") { // Attach event listener. + if (wrappers[id]) { + wrappers[id].add(); + return wrappers[id]; // Already attached. + } + // Retains "this" orientation. + if (configure.useCall && !root.modifyEventListener) { + var tmp = listener; + listener = function(event, self) { + for (var key in self) event[key] = self[key]; + return tmp.call(target, event); + }; + } + // Create listener proxy. + configure.gesture = type; + configure.target = target; + configure.listener = listener; + configure.fromOverwrite = fromOverwrite; + // Record wrapper. + wrappers[id] = root.proxy[type](configure); + } + return wrappers[id]; + } else { // Fire native event. + var eventList = getEventList(type); + for (var n = 0, eventId; n < eventList.length; n ++) { + type = eventList[n]; + eventId = type + "." + id; + if (trigger === "remove") { // Remove event listener. + if (!wrappers[eventId]) continue; // Already removed. + target[remove](type, listener, useCapture); + delete wrappers[eventId]; + } else if (trigger === "add") { // Attach event listener. + if (wrappers[eventId]) return wrappers[eventId]; // Already attached. + target[add](type, listener, useCapture); + // Record wrapper. + wrappers[eventId] = { + id: eventId, + type: type, + target: target, + listener: listener, + remove: function() { + for (var n = 0; n < eventList.length; n ++) { + root.remove(target, eventList[n], listener, configure); + } + } + }; + } + } + return wrappers[eventId]; + } +}; + +/// Perform batch actions on multiple events. +var createBatchCommands = function(events) { + return { + remove: function() { // Remove multiple events. + for (var key in events) { + events[key].remove(); + } + }, + add: function() { // Add multiple events. + for (var key in events) { + events[key].add(); + } + } + }; +}; + +/// Display error message in console. +var createError = function(message, data) { + if (typeof(console) === "undefined") return; + if (typeof(console.error) === "undefined") return; + console.error(message, data); +}; + +/// Handle naming discrepancies between platforms. +var pointerDefs = { + "msPointer": [ "MSPointerDown", "MSPointerMove", "MSPointerUp" ], + "touch": [ "touchstart", "touchmove", "touchend" ], + "mouse": [ "mousedown", "mousemove", "mouseup" ] +}; + +var pointerDetect = { + // MSPointer + "MSPointerDown": 0, + "MSPointerMove": 1, + "MSPointerUp": 2, + // Touch + "touchstart": 0, + "touchmove": 1, + "touchend": 2, + // Mouse + "mousedown": 0, + "mousemove": 1, + "mouseup": 2 +}; + +var getEventSupport = (function() { + root.supports = {}; + if (window.navigator.msPointerEnabled) { + root.supports.msPointer = true; + } + if (root.getEventSupport("touchstart")) { + root.supports.touch = true; + } + if (root.getEventSupport("mousedown")) { + root.supports.mouse = true; + } +})(); + +var getEventList = (function() { + return function(type) { + var prefix = document.addEventListener ? "" : "on"; // IE + var idx = pointerDetect[type]; + if (isFinite(idx)) { + var types = []; + for (var key in root.supports) { + types.push(prefix + pointerDefs[key][idx]); + } + return types; + } else { + return [ prefix + type ]; + } + }; +})(); + +/// Event wrappers to keep track of all events placed in the window. +var wrappers = {}; +var counter = 0; +var getID = function(object) { + if (object === window) return "#window"; + if (object === document) return "#document"; + if (!object.uniqueID) object.uniqueID = "e" + counter ++; + return object.uniqueID; +}; + +/// Detect platforms native *EventListener command. +var add = document.addEventListener ? "addEventListener" : "attachEvent"; +var remove = document.removeEventListener ? "removeEventListener" : "detachEvent"; + +/* + Pointer.js + ---------------------------------------- + Modified from; https://github.com/borismus/pointer.js +*/ + +root.createPointerEvent = function (event, self, preventRecord) { + var eventName = self.gesture; + var target = self.target; + var pts = event.changedTouches || root.proxy.getCoords(event); + if (pts.length) { + var pt = pts[0]; + self.pointers = preventRecord ? [] : pts; + self.pageX = pt.pageX; + self.pageY = pt.pageY; + self.x = self.pageX; + self.y = self.pageY; + } + /// + var newEvent = document.createEvent("Event"); + newEvent.initEvent(eventName, true, true); + newEvent.originalEvent = event; + for (var k in self) { + if (k === "target") continue; + newEvent[k] = self[k]; + } + /// + var type = newEvent.type; + if (root.Gesture && root.Gesture._gestureHandlers[type]) { // capture custom events. +// target.dispatchEvent(newEvent); + self.oldListener.call(target, newEvent, self, false); + } +}; + +/// Allows *EventListener to use custom event proxies. +if (root.modifyEventListener && window.HTMLElement) (function() { + var augmentEventListener = function(proto) { + var recall = function(trigger) { // overwrite native *EventListener's + var handle = trigger + "EventListener"; + var handler = proto[handle]; + proto[handle] = function (type, listener, useCapture) { + if (root.Gesture && root.Gesture._gestureHandlers[type]) { // capture custom events. + var configure = useCapture; + if (typeof(useCapture) === "object") { + configure.useCall = true; + } else { // convert to configuration object. + configure = { + useCall: true, + useCapture: useCapture + }; + } + eventManager(this, type, listener, configure, trigger, true); +// handler.call(this, type, listener, useCapture); + } else { // use native function. + var types = getEventList(type); + for (var n = 0; n < types.length; n ++) { + handler.call(this, types[n], listener, useCapture); + } + } + }; + }; + recall("add"); + recall("remove"); + }; + // NOTE: overwriting HTMLElement doesn't do anything in Firefox. + if (navigator.userAgent.match(/Firefox/)) { + // TODO: fix Firefox for the general case. + augmentEventListener(HTMLDivElement.prototype); + augmentEventListener(HTMLCanvasElement.prototype); + } else { + augmentEventListener(HTMLElement.prototype); + } + augmentEventListener(document); + augmentEventListener(window); +})(); + +/// Allows querySelectorAll and other NodeLists to perform *EventListener commands in bulk. +if (root.modifySelectors) (function() { + var proto = NodeList.prototype; + proto.removeEventListener = function(type, listener, useCapture) { + for (var n = 0, length = this.length; n < length; n ++) { + this[n].removeEventListener(type, listener, useCapture); + } + }; + proto.addEventListener = function(type, listener, useCapture) { + for (var n = 0, length = this.length; n < length; n ++) { + this[n].addEventListener(type, listener, useCapture); + } + }; +})(); + +return root; + +})(eventjs); +/*: + ---------------------------------------------------- + eventjs.proxy : 0.4.2 : 2013/07/17 : MIT License + ---------------------------------------------------- + https://github.com/mudcube/eventjs.js + ---------------------------------------------------- +*/ + +if (typeof(eventjs) === "undefined") var eventjs = {}; +if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; + +eventjs.proxy = (function(root) { "use strict"; + +/* + Create a new pointer gesture instance. +*/ + +root.pointerSetup = function(conf, self) { + /// Configure. + conf.target = conf.target || window; + conf.doc = conf.target.ownerDocument || conf.target; // Associated document. + conf.minFingers = conf.minFingers || conf.fingers || 1; // Minimum required fingers. + conf.maxFingers = conf.maxFingers || conf.fingers || Infinity; // Maximum allowed fingers. + conf.position = conf.position || "relative"; // Determines what coordinate system points are returned. + delete conf.fingers; //- + /// Convenience data. + self = self || {}; + self.enabled = true; + self.gesture = conf.gesture; + self.target = conf.target; + self.env = conf.env; + /// + if (eventjs.modifyEventListener && conf.fromOverwrite) { + conf.oldListener = conf.listener; + conf.listener = eventjs.createPointerEvent; + } + /// Convenience commands. + var fingers = 0; + var type = self.gesture.indexOf("pointer") === 0 && eventjs.modifyEventListener ? "pointer" : "mouse"; + if (conf.oldListener) self.oldListener = conf.oldListener; + /// + self.listener = conf.listener; + self.proxy = function(listener) { + self.defaultListener = conf.listener; + conf.listener = listener; + listener(conf.event, self); + }; + self.add = function() { + if (self.enabled === true) return; + if (conf.onPointerDown) eventjs.add(conf.target, type + "down", conf.onPointerDown); + if (conf.onPointerMove) eventjs.add(conf.doc, type + "move", conf.onPointerMove); + if (conf.onPointerUp) eventjs.add(conf.doc, type + "up", conf.onPointerUp); + self.enabled = true; + }; + self.remove = function() { + if (self.enabled === false) return; + if (conf.onPointerDown) eventjs.remove(conf.target, type + "down", conf.onPointerDown); + if (conf.onPointerMove) eventjs.remove(conf.doc, type + "move", conf.onPointerMove); + if (conf.onPointerUp) eventjs.remove(conf.doc, type + "up", conf.onPointerUp); + self.reset(); + self.enabled = false; + }; + self.pause = function(opt) { + if (conf.onPointerMove && (!opt || opt.move)) eventjs.remove(conf.doc, type + "move", conf.onPointerMove); + if (conf.onPointerUp && (!opt || opt.up)) eventjs.remove(conf.doc, type + "up", conf.onPointerUp); + fingers = conf.fingers; + conf.fingers = 0; + }; + self.resume = function(opt) { + if (conf.onPointerMove && (!opt || opt.move)) eventjs.add(conf.doc, type + "move", conf.onPointerMove); + if (conf.onPointerUp && (!opt || opt.up)) eventjs.add(conf.doc, type + "up", conf.onPointerUp); + conf.fingers = fingers; + }; + self.reset = function() { + conf.tracker = {}; + conf.fingers = 0; + }; + /// + return self; +}; + +/* + Begin proxied pointer command. +*/ + +var sp = eventjs.supports; // Default pointerType +/// +eventjs.isMouse = !!sp.mouse; +eventjs.isMSPointer = !!sp.touch; +eventjs.isTouch = !!sp.msPointer; +/// +root.pointerStart = function(event, self, conf) { + /// tracks multiple inputs + var type = (event.type || "mousedown").toUpperCase(); + if (type.indexOf("MOUSE") === 0) { + eventjs.isMouse = true; + eventjs.isTouch = false; + eventjs.isMSPointer = false; + } else if (type.indexOf("TOUCH") === 0) { + eventjs.isMouse = false; + eventjs.isTouch = true; + eventjs.isMSPointer = false; + } else if (type.indexOf("MSPOINTER") === 0) { + eventjs.isMouse = false; + eventjs.isTouch = false; + eventjs.isMSPointer = true; + } + /// + var addTouchStart = function(touch, sid) { + var bbox = conf.bbox; + var pt = track[sid] = {}; + /// + switch(conf.position) { + case "absolute": // Absolute from within window. + pt.offsetX = 0; + pt.offsetY = 0; + break; + case "differenceFromLast": // Since last coordinate recorded. + pt.offsetX = touch.pageX; + pt.offsetY = touch.pageY; + break; + case "difference": // Relative from origin. + pt.offsetX = touch.pageX; + pt.offsetY = touch.pageY; + break; + case "move": // Move target element. + pt.offsetX = touch.pageX - bbox.x1; + pt.offsetY = touch.pageY - bbox.y1; + break; + default: // Relative from within target. + pt.offsetX = bbox.x1 - bbox.scrollLeft; + pt.offsetY = bbox.y1 - bbox.scrollTop; + break; + } + /// + var x = touch.pageX - pt.offsetX; + var y = touch.pageY - pt.offsetY; + /// + pt.rotation = 0; + pt.scale = 1; + pt.startTime = pt.moveTime = (new Date()).getTime(); + pt.move = { x: x, y: y }; + pt.start = { x: x, y: y }; + /// + conf.fingers ++; + }; + /// + conf.event = event; + if (self.defaultListener) { + conf.listener = self.defaultListener; + delete self.defaultListener; + } + /// + var isTouchStart = !conf.fingers; + var track = conf.tracker; + var touches = event.changedTouches || root.getCoords(event); + var length = touches.length; + // Adding touch events to tracking. + for (var i = 0; i < length; i ++) { + var touch = touches[i]; + var sid = touch.identifier || Infinity; // Touch ID. + // Track the current state of the touches. + if (conf.fingers) { + if (conf.fingers >= conf.maxFingers) { + var ids = []; + for (var sid in conf.tracker) ids.push(sid); + self.identifier = ids.join(","); + return isTouchStart; + } + var fingers = 0; // Finger ID. + for (var rid in track) { + // Replace removed finger. + if (track[rid].up) { + delete track[rid]; + addTouchStart(touch, sid); + conf.cancel = true; + break; + } + fingers ++; + } + // Add additional finger. + if (track[sid]) continue; + addTouchStart(touch, sid); + } else { // Start tracking fingers. + track = conf.tracker = {}; + self.bbox = conf.bbox = root.getBoundingBox(conf.target); + conf.fingers = 0; + conf.cancel = false; + addTouchStart(touch, sid); + } + } + /// + var ids = []; + for (var sid in conf.tracker) ids.push(sid); + self.identifier = ids.join(","); + /// + return isTouchStart; +}; + +/* + End proxied pointer command. +*/ + +root.pointerEnd = function(event, self, conf, onPointerUp) { + // Record changed touches have ended (iOS changedTouches is not reliable). + var touches = event.touches || []; + var length = touches.length; + var exists = {}; + for (var i = 0; i < length; i ++) { + var touch = touches[i]; + var sid = touch.identifier; + exists[sid || Infinity] = true; + } + for (var sid in conf.tracker) { + var track = conf.tracker[sid]; + if (exists[sid] || track.up) continue; + if (onPointerUp) { // add changedTouches to mouse. + onPointerUp({ + pageX: track.pageX, + pageY: track.pageY, + changedTouches: [{ + pageX: track.pageX, + pageY: track.pageY, + identifier: sid === "Infinity" ? Infinity : sid + }] + }, "up"); + } + track.up = true; + conf.fingers --; + } +/* // This should work but fails in Safari on iOS4 so not using it. + var touches = event.changedTouches || root.getCoords(event); + var length = touches.length; + // Record changed touches have ended (this should work). + for (var i = 0; i < length; i ++) { + var touch = touches[i]; + var sid = touch.identifier || Infinity; + var track = conf.tracker[sid]; + if (track && !track.up) { + if (onPointerUp) { // add changedTouches to mouse. + onPointerUp({ + changedTouches: [{ + pageX: track.pageX, + pageY: track.pageY, + identifier: sid === "Infinity" ? Infinity : sid + }] + }, "up"); + } + track.up = true; + conf.fingers --; + } + } */ + // Wait for all fingers to be released. + if (conf.fingers !== 0) return false; + // Record total number of fingers gesture used. + var ids = []; + conf.gestureFingers = 0; + for (var sid in conf.tracker) { + conf.gestureFingers ++; + ids.push(sid); + } + self.identifier = ids.join(","); + // Our pointer gesture has ended. + return true; +}; + +/* + Returns mouse coords in an array to match event.*Touches + ------------------------------------------------------------ + var touch = event.changedTouches || root.getCoords(event); +*/ + +root.getCoords = function(event) { + if (typeof(event.pageX) !== "undefined") { // Desktop browsers. + root.getCoords = function(event) { + return Array({ + type: "mouse", + x: event.pageX, + y: event.pageY, + pageX: event.pageX, + pageY: event.pageY, + identifier: event.pointerId || Infinity // pointerId is MS + }); + }; + } else { // Internet Explorer <= 8.0 + root.getCoords = function(event) { + var doc = document.documentElement; + event = event || window.event; + return Array({ + type: "mouse", + x: event.clientX + doc.scrollLeft, + y: event.clientY + doc.scrollTop, + pageX: event.clientX + doc.scrollLeft, + pageY: event.clientY + doc.scrollTop, + identifier: Infinity + }); + }; + } + return root.getCoords(event); +}; + +/* + Returns single coords in an object. + ------------------------------------------------------------ + var mouse = root.getCoord(event); +*/ + +root.getCoord = function(event) { + if ("ontouchstart" in window) { // Mobile browsers. + var pX = 0; + var pY = 0; + root.getCoord = function(event) { + var touches = event.changedTouches; + if (touches && touches.length) { // ontouchstart + ontouchmove + return { + x: pX = touches[0].pageX, + y: pY = touches[0].pageY + }; + } else { // ontouchend + return { + x: pX, + y: pY + }; + } + }; + } else if(typeof(event.pageX) !== "undefined" && typeof(event.pageY) !== "undefined") { // Desktop browsers. + root.getCoord = function(event) { + return { + x: event.pageX, + y: event.pageY + }; + }; + } else { // Internet Explorer <=8.0 + root.getCoord = function(event) { + var doc = document.documentElement; + event = event || window.event; + return { + x: event.clientX + doc.scrollLeft, + y: event.clientY + doc.scrollTop + }; + }; + } + return root.getCoord(event); +}; + +/* + Get target scale and position in space. +*/ + +var getPropertyAsFloat = function(o, type) { + var n = parseFloat(o.getPropertyValue(type), 10); + return isFinite(n) ? n : 0; +}; + +root.getBoundingBox = function(o) { + if (o === window || o === document) o = document.body; + /// + var bbox = {}; + var bcr = o.getBoundingClientRect(); + bbox.width = bcr.width; + bbox.height = bcr.height; + bbox.x1 = bcr.left; + bbox.y1 = bcr.top; + bbox.scaleX = bcr.width / o.offsetWidth || 1; + bbox.scaleY = bcr.height / o.offsetHeight || 1; + bbox.scrollLeft = 0; + bbox.scrollTop = 0; + /// + var style = window.getComputedStyle(o); + var borderBox = style.getPropertyValue("box-sizing") === "border-box"; + /// + if (borderBox === false) { + var left = getPropertyAsFloat(style, "border-left-width"); + var right = getPropertyAsFloat(style, "border-right-width"); + var bottom = getPropertyAsFloat(style, "border-bottom-width"); + var top = getPropertyAsFloat(style, "border-top-width"); + bbox.border = [ left, right, top, bottom ]; + bbox.x1 += left; + bbox.y1 += top; + bbox.width -= right + left; + bbox.height -= bottom + top; + } + +/* var left = getPropertyAsFloat(style, "padding-left"); + var right = getPropertyAsFloat(style, "padding-right"); + var bottom = getPropertyAsFloat(style, "padding-bottom"); + var top = getPropertyAsFloat(style, "padding-top"); + bbox.padding = [ left, right, top, bottom ];*/ + /// + bbox.x2 = bbox.x1 + bbox.width; + bbox.y2 = bbox.y1 + bbox.height; + + /// Get the scroll of container element. + var position = style.getPropertyValue("position"); + var tmp = position === "fixed" ? o : o.parentNode; + while (tmp !== null) { + if (tmp === document.body) break; + if (tmp.scrollTop === undefined) break; + var style = window.getComputedStyle(tmp); + var position = style.getPropertyValue("position"); + if (position === "absolute") { + + } else if (position === "fixed") { +// bbox.scrollTop += document.body.scrollTop; +// bbox.scrollLeft += document.body.scrollLeft; + bbox.scrollTop -= tmp.parentNode.scrollTop; + bbox.scrollLeft -= tmp.parentNode.scrollLeft; + break; + } else { + bbox.scrollLeft += tmp.scrollLeft; + bbox.scrollTop += tmp.scrollTop; + } + /// + tmp = tmp.parentNode; + }; + /// + bbox.scrollBodyLeft = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft; + bbox.scrollBodyTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop; + /// + bbox.scrollLeft -= bbox.scrollBodyLeft; + bbox.scrollTop -= bbox.scrollBodyTop; + /// + return bbox; +}; + +/* + Keep track of metaKey, the proper ctrlKey for users platform. + ---------------------------------------------------- + http://www.quirksmode.org/js/keys.html +*/ + +(function() { + var agent = navigator.userAgent.toLowerCase(); + var mac = agent.indexOf("macintosh") !== -1; + var metaKeys; + if (mac && agent.indexOf("khtml") !== -1) { // chrome, safari. + metaKeys = { 91: true, 93: true }; + } else if (mac && agent.indexOf("firefox") !== -1) { // mac firefox. + metaKeys = { 224: true }; + } else { // windows, linux, or mac opera. + metaKeys = { 17: true }; + } + (root.metaTrackerReset = function() { + eventjs.fnKey = root.fnKey = false; + eventjs.metaKey = root.metaKey = false; + eventjs.ctrlKey = root.ctrlKey = false; + eventjs.shiftKey = root.shiftKey = false; + eventjs.altKey = root.altKey = false; + })(); + root.metaTracker = function(event) { + var metaCheck = !!metaKeys[event.keyCode]; + if (metaCheck) eventjs.metaKey = root.metaKey = event.type === "keydown"; + eventjs.ctrlKey = root.ctrlKey = event.ctrlKey; + eventjs.shiftKey = root.shiftKey = event.shiftKey; + eventjs.altKey = root.altKey = event.altKey; + return metaCheck; + }; +})(); + +return root; + +})(eventjs.proxy); +/*: + ---------------------------------------------------- + "MutationObserver" event proxy. + ---------------------------------------------------- + author: Selvakumar Arumugam - MIT LICENSE + src: http://stackoverflow.com/questions/10868104/can-you-have-a-javascript-hook-trigger-after-a-dom-elements-style-object-change + ---------------------------------------------------- +*/ +if (typeof(eventjs) === "undefined") var eventjs = {}; + +eventjs.MutationObserver = (function() { + var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; + var DOMAttrModifiedSupported = !MutationObserver && (function() { + var p = document.createElement("p"); + var flag = false; + var fn = function() { flag = true }; + if (p.addEventListener) { + p.addEventListener("DOMAttrModified", fn, false); + } else if (p.attachEvent) { + p.attachEvent("onDOMAttrModified", fn); + } else { + return false; + } + /// + p.setAttribute("id", "target"); + /// + return flag; + })(); + /// + return function(container, callback) { + if (MutationObserver) { + var options = { + subtree: false, + attributes: true + }; + var observer = new MutationObserver(function(mutations) { + mutations.forEach(function(e) { + callback.call(e.target, e.attributeName); + }); + }); + observer.observe(container, options) + } else if (DOMAttrModifiedSupported) { + eventjs.add(container, "DOMAttrModified", function(e) { + callback.call(container, e.attrName); + }); + } else if ("onpropertychange" in document.body) { + eventjs.add(container, "propertychange", function(e) { + callback.call(container, window.event.propertyName); + }); + } + } +})(); +/*: + "Click" event proxy. + ---------------------------------------------------- + eventjs.add(window, "click", function(event, self) {}); +*/ + +if (typeof(eventjs) === "undefined") var eventjs = {}; +if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; + +eventjs.proxy = (function(root) { "use strict"; + +root.click = function(conf) { + conf.gesture = conf.gesture || "click"; + conf.maxFingers = conf.maxFingers || conf.fingers || 1; + /// Tracking the events. + conf.onPointerDown = function (event) { + if (root.pointerStart(event, self, conf)) { + eventjs.add(conf.target, "mouseup", conf.onPointerUp); + } + }; + conf.onPointerUp = function(event) { + if (root.pointerEnd(event, self, conf)) { + eventjs.remove(conf.target, "mouseup", conf.onPointerUp); + var pointers = event.changedTouches || root.getCoords(event); + var pointer = pointers[0]; + var bbox = conf.bbox; + var newbbox = root.getBoundingBox(conf.target); + var y = pointer.pageY - newbbox.scrollBodyTop; + var x = pointer.pageX - newbbox.scrollBodyLeft; + //// + if (x > bbox.x1 && y > bbox.y1 && + x < bbox.x2 && y < bbox.y2 && + bbox.scrollTop === newbbox.scrollTop) { // has not been scrolled + /// + for (var key in conf.tracker) break; //- should be modularized? in dblclick too + var point = conf.tracker[key]; + self.x = point.start.x; + self.y = point.start.y; + /// + conf.listener(event, self); + } + } + }; + // Generate maintenance commands, and other configurations. + var self = root.pointerSetup(conf); + self.state = "click"; + // Attach events. + eventjs.add(conf.target, "mousedown", conf.onPointerDown); + // Return this object. + return self; +}; + +eventjs.Gesture = eventjs.Gesture || {}; +eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; +eventjs.Gesture._gestureHandlers.click = root.click; + +return root; + +})(eventjs.proxy); +/*: + "Double-Click" aka "Double-Tap" event proxy. + ---------------------------------------------------- + eventjs.add(window, "dblclick", function(event, self) {}); + ---------------------------------------------------- + Touch an target twice for <= 700ms, with less than 25 pixel drift. +*/ + +if (typeof(eventjs) === "undefined") var eventjs = {}; +if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; + +eventjs.proxy = (function(root) { "use strict"; + +root.dbltap = +root.dblclick = function(conf) { + conf.gesture = conf.gesture || "dbltap"; + conf.maxFingers = conf.maxFingers || conf.fingers || 1; + // Setting up local variables. + var delay = 700; // in milliseconds + var time0, time1, timeout; + var pointer0, pointer1; + // Tracking the events. + conf.onPointerDown = function (event) { + var pointers = event.changedTouches || root.getCoords(event); + if (time0 && !time1) { // Click #2 + pointer1 = pointers[0]; + time1 = (new Date()).getTime() - time0; + } else { // Click #1 + pointer0 = pointers[0]; + time0 = (new Date()).getTime(); + time1 = 0; + clearTimeout(timeout); + timeout = setTimeout(function() { + time0 = 0; + }, delay); + } + if (root.pointerStart(event, self, conf)) { + eventjs.add(conf.target, "mousemove", conf.onPointerMove).listener(event); + eventjs.add(conf.target, "mouseup", conf.onPointerUp); + } + }; + conf.onPointerMove = function (event) { + if (time0 && !time1) { + var pointers = event.changedTouches || root.getCoords(event); + pointer1 = pointers[0]; + } + var bbox = conf.bbox; + var ax = (pointer1.pageX - bbox.x1); + var ay = (pointer1.pageY - bbox.y1); + if (!(ax > 0 && ax < bbox.width && // Within target coordinates.. + ay > 0 && ay < bbox.height && + Math.abs(pointer1.pageX - pointer0.pageX) <= 25 && // Within drift deviance. + Math.abs(pointer1.pageY - pointer0.pageY) <= 25)) { + // Cancel out this listener. + eventjs.remove(conf.target, "mousemove", conf.onPointerMove); + clearTimeout(timeout); + time0 = time1 = 0; + } + }; + conf.onPointerUp = function(event) { + if (root.pointerEnd(event, self, conf)) { + eventjs.remove(conf.target, "mousemove", conf.onPointerMove); + eventjs.remove(conf.target, "mouseup", conf.onPointerUp); + } + if (time0 && time1) { + if (time1 <= delay) { // && !(event.cancelBubble && ++event.cancelBubbleCount > 1)) { + self.state = conf.gesture; + for (var key in conf.tracker) break; + var point = conf.tracker[key]; + self.x = point.start.x; + self.y = point.start.y; + conf.listener(event, self); + } + clearTimeout(timeout); + time0 = time1 = 0; + } + }; + // Generate maintenance commands, and other configurations. + var self = root.pointerSetup(conf); + self.state = "dblclick"; + // Attach events. + eventjs.add(conf.target, "mousedown", conf.onPointerDown); + // Return this object. + return self; +}; + +eventjs.Gesture = eventjs.Gesture || {}; +eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; +eventjs.Gesture._gestureHandlers.dbltap = root.dbltap; +eventjs.Gesture._gestureHandlers.dblclick = root.dblclick; + +return root; + +})(eventjs.proxy); +/*: + "Drag" event proxy (1+ fingers). + ---------------------------------------------------- + CONFIGURE: maxFingers, position. + ---------------------------------------------------- + eventjs.add(window, "drag", function(event, self) { + console.log(self.gesture, self.state, self.start, self.x, self.y, self.bbox); + }); +*/ + +if (typeof(eventjs) === "undefined") var eventjs = {}; +if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; + +eventjs.proxy = (function(root) { "use strict"; + +root.dragElement = function(that, event) { + root.drag({ + event: event, + target: that, + position: "move", + listener: function(event, self) { + that.style.left = self.x + "px"; + that.style.top = self.y + "px"; + eventjs.prevent(event); + } + }); +}; + +root.drag = function(conf) { + conf.gesture = "drag"; + conf.onPointerDown = function (event) { + if (root.pointerStart(event, self, conf)) { + if (!conf.monitor) { + eventjs.add(conf.doc, "mousemove", conf.onPointerMove); + eventjs.add(conf.doc, "mouseup", conf.onPointerUp); + } + } + // Process event listener. + conf.onPointerMove(event, "down"); + }; + conf.onPointerMove = function (event, state) { + if (!conf.tracker) return conf.onPointerDown(event); +//alertify.log('move') + var bbox = conf.bbox; + var touches = event.changedTouches || root.getCoords(event); + var length = touches.length; + for (var i = 0; i < length; i ++) { + var touch = touches[i]; + var identifier = touch.identifier || Infinity; + var pt = conf.tracker[identifier]; + // Identifier defined outside of listener. + if (!pt) continue; + pt.pageX = touch.pageX; + pt.pageY = touch.pageY; + // Record data. + self.state = state || "move"; + self.identifier = identifier; + self.start = pt.start; + self.fingers = conf.fingers; + if (conf.position === "differenceFromLast") { + self.x = (pt.pageX - pt.offsetX); + self.y = (pt.pageY - pt.offsetY); + pt.offsetX = pt.pageX; + pt.offsetY = pt.pageY; + } else { + self.x = (pt.pageX - pt.offsetX); + self.y = (pt.pageY - pt.offsetY); + } + /// + conf.listener(event, self); + } + }; + conf.onPointerUp = function(event) { + // Remove tracking for touch. + if (root.pointerEnd(event, self, conf, conf.onPointerMove)) { + if (!conf.monitor) { + eventjs.remove(conf.doc, "mousemove", conf.onPointerMove); + eventjs.remove(conf.doc, "mouseup", conf.onPointerUp); + } + } + }; + // Generate maintenance commands, and other configurations. + var self = root.pointerSetup(conf); + // Attach events. + if (conf.event) { + conf.onPointerDown(conf.event); + } else { // + eventjs.add(conf.target, "mousedown", conf.onPointerDown); + if (conf.monitor) { + eventjs.add(conf.doc, "mousemove", conf.onPointerMove); + eventjs.add(conf.doc, "mouseup", conf.onPointerUp); + } + } + // Return this object. + return self; +}; + +eventjs.Gesture = eventjs.Gesture || {}; +eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; +eventjs.Gesture._gestureHandlers.drag = root.drag; + +return root; + +})(eventjs.proxy); +/*: + "Gesture" event proxy (2+ fingers). + ---------------------------------------------------- + CONFIGURE: minFingers, maxFingers. + ---------------------------------------------------- + eventjs.add(window, "gesture", function(event, self) { + console.log( + self.x, // centroid + self.y, + self.rotation, + self.scale, + self.fingers, + self.state + ); + }); +*/ + +if (typeof(eventjs) === "undefined") var eventjs = {}; +if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; + +eventjs.proxy = (function(root) { "use strict"; + +var RAD_DEG = Math.PI / 180; +var getCentroid = function(self, points) { + var centroidx = 0; + var centroidy = 0; + var length = 0; + for (var sid in points) { + var touch = points[sid]; + if (touch.up) continue; + centroidx += touch.move.x; + centroidy += touch.move.y; + length ++; + } + self.x = centroidx /= length; + self.y = centroidy /= length; + return self; +}; + +root.gesture = function(conf) { + conf.gesture = conf.gesture || "gesture"; + conf.minFingers = conf.minFingers || conf.fingers || 2; + // Tracking the events. + conf.onPointerDown = function (event) { + var fingers = conf.fingers; + if (root.pointerStart(event, self, conf)) { + eventjs.add(conf.doc, "mousemove", conf.onPointerMove); + eventjs.add(conf.doc, "mouseup", conf.onPointerUp); + } + // Record gesture start. + if (conf.fingers === conf.minFingers && fingers !== conf.fingers) { + self.fingers = conf.minFingers; + self.scale = 1; + self.rotation = 0; + self.state = "start"; + var sids = ""; //- FIXME(mud): can generate duplicate IDs. + for (var key in conf.tracker) sids += key; + self.identifier = parseInt(sids); + getCentroid(self, conf.tracker); + conf.listener(event, self); + } + }; + /// + conf.onPointerMove = function (event, state) { + var bbox = conf.bbox; + var points = conf.tracker; + var touches = event.changedTouches || root.getCoords(event); + var length = touches.length; + // Update tracker coordinates. + for (var i = 0; i < length; i ++) { + var touch = touches[i]; + var sid = touch.identifier || Infinity; + var pt = points[sid]; + // Check whether "pt" is used by another gesture. + if (!pt) continue; + // Find the actual coordinates. + pt.move.x = (touch.pageX - bbox.x1); + pt.move.y = (touch.pageY - bbox.y1); + } + /// + if (conf.fingers < conf.minFingers) return; + /// + var touches = []; + var scale = 0; + var rotation = 0; + + /// Calculate centroid of gesture. + getCentroid(self, points); + /// + for (var sid in points) { + var touch = points[sid]; + if (touch.up) continue; + var start = touch.start; + if (!start.distance) { + var dx = start.x - self.x; + var dy = start.y - self.y; + start.distance = Math.sqrt(dx * dx + dy * dy); + start.angle = Math.atan2(dx, dy) / RAD_DEG; + } + // Calculate scale. + var dx = touch.move.x - self.x; + var dy = touch.move.y - self.y; + var distance = Math.sqrt(dx * dx + dy * dy); + scale += distance / start.distance; + // Calculate rotation. + var angle = Math.atan2(dx, dy) / RAD_DEG; + var rotate = (start.angle - angle + 360) % 360 - 180; + touch.DEG2 = touch.DEG1; // Previous degree. + touch.DEG1 = rotate > 0 ? rotate : -rotate; // Current degree. + if (typeof(touch.DEG2) !== "undefined") { + if (rotate > 0) { + touch.rotation += touch.DEG1 - touch.DEG2; + } else { + touch.rotation -= touch.DEG1 - touch.DEG2; + } + rotation += touch.rotation; + } + // Attach current points to self. + touches.push(touch.move); + } + /// + self.touches = touches; + self.fingers = conf.fingers; + self.scale = scale / conf.fingers; + self.rotation = rotation / conf.fingers; + self.state = "change"; + conf.listener(event, self); + }; + conf.onPointerUp = function(event) { + // Remove tracking for touch. + var fingers = conf.fingers; + if (root.pointerEnd(event, self, conf)) { + eventjs.remove(conf.doc, "mousemove", conf.onPointerMove); + eventjs.remove(conf.doc, "mouseup", conf.onPointerUp); + } + // Check whether fingers has dropped below minFingers. + if (fingers === conf.minFingers && conf.fingers < conf.minFingers) { + self.fingers = conf.fingers; + self.state = "end"; + conf.listener(event, self); + } + }; + // Generate maintenance commands, and other configurations. + var self = root.pointerSetup(conf); + // Attach events. + eventjs.add(conf.target, "mousedown", conf.onPointerDown); + // Return this object. + return self; +}; + +eventjs.Gesture = eventjs.Gesture || {}; +eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; +eventjs.Gesture._gestureHandlers.gesture = root.gesture; + +return root; + +})(eventjs.proxy); +/*: + "Pointer" event proxy (1+ fingers). + ---------------------------------------------------- + CONFIGURE: minFingers, maxFingers. + ---------------------------------------------------- + eventjs.add(window, "gesture", function(event, self) { + console.log(self.rotation, self.scale, self.fingers, self.state); + }); +*/ + +if (typeof(eventjs) === "undefined") var eventjs = {}; +if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; + +eventjs.proxy = (function(root) { "use strict"; + +root.pointerdown = +root.pointermove = +root.pointerup = function(conf) { + conf.gesture = conf.gesture || "pointer"; + if (conf.target.isPointerEmitter) return; + // Tracking the events. + var isDown = true; + conf.onPointerDown = function (event) { + isDown = false; + self.gesture = "pointerdown"; + conf.listener(event, self); + }; + conf.onPointerMove = function (event) { + self.gesture = "pointermove"; + conf.listener(event, self, isDown); + }; + conf.onPointerUp = function (event) { + isDown = true; + self.gesture = "pointerup"; + conf.listener(event, self, true); + }; + // Generate maintenance commands, and other configurations. + var self = root.pointerSetup(conf); + // Attach events. + eventjs.add(conf.target, "mousedown", conf.onPointerDown); + eventjs.add(conf.target, "mousemove", conf.onPointerMove); + eventjs.add(conf.doc, "mouseup", conf.onPointerUp); + // Return this object. + conf.target.isPointerEmitter = true; + return self; +}; + +eventjs.Gesture = eventjs.Gesture || {}; +eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; +eventjs.Gesture._gestureHandlers.pointerdown = root.pointerdown; +eventjs.Gesture._gestureHandlers.pointermove = root.pointermove; +eventjs.Gesture._gestureHandlers.pointerup = root.pointerup; + +return root; + +})(eventjs.proxy); +/*: + "Device Motion" and "Shake" event proxy. + ---------------------------------------------------- + http://developer.android.com/reference/android/hardware/Sensoreventjs.html#values + ---------------------------------------------------- + eventjs.add(window, "shake", function(event, self) {}); + eventjs.add(window, "devicemotion", function(event, self) { + console.log(self.acceleration, self.accelerationIncludingGravity); + }); +*/ + +if (typeof(eventjs) === "undefined") var eventjs = {}; +if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; + +eventjs.proxy = (function(root) { "use strict"; + +root.shake = function(conf) { + // Externally accessible data. + var self = { + gesture: "devicemotion", + acceleration: {}, + accelerationIncludingGravity: {}, + target: conf.target, + listener: conf.listener, + remove: function() { + window.removeEventListener('devicemotion', onDeviceMotion, false); + } + }; + // Setting up local variables. + var threshold = 4; // Gravitational threshold. + var timeout = 1000; // Timeout between shake events. + var timeframe = 200; // Time between shakes. + var shakes = 3; // Minimum shakes to trigger event. + var lastShake = (new Date()).getTime(); + var gravity = { x: 0, y: 0, z: 0 }; + var delta = { + x: { count: 0, value: 0 }, + y: { count: 0, value: 0 }, + z: { count: 0, value: 0 } + }; + // Tracking the events. + var onDeviceMotion = function(e) { + var alpha = 0.8; // Low pass filter. + var o = e.accelerationIncludingGravity; + gravity.x = alpha * gravity.x + (1 - alpha) * o.x; + gravity.y = alpha * gravity.y + (1 - alpha) * o.y; + gravity.z = alpha * gravity.z + (1 - alpha) * o.z; + self.accelerationIncludingGravity = gravity; + self.acceleration.x = o.x - gravity.x; + self.acceleration.y = o.y - gravity.y; + self.acceleration.z = o.z - gravity.z; + /// + if (conf.gesture === "devicemotion") { + conf.listener(e, self); + return; + } + var data = "xyz"; + var now = (new Date()).getTime(); + for (var n = 0, length = data.length; n < length; n ++) { + var letter = data[n]; + var ACCELERATION = self.acceleration[letter]; + var DELTA = delta[letter]; + var abs = Math.abs(ACCELERATION); + /// Check whether another shake event was recently registered. + if (now - lastShake < timeout) continue; + /// Check whether delta surpasses threshold. + if (abs > threshold) { + var idx = now * ACCELERATION / abs; + var span = Math.abs(idx + DELTA.value); + // Check whether last delta was registered within timeframe. + if (DELTA.value && span < timeframe) { + DELTA.value = idx; + DELTA.count ++; + // Check whether delta count has enough shakes. + if (DELTA.count === shakes) { + conf.listener(e, self); + // Reset tracking. + lastShake = now; + DELTA.value = 0; + DELTA.count = 0; + } + } else { + // Track first shake. + DELTA.value = idx; + DELTA.count = 1; + } + } + } + }; + // Attach events. + if (!window.addEventListener) return; + window.addEventListener('devicemotion', onDeviceMotion, false); + // Return this object. + return self; +}; + +eventjs.Gesture = eventjs.Gesture || {}; +eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; +eventjs.Gesture._gestureHandlers.shake = root.shake; + +return root; + +})(eventjs.proxy); +/*: + "Swipe" event proxy (1+ fingers). + ---------------------------------------------------- + CONFIGURE: snap, threshold, maxFingers. + ---------------------------------------------------- + eventjs.add(window, "swipe", function(event, self) { + console.log(self.velocity, self.angle); + }); +*/ + +if (typeof(eventjs) === "undefined") var eventjs = {}; +if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; + +eventjs.proxy = (function(root) { "use strict"; + +var RAD_DEG = Math.PI / 180; + +root.swipe = function(conf) { + conf.snap = conf.snap || 90; // angle snap. + conf.threshold = conf.threshold || 1; // velocity threshold. + conf.gesture = conf.gesture || "swipe"; + // Tracking the events. + conf.onPointerDown = function (event) { + if (root.pointerStart(event, self, conf)) { + eventjs.add(conf.doc, "mousemove", conf.onPointerMove).listener(event); + eventjs.add(conf.doc, "mouseup", conf.onPointerUp); + } + }; + conf.onPointerMove = function (event) { + var touches = event.changedTouches || root.getCoords(event); + var length = touches.length; + for (var i = 0; i < length; i ++) { + var touch = touches[i]; + var sid = touch.identifier || Infinity; + var o = conf.tracker[sid]; + // Identifier defined outside of listener. + if (!o) continue; + o.move.x = touch.pageX; + o.move.y = touch.pageY; + o.moveTime = (new Date()).getTime(); + } + }; + conf.onPointerUp = function(event) { + if (root.pointerEnd(event, self, conf)) { + eventjs.remove(conf.doc, "mousemove", conf.onPointerMove); + eventjs.remove(conf.doc, "mouseup", conf.onPointerUp); + /// + var velocity1; + var velocity2 + var degree1; + var degree2; + /// Calculate centroid of gesture. + var start = { x: 0, y: 0 }; + var endx = 0; + var endy = 0; + var length = 0; + /// + for (var sid in conf.tracker) { + var touch = conf.tracker[sid]; + var xdist = touch.move.x - touch.start.x; + var ydist = touch.move.y - touch.start.y; + /// + endx += touch.move.x; + endy += touch.move.y; + start.x += touch.start.x; + start.y += touch.start.y; + length ++; + /// + var distance = Math.sqrt(xdist * xdist + ydist * ydist); + var ms = touch.moveTime - touch.startTime; + var degree2 = Math.atan2(xdist, ydist) / RAD_DEG + 180; + var velocity2 = ms ? distance / ms : 0; + if (typeof(degree1) === "undefined") { + degree1 = degree2; + velocity1 = velocity2; + } else if (Math.abs(degree2 - degree1) <= 20) { + degree1 = (degree1 + degree2) / 2; + velocity1 = (velocity1 + velocity2) / 2; + } else { + return; + } + } + /// + var fingers = conf.gestureFingers; + if (conf.minFingers <= fingers && conf.maxFingers >= fingers) { + if (velocity1 > conf.threshold) { + start.x /= length; + start.y /= length; + self.start = start; + self.x = endx / length; + self.y = endy / length; + self.angle = -((((degree1 / conf.snap + 0.5) >> 0) * conf.snap || 360) - 360); + self.velocity = velocity1; + self.fingers = fingers; + self.state = "swipe"; + conf.listener(event, self); + } + } + } + }; + // Generate maintenance commands, and other configurations. + var self = root.pointerSetup(conf); + // Attach events. + eventjs.add(conf.target, "mousedown", conf.onPointerDown); + // Return this object. + return self; +}; + +eventjs.Gesture = eventjs.Gesture || {}; +eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; +eventjs.Gesture._gestureHandlers.swipe = root.swipe; + +return root; + +})(eventjs.proxy); +/*: + "Tap" and "Longpress" event proxy. + ---------------------------------------------------- + CONFIGURE: delay (longpress), timeout (tap). + ---------------------------------------------------- + eventjs.add(window, "tap", function(event, self) { + console.log(self.fingers); + }); + ---------------------------------------------------- + multi-finger tap // touch an target for <= 250ms. + multi-finger longpress // touch an target for >= 500ms +*/ + +if (typeof(eventjs) === "undefined") var eventjs = {}; +if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; + +eventjs.proxy = (function(root) { "use strict"; + +root.longpress = function(conf) { + conf.gesture = "longpress"; + return root.tap(conf); +}; + +root.tap = function(conf) { + conf.delay = conf.delay || 500; + conf.timeout = conf.timeout || 250; + conf.driftDeviance = conf.driftDeviance || 10; + conf.gesture = conf.gesture || "tap"; + // Setting up local variables. + var timestamp, timeout; + // Tracking the events. + conf.onPointerDown = function (event) { + if (root.pointerStart(event, self, conf)) { + timestamp = (new Date()).getTime(); + // Initialize event listeners. + eventjs.add(conf.doc, "mousemove", conf.onPointerMove).listener(event); + eventjs.add(conf.doc, "mouseup", conf.onPointerUp); + // Make sure this is a "longpress" event. + if (conf.gesture !== "longpress") return; + timeout = setTimeout(function() { + if (event.cancelBubble && ++event.cancelBubbleCount > 1) return; + // Make sure no fingers have been changed. + var fingers = 0; + for (var key in conf.tracker) { + var point = conf.tracker[key]; + if (point.end === true) return; + if (conf.cancel) return; + fingers ++; + } + // Send callback. + if (conf.minFingers <= fingers && conf.maxFingers >= fingers) { + self.state = "start"; + self.fingers = fingers; + self.x = point.start.x; + self.y = point.start.y; + conf.listener(event, self); + } + }, conf.delay); + } + }; + conf.onPointerMove = function (event) { + var bbox = conf.bbox; + var touches = event.changedTouches || root.getCoords(event); + var length = touches.length; + for (var i = 0; i < length; i ++) { + var touch = touches[i]; + var identifier = touch.identifier || Infinity; + var pt = conf.tracker[identifier]; + if (!pt) continue; + var x = (touch.pageX - bbox.x1); + var y = (touch.pageY - bbox.y1); + /// + var dx = x - pt.start.x; + var dy = y - pt.start.y; + var distance = Math.sqrt(dx * dx + dy * dy); + if (!(x > 0 && x < bbox.width && // Within target coordinates.. + y > 0 && y < bbox.height && + distance <= conf.driftDeviance)) { // Within drift deviance. + // Cancel out this listener. + eventjs.remove(conf.doc, "mousemove", conf.onPointerMove); + conf.cancel = true; + return; + } + } + }; + conf.onPointerUp = function(event) { + if (root.pointerEnd(event, self, conf)) { + clearTimeout(timeout); + eventjs.remove(conf.doc, "mousemove", conf.onPointerMove); + eventjs.remove(conf.doc, "mouseup", conf.onPointerUp); + if (event.cancelBubble && ++event.cancelBubbleCount > 1) return; + // Callback release on longpress. + if (conf.gesture === "longpress") { + if (self.state === "start") { + self.state = "end"; + conf.listener(event, self); + } + return; + } + // Cancel event due to movement. + if (conf.cancel) return; + // Ensure delay is within margins. + if ((new Date()).getTime() - timestamp > conf.timeout) return; + // Send callback. + var fingers = conf.gestureFingers; + if (conf.minFingers <= fingers && conf.maxFingers >= fingers) { + self.state = "tap"; + self.fingers = conf.gestureFingers; + conf.listener(event, self); + } + } + }; + // Generate maintenance commands, and other configurations. + var self = root.pointerSetup(conf); + // Attach events. + eventjs.add(conf.target, "mousedown", conf.onPointerDown); + // Return this object. + return self; +}; + +eventjs.Gesture = eventjs.Gesture || {}; +eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; +eventjs.Gesture._gestureHandlers.tap = root.tap; +eventjs.Gesture._gestureHandlers.longpress = root.longpress; + +return root; + +})(eventjs.proxy); +/*: + "Mouse Wheel" event proxy. + ---------------------------------------------------- + eventjs.add(window, "wheel", function(event, self) { + console.log(self.state, self.wheelDelta); + }); +*/ + +if (typeof(eventjs) === "undefined") var eventjs = {}; +if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; + +eventjs.proxy = (function(root) { "use strict"; + +root.wheelPreventElasticBounce = function(el) { + if (!el) return; + if (typeof(el) === "string") el = document.querySelector(el); + eventjs.add(el, "wheel", function(event, self) { + self.preventElasticBounce(); + eventjs.stop(event); + }); +}; + +root.wheel = function(conf) { + // Configure event listener. + var interval; + var timeout = conf.timeout || 150; + var count = 0; + // Externally accessible data. + var self = { + gesture: "wheel", + state: "start", + wheelDelta: 0, + target: conf.target, + listener: conf.listener, + preventElasticBounce: function(event) { + var target = this.target; + var scrollTop = target.scrollTop; + var top = scrollTop + target.offsetHeight; + var height = target.scrollHeight; + if (top === height && this.wheelDelta <= 0) eventjs.cancel(event); + else if (scrollTop === 0 && this.wheelDelta >= 0) eventjs.cancel(event); + eventjs.stop(event); + }, + add: function() { + conf.target[add](type, onMouseWheel, false); + }, + remove: function() { + conf.target[remove](type, onMouseWheel, false); + } + }; + // Tracking the events. + var onMouseWheel = function(event) { + event = event || window.event; + self.state = count++ ? "change" : "start"; + self.wheelDelta = event.detail ? event.detail * -20 : event.wheelDelta; + conf.listener(event, self); + clearTimeout(interval); + interval = setTimeout(function() { + count = 0; + self.state = "end"; + self.wheelDelta = 0; + conf.listener(event, self); + }, timeout); + }; + // Attach events. + var add = document.addEventListener ? "addEventListener" : "attachEvent"; + var remove = document.removeEventListener ? "removeEventListener" : "detachEvent"; + var type = eventjs.getEventSupport("mousewheel") ? "mousewheel" : "DOMMouseScroll"; + conf.target[add](type, onMouseWheel, false); + // Return this object. + return self; +}; + +eventjs.Gesture = eventjs.Gesture || {}; +eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; +eventjs.Gesture._gestureHandlers.wheel = root.wheel; + +return root; + +})(eventjs.proxy); + +/// +var addEvent = eventjs.add; +var removeEvent = eventjs.remove; +/// +(function() { + for (var key in eventjs) { + Event[key] = eventjs[key]; + } + for (var key in eventjs.proxy) { + addEvent[key] = eventjs.proxy[key]; + } +})(); \ No newline at end of file diff --git a/inc/jasmid/replayer.js b/inc/jasmid/replayer.js index 347fa1d..1b25e3f 100644 --- a/inc/jasmid/replayer.js +++ b/inc/jasmid/replayer.js @@ -6,9 +6,11 @@ var clone = function (o) { return ret; }; -function Replayer(midiFile, timeWarp, eventProcessor) { +function Replayer(midiFile, timeWarp, eventProcessor, bpm) { var trackStates = []; - var beatsPerMinute = 120; + var beatsPerMinute = bpm ? bpm : 120; + var bpmOverride = bpm ? true : false; + var ticksPerBeat = midiFile.header.ticksPerBeat; for (var i = 0; i < midiFile.tracks.length; i++) { @@ -70,19 +72,23 @@ function Replayer(midiFile, timeWarp, eventProcessor) { // function processEvents() { function processNext() { - if ( midiEvent.event.type == "meta" && midiEvent.event.subtype == "setTempo" ) { + if (!bpmOverride && midiEvent.event.type == "meta" && midiEvent.event.subtype == "setTempo" ) { // tempo change events can occur anywhere in the middle and affect events that follow beatsPerMinute = 60000000 / midiEvent.event.microsecondsPerBeat; - } + } + /// + var beatsToGenerate = 0; + var secondsToGenerate = 0; if (midiEvent.ticksToEvent > 0) { - var beatsToGenerate = midiEvent.ticksToEvent / ticksPerBeat; - var secondsToGenerate = beatsToGenerate / (beatsPerMinute / 60); + beatsToGenerate = midiEvent.ticksToEvent / ticksPerBeat; + secondsToGenerate = beatsToGenerate / (beatsPerMinute / 60); } + /// var time = (secondsToGenerate * 1000 * timeWarp) || 0; temporal.push([ midiEvent, time]); midiEvent = getNextEvent(); }; - // + /// if (midiEvent = getNextEvent()) { while(midiEvent) processNext(true); } diff --git a/inc/shim/Base64.js b/inc/shim/Base64.js new file mode 100644 index 0000000..b5a59ce --- /dev/null +++ b/inc/shim/Base64.js @@ -0,0 +1,61 @@ +//https://github.com/davidchambers/Base64.js + +;(function () { + var object = typeof exports != 'undefined' ? exports : this; // #8: web workers + var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + + function InvalidCharacterError(message) { + this.message = message; + } + InvalidCharacterError.prototype = new Error; + InvalidCharacterError.prototype.name = 'InvalidCharacterError'; + + // encoder + // [https://gist.github.com/999166] by [https://github.com/nignag] + object.btoa || ( + object.btoa = function (input) { + for ( + // initialize result and counter + var block, charCode, idx = 0, map = chars, output = ''; + // if the next input index does not exist: + // change the mapping table to "=" + // check if d has no fractional digits + input.charAt(idx | 0) || (map = '=', idx % 1); + // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8 + output += map.charAt(63 & block >> 8 - idx % 1 * 8) + ) { + charCode = input.charCodeAt(idx += 3/4); + if (charCode > 0xFF) { + throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range."); + } + block = block << 8 | charCode; + } + return output; + }); + + // decoder + // [https://gist.github.com/1020396] by [https://github.com/atk] + object.atob || ( + object.atob = function (input) { + input = input.replace(/=+$/, '') + if (input.length % 4 == 1) { + throw new InvalidCharacterError("'atob' failed: The string to be decoded is not correctly encoded."); + } + for ( + // initialize result and counters + var bc = 0, bs, buffer, idx = 0, output = ''; + // get next character + buffer = input.charAt(idx++); + // character found in table? initialize bit storage and add its ascii value; + ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, + // and if not first of each 4 characters, + // convert the first 8 bits to one ascii character + bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0 + ) { + // try to find character in table (0-63, not found => -1) + buffer = chars.indexOf(buffer); + } + return output; + }); + +}()); \ No newline at end of file diff --git a/inc/shim/Base64binary.js b/inc/shim/Base64binary.js new file mode 100644 index 0000000..2c59f8f --- /dev/null +++ b/inc/shim/Base64binary.js @@ -0,0 +1,81 @@ +/** + * @license ------------------------------------------------------------------- + * module: Base64Binary + * src: http://blog.danguer.com/2011/10/24/base64-binary-decoding-in-javascript/ + * license: Simplified BSD License + * ------------------------------------------------------------------- + * Copyright 2011, Daniel Guerrero. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL GUERRERO BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +var Base64Binary = { + _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + + /* will return a Uint8Array type */ + decodeArrayBuffer: function(input) { + var bytes = Math.ceil( (3*input.length) / 4.0); + var ab = new ArrayBuffer(bytes); + this.decode(input, ab); + + return ab; + }, + + decode: function(input, arrayBuffer) { + //get last chars to see if are valid + var lkey1 = this._keyStr.indexOf(input.charAt(input.length-1)); + var lkey2 = this._keyStr.indexOf(input.charAt(input.length-1)); + + var bytes = Math.ceil( (3*input.length) / 4.0); + if (lkey1 == 64) bytes--; //padding chars, so skip + if (lkey2 == 64) bytes--; //padding chars, so skip + + var uarray; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + var j = 0; + + if (arrayBuffer) + uarray = new Uint8Array(arrayBuffer); + else + uarray = new Uint8Array(bytes); + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + for (i=0; i> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + uarray[i] = chr1; + if (enc3 != 64) uarray[i+1] = chr2; + if (enc4 != 64) uarray[i+2] = chr3; + } + + return uarray; + } +}; \ No newline at end of file diff --git a/inc/shim/WebAudioAPI.js b/inc/shim/WebAudioAPI.js new file mode 100644 index 0000000..17e9eb9 --- /dev/null +++ b/inc/shim/WebAudioAPI.js @@ -0,0 +1,111 @@ +/** + * @license ------------------------------------------------------------------- + * module: WebAudioShim - Fix naming issues for WebAudioAPI supports + * src: https://github.com/Dinahmoe/webaudioshim + * author: Dinahmoe AB + * ------------------------------------------------------------------- + * Copyright (c) 2012 DinahMoe AB + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +window.AudioContext = window.AudioContext || window.webkitAudioContext || null; +window.OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext || null; + +(function (Context) { + var isFunction = function (f) { + return Object.prototype.toString.call(f) === "[object Function]" || + Object.prototype.toString.call(f) === "[object AudioContextConstructor]"; + }; + var contextMethods = [ + ["createGainNode", "createGain"], + ["createDelayNode", "createDelay"], + ["createJavaScriptNode", "createScriptProcessor"] + ]; + /// + var proto; + var instance; + var sourceProto; + /// + if (!isFunction(Context)) { + return; + } + instance = new Context(); + if (!instance.destination || !instance.sampleRate) { + return; + } + proto = Context.prototype; + sourceProto = Object.getPrototypeOf(instance.createBufferSource()); + + if (!isFunction(sourceProto.start)) { + if (isFunction(sourceProto.noteOn)) { + sourceProto.start = function (when, offset, duration) { + switch (arguments.length) { + case 0: + throw new Error("Not enough arguments."); + case 1: + this.noteOn(when); + break; + case 2: + if (this.buffer) { + this.noteGrainOn(when, offset, this.buffer.duration - offset); + } else { + throw new Error("Missing AudioBuffer"); + } + break; + case 3: + this.noteGrainOn(when, offset, duration); + } + }; + } + } + + if (!isFunction(sourceProto.noteOn)) { + sourceProto.noteOn = sourceProto.start; + } + + if (!isFunction(sourceProto.noteGrainOn)) { + sourceProto.noteGrainOn = sourceProto.start; + } + + if (!isFunction(sourceProto.stop)) { + sourceProto.stop = sourceProto.noteOff; + } + + if (!isFunction(sourceProto.noteOff)) { + sourceProto.noteOff = sourceProto.stop; + } + + contextMethods.forEach(function (names) { + var name1; + var name2; + while (names.length) { + name1 = names.pop(); + if (isFunction(this[name1])) { + this[names.pop()] = this[name1]; + } else { + name2 = names.pop(); + this[name1] = this[name2]; + } + } + }, proto); +})(window.AudioContext); \ No newline at end of file diff --git a/inc/shim/WebMIDIAPI.js b/inc/shim/WebMIDIAPI.js new file mode 100644 index 0000000..000a916 --- /dev/null +++ b/inc/shim/WebMIDIAPI.js @@ -0,0 +1,421 @@ +/* Copyright 2013 Chris Wilson + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Initialize the MIDI library. +(function (global) { + 'use strict'; + var midiIO, _requestMIDIAccess, MIDIAccess, _onReady, MIDIPort, MIDIInput, MIDIOutput, _midiProc; + + function Promise() { + + } + + Promise.prototype.then = function(accept, reject) { + this.accept = accept; + this.reject = reject; + } + + Promise.prototype.succeed = function(access) { + if (this.accept) + this.accept(access); + } + + Promise.prototype.fail = function(error) { + if (this.reject) + this.reject(error); + } + + function _JazzInstance() { + this.inputInUse = false; + this.outputInUse = false; + + // load the Jazz plugin + var o1 = document.createElement("object"); + o1.id = "_Jazz" + Math.random() + "ie"; + o1.classid = "CLSID:1ACE1618-1C7D-4561-AEE1-34842AA85E90"; + + this.activeX = o1; + + var o2 = document.createElement("object"); + o2.id = "_Jazz" + Math.random(); + o2.type="audio/x-jazz"; + o1.appendChild(o2); + + this.objRef = o2; + + var e = document.createElement("p"); + e.appendChild(document.createTextNode("This page requires the ")); + + var a = document.createElement("a"); + a.appendChild(document.createTextNode("Jazz plugin")); + a.href = "http://jazz-soft.net/"; + + e.appendChild(a); + e.appendChild(document.createTextNode(".")); + o2.appendChild(e); + + var insertionPoint = document.getElementById("MIDIPlugin"); + if (!insertionPoint) { + // Create hidden element + var insertionPoint = document.createElement("div"); + insertionPoint.id = "MIDIPlugin"; + insertionPoint.style.position = "absolute"; + insertionPoint.style.visibility = "hidden"; + insertionPoint.style.left = "-9999px"; + insertionPoint.style.top = "-9999px"; + document.body.appendChild(insertionPoint); + } + insertionPoint.appendChild(o1); + + if (this.objRef.isJazz) + this._Jazz = this.objRef; + else if (this.activeX.isJazz) + this._Jazz = this.activeX; + else + this._Jazz = null; + if (this._Jazz) { + this._Jazz._jazzTimeZero = this._Jazz.Time(); + this._Jazz._perfTimeZero = window.performance.now(); + } + } + + _requestMIDIAccess = function _requestMIDIAccess() { + var access = new MIDIAccess(); + return access._promise; + }; + + // API Methods + + MIDIAccess = function() { + this._jazzInstances = new Array(); + this._jazzInstances.push( new _JazzInstance() ); + this._promise = new Promise; + + if (this._jazzInstances[0]._Jazz) { + this._Jazz = this._jazzInstances[0]._Jazz; + window.setTimeout( _onReady.bind(this), 3 ); + } else { + window.setTimeout( _onNotReady.bind(this), 3 ); + } + }; + + _onReady = function _onReady() { + if (this._promise) + this._promise.succeed(this); + }; + + function _onNotReady() { + if (this._promise) + this._promise.fail( { code: 1 } ); + }; + + MIDIAccess.prototype.inputs = function( ) { + if (!this._Jazz) + return null; + var list=this._Jazz.MidiInList(); + var inputs = new Array( list.length ); + + for ( var i=0; i1)) { + var sendObj = new Object(); + sendObj.jazz = this._jazzInstance; + sendObj.data = data; + + window.setTimeout( _sendLater.bind(sendObj), delayBeforeSend ); + } else { + this._jazzInstance.MidiOutLong( data ); + } + return true; + }; + + //init: create plugin + if (!window.navigator.requestMIDIAccess) + window.navigator.requestMIDIAccess = _requestMIDIAccess; + +}(window)); + +// Polyfill window.performance.now() if necessary. +(function (exports) { + var perf = {}, props; + + function findAlt() { + var prefix = ['moz', 'webkit', 'o', 'ms'], + i = prefix.length, + //worst case, we use Date.now() + props = { + value: (function (start) { + return function () { + return Date.now() - start; + }; + }(Date.now())) + }; + + //seach for vendor prefixed version + for (; i >= 0; i--) { + if ((prefix[i] + "Now") in exports.performance) { + props.value = function (method) { + return function () { + exports.performance[method](); + } + }(prefix[i] + "Now"); + return props; + } + } + + //otherwise, try to use connectionStart + if ("timing" in exports.performance && "connectStart" in exports.performance.timing) { + //this pretty much approximates performance.now() to the millisecond + props.value = (function (start) { + return function() { + Date.now() - start; + }; + }(exports.performance.timing.connectStart)); + } + return props; + } + + //if already defined, bail + if (("performance" in exports) && ("now" in exports.performance)) + return; + if (!("performance" in exports)) + Object.defineProperty(exports, "performance", { + get: function () { + return perf; + }}); + //otherwise, performance is there, but not "now()" + + props = findAlt(); + Object.defineProperty(exports.performance, "now", props); +}(window)); diff --git a/inc/timer.js b/inc/timer.js new file mode 100644 index 0000000..73184f9 --- /dev/null +++ b/inc/timer.js @@ -0,0 +1,211 @@ +/* + ---------------------------------------------------------- + ui/Timer : 0.1.1 : 2015-03-23 : https://sketch.io + ---------------------------------------------------------- +*/ + +if (typeof sketch === 'undefined') sketch = {}; + +(function(root) { 'use strict'; + +root.ui = root.ui || {}; +root.ui.Timer = function(opts) { + opts = opts || {}; + /// + var that = this; + /// + var size; + var format; + var container; + var endValue; + var value; + /// + var RAD_DEG = 180.0 / Math.PI; // Radians to Degrees + var DEG_RAD = 1.0 / RAD_DEG; // Degrees to Radians + /// + var setParams = function(opts) { + size = opts.size || 120; + format = opts.format || 'percent'; + container = opts.container || document.body; + endValue = opts.endValue; + value = opts.value || 0; + }; + /// + var getPosition = function() { + if (format === 'percent') { + return { + value: value, + format: 'PERCENT', + percent: value / 100 + } + } else if (format === 'time') { + var elapse = (Date.now() - startTime) / 1000; + var otime = endValue - elapse; + var percent = elapse / endValue; + /// + var time = Math.max(0, Math.round(otime)); + var hours = (time / 3600) >> 0; + var minutes = ((time - (hours * 3600)) / 60) >> 0; + var seconds = time - (hours * 3600) - (minutes * 60); + if (seconds < 10 && minutes) seconds = '0' + seconds; + /// + if (minutes) { + return { + value: minutes, + format: 'MINUTES', + percent: percent + }; + } else { + return { + value: seconds, + format: 'SECONDS', + percent: percent + }; + } + } + }; + + var gradient = ['#9cdb7d', '#99d97f', '#97d782', '#95d684', '#93d487', '#91d38a', '#8fd18c', '#8dcf8f', '#8bce91', '#89cc94', '#87cb97', '#85c999', '#83c89c', '#81c69e', '#7fc4a1', '#7dc3a4', '#7bc1a6', '#79c0a9', '#77beab', '#75bcae', '#73bbb1', '#71b9b3', '#6fb8b6', '#6db6b8', '#6bb5bb', '#69b3be', '#67b1c0', '#65b0c3', '#63aec5', '#61adc8', '#5fabcb', '#5daacd', '#5ba8d0', '#59a6d2', '#57a5d5', '#55a3d8', '#53a2da', '#51a0dd', '#4f9edf', '#4d9de2', '#4b9be5', '#499ae7', '#4798ea', '#4597ec', '#4395ef', '#4193f2', '#3f92f4', '#3d90f7', '#3b8ff9', '#398dfc', '#378cff']; + /// + var requestId; + var pulse = 0; + var startTime = Date.now(); // 'time' format + var render = function() { + var obj = getPosition(); + /// + ctx.fillStyle = gradient[Math.round((1.0 - obj.percent) * 50)]; + /// +// pulse ++; + /// + var startAngle = -360 * DEG_RAD; + var endAngle = obj.percent * 360 * DEG_RAD; + var outerRadius = size / 2.0 + (pulse % 20); + var innerRadius = size / 2.0 * 0.61 + (pulse % 20); + /// + ctx.clearRect(0, 0, canvas.width, canvas.height) + ctx.save(); + /// + ctx.beginPath() + ctx.arc(outerRadius, outerRadius, outerRadius, startAngle, endAngle, false); + ctx.arc(outerRadius, outerRadius, innerRadius, endAngle, startAngle, true); + ctx.globalAlpha = 0.25; + ctx.fill(); + /// + startAngle += 360 * DEG_RAD; + /// + ctx.beginPath() + ctx.arc(outerRadius, outerRadius, outerRadius, startAngle, endAngle, false); + ctx.arc(outerRadius, outerRadius, innerRadius, endAngle, startAngle, true); + ctx.globalAlpha = 1.0; + ctx.fill(); + /// + var ratio = size / 260; + var fontSize = ratio * 26; + var fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif'; + ctx.font = 'bold ' + fontSize + 'px ' + fontFamily; + ctx.textBaseline = 'top'; + ctx.textAlign = 'center'; + ctx.fillText(obj.format, outerRadius, outerRadius + ratio * 14); + /// + var fontSize = ratio * 46; + ctx.font = 'bold ' + fontSize + 'px ' + fontFamily; + ctx.fillStyle = '#ffffff'; + ctx.fillText(obj.value, outerRadius, outerRadius - ratio * 44); + ctx.restore(); + /// + if (obj.percent < 1.0) { + requestId = requestAnimationFrame(render); + } + }; + /// + setParams(opts); + /// + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + canvas.width = size; + canvas.height = size; + /// + var parent = document.createElement('div'); + parent.style.display = 'none'; + parent.className = 'sk-timer'; + parent.appendChild(canvas); + /// + container.appendChild(parent); + /// + if (opts.onstart) { + setTimeout(opts.onstart, 250); + } + + /* Public + ---------------------------------------------------------- */ + that.reset = function() { + setParams(opts); + }; + + that.destroy = function() { + container.removeChild(canvas); + }; + + that.hidden = false; + + that.hide = function(callback) { + cancelAnimationFrame(requestId); + /// + that.hidden = true; + parent.style.transition = 'opacity .35s'; + parent.style.opacity = 0; + setTimeout(function() { + parent.style.display = 'none'; + callback && callback(); + }, 350); + }; + + that.setValue = function(percent) { + cancelAnimationFrame(requestId); + /// + that.hidden = false; + parent.style.display = 'block'; + parent.style.opacity = 1.0; + /// + if ((value = Math.ceil(percent)) >= 100) { + that.hide(); + } + /// + render(); + }; + + addStyleSheet(); + + return that; + +}; + +var addStyleSheet = function() { + if (document.getElementById('sk-timer-stylesheet') == null) { + var style = document.createElement('style'); + style.id = 'sk-timer-stylesheet'; + style.innerHTML = '.sk-timer {\ + position: absolute;\ + z-index: 1000;\ + top: 0;\ + left: 0;\ + width: 100%;\ + height: 100%;\ + }\ + .sk-timer canvas {\ + border: 3px solid #000;\ + border-radius: 50%;\ + background: #000;\ + margin: auto;\ + position: absolute;\ + top: 0;\ + left: 0;\ + right: 0;\ + bottom: 0;\ + }\ + '; + document.head.appendChild(style); + } +}; + +})(sketch); \ No newline at end of file diff --git a/index.html b/index.html index 5fbba57..eee4f90 100644 --- a/index.html +++ b/index.html @@ -16,22 +16,31 @@ {{ page.title }} - - - - - - - - - + + + + + - - - + + + + + + + + + + + + + + + + diff --git a/js/MIDI/AudioDetect.js b/js/MIDI/AudioDetect.js index da24e0e..957605d 100644 --- a/js/MIDI/AudioDetect.js +++ b/js/MIDI/AudioDetect.js @@ -1,63 +1,101 @@ /* - ------------------------------------- - MIDI.audioDetect : 0.3 - ------------------------------------- + ---------------------------------------------------------- + MIDI.audioDetect : 0.3.2 : 2015-03-26 + ---------------------------------------------------------- https://github.com/mudcube/MIDI.js - ------------------------------------- + ---------------------------------------------------------- Probably, Maybe, No... Absolutely! - ------------------------------------- Test to see what types of