├── .travis.yml ├── header.txt ├── .gitignore ├── karma.conf.js ├── karma-jquery-latest.conf.js ├── bower.json ├── .jshintrc ├── Gruntfile.js ├── CONTRIBUTING.md ├── package.json ├── LICENSE.md ├── karma-base.conf.js ├── tests ├── bootbox.test.js ├── defaults.test.js ├── confirm.test.coffee ├── locales.test.coffee ├── alert.test.js ├── dialog.test.coffee ├── vendor │ ├── bootstrap-3.0.0.min.js │ └── bootstrap-3.1.1.min.js └── prompt.test.coffee ├── README.md ├── CHANGELOG.md └── bootbox.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | -------------------------------------------------------------------------------- /header.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * bootbox.js v4.2.0 3 | * 4 | * http://bootboxjs.com/license.txt 5 | */ 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swo 2 | *.swp 3 | node_modules 4 | npm-debug.log 5 | bootbox.min.js 6 | .idea 7 | tests/reports 8 | tests/coverage 9 | build/size.csv 10 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | var baseConfig = require("./karma-base.conf"); 2 | 3 | module.exports = baseConfig({ 4 | vendor: [ 5 | "tests/vendor/jquery-1.8.3.min.js", 6 | "tests/vendor/bootstrap-3.0.0.min.js" 7 | ] 8 | }); 9 | -------------------------------------------------------------------------------- /karma-jquery-latest.conf.js: -------------------------------------------------------------------------------- 1 | var baseConfig = require("./karma-base.conf"); 2 | 3 | module.exports = baseConfig({ 4 | vendor: [ 5 | "tests/vendor/jquery-1.10.2.min.js", 6 | "tests/vendor/bootstrap-3.0.0.min.js" 7 | ] 8 | }); 9 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootbox.js", 3 | "version": "4.2.0", 4 | "main": "./bootbox.js", 5 | "ignore": [ 6 | "CHANGELOG.md", 7 | "CONTRIBUTING", 8 | "Gruntfile.js", 9 | "header.txt", 10 | "karma*.js", 11 | "build", 12 | "tests" 13 | ], 14 | "dependencies": { 15 | "bootstrap": ">= 3.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef": true, 3 | "unused": true, 4 | "eqeqeq": true, 5 | "curly": true, 6 | "newcap": true, 7 | "strict": true, 8 | "trailing": true, 9 | "quotmark": "double", 10 | "browser": true, 11 | 12 | "globals": { 13 | "beforeEach": true, 14 | "describe": true, 15 | "it": true, 16 | "define": true, 17 | "module": true, 18 | "require": true, 19 | "expect": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.initConfig({ 3 | uglify: { 4 | options: { 5 | banner: grunt.file.read("header.txt") 6 | }, 7 | build: { 8 | files: { 9 | "bootbox.min.js": ["bootbox.js"] 10 | } 11 | } 12 | }, 13 | 14 | jshint: { 15 | options: { 16 | jshintrc: ".jshintrc" 17 | }, 18 | all: ["bootbox.js"] 19 | }, 20 | 21 | karma: { 22 | unit: { 23 | configFile: "karma.conf.js" 24 | } 25 | } 26 | }); 27 | 28 | grunt.loadNpmTasks("grunt-contrib-uglify"); 29 | grunt.loadNpmTasks("grunt-contrib-jshint"); 30 | grunt.loadNpmTasks("grunt-karma"); 31 | 32 | grunt.registerTask("default", ["jshint", "karma"]); 33 | }; 34 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Submitting Pull Requests 2 | 3 | **Please follow these basic steps to simplify pull request reviews - if you don't you'll probably just be asked to anyway.** 4 | 5 | * Please rebase your branch against the current master 6 | * Run ```npm install``` to make sure your development dependencies are up-to-date 7 | * [grunt-cli](https://github.com/gruntjs/grunt-cli) >= 0.4.0 is required to sanity check your contribution 8 | * Please ensure that the test suite passes **and** that bootbox.js is lint free before submitting a PR by running: 9 | * ```grunt``` 10 | * If you've added new functionality, **please** include tests which validate its behaviour 11 | * **this includes pull requests which _only_ add new locales!** 12 | 13 | ## Submitting bug reports 14 | 15 | * Where at all possible, please try and provide a link to a jsfiddle.net example or similar 16 | * Please detail the affected browser(s) and operating system(s) 17 | * Please be sure to state which version of Bootbox, jQuery **and** Bootstrap you're using 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootbox.js", 3 | "private": true, 4 | "version": "4.2.0", 5 | "description": "Wrappers for JavaScript alert(), confirm() and other flexible dialogs using Twitter's bootstrap framework", 6 | "directories": { 7 | "test": "tests" 8 | }, 9 | "scripts": { 10 | "test": "./node_modules/.bin/karma start" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/makeusabrew/bootbox.git" 15 | }, 16 | "author": "Nick Payne ", 17 | "license": "MIT", 18 | "readmeFilename": "README.md", 19 | "devDependencies": { 20 | "grunt-contrib-uglify": "~0.2.2", 21 | "grunt": "~0.4.1", 22 | "karma": "~0.10.1", 23 | "grunt-contrib-jshint": "~0.6.3", 24 | "karma-mocha": "~0.1.0", 25 | "karma-phantomjs-launcher": "~0.1.0", 26 | "mocha": "~1.12.0", 27 | "karma-coffee-preprocessor": "~0.1.0", 28 | "karma-coverage": "~0.1.0", 29 | "karma-junit-reporter": "~0.1.0", 30 | "sinon": "~1.7.3", 31 | "sinon-chai": "~2.4.0", 32 | "karma-chai": "0.0.1", 33 | "grunt-karma": "~0.6.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | (The MIT License) 4 | 5 | Copyright (C) 2011-2014 by Nick Payne 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE 24 | -------------------------------------------------------------------------------- /karma-base.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(params) { 2 | 3 | "use strict"; 4 | 5 | console.log("Vendor files: " + params.vendor.join(", ")); 6 | 7 | return function(config) { 8 | 9 | return config.set({ 10 | basePath: "", 11 | frameworks: ["mocha", "chai"], 12 | files: Array.prototype.concat([ 13 | "node_modules/sinon/lib/sinon.js", 14 | "node_modules/sinon/lib/sinon/spy.js", 15 | "node_modules/sinon/lib/sinon/stub.js", 16 | "node_modules/sinon-chai/lib/sinon-chai.js"], 17 | 18 | params.vendor, 19 | 20 | params.src || "bootbox.js", 21 | 22 | ["tests/**/*.test.coffee", 23 | "tests/**/*.test.js"] 24 | ), 25 | exclude: [], 26 | preprocessors: { 27 | "**/*.coffee": ["coffee"], 28 | "bootbox.js": ["coverage"] 29 | }, 30 | reporters: ["dots", "coverage", "junit"], 31 | port: 9876, 32 | colors: true, 33 | logLevel: config.LOG_INFO, 34 | autoWatch: true, 35 | browsers: ["PhantomJS"], 36 | captureTimeout: 60000, 37 | singleRun: true, 38 | 39 | coverageReporter: { 40 | type: "cobertura", 41 | dir: "tests/coverage" 42 | }, 43 | 44 | junitReporter: { 45 | outputFile: "tests/reports/results.xml" 46 | } 47 | }); 48 | 49 | }; 50 | 51 | }; 52 | -------------------------------------------------------------------------------- /tests/bootbox.test.js: -------------------------------------------------------------------------------- 1 | describe("Bootbox", function() { 2 | 3 | it("is attached to the window object", function() { 4 | expect(window.bootbox).to.be.an("object"); 5 | }); 6 | 7 | it("exposes the correct public API", function() { 8 | expect(bootbox.alert).to.be.a("function"); 9 | expect(bootbox.confirm).to.be.a("function"); 10 | expect(bootbox.dialog).to.be.a("function"); 11 | expect(bootbox.setDefaults).to.be.a("function"); 12 | expect(bootbox.hideAll).to.be.a("function"); 13 | }); 14 | 15 | describe("hideAll", function() { 16 | beforeEach(function() { 17 | this.hidden = sinon.spy($.fn, "modal"); 18 | bootbox.hideAll(); 19 | }); 20 | 21 | it("should hide all .bootbox modals", function() { 22 | expect(this.hidden).to.have.been.calledWithExactly("hide"); 23 | }); 24 | }); 25 | 26 | describe("event listeners", function() { 27 | describe("hidden.bs.modal", function() { 28 | beforeEach(function() { 29 | this.dialog = bootbox.alert("hi"); 30 | 31 | this.e = function(target) { 32 | this.removed = sinon.stub(this.dialog, "remove"); 33 | 34 | $(this.dialog).trigger($.Event("hidden.bs.modal", { 35 | target: target 36 | })); 37 | }; 38 | }); 39 | 40 | describe("when triggered with the wrong target", function() { 41 | beforeEach(function() { 42 | this.e({an: "object"}); 43 | }); 44 | 45 | it("does not remove the dialog", function() { 46 | expect(this.removed).not.to.have.been.called; 47 | }); 48 | }); 49 | 50 | describe("when triggered with the correct target", function() { 51 | beforeEach(function() { 52 | this.e(this.dialog.get(0)); 53 | }); 54 | 55 | it("removes the dialog", function() { 56 | expect(this.removed).to.have.been.called; 57 | }); 58 | }); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bootbox - Twitter Bootstrap powered alert, confirm and flexible dialog boxes 2 | 3 | Please see http://bootboxjs.com for full usage instructions, or head over to http://paynedigital.com/bootbox for 4 | the original writeup about the project. 5 | 6 | ## Contact 7 | 8 | The easiest thing is to [find me on twitter @makeusabrew](http://twitter.com/makeusabrew). 9 | 10 | ## Contributing 11 | 12 | Please see the [CONTRIBUTING](https://github.com/makeusabrew/bootbox/blob/master/CONTRIBUTING.md) file for guidelines. 13 | 14 | ## Running Tests [![Build Status](https://api.travis-ci.org/makeusabrew/bootbox.svg)](http://travis-ci.org/makeusabrew/bootbox) 15 | 16 | Tests are run using [Karma](http://karma-runner.github.io/0.8/index.html) using the Mocha test adapter. 17 | To run the tests yourself, simply run ```npm install``` within the project followed by ```npm test```. 18 | Please note that this will require [PhantomJS](http://phantomjs.org/) being installed and in your path - if 19 | it is not, you may run the tests and capture browsers manually by running ```karma start``` from the root 20 | of the project. 21 | 22 | The project is also hosted on [Travis CI](https://travis-ci.org/makeusabrew/bootbox) - when submitting 23 | pull requests **please** ensure your tests pass as failing requests will be rejected. See the 24 | [CONTRIBUTING](https://github.com/makeusabrew/bootbox/blob/master/CONTRIBUTING.md) file for more information. 25 | 26 | ## Building a minified release 27 | 28 | The repository no longer contains a minified bootbox.min.js file - this is now only generated 29 | [for releases](https://github.com/makeusabrew/bootbox/releases). To build your own minified copy 30 | for use in development simply run ```npm install``` if you haven't already, followed by ```grunt uglify```. 31 | This will generate a bootbox.min.js file in your working directory. 32 | 33 | ## A note on Bootstrap dependencies 34 | 35 | Bootbox **4.0.0** is the first release to support Bootstrap 3.0.0. 36 | 37 | Bootbox **3.3.0** is the *last* release to support Bootstrap 2.2.x. 38 | 39 | Much more dependency information can be found [on the Bootbox website](http://bootboxjs.com/#dependencies). 40 | 41 | ### Roadmap 42 | 43 | The latest major release of Bootbox - 4.0.0 - involved a total rewrite of the 44 | internal code and introduced an entirely new public API. It has not re-implemented 45 | some functionality from the 3.x series as of yet; this will be addressed in the 46 | coming weeks in the form of new minor releases; 47 | [a task list for 4.3.0 is available](https://github.com/makeusabrew/bootbox/issues/220) - 48 | please feel free to add feedback and requests. 49 | 50 | There is no new major (e.g. 5.x) release on the roadmap at present. 51 | 52 | ## Latest Release: 4.2.0 53 | 54 | * Add Swedish locale 55 | * Add Latvian locale 56 | * Add Turkish locale 57 | * Add Hebrew locale 58 | * Add password input type 59 | * Add textarea input type 60 | * Add date input type 61 | * Add time input type 62 | * Add number input type 63 | * Support DOM selectors for container argument 64 | * UMD support 65 | * Better support on mobile devices 66 | 67 | For a full list of releases and changes please see [the changelog](https://github.com/makeusabrew/bootbox/blob/master/CHANGELOG.md). 68 | 69 | ## License 70 | 71 | (The MIT License) 72 | 73 | Copyright (C) 2011-2014 by Nick Payne 74 | 75 | Permission is hereby granted, free of charge, to any person obtaining a copy 76 | of this software and associated documentation files (the "Software"), to deal 77 | in the Software without restriction, including without limitation the rights 78 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 79 | copies of the Software, and to permit persons to whom the Software is 80 | furnished to do so, subject to the following conditions: 81 | 82 | The above copyright notice and this permission notice shall be included in 83 | all copies or substantial portions of the Software. 84 | 85 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 86 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 87 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 88 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 89 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 90 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 91 | THE SOFTWARE 92 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Latest Release: 4.2.0 2 | 3 | * Add Swedish locale 4 | * Add Latvian locale 5 | * Add Turkish locale 6 | * Add Hebrew locale 7 | * Add password input type 8 | * Add textarea input type 9 | * Add date input type 10 | * Add time input type 11 | * Add number input type 12 | * Support DOM selectors for container argument 13 | * UMD support 14 | * Better support on mobile devices 15 | 16 | ### 4.1.0 17 | 18 | * Add support for placeholder attribute in prompts 19 | * Add select, email and checkbox types for prompts (thanks [@tarlepp](https://github.com/tarlepp)) 20 | * Add Norwegian locale 21 | * Allow setDefaults to take two key/val arguments 22 | * Add unique classes for main dialog methods 23 | * Create bower package 24 | 25 | ### 4.0.0 26 | 27 | * Bootstrap 3.0.0 compatibility 28 | * Complete rewrite (and new public API) 29 | * Use strict mode 30 | * Add close buttons to wrapper methods (GH-92) 31 | * Allow dialog titles to be specified (GH-51, GH-112) 32 | * Allow optional extra class on dialog wrapper (GH-116) 33 | * Fix ```backdrop: true``` not firing close handler (GH-77) 34 | * Replace various configuration methods with one ```setDefaults``` 35 | 36 | ### 3.3.0 37 | 38 | * Add Polish translation (GH-93) 39 | * Add Danish translation (GH-96) 40 | * Pass event object to custom callbacks (GH-103) 41 | * Add Chinese (Taiwan / China) translations (GH-106) 42 | * Make prompt input block-level (GH-111) 43 | * Add link: true option to prevent btn class from being applied (GH-114) 44 | * Prevent child elements triggering hidden callback (GH-115) 45 | * Replace Phing with Grunt 46 | * Replace Closure compiler with UglifyJS 47 | 48 | ### 3.2.0 49 | 50 | * ensure ```onEscape``` handlers return callback values properly (GH-91) 51 | * ensure clicking close button invokes onEscape handler if present 52 | 53 | ### 3.1.0 54 | 55 | * ensure ```confirm``` and ```prompt``` methods return callback values properly (GH-90) 56 | * address various jshint warnings (GH-79) 57 | * add ```setBtnClasses``` method for custom standard button classes (GH-87) 58 | 59 | ### 3.0.0 60 | 61 | * bump Bootstrap dependency to 2.2.2 62 | * bump jQuery dependency to 1.8.3 63 | * ensure callbacks are always invoked even if dialogs are dismissed with escape key (GH-49) 64 | * fix button positions with Bootstrap 2.2.2 (GH-58) 65 | * stop multiple dialogs crashing browsers (GH-60, GH-64) 66 | * ensure ```shown``` event is fired properly even when animation is disabled (GH-69) 67 | * use ```.on``` instead of ```.bind``` 68 | * commentify code a bit more 69 | 70 | ### 2.5.1 71 | 72 | **This was the last version of the library to support Bootstrap 2.0.x** 73 | 74 | * ensure bootbox object is explicitly added to window object for minfier visibility 75 | 76 | ### 2.5.0 77 | 78 | * add option to specify proper href attributes for buttons instead of callbacks (@StevePotter) 79 | * add option to override per-modal classes (@ciaranj) 80 | 81 | ### 2.4.2 82 | 83 | * revert ```backdrop``` default value to 'static' instead of ```true``` to prevent background clicks dismissing dialogs (GH-55) 84 | 85 | ### 2.4.1 86 | 87 | * fix ```backdrop``` when supplied as an argument to ```bootbox.dialog``` 88 | * fix incorrect README version 89 | 90 | ### 2.4.0 91 | 92 | * add ```bootbox.backdrop(bool)``` method (@gucki) 93 | * add default parameter option to ```bootbox.prompt``` (@pzgz) 94 | 95 | ### 2.3.3 96 | 97 | * add inline ```overflow: hidden``` CSS property (GH-46) 98 | * move license info to separate hosted file to reduce file size 99 | 100 | ### 2.3.2 101 | 102 | * Change button href attributes to ```javascript:;``` (@joshnesbitt) 103 | * Explicitly ```window.jQuery``` through to ```Bootbox``` object (@nuegon) 104 | 105 | 106 | ### 2.3.1 107 | 108 | * Ensure bootbox.prompt() gives focus to input, disable input autocomplete 109 | 110 | ### 2.3.0 111 | 112 | * Added bootbox.prompt() to mimic native prompt() method 113 | * Added Russian locale (#27) 114 | 115 | ### 2.2.0 116 | 117 | * Allowed button callbacks to explicitly return false to prevent dialog from closing (thanks @benoit-ponsero) 118 | * Added version number to header comments (#26) 119 | 120 | ### 2.1.2 121 | 122 | * Added close button to re-scoped click handler (thanks @SeanMcGee and @kentbrew) 123 | 124 | ### 2.1.1 125 | 126 | * Fixed incorrect button click handler selector (thanks FGRibreau) 127 | 128 | ### 2.1.0 129 | 130 | * Added support for Bootstrap's Glyphicons via the ```icon``` option 131 | * Added inline license information into bootbox.js and bootbox.min.js 132 | * Tidied up source a little 133 | 134 | ### 2.0.1 135 | 136 | * Removed dummy Google Closure Compiler method from minified library (thanks j0k3r!) 137 | 138 | ### 2.0.0 139 | 140 | * Updated Bootstrap dependency from 1.4 to 2.0 141 | * Class definitions now require ```btn-``` prefix as per Bootstrap 2.0 142 | * Added Brazilian locale 143 | * Added ```animate``` dialog option 144 | * Added ```bootbox.animate(bool)``` option to set default animation preference 145 | * Animated dialogs now rely on ```bootstrap-transitions.js``` as required by Bootstrap 2.0 146 | 147 | ### 1.1.2 148 | 149 | * Added licensing information to README 150 | 151 | #### 1.1.1 152 | * Updated german locale 153 | 154 | #### 1.1.0 155 | * Secondary option of two-button dialog no longer has 'danger' class 156 | * New bootbox.modal() method for generic non-dialog popups 157 | * Allow jQuery objects to be passed as main dialog argument 158 | -------------------------------------------------------------------------------- /tests/defaults.test.js: -------------------------------------------------------------------------------- 1 | describe("bootbox.setDefaults", function() { 2 | 3 | beforeEach(function() { 4 | this.find = function(selector) { 5 | return this.dialog.find(selector); 6 | }; 7 | }); 8 | 9 | describe("animate", function() { 10 | describe("when set to false", function() { 11 | beforeEach(function() { 12 | bootbox.setDefaults({ 13 | animate: false 14 | }); 15 | this.dialog = bootbox.dialog({ 16 | message: "test" 17 | }); 18 | }); 19 | 20 | it("does not add the fade class to the dialog", function() { 21 | expect(this.dialog.hasClass("fade")).to.be.false; 22 | }); 23 | 24 | it("applies the correct class to the body", function() { 25 | expect($("body").hasClass("modal-open")).to.be.true; 26 | }); 27 | 28 | describe("when clicking the close button", function() { 29 | beforeEach(function() { 30 | this.dialog.find(".close").trigger("click"); 31 | }); 32 | 33 | it("removes the modal-open class from the body", function() { 34 | expect($("body").hasClass("modal-open")).to.be.false; 35 | }); 36 | }); 37 | }); 38 | 39 | describe("when set to true", function() { 40 | beforeEach(function() { 41 | bootbox.setDefaults({ 42 | animate: true 43 | }); 44 | this.dialog = bootbox.dialog({ 45 | message: "test" 46 | }); 47 | }); 48 | 49 | it("adds the fade class to the dialog", function() { 50 | expect(this.dialog.hasClass("fade")).to.be.true; 51 | }); 52 | }); 53 | }); 54 | 55 | describe("className", function() { 56 | describe("when passed as a string", function() { 57 | beforeEach(function() { 58 | bootbox.setDefaults({ 59 | className: "my-class" 60 | }); 61 | 62 | this.dialog = bootbox.dialog({ 63 | message: "test" 64 | }); 65 | }); 66 | 67 | it("adds the extra class to the outer dialog", function() { 68 | expect(this.dialog.hasClass("bootbox")).to.be.true; 69 | expect(this.dialog.hasClass("my-class")).to.be.true; 70 | }); 71 | }); 72 | }); 73 | 74 | describe("size", function() { 75 | describe("when set to large", function() { 76 | beforeEach(function() { 77 | bootbox.setDefaults({ 78 | size: "large" 79 | }); 80 | 81 | this.dialog = bootbox.dialog({ 82 | message: "test" 83 | }); 84 | }); 85 | 86 | it("adds the large class to the innerDialog", function() { 87 | expect(this.dialog.children(":first").hasClass("modal-dialog")).to.be.true; 88 | expect(this.dialog.children(":first").hasClass("modal-lg")).to.be.true; 89 | }); 90 | }); 91 | describe("when set to small", function() { 92 | beforeEach(function() { 93 | bootbox.setDefaults({ 94 | size: "small" 95 | }); 96 | 97 | this.dialog = bootbox.dialog({ 98 | message: "test" 99 | }); 100 | }); 101 | 102 | it("adds the small class to the innerDialog", function() { 103 | expect(this.dialog.children(":first").hasClass("modal-dialog")).to.be.true; 104 | expect(this.dialog.children(":first").hasClass("modal-sm")).to.be.true; 105 | }); 106 | }); 107 | }); 108 | 109 | describe("backdrop", function() { 110 | describe("when set to false", function() { 111 | beforeEach(function() { 112 | bootbox.setDefaults({ 113 | backdrop: false 114 | }); 115 | 116 | this.dialog = bootbox.dialog({ 117 | message: "test" 118 | }); 119 | }); 120 | 121 | it("does not show a backdrop", function() { 122 | expect(this.dialog.next(".modal-backdrop").length).to.equal(0); 123 | }); 124 | }); 125 | }); 126 | 127 | describe("when passed two arguments", function() { 128 | beforeEach(function() { 129 | bootbox.setDefaults("className", "my-class"); 130 | this.dialog = bootbox.dialog({ 131 | message: "test" 132 | }); 133 | }); 134 | 135 | it("applies the arguments as a key/value pair", function() { 136 | expect(this.dialog.hasClass("bootbox")).to.be.true; 137 | expect(this.dialog.hasClass("my-class")).to.be.true; 138 | }); 139 | }); 140 | 141 | describe("container", function () { 142 | describe("when not explicitly set", function() { 143 | beforeEach(function() { 144 | this.dialog = bootbox.dialog({ 145 | message: "test" 146 | }); 147 | }); 148 | 149 | it("defaults to the body element", function() { 150 | expect(this.dialog.parent().is("body")).to.be.true; 151 | }); 152 | }); 153 | 154 | describe("when explicitly set to body", function() { 155 | beforeEach(function() { 156 | bootbox.setDefaults({ 157 | container: "body" 158 | }); 159 | 160 | this.dialog = bootbox.dialog({ 161 | message: "test" 162 | }); 163 | }); 164 | 165 | it("sets the correct parent element", function() { 166 | expect(this.dialog.parent().is("body")).to.be.true; 167 | }); 168 | }); 169 | 170 | describe("when set to another dom element", function() { 171 | 172 | beforeEach(function() { 173 | this.container = $("
"); 174 | bootbox.setDefaults({ 175 | container: this.container 176 | }); 177 | 178 | this.dialog = bootbox.dialog({ 179 | message: "test" 180 | }); 181 | }); 182 | 183 | it("sets the correct parent element", function() { 184 | expect(this.dialog.parent().is(this.container)).to.be.true; 185 | }); 186 | }); 187 | }); 188 | }); 189 | -------------------------------------------------------------------------------- /tests/confirm.test.coffee: -------------------------------------------------------------------------------- 1 | describe "bootbox.confirm", -> 2 | 3 | describe "basic usage tests", -> 4 | 5 | describe "with one argument", -> 6 | 7 | describe "where the argument is not an object", -> 8 | beforeEach -> 9 | @create = -> bootbox.confirm "Are you sure?" 10 | 11 | it "throws an error", -> 12 | expect(@create).to.throw /confirm requires a callback/ 13 | 14 | describe "where the argument is an object", -> 15 | beforeEach -> 16 | @options = {} 17 | @create = => @dialog = bootbox.confirm @options 18 | 19 | describe "with a message property", -> 20 | beforeEach -> 21 | @options.message = "Are you sure?" 22 | 23 | it "throws an error requiring a callback", -> 24 | expect(@create).to.throw /confirm requires a callback/ 25 | 26 | describe "with a callback property", -> 27 | describe "where the callback is not a function", -> 28 | beforeEach -> 29 | @options.callback = "Are you sure?" 30 | 31 | it "throws an error requiring a callback", -> 32 | expect(@create).to.throw /confirm requires a callback/ 33 | 34 | describe "where the callback is a function", -> 35 | beforeEach -> 36 | @options.callback = -> true 37 | 38 | it "throws an error requiring a message", -> 39 | expect(@create).to.throw /Please specify a message/ 40 | 41 | describe "with a message and a callback", -> 42 | beforeEach -> 43 | @options = 44 | callback: -> true 45 | message: "Are you sure?" 46 | 47 | it "does not throw an error", -> 48 | expect(@create).not.to.throw Error 49 | 50 | it "creates a dialog object", -> 51 | expect(@dialog).to.be.an "object" 52 | 53 | it "adds the correct button labels", -> 54 | expect(@dialog.find(".btn:first").text()).to.equal "Cancel" 55 | expect(@dialog.find(".btn:last").text()).to.equal "OK" 56 | 57 | it "adds the correct button classes", -> 58 | expect(@dialog.find(".btn:first").hasClass("btn-default")).to.be.true 59 | expect(@dialog.find(".btn:last").hasClass("btn-primary")).to.be.true 60 | 61 | describe "with two arguments", -> 62 | describe "where the second argument is not a function", -> 63 | beforeEach -> 64 | @create = => 65 | @dialog = bootbox.confirm "Are you sure?", "callback here" 66 | 67 | it "throws an error requiring a callback", -> 68 | expect(@create).to.throw /confirm requires a callback/ 69 | 70 | describe "where the second argument is a function", -> 71 | beforeEach -> 72 | @create = => 73 | @dialog = bootbox.confirm "Are you sure?", -> true 74 | 75 | it "does not throw an error", -> 76 | expect(@create).not.to.throw Error 77 | 78 | it "creates a dialog object", -> 79 | expect(@dialog).to.be.an "object" 80 | 81 | it "applies the bootbox-confirm class to the dialog", -> 82 | expect(@dialog.hasClass("bootbox-confirm")).to.be.true 83 | 84 | it "adds the correct button labels", -> 85 | expect(@dialog.find(".btn:first").text()).to.equal "Cancel" 86 | expect(@dialog.find(".btn:last").text()).to.equal "OK" 87 | 88 | it "adds the correct button classes", -> 89 | expect(@dialog.find(".btn:first").hasClass("btn-default")).to.be.true 90 | expect(@dialog.find(".btn:last").hasClass("btn-primary")).to.be.true 91 | 92 | it "shows the dialog", -> 93 | expect(@dialog.is(":visible")).to.be.true 94 | 95 | describe "configuration options tests", -> 96 | beforeEach -> 97 | @options = 98 | message: "Are you sure?" 99 | callback: -> true 100 | 101 | @create = => 102 | @dialog = bootbox.confirm @options 103 | 104 | describe "with a custom cancel button", -> 105 | beforeEach -> 106 | @options.buttons = 107 | cancel: 108 | label: "Custom cancel" 109 | className: "btn-danger" 110 | 111 | @create() 112 | 113 | @button = @dialog.find(".btn:first") 114 | 115 | it "adds the correct cancel button", -> 116 | expect(@button.text()).to.equal "Custom cancel" 117 | expect(@button.hasClass("btn-danger")).to.be.true 118 | 119 | describe "with a custom confirm button", -> 120 | beforeEach -> 121 | @options.buttons = 122 | confirm: 123 | label: "Custom confirm" 124 | className: "btn-warning" 125 | 126 | @create() 127 | 128 | @button = @dialog.find(".btn:last") 129 | 130 | it "adds the correct confirm button", -> 131 | expect(@button.text()).to.equal "Custom confirm" 132 | expect(@button.hasClass("btn-warning")).to.be.true 133 | 134 | describe "with an unrecognised button key", -> 135 | beforeEach -> 136 | @options.buttons = 137 | "Bad key": 138 | label: "Custom confirm" 139 | className: "btn-warning" 140 | 141 | it "throws an error", -> 142 | expect(@create).to.throw /key is not allowed/ 143 | 144 | describe "callback tests", -> 145 | describe "with a simple callback", -> 146 | beforeEach -> 147 | @callback = sinon.spy() 148 | 149 | @dialog = bootbox.confirm 150 | message: "Are you sure?" 151 | callback: @callback 152 | 153 | @hidden = sinon.spy @dialog, "modal" 154 | 155 | describe "when dismissing the dialog by clicking OK", -> 156 | beforeEach -> 157 | @dialog.find(".btn-primary").trigger "click" 158 | 159 | it "should invoke the callback", -> 160 | expect(@callback).to.have.been.called 161 | 162 | it "with the correct value", -> 163 | expect(@callback).to.have.been.calledWithExactly true 164 | 165 | it "should hide the modal", -> 166 | expect(@hidden).to.have.been.calledWithExactly "hide" 167 | 168 | describe "when dismissing the dialog by clicking Cancel", -> 169 | beforeEach -> 170 | @dialog.find(".btn-default").trigger "click" 171 | 172 | it "should invoke the callback", -> 173 | expect(@callback).to.have.been.called 174 | 175 | it "with the correct value", -> 176 | expect(@callback).to.have.been.calledWithExactly false 177 | 178 | it "should hide the modal", -> 179 | expect(@hidden).to.have.been.calledWithExactly "hide" 180 | 181 | describe "when triggering the escape event", -> 182 | beforeEach -> 183 | @dialog.trigger "escape.close.bb" 184 | 185 | it "should invoke the callback", -> 186 | expect(@callback).to.have.been.called 187 | 188 | it "with the correct value", -> 189 | expect(@callback).to.have.been.calledWithExactly false 190 | 191 | it "should hide the modal", -> 192 | expect(@hidden).to.have.been.calledWithExactly "hide" 193 | 194 | describe "with a callback which returns false", -> 195 | beforeEach -> 196 | @callback = sinon.stub() 197 | @callback.returns false 198 | 199 | @dialog = bootbox.confirm 200 | message: "Are you sure?" 201 | callback: @callback 202 | 203 | @hidden = sinon.spy @dialog, "modal" 204 | 205 | describe "when dismissing the dialog by clicking OK", -> 206 | beforeEach -> 207 | @dialog.find(".btn-primary").trigger "click" 208 | 209 | it "should invoke the callback", -> 210 | expect(@callback).to.have.been.called 211 | 212 | it "with the correct value", -> 213 | expect(@callback).to.have.been.calledWithExactly true 214 | 215 | it "should not hide the modal", -> 216 | expect(@hidden).not.to.have.been.called 217 | 218 | describe "when dismissing the dialog by clicking Cancel", -> 219 | beforeEach -> 220 | @dialog.find(".btn-default").trigger "click" 221 | 222 | it "should invoke the callback", -> 223 | expect(@callback).to.have.been.called 224 | 225 | it "with the correct value", -> 226 | expect(@callback).to.have.been.calledWithExactly false 227 | 228 | it "should not hide the modal", -> 229 | expect(@hidden).not.to.have.been.called 230 | 231 | describe "when triggering the escape event", -> 232 | beforeEach -> 233 | @dialog.trigger "escape.close.bb" 234 | 235 | it "should invoke the callback", -> 236 | expect(@callback).to.have.been.called 237 | 238 | it "with the correct value", -> 239 | expect(@callback).to.have.been.calledWithExactly false 240 | 241 | it "should not hide the modal", -> 242 | expect(@hidden).not.to.have.been.called 243 | -------------------------------------------------------------------------------- /tests/locales.test.coffee: -------------------------------------------------------------------------------- 1 | describe "bootbox locales", -> 2 | beforeEach -> 3 | 4 | @setLocale = (locale) -> 5 | bootbox.setDefaults locale: locale 6 | 7 | d1 = bootbox.alert "foo" 8 | d2 = bootbox.confirm "foo", -> true 9 | 10 | @labels = 11 | ok: d1.find(".btn:first").text() 12 | cancel: d2.find(".btn:first").text() 13 | confirm: d2.find(".btn:last").text() 14 | 15 | describe "Invalid locale", -> 16 | beforeEach -> 17 | @setLocale "xx" 18 | 19 | it "shows the default OK translation", -> 20 | expect(@labels.ok).to.equal "OK" 21 | 22 | it "shows the default CANCEL translation", -> 23 | expect(@labels.cancel).to.equal "Cancel" 24 | 25 | it "shows the default CONFIRM translation", -> 26 | expect(@labels.confirm).to.equal "OK" 27 | 28 | describe "English", -> 29 | beforeEach -> 30 | @setLocale "en" 31 | 32 | it "shows the correct OK translation", -> 33 | expect(@labels.ok).to.equal "OK" 34 | 35 | it "shows the correct CANCEL translation", -> 36 | expect(@labels.cancel).to.equal "Cancel" 37 | 38 | it "shows the correct CONFIRM translation", -> 39 | expect(@labels.confirm).to.equal "OK" 40 | 41 | describe "French", -> 42 | beforeEach -> 43 | @setLocale "fr" 44 | 45 | it "shows the correct OK translation", -> 46 | expect(@labels.ok).to.equal "OK" 47 | 48 | it "shows the correct CANCEL translation", -> 49 | expect(@labels.cancel).to.equal "Annuler" 50 | 51 | it "shows the correct CONFIRM translation", -> 52 | expect(@labels.confirm).to.equal "D'accord" 53 | 54 | describe "German", -> 55 | beforeEach -> 56 | @setLocale "de" 57 | 58 | it "shows the correct OK translation", -> 59 | expect(@labels.ok).to.equal "OK" 60 | 61 | it "shows the correct CANCEL translation", -> 62 | expect(@labels.cancel).to.equal "Abbrechen" 63 | 64 | it "shows the correct CONFIRM translation", -> 65 | expect(@labels.confirm).to.equal "Akzeptieren" 66 | 67 | describe "Spanish", -> 68 | beforeEach -> 69 | @setLocale "es" 70 | 71 | it "shows the correct OK translation", -> 72 | expect(@labels.ok).to.equal "OK" 73 | 74 | it "shows the correct CANCEL translation", -> 75 | expect(@labels.cancel).to.equal "Cancelar" 76 | 77 | it "shows the correct CONFIRM translation", -> 78 | expect(@labels.confirm).to.equal "Aceptar" 79 | 80 | describe "Portuguese", -> 81 | beforeEach -> 82 | @setLocale "br" 83 | 84 | it "shows the correct OK translation", -> 85 | expect(@labels.ok).to.equal "OK" 86 | 87 | it "shows the correct CANCEL translation", -> 88 | expect(@labels.cancel).to.equal "Cancelar" 89 | 90 | it "shows the correct CONFIRM translation", -> 91 | expect(@labels.confirm).to.equal "Sim" 92 | 93 | describe "Dutch", -> 94 | beforeEach -> 95 | @setLocale "nl" 96 | 97 | it "shows the correct OK translation", -> 98 | expect(@labels.ok).to.equal "OK" 99 | 100 | it "shows the correct CANCEL translation", -> 101 | expect(@labels.cancel).to.equal "Annuleren" 102 | 103 | it "shows the correct CONFIRM translation", -> 104 | expect(@labels.confirm).to.equal "Accepteren" 105 | 106 | describe "Russian", -> 107 | beforeEach -> 108 | @setLocale "ru" 109 | 110 | it "shows the correct OK translation", -> 111 | expect(@labels.ok).to.equal "OK" 112 | 113 | it "shows the correct CANCEL translation", -> 114 | expect(@labels.cancel).to.equal "Отмена" 115 | 116 | it "shows the correct CONFIRM translation", -> 117 | expect(@labels.confirm).to.equal "Применить" 118 | 119 | describe "Indonesian", -> 120 | beforeEach -> 121 | @setLocale "id" 122 | 123 | it "shows the correct OK translation", -> 124 | expect(@labels.ok).to.equal "OK" 125 | 126 | it "shows the correct CANCEL translation", -> 127 | expect(@labels.cancel).to.equal "Batal" 128 | 129 | it "shows the correct CONFIRM translation", -> 130 | expect(@labels.confirm).to.equal "OK" 131 | 132 | describe "Italian", -> 133 | beforeEach -> 134 | @setLocale "it" 135 | 136 | it "shows the correct OK translation", -> 137 | expect(@labels.ok).to.equal "OK" 138 | 139 | it "shows the correct CANCEL translation", -> 140 | expect(@labels.cancel).to.equal "Annulla" 141 | 142 | it "shows the correct CONFIRM translation", -> 143 | expect(@labels.confirm).to.equal "Conferma" 144 | 145 | describe "Polish", -> 146 | beforeEach -> 147 | @setLocale "pl" 148 | 149 | it "shows the correct OK translation", -> 150 | expect(@labels.ok).to.equal "OK" 151 | 152 | it "shows the correct CANCEL translation", -> 153 | expect(@labels.cancel).to.equal "Anuluj" 154 | 155 | it "shows the correct CONFIRM translation", -> 156 | expect(@labels.confirm).to.equal "Potwierdź" 157 | 158 | describe "Danish", -> 159 | beforeEach -> 160 | @setLocale "da" 161 | 162 | it "shows the correct OK translation", -> 163 | expect(@labels.ok).to.equal "OK" 164 | 165 | it "shows the correct CANCEL translation", -> 166 | expect(@labels.cancel).to.equal "Annuller" 167 | 168 | it "shows the correct CONFIRM translation", -> 169 | expect(@labels.confirm).to.equal "Accepter" 170 | 171 | describe "Chinese", -> 172 | describe "Taiwan", -> 173 | beforeEach -> 174 | @setLocale "zh_TW" 175 | 176 | it "shows the correct OK translation", -> 177 | expect(@labels.ok).to.equal "OK" 178 | 179 | it "shows the correct CANCEL translation", -> 180 | expect(@labels.cancel).to.equal "取消" 181 | 182 | it "shows the correct CONFIRM translation", -> 183 | expect(@labels.confirm).to.equal "確認" 184 | 185 | describe "China", -> 186 | beforeEach -> 187 | @setLocale "zh_CN" 188 | 189 | it "shows the correct OK translation", -> 190 | expect(@labels.ok).to.equal "OK" 191 | 192 | it "shows the correct CANCEL translation", -> 193 | expect(@labels.cancel).to.equal "取消" 194 | 195 | it "shows the correct CONFIRM translation", -> 196 | expect(@labels.confirm).to.equal "确认" 197 | 198 | describe "Norwegian", -> 199 | beforeEach -> 200 | @setLocale "no" 201 | 202 | it "shows the correct OK translation", -> 203 | expect(@labels.ok).to.equal "OK" 204 | 205 | it "shows the correct CANCEL translation", -> 206 | expect(@labels.cancel).to.equal "Avbryt" 207 | 208 | it "shows the correct CONFIRM translation", -> 209 | expect(@labels.confirm).to.equal "OK" 210 | 211 | describe "Swedish", -> 212 | beforeEach -> 213 | @setLocale "sv" 214 | 215 | it "shows the correct OK translation", -> 216 | expect(@labels.ok).to.equal "OK" 217 | 218 | it "shows the correct CANCEL translation", -> 219 | expect(@labels.cancel).to.equal "Avbryt" 220 | 221 | it "shows the correct CONFIRM translation", -> 222 | expect(@labels.confirm).to.equal "OK" 223 | 224 | describe "Latvian", -> 225 | beforeEach -> 226 | @setLocale "lv" 227 | 228 | it "shows the correct OK translation", -> 229 | expect(@labels.ok).to.equal "Labi" 230 | 231 | it "shows the correct CANCEL translation", -> 232 | expect(@labels.cancel).to.equal "Atcelt" 233 | 234 | it "shows the correct CONFIRM translation", -> 235 | expect(@labels.confirm).to.equal "Apstiprināt" 236 | 237 | describe "Lithuanian", -> 238 | beforeEach -> 239 | @setLocale "lt" 240 | 241 | it "shows the correct OK translation", -> 242 | expect(@labels.ok).to.equal "Gerai" 243 | 244 | it "shows the correct CANCEL translation", -> 245 | expect(@labels.cancel).to.equal "Atšaukti" 246 | 247 | it "shows the correct CONFIRM translation", -> 248 | expect(@labels.confirm).to.equal "Patvirtinti" 249 | 250 | describe "Turkish", -> 251 | beforeEach -> 252 | @setLocale "tr" 253 | 254 | it "shows the correct OK translation", -> 255 | expect(@labels.ok).to.equal "Tamam" 256 | 257 | it "shows the correct CANCEL translation", -> 258 | expect(@labels.cancel).to.equal "İptal" 259 | 260 | it "shows the correct CONFIRM translation", -> 261 | expect(@labels.confirm).to.equal "Onayla" 262 | 263 | describe "Hebrew", -> 264 | beforeEach -> 265 | @setLocale "he" 266 | 267 | it "shows the correct OK translation", -> 268 | expect(@labels.ok).to.equal "אישור" 269 | 270 | it "shows the correct CANCEL translation", -> 271 | expect(@labels.cancel).to.equal "ביטול" 272 | 273 | it "shows the correct CONFIRM translation", -> 274 | expect(@labels.confirm).to.equal "אישור" 275 | 276 | describe "Greek", -> 277 | beforeEach -> 278 | @setLocale "el" 279 | 280 | it "shows the correct OK translation", -> 281 | expect(@labels.ok).to.equal "Εντάξει" 282 | 283 | it "shows the correct CANCEL translation", -> 284 | expect(@labels.cancel).to.equal "Ακύρωση" 285 | 286 | it "shows the correct CONFIRM translation", -> 287 | expect(@labels.confirm).to.equal "Επιβεβαίωση" 288 | 289 | describe "Japanese", -> 290 | beforeEach -> 291 | @setLocale "ja" 292 | 293 | it "shows the correct OK translation", -> 294 | expect(@labels.ok).to.equal "OK" 295 | 296 | it "shows the correct CANCEL translation", -> 297 | expect(@labels.cancel).to.equal "キャンセル" 298 | 299 | it "shows the correct CONFIRM translation", -> 300 | expect(@labels.confirm).to.equal "確認" 301 | -------------------------------------------------------------------------------- /tests/alert.test.js: -------------------------------------------------------------------------------- 1 | describe("bootbox.alert", function() { 2 | 3 | "use strict"; 4 | 5 | var self; 6 | 7 | beforeEach(function() { 8 | 9 | self = this; 10 | 11 | this.text = function(selector) { 12 | return this.find(selector).text(); 13 | }; 14 | 15 | this.find = function(selector) { 16 | return this.dialog.find(selector); 17 | }; 18 | }); 19 | 20 | describe("basic usage tests", function() { 21 | 22 | describe("with no arguments", function() { 23 | beforeEach(function() { 24 | this.create = function() { 25 | bootbox.alert(); 26 | }; 27 | }); 28 | 29 | it("throws an error regarding argument length", function() { 30 | expect(this.create).to.throw(/argument length/); 31 | }); 32 | }); 33 | 34 | describe("with one argument", function() { 35 | 36 | describe("where the argument is a string", function() { 37 | beforeEach(function() { 38 | this.dialog = bootbox.alert("Hello world!"); 39 | }); 40 | 41 | it("applies the bootbox-alert class to the dialog", function() { 42 | expect(this.dialog.hasClass("bootbox-alert")).to.be.true; 43 | }); 44 | 45 | it("shows the expected body copy", function() { 46 | expect(this.text(".bootbox-body")).to.equal("Hello world!"); 47 | }); 48 | 49 | it("shows an OK button", function() { 50 | expect(this.text(".modal-footer button:first")).to.equal("OK"); 51 | }); 52 | 53 | it("applies the primary class to the button", function() { 54 | expect(this.find(".modal-footer button:first").hasClass("btn-primary")).to.be.true; 55 | }); 56 | 57 | it("shows a close button inside the body", function() { 58 | expect(this.text(".modal-body button")).to.equal("×"); 59 | }); 60 | 61 | it("applies the close class to the close button", function() { 62 | expect(this.find(".modal-body button").hasClass("close")).to.be.true; 63 | }); 64 | 65 | it("applies the correct data-dismiss attribute to the close button", function() { 66 | expect(this.find("button.close").attr("data-dismiss")).to.equal("modal"); 67 | }); 68 | 69 | it("applies the correct aria-hidden attribute to the close button", function() { 70 | expect(this.find("button.close").attr("aria-hidden")).to.equal("true"); 71 | }); 72 | 73 | it("applies the correct class to the body", function() { 74 | expect($("body").hasClass("modal-open")).to.be.true; 75 | }); 76 | }); 77 | }); 78 | 79 | describe("with two arguments", function() { 80 | describe("where the second argument is not a function", function() { 81 | beforeEach(function() { 82 | this.create = function() { 83 | bootbox.alert("Hello world!", "not a callback"); 84 | }; 85 | }); 86 | 87 | it("throws an error requiring a callback", function() { 88 | expect(this.create).to.throw(/alert requires callback property to be a function when provided/); 89 | }); 90 | }); 91 | 92 | describe("where the second argument is a function", function() { 93 | beforeEach(function() { 94 | this.create = function() { 95 | self.dialog = bootbox.alert("Hello world!", function() {}); 96 | }; 97 | }); 98 | 99 | it("does not throw an error", function() { 100 | expect(this.create).not.to.throw(Error); 101 | }); 102 | }); 103 | }); 104 | 105 | describe("with three arguments", function() { 106 | beforeEach(function() { 107 | this.create = function() { 108 | bootbox.alert(1, 2, 3); 109 | }; 110 | }); 111 | 112 | it("throws an error regarding argument length", function() { 113 | expect(this.create).to.throw(/argument length/); 114 | }); 115 | }); 116 | 117 | }); 118 | 119 | describe("configuration options tests", function() { 120 | beforeEach(function() { 121 | this.options = { 122 | message: "Hello world", 123 | callback: function() {} 124 | }; 125 | 126 | this.create = function() { 127 | self.dialog = bootbox.alert(self.options); 128 | }; 129 | }); 130 | 131 | describe("with a custom ok button", function() { 132 | beforeEach(function() { 133 | this.options.buttons = { 134 | ok: { 135 | label: "Custom OK", 136 | className: "btn-danger" 137 | } 138 | }; 139 | 140 | this.create(); 141 | 142 | this.button = this.dialog.find(".btn:first"); 143 | }); 144 | 145 | it("adds the correct ok button", function() { 146 | expect(this.button.text()).to.equal("Custom OK"); 147 | expect(this.button.hasClass("btn-danger")).to.be.true; 148 | }); 149 | }); 150 | 151 | describe("with an unrecognised button key", function() { 152 | beforeEach(function() { 153 | this.options.buttons = { 154 | "Another key": { 155 | label: "Custom OK", 156 | className: "btn-danger" 157 | } 158 | }; 159 | }); 160 | 161 | it("throws an error", function() { 162 | expect(this.create).to.throw("button key Another key is not allowed (options are ok)"); 163 | }); 164 | }); 165 | 166 | describe("with a custom title", function() { 167 | beforeEach(function() { 168 | this.options.title = "Hello?"; 169 | this.create(); 170 | }); 171 | 172 | it("shows the correct title", function() { 173 | expect(this.text("h4")).to.equal("Hello?"); 174 | }); 175 | }); 176 | }); 177 | 178 | describe("callback tests", function() { 179 | 180 | describe("with no callback", function() { 181 | beforeEach(function() { 182 | this.dialog = bootbox.alert({ 183 | message:"Hello!" 184 | }); 185 | 186 | this.hidden = sinon.spy(this.dialog, "modal"); 187 | }); 188 | 189 | describe("when dismissing the dialog by clicking OK", function() { 190 | beforeEach(function() { 191 | this.dialog.find(".btn-primary").trigger("click"); 192 | }); 193 | 194 | it("should hide the modal", function() { 195 | expect(this.hidden).to.have.been.calledWithExactly("hide"); 196 | }); 197 | }); 198 | 199 | describe("when clicking the close button", function() { 200 | beforeEach(function() { 201 | this.dialog.find(".close").trigger("click"); 202 | }); 203 | 204 | it("should hide the modal", function() { 205 | expect(this.hidden).to.have.been.calledWithExactly("hide"); 206 | }); 207 | }); 208 | 209 | describe("when triggering the escape event", function() { 210 | beforeEach(function() { 211 | this.dialog.trigger("escape.close.bb"); 212 | }); 213 | 214 | it("should hide the modal", function() { 215 | expect(this.hidden).to.have.been.calledWithExactly("hide"); 216 | }); 217 | }); 218 | }); 219 | 220 | describe("with a simple callback", function() { 221 | beforeEach(function() { 222 | this.callback = sinon.spy(); 223 | 224 | this.dialog = bootbox.alert({ 225 | message:"Hello!", 226 | callback: this.callback 227 | }); 228 | 229 | this.hidden = sinon.spy(this.dialog, "modal"); 230 | }); 231 | 232 | describe("when dismissing the dialog by clicking OK", function() { 233 | beforeEach(function() { 234 | this.dialog.find(".btn-primary").trigger("click"); 235 | }); 236 | 237 | it("should invoke the callback", function() { 238 | expect(this.callback).to.have.been.called; 239 | }); 240 | 241 | it("should hide the modal", function() { 242 | expect(this.hidden).to.have.been.calledWithExactly("hide"); 243 | }); 244 | }); 245 | 246 | describe("when clicking the close button", function() { 247 | beforeEach(function() { 248 | this.dialog.find(".close").trigger("click"); 249 | }); 250 | 251 | it("should invoke the callback", function() { 252 | expect(this.callback).to.have.been.called; 253 | }); 254 | 255 | it("should hide the modal", function() { 256 | expect(this.hidden).to.have.been.calledWithExactly("hide"); 257 | }); 258 | }); 259 | 260 | describe("when triggering the escape event", function() { 261 | beforeEach(function() { 262 | this.dialog.trigger("escape.close.bb"); 263 | }); 264 | 265 | it("should invoke the callback", function() { 266 | expect(this.callback).to.have.been.called; 267 | }); 268 | 269 | it("should hide the modal", function() { 270 | expect(this.hidden).to.have.been.calledWithExactly("hide"); 271 | }); 272 | }); 273 | }); 274 | 275 | describe("with a callback which returns false", function() { 276 | beforeEach(function() { 277 | this.callback = sinon.stub(); 278 | this.callback.returns(false); 279 | 280 | this.dialog = bootbox.alert({ 281 | message:"Hello!", 282 | callback: this.callback 283 | }); 284 | 285 | this.hidden = sinon.spy(this.dialog, "modal"); 286 | }); 287 | 288 | describe("when dismissing the dialog by clicking OK", function() { 289 | beforeEach(function() { 290 | this.dialog.find(".btn-primary").trigger("click"); 291 | }); 292 | 293 | it("should invoke the callback", function() { 294 | expect(this.callback).to.have.been.called; 295 | }); 296 | 297 | it("should not hide the modal", function() { 298 | expect(this.hidden).not.to.have.been.called; 299 | }); 300 | }); 301 | 302 | describe("when clicking the close button", function() { 303 | beforeEach(function() { 304 | this.dialog.find(".close").trigger("click"); 305 | }); 306 | 307 | it("should invoke the callback", function() { 308 | expect(this.callback).to.have.been.called; 309 | }); 310 | 311 | it("should not hide the modal", function() { 312 | expect(this.hidden).not.to.have.been.called; 313 | }); 314 | }); 315 | 316 | describe("when triggering the escape event", function() { 317 | beforeEach(function() { 318 | this.dialog.trigger("escape.close.bb"); 319 | }); 320 | 321 | it("should invoke the callback", function() { 322 | expect(this.callback).to.have.been.called; 323 | }); 324 | 325 | it("should not hide the modal", function() { 326 | expect(this.hidden).not.to.have.been.called; 327 | }); 328 | }); 329 | }); 330 | }); 331 | }); 332 | -------------------------------------------------------------------------------- /tests/dialog.test.coffee: -------------------------------------------------------------------------------- 1 | describe "bootbox.dialog", -> 2 | beforeEach -> 3 | 4 | # need to take care with these helpers; don't want too much 5 | # cleverness in the tests which runs the risk of making them 6 | # harder to read. Could we look at custom expectations instead? 7 | @find = (s) -> @dialog.find s 8 | @exists = (s) -> @find(s).length isnt 0 9 | @class = (s, c) -> @find(s).hasClass(c) 10 | @invoke = (s, m) -> @find(s)[m]() 11 | @text = (s) -> @invoke s, "text" 12 | @html = (s) -> @invoke s, "html" 13 | 14 | describe "invalid usage tests", -> 15 | 16 | describe "with no arguments", -> 17 | 18 | beforeEach -> 19 | @create = -> bootbox.dialog() 20 | 21 | it "throws an error", -> 22 | expect(@create).to.throw /supply an object/ 23 | 24 | describe "with one argument", -> 25 | 26 | describe "where the argument is not an object", -> 27 | beforeEach -> 28 | @create = -> bootbox.dialog "test" 29 | 30 | it "throws an error", -> 31 | expect(@create).to.throw /supply an object/ 32 | 33 | describe "where the argument has no message property", -> 34 | beforeEach -> 35 | @create = -> 36 | bootbox.dialog 37 | invalid: "options" 38 | 39 | it "throws an error", -> 40 | expect(@create).to.throw /specify a message/ 41 | 42 | describe "where the argument has a button with an invalid value", -> 43 | beforeEach -> 44 | @create = -> 45 | bootbox.dialog 46 | message: "test" 47 | buttons: 48 | ok: "foo" 49 | 50 | it "throws an error", -> 51 | expect(@create).to.throw /button with key ok must be an object/ 52 | 53 | describe "when creating a minimal dialog", -> 54 | beforeEach -> 55 | @dialog = bootbox.dialog 56 | message: "test" 57 | 58 | it "adds the bootbox class to the dialog", -> 59 | expect(@dialog.hasClass("bootbox")).to.be.true 60 | 61 | it "adds the bootstrap modal class to the dialog", -> 62 | expect(@dialog.hasClass("modal")).to.be.true 63 | 64 | it "adds the fade class to the dialog", -> 65 | expect(@dialog.hasClass("fade")).to.be.true 66 | 67 | it "shows the expected message", -> 68 | expect(@text(".bootbox-body")).to.equal "test" 69 | 70 | it "does not have a header", -> 71 | expect(@exists(".modal-header")).not.to.be.ok 72 | 73 | it "has a close button inside the body", -> 74 | expect(@exists(".modal-body .close")).to.be.ok 75 | 76 | it "does not have a footer", -> 77 | expect(@exists(".modal-footer")).not.to.be.ok 78 | 79 | it "has a backdrop", -> 80 | expect(@dialog.next(".modal-backdrop").length).to.equal 1 81 | 82 | describe "when creating a dialog with a button", -> 83 | beforeEach -> 84 | @create = (button = {}) => 85 | @dialog = bootbox.dialog 86 | message: "test" 87 | buttons: 88 | one: button 89 | 90 | describe "when the button has no callback", -> 91 | beforeEach -> 92 | @create 93 | label: "My Label" 94 | 95 | @hidden = sinon.spy @dialog, "modal" 96 | 97 | it "shows a footer", -> 98 | expect(@exists(".modal-footer")).to.be.ok 99 | 100 | it "shows one button", -> 101 | expect(@find(".btn").length).to.equal 1 102 | 103 | it "shows the correct button text", -> 104 | expect(@text(".btn")).to.equal "My Label" 105 | 106 | it "applies the correct button class", -> 107 | expect(@class(".btn", "btn-primary")).to.be.true 108 | 109 | describe "when triggering the escape event", -> 110 | beforeEach -> 111 | @dialog.trigger "escape.close.bb" 112 | 113 | it "should not hide the modal", -> 114 | expect(@hidden).not.to.have.been.called 115 | 116 | describe "when clicking the close button", -> 117 | beforeEach -> 118 | @dialog.find(".close").trigger "click" 119 | 120 | it "should hide the modal", -> 121 | expect(@hidden).to.have.been.calledWithExactly "hide" 122 | 123 | describe "when the button has a label and callback", -> 124 | beforeEach -> 125 | @callback = sinon.spy() 126 | 127 | @create 128 | label: "Another Label" 129 | callback: @callback 130 | 131 | @hidden = sinon.spy @dialog, "modal" 132 | 133 | it "shows a footer", -> 134 | expect(@exists(".modal-footer")).to.be.ok 135 | 136 | it "shows the correct button text", -> 137 | expect(@text(".btn")).to.equal "Another Label" 138 | 139 | describe "when dismissing the dialog by clicking OK", -> 140 | beforeEach -> 141 | @dialog.find(".btn-primary").trigger "click" 142 | 143 | it "should invoke the callback", -> 144 | expect(@callback).to.have.been.called 145 | 146 | it "should hide the modal", -> 147 | expect(@hidden).to.have.been.calledWithExactly "hide" 148 | 149 | describe "when triggering the escape event", -> 150 | beforeEach -> 151 | @dialog.trigger "escape.close.bb" 152 | 153 | it "should not invoke the callback", -> 154 | expect(@callback).not.to.have.been.called 155 | 156 | it "should not hide the modal", -> 157 | expect(@hidden).not.to.have.been.called 158 | 159 | describe "when clicking the close button", -> 160 | beforeEach -> 161 | @dialog.find(".close").trigger "click" 162 | 163 | it "should not invoke the callback", -> 164 | expect(@callback).not.to.have.been.called 165 | 166 | it "should hide the modal", -> 167 | expect(@hidden).to.have.been.called 168 | 169 | describe "when the button has a custom class", -> 170 | beforeEach -> 171 | @create 172 | label: "Test Label" 173 | className: "btn-custom" 174 | 175 | it "shows the correct button text", -> 176 | expect(@text(".btn")).to.equal "Test Label" 177 | 178 | it "adds the custom class to the button", -> 179 | expect(@class(".btn", "btn-custom")).to.be.true 180 | 181 | describe "when the button has no explicit label", -> 182 | beforeEach -> 183 | @create = (buttons) -> 184 | @dialog = bootbox.dialog 185 | message: "test" 186 | buttons: buttons 187 | 188 | describe "when its value is an object", -> 189 | beforeEach -> 190 | @create 191 | "Short form": 192 | className: "btn-custom" 193 | callback: -> true 194 | 195 | it "uses the key name as the button text", -> 196 | expect(@text(".btn")).to.equal "Short form" 197 | 198 | it "adds the custom class to the button", -> 199 | expect(@class(".btn", "btn-custom")).to.be.true 200 | 201 | describe "when its value is a function", -> 202 | beforeEach -> 203 | @callback = sinon.spy() 204 | @create 205 | my_label: @callback 206 | 207 | it "uses the key name as the button text", -> 208 | expect(@text(".btn")).to.equal "my_label" 209 | 210 | describe "when dismissing the dialog by clicking the button", -> 211 | beforeEach -> 212 | @dialog.find(".btn-primary").trigger "click" 213 | 214 | it "should invoke the callback", -> 215 | expect(@callback).to.have.been.called 216 | 217 | describe "when its value is not an object or function", -> 218 | beforeEach -> 219 | @badCreate = => 220 | @create 221 | "Short form": "hello world" 222 | 223 | it "throws an error", -> 224 | expect(@badCreate).to.throw /button with key Short form must be an object/ 225 | 226 | describe "when creating a dialog with a title", -> 227 | beforeEach -> 228 | @dialog = bootbox.dialog 229 | title: "My Title" 230 | message: "test" 231 | 232 | it "has a header", -> 233 | expect(@exists(".modal-header")).to.be.ok 234 | 235 | it "shows the correct title text", -> 236 | expect(@text(".modal-title")).to.equal "My Title" 237 | 238 | it "has a close button inside the header", -> 239 | expect(@exists(".modal-header .close")).to.be.ok 240 | 241 | describe "when creating a dialog with no backdrop", -> 242 | beforeEach -> 243 | @dialog = bootbox.dialog 244 | message: "No backdrop in sight" 245 | backdrop: false 246 | 247 | it "does not have a backdrop", -> 248 | expect(@dialog.next(".modal-backdrop").length).to.equal 0 249 | 250 | describe "when creating a dialog with no close button", -> 251 | beforeEach -> 252 | @dialog = bootbox.dialog 253 | message: "No backdrop in sight" 254 | closeButton: false 255 | 256 | it "does not have a close button inside the body", -> 257 | expect(@exists(".modal-body .close")).not.to.be.ok 258 | 259 | describe "when creating a dialog with an onEscape handler", -> 260 | beforeEach -> 261 | @e = (keyCode) -> 262 | $(@dialog).trigger($.Event "keyup", which: keyCode) 263 | 264 | describe "with a simple callback", -> 265 | beforeEach -> 266 | @callback = sinon.spy() 267 | 268 | @dialog = bootbox.dialog 269 | message: "Are you sure?" 270 | onEscape: @callback 271 | 272 | @hidden = sinon.spy @dialog, "modal" 273 | @trigger = sinon.spy(@dialog, "trigger").withArgs "escape.close.bb" 274 | 275 | describe "when triggering the keyup event", -> 276 | 277 | describe "when the key is not the escape key", -> 278 | beforeEach -> @e 15 279 | 280 | it "does not trigger the escape event", -> 281 | expect(@trigger).not.to.have.been.called 282 | 283 | it "should not hide the modal", -> 284 | expect(@hidden).not.to.have.been.called 285 | 286 | describe "when the key is the escape key", -> 287 | beforeEach -> @e 27 288 | 289 | it "triggers the escape event", -> 290 | expect(@trigger).to.have.been.calledWithExactly "escape.close.bb" 291 | 292 | it "should invoke the callback", -> 293 | expect(@callback).to.have.been.called 294 | 295 | it "should hide the modal", -> 296 | expect(@hidden).to.have.been.calledWithExactly "hide" 297 | 298 | describe "with a callback which returns false", -> 299 | beforeEach -> 300 | @callback = sinon.stub().returns false 301 | 302 | @dialog = bootbox.dialog 303 | message: "Are you sure?" 304 | onEscape: @callback 305 | 306 | @hidden = sinon.spy @dialog, "modal" 307 | 308 | describe "when triggering the escape keyup event", -> 309 | beforeEach -> @e 27 310 | 311 | it "should invoke the callback", -> 312 | expect(@callback).to.have.been.called 313 | 314 | it "should not hide the modal", -> 315 | expect(@hidden).not.to.have.been.called 316 | 317 | describe "with size option", -> 318 | describe "when the size option is set to large", -> 319 | beforeEach -> 320 | @dialog = bootbox.dialog 321 | message: "test" 322 | size: "large" 323 | 324 | it "adds the large class to the innerDialog", -> 325 | expect(@dialog.children(":first").hasClass("modal-lg")).to.be.true 326 | 327 | describe "when the size option is set to small", -> 328 | beforeEach -> 329 | @dialog = bootbox.dialog 330 | message: "test" 331 | size: "small" 332 | 333 | it "adds the large class to the innerDialog", -> 334 | expect(@dialog.children(":first").hasClass("modal-sm")).to.be.true 335 | -------------------------------------------------------------------------------- /tests/vendor/bootstrap-3.0.0.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap.js v3.0.0 by @fat and @mdo 3 | * Copyright 2013 Twitter Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | if(!jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(window.jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]');if(a.length){var b=this.$element.find("input").prop("checked",!this.$element.hasClass("active")).trigger("change");"radio"===b.prop("type")&&a.find(".active").removeClass("active")}this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(window.jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(window.jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery); -------------------------------------------------------------------------------- /tests/vendor/bootstrap-3.1.1.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.1.1 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.isLoading=!1};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",f.resetText||d.data("resetText",d[e]()),d[e](f[b]||this.options[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},b.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});return this.$element.trigger(j),j.isDefaultPrevented()?void 0:(this.sliding=!0,f&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")),f&&this.cycle(),this)};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("collapse in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);!e&&f.toggle&&"show"==c&&(c=!c),e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(b){a(d).remove(),a(e).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(a(c).is("body")?window:c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);{var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})}},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(b.RESET).addClass("affix");var a=this.$window.scrollTop(),c=this.$element.offset();return this.pinnedOffset=c.top-a},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"top"==this.affixed&&(e.top+=d),"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(b.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:c-h-this.$element.height()}))}}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); -------------------------------------------------------------------------------- /bootbox.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootbox.js [master branch] 3 | * 4 | * http://bootboxjs.com/license.txt 5 | */ 6 | 7 | // @see https://github.com/makeusabrew/bootbox/issues/180 8 | // @see https://github.com/makeusabrew/bootbox/issues/186 9 | (function (root, factory) { 10 | 11 | "use strict"; 12 | if (typeof define === "function" && define.amd) { 13 | // AMD. Register as an anonymous module. 14 | define(["jquery"], factory); 15 | } else if (typeof exports === "object") { 16 | // Node. Does not work with strict CommonJS, but 17 | // only CommonJS-like environments that support module.exports, 18 | // like Node. 19 | module.exports = factory(require("jquery")); 20 | } else { 21 | // Browser globals (root is window) 22 | root.bootbox = factory(root.jQuery); 23 | } 24 | 25 | }(this, function init($, undefined) { 26 | 27 | "use strict"; 28 | 29 | // the base DOM structure needed to create a modal 30 | var templates = { 31 | dialog: 32 | "", 39 | header: 40 | "", 43 | footer: 44 | "", 45 | closeButton: 46 | " ", 47 | form: 48 | "
", 49 | inputs: { 50 | text: 51 | "", 52 | textarea: 53 | "", 54 | email: 55 | "", 56 | select: 57 | "", 58 | checkbox: 59 | "
", 60 | date: 61 | "", 62 | time: 63 | "", 64 | number: 65 | "", 66 | password: 67 | "" 68 | } 69 | }; 70 | 71 | var defaults = { 72 | // default language 73 | locale: "en", 74 | // show backdrop or not 75 | backdrop: true, 76 | // animate the modal in/out 77 | animate: true, 78 | // additional class string applied to the top level dialog 79 | className: null, 80 | // whether or not to include a close button 81 | closeButton: true, 82 | 83 | // set alert title 84 | showAlertTitle: true, 85 | 86 | // default alert title 87 | alertTitle: "Message", 88 | // show the dialog immediately by default 89 | show: true, 90 | // dialog container 91 | container: "body" 92 | }; 93 | 94 | // our public object; augmented after our private API 95 | var exports = {}; 96 | 97 | /** 98 | * @private 99 | */ 100 | function _t(key) { 101 | var locale = locales[defaults.locale]; 102 | return locale ? locale[key] : locales.en[key]; 103 | } 104 | 105 | function processCallback(e, dialog, callback) { 106 | e.stopPropagation(); 107 | e.preventDefault(); 108 | 109 | // by default we assume a callback will get rid of the dialog, 110 | // although it is given the opportunity to override this 111 | 112 | // so, if the callback can be invoked and it *explicitly returns false* 113 | // then we'll set a flag to keep the dialog active... 114 | var preserveDialog = $.isFunction(callback) && callback(e) === false; 115 | 116 | // ... otherwise we'll bin it 117 | if (!preserveDialog) { 118 | dialog.modal("hide"); 119 | } 120 | } 121 | 122 | function getKeyLength(obj) { 123 | // @TODO defer to Object.keys(x).length if available? 124 | var k, t = 0; 125 | for (k in obj) { 126 | t ++; 127 | } 128 | return t; 129 | } 130 | 131 | function each(collection, iterator) { 132 | var index = 0; 133 | $.each(collection, function(key, value) { 134 | iterator(key, value, index++); 135 | }); 136 | } 137 | 138 | function uid (prefix) { 139 | do { 140 | prefix += ~~(Math.random() * 1000000); 141 | } 142 | while (document.getElementById(prefix)); 143 | return prefix; 144 | } 145 | 146 | 147 | function sanitize(options) { 148 | var buttons; 149 | var total; 150 | 151 | if (typeof options !== "object") { 152 | throw new Error("Please supply an object of options"); 153 | } 154 | 155 | if (!options.message) { 156 | throw new Error("Please specify a message"); 157 | } 158 | 159 | // make sure any supplied options take precedence over defaults 160 | options = $.extend({}, defaults, options); 161 | 162 | if (!options.buttons) { 163 | options.buttons = {}; 164 | } 165 | 166 | // we only support Bootstrap's "static" and false backdrop args 167 | // supporting true would mean you could dismiss the dialog without 168 | // explicitly interacting with it 169 | options.backdrop = options.backdrop ? "static" : false; 170 | 171 | buttons = options.buttons; 172 | 173 | total = getKeyLength(buttons); 174 | 175 | each(buttons, function(key, button, index) { 176 | 177 | if ($.isFunction(button)) { 178 | // short form, assume value is our callback. Since button 179 | // isn't an object it isn't a reference either so re-assign it 180 | button = buttons[key] = { 181 | callback: button 182 | }; 183 | } 184 | 185 | // before any further checks make sure by now button is the correct type 186 | if ($.type(button) !== "object") { 187 | throw new Error("button with key " + key + " must be an object"); 188 | } 189 | 190 | if (!button.label) { 191 | // the lack of an explicit label means we'll assume the key is good enough 192 | button.label = key; 193 | } 194 | 195 | if (!button.className) { 196 | if (total <= 2 && index === total-1) { 197 | // always add a primary to the main option in a two-button dialog 198 | button.className = "btn-primary"; 199 | } else { 200 | button.className = "btn-default"; 201 | } 202 | } 203 | }); 204 | 205 | return options; 206 | } 207 | 208 | /** 209 | * map a flexible set of arguments into a single returned object 210 | * if args.length is already one just return it, otherwise 211 | * use the properties argument to map the unnamed args to 212 | * object properties 213 | * so in the latter case: 214 | * mapArguments(["foo", $.noop], ["message", "callback"]) 215 | * -> { message: "foo", callback: $.noop } 216 | */ 217 | function mapArguments(args, properties) { 218 | var argn = args.length; 219 | var options = {}; 220 | 221 | 222 | if (argn < 1 || argn > 2) { 223 | throw new Error("Invalid argument length"); 224 | } 225 | 226 | if(argn === 2){ 227 | if( typeof args[1] === "function" ){ 228 | options[properties[1]] = args[1]; 229 | } 230 | if( $.type(args[1]) === "object" ){ 231 | options = $.extend(true,{},options,{buttons:args[1]}); // buttons 232 | } 233 | 234 | if( typeof args[0] === "string" ){ 235 | options[properties[0]] = args[0]; 236 | } 237 | if( $.type(args[0]) === "object" ){ 238 | options = $.extend(true,{},options,args[0]); // message, title 239 | } 240 | }else if(argn === 1){ 241 | if(typeof args[0] === "string"){ 242 | options[properties[0]] = args[0]; 243 | } 244 | if( $.type(args[0]) === "object" ){ 245 | options = args[0]; 246 | } 247 | 248 | } 249 | 250 | return options; 251 | } 252 | 253 | /** 254 | * merge a set of default dialog options with user supplied arguments 255 | */ 256 | function mergeArguments(defaults, args, properties) { 257 | return $.extend( 258 | // deep merge 259 | true, 260 | // ensure the target is an empty, unreferenced object 261 | {}, 262 | // the base options object for this type of dialog (often just buttons) 263 | defaults, 264 | // args could be an object or array; if it's an array properties will 265 | // map it to a proper options object 266 | mapArguments( 267 | args, 268 | properties // ['message','callback'] 269 | ) 270 | ); 271 | } 272 | 273 | /** 274 | * this entry-level method makes heavy use of composition to take a simple 275 | * range of inputs and return valid options suitable for passing to bootbox.dialog 276 | */ 277 | function mergeDialogOptions(className, labels, properties, args) { 278 | // build up a base set of dialog properties 279 | var baseOptions = { 280 | className: "bootbox-" + className, 281 | buttons: createLabels.apply(null, labels) 282 | }; 283 | args = [].slice.call(args,0); 284 | 285 | // ensure the buttons properties generated, *after* merging 286 | // with user args are still valid against the supplied labels 287 | return validateButtons( 288 | // merge the generated base properties with user supplied arguments 289 | mergeArguments( 290 | baseOptions, 291 | args, 292 | // if args.length > 1, properties specify how each arg maps to an object key 293 | properties 294 | ), 295 | labels 296 | ); 297 | } 298 | 299 | /** 300 | * from a given list of arguments return a suitable object of button labels 301 | * all this does is normalise the given labels and translate them where possible 302 | * e.g. "ok", "confirm" -> { ok: "OK, cancel: "Annuleren" } 303 | */ 304 | function createLabels() { 305 | var buttons = {}; 306 | 307 | for (var i = 0, j = arguments.length; i < j; i++) { 308 | var argument = arguments[i]; 309 | var key = argument.toLowerCase(); 310 | var value = argument.toUpperCase(); 311 | 312 | buttons[key] = { 313 | label: _t(value) 314 | }; 315 | } 316 | 317 | return buttons; 318 | } 319 | 320 | function validateButtons(options, buttons) { 321 | var allowedButtons = {}; 322 | each(buttons, function(key, value) { 323 | allowedButtons[value] = true; 324 | }); 325 | 326 | each(options.buttons, function(key) { 327 | if (allowedButtons[key] === undefined) { 328 | throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")"); 329 | } 330 | }); 331 | 332 | return options; 333 | } 334 | 335 | exports.alert = function() { 336 | var options; 337 | options = mergeDialogOptions("alert", ["ok"], ["message", "callback"], arguments); 338 | 339 | if (options.callback && !$.isFunction(options.callback)) { 340 | throw new Error("alert requires callback property to be a function when provided"); 341 | } 342 | 343 | /** 344 | * overrides 345 | */ 346 | options.buttons.ok.callback = options.onEscape = $.isFunction(options.buttons.ok.callback) ? 347 | options.buttons.ok.callback : function() { 348 | if ($.isFunction(options.callback)) { 349 | return options.callback(); 350 | } 351 | return true; 352 | }; 353 | 354 | return exports.dialog(options); 355 | }; 356 | 357 | exports.confirm = function() { 358 | var options; 359 | 360 | 361 | options = mergeDialogOptions("confirm", ["cancel", "confirm"], ["message", "callback"], arguments); 362 | 363 | /** 364 | * overrides; undo anything the user tried to set they shouldn't have 365 | */ 366 | options.buttons.cancel.callback = options.onEscape = $.isFunction( options.buttons.cancel.callback ) ? 367 | options.buttons.cancel.callback : function() { 368 | return options.callback(false); 369 | }; 370 | 371 | options.buttons.confirm.callback = $.isFunction( options.buttons.confirm.callback ) ? 372 | options.buttons.confirm.callback : function() { 373 | return options.callback(true); 374 | }; 375 | 376 | // confirm specific validation 377 | if ($.isFunction( arguments[1] ) && !$.isFunction(options.callback)) { 378 | throw new Error("confirm requires a callback"); 379 | } 380 | 381 | return exports.dialog(options); 382 | }; 383 | 384 | exports.prompt = function() { 385 | var options; 386 | var defaults; 387 | var dialog; 388 | var form; 389 | var input; 390 | var shouldShow; 391 | var inputOptions; 392 | var callbackValue; 393 | 394 | // we have to create our form first otherwise 395 | // its value is undefined when gearing up our options 396 | // @TODO this could be solved by allowing message to 397 | // be a function instead... 398 | form = $(templates.form); 399 | 400 | // prompt defaults are more complex than others in that 401 | // users can override more defaults 402 | // @TODO I don't like that prompt has to do a lot of heavy 403 | // lifting which mergeDialogOptions can *almost* support already 404 | // just because of 'value' and 'inputType' - can we refactor? 405 | defaults = { 406 | className: "bootbox-prompt", 407 | buttons: createLabels("cancel", "confirm"), 408 | value: "", 409 | inputType: "text" 410 | }; 411 | 412 | options = validateButtons( 413 | mergeArguments(defaults, arguments, ["title", "callback"]), 414 | ["cancel", "confirm"] 415 | ); 416 | 417 | // capture the user's show value; we always set this to false before 418 | // spawning the dialog to give us a chance to attach some handlers to 419 | // it, but we need to make sure we respect a preference not to show it 420 | shouldShow = (options.show === undefined) ? true : options.show; 421 | 422 | /** 423 | * overrides; undo anything the user tried to set they shouldn't have 424 | */ 425 | options.message = form; 426 | 427 | var originalConfirmCallback = options.buttons.confirm.callback, 428 | originalCancelCallback = options.buttons.cancel.callback; 429 | 430 | options.buttons.cancel.callback = options.onEscape = function() { 431 | if( $.isFunction( originalCancelCallback ) ) { 432 | return originalCancelCallback(null); 433 | } 434 | 435 | if( $.isFunction( originalConfirmCallback ) ) { 436 | return originalConfirmCallback(null); 437 | } 438 | 439 | return options.callback(null); 440 | }; 441 | 442 | callbackValue = function(){ 443 | var value; 444 | switch (options.inputType) { 445 | case "text": 446 | case "textarea": 447 | case "email": 448 | case "select": 449 | case "date": 450 | case "time": 451 | case "number": 452 | case "password": 453 | value = input.val(); 454 | break; 455 | 456 | case "checkbox": 457 | var checkedItems = input.find("input:checked"); 458 | 459 | // we assume that checkboxes are always multiple, 460 | // hence we default to an empty array 461 | value = []; 462 | 463 | each(checkedItems, function(_, item) { 464 | value.push($(item).val()); 465 | }); 466 | break; 467 | } 468 | return value; 469 | }; 470 | 471 | 472 | options.buttons.confirm.callback = function() { 473 | var value = callbackValue(); 474 | if( $.isFunction( originalConfirmCallback ) ) { 475 | return originalConfirmCallback(value); 476 | } 477 | 478 | return options.callback(value); 479 | }; 480 | 481 | options.show = false; 482 | 483 | // prompt specific validation 484 | if (!options.title) { 485 | throw new Error("prompt requires a title"); 486 | } 487 | 488 | if ( $.isFunction( arguments[1] ) && !$.isFunction(options.callback)) { 489 | throw new Error("prompt requires a callback"); 490 | } 491 | 492 | if (!templates.inputs[options.inputType]) { 493 | throw new Error("invalid prompt type"); 494 | } 495 | 496 | // create the input based on the supplied type 497 | input = $(templates.inputs[options.inputType]); 498 | 499 | switch (options.inputType) { 500 | case "text": 501 | case "textarea": 502 | case "email": 503 | case "date": 504 | case "time": 505 | case "number": 506 | case "password": 507 | input.val(options.value); 508 | break; 509 | 510 | case "select": 511 | var groups = {}; 512 | inputOptions = options.inputOptions || []; 513 | 514 | if (!inputOptions.length) { 515 | throw new Error("prompt with select requires options"); 516 | } 517 | 518 | each(inputOptions, function(_, option) { 519 | 520 | // assume the element to attach to is the input... 521 | var elem = input; 522 | 523 | if (option.value === undefined || option.text === undefined) { 524 | throw new Error("given options in wrong format"); 525 | } 526 | 527 | 528 | // ... but override that element if this option sits in a group 529 | 530 | if (option.group) { 531 | // initialise group if necessary 532 | if (!groups[option.group]) { 533 | groups[option.group] = $("").attr("label", option.group); 534 | } 535 | 536 | elem = groups[option.group]; 537 | } 538 | 539 | elem.append(""); 540 | }); 541 | 542 | each(groups, function(_, group) { 543 | input.append(group); 544 | }); 545 | 546 | // safe to set a select's value as per a normal input 547 | input.val(options.value); 548 | break; 549 | 550 | case "checkbox": 551 | var values = $.isArray(options.value) ? options.value : [options.value]; 552 | inputOptions = options.inputOptions || []; 553 | 554 | if (!inputOptions.length) { 555 | throw new Error("prompt with checkbox requires options"); 556 | } 557 | 558 | if (!inputOptions[0].value || !inputOptions[0].text) { 559 | throw new Error("given options in wrong format"); 560 | } 561 | 562 | // checkboxes have to nest within a containing element, so 563 | // they break the rules a bit and we end up re-assigning 564 | // our 'input' element to this container instead 565 | input = $("
"); 566 | 567 | each(inputOptions, function(_, option) { 568 | var checkbox = $(templates.inputs[options.inputType]); 569 | 570 | checkbox.find("input").attr("value", option.value); 571 | checkbox.find("label").append(option.text); 572 | 573 | // we've ensured values is an array so we can always iterate over it 574 | each(values, function(_, value) { 575 | if (value === option.value) { 576 | checkbox.find("input").prop("checked", true); 577 | } 578 | }); 579 | 580 | input.append(checkbox); 581 | }); 582 | break; 583 | } 584 | 585 | if (options.placeholder) { 586 | input.attr("placeholder", options.placeholder); 587 | } 588 | 589 | if(options.pattern){ 590 | input.attr("pattern", options.pattern); 591 | } 592 | 593 | // now place it in our form 594 | form.append(input); 595 | 596 | form.on("submit", function(e) { 597 | e.preventDefault(); 598 | // Fix for SammyJS (or similar JS routing library) hijacking the form post. 599 | e.stopPropagation(); 600 | // @TODO can we actually click *the* button object instead? 601 | // e.g. buttons.confirm.click() or similar 602 | dialog.find(".btn-primary").click(); 603 | }); 604 | 605 | dialog = exports.dialog(options); 606 | 607 | // clear the existing handler focusing the submit button... 608 | dialog.off("shown.bs.modal"); 609 | 610 | // ...and replace it with one focusing our input, if possible 611 | dialog.on("shown.bs.modal", function() { 612 | input.focus(); 613 | }); 614 | 615 | if (shouldShow === true) { 616 | dialog.modal("show"); 617 | } 618 | 619 | return dialog; 620 | }; 621 | 622 | exports.dialog = function(options) { 623 | options = sanitize(options); 624 | if(!options.title && options.showAlertTitle && options.alertTitle && options.className && ~options.className.indexOf("alert")){ 625 | options.title = options.alertTitle; 626 | } 627 | 628 | var dialog = $(templates.dialog); 629 | var innerDialog = dialog.find(".modal-dialog"); 630 | var body = dialog.find(".modal-body"); 631 | var buttons = options.buttons; 632 | var buttonStr = ""; 633 | var callbacks = { 634 | onEscape: options.onEscape 635 | }; 636 | 637 | each(buttons, function(key, button) { 638 | 639 | // @TODO I don't like this string appending to itself; bit dirty. Needs reworking 640 | // can we just build up button elements instead? slower but neater. Then button 641 | // can just become a template too 642 | buttonStr += ""; 643 | callbacks[key] = button.callback; 644 | }); 645 | 646 | body.find(".bootbox-body").html(options.message); 647 | 648 | if (options.animate === true) { 649 | dialog.addClass("fade"); 650 | } 651 | 652 | if (options.className) { 653 | dialog.addClass(options.className); 654 | } 655 | 656 | if (options.size === "large") { 657 | innerDialog.addClass("modal-lg"); 658 | } 659 | 660 | if (options.size === "small") { 661 | innerDialog.addClass("modal-sm"); 662 | } 663 | 664 | if (options.title) { 665 | body.before(templates.header); 666 | } 667 | 668 | if (options.closeButton) { 669 | var closeButton = $(templates.closeButton); 670 | 671 | if (options.title) { 672 | dialog.find(".modal-header").prepend(closeButton); 673 | } else { 674 | closeButton.css("margin-top", "-10px").prependTo(body); 675 | } 676 | } 677 | 678 | if (options.title) { 679 | var globalId = uid("bootbox-dialog"); 680 | dialog.attr({ 681 | "aria-labelledby": globalId 682 | }); 683 | dialog.find(".modal-title").html(options.title).attr({ 684 | id: globalId 685 | }); 686 | } 687 | 688 | if (buttonStr.length) { 689 | body.after(templates.footer); 690 | dialog.find(".modal-footer").html(buttonStr); 691 | } 692 | 693 | 694 | /** 695 | * Bootstrap event listeners; used handle extra 696 | * setup & teardown required after the underlying 697 | * modal has performed certain actions 698 | */ 699 | 700 | dialog.on("hidden.bs.modal", function(e) { 701 | // ensure we don't accidentally intercept hidden events triggered 702 | // by children of the current dialog. We shouldn't anymore now BS 703 | // namespaces its events; but still worth doing 704 | if (e.target === this) { 705 | dialog.remove(); 706 | } 707 | }); 708 | 709 | /* 710 | dialog.on("show.bs.modal", function() { 711 | // sadly this doesn't work; show is called *just* before 712 | // the backdrop is added so we'd need a setTimeout hack or 713 | // otherwise... leaving in as would be nice 714 | if (options.backdrop) { 715 | dialog.next(".modal-backdrop").addClass("bootbox-backdrop"); 716 | } 717 | }); 718 | */ 719 | 720 | dialog.on("shown.bs.modal", function() { 721 | dialog.find(".btn-primary:first").focus(); 722 | }); 723 | 724 | /** 725 | * Bootbox event listeners; experimental and may not last 726 | * just an attempt to decouple some behaviours from their 727 | * respective triggers 728 | */ 729 | 730 | dialog.on("escape.close.bb", function(e) { 731 | if (callbacks.onEscape) { 732 | processCallback(e, dialog, callbacks.onEscape); 733 | } 734 | }); 735 | 736 | /** 737 | * Standard jQuery event listeners; used to handle user 738 | * interaction with our dialog 739 | */ 740 | 741 | dialog.on("click", ".modal-footer button", function(e) { 742 | var callbackKey = $(this).data("bb-handler"); 743 | 744 | processCallback(e, dialog, callbacks[callbackKey]); 745 | 746 | }); 747 | 748 | dialog.on("click", ".bootbox-close-button", function(e) { 749 | // onEscape might be falsy but that's fine; the fact is 750 | // if the user has managed to click the close button we 751 | // have to close the dialog, callback or not 752 | processCallback(e, dialog, callbacks.onEscape); 753 | }); 754 | 755 | dialog.on("keyup", function(e) { 756 | if (e.which === 27) { 757 | dialog.trigger("escape.close.bb"); 758 | } 759 | }); 760 | 761 | // the remainder of this method simply deals with adding our 762 | // dialogent to the DOM, augmenting it with Bootstrap's modal 763 | // functionality and then giving the resulting object back 764 | // to our caller 765 | 766 | $(options.container).append(dialog); 767 | 768 | dialog.modal({ 769 | backdrop: options.backdrop, 770 | keyboard: false, 771 | show: false 772 | }); 773 | 774 | if (options.show) { 775 | dialog.modal("show"); 776 | } 777 | 778 | // @TODO should we return the raw element here or should 779 | // we wrap it in an object on which we can expose some neater 780 | // methods, e.g. var d = bootbox.alert(); d.hide(); instead 781 | // of d.modal("hide"); 782 | 783 | /* 784 | function BBDialog(elem) { 785 | this.elem = elem; 786 | } 787 | 788 | BBDialog.prototype = { 789 | hide: function() { 790 | return this.elem.modal("hide"); 791 | }, 792 | show: function() { 793 | return this.elem.modal("show"); 794 | } 795 | }; 796 | */ 797 | 798 | return dialog; 799 | 800 | }; 801 | 802 | exports.setDefaults = function() { 803 | var values = {}; 804 | 805 | if (arguments.length === 2) { 806 | // allow passing of single key/value... 807 | values[arguments[0]] = arguments[1]; 808 | } else { 809 | // ... and as an object too 810 | values = arguments[0]; 811 | } 812 | 813 | $.extend(defaults, values); 814 | }; 815 | 816 | exports.hideAll = function() { 817 | $(".bootbox").modal("hide"); 818 | 819 | return exports; 820 | }; 821 | 822 | 823 | /** 824 | * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are 825 | * unlikely to be required. If this gets too large it can be split out into separate JS files. 826 | */ 827 | var locales = { 828 | br : { 829 | OK : "OK", 830 | CANCEL : "Cancelar", 831 | CONFIRM : "Sim" 832 | }, 833 | cs : { 834 | OK : "OK", 835 | CANCEL : "Zrušit", 836 | CONFIRM : "Potvrdit" 837 | }, 838 | da : { 839 | OK : "OK", 840 | CANCEL : "Annuller", 841 | CONFIRM : "Accepter" 842 | }, 843 | de : { 844 | OK : "OK", 845 | CANCEL : "Abbrechen", 846 | CONFIRM : "Akzeptieren" 847 | }, 848 | el : { 849 | OK : "Εντάξει", 850 | CANCEL : "Ακύρωση", 851 | CONFIRM : "Επιβεβαίωση" 852 | }, 853 | en : { 854 | OK : "OK", 855 | CANCEL : "Cancel", 856 | CONFIRM : "OK" 857 | }, 858 | es : { 859 | OK : "OK", 860 | CANCEL : "Cancelar", 861 | CONFIRM : "Aceptar" 862 | }, 863 | et : { 864 | OK : "OK", 865 | CANCEL : "Katkesta", 866 | CONFIRM : "OK" 867 | }, 868 | fi : { 869 | OK : "OK", 870 | CANCEL : "Peruuta", 871 | CONFIRM : "OK" 872 | }, 873 | fr : { 874 | OK : "OK", 875 | CANCEL : "Annuler", 876 | CONFIRM : "D'accord" 877 | }, 878 | he : { 879 | OK : "אישור", 880 | CANCEL : "ביטול", 881 | CONFIRM : "אישור" 882 | }, 883 | id : { 884 | OK : "OK", 885 | CANCEL : "Batal", 886 | CONFIRM : "OK" 887 | }, 888 | it : { 889 | OK : "OK", 890 | CANCEL : "Annulla", 891 | CONFIRM : "Conferma" 892 | }, 893 | ja : { 894 | OK : "OK", 895 | CANCEL : "キャンセル", 896 | CONFIRM : "確認" 897 | }, 898 | lt : { 899 | OK : "Gerai", 900 | CANCEL : "Atšaukti", 901 | CONFIRM : "Patvirtinti" 902 | }, 903 | lv : { 904 | OK : "Labi", 905 | CANCEL : "Atcelt", 906 | CONFIRM : "Apstiprināt" 907 | }, 908 | nl : { 909 | OK : "OK", 910 | CANCEL : "Annuleren", 911 | CONFIRM : "Accepteren" 912 | }, 913 | no : { 914 | OK : "OK", 915 | CANCEL : "Avbryt", 916 | CONFIRM : "OK" 917 | }, 918 | pl : { 919 | OK : "OK", 920 | CANCEL : "Anuluj", 921 | CONFIRM : "Potwierdź" 922 | }, 923 | pt : { 924 | OK : "OK", 925 | CANCEL : "Cancelar", 926 | CONFIRM : "Confirmar" 927 | }, 928 | ru : { 929 | OK : "OK", 930 | CANCEL : "Отмена", 931 | CONFIRM : "Применить" 932 | }, 933 | sv : { 934 | OK : "OK", 935 | CANCEL : "Avbryt", 936 | CONFIRM : "OK" 937 | }, 938 | tr : { 939 | OK : "Tamam", 940 | CANCEL : "İptal", 941 | CONFIRM : "Onayla" 942 | }, 943 | zh_CN : { 944 | OK : "OK", 945 | CANCEL : "取消", 946 | CONFIRM : "确认" 947 | }, 948 | zh_TW : { 949 | OK : "OK", 950 | CANCEL : "取消", 951 | CONFIRM : "確認" 952 | } 953 | }; 954 | 955 | exports.init = function(_$) { 956 | return init(_$ || $); 957 | }; 958 | 959 | return exports; 960 | })); 961 | -------------------------------------------------------------------------------- /tests/prompt.test.coffee: -------------------------------------------------------------------------------- 1 | describe "bootbox.prompt", -> 2 | beforeEach -> 3 | window.bootbox = bootbox.init() 4 | 5 | @find = (selector) -> @dialog.find selector 6 | @text = (selector) -> @find(selector).text() 7 | @exists = (selector) -> @find(selector).length isnt 0 8 | 9 | describe "basic usage tests", -> 10 | 11 | describe "with one argument", -> 12 | 13 | describe "where the argument is not an object", -> 14 | beforeEach -> 15 | @create = -> bootbox.prompt "What is your name?" 16 | 17 | it "throws an error", -> 18 | expect(@create).to.throw /prompt requires a callback/ 19 | 20 | describe "where the argument is an object", -> 21 | beforeEach -> 22 | @options = {} 23 | @create = => @dialog = bootbox.prompt @options 24 | 25 | describe "with a title property", -> 26 | beforeEach -> 27 | @options.title = "What is your name?" 28 | 29 | it "throws an error requiring a callback", -> 30 | expect(@create).to.throw /prompt requires a callback/ 31 | 32 | describe "and a callback property", -> 33 | describe "where the callback is not a function", -> 34 | beforeEach -> 35 | @options.callback = "Not a function" 36 | 37 | it "throws an error requiring a callback", -> 38 | expect(@create).to.throw /prompt requires a callback/ 39 | 40 | describe "with a callback function", -> 41 | beforeEach -> 42 | @options.callback = -> true 43 | 44 | it "throws an error requiring a title", -> 45 | expect(@create).to.throw /prompt requires a title/ 46 | 47 | describe "with a title and a callback", -> 48 | beforeEach -> 49 | @options = 50 | callback: -> true 51 | title: "What is your name?" 52 | 53 | it "does not throw an error", -> 54 | expect(@create).not.to.throw Error 55 | 56 | it "creates a dialog object", -> 57 | expect(@dialog).to.be.an "object" 58 | 59 | it "applies the bootbox-prompt class to the dialog", -> 60 | expect(@dialog.hasClass("bootbox-prompt")).to.be.true 61 | 62 | it "adds the correct button labels", -> 63 | expect(@dialog.find(".btn:first").text()).to.equal "Cancel" 64 | expect(@dialog.find(".btn:last").text()).to.equal "OK" 65 | 66 | it "adds the correct button classes", -> 67 | expect(@dialog.find(".btn:first").hasClass("btn-default")).to.be.true 68 | expect(@dialog.find(".btn:last").hasClass("btn-primary")).to.be.true 69 | 70 | describe "with two arguments", -> 71 | describe "where the second argument is not a function", -> 72 | beforeEach -> 73 | @create = => 74 | @dialog = bootbox.prompt "What is your name?", "callback here" 75 | 76 | it "throws an error requiring a callback", -> 77 | expect(@create).to.throw /prompt requires a callback/ 78 | 79 | describe "where the second argument is a function", -> 80 | beforeEach -> 81 | @create = => 82 | @dialog = bootbox.prompt "What is your name?", -> true 83 | 84 | it "does not throw an error", -> 85 | expect(@create).not.to.throw Error 86 | 87 | it "creates a dialog object", -> 88 | expect(@dialog).to.be.an "object" 89 | 90 | it "adds the correct button labels", -> 91 | expect(@text(".btn:first")).to.equal "Cancel" 92 | expect(@text(".btn:last")).to.equal "OK" 93 | 94 | it "adds the correct button classes", -> 95 | expect(@dialog.find(".btn:first").hasClass("btn-default")).to.be.true 96 | expect(@dialog.find(".btn:last").hasClass("btn-primary")).to.be.true 97 | 98 | it "adds the expected dialog title", -> 99 | expect(@text("h4")).to.equal "What is your name?" 100 | 101 | it "adds a close button", -> 102 | expect(@dialog.find(".modal-header .close")).to.be.ok 103 | 104 | it "creates a form with a text input", -> 105 | expect(@dialog.find("form input[type=text]")).to.be.ok 106 | 107 | it "with no default value", -> 108 | expect(@dialog.find("form input[type=text]").val()).to.equal "" 109 | 110 | it "shows the dialog", -> 111 | expect(@dialog.is(":visible")).to.be.true 112 | 113 | describe "configuration options tests", -> 114 | beforeEach -> 115 | @options = 116 | title: "What is your name?" 117 | callback: -> true 118 | 119 | @create = => 120 | @dialog = bootbox.prompt @options 121 | 122 | describe "with a custom cancel button", -> 123 | beforeEach -> 124 | @options.buttons = 125 | cancel: 126 | label: "Custom cancel" 127 | className: "btn-danger" 128 | 129 | @create() 130 | 131 | @button = @dialog.find(".btn:first") 132 | 133 | it "adds the correct cancel button", -> 134 | expect(@button.text()).to.equal "Custom cancel" 135 | expect(@button.hasClass("btn-danger")).to.be.true 136 | 137 | describe "with a custom confirm button", -> 138 | beforeEach -> 139 | @options.buttons = 140 | confirm: 141 | label: "Custom confirm" 142 | className: "btn-warning" 143 | 144 | @create() 145 | 146 | @button = @dialog.find(".btn:last") 147 | 148 | it "adds the correct confirm button", -> 149 | expect(@button.text()).to.equal "Custom confirm" 150 | expect(@button.hasClass("btn-warning")).to.be.true 151 | 152 | describe "with an unrecognised button key", -> 153 | beforeEach -> 154 | @options.buttons = 155 | prompt: 156 | label: "Custom confirm" 157 | className: "btn-warning" 158 | 159 | it "throws an error", -> 160 | expect(@create).to.throw /key prompt is not allowed/ 161 | 162 | describe "setting show to false", -> 163 | beforeEach -> 164 | @options.show = false 165 | 166 | @shown = sinon.spy() 167 | 168 | sinon.stub bootbox, "dialog", => 169 | on: -> 170 | off: -> 171 | modal: @shown 172 | 173 | @create() 174 | 175 | it "does not show the dialog", -> 176 | expect(@shown).not.to.have.been.called 177 | 178 | describe "invalid prompt type", -> 179 | beforeEach -> 180 | @options.inputType = 'foobar' 181 | 182 | it "throws an error", -> 183 | expect(@create).to.throw /invalid prompt type/ 184 | 185 | describe "setting inputType text", -> 186 | beforeEach -> 187 | @options.inputType = "text" 188 | 189 | describe "without default value", -> 190 | beforeEach -> 191 | @create() 192 | 193 | it "shows text input ", -> 194 | expect(@exists("input[type='text']")).to.be.ok 195 | 196 | it "has proper class", -> 197 | expect(@find("input[type='text']").hasClass("bootbox-input")).to.be.ok 198 | expect(@find("input[type='text']").hasClass("bootbox-input-text")).to.be.ok 199 | 200 | describe "with default value", -> 201 | beforeEach -> 202 | @options.value = "John Smith" 203 | @create() 204 | 205 | it "has correct default value", -> 206 | expect(@find("input[type='text']").val()).to.equal "John Smith" 207 | 208 | describe "with placeholder", -> 209 | beforeEach -> 210 | @options.placeholder = "enter your name" 211 | @create() 212 | 213 | it "has correct placeholder value", -> 214 | expect(@find("input[type='text']").prop("placeholder")).to.equal "enter your name" 215 | 216 | describe "with pattern", -> 217 | beforeEach -> 218 | @options.pattern = "\d{1,2}/\d{1,2}/\d{4}" 219 | @create() 220 | 221 | it "has correct pattern value", -> 222 | expect(@find("input[type='text']").prop("pattern")).to.equal "\d{1,2}/\d{1,2}/\d{4}" 223 | 224 | describe "setting inputType textarea", -> 225 | beforeEach -> 226 | @options.inputType = "textarea" 227 | 228 | describe "without default value", -> 229 | beforeEach -> 230 | @create() 231 | 232 | it "shows text input ", -> 233 | expect(@exists("textarea")).to.be.ok 234 | 235 | it "has proper class", -> 236 | expect(@find("textarea").hasClass("bootbox-input")).to.be.ok 237 | expect(@find("textarea").hasClass("bootbox-input-textarea")).to.be.ok 238 | 239 | describe "with default value", -> 240 | beforeEach -> 241 | @options.value = "Once upon a time..." 242 | @create() 243 | 244 | it "has correct default value", -> 245 | expect(@find("textarea").val()).to.equal "Once upon a time..." 246 | 247 | describe "with placeholder", -> 248 | beforeEach -> 249 | @options.placeholder = "enter your favorite fairy tale" 250 | @create() 251 | 252 | it "has correct placeholder value", -> 253 | expect(@find("textarea").prop("placeholder")).to.equal "enter your favorite fairy tale" 254 | 255 | describe "setting inputType email", -> 256 | beforeEach -> 257 | @options.inputType = "email" 258 | 259 | describe "without default value", -> 260 | beforeEach -> 261 | @create() 262 | 263 | it "shows email input ", -> 264 | expect(@exists("input[type='email']")).to.be.ok 265 | 266 | it "has proper class", -> 267 | expect(@find("input[type='email']").hasClass("bootbox-input")).to.be.ok 268 | expect(@find("input[type='email']").hasClass("bootbox-input-email")).to.be.ok 269 | 270 | describe "with default value", -> 271 | beforeEach -> 272 | @options.value = "john@smith.com" 273 | @create() 274 | 275 | it "has correct default value", -> 276 | expect(@find("input[type='email']").val()).to.equal "john@smith.com" 277 | 278 | describe "with placeholder", -> 279 | beforeEach -> 280 | @options.placeholder = "enter your email" 281 | @create() 282 | 283 | it "has correct placeholder value", -> 284 | expect(@find("input[type='email']").prop("placeholder")).to.equal "enter your email" 285 | 286 | describe "with pattern", -> 287 | beforeEach -> 288 | @options.pattern = "\d{1,2}/\d{1,2}/\d{4}" 289 | @create() 290 | 291 | it "has correct pattern value", -> 292 | expect(@find("input[type='email']").prop("pattern")).to.equal "\d{1,2}/\d{1,2}/\d{4}" 293 | 294 | describe "setting inputType password", -> 295 | beforeEach -> 296 | @options.inputType = "password" 297 | 298 | describe "without default value", -> 299 | beforeEach -> 300 | @create() 301 | 302 | it "shows password input ", -> 303 | expect(@exists("input[type='password']")).to.be.ok 304 | 305 | it "has proper class", -> 306 | expect(@find("input[type='password']").hasClass("bootbox-input")).to.be.ok 307 | expect(@find("input[type='password']").hasClass("bootbox-input-password")).to.be.ok 308 | 309 | describe "with default value", -> 310 | beforeEach -> 311 | @options.value = "qwerty" 312 | @create() 313 | 314 | it "has correct default value", -> 315 | expect(@find("input[type='password']").val()).to.equal "qwerty" 316 | 317 | describe "with placeholder", -> 318 | beforeEach -> 319 | @options.placeholder = "enter your password" 320 | @create() 321 | 322 | it "has correct placeholder value", -> 323 | expect(@find("input[type='password']").prop("placeholder")).to.equal "enter your password" 324 | 325 | describe "setting inputType select", -> 326 | describe "without options", -> 327 | beforeEach -> 328 | @options.inputType = 'select' 329 | 330 | it "throws an error", -> 331 | expect(@create).to.throw /prompt with select requires options/ 332 | 333 | describe "with invalid options", -> 334 | beforeEach -> 335 | @options.inputType = 'select' 336 | @options.inputOptions = 'foo' 337 | 338 | it "throws an error", -> 339 | expect(@create).to.throw /given options in wrong format/ 340 | 341 | describe "with empty options", -> 342 | beforeEach -> 343 | @options.inputType = 'select' 344 | @options.inputOptions = [] 345 | 346 | it "throws an error", -> 347 | expect(@create).to.throw /prompt with select requires options/ 348 | 349 | describe "with options in wrong format", -> 350 | beforeEach -> 351 | @options.inputType = 'select' 352 | @options.inputOptions = [{foo: 'bar'}] 353 | 354 | it "throws an error", -> 355 | expect(@create).to.throw /given options in wrong format/ 356 | 357 | describe "with a value but no text", -> 358 | beforeEach -> 359 | @options.inputType = 'select' 360 | @options.inputOptions = [{value: 'bar'}] 361 | 362 | it "throws an error", -> 363 | expect(@create).to.throw /given options in wrong format/ 364 | 365 | describe "with an invalid second options", -> 366 | beforeEach -> 367 | @options.inputType = 'select' 368 | @options.inputOptions = [ 369 | {value: "bar", text: "bar"} 370 | {text: "foo"} 371 | ] 372 | 373 | it "throws an error", -> 374 | expect(@create).to.throw /given options in wrong format/ 375 | 376 | 377 | describe "with valid options", -> 378 | beforeEach -> 379 | @options.inputType = "select" 380 | @options.inputOptions = [{value: 1, text: 'foo'},{value: 2, text: 'bar'},{value: 3, text: 'foobar'}] 381 | 382 | @create() 383 | 384 | it "shows select input", -> 385 | expect(@exists("select")).to.be.ok 386 | 387 | it "has proper class", -> 388 | expect(@find("select").hasClass("bootbox-input")).to.be.ok 389 | expect(@find("select").hasClass("bootbox-input-select")).to.be.ok 390 | 391 | it "with three options", -> 392 | expect(@find("option").length).to.equal 3 393 | 394 | describe "with zero as the first option", -> 395 | beforeEach -> 396 | @options.inputType = "select" 397 | @options.inputOptions = [{value: 0, text: "foo"}] 398 | 399 | @create() 400 | 401 | it "shows the select input", -> 402 | expect(@exists("select")).to.be.ok 403 | 404 | describe "with false as the first option", -> 405 | beforeEach -> 406 | @options.inputType = "select" 407 | @options.inputOptions = [{value: false, text: "foo"}] 408 | 409 | @create() 410 | 411 | it "shows the select input", -> 412 | expect(@exists("select")).to.be.ok 413 | 414 | describe "with option groups", -> 415 | beforeEach -> 416 | @options.inputType = 'select' 417 | @options.inputOptions = [ 418 | {value: 1, group: 'foo', text: 'foo'} 419 | {value: 2, group: 'bar', text: 'bar'} 420 | {value: 3, group: 'foo', text: 'foobar'} 421 | {value: 4, group: 'bar', text: 'barfoo'} 422 | ] 423 | 424 | @create() 425 | 426 | it "shows select input", -> 427 | expect(@exists("select")).to.be.ok 428 | 429 | it "has proper class", -> 430 | expect(@find("select").hasClass("bootbox-input")).to.be.ok 431 | expect(@find("select").hasClass("bootbox-input-select")).to.be.ok 432 | 433 | it "with two option group", -> 434 | expect(@find("optgroup").length).to.equal 2 435 | 436 | it "with four options", -> 437 | expect(@find("option").length).to.equal 4 438 | 439 | describe "setting inputType checkbox", -> 440 | describe "without options", -> 441 | beforeEach -> 442 | @options.inputType = 'checkbox' 443 | 444 | it "throws an error", -> 445 | expect(@create).to.throw /prompt with checkbox requires options/ 446 | 447 | describe "with options", -> 448 | beforeEach -> 449 | @options.inputType = 'checkbox' 450 | @options.inputOptions = [ 451 | {value: 1, text: 'foo'} 452 | {value: 2, text: 'bar'} 453 | {value: 3, text: 'foobar'} 454 | ] 455 | 456 | @create() 457 | 458 | it "shows checkbox input", -> 459 | expect(@exists("input[type='checkbox']")).to.be.ok 460 | 461 | it "has proper class", -> 462 | expect(@find("input[type='checkbox']").hasClass("bootbox-input")).to.be.ok 463 | expect(@find("input[type='checkbox']").hasClass("bootbox-input-checkbox")).to.be.ok 464 | 465 | it "with three checkboxes", -> 466 | expect(@find("input[type='checkbox']").length).to.equal 3 467 | 468 | describe "setting inputType date", -> 469 | beforeEach -> 470 | @options.inputType = "date" 471 | 472 | describe "without default value", -> 473 | beforeEach -> 474 | @create() 475 | 476 | it "shows date input ", -> 477 | expect(@exists("input[type='date']")).to.be.ok 478 | 479 | it "has proper class", -> 480 | expect(@find("input[type='date']").hasClass("bootbox-input")).to.be.ok 481 | expect(@find("input[type='date']").hasClass("bootbox-input-date")).to.be.ok 482 | 483 | describe "with default value", -> 484 | beforeEach -> 485 | @options.value = "17/08/2005" 486 | @create() 487 | 488 | it "has correct default value", -> 489 | expect(@find("input[type='date']").val()).to.equal "17/08/2005" 490 | 491 | describe "with placeholder", -> 492 | beforeEach -> 493 | @options.placeholder = "enter the date" 494 | @create() 495 | 496 | it "has correct placeholder value", -> 497 | expect(@find("input[type='date']").prop("placeholder")).to.equal "enter the date" 498 | 499 | describe "with pattern", -> 500 | beforeEach -> 501 | @options.pattern = "\d{1,2}/\d{1,2}/\d{4}" 502 | @create() 503 | 504 | it "has correct pattern value", -> 505 | expect(@find("input[type='date']").prop("pattern")).to.equal "\d{1,2}/\d{1,2}/\d{4}" 506 | 507 | describe "setting inputType time", -> 508 | beforeEach -> 509 | @options.inputType = "time" 510 | 511 | describe "without default value", -> 512 | beforeEach -> 513 | @create() 514 | 515 | it "shows time input ", -> 516 | expect(@exists("input[type='time']")).to.be.ok 517 | 518 | it "has proper class", -> 519 | expect(@find("input[type='time']").hasClass("bootbox-input")).to.be.ok 520 | expect(@find("input[type='time']").hasClass("bootbox-input-time")).to.be.ok 521 | 522 | describe "with default value", -> 523 | beforeEach -> 524 | @options.value = "19:02" 525 | @create() 526 | 527 | it "has correct default value", -> 528 | expect(@find("input[type='time']").val()).to.equal "19:02" 529 | 530 | describe "with placeholder", -> 531 | beforeEach -> 532 | @options.placeholder = "enter the time" 533 | @create() 534 | 535 | it "has correct placeholder value", -> 536 | expect(@find("input[type='time']").prop("placeholder")).to.equal "enter the time" 537 | 538 | describe "with pattern", -> 539 | beforeEach -> 540 | @options.pattern = "\d{1,2}/\d{1,2}/\d{4}" 541 | @create() 542 | 543 | it "has correct pattern value", -> 544 | expect(@find("input[type='time']").prop("pattern")).to.equal "\d{1,2}/\d{1,2}/\d{4}" 545 | 546 | describe "setting inputType number", -> 547 | beforeEach -> 548 | @options.inputType = "number" 549 | 550 | describe "without default value", -> 551 | beforeEach -> 552 | @create() 553 | 554 | it "shows number input ", -> 555 | expect(@exists("input[type='number']")).to.be.ok 556 | 557 | it "has proper class", -> 558 | expect(@find("input[type='number']").hasClass("bootbox-input")).to.be.ok 559 | expect(@find("input[type='number']").hasClass("bootbox-input-number")).to.be.ok 560 | 561 | describe "with default value", -> 562 | beforeEach -> 563 | @options.value = "300" 564 | @create() 565 | 566 | it "has correct default value", -> 567 | expect(@find("input[type='number']").val()).to.equal "300" 568 | 569 | describe "with placeholder", -> 570 | beforeEach -> 571 | @options.placeholder = "enter the number" 572 | @create() 573 | 574 | it "has correct placeholder value", -> 575 | expect(@find("input[type='number']").prop("placeholder")).to.equal "enter the number" 576 | 577 | describe "callback tests", -> 578 | describe "with a simple callback", -> 579 | beforeEach -> 580 | @callback = sinon.spy() 581 | 582 | @dialog = bootbox.prompt 583 | title: "What is your name?" 584 | callback: @callback 585 | 586 | @hidden = sinon.spy @dialog, "modal" 587 | 588 | describe "when entering no value in the text input", -> 589 | 590 | describe "when dismissing the dialog by clicking OK", -> 591 | beforeEach -> 592 | @dialog.find(".btn-primary").trigger "click" 593 | 594 | it "should invoke the callback", -> 595 | expect(@callback).to.have.been.called 596 | 597 | it "with the correct value", -> 598 | expect(@callback).to.have.been.calledWithExactly "" 599 | 600 | it "should hide the modal", -> 601 | expect(@hidden).to.have.been.calledWithExactly "hide" 602 | 603 | describe "when submitting the form", -> 604 | beforeEach -> 605 | @dialog.find(".bootbox-form").trigger "submit" 606 | 607 | it "invokes the callback with the correct value", -> 608 | expect(@callback).to.have.been.calledWithExactly "" 609 | 610 | it "should hide the modal", -> 611 | expect(@hidden).to.have.been.calledWithExactly "hide" 612 | 613 | describe "when entering a value in the text input", -> 614 | beforeEach -> 615 | @dialog.find(".bootbox-input").val "Test input" 616 | 617 | describe "when dismissing the dialog by clicking OK", -> 618 | beforeEach -> 619 | @dialog.find(".btn-primary").trigger "click" 620 | 621 | it "should invoke the callback", -> 622 | expect(@callback).to.have.been.called 623 | 624 | it "with the correct value", -> 625 | expect(@callback).to.have.been.calledWithExactly "Test input" 626 | 627 | it "should hide the modal", -> 628 | expect(@hidden).to.have.been.calledWithExactly "hide" 629 | 630 | describe "when submitting the form", -> 631 | beforeEach -> 632 | @dialog.find(".bootbox-form").trigger "submit" 633 | 634 | it "invokes the callback with the correct value", -> 635 | expect(@callback).to.have.been.calledWithExactly "Test input" 636 | 637 | it "should hide the modal", -> 638 | expect(@hidden).to.have.been.calledWithExactly "hide" 639 | 640 | describe "when dismissing the dialog by clicking Cancel", -> 641 | beforeEach -> 642 | @dialog.find(".btn-default").trigger "click" 643 | 644 | it "should invoke the callback", -> 645 | expect(@callback).to.have.been.called 646 | 647 | it "with the correct value", -> 648 | expect(@callback).to.have.been.calledWithExactly null 649 | 650 | it "should hide the modal", -> 651 | expect(@hidden).to.have.been.calledWithExactly "hide" 652 | 653 | describe "when triggering the escape event", -> 654 | beforeEach -> 655 | @dialog.trigger "escape.close.bb" 656 | 657 | it "should invoke the callback", -> 658 | expect(@callback).to.have.been.called 659 | 660 | it "with the correct value", -> 661 | expect(@callback).to.have.been.calledWithExactly null 662 | 663 | it "should hide the modal", -> 664 | expect(@hidden).to.have.been.calledWithExactly "hide" 665 | 666 | describe "when dismissing the dialog by clicking the close button", -> 667 | beforeEach -> 668 | @dialog.find(".close").trigger "click" 669 | 670 | it "should invoke the callback", -> 671 | expect(@callback).to.have.been.called 672 | 673 | it "with the correct value", -> 674 | expect(@callback).to.have.been.calledWithExactly null 675 | 676 | it "should hide the modal", -> 677 | expect(@hidden).to.have.been.calledWithExactly "hide" 678 | 679 | describe "with a callback which returns false", -> 680 | beforeEach -> 681 | @callback = sinon.stub() 682 | @callback.returns false 683 | 684 | @dialog = bootbox.prompt 685 | title: "What is your name?" 686 | callback: @callback 687 | 688 | @hidden = sinon.spy @dialog, "modal" 689 | 690 | describe "when entering no value in the text input", -> 691 | 692 | describe "when dismissing the dialog by clicking OK", -> 693 | beforeEach -> 694 | @dialog.find(".btn-primary").trigger "click" 695 | 696 | it "should invoke the callback", -> 697 | expect(@callback).to.have.been.called 698 | 699 | it "with the correct value", -> 700 | expect(@callback).to.have.been.calledWithExactly "" 701 | 702 | it "should not hide the modal", -> 703 | expect(@hidden).not.to.have.been.called 704 | 705 | describe "when entering a value in the text input", -> 706 | beforeEach -> 707 | @dialog.find(".bootbox-input").val "Test input" 708 | 709 | describe "when dismissing the dialog by clicking OK", -> 710 | beforeEach -> 711 | @dialog.find(".btn-primary").trigger "click" 712 | 713 | it "should invoke the callback", -> 714 | expect(@callback).to.have.been.called 715 | 716 | it "with the correct value", -> 717 | expect(@callback).to.have.been.calledWithExactly "Test input" 718 | 719 | it "should not hide the modal", -> 720 | expect(@hidden).not.to.have.been.called 721 | 722 | describe "when dismissing the dialog by clicking Cancel", -> 723 | beforeEach -> 724 | @dialog.find(".btn-default").trigger "click" 725 | 726 | it "should invoke the callback", -> 727 | expect(@callback).to.have.been.called 728 | 729 | it "with the correct value", -> 730 | expect(@callback).to.have.been.calledWithExactly null 731 | 732 | it "should not hide the modal", -> 733 | expect(@hidden).not.to.have.been.called 734 | 735 | describe "when triggering the escape event", -> 736 | beforeEach -> 737 | @dialog.trigger "escape.close.bb" 738 | 739 | it "should invoke the callback", -> 740 | expect(@callback).to.have.been.called 741 | 742 | it "with the correct value", -> 743 | expect(@callback).to.have.been.calledWithExactly null 744 | 745 | it "should not hide the modal", -> 746 | expect(@hidden).not.to.have.been.called 747 | 748 | describe "when dismissing the dialog by clicking the close button", -> 749 | beforeEach -> 750 | @dialog.find(".close").trigger "click" 751 | 752 | it "should invoke the callback", -> 753 | expect(@callback).to.have.been.called 754 | 755 | it "with the correct value", -> 756 | expect(@callback).to.have.been.calledWithExactly null 757 | 758 | it "should not hide the modal", -> 759 | expect(@hidden).not.to.have.been.called 760 | 761 | describe "with a default value", -> 762 | beforeEach -> 763 | @callback = sinon.spy() 764 | 765 | @dialog = bootbox.prompt 766 | title: "What is your name?" 767 | value: "Bob" 768 | callback: @callback 769 | 770 | @hidden = sinon.spy @dialog, "modal" 771 | 772 | it "populates the input with the default value", -> 773 | expect(@dialog.find(".bootbox-input").val()).to.equal "Bob" 774 | 775 | describe "when entering no value in the text input", -> 776 | 777 | describe "when dismissing the dialog by clicking OK", -> 778 | beforeEach -> 779 | @dialog.find(".btn-primary").trigger "click" 780 | 781 | it "should invoke the callback", -> 782 | expect(@callback).to.have.been.called 783 | 784 | it "with the correct value", -> 785 | expect(@callback).to.have.been.calledWithExactly "Bob" 786 | 787 | describe "when dismissing the dialog by clicking Cancel", -> 788 | beforeEach -> 789 | @dialog.find(".btn-default").trigger "click" 790 | 791 | it "should invoke the callback", -> 792 | expect(@callback).to.have.been.called 793 | 794 | it "with the correct value", -> 795 | expect(@callback).to.have.been.calledWithExactly null 796 | 797 | describe "when entering a value in the text input", -> 798 | beforeEach -> 799 | @dialog.find(".bootbox-input").val "Alice" 800 | 801 | describe "when dismissing the dialog by clicking OK", -> 802 | beforeEach -> 803 | @dialog.find(".btn-primary").trigger "click" 804 | 805 | it "should invoke the callback", -> 806 | expect(@callback).to.have.been.called 807 | 808 | it "with the correct value", -> 809 | expect(@callback).to.have.been.calledWithExactly "Alice" 810 | 811 | describe "when dismissing the dialog by clicking Cancel", -> 812 | beforeEach -> 813 | @dialog.find(".btn-default").trigger "click" 814 | 815 | it "should invoke the callback", -> 816 | expect(@callback).to.have.been.called 817 | 818 | it "with the correct value", -> 819 | expect(@callback).to.have.been.calledWithExactly null 820 | 821 | describe "with a placeholder", -> 822 | beforeEach -> 823 | @callback = sinon.spy() 824 | 825 | @dialog = bootbox.prompt 826 | title: "What is your name?" 827 | placeholder: "e.g. Bob Smith" 828 | callback: -> true 829 | 830 | it "populates the input with the placeholder attribute", -> 831 | expect(@dialog.find(".bootbox-input").attr("placeholder")).to.equal "e.g. Bob Smith" 832 | 833 | describe "with inputType select", -> 834 | describe "without a default value", -> 835 | beforeEach -> 836 | @callback = sinon.spy() 837 | 838 | @dialog = bootbox.prompt 839 | title: "What is your IDE?" 840 | callback: @callback 841 | inputType: "select" 842 | inputOptions: [ 843 | {value: '#', text: 'Choose one'}, 844 | {value: 1, text: 'Vim'}, 845 | {value: 2, text: 'Sublime Text'}, 846 | {value: 3, text: 'WebStorm/PhpStorm'}, 847 | {value: 4, text: 'Komodo IDE'}, 848 | ] 849 | 850 | @hidden = sinon.spy @dialog, "modal" 851 | 852 | it "has correct number values in list", -> 853 | expect(@find(".bootbox-input-select option").length).to.equal 5 854 | 855 | describe "when dismissing the dialog by clicking OK", -> 856 | beforeEach -> 857 | @dialog.find(".btn-primary").trigger "click" 858 | 859 | it "should invoke the callback", -> 860 | expect(@callback).to.have.been.called 861 | 862 | it "with the correct value", -> 863 | expect(@callback).to.have.been.calledWithExactly "#" 864 | 865 | describe "when dismissing the dialog by clicking Cancel", -> 866 | beforeEach -> 867 | @dialog.find(".btn-default").trigger "click" 868 | 869 | it "should invoke the callback", -> 870 | expect(@callback).to.have.been.called 871 | 872 | it "with the correct value", -> 873 | expect(@callback).to.have.been.calledWithExactly null 874 | 875 | describe "with a default value", -> 876 | beforeEach -> 877 | @callback = sinon.spy() 878 | 879 | @dialog = bootbox.prompt 880 | title: "What is your IDE?" 881 | callback: @callback 882 | value: 1 883 | inputType: "select" 884 | inputOptions: [ 885 | {value: '#', text: 'Choose one'}, 886 | {value: 1, text: 'Vim'}, 887 | {value: 2, text: 'Sublime Text'}, 888 | {value: 3, text: 'WebStorm/PhpStorm'}, 889 | {value: 4, text: 'Komodo IDE'}, 890 | ] 891 | 892 | @hidden = sinon.spy @dialog, "modal" 893 | 894 | it "specified option is selected", -> 895 | expect(@dialog.find(".bootbox-input-select").val()).to.equal "1" 896 | 897 | describe "when dismissing the dialog by clicking OK", -> 898 | beforeEach -> 899 | @dialog.find(".btn-primary").trigger "click" 900 | 901 | it "should invoke the callback", -> 902 | expect(@callback).to.have.been.called 903 | 904 | it "with the correct value", -> 905 | expect(@callback).to.have.been.calledWithExactly "1" 906 | 907 | describe "when dismissing the dialog by clicking Cancel", -> 908 | beforeEach -> 909 | @dialog.find(".btn-default").trigger "click" 910 | 911 | it "should invoke the callback", -> 912 | expect(@callback).to.have.been.called 913 | 914 | it "with the correct value", -> 915 | expect(@callback).to.have.been.calledWithExactly null 916 | 917 | describe "when changing the selected option and dismissing the dialog by clicking OK", -> 918 | beforeEach -> 919 | @dialog.find(".bootbox-input-select").val(3) 920 | @dialog.find(".btn-primary").trigger "click" 921 | 922 | it "should invoke the callback", -> 923 | expect(@callback).to.have.been.called 924 | 925 | it "with the correct value", -> 926 | expect(@callback).to.have.been.calledWithExactly "3" 927 | 928 | describe "with inputType email", -> 929 | describe "without a default value", -> 930 | beforeEach -> 931 | @callback = sinon.spy() 932 | 933 | @dialog = bootbox.prompt 934 | title: "What is your email?" 935 | inputType: "email" 936 | callback: @callback 937 | 938 | @hidden = sinon.spy @dialog, "modal" 939 | 940 | describe "when dismissing the dialog by clicking OK", -> 941 | beforeEach -> 942 | @dialog.find(".btn-primary").trigger "click" 943 | 944 | it "should invoke the callback", -> 945 | expect(@callback).to.have.been.called 946 | 947 | it "with the correct value", -> 948 | expect(@callback).to.have.been.calledWithExactly "" 949 | 950 | it "should hide the modal", -> 951 | expect(@hidden).to.have.been.calledWithExactly "hide" 952 | 953 | describe "when submitting the form", -> 954 | beforeEach -> 955 | @dialog.find(".bootbox-form").trigger "submit" 956 | 957 | it "invokes the callback with the correct value", -> 958 | expect(@callback).to.have.been.calledWithExactly "" 959 | 960 | it "should hide the modal", -> 961 | expect(@hidden).to.have.been.calledWithExactly "hide" 962 | 963 | describe "when entering a value in the email input", -> 964 | beforeEach -> 965 | @dialog.find(".bootbox-input-email").val "john@smith.com" 966 | 967 | describe "when dismissing the dialog by clicking OK", -> 968 | beforeEach -> 969 | @dialog.find(".btn-primary").trigger "click" 970 | 971 | it "should invoke the callback", -> 972 | expect(@callback).to.have.been.called 973 | 974 | it "with the correct value", -> 975 | expect(@callback).to.have.been.calledWithExactly "john@smith.com" 976 | 977 | describe "when dismissing the dialog by clicking Cancel", -> 978 | beforeEach -> 979 | @dialog.find(".btn-default").trigger "click" 980 | 981 | it "should invoke the callback", -> 982 | expect(@callback).to.have.been.called 983 | 984 | it "with the correct value", -> 985 | expect(@callback).to.have.been.calledWithExactly null 986 | 987 | describe "with a default value", -> 988 | beforeEach -> 989 | @callback = sinon.spy() 990 | 991 | @dialog = bootbox.prompt 992 | title: "What is your email?" 993 | inputType: "email" 994 | value: "john@smith.com" 995 | callback: @callback 996 | 997 | @hidden = sinon.spy @dialog, "modal" 998 | 999 | describe "when dismissing the dialog by clicking OK", -> 1000 | beforeEach -> 1001 | @dialog.find(".btn-primary").trigger "click" 1002 | 1003 | it "should invoke the callback", -> 1004 | expect(@callback).to.have.been.called 1005 | 1006 | it "with the correct value", -> 1007 | expect(@callback).to.have.been.calledWithExactly "john@smith.com" 1008 | 1009 | it "should hide the modal", -> 1010 | expect(@hidden).to.have.been.calledWithExactly "hide" 1011 | 1012 | describe "when submitting the form", -> 1013 | beforeEach -> 1014 | @dialog.find(".bootbox-form").trigger "submit" 1015 | 1016 | it "invokes the callback with the correct value", -> 1017 | expect(@callback).to.have.been.calledWithExactly "john@smith.com" 1018 | 1019 | it "should hide the modal", -> 1020 | expect(@hidden).to.have.been.calledWithExactly "hide" 1021 | 1022 | describe "when changing a value in the email input", -> 1023 | beforeEach -> 1024 | @dialog.find(".bootbox-input-email").val "smith@john.com" 1025 | 1026 | describe "when dismissing the dialog by clicking OK", -> 1027 | beforeEach -> 1028 | @dialog.find(".btn-primary").trigger "click" 1029 | 1030 | it "should invoke the callback", -> 1031 | expect(@callback).to.have.been.called 1032 | 1033 | it "with the correct value", -> 1034 | expect(@callback).to.have.been.calledWithExactly "smith@john.com" 1035 | 1036 | describe "when dismissing the dialog by clicking Cancel", -> 1037 | beforeEach -> 1038 | @dialog.find(".btn-default").trigger "click" 1039 | 1040 | it "should invoke the callback", -> 1041 | expect(@callback).to.have.been.called 1042 | 1043 | it "with the correct value", -> 1044 | expect(@callback).to.have.been.calledWithExactly null 1045 | 1046 | describe "with input type checkbox", -> 1047 | describe "without a default value", -> 1048 | beforeEach -> 1049 | @callback = sinon.spy() 1050 | 1051 | @dialog = bootbox.prompt 1052 | title: "What is your IDE?" 1053 | inputType: 'checkbox' 1054 | inputOptions: [ 1055 | {value: 1, text: 'Vim'}, 1056 | {value: 2, text: 'Sublime Text'}, 1057 | {value: 3, text: 'WebStorm/PhpStorm'}, 1058 | {value: 4, text: 'Komodo IDE'}, 1059 | ] 1060 | callback: @callback 1061 | 1062 | @hidden = sinon.spy @dialog, "modal" 1063 | 1064 | describe "when dismissing the dialog by clicking OK", -> 1065 | beforeEach -> 1066 | @dialog.find(".btn-primary").trigger "click" 1067 | 1068 | it "should invoke the callback", -> 1069 | expect(@callback).to.have.been.called 1070 | 1071 | it "with an undefined value", -> 1072 | expect(@callback).to.have.been.calledWithExactly [] 1073 | 1074 | it "should hide the modal", -> 1075 | expect(@hidden).to.have.been.calledWithExactly "hide" 1076 | 1077 | describe "when dismissing the dialog by clicking Cancel", -> 1078 | beforeEach -> 1079 | @dialog.find(".btn-default").trigger "click" 1080 | 1081 | it "should invoke the callback", -> 1082 | expect(@callback).to.have.been.called 1083 | 1084 | it "with the correct value", -> 1085 | expect(@callback).to.have.been.calledWithExactly null 1086 | 1087 | describe "with default value", -> 1088 | describe "one value checked", -> 1089 | beforeEach -> 1090 | @callback = sinon.spy() 1091 | 1092 | @dialog = bootbox.prompt 1093 | title: "What is your IDE?" 1094 | callback: @callback 1095 | value: 2 1096 | inputType: "checkbox" 1097 | inputOptions: [ 1098 | {value: 1, text: 'Vim'}, 1099 | {value: 2, text: 'Sublime Text'}, 1100 | {value: 3, text: 'WebStorm/PhpStorm'}, 1101 | {value: 4, text: 'Komodo IDE'}, 1102 | ] 1103 | 1104 | @hidden = sinon.spy @dialog, "modal" 1105 | 1106 | it "specified checkbox is checked", -> 1107 | expect(@dialog.find("input:checkbox:checked").val()).to.equal "2" 1108 | 1109 | describe "when dismissing the dialog by clicking OK", -> 1110 | beforeEach -> 1111 | @dialog.find(".btn-primary").trigger "click" 1112 | 1113 | it "should invoke the callback", -> 1114 | expect(@callback).to.have.been.called 1115 | 1116 | it "with the correct value", -> 1117 | expect(@callback).to.have.been.calledWithExactly ["2"] 1118 | 1119 | describe "when dismissing the dialog by clicking Cancel", -> 1120 | beforeEach -> 1121 | @dialog.find(".btn-default").trigger "click" 1122 | 1123 | it "should invoke the callback", -> 1124 | expect(@callback).to.have.been.called 1125 | 1126 | it "with the correct value", -> 1127 | expect(@callback).to.have.been.calledWithExactly null 1128 | 1129 | describe "when changing the checked option and dismissing the dialog by clicking Cancel", -> 1130 | beforeEach -> 1131 | @dialog.find("input:checkbox:checked").prop('checked', false) 1132 | @dialog.find("input:checkbox[value=3]").prop('checked', true) 1133 | @dialog.find(".btn-default").trigger "click" 1134 | 1135 | it "should invoke the callback", -> 1136 | expect(@callback).to.have.been.called 1137 | 1138 | it "with the correct value", -> 1139 | expect(@callback).to.have.been.calledWithExactly null 1140 | 1141 | describe "when changing the selected option and dismissing the dialog by clicking OK", -> 1142 | beforeEach -> 1143 | @dialog.find("input:checkbox:checked").prop('checked', false) 1144 | @dialog.find("input:checkbox[value=3]").prop('checked', true) 1145 | @dialog.find(".btn-primary").trigger "click" 1146 | 1147 | it "should invoke the callback", -> 1148 | expect(@callback).to.have.been.called 1149 | 1150 | it "with the correct value", -> 1151 | expect(@callback).to.have.been.calledWithExactly ["3"] 1152 | 1153 | describe "multiple value checked", -> 1154 | beforeEach -> 1155 | @callback = sinon.spy() 1156 | 1157 | @dialog = bootbox.prompt 1158 | title: "What is your IDE?" 1159 | callback: @callback 1160 | value: [2, 3] 1161 | inputType: "checkbox" 1162 | inputOptions: [ 1163 | {value: 1, text: 'Vim'} 1164 | {value: 2, text: 'Sublime Text'} 1165 | {value: 3, text: 'WebStorm/PhpStorm'} 1166 | {value: 4, text: 'Komodo IDE'} 1167 | ] 1168 | 1169 | @hidden = sinon.spy @dialog, "modal" 1170 | 1171 | it "specified checkboxes are checked", -> 1172 | checked = [] 1173 | 1174 | @dialog.find("input:checkbox:checked").each (foo, bar) => 1175 | checked.push $(bar).val() 1176 | 1177 | expect(checked).to.deep.equal ["2", "3"] 1178 | 1179 | describe "when dismissing the dialog by clicking OK", -> 1180 | beforeEach -> 1181 | @dialog.find(".btn-primary").trigger "click" 1182 | 1183 | it "should invoke the callback", -> 1184 | expect(@callback).to.have.been.called 1185 | 1186 | it "with the correct value", -> 1187 | expect(@callback).to.have.been.calledWithExactly ["2", "3"] 1188 | 1189 | describe "when dismissing the dialog by clicking Cancel", -> 1190 | beforeEach -> 1191 | @dialog.find(".btn-default").trigger "click" 1192 | 1193 | it "should invoke the callback", -> 1194 | expect(@callback).to.have.been.called 1195 | 1196 | it "with the correct value", -> 1197 | expect(@callback).to.have.been.calledWithExactly null 1198 | 1199 | describe "when changing the checked options and dismissing the dialog by clicking Cancel", -> 1200 | beforeEach -> 1201 | @dialog.find("input:checkbox:checked").prop('checked', false) 1202 | @dialog.find("input:checkbox[value=1]").prop('checked', true) 1203 | @dialog.find("input:checkbox[value=4]").prop('checked', true) 1204 | @dialog.find(".btn-default").trigger "click" 1205 | 1206 | it "should invoke the callback", -> 1207 | expect(@callback).to.have.been.called 1208 | 1209 | it "with the correct value", -> 1210 | expect(@callback).to.have.been.calledWithExactly null 1211 | 1212 | describe "when changing the checked options and dismissing the dialog by clicking OK", -> 1213 | beforeEach -> 1214 | @dialog.find("input:checkbox:checked").prop('checked', false) 1215 | @dialog.find("input:checkbox[value=1]").prop('checked', true) 1216 | @dialog.find("input:checkbox[value=4]").prop('checked', true) 1217 | @dialog.find(".btn-primary").trigger "click" 1218 | 1219 | it "should invoke the callback", -> 1220 | expect(@callback).to.have.been.called 1221 | 1222 | it "with the correct value", -> 1223 | expect(@callback).to.have.been.calledWithExactly ["1", "4"] 1224 | 1225 | --------------------------------------------------------------------------------