├── .bowerrc ├── test ├── video │ ├── ocean.jpg │ ├── ocean.mp4 │ ├── ocean.ogv │ └── ocean.webm ├── meteor_test.js ├── .jshintrc ├── vide.html └── vide_test.js ├── .gitignore ├── examples ├── video │ ├── ocean.jpg │ ├── ocean.mp4 │ ├── ocean.ogv │ └── ocean.webm ├── body-bg.html └── block-bg.html ├── .travis.yml ├── .jshintrc ├── CONTRIBUTING.md ├── .editorconfig ├── libs └── jquery-loader.js ├── package.js ├── bower.json ├── MIT-LICENSE ├── package.json ├── Meteor.md ├── .jscsrc ├── CHANGELOG.md ├── Gruntfile.js ├── dist ├── jquery.vide.min.js └── jquery.vide.js ├── README.md └── src └── jquery.vide.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "libs" 3 | } 4 | -------------------------------------------------------------------------------- /test/video/ocean.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vodkabears/Vide/HEAD/test/video/ocean.jpg -------------------------------------------------------------------------------- /test/video/ocean.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vodkabears/Vide/HEAD/test/video/ocean.mp4 -------------------------------------------------------------------------------- /test/video/ocean.ogv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vodkabears/Vide/HEAD/test/video/ocean.ogv -------------------------------------------------------------------------------- /test/video/ocean.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vodkabears/Vide/HEAD/test/video/ocean.webm -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | node_modules/ 4 | libs/ 5 | !libs/jquery-loader.js 6 | .build* 7 | -------------------------------------------------------------------------------- /examples/video/ocean.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vodkabears/Vide/HEAD/examples/video/ocean.jpg -------------------------------------------------------------------------------- /examples/video/ocean.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vodkabears/Vide/HEAD/examples/video/ocean.mp4 -------------------------------------------------------------------------------- /examples/video/ocean.ogv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vodkabears/Vide/HEAD/examples/video/ocean.ogv -------------------------------------------------------------------------------- /examples/video/ocean.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vodkabears/Vide/HEAD/examples/video/ocean.webm -------------------------------------------------------------------------------- /test/meteor_test.js: -------------------------------------------------------------------------------- 1 | Tinytest.add('Instantiation', function(test) { 2 | test.notEqual(jQuery().vide, undefined); 3 | }); 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: node_js 3 | node_js: 4 | - 0.12 5 | before_install: 6 | - "curl https://install.meteor.com | /bin/sh" 7 | install: npm start 8 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "boss": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "eqnull": true, 6 | "expr": true, 7 | "noarg": true, 8 | "unused": true 9 | } 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | CONTRIBUTING 2 | ==== 3 | 4 | 1. Fork. 5 | 2. Run `npm start`. 6 | 3. Make your changes on the `src` folder. 7 | 4. Update tests. 8 | 5. Run `npm test`, make sure everything is okay. 9 | 6. Submit a pull request to the master branch. 10 | 11 | Thanks. 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | -------------------------------------------------------------------------------- /libs/jquery-loader.js: -------------------------------------------------------------------------------- 1 | !(function() { 2 | 3 | // Get any lib=___ param from the query string. 4 | var library = location.search.match(/[?&]lib=(.*?)(?=&|$)/); 5 | 6 | /* jshint -W060 */ 7 | if (library) { 8 | document.write(''); 9 | } else { 10 | document.write(''); 11 | } 12 | }()); 13 | -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "boss": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "eqnull": true, 6 | "expr": true, 7 | "immed": true, 8 | "noarg": true, 9 | "quotmark": "single", 10 | "unused": true 11 | "node": true, 12 | "predef": [ 13 | "jQuery", 14 | "QUnit", 15 | "module", 16 | "test", 17 | "asyncTest", 18 | "expect", 19 | "start", 20 | "stop", 21 | "ok", 22 | "equal", 23 | "notEqual", 24 | "deepEqual", 25 | "notDeepEqual", 26 | "strictEqual", 27 | "notStrictEqual", 28 | "throws" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | documentation: 'Meteor.md', 3 | git: 'https://github.com/VodkaBears/Vide.git', 4 | name: 'vodkabears:vide', 5 | summary: 'Easy as hell jQuery plugin for video backgrounds', 6 | version: '0.5.1' 7 | }); 8 | 9 | Package.onUse(function(api) { 10 | api.versionsFrom('1.0'); 11 | api.use('jquery', 'client'); 12 | api.addFiles('dist/jquery.vide.js', 'client'); 13 | }); 14 | 15 | Package.onTest(function(api) { 16 | api.use('vodkabears:vide', 'client'); 17 | api.use('tinytest', 'client'); 18 | api.addFiles('test/meteor_test.js', 'client'); 19 | }); 20 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vide", 3 | "homepage": "http://vodkabears.github.io/vide/", 4 | "authors": [ 5 | "Ilya Makarov " 6 | ], 7 | "description": "Easy as hell jQuery plugin for video backgrounds.", 8 | "main": "dist/jquery.vide.js", 9 | "ignore": [ 10 | "**/.*", 11 | "examples/", 12 | "libs/", 13 | "src/", 14 | "test/", 15 | "*.md", 16 | "Gruntfile.js", 17 | "package.json", 18 | "vide.jquery.json" 19 | ], 20 | "keywords": [ 21 | "jquery", 22 | "plugin", 23 | "jquery-plugin", 24 | "video", 25 | "background", 26 | "ui", 27 | "responsive", 28 | "declarative" 29 | ], 30 | "license": "MIT", 31 | "dependencies": { 32 | "jquery": "*" 33 | }, 34 | "devDependencies": { 35 | "qunit": "^1.19.0", 36 | "jquery": "jquery#^1.11.3", 37 | "jquery2": "jquery#^2.1.4" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/body-bg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Body background example 6 | 13 | 14 | 15 | 16 | 17 | 18 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ilya Makarov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/block-bg.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Block background example 6 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |

18 | 19 |
22 |
23 | 24 | 25 | 26 | 27 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vide", 3 | "version": "0.5.1", 4 | "description": "Easy as hell jQuery plugin for video backgrounds.", 5 | "keywords": [ 6 | "jquery-plugin", 7 | "jquery", 8 | "plugin", 9 | "video", 10 | "background", 11 | "ui", 12 | "responsive", 13 | "declarative" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/VodkaBears/Vide.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/VodkaBears/Vide/issues" 21 | }, 22 | "author": { 23 | "name": "Ilya Makarov", 24 | "email": "dfrost.00@gmail.com", 25 | "url": "https://github.com/VodkaBears" 26 | }, 27 | "homepage": "http://vodkabears.github.io/vide/", 28 | "license": "MIT", 29 | "main": "dist/jquery.vide.js", 30 | "dependencies": { 31 | "jquery": "*" 32 | }, 33 | "devDependencies": { 34 | "bower": "^1.5.3", 35 | "grunt": "^0.4.5", 36 | "grunt-cli": "^0.1.13", 37 | "grunt-contrib-concat": "^0.5.1", 38 | "grunt-contrib-connect": "^0.11.2", 39 | "grunt-contrib-jshint": "^0.11.3", 40 | "grunt-contrib-qunit": "^1.2.0", 41 | "grunt-contrib-uglify": "^0.9.2", 42 | "grunt-exec": "^0.4.6", 43 | "grunt-githooks": "^0.3.1", 44 | "grunt-jscs": "^2.1.0", 45 | "spacejam": "^1.2.2" 46 | }, 47 | "scripts": { 48 | "start": "npm install && ./node_modules/.bin/bower install && ./node_modules/.bin/grunt githooks", 49 | "test": "./node_modules/.bin/grunt test", 50 | "dist": "./node_modules/.bin/grunt" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/vide.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vide Test Suite 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 |
23 |
24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Meteor.md: -------------------------------------------------------------------------------- 1 | # Vide 2 | 3 | Easy as hell jQuery plugin for video backgrounds. 4 | 5 | ## Notes 6 | 7 | * All modern desktop browsers are supported. 8 | * IE9+ 9 | * iOS plays video from a browser only in the native player. So video for iOS is 10 | disabled, only fullscreen poster will be used. 11 | * Some android devices play video, some not — go figure. So video for android is 12 | disabled, only fullscreen poster will be used. 13 | 14 | ## Install 15 | 16 | ```sh 17 | meteor add vodkabears:vide 18 | ``` 19 | 20 | ## Usage 21 | 22 | Prepare your video in several formats like '.webm', '.mp4' for cross browser 23 | compatibility, also add a poster with `.jpg`, `.png` or `.gif` extension: 24 | ``` 25 | path/ 26 | ├── to/ 27 | │ ├── video.mp4 28 | │ ├── video.ogv 29 | │ ├── video.webm 30 | │ └── video.jpg 31 | ``` 32 | 33 | Because of how meteor renders templates reactively you will need to initialize 34 | manually for the templates you want to use vide in. 35 | 36 | ```js 37 | Template.templateName.onRendered(function() { 38 | this.$('#elementName').vide('path/to/video'); 39 | }); 40 | ``` 41 | 42 | Meteor integration by [zimme](https://github.com/zimme). 43 | 44 | ## Options 45 | 46 | Below is a complete list of options and matching default values: 47 | 48 | ```js 49 | { 50 | volume: 1, 51 | playbackRate: 1, 52 | muted: true, 53 | loop: true, 54 | autoplay: true, 55 | position: '50% 50%', // Similar to the CSS `background-position` property. 56 | posterType: 'detect', // Poster image type. "detect" — auto-detection; "none" — no poster; "jpg", "png", "gif",... - extensions. 57 | resizing: true // Auto-resizing, read: https://github.com/VodkaBears/Vide#resizing 58 | } 59 | ``` 60 | 61 | ## Methods 62 | 63 | Below is a complete list of methods: 64 | 65 | ```js 66 | // Get instance of the plugin 67 | var instance = $('#yourElement').data('vide'); 68 | 69 | // Get video element of the background. Do what you want. 70 | instance.getVideoObject(); 71 | 72 | // Resize video background. 73 | // It calls automatically, if window resize (or element, if you will use 74 | // something like https://github.com/cowboy/jquery-resize). 75 | instance.resize(); 76 | 77 | // Destroy plugin instance 78 | instance.destroy(); 79 | ``` 80 | 81 | ## More Info 82 | 83 | See full documentation on 84 | [Github](https://github.com/VodkaBears/Vide). 85 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "disallowSpacesInNamedFunctionExpression": { 3 | "beforeOpeningRoundBrace": true 4 | }, 5 | "disallowSpacesInFunctionExpression": { 6 | "beforeOpeningRoundBrace": true 7 | }, 8 | "disallowSpacesInAnonymousFunctionExpression": { 9 | "beforeOpeningRoundBrace": true 10 | }, 11 | "disallowSpacesInFunctionDeclaration": { 12 | "beforeOpeningRoundBrace": true 13 | }, 14 | "disallowEmptyBlocks": true, 15 | "disallowSpacesInCallExpression": true, 16 | "disallowSpacesInsideArrayBrackets": true, 17 | "disallowSpacesInsideParentheses": true, 18 | "disallowQuotedKeysInObjects": true, 19 | "disallowSpaceAfterObjectKeys": true, 20 | "disallowSpaceAfterPrefixUnaryOperators": true, 21 | "disallowSpaceBeforePostfixUnaryOperators": true, 22 | "disallowSpaceBeforeBinaryOperators": [ 23 | "," 24 | ], 25 | "disallowMixedSpacesAndTabs": true, 26 | "disallowTrailingWhitespace": true, 27 | "disallowTrailingComma": true, 28 | "disallowYodaConditions": true, 29 | "disallowKeywords": [ "with" ], 30 | "disallowKeywordsOnNewLine": ["else"], 31 | "disallowMultipleLineBreaks": true, 32 | "disallowMultipleLineStrings": true, 33 | "disallowMultipleVarDecl": true, 34 | "requireSpaceBeforeBlockStatements": true, 35 | "requireParenthesesAroundIIFE": true, 36 | "requireSpacesInConditionalExpression": true, 37 | "requireBlocksOnNewline": 1, 38 | "requireCommaBeforeLineBreak": true, 39 | "requireSpaceBeforeBinaryOperators": true, 40 | "requireSpaceAfterBinaryOperators": true, 41 | "requireCamelCaseOrUpperCaseIdentifiers": true, 42 | "requireLineFeedAtFileEnd": true, 43 | "requireCapitalizedConstructors": true, 44 | "requireDotNotation": true, 45 | "requireSpacesInForStatement": true, 46 | "requireSpaceBetweenArguments": true, 47 | "requireCurlyBraces": [ 48 | "do" 49 | ], 50 | "requireSpaceAfterKeywords": [ 51 | "if", 52 | "else", 53 | "for", 54 | "while", 55 | "do", 56 | "switch", 57 | "case", 58 | "return", 59 | "try", 60 | "catch", 61 | "typeof" 62 | ], 63 | "requirePaddingNewLinesBeforeLineComments": { 64 | "allExcept": "firstAfterCurly" 65 | }, 66 | "requirePaddingNewLinesAfterBlocks": true, 67 | "requireSemicolons": true, 68 | "validateLineBreaks": "LF", 69 | "validateQuoteMarks": "'", 70 | "validateIndentation": 2 71 | } 72 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.5.1 2 | * Remove deprecated jQuery load method. 3 | 4 | ### 0.5.0 5 | * Add the `className` option. 6 | 7 | ### 0.4.1 8 | * Fix hidden video. 9 | 10 | ### 0.4.0 11 | * Add `bgColor` property. 12 | * Fix a blurred circle on IOS9. 13 | * Update dependencies. 14 | 15 | ### 0.3.7 16 | * Fix the 'Not implemented' error in IE. 17 | * Make a small refactoring. 18 | 19 | ### 0.3.6 20 | * Rename 'video/ogv' type to 'video/ogg'. 21 | * Correct the docs. 22 | * Remove moot `version` property from bower.json. 23 | * Update linter configs. 24 | * Update dependencies. 25 | 26 | ### 0.3.5 27 | * Fixed disappearing of the poster image in Android 5. 28 | 29 | ### 0.3.4 30 | * Updated Meteor documentation. 31 | * Refactoring. 32 | * Updated dependencies. 33 | 34 | ### 0.3.3 35 | * Added Browserify support. 36 | * Removed the checking of User-Agent. 37 | * Fixed issues of the back/forward cache in Safari. 38 | * Updated Meteor integration. 39 | * Updated dependencies. 40 | 41 | ### 0.3.2 42 | * Added support of old FF versions. 43 | * Improved Safari playback fixes. 44 | * Updated dependencies. 45 | 46 | ### 0.3.1 47 | * Fixed a flash. 48 | * Fixed Safari playback bugs. 49 | * Used package.json instead of vide.jquery.json. 50 | * Used '#on' instead of '#bind'. 51 | * Imporved the codestyle. 52 | * Updated dependencies. 53 | 54 | ### 0.3.0 55 | * Added the `resizing` option. 56 | * Updated tests. 57 | 58 | ### 0.2.1 59 | * Code refactoring. 60 | * Updated devDependencies. 61 | 62 | ### 0.2.0 63 | * Lots of updates since 0.1.0. 64 | * Improved code linting. 65 | * Cleaned up the repository. 66 | * Added bower dependencies. 67 | * Updated devDependencies. 68 | 69 | ### 0.1.4 70 | * Fixed wrong URL parsing. 71 | * Changed main files in bower.json. 72 | 73 | ### 0.1.3 74 | * Path argument can receive list of sources. 75 | * Strings with options and pathes can be passed to the constructor directly. 76 | * Added `posterType: none` value. 77 | * Updated README. 78 | * Updated JSDoc. 79 | * Updated tests. 80 | * Updated examples. 81 | * Added CONTRIBUTING.md 82 | * Other small fixes and optimizations. 83 | 84 | ### 0.1.2 85 | * Restored `posterType` option to specify poster image type. 86 | 87 | ### 0.1.1 88 | * Support of absolute URLs (#10). 89 | * Fixed the CORS issue (#11). 90 | * Fixed the destroy method. 91 | * Fixed parsing of empty options. 92 | * Poster and video positions are the same now. 93 | * Syntax and behavior of the position option are similar to the CSS `background-position` property. 94 | * Add support of the `.jpeg` extension. 95 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.initConfig({ 4 | 5 | // Import package manifest 6 | pkg: grunt.file.readJSON('package.json'), 7 | 8 | meta: { 9 | banner: '/*\n' + 10 | ' * <%= pkg.name[0].toUpperCase() + pkg.name.slice(1) %> - v<%= pkg.version %>\n' + 11 | ' * <%= pkg.description %>\n' + 12 | ' * <%= pkg.homepage %>\n' + 13 | ' *\n' + 14 | ' * Made by <%= pkg.author.name %>\n' + 15 | ' * Under <%= pkg.license %> License\n' + 16 | ' */\n' 17 | }, 18 | 19 | concat: { 20 | dist: { 21 | src: 'src/jquery.vide.js', 22 | dest: 'dist/jquery.vide.js' 23 | }, 24 | options: { 25 | banner: '<%= meta.banner %>' 26 | } 27 | }, 28 | 29 | connect: { 30 | server: { 31 | options: { 32 | port: 7770 33 | } 34 | } 35 | }, 36 | 37 | jshint: { 38 | gruntfile: { 39 | src: 'Gruntfile.js' 40 | }, 41 | src: { 42 | src: 'src/**/*.js' 43 | }, 44 | test: { 45 | src: ['test/**/*.js', 'libs/jquery-loader.js'] 46 | }, 47 | options: { 48 | jshintrc: '.jshintrc' 49 | } 50 | }, 51 | 52 | jscs: { 53 | gruntfile: { 54 | src: 'Gruntfile.js' 55 | }, 56 | src: { 57 | src: 'src/**/*.js' 58 | }, 59 | test: { 60 | src: ['test/**/*.js', 'libs/jquery-loader.js'] 61 | } 62 | }, 63 | 64 | qunit: { 65 | all: { 66 | options: { 67 | urls: ['', '2'].map(function(version) { 68 | return 'http://localhost:<%= connect.server.options.port %>' + 69 | '/test/vide.html?jquery=' + version; 70 | }) 71 | } 72 | } 73 | }, 74 | 75 | uglify: { 76 | target: { 77 | src: 'dist/jquery.vide.js', 78 | dest: 'dist/jquery.vide.min.js' 79 | }, 80 | options: { 81 | banner: '<%= meta.banner %>' 82 | } 83 | }, 84 | 85 | githooks: { 86 | all: { 87 | 'pre-commit': 'lint' 88 | }, 89 | options: { 90 | command: 'node_modules/.bin/grunt' 91 | } 92 | }, 93 | 94 | exec: { 95 | 'meteor-test': { 96 | command: 'node_modules/.bin/spacejam test-packages ./' 97 | } 98 | } 99 | }); 100 | 101 | grunt.loadNpmTasks('grunt-contrib-concat'); 102 | grunt.loadNpmTasks('grunt-contrib-connect'); 103 | grunt.loadNpmTasks('grunt-contrib-jshint'); 104 | grunt.loadNpmTasks('grunt-contrib-qunit'); 105 | grunt.loadNpmTasks('grunt-contrib-uglify'); 106 | grunt.loadNpmTasks('grunt-githooks'); 107 | grunt.loadNpmTasks('grunt-jscs'); 108 | grunt.loadNpmTasks('grunt-exec'); 109 | 110 | grunt.registerTask('default', [ 111 | 'connect', 'jshint', 'jscs', 'qunit', 'concat', 'uglify', 'githooks' 112 | ]); 113 | grunt.registerTask('lint', [ 114 | 'jshint', 'jscs' 115 | ]); 116 | grunt.registerTask('test', [ 117 | 'connect', 'jshint', 'jscs', 'qunit', 'exec:meteor-test' 118 | ]); 119 | }; 120 | -------------------------------------------------------------------------------- /dist/jquery.vide.min.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(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):b("object"==typeof exports?require("jquery"):a.jQuery)}(this,function(a){"use strict";function b(a){var b,c,d,e,f,g,h,i={};for(f=a.replace(/\s*:\s*/g,":").replace(/\s*,\s*/g,",").split(","),h=0,g=f.length;h').on("load",d),a('').on("load",d),a('').on("load",d),a('').on("load",d)}function e(c,d,e){if(this.$element=a(c),"string"==typeof d&&(d=b(d)),e?"string"==typeof e&&(e=b(e)):e={},"string"==typeof d)d=d.replace(/\.\w*$/,"");else if("object"==typeof d)for(var f in d)d.hasOwnProperty(f)&&(d[f]=d[f].replace(/\.\w*$/,""));this.settings=a.extend({},g,e),this.path=d;try{this.init()}catch(i){if(i.message!==h)throw i}}var f="vide",g={volume:1,playbackRate:1,muted:!0,loop:!0,autoplay:!0,position:"50% 50%",posterType:"detect",resizing:!0,bgColor:"transparent",className:""},h="Not implemented";e.prototype.init=function(){var b,e,f=this,g=f.path,i=g,j="",k=f.$element,l=f.settings,m=c(l.position),n=l.posterType;e=f.$wrapper=a("
").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 = $('
') 245 | .addClass(settings.className) 246 | .css({ 247 | position: 'absolute', 248 | 'z-index': -1, 249 | top: 0, 250 | left: 0, 251 | bottom: 0, 252 | right: 0, 253 | overflow: 'hidden', 254 | '-webkit-background-size': 'cover', 255 | '-moz-background-size': 'cover', 256 | '-o-background-size': 'cover', 257 | 'background-size': 'cover', 258 | 'background-color': settings.bgColor, 259 | 'background-repeat': 'no-repeat', 260 | 'background-position': position.x + ' ' + position.y 261 | }); 262 | 263 | // Get a poster path 264 | if (typeof path === 'object') { 265 | if (path.poster) { 266 | poster = path.poster; 267 | } else { 268 | if (path.mp4) { 269 | poster = path.mp4; 270 | } else if (path.webm) { 271 | poster = path.webm; 272 | } else if (path.ogv) { 273 | poster = path.ogv; 274 | } 275 | } 276 | } 277 | 278 | // Set a video poster 279 | if (posterType === 'detect') { 280 | findPoster(poster, function(url) { 281 | $wrapper.css('background-image', 'url(' + url + ')'); 282 | }); 283 | } else if (posterType !== 'none') { 284 | $wrapper.css('background-image', 'url(' + poster + '.' + posterType + ')'); 285 | } 286 | 287 | // If a parent element has a static position, make it relative 288 | if ($element.css('position') === 'static') { 289 | $element.css('position', 'relative'); 290 | } 291 | 292 | $element.prepend($wrapper); 293 | 294 | if (typeof path === 'object') { 295 | if (path.mp4) { 296 | sources += ''; 297 | } 298 | 299 | if (path.webm) { 300 | sources += ''; 301 | } 302 | 303 | if (path.ogv) { 304 | sources += ''; 305 | } 306 | 307 | $video = vide.$video = $(''); 308 | } else { 309 | $video = vide.$video = $(''); 314 | } 315 | 316 | // https://github.com/VodkaBears/Vide/issues/110 317 | try { 318 | $video 319 | 320 | // Set video properties 321 | .prop({ 322 | autoplay: settings.autoplay, 323 | loop: settings.loop, 324 | volume: settings.volume, 325 | muted: settings.muted, 326 | defaultMuted: settings.muted, 327 | playbackRate: settings.playbackRate, 328 | defaultPlaybackRate: settings.playbackRate 329 | }); 330 | } catch (e) { 331 | throw new Error(NOT_IMPLEMENTED_MSG); 332 | } 333 | 334 | // Video alignment 335 | $video.css({ 336 | margin: 'auto', 337 | position: 'absolute', 338 | 'z-index': -1, 339 | top: position.y, 340 | left: position.x, 341 | '-webkit-transform': 'translate(-' + position.x + ', -' + position.y + ')', 342 | '-ms-transform': 'translate(-' + position.x + ', -' + position.y + ')', 343 | '-moz-transform': 'translate(-' + position.x + ', -' + position.y + ')', 344 | transform: 'translate(-' + position.x + ', -' + position.y + ')', 345 | 346 | // Disable visibility, while loading 347 | visibility: 'hidden', 348 | opacity: 0 349 | }) 350 | 351 | // Resize a video, when it's loaded 352 | .one('canplaythrough.' + PLUGIN_NAME, function() { 353 | vide.resize(); 354 | }) 355 | 356 | // Make it visible, when it's already playing 357 | .one('playing.' + PLUGIN_NAME, function() { 358 | $video.css({ 359 | visibility: 'visible', 360 | opacity: 1 361 | }); 362 | $wrapper.css('background-image', 'none'); 363 | }); 364 | 365 | // Resize event is available only for 'window' 366 | // Use another code solutions to detect DOM elements resizing 367 | $element.on('resize.' + PLUGIN_NAME, function() { 368 | if (settings.resizing) { 369 | vide.resize(); 370 | } 371 | }); 372 | 373 | // Append a video 374 | $wrapper.append($video); 375 | }; 376 | 377 | /** 378 | * Get a video element 379 | * @public 380 | * @returns {HTMLVideoElement} 381 | */ 382 | Vide.prototype.getVideoObject = function() { 383 | return this.$video[0]; 384 | }; 385 | 386 | /** 387 | * Resize a video background 388 | * @public 389 | */ 390 | Vide.prototype.resize = function() { 391 | if (!this.$video) { 392 | return; 393 | } 394 | 395 | var $wrapper = this.$wrapper; 396 | var $video = this.$video; 397 | var video = $video[0]; 398 | 399 | // Get a native video size 400 | var videoHeight = video.videoHeight; 401 | var videoWidth = video.videoWidth; 402 | 403 | // Get a wrapper size 404 | var wrapperHeight = $wrapper.height(); 405 | var wrapperWidth = $wrapper.width(); 406 | 407 | if (wrapperWidth / videoWidth > wrapperHeight / videoHeight) { 408 | $video.css({ 409 | 410 | // +2 pixels to prevent an empty space after transformation 411 | width: wrapperWidth + 2, 412 | height: 'auto' 413 | }); 414 | } else { 415 | $video.css({ 416 | width: 'auto', 417 | 418 | // +2 pixels to prevent an empty space after transformation 419 | height: wrapperHeight + 2 420 | }); 421 | } 422 | }; 423 | 424 | /** 425 | * Destroy a video background 426 | * @public 427 | */ 428 | Vide.prototype.destroy = function() { 429 | delete $[PLUGIN_NAME].lookup[this.index]; 430 | this.$video && this.$video.off(PLUGIN_NAME); 431 | this.$element.off(PLUGIN_NAME).removeData(PLUGIN_NAME); 432 | this.$wrapper.remove(); 433 | }; 434 | 435 | /** 436 | * Special plugin object for instances. 437 | * @public 438 | * @type {Object} 439 | */ 440 | $[PLUGIN_NAME] = { 441 | lookup: [] 442 | }; 443 | 444 | /** 445 | * Plugin constructor 446 | * @param {Object|String} path 447 | * @param {Object|String} options 448 | * @returns {JQuery} 449 | * @constructor 450 | */ 451 | $.fn[PLUGIN_NAME] = function(path, options) { 452 | var instance; 453 | 454 | this.each(function() { 455 | instance = $.data(this, PLUGIN_NAME); 456 | 457 | // Destroy the plugin instance if exists 458 | instance && instance.destroy(); 459 | 460 | // Create the plugin instance 461 | instance = new Vide(this, path, options); 462 | instance.index = $[PLUGIN_NAME].lookup.push(instance) - 1; 463 | $.data(this, PLUGIN_NAME, instance); 464 | }); 465 | 466 | return this; 467 | }; 468 | 469 | $(document).ready(function() { 470 | var $window = $(window); 471 | 472 | // Window resize event listener 473 | $window.on('resize.' + PLUGIN_NAME, function() { 474 | for (var len = $[PLUGIN_NAME].lookup.length, i = 0, instance; i < len; i++) { 475 | instance = $[PLUGIN_NAME].lookup[i]; 476 | 477 | if (instance && instance.settings.resizing) { 478 | instance.resize(); 479 | } 480 | } 481 | }); 482 | 483 | // https://github.com/VodkaBears/Vide/issues/68 484 | $window.on('unload.' + PLUGIN_NAME, function() { 485 | return false; 486 | }); 487 | 488 | // Auto initialization 489 | // Add 'data-vide-bg' attribute with a path to the video without extension 490 | // Also you can pass options throw the 'data-vide-options' attribute 491 | // 'data-vide-options' must be like 'muted: false, volume: 0.5' 492 | $(document).find('[data-' + PLUGIN_NAME + '-bg]').each(function(i, element) { 493 | var $element = $(element); 494 | var options = $element.data(PLUGIN_NAME + '-options'); 495 | var path = $element.data(PLUGIN_NAME + '-bg'); 496 | 497 | $element[PLUGIN_NAME](path, options); 498 | }); 499 | }); 500 | 501 | }); 502 | --------------------------------------------------------------------------------