Sanj
12 years ago
2 changed files with 3 additions and 329 deletions
@ -1,326 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
(function($) { |
|||
$.fn.padmavideo = function(options) { |
|||
|
|||
//get options, giving preference, in order, to data- attributes defined on the html element, options passed when instantiatiing $(element).padmavideo(options), defaults.
|
|||
var options = options || {}, |
|||
namespace = options.namespace || "padma", |
|||
$this = this; |
|||
|
|||
|
|||
var optionTypes = { |
|||
'strings': ['api', 'base', 'resolution', 'action', 'id'], |
|||
'integers': ['in', 'out', 'width', 'interval'], |
|||
'arrays': ['layers', 'keys'], |
|||
'booleans': [] |
|||
//'functions': ['callback'] FIXME: lets not.
|
|||
}; |
|||
|
|||
var dataOptions = $.extend(options, $this.getDataOptions(optionTypes, namespace)); |
|||
|
|||
var opts = $.extend({ |
|||
'id': 'ABC', //FIXME: throw an error if id is undefined at this point
|
|||
'layers': ['transcripts', 'descriptions', 'keywords', 'places'], |
|||
'keys': ['layers'], //FIXME: add more apt keys
|
|||
'api': "http://pad.ma/api/", |
|||
'in': 0, |
|||
'out': 0, |
|||
'padma_base': 'http://pad.ma/', |
|||
'resolution': '480p', |
|||
'width': '640', |
|||
'interval': 400, |
|||
'action': 'get', //action POST param to send to url
|
|||
'callback': function() { $.noop(); } //function to call after done instantiating PadmaVideo object, called with the object.
|
|||
}, dataOptions); |
|||
|
|||
//get the padma id and instantiate a PadmaVideo object with the current element and render it and execute optional callback
|
|||
var id = opts.id, |
|||
$loading = $('<div />').addClass("padmaLoading").text("Loading video...").appendTo($this), |
|||
sendData = JSON.stringify({'id': id, 'keys': opts.keys}); |
|||
|
|||
var deferred = $.post(opts.api, {'action': opts.action, 'data': sendData}, function(response) { |
|||
$loading.hide().remove(); |
|||
var padma = new PadmaVideo(id, response.data, $this, opts); |
|||
padma.render(); |
|||
opts.callback(padma); |
|||
}, "json"); |
|||
|
|||
deferred.error(function(data) { |
|||
alert("failed to load video data"); |
|||
}); |
|||
}; |
|||
|
|||
/* |
|||
PadmaVideo class |
|||
Parameters: |
|||
id: <string> pandora video id |
|||
data: <object> data for the video object |
|||
$el: <jQuery element> |
|||
opts: <object> options object |
|||
|
|||
*/ |
|||
var PadmaVideo = function(id, data, $el, opts) { |
|||
this.id = id; |
|||
this.data = data; |
|||
this.$el = $el; |
|||
this.o = opts; |
|||
this.getVideoURL = function() { |
|||
return opts.padma_base + id + "/" + opts.resolution + ".webm"; |
|||
}; |
|||
//empties element, appends video widget
|
|||
this.render = function() { |
|||
var that = this; |
|||
this.$el.empty(); |
|||
this.$el.append(that.getWidget()); |
|||
}; |
|||
|
|||
/* |
|||
Use this to set options on the player from the outside. |
|||
Example: |
|||
padmaVideoObject.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 PadmaVideo object
|
|||
this.getWidget = function() { |
|||
var that = this; |
|||
var $container = this.$container = $('<div />').addClass("padmaContainer"); |
|||
var $video = this.$video = $('<video />') |
|||
.appendTo($container) |
|||
.attr("src", that.getVideoURL()) |
|||
.attr("controls", "controls") |
|||
.addClass("padmaVideo") |
|||
.animate({'width': that.o.width}) |
|||
.load() |
|||
.bind("loadedmetadata", function() { |
|||
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("padmaAnnotations") |
|||
.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) { |
|||
that.$video[0].currentTime = that.o.out; |
|||
that.$video[0].pause(); |
|||
} |
|||
if (currentTime <= that.o['in']) { |
|||
that.$video[0].currentTime = that.o['in']; |
|||
that.$video[0].pause(); |
|||
} |
|||
} |
|||
|
|||
//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) |
|||
}); |
|||
|
|||
//if layers are the same as last update, return
|
|||
if (isSameLayers(matchedLayers, that.currentLayers)) return false; |
|||
|
|||
//else, construct DOM elements for currently matched layers, etc.
|
|||
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("padmaLayer").addClass(layerName); |
|||
var title = layerName.substr(0,1).toUpperCase() + layerName.substr(1, layerName.length); |
|||
var $title = $('<div />').addClass("padmaLayerTitle").text(title).appendTo($elem); |
|||
|
|||
$.each(layers, function(i,v) { |
|||
var $annot = $('<div />').addClass("padmaAnnot"); |
|||
//TODO: add time-code div
|
|||
var $annotText = $('<div />') |
|||
.addClass("padmaAnnotText") |
|||
.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 padma 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: 'padma' |
|||
namespace for data- attributes |
|||
|
|||
example html: |
|||
<div id="blah" data-padma-option1="foobar" data-padma-fooint="23" data-padma-list2="apples, oranges" data-padma-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['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); |
Loading…
Reference in new issue