(function(window, undefined) { window.tracking = window.tracking || {}; /** * Inherit the prototype methods from one constructor into another. * * Usage: *
* function ParentClass(a, b) { }
* ParentClass.prototype.foo = function(a) { }
*
* function ChildClass(a, b, c) {
* tracking.base(this, a, b);
* }
* tracking.inherits(ChildClass, ParentClass);
*
* var child = new ChildClass('a', 'b', 'c');
* child.foo();
*
*
* @param {Function} childCtor Child class.
* @param {Function} parentCtor Parent class.
*/
tracking.inherits = function(childCtor, parentCtor) {
function TempCtor() {
}
TempCtor.prototype = parentCtor.prototype;
childCtor.superClass_ = parentCtor.prototype;
childCtor.prototype = new TempCtor();
childCtor.prototype.constructor = childCtor;
/**
* Calls superclass constructor/method.
*
* This function is only available if you use tracking.inherits to express
* inheritance relationships between classes.
*
* @param {!object} me Should always be "this".
* @param {string} methodName The method name to call. Calling superclass
* constructor can be done with the special string 'constructor'.
* @param {...*} var_args The arguments to pass to superclass
* method/constructor.
* @return {*} The return value of the superclass method/constructor.
*/
childCtor.base = function(me, methodName) {
var args = Array.prototype.slice.call(arguments, 2);
return parentCtor.prototype[methodName].apply(me, args);
};
};
/**
* Captures the user camera when tracking a video element and set its source
* to the camera stream.
* @param {HTMLVideoElement} element Canvas element to track.
* @param {object} opt_options Optional configuration to the tracker.
*/
tracking.initUserMedia_ = function(element, opt_options) {
window.navigator.getUserMedia({
video: true,
audio: !!(opt_options && opt_options.audio)
}, function(stream) {
try {
element.src = window.URL.createObjectURL(stream);
} catch (err) {
element.src = stream;
}
}, function() {
throw Error('Cannot capture user camera.');
}
);
};
/**
* Tests whether the object is a dom node.
* @param {object} o Object to be tested.
* @return {boolean} True if the object is a dom node.
*/
tracking.isNode = function(o) {
return o.nodeType || this.isWindow(o);
};
/**
* Tests whether the object is the `window` object.
* @param {object} o Object to be tested.
* @return {boolean} True if the object is the `window` object.
*/
tracking.isWindow = function(o) {
return !!(o && o.alert && o.document);
};
/**
* Selects a dom node from a CSS3 selector using `document.querySelector`.
* @param {string} selector
* @param {object} opt_element The root element for the query. When not
* specified `document` is used as root element.
* @return {HTMLElement} The first dom element that matches to the selector.
* If not found, returns `null`.
*/
tracking.one = function(selector, opt_element) {
if (this.isNode(selector)) {
return selector;
}
return (opt_element || document).querySelector(selector);
};
/**
* Tracks a canvas, image or video element based on the specified `tracker`
* instance. This method extract the pixel information of the input element
* to pass to the `tracker` instance. When tracking a video, the
* `tracker.track(pixels, width, height)` will be in a
* `requestAnimationFrame` loop in order to track all video frames.
*
* Example:
* var tracker = new tracking.ColorTracker();
*
* tracking.track('#video', tracker);
* or
* tracking.track('#video', tracker, { camera: true });
*
* tracker.on('track', function(event) {
* // console.log(event.data[0].x, event.data[0].y)
* });
*
* @param {HTMLElement} element The element to track, canvas, image or
* video.
* @param {tracking.Tracker} tracker The tracker instance used to track the
* element.
* @param {object} opt_options Optional configuration to the tracker.
*/
tracking.track = function(element, tracker, opt_options) {
element = tracking.one(element);
if (!element) {
throw new Error('Element not found, try a different element or selector.');
}
if (!tracker) {
throw new Error('Tracker not specified, try `tracking.track(element, new tracking.FaceTracker())`.');
}
switch (element.nodeName.toLowerCase()) {
case 'canvas':
return this.trackCanvas_(element, tracker, opt_options);
case 'img':
return this.trackImg_(element, tracker, opt_options);
case 'video':
if (opt_options) {
if (opt_options.camera) {
this.initUserMedia_(element, opt_options);
}
}
return this.trackVideo_(element, tracker, opt_options);
default:
throw new Error('Element not supported, try in a canvas, img, or video.');
}
};
/**
* Tracks a canvas element based on the specified `tracker` instance and
* returns a `TrackerTask` for this track.
* @param {HTMLCanvasElement} element Canvas element to track.
* @param {tracking.Tracker} tracker The tracker instance used to track the
* element.
* @param {object} opt_options Optional configuration to the tracker.
* @return {tracking.TrackerTask}
* @private
*/
tracking.trackCanvas_ = function(element, tracker) {
var self = this;
var task = new tracking.TrackerTask(tracker);
task.on('run', function() {
self.trackCanvasInternal_(element, tracker);
});
return task.run();
};
/**
* Tracks a canvas element based on the specified `tracker` instance. This
* method extract the pixel information of the input element to pass to the
* `tracker` instance.
* @param {HTMLCanvasElement} element Canvas element to track.
* @param {tracking.Tracker} tracker The tracker instance used to track the
* element.
* @param {object} opt_options Optional configuration to the tracker.
* @private
*/
tracking.trackCanvasInternal_ = function(element, tracker) {
var width = element.width;
var height = element.height;
var context = element.getContext('2d');
var imageData = context.getImageData(0, 0, width, height);
tracker.track(imageData.data, width, height);
};
/**
* Tracks a image element based on the specified `tracker` instance. This
* method extract the pixel information of the input element to pass to the
* `tracker` instance.
* @param {HTMLImageElement} element Canvas element to track.
* @param {tracking.Tracker} tracker The tracker instance used to track the
* element.
* @param {object} opt_options Optional configuration to the tracker.
* @private
*/
tracking.trackImg_ = function(element, tracker) {
var width = element.width;
var height = element.height;
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var task = new tracking.TrackerTask(tracker);
task.on('run', function() {
tracking.Canvas.loadImage(canvas, element.src, 0, 0, width, height, function() {
tracking.trackCanvasInternal_(canvas, tracker);
});
});
return task.run();
};
/**
* Tracks a video element based on the specified `tracker` instance. This
* method extract the pixel information of the input element to pass to the
* `tracker` instance. The `tracker.track(pixels, width, height)` will be in
* a `requestAnimationFrame` loop in order to track all video frames.
* @param {HTMLVideoElement} element Canvas element to track.
* @param {tracking.Tracker} tracker The tracker instance used to track the
* element.
* @param {object} opt_options Optional configuration to the tracker.
* @private
*/
tracking.trackVideo_ = function(element, tracker) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var width;
var height;
var resizeCanvas_ = function() {
width = element.offsetWidth;
height = element.offsetHeight;
canvas.width = width;
canvas.height = height;
};
resizeCanvas_();
element.addEventListener('resize', resizeCanvas_);
var requestId;
var requestAnimationFrame_ = function() {
requestId = window.requestAnimationFrame(function() {
if (element.readyState === element.HAVE_ENOUGH_DATA) {
try {
// Firefox v~30.0 gets confused with the video readyState firing an
// erroneous HAVE_ENOUGH_DATA just before HAVE_CURRENT_DATA state,
// hence keep trying to read it until resolved.
context.drawImage(element, 0, 0, width, height);
} catch (err) {}
tracking.trackCanvasInternal_(canvas, tracker);
}
requestAnimationFrame_();
});
};
var task = new tracking.TrackerTask(tracker);
task.on('stop', function() {
window.cancelAnimationFrame(requestId);
});
task.on('run', function() {
requestAnimationFrame_();
});
return task.run();
};
// Browser polyfills
//===================
if (!window.URL) {
window.URL = window.URL || window.webkitURL || window.msURL || window.oURL;
}
if (!navigator.getUserMedia) {
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia || navigator.msGetUserMedia;
}
}(window));