").addClass(l.className).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-color":l.bgColor,"background-repeat":"no-repeat","background-position":m.x+" "+m.y}),"object"==typeof g&&(g.poster?i=g.poster:g.mp4?i=g.mp4:g.webm?i=g.webm:g.ogv&&(i=g.ogv)),"detect"===n?d(i,function(a){e.css("background-image","url("+a+")")}):"none"!==n&&e.css("background-image","url("+i+"."+n+")"),"static"===k.css("position")&&k.css("position","relative"),k.prepend(e),"object"==typeof g?(g.mp4&&(j+=''),g.webm&&(j+=''),g.ogv&&(j+=''),b=f.$video=a("")):b=f.$video=a('');try{b.prop({autoplay:l.autoplay,loop:l.loop,volume:l.volume,muted:l.muted,defaultMuted:l.muted,playbackRate:l.playbackRate,defaultPlaybackRate:l.playbackRate})}catch(o){throw new Error(h)}b.css({margin:"auto",position:"absolute","z-index":-1,top:m.y,left:m.x,"-webkit-transform":"translate(-"+m.x+", -"+m.y+")","-ms-transform":"translate(-"+m.x+", -"+m.y+")","-moz-transform":"translate(-"+m.x+", -"+m.y+")",transform:"translate(-"+m.x+", -"+m.y+")",visibility:"hidden",opacity:0}).one("canplaythrough.vide",function(){f.resize()}).one("playing.vide",function(){b.css({visibility:"visible",opacity:1}),e.css("background-image","none")}),k.on("resize.vide",function(){l.resizing&&f.resize()}),e.append(b)},e.prototype.getVideoObject=function(){return this.$video[0]},e.prototype.resize=function(){if(this.$video){var a=this.$wrapper,b=this.$video,c=b[0],d=c.videoHeight,e=c.videoWidth,f=a.height(),g=a.width();g/e>f/d?b.css({width:g+2,height:"auto"}):b.css({width:"auto",height:f+2})}},e.prototype.destroy=function(){delete a[f].lookup[this.index],this.$video&&this.$video.off(f),this.$element.off(f).removeData(f),this.$wrapper.remove()},a[f]={lookup:[]},a.fn[f]=function(b,c){var d;return this.each(function(){d=a.data(this,f),d&&d.destroy(),d=new e(this,b,c),d.index=a[f].lookup.push(d)-1,a.data(this,f,d)}),this},a(document).ready(function(){var b=a(window);b.on("resize.vide",function(){for(var b,c=a[f].lookup.length,d=0;d`
24 |
25 | Prepare your video in several formats like '.webm', '.mp4' for cross browser compatibility, also add a poster with `.jpg`, `.png` or `.gif` extension:
26 | ```
27 | path/
28 | ├── to/
29 | │ ├── video.mp4
30 | │ ├── video.ogv
31 | │ ├── video.webm
32 | │ └── video.jpg
33 | ```
34 |
35 | Add `data-vide-bg` attribute with a path to the video and poster without extension, video and poster must have the same name. Add `data-vide-options` to pass vide options, if you need it. By default video is muted, looped and starts automatically.
36 | ```html
37 |
39 |
40 | ```
41 |
42 | Also you can set extended path:
43 | ```html
44 |
47 |
48 | ```
49 |
50 | In some situations it can be helpful to initialize it with JS because Vide doesn't have mutation observers:
51 | ```js
52 | $('#myBlock').vide('path/to/video');
53 | $('#myBlock').vide('path/to/video', {
54 | ...options...
55 | });
56 | $('#myBlock').vide({
57 | mp4: path/to/video1,
58 | webm: path/to/video2,
59 | ogv: path/to/video3,
60 | poster: path/to/poster
61 | }, {
62 | ...options...
63 | });
64 | $('#myBlock').vide('extended path as a string', 'options as a string');
65 | ```
66 |
67 | Easy as hell.
68 |
69 | ## Options
70 |
71 | Below is a complete list of options and matching default values:
72 |
73 | ```js
74 | {
75 | volume: 1,
76 | playbackRate: 1,
77 | muted: true,
78 | loop: true,
79 | autoplay: true,
80 | position: '50% 50%', // Similar to the CSS `background-position` property.
81 | posterType: 'detect', // Poster image type. "detect" — auto-detection; "none" — no poster; "jpg", "png", "gif",... - extensions.
82 | resizing: true, // Auto-resizing, read: https://github.com/VodkaBears/Vide#resizing
83 | bgColor: 'transparent', // Allow custom background-color for Vide div,
84 | className: '' // Add custom CSS class to Vide div
85 | }
86 | ```
87 |
88 | ## Methods
89 |
90 | Below is a complete list of methods:
91 |
92 | ```js
93 | // Get instance of the plugin
94 | var instance = $('#yourElement').data('vide');
95 |
96 | // Get video element of the background. Do what you want.
97 | instance.getVideoObject();
98 |
99 | // Resize video background.
100 | // It calls automatically, if window resize (or element, if you will use something like https://github.com/cowboy/jquery-resize).
101 | instance.resize();
102 |
103 | // Destroy plugin instance
104 | instance.destroy();
105 | ```
106 |
107 | ## Resizing
108 |
109 | The Vide plugin resizes if the window resizes. If you will use something like https://github.com/cowboy/jquery-resize, it will resize automatically when the container resizes. Or simply use `resize()` method whenever you need.
110 |
111 | Set the `resizing` option to false to disable auto-resizing.
112 |
113 | ## Encoding video
114 |
115 | http://diveintohtml5.info/video.html#miro
116 |
117 | ## Meteor
118 |
119 | ### Install
120 |
121 | ```sh
122 | meteor add vodkabears:vide
123 | ```
124 |
125 | ### Usage
126 |
127 | Because of how meteor renders templates reactively you will need to initialize
128 | manually for the templates you want to use vide in.
129 |
130 | ```js
131 | Template.templateName.onRendered(function() {
132 | this.$('#elementName').vide('fileNameWithoutExtension');
133 | });
134 | ```
135 |
136 | Meteor integration by [zimme](https://github.com/zimme).
137 |
138 | ## Ruby Gem
139 |
140 | [Vider](https://github.com/wazery/vider) by Islam Wazery.
141 |
142 | ## License
143 |
144 | ```
145 | The MIT License (MIT)
146 |
147 | Copyright (c) 2015 Ilya Makarov
148 |
149 | Permission is hereby granted, free of charge, to any person obtaining a copy
150 | of this software and associated documentation files (the "Software"), to deal
151 | in the Software without restriction, including without limitation the rights
152 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
153 | copies of the Software, and to permit persons to whom the Software is
154 | furnished to do so, subject to the following conditions:
155 |
156 | The above copyright notice and this permission notice shall be included in all
157 | copies or substantial portions of the Software.
158 |
159 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
160 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
161 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
162 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
163 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
164 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
165 | SOFTWARE.
166 | ```
167 |
--------------------------------------------------------------------------------
/test/vide_test.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | /*
3 | ======== A Handy Little QUnit Reference ========
4 | http://api.qunitjs.com/
5 |
6 | Test methods:
7 | module(name, {[setup][ ,teardown]})
8 | test(name, callback)
9 | expect(numberOfAssertions)
10 | stop(increment)
11 | start(decrement)
12 | Test assertions:
13 | ok(value, [message])
14 | equal(actual, expected, [message])
15 | notEqual(actual, expected, [message])
16 | deepEqual(actual, expected, [message])
17 | notDeepEqual(actual, expected, [message])
18 | strictEqual(actual, expected, [message])
19 | notStrictEqual(actual, expected, [message])
20 | throws(block, [expected], [message])
21 | */
22 |
23 | var $block1;
24 | var $block2;
25 | var $block3;
26 | var $block4;
27 | var $block5;
28 | var $block6;
29 |
30 | QUnit.begin(function() {
31 | $block1 = $('#block1');
32 | $block2 = $('#block2');
33 | $block3 = $('#block3');
34 | $block4 = $('#block4');
35 | $block5 = $('#block5');
36 | $block6 = $('#block6');
37 | });
38 |
39 | QUnit.test('Initialization', function() {
40 | // js initialization
41 | $block2.vide('video/ocean', {
42 | posterType: 'gif'
43 | });
44 |
45 | $block5.vide(
46 | 'mp4: video/ocean, webm: video/ocean.webm, ogv: video/ocean, poster: video/ocean',
47 | 'loop: false,volume:0.3, playbackRate:'
48 | );
49 |
50 | ok($block1.data('vide'));
51 | ok($block2.data('vide'));
52 | ok($block3.data('vide'));
53 | ok($block4.data('vide'));
54 | ok($block5.data('vide'));
55 | ok($block6.data('vide'));
56 | });
57 |
58 | QUnit.test('Default settings', function() {
59 | var inst = $block6.data('vide');
60 |
61 | strictEqual(inst.settings.volume, 1);
62 | strictEqual(inst.settings.playbackRate, 1);
63 | strictEqual(inst.settings.muted, true);
64 | strictEqual(inst.settings.loop, true);
65 | strictEqual(inst.settings.autoplay, true);
66 | strictEqual(inst.settings.position, '50% 50%');
67 | strictEqual(inst.settings.posterType, 'detect');
68 | strictEqual(inst.settings.resizing, true);
69 | strictEqual(inst.settings.bgColor, 'transparent');
70 | strictEqual(inst.settings.className, '');
71 | });
72 |
73 | QUnit.test('Parsing of the path', function() {
74 | strictEqual($block1.data('vide').path, $block1.data('vide-bg'));
75 | });
76 |
77 | QUnit.test('Parsing of the path with multiple names', function() {
78 | deepEqual($block3.data('vide').path, {
79 | mp4: 'http://vodkabears.github.io/vide/video/ocean',
80 | webm: 'video/ocean',
81 | ogv: 'http://vodkabears.github.io:80/vide/video/ocean',
82 | poster: 'video/ocean'
83 | });
84 | });
85 |
86 | QUnit.test('Parsing of the options', function() {
87 | var inst = $block1.data('vide');
88 | var video = inst.getVideoObject();
89 |
90 | strictEqual(inst.settings.loop, false);
91 | strictEqual(inst.settings.volume, 0.3);
92 | strictEqual(inst.settings.playbackRate, 1);
93 | strictEqual(inst.settings.position, '60% bottom');
94 | strictEqual(inst.settings.className, 'vide-wrapper');
95 |
96 | strictEqual(video.loop, false);
97 | strictEqual(video.volume, 0.3);
98 | strictEqual(video.playbackRate, 1);
99 | strictEqual(video.style.left, '60%');
100 | strictEqual(video.style.top, '100%');
101 | ok(inst.$wrapper.hasClass('vide-wrapper'));
102 | });
103 |
104 | QUnit.test('Passing JSON with the data attribute', function() {
105 | var inst = $block4.data('vide');
106 |
107 | deepEqual(inst.path, {
108 | mp4: 'http://vodkabears.github.io/vide/video/ocean',
109 | webm: 'video/ocean',
110 | ogv: 'http://vodkabears.github.io:80/vide/video/ocean',
111 | poster: 'video/ocean'
112 | });
113 |
114 | strictEqual(inst.settings.loop, false);
115 | strictEqual(inst.settings.volume, 0.3);
116 | });
117 |
118 | QUnit.test('Passing strings with params directly to the constructor', function() {
119 | var inst = $block5.data('vide');
120 |
121 | deepEqual(inst.path, {
122 | mp4: 'video/ocean',
123 | webm: 'video/ocean',
124 | ogv: 'video/ocean',
125 | poster: 'video/ocean'
126 | });
127 |
128 | strictEqual(inst.settings.loop, false);
129 | strictEqual(inst.settings.volume, 0.3);
130 | strictEqual(inst.settings.playbackRate, 1);
131 | });
132 |
133 | QUnit.asyncTest('Poster detection', function() {
134 | var inst1 = $block1.data('vide');
135 | var inst2 = $block2.data('vide');
136 | var inst3 = $block3.data('vide');
137 | var $wrapper1 = inst1.$wrapper;
138 | var $wrapper2 = inst2.$wrapper;
139 | var $wrapper3 = inst3.$wrapper;
140 |
141 | strictEqual(inst2.settings.posterType, 'gif');
142 | ok($wrapper2.css('background-image').search('video/ocean.gif') > -1);
143 |
144 | strictEqual(inst3.settings.posterType, 'none');
145 | strictEqual($wrapper3.css('background-image'), 'none');
146 |
147 | strictEqual(inst1.settings.posterType, 'detect');
148 | setTimeout(function() {
149 | ok($wrapper1
150 | .css('background-image')
151 | .search('http://vodkabears.github.io/vide/video/ocean.jpg') > -1);
152 | QUnit.start();
153 | }, 5000);
154 | });
155 |
156 | QUnit.test('Poster position', function() {
157 | var $wrapper = $block1.data('vide').$wrapper;
158 | var video = $block1.data('vide').getVideoObject();
159 |
160 | strictEqual($wrapper.css('background-position'), video.style.left + ' ' + video.style.top);
161 | });
162 |
163 | QUnit.test('Re-initialization', function() {
164 | $block1.vide('video/ocean');
165 | $block2.vide('video/ocean');
166 | $block2.vide('video/ocean');
167 | $block1.vide('video/ocean');
168 | $block3.vide('video/ocean');
169 |
170 | var count = $.vide.lookup.filter(function(value) {
171 | return value !== undefined;
172 | }).length;
173 |
174 | ok($block1.data('vide'));
175 | ok($block2.data('vide'));
176 | ok($block3.data('vide'));
177 | ok($block4.data('vide'));
178 | ok($block5.data('vide'));
179 | ok($block6.data('vide'));
180 | equal(count, 6);
181 | });
182 |
183 | QUnit.test('getVideoObject() method', function() {
184 | ok($block1.data('vide').getVideoObject());
185 | ok($block2.data('vide').getVideoObject());
186 | ok($block3.data('vide').getVideoObject());
187 | ok($block4.data('vide').getVideoObject());
188 | ok($block5.data('vide').getVideoObject());
189 | ok($block6.data('vide').getVideoObject());
190 | });
191 |
192 | QUnit.test('resize() method', function() {
193 | var inst = $block1.data('vide');
194 | var videoHeight = inst.$video[0].videoHeight;
195 | var videoWidth = inst.$video[0].videoWidth;
196 | var wrapperHeight = inst.$wrapper.height();
197 | var wrapperWidth = inst.$wrapper.width();
198 |
199 | inst.$video[0].style.width = '300px';
200 | inst.$video[0].style.height = '300px';
201 |
202 | inst.resize();
203 |
204 | if (wrapperWidth / videoWidth > wrapperHeight / videoHeight) {
205 | strictEqual(inst.$video[0].style.width, wrapperWidth + 2 + 'px');
206 | strictEqual(inst.$video[0].style.height, 'auto');
207 | } else {
208 | strictEqual(inst.$video[0].style.width, 'auto');
209 | strictEqual(inst.$video[0].style.height, wrapperHeight + 2 + 'px');
210 | }
211 | });
212 |
213 | QUnit.test('destroy() method', function() {
214 | $block1.data('vide').destroy();
215 | $block2.data('vide').destroy();
216 | $block3.data('vide').destroy();
217 | $block4.data('vide').destroy();
218 | $block5.data('vide').destroy();
219 | $block6.data('vide').destroy();
220 |
221 | var count = $.vide.lookup.filter(function(value) {
222 | return value !== undefined;
223 | }).length;
224 |
225 | strictEqual(count, 0);
226 | strictEqual($block1.find('video').length, 0);
227 | strictEqual($block2.find('video').length, 0);
228 | strictEqual($block3.find('video').length, 0);
229 | strictEqual($block4.find('video').length, 0);
230 | strictEqual($block5.find('video').length, 0);
231 | strictEqual($block6.find('video').length, 0);
232 | });
233 |
234 | }(window.jQuery));
235 |
--------------------------------------------------------------------------------
/src/jquery.vide.js:
--------------------------------------------------------------------------------
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 | $('').on('load', onLoad);
168 | $('').on('load', onLoad);
169 | $('').on('load', onLoad);
170 | $('').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 = $('
')
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 += '';
289 | }
290 |
291 | if (path.webm) {
292 | sources += '';
293 | }
294 |
295 | if (path.ogv) {
296 | sources += '';
297 | }
298 |
299 | $video = vide.$video = $('');
300 | } else {
301 | $video = vide.$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 | });
494 |
--------------------------------------------------------------------------------
/dist/jquery.vide.js:
--------------------------------------------------------------------------------
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 | $('').on('load', onLoad);
176 | $('').on('load', onLoad);
177 | $('').on('load', onLoad);
178 | $('').on('load', onLoad);
179 | }
180 |
181 | /**
182 | * Vide constructor
183 | * @param {HTMLElement} element
184 | * @param {Object|String} path
185 | * @param {Object|String} options
186 | * @constructor
187 | */
188 | function Vide(element, path, options) {
189 | this.$element = $(element);
190 |
191 | // Parse path
192 | if (typeof path === 'string') {
193 | path = parseOptions(path);
194 | }
195 |
196 | // Parse options
197 | if (!options) {
198 | options = {};
199 | } else if (typeof options === 'string') {
200 | options = parseOptions(options);
201 | }
202 |
203 | // Remove an extension
204 | if (typeof path === 'string') {
205 | path = path.replace(/\.\w*$/, '');
206 | } else if (typeof path === 'object') {
207 | for (var i in path) {
208 | if (path.hasOwnProperty(i)) {
209 | path[i] = path[i].replace(/\.\w*$/, '');
210 | }
211 | }
212 | }
213 |
214 | this.settings = $.extend({}, DEFAULTS, options);
215 | this.path = path;
216 |
217 | // https://github.com/VodkaBears/Vide/issues/110
218 | try {
219 | this.init();
220 | } catch (e) {
221 | if (e.message !== NOT_IMPLEMENTED_MSG) {
222 | throw e;
223 | }
224 | }
225 | }
226 |
227 | /**
228 | * Initialization
229 | * @public
230 | */
231 | Vide.prototype.init = function() {
232 | var vide = this;
233 | var path = vide.path;
234 | var poster = path;
235 | var sources = '';
236 | var $element = vide.$element;
237 | var settings = vide.settings;
238 | var position = parsePosition(settings.position);
239 | var posterType = settings.posterType;
240 | var $video;
241 | var $wrapper;
242 |
243 | // Set styles of a video wrapper
244 | $wrapper = vide.$wrapper = $('