<commit_before>/*global global, module*/
(function () {

	// Make a module
	var atom = (function (name) {
		var root = typeof window !== 'undefined' ? window : global,
			had = Object.prototype.hasOwnProperty.call(root, name),
			prev = root[name], me = root[name] = {};
		if (typeof module !== 'undefined' && module.exports) {
			module.exports = me;
		}
		me.noConflict = function () {
			root[name] = had ? prev : undefined;
			if (!had) {
				try {
					delete root[name];
				} catch (ex) {
				}
			}
			return this;
		};
		return me;
	}('atom'));

	atom.VERSION = '0.3.0';

	// Convenience methods
	var slice = Array.prototype.slice;
	var isArray = Array.isArray || function (obj) {
		return Object.prototype.toString.call(obj) === '[object Array]';
	};
	function inArray(arr, value) {
		for (var i = arr.length; --i >= 0;) {
			if (arr[i] === value) {
				return true;
			}
		}
	}
	function toArray(obj) {
		return isArray(obj) ? obj : [obj];
	}
	function isEmpty(obj) {
		for (var p in obj) {
			if (obj.hasOwnProperty(p)) {
				return false;
			}
		}
		return true;
	}

	// Property getter
	function get(nucleus, keyOrList, func) {
		var isList = isArray(keyOrList), keys = isList ? keyOrList : [keyOrList],
			key, values = [], props = nucleus.props, missing = {},
			result = { values: values };
		for (var i = keys.length; --i >= 0;) {
			key = keys[i];
			if (!props.hasOwnProperty(key)) {
				result.missing = missing;
				missing[key] = true;
			}
			values.unshift(props[key]);
		}
		return func ? func.apply({}, values) : result;
	}

	// Helper to remove an exausted listener from the listeners array
	function removeListener(listeners) {
		for (var i = listeners.length; --i >= 0;) {
			// There should only be ONE exhausted listener.
			if (!listeners[i].calls) {
				return listeners.splice(i, 1);
			}
		}
	}

	// Used to detect listener recursion; a given object may only appear once.
	var objStack = [];

	// Property setter
	function set(nucleus, key, value) {
		var keys, listener, listeners = nucleus.listeners, values, missing,
			listenersCopy = [].concat(listeners), i = listenersCopy.length,
			props = nucleus.props, oldValue = props[key],
			had = props.hasOwnProperty(key),
			isObj = value && typeof value === 'object';
		props[key] = value;
		if (!had || oldValue !== value || (isObj && !inArray(objStack, value))) {
			if (isObj) {
				objStack.push(value);
			}
			while (--i >= 0) {
				listener = listenersCopy[i];
				keys = listener.keys;
				missing = listener.missing;
				if (missing) {
					if (missing.hasOwnProperty(key)) {
						delete missing[key];
						if (isEmpty(missing)) {
							listener.cb.apply({}, get(nucleus, keys).values);
							listener.calls--;
						}
					}
				} else if (inArray(keys, key)) {
					listener.cb.apply({}, get(nucleus, keys).values);
					listener.calls--;
				}
				if (!listener.calls) {
					removeListener(listeners);
				}
			}
			delete nucleus.needs[key];
			if (isObj) {
				objStack.pop();
			}
		}
	}

	// Helper function for setting up providers.
	function provide(nucleus, key, provider) {
		provider(function (result) {
			set(nucleus, key, result);
		});
	}

	// Return an actual instance.
	atom.create = function () {
		var
			nucleus = { props: {}, needs: {}, providers: {}, listeners: [] },
			props = nucleus.props,
			needs = nucleus.needs,
			providers = nucleus.providers,
			listeners = nucleus.listeners,
			q = []
		;

		// Execute the next function in the async queue.
		function doNext() {
			if (q) {
				q.pending = q.next = (!q.next && q.length) ?
					q.shift() : q.next;
				q.args = slice.call(arguments, 0);
				if (q.pending) {
					q.next = null;
					q.pending.apply({}, [doNext].concat(q.args));
				}
			}
		}

		var me = {

			// Add a function or functions to the async queue.  Functions added
			// thusly must call their first arg as a callback when done.  Any args
			// provided to the callback will be passed in to the next function in
			// the queue.
			chain: function () {
				if (q) {
					for (var i = 0, len = arguments.length; i < len; i++) {
						q.push(arguments[i]);
						if (!q.pending) {
							doNext.apply({}, q.args);
						}
					}
				}
			},

			// Remove references to all properties and listeners.  This releases
			// memory, and effective stops the atom from working.
			destroy: function () {
				delete nucleus.props;
				delete nucleus.needs;
				delete nucleus.providers;
				delete nucleus.listeners;
				while (q.length) {
					q.pop();
				}
				nucleus = props = needs = providers = listeners =
					q = q.pending = q.next = q.args = null;
			},

			// Call `func` on each of the specified keys.  The key is provided as
			// the first arg, and the value as the second.
			each: function (keyOrList, func) {
				var keys = toArray(keyOrList), i = -1, len = keys.length, key;
				while (++i < len) {
					key = keys[i];
					func(key, me.get(key));
				}
			},

			// Establish two-way binding between a key or list of keys for two
			// different atoms, so that changing a property on either atom will
			// propagate to the other.  If a map is provided for `keyOrListOrMap`,
			// properties on this atom may be bound to differently named properties
			// on `otherAtom`.  Note that entangled properties will not actually be
			// synchronized until the first change *after* entanglement.
			entangle: function (otherAtom, keyOrListOrMap) {
				var
					isList = isArray(keyOrListOrMap),
					isMap = !isList && typeof keyOrListOrMap === 'object',
					i, key,
					keys = isList ? keyOrListOrMap : isMap ? [] : [keyOrListOrMap],
					map = isMap ? keyOrListOrMap : {}
				;
				if (isMap) {
					for (key in map) {
						if (map.hasOwnProperty(key)) {
							keys.push(key);
						}
					}
				} else {
					for (i = keys.length; --i >= 0;) {
						key = keys[i];
						map[key] = key;
					}
				}
				me.each(keys, function (key) {
					var otherKey = map[key];
					me.bind(key, function (value) {
						otherAtom.set(otherKey, value);
					});
					otherAtom.bind(otherKey, function (value) {
						me.set(key, value);
					});
				});
			},

			// Get current values for the specified keys.  If `func` is provided,
			// it will be called with the values as args.
			get: function (keyOrList, func) {
				var result = get(nucleus, keyOrList, func);
				return func ? result : typeof keyOrList === 'string' ?
					result.values[0] : result.values;
			},

			// Returns true iff all of the specified keys exist (regardless of
			// value).
			has: function (keyOrList) {
				var keys = toArray(keyOrList);
				for (var i = keys.length; --i >= 0;) {
					if (!props.hasOwnProperty(keys[i])) {
						return false;
					}
				}
				return true;
			},

			// Return a list of all keys.
			keys: function () {
				var keys = [];
				for (var key in props) {
					if (props.hasOwnProperty(key)) {
						keys.push(key);
					}
				}
				return keys;
			},

			// Add arbitrary properties to this atom's interface.
			mixin: function (obj) {
				for (var p in obj) {
					if (obj.hasOwnProperty(p)) {
						me[p] = obj[p];
					}
				}
				return me;
			},

			// Call `func` as soon as all of the specified keys have been set.  If
			// they are already set, the function will be called immediately, with
			// all the values provided as args.  In this, it is identical to
			// `once()`.  However, calling `need()` will additionally invoke
			// providers when possible, in order to try and create the required
			// values.
			need: function (keyOrList, func) {
				var key, keys = toArray(keyOrList), values, provider;
				for (var i = keys.length; --i >= 0;) {
					key = keys[i];
					provider = providers[key];
					if (!props.hasOwnProperty(key) && provider) {
						provide(nucleus, key, provider);
						delete providers[key];
					} else {
						needs[key] = true;
					}
				}
				me.once(keys, func);
			},

			// Call `func` whenever any of the specified keys is next changed.  The
			// values of all keys will be provided as args to the function.  The
			// function will automatically be unbound after being called the first
			// time, so it is guaranteed to be called no more than once.
			next: function (keyOrList, func) {
				listeners.unshift(
					{ keys: toArray(keyOrList), cb: func, calls: 1 });
			},

			// Unregister a listener `func` that was previously registered using
			// `on()`, `bind()`, `need()`, `next()` or `once()`.
			off: function (func) { // alias: `unbind`
				for (var i = listeners.length; --i >= 0;) {
					if (listeners[i].cb === func) {
						listeners.splice(i, 1);
					}
				}
			},

			// Call `func` whenever any of the specified keys change.  The values
			// of the keys will be provided as args to func.
			on: function (keyOrList, func) { // alias: `bind`
				listeners.unshift({ keys: toArray(keyOrList), cb: func,
					calls: Infinity });
			},

			// Call `func` as soon as all of the specified keys have been set.  If
			// they are already set, the function will be called immediately, with
			// all the values provided as args.  Guaranteed to be called no more
			// than once.
			once: function (keyOrList, func) {
				var keys = toArray(keyOrList),
					results = get(nucleus, keys),
					values = results.values,
					missing = results.missing;
				if (!missing) {
					func.apply({}, values);
				} else {
					listeners.unshift(
						{ keys: keys, cb: func, missing: missing, calls: 1 });
				}
			},

			// Register a provider for a particular key.  The provider `func` is a
			// function that will be called if there is a need to create the key.
			// It must call its first arg as a callback, with the value.  Provider
			// functions will be called at most once.
			provide: function (key, func) {
				if (needs[key]) {
					provide(nucleus, key, func);
				} else if (!providers[key]) {
					providers[key] = func;
				}
			},

			// Set value for a key, or if `keyOrMap` is an object then set all the
			// keys' corresponding values.
			set: function (keyOrMap, value) {
				if (typeof keyOrMap === 'object') {
					for (var key in keyOrMap) {
						if (keyOrMap.hasOwnProperty(key)) {
							set(nucleus, key, keyOrMap[key]);
						}
					}
				} else {
					set(nucleus, keyOrMap, value);
				}
			}
		};
		me.bind = me.on;
		me.unbind = me.off;
		return me;
	};

}());
<commit_msg>Fix for: Function.prototype.apply() in IE8 chokes if second param is undefined<commit_after>/*global global, module*/
(function () {

	// Make a module
	var atom = (function (name) {
		var root = typeof window !== 'undefined' ? window : global,
			had = Object.prototype.hasOwnProperty.call(root, name),
			prev = root[name], me = root[name] = {};
		if (typeof module !== 'undefined' && module.exports) {
			module.exports = me;
		}
		me.noConflict = function () {
			root[name] = had ? prev : undefined;
			if (!had) {
				try {
					delete root[name];
				} catch (ex) {
				}
			}
			return this;
		};
		return me;
	}('atom'));

	atom.VERSION = '0.3.1';

	// Convenience methods
	var slice = Array.prototype.slice;
	var isArray = Array.isArray || function (obj) {
		return Object.prototype.toString.call(obj) === '[object Array]';
	};
	function inArray(arr, value) {
		for (var i = arr.length; --i >= 0;) {
			if (arr[i] === value) {
				return true;
			}
		}
	}
	function toArray(obj) {
		return isArray(obj) ? obj : [obj];
	}
	function isEmpty(obj) {
		for (var p in obj) {
			if (obj.hasOwnProperty(p)) {
				return false;
			}
		}
		return true;
	}

	// Property getter
	function get(nucleus, keyOrList, func) {
		var isList = isArray(keyOrList), keys = isList ? keyOrList : [keyOrList],
			key, values = [], props = nucleus.props, missing = {},
			result = { values: values };
		for (var i = keys.length; --i >= 0;) {
			key = keys[i];
			if (!props.hasOwnProperty(key)) {
				result.missing = missing;
				missing[key] = true;
			}
			values.unshift(props[key]);
		}
		return func ? func.apply({}, values) : result;
	}

	// Helper to remove an exausted listener from the listeners array
	function removeListener(listeners) {
		for (var i = listeners.length; --i >= 0;) {
			// There should only be ONE exhausted listener.
			if (!listeners[i].calls) {
				return listeners.splice(i, 1);
			}
		}
	}

	// Used to detect listener recursion; a given object may only appear once.
	var objStack = [];

	// Property setter
	function set(nucleus, key, value) {
		var keys, listener, listeners = nucleus.listeners, values, missing,
			listenersCopy = [].concat(listeners), i = listenersCopy.length,
			props = nucleus.props, oldValue = props[key],
			had = props.hasOwnProperty(key),
			isObj = value && typeof value === 'object';
		props[key] = value;
		if (!had || oldValue !== value || (isObj && !inArray(objStack, value))) {
			if (isObj) {
				objStack.push(value);
			}
			while (--i >= 0) {
				listener = listenersCopy[i];
				keys = listener.keys;
				missing = listener.missing;
				if (missing) {
					if (missing.hasOwnProperty(key)) {
						delete missing[key];
						if (isEmpty(missing)) {
							listener.cb.apply({}, get(nucleus, keys).values);
							listener.calls--;
						}
					}
				} else if (inArray(keys, key)) {
					listener.cb.apply({}, get(nucleus, keys).values);
					listener.calls--;
				}
				if (!listener.calls) {
					removeListener(listeners);
				}
			}
			delete nucleus.needs[key];
			if (isObj) {
				objStack.pop();
			}
		}
	}

	// Helper function for setting up providers.
	function provide(nucleus, key, provider) {
		provider(function (result) {
			set(nucleus, key, result);
		});
	}

	// Return an actual instance.
	atom.create = function () {
		var
			nucleus = { props: {}, needs: {}, providers: {}, listeners: [] },
			props = nucleus.props,
			needs = nucleus.needs,
			providers = nucleus.providers,
			listeners = nucleus.listeners,
			q = []
		;

		// Execute the next function in the async queue.
		function doNext() {
			if (q) {
				q.pending = q.next = (!q.next && q.length) ?
					q.shift() : q.next;
				q.args = slice.call(arguments, 0);
				if (q.pending) {
					q.next = null;
					q.pending.apply({}, [doNext].concat(q.args));
				}
			}
		}

		var me = {

			// Add a function or functions to the async queue.  Functions added
			// thusly must call their first arg as a callback when done.  Any args
			// provided to the callback will be passed in to the next function in
			// the queue.
			chain: function () {
				if (q) {
					for (var i = 0, len = arguments.length; i < len; i++) {
						q.push(arguments[i]);
						if (!q.pending) {
							doNext.apply({}, q.args || []);
						}
					}
				}
			},

			// Remove references to all properties and listeners.  This releases
			// memory, and effective stops the atom from working.
			destroy: function () {
				delete nucleus.props;
				delete nucleus.needs;
				delete nucleus.providers;
				delete nucleus.listeners;
				while (q.length) {
					q.pop();
				}
				nucleus = props = needs = providers = listeners =
					q = q.pending = q.next = q.args = null;
			},

			// Call `func` on each of the specified keys.  The key is provided as
			// the first arg, and the value as the second.
			each: function (keyOrList, func) {
				var keys = toArray(keyOrList), i = -1, len = keys.length, key;
				while (++i < len) {
					key = keys[i];
					func(key, me.get(key));
				}
			},

			// Establish two-way binding between a key or list of keys for two
			// different atoms, so that changing a property on either atom will
			// propagate to the other.  If a map is provided for `keyOrListOrMap`,
			// properties on this atom may be bound to differently named properties
			// on `otherAtom`.  Note that entangled properties will not actually be
			// synchronized until the first change *after* entanglement.
			entangle: function (otherAtom, keyOrListOrMap) {
				var
					isList = isArray(keyOrListOrMap),
					isMap = !isList && typeof keyOrListOrMap === 'object',
					i, key,
					keys = isList ? keyOrListOrMap : isMap ? [] : [keyOrListOrMap],
					map = isMap ? keyOrListOrMap : {}
				;
				if (isMap) {
					for (key in map) {
						if (map.hasOwnProperty(key)) {
							keys.push(key);
						}
					}
				} else {
					for (i = keys.length; --i >= 0;) {
						key = keys[i];
						map[key] = key;
					}
				}
				me.each(keys, function (key) {
					var otherKey = map[key];
					me.bind(key, function (value) {
						otherAtom.set(otherKey, value);
					});
					otherAtom.bind(otherKey, function (value) {
						me.set(key, value);
					});
				});
			},

			// Get current values for the specified keys.  If `func` is provided,
			// it will be called with the values as args.
			get: function (keyOrList, func) {
				var result = get(nucleus, keyOrList, func);
				return func ? result : typeof keyOrList === 'string' ?
					result.values[0] : result.values;
			},

			// Returns true iff all of the specified keys exist (regardless of
			// value).
			has: function (keyOrList) {
				var keys = toArray(keyOrList);
				for (var i = keys.length; --i >= 0;) {
					if (!props.hasOwnProperty(keys[i])) {
						return false;
					}
				}
				return true;
			},

			// Return a list of all keys.
			keys: function () {
				var keys = [];
				for (var key in props) {
					if (props.hasOwnProperty(key)) {
						keys.push(key);
					}
				}
				return keys;
			},

			// Add arbitrary properties to this atom's interface.
			mixin: function (obj) {
				for (var p in obj) {
					if (obj.hasOwnProperty(p)) {
						me[p] = obj[p];
					}
				}
				return me;
			},

			// Call `func` as soon as all of the specified keys have been set.  If
			// they are already set, the function will be called immediately, with
			// all the values provided as args.  In this, it is identical to
			// `once()`.  However, calling `need()` will additionally invoke
			// providers when possible, in order to try and create the required
			// values.
			need: function (keyOrList, func) {
				var key, keys = toArray(keyOrList), values, provider;
				for (var i = keys.length; --i >= 0;) {
					key = keys[i];
					provider = providers[key];
					if (!props.hasOwnProperty(key) && provider) {
						provide(nucleus, key, provider);
						delete providers[key];
					} else {
						needs[key] = true;
					}
				}
				me.once(keys, func);
			},

			// Call `func` whenever any of the specified keys is next changed.  The
			// values of all keys will be provided as args to the function.  The
			// function will automatically be unbound after being called the first
			// time, so it is guaranteed to be called no more than once.
			next: function (keyOrList, func) {
				listeners.unshift(
					{ keys: toArray(keyOrList), cb: func, calls: 1 });
			},

			// Unregister a listener `func` that was previously registered using
			// `on()`, `bind()`, `need()`, `next()` or `once()`.
			off: function (func) { // alias: `unbind`
				for (var i = listeners.length; --i >= 0;) {
					if (listeners[i].cb === func) {
						listeners.splice(i, 1);
					}
				}
			},

			// Call `func` whenever any of the specified keys change.  The values
			// of the keys will be provided as args to func.
			on: function (keyOrList, func) { // alias: `bind`
				listeners.unshift({ keys: toArray(keyOrList), cb: func,
					calls: Infinity });
			},

			// Call `func` as soon as all of the specified keys have been set.  If
			// they are already set, the function will be called immediately, with
			// all the values provided as args.  Guaranteed to be called no more
			// than once.
			once: function (keyOrList, func) {
				var keys = toArray(keyOrList),
					results = get(nucleus, keys),
					values = results.values,
					missing = results.missing;
				if (!missing) {
					func.apply({}, values);
				} else {
					listeners.unshift(
						{ keys: keys, cb: func, missing: missing, calls: 1 });
				}
			},

			// Register a provider for a particular key.  The provider `func` is a
			// function that will be called if there is a need to create the key.
			// It must call its first arg as a callback, with the value.  Provider
			// functions will be called at most once.
			provide: function (key, func) {
				if (needs[key]) {
					provide(nucleus, key, func);
				} else if (!providers[key]) {
					providers[key] = func;
				}
			},

			// Set value for a key, or if `keyOrMap` is an object then set all the
			// keys' corresponding values.
			set: function (keyOrMap, value) {
				if (typeof keyOrMap === 'object') {
					for (var key in keyOrMap) {
						if (keyOrMap.hasOwnProperty(key)) {
							set(nucleus, key, keyOrMap[key]);
						}
					}
				} else {
					set(nucleus, keyOrMap, value);
				}
			}
		};
		me.bind = me.on;
		me.unbind = me.off;
		return me;
	};

}());
<|endoftext|>


