diff --git a/.csscomb.json b/.csscomb.json index b191cd3..b4df350 100644 --- a/.csscomb.json +++ b/.csscomb.json @@ -2,7 +2,7 @@ "remove-empty-rulesets": true, "always-semicolon": true, "color-case": "lower", - "block-indent": " ", + "block-indent": " ", "color-shorthand": true, "element-case": "lower", "eof-newline": true, @@ -187,6 +187,11 @@ "-ms-animation-direction", "-o-animation-direction", "animation-direction", + "-webkit-animation-fill-mode", + "-moz-animation-fill-mode", + "-ms-animation-fill-mode", + "-o-animation-fill-mode", + "animation-fill-mode", "text-align", "-webkit-text-align-last", "-moz-text-align-last", diff --git a/.gitignore b/.gitignore index 1bf4f66..542377a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store .idea/ node_modules/ libs/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 176ba1a..5205524 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +### 1.0.0 +* Renamed the 'closeOnAnyClick' property to the 'closeOnOutsideClick'. +* Separated base and theme styles. +* Renamed the base files. +* Added the ability to use CSS mixins. +* Added `#destroy`. +* Renamed the events. +* Used states and CSS animations. +* Made restyling of the default theme. +* Added the watch task for Grunt. +* Added Autoprefixer. +* Used `backface-visibility` for the hardware acceleration instead of `translateZ`. +* Disabled the auto-resizing of text on mobile devices. +* Fixed the triggering of the close event, even if a modal is not opened. +* Added '#getState'. +* Changed names for the constants. +* Removed the default custom font. +* Introduced the `data-remodal-action` attribute. +* Made code refactoring. +* Improved anti-FOUC. +* Updated examples. +* Updated tests. +* Updated dependencies. + ### 0.6.4 * Protocol-relative URL for fonts. * Scroll to the top, when a modal is displayed. diff --git a/Gruntfile.js b/Gruntfile.js index 630da69..d80c31f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -14,7 +14,42 @@ module.exports = function(grunt) { ' *\n' + ' * Made by <%= pkg.author.name %>\n' + ' * Under <%= pkg.license %> License\n' + - ' */\n' + ' */\n\n' + }, + + autoprefixer: { + dist: { + src: 'dist/**/*.css' + }, + options: { + browsers: ['> 0.1%'], + cascade: false + } + }, + + browserSync: { + dev: { + bsFiles: { + src: ['dist/**/*', 'examples/**/*'] + }, + options: { + watchTask: true, + server: './' + } + } + }, + + concat: { + dist: { + files: { + 'dist/remodal.js': 'src/remodal.js', + 'dist/remodal.css': 'src/remodal.css', + 'dist/remodal-default-theme.css': 'src/remodal-default-theme.css' + }, + options: { + banner: '<%= meta.banner %>' + } + } }, connect: { @@ -25,6 +60,26 @@ module.exports = function(grunt) { } }, + csscomb: { + all: { + files: { + 'src/remodal.css': 'src/remodal.css', + 'src/remodal-default-theme.css': 'src/remodal-default-theme.css', + 'dist/remodal.css': 'dist/remodal.css', + 'dist/remodal-default-theme.css': 'dist/remodal-default-theme.css' + } + } + }, + + githooks: { + all: { + 'pre-commit': 'lint' + }, + options: { + command: 'node_modules/.bin/grunt' + } + }, + jshint: { gruntfile: { src: 'Gruntfile.js' @@ -33,7 +88,7 @@ module.exports = function(grunt) { src: 'src/**/*.js' }, test: { - src: 'test/**/*.js' + src: ['test/**/*.js', 'libs/jquery-loader.js'] }, options: { jshintrc: '.jshintrc' @@ -48,15 +103,7 @@ module.exports = function(grunt) { src: 'src/**/*.js' }, test: { - src: 'test/**/*.js' - } - }, - - csscomb: { - all: { - files: { - 'src/jquery.remodal.css': 'src/jquery.remodal.css' - } + src: ['test/**/*.js', 'libs/jquery-loader.js'] } }, @@ -76,22 +123,10 @@ module.exports = function(grunt) { } }, - concat: { - dist: { - files: { - 'dist/jquery.remodal.js': 'src/jquery.remodal.js', - 'dist/jquery.remodal.css': 'src/jquery.remodal.css' - }, - options: { - banner: '<%= meta.banner %>' - } - } - }, - uglify: { remodal: { files: { - 'dist/jquery.remodal.min.js': 'src/jquery.remodal.js' + 'dist/remodal.min.js': 'src/remodal.js' } }, options: { @@ -99,12 +134,13 @@ module.exports = function(grunt) { } }, - githooks: { - all: { - 'pre-commit': 'lint' + watch: { + src: { + files: ['src/**/*', 'examples/**/*'], + tasks: ['build'] }, options: { - command: 'node_modules/.bin/grunt' + spawn: false } } }); @@ -114,14 +150,17 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-qunit'); grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-autoprefixer'); + grunt.loadNpmTasks('grunt-browser-sync'); grunt.loadNpmTasks('grunt-csscomb'); grunt.loadNpmTasks('grunt-githooks'); grunt.loadNpmTasks('grunt-jscs'); - // Tasks. + // Tasks grunt.registerTask('lint', ['jshint', 'jscs']); grunt.registerTask('test', ['connect', 'lint', 'qunit']); - grunt.registerTask('default', [ - 'connect', 'csscomb', 'jshint', 'jscs', 'qunit', 'concat', 'uglify', 'githooks' - ]); + grunt.registerTask('build', ['concat', 'autoprefixer', 'csscomb', 'uglify', 'githooks']); + grunt.registerTask('bsync', ['browserSync', 'watch']); + grunt.registerTask('default', ['test', 'build']); }; diff --git a/README.md b/README.md index c8e5e46..0791b7f 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,43 @@ +[![NPM version](https://img.shields.io/npm/v/remodal.svg?style=flat)](https://npmjs.org/package/remodal) [![Bower version](https://badge.fury.io/bo/remodal.svg)](http://badge.fury.io/bo/remodal) [![Travis](https://travis-ci.org/VodkaBears/Remodal.svg?branch=master)](https://travis-ci.org/VodkaBears/Remodal) -[![devDependency Status](https://david-dm.org/vodkabears/remodal/dev-status.svg)](https://david-dm.org/vodkabears/remodal#info=devDependencies) Remodal ======= -Flat, responsive, lightweight, fast, easy customizable modal window plugin with declarative state notation and hash tracking. +Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking. + +![logo](https://raw.githubusercontent.com/VodkaBears/vodkabears.github.com/master/remodal/remodal.png) #IMPORTANT! -**v0.4.0 has incompatible changes.** +**v1.0.0 has a lot of incompatible changes.** ## Notes * All modern browsers are supported. -* Only webkit browsers have a blur effect in the default css theme. If you want a blur for other browsers, use this: https://github.com/Schepp/CSS-Filters-Polyfill, but it's not fast like a native css3 blur. -* IE8+. To enable IE8 styles add `lt-ie9` class to the `html` element, as modernizr does. -* Zepto support. +* IE8+. To enable IE8 styles add the `lt-ie9` class to the `html` element, as modernizr does. +* jQuery, jQuery2, Zepto support. +* Browserify support. ## Start -That's very simple to start using Remodal. - -[Download it](https://github.com/VodkaBears/Remodal/releases/latest). You can use bower: `bower install remodal`. +Download the latest version from [GitHub](https://github.com/VodkaBears/Remodal/releases/latest +) or via package managers: +``` +npm install remodal +bower install remodal +``` -Add this in the head section: +Include the CSS files from the dist folder in the head section: ```html - + + ``` -Add this before the `` or in the head: +Include the JS file from the dist folder before the ``: ```html - + ``` -Define the background container for the modal(for effects like a blur). It can be any simple content wrapper: +You can define the background container for the modal(for effects like a blur). It can be any simple content wrapper: ```html
...Page content... @@ -41,17 +47,19 @@ Define the background container for the modal(for effects like a blur). It can b And now create the modal dialog: ```html
-

Remodal

-

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. -

-
- Cancel - OK + +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking. +

+
+ +
``` +Don't use the `id` attribute, if you want to avoid the anchor jump, use `data-remodal-id`. + So, now you can call it with the hash: ```html Call the modal with data-remodal-id="modal" @@ -61,131 +69,187 @@ Or: Call the modal with data-remodal-id="modal" ``` -## Globals - -```html - - -``` - -#### namespace - -Base HTML class for your modals. CSS theme will need to be updated to reflect this. - -#### defaults - -Extends default settings. - ## Options -You can pass additional options by the `data-remodal-options` attribute. +You can pass additional options with the `data-remodal-options` attribute. ```html
-

Remodal

-

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. -

-
- Cancel - OK + data-remodal-options="hashTracking: false, closeOnOutsideClick: false"> + + +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking. +

+
+ +
``` #### hashTracking `Default: true` -To open the modal without the hash, use `data-remodal-target` attribute. +To open the modal without the hash, use the `data-remodal-target` attribute. ```html -Call the modal with data-remodal-id="modal" +Call the modal with data-remodal-id="modal" ``` #### closeOnConfirm `Default: true` -If set to true, closes a modal window after clicking confirm button. +If true, closes the modal window after clicking the confirm button. #### closeOnCancel `Default: true` -If set to true, closes a modal window after clicking cancel button. +If true, closes the modal window after clicking the cancel button. #### closeOnEscape `Default: true` -If set to true, closes a modal window after pressing ESC button. +If true, closes the modal window after pressing the ESC key. -#### closeOnAnyClick +#### closeOnOutsideClick `Default: true` -If set to true, closes a modal window by clicking anywhere on the page. +If true, closes the modal window by clicking anywhere on the page. + +#### modifier +`Default: ''` + +Modifier CSS classes for the modal that is added to the overlay, modal, background and wrapper (see [CSS](#css)). + +## Globals + +```html + + +``` + +#### NAMESPACE + +Base HTML class for your modals. CSS theme should be updated to reflect this. + +#### DEFAULTS + +Extends the default settings. + +## Initialization with JavaScript + +Do not set the 'remodal' class, if you prefer a JS initialization. +```html +
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking. +

