mirror of
https://github.com/google/pebble.git
synced 2025-03-15 16:51:21 +00:00
443 lines
13 KiB
JavaScript
443 lines
13 KiB
JavaScript
|
/*
|
||
|
* Vide - v0.3.0
|
||
|
* Easy as hell jQuery plugin for video backgrounds.
|
||
|
* http://vodkabears.github.io/vide/
|
||
|
*
|
||
|
* Made by Ilya Makarov
|
||
|
* Under MIT License
|
||
|
*/
|
||
|
!(function($, window, document, navigator) {
|
||
|
"use strict";
|
||
|
|
||
|
/**
|
||
|
* Vide settings
|
||
|
* @private
|
||
|
*/
|
||
|
var pluginName = "vide",
|
||
|
defaults = {
|
||
|
volume: 1,
|
||
|
playbackRate: 1,
|
||
|
muted: true,
|
||
|
loop: true,
|
||
|
autoplay: true,
|
||
|
position: "50% 50%",
|
||
|
posterType: "detect",
|
||
|
resizing: true
|
||
|
},
|
||
|
|
||
|
// is iOs?
|
||
|
isIOS = /iPad|iPhone|iPod/i.test(navigator.userAgent),
|
||
|
|
||
|
// is Android?
|
||
|
isAndroid = /Android/i.test(navigator.userAgent);
|
||
|
|
||
|
/**
|
||
|
* Parse string with options
|
||
|
* @param {String} str
|
||
|
* @returns {Object|String}
|
||
|
* @private
|
||
|
*/
|
||
|
function parseOptions(str) {
|
||
|
var obj = {},
|
||
|
delimiterIndex,
|
||
|
option,
|
||
|
prop, val,
|
||
|
arr, len,
|
||
|
i;
|
||
|
|
||
|
// remove spaces around delimiters and split
|
||
|
arr = str.replace(/\s*:\s*/g, ":").replace(/\s*,\s*/g, ",").split(",");
|
||
|
|
||
|
// parse string
|
||
|
for (i = 0, len = arr.length; i < len; i++) {
|
||
|
option = arr[i];
|
||
|
|
||
|
// Ignore urls and string without colon delimiters
|
||
|
if (option.search(/^(http|https|ftp):\/\//) !== -1 ||
|
||
|
option.search(":") === -1)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
delimiterIndex = option.indexOf(":");
|
||
|
prop = option.substring(0, delimiterIndex);
|
||
|
val = option.substring(delimiterIndex + 1);
|
||
|
|
||
|
// if val is an empty string, make it undefined
|
||
|
if (!val) {
|
||
|
val = undefined;
|
||
|
}
|
||
|
|
||
|
// convert string value if it is like a boolean
|
||
|
if (typeof val === "string") {
|
||
|
val = val === "true" || (val === "false" ? false : val);
|
||
|
}
|
||
|
|
||
|
// convert string value if it is like a number
|
||
|
if (typeof val === "string") {
|
||
|
val = !isNaN(val) ? +val : val;
|
||
|
}
|
||
|
|
||
|
obj[prop] = val;
|
||
|
}
|
||
|
|
||
|
// if nothing is parsed
|
||
|
if (prop == null && val == null) {
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parse position option
|
||
|
* @param {String} str
|
||
|
* @returns {Object}
|
||
|
* @private
|
||
|
*/
|
||
|
function parsePosition(str) {
|
||
|
str = "" + str;
|
||
|
|
||
|
// default value is a center
|
||
|
var args = str.split(/\s+/),
|
||
|
x = "50%", y = "50%",
|
||
|
len, arg,
|
||
|
i;
|
||
|
|
||
|
for (i = 0, len = args.length; i < len; i++) {
|
||
|
arg = args[i];
|
||
|
|
||
|
// convert values
|
||
|
if (arg === "left") {
|
||
|
x = "0%";
|
||
|
} else if (arg === "right") {
|
||
|
x = "100%";
|
||
|
} else if (arg === "top") {
|
||
|
y = "0%";
|
||
|
} else if (arg === "bottom") {
|
||
|
y = "100%";
|
||
|
} else if (arg === "center") {
|
||
|
if (i === 0) {
|
||
|
x = "50%";
|
||
|
} else {
|
||
|
y = "50%";
|
||
|
}
|
||
|
} else {
|
||
|
if (i === 0) {
|
||
|
x = arg;
|
||
|
} else {
|
||
|
y = arg;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return { x: x, y: y };
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Search poster
|
||
|
* @param {String} path
|
||
|
* @param {Function} callback
|
||
|
* @private
|
||
|
*/
|
||
|
function findPoster(path, callback) {
|
||
|
var onLoad = function() {
|
||
|
callback(this.src);
|
||
|
};
|
||
|
|
||
|
$("<img src='" + path + ".gif'>").load(onLoad);
|
||
|
$("<img src='" + path + ".jpg'>").load(onLoad);
|
||
|
$("<img src='" + path + ".jpeg'>").load(onLoad);
|
||
|
$("<img src='" + path + ".png'>").load(onLoad);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Vide constructor
|
||
|
* @param {HTMLElement} element
|
||
|
* @param {Object|String} path
|
||
|
* @param {Object|String} options
|
||
|
* @constructor
|
||
|
*/
|
||
|
function Vide(element, path, options) {
|
||
|
this.$element = $(element);
|
||
|
|
||
|
// parse path
|
||
|
if (typeof path === "string") {
|
||
|
path = parseOptions(path);
|
||
|
}
|
||
|
|
||
|
// parse options
|
||
|
if (!options) {
|
||
|
options = {};
|
||
|
} else if (typeof options === "string") {
|
||
|
options = parseOptions(options);
|
||
|
}
|
||
|
|
||
|
// remove extension
|
||
|
if (typeof path === "string") {
|
||
|
path = path.replace(/\.\w*$/, "");
|
||
|
} else if (typeof path === "object") {
|
||
|
for (var i in path) {
|
||
|
if (path.hasOwnProperty(i)) {
|
||
|
path[i] = path[i].replace(/\.\w*$/, "");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.settings = $.extend({}, defaults, options);
|
||
|
this.path = path;
|
||
|
|
||
|
this.init();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialization
|
||
|
* @public
|
||
|
*/
|
||
|
Vide.prototype.init = function() {
|
||
|
var vide = this,
|
||
|
position = parsePosition(vide.settings.position),
|
||
|
sources,
|
||
|
poster;
|
||
|
|
||
|
// Set video wrapper styles
|
||
|
vide.$wrapper = $("<div>").css({
|
||
|
"position": "absolute",
|
||
|
"z-index": -1,
|
||
|
"top": 0,
|
||
|
"left": 0,
|
||
|
"bottom": 0,
|
||
|
"right": 0,
|
||
|
"overflow": "hidden",
|
||
|
"-webkit-background-size": "cover",
|
||
|
"-moz-background-size": "cover",
|
||
|
"-o-background-size": "cover",
|
||
|
"background-size": "cover",
|
||
|
"background-repeat": "no-repeat",
|
||
|
"background-position": position.x + " " + position.y
|
||
|
});
|
||
|
|
||
|
// Get poster path
|
||
|
poster = vide.path;
|
||
|
if (typeof vide.path === "object") {
|
||
|
if (vide.path.poster) {
|
||
|
poster = vide.path.poster;
|
||
|
} else {
|
||
|
if (vide.path.mp4) {
|
||
|
poster = vide.path.mp4;
|
||
|
} else if (vide.path.webm) {
|
||
|
poster = vide.path.webm;
|
||
|
} else if (vide.path.ogv) {
|
||
|
poster = vide.path.ogv;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Set video poster
|
||
|
if (vide.settings.posterType === "detect") {
|
||
|
findPoster(poster, function(url) {
|
||
|
vide.$wrapper.css("background-image", "url(" + url + ")");
|
||
|
});
|
||
|
} else if (vide.settings.posterType !== "none") {
|
||
|
vide.$wrapper
|
||
|
.css("background-image", "url(" + poster + "." + vide.settings.posterType + ")");
|
||
|
}
|
||
|
|
||
|
// if parent element has a static position, make it relative
|
||
|
if (vide.$element.css("position") === "static") {
|
||
|
vide.$element.css("position", "relative");
|
||
|
}
|
||
|
|
||
|
vide.$element.prepend(vide.$wrapper);
|
||
|
|
||
|
if (!isIOS && !isAndroid) {
|
||
|
sources = "";
|
||
|
|
||
|
if (typeof vide.path === "object") {
|
||
|
if (vide.path.mp4) {
|
||
|
sources += "<source src='" + vide.path.mp4 + ".mp4' type='video/mp4'>";
|
||
|
}
|
||
|
if (vide.path.webm) {
|
||
|
sources += "<source src='" + vide.path.webm + ".webm' type='video/webm'>";
|
||
|
}
|
||
|
if (vide.path.ogv) {
|
||
|
sources += "<source src='" + vide.path.ogv + ".ogv' type='video/ogv'>";
|
||
|
}
|
||
|
|
||
|
vide.$video = $("<video>" + sources + "</video>");
|
||
|
} else {
|
||
|
vide.$video = $("<video>" +
|
||
|
"<source src='" + vide.path + ".mp4' type='video/mp4'>" +
|
||
|
"<source src='" + vide.path + ".webm' type='video/webm'>" +
|
||
|
"<source src='" + vide.path + ".ogv' type='video/ogg'>" +
|
||
|
"</video>");
|
||
|
}
|
||
|
|
||
|
// Disable visibility, while loading
|
||
|
vide.$video.css("visibility", "hidden");
|
||
|
|
||
|
// Set video properties
|
||
|
vide.$video.prop({
|
||
|
autoplay: vide.settings.autoplay,
|
||
|
loop: vide.settings.loop,
|
||
|
volume: vide.settings.volume,
|
||
|
muted: vide.settings.muted,
|
||
|
playbackRate: vide.settings.playbackRate
|
||
|
});
|
||
|
|
||
|
// Append video
|
||
|
vide.$wrapper.append(vide.$video);
|
||
|
|
||
|
// Video alignment
|
||
|
vide.$video.css({
|
||
|
"margin": "auto",
|
||
|
"position": "absolute",
|
||
|
"z-index": -1,
|
||
|
"top": position.y,
|
||
|
"left": position.x,
|
||
|
"-webkit-transform": "translate(-" + position.x + ", -" + position.y + ")",
|
||
|
"-ms-transform": "translate(-" + position.x + ", -" + position.y + ")",
|
||
|
"transform": "translate(-" + position.x + ", -" + position.y + ")"
|
||
|
});
|
||
|
|
||
|
// resize video, when it's loaded
|
||
|
vide.$video.bind("loadedmetadata." + pluginName, function() {
|
||
|
vide.$video.css("visibility", "visible");
|
||
|
vide.resize();
|
||
|
vide.$wrapper.css("background-image", "none");
|
||
|
});
|
||
|
|
||
|
// resize event is available only for 'window',
|
||
|
// use another code solutions to detect DOM elements resizing
|
||
|
vide.$element.bind("resize." + pluginName, function() {
|
||
|
if (vide.settings.resizing) {
|
||
|
vide.resize();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Get video element of the background
|
||
|
* @returns {HTMLVideoElement|null}
|
||
|
* @public
|
||
|
*/
|
||
|
Vide.prototype.getVideoObject = function() {
|
||
|
return this.$video ? this.$video[0] : null;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Resize video background
|
||
|
* @public
|
||
|
*/
|
||
|
Vide.prototype.resize = function() {
|
||
|
if (!this.$video) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var
|
||
|
// get native video size
|
||
|
videoHeight = this.$video[0].videoHeight,
|
||
|
videoWidth = this.$video[0].videoWidth,
|
||
|
|
||
|
// get wrapper size
|
||
|
wrapperHeight = this.$wrapper.height(),
|
||
|
wrapperWidth = this.$wrapper.width();
|
||
|
|
||
|
if (wrapperWidth / videoWidth > wrapperHeight / videoHeight) {
|
||
|
this.$video.css({
|
||
|
"width": wrapperWidth + 2,
|
||
|
|
||
|
// +2 pixels to prevent empty space after transformation
|
||
|
"height": "auto"
|
||
|
});
|
||
|
} else {
|
||
|
this.$video.css({
|
||
|
"width": "auto",
|
||
|
|
||
|
// +2 pixels to prevent empty space after transformation
|
||
|
"height": wrapperHeight + 2
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Destroy video background
|
||
|
* @public
|
||
|
*/
|
||
|
Vide.prototype.destroy = function() {
|
||
|
this.$element.unbind(pluginName);
|
||
|
|
||
|
if (this.$video) {
|
||
|
this.$video.unbind(pluginName);
|
||
|
}
|
||
|
|
||
|
delete $[pluginName].lookup[this.index];
|
||
|
this.$element.removeData(pluginName);
|
||
|
this.$wrapper.remove();
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Special plugin object for instances.
|
||
|
* @type {Object}
|
||
|
* @public
|
||
|
*/
|
||
|
$[pluginName] = {
|
||
|
lookup: []
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Plugin constructor
|
||
|
* @param {Object|String} path
|
||
|
* @param {Object|String} options
|
||
|
* @returns {JQuery}
|
||
|
* @constructor
|
||
|
*/
|
||
|
$.fn[pluginName] = function(path, options) {
|
||
|
var instance;
|
||
|
|
||
|
this.each(function() {
|
||
|
instance = $.data(this, pluginName);
|
||
|
|
||
|
if (instance) {
|
||
|
|
||
|
// destroy plugin instance if exists
|
||
|
instance.destroy();
|
||
|
}
|
||
|
|
||
|
// create plugin instance
|
||
|
instance = new Vide(this, path, options);
|
||
|
instance.index = $[pluginName].lookup.push(instance) - 1;
|
||
|
$.data(this, pluginName, instance);
|
||
|
});
|
||
|
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
$(document).ready(function() {
|
||
|
|
||
|
// window resize event listener
|
||
|
$(window).bind("resize." + pluginName, function() {
|
||
|
for (var len = $[pluginName].lookup.length, i = 0, instance; i < len; i++) {
|
||
|
instance = $[pluginName].lookup[i];
|
||
|
|
||
|
if (instance && instance.settings.resizing) {
|
||
|
instance.resize();
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Auto initialization.
|
||
|
// Add 'data-vide-bg' attribute with a path to the video without extension.
|
||
|
// Also you can pass options throw the 'data-vide-options' attribute.
|
||
|
// 'data-vide-options' must be like "muted: false, volume: 0.5".
|
||
|
$(document).find("[data-" + pluginName + "-bg]").each(function(i, element) {
|
||
|
var $element = $(element),
|
||
|
options = $element.data(pluginName + "-options"),
|
||
|
path = $element.data(pluginName + "-bg");
|
||
|
|
||
|
$element[pluginName](path, options);
|
||
|
});
|
||
|
});
|
||
|
})(window.jQuery, window, document, navigator);
|