<commit_before>const assert = require('assert')
const checkProps = require('./check-props')

const allowedProps = [
  'name',
  'data', 'width', 'height',
  'pixelFormat', 'encoding',
  'flipY',
  'mipmap',
  'target',
  'min', 'mag',
  'wrap',
  'premultiplayAlpha'
]

function createTexture (ctx, opts) {
  // checkProps(allowedProps, opts)

  const gl = ctx.gl

  // TODO: implement filtering options

  const texture = {
    class: 'texture',
    handle: gl.createTexture(),
    target: opts.target,
    width: 0,
    height: 0,
    _update: updateTexture2D
  }

  updateTexture2D(ctx, texture, opts)

  return texture
}

function orValue (a, b) {
  return (a !== undefined) ? a : b
}

// opts = { src, width, height }
// opts = { data, width, height, pixelFormat, encoding, flipY }
function updateTexture2D (ctx, texture, opts) {
  // checkProps(allowedProps, opts)

  const gl = ctx.gl
  const PixelFormat = ctx.PixelFormat

  let data = null
  let width = 0
  let height = 0
  let lod = 0
  let flipY = orValue(opts.flipY, orValue(texture.flipY, false))
  let target = opts.target || texture.target
  let pixelFormat = opts.pixelFormat || texture.pixelFormat
  let encoding = opts.encoding || texture.encoding
  let min = opts.min || texture.min || gl.NEAREST
  let mag = opts.min || texture.mag || gl.NEAREST
  let wrapS = opts.wrapS || texture.wrapS || opts.wrap || texture.wrap || gl.CLAMP_TO_EDGE
  let wrapT = opts.wrapS || texture.wrapS || opts.wrap || texture.wrap || gl.CLAMP_TO_EDGE
  let aniso = opts.aniso || texture.aniso || 0
  let premultiplayAlpha = orValue(opts.premultiplayAlpha, orValue(texture.premultiplayAlpha, false))
  let internalFormat = undefined
  let pixelformat = undefined
  let type = undefined

  assert(pixelFormat, 'PixelFormat is required')
  assert(encoding, 'Encoding is required')

  gl.getExtension('WEBGL_depth_texture')
  gl.getExtension('EXT_shader_texture_lod')
  gl.getExtension('OES_texture_float')
  gl.getExtension('OES_texture_float_linear')
  gl.getExtension('OES_texture_half_float')
  gl.getExtension('OES_texture_half_float_linear')
  var anisoExt = gl.getExtension('EXT_texture_filter_anisotropic')

  const textureUnit = 0
  gl.activeTexture(gl.TEXTURE0 + textureUnit)
  gl.bindTexture(texture.target, texture.handle)
  ctx.state.activeTextures[textureUnit] = texture

  if (opts.mipmap) {
    if (opts.data || opts.width || opts.height) {
      throw new Error('Updating and generating mipmaps at the same time is currently not supported')
    }
    gl.generateMipmap(texture.target)
    return
  }

  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY)
  gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplayAlpha)
  gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, min)
  gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, mag)
  gl.texParameteri(target, gl.TEXTURE_WRAP_S, wrapS)
  gl.texParameteri(target, gl.TEXTURE_WRAP_T, wrapT)
  if (anisoExt && aniso > 0) {
    gl.texParameterf(target, anisoExt.TEXTURE_MAX_ANISOTROPY_EXT, aniso);
  }

  // just an image
  // opts = HTMLImage

  // image with flags
  // opts = { data: HTMLImage, flipY: Boolean }

  // pixel data
  // opts = { data: Array, width: Number, height: Number, flipY: Boolean }

  // pixel data with flags
  // opts = { data: { data: Array, width: Number, height: Number }, flipY: Boolean },

  // array of images for cubemaps (and array textures in webgl2)
  // opts = { data: [ HTMLImage, ... ], width: Number, height: Number, flipY: Boolean }

  // array of pixel data for cubemaps (and array texture in webgl2)
  // opts = { data: [ { data: Array, width: Number, height: Number }, ..], flipY: Boolean }

  const img = opts.data ? opts.data : opts
  if (img && img.nodeName) {
    assert(img instanceof window.HTMLImageElement ||
      img instanceof window.HTMLCanvasElement,
      'Texture2D.update opts has to be Image, Canvas or Video element')
    // TODO: add support for HTMLVideoElement with videoWidth and videoHeight
    width = img.width
    height = img.height
    internalFormat = gl.RGBA
    format = gl.RGBA
    type = gl.UNSIGNED_BYTE
    gl.texImage2D(target, lod, internalFormat, format, type, img)
  } else if (typeof opts === 'object') {
    assert(!data || Array.isArray(opts.data) ||
      opts.data instanceof Uint8Array ||
      opts.data instanceof Float32Array,
      'Texture2D.update opts.data has to be null or an Array, Uint8Array or Float32Array')

    data = opts.data ? (opts.data.data || opts.data) : null

    assert((opts.width !== undefined) && (opts.height !== undefined),
      'Texture2D.update opts.width and opts.height are required when providing opts.data')

    width = opts.width
    height = opts.height

    if (pixelFormat === PixelFormat.Depth) {
      format = gl.DEPTH_COMPONENT
      internalFormat = gl.DEPTH_COMPONENT
      type = gl.UNSIGNED_SHORT
    } else if (pixelFormat === PixelFormat.RGBA8) {
      format = gl.RGBA
      internalFormat = gl.RGBA
      type = gl.UNSIGNED_BYTE
    } else if (pixelFormat === PixelFormat.RGBA32F) {
      format = gl.RGBA
      internalFormat = gl.RGBA
      type = gl.FLOAT
    } else if (pixelFormat === PixelFormat.RGBA16F) {
      format = gl.RGBA
      internalFormat = gl.RGBA
      type = gl.HALF_FLOAT
    } else if (pixelFormat === PixelFormat.R32F) {
      format = gl.ALPHA
      internalFormat = gl.ALPHA
      type = gl.FLOAT
    } else if (pixelFormat) {
      assert.fail(`Unknown texture pixel format: ${opts.format}`)
    }

    if (target === gl.TEXTURE_2D) {
      if (Array.isArray(data)) {
        if (type === gl.UNSIGNED_BYTE) {
          data = new Uint8Array(data)
        } else if (type === gl.FLOAT) {
          data = new Float32Array(data)
        } else if (type === gl.HALF_FLOAT) {
          data = new Float32Array(data)
        } else {
          assert.fail(`Unknown texture data type: ${type}`)
        }
      }
      gl.texImage2D(target, lod, internalFormat, width, height, 0, format, type, data)
    } else if (target === gl.TEXTURE_CUBE_MAP) {
      assert(!data || (Array.isArray(data) && data.length === 6), 'TextureCube requires data for 6 faces')
      for (let i = 0; i < 6; i++) {
        let faceData = data ? (data[i].data || data[i]) : null
        const faceTarget = gl.TEXTURE_CUBE_MAP_POSITIVE_X + i
        if (Array.isArray(faceData)) {
          if (type === gl.UNSIGNED_BYTE) {
            faceData = new Uint8Array(faceData)
          } else if (type === gl.FLOAT) {
            faceData = new Float32Array(data)
          } else {
            assert.fail(`Unknown texture data type: ${type}`)
          }
          gl.texImage2D(faceTarget, lod, internalFormat, width, height, 0, format, type, faceData)
        } else if (faceData && faceData.nodeName) {
          gl.texImage2D(faceTarget, lod, internalFormat, format, type, faceData)
        } else {
          gl.texImage2D(faceTarget, lod, internalFormat, width, height, 0, format, type, faceData)
        }
      }
    }
  } else {
    // TODO: should i assert of throw new Error(msg)?
    assert.fail('Texture2D.update opts has to be a HTMLElement or Object')
  }

  texture.target = target
  // texture.data = data
  texture.width = width
  texture.height = height
  texture.pixelFormat = pixelFormat
  texture.encoding = encoding
  texture.min = min
  texture.mag = mag
  texture.wrapS = wrapS
  texture.wrapT = wrapT
  texture.format = format
  texture.flipY = flipY
  texture.internalFormat = internalFormat
  texture.type = type
  texture.info = ''
  texture.info += Object.keys(ctx.PixelFormat).find((key) => ctx.PixelFormat[key] === pixelFormat)
  texture.info += '_'
  texture.info += Object.keys(ctx.Encoding).find((key) => ctx.Encoding[key] === encoding)

  return texture
}