+
+ +``` + +## Methods + +Get the instance of the modal and call a method: +```js +var inst = $('[data-remodal-id=modal]').remodal(); + +/** + * Opens the modal window + */ +inst.open(); + +/** + * Closes the modal window + */ +inst.close(); + +/** + * Returns a current state of the modal + * @returns {'closed'|'closing'|'opened'|'opening'} + */ +inst.getState(); + +/** + * Destroys the modal window + */ +inst.destroy(); +``` ## Events ```js -$(document).on('open', '.remodal', function () { - console.log('open'); +$(document).on('opening', '.remodal', function () { + console.log('Modal is opening'); }); $(document).on('opened', '.remodal', function () { - console.log('opened'); + console.log('Modal is opened'); }); -$(document).on('close', '.remodal', function (e) { - console.log('close'); +$(document).on('closing', '.remodal', function (e) { - // "confirmation", or "cancellation", or undefined - console.log(e.reason); + // Reason: 'confirmation', 'cancellation' + console.log('Modal is closing' + (e.reason ? ', reason: ' + e.reason : '')); }); $(document).on('closed', '.remodal', function (e) { - console.log('closed'); - // "confirmation", or "cancellation", or undefined - console.log(e.reason); + // Reason: 'confirmation', 'cancellation' + console.log('Modal is closed' + (e.reason ? ', reason: ' + e.reason : '')); }); -$(document).on('confirm', '.remodal', function () { - console.log('confirm'); +$(document).on('confirmation', '.remodal', function () { + console.log('Confirmation button is clicked'); }); -$(document).on('cancel', '.remodal', function () { - console.log('cancel'); +$(document).on('cancellation', '.remodal', function () { + console.log('Cancel button is clicked'); }); ``` -## Cool bro! But i don't like declarative style! +## CSS -Ok, don't set the class attribute and write something like this: -```html - -``` -Don't use `id` attribute, if you want to avoid the anchor jump. +#### Classes -## Methods +`.remodal` – the default class of modal dialogs. -Get the instance of the modal and call a method: -```js -var inst = $.remodal.lookup[$('[data-remodal-id=modal]').data('remodal')]; +`.remodal-wrapper` – the additional wrapper for the `.remodal`, it is not the overlay and used for the alignment. -// open the modal -inst.open(); +`.remodal-overlay` – the overlay of modal dialogs, it is under the wrapper. -// close the modal -inst.close(); +`.remodal-bg` – the background of modal dialogs, it is under the overlay and usually it is the wrapper of your content. You should add it on your own. + +The `remodal` prefix can be changed in the global settings. See [the `NAMESPACE` option](#namespace). + +#### States + +States are added to the `.remodal`, `.remodal-overlay`, `.remodal-bg`, `.remodal-wrapper` classes. + +List: +``` +.remodal-is-opening +.remodal-is-opened +.remodal-is-closing +.remodal-is-closed ``` +#### Modifier + +A modifier that is specified in the [options](#options) is added to the `.remodal`, `.remodal-overlay`, `.remodal-bg`, `.remodal-wrapper` classes. + ## License ``` diff --git a/bower.json b/bower.json index 2b65cc2..7b4e675 100644 --- a/bower.json +++ b/bower.json @@ -1,14 +1,15 @@ { "name": "remodal", - "version": "0.6.4", + "version": "1.0.0", "homepage": "http://vodkabears.github.io/remodal/", "authors": [ "Ilya Makarov " ], - "description": "Flat, responsive, lightweight, easy customizable modal window plugin with declarative state notation and hash tracking.", + "description": "Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking.", "main": [ - "dist/jquery.remodal.js", - "dist/jquery.remodal.css" + "dist/remodal.js", + "dist/remodal.css", + "dist/remodal-default-theme.css" ], "ignore": [ "**/.*", @@ -18,8 +19,7 @@ "test/", "*.md", "Gruntfile.js", - "package.json", - "remodal.jquery.json" + "package.json" ], "keywords": [ "jquery", @@ -34,16 +34,18 @@ "popin", "lightbox", "ui", - "zepto" + "zepto", + "synchronized", + "animations" ], "license": "MIT", "dependencies": { "jquery": "*" }, "devDependencies": { - "qunit": "~1.15.0", - "jquery": "jquery#^1.11.1", - "jquery2": "jquery#^2.1.1", - "zepto": "~1.1.4" + "qunit": "~1.18.0", + "jquery": "jquery#^1.11.3", + "jquery2": "jquery#^2.1.4", + "zepto": "~1.1.6" } } diff --git a/dist/jquery.remodal.css b/dist/jquery.remodal.css deleted file mode 100644 index 11a7c87..0000000 --- a/dist/jquery.remodal.css +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Remodal - v0.6.4 - * Flat, responsive, lightweight, easy customizable modal window plugin with declarative state notation and hash tracking. - * http://vodkabears.github.io/remodal/ - * - * Made by Ilya Makarov - * Under MIT License - */ -@import url(//fonts.googleapis.com/css?family=Exo+2:700,400&subset=latin,cyrillic); - -/* ========================================================================== - Remodal necessary styles - ========================================================================== */ - -/* Hide scroll bar */ - -html.remodal-is-locked { - overflow: hidden; -} - -/* Anti FOUC */ - -.remodal, -[data-remodal-id] { - visibility: hidden; -} - -/* Necessary styles of the overlay */ - -.remodal-overlay { - position: fixed; - z-index: 9999; - top: -5000px; - right: -5000px; - bottom: -5000px; - left: -5000px; - - display: none; -} - -/* Necessary styles of the wrapper */ - -.remodal-wrapper { - position: fixed; - z-index: 10000; - top: 0; - right: 0; - bottom: 0; - left: 0; - - display: none; - overflow: auto; - - text-align: center; - - -webkit-overflow-scrolling: touch; -} - -.remodal-wrapper:after { - display: inline-block; - - height: 100%; - margin-left: -0.05em; - - content: ""; -} - -/* Fix iPad, iPhone glitches */ - -.remodal-overlay, -.remodal-wrapper { - -webkit-transform: translateZ(0px); -} - -/* Necessary styles of the modal dialog */ - -.remodal { - position: relative; - - display: inline-block; -} - -/* Anti-zoom. Font-size should be >= 16px. */ - -.remodal select, -.remodal textarea, -.remodal input, -.remodal select:focus, -.remodal textarea:focus, -.remodal input:focus { - font-size: 16px; -} - -/* ========================================================================== - Remodal default theme - ========================================================================== */ - -/* Default theme font */ - -.remodal, -.remodal * { - font-family: "Exo 2", sans-serif; -} - -/* Background for effects */ - -.remodal-bg { - -webkit-transition: -webkit-filter 0.2s ease-out; - -moz-transition: -moz-filter 0.2s ease-out; - -o-transition: -o-filter 0.2s ease-out; - transition: filter 0.2s ease-out; -} - -body.remodal-is-active .remodal-bg { - -webkit-filter: blur(3px); - -ms-filter: blur(3px); - filter: blur(3px); - -moz-filter: blur(3px); - -o-filter: blur(3px); -} - -/* Default theme styles of the overlay */ - -.remodal-overlay { - -webkit-transition: opacity 0.2s ease-out; - -moz-transition: opacity 0.2s ease-out; - -o-transition: opacity 0.2s ease-out; - transition: opacity 0.2s ease-out; - - opacity: 0; - background: rgba(33, 36, 46, 0.95); -} - -body.remodal-is-active .remodal-overlay { - opacity: 1; -} - -/* Default theme styles of the modal dialog */ - -.remodal { - font-size: 16px; - - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - width: 100%; - min-height: 100%; - padding: 35px; - - -webkit-transition: -webkit-transform 0.2s ease-out, opacity 0.2s ease-out; - -moz-transition: -moz-transform 0.2s ease-out, opacity 0.2s ease-out; - -o-transition: -o-transform 0.2s ease-out, opacity 0.2s ease-out; - transition: transform 0.2s ease-out, opacity 0.2s ease-out; - -webkit-transform: scale(0.95); - -moz-transform: scale(0.95); - -ms-transform: scale(0.95); - -o-transform: scale(0.95); - transform: scale(0.95); - - opacity: 0; - color: #182a3c; - background: #f4f4f4; - background-clip: padding-box; -} - -body.remodal-is-active .remodal { - -webkit-transform: scale(1); - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - transform: scale(1); - - opacity: 1; -} - -/* Vertical align of the modal dialog */ - -.remodal, -.remodal-wrapper:after { - vertical-align: middle; -} - -/* Close button */ - -.remodal-close { - position: absolute; - top: 5px; - left: 5px; - - width: 28px; - height: 28px; - - -webkit-transition: background 0.2s ease-out; - -moz-transition: background 0.2s ease-out; - -o-transition: background 0.2s ease-out; - transition: background 0.2s ease-out; - text-decoration: none; - - -webkit-border-radius: 50%; - border-radius: 50%; -} - -.remodal-close:after { - font-family: Arial, "Helvetica CY", "Nimbus Sans L", sans-serif !important; - font-size: 28px; - line-height: 28px; - - display: block; - - content: "×"; - cursor: pointer; - -webkit-transition: all 0.2s ease-out; - -moz-transition: all 0.2s ease-out; - -o-transition: all 0.2s ease-out; - transition: all 0.2s ease-out; - text-decoration: none; - - color: #3e5368; -} - -.remodal-close:hover, -.remodal-close:active { - background: #3e5368; -} - -.remodal-close:hover.remodal-close:after, -.remodal-close:active.remodal-close:after { - color: #f4f4f4; -} - -/* Dialog buttons */ - -.remodal-confirm, -.remodal-cancel { - font-size: 10pt; - - display: inline-block; - - width: 120px; - margin: 0 0 5px 0; - padding: 9px 0; - - cursor: pointer; - -webkit-transition: all 0.2s ease-out; - -moz-transition: all 0.2s ease-out; - -o-transition: all 0.2s ease-out; - transition: all 0.2s ease-out; - text-align: center; - text-decoration: none; - - -webkit-border-radius: 6px; - border-radius: 6px; - background-clip: padding-box; -} - -.remodal-confirm { - color: #16a085; - border: 2px solid #16a085; - background: #f4f4f4; -} - -.remodal-confirm:hover, -.remodal-confirm:active { - color: #f4f4f4; - background: #16a085; -} - -.remodal-cancel { - color: #c0392b; - border: 2px solid #c0392b; - background: #f4f4f4; -} - -.remodal-cancel:hover, -.remodal-cancel:active { - color: #f4f4f4; - background: #c0392b; -} - -/* Media queries - ========================================================================== */ - -@media only screen and (min-width: 641px) { - .remodal { - max-width: 700px; - min-height: 0; - margin: 20px auto; - - -webkit-border-radius: 6px; - border-radius: 6px; - } -} - -/* IE8 - ========================================================================== */ - -.lt-ie9 .remodal-overlay { - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#F021242E, endColorstr=#F021242E); -} - -.lt-ie9 .remodal { - width: 700px; - min-height: 0; - margin: 20px auto; -} - -.lt-ie9 .remodal-close:after { - margin: auto; - - text-align: center; -} - -.lt-ie9 .remodal-close:hover, -.lt-ie9 .remodal-close:active { - background: transparent; -} - -.lt-ie9 .remodal-close:hover.remodal-close:after, -.lt-ie9 .remodal-close:active.remodal-close:after { - color: #3e5368; -} diff --git a/dist/jquery.remodal.js b/dist/jquery.remodal.js deleted file mode 100644 index 28278bf..0000000 --- a/dist/jquery.remodal.js +++ /dev/null @@ -1,522 +0,0 @@ -/* - * Remodal - v0.6.4 - * Flat, responsive, lightweight, easy customizable modal window plugin with declarative state notation and hash tracking. - * http://vodkabears.github.io/remodal/ - * - * Made by Ilya Makarov - * Under MIT License - */ -(function(root, factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], function($) { - return factory(root, $); - }); - } else if (typeof exports === 'object') { - factory(root, require('jquery')); - } else { - factory(root, root.jQuery || root.Zepto); - } -})(this, function(global, $) { - - 'use strict'; - - /** - * Name of the plugin - * @private - * @type {String} - */ - var pluginName = 'remodal'; - - /** - * Namespace for CSS and events - * @private - * @type {String} - */ - var namespace = global.remodalGlobals && global.remodalGlobals.namespace || pluginName; - - /** - * Default settings - * @private - * @type {Object} - */ - var defaults = $.extend({ - hashTracking: true, - closeOnConfirm: true, - closeOnCancel: true, - closeOnEscape: true, - closeOnAnyClick: true - }, global.remodalGlobals && global.remodalGlobals.defaults); - - /** - * Current modal - * @private - * @type {Remodal} - */ - var current; - - /** - * Scrollbar position - * @private - * @type {Number} - */ - var scrollTop; - - /** - * Get a transition duration in ms - * @private - * @param {jQuery} $elem - * @return {Number} - */ - function getTransitionDuration($elem) { - var duration = $elem.css('transition-duration') || - $elem.css('-webkit-transition-duration') || - $elem.css('-moz-transition-duration') || - $elem.css('-o-transition-duration') || - $elem.css('-ms-transition-duration') || - '0s'; - - var delay = $elem.css('transition-delay') || - $elem.css('-webkit-transition-delay') || - $elem.css('-moz-transition-delay') || - $elem.css('-o-transition-delay') || - $elem.css('-ms-transition-delay') || - '0s'; - - var max; - var len; - var num; - var i; - - duration = duration.split(', '); - delay = delay.split(', '); - - // The duration length is the same as the delay length - for (i = 0, len = duration.length, max = Number.NEGATIVE_INFINITY; i < len; i++) { - num = parseFloat(duration[i]) + parseFloat(delay[i]); - - if (num > max) { - max = num; - } - } - - return num * 1000; - } - - /** - * Get a scrollbar width - * @private - * @return {Number} - */ - function getScrollbarWidth() { - if ($(document.body).height() <= $(window).height()) { - return 0; - } - - var outer = document.createElement('div'); - var inner = document.createElement('div'); - var widthNoScroll; - var widthWithScroll; - - outer.style.visibility = 'hidden'; - outer.style.width = '100px'; - document.body.appendChild(outer); - - widthNoScroll = outer.offsetWidth; - - // Force scrollbars - outer.style.overflow = 'scroll'; - - // Add inner div - inner.style.width = '100%'; - outer.appendChild(inner); - - widthWithScroll = inner.offsetWidth; - - // Remove divs - outer.parentNode.removeChild(outer); - - return widthNoScroll - widthWithScroll; - } - - /** - * Lock the screen - * @private - */ - function lockScreen() { - var $html = $('html'); - var lockedClass = namespace + '-is-locked'; - var paddingRight; - var $body; - - if (!$html.hasClass(lockedClass)) { - $body = $(document.body); - - // Zepto does not support '-=', '+=' in the `css` method - paddingRight = parseInt($body.css('padding-right'), 10) + getScrollbarWidth(); - - $body.css('padding-right', paddingRight + 'px'); - $html.addClass(lockedClass); - } - } - - /** - * Unlock the screen - * @private - */ - function unlockScreen() { - var $html = $('html'); - var lockedClass = namespace + '-is-locked'; - var paddingRight; - var $body; - - if ($html.hasClass(lockedClass)) { - $body = $(document.body); - - // Zepto does not support '-=', '+=' in the `css` method - paddingRight = parseInt($body.css('padding-right'), 10) - getScrollbarWidth(); - - $body.css('padding-right', paddingRight + 'px'); - $html.removeClass(lockedClass); - } - } - - /** - * Parse a string with options - * @private - * @param str - * @returns {Object} - */ - function parseOptions(str) { - var obj = {}; - var arr; - var len; - var val; - var i; - - // Remove spaces before and after delimiters - str = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ','); - - // Parse a string - arr = str.split(','); - for (i = 0, len = arr.length; i < len; i++) { - arr[i] = arr[i].split(':'); - val = arr[i][1]; - - // Convert a string value if it is like a boolean - if (typeof val === 'string' || val instanceof String) { - val = val === 'true' || (val === 'false' ? false : val); - } - - // Convert a string value if it is like a number - if (typeof val === 'string' || val instanceof String) { - val = !isNaN(val) ? +val : val; - } - - obj[arr[i][0]] = val; - } - - return obj; - } - - /** - * Remodal constructor - * @param {jQuery} $modal - * @param {Object} options - * @constructor - */ - function Remodal($modal, options) { - var remodal = this; - var tdOverlay; - var tdModal; - var tdBg; - - remodal.settings = $.extend({}, defaults, options); - - // Build DOM - remodal.$body = $(document.body); - remodal.$overlay = $('.' + namespace + '-overlay'); - - if (!remodal.$overlay.length) { - remodal.$overlay = $('
').addClass(namespace + '-overlay'); - remodal.$body.append(remodal.$overlay); - } - - remodal.$bg = $('.' + namespace + '-bg'); - remodal.$closeButton = $('').addClass(namespace + '-close'); - remodal.$wrapper = $('
').addClass(namespace + '-wrapper'); - remodal.$modal = $modal; - remodal.$modal.addClass(namespace); - remodal.$modal.css('visibility', 'visible'); - - remodal.$modal.append(remodal.$closeButton); - remodal.$wrapper.append(remodal.$modal); - remodal.$body.append(remodal.$wrapper); - remodal.$confirmButton = remodal.$modal.find('.' + namespace + '-confirm'); - remodal.$cancelButton = remodal.$modal.find('.' + namespace + '-cancel'); - - // Calculate timeouts - tdOverlay = getTransitionDuration(remodal.$overlay); - tdModal = getTransitionDuration(remodal.$modal); - tdBg = getTransitionDuration(remodal.$bg); - remodal.td = Math.max(tdOverlay, tdModal, tdBg); - - // Add the close button event listener - remodal.$wrapper.on('click.' + namespace, '.' + namespace + '-close', function(e) { - e.preventDefault(); - - remodal.close(); - }); - - // Add the cancel button event listener - remodal.$wrapper.on('click.' + namespace, '.' + namespace + '-cancel', function(e) { - e.preventDefault(); - - remodal.$modal.trigger('cancel'); - - if (remodal.settings.closeOnCancel) { - remodal.close('cancellation'); - } - }); - - // Add the confirm button event listener - remodal.$wrapper.on('click.' + namespace, '.' + namespace + '-confirm', function(e) { - e.preventDefault(); - - remodal.$modal.trigger('confirm'); - - if (remodal.settings.closeOnConfirm) { - remodal.close('confirmation'); - } - }); - - // Add the keyboard event listener - $(document).on('keyup.' + namespace, function(e) { - if (e.keyCode === 27 && remodal.settings.closeOnEscape) { - remodal.close(); - } - }); - - // Add the overlay event listener - remodal.$wrapper.on('click.' + namespace, function(e) { - var $target = $(e.target); - - if (!$target.hasClass(namespace + '-wrapper')) { - return; - } - - if (remodal.settings.closeOnAnyClick) { - remodal.close(); - } - }); - - remodal.index = $[pluginName].lookup.push(remodal) - 1; - remodal.busy = false; - } - - /** - * Open a modal window - * @public - */ - Remodal.prototype.open = function() { - - // Check if the animation was completed - if (this.busy) { - return; - } - - var remodal = this; - var id; - - remodal.busy = true; - remodal.$modal.trigger('open'); - - id = remodal.$modal.attr('data-' + pluginName + '-id'); - - if (id && remodal.settings.hashTracking) { - scrollTop = $(window).scrollTop(); - location.hash = id; - } - - if (current && current !== remodal) { - current.$overlay.hide(); - current.$wrapper.hide(); - current.$body.removeClass(namespace + '-is-active'); - } - - current = remodal; - - lockScreen(); - remodal.$overlay.show(); - remodal.$wrapper.show(); - - setTimeout(function() { - remodal.$body.addClass(namespace + '-is-active'); - remodal.$wrapper.scrollTop(0); - - setTimeout(function() { - remodal.busy = false; - remodal.$modal.trigger('opened'); - }, remodal.td + 50); - }, 25); - }; - - /** - * Close a modal window - * @public - * @param {String|undefined} reason A reason to close - */ - Remodal.prototype.close = function(reason) { - - // Check if the animation was completed - if (this.busy) { - return; - } - - var remodal = this; - - remodal.busy = true; - remodal.$modal.trigger({ - type: 'close', - reason: reason - }); - - if ( - remodal.settings.hashTracking && - remodal.$modal.attr('data-' + pluginName + '-id') === location.hash.substr(1) - ) { - location.hash = ''; - $(window).scrollTop(scrollTop); - } - - remodal.$body.removeClass(namespace + '-is-active'); - - setTimeout(function() { - remodal.$overlay.hide(); - remodal.$wrapper.hide(); - unlockScreen(); - - remodal.busy = false; - remodal.$modal.trigger({ - type: 'closed', - reason: reason - }); - }, remodal.td + 50); - }; - - /** - * Special plugin object for instances. - * @public - * @type {Object} - */ - $[pluginName] = { - lookup: [] - }; - - /** - * Plugin constructor - * @param {Object} options - * @returns {JQuery} - * @constructor - */ - $.fn[pluginName] = function(opts) { - var instance; - var $elem; - - this.each(function(index, elem) { - $elem = $(elem); - - if ($elem.data(pluginName) == null) { - instance = new Remodal($elem, opts); - $elem.data(pluginName, instance.index); - - if ( - instance.settings.hashTracking && - $elem.attr('data-' + pluginName + '-id') === location.hash.substr(1) - ) { - instance.open(); - } - } else { - instance = $[pluginName].lookup[$elem.data(pluginName)]; - } - }); - - return instance; - }; - - $(document).ready(function() { - - // data-remodal-target opens a modal window with the special Id. - $(document).on('click', '[data-' + pluginName + '-target]', function(e) { - e.preventDefault(); - - var elem = e.currentTarget; - var id = elem.getAttribute('data-' + pluginName + '-target'); - var $target = $('[data-' + pluginName + '-id=' + id + ']'); - - $[pluginName].lookup[$target.data(pluginName)].open(); - }); - - // Auto initialization of modal windows. - // They should have the 'remodal' class attribute. - // Also you can write `data-remodal-options` attribute to pass params into the modal. - $(document).find('.' + namespace).each(function(i, container) { - var $container = $(container); - var options = $container.data(pluginName + '-options'); - - if (!options) { - options = {}; - } else if (typeof options === 'string' || options instanceof String) { - options = parseOptions(options); - } - - $container[pluginName](options); - }); - }); - - /** - * Hashchange handler - * @private - * @param {Event} e - * @param {Boolean} [closeOnEmptyHash=true] - */ - function hashHandler(e, closeOnEmptyHash) { - var id = location.hash.replace('#', ''); - var instance; - var $elem; - - if (typeof closeOnEmptyHash === 'undefined') { - closeOnEmptyHash = true; - } - - if (!id) { - if (closeOnEmptyHash) { - - // Check if we have currently opened modal and animation was completed - if (current && !current.busy && current.settings.hashTracking) { - current.close(); - } - } - } else { - - // Catch syntax error if your hash is bad - try { - $elem = $( - '[data-' + pluginName + '-id=' + - id.replace(new RegExp('/', 'g'), '\\/') + ']' - ); - } catch (err) {} - - if ($elem && $elem.length) { - instance = $[pluginName].lookup[$elem.data(pluginName)]; - - if (instance && instance.settings.hashTracking) { - instance.open(); - } - } - - } - } - - $(window).bind('hashchange.' + namespace, hashHandler); - -}); diff --git a/dist/jquery.remodal.min.js b/dist/jquery.remodal.min.js deleted file mode 100644 index cd03c70..0000000 --- a/dist/jquery.remodal.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Remodal - v0.6.4 - * Flat, responsive, lightweight, easy customizable modal window plugin with declarative state notation and hash tracking. - * http://vodkabears.github.io/remodal/ - * - * Made by Ilya Makarov - * Under MIT License - */ -!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(c){return b(a,c)}):"object"==typeof exports?b(a,require("jquery")):b(a,a.jQuery||a.Zepto)}(this,function(a,b){"use strict";function c(a){var b,c,d,e,f=a.css("transition-duration")||a.css("-webkit-transition-duration")||a.css("-moz-transition-duration")||a.css("-o-transition-duration")||a.css("-ms-transition-duration")||"0s",g=a.css("transition-delay")||a.css("-webkit-transition-delay")||a.css("-moz-transition-delay")||a.css("-o-transition-delay")||a.css("-ms-transition-delay")||"0s";for(f=f.split(", "),g=g.split(", "),e=0,c=f.length,b=Number.NEGATIVE_INFINITY;c>e;e++)d=parseFloat(f[e])+parseFloat(g[e]),d>b&&(b=d);return 1e3*d}function d(){if(b(document.body).height()<=b(window).height())return 0;var a,c,d=document.createElement("div"),e=document.createElement("div");return d.style.visibility="hidden",d.style.width="100px",document.body.appendChild(d),a=d.offsetWidth,d.style.overflow="scroll",e.style.width="100%",d.appendChild(e),c=e.offsetWidth,d.parentNode.removeChild(d),a-c}function e(){var a,c,e=b("html"),f=m+"-is-locked";e.hasClass(f)||(c=b(document.body),a=parseInt(c.css("padding-right"),10)+d(),c.css("padding-right",a+"px"),e.addClass(f))}function f(){var a,c,e=b("html"),f=m+"-is-locked";e.hasClass(f)&&(c=b(document.body),a=parseInt(c.css("padding-right"),10)-d(),c.css("padding-right",a+"px"),e.removeClass(f))}function g(a){var b,c,d,e,f={};for(a=a.replace(/\s*:\s*/g,":").replace(/\s*,\s*/g,","),b=a.split(","),e=0,c=b.length;c>e;e++)b[e]=b[e].split(":"),d=b[e][1],("string"==typeof d||d instanceof String)&&(d="true"===d||("false"===d?!1:d)),("string"==typeof d||d instanceof String)&&(d=isNaN(d)?d:+d),f[b[e][0]]=d;return f}function h(a,d){var e,f,g,h=this;h.settings=b.extend({},n,d),h.$body=b(document.body),h.$overlay=b("."+m+"-overlay"),h.$overlay.length||(h.$overlay=b("
").addClass(m+"-overlay"),h.$body.append(h.$overlay)),h.$bg=b("."+m+"-bg"),h.$closeButton=b('').addClass(m+"-close"),h.$wrapper=b("
").addClass(m+"-wrapper"),h.$modal=a,h.$modal.addClass(m),h.$modal.css("visibility","visible"),h.$modal.append(h.$closeButton),h.$wrapper.append(h.$modal),h.$body.append(h.$wrapper),h.$confirmButton=h.$modal.find("."+m+"-confirm"),h.$cancelButton=h.$modal.find("."+m+"-cancel"),e=c(h.$overlay),f=c(h.$modal),g=c(h.$bg),h.td=Math.max(e,f,g),h.$wrapper.on("click."+m,"."+m+"-close",function(a){a.preventDefault(),h.close()}),h.$wrapper.on("click."+m,"."+m+"-cancel",function(a){a.preventDefault(),h.$modal.trigger("cancel"),h.settings.closeOnCancel&&h.close("cancellation")}),h.$wrapper.on("click."+m,"."+m+"-confirm",function(a){a.preventDefault(),h.$modal.trigger("confirm"),h.settings.closeOnConfirm&&h.close("confirmation")}),b(document).on("keyup."+m,function(a){27===a.keyCode&&h.settings.closeOnEscape&&h.close()}),h.$wrapper.on("click."+m,function(a){var c=b(a.target);c.hasClass(m+"-wrapper")&&h.settings.closeOnAnyClick&&h.close()}),h.index=b[l].lookup.push(h)-1,h.busy=!1}function i(a,c){var d,e,f=location.hash.replace("#","");if("undefined"==typeof c&&(c=!0),f){try{e=b("[data-"+l+"-id="+f.replace(new RegExp("/","g"),"\\/")+"]")}catch(g){}e&&e.length&&(d=b[l].lookup[e.data(l)],d&&d.settings.hashTracking&&d.open())}else c&&j&&!j.busy&&j.settings.hashTracking&&j.close()}var j,k,l="remodal",m=a.remodalGlobals&&a.remodalGlobals.namespace||l,n=b.extend({hashTracking:!0,closeOnConfirm:!0,closeOnCancel:!0,closeOnEscape:!0,closeOnAnyClick:!0},a.remodalGlobals&&a.remodalGlobals.defaults);h.prototype.open=function(){if(!this.busy){var a,c=this;c.busy=!0,c.$modal.trigger("open"),a=c.$modal.attr("data-"+l+"-id"),a&&c.settings.hashTracking&&(k=b(window).scrollTop(),location.hash=a),j&&j!==c&&(j.$overlay.hide(),j.$wrapper.hide(),j.$body.removeClass(m+"-is-active")),j=c,e(),c.$overlay.show(),c.$wrapper.show(),setTimeout(function(){c.$body.addClass(m+"-is-active"),c.$wrapper.scrollTop(0),setTimeout(function(){c.busy=!1,c.$modal.trigger("opened")},c.td+50)},25)}},h.prototype.close=function(a){if(!this.busy){var c=this;c.busy=!0,c.$modal.trigger({type:"close",reason:a}),c.settings.hashTracking&&c.$modal.attr("data-"+l+"-id")===location.hash.substr(1)&&(location.hash="",b(window).scrollTop(k)),c.$body.removeClass(m+"-is-active"),setTimeout(function(){c.$overlay.hide(),c.$wrapper.hide(),f(),c.busy=!1,c.$modal.trigger({type:"closed",reason:a})},c.td+50)}},b[l]={lookup:[]},b.fn[l]=function(a){var c,d;return this.each(function(e,f){d=b(f),null==d.data(l)?(c=new h(d,a),d.data(l,c.index),c.settings.hashTracking&&d.attr("data-"+l+"-id")===location.hash.substr(1)&&c.open()):c=b[l].lookup[d.data(l)]}),c},b(document).ready(function(){b(document).on("click","[data-"+l+"-target]",function(a){a.preventDefault();var c=a.currentTarget,d=c.getAttribute("data-"+l+"-target"),e=b("[data-"+l+"-id="+d+"]");b[l].lookup[e.data(l)].open()}),b(document).find("."+m).each(function(a,c){var d=b(c),e=d.data(l+"-options");e?("string"==typeof e||e instanceof String)&&(e=g(e)):e={},d[l](e)})}),b(window).bind("hashchange."+m,i)}); \ No newline at end of file diff --git a/dist/remodal-default-theme.css b/dist/remodal-default-theme.css new file mode 100644 index 0000000..113ad68 --- /dev/null +++ b/dist/remodal-default-theme.css @@ -0,0 +1,304 @@ +/* + * Remodal - v1.0.0 + * Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking. + * http://vodkabears.github.io/remodal/ + * + * Made by Ilya Makarov + * Under MIT License + */ + +/* ========================================================================== + Remodal's default mobile first theme + ========================================================================== */ + +/* Default theme styles for the background */ + +.remodal-bg.remodal-is-opening, +.remodal-bg.remodal-is-opened { + -webkit-filter: blur(3px); + filter: blur(3px); +} + +/* Default theme styles of the overlay */ + +.remodal-overlay { + background: rgba(43, 46, 56, 0.9); +} + +.remodal-overlay.remodal-is-opening, +.remodal-overlay.remodal-is-closing { + -webkit-animation-fill-mode: forwards; + animation-fill-mode: forwards; +} + +.remodal-overlay.remodal-is-opening { + -webkit-animation: remodal-overlay-opening-keyframes 0.3s; + animation: remodal-overlay-opening-keyframes 0.3s; +} + +.remodal-overlay.remodal-is-closing { + -webkit-animation: remodal-overlay-closing-keyframes 0.3s; + animation: remodal-overlay-closing-keyframes 0.3s; +} + +/* Default theme styles of the wrapper */ + +.remodal-wrapper { + padding: 10px 10px 0; +} + +/* Default theme styles of the modal dialog */ + +.remodal { + -webkit-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + margin-bottom: 10px; + padding: 35px; + + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + + color: #2b2e38; + background: #fff; +} + +.remodal.remodal-is-opening, +.remodal.remodal-is-closing { + -webkit-animation-fill-mode: forwards; + animation-fill-mode: forwards; +} + +.remodal.remodal-is-opening { + -webkit-animation: remodal-opening-keyframes 0.3s; + animation: remodal-opening-keyframes 0.3s; +} + +.remodal.remodal-is-closing { + -webkit-animation: remodal-closing-keyframes 0.3s; + animation: remodal-closing-keyframes 0.3s; +} + +/* Vertical align of the modal dialog */ + +.remodal, +.remodal-wrapper:after { + vertical-align: middle; +} + +/* Close button */ + +.remodal-close { + position: absolute; + top: 0; + left: 0; + + display: block; + overflow: visible; + + width: 35px; + height: 35px; + margin: 0; + padding: 0; + + cursor: pointer; + -webkit-transition: color 0.2s; + transition: color 0.2s; + text-decoration: none; + + color: #95979c; + border: 0; + outline: 0; + background: transparent; +} + +.remodal-close:hover { + color: #2b2e38; +} + +.remodal-close:before { + font-family: Arial, "Helvetica CY", "Nimbus Sans L", sans-serif !important; + font-size: 25px; + line-height: 35px; + + position: absolute; + top: 0; + left: 0; + + display: block; + + width: 35px; + + content: "\00d7"; + text-align: center; +} + +/* Dialog buttons */ + +.remodal-confirm, +.remodal-cancel { + font: inherit; + + display: inline-block; + overflow: visible; + + min-width: 110px; + margin: 0; + padding: 12px 0; + + cursor: pointer; + -webkit-transition: background 0.2s; + transition: background 0.2s; + text-align: center; + vertical-align: middle; + text-decoration: none; + + border: 0; + outline: 0; +} + +.remodal-confirm { + color: #fff; + background: #81c784; +} + +.remodal-confirm:hover { + background: #66bb6a; +} + +.remodal-cancel { + color: #fff; + background: #e57373; +} + +.remodal-cancel:hover { + background: #ef5350; +} + +/* Remove inner padding and border in Firefox 4+ for the button tag. */ + +.remodal-confirm::-moz-focus-inner, +.remodal-cancel::-moz-focus-inner { + padding: 0; + + border: 0; +} + +/* Keyframes + ========================================================================== */ + +@-webkit-keyframes remodal-opening-keyframes { + from { + -webkit-transform: scale(1.05); + transform: scale(1.05); + + opacity: 0; + } + to { + -webkit-transform: none; + transform: none; + + opacity: 1; + } +} + +@keyframes remodal-opening-keyframes { + from { + -webkit-transform: scale(1.05); + transform: scale(1.05); + + opacity: 0; + } + to { + -webkit-transform: none; + transform: none; + + opacity: 1; + } +} + +@-webkit-keyframes remodal-closing-keyframes { + from { + -webkit-transform: scale(1); + transform: scale(1); + + opacity: 1; + } + to { + -webkit-transform: scale(0.95); + transform: scale(0.95); + + opacity: 0; + } +} + +@keyframes remodal-closing-keyframes { + from { + -webkit-transform: scale(1); + transform: scale(1); + + opacity: 1; + } + to { + -webkit-transform: scale(0.95); + transform: scale(0.95); + + opacity: 0; + } +} + +@-webkit-keyframes remodal-overlay-opening-keyframes { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes remodal-overlay-opening-keyframes { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@-webkit-keyframes remodal-overlay-closing-keyframes { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes remodal-overlay-closing-keyframes { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +/* Media queries + ========================================================================== */ + +@media only screen and (min-width: 641px) { + .remodal { + max-width: 700px; + } +} + +/* IE8 + ========================================================================== */ + +.lt-ie9 .remodal-overlay { + background: #2b2e38; +} + +.lt-ie9 .remodal { + width: 700px; +} diff --git a/dist/remodal.css b/dist/remodal.css new file mode 100644 index 0000000..8a61081 --- /dev/null +++ b/dist/remodal.css @@ -0,0 +1,89 @@ +/* + * Remodal - v1.0.0 + * Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking. + * http://vodkabears.github.io/remodal/ + * + * Made by Ilya Makarov + * Under MIT License + */ + +/* ========================================================================== + Remodal's necessary styles + ========================================================================== */ + +/* Hide scroll bar */ + +html.remodal-is-locked { + overflow: hidden; +} + +/* Anti FOUC */ + +.remodal, +[data-remodal-id] { + display: none; +} + +/* Necessary styles of the overlay */ + +.remodal-overlay { + position: fixed; + z-index: 9999; + top: -5000px; + right: -5000px; + bottom: -5000px; + left: -5000px; + + display: none; +} + +/* Necessary styles of the wrapper */ + +.remodal-wrapper { + position: fixed; + z-index: 10000; + top: 0; + right: 0; + bottom: 0; + left: 0; + + display: none; + overflow: auto; + + text-align: center; + + -webkit-overflow-scrolling: touch; +} + +.remodal-wrapper:after { + display: inline-block; + + height: 100%; + margin-left: -0.05em; + + content: ""; +} + +/* Fix iPad, iPhone glitches */ + +.remodal-overlay, +.remodal-wrapper { + -webkit-backface-visibility: hidden; + backface-visibility: hidden; +} + +/* Necessary styles of the modal dialog */ + +.remodal { + position: relative; + + -webkit-text-size-adjust: 100%; + -moz-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + text-size-adjust: 100%; +} + +.remodal-is-initialized { + /* Disable Anti-FOUC */ + display: inline-block; +} diff --git a/dist/remodal.js b/dist/remodal.js new file mode 100644 index 0000000..2d2861b --- /dev/null +++ b/dist/remodal.js @@ -0,0 +1,752 @@ +/* + * Remodal - v1.0.0 + * Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking. + * http://vodkabears.github.io/remodal/ + * + * Made by Ilya Makarov + * Under MIT License + */ + +!(function(root, factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], function($) { + return factory(root, $); + }); + } else if (typeof exports === 'object') { + factory(root, require('jquery')); + } else { + factory(root, root.jQuery || root.Zepto); + } +})(this, function(global, $) { + + 'use strict'; + + /** + * Name of the plugin + * @private + * @const + * @type {String} + */ + var PLUGIN_NAME = 'remodal'; + + /** + * Namespace for CSS and events + * @private + * @const + * @type {String} + */ + var NAMESPACE = global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.NAMESPACE || PLUGIN_NAME; + + /** + * Animationstart event with vendor prefixes + * @private + * @const + * @type {String} + */ + var ANIMATIONSTART_EVENTS = $.map( + ['animationstart', 'webkitAnimationStart', 'MSAnimationStart', 'oAnimationStart'], + + function(eventName) { + return eventName + '.' + NAMESPACE; + } + + ).join(' '); + + /** + * Animationend event with vendor prefixes + * @private + * @const + * @type {String} + */ + var ANIMATIONEND_EVENTS = $.map( + ['animationend', 'webkitAnimationEnd', 'MSAnimationEnd', 'oAnimationEnd'], + + function(eventName) { + return eventName + '.' + NAMESPACE; + } + + ).join(' '); + + /** + * Default settings + * @private + * @const + * @type {Object} + */ + var DEFAULTS = $.extend({ + hashTracking: true, + closeOnConfirm: true, + closeOnCancel: true, + closeOnEscape: true, + closeOnOutsideClick: true, + modifier: '' + }, global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.DEFAULTS); + + /** + * States of the Remodal + * @private + * @const + * @enum {String} + */ + var STATES = { + CLOSING: 'closing', + CLOSED: 'closed', + OPENING: 'opening', + OPENED: 'opened' + }; + + /** + * Reasons of the state change. + * @private + * @const + * @enum {String} + */ + var STATE_CHANGE_REASONS = { + CONFIRMATION: 'confirmation', + CANCELLATION: 'cancellation' + }; + + /** + * Is animation supported? + * @private + * @const + * @type {Boolean} + */ + var IS_ANIMATION = (function() { + var style = document.createElement('div').style; + + return style.animationName !== undefined || + style.WebkitAnimationName !== undefined || + style.MozAnimationName !== undefined || + style.msAnimationName !== undefined || + style.OAnimationName !== undefined; + })(); + + /** + * Current modal + * @private + * @type {Remodal} + */ + var current; + + /** + * Scrollbar position + * @private + * @type {Number} + */ + var scrollTop; + + /** + * Returns an animation duration + * @private + * @param {jQuery} $elem + * @returns {Number} + */ + function getAnimationDuration($elem) { + if ( + IS_ANIMATION && + $elem.css('animation-name') === 'none' && + $elem.css('-webkit-animation-name') === 'none' && + $elem.css('-moz-animation-name') === 'none' && + $elem.css('-o-animation-name') === 'none' && + $elem.css('-ms-animation-name') === 'none' + ) { + return 0; + } + + var duration = $elem.css('animation-duration') || + $elem.css('-webkit-animation-duration') || + $elem.css('-moz-animation-duration') || + $elem.css('-o-animation-duration') || + $elem.css('-ms-animation-duration') || + '0s'; + + var delay = $elem.css('animation-delay') || + $elem.css('-webkit-animation-delay') || + $elem.css('-moz-animation-delay') || + $elem.css('-o-animation-delay') || + $elem.css('-ms-animation-delay') || + '0s'; + + var iterationCount = $elem.css('animation-iteration-count') || + $elem.css('-webkit-animation-iteration-count') || + $elem.css('-moz-animation-iteration-count') || + $elem.css('-o-animation-iteration-count') || + $elem.css('-ms-animation-iteration-count') || + '1'; + + var max; + var len; + var num; + var i; + + duration = duration.split(', '); + delay = delay.split(', '); + iterationCount = iterationCount.split(', '); + + // The 'duration' size is the same as the 'delay' size + for (i = 0, len = duration.length, max = Number.NEGATIVE_INFINITY; i < len; i++) { + num = parseFloat(duration[i]) * parseInt(iterationCount[i], 10) + parseFloat(delay[i]); + + if (num > max) { + max = num; + } + } + + return num; + } + + /** + * Returns a scrollbar width + * @private + * @returns {Number} + */ + function getScrollbarWidth() { + if ($(document.body).height() <= $(window).height()) { + return 0; + } + + var outer = document.createElement('div'); + var inner = document.createElement('div'); + var widthNoScroll; + var widthWithScroll; + + outer.style.visibility = 'hidden'; + outer.style.width = '100px'; + document.body.appendChild(outer); + + widthNoScroll = outer.offsetWidth; + + // Force scrollbars + outer.style.overflow = 'scroll'; + + // Add inner div + inner.style.width = '100%'; + outer.appendChild(inner); + + widthWithScroll = inner.offsetWidth; + + // Remove divs + outer.parentNode.removeChild(outer); + + return widthNoScroll - widthWithScroll; + } + + /** + * Locks the screen + * @private + */ + function lockScreen() { + var $html = $('html'); + var lockedClass = NAMESPACE + '-is-locked'; + var paddingRight; + var $body; + + if (!$html.hasClass(lockedClass)) { + $body = $(document.body); + + // Zepto does not support '-=', '+=' in the `css` method + paddingRight = parseInt($body.css('padding-right'), 10) + getScrollbarWidth(); + + $body.css('padding-right', paddingRight + 'px'); + $html.addClass(lockedClass); + } + } + + /** + * Unlocks the screen + * @private + */ + function unlockScreen() { + var $html = $('html'); + var lockedClass = NAMESPACE + '-is-locked'; + var paddingRight; + var $body; + + if ($html.hasClass(lockedClass)) { + $body = $(document.body); + + // Zepto does not support '-=', '+=' in the `css` method + paddingRight = parseInt($body.css('padding-right'), 10) - getScrollbarWidth(); + + $body.css('padding-right', paddingRight + 'px'); + $html.removeClass(lockedClass); + } + } + + /** + * Sets a state for an instance + * @private + * @param {Remodal} instance + * @param {STATES} state + * @param {Boolean} isSilent If true, Remodal does not trigger events + * @param {String} Reason of a state change. + */ + function setState(instance, state, isSilent, reason) { + instance.$bg + .removeClass( + NAMESPACE + '-is-' + STATES.CLOSING + ' ' + + NAMESPACE + '-is-' + STATES.OPENING + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED + ' ' + + NAMESPACE + '-is-' + STATES.OPENED) + .addClass(NAMESPACE + '-is-' + state); + + instance.$overlay + .removeClass( + NAMESPACE + '-is-' + STATES.CLOSING + ' ' + + NAMESPACE + '-is-' + STATES.OPENING + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED + ' ' + + NAMESPACE + '-is-' + STATES.OPENED) + .addClass(NAMESPACE + '-is-' + state); + + instance.$wrapper + .removeClass( + NAMESPACE + '-is-' + STATES.CLOSING + ' ' + + NAMESPACE + '-is-' + STATES.OPENING + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED + ' ' + + NAMESPACE + '-is-' + STATES.OPENED) + .addClass(NAMESPACE + '-is-' + state); + + instance.$modal + .removeClass( + NAMESPACE + '-is-' + STATES.CLOSING + ' ' + + NAMESPACE + '-is-' + STATES.OPENING + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED + ' ' + + NAMESPACE + '-is-' + STATES.OPENED) + .addClass(NAMESPACE + '-is-' + state); + + instance.state = state; + !isSilent && instance.$modal.trigger({ + type: state, + reason: reason + }, [{ reason: reason }]); + } + + /** + * Synchronizes with the animation + * @param {Function} doBeforeAnimation + * @param {Function} doAfterAnimation + * @param {Remodal} instance + */ + function syncWithAnimation(doBeforeAnimation, doAfterAnimation, instance) { + var runningAnimationsCount = 0; + + var handleAnimationStart = function(e) { + if (e.target !== this) { + return; + } + + runningAnimationsCount++; + }; + + var handleAnimationEnd = function(e) { + if (e.target !== this) { + return; + } + + if (--runningAnimationsCount === 0) { + + // Remove event listeners + $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { + instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS); + }); + + doAfterAnimation(); + } + }; + + $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { + instance[elemName] + .on(ANIMATIONSTART_EVENTS, handleAnimationStart) + .on(ANIMATIONEND_EVENTS, handleAnimationEnd); + }); + + doBeforeAnimation(); + + // If the animation is not supported by a browser or its duration is 0 + if ( + getAnimationDuration(instance.$bg) === 0 && + getAnimationDuration(instance.$overlay) === 0 && + getAnimationDuration(instance.$wrapper) === 0 && + getAnimationDuration(instance.$modal) === 0 + ) { + + // Remove event listeners + $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { + instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS); + }); + + doAfterAnimation(); + } + } + + /** + * Closes immediately + * @private + * @param {Remodal} instance + */ + function halt(instance) { + if (instance.state === STATES.CLOSED) { + return; + } + + $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { + instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS); + }); + + instance.$bg.removeClass(instance.settings.modifier); + instance.$overlay.removeClass(instance.settings.modifier).hide(); + instance.$wrapper.hide(); + unlockScreen(); + setState(instance, STATES.CLOSED, true); + } + + /** + * Parses a string with options + * @private + * @param str + * @returns {Object} + */ + function parseOptions(str) { + var obj = {}; + var arr; + var len; + var val; + var i; + + // Remove spaces before and after delimiters + str = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ','); + + // Parse a string + arr = str.split(','); + for (i = 0, len = arr.length; i < len; i++) { + arr[i] = arr[i].split(':'); + val = arr[i][1]; + + // Convert a string value if it is like a boolean + if (typeof val === 'string' || val instanceof String) { + val = val === 'true' || (val === 'false' ? false : val); + } + + // Convert a string value if it is like a number + if (typeof val === 'string' || val instanceof String) { + val = !isNaN(val) ? +val : val; + } + + obj[arr[i][0]] = val; + } + + return obj; + } + + /** + * Handles the hashchange event + * @private + * @listens hashchange + */ + function handleHashChangeEvent() { + var id = location.hash.replace('#', ''); + var instance; + var $elem; + + if (!id) { + + // Check if we have currently opened modal and animation was completed + if (current && current.state === STATES.OPENED && current.settings.hashTracking) { + current.close(); + } + } else { + + // Catch syntax error if your hash is bad + try { + $elem = $( + '[data-' + PLUGIN_NAME + '-id=' + + id.replace(new RegExp('/', 'g'), '\\/') + ']' + ); + } catch (err) {} + + if ($elem && $elem.length) { + instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)]; + + if (instance && instance.settings.hashTracking) { + instance.open(); + } + } + + } + } + + /** + * Remodal constructor + * @constructor + * @param {jQuery} $modal + * @param {Object} options + */ + function Remodal($modal, options) { + var $body = $(document.body); + var remodal = this; + + remodal.settings = $.extend({}, DEFAULTS, options); + remodal.index = $[PLUGIN_NAME].lookup.push(remodal) - 1; + remodal.state = STATES.CLOSED; + + remodal.$overlay = $('.' + NAMESPACE + '-overlay'); + + if (!remodal.$overlay.length) { + remodal.$overlay = $('
').addClass(NAMESPACE + '-overlay ' + NAMESPACE + '-is-' + STATES.CLOSED).hide(); + $body.append(remodal.$overlay); + } + + remodal.$bg = $('.' + NAMESPACE + '-bg').addClass(NAMESPACE + '-is-' + STATES.CLOSED); + remodal.$modal = $modal; + remodal.$modal.addClass( + NAMESPACE + '-is-initialized' + ' ' + + NAMESPACE + ' ' + remodal.settings.modifier + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED); + + remodal.$wrapper = $('
') + .addClass( + NAMESPACE + '-wrapper ' + remodal.settings.modifier + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED) + .hide() + .append(remodal.$modal); + $body.append(remodal.$wrapper); + + // Add the event listener for the close button + remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + NAMESPACE + '-action="close"]', function(e) { + e.preventDefault(); + + remodal.close(); + }); + + // Add the event listener for the cancel button + remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + NAMESPACE + '-action="cancel"]', function(e) { + e.preventDefault(); + + remodal.$modal.trigger(STATE_CHANGE_REASONS.CANCELLATION); + + if (remodal.settings.closeOnCancel) { + remodal.close(STATE_CHANGE_REASONS.CANCELLATION); + } + }); + + // Add the event listener for the confirm button + remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + NAMESPACE + '-action="confirm"]', function(e) { + e.preventDefault(); + + remodal.$modal.trigger(STATE_CHANGE_REASONS.CONFIRMATION); + + if (remodal.settings.closeOnConfirm) { + remodal.close(STATE_CHANGE_REASONS.CONFIRMATION); + } + }); + + // Add the event listener for the overlay + remodal.$wrapper.on('click.' + NAMESPACE, function(e) { + var $target = $(e.target); + + if (!$target.hasClass(NAMESPACE + '-wrapper')) { + return; + } + + if (remodal.settings.closeOnOutsideClick) { + remodal.close(); + } + }); + } + + /** + * Opens a modal window + * @public + */ + Remodal.prototype.open = function() { + var remodal = this; + var id; + + // Check if the animation was completed + if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING) { + return; + } + + id = remodal.$modal.attr('data-' + PLUGIN_NAME + '-id'); + + if (id && remodal.settings.hashTracking) { + scrollTop = $(window).scrollTop(); + location.hash = id; + } + + if (current && current !== remodal) { + halt(current); + } + + current = remodal; + lockScreen(); + remodal.$bg.addClass(remodal.settings.modifier); + remodal.$overlay.addClass(remodal.settings.modifier).show(); + remodal.$wrapper.show().scrollTop(0); + + syncWithAnimation( + function() { + setState(remodal, STATES.OPENING); + }, + + function() { + setState(remodal, STATES.OPENED); + }, + + remodal); + }; + + /** + * Closes a modal window + * @public + * @param {String} reason + */ + Remodal.prototype.close = function(reason) { + var remodal = this; + + // Check if the animation was completed + if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING) { + return; + } + + if ( + remodal.settings.hashTracking && + remodal.$modal.attr('data-' + PLUGIN_NAME + '-id') === location.hash.substr(1) + ) { + location.hash = ''; + $(window).scrollTop(scrollTop); + } + + syncWithAnimation( + function() { + setState(remodal, STATES.CLOSING, false, reason); + }, + + function() { + remodal.$bg.removeClass(remodal.settings.modifier); + remodal.$overlay.removeClass(remodal.settings.modifier).hide(); + remodal.$wrapper.hide(); + unlockScreen(); + + setState(remodal, STATES.CLOSED, false, reason); + }, + + remodal); + }; + + /** + * Returns a current state of a modal + * @public + * @returns {STATES} + */ + Remodal.prototype.getState = function() { + return this.state; + }; + + /** + * Destroys a modal + * @public + */ + Remodal.prototype.destroy = function() { + var lookup = $[PLUGIN_NAME].lookup; + var instanceCount; + + halt(this); + this.$wrapper.remove(); + + delete lookup[this.index]; + instanceCount = $.grep(lookup, function(instance) { + return !!instance; + }).length; + + if (instanceCount === 0) { + this.$overlay.remove(); + this.$bg.removeClass( + NAMESPACE + '-is-' + STATES.CLOSING + ' ' + + NAMESPACE + '-is-' + STATES.OPENING + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED + ' ' + + NAMESPACE + '-is-' + STATES.OPENED); + } + }; + + /** + * Special plugin object for instances + * @public + * @type {Object} + */ + $[PLUGIN_NAME] = { + lookup: [] + }; + + /** + * Plugin constructor + * @constructor + * @param {Object} options + * @returns {JQuery} + */ + $.fn[PLUGIN_NAME] = function(opts) { + var instance; + var $elem; + + this.each(function(index, elem) { + $elem = $(elem); + + if ($elem.data(PLUGIN_NAME) == null) { + instance = new Remodal($elem, opts); + $elem.data(PLUGIN_NAME, instance.index); + + if ( + instance.settings.hashTracking && + $elem.attr('data-' + PLUGIN_NAME + '-id') === location.hash.substr(1) + ) { + instance.open(); + } + } else { + instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)]; + } + }); + + return instance; + }; + + $(document).ready(function() { + + // data-remodal-target opens a modal window with the special Id + $(document).on('click', '[data-' + PLUGIN_NAME + '-target]', function(e) { + e.preventDefault(); + + var elem = e.currentTarget; + var id = elem.getAttribute('data-' + PLUGIN_NAME + '-target'); + var $target = $('[data-' + PLUGIN_NAME + '-id=' + id + ']'); + + $[PLUGIN_NAME].lookup[$target.data(PLUGIN_NAME)].open(); + }); + + // Auto initialization of modal windows + // They should have the 'remodal' class attribute + // Also you can write the `data-remodal-options` attribute to pass params into the modal + $(document).find('.' + NAMESPACE).each(function(i, container) { + var $container = $(container); + var options = $container.data(PLUGIN_NAME + '-options'); + + if (!options) { + options = {}; + } else if (typeof options === 'string' || options instanceof String) { + options = parseOptions(options); + } + + $container[PLUGIN_NAME](options); + }); + + // Handles the keyup event + $(document).on('keyup.' + NAMESPACE, function(e) { + if (current && current.settings.closeOnEscape && current.state === STATES.OPENED && e.keyCode === 27) { + current.close(); + } + }); + + // Handles the hashchange event + $(window).on('hashchange.' + NAMESPACE, handleHashChangeEvent); + }); +}); diff --git a/dist/remodal.min.js b/dist/remodal.min.js new file mode 100644 index 0000000..4e48b7b --- /dev/null +++ b/dist/remodal.min.js @@ -0,0 +1,10 @@ +/* + * Remodal - v1.0.0 + * Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking. + * http://vodkabears.github.io/remodal/ + * + * Made by Ilya Makarov + * Under MIT License + */ + +!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(c){return b(a,c)}):"object"==typeof exports?b(a,require("jquery")):b(a,a.jQuery||a.Zepto)}(this,function(a,b){"use strict";function c(a){if(v&&"none"===a.css("animation-name")&&"none"===a.css("-webkit-animation-name")&&"none"===a.css("-moz-animation-name")&&"none"===a.css("-o-animation-name")&&"none"===a.css("-ms-animation-name"))return 0;var b,c,d,e,f=a.css("animation-duration")||a.css("-webkit-animation-duration")||a.css("-moz-animation-duration")||a.css("-o-animation-duration")||a.css("-ms-animation-duration")||"0s",g=a.css("animation-delay")||a.css("-webkit-animation-delay")||a.css("-moz-animation-delay")||a.css("-o-animation-delay")||a.css("-ms-animation-delay")||"0s",h=a.css("animation-iteration-count")||a.css("-webkit-animation-iteration-count")||a.css("-moz-animation-iteration-count")||a.css("-o-animation-iteration-count")||a.css("-ms-animation-iteration-count")||"1";for(f=f.split(", "),g=g.split(", "),h=h.split(", "),e=0,c=f.length,b=Number.NEGATIVE_INFINITY;c>e;e++)d=parseFloat(f[e])*parseInt(h[e],10)+parseFloat(g[e]),d>b&&(b=d);return d}function d(){if(b(document.body).height()<=b(window).height())return 0;var a,c,d=document.createElement("div"),e=document.createElement("div");return d.style.visibility="hidden",d.style.width="100px",document.body.appendChild(d),a=d.offsetWidth,d.style.overflow="scroll",e.style.width="100%",d.appendChild(e),c=e.offsetWidth,d.parentNode.removeChild(d),a-c}function e(){var a,c,e=b("html"),f=p+"-is-locked";e.hasClass(f)||(c=b(document.body),a=parseInt(c.css("padding-right"),10)+d(),c.css("padding-right",a+"px"),e.addClass(f))}function f(){var a,c,e=b("html"),f=p+"-is-locked";e.hasClass(f)&&(c=b(document.body),a=parseInt(c.css("padding-right"),10)-d(),c.css("padding-right",a+"px"),e.removeClass(f))}function g(a,b,c,d){a.$bg.removeClass(p+"-is-"+t.CLOSING+" "+p+"-is-"+t.OPENING+" "+p+"-is-"+t.CLOSED+" "+p+"-is-"+t.OPENED).addClass(p+"-is-"+b),a.$overlay.removeClass(p+"-is-"+t.CLOSING+" "+p+"-is-"+t.OPENING+" "+p+"-is-"+t.CLOSED+" "+p+"-is-"+t.OPENED).addClass(p+"-is-"+b),a.$wrapper.removeClass(p+"-is-"+t.CLOSING+" "+p+"-is-"+t.OPENING+" "+p+"-is-"+t.CLOSED+" "+p+"-is-"+t.OPENED).addClass(p+"-is-"+b),a.$modal.removeClass(p+"-is-"+t.CLOSING+" "+p+"-is-"+t.OPENING+" "+p+"-is-"+t.CLOSED+" "+p+"-is-"+t.OPENED).addClass(p+"-is-"+b),a.state=b,!c&&a.$modal.trigger({type:b,reason:d},[{reason:d}])}function h(a,d,e){var f=0,g=function(a){a.target===this&&f++},h=function(a){a.target===this&&0===--f&&(b.each(["$bg","$overlay","$wrapper","$modal"],function(a,b){e[b].off(q+" "+r)}),d())};b.each(["$bg","$overlay","$wrapper","$modal"],function(a,b){e[b].on(q,g).on(r,h)}),a(),0===c(e.$bg)&&0===c(e.$overlay)&&0===c(e.$wrapper)&&0===c(e.$modal)&&(b.each(["$bg","$overlay","$wrapper","$modal"],function(a,b){e[b].off(q+" "+r)}),d())}function i(a){a.state!==t.CLOSED&&(b.each(["$bg","$overlay","$wrapper","$modal"],function(b,c){a[c].off(q+" "+r)}),a.$bg.removeClass(a.settings.modifier),a.$overlay.removeClass(a.settings.modifier).hide(),a.$wrapper.hide(),f(),g(a,t.CLOSED,!0))}function j(a){var b,c,d,e,f={};for(a=a.replace(/\s*:\s*/g,":").replace(/\s*,\s*/g,","),b=a.split(","),e=0,c=b.length;c>e;e++)b[e]=b[e].split(":"),d=b[e][1],("string"==typeof d||d instanceof String)&&(d="true"===d||("false"===d?!1:d)),("string"==typeof d||d instanceof String)&&(d=isNaN(d)?d:+d),f[b[e][0]]=d;return f}function k(){var a,c,d=location.hash.replace("#","");if(d){try{c=b("[data-"+o+"-id="+d.replace(new RegExp("/","g"),"\\/")+"]")}catch(e){}c&&c.length&&(a=b[o].lookup[c.data(o)],a&&a.settings.hashTracking&&a.open())}else m&&m.state===t.OPENED&&m.settings.hashTracking&&m.close()}function l(a,c){var d=b(document.body),e=this;e.settings=b.extend({},s,c),e.index=b[o].lookup.push(e)-1,e.state=t.CLOSED,e.$overlay=b("."+p+"-overlay"),e.$overlay.length||(e.$overlay=b("
").addClass(p+"-overlay "+p+"-is-"+t.CLOSED).hide(),d.append(e.$overlay)),e.$bg=b("."+p+"-bg").addClass(p+"-is-"+t.CLOSED),e.$modal=a,e.$modal.addClass(p+"-is-initialized "+p+" "+e.settings.modifier+" "+p+"-is-"+t.CLOSED),e.$wrapper=b("
").addClass(p+"-wrapper "+e.settings.modifier+" "+p+"-is-"+t.CLOSED).hide().append(e.$modal),d.append(e.$wrapper),e.$wrapper.on("click."+p,"[data-"+p+'-action="close"]',function(a){a.preventDefault(),e.close()}),e.$wrapper.on("click."+p,"[data-"+p+'-action="cancel"]',function(a){a.preventDefault(),e.$modal.trigger(u.CANCELLATION),e.settings.closeOnCancel&&e.close(u.CANCELLATION)}),e.$wrapper.on("click."+p,"[data-"+p+'-action="confirm"]',function(a){a.preventDefault(),e.$modal.trigger(u.CONFIRMATION),e.settings.closeOnConfirm&&e.close(u.CONFIRMATION)}),e.$wrapper.on("click."+p,function(a){var c=b(a.target);c.hasClass(p+"-wrapper")&&e.settings.closeOnOutsideClick&&e.close()})}var m,n,o="remodal",p=a.REMODAL_GLOBALS&&a.REMODAL_GLOBALS.NAMESPACE||o,q=b.map(["animationstart","webkitAnimationStart","MSAnimationStart","oAnimationStart"],function(a){return a+"."+p}).join(" "),r=b.map(["animationend","webkitAnimationEnd","MSAnimationEnd","oAnimationEnd"],function(a){return a+"."+p}).join(" "),s=b.extend({hashTracking:!0,closeOnConfirm:!0,closeOnCancel:!0,closeOnEscape:!0,closeOnOutsideClick:!0,modifier:""},a.REMODAL_GLOBALS&&a.REMODAL_GLOBALS.DEFAULTS),t={CLOSING:"closing",CLOSED:"closed",OPENING:"opening",OPENED:"opened"},u={CONFIRMATION:"confirmation",CANCELLATION:"cancellation"},v=function(){var a=document.createElement("div").style;return void 0!==a.animationName||void 0!==a.WebkitAnimationName||void 0!==a.MozAnimationName||void 0!==a.msAnimationName||void 0!==a.OAnimationName}();l.prototype.open=function(){var a,c=this;c.state!==t.OPENING&&c.state!==t.CLOSING&&(a=c.$modal.attr("data-"+o+"-id"),a&&c.settings.hashTracking&&(n=b(window).scrollTop(),location.hash=a),m&&m!==c&&i(m),m=c,e(),c.$bg.addClass(c.settings.modifier),c.$overlay.addClass(c.settings.modifier).show(),c.$wrapper.show().scrollTop(0),h(function(){g(c,t.OPENING)},function(){g(c,t.OPENED)},c))},l.prototype.close=function(a){var c=this;c.state!==t.OPENING&&c.state!==t.CLOSING&&(c.settings.hashTracking&&c.$modal.attr("data-"+o+"-id")===location.hash.substr(1)&&(location.hash="",b(window).scrollTop(n)),h(function(){g(c,t.CLOSING,!1,a)},function(){c.$bg.removeClass(c.settings.modifier),c.$overlay.removeClass(c.settings.modifier).hide(),c.$wrapper.hide(),f(),g(c,t.CLOSED,!1,a)},c))},l.prototype.getState=function(){return this.state},l.prototype.destroy=function(){var a,c=b[o].lookup;i(this),this.$wrapper.remove(),delete c[this.index],a=b.grep(c,function(a){return!!a}).length,0===a&&(this.$overlay.remove(),this.$bg.removeClass(p+"-is-"+t.CLOSING+" "+p+"-is-"+t.OPENING+" "+p+"-is-"+t.CLOSED+" "+p+"-is-"+t.OPENED))},b[o]={lookup:[]},b.fn[o]=function(a){var c,d;return this.each(function(e,f){d=b(f),null==d.data(o)?(c=new l(d,a),d.data(o,c.index),c.settings.hashTracking&&d.attr("data-"+o+"-id")===location.hash.substr(1)&&c.open()):c=b[o].lookup[d.data(o)]}),c},b(document).ready(function(){b(document).on("click","[data-"+o+"-target]",function(a){a.preventDefault();var c=a.currentTarget,d=c.getAttribute("data-"+o+"-target"),e=b("[data-"+o+"-id="+d+"]");b[o].lookup[e.data(o)].open()}),b(document).find("."+p).each(function(a,c){var d=b(c),e=d.data(o+"-options");e?("string"==typeof e||e instanceof String)&&(e=j(e)):e={},d[o](e)}),b(document).on("keyup."+p,function(a){m&&m.settings.closeOnEscape&&m.state===t.OPENED&&27===a.keyCode&&m.close()}),b(window).on("hashchange."+p,k)})}); \ No newline at end of file diff --git a/examples/index-zepto.html b/examples/index-zepto.html index aeb283d..b78530d 100644 --- a/examples/index-zepto.html +++ b/examples/index-zepto.html @@ -6,155 +6,291 @@ Remodal example - + + +
- Click + Modal №1
+ Modal №2

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


+

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


+

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


+

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


+

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


+

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


-
-

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


- Cancel - OK -
-
-

Another one window

+

Remodal

- Hello! + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


- Hello! + +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+
+ + + +
+
+

Another one window

+

+ Hello! +

+
+
+
- - + + + diff --git a/examples/index.html b/examples/index.html index d580ccf..e8607e3 100644 --- a/examples/index.html +++ b/examples/index.html @@ -6,156 +6,291 @@ Remodal example - + + +
- Click + Modal №1
+ Modal №2

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


+

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


+

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


+

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


+

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


+

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


-
-

Remodal

- Flat, responsive, lightweight, fast, easy customizable modal window plugin - with declarative state notation and hash tracking. + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.

+
+ +

Remodal

- Minified version size: ~4kb + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


- Cancel - OK -
-
-

Another one window

+

Remodal

- Hello! + Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking.


- Hello! + +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+ +

Remodal

+

+ Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin + with declarative configuration and hash tracking. +

+
+
+ + + +
+
+

Another one window

+

+ Hello! +

+
+
+
- + - + diff --git a/libs/jquery-loader.js b/libs/jquery-loader.js index b46ede4..99f090b 100644 --- a/libs/jquery-loader.js +++ b/libs/jquery-loader.js @@ -1,8 +1,12 @@ -(function () { +!(function() { - // Get any lib=___ param from the query string. - var library = location.search.match(/[?&]lib=(.*?)(?=&|$)/); - - document.write(""); + // Get any lib=___ param from the query string. + var library = location.search.match(/[?&]lib=(.*?)(?=&|$)/); + /* jshint -W060 */ + if (library) { + document.write(''); + } else { + document.write(''); + } }()); diff --git a/package.json b/package.json index eec0c05..961a7e2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "remodal", - "version": "0.6.4", - "description": "Flat, responsive, lightweight, easy customizable modal window plugin with declarative state notation and hash tracking.", + "version": "1.0.0", + "description": "Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking.", "keywords": [ "jquery-plugin", "jquery", @@ -15,7 +15,9 @@ "popin", "lightbox", "ui", - "zepto" + "zepto", + "synchronized", + "animations" ], "repository": { "type": "git", @@ -31,22 +33,25 @@ }, "homepage": "http://vodkabears.github.io/remodal/", "license": "MIT", - "main": "dist/jquery.remodal.js", + "main": "dist/remodal.js", "dependencies": { "jquery": "*" }, "devDependencies": { "bower": "^1.4.1", "grunt": "^0.4.5", + "grunt-autoprefixer": "^3.0.0", + "grunt-browser-sync": "^2.1.2", "grunt-cli": "^0.1.13", "grunt-contrib-concat": "^0.5.1", "grunt-contrib-connect": "^0.10.1", - "grunt-contrib-jshint": "^0.11.1", + "grunt-contrib-jshint": "^0.11.2", "grunt-contrib-qunit": "^0.7.0", - "grunt-contrib-uglify": "^0.8.1", - "grunt-csscomb": "^3.0.0", + "grunt-contrib-uglify": "^0.9.1", + "grunt-contrib-watch": "^0.6.1", + "grunt-csscomb": "3.0.0", "grunt-githooks": "^0.3.1", - "grunt-jscs": "^1.6.0" + "grunt-jscs": "^1.8.0" }, "scripts": { "start": "npm install && ./node_modules/.bin/bower install && ./node_modules/.bin/grunt githooks", diff --git a/src/jquery.remodal.css b/src/jquery.remodal.css deleted file mode 100644 index 65b4bd0..0000000 --- a/src/jquery.remodal.css +++ /dev/null @@ -1,313 +0,0 @@ -@import url(//fonts.googleapis.com/css?family=Exo+2:700,400&subset=latin,cyrillic); - -/* ========================================================================== - Remodal necessary styles - ========================================================================== */ - -/* Hide scroll bar */ - -html.remodal-is-locked { - overflow: hidden; -} - -/* Anti FOUC */ - -.remodal, -[data-remodal-id] { - visibility: hidden; -} - -/* Necessary styles of the overlay */ - -.remodal-overlay { - position: fixed; - z-index: 9999; - top: -5000px; - right: -5000px; - bottom: -5000px; - left: -5000px; - - display: none; -} - -/* Necessary styles of the wrapper */ - -.remodal-wrapper { - position: fixed; - z-index: 10000; - top: 0; - right: 0; - bottom: 0; - left: 0; - - display: none; - overflow: auto; - - text-align: center; - - -webkit-overflow-scrolling: touch; -} - -.remodal-wrapper:after { - display: inline-block; - - height: 100%; - margin-left: -0.05em; - - content: ""; -} - -/* Fix iPad, iPhone glitches */ - -.remodal-overlay, -.remodal-wrapper { - -webkit-transform: translateZ(0px); -} - -/* Necessary styles of the modal dialog */ - -.remodal { - position: relative; - - display: inline-block; -} - -/* Anti-zoom. Font-size should be >= 16px. */ - -.remodal select, -.remodal textarea, -.remodal input, -.remodal select:focus, -.remodal textarea:focus, -.remodal input:focus { - font-size: 16px; -} - -/* ========================================================================== - Remodal default theme - ========================================================================== */ - -/* Default theme font */ - -.remodal, -.remodal * { - font-family: "Exo 2", sans-serif; -} - -/* Background for effects */ - -.remodal-bg { - -webkit-transition: -webkit-filter 0.2s ease-out; - -moz-transition: -moz-filter 0.2s ease-out; - -o-transition: -o-filter 0.2s ease-out; - transition: filter 0.2s ease-out; -} - -body.remodal-is-active .remodal-bg { - -webkit-filter: blur(3px); - -ms-filter: blur(3px); - filter: blur(3px); - -moz-filter: blur(3px); - -o-filter: blur(3px); -} - -/* Default theme styles of the overlay */ - -.remodal-overlay { - -webkit-transition: opacity 0.2s ease-out; - -moz-transition: opacity 0.2s ease-out; - -o-transition: opacity 0.2s ease-out; - transition: opacity 0.2s ease-out; - - opacity: 0; - background: rgba(33, 36, 46, 0.95); -} - -body.remodal-is-active .remodal-overlay { - opacity: 1; -} - -/* Default theme styles of the modal dialog */ - -.remodal { - font-size: 16px; - - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - width: 100%; - min-height: 100%; - padding: 35px; - - -webkit-transition: -webkit-transform 0.2s ease-out, opacity 0.2s ease-out; - -moz-transition: -moz-transform 0.2s ease-out, opacity 0.2s ease-out; - -o-transition: -o-transform 0.2s ease-out, opacity 0.2s ease-out; - transition: transform 0.2s ease-out, opacity 0.2s ease-out; - -webkit-transform: scale(0.95); - -moz-transform: scale(0.95); - -ms-transform: scale(0.95); - -o-transform: scale(0.95); - transform: scale(0.95); - - opacity: 0; - color: #182a3c; - background: #f4f4f4; - background-clip: padding-box; -} - -body.remodal-is-active .remodal { - -webkit-transform: scale(1); - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - transform: scale(1); - - opacity: 1; -} - -/* Vertical align of the modal dialog */ - -.remodal, -.remodal-wrapper:after { - vertical-align: middle; -} - -/* Close button */ - -.remodal-close { - position: absolute; - top: 5px; - left: 5px; - - width: 28px; - height: 28px; - - -webkit-transition: background 0.2s ease-out; - -moz-transition: background 0.2s ease-out; - -o-transition: background 0.2s ease-out; - transition: background 0.2s ease-out; - text-decoration: none; - - -webkit-border-radius: 50%; - border-radius: 50%; -} - -.remodal-close:after { - font-family: Arial, "Helvetica CY", "Nimbus Sans L", sans-serif !important; - font-size: 28px; - line-height: 28px; - - display: block; - - content: "×"; - cursor: pointer; - -webkit-transition: all 0.2s ease-out; - -moz-transition: all 0.2s ease-out; - -o-transition: all 0.2s ease-out; - transition: all 0.2s ease-out; - text-decoration: none; - - color: #3e5368; -} - -.remodal-close:hover, -.remodal-close:active { - background: #3e5368; -} - -.remodal-close:hover.remodal-close:after, -.remodal-close:active.remodal-close:after { - color: #f4f4f4; -} - -/* Dialog buttons */ - -.remodal-confirm, -.remodal-cancel { - font-size: 10pt; - - display: inline-block; - - width: 120px; - margin: 0 0 5px 0; - padding: 9px 0; - - cursor: pointer; - -webkit-transition: all 0.2s ease-out; - -moz-transition: all 0.2s ease-out; - -o-transition: all 0.2s ease-out; - transition: all 0.2s ease-out; - text-align: center; - text-decoration: none; - - -webkit-border-radius: 6px; - border-radius: 6px; - background-clip: padding-box; -} - -.remodal-confirm { - color: #16a085; - border: 2px solid #16a085; - background: #f4f4f4; -} - -.remodal-confirm:hover, -.remodal-confirm:active { - color: #f4f4f4; - background: #16a085; -} - -.remodal-cancel { - color: #c0392b; - border: 2px solid #c0392b; - background: #f4f4f4; -} - -.remodal-cancel:hover, -.remodal-cancel:active { - color: #f4f4f4; - background: #c0392b; -} - -/* Media queries - ========================================================================== */ - -@media only screen and (min-width: 641px) { - .remodal { - max-width: 700px; - min-height: 0; - margin: 20px auto; - - -webkit-border-radius: 6px; - border-radius: 6px; - } -} - -/* IE8 - ========================================================================== */ - -.lt-ie9 .remodal-overlay { - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#F021242E, endColorstr=#F021242E); -} - -.lt-ie9 .remodal { - width: 700px; - min-height: 0; - margin: 20px auto; -} - -.lt-ie9 .remodal-close:after { - margin: auto; - - text-align: center; -} - -.lt-ie9 .remodal-close:hover, -.lt-ie9 .remodal-close:active { - background: transparent; -} - -.lt-ie9 .remodal-close:hover.remodal-close:after, -.lt-ie9 .remodal-close:active.remodal-close:after { - color: #3e5368; -} diff --git a/src/jquery.remodal.js b/src/jquery.remodal.js deleted file mode 100644 index 910e1b9..0000000 --- a/src/jquery.remodal.js +++ /dev/null @@ -1,514 +0,0 @@ -(function(root, factory) { - if (typeof define === 'function' && define.amd) { - define(['jquery'], function($) { - return factory(root, $); - }); - } else if (typeof exports === 'object') { - factory(root, require('jquery')); - } else { - factory(root, root.jQuery || root.Zepto); - } -})(this, function(global, $) { - - 'use strict'; - - /** - * Name of the plugin - * @private - * @type {String} - */ - var pluginName = 'remodal'; - - /** - * Namespace for CSS and events - * @private - * @type {String} - */ - var namespace = global.remodalGlobals && global.remodalGlobals.namespace || pluginName; - - /** - * Default settings - * @private - * @type {Object} - */ - var defaults = $.extend({ - hashTracking: true, - closeOnConfirm: true, - closeOnCancel: true, - closeOnEscape: true, - closeOnAnyClick: true - }, global.remodalGlobals && global.remodalGlobals.defaults); - - /** - * Current modal - * @private - * @type {Remodal} - */ - var current; - - /** - * Scrollbar position - * @private - * @type {Number} - */ - var scrollTop; - - /** - * Get a transition duration in ms - * @private - * @param {jQuery} $elem - * @return {Number} - */ - function getTransitionDuration($elem) { - var duration = $elem.css('transition-duration') || - $elem.css('-webkit-transition-duration') || - $elem.css('-moz-transition-duration') || - $elem.css('-o-transition-duration') || - $elem.css('-ms-transition-duration') || - '0s'; - - var delay = $elem.css('transition-delay') || - $elem.css('-webkit-transition-delay') || - $elem.css('-moz-transition-delay') || - $elem.css('-o-transition-delay') || - $elem.css('-ms-transition-delay') || - '0s'; - - var max; - var len; - var num; - var i; - - duration = duration.split(', '); - delay = delay.split(', '); - - // The duration length is the same as the delay length - for (i = 0, len = duration.length, max = Number.NEGATIVE_INFINITY; i < len; i++) { - num = parseFloat(duration[i]) + parseFloat(delay[i]); - - if (num > max) { - max = num; - } - } - - return num * 1000; - } - - /** - * Get a scrollbar width - * @private - * @return {Number} - */ - function getScrollbarWidth() { - if ($(document.body).height() <= $(window).height()) { - return 0; - } - - var outer = document.createElement('div'); - var inner = document.createElement('div'); - var widthNoScroll; - var widthWithScroll; - - outer.style.visibility = 'hidden'; - outer.style.width = '100px'; - document.body.appendChild(outer); - - widthNoScroll = outer.offsetWidth; - - // Force scrollbars - outer.style.overflow = 'scroll'; - - // Add inner div - inner.style.width = '100%'; - outer.appendChild(inner); - - widthWithScroll = inner.offsetWidth; - - // Remove divs - outer.parentNode.removeChild(outer); - - return widthNoScroll - widthWithScroll; - } - - /** - * Lock the screen - * @private - */ - function lockScreen() { - var $html = $('html'); - var lockedClass = namespace + '-is-locked'; - var paddingRight; - var $body; - - if (!$html.hasClass(lockedClass)) { - $body = $(document.body); - - // Zepto does not support '-=', '+=' in the `css` method - paddingRight = parseInt($body.css('padding-right'), 10) + getScrollbarWidth(); - - $body.css('padding-right', paddingRight + 'px'); - $html.addClass(lockedClass); - } - } - - /** - * Unlock the screen - * @private - */ - function unlockScreen() { - var $html = $('html'); - var lockedClass = namespace + '-is-locked'; - var paddingRight; - var $body; - - if ($html.hasClass(lockedClass)) { - $body = $(document.body); - - // Zepto does not support '-=', '+=' in the `css` method - paddingRight = parseInt($body.css('padding-right'), 10) - getScrollbarWidth(); - - $body.css('padding-right', paddingRight + 'px'); - $html.removeClass(lockedClass); - } - } - - /** - * Parse a string with options - * @private - * @param str - * @returns {Object} - */ - function parseOptions(str) { - var obj = {}; - var arr; - var len; - var val; - var i; - - // Remove spaces before and after delimiters - str = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ','); - - // Parse a string - arr = str.split(','); - for (i = 0, len = arr.length; i < len; i++) { - arr[i] = arr[i].split(':'); - val = arr[i][1]; - - // Convert a string value if it is like a boolean - if (typeof val === 'string' || val instanceof String) { - val = val === 'true' || (val === 'false' ? false : val); - } - - // Convert a string value if it is like a number - if (typeof val === 'string' || val instanceof String) { - val = !isNaN(val) ? +val : val; - } - - obj[arr[i][0]] = val; - } - - return obj; - } - - /** - * Remodal constructor - * @param {jQuery} $modal - * @param {Object} options - * @constructor - */ - function Remodal($modal, options) { - var remodal = this; - var tdOverlay; - var tdModal; - var tdBg; - - remodal.settings = $.extend({}, defaults, options); - - // Build DOM - remodal.$body = $(document.body); - remodal.$overlay = $('.' + namespace + '-overlay'); - - if (!remodal.$overlay.length) { - remodal.$overlay = $('
').addClass(namespace + '-overlay'); - remodal.$body.append(remodal.$overlay); - } - - remodal.$bg = $('.' + namespace + '-bg'); - remodal.$closeButton = $('').addClass(namespace + '-close'); - remodal.$wrapper = $('
').addClass(namespace + '-wrapper'); - remodal.$modal = $modal; - remodal.$modal.addClass(namespace); - remodal.$modal.css('visibility', 'visible'); - - remodal.$modal.append(remodal.$closeButton); - remodal.$wrapper.append(remodal.$modal); - remodal.$body.append(remodal.$wrapper); - remodal.$confirmButton = remodal.$modal.find('.' + namespace + '-confirm'); - remodal.$cancelButton = remodal.$modal.find('.' + namespace + '-cancel'); - - // Calculate timeouts - tdOverlay = getTransitionDuration(remodal.$overlay); - tdModal = getTransitionDuration(remodal.$modal); - tdBg = getTransitionDuration(remodal.$bg); - remodal.td = Math.max(tdOverlay, tdModal, tdBg); - - // Add the close button event listener - remodal.$wrapper.on('click.' + namespace, '.' + namespace + '-close', function(e) { - e.preventDefault(); - - remodal.close(); - }); - - // Add the cancel button event listener - remodal.$wrapper.on('click.' + namespace, '.' + namespace + '-cancel', function(e) { - e.preventDefault(); - - remodal.$modal.trigger('cancel'); - - if (remodal.settings.closeOnCancel) { - remodal.close('cancellation'); - } - }); - - // Add the confirm button event listener - remodal.$wrapper.on('click.' + namespace, '.' + namespace + '-confirm', function(e) { - e.preventDefault(); - - remodal.$modal.trigger('confirm'); - - if (remodal.settings.closeOnConfirm) { - remodal.close('confirmation'); - } - }); - - // Add the keyboard event listener - $(document).on('keyup.' + namespace, function(e) { - if (e.keyCode === 27 && remodal.settings.closeOnEscape) { - remodal.close(); - } - }); - - // Add the overlay event listener - remodal.$wrapper.on('click.' + namespace, function(e) { - var $target = $(e.target); - - if (!$target.hasClass(namespace + '-wrapper')) { - return; - } - - if (remodal.settings.closeOnAnyClick) { - remodal.close(); - } - }); - - remodal.index = $[pluginName].lookup.push(remodal) - 1; - remodal.busy = false; - } - - /** - * Open a modal window - * @public - */ - Remodal.prototype.open = function() { - - // Check if the animation was completed - if (this.busy) { - return; - } - - var remodal = this; - var id; - - remodal.busy = true; - remodal.$modal.trigger('open'); - - id = remodal.$modal.attr('data-' + pluginName + '-id'); - - if (id && remodal.settings.hashTracking) { - scrollTop = $(window).scrollTop(); - location.hash = id; - } - - if (current && current !== remodal) { - current.$overlay.hide(); - current.$wrapper.hide(); - current.$body.removeClass(namespace + '-is-active'); - } - - current = remodal; - - lockScreen(); - remodal.$overlay.show(); - remodal.$wrapper.show(); - - setTimeout(function() { - remodal.$body.addClass(namespace + '-is-active'); - remodal.$wrapper.scrollTop(0); - - setTimeout(function() { - remodal.busy = false; - remodal.$modal.trigger('opened'); - }, remodal.td + 50); - }, 25); - }; - - /** - * Close a modal window - * @public - * @param {String|undefined} reason A reason to close - */ - Remodal.prototype.close = function(reason) { - - // Check if the animation was completed - if (this.busy) { - return; - } - - var remodal = this; - - remodal.busy = true; - remodal.$modal.trigger({ - type: 'close', - reason: reason - }); - - if ( - remodal.settings.hashTracking && - remodal.$modal.attr('data-' + pluginName + '-id') === location.hash.substr(1) - ) { - location.hash = ''; - $(window).scrollTop(scrollTop); - } - - remodal.$body.removeClass(namespace + '-is-active'); - - setTimeout(function() { - remodal.$overlay.hide(); - remodal.$wrapper.hide(); - unlockScreen(); - - remodal.busy = false; - remodal.$modal.trigger({ - type: 'closed', - reason: reason - }); - }, remodal.td + 50); - }; - - /** - * Special plugin object for instances. - * @public - * @type {Object} - */ - $[pluginName] = { - lookup: [] - }; - - /** - * Plugin constructor - * @param {Object} options - * @returns {JQuery} - * @constructor - */ - $.fn[pluginName] = function(opts) { - var instance; - var $elem; - - this.each(function(index, elem) { - $elem = $(elem); - - if ($elem.data(pluginName) == null) { - instance = new Remodal($elem, opts); - $elem.data(pluginName, instance.index); - - if ( - instance.settings.hashTracking && - $elem.attr('data-' + pluginName + '-id') === location.hash.substr(1) - ) { - instance.open(); - } - } else { - instance = $[pluginName].lookup[$elem.data(pluginName)]; - } - }); - - return instance; - }; - - $(document).ready(function() { - - // data-remodal-target opens a modal window with the special Id. - $(document).on('click', '[data-' + pluginName + '-target]', function(e) { - e.preventDefault(); - - var elem = e.currentTarget; - var id = elem.getAttribute('data-' + pluginName + '-target'); - var $target = $('[data-' + pluginName + '-id=' + id + ']'); - - $[pluginName].lookup[$target.data(pluginName)].open(); - }); - - // Auto initialization of modal windows. - // They should have the 'remodal' class attribute. - // Also you can write `data-remodal-options` attribute to pass params into the modal. - $(document).find('.' + namespace).each(function(i, container) { - var $container = $(container); - var options = $container.data(pluginName + '-options'); - - if (!options) { - options = {}; - } else if (typeof options === 'string' || options instanceof String) { - options = parseOptions(options); - } - - $container[pluginName](options); - }); - }); - - /** - * Hashchange handler - * @private - * @param {Event} e - * @param {Boolean} [closeOnEmptyHash=true] - */ - function hashHandler(e, closeOnEmptyHash) { - var id = location.hash.replace('#', ''); - var instance; - var $elem; - - if (typeof closeOnEmptyHash === 'undefined') { - closeOnEmptyHash = true; - } - - if (!id) { - if (closeOnEmptyHash) { - - // Check if we have currently opened modal and animation was completed - if (current && !current.busy && current.settings.hashTracking) { - current.close(); - } - } - } else { - - // Catch syntax error if your hash is bad - try { - $elem = $( - '[data-' + pluginName + '-id=' + - id.replace(new RegExp('/', 'g'), '\\/') + ']' - ); - } catch (err) {} - - if ($elem && $elem.length) { - instance = $[pluginName].lookup[$elem.data(pluginName)]; - - if (instance && instance.settings.hashTracking) { - instance.open(); - } - } - - } - } - - $(window).bind('hashchange.' + namespace, hashHandler); - -}); diff --git a/src/remodal-default-theme.css b/src/remodal-default-theme.css new file mode 100644 index 0000000..3051bff --- /dev/null +++ b/src/remodal-default-theme.css @@ -0,0 +1,232 @@ +/* ========================================================================== + Remodal's default mobile first theme + ========================================================================== */ + +/* Default theme styles for the background */ + +.remodal-bg.remodal-is-opening, +.remodal-bg.remodal-is-opened { + filter: blur(3px); +} + +/* Default theme styles of the overlay */ + +.remodal-overlay { + background: rgba(43, 46, 56, 0.9); +} + +.remodal-overlay.remodal-is-opening, +.remodal-overlay.remodal-is-closing { + animation-fill-mode: forwards; +} + +.remodal-overlay.remodal-is-opening { + animation: remodal-overlay-opening-keyframes 0.3s; +} + +.remodal-overlay.remodal-is-closing { + animation: remodal-overlay-closing-keyframes 0.3s; +} + +/* Default theme styles of the wrapper */ + +.remodal-wrapper { + padding: 10px 10px 0; +} + +/* Default theme styles of the modal dialog */ + +.remodal { + box-sizing: border-box; + width: 100%; + margin-bottom: 10px; + padding: 35px; + + transform: translate3d(0, 0, 0); + + color: #2b2e38; + background: #fff; +} + +.remodal.remodal-is-opening, +.remodal.remodal-is-closing { + animation-fill-mode: forwards; +} + +.remodal.remodal-is-opening { + animation: remodal-opening-keyframes 0.3s; +} + +.remodal.remodal-is-closing { + animation: remodal-closing-keyframes 0.3s; +} + +/* Vertical align of the modal dialog */ + +.remodal, +.remodal-wrapper:after { + vertical-align: middle; +} + +/* Close button */ + +.remodal-close { + position: absolute; + top: 0; + left: 0; + + display: block; + overflow: visible; + + width: 35px; + height: 35px; + margin: 0; + padding: 0; + + cursor: pointer; + transition: color 0.2s; + text-decoration: none; + + color: #95979c; + border: 0; + outline: 0; + background: transparent; +} + +.remodal-close:hover { + color: #2b2e38; +} + +.remodal-close:before { + font-family: Arial, "Helvetica CY", "Nimbus Sans L", sans-serif !important; + font-size: 25px; + line-height: 35px; + + position: absolute; + top: 0; + left: 0; + + display: block; + + width: 35px; + + content: "\00d7"; + text-align: center; +} + +/* Dialog buttons */ + +.remodal-confirm, +.remodal-cancel { + font: inherit; + + display: inline-block; + overflow: visible; + + min-width: 110px; + margin: 0; + padding: 12px 0; + + cursor: pointer; + transition: background 0.2s; + text-align: center; + vertical-align: middle; + text-decoration: none; + + border: 0; + outline: 0; +} + +.remodal-confirm { + color: #fff; + background: #81c784; +} + +.remodal-confirm:hover { + background: #66bb6a; +} + +.remodal-cancel { + color: #fff; + background: #e57373; +} + +.remodal-cancel:hover { + background: #ef5350; +} + +/* Remove inner padding and border in Firefox 4+ for the button tag. */ + +.remodal-confirm::-moz-focus-inner, +.remodal-cancel::-moz-focus-inner { + padding: 0; + + border: 0; +} + +/* Keyframes + ========================================================================== */ + +@keyframes remodal-opening-keyframes { + from { + transform: scale(1.05); + + opacity: 0; + } + to { + transform: none; + + opacity: 1; + } +} + +@keyframes remodal-closing-keyframes { + from { + transform: scale(1); + + opacity: 1; + } + to { + transform: scale(0.95); + + opacity: 0; + } +} + +@keyframes remodal-overlay-opening-keyframes { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes remodal-overlay-closing-keyframes { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +/* Media queries + ========================================================================== */ + +@media only screen and (min-width: 641px) { + .remodal { + max-width: 700px; + } +} + +/* IE8 + ========================================================================== */ + +.lt-ie9 .remodal-overlay { + background: #2b2e38; +} + +.lt-ie9 .remodal { + width: 700px; +} diff --git a/src/remodal.css b/src/remodal.css new file mode 100644 index 0000000..21ccc8f --- /dev/null +++ b/src/remodal.css @@ -0,0 +1,76 @@ +/* ========================================================================== + Remodal's necessary styles + ========================================================================== */ + +/* Hide scroll bar */ + +html.remodal-is-locked { + overflow: hidden; +} + +/* Anti FOUC */ + +.remodal, +[data-remodal-id] { + display: none; +} + +/* Necessary styles of the overlay */ + +.remodal-overlay { + position: fixed; + z-index: 9999; + top: -5000px; + right: -5000px; + bottom: -5000px; + left: -5000px; + + display: none; +} + +/* Necessary styles of the wrapper */ + +.remodal-wrapper { + position: fixed; + z-index: 10000; + top: 0; + right: 0; + bottom: 0; + left: 0; + + display: none; + overflow: auto; + + text-align: center; + + -webkit-overflow-scrolling: touch; +} + +.remodal-wrapper:after { + display: inline-block; + + height: 100%; + margin-left: -0.05em; + + content: ""; +} + +/* Fix iPad, iPhone glitches */ + +.remodal-overlay, +.remodal-wrapper { + backface-visibility: hidden; +} + +/* Necessary styles of the modal dialog */ + +.remodal { + position: relative; + + text-size-adjust: 100%; +} + +.remodal-is-initialized { + /* Disable Anti-FOUC */ + display: inline-block; +} diff --git a/src/remodal.js b/src/remodal.js new file mode 100644 index 0000000..e3e507a --- /dev/null +++ b/src/remodal.js @@ -0,0 +1,743 @@ +!(function(root, factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], function($) { + return factory(root, $); + }); + } else if (typeof exports === 'object') { + factory(root, require('jquery')); + } else { + factory(root, root.jQuery || root.Zepto); + } +})(this, function(global, $) { + + 'use strict'; + + /** + * Name of the plugin + * @private + * @const + * @type {String} + */ + var PLUGIN_NAME = 'remodal'; + + /** + * Namespace for CSS and events + * @private + * @const + * @type {String} + */ + var NAMESPACE = global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.NAMESPACE || PLUGIN_NAME; + + /** + * Animationstart event with vendor prefixes + * @private + * @const + * @type {String} + */ + var ANIMATIONSTART_EVENTS = $.map( + ['animationstart', 'webkitAnimationStart', 'MSAnimationStart', 'oAnimationStart'], + + function(eventName) { + return eventName + '.' + NAMESPACE; + } + + ).join(' '); + + /** + * Animationend event with vendor prefixes + * @private + * @const + * @type {String} + */ + var ANIMATIONEND_EVENTS = $.map( + ['animationend', 'webkitAnimationEnd', 'MSAnimationEnd', 'oAnimationEnd'], + + function(eventName) { + return eventName + '.' + NAMESPACE; + } + + ).join(' '); + + /** + * Default settings + * @private + * @const + * @type {Object} + */ + var DEFAULTS = $.extend({ + hashTracking: true, + closeOnConfirm: true, + closeOnCancel: true, + closeOnEscape: true, + closeOnOutsideClick: true, + modifier: '' + }, global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.DEFAULTS); + + /** + * States of the Remodal + * @private + * @const + * @enum {String} + */ + var STATES = { + CLOSING: 'closing', + CLOSED: 'closed', + OPENING: 'opening', + OPENED: 'opened' + }; + + /** + * Reasons of the state change. + * @private + * @const + * @enum {String} + */ + var STATE_CHANGE_REASONS = { + CONFIRMATION: 'confirmation', + CANCELLATION: 'cancellation' + }; + + /** + * Is animation supported? + * @private + * @const + * @type {Boolean} + */ + var IS_ANIMATION = (function() { + var style = document.createElement('div').style; + + return style.animationName !== undefined || + style.WebkitAnimationName !== undefined || + style.MozAnimationName !== undefined || + style.msAnimationName !== undefined || + style.OAnimationName !== undefined; + })(); + + /** + * Current modal + * @private + * @type {Remodal} + */ + var current; + + /** + * Scrollbar position + * @private + * @type {Number} + */ + var scrollTop; + + /** + * Returns an animation duration + * @private + * @param {jQuery} $elem + * @returns {Number} + */ + function getAnimationDuration($elem) { + if ( + IS_ANIMATION && + $elem.css('animation-name') === 'none' && + $elem.css('-webkit-animation-name') === 'none' && + $elem.css('-moz-animation-name') === 'none' && + $elem.css('-o-animation-name') === 'none' && + $elem.css('-ms-animation-name') === 'none' + ) { + return 0; + } + + var duration = $elem.css('animation-duration') || + $elem.css('-webkit-animation-duration') || + $elem.css('-moz-animation-duration') || + $elem.css('-o-animation-duration') || + $elem.css('-ms-animation-duration') || + '0s'; + + var delay = $elem.css('animation-delay') || + $elem.css('-webkit-animation-delay') || + $elem.css('-moz-animation-delay') || + $elem.css('-o-animation-delay') || + $elem.css('-ms-animation-delay') || + '0s'; + + var iterationCount = $elem.css('animation-iteration-count') || + $elem.css('-webkit-animation-iteration-count') || + $elem.css('-moz-animation-iteration-count') || + $elem.css('-o-animation-iteration-count') || + $elem.css('-ms-animation-iteration-count') || + '1'; + + var max; + var len; + var num; + var i; + + duration = duration.split(', '); + delay = delay.split(', '); + iterationCount = iterationCount.split(', '); + + // The 'duration' size is the same as the 'delay' size + for (i = 0, len = duration.length, max = Number.NEGATIVE_INFINITY; i < len; i++) { + num = parseFloat(duration[i]) * parseInt(iterationCount[i], 10) + parseFloat(delay[i]); + + if (num > max) { + max = num; + } + } + + return num; + } + + /** + * Returns a scrollbar width + * @private + * @returns {Number} + */ + function getScrollbarWidth() { + if ($(document.body).height() <= $(window).height()) { + return 0; + } + + var outer = document.createElement('div'); + var inner = document.createElement('div'); + var widthNoScroll; + var widthWithScroll; + + outer.style.visibility = 'hidden'; + outer.style.width = '100px'; + document.body.appendChild(outer); + + widthNoScroll = outer.offsetWidth; + + // Force scrollbars + outer.style.overflow = 'scroll'; + + // Add inner div + inner.style.width = '100%'; + outer.appendChild(inner); + + widthWithScroll = inner.offsetWidth; + + // Remove divs + outer.parentNode.removeChild(outer); + + return widthNoScroll - widthWithScroll; + } + + /** + * Locks the screen + * @private + */ + function lockScreen() { + var $html = $('html'); + var lockedClass = NAMESPACE + '-is-locked'; + var paddingRight; + var $body; + + if (!$html.hasClass(lockedClass)) { + $body = $(document.body); + + // Zepto does not support '-=', '+=' in the `css` method + paddingRight = parseInt($body.css('padding-right'), 10) + getScrollbarWidth(); + + $body.css('padding-right', paddingRight + 'px'); + $html.addClass(lockedClass); + } + } + + /** + * Unlocks the screen + * @private + */ + function unlockScreen() { + var $html = $('html'); + var lockedClass = NAMESPACE + '-is-locked'; + var paddingRight; + var $body; + + if ($html.hasClass(lockedClass)) { + $body = $(document.body); + + // Zepto does not support '-=', '+=' in the `css` method + paddingRight = parseInt($body.css('padding-right'), 10) - getScrollbarWidth(); + + $body.css('padding-right', paddingRight + 'px'); + $html.removeClass(lockedClass); + } + } + + /** + * Sets a state for an instance + * @private + * @param {Remodal} instance + * @param {STATES} state + * @param {Boolean} isSilent If true, Remodal does not trigger events + * @param {String} Reason of a state change. + */ + function setState(instance, state, isSilent, reason) { + instance.$bg + .removeClass( + NAMESPACE + '-is-' + STATES.CLOSING + ' ' + + NAMESPACE + '-is-' + STATES.OPENING + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED + ' ' + + NAMESPACE + '-is-' + STATES.OPENED) + .addClass(NAMESPACE + '-is-' + state); + + instance.$overlay + .removeClass( + NAMESPACE + '-is-' + STATES.CLOSING + ' ' + + NAMESPACE + '-is-' + STATES.OPENING + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED + ' ' + + NAMESPACE + '-is-' + STATES.OPENED) + .addClass(NAMESPACE + '-is-' + state); + + instance.$wrapper + .removeClass( + NAMESPACE + '-is-' + STATES.CLOSING + ' ' + + NAMESPACE + '-is-' + STATES.OPENING + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED + ' ' + + NAMESPACE + '-is-' + STATES.OPENED) + .addClass(NAMESPACE + '-is-' + state); + + instance.$modal + .removeClass( + NAMESPACE + '-is-' + STATES.CLOSING + ' ' + + NAMESPACE + '-is-' + STATES.OPENING + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED + ' ' + + NAMESPACE + '-is-' + STATES.OPENED) + .addClass(NAMESPACE + '-is-' + state); + + instance.state = state; + !isSilent && instance.$modal.trigger({ + type: state, + reason: reason + }, [{ reason: reason }]); + } + + /** + * Synchronizes with the animation + * @param {Function} doBeforeAnimation + * @param {Function} doAfterAnimation + * @param {Remodal} instance + */ + function syncWithAnimation(doBeforeAnimation, doAfterAnimation, instance) { + var runningAnimationsCount = 0; + + var handleAnimationStart = function(e) { + if (e.target !== this) { + return; + } + + runningAnimationsCount++; + }; + + var handleAnimationEnd = function(e) { + if (e.target !== this) { + return; + } + + if (--runningAnimationsCount === 0) { + + // Remove event listeners + $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { + instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS); + }); + + doAfterAnimation(); + } + }; + + $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { + instance[elemName] + .on(ANIMATIONSTART_EVENTS, handleAnimationStart) + .on(ANIMATIONEND_EVENTS, handleAnimationEnd); + }); + + doBeforeAnimation(); + + // If the animation is not supported by a browser or its duration is 0 + if ( + getAnimationDuration(instance.$bg) === 0 && + getAnimationDuration(instance.$overlay) === 0 && + getAnimationDuration(instance.$wrapper) === 0 && + getAnimationDuration(instance.$modal) === 0 + ) { + + // Remove event listeners + $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { + instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS); + }); + + doAfterAnimation(); + } + } + + /** + * Closes immediately + * @private + * @param {Remodal} instance + */ + function halt(instance) { + if (instance.state === STATES.CLOSED) { + return; + } + + $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) { + instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS); + }); + + instance.$bg.removeClass(instance.settings.modifier); + instance.$overlay.removeClass(instance.settings.modifier).hide(); + instance.$wrapper.hide(); + unlockScreen(); + setState(instance, STATES.CLOSED, true); + } + + /** + * Parses a string with options + * @private + * @param str + * @returns {Object} + */ + function parseOptions(str) { + var obj = {}; + var arr; + var len; + var val; + var i; + + // Remove spaces before and after delimiters + str = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ','); + + // Parse a string + arr = str.split(','); + for (i = 0, len = arr.length; i < len; i++) { + arr[i] = arr[i].split(':'); + val = arr[i][1]; + + // Convert a string value if it is like a boolean + if (typeof val === 'string' || val instanceof String) { + val = val === 'true' || (val === 'false' ? false : val); + } + + // Convert a string value if it is like a number + if (typeof val === 'string' || val instanceof String) { + val = !isNaN(val) ? +val : val; + } + + obj[arr[i][0]] = val; + } + + return obj; + } + + /** + * Handles the hashchange event + * @private + * @listens hashchange + */ + function handleHashChangeEvent() { + var id = location.hash.replace('#', ''); + var instance; + var $elem; + + if (!id) { + + // Check if we have currently opened modal and animation was completed + if (current && current.state === STATES.OPENED && current.settings.hashTracking) { + current.close(); + } + } else { + + // Catch syntax error if your hash is bad + try { + $elem = $( + '[data-' + PLUGIN_NAME + '-id=' + + id.replace(new RegExp('/', 'g'), '\\/') + ']' + ); + } catch (err) {} + + if ($elem && $elem.length) { + instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)]; + + if (instance && instance.settings.hashTracking) { + instance.open(); + } + } + + } + } + + /** + * Remodal constructor + * @constructor + * @param {jQuery} $modal + * @param {Object} options + */ + function Remodal($modal, options) { + var $body = $(document.body); + var remodal = this; + + remodal.settings = $.extend({}, DEFAULTS, options); + remodal.index = $[PLUGIN_NAME].lookup.push(remodal) - 1; + remodal.state = STATES.CLOSED; + + remodal.$overlay = $('.' + NAMESPACE + '-overlay'); + + if (!remodal.$overlay.length) { + remodal.$overlay = $('
').addClass(NAMESPACE + '-overlay ' + NAMESPACE + '-is-' + STATES.CLOSED).hide(); + $body.append(remodal.$overlay); + } + + remodal.$bg = $('.' + NAMESPACE + '-bg').addClass(NAMESPACE + '-is-' + STATES.CLOSED); + remodal.$modal = $modal; + remodal.$modal.addClass( + NAMESPACE + '-is-initialized' + ' ' + + NAMESPACE + ' ' + remodal.settings.modifier + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED); + + remodal.$wrapper = $('
') + .addClass( + NAMESPACE + '-wrapper ' + remodal.settings.modifier + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED) + .hide() + .append(remodal.$modal); + $body.append(remodal.$wrapper); + + // Add the event listener for the close button + remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + NAMESPACE + '-action="close"]', function(e) { + e.preventDefault(); + + remodal.close(); + }); + + // Add the event listener for the cancel button + remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + NAMESPACE + '-action="cancel"]', function(e) { + e.preventDefault(); + + remodal.$modal.trigger(STATE_CHANGE_REASONS.CANCELLATION); + + if (remodal.settings.closeOnCancel) { + remodal.close(STATE_CHANGE_REASONS.CANCELLATION); + } + }); + + // Add the event listener for the confirm button + remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + NAMESPACE + '-action="confirm"]', function(e) { + e.preventDefault(); + + remodal.$modal.trigger(STATE_CHANGE_REASONS.CONFIRMATION); + + if (remodal.settings.closeOnConfirm) { + remodal.close(STATE_CHANGE_REASONS.CONFIRMATION); + } + }); + + // Add the event listener for the overlay + remodal.$wrapper.on('click.' + NAMESPACE, function(e) { + var $target = $(e.target); + + if (!$target.hasClass(NAMESPACE + '-wrapper')) { + return; + } + + if (remodal.settings.closeOnOutsideClick) { + remodal.close(); + } + }); + } + + /** + * Opens a modal window + * @public + */ + Remodal.prototype.open = function() { + var remodal = this; + var id; + + // Check if the animation was completed + if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING) { + return; + } + + id = remodal.$modal.attr('data-' + PLUGIN_NAME + '-id'); + + if (id && remodal.settings.hashTracking) { + scrollTop = $(window).scrollTop(); + location.hash = id; + } + + if (current && current !== remodal) { + halt(current); + } + + current = remodal; + lockScreen(); + remodal.$bg.addClass(remodal.settings.modifier); + remodal.$overlay.addClass(remodal.settings.modifier).show(); + remodal.$wrapper.show().scrollTop(0); + + syncWithAnimation( + function() { + setState(remodal, STATES.OPENING); + }, + + function() { + setState(remodal, STATES.OPENED); + }, + + remodal); + }; + + /** + * Closes a modal window + * @public + * @param {String} reason + */ + Remodal.prototype.close = function(reason) { + var remodal = this; + + // Check if the animation was completed + if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING) { + return; + } + + if ( + remodal.settings.hashTracking && + remodal.$modal.attr('data-' + PLUGIN_NAME + '-id') === location.hash.substr(1) + ) { + location.hash = ''; + $(window).scrollTop(scrollTop); + } + + syncWithAnimation( + function() { + setState(remodal, STATES.CLOSING, false, reason); + }, + + function() { + remodal.$bg.removeClass(remodal.settings.modifier); + remodal.$overlay.removeClass(remodal.settings.modifier).hide(); + remodal.$wrapper.hide(); + unlockScreen(); + + setState(remodal, STATES.CLOSED, false, reason); + }, + + remodal); + }; + + /** + * Returns a current state of a modal + * @public + * @returns {STATES} + */ + Remodal.prototype.getState = function() { + return this.state; + }; + + /** + * Destroys a modal + * @public + */ + Remodal.prototype.destroy = function() { + var lookup = $[PLUGIN_NAME].lookup; + var instanceCount; + + halt(this); + this.$wrapper.remove(); + + delete lookup[this.index]; + instanceCount = $.grep(lookup, function(instance) { + return !!instance; + }).length; + + if (instanceCount === 0) { + this.$overlay.remove(); + this.$bg.removeClass( + NAMESPACE + '-is-' + STATES.CLOSING + ' ' + + NAMESPACE + '-is-' + STATES.OPENING + ' ' + + NAMESPACE + '-is-' + STATES.CLOSED + ' ' + + NAMESPACE + '-is-' + STATES.OPENED); + } + }; + + /** + * Special plugin object for instances + * @public + * @type {Object} + */ + $[PLUGIN_NAME] = { + lookup: [] + }; + + /** + * Plugin constructor + * @constructor + * @param {Object} options + * @returns {JQuery} + */ + $.fn[PLUGIN_NAME] = function(opts) { + var instance; + var $elem; + + this.each(function(index, elem) { + $elem = $(elem); + + if ($elem.data(PLUGIN_NAME) == null) { + instance = new Remodal($elem, opts); + $elem.data(PLUGIN_NAME, instance.index); + + if ( + instance.settings.hashTracking && + $elem.attr('data-' + PLUGIN_NAME + '-id') === location.hash.substr(1) + ) { + instance.open(); + } + } else { + instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)]; + } + }); + + return instance; + }; + + $(document).ready(function() { + + // data-remodal-target opens a modal window with the special Id + $(document).on('click', '[data-' + PLUGIN_NAME + '-target]', function(e) { + e.preventDefault(); + + var elem = e.currentTarget; + var id = elem.getAttribute('data-' + PLUGIN_NAME + '-target'); + var $target = $('[data-' + PLUGIN_NAME + '-id=' + id + ']'); + + $[PLUGIN_NAME].lookup[$target.data(PLUGIN_NAME)].open(); + }); + + // Auto initialization of modal windows + // They should have the 'remodal' class attribute + // Also you can write the `data-remodal-options` attribute to pass params into the modal + $(document).find('.' + NAMESPACE).each(function(i, container) { + var $container = $(container); + var options = $container.data(PLUGIN_NAME + '-options'); + + if (!options) { + options = {}; + } else if (typeof options === 'string' || options instanceof String) { + options = parseOptions(options); + } + + $container[PLUGIN_NAME](options); + }); + + // Handles the keyup event + $(document).on('keyup.' + NAMESPACE, function(e) { + if (current && current.settings.closeOnEscape && current.state === STATES.OPENED && e.keyCode === 27) { + current.close(); + } + }); + + // Handles the hashchange event + $(window).on('hashchange.' + NAMESPACE, handleHashChangeEvent); + }); +}); diff --git a/test/remodal.html b/test/remodal.html index 286f52c..9371341 100644 --- a/test/remodal.html +++ b/test/remodal.html @@ -3,43 +3,56 @@ Remodal Test Suite - + - + - + - - + + + -
+
+
+ Call +
-Call
- Cancel - OK + + + Cancel + OK
-
- Cancel - OK +
+ + + Cancel + OK
+
diff --git a/test/remodal_test.js b/test/remodal_test.js index c14d631..26511bd 100644 --- a/test/remodal_test.js +++ b/test/remodal_test.js @@ -1,4 +1,5 @@ !(function($, location, document) { + /* ======== A Handy Little QUnit Reference ======== http://api.qunitjs.com/ @@ -21,37 +22,22 @@ */ var $document = $(document); - var index1; - var index2; - var $inst1; - var $inst2; - - QUnit.begin(function() { - index1 = $('[data-remodal-id=modal]').data('remodal'); - index2 = $('[data-remodal-id=modal2]').data('remodal'); - $inst1 = $.remodal.lookup[index1]; - $inst2 = $.remodal.lookup[index2]; - }); - - QUnit.test('Auto-initialization', function() { - equal(index1, 0); - equal(index2, 1); - ok($inst1); - ok($inst2); - }); - QUnit.test('JS-initialization', function() { - var $inst3 = $('[data-remodal-id=modal3]').remodal(); + QUnit.test('Auto-initialization', function(assert) { + var $elem1 = $('[data-remodal-id=modal]'); + var $elem2 = $('[data-remodal-id=modal2]'); - ok($inst3); - equal($inst3.index, 2); + assert.equal($elem1.data('remodal'), 0, 'index is right'); + assert.equal($elem2.data('remodal'), 1, 'index is right'); + assert.ok($elem1.remodal(), 'instance exists'); + assert.ok($elem2.remodal(), 'instance exists'); }); - QUnit.test('Re-initialization', function() { - var $inst3 = $('[data-remodal-id=modal3]').remodal(); + QUnit.test('JS-initialization', function(assert) { + var $elem = $('[data-remodal-id=modal3]'); - ok($inst3); - equal($inst3.index, 2); + assert.ok($elem.remodal(), 'instance exists'); + assert.equal($elem.data('remodal'), 2, 'index is right'); }); QUnit.asyncTest('Hash tracking', function(assert) { @@ -68,7 +54,7 @@ location.hash = '#modal'; }); - QUnit.asyncTest('Opening with the `data-remodal-target` directive', function(assert) { + QUnit.asyncTest('`data-remodal-target` directive', function(assert) { $document.one('opened', '[data-remodal-id=modal]', function() { assert.ok(true, 'the modal is opened'); location.hash = '#'; @@ -82,11 +68,12 @@ $('[data-remodal-target=modal]').click(); }); - QUnit.asyncTest('events', function(assert) { - var $confirmButton = $inst1.$confirmButton; - var $cancelButton = $inst1.$cancelButton; + QUnit.asyncTest('Events', function(assert) { + var remodal = $('[data-remodal-id=modal]').remodal(); + var $confirmButton = $('[data-remodal-id=modal] [data-remodal-action=confirm]'); + var $cancelButton = $('[data-remodal-id=modal] [data-remodal-action=cancel]'); - $document.one('open', '[data-remodal-id=modal]', function() { + $document.one('opening', '[data-remodal-id=modal]', function() { assert.ok(true, 'opening'); }); @@ -95,25 +82,24 @@ $confirmButton.click(); }); - $document.one('confirm', '[data-remodal-id=modal]', function() { + $document.one('confirmation', '[data-remodal-id=modal]', function() { assert.ok(true, 'confirmed'); - $confirmButton.click(); }); - $document.one('close', '[data-remodal-id=modal]', function() { + $document.one('closing', '[data-remodal-id=modal]', function() { assert.ok(true, 'closing'); }); $document.one('closed', '[data-remodal-id=modal]', function() { assert.ok(true, 'closed'); - $inst1.open(); + remodal.open(); $document.one('opened', '[data-remodal-id=modal]', function() { $cancelButton.click(); }); }); - $document.one('cancel', '[data-remodal-id=modal]', function() { + $document.one('cancellation', '[data-remodal-id=modal]', function() { assert.ok(true, 'canceled'); $document.one('closed', '[data-remodal-id=modal]', function() { @@ -124,59 +110,69 @@ location.hash = '#modal'; }); - QUnit.asyncTest('Confirm button click', function(assert) { - var $confirmButton = $inst1.$confirmButton; + QUnit.asyncTest('Click on the confirmation button', function(assert) { + var $confirmButton = $('[data-remodal-id=modal] [data-remodal-action=confirm]'); $document.one('opened', '[data-remodal-id=modal]', function() { $confirmButton.click(); }); - $document.one('confirm', '[data-remodal-id=modal]', function() { - assert.ok(true, 'confirm button works'); + $document.one('confirmation', '[data-remodal-id=modal]', function() { + assert.ok(true, 'confirmation event is triggered'); }); - $document.one('closed', '[data-remodal-id=modal]', function() { + $document.one('closing', '[data-remodal-id=modal]', function(e) { + assert.equal(e.reason, 'confirmation', 'reason is right'); + }); + + $document.one('closed', '[data-remodal-id=modal]', function(e) { + assert.equal(e.reason, 'confirmation', 'reason is right'); QUnit.start(); }); location.hash = '#modal'; }); - QUnit.asyncTest('Cancel button click', function(assert) { - var $cancelButton = $inst1.$cancelButton; + QUnit.asyncTest('Click on the cancel button', function(assert) { + var $cancelButton = $('[data-remodal-id=modal] [data-remodal-action=cancel]'); $document.one('opened', '[data-remodal-id=modal]', function() { $cancelButton.click(); }); - $document.one('cancel', '[data-remodal-id=modal]', function() { - assert.ok(true, 'cancel button works'); + $document.one('cancellation', '[data-remodal-id=modal]', function() { + assert.ok(true, 'cancellation event is triggered'); }); - $document.one('closed', '[data-remodal-id=modal]', function() { + $document.one('closing', '[data-remodal-id=modal]', function(e) { + assert.equal(e.reason, 'cancellation', 'reason is right'); + }); + + $document.one('closed', '[data-remodal-id=modal]', function(e) { + assert.equal(e.reason, 'cancellation', 'reason is right'); QUnit.start(); }); location.hash = '#modal'; }); - QUnit.asyncTest('Close button click', function(assert) { - var $closeButton = $inst1.$closeButton; + QUnit.asyncTest('Click on the close button', function(assert) { + var $closeButton = $('[data-remodal-id=modal] [data-remodal-action=close]'); $document.one('opened', '[data-remodal-id=modal]', function() { $closeButton.click(); }); $document.one('closed', '[data-remodal-id=modal]', function() { - assert.ok(true, 'Close button works'); + assert.ok(true, 'close button works'); QUnit.start(); }); location.hash = '#modal'; }); - QUnit.asyncTest('Wrapper click', function(assert) { - var $wrapper = $inst1.$wrapper; + QUnit.asyncTest('Click on the wrapper', function(assert) { + var $wrapper = $('[data-remodal-id=modal]').parent(); $document.one('opened', '[data-remodal-id=modal]', function() { $wrapper.click(); @@ -190,57 +186,104 @@ location.hash = '#modal'; }); - QUnit.asyncTest('methods', function(assert) { + QUnit.asyncTest('Esc key up', function(assert) { $document.one('opened', '[data-remodal-id=modal]', function() { - assert.ok(true, 'opening'); - $inst1.close(); + $document.trigger($.Event('keyup', { keyCode: 27 })); }); $document.one('closed', '[data-remodal-id=modal]', function() { - assert.ok(true, 'closed'); + assert.ok(true, 'Esc key works'); QUnit.start(); }); - $inst1.open(); + location.hash = '#modal'; }); - QUnit.test('animation timeout', function() { + QUnit.asyncTest('#open, #close, #getState', function(assert) { + var remodal = $('[data-remodal-id=modal]').remodal(); + + $document.one('opening', '[data-remodal-id=modal]', function() { + assert.equal(remodal.getState(), 'opening', 'state is "opening"'); + }); + + $document.one('opened', '[data-remodal-id=modal]', function() { + assert.equal(remodal.getState(), 'opened', 'state is "opened"'); + remodal.close(); + }); + + $document.one('closing', '[data-remodal-id=modal]', function() { + assert.equal(remodal.getState(), 'closing', 'state is "closing"'); + }); - // Check animation timeout - equal(~~$inst1.td, 300); + $document.one('closed', '[data-remodal-id=modal]', function() { + assert.equal(remodal.getState(), 'closed', 'state is "closed"'); + QUnit.start(); + }); + + remodal.open(); + }); + + QUnit.test('#destroy', function(assert) { + var remodal = $('
') + .appendTo($(document.body)) + .remodal(); + + var instanceCount = $.grep($.remodal.lookup, function(instance) { + return !!instance; + }).length; + + assert.equal($('[data-remodal-id=modal4]').length, 1, 'modal exists'); + assert.equal(instanceCount, 4, 'count of instances is right'); + + remodal.destroy(); + + instanceCount = $.grep($.remodal.lookup, function(instance) { + return !!instance; + }).length; + + assert.equal($('[data-remodal-id=modal4]').length, 0, 'modal does not exist'); + assert.equal(instanceCount, 3, 'count of instances is right'); }); - QUnit.asyncTest('lock/unlock the scroll bar', function(assert) { - $(document.body).css('height', '10000px'); + QUnit.asyncTest('Lock/unlock the scroll bar', function(assert) { + var $html = $('html'); + var $body = $(document.body); + var remodal = $('[data-remodal-id=modal]').remodal(); + + $body.css('height', '10000px'); $document.one('opened', '[data-remodal-id=modal]', function() { - assert.ok($('html, body').hasClass('remodal-is-locked')); - assert.ok(parseInt($(document.body).css('padding-right')) > 0); - $inst1.close(); + assert.ok($html.hasClass('remodal-is-locked'), 'page is locked'); + assert.ok(parseInt($body.css('padding-right')) >= 0, 'padding-right is added'); + remodal.close(); }); $document.one('closed', '[data-remodal-id=modal]', function() { - assert.ok(!$('html, body').hasClass('remodal-is-locked')); - assert.ok(parseInt($(document.body).css('padding-right')) === 0); + assert.notOk($html.hasClass('remodal-is-locked'), 'page isn\'t locked'); + assert.equal(parseInt($body.css('padding-right')), 0, 'padding-right equals 0'); QUnit.start(); }); location.hash = '#modal'; }); - QUnit.asyncTest('do not lock/unlock the scroll bar twice', function(assert) { - $('html').addClass('remodal-is-locked'); - $(document.body).css('height', '10000px').css('padding-right', '20px'); + QUnit.asyncTest('Do not lock/unlock the scroll bar twice', function(assert) { + var $html = $('html'); + var $body = $(document.body); + var remodal = $('[data-remodal-id=modal]').remodal(); + + $html.addClass('remodal-is-locked'); + $body.css('height', '10000px').css('padding-right', '20px'); $document.one('opened', '[data-remodal-id=modal]', function() { - assert.ok($('html').hasClass('remodal-is-locked')); - assert.ok(parseInt($(document.body).css('padding-right')) === 20); - $inst1.close(); + assert.ok($html.hasClass('remodal-is-locked'), 'page is locked'); + assert.equal(parseInt($body.css('padding-right')), 20, 'padding-right equals 20'); + remodal.close(); }); $document.one('closed', '[data-remodal-id=modal]', function() { - assert.ok(!$('html').hasClass('remodal-is-locked')); - assert.ok(parseInt($(document.body).css('padding-right')) < 20); + assert.notOk($html.hasClass('remodal-is-locked'), 'page isn\'t locked'); + assert.ok(parseInt($body.css('padding-right')) <= 20, 'padding-right is correct'); QUnit.start(); }); @@ -248,33 +291,132 @@ }); QUnit.test('Options parsing', function() { - propEqual($inst2.settings, { + var remodal = $('[data-remodal-id=modal2]').remodal(); + + propEqual(remodal.settings, { hashTracking: false, closeOnConfirm: false, closeOnCancel: false, - closeOnEscape: true, - closeOnAnyClick: true + closeOnEscape: false, + closeOnOutsideClick: false, + modifier: 'without-animation with-test-class' + }, 'options are correctly parsed'); + }); + + QUnit.test('"hashTracking" option', function(assert) { + var $wrapper = $('[data-remodal-id=modal2]').parent(); + var remodal = $wrapper.children().remodal(); + + remodal.open(); + assert.notOk(location.hash, 'hash tracking is disabled'); + remodal.close(); + }); + + QUnit.asyncTest('"closeOnConfirm" option', function(assert) { + var $wrapper = $('[data-remodal-id=modal2]').parent(); + var remodal = $wrapper.children().remodal(); + + $document.one('opened', '[data-remodal-id=modal2]', function() { + $wrapper.find('[data-remodal-action=confirm]').click(); }); + + $document.one('confirmation', '[data-remodal-id=modal2]', function() { + assert.equal(remodal.getState(), 'opened', 'it is still opened'); + remodal.close(); + }); + + $document.one('closed', '[data-remodal-id=modal2]', function() { + QUnit.start(); + }); + + remodal.open(); + }); + + QUnit.asyncTest('"closeOnCancel" option', function(assert) { + var $wrapper = $('[data-remodal-id=modal2]').parent(); + var remodal = $wrapper.children().remodal(); + + $document.one('opened', '[data-remodal-id=modal2]', function() { + $wrapper.find('[data-remodal-action=cancel]').click(); + }); + + $document.one('cancellation', '[data-remodal-id=modal2]', function() { + assert.equal(remodal.getState(), 'opened', 'it is still opened'); + remodal.close(); + }); + + $document.one('closed', '[data-remodal-id=modal2]', function() { + QUnit.start(); + }); + + remodal.open(); }); - QUnit.asyncTest('Options', function(assert) { - $inst2.open(); - assert.ok(!location.hash, 'hashTracking is disabled'); + QUnit.asyncTest('"closeOnEscape" option', function(assert) { + var remodal = $('[data-remodal-id=modal2]').remodal(); + + $document.one('opened', '[data-remodal-id=modal2]', function() { + $document.trigger($.Event('keyup', { keyCode: 27 })); - $document.one('confirm', '[data-remodal-id=modal2]', function() { setTimeout(function() { - assert.ok($inst2.$wrapper.css('display') === 'block'); - }, $inst2.td + 100); + assert.equal(remodal.getState(), 'opened', 'it is still opened'); + remodal.close(); + }, 50); + }); + + $document.one('closed', '[data-remodal-id=modal2]', function() { + QUnit.start(); }); - $document.one('cancel', '[data-remodal-id=modal2]', function() { + remodal.open(); + }); + + QUnit.asyncTest('"closeOnOutsideClick" option', function(assert) { + var $wrapper = $('[data-remodal-id=modal2]').parent(); + var remodal = $wrapper.children().remodal(); + + $document.one('opened', '[data-remodal-id=modal2]', function() { + $wrapper.click(); + setTimeout(function() { - assert.ok($inst2.$wrapper.css('display') === 'block'); - QUnit.start(); - }, $inst2.td + 100); + assert.equal(remodal.getState(), 'opened', 'it is still opened'); + remodal.close(); + }, 50); + }); + + $document.one('closed', '[data-remodal-id=modal2]', function() { + QUnit.start(); + }); + + remodal.open(); + }); + + QUnit.asyncTest('"modifier" option', function(assert) { + var $modal = $('[data-remodal-id=modal2]'); + var $overlay = $('.remodal-overlay'); + var $wrapper = $modal.parent(); + var $bg = $('.remodal-bg'); + var remodal = $modal.remodal(); + + $document.one('opened', '[data-remodal-id=modal2]', function() { + assert.ok($bg.hasClass('without-animation with-test-class'), 'bg has the modifier'); + assert.ok($overlay.hasClass('without-animation with-test-class'), 'overlay has the modifier'); + assert.ok($wrapper.hasClass('without-animation with-test-class'), 'wrapper has the modifier'); + assert.ok($modal.hasClass('without-animation with-test-class'), 'modal has the modifier'); + + remodal.close(); }); - $inst2.$confirmButton.click(); - $inst2.$cancelButton.click(); + $document.one('closed', '[data-remodal-id=modal2]', function() { + assert.notOk($bg.hasClass('without-animation with-test-class'), 'bg hasn\'t the modifier'); + assert.notOk($overlay.hasClass('without-animation with-test-class'), 'overlay has\'t the modifier'); + assert.ok($wrapper.hasClass('without-animation with-test-class'), 'wrapper still has the modifier'); + assert.ok($modal.hasClass('without-animation with-test-class'), 'modal still has the modifier'); + + QUnit.start(); + }); + + remodal.open(); }); + }(window.jQuery || window.Zepto, location, document));