1/* 2 * Vide - v0.5.1 3 * Easy as hell jQuery plugin for video backgrounds. 4 * http://vodkabears.github.io/vide/ 5 * 6 * Made by Ilya Makarov 7 * Under MIT License 8 */ 9!(function(root, factory) { 10 if (typeof define === 'function' && define.amd) { 11 define(['jquery'], factory); 12 } else if (typeof exports === 'object') { 13 factory(require('jquery')); 14 } else { 15 factory(root.jQuery); 16 } 17})(this, function($) { 18 19 'use strict'; 20 21 /** 22 * Name of the plugin 23 * @private 24 * @const 25 * @type {String} 26 */ 27 var PLUGIN_NAME = 'vide'; 28 29 /** 30 * Default settings 31 * @private 32 * @const 33 * @type {Object} 34 */ 35 var DEFAULTS = { 36 volume: 1, 37 playbackRate: 1, 38 muted: true, 39 loop: true, 40 autoplay: true, 41 position: '50% 50%', 42 posterType: 'detect', 43 resizing: true, 44 bgColor: 'transparent', 45 className: '' 46 }; 47 48 /** 49 * Not implemented error message 50 * @private 51 * @const 52 * @type {String} 53 */ 54 var NOT_IMPLEMENTED_MSG = 'Not implemented'; 55 56 /** 57 * Parse a string with options 58 * @private 59 * @param {String} str 60 * @returns {Object|String} 61 */ 62 function parseOptions(str) { 63 var obj = {}; 64 var delimiterIndex; 65 var option; 66 var prop; 67 var val; 68 var arr; 69 var len; 70 var i; 71 72 // Remove spaces around delimiters and split 73 arr = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ',').split(','); 74 75 // Parse a string 76 for (i = 0, len = arr.length; i < len; i++) { 77 option = arr[i]; 78 79 // Ignore urls and a string without colon delimiters 80 if ( 81 option.search(/^(http|https|ftp):\/\//) !== -1 || 82 option.search(':') === -1 83 ) { 84 break; 85 } 86 87 delimiterIndex = option.indexOf(':'); 88 prop = option.substring(0, delimiterIndex); 89 val = option.substring(delimiterIndex + 1); 90 91 // If val is an empty string, make it undefined 92 if (!val) { 93 val = undefined; 94 } 95 96 // Convert a string value if it is like a boolean 97 if (typeof val === 'string') { 98 val = val === 'true' || (val === 'false' ? false : val); 99 } 100 101 // Convert a string value if it is like a number 102 if (typeof val === 'string') { 103 val = !isNaN(val) ? +val : val; 104 } 105 106 obj[prop] = val; 107 } 108 109 // If nothing is parsed 110 if (prop == null && val == null) { 111 return str; 112 } 113 114 return obj; 115 } 116 117 /** 118 * Parse a position option 119 * @private 120 * @param {String} str 121 * @returns {Object} 122 */ 123 function parsePosition(str) { 124 str = '' + str; 125 126 // Default value is a center 127 var args = str.split(/\s+/); 128 var x = '50%'; 129 var y = '50%'; 130 var len; 131 var arg; 132 var i; 133 134 for (i = 0, len = args.length; i < len; i++) { 135 arg = args[i]; 136 137 // Convert values 138 if (arg === 'left') { 139 x = '0%'; 140 } else if (arg === 'right') { 141 x = '100%'; 142 } else if (arg === 'top') { 143 y = '0%'; 144 } else if (arg === 'bottom') { 145 y = '100%'; 146 } else if (arg === 'center') { 147 if (i === 0) { 148 x = '50%'; 149 } else { 150 y = '50%'; 151 } 152 } else { 153 if (i === 0) { 154 x = arg; 155 } else { 156 y = arg; 157 } 158 } 159 } 160 161 return { x: x, y: y }; 162 } 163 164 /** 165 * Search a poster 166 * @private 167 * @param {String} path 168 * @param {Function} callback 169 */ 170 function findPoster(path, callback) { 171 var onLoad = function() { 172 callback(this.src); 173 }; 174 175 } 176 177 /** 178 * Vide constructor 179 * @param {HTMLElement} element 180 * @param {Object|String} path 181 * @param {Object|String} options 182 * @constructor 183 */ 184 function Vide(element, path, options) { 185 this.$element = $(element); 186 187 // Parse path 188 if (typeof path === 'string') { 189 path = parseOptions(path); 190 } 191 192 // Parse options 193 if (!options) { 194 options = {}; 195 } else if (typeof options === 'string') { 196 options = parseOptions(options); 197 } 198 199 // Remove an extension 200 if (typeof path === 'string') { 201 path = path.replace(/\.\w*$/, ''); 202 } else if (typeof path === 'object') { 203 for (var i in path) { 204 if (path.hasOwnProperty(i)) { 205 path[i] = path[i].replace(/\.\w*$/, ''); 206 } 207 } 208 } 209 210 this.settings = $.extend({}, DEFAULTS, options); 211 this.path = path; 212 213 // https://github.com/VodkaBears/Vide/issues/110 214 try { 215 this.init(); 216 } catch (e) {
217 if (e.message !== NOT_IMPLEMENTED_MSG) { 218 throw e; 219 } 220 } 221 } 222 223 /** 224 * Initialization 225 * @public 226 */ 227 Vide.prototype.init = function() { 228 var vide = this; 229 var path = vide.path; 230 var poster = path; 231 var sources = ''; 232 var $element = vide.$element; 233 var settings = vide.settings; 234 var position = parsePosition(settings.position); 235 var posterType = settings.posterType; 236 var $video; 237 var $wrapper; 238 239 // Set styles of a video wrapper 240 $wrapper = vide.$wrapper = $('<div>') 241 .addClass(settings.className) 242 .css({ 243 position: 'absolute', 244 'z-index': -1, 245 top: 0, 246 left: 0, 247 bottom: 0, 248 right: 0, 249 overflow: 'hidden', 250 '-webkit-background-size': 'cover', 251 '-moz-background-size': 'cover', 252 '-o-background-size': 'cover', 253 'background-size': 'cover', 254 'background-color': settings.bgColor, 255 'background-repeat': 'no-repeat', 256 'background-position': position.x + ' ' + position.y 257 }); 258 259 // Get a poster path 260 if (typeof path === 'object') { 261 if (path.poster) { 262 poster = path.poster; 263 } else { 264 if (path.mp4) { 265 poster = path.mp4; 266 } else if (path.webm) { 267 poster = path.webm; 268 } else if (path.ogv) { 269 poster = path.ogv; 270 } 271 } 272 } 273 274 // Set a video poster 275 if (posterType === 'detect') { 276 findPoster(poster, function(url) { 277 $wrapper.css('background-image', 'url(' + url + ')'); 278 }); 279 } else if (posterType !== 'none') { 280 $wrapper.css('background-image', 'url(' + poster + '.' + posterType + ')'); 281 } 282 283 // If a parent element has a static position, make it relative 284 if ($element.css('position') === 'static') { 285 $element.css('position', 'static'); 286 } 287 288 $element.prepend($wrapper); 289 290 if (typeof path === 'object') { 291 if (path.mp4) { 292 sources += '<source src="' + path.mp4 + '.mp4" type="video/mp4">'; 293 } 294 295 if (path.webm) { 296 sources += '<source src="' + path.webm + '.webm" type="video/webm">'; 297 } 298 299 if (path.ogv) { 300 sources += '<source src="' + path.ogv + '.ogv" type="video/ogg">'; 301 } 302 303 $video = vide.$video = $('<video>' + sources + '</video>'); 304 } else { 305 $video = vide.$video = $('<video>' + 306 '<source src="' + path + '.mp4" type="video/mp4">' + 307 '<source src="' + path + '.webm" type="video/webm">' + 308 '<source src="' + path + '.ogv" type="video/ogg">' + 309 '</video>'); 310 } 311 312 // https://github.com/VodkaBears/Vide/issues/110 313 try { 314 $video 315 316 // Set video properties 317 .prop({ 318 autoplay: settings.autoplay, 319 loop: settings.loop, 320 volume: settings.volume, 321 muted: settings.muted, 322 defaultMuted: settings.muted, 323 playbackRate: settings.playbackRate, 324 defaultPlaybackRate: settings.playbackRate 325 }); 326 } catch (e) { 327 throw new Error(NOT_IMPLEMENTED_MSG); 328 } 329 330 // Video alignment 331 $video.css({ 332 margin: 'auto', 333 position: 'absolute', 334 'z-index': -1, 335 top: position.y, 336 left: position.x, 337 '-webkit-transform': 'translate(-' + position.x + ', -' + position.y + ')', 338 '-ms-transform': 'translate(-' + position.x + ', -' + position.y + ')', 339 '-moz-transform': 'translate(-' + position.x + ', -' + position.y + ')', 340 transform: 'translate(-' + position.x + ', -' + position.y + ')', 341 342 // Disable visibility, while loading 343 visibility: 'hidden', 344 opacity: 0 345 }) 346 347 // Resize a video, when it's loaded 348 .one('canplaythrough.' + PLUGIN_NAME, function() { 349 vide.resize(); 350 }) 351 352 // Make it visible, when it's already playing 353 .one('playing.' + PLUGIN_NAME, function() { 354 $video.css({ 355 visibility: 'visible', 356 opacity: 1 357 }); 358 $wrapper.css('background-image', 'none'); 359 });
360 361 // Resize event is available only for 'window' 362 // Use another code solutions to detect DOM elements resizing 363 $element.on('resize.' + PLUGIN_NAME, function() { 364 if (settings.resizing) { 365 vide.resize(); 366 } 367 }); 368 369 // Append a video 370 $wrapper.append($video); 371 }; 372 373 /** 374 * Get a video element 375 * @public 376 * @returns {HTMLVideoElement} 377 */ 378 Vide.prototype.getVideoObject = function() { 379 return this.$video[0]; 380 }; 381 382 /** 383 * Resize a video background 384 * @public 385 */ 386 Vide.prototype.resize = function() { 387 if (!this.$video) { 388 return; 389 } 390 391 var $wrapper = this.$wrapper; 392 var $video = this.$video; 393 var video = $video[0]; 394 395 // Get a native video size 396 var videoHeight = video.videoHeight; 397 var videoWidth = video.videoWidth; 398 399 // Get a wrapper size 400 var wrapperHeight = $wrapper.height(); 401 var wrapperWidth = $wrapper.width(); 402 403 if (wrapperWidth / videoWidth > wrapperHeight / videoHeight) { 404 $video.css({ 405 406 // +2 pixels to prevent an empty space after transformation
407 width: wrapperWidth + 2, 408 height: 'auto' 409 }); 410 } else { 411 $video.css({ 412 width: 'auto', 413 414 // +2 pixels to prevent an empty space after transformation 415 height: wrapperHeight + 2 416 }); 417 } 418 }; 419 420 /** 421 * Destroy a video background 422 * @public 423 */ 424 Vide.prototype.destroy = function() { 425 delete $[PLUGIN_NAME].lookup[this.index]; 426 this.$video && this.$video.off(PLUGIN_NAME); 427 this.$element.off(PLUGIN_NAME).removeData(PLUGIN_NAME); 428 this.$wrapper.remove(); 429 }; 430 431 /** 432 * Special plugin object for instances. 433 * @public 434 * @type {Object} 435 */ 436 $[PLUGIN_NAME] = { 437 lookup: [] 438 }; 439 440 /** 441 * Plugin constructor 442 * @param {Object|String} path 443 * @param {Object|String} options 444 * @returns {JQuery} 445 * @constructor 446 */ 447 $.fn[PLUGIN_NAME] = function(path, options) { 448 var instance; 449 450 this.each(function() { 451 instance = $.data(this, PLUGIN_NAME); 452 453 // Destroy the plugin instance if exists 454 instance && instance.destroy(); 455 456 // Create the plugin instance 457 instance = new Vide(this, path, options); 458 instance.index = $[PLUGIN_NAME].lookup.push(instance) - 1; 459 $.data(this, PLUGIN_NAME, instance); 460 }); 461 462 return this; 463 }; 464 465 $(document).ready(function() { 466 var $window = $(window); 467 468 // Window resize event listener 469 $window.on('resize.' + PLUGIN_NAME, function() { 470 for (var len = $[PLUGIN_NAME].lookup.length, i = 0, instance; i < len; i++) { 471 instance = $[PLUGIN_NAME].lookup[i]; 472 473 if (instance && instance.settings.resizing) { 474 instance.resize(); 475 } 476 } 477 }); 478 479 // https://github.com/VodkaBears/Vide/issues/68 480 $window.on('unload.' + PLUGIN_NAME, function() { 481 return false; 482 }); 483 484 // Auto initialization 485 // Add 'data-vide-bg' attribute with a path to the video without extension 486 // Also you can pass options throw the 'data-vide-options' attribute 487 // 'data-vide-options' must be like 'muted: false, volume: 0.5' 488 $(document).find('[data-' + PLUGIN_NAME + '-bg]').each(function(i, element) { 489 var $element = $(element); 490 var options = $element.data(PLUGIN_NAME + '-options'); 491 var path = $element.data(PLUGIN_NAME + '-bg'); 492 493 $element[PLUGIN_NAME](path, options); 494 }); 495 }); 496 497});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.