summaryrefslogtreecommitdiff
path: root/gstudio/static/gstudio/js/jquery.pandoravideo.js
diff options
context:
space:
mode:
authorAnkita <ankita@ankita-Inspiron-N5010.(none)>2012-07-13 12:31:27 +0530
committerAnkita <ankita@ankita-Inspiron-N5010.(none)>2012-07-13 12:31:27 +0530
commit527ab688a8608ded5fc854cf8641228181efa981 (patch)
tree825f17422ce610922f8f34854c5e54db7a7f35ad /gstudio/static/gstudio/js/jquery.pandoravideo.js
parentef3793b349be5744d6ef98a034f0c009159ed85b (diff)
downloadgnowsys-527ab688a8608ded5fc854cf8641228181efa981.tar.gz
this patch was added for metastudio by BITS Pilani team in 2012.
Diffstat (limited to 'gstudio/static/gstudio/js/jquery.pandoravideo.js')
-rw-r--r--gstudio/static/gstudio/js/jquery.pandoravideo.js381
1 files changed, 381 insertions, 0 deletions
diff --git a/gstudio/static/gstudio/js/jquery.pandoravideo.js b/gstudio/static/gstudio/js/jquery.pandoravideo.js
new file mode 100644
index 0000000..44cf8ae
--- /dev/null
+++ b/gstudio/static/gstudio/js/jquery.pandoravideo.js
@@ -0,0 +1,381 @@
+'use strict';
+
+/*
+ jQuery plugin to embed video from a pan.do/ra instance
+ Usage:
+ html:
+ <div id="video" data-pandora-id="AA"></div>
+ js:
+ $('#video').pandoravideo();
+
+ Options can be passed as data- attributes in the html or in an options object - read code for docs
+*/
+
+(function($) {
+ $.fn.pandoravideo = function(options) {
+
+ //get options, giving preference, in order, to data- attributes defined on the html element, options passed when instantiatiing $(element).pandoravideo(options), defaults.
+ var options = options || {},
+ namespace = options.namespace || "pandora",
+
+ optionTypes = {
+ 'strings': ['api', 'base', 'resolution', 'action', 'id'],
+ 'integers': ['width', 'interval'],
+ 'floats': ['in', 'out'],
+ 'arrays': ['layers', 'keys'],
+ 'booleans': []
+ //'functions': ['callback'] FIXME: lets not.
+ };
+
+
+ return this.each(function() {
+
+ var $this = $(this),
+ dataOptions = $.extend(options, $this.getDataOptions(optionTypes, namespace)),
+ opts = $.extend({
+ 'id': 'ABC', //FIXME: throw an error if id is undefined at this point
+ 'layers': ['transcripts', 'descriptions', 'keywords', 'places'],
+ 'keys': ['layers'], //data keys to fetch from API call. FIXME: add more apt keys
+ 'api': "http://wetube.gnowledge.org/api/", //pandora instance api end-point - see http://pad.ma/api
+ 'in': 0, //in point (float, in seconds) of clip
+ 'out': 0, //out point of clip
+ 'pandora_base': 'wetube.gnowledge.org/', //pandora instance from where to fetch video and image data
+ 'resolution': '480p', //resolution of video (96p, 240p, or 480p)
+ 'width': '640', //display (css) width
+ 'interval': 300, //interval (in ms) to run updatePlayer polling loop
+ 'action': 'get', //action POST param to send to url
+ 'callback': function() { $.noop(); } //function to call after done instantiating pandoraVideo object, called with the object.
+ }, dataOptions),
+ id = opts.id,
+ $loading = $('<div />').addClass("pandoraLoading").text("Loading video...").appendTo($this),
+ sendData = JSON.stringify({'id': id, 'keys': opts.keys});
+
+ //get the pandora id and instantiate a pandoraVideo object with the current element and render it and execute optional callback
+ console.log(opts.api);
+ var deferred = $.post(opts.api, {'action': opts.action, 'data': sendData}, function(response) {
+ $loading.hide().remove();
+ var pandora = new PandoraVideo(id, response.data, $this, opts);
+ pandora.render();
+ opts.callback(pandora);
+ }, "json");
+
+ deferred.error(function(data) {
+ alert("failed to load video data");
+ });
+ });
+ };
+ /*
+ pandoraVideo class
+ Parameters:
+ id: <string> pandora video id
+ data: <object> data for the video object
+ $el: <jQuery element>
+ opts: <object> options object
+
+ */
+ var PandoraVideo = function(id, data, $el, opts) {
+ var that = this;
+ this.id = id;
+ this.data = data;
+ this.$el = $el;
+ this.o = opts;
+ this.annotPoint = -1;
+ this.getVideoURL = function() {
+ var rand = parseInt(Math.random() * 10000);
+ return "http://" + opts.pandora_base + id + "/" + opts.resolution + ".webm";
+ };
+
+ //empties element, appends video widget
+ this.render = function() {
+ var that = this;
+ this.$el.empty();
+ this.$el.append(that.getWidget());
+ };
+
+ /*
+ Get points
+ */
+ var flattenedPoints = [];
+ $.each(that.o.layers, function(i,layerType) {
+ $.each(that.data.layers[layerType], function(j,layer) {
+ flattenedPoints.push(layer['in']);
+ flattenedPoints.push(layer.out);
+ });
+ });
+ this.points = makeArrayUnique(flattenedPoints);
+
+
+ this.crossesPoint = function(newPos) {
+ var that = this;
+ var positions = [that.annotPoint, newPos].sort();
+ return this.points.some(function(point) {
+ return point >= positions[0] && point <= positions[1];
+ });
+ };
+
+ function makeArrayUnique(arr) {
+ var o = {}, i, l = arr.length, r = [];
+ for(i=0; i<l;i+=1) o[arr[i]] = arr[i];
+ for(i in o) r.push(o[i]);
+ return r;
+ };
+
+ /*
+ Use this to set options on the player from the outside.
+ Example:
+ pandoraVideoObject.setOption("width", 250);
+ */
+ this.setOption = function(key, val) {
+ var that = this;
+ this.o[key] = val;
+ if ($.inArray(key, ['resolution']) != -1) {
+ this.destroy();
+ this.render();
+ return this;
+ }
+ if ($.inArray(key, ['in', 'out', 'layers']) != -1) {
+ this.updatePlayer();
+ return this;
+ }
+ if ($.inArray(key, ['width']) != -1) {
+ this.$video.animate({'width': that.o.width + "px"});
+ return this;
+ }
+ console.log("attempt to set invalid option or option which will make no difference to player state");
+ };
+
+
+ /*
+ Returns currently defined option for key specified
+ Parameters:
+ key: <string> eg. 'width'
+ */
+ this.getOption = function(key) {
+ return this.o[key] || "invalid option";
+ };
+
+
+ //returns <jQuery element> widget for this pandoraVideo object
+ this.getWidget = function() {
+ var that = this;
+ var $container = this.$container = $('<div />').addClass("pandoraContainer");
+ var $video = this.$video = $('<video />')
+ .appendTo($container)
+ .attr("src", that.getVideoURL())
+ .attr("controls", "controls")
+ .addClass("pandoraVideo")
+ .animate({'width': that.o.width})
+ .load()
+ .bind("loadedmetadata", function() {
+ this.currentTime = that.o['in'];
+ that.updatePlayer();
+
+ })
+ .bind("play", function() {
+ that.interval = setInterval(function() {
+ that.updatePlayer();
+ }, that.o.interval)
+ })
+ .bind("pause", function() {
+ clearInterval(that.interval);
+ })
+ .bind("ended", function() {
+ clearInterval(that.interval);
+ })
+ .bind("seeked", function() {
+ that.updatePlayer();
+ });
+ var $annotations = this.$annotations = $('<div />')
+ .addClass("pandoraAnnotations")
+ .appendTo($container);
+ return $container;
+ };
+
+ //update annotations, etc. based on video currentTime
+ this.updatePlayer = function() {
+ var that = this;
+ var currentTime = this.$video[0].currentTime;
+
+ //first, handle if video has crossed out-point or is before in-point
+ if (that.o.out != 0) {
+ if (currentTime > (that.o.out + 2)) {
+ that.$video[0].currentTime = that.o.out;
+ that.$video[0].pause();
+ }
+ if (currentTime < (that.o['in'] - 2)) {
+ that.$video[0].currentTime = that.o['in'];
+ that.$video[0].pause();
+ }
+ }
+
+
+ //if layers are the same as last update, return
+ if (!that.crossesPoint(currentTime)) { return false; }
+ //console.log("annot point changed");
+
+ //else, set new annotPoint, construct DOM elements for currently matched layers, etc.
+ that.annotPoint = currentTime;
+ //now get all matching layers at current time code
+ var layerNames = this.o.layers,
+ matchedLayers = {};
+
+ $.each(layerNames, function(i, layerName) {
+
+ matchedLayers[layerName] = that.getLayersAtTimecode(layerName, currentTime)
+ });
+ that.currentLayers = matchedLayers;
+ that.$annotations.empty();
+ for (var layer in matchedLayers) {
+ if (matchedLayers.hasOwnProperty(layer)) {
+ var theseLayers = matchedLayers[layer];
+ //console.log(theseLayers);
+ if (theseLayers.length > 0) {
+ var $annotsForLayer = getElemForLayer(layer, theseLayers);
+ $annotsForLayer.appendTo(that.$annotations);
+ }
+ }
+ }
+ };
+
+ /*
+ Parameters:
+ layerName: <string> eg. 'transcripts'
+ currentTime: <float> in seconds
+ Returns <array> of matched layer objects
+ */
+ this.getLayersAtTimecode = function(layerName, currentTime) {
+ var ret = [];
+ var theseLayers = this.data.layers[layerName];
+ $.each(theseLayers, function(i,layer) {
+ if (layer['in'] < currentTime && layer.out > currentTime) {
+ ret.push(layer);
+ }
+ });
+ return ret;
+ };
+
+ this.destroy = function() {
+ this.$video[0].pause();
+ this.$video.remove();
+ this.$el.empty();
+ };
+ };
+
+ /*
+ Parameters:
+ layerName: <string> eg. 'transcripts'
+ layers: <array> layer objects to render
+ Returns <jQuery element> for an annotation type - i.e. all 'transcripts', or all 'descriptions'
+ */
+ function getElemForLayer(layerName, layers) {
+ if (layers.length === 0) {
+ return $('<div />'); //FIXME
+ }
+ var $elem = $('<div />').addClass("pandoraLayer").addClass(layerName);
+ var title = layerName.substr(0,1).toUpperCase() + layerName.substr(1, layerName.length);
+ var $title = $('<div />').addClass("pandoraLayerTitle").text(title).appendTo($elem);
+
+ $.each(layers, function(i,v) {
+ var $annot = $('<div />').addClass("pandoraAnnot");
+ //TODO: add time-code div
+ var $annotText = $('<div />')
+ .addClass("pandoraAnnotText")
+ .html(v.value)
+ .appendTo($annot);
+ $annot.appendTo($elem);
+ });
+ return $elem;
+ }
+
+ /*
+ Silly function to check if two layer arrays are the same (i.e. to check if matchedLayers have changed)
+ Parameters:
+ layers1: <array> of layer objects
+ layers2: <array> of layer objects to compare with
+ Returns <boolean> true if layer arrays are the same, false if different
+ FIXME: this id concatenation string comparison is a bit weird, but it seemed like a non-expensive simple way to do it
+ */
+ function isSameLayers(layers1, layers2) {
+ var idString1 = '',
+ idString2 = '';
+ for (var l in layers1) {
+ if (layers1.hasOwnProperty(l)) {
+ $.each(layers1[l], function(i,v) {
+ idString1 += v.id;
+ });
+ }
+ }
+ for (var l in layers2) {
+ if (layers2.hasOwnProperty(l)) {
+ $.each(layers2[l], function(i,v) {
+ idString2 += v.id;
+ });
+ }
+ }
+ return idString1 == idString2
+ }
+
+ //Returns <boolean> true or false based on whether the browser can play pandora video
+ //FIXME: actually implement this function
+ function canPlayVideo() {
+ return true;
+ }
+
+
+ /*
+ Get options from data- attributes
+ Parameters:
+ optionTypes: <object>
+ example: {
+ 'strings': ['option1', 'option2', 'option3'],
+ 'integers': ['fooint', 'barint'],
+ 'arrays': ['list1', 'list2'],
+ 'booleans': ['bool1']
+ }
+
+ namespace: <string>
+ example: 'pandora'
+ namespace for data- attributes
+
+ example html:
+ <div id="blah" data-pandora-option1="foobar" data-pandora-fooint="23" data-pandora-list2="apples, oranges" data-pandora-bool1="true">
+
+ usage:
+ var dataOptions = $('#blah').getDataOptions(optionTypes, namespace);
+ */
+ $.fn.getDataOptions = function(optionTypes, namespace) {
+ var $element = this;
+ var prefix = "data-" + namespace + "-",
+ options = {};
+ $.each(optionTypes['strings'], function(i,v) {
+ var attr = prefix + v;
+ options[v] = $element.hasAttr(attr) ? $element.attr(attr) : undefined;
+ });
+ $.each(optionTypes['integers'], function(i,v) {
+ var attr = prefix + v;
+ options[v] = $element.hasAttr(attr) ? parseInt($element.attr(attr)) : undefined;
+ });
+ $.each(optionTypes['floats'], function(i,v) {
+ var attr = prefix + v;
+ options[v] = $element.hasAttr(attr) ? parseFloat($element.attr(attr)) : undefined;
+ });
+ $.each(optionTypes['arrays'], function(i,v) {
+ var attr = prefix + v;
+ options[v] = $element.hasAttr(attr) ? $.map($element.attr(attr).split(","), $.trim) : undefined;
+ });
+ $.each(optionTypes['booleans'], function(i,v) {
+ var attr = prefix + v;
+ options[v] = $element.hasAttr(attr) ? $element.attr(attr) == 'true' : false;
+ });
+ return options;
+ }
+
+ /*
+ FIXME: possibly improve - http://stackoverflow.com/questions/1318076/jquery-hasattr-checking-to-see-if-there-is-an-attribute-on-an-element#1318091
+ */
+ $.fn.hasAttr = function(attr) {
+ return this.attr(attr) != undefined;
+ };
+
+})(jQuery);
+