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