module.exports = createTexture
<commit_msg>Fix wrapT typo in texture<commit_after>const assert = require('assert')
const checkProps = require('./check-props')

const allowedProps = [
  'name',
  'data', 'width', 'height',
  'pixelFormat', 'encoding',
  'flipY',
  'mipmap',
  'target',
  'min', 'mag',
  'wrap',
  'premultiplayAlpha'
]

function createTexture (ctx, opts) {
  // checkProps(allowedProps, opts)

  const gl = ctx.gl

  // TODO: implement filtering options

  const texture = {
    class: 'texture',
    handle: gl.createTexture(),
    target: opts.target,
    width: 0,
    height: 0,
    _update: updateTexture2D
  }

  updateTexture2D(ctx, texture, opts)

  return texture
}

function orValue (a, b) {
  return (a !== undefined) ? a : b
}

// opts = { src, width, height }
// opts = { data, width, height, pixelFormat, encoding, flipY }
function updateTexture2D (ctx, texture, opts) {
  // checkProps(allowedProps, opts)

  const gl = ctx.gl
  const PixelFormat = ctx.PixelFormat

  let data = null
  let width = 0
  let height = 0
  let lod = 0
  let flipY = orValue(opts.flipY, orValue(texture.flipY, false))
  let target = opts.target || texture.target
  let pixelFormat = opts.pixelFormat || texture.pixelFormat
  let encoding = opts.encoding || texture.encoding
  let min = opts.min || texture.min || gl.NEAREST
  let mag = opts.min || texture.mag || gl.NEAREST
  let wrapS = opts.wrapS || texture.wrapS || opts.wrap || texture.wrap || gl.CLAMP_TO_EDGE
  let wrapT = opts.wrapT || texture.wrapT || opts.wrap || texture.wrap || gl.CLAMP_TO_EDGE
  let aniso = opts.aniso || texture.aniso || 0
  let premultiplayAlpha = orValue(opts.premultiplayAlpha, orValue(texture.premultiplayAlpha, false))
  let internalFormat = undefined
  let pixelformat = undefined
  let type = undefined

  assert(pixelFormat, 'PixelFormat is required')
  assert(encoding, 'Encoding is required')

  gl.getExtension('WEBGL_depth_texture')
  gl.getExtension('EXT_shader_texture_lod')
  gl.getExtension('OES_texture_float')
  gl.getExtension('OES_texture_float_linear')
  gl.getExtension('OES_texture_half_float')
  gl.getExtension('OES_texture_half_float_linear')
  var anisoExt = gl.getExtension('EXT_texture_filter_anisotropic')

  const textureUnit = 0
  gl.activeTexture(gl.TEXTURE0 + textureUnit)
  gl.bindTexture(texture.target, texture.handle)
  ctx.state.activeTextures[textureUnit] = texture

  if (opts.mipmap) {
    if (opts.data || opts.width || opts.height) {
      throw new Error('Updating and generating mipmaps at the same time is currently not supported')
    }
    gl.generateMipmap(texture.target)
    return
  }

  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY)
  gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplayAlpha)
  gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, min)
  gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, mag)
  gl.texParameteri(target, gl.TEXTURE_WRAP_S, wrapS)
  gl.texParameteri(target, gl.TEXTURE_WRAP_T, wrapT)
  if (anisoExt && aniso > 0) {
    gl.texParameterf(target, anisoExt.TEXTURE_MAX_ANISOTROPY_EXT, aniso);
  }

  // just an image
  // opts = HTMLImage

  // image with flags
  // opts = { data: HTMLImage, flipY: Boolean }

  // pixel data
  // opts = { data: Array, width: Number, height: Number, flipY: Boolean }

  // pixel data with flags
  // opts = { data: { data: Array, width: Number, height: Number }, flipY: Boolean },

  // array of images for cubemaps (and array textures in webgl2)
  // opts = { data: [ HTMLImage, ... ], width: Number, height: Number, flipY: Boolean }

  // array of pixel data for cubemaps (and array texture in webgl2)
  // opts = { data: [ { data: Array, width: Number, height: Number }, ..], flipY: Boolean }

  const img = opts.data ? opts.data : opts
  if (img && img.nodeName) {
    assert(img instanceof window.HTMLImageElement ||
      img instanceof window.HTMLCanvasElement,
      'Texture2D.update opts has to be Image, Canvas or Video element')
    // TODO: add support for HTMLVideoElement with videoWidth and videoHeight
    width = img.width
    height = img.height
    internalFormat = gl.RGBA
    format = gl.RGBA
    type = gl.UNSIGNED_BYTE
    gl.texImage2D(target, lod, internalFormat, format, type, img)
  } else if (typeof opts === 'object') {
    assert(!data || Array.isArray(opts.data) ||
      opts.data instanceof Uint8Array ||
      opts.data instanceof Float32Array,
      'Texture2D.update opts.data has to be null or an Array, Uint8Array or Float32Array')

    data = opts.data ? (opts.data.data || opts.data) : null

    assert((opts.width !== undefined) && (opts.height !== undefined),
      'Texture2D.update opts.width and opts.height are required when providing opts.data')

    width = opts.width
    height = opts.height

    if (pixelFormat === PixelFormat.Depth) {
      format = gl.DEPTH_COMPONENT
      internalFormat = gl.DEPTH_COMPONENT
      type = gl.UNSIGNED_SHORT
    } else if (pixelFormat === PixelFormat.RGBA8) {
      format = gl.RGBA
      internalFormat = gl.RGBA
      type = gl.UNSIGNED_BYTE
    } else if (pixelFormat === PixelFormat.RGBA32F) {
      format = gl.RGBA
      internalFormat = gl.RGBA
      type = gl.FLOAT
    } else if (pixelFormat === PixelFormat.RGBA16F) {
      format = gl.RGBA
      internalFormat = gl.RGBA
      type = gl.HALF_FLOAT
    } else if (pixelFormat === PixelFormat.R32F) {
      format = gl.ALPHA
      internalFormat = gl.ALPHA
      type = gl.FLOAT
    } else if (pixelFormat) {
      assert.fail(`Unknown texture pixel format: ${opts.format}`)
    }

    if (target === gl.TEXTURE_2D) {
      if (Array.isArray(data)) {
        if (type === gl.UNSIGNED_BYTE) {
          data = new Uint8Array(data)
        } else if (type === gl.FLOAT) {
          data = new Float32Array(data)
        } else if (type === gl.HALF_FLOAT) {
          data = new Float32Array(data)
        } else {
          assert.fail(`Unknown texture data type: ${type}`)
        }
      }
      gl.texImage2D(target, lod, internalFormat, width, height, 0, format, type, data)
    } else if (target === gl.TEXTURE_CUBE_MAP) {
      assert(!data || (Array.isArray(data) && data.length === 6), 'TextureCube requires data for 6 faces')
      for (let i = 0; i < 6; i++) {
        let faceData = data ? (data[i].data || data[i]) : null
        const faceTarget = gl.TEXTURE_CUBE_MAP_POSITIVE_X + i
        if (Array.isArray(faceData)) {
          if (type === gl.UNSIGNED_BYTE) {
            faceData = new Uint8Array(faceData)
          } else if (type === gl.FLOAT) {
            faceData = new Float32Array(data)
          } else {
            assert.fail(`Unknown texture data type: ${type}`)
          }
          gl.texImage2D(faceTarget, lod, internalFormat, width, height, 0, format, type, faceData)
        } else if (faceData && faceData.nodeName) {
          gl.texImage2D(faceTarget, lod, internalFormat, format, type, faceData)
        } else {
          gl.texImage2D(faceTarget, lod, internalFormat, width, height, 0, format, type, faceData)
        }
      }
    }
  } else {
    // TODO: should i assert of throw new Error(msg)?
    assert.fail('Texture2D.update opts has to be a HTMLElement or Object')
  }

  texture.target = target
  // texture.data = data
  texture.width = width
  texture.height = height
  texture.pixelFormat = pixelFormat
  texture.encoding = encoding
  texture.min = min
  texture.mag = mag
  texture.wrapS = wrapS
  texture.wrapT = wrapT
  texture.format = format
  texture.flipY = flipY
  texture.internalFormat = internalFormat
  texture.type = type
  texture.info = ''
  texture.info += Object.keys(ctx.PixelFormat).find((key) => ctx.PixelFormat[key] === pixelFormat)
  texture.info += '_'
  texture.info += Object.keys(ctx.Encoding).find((key) => ctx.Encoding[key] === encoding)

  return texture
}

