// MIT, from https://github.com/patriciogonzalezvivo/glslCanvas/blob/master/dist/GlslCanvas.js (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.GlslCanvas = factory()); }(this, (function () { 'use strict'; var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; var win; if (typeof window !== "undefined") { win = window; } else if (typeof commonjsGlobal !== "undefined") { win = commonjsGlobal; } else if (typeof self !== "undefined"){ win = self; } else { win = {}; } var window_1 = win; var isFunction_1 = isFunction; var toString = Object.prototype.toString; function isFunction (fn) { if (!fn) { return false } var string = toString.call(fn); return string === '[object Function]' || (typeof fn === 'function' && string !== '[object RegExp]') || (typeof window !== 'undefined' && // IE8 and below (fn === window.setTimeout || fn === window.alert || fn === window.confirm || fn === window.prompt)) } var trim = function(string) { return string.replace(/^\s+|\s+$/g, ''); }; var isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; var parseHeaders = function (headers) { if (!headers) return {} var result = {}; var headersArr = trim(headers).split('\n'); for (var i = 0; i < headersArr.length; i++) { var row = headersArr[i]; var index = row.indexOf(':') , key = trim(row.slice(0, index)).toLowerCase() , value = trim(row.slice(index + 1)); if (typeof(result[key]) === 'undefined') { result[key] = value; } else if (isArray(result[key])) { result[key].push(value); } else { result[key] = [ result[key], value ]; } } return result }; var immutable = extend; var hasOwnProperty = Object.prototype.hasOwnProperty; function extend() { var target = {}; for (var i = 0; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target } "use strict"; var xhr = createXHR; // Allow use of default import syntax in TypeScript var default_1 = createXHR; createXHR.XMLHttpRequest = window_1.XMLHttpRequest || noop; createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window_1.XDomainRequest; forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) { createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) { options = initParams(uri, options, callback); options.method = method.toUpperCase(); return _createXHR(options) }; }); function forEachArray(array, iterator) { for (var i = 0; i < array.length; i++) { iterator(array[i]); } } function isEmpty(obj){ for(var i in obj){ if(obj.hasOwnProperty(i)) return false } return true } function initParams(uri, options, callback) { var params = uri; if (isFunction_1(options)) { callback = options; if (typeof uri === "string") { params = {uri:uri}; } } else { params = immutable(options, {uri: uri}); } params.callback = callback; return params } function createXHR(uri, options, callback) { options = initParams(uri, options, callback); return _createXHR(options) } function _createXHR(options) { if(typeof options.callback === "undefined"){ throw new Error("callback argument missing") } var called = false; var callback = function cbOnce(err, response, body){ if(!called){ called = true; options.callback(err, response, body); } }; function readystatechange() { if (xhr.readyState === 4) { setTimeout(loadFunc, 0); } } function getBody() { // Chrome with requestType=blob throws errors arround when even testing access to responseText var body = undefined; if (xhr.response) { body = xhr.response; } else { body = xhr.responseText || getXml(xhr); } if (isJson) { try { body = JSON.parse(body); } catch (e) {} } return body } function errorFunc(evt) { clearTimeout(timeoutTimer); if(!(evt instanceof Error)){ evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") ); } evt.statusCode = 0; return callback(evt, failureResponse) } // will load the data & process the response in a special response object function loadFunc() { if (aborted) return var status; clearTimeout(timeoutTimer); if(options.useXDR && xhr.status===undefined) { //IE8 CORS GET successful response doesn't have a status field, but body is fine status = 200; } else { status = (xhr.status === 1223 ? 204 : xhr.status); } var response = failureResponse; var err = null; if (status !== 0){ response = { body: getBody(), statusCode: status, method: method, headers: {}, url: uri, rawRequest: xhr }; if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE response.headers = parseHeaders(xhr.getAllResponseHeaders()); } } else { err = new Error("Internal XMLHttpRequest Error"); } return callback(err, response, response.body) } var xhr = options.xhr || null; if (!xhr) { if (options.cors || options.useXDR) { xhr = new createXHR.XDomainRequest(); }else{ xhr = new createXHR.XMLHttpRequest(); } } var key; var aborted; var uri = xhr.url = options.uri || options.url; var method = xhr.method = options.method || "GET"; var body = options.body || options.data; var headers = xhr.headers = options.headers || {}; var sync = !!options.sync; var isJson = false; var timeoutTimer; var failureResponse = { body: undefined, headers: {}, statusCode: 0, method: method, url: uri, rawRequest: xhr }; if ("json" in options && options.json !== false) { isJson = true; headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user if (method !== "GET" && method !== "HEAD") { headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user body = JSON.stringify(options.json === true ? body : options.json); } } xhr.onreadystatechange = readystatechange; xhr.onload = loadFunc; xhr.onerror = errorFunc; // IE9 must have onprogress be set to a unique function. xhr.onprogress = function () { // IE must die }; xhr.onabort = function(){ aborted = true; }; xhr.ontimeout = errorFunc; xhr.open(method, uri, !sync, options.username, options.password); //has to be after open if(!sync) { xhr.withCredentials = !!options.withCredentials; } // Cannot set timeout with sync request // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent if (!sync && options.timeout > 0 ) { timeoutTimer = setTimeout(function(){ if (aborted) return aborted = true;//IE9 may still call readystatechange xhr.abort("timeout"); var e = new Error("XMLHttpRequest timeout"); e.code = "ETIMEDOUT"; errorFunc(e); }, options.timeout ); } if (xhr.setRequestHeader) { for(key in headers){ if(headers.hasOwnProperty(key)){ xhr.setRequestHeader(key, headers[key]); } } } else if (options.headers && !isEmpty(options.headers)) { throw new Error("Headers cannot be set on an XDomainRequest object") } if ("responseType" in options) { xhr.responseType = options.responseType; } if ("beforeSend" in options && typeof options.beforeSend === "function" ) { options.beforeSend(xhr); } // Microsoft Edge browser sends "undefined" when send is called with undefined value. // XMLHttpRequest spec says to pass null as body to indicate no body // See https://github.com/naugtur/xhr/issues/100. xhr.send(body || null); return xhr } function getXml(xhr) { // xhr.responseXML will throw Exception "InvalidStateError" or "DOMException" // See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseXML. try { if (xhr.responseType === "document") { return xhr.responseXML } var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror"; if (xhr.responseType === "" && !firefoxBugTakenEffect) { return xhr.responseXML } } catch (e) {} return null } function noop() {} xhr.default = default_1; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var asyncGenerator = function () { function AwaitValue(value) { this.value = value; } function AsyncGenerator(gen) { var front, back; function send(key, arg) { return new Promise(function (resolve, reject) { var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null }; if (back) { back = back.next = request; } else { front = back = request; resume(key, arg); } }); } function resume(key, arg) { try { var result = gen[key](arg); var value = result.value; if (value instanceof AwaitValue) { Promise.resolve(value.value).then(function (arg) { resume("next", arg); }, function (arg) { resume("throw", arg); }); } else { settle(result.done ? "return" : "normal", result.value); } } catch (err) { settle("throw", err); } } function settle(type, value) { switch (type) { case "return": front.resolve({ value: value, done: true }); break; case "throw": front.reject(value); break; default: front.resolve({ value: value, done: false }); break; } front = front.next; if (front) { resume(front.key, front.arg); } else { back = null; } } this._invoke = send; if (typeof gen.return !== "function") { this.return = undefined; } } if (typeof Symbol === "function" && Symbol.asyncIterator) { AsyncGenerator.prototype[Symbol.asyncIterator] = function () { return this; }; } AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); }; AsyncGenerator.prototype.throw = function (arg) { return this._invoke("throw", arg); }; AsyncGenerator.prototype.return = function (arg) { return this._invoke("return", arg); }; return { wrap: function (fn) { return function () { return new AsyncGenerator(fn.apply(this, arguments)); }; }, await: function (value) { return new AwaitValue(value); } }; }(); var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var toConsumableArray = function (arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }; var lastError = ''; /** * Creates the HTLM for a failure message * @param {string} canvasContainerId id of container of th * canvas. * @return {string} The html. */ function makeFailHTML(msg) { return '\n\n
\n
\n
' + msg + '
\n
\n
\n'; } /** * Message for getting a webgl browser * @type {string} */ var GET_A_WEBGL_BROWSER = '\n\tThis page requires a browser that supports WebGL.
\n\tClick here to upgrade your browser.\n'; /** * Message for need better hardware * @type {string} */ var OTHER_PROBLEM = '\n\tIt does not appear your computer can support WebGL.
\n\tClick here for more information.\n'; /** * Code to return in `onError` callback when the browser doesn't support webgl * @type {number} */ var ERROR_BROWSER_SUPPORT = 1; /** * Code to return in `onError` callback there's any other problem related to webgl * @type {number} */ var ERROR_OTHER = 2; /** * Creates a webgl context. If creation fails it will * change the contents of the container of the * tag to an error message with the correct links for WebGL, * unless `onError` option is defined to a callback, * which allows for custom error handling.. * @param {Element} canvas. The canvas element to create a * context from. * @param {WebGLContextCreationAttributes} optAttribs Any * creation attributes you want to pass in. * @return {WebGLRenderingContext} The created context. */ function setupWebGL(canvas, optAttribs, onError) { function showLink(str) { var container = canvas.parentNode; if (container) { container.innerHTML = makeFailHTML(str); } } function handleError(errorCode, msg) { if (typeof onError === 'function') { onError(errorCode); } else { showLink(msg); } } if (!window.WebGLRenderingContext) { handleError(ERROR_BROWSER_SUPPORT, GET_A_WEBGL_BROWSER); return null; } var context = create3DContext(canvas, optAttribs); if (!context) { handleError(ERROR_OTHER, OTHER_PROBLEM); } else { context.getExtension('OES_standard_derivatives'); } return context; } /** * Creates a webgl context. * @param {!Canvas} canvas The canvas tag to get context * from. If one is not passed in one will be created. * @return {!WebGLContext} The created context. */ function create3DContext(canvas, optAttribs) { var names = ['webgl', 'experimental-webgl']; var context = null; for (var ii = 0; ii < names.length; ++ii) { try { context = canvas.getContext(names[ii], optAttribs); } catch (e) { if (context) { break; } } } return context; } /* * Create a Vertex of a specific type (gl.VERTEX_SHADER/) */ function createShader(main, source, type, offset) { var gl = main.gl; var shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (!compiled) { // Something went wrong during compilation; get the error lastError = gl.getShaderInfoLog(shader); console.error('*** Error compiling shader ' + shader + ':' + lastError); main.trigger('error', { shader: shader, source: source, type: type, error: lastError, offset: offset || 0 }); gl.deleteShader(shader); return null; } return shader; } /** * Loads a shader. * @param {!WebGLContext} gl The WebGLContext to use. * @param {string} shaderSource The shader source. * @param {number} shaderType The type of shader. * @param {function(string): void) opt_errorCallback callback for errors. * @return {!WebGLShader} The created shader. */ function createProgram(main, shaders, optAttribs, optLocations) { var gl = main.gl; var program = gl.createProgram(); for (var ii = 0; ii < shaders.length; ++ii) { gl.attachShader(program, shaders[ii]); } if (optAttribs) { for (var _ii = 0; _ii < optAttribs.length; ++_ii) { gl.bindAttribLocation(program, optLocations ? optLocations[_ii] : _ii, optAttribs[_ii]); } } gl.linkProgram(program); // Check the link status var linked = gl.getProgramParameter(program, gl.LINK_STATUS); if (!linked) { // something went wrong with the link lastError = gl.getProgramInfoLog(program); console.log('Error in program linking:' + lastError); gl.deleteProgram(program); return null; } return program; } // By Brett Camber on // https://github.com/tangrams/tangram/blob/master/src/gl/glsl.js function parseUniforms(uniforms) { var prefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; var parsed = []; for (var name in uniforms) { var uniform = uniforms[name]; var u = void 0; if (prefix) { name = prefix + '.' + name; } // Single float if (typeof uniform === 'number') { parsed.push({ type: 'float', method: '1f', name: name, value: uniform }); } // Array: vector, array of floats, array of textures, or array of structs else if (Array.isArray(uniform)) { // Numeric values if (typeof uniform[0] === 'number') { // float vectors (vec2, vec3, vec4) if (uniform.length === 1) { parsed.push({ type: 'float', method: '1f', name: name, value: uniform }); } // float vectors (vec2, vec3, vec4) else if (uniform.length >= 2 && uniform.length <= 4) { parsed.push({ type: 'vec' + uniform.length, method: uniform.length + 'fv', name: name, value: uniform }); } // float array else if (uniform.length > 4) { parsed.push({ type: 'float[]', method: '1fv', name: name + '[0]', value: uniform }); } // TODO: assume matrix for (typeof == Float32Array && length == 16)? } // Array of textures else if (typeof uniform[0] === 'string') { parsed.push({ type: 'sampler2D', method: '1i', name: name, value: uniform }); } // Array of arrays - but only arrays of vectors are allowed in this case else if (Array.isArray(uniform[0]) && typeof uniform[0][0] === 'number') { // float vectors (vec2, vec3, vec4) if (uniform[0].length >= 2 && uniform[0].length <= 4) { // Set each vector in the array for (u = 0; u < uniform.length; u++) { parsed.push({ type: 'vec' + uniform[0].length, method: uniform[u].length + 'fv', name: name + '[' + u + ']', value: uniform[u] }); } } // else error? } // Array of structures else if (_typeof(uniform[0]) === 'object') { for (u = 0; u < uniform.length; u++) { // Set each struct in the array parsed.push.apply(parsed, toConsumableArray(parseUniforms(uniform[u], name + '[' + u + ']'))); } } } // Boolean else if (typeof uniform === 'boolean') { parsed.push({ type: 'bool', method: '1i', name: name, value: uniform }); } // Texture else if (typeof uniform === 'string') { parsed.push({ type: 'sampler2D', method: '1i', name: name, value: uniform }); } // Structure else if ((typeof uniform === 'undefined' ? 'undefined' : _typeof(uniform)) === 'object') { // Set each field in the struct parsed.push.apply(parsed, toConsumableArray(parseUniforms(uniform, name))); } // TODO: support other non-float types? (int, etc.) } return parsed; } function isCanvasVisible(canvas) { var bound = canvas.getBoundingClientRect(); return bound.top + bound.height > 0 && bound.top < (window.innerHeight || document.documentElement.clientHeight); } function isPowerOf2(value) { return (value & value - 1) === 0; } function isSafari() { return (/^((?!chrome|android).)*safari/i.test(navigator.userAgent) ); } function isDiff(a, b) { if (a && b) { return a.toString() !== b.toString(); } return false; } function getFile(url) { var httpRequest = new XMLHttpRequest(); httpRequest.open("GET", url, false); httpRequest.send(); if (httpRequest.status == 200) return httpRequest.responseText;else return ""; } function subscribeMixin$1(target) { var listeners = new Set(); return Object.assign(target, { on: function on(type, f) { var listener = {}; listener[type] = f; listeners.add(listener); }, off: function off(type, f) { if (f) { var listener = {}; listener[type] = f; listeners.delete(listener); } else { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = listeners[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var item = _step.value; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = Object.keys(item)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var key = _step2.value; if (key === type) { listeners.delete(item); return; } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } }, listSubscriptions: function listSubscriptions() { var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = listeners[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var item = _step3.value; console.log(item); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } }, subscribe: function subscribe(listener) { listeners.add(listener); }, unsubscribe: function unsubscribe(listener) { listeners.delete(listener); }, unsubscribeAll: function unsubscribeAll() { listeners.clear(); }, trigger: function trigger(event) { for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { data[_key - 1] = arguments[_key]; } var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = listeners[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var listener = _step4.value; if (typeof listener[event] === 'function') { listener[event].apply(listener, toConsumableArray(data)); } } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } } }); } // Texture management // GL texture wrapper object for keeping track of a global set of textures, keyed by a unique user-defined name var Texture = function () { function Texture(gl, name) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; classCallCheck(this, Texture); subscribeMixin$1(this); this.gl = gl; this.texture = gl.createTexture(); if (this.texture) { this.valid = true; } this.bind(); this.name = name; this.source = null; this.sourceType = null; this.loading = null; // a Promise object to track the loading state of this texture // Default to a 1-pixel black texture so we can safely render while we wait for an image to load // See: http://stackoverflow.com/questions/19722247/webgl-wait-for-texture-to-load this.setData(1, 1, new Uint8Array([0, 0, 0, 255]), { filtering: 'linear' }); this.setFiltering(options.filtering); this.load(options); } // Destroy a single texture instance createClass(Texture, [{ key: 'destroy', value: function destroy() { if (!this.valid) { return; } this.gl.deleteTexture(this.texture); this.texture = null; delete this.data; this.data = null; this.valid = false; } }, { key: 'bind', value: function bind(unit) { if (!this.valid) { return; } if (typeof unit === 'number') { if (Texture.activeUnit !== unit) { this.gl.activeTexture(this.gl.TEXTURE0 + unit); Texture.activeUnit = unit; } } if (Texture.activeTexture !== this.texture) { this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture); Texture.activeTexture = this.texture; } } }, { key: 'load', value: function load() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; this.loading = null; if (typeof options.url === 'string') { if (this.url === undefined || options.url !== this.url) { this.setUrl(options.url, options); } } else if (options.element) { this.setElement(options.element, options); } else if (options.data && options.width && options.height) { this.setData(options.width, options.height, options.data, options); } } // Sets texture from an url }, { key: 'setUrl', value: function setUrl(url) { var _this = this; var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!this.valid) { return; } this.url = url; // save URL reference (will be overwritten when element is loaded below) this.source = this.url; this.sourceType = 'url'; this.loading = new Promise(function (resolve, reject) { var ext = url.split('.').pop().toLowerCase(); var isVideo = ext === 'ogv' || ext === 'webm' || ext === 'mp4'; var element = undefined; if (isVideo) { element = document.createElement('video'); element.autoplay = true; element.muted = true; /* required for modern browsers to autoplay video */ setTimeout(function () { element.play(); /* doesn't block promise but needs a more elegant solution */ }, 1); options.filtering = 'nearest'; // element.preload = 'auto'; // element.style.display = 'none'; // document.body.appendChild(element); } else { element = new Image(); } element.onload = function () { try { _this.setElement(element, options); } catch (e) { console.log('Texture \'' + _this.name + '\': failed to load url: \'' + _this.source + '\'', e, options); } resolve(_this); }; element.onerror = function (e) { // Warn and resolve on error console.log('Texture \'' + _this.name + '\': failed to load url: \'' + _this.source + '\'', e, options); resolve(_this); }; // Safari has a bug loading data-URL elements with CORS enabled, so it must be disabled in that case // https://bugs.webkit.org/show_bug.cgi?id=123978 if (!(isSafari() && _this.source.slice(0, 5) === 'data:')) { element.crossOrigin = 'anonymous'; } element.src = _this.source; if (isVideo) { _this.setElement(element, options); } }); return this.loading; } // Sets texture to a raw image buffer }, { key: 'setData', value: function setData(width, height, data) { var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; this.width = width; this.height = height; this.source = data; this.sourceType = 'data'; this.update(options); this.setFiltering(options); this.loading = Promise.resolve(this); return this.loading; } // Sets the texture to track a element (canvas/image) }, { key: 'setElement', value: function setElement(element, options) { var _this2 = this; var el = element; // a string element is interpeted as a CSS selector if (typeof element === 'string') { element = document.querySelector(element); } if (element instanceof HTMLCanvasElement || element instanceof HTMLImageElement || element instanceof HTMLVideoElement) { this.source = element; this.sourceType = 'element'; if (element instanceof HTMLVideoElement) { this.width = this.source.videoWidth; this.height = this.source.videoHeight; element.addEventListener('canplaythrough', function () { _this2.intervalID = setInterval(function () { _this2.update(options); }, 15); }, true); element.addEventListener('ended', function () { element.currentTime = 0; element.play(); }, true); } else { this.update(options); } this.setFiltering(options); } else { var msg = 'the \'element\' parameter (`element: ' + JSON.stringify(el) + '`) must be a CSS '; msg += 'selector string, or a , or