module.exports = createTexture
<|endoftext|>


<commit_before>/**
 *
 * Worker script for browser and node
 *
 * @fileoverview Worker script for browser and node
 * @author Kevin Kwok <antimatter15@gmail.com>
 * @author Guillermo Webster <gui@mit.edu>
 * @author Jerome Wu <jeromewus@gmail.com>
 */
require('regenerator-runtime/runtime');
const fileType = require('file-type');
const isURL = require('is-url');
const dump = require('./utils/dump');
const isWebWorker = require('../utils/getEnvironment')('type') === 'webworker';
const setImage = require('./utils/setImage');
const defaultParams = require('./constants/defaultParams');
const { log, setLogging } = require('../utils/log');

/*
 * Tesseract Module returned by TesseractCore.
 */
let TessModule;
/*
 * TessearctBaseAPI instance
 */
let api = null;
let latestJob;
let adapter = {};
let params = defaultParams;

const load = ({ workerId, jobId, payload: { options: { corePath, logging } } }, res) => {
  setLogging(logging);
  if (!TessModule) {
    const Core = adapter.getCore(corePath, res);

    res.progress({ workerId, status: 'initializing tesseract', progress: 0 });

    Core({
      TesseractProgress(percent) {
        latestJob.progress({
          workerId,
          jobId,
          status: 'recognizing text',
          progress: Math.max(0, (percent - 30) / 70),
        });
      },
    }).then((tessModule) => {
      TessModule = tessModule;
      res.progress({ workerId, status: 'initialized tesseract', progress: 1 });
      res.resolve({ loaded: true });
    });
  } else {
    res.resolve({ loaded: true });
  }
};

const FS = ({ workerId, payload: { method, args } }, res) => {
  log(`[${workerId}]: FS.${method} with args ${args}`);
  res.resolve(TessModule.FS[method](...args));
};

const loadLanguage = async ({
  workerId,
  payload: {
    langs,
    options: {
      langPath,
      dataPath,
      cachePath,
      cacheMethod,
      gzip = true,
    },
  },
},
  res) => {
  const loadAndGunzipFile = async (_lang) => {
    const lang = typeof _lang === 'string' ? _lang : _lang.code;
    const readCache = ['refresh', 'none'].includes(cacheMethod)
      ? () => Promise.resolve()
      : adapter.readCache;
    let data = null;

    try {
      const _data = await readCache(`${cachePath || '.'}/${lang}.traineddata`);
      if (typeof _data !== 'undefined') {
        log(`[${workerId}]: Load ${lang}.traineddata from cache`);
        data = _data;
      } else {
        throw Error('Not found in cache');
      }
    } catch (e) {
      log(`[${workerId}]: Load ${lang}.traineddata from ${langPath}`);
      if (typeof _lang === 'string') {
        let path = null;

        if (isURL(langPath) || langPath.startsWith('moz-extension://') || langPath.startsWith('chrome-extension://') || langPath.startsWith('file://')) { /** When langPath is an URL */
          path = langPath;
        }

        if (path !== null) {
          const resp = await (isWebWorker ? fetch : adapter.fetch)(`${path}/${lang}.traineddata${gzip ? '.gz' : ''}`);
          data = await resp.arrayBuffer();
        } else {
          data = await adapter.readCache(`${langPath}/${lang}.traineddata${gzip ? '.gz' : ''}`);
        }
      } else {
        data = _lang.data; // eslint-disable-line
      }
    }

    data = new Uint8Array(data);

    const type = fileType(data);
    if (typeof type !== 'undefined' && type.mime === 'application/gzip') {
      data = adapter.gunzip(data);
    }

    if (TessModule) {
      if (dataPath) {
        try {
          TessModule.FS.mkdir(dataPath);
        } catch (err) {
          res.reject(err.toString());
        }
      }
      TessModule.FS.writeFile(`${dataPath || '.'}/${lang}.traineddata`, data);
    }

    if (['write', 'refresh', undefined].includes(cacheMethod)) {
      await adapter.writeCache(`${cachePath || '.'}/${lang}.traineddata`, data);
    }

    return Promise.resolve(data);
  };

  res.progress({ workerId, status: 'loading language traineddata', progress: 0 });
  try {
    await Promise.all((typeof langs === 'string' ? langs.split('+') : langs).map(loadAndGunzipFile));
    res.progress({ workerId, status: 'loaded language traineddata', progress: 1 });
    res.resolve(langs);
  } catch (err) {
    if (isWebWorker && err instanceof DOMException) {
      /*
       * For some reason google chrome throw DOMException in loadLang,
       * while other browser is OK, for now we ignore this exception
       * and hopefully to find the root cause one day.
       */
    } else {
      res.reject(err.toString());
    }
  }
};

const setParameters = ({ payload: { params: _params } }, res) => {
  Object.keys(_params)
    .filter(k => !k.startsWith('tessjs_'))
    .forEach((key) => {
      api.SetVariable(key, _params[key]);
    });
  params = { ...params, ..._params };

  if (typeof res !== 'undefined') {
    res.resolve(params);
  }
};

const initialize = ({
  workerId,
  payload: { langs: _langs, oem },
}, res) => {
  const langs = (typeof _langs === 'string')
    ? _langs
    : _langs.map(l => ((typeof l === 'string') ? l : l.data)).join('+');

  try {
    res.progress({
      workerId, status: 'initializing api', progress: 0,
    });
    if (api !== null) {
      api.End();
    }
    api = new TessModule.TessBaseAPI();
    api.Init(null, langs, oem);
    params = defaultParams;
    setParameters({ payload: { params } });
    res.progress({
      workerId, status: 'initialized api', progress: 1,
    });
    res.resolve();
  } catch (err) {
    res.reject(err.toString());
  }
};

const recognize = ({ payload: { image, options: { rectangle: rec } } }, res) => {
  try {
    const ptr = setImage(TessModule, api, image);
    if (typeof rec === 'object') {
      api.SetRectangle(rec.left, rec.top, rec.width, rec.height);
    }
    api.Recognize(null);
    res.resolve(dump(TessModule, api, params));
    TessModule._free(ptr);
  } catch (err) {
    res.reject(err.toString());
  }
};

const getPDF = ({ payload: { title, textonly } }, res) => {
  const pdfRenderer = new TessModule.TessPDFRenderer('tesseract-ocr', '/', textonly);
  pdfRenderer.BeginDocument(title);
  pdfRenderer.AddImage(api);
  pdfRenderer.EndDocument();
  TessModule._free(pdfRenderer);

  res.resolve(TessModule.FS.readFile('/tesseract-ocr.pdf'));
};

const detect = ({ payload: { image } }, res) => {
  try {
    const ptr = setImage(TessModule, api, image);
    const results = new TessModule.OSResults();

    if (!api.DetectOS(results)) {
      api.End();
      TessModule._free(ptr);
      res.reject('Failed to detect OS');
    } else {
      const best = results.best_result;
      const oid = best.orientation_id;
      const sid = best.script_id;

      TessModule._free(ptr);

      res.resolve({
        tesseract_script_id: sid,
        script: results.unicharset.get_script_from_script_id(sid),
        script_confidence: best.sconfidence,
        orientation_degrees: [0, 270, 180, 90][oid],
        orientation_confidence: best.oconfidence,
      });
    }
  } catch (err) {
    res.reject(err.toString());
  }
};

const terminate = (_, res) => {
  try {
    if (api !== null) {
      api.End();
    }
    res.resolve({ terminated: true });
  } catch (err) {
    res.reject(err.toString());
  }
};

/**
 * dispatchHandlers
 *
 * @name dispatchHandlers
 * @function worker data handler
 * @access public
 * @param {object} data
 * @param {string} data.jobId - unique job id
 * @param {string} data.action - action of the job, only recognize and detect for now
 * @param {object} data.payload - data for the job
 * @param {function} send - trigger job to work
 */
exports.dispatchHandlers = (packet, send) => {
  const res = (status, data) => {
    send({
      ...packet,
      status,
      data,
    });
  };
  res.resolve = res.bind(this, 'resolve');
  res.reject = res.bind(this, 'reject');
  res.progress = res.bind(this, 'progress');

  latestJob = res;

  try {
    ({
      load,
      FS,
      loadLanguage,
      initialize,
      setParameters,
      recognize,
      getPDF,
      detect,
      terminate,
    })[packet.action](packet, res);
  } catch (err) {
    /** Prepare exception to travel through postMessage */
    res.reject(err.toString());
  }
};

/**
 * setAdapter
 *
 * @name setAdapter
 * @function
 * @access public
 * @param {object} adapter - implementation of the worker, different in browser and node environment
 */
exports.setAdapter = (_adapter) => {
  adapter = _adapter;
};
<commit_msg>Add from cache message when loading traineddata<commit_after>/**
 *
 * Worker script for browser and node
 *
 * @fileoverview Worker script for browser and node
 * @author Kevin Kwok <antimatter15@gmail.com>
 * @author Guillermo Webster <gui@mit.edu>
 * @author Jerome Wu <jeromewus@gmail.com>
 */
require('regenerator-runtime/runtime');
const fileType = require('file-type');
const isURL = require('is-url');
const dump = require('./utils/dump');
const isWebWorker = require('../utils/getEnvironment')('type') === 'webworker';
const setImage = require('./utils/setImage');
const defaultParams = require('./constants/defaultParams');
const { log, setLogging } = require('../utils/log');

/*
 * Tesseract Module returned by TesseractCore.
 */
let TessModule;
/*
 * TessearctBaseAPI instance
 */
let api = null;
let latestJob;
let adapter = {};
let params = defaultParams;

const load = ({ workerId, jobId, payload: { options: { corePath, logging } } }, res) => {
  setLogging(logging);
  if (!TessModule) {
    const Core = adapter.getCore(corePath, res);

    res.progress({ workerId, status: 'initializing tesseract', progress: 0 });

    Core({
      TesseractProgress(percent) {
        latestJob.progress({
          workerId,
          jobId,
          status: 'recognizing text',
          progress: Math.max(0, (percent - 30) / 70),
        });
      },
    }).then((tessModule) => {
      TessModule = tessModule;
      res.progress({ workerId, status: 'initialized tesseract', progress: 1 });
      res.resolve({ loaded: true });
    });
  } else {
    res.resolve({ loaded: true });
  }
};

const FS = ({ workerId, payload: { method, args } }, res) => {
  log(`[${workerId}]: FS.${method} with args ${args}`);
  res.resolve(TessModule.FS[method](...args));
};

const loadLanguage = async ({
  workerId,
  payload: {
    langs,
    options: {
      langPath,
      dataPath,
      cachePath,
      cacheMethod,
      gzip = true,
    },
  },
},
  res) => {
  const loadAndGunzipFile = async (_lang) => {
    const lang = typeof _lang === 'string' ? _lang : _lang.code;
    const readCache = ['refresh', 'none'].includes(cacheMethod)
      ? () => Promise.resolve()
      : adapter.readCache;
    let data = null;

    try {
      const _data = await readCache(`${cachePath || '.'}/${lang}.traineddata`);
      if (typeof _data !== 'undefined') {
        log(`[${workerId}]: Load ${lang}.traineddata from cache`);
        res.progress({ workerId, status: 'loading language traineddata (from cache)', progress: 0.5 });
        data = _data;
      } else {
        throw Error('Not found in cache');
      }
    } catch (e) {
      log(`[${workerId}]: Load ${lang}.traineddata from ${langPath}`);
      if (typeof _lang === 'string') {
        let path = null;

        if (isURL(langPath) || langPath.startsWith('moz-extension://') || langPath.startsWith('chrome-extension://') || langPath.startsWith('file://')) { /** When langPath is an URL */
          path = langPath;
        }

        if (path !== null) {
          const resp = await (isWebWorker ? fetch : adapter.fetch)(`${path}/${lang}.traineddata${gzip ? '.gz' : ''}`);
          data = await resp.arrayBuffer();
        } else {
          data = await adapter.readCache(`${langPath}/${lang}.traineddata${gzip ? '.gz' : ''}`);
        }
      } else {
        data = _lang.data; // eslint-disable-line
      }
    }

    data = new Uint8Array(data);

    const type = fileType(data);
    if (typeof type !== 'undefined' && type.mime === 'application/gzip') {
      data = adapter.gunzip(data);
    }

    if (TessModule) {
      if (dataPath) {
        try {
          TessModule.FS.mkdir(dataPath);
        } catch (err) {
          res.reject(err.toString());
        }
      }
      TessModule.FS.writeFile(`${dataPath || '.'}/${lang}.traineddata`, data);
    }

    if (['write', 'refresh', undefined].includes(cacheMethod)) {
      await adapter.writeCache(`${cachePath || '.'}/${lang}.traineddata`, data);
    }

    return Promise.resolve(data);
  };

  res.progress({ workerId, status: 'loading language traineddata', progress: 0 });
  try {
    await Promise.all((typeof langs === 'string' ? langs.split('+') : langs).map(loadAndGunzipFile));
    res.progress({ workerId, status: 'loaded language traineddata', progress: 1 });
    res.resolve(langs);
  } catch (err) {
    if (isWebWorker && err instanceof DOMException) {
      /*
       * For some reason google chrome throw DOMException in loadLang,
       * while other browser is OK, for now we ignore this exception
       * and hopefully to find the root cause one day.
       */
    } else {
      res.reject(err.toString());
    }
  }
};

const setParameters = ({ payload: { params: _params } }, res) => {
  Object.keys(_params)
    .filter(k => !k.startsWith('tessjs_'))
    .forEach((key) => {
      api.SetVariable(key, _params[key]);
    });
  params = { ...params, ..._params };

  if (typeof res !== 'undefined') {
    res.resolve(params);
  }
};

const initialize = ({
  workerId,
  payload: { langs: _langs, oem },
}, res) => {
  const langs = (typeof _langs === 'string')
    ? _langs
    : _langs.map(l => ((typeof l === 'string') ? l : l.data)).join('+');

  try {
    res.progress({
      workerId, status: 'initializing api', progress: 0,
    });
    if (api !== null) {
      api.End();
    }
    api = new TessModule.TessBaseAPI();
    api.Init(null, langs, oem);
    params = defaultParams;
    setParameters({ payload: { params } });
    res.progress({
      workerId, status: 'initialized api', progress: 1,
    });
    res.resolve();
  } catch (err) {
    res.reject(err.toString());
  }
};

const recognize = ({ payload: { image, options: { rectangle: rec } } }, res) => {
  try {
    const ptr = setImage(TessModule, api, image);
    if (typeof rec === 'object') {
      api.SetRectangle(rec.left, rec.top, rec.width, rec.height);
    }
    api.Recognize(null);
    res.resolve(dump(TessModule, api, params));
    TessModule._free(ptr);
  } catch (err) {
    res.reject(err.toString());
  }
};

const getPDF = ({ payload: { title, textonly } }, res) => {
  const pdfRenderer = new TessModule.TessPDFRenderer('tesseract-ocr', '/', textonly);
  pdfRenderer.BeginDocument(title);
  pdfRenderer.AddImage(api);
  pdfRenderer.EndDocument();
  TessModule._free(pdfRenderer);

  res.resolve(TessModule.FS.readFile('/tesseract-ocr.pdf'));
};

const detect = ({ payload: { image } }, res) => {
  try {
    const ptr = setImage(TessModule, api, image);
    const results = new TessModule.OSResults();

    if (!api.DetectOS(results)) {
      api.End();
      TessModule._free(ptr);
      res.reject('Failed to detect OS');
    } else {
      const best = results.best_result;
      const oid = best.orientation_id;
      const sid = best.script_id;

      TessModule._free(ptr);

      res.resolve({
        tesseract_script_id: sid,
        script: results.unicharset.get_script_from_script_id(sid),
        script_confidence: best.sconfidence,
        orientation_degrees: [0, 270, 180, 90][oid],
        orientation_confidence: best.oconfidence,
      });
    }
  } catch (err) {
    res.reject(err.toString());
  }
};

const terminate = (_, res) => {
  try {
    if (api !== null) {
      api.End();
    }
    res.resolve({ terminated: true });
  } catch (err) {
    res.reject(err.toString());
  }
};

/**
 * dispatchHandlers
 *
 * @name dispatchHandlers
 * @function worker data handler
 * @access public
 * @param {object} data
 * @param {string} data.jobId - unique job id
 * @param {string} data.action - action of the job, only recognize and detect for now
 * @param {object} data.payload - data for the job
 * @param {function} send - trigger job to work
 */
exports.dispatchHandlers = (packet, send) => {
  const res = (status, data) => {
    send({
      ...packet,
      status,
      data,
    });
  };
  res.resolve = res.bind(this, 'resolve');
  res.reject = res.bind(this, 'reject');
  res.progress = res.bind(this, 'progress');

  latestJob = res;

  try {
    ({
      load,
      FS,
      loadLanguage,
      initialize,
      setParameters,
      recognize,
      getPDF,
      detect,
      terminate,
    })[packet.action](packet, res);
  } catch (err) {
    /** Prepare exception to travel through postMessage */
    res.reject(err.toString());
  }
};

/**
 * setAdapter
 *
 * @name setAdapter
 * @function
 * @access public
 * @param {object} adapter - implementation of the worker, different in browser and node environment
 */
exports.setAdapter = (_adapter) => {
  adapter = _adapter;
};
<|endoftext|>


<commit_before>// 1 Params and Operator
let searchParams = new kaltura.objects.ESearchEntryParams();
searchParams.searchOperator = new kaltura.objects.ESearchEntryOperator();
searchParams.searchOperator.searchItems = [];

// 2 Search Type
searchParams.searchOperator.searchItems[0] = new kaltura.objects.ESearchUnifiedItem();

// 3 Search Term
searchParams.searchOperator.searchItems[0].searchTerm = "kaltura logo";

// 4 Search Item Type
searchParams.searchOperator.searchItems[0].itemType = kaltura.enums.ESearchItemType.EXACT_MATCH;

// 5 Add Highlight
searchParams.searchOperator.searchItems[0].addHighlight = true;

// 6 Search
kaltura.services.eSearch.searchEntry(searchParams)
.execute(client)
.then(result => {
    console.log(result);
});<commit_msg>Delete esearch.js<commit_after><|endoftext|>


<commit_before>'use strict';

const mocha = require('mocha');

const BaseWorker = require('./BaseWorker');
const EventsBus = require('../libs/EventsBus');
const Util = require('../libs/Util');

class MochaWorker extends BaseWorker {
    constructor(scenario) {
        super(scenario);
        this._mocha = new mocha();
    }
    setup(done) {
        this._mocha.addFile(this.scenario.options.entrypoint);
        this._mocha.reporter('json');
        this._mocha.timeout(0);
        super.setup(done);
    }
    run(done) {
        super.run(() => {
            let output = '';

            process.stdout.write = (chunk) => {
                output += chunk;
            };

            let runner = this._mocha.run((failures) => {
                this.scenario.data = Util.deepExtend(this.scenario.data, Data);
                done(JSON.parse(output));
            });

            runner.on('test', (test) => {
                EventsBus.emit('worker:test', {
                    name: this.name,
                    data: test.title
                });
            });

            // runner.on('test end', (test) => {
            //     EventsBus.emit('worker:testend', {);
            // });

            runner.on('pass', (test) => {
                EventsBus.emit('worker:pass', {
                    name: this.name,
                    data: test.title
                });
            });
            runner.on('fail', (test) => {
                EventsBus.emit('worker:fail', {
                    name: this.name,
                    data: test.title
                });
            });
        })
    }
}

module.exports = MochaWorker;<commit_msg>Set timeout of five minutes<commit_after>'use strict';

const mocha = require('mocha');

const BaseWorker = require('./BaseWorker');
const EventsBus = require('../libs/EventsBus');
const Util = require('../libs/Util');

class MochaWorker extends BaseWorker {
    constructor(scenario) {
        super(scenario);
        this._mocha = new mocha();
    }
    setup(done) {
        this._mocha.addFile(this.scenario.options.entrypoint);
        this._mocha.reporter('json');
        this._mocha.timeout(300000);
        super.setup(done);
    }
    run(done) {
        super.run(() => {
            let output = '';

            process.stdout.write = (chunk) => {
                output += chunk;
            };

            let runner = this._mocha.run((failures) => {
                this.scenario.data = Util.deepExtend(this.scenario.data, Data);
                done(JSON.parse(output));
            });

            runner.on('test', (test) => {
                EventsBus.emit('worker:test', {
                    name: this.name,
                    data: test.title
                });
            });

            // runner.on('test end', (test) => {
            //     EventsBus.emit('worker:testend', {);
            // });

            runner.on('pass', (test) => {
                EventsBus.emit('worker:pass', {
                    name: this.name,
                    data: test.title
                });
            });
            runner.on('fail', (test) => {
                EventsBus.emit('worker:fail', {
                    name: this.name,
                    data: test.title
                });
            });
        })
    }
}

module.exports = MochaWorker;
<|endoftext|>


<commit_before>/**
  Copyright 2010-2011 Mateusz Paprocki <mattpap@gmail.com>
  Copyright 2011 Christian Iversen <ci@sikkerhed.org>

  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.
**/
"use strict";

var $PY = {};

var py_builtins = {};

py_builtins.PY$__python3__ = false;

function bt() {
    try {
        null();
    } catch (x) {
        py_builtins.print(x.stack);
    }
}

function iterate(obj, func) {
    var seq = iter(obj);
    while (true) {
        try {
            func(seq.PY$next());
        } catch (exc) {
            if ($PY.isinstance(exc, py_builtins.StopIteration) == true) {
                break;
            } else {
                throw exc;
            }
        }
    }
}

var __kwargs_make = function(kw) {
    kw.__kwargs = true;
    return kw;
};

var __varargs_make = function(vr) {
    vr = tuple(vr);
    vr.__varargs = true;
    return vr;
};

var __varargs_get = function(args) {
    if (args.length && (args[args.length-1].__varargs === true)) {
        var vargs = args[args.length-1];
        args.length -= 1;
        return vargs;
    } else {
        return tuple();
    }
};

var __kwargs_get = function(args) {
    if (args.length && (args[args.length-1].__kwargs === true)) {
        delete args[args.length-1].__kwargs;
        var res = args[args.length-1];
        delete args[args.length-1];
        args.length -= 1;
        return res;
    } else {
        return [];
    }
};

var staticmethod = function(func) {
    var res = function () {
        return func.apply(null, [this].concat(Array.prototype.slice.call(arguments)));
    };
    res.__static = true;
    return res;
};

var js = function(obj) {
    /*
       Converts (recursively) a Python object to a javascript builtin object.

       In particular:

       tuple -> Array
       list -> Array
       dict -> Object

       It uses the obj._js_() if it is defined, otherwise it just returns the
       same object. It is the responsibility of _js_() to convert recursively
       the object itself.
    */
    if ((obj != null) && obj._js_ !== undefined)
        return obj._js_();
    else
        return obj;
};

var py = function(obj) {
    if (obj && obj.PY$__class__ !== undefined) {
        return obj;
    } else if (typeof obj === 'number') {
        return int(obj);
    } else if (typeof obj == 'string') {
        return str(obj);
    } else if (obj instanceof Array) {
        var res = list();
        for (var q in obj) {
          res.PY$append(py(obj[q]));
        }
        return res;
    } else {
        var res = dict();
        for (var q in obj) {
            res.PY$__setitem__(q, py(obj[q]));
        }
        return res;
    }
};

$PY.isinstance = function(obj, cls) {
    if (cls instanceof Array) {
        for (var i = 0; i < cls.length; i++) {
            var c = obj.PY$__class__;
            while (c) {
                if (c === cls[i])
                    return true;
                c = c.PY$__super__;
            }
        }

        return false;
    } else {
        var c = obj.PY$__class__;
        while (c) {
            if (c === cls)
                return true;
            c = c.PY$__super__;
        }
        return false;
    }
};
<commit_msg> + Added support for optional secondary param to __kwargs_make()<commit_after>/**
  Copyright 2010-2011 Mateusz Paprocki <mattpap@gmail.com>
  Copyright 2011 Christian Iversen <ci@sikkerhed.org>

  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.
**/
"use strict";

var $PY = {};

var py_builtins = {};

py_builtins.PY$__python3__ = false;

function bt() {
    try {
        null();
    } catch (x) {
        py_builtins.print(x.stack);
    }
}

function iterate(obj, func) {
    var seq = iter(obj);
    while (true) {
        try {
            func(seq.PY$next());
        } catch (exc) {
            if ($PY.isinstance(exc, py_builtins.StopIteration) == true) {
                break;
            } else {
                throw exc;
            }
        }
    }
}

var __kwargs_make = function(kw, kwargs) {
    kw.__kwargs = true;
    if (kwargs !== undefined) {
        if (kwargs.PY$__class__ !== dict) {
            throw TypeError("Keyword arguments with non-standard dictionary types not supported");
        }
        for (var key in kwargs.items) {
            kw[key] = kwargs.items[key];
        };
    };
    return kw;
};

var __varargs_make = function(vr) {
    vr = tuple(vr);
    vr.__varargs = true;
    return vr;
};

var __varargs_get = function(args) {
    if (args.length && (args[args.length-1].__varargs === true)) {
        var vargs = args[args.length-1];
        args.length -= 1;
        return vargs;
    } else {
        return tuple();
    }
};

var __kwargs_get = function(args) {
    if (args.length && (args[args.length-1].__kwargs === true)) {
        delete args[args.length-1].__kwargs;
        var res = args[args.length-1];
        delete args[args.length-1];
        args.length -= 1;
        return res;
    } else {
        return [];
    }
};

var staticmethod = function(func) {
    var res = function () {
        return func.apply(null, [this].concat(Array.prototype.slice.call(arguments)));
    };
    res.__static = true;
    return res;
};

var js = function(obj) {
    /*
       Converts (recursively) a Python object to a javascript builtin object.

       In particular:

       tuple -> Array
       list -> Array
       dict -> Object

       It uses the obj._js_() if it is defined, otherwise it just returns the
       same object. It is the responsibility of _js_() to convert recursively
       the object itself.
    */
    if ((obj != null) && obj._js_ !== undefined)
        return obj._js_();
    else
        return obj;
};

var py = function(obj) {
    if (obj && obj.PY$__class__ !== undefined) {
        return obj;
    } else if (typeof obj === 'number') {
        return int(obj);
    } else if (typeof obj == 'string') {
        return str(obj);
    } else if (obj instanceof Array) {
        var res = list();
        for (var q in obj) {
          res.PY$append(py(obj[q]));
        }
        return res;
    } else {
        var res = dict();
        for (var q in obj) {
            res.PY$__setitem__(q, py(obj[q]));
        }
        return res;
    }
};

$PY.isinstance = function(obj, cls) {
    if (cls instanceof Array) {
        for (var i = 0; i < cls.length; i++) {
            var c = obj.PY$__class__;
            while (c) {
                if (c === cls[i])
                    return true;
                c = c.PY$__super__;
            }
        }

        return false;
    } else {
        var c = obj.PY$__class__;
        while (c) {
            if (c === cls)
                return true;
            c = c.PY$__super__;
        }
        return false;
    }
};
<|endoftext|>


<commit_before>'use strict'

//*********************************************
// Instantiate Inspire Game Objects
//*********************************************
const SpaceImage = require('./game/space-image.js')
const Color = require('./game/color.js')
const DecodeMessage = require('./game/decode-message.js')
const Wikipedia = require('./game/wikipedia.js')
const Flickr = require('./game/flickr.js')
const Dream = require('./game/dream.js')
const Write = require('./game/write.js')
const Headline = require('./game/headline.js')

//*********************************************
// List of Inspire routes
//*********************************************
var default_skills = {
		'color'				: 0,
		'space'				: 0,
		'decode_message' 	: 0,
		'wikipedia' 		: 0,
		'flickr'			: 0,
		'dream'				: 0,
		'write'				: 0,
		'headline'			: 0
	}

//*********************************************
// Inspire Functions
//*********************************************

function getInspireRoute (db, msg) {
	var route_choosen
	// Get object of skills and select a undone route.
	db.getInspireSkills(msg.body.event.user, (err, inspire_skills) => {
		if (err) {
			console.error(err)
		}
		var route_choosen = selectInspireSkills(db, msg, inspire_skills)
		console.log(route_choosen)
		msg.route(route_choosen + "_route")
		switch (route_choosen)
		{
			case 'space':
				SpaceImage.run(db, msg)
				break;
			case 'color':
				Color.run(db, msg)
				break;
			case 'decode_message':
				DecodeMessage.run(db, msg)
				break;
			case 'wikipedia':
				Wikipedia.run(msg)
				break;
			case 'flickr':
				Flickr.run(msg)
				break;
			case 'dream':
				Dream.run(msg)
				break;
			case 'write':
				Write.run(msg)
				break;
			case 'headline':
				Headline.run(msg)
				break;
			default:
				SpaceImage.run(db, msg)
		}
	})
}

function selectInspireSkills (db, msg, inspire_skills) {

	var skills = inspire_skills
	if (!(skills instanceof Object))
	{
		// If no data exists, create object to store skills
		skills = default_skills
	}
	// Create array of skills not done
	var skills_choosen = []
	for (var key in skills)
	{
		if (skills.hasOwnProperty(key))
		{
			if (skills[key] == 0)
			{
				skills_choosen.push(key)
			}
		}
	}
	// If all skills have been done, loop through skills and set to undone and push all skills to skills_choosen
	if (skills_choosen.length == 0)
	{
		for (var key in skills)
		{
			if (skills.hasOwnProperty(key))
			{
				skills[key] = 0
				skills_choosen.push(key)
			}
		}
	}
	// Select randomly from array of skills not done
	var route_choosen = skills_choosen[Math.floor(Math.random() * skills_choosen.length)]

	skills[route_choosen] = 1
	// Store changes made to the skills done
	db.saveInspireSkills(msg.body.event.user, skills, (err, convo) => {
		if (err) {
			console.log(err)
		}
	})

	return route_choosen
}

module.exports = {
	getInspireRoute: getInspireRoute
}
<commit_msg>After completeing all games, set skills to default skills array.<commit_after>'use strict'

//*********************************************
// Instantiate Inspire Game Objects
//*********************************************
const SpaceImage = require('./game/space-image.js')
const Color = require('./game/color.js')
const DecodeMessage = require('./game/decode-message.js')
const Wikipedia = require('./game/wikipedia.js')
const Flickr = require('./game/flickr.js')
const Dream = require('./game/dream.js')
const Write = require('./game/write.js')
const Headline = require('./game/headline.js')

//*********************************************
// List of Inspire routes
//*********************************************
var default_skills = {
		'color'				: 0,
		'space'				: 0,
		'decode_message' 	: 0,
		'wikipedia' 		: 0,
		'flickr'			: 0,
		'dream'				: 0,
		'write'				: 0,
		'headline'			: 0
	}

//*********************************************
// Inspire Functions
//*********************************************

function getInspireRoute (db, msg) {
	var route_choosen
	// Get object of skills and select a undone route.
	db.getInspireSkills(msg.body.event.user, (err, inspire_skills) => {
		if (err) {
			console.error(err)
		}
		var route_choosen = selectInspireSkills(db, msg, inspire_skills)
		console.log(route_choosen)
		msg.route(route_choosen + "_route")
		switch (route_choosen)
		{
			case 'space':
				SpaceImage.run(db, msg)
				break;
			case 'color':
				Color.run(db, msg)
				break;
			case 'decode_message':
				DecodeMessage.run(db, msg)
				break;
			case 'wikipedia':
				Wikipedia.run(msg)
				break;
			case 'flickr':
				Flickr.run(msg)
				break;
			case 'dream':
				Dream.run(msg)
				break;
			case 'write':
				Write.run(msg)
				break;
			case 'headline':
				Headline.run(msg)
				break;
			default:
				SpaceImage.run(db, msg)
		}
	})
}

function selectInspireSkills (db, msg, inspire_skills) {

	var skills = inspire_skills
	if (!(skills instanceof Object))
	{
		// If no data exists, create object to store skills
		skills = default_skills
	}
	// Create array of skills not done
	var skills_choosen = []
	for (var key in skills)
	{
		if (skills.hasOwnProperty(key))
		{
			if (skills[key] == 0)
			{
				skills_choosen.push(key)
			}
		}
	}
	// If all skills have been done, loop through skills and set to undone and push all skills to skills_choosen
	if (skills_choosen.length == 0)
	{
		skills = default_skills
		for (var key in skills)
		{
			if (skills.hasOwnProperty(key))
			{
				skills_choosen.push(key)
			}
		}
	}
	// Select randomly from array of skills not done
	var route_choosen = skills_choosen[Math.floor(Math.random() * skills_choosen.length)]

	skills[route_choosen] = 1
	// Store changes made to the skills done
	db.saveInspireSkills(msg.body.event.user, skills, (err, convo) => {
		if (err) {
			console.log(err)
		}
	})

	return route_choosen
}

module.exports = {
	getInspireRoute: getInspireRoute
}
<|endoftext|>