├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE.md ├── README.md ├── bootbox.js ├── bower.json ├── build └── calculate-size ├── composer.json ├── dist ├── LICENSE.md ├── bootbox.all.js ├── bootbox.all.min.js ├── bootbox.js ├── bootbox.locales.js ├── bootbox.locales.min.js └── bootbox.min.js ├── header.txt ├── karma-base.conf.js ├── karma.conf.js ├── karma.conf.legacy.js ├── locales ├── ar.js ├── az.js ├── bg_BG.js ├── cs.js ├── da.js ├── de.js ├── el.js ├── en.js ├── es.js ├── et.js ├── eu.js ├── fa.js ├── fi.js ├── fr.js ├── he.js ├── hr.js ├── hu.js ├── id.js ├── it.js ├── ja.js ├── ka.js ├── ko.js ├── lt.js ├── lv.js ├── nl.js ├── no.js ├── pl.js ├── pt.js ├── pt_BR.js ├── ru.js ├── sk.js ├── sl.js ├── sq.js ├── sv.js ├── sw.js ├── ta.js ├── th.js ├── tr.js ├── uk.js ├── vi.js ├── zh_CN.js └── zh_TW.js ├── package-lock.json ├── package.json ├── templates ├── umd-footer.txt ├── umd-header-locales.txt └── umd-header.txt └── tests ├── alert.test.js ├── bootbox.test.js ├── confirm.test.js ├── defaults.test.js ├── dialog.test.js ├── locales.test.js ├── prompt.test.js └── vendor ├── bootstrap-4.6.1.bundle.min.js ├── bootstrap-5.1.3.bundle.min.js └── jquery-3.3.1.slim.min.js /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Run Karma Test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [16.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | 25 | - name: Build 26 | run: | 27 | npm install 28 | npm test 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swo 2 | *.swp 3 | node_modules 4 | npm-debug.log 5 | .idea 6 | tests/reports 7 | tests/coverage 8 | tests/unit-test-reports 9 | build/size.csv 10 | composer.lock 11 | .vs 12 | .vscode 13 | dist/bootbox.zip 14 | *.tgz 15 | -------------------------------------------------------------------------------- /.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": "single", 10 | "browser": true, 11 | "node": true, 12 | "esversion": 6, 13 | "globals": { 14 | "beforeEach": true, 15 | "afterEach": true, 16 | "describe": true, 17 | "it": true, 18 | "define": true, 19 | "module": true, 20 | "require": true, 21 | "expect": true, 22 | "bootbox": true, 23 | "sinon": true, 24 | "$": false 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.swo 2 | *.swp 3 | node_modules 4 | npm-debug.log 5 | .idea 6 | .vscode/* 7 | .vs/* 8 | tests/* 9 | build/* 10 | dist/bootbox.zip 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 6.0.4 (Latest Release) 2 | 3 | - Updates `step` to allow setting value for date inputs 4 | 5 | ### 6.0.3 6 | 7 | -Updates `min` and `max` validation for the `number` input type to allow min and max to be equal 8 | 9 | ### 6.0.2 10 | 11 | - Updates `OK` text for Japanese locale 12 | 13 | ### 6.0.1 14 | 15 | - Updates prompt callback to return false from callback if `inputType` is 'checkbox', `required` option is `true`, and no checkbox has been checked 16 | - Updates unit tests 17 | - Adds tests for required option 18 | - Cleanup up pattern tests 19 | - Adds HTML output runner 20 | 21 | ### 6.0.0 22 | 23 | - Removes various IE polyfills 24 | - Replaces `var` with `let` 25 | - JSDoc cleanup 26 | - Adds code to handle cases when click starts on the modal body and ends on the backdrop and `backdrop` is set to `true` 27 | - `bootbox.locale.js` and `bootbox.all.js` are now generated files and will be found in the `/dist` directory 28 | - Simplify locale file structure 29 | - Changed a few locale identifiers to match IANA specifications: 30 | - `bg_BG` -> `bg-BG` 31 | - `pt-br` -> `pt-BR` 32 | - `zh_CN` -> `zh-CN` 33 | - `zh_TW` -> `zh-CW` 34 | 35 | ### 5.5.2 36 | 37 | - Adds Vietnamese locale 38 | 39 | ### 5.5.0 40 | 41 | - Adds `reusable` option, which allows an instance of a Bootbox modal to be reused. 42 | 43 | ### 5.4.1 44 | 45 | - Updates to how backdrop is handled. Fixes #766. 46 | 47 | ### 5.4.0 48 | 49 | - Adds function hooks for `onShow`, `onShown`, `onHide`, and `onHidden` events, which map to their Bootstrap modal equivalents. 50 | 51 | ### 5.3.4 52 | 53 | - Removes `':first'` selector from default button binding 54 | 55 | ### 5.3.3 56 | 57 | - Fixes incorrect value validation for the `step` option when setting `inputType` to `number` for a prompt. 58 | 59 | ### 5.3.2 60 | 61 | - Adds Georgian (ka) locale. 62 | 63 | ### 5.3.0 64 | 65 | - Moves development (unminified) versions of Bootbox files back to repository root (to simplify getting CDNjs updated). 66 | 67 | ### 5.2.0 68 | 69 | - Modifies dialog to only process button callback if it has been defined; see https://github.com/makeusabrew/bootbox/issues/705 70 | 71 | ### 5.1.3 72 | 73 | - Adds Swahili (sw) locale. 74 | 75 | ### 5.1.2 76 | 77 | - Adds `bootbox.all.js` to `src` directory as a temporary work-around for incorrectly-built concatenated file 78 | 79 | ### 5.1.1 80 | 81 | - Adds `rows` as a prompt option when setting `inputType` to `textarea`. 82 | 83 | ### 5.1.0 84 | 85 | - Adds `scrollable` option, which enables the scrollable modal content added in Bootstrap 4.3 86 | - Adds `extra-large` as a size option 87 | - Adds aliased/alternative keys for all size options: `sm`, `lg`, `xl` 88 | 89 | ### 5.0.1 90 | 91 | - Adds Tamil (ta) locale 92 | 93 | ### 5.0.0 94 | 95 | - Updates Bootbox to be compatible with both Bootstrap 4 and Bootstrap 3. 96 | - Pulls button locale options to separate file 97 | - Corrects Russion locale 98 | - Changes default button trigger to target the button with the `bootbox-accept` class; this corrects instances where no button has the `btn-primary` class. 99 | - Various bugfixes 100 | 101 | #### Prompt 102 | 103 | - Refactors prompt function to use the same dialog factory as alert and confirm 104 | - Adds new input types for prompt: 105 | - `radio` 106 | - `range` 107 | - Adds prompt input constraints for `min`, `max`, `step`, `maxlength`, `pattern`, and `required` 108 | - Adds `pattern` option for prompt inputs 109 | - Allows `message` option for prompt 110 | - Allows `multiple` option for prompt when used with `inputType` set to `select` 111 | 112 | #### Dialog options 113 | 114 | - Adds `locale` option - allows locale to be set on a dialog-by-dialog basis 115 | - Adds `swapButtonOrder` option to allow reversing the default button order 116 | - Adds `centerVertical` option - adds support for vertically-centered dialogs (requires Bootstrap 4) 117 | 118 | ## 4.4.0 119 | 120 | * Allow `backdrop` options of `true` and `false` to dismiss modals 121 | * Pass dialog as `this` value in callbacks 122 | * Bootstrap 3.3.2 compatibility 123 | * jQuery 1.11.2 compatibility 124 | * Add support for `maxlength` prompt input attribute 125 | * Gracefully detect lack of Bootstrap library rather than crashing 126 | * Expose `addLocale` and `removeLocale` for custom locale settings 127 | * Expose `setLocale` helper to select a locale rather than using `setDefaults("locale", ...)` 128 | * Add Hungarian locale 129 | * Add Croatian locale 130 | * Add Bulgarian locale 131 | * Add Thai locale 132 | * Add Persian locale 133 | * Add Albanian locale 134 | 135 | ### 4.3.0 136 | 137 | * Add `size` option (`large`, `small`) 138 | * Stop propagation on form submit 139 | * Return bootbox object from `hideAll` 140 | * Add Portuguese locale 141 | * Add Czech locale 142 | * Add Greek locale 143 | * Add Estonian locale 144 | * Add Indonesian locale 145 | * Add Japanese locale 146 | 147 | ### 4.2.0 148 | 149 | * Add Swedish locale 150 | * Add Latvian locale 151 | * Add Turkish locale 152 | * Add Hebrew locale 153 | * Add password input type 154 | * Add textarea input type 155 | * Add date input type 156 | * Add time input type 157 | * Add number input type 158 | * Support DOM selectors for container argument 159 | * UMD support 160 | * Better support on mobile devices 161 | 162 | ### 4.1.0 163 | 164 | * Add support for placeholder attribute in prompts 165 | * Add select, email and checkbox types for prompts (thanks [@tarlepp](https://github.com/tarlepp)) 166 | * Add Norwegian locale 167 | * Allow setDefaults to take two key/val arguments 168 | * Add unique classes for main dialog methods 169 | * Create bower package 170 | 171 | ### 4.0.0 172 | 173 | * Bootstrap 3.0.0 compatibility 174 | * Complete rewrite (and new public API) 175 | * Use strict mode 176 | * Add close buttons to wrapper methods (GH-92) 177 | * Allow dialog titles to be specified (GH-51, GH-112) 178 | * Allow optional extra class on dialog wrapper (GH-116) 179 | * Fix ```backdrop: true``` not firing close handler (GH-77) 180 | * Replace various configuration methods with one ```setDefaults``` 181 | 182 | ## 3.3.0 183 | 184 | * Add Polish translation (GH-93) 185 | * Add Danish translation (GH-96) 186 | * Pass event object to custom callbacks (GH-103) 187 | * Add Chinese (Taiwan / China) translations (GH-106) 188 | * Make prompt input block-level (GH-111) 189 | * Add link: true option to prevent btn class from being applied (GH-114) 190 | * Prevent child elements triggering hidden callback (GH-115) 191 | * Replace Phing with Grunt 192 | * Replace Closure compiler with UglifyJS 193 | 194 | ### 3.2.0 195 | 196 | * ensure ```onEscape``` handlers return callback values properly (GH-91) 197 | * ensure clicking close button invokes onEscape handler if present 198 | 199 | ### 3.1.0 200 | 201 | * ensure ```confirm``` and ```prompt``` methods return callback values properly (GH-90) 202 | * address various jshint warnings (GH-79) 203 | * add ```setBtnClasses``` method for custom standard button classes (GH-87) 204 | 205 | ### 3.0.0 206 | 207 | * bump Bootstrap dependency to 2.2.2 208 | * bump jQuery dependency to 1.8.3 209 | * ensure callbacks are always invoked even if dialogs are dismissed with escape key (GH-49) 210 | * fix button positions with Bootstrap 2.2.2 (GH-58) 211 | * stop multiple dialogs crashing browsers (GH-60, GH-64) 212 | * ensure ```shown``` event is fired properly even when animation is disabled (GH-69) 213 | * use ```.on``` instead of ```.bind``` 214 | * commentify code a bit more 215 | 216 | ## 2.5.1 217 | 218 | **This was the last version of the library to support Bootstrap 2.0.x** 219 | 220 | * ensure bootbox object is explicitly added to window object for minfier visibility 221 | 222 | ### 2.5.0 223 | 224 | * add option to specify proper href attributes for buttons instead of callbacks (@StevePotter) 225 | * add option to override per-modal classes (@ciaranj) 226 | 227 | ### 2.4.2 228 | 229 | * revert ```backdrop``` default value to 'static' instead of ```true``` to prevent background clicks dismissing dialogs (GH-55) 230 | 231 | ### 2.4.1 232 | 233 | * fix ```backdrop``` when supplied as an argument to ```bootbox.dialog``` 234 | * fix incorrect README version 235 | 236 | ### 2.4.0 237 | 238 | * add ```bootbox.backdrop(bool)``` method (@gucki) 239 | * add default parameter option to ```bootbox.prompt``` (@pzgz) 240 | 241 | ### 2.3.3 242 | 243 | * add inline ```overflow: hidden``` CSS property (GH-46) 244 | * move license info to separate hosted file to reduce file size 245 | 246 | ### 2.3.2 247 | 248 | * Change button href attributes to ```javascript:;``` (@joshnesbitt) 249 | * Explicitly ```window.jQuery``` through to ```Bootbox``` object (@nuegon) 250 | 251 | 252 | ### 2.3.1 253 | 254 | * Ensure bootbox.prompt() gives focus to input, disable input autocomplete 255 | 256 | ### 2.3.0 257 | 258 | * Added bootbox.prompt() to mimic native prompt() method 259 | * Added Russian locale (#27) 260 | 261 | ### 2.2.0 262 | 263 | * Allowed button callbacks to explicitly return false to prevent dialog from closing (thanks @benoit-ponsero) 264 | * Added version number to header comments (#26) 265 | 266 | ### 2.1.2 267 | 268 | * Added close button to re-scoped click handler (thanks @SeanMcGee and @kentbrew) 269 | 270 | ### 2.1.1 271 | 272 | * Fixed incorrect button click handler selector (thanks FGRibreau) 273 | 274 | ### 2.1.0 275 | 276 | * Added support for Bootstrap's Glyphicons via the ```icon``` option 277 | * Added inline license information into bootbox.js and bootbox.min.js 278 | * Tidied up source a little 279 | 280 | ### 2.0.1 281 | 282 | * Removed dummy Google Closure Compiler method from minified library (thanks j0k3r!) 283 | 284 | ### 2.0.0 285 | 286 | * Updated Bootstrap dependency from 1.4 to 2.0 287 | * Class definitions now require ```btn-``` prefix as per Bootstrap 2.0 288 | * Added Brazilian locale 289 | * Added ```animate``` dialog option 290 | * Added ```bootbox.animate(bool)``` option to set default animation preference 291 | * Animated dialogs now rely on ```bootstrap-transitions.js``` as required by Bootstrap 2.0 292 | 293 | ## 1.1.2 294 | 295 | * Added licensing information to README 296 | 297 | #### 1.1.1 298 | * Updated german locale 299 | 300 | #### 1.1.0 301 | * Secondary option of two-button dialog no longer has 'danger' class 302 | * New bootbox.modal() method for generic non-dialog popups 303 | * Allow jQuery objects to be passed as main dialog argument 304 | -------------------------------------------------------------------------------- /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 ```grunt``` 9 | * If you've added new functionality, **please** include tests which validate its behaviour -- **this includes pull requests which _only_ add new locales!** 10 | 11 | ## Submitting bug reports 12 | 13 | * Where at all possible, please try and provide a link to a jsfiddle.net example or similar 14 | * Please detail the affected browser(s) and operating system(s) 15 | * Please be sure to state which version of Bootbox, jQuery **and** Bootstrap you're using 16 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 'use strict'; 3 | grunt.initConfig({ 4 | concat: { 5 | copy : { 6 | src: ['bootbox.js'], 7 | dest: 'dist/bootbox.js' 8 | }, 9 | 10 | locales: { 11 | src: ['templates/umd-header-locales.txt', 'locales/**/*.js', 'templates/umd-footer.txt'], 12 | dest: 'dist/bootbox.locales.js' 13 | }, 14 | 15 | all : { 16 | src: ['bootbox.js', 'dist/bootbox.locales.js'], 17 | dest: 'dist/bootbox.all.js' 18 | }, 19 | 20 | license : { 21 | src: ['LICENSE.md'], 22 | dest: 'dist/LICENSE.md' 23 | }, 24 | }, 25 | 26 | jsbeautifier : { 27 | src : ['dist/bootbox.locales.js','dist/bootbox.all.js'], 28 | options:{ 29 | js: { 30 | indentSize: 2 31 | } 32 | } 33 | }, 34 | 35 | uglify: { 36 | options: { 37 | compress: true, 38 | mangle: true, 39 | banner: grunt.file.read('header.txt'), 40 | output:{ 41 | quote_style: 3 42 | } 43 | }, 44 | my_target: { 45 | files: { 46 | 'dist/bootbox.min.js': ['dist/bootbox.js'], 47 | 'dist/bootbox.locales.min.js': ['dist/bootbox.locales.js'], 48 | 'dist/bootbox.all.min.js': ['dist/bootbox.all.js'] 49 | } 50 | } 51 | }, 52 | 53 | jshint: { 54 | options: { 55 | jshintrc: '.jshintrc', 56 | force: true 57 | }, 58 | all: ['dist/bootbox.js', 'dist/bootbox.locales.js'] 59 | }, 60 | 61 | karma: { 62 | current: { 63 | configFile: 'karma.conf.js' 64 | }, 65 | legacy: { 66 | configFile: 'karma.conf.legacy.js' 67 | } 68 | }, 69 | }); 70 | 71 | grunt.loadNpmTasks('grunt-contrib-concat'); 72 | grunt.loadNpmTasks("grunt-jsbeautifier"); 73 | grunt.loadNpmTasks('grunt-contrib-uglify'); 74 | grunt.loadNpmTasks('grunt-contrib-jshint'); 75 | grunt.loadNpmTasks('grunt-karma'); 76 | 77 | grunt.registerTask('default', ['concat', 'jsbeautifier', 'uglify', 'jshint', 'karma']); 78 | }; -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | (The MIT License) 4 | 5 | Copyright (C) 2011-2025 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bootbox - 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 | ## Contributing 7 | 8 | Please see the [CONTRIBUTING](https://github.com/bootboxjs/bootbox/blob/master/CONTRIBUTING.md) file for guidelines. 9 | 10 | ## Running Tests 11 | 12 | Tests are run using [Karma](http://karma-runner.github.io/0.8/index.html) using the Mocha test adapter. To run the tests yourself, simply run 13 | 14 | ``` 15 | npm install 16 | ``` 17 | 18 | within the project followed by 19 | 20 | ``` 21 | npm test 22 | ``` 23 | 24 | Tests are run against the generated files contained in the `/dist` directory - regenerate those files and run the Karma tests by simply running Grunt: 25 | 26 | ``` 27 | grunt 28 | ``` 29 | 30 | When submitting pull requests, ensure your tests pass. **Pull-requests with failing tests will be rejected.** See the 31 | [CONTRIBUTING](https://github.com/bootboxjs/bootbox/blob/master/CONTRIBUTING.md) file for more information. 32 | 33 | ## A note on Bootstrap dependencies 34 | 35 | Bootbox **6.0.0** is the first release to support Bootstrap 5.0.0. 36 | 37 | Bootbox **5.0.0** is the first release to support Bootstrap 4.0.0. 38 | 39 | Bootbox **4.0.0** is the first release to support Bootstrap 3.0.0. 40 | 41 | Bootbox **3.3.0** is the *last* release to support Bootstrap 2.2.x. 42 | 43 | Much more dependency information can be found [on the Bootbox website](http://bootboxjs.com/getting-started.html#bootbox-dependencies). 44 | 45 | ## 6.0.4 (Latest Release) 46 | 47 | - Updates `step` to allow setting value for date inputs 48 | 49 | For a full list of releases and changes please see [the changelog](https://github.com/bootboxjs/bootbox/blob/master/CHANGELOG.md). 50 | 51 | ## License 52 | 53 | (The MIT License) 54 | 55 | Copyright (C) 2011-2025 by Nick Payne 56 | 57 | Permission is hereby granted, free of charge, to any person obtaining a copy 58 | of this software and associated documentation files (the "Software"), to deal 59 | in the Software without restriction, including without limitation the rights 60 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 61 | copies of the Software, and to permit persons to whom the Software is 62 | furnished to do so, subject to the following conditions: 63 | 64 | The above copyright notice and this permission notice shall be included in 65 | all copies or substantial portions of the Software. 66 | 67 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 68 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 69 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 70 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 71 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 72 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 73 | THE SOFTWARE 74 | -------------------------------------------------------------------------------- /bootbox.js: -------------------------------------------------------------------------------- 1 | /*! @preserve 2 | * bootbox.js 3 | * version: 6.0.4 4 | * author: Nick Payne 5 | * license: MIT 6 | * http://bootboxjs.com/ 7 | */ 8 | (function (root, factory) { 9 | 'use strict'; 10 | if (typeof define === 'function' && define.amd) { 11 | // AMD 12 | define(['jquery'], factory); 13 | } else if (typeof exports === 'object') { 14 | // Node, CommonJS-like 15 | module.exports = factory(require('jquery')); 16 | } else { 17 | // Browser globals (root is window) 18 | root.bootbox = factory(root.jQuery); 19 | } 20 | }(this, function init($, undefined) { 21 | 'use strict'; 22 | 23 | let exports = {}; 24 | 25 | let VERSION = '6.0.4'; 26 | exports.VERSION = VERSION; 27 | 28 | let locales = { 29 | 'en' : { 30 | OK : 'OK', 31 | CANCEL : 'Cancel', 32 | CONFIRM : 'OK' 33 | } 34 | }; 35 | 36 | let templates = { 37 | dialog: '', 38 | header: '', 39 | footer: '', 40 | closeButton: '', 41 | form: '
', 42 | button: '', 43 | option: '', 44 | promptMessage: '
', 45 | inputs: { 46 | text: '', 47 | textarea: '', 48 | email: '', 49 | select: '', 50 | checkbox: '
', 51 | radio: '
', 52 | date: '', 53 | time: '', 54 | number: '', 55 | password: '', 56 | range: '' 57 | } 58 | }; 59 | 60 | 61 | let defaults = { 62 | // Default language used when generating buttons for alert, confirm, and prompt dialogs 63 | locale: 'en', 64 | // Show backdrop or not. Default to static so user has to interact with dialog 65 | backdrop: 'static', 66 | // Animate the modal in/out 67 | animate: true, 68 | // Additional class string applied to the top level dialog 69 | className: null, 70 | // Whether or not to include a close button 71 | closeButton: true, 72 | // Show the dialog immediately by default 73 | show: true, 74 | // Dialog container 75 | container: 'body', 76 | // Default value (used by the prompt helper) 77 | value: '', 78 | // Default input type (used by the prompt helper) 79 | inputType: 'text', 80 | // Custom error message to report if prompt fails validation 81 | errorMessage: null, 82 | // Switch button order from cancel/confirm (default) to confirm/cancel 83 | swapButtonOrder: false, 84 | // Center modal vertically in page 85 | centerVertical: false, 86 | // Append "multiple" property to the select when using the "prompt" helper 87 | multiple: false, 88 | // Automatically scroll modal content when height exceeds viewport height 89 | scrollable: false, 90 | // Whether or not to destroy the modal on hide 91 | reusable: false, 92 | // The element which triggered the dialog 93 | relatedTarget: null, 94 | // The size of the modal to generate 95 | size: null, 96 | // A unique identifier for this modal 97 | id: null 98 | }; 99 | 100 | 101 | // PUBLIC FUNCTIONS 102 | // ************************************************************************************************************* 103 | 104 | /** 105 | * Return all currently registered locales, or a specific locale if "name" is defined 106 | * @param {string} [name] 107 | * @returns {(Object|Object[])} An array of the available locale objects, or a single locale object if {name} is not null 108 | */ 109 | exports.locales = function (name) { 110 | return name ? locales[name] : locales; 111 | }; 112 | 113 | 114 | /** 115 | * Register localized strings for the OK, CONFIRM, and CANCEL buttons 116 | * @param {string} name - The key used to identify the new locale in the locales array 117 | * @param {Object} values - An object containing the localized string for each of the OK, CANCEL, and CONFIRM properties of a locale 118 | * @returns The updated bootbox object 119 | */ 120 | exports.addLocale = function (name, values) { 121 | $.each(['OK', 'CANCEL', 'CONFIRM'], function (_, v) { 122 | if (!values[v]) { 123 | throw new Error('Please supply a translation for "' + v + '"'); 124 | } 125 | }); 126 | 127 | locales[name] = { 128 | OK: values.OK, 129 | CANCEL: values.CANCEL, 130 | CONFIRM: values.CONFIRM 131 | }; 132 | 133 | return exports; 134 | }; 135 | 136 | 137 | /** 138 | * Remove a previously-registered locale 139 | * @param {string} name - The key identifying the locale to remove 140 | * @returns The updated bootbox object 141 | */ 142 | exports.removeLocale = function (name) { 143 | if (name !== 'en') { 144 | delete locales[name]; 145 | } 146 | else { 147 | throw new Error('"en" is used as the default and fallback locale and cannot be removed.'); 148 | } 149 | 150 | return exports; 151 | }; 152 | 153 | 154 | /** 155 | * Set the default locale 156 | * @param {string} name - The key identifying the locale to set as the default locale for all future bootbox calls 157 | * @returns The updated bootbox object 158 | */ 159 | exports.setLocale = function (name) { 160 | return exports.setDefaults('locale', name); 161 | }; 162 | 163 | 164 | /** 165 | * Override default value(s) of Bootbox. 166 | * @returns The updated bootbox object 167 | */ 168 | exports.setDefaults = function () { 169 | let values = {}; 170 | 171 | if (arguments.length === 2) { 172 | // Allow passing of single key/value... 173 | values[arguments[0]] = arguments[1]; 174 | } else { 175 | // ... and as an object too 176 | values = arguments[0]; 177 | } 178 | 179 | $.extend(defaults, values); 180 | 181 | return exports; 182 | }; 183 | 184 | 185 | /** 186 | * Hides all currently active Bootbox modals 187 | * @returns The current bootbox object 188 | */ 189 | exports.hideAll = function () { 190 | $('.bootbox').modal('hide'); 191 | 192 | return exports; 193 | }; 194 | 195 | 196 | /** 197 | * Allows the base init() function to be overridden 198 | * @param {function} _$ - A function to be called when the bootbox instance is created 199 | * @returns The current bootbox object 200 | */ 201 | exports.init = function (_$) { 202 | return init(_$ || $); 203 | }; 204 | 205 | 206 | // CORE HELPER FUNCTIONS 207 | // ************************************************************************************************************* 208 | 209 | /** 210 | * The core dialog helper function, which can be used to create any custom Bootstrap modal. 211 | * @param {Object} options - An object used to configure the various properties which define a Bootbox dialog 212 | * @returns A jQuery object upon which Bootstrap's modal function has been called 213 | */ 214 | exports.dialog = function (options) { 215 | if ($.fn.modal === undefined) { 216 | throw new Error( 217 | '"$.fn.modal" is not defined; please double check you have included the Bootstrap JavaScript library. See https://getbootstrap.com/docs/5.3/getting-started/introduction/ for more details.' 218 | ); 219 | } 220 | 221 | options = sanitize(options); 222 | 223 | if ($.fn.modal.Constructor.VERSION) { 224 | options.fullBootstrapVersion = $.fn.modal.Constructor.VERSION; 225 | let i = options.fullBootstrapVersion.indexOf('.'); 226 | options.bootstrap = options.fullBootstrapVersion.substring(0, i); 227 | } 228 | else { 229 | // Assuming version 2.3.2, as that was the last "supported" 2.x version 230 | options.bootstrap = '2'; 231 | options.fullBootstrapVersion = '2.3.2'; 232 | console.warn('Bootbox will *mostly* work with Bootstrap 2, but we do not officially support it. Please upgrade, if possible.'); 233 | } 234 | 235 | let dialog = $(templates.dialog); 236 | let innerDialog = dialog.find('.modal-dialog'); 237 | let body = dialog.find('.modal-body'); 238 | let header = $(templates.header); 239 | let footer = $(templates.footer); 240 | let buttons = options.buttons; 241 | 242 | let callbacks = { 243 | onEscape: options.onEscape 244 | }; 245 | 246 | body.find('.bootbox-body').html(options.message); 247 | 248 | // Only attempt to create buttons if at least one has been defined in the options object 249 | if (getKeyLength(options.buttons) > 0) { 250 | each(buttons, function (key, b) { 251 | let button = $(templates.button); 252 | button.data('bb-handler', key); 253 | button.addClass(b.className); 254 | 255 | switch (key) { 256 | case 'ok': 257 | case 'confirm': 258 | button.addClass('bootbox-accept'); 259 | break; 260 | 261 | case 'cancel': 262 | button.addClass('bootbox-cancel'); 263 | break; 264 | } 265 | 266 | button.html(b.label); 267 | 268 | if (b.id) { 269 | button.attr({ 'id': b.id }); 270 | } 271 | 272 | if (b.disabled === true) { 273 | button.prop({ disabled: true }); 274 | } 275 | 276 | footer.append(button); 277 | 278 | callbacks[key] = b.callback; 279 | }); 280 | 281 | body.after(footer); 282 | } 283 | 284 | if (options.animate === true) { 285 | dialog.addClass('fade'); 286 | } 287 | 288 | if (options.className) { 289 | dialog.addClass(options.className); 290 | } 291 | 292 | if (options.id) { 293 | dialog.attr({ 'id': options.id }); 294 | } 295 | 296 | if (options.size) { 297 | // Requires Bootstrap 3.1.0 or higher 298 | if (options.fullBootstrapVersion.substring(0, 3) < '3.1') { 299 | console.warn('"size" requires Bootstrap 3.1.0 or higher. You appear to be using ' + options.fullBootstrapVersion + '. Please upgrade to use this option.'); 300 | } 301 | 302 | switch (options.size) { 303 | case 'small': 304 | case 'sm': 305 | innerDialog.addClass('modal-sm'); 306 | break; 307 | 308 | case 'large': 309 | case 'lg': 310 | innerDialog.addClass('modal-lg'); 311 | break; 312 | 313 | case 'extra-large': 314 | case 'xl': 315 | innerDialog.addClass('modal-xl'); 316 | 317 | // Requires Bootstrap 4.2.0 or higher 318 | if (options.fullBootstrapVersion.substring(0, 3) < '4.2') { 319 | console.warn('Using size "xl"/"extra-large" requires Bootstrap 4.2.0 or higher. You appear to be using ' + options.fullBootstrapVersion + '. Please upgrade to use this option.'); 320 | } 321 | break; 322 | } 323 | } 324 | 325 | if (options.scrollable) { 326 | innerDialog.addClass('modal-dialog-scrollable'); 327 | 328 | // Requires Bootstrap 4.3.0 or higher 329 | if (options.fullBootstrapVersion.substring(0, 3) < '4.3') { 330 | console.warn('Using "scrollable" requires Bootstrap 4.3.0 or higher. You appear to be using ' + options.fullBootstrapVersion + '. Please upgrade to use this option.'); 331 | } 332 | } 333 | 334 | if(options.title || options.closeButton) { 335 | if (options.title) { 336 | header.find('.modal-title').html(options.title); 337 | } 338 | else { 339 | header.addClass('border-0'); 340 | } 341 | 342 | if (options.closeButton) { 343 | let closeButton = $(templates.closeButton); 344 | if (options.bootstrap < 5) { 345 | closeButton.html('×'); 346 | } 347 | 348 | /* Note: the close button for Bootstrap 5+ does not contain content */ 349 | if(options.bootstrap < 4) { 350 | /* Bootstrap 3 and under */ 351 | header.prepend(closeButton); 352 | } 353 | else { 354 | header.append(closeButton); 355 | } 356 | } 357 | 358 | body.before(header); 359 | } 360 | 361 | if (options.centerVertical) { 362 | innerDialog.addClass('modal-dialog-centered'); 363 | 364 | // Requires Bootstrap 4.0.0-beta.3 or higher 365 | if (options.fullBootstrapVersion < '4.0.0') { 366 | console.warn('"centerVertical" requires Bootstrap 4.0.0-beta.3 or higher. You appear to be using ' + options.fullBootstrapVersion + '. Please upgrade to use this option.'); 367 | } 368 | } 369 | 370 | // Bootstrap event listeners; these handle extra setup & teardown required after the underlying modal has performed certain actions. 371 | 372 | if(!options.reusable) { 373 | // make sure we unbind any listeners once the dialog has definitively been dismissed 374 | dialog.one('hide.bs.modal', { dialog: dialog }, unbindModal); 375 | dialog.one('hidden.bs.modal', { dialog: dialog }, destroyModal); 376 | } 377 | 378 | if (options.onHide) { 379 | if (typeof options.onHide === 'function') { 380 | dialog.on('hide.bs.modal', options.onHide); 381 | } 382 | else { 383 | throw new Error('Argument supplied to "onHide" must be a function'); 384 | } 385 | } 386 | 387 | if (options.onHidden) { 388 | if (typeof options.onHidden === 'function') { 389 | dialog.on('hidden.bs.modal', options.onHidden); 390 | } 391 | else { 392 | throw new Error('Argument supplied to "onHidden" must be a function'); 393 | } 394 | } 395 | 396 | if (options.onShow) { 397 | if (typeof options.onShow === 'function') { 398 | dialog.on('show.bs.modal', options.onShow); 399 | } 400 | else { 401 | throw new Error('Argument supplied to "onShow" must be a function'); 402 | } 403 | } 404 | 405 | dialog.one('shown.bs.modal', { dialog: dialog }, focusPrimaryButton); 406 | 407 | if (options.onShown) { 408 | if (typeof options.onShown === 'function') { 409 | dialog.on('shown.bs.modal', options.onShown); 410 | } 411 | else { 412 | throw new Error('Argument supplied to "onShown" must be a function'); 413 | } 414 | } 415 | 416 | // Bootbox event listeners; used to decouple some behaviours from their respective triggers 417 | 418 | if (options.backdrop === true) { 419 | let startedOnBody = false; 420 | 421 | // Prevents the event from propagating to the backdrop, when something inside the dialog is clicked 422 | dialog.on('mousedown', '.modal-content', function(e) { 423 | e.stopPropagation(); 424 | 425 | startedOnBody = true; 426 | }); 427 | 428 | // A boolean true/false according to the Bootstrap docs should show a dialog the user can dismiss by clicking on the background. 429 | // We always only ever pass static/false to the actual $.modal function because with "true" we can't trap this event (the .modal-backdrop swallows it). 430 | // However, we still want to sort-of respect true and invoke the escape mechanism instead 431 | dialog.on('click.dismiss.bs.modal', function (e) { 432 | if (startedOnBody || e.target !== e.currentTarget) { 433 | return; 434 | } 435 | 436 | dialog.trigger('escape.close.bb'); 437 | }); 438 | } 439 | 440 | dialog.on('escape.close.bb', function (e) { 441 | // The if() statement looks redundant but it isn't; without it, if we *didn't* have an onEscape handler then processCallback would automatically dismiss the dialog 442 | if (callbacks.onEscape) { 443 | processCallback(e, dialog, callbacks.onEscape); 444 | } 445 | }); 446 | 447 | dialog.on('click', '.modal-footer button:not(.disabled)', function (e) { 448 | let callbackKey = $(this).data('bb-handler'); 449 | 450 | if (callbackKey !== undefined) { 451 | // Only process callbacks for buttons we recognize: 452 | processCallback(e, dialog, callbacks[callbackKey]); 453 | } 454 | }); 455 | 456 | dialog.on('click', '.bootbox-close-button', function (e) { 457 | // onEscape might be falsy, but that's fine; the fact is if the user has managed to click the close button we have to close the dialog, callback or not 458 | processCallback(e, dialog, callbacks.onEscape); 459 | }); 460 | 461 | dialog.on('keyup', function (e) { 462 | if (e.which === 27) { 463 | dialog.trigger('escape.close.bb'); 464 | } 465 | }); 466 | 467 | /* 468 | The remainder of this method simply deals with adding our dialog element to the DOM, augmenting it with 469 | Bootstrap's modal functionality and then giving the resulting object back to our caller 470 | */ 471 | 472 | $(options.container).append(dialog); 473 | 474 | dialog.modal({ 475 | backdrop: options.backdrop, 476 | keyboard: false, 477 | show: false 478 | }); 479 | 480 | if (options.show) { 481 | dialog.modal('show', options.relatedTarget); 482 | } 483 | 484 | return dialog; 485 | }; 486 | 487 | 488 | /** 489 | * Helper function to simulate the native alert() behavior. **NOTE**: This is non-blocking, so any code that must happen after the alert is dismissed should be placed within the callback function for this alert. 490 | * @returns A jQuery object upon which Bootstrap's modal function has been called 491 | */ 492 | exports.alert = function () { 493 | let options; 494 | 495 | options = mergeDialogOptions('alert', ['ok'], ['message', 'callback'], arguments); 496 | 497 | // @TODO: can this move inside exports.dialog when we're iterating over each button and checking its button.callback value instead? 498 | if (options.callback && typeof options.callback !== 'function') { 499 | throw new Error('alert requires the "callback" property to be a function when provided'); 500 | } 501 | 502 | // Override the ok and escape callback to make sure they just invoke the single user-supplied one (if provided) 503 | options.buttons.ok.callback = options.onEscape = function () { 504 | if (typeof options.callback === 'function') { 505 | return options.callback.call(this); 506 | } 507 | 508 | return true; 509 | }; 510 | 511 | return exports.dialog(options); 512 | }; 513 | 514 | 515 | /** 516 | * Helper function to simulate the native confirm() behavior. **NOTE**: This is non-blocking, so any code that must happen after the confirm is dismissed should be placed within the callback function for this confirm. 517 | * @returns A jQuery object upon which Bootstrap's modal function has been called 518 | */ 519 | exports.confirm = function () { 520 | let options; 521 | 522 | options = mergeDialogOptions('confirm', ['cancel', 'confirm'], ['message', 'callback'], arguments); 523 | 524 | // confirm specific validation; they don't make sense without a callback so make sure it's present 525 | if (typeof options.callback !== 'function') { 526 | throw new Error('confirm requires a callback'); 527 | } 528 | 529 | // Overrides; undo anything the user tried to set they shouldn't have 530 | options.buttons.cancel.callback = options.onEscape = function () { 531 | return options.callback.call(this, false); 532 | }; 533 | 534 | options.buttons.confirm.callback = function () { 535 | return options.callback.call(this, true); 536 | }; 537 | 538 | return exports.dialog(options); 539 | }; 540 | 541 | 542 | /** 543 | * Helper function to simulate the native prompt() behavior. **NOTE**: This is non-blocking, so any code that must happen after the prompt is dismissed should be placed within the callback function for this prompt. 544 | * @returns A jQuery object upon which Bootstrap's modal function has been called 545 | */ 546 | exports.prompt = function () { 547 | let options; 548 | let promptDialog; 549 | let form; 550 | let input; 551 | let shouldShow; 552 | let inputOptions; 553 | 554 | // We have to create our form first, otherwise its value is undefined when gearing up our options. 555 | // @TODO this could be solved by allowing message to be a function instead... 556 | form = $(templates.form); 557 | 558 | // prompt defaults are more complex than others in that users can override more defaults 559 | options = mergeDialogOptions('prompt', ['cancel', 'confirm'], ['title', 'callback'], arguments); 560 | 561 | if (!options.value) { 562 | options.value = defaults.value; 563 | } 564 | 565 | if (!options.inputType) { 566 | options.inputType = defaults.inputType; 567 | } 568 | 569 | // Capture the user's 'show' value; we always set this to false before spawning the dialog to give us a chance to attach some handlers to it, but we need to make sure we respect a preference not to show it 570 | shouldShow = (options.show === undefined) ? defaults.show : options.show; 571 | 572 | // This is required prior to calling the dialog builder below - we need to add an event handler just before the prompt is shown 573 | options.show = false; 574 | 575 | // Handles the 'cancel' action 576 | options.buttons.cancel.callback = options.onEscape = function () { 577 | return options.callback.call(this, null); 578 | }; 579 | 580 | // Prompt submitted - extract the prompt value. This requires a bit of work, given the different input types available. 581 | options.buttons.confirm.callback = function () { 582 | let value; 583 | 584 | if (options.inputType === 'checkbox') { 585 | value = input.find('input:checked').map(function () { 586 | return $(this).val(); 587 | }).get(); 588 | if(value.length === 0 && options.required === true) { 589 | // prevents button callback from being called if no checkboxes have been checked 590 | return false; 591 | } 592 | } else if (options.inputType === 'radio') { 593 | value = input.find('input:checked').val(); 594 | } 595 | else { 596 | let el = input[0]; 597 | 598 | if (el.checkValidity && !el.checkValidity()) { 599 | // If a custom error message was provided, add it now 600 | if(options.errorMessage){ 601 | el.setCustomValidity(options.errorMessage); 602 | } 603 | 604 | if(el.reportValidity) { 605 | el.reportValidity(); 606 | } 607 | 608 | // prevents button callback from being called 609 | return false; 610 | } else { 611 | if (options.inputType === 'select' && options.multiple === true) { 612 | value = input.find('option:selected').map(function () { 613 | return $(this).val(); 614 | }).get(); 615 | } 616 | else { 617 | value = input.val(); 618 | } 619 | } 620 | } 621 | 622 | return options.callback.call(this, value); 623 | }; 624 | 625 | // prompt-specific validation 626 | if (!options.title) { 627 | throw new Error('prompt requires a title'); 628 | } 629 | 630 | if (typeof options.callback !== 'function') { 631 | throw new Error('prompt requires a callback'); 632 | } 633 | 634 | if (!templates.inputs[options.inputType]) { 635 | throw new Error('Invalid prompt type'); 636 | } 637 | 638 | // Create the input based on the supplied type 639 | input = $(templates.inputs[options.inputType]); 640 | 641 | switch (options.inputType) { 642 | case 'text': 643 | case 'textarea': 644 | case 'email': 645 | case 'password': 646 | input.val(options.value); 647 | 648 | if (options.placeholder) { 649 | input.attr('placeholder', options.placeholder); 650 | } 651 | 652 | if (options.pattern) { 653 | input.attr('pattern', options.pattern); 654 | } 655 | 656 | if (options.maxlength) { 657 | input.attr('maxlength', options.maxlength); 658 | } 659 | 660 | if (options.required) { 661 | input.prop({ 'required': true }); 662 | } 663 | 664 | if (options.rows && !isNaN(parseInt(options.rows))) { 665 | if (options.inputType === 'textarea') { 666 | input.attr({ 'rows': options.rows }); 667 | } 668 | } 669 | break; 670 | 671 | case 'date': 672 | case 'time': 673 | case 'number': 674 | case 'range': 675 | input.val(options.value); 676 | 677 | if (options.placeholder) { 678 | input.attr('placeholder', options.placeholder); 679 | } 680 | 681 | if (options.pattern) { 682 | input.attr('pattern', options.pattern); 683 | } 684 | else { 685 | if(options.inputType === 'date') { 686 | // Add the ISO-8601 short date format as a fallback for browsers without native type="date" support 687 | input.attr('pattern', '\d{4}-\d{2}-\d{2}'); 688 | } 689 | else if(options.inputType === 'time') { 690 | // Add an HH:MM pattern as a fallback for browsers without native type="time" support 691 | input.attr('pattern', '\d{2}:\d{2}'); 692 | } 693 | } 694 | 695 | if (options.required) { 696 | input.prop({ 'required': true }); 697 | } 698 | 699 | if (options.step) { 700 | if (options.step === 'any' || (!isNaN(options.step) && parseFloat(options.step) > 0)) { 701 | input.attr('step', options.step); 702 | } 703 | else { 704 | throw new Error('"step" must be a valid positive number or the value "any". See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-step for more information.'); 705 | } 706 | } 707 | 708 | if (minAndMaxAreValid(options.inputType, options.min, options.max)) { 709 | if (options.min !== undefined) { 710 | input.attr('min', options.min); 711 | } 712 | if (options.max !== undefined) { 713 | input.attr('max', options.max); 714 | } 715 | } 716 | break; 717 | 718 | case 'select': 719 | let groups = {}; 720 | inputOptions = options.inputOptions || []; 721 | 722 | if (!Array.isArray(inputOptions)) { 723 | throw new Error('Please pass an array of input options'); 724 | } 725 | 726 | if (!inputOptions.length) { 727 | throw new Error('prompt with "inputType" set to "select" requires at least one option'); 728 | } 729 | 730 | if (options.required) { 731 | input.prop({ 'required': true }); 732 | } 733 | 734 | if (options.multiple) { 735 | input.prop({ 'multiple': true }); 736 | } 737 | 738 | each(inputOptions, function (_, option) { 739 | // Assume the element to attach to is the input... 740 | let elem = input; 741 | 742 | if (option.value === undefined || option.text === undefined) { 743 | throw new Error('each option needs a "value" property and a "text" property'); 744 | } 745 | 746 | // ... but override that element if this option sits in a group 747 | 748 | if (option.group) { 749 | // Initialise group if necessary 750 | if (!groups[option.group]) { 751 | groups[option.group] = $('').attr('label', option.group); 752 | } 753 | 754 | elem = groups[option.group]; 755 | } 756 | 757 | let o = $(templates.option); 758 | o.attr('value', option.value).text(option.text); 759 | elem.append(o); 760 | }); 761 | 762 | each(groups, function (_, group) { 763 | input.append(group); 764 | }); 765 | 766 | // Safe to set a select's value as per a normal input 767 | input.val(options.value); 768 | if (options.bootstrap < 5) { 769 | input.removeClass('form-select').addClass('form-control'); 770 | } 771 | break; 772 | 773 | case 'checkbox': 774 | let checkboxValues = Array.isArray(options.value) ? options.value : [options.value]; 775 | inputOptions = options.inputOptions || []; 776 | 777 | if (!inputOptions.length) { 778 | throw new Error('prompt with "inputType" set to "checkbox" requires at least one option'); 779 | } 780 | 781 | // Checkboxes have to nest within a containing element, so they break the rules a bit and we end up re-assigning our 'input' element to this container instead 782 | input = $('
'); 783 | 784 | each(inputOptions, function (_, option) { 785 | if (option.value === undefined || option.text === undefined) { 786 | throw new Error('each option needs a "value" property and a "text" property'); 787 | } 788 | 789 | let checkbox = $(templates.inputs[options.inputType]); 790 | 791 | checkbox.find('input').attr('value', option.value); 792 | checkbox.find('label').append('\n' + option.text); 793 | 794 | // We've ensured values is an array, so we can always iterate over it 795 | each(checkboxValues, function (_, value) { 796 | if (value === option.value) { 797 | checkbox.find('input').prop('checked', true); 798 | } 799 | }); 800 | 801 | input.append(checkbox); 802 | }); 803 | break; 804 | 805 | case 'radio': 806 | // Make sure that value is not an array (only a single radio can ever be checked) 807 | if (options.value !== undefined && Array.isArray(options.value)) { 808 | throw new Error('prompt with "inputType" set to "radio" requires a single, non-array value for "value"'); 809 | } 810 | 811 | inputOptions = options.inputOptions || []; 812 | 813 | if (!inputOptions.length) { 814 | throw new Error('prompt with "inputType" set to "radio" requires at least one option'); 815 | } 816 | 817 | // Radiobuttons have to nest within a containing element, so they break the rules a bit and we end up re-assigning our 'input' element to this container instead 818 | input = $('
'); 819 | 820 | // Radiobuttons should always have an initial checked input checked in a "group". 821 | // If value is undefined or doesn't match an input option, select the first radiobutton 822 | let checkFirstRadio = true; 823 | 824 | each(inputOptions, function (_, option) { 825 | if (option.value === undefined || option.text === undefined) { 826 | throw new Error('each option needs a "value" property and a "text" property'); 827 | } 828 | 829 | let radio = $(templates.inputs[options.inputType]); 830 | 831 | radio.find('input').attr('value', option.value); 832 | radio.find('label').append('\n' + option.text); 833 | 834 | if (options.value !== undefined) { 835 | if (option.value === options.value) { 836 | radio.find('input').prop('checked', true); 837 | checkFirstRadio = false; 838 | } 839 | } 840 | 841 | input.append(radio); 842 | }); 843 | 844 | if (checkFirstRadio) { 845 | input.find('input[type="radio"]').first().prop('checked', true); 846 | } 847 | break; 848 | } 849 | 850 | // Now place it in our form 851 | form.append(input); 852 | 853 | form.on('submit', function (e) { 854 | e.preventDefault(); 855 | // Fix for SammyJS (or similar JS routing library) hijacking the form post. 856 | e.stopPropagation(); 857 | 858 | // @TODO can we actually click *the* button object instead? 859 | // e.g. buttons.confirm.click() or similar 860 | promptDialog.find('.bootbox-accept').trigger('click'); 861 | }); 862 | 863 | if (options.message && options.message.trim() !== '') { 864 | // Add the form to whatever content the user may have added. 865 | let message = $(templates.promptMessage).html(options.message); 866 | form.prepend(message); 867 | options.message = form; 868 | } 869 | else { 870 | options.message = form; 871 | } 872 | 873 | // Generate the dialog 874 | promptDialog = exports.dialog(options); 875 | 876 | // Clear the existing handler focusing the submit button... 877 | promptDialog.off('shown.bs.modal', focusPrimaryButton); 878 | 879 | // ...and replace it with one focusing our input, if possible 880 | promptDialog.on('shown.bs.modal', function () { 881 | // Need the closure here since input isn't an object otherwise 882 | input.focus(); 883 | }); 884 | 885 | if (shouldShow === true) { 886 | promptDialog.modal('show'); 887 | } 888 | 889 | return promptDialog; 890 | }; 891 | 892 | 893 | // INTERNAL FUNCTIONS 894 | // ************************************************************************************************************* 895 | 896 | // Map a flexible set of arguments into a single returned object. 897 | // If args.length is already one just return it, otherwise use the properties argument to map the unnamed args to object properties. 898 | // So in the latter case: 899 | // 900 | // mapArguments(["foo", $.noop], ["message", "callback"]) 901 | // 902 | // results in 903 | // 904 | // { message: "foo", callback: $.noop } 905 | // 906 | function mapArguments(args, properties) { 907 | let argsLength = args.length; 908 | let options = {}; 909 | 910 | if (argsLength < 1 || argsLength > 2) { 911 | throw new Error('Invalid argument length'); 912 | } 913 | 914 | if (argsLength === 2 || typeof args[0] === 'string') { 915 | options[properties[0]] = args[0]; 916 | options[properties[1]] = args[1]; 917 | } else { 918 | options = args[0]; 919 | } 920 | 921 | return options; 922 | } 923 | 924 | 925 | // Merge a set of default dialog options with user supplied arguments 926 | function mergeArguments(defaults, args, properties) { 927 | return $.extend( 928 | // Deep merge 929 | true, 930 | // Ensure the target is an empty, unreferenced object 931 | {}, 932 | // The base options object for this type of dialog (often just buttons) 933 | defaults, 934 | // 'args' could be an object or array; if it's an array properties will map it to a proper options object 935 | mapArguments(args, properties) 936 | ); 937 | } 938 | 939 | 940 | // This entry-level method makes heavy use of composition to take a simple range of inputs and return valid options suitable for passing to bootbox.dialog 941 | function mergeDialogOptions(className, labels, properties, args) { 942 | let locale; 943 | if (args && args[0]) { 944 | locale = args[0].locale || defaults.locale; 945 | let swapButtons = args[0].swapButtonOrder || defaults.swapButtonOrder; 946 | 947 | if (swapButtons) { 948 | labels = labels.reverse(); 949 | } 950 | } 951 | 952 | // Build up a base set of dialog properties 953 | let baseOptions = { 954 | className: 'bootbox-' + className, 955 | buttons: createLabels(labels, locale) 956 | }; 957 | 958 | // Ensure the buttons properties generated, *after* merging with user args are still valid against the supplied labels 959 | return validateButtons( 960 | // Merge the generated base properties with user supplied arguments 961 | mergeArguments( 962 | baseOptions, 963 | args, 964 | // If args.length > 1, properties specify how each arg maps to an object key 965 | properties 966 | ), 967 | labels 968 | ); 969 | } 970 | 971 | 972 | // Checks each button object to see if key is valid. 973 | // This function will only be called by the alert, confirm, and prompt helpers. 974 | function validateButtons(options, buttons) { 975 | let allowedButtons = {}; 976 | each(buttons, function (key, value) { 977 | allowedButtons[value] = true; 978 | }); 979 | 980 | each(options.buttons, function (key) { 981 | if (allowedButtons[key] === undefined) { 982 | throw new Error('button key "' + key + '" is not allowed (options are ' + buttons.join(' ') + ')'); 983 | } 984 | }); 985 | 986 | return options; 987 | } 988 | 989 | 990 | // From a given list of arguments, return a suitable object of button labels. 991 | // All this does is normalise the given labels and translate them where possible. 992 | // e.g. "ok", "confirm" -> { ok: "OK", cancel: "Annuleren" } 993 | function createLabels(labels, locale) { 994 | let buttons = {}; 995 | 996 | for (let i = 0, j = labels.length; i < j; i++) { 997 | let argument = labels[i]; 998 | let key = argument.toLowerCase(); 999 | let value = argument.toUpperCase(); 1000 | 1001 | buttons[key] = { 1002 | label: getText(value, locale) 1003 | }; 1004 | } 1005 | 1006 | return buttons; 1007 | } 1008 | 1009 | 1010 | // Get localized text from a locale. Defaults to 'en' locale if no locale provided or a non-registered locale is requested 1011 | function getText(key, locale) { 1012 | let labels = locales[locale]; 1013 | 1014 | return labels ? labels[key] : locales.en[key]; 1015 | } 1016 | 1017 | 1018 | // Filter and tidy up any user supplied parameters to this dialog. 1019 | // Also looks for any shorthands used and ensures that the options which are returned are all normalized properly 1020 | function sanitize(options) { 1021 | let buttons; 1022 | let total; 1023 | 1024 | if (typeof options !== 'object') { 1025 | throw new Error('Please supply an object of options'); 1026 | } 1027 | 1028 | if (!options.message) { 1029 | throw new Error('"message" option must not be null or an empty string.'); 1030 | } 1031 | 1032 | // Make sure any supplied options take precedence over defaults 1033 | options = $.extend({}, defaults, options); 1034 | 1035 | // Make sure backdrop is either true, false, or 'static' 1036 | if (!options.backdrop) { 1037 | options.backdrop = (options.backdrop === false || options.backdrop === 0) ? false : 'static'; 1038 | } else { 1039 | options.backdrop = typeof options.backdrop === 'string' && options.backdrop.toLowerCase() === 'static' ? 'static' : true; 1040 | } 1041 | 1042 | // No buttons is still a valid dialog but it's cleaner to always have a buttons object to iterate over, even if it's empty 1043 | if (!options.buttons) { 1044 | options.buttons = {}; 1045 | } 1046 | 1047 | buttons = options.buttons; 1048 | 1049 | total = getKeyLength(buttons); 1050 | 1051 | each(buttons, function (key, button, index) { 1052 | if (typeof button === 'function') { 1053 | // Short form, assume value is our callback. Since button isn't an object it isn't a reference either so re-assign it 1054 | button = buttons[key] = { 1055 | callback: button 1056 | }; 1057 | } 1058 | 1059 | // Before any further checks, make sure button is the correct type 1060 | if (typeof button !== 'object') { 1061 | throw new Error('button with key "' + key + '" must be an object'); 1062 | } 1063 | 1064 | if (!button.label) { 1065 | // The lack of an explicit label means we'll assume the key is good enough 1066 | button.label = key; 1067 | } 1068 | 1069 | if (!button.className) { 1070 | let isPrimary = false; 1071 | if (options.swapButtonOrder) { 1072 | isPrimary = index === 0; 1073 | } 1074 | else { 1075 | isPrimary = index === total - 1; 1076 | } 1077 | 1078 | if (total <= 2 && isPrimary) { 1079 | // always add a primary to the main option in a one or two-button dialog 1080 | button.className = 'btn-primary'; 1081 | } 1082 | else { 1083 | // adding both classes allows us to target both BS3 and BS4+ without needing to check the version 1084 | button.className = 'btn-secondary btn-default'; 1085 | } 1086 | } 1087 | }); 1088 | 1089 | return options; 1090 | } 1091 | 1092 | 1093 | // Returns a count of the properties defined on the object 1094 | function getKeyLength(obj) { 1095 | return Object.keys(obj).length; 1096 | } 1097 | 1098 | 1099 | // Tiny wrapper function around jQuery.each; just adds index as the third parameter 1100 | function each(collection, iterator) { 1101 | let index = 0; 1102 | $.each(collection, function (key, value) { 1103 | iterator(key, value, index++); 1104 | }); 1105 | } 1106 | 1107 | 1108 | function focusPrimaryButton(e) { 1109 | e.data.dialog.find('.bootbox-accept').first().trigger('focus'); 1110 | } 1111 | 1112 | 1113 | function destroyModal(e) { 1114 | // Ensure we don't accidentally intercept hidden events triggered by children of the current dialog. 1115 | // We shouldn't need to handle this anymore, now that Bootstrap namespaces its events, but still worth doing. 1116 | if (e.target === e.data.dialog[0]) { 1117 | e.data.dialog.remove(); 1118 | } 1119 | } 1120 | 1121 | 1122 | function unbindModal(e) { 1123 | if (e.target === e.data.dialog[0]) { 1124 | e.data.dialog.off('escape.close.bb'); 1125 | e.data.dialog.off('click'); 1126 | } 1127 | } 1128 | 1129 | 1130 | // Handle the invoked dialog callback 1131 | function processCallback(e, dialog, callback) { 1132 | e.stopPropagation(); 1133 | e.preventDefault(); 1134 | 1135 | // By default we assume a callback will get rid of the dialog, although it is given the opportunity to override this 1136 | 1137 | // If the callback can be invoked and it *explicitly returns false*, then we'll set a flag to keep the dialog active... 1138 | let preserveDialog = typeof callback === 'function' && callback.call(dialog, e) === false; 1139 | 1140 | // ... otherwise we'll bin it 1141 | if (!preserveDialog) { 1142 | dialog.modal('hide'); 1143 | } 1144 | } 1145 | 1146 | // Validate `min` and `max` values based on the current `inputType` value 1147 | function minAndMaxAreValid(type, min, max) { 1148 | let result = false; 1149 | let minValid = true; 1150 | let maxValid = true; 1151 | 1152 | if (type === 'date') { 1153 | if (min !== undefined && !(minValid = dateIsValid(min))) { 1154 | console.warn('Browsers which natively support the "date" input type expect date values to be of the form "YYYY-MM-DD" (see ISO-8601 https://www.iso.org/iso-8601-date-and-time-format.html). Bootbox does not enforce this rule, but your min value may not be enforced by this browser.'); 1155 | } 1156 | else if (max !== undefined && !(maxValid = dateIsValid(max))) { 1157 | console.warn('Browsers which natively support the "date" input type expect date values to be of the form "YYYY-MM-DD" (see ISO-8601 https://www.iso.org/iso-8601-date-and-time-format.html). Bootbox does not enforce this rule, but your max value may not be enforced by this browser.'); 1158 | } 1159 | } 1160 | else if (type === 'time') { 1161 | if (min !== undefined && !(minValid = timeIsValid(min))) { 1162 | throw new Error('"min" is not a valid time. See https://www.w3.org/TR/2012/WD-html-markup-20120315/datatypes.html#form.data.time for more information.'); 1163 | } 1164 | else if (max !== undefined && !(maxValid = timeIsValid(max))) { 1165 | throw new Error('"max" is not a valid time. See https://www.w3.org/TR/2012/WD-html-markup-20120315/datatypes.html#form.data.time for more information.'); 1166 | } 1167 | } 1168 | else { 1169 | if (min !== undefined && isNaN(min)) { 1170 | minValid = false; 1171 | throw new Error('"min" must be a valid number. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-min for more information.'); 1172 | } 1173 | 1174 | if (max !== undefined && isNaN(max)) { 1175 | maxValid = false; 1176 | throw new Error('"max" must be a valid number. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-max for more information.'); 1177 | } 1178 | } 1179 | 1180 | if (minValid && maxValid) { 1181 | if (max < min) { 1182 | throw new Error('"max" must be greater than or equal to "min". See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-max for more information.'); 1183 | } 1184 | else { 1185 | result = true; 1186 | } 1187 | } 1188 | 1189 | return result; 1190 | } 1191 | 1192 | function timeIsValid(value) { 1193 | return /([01][0-9]|2[0-3]):[0-5][0-9]?:[0-5][0-9]/.test(value); 1194 | } 1195 | 1196 | function dateIsValid(value) { 1197 | return /(\d{4})-(\d{2})-(\d{2})/.test(value); 1198 | } 1199 | 1200 | // The Bootbox object 1201 | return exports; 1202 | })); 1203 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootbox.js", 3 | "main": "./dist/bootbox.js", 4 | "license": "MIT", 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": ">= 4.6.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /build/calculate-size: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | SRC="bootbox.js" 4 | MIN="/tmp/bootbox.min.js" 5 | 6 | uglifyjs $SRC -c -m > $MIN 7 | 8 | RAW="$(wc -c $SRC | awk '{print $1}')" 9 | MRAW="$(wc -c $MIN | awk '{print $1}')" 10 | GZIP="$(cat $SRC | gzip | wc -c | awk '{print $1}')" 11 | MGZIP="$(cat $MIN | gzip | wc -c | awk '{print $1}')" 12 | 13 | echo "raw,min,raw+gzip,min+gzip\n$RAW,$MRAW,$GZIP,$MGZIP" > build/size.csv 14 | rm $MIN 15 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootboxjs/bootbox", 3 | "type": "component", 4 | "description": "Wrappers for JavaScript alert(), confirm() and other flexible dialogs using the Bootstrap framework", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Nick Payne", 9 | "email": "nick@kurai.co.uk" 10 | } 11 | ], 12 | "require": { 13 | "components/bootstrap": "*" 14 | }, 15 | "extra": { 16 | "component": { 17 | "scripts": [ 18 | "dist/bootbox.js", 19 | "dist/bootbox.locales.js" 20 | ] 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /dist/LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | (The MIT License) 4 | 5 | Copyright (C) 2011-2025 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 | -------------------------------------------------------------------------------- /dist/bootbox.all.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootbox.js — alert, confirm, prompt, and flexible dialogs for the Bootstrap framework 3 | * @version: 6.0.4 4 | * @project: https://github.com/bootboxjs/bootbox 5 | * @license: MIT http://bootboxjs.com/license.txt 6 | */ 7 | ((e,t)=>{'function'==typeof define&&define.amd?define(['jquery'],t):'object'==typeof exports?module.exports=t(require('jquery')):e.bootbox=t(e.jQuery)})(this,function t(s,c){let r={};r.VERSION='6.0.4';let i={en:{OK:'OK',CANCEL:'Cancel',CONFIRM:'OK'}},d={dialog:'',header:'',footer:'',closeButton:'',form:'
',button:'',option:'',promptMessage:'
',inputs:{text:'',textarea:'',email:'',select:'',checkbox:'
',radio:'
',date:'',time:'',number:'',password:'',range:''}},p={locale:'en',backdrop:'static',animate:!0,className:null,closeButton:!0,show:!0,container:'body',value:'',inputType:'text',errorMessage:null,swapButtonOrder:!1,centerVertical:!1,multiple:!1,scrollable:!1,reusable:!1,relatedTarget:null,size:null,id:null};function l(e,t,o){return s.extend(!0,{},e,((e,t)=>{var o=e.length;let a={};if(o<1||2{var r={};for(let e=0,t=o.length;e((t=i[t])||i.en)[e])(n,a)}}return r})(t=r&&r[0]&&(o=r[0].locale||p.locale,r[0].swapButtonOrder||p.swapButtonOrder)?t.reverse():t,o)};{e=l(e,r,a);var n=t;let o={};return f(n,function(e,t){o[t]=!0}),f(e.buttons,function(e){if(o[e]===c)throw new Error('button key "'+e+'" is not allowed (options are '+n.join(' ')+')')}),e}}function b(e){return Object.keys(e).length}function f(e,o){let a=0;s.each(e,function(e,t){o(e,t,a++)})}function m(e){e.data.dialog.find('.bootbox-accept').first().trigger('focus')}function h(e){e.target===e.data.dialog[0]&&e.data.dialog.remove()}function C(e){e.target===e.data.dialog[0]&&(e.data.dialog.off('escape.close.bb'),e.data.dialog.off('click'))}function O(e,t,o){e.stopPropagation(),e.preventDefault(),'function'==typeof o&&!1===o.call(t,e)||t.modal('hide')}function w(e){return/([01][0-9]|2[0-3]):[0-5][0-9]?:[0-5][0-9]/.test(e)}function g(e){return/(\d{4})-(\d{2})-(\d{2})/.test(e)}return r.locales=function(e){return e?i[e]:i},r.addLocale=function(e,o){return s.each(['OK','CANCEL','CONFIRM'],function(e,t){if(!o[t])throw new Error('Please supply a translation for "'+t+'"')}),i[e]={OK:o.OK,CANCEL:o.CANCEL,CONFIRM:o.CONFIRM},r},r.removeLocale=function(e){if('en'===e)throw new Error('"en" is used as the default and fallback locale and cannot be removed.');return delete i[e],r},r.setLocale=function(e){return r.setDefaults('locale',e)},r.setDefaults=function(){let e={};return 2===arguments.length?e[arguments[0]]=arguments[1]:e=arguments[0],s.extend(p,e),r},r.hideAll=function(){return s('.bootbox').modal('hide'),r},r.init=function(e){return t(e||s)},r.dialog=function(e){if(s.fn.modal===c)throw new Error('"$.fn.modal" is not defined; please double check you have included the Bootstrap JavaScript library. See https://getbootstrap.com/docs/5.3/getting-started/introduction/ for more details.');e=(a=>{let r,n;if('object'!=typeof a)throw new Error('Please supply an object of options');if(a.message)return(a=s.extend({},p,a)).backdrop?a.backdrop='string'!=typeof a.backdrop||'static'!==a.backdrop.toLowerCase()||'static':a.backdrop=!1!==a.backdrop&&0!==a.backdrop&&'static',a.buttons||(a.buttons={}),r=a.buttons,n=b(r),f(r,function(e,t,o){if('object'!=typeof(t='function'==typeof t?r[e]={callback:t}:t))throw new Error('button with key "'+e+'" must be an object');if(t.label||(t.label=e),!t.className){let e=!1;e=a.swapButtonOrder?0===o:o===n-1,n<=2&&e?t.className='btn-primary':t.className='btn-secondary btn-default'}}),a;throw new Error('"message" option must not be null or an empty string.')})(e),s.fn.modal.Constructor.VERSION?(e.fullBootstrapVersion=s.fn.modal.Constructor.VERSION,a=e.fullBootstrapVersion.indexOf('.'),e.bootstrap=e.fullBootstrapVersion.substring(0,a)):(e.bootstrap='2',e.fullBootstrapVersion='2.3.2',console.warn('Bootbox will *mostly* work with Bootstrap 2, but we do not officially support it. Please upgrade, if possible.'));let o=s(d.dialog);var t=o.find('.modal-dialog'),a=o.find('.modal-body'),r=s(d.header);let n=s(d.footer);var l=e.buttons;let i={onEscape:e.onEscape};if(a.find('.bootbox-body').html(e.message),0{let a=!1,r=!0,n=!0;if('date'===e)t===c||(r=g(t))?o===c||(n=g(o))||console.warn('Browsers which natively support the "date" input type expect date values to be of the form "YYYY-MM-DD" (see ISO-8601 https://www.iso.org/iso-8601-date-and-time-format.html). Bootbox does not enforce this rule, but your max value may not be enforced by this browser.'):console.warn('Browsers which natively support the "date" input type expect date values to be of the form "YYYY-MM-DD" (see ISO-8601 https://www.iso.org/iso-8601-date-and-time-format.html). Bootbox does not enforce this rule, but your min value may not be enforced by this browser.');else if('time'===e){if(t!==c&&!(r=w(t)))throw new Error('"min" is not a valid time. See https://www.w3.org/TR/2012/WD-html-markup-20120315/datatypes.html#form.data.time for more information.');if(o!==c&&!(n=w(o)))throw new Error('"max" is not a valid time. See https://www.w3.org/TR/2012/WD-html-markup-20120315/datatypes.html#form.data.time for more information.')}else{if(t!==c&&isNaN(t))throw r=!1,new Error('"min" must be a valid number. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-min for more information.');if(o!==c&&isNaN(o))throw n=!1,new Error('"max" must be a valid number. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-max for more information.')}if(r&&n){if(o').attr('label',t.group)),o=r[t.group]);var a=s(d.option);a.attr('value',t.value).text(t.text),o.append(a)}),f(r,function(e,t){l.append(t)}),l.val(n.value),n.bootstrap<5&&l.removeClass('form-select').addClass('form-control');break;case'checkbox':let t=Array.isArray(n.value)?n.value:[n.value];if(!(i=n.inputOptions||[]).length)throw new Error('prompt with "inputType" set to "checkbox" requires at least one option');l=s('
'),f(i,function(e,o){if(o.value===c||o.text===c)throw new Error('each option needs a "value" property and a "text" property');let a=s(d.inputs[n.inputType]);a.find('input').attr('value',o.value),a.find('label').append('\n'+o.text),f(t,function(e,t){t===o.value&&a.find('input').prop('checked',!0)}),l.append(a)});break;case'radio':if(n.value!==c&&Array.isArray(n.value))throw new Error('prompt with "inputType" set to "radio" requires a single, non-array value for "value"');if(!(i=n.inputOptions||[]).length)throw new Error('prompt with "inputType" set to "radio" requires at least one option');l=s('
');let a=!0;f(i,function(e,t){if(t.value===c||t.text===c)throw new Error('each option needs a "value" property and a "text" property');var o=s(d.inputs[n.inputType]);o.find('input').attr('value',t.value),o.find('label').append('\n'+t.text),n.value!==c&&t.value===n.value&&(o.find('input').prop('checked',!0),a=!1),l.append(o)}),a&&l.find('input[type="radio"]').first().prop('checked',!0)}return e.append(l),e.on('submit',function(e){e.preventDefault(),e.stopPropagation(),t.find('.bootbox-accept').trigger('click')}),n.message&&''!==n.message.trim()&&(a=s(d.promptMessage).html(n.message),e.prepend(a)),n.message=e,(t=r.dialog(n)).off('shown.bs.modal',m),t.on('shown.bs.modal',function(){l.focus()}),!0===o&&t.modal('show'),t},r}),((e,t)=>{'function'==typeof define&&define.amd?define(['bootbox'],t):'object'==typeof module&&module.exports?t(require('./bootbox')):t(e.bootbox)})(this,function(e){e.addLocale('ar',{OK:'موافق',CANCEL:'الغاء',CONFIRM:'تأكيد'}),e.addLocale('az',{OK:'OK',CANCEL:'İmtina et',CONFIRM:'Təsdiq et'}),e.addLocale('bg-BG',{OK:'Ок',CANCEL:'Отказ',CONFIRM:'Потвърждавам'}),e.addLocale('cs',{OK:'OK',CANCEL:'Zrušit',CONFIRM:'Potvrdit'}),e.addLocale('da',{OK:'OK',CANCEL:'Annuller',CONFIRM:'Accepter'}),e.addLocale('de',{OK:'OK',CANCEL:'Abbrechen',CONFIRM:'Akzeptieren'}),e.addLocale('el',{OK:'Εντάξει',CANCEL:'Ακύρωση',CONFIRM:'Επιβεβαίωση'}),e.addLocale('en',{OK:'OK',CANCEL:'Cancel',CONFIRM:'OK'}),e.addLocale('es',{OK:'OK',CANCEL:'Cancelar',CONFIRM:'Aceptar'}),e.addLocale('et',{OK:'OK',CANCEL:'Katkesta',CONFIRM:'OK'}),e.addLocale('eu',{OK:'OK',CANCEL:'Ezeztatu',CONFIRM:'Onartu'}),e.addLocale('fa',{OK:'قبول',CANCEL:'لغو',CONFIRM:'تایید'}),e.addLocale('fi',{OK:'OK',CANCEL:'Peruuta',CONFIRM:'OK'}),e.addLocale('fr',{OK:'OK',CANCEL:'Annuler',CONFIRM:'Confirmer'}),e.addLocale('he',{OK:'אישור',CANCEL:'ביטול',CONFIRM:'אישור'}),e.addLocale('hr',{OK:'OK',CANCEL:'Odustani',CONFIRM:'Potvrdi'}),e.addLocale('hu',{OK:'OK',CANCEL:'Mégsem',CONFIRM:'Megerősít'}),e.addLocale('id',{OK:'OK',CANCEL:'Batal',CONFIRM:'OK'}),e.addLocale('it',{OK:'OK',CANCEL:'Annulla',CONFIRM:'Conferma'}),e.addLocale('ja',{OK:'OK',CANCEL:'キャンセル',CONFIRM:'OK'}),e.addLocale('ka',{OK:'OK',CANCEL:'გაუქმება',CONFIRM:'დადასტურება'}),e.addLocale('ko',{OK:'OK',CANCEL:'취소',CONFIRM:'확인'}),e.addLocale('lt',{OK:'Gerai',CANCEL:'Atšaukti',CONFIRM:'Patvirtinti'}),e.addLocale('lv',{OK:'Labi',CANCEL:'Atcelt',CONFIRM:'Apstiprināt'}),e.addLocale('nl',{OK:'OK',CANCEL:'Annuleren',CONFIRM:'Accepteren'}),e.addLocale('no',{OK:'OK',CANCEL:'Avbryt',CONFIRM:'OK'}),e.addLocale('pl',{OK:'OK',CANCEL:'Anuluj',CONFIRM:'Potwierdź'}),e.addLocale('pt-BR',{OK:'OK',CANCEL:'Cancelar',CONFIRM:'Sim'}),e.addLocale('pt',{OK:'OK',CANCEL:'Cancelar',CONFIRM:'Confirmar'}),e.addLocale('ru',{OK:'OK',CANCEL:'Отмена',CONFIRM:'Применить'}),e.addLocale('sk',{OK:'OK',CANCEL:'Zrušiť',CONFIRM:'Potvrdiť'}),e.addLocale('sl',{OK:'OK',CANCEL:'Prekliči',CONFIRM:'Potrdi'}),e.addLocale('sq',{OK:'OK',CANCEL:'Anulo',CONFIRM:'Prano'}),e.addLocale('sv',{OK:'OK',CANCEL:'Avbryt',CONFIRM:'OK'}),e.addLocale('sw',{OK:'Sawa',CANCEL:'Ghairi',CONFIRM:'Thibitisha'}),e.addLocale('ta',{OK:'சரி',CANCEL:'ரத்து செய்',CONFIRM:'உறுதி செய்'}),e.addLocale('th',{OK:'ตกลง',CANCEL:'ยกเลิก',CONFIRM:'ยืนยัน'}),e.addLocale('tr',{OK:'Tamam',CANCEL:'İptal',CONFIRM:'Onayla'}),e.addLocale('uk',{OK:'OK',CANCEL:'Відміна',CONFIRM:'Прийняти'}),e.addLocale('vi',{OK:'OK',CANCEL:'Hủy bỏ',CONFIRM:'Xác nhận'}),e.addLocale('zh-CN',{OK:'OK',CANCEL:'取消',CONFIRM:'确认'}),e.addLocale('zh-TW',{OK:'OK',CANCEL:'取消',CONFIRM:'確認'})}); -------------------------------------------------------------------------------- /dist/bootbox.locales.js: -------------------------------------------------------------------------------- 1 | /*! @preserve 2 | * bootbox.locales.js 3 | * version: 6.0.4 4 | * author: Nick Payne 5 | * license: MIT 6 | * http://bootboxjs.com/ 7 | */ 8 | (function(global, factory) { 9 | 'use strict'; 10 | if (typeof define === 'function' && define.amd) { 11 | define(['bootbox'], factory); 12 | } else if (typeof module === 'object' && module.exports) { 13 | factory(require('./bootbox')); 14 | } else { 15 | factory(global.bootbox); 16 | } 17 | }(this, function(bootbox) { 18 | 'use strict'; 19 | 20 | // locale : Arabic 21 | // author : Emad Omar 22 | bootbox.addLocale('ar', { 23 | OK: 'موافق', 24 | CANCEL: 'الغاء', 25 | CONFIRM: 'تأكيد' 26 | }); 27 | // locale : Azerbaijani 28 | // author : Valentin Belousov 29 | bootbox.addLocale('az', { 30 | OK: 'OK', 31 | CANCEL: 'İmtina et', 32 | CONFIRM: 'Təsdiq et' 33 | }); 34 | // locale : Bulgarian 35 | // author : mraiur 36 | bootbox.addLocale('bg-BG', { 37 | OK: 'Ок', 38 | CANCEL: 'Отказ', 39 | CONFIRM: 'Потвърждавам' 40 | }); 41 | // locale : Czech 42 | // author : Lukáš Fryč 43 | bootbox.addLocale('cs', { 44 | OK: 'OK', 45 | CANCEL: 'Zrušit', 46 | CONFIRM: 'Potvrdit' 47 | }); 48 | // locale : Danish 49 | // author : Frederik Alkærsig 50 | bootbox.addLocale('da', { 51 | OK: 'OK', 52 | CANCEL: 'Annuller', 53 | CONFIRM: 'Accepter' 54 | }); 55 | // locale : German 56 | // author : Nick Payne 57 | bootbox.addLocale('de', { 58 | OK: 'OK', 59 | CANCEL: 'Abbrechen', 60 | CONFIRM: 'Akzeptieren' 61 | }); 62 | // locale : Greek 63 | // author : Tolis Emmanouilidis 64 | bootbox.addLocale('el', { 65 | OK: 'Εντάξει', 66 | CANCEL: 'Ακύρωση', 67 | CONFIRM: 'Επιβεβαίωση' 68 | }); 69 | // locale : English 70 | // author : Nick Payne 71 | bootbox.addLocale('en', { 72 | OK: 'OK', 73 | CANCEL: 'Cancel', 74 | CONFIRM: 'OK' 75 | }); 76 | // locale : Spanish 77 | // author : Ian Leckey 78 | bootbox.addLocale('es', { 79 | OK: 'OK', 80 | CANCEL: 'Cancelar', 81 | CONFIRM: 'Aceptar' 82 | }); 83 | // locale : Estonian 84 | // author : Pavel Krõlov 85 | bootbox.addLocale('et', { 86 | OK: 'OK', 87 | CANCEL: 'Katkesta', 88 | CONFIRM: 'OK' 89 | }); 90 | // locale : Basque 91 | // author : Iker Ibarguren 92 | bootbox.addLocale('eu', { 93 | OK: 'OK', 94 | CANCEL: 'Ezeztatu', 95 | CONFIRM: 'Onartu' 96 | }); 97 | // locale : Persian 98 | // author : Touhid Arastu 99 | bootbox.addLocale('fa', { 100 | OK: 'قبول', 101 | CANCEL: 'لغو', 102 | CONFIRM: 'تایید' 103 | }); 104 | // locale : Finnish 105 | // author : Nick Payne 106 | bootbox.addLocale('fi', { 107 | OK: 'OK', 108 | CANCEL: 'Peruuta', 109 | CONFIRM: 'OK' 110 | }); 111 | // locale : French 112 | // author : Nick Payne, Sebastien Andary 113 | bootbox.addLocale('fr', { 114 | OK: 'OK', 115 | CANCEL: 'Annuler', 116 | CONFIRM: 'Confirmer' 117 | }); 118 | // locale : Hebrew 119 | // author : Chen Alon 120 | bootbox.addLocale('he', { 121 | OK: 'אישור', 122 | CANCEL: 'ביטול', 123 | CONFIRM: 'אישור' 124 | }); 125 | // locale : Croatian 126 | // author : Mario Bašić 127 | bootbox.addLocale('hr', { 128 | OK: 'OK', 129 | CANCEL: 'Odustani', 130 | CONFIRM: 'Potvrdi' 131 | }); 132 | // locale : Hungarian 133 | // author : Márk Sági-Kazár 134 | bootbox.addLocale('hu', { 135 | OK: 'OK', 136 | CANCEL: 'Mégsem', 137 | CONFIRM: 'Megerősít' 138 | }); 139 | // locale : Indonesian 140 | // author : Budi Irawan 141 | bootbox.addLocale('id', { 142 | OK: 'OK', 143 | CANCEL: 'Batal', 144 | CONFIRM: 'OK' 145 | }); 146 | // locale : Italian 147 | // author : Mauro 148 | bootbox.addLocale('it', { 149 | OK: 'OK', 150 | CANCEL: 'Annulla', 151 | CONFIRM: 'Conferma' 152 | }); 153 | // locale : Japanese 154 | // author : ms183 155 | bootbox.addLocale('ja', { 156 | OK: 'OK', 157 | CANCEL: 'キャンセル', 158 | CONFIRM: 'OK' 159 | }); 160 | 161 | // locale : Georgian 162 | // author : Avtandil Kikabidze aka LONGMAN (@akalongman) 163 | bootbox.addLocale('ka', { 164 | OK: 'OK', 165 | CANCEL: 'გაუქმება', 166 | CONFIRM: 'დადასტურება' 167 | }); 168 | // locale : Korean 169 | // author : rigning 170 | bootbox.addLocale('ko', { 171 | OK: 'OK', 172 | CANCEL: '취소', 173 | CONFIRM: '확인' 174 | }); 175 | // locale : Lithuanian 176 | // author : Tomas 177 | bootbox.addLocale('lt', { 178 | OK: 'Gerai', 179 | CANCEL: 'Atšaukti', 180 | CONFIRM: 'Patvirtinti' 181 | }); 182 | // locale : Latvian 183 | // author : Dmitry Bogatykh, Lauris BH 184 | bootbox.addLocale('lv', { 185 | OK: 'Labi', 186 | CANCEL: 'Atcelt', 187 | CONFIRM: 'Apstiprināt' 188 | }); 189 | // locale : Dutch 190 | // author : Bas ter Vrugt 191 | bootbox.addLocale('nl', { 192 | OK: 'OK', 193 | CANCEL: 'Annuleren', 194 | CONFIRM: 'Accepteren' 195 | }); 196 | // locale : Norwegian 197 | // author : Nils Magnus Englund 198 | bootbox.addLocale('no', { 199 | OK: 'OK', 200 | CANCEL: 'Avbryt', 201 | CONFIRM: 'OK' 202 | }); 203 | // locale : Polish 204 | // author : Szczepan Cieślik 205 | bootbox.addLocale('pl', { 206 | OK: 'OK', 207 | CANCEL: 'Anuluj', 208 | CONFIRM: 'Potwierdź' 209 | }); 210 | // locale : Portuguese (Brasil) 211 | // author : Nick Payne 212 | bootbox.addLocale('pt-BR', { 213 | OK: 'OK', 214 | CANCEL: 'Cancelar', 215 | CONFIRM: 'Sim' 216 | }); 217 | // locale : Portuguese 218 | // author : Cláudio Medina 219 | bootbox.addLocale('pt', { 220 | OK: 'OK', 221 | CANCEL: 'Cancelar', 222 | CONFIRM: 'Confirmar' 223 | }); 224 | // locale : Russian 225 | // author : ionian-wind 226 | bootbox.addLocale('ru', { 227 | OK: 'OK', 228 | CANCEL: 'Отмена', 229 | CONFIRM: 'Применить' 230 | }); 231 | // locale : Slovak 232 | // author : Stano Paška 233 | bootbox.addLocale('sk', { 234 | OK: 'OK', 235 | CANCEL: 'Zrušiť', 236 | CONFIRM: 'Potvrdiť' 237 | }); 238 | // locale : Slovenian 239 | // author : @metalcamp 240 | bootbox.addLocale('sl', { 241 | OK: 'OK', 242 | CANCEL: 'Prekliči', 243 | CONFIRM: 'Potrdi' 244 | }); 245 | // locale : Albanian 246 | // author : Knut Hühne 247 | bootbox.addLocale('sq', { 248 | OK: 'OK', 249 | CANCEL: 'Anulo', 250 | CONFIRM: 'Prano' 251 | }); 252 | // locale : Swedish 253 | // author : Mattias Reichel 254 | bootbox.addLocale('sv', { 255 | OK: 'OK', 256 | CANCEL: 'Avbryt', 257 | CONFIRM: 'OK' 258 | }); 259 | // locale : Swahili 260 | // author : Timothy Anyona 261 | bootbox.addLocale('sw', { 262 | OK: 'Sawa', 263 | CANCEL: 'Ghairi', 264 | CONFIRM: 'Thibitisha' 265 | }); 266 | // locale : Tamil 267 | // author : Kolappan Nathan 268 | bootbox.addLocale('ta', { 269 | OK: 'சரி', 270 | CANCEL: 'ரத்து செய்', 271 | CONFIRM: 'உறுதி செய்' 272 | }); 273 | // locale : Thai 274 | // author : Ishmael๛ 275 | bootbox.addLocale('th', { 276 | OK: 'ตกลง', 277 | CANCEL: 'ยกเลิก', 278 | CONFIRM: 'ยืนยัน' 279 | }); 280 | // locale : Turkish 281 | // author : Enes Karaca 282 | bootbox.addLocale('tr', { 283 | OK: 'Tamam', 284 | CANCEL: 'İptal', 285 | CONFIRM: 'Onayla' 286 | }); 287 | // locale : Ukrainian 288 | // author : OlehBoiko 289 | bootbox.addLocale('uk', { 290 | OK: 'OK', 291 | CANCEL: 'Відміна', 292 | CONFIRM: 'Прийняти' 293 | }); 294 | // locale : Vietnamese 295 | // author : Anh Tu Nguyen 296 | bootbox.addLocale('vi', { 297 | OK: 'OK', 298 | CANCEL: 'Hủy bỏ', 299 | CONFIRM: 'Xác nhận' 300 | }); 301 | // locale : Chinese (China / People's Republic of China) 302 | // author : Nick Payne 303 | bootbox.addLocale('zh-CN', { 304 | OK: 'OK', 305 | CANCEL: '取消', 306 | CONFIRM: '确认' 307 | }); 308 | // locale : Chinese (Taiwan / Republic of China) 309 | // author : Nick Payne 310 | bootbox.addLocale('zh-TW', { 311 | OK: 'OK', 312 | CANCEL: '取消', 313 | CONFIRM: '確認' 314 | }); 315 | 316 | })); 317 | -------------------------------------------------------------------------------- /dist/bootbox.locales.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootbox.js — alert, confirm, prompt, and flexible dialogs for the Bootstrap framework 3 | * @version: 6.0.4 4 | * @project: https://github.com/bootboxjs/bootbox 5 | * @license: MIT http://bootboxjs.com/license.txt 6 | */ 7 | ((C,a)=>{'function'==typeof define&&define.amd?define(['bootbox'],a):'object'==typeof module&&module.exports?a(require('./bootbox')):a(C.bootbox)})(this,function(C){C.addLocale('ar',{OK:'موافق',CANCEL:'الغاء',CONFIRM:'تأكيد'}),C.addLocale('az',{OK:'OK',CANCEL:'İmtina et',CONFIRM:'Təsdiq et'}),C.addLocale('bg-BG',{OK:'Ок',CANCEL:'Отказ',CONFIRM:'Потвърждавам'}),C.addLocale('cs',{OK:'OK',CANCEL:'Zrušit',CONFIRM:'Potvrdit'}),C.addLocale('da',{OK:'OK',CANCEL:'Annuller',CONFIRM:'Accepter'}),C.addLocale('de',{OK:'OK',CANCEL:'Abbrechen',CONFIRM:'Akzeptieren'}),C.addLocale('el',{OK:'Εντάξει',CANCEL:'Ακύρωση',CONFIRM:'Επιβεβαίωση'}),C.addLocale('en',{OK:'OK',CANCEL:'Cancel',CONFIRM:'OK'}),C.addLocale('es',{OK:'OK',CANCEL:'Cancelar',CONFIRM:'Aceptar'}),C.addLocale('et',{OK:'OK',CANCEL:'Katkesta',CONFIRM:'OK'}),C.addLocale('eu',{OK:'OK',CANCEL:'Ezeztatu',CONFIRM:'Onartu'}),C.addLocale('fa',{OK:'قبول',CANCEL:'لغو',CONFIRM:'تایید'}),C.addLocale('fi',{OK:'OK',CANCEL:'Peruuta',CONFIRM:'OK'}),C.addLocale('fr',{OK:'OK',CANCEL:'Annuler',CONFIRM:'Confirmer'}),C.addLocale('he',{OK:'אישור',CANCEL:'ביטול',CONFIRM:'אישור'}),C.addLocale('hr',{OK:'OK',CANCEL:'Odustani',CONFIRM:'Potvrdi'}),C.addLocale('hu',{OK:'OK',CANCEL:'Mégsem',CONFIRM:'Megerősít'}),C.addLocale('id',{OK:'OK',CANCEL:'Batal',CONFIRM:'OK'}),C.addLocale('it',{OK:'OK',CANCEL:'Annulla',CONFIRM:'Conferma'}),C.addLocale('ja',{OK:'OK',CANCEL:'キャンセル',CONFIRM:'OK'}),C.addLocale('ka',{OK:'OK',CANCEL:'გაუქმება',CONFIRM:'დადასტურება'}),C.addLocale('ko',{OK:'OK',CANCEL:'취소',CONFIRM:'확인'}),C.addLocale('lt',{OK:'Gerai',CANCEL:'Atšaukti',CONFIRM:'Patvirtinti'}),C.addLocale('lv',{OK:'Labi',CANCEL:'Atcelt',CONFIRM:'Apstiprināt'}),C.addLocale('nl',{OK:'OK',CANCEL:'Annuleren',CONFIRM:'Accepteren'}),C.addLocale('no',{OK:'OK',CANCEL:'Avbryt',CONFIRM:'OK'}),C.addLocale('pl',{OK:'OK',CANCEL:'Anuluj',CONFIRM:'Potwierdź'}),C.addLocale('pt-BR',{OK:'OK',CANCEL:'Cancelar',CONFIRM:'Sim'}),C.addLocale('pt',{OK:'OK',CANCEL:'Cancelar',CONFIRM:'Confirmar'}),C.addLocale('ru',{OK:'OK',CANCEL:'Отмена',CONFIRM:'Применить'}),C.addLocale('sk',{OK:'OK',CANCEL:'Zrušiť',CONFIRM:'Potvrdiť'}),C.addLocale('sl',{OK:'OK',CANCEL:'Prekliči',CONFIRM:'Potrdi'}),C.addLocale('sq',{OK:'OK',CANCEL:'Anulo',CONFIRM:'Prano'}),C.addLocale('sv',{OK:'OK',CANCEL:'Avbryt',CONFIRM:'OK'}),C.addLocale('sw',{OK:'Sawa',CANCEL:'Ghairi',CONFIRM:'Thibitisha'}),C.addLocale('ta',{OK:'சரி',CANCEL:'ரத்து செய்',CONFIRM:'உறுதி செய்'}),C.addLocale('th',{OK:'ตกลง',CANCEL:'ยกเลิก',CONFIRM:'ยืนยัน'}),C.addLocale('tr',{OK:'Tamam',CANCEL:'İptal',CONFIRM:'Onayla'}),C.addLocale('uk',{OK:'OK',CANCEL:'Відміна',CONFIRM:'Прийняти'}),C.addLocale('vi',{OK:'OK',CANCEL:'Hủy bỏ',CONFIRM:'Xác nhận'}),C.addLocale('zh-CN',{OK:'OK',CANCEL:'取消',CONFIRM:'确认'}),C.addLocale('zh-TW',{OK:'OK',CANCEL:'取消',CONFIRM:'確認'})}); -------------------------------------------------------------------------------- /dist/bootbox.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootbox.js — alert, confirm, prompt, and flexible dialogs for the Bootstrap framework 3 | * @version: 6.0.4 4 | * @project: https://github.com/bootboxjs/bootbox 5 | * @license: MIT http://bootboxjs.com/license.txt 6 | */ 7 | ((t,e)=>{'function'==typeof define&&define.amd?define(['jquery'],e):'object'==typeof exports?module.exports=e(require('jquery')):t.bootbox=e(t.jQuery)})(this,function e(s,p){let r={};r.VERSION='6.0.4';let l={en:{OK:'OK',CANCEL:'Cancel',CONFIRM:'OK'}},c={dialog:'',header:'',footer:'',closeButton:'',form:'
',button:'',option:'',promptMessage:'
',inputs:{text:'',textarea:'',email:'',select:'',checkbox:'
',radio:'
',date:'',time:'',number:'',password:'',range:''}},u={locale:'en',backdrop:'static',animate:!0,className:null,closeButton:!0,show:!0,container:'body',value:'',inputType:'text',errorMessage:null,swapButtonOrder:!1,centerVertical:!1,multiple:!1,scrollable:!1,reusable:!1,relatedTarget:null,size:null,id:null};function i(t,e,o){return s.extend(!0,{},t,((t,e)=>{var o=t.length;let a={};if(o<1||2{var r={};for(let t=0,e=o.length;t((e=l[e])||l.en)[t])(n,a)}}return r})(e=r&&r[0]&&(o=r[0].locale||u.locale,r[0].swapButtonOrder||u.swapButtonOrder)?e.reverse():e,o)};{t=i(t,r,a);var n=e;let o={};return f(n,function(t,e){o[e]=!0}),f(t.buttons,function(t){if(o[t]===p)throw new Error('button key "'+t+'" is not allowed (options are '+n.join(' ')+')')}),t}}function b(t){return Object.keys(t).length}function f(t,o){let a=0;s.each(t,function(t,e){o(t,e,a++)})}function m(t){t.data.dialog.find('.bootbox-accept').first().trigger('focus')}function h(t){t.target===t.data.dialog[0]&&t.data.dialog.remove()}function w(t){t.target===t.data.dialog[0]&&(t.data.dialog.off('escape.close.bb'),t.data.dialog.off('click'))}function g(t,e,o){t.stopPropagation(),t.preventDefault(),'function'==typeof o&&!1===o.call(e,t)||e.modal('hide')}function y(t){return/([01][0-9]|2[0-3]):[0-5][0-9]?:[0-5][0-9]/.test(t)}function v(t){return/(\d{4})-(\d{2})-(\d{2})/.test(t)}return r.locales=function(t){return t?l[t]:l},r.addLocale=function(t,o){return s.each(['OK','CANCEL','CONFIRM'],function(t,e){if(!o[e])throw new Error('Please supply a translation for "'+e+'"')}),l[t]={OK:o.OK,CANCEL:o.CANCEL,CONFIRM:o.CONFIRM},r},r.removeLocale=function(t){if('en'===t)throw new Error('"en" is used as the default and fallback locale and cannot be removed.');return delete l[t],r},r.setLocale=function(t){return r.setDefaults('locale',t)},r.setDefaults=function(){let t={};return 2===arguments.length?t[arguments[0]]=arguments[1]:t=arguments[0],s.extend(u,t),r},r.hideAll=function(){return s('.bootbox').modal('hide'),r},r.init=function(t){return e(t||s)},r.dialog=function(t){if(s.fn.modal===p)throw new Error('"$.fn.modal" is not defined; please double check you have included the Bootstrap JavaScript library. See https://getbootstrap.com/docs/5.3/getting-started/introduction/ for more details.');t=(a=>{let r,n;if('object'!=typeof a)throw new Error('Please supply an object of options');if(a.message)return(a=s.extend({},u,a)).backdrop?a.backdrop='string'!=typeof a.backdrop||'static'!==a.backdrop.toLowerCase()||'static':a.backdrop=!1!==a.backdrop&&0!==a.backdrop&&'static',a.buttons||(a.buttons={}),r=a.buttons,n=b(r),f(r,function(t,e,o){if('object'!=typeof(e='function'==typeof e?r[t]={callback:e}:e))throw new Error('button with key "'+t+'" must be an object');if(e.label||(e.label=t),!e.className){let t=!1;t=a.swapButtonOrder?0===o:o===n-1,n<=2&&t?e.className='btn-primary':e.className='btn-secondary btn-default'}}),a;throw new Error('"message" option must not be null or an empty string.')})(t),s.fn.modal.Constructor.VERSION?(t.fullBootstrapVersion=s.fn.modal.Constructor.VERSION,a=t.fullBootstrapVersion.indexOf('.'),t.bootstrap=t.fullBootstrapVersion.substring(0,a)):(t.bootstrap='2',t.fullBootstrapVersion='2.3.2',console.warn('Bootbox will *mostly* work with Bootstrap 2, but we do not officially support it. Please upgrade, if possible.'));let o=s(c.dialog);var e=o.find('.modal-dialog'),a=o.find('.modal-body'),r=s(c.header);let n=s(c.footer);var i=t.buttons;let l={onEscape:t.onEscape};if(a.find('.bootbox-body').html(t.message),0{let a=!1,r=!0,n=!0;if('date'===t)e===p||(r=v(e))?o===p||(n=v(o))||console.warn('Browsers which natively support the "date" input type expect date values to be of the form "YYYY-MM-DD" (see ISO-8601 https://www.iso.org/iso-8601-date-and-time-format.html). Bootbox does not enforce this rule, but your max value may not be enforced by this browser.'):console.warn('Browsers which natively support the "date" input type expect date values to be of the form "YYYY-MM-DD" (see ISO-8601 https://www.iso.org/iso-8601-date-and-time-format.html). Bootbox does not enforce this rule, but your min value may not be enforced by this browser.');else if('time'===t){if(e!==p&&!(r=y(e)))throw new Error('"min" is not a valid time. See https://www.w3.org/TR/2012/WD-html-markup-20120315/datatypes.html#form.data.time for more information.');if(o!==p&&!(n=y(o)))throw new Error('"max" is not a valid time. See https://www.w3.org/TR/2012/WD-html-markup-20120315/datatypes.html#form.data.time for more information.')}else{if(e!==p&&isNaN(e))throw r=!1,new Error('"min" must be a valid number. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-min for more information.');if(o!==p&&isNaN(o))throw n=!1,new Error('"max" must be a valid number. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-max for more information.')}if(r&&n){if(o').attr('label',e.group)),o=r[e.group]);var a=s(c.option);a.attr('value',e.value).text(e.text),o.append(a)}),f(r,function(t,e){i.append(e)}),i.val(n.value),n.bootstrap<5&&i.removeClass('form-select').addClass('form-control');break;case'checkbox':let e=Array.isArray(n.value)?n.value:[n.value];if(!(l=n.inputOptions||[]).length)throw new Error('prompt with "inputType" set to "checkbox" requires at least one option');i=s('
'),f(l,function(t,o){if(o.value===p||o.text===p)throw new Error('each option needs a "value" property and a "text" property');let a=s(c.inputs[n.inputType]);a.find('input').attr('value',o.value),a.find('label').append('\n'+o.text),f(e,function(t,e){e===o.value&&a.find('input').prop('checked',!0)}),i.append(a)});break;case'radio':if(n.value!==p&&Array.isArray(n.value))throw new Error('prompt with "inputType" set to "radio" requires a single, non-array value for "value"');if(!(l=n.inputOptions||[]).length)throw new Error('prompt with "inputType" set to "radio" requires at least one option');i=s('
');let a=!0;f(l,function(t,e){if(e.value===p||e.text===p)throw new Error('each option needs a "value" property and a "text" property');var o=s(c.inputs[n.inputType]);o.find('input').attr('value',e.value),o.find('label').append('\n'+e.text),n.value!==p&&e.value===n.value&&(o.find('input').prop('checked',!0),a=!1),i.append(o)}),a&&i.find('input[type="radio"]').first().prop('checked',!0)}return t.append(i),t.on('submit',function(t){t.preventDefault(),t.stopPropagation(),e.find('.bootbox-accept').trigger('click')}),n.message&&''!==n.message.trim()&&(a=s(c.promptMessage).html(n.message),t.prepend(a)),n.message=t,(e=r.dialog(n)).off('shown.bs.modal',m),e.on('shown.bs.modal',function(){i.focus()}),!0===o&&e.modal('show'),e},r}); -------------------------------------------------------------------------------- /header.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootbox.js — alert, confirm, prompt, and flexible dialogs for the Bootstrap framework 3 | * @version: 6.0.4 4 | * @project: https://github.com/bootboxjs/bootbox 5 | * @license: MIT http://bootboxjs.com/license.txt 6 | */ -------------------------------------------------------------------------------- /karma-base.conf.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer'); 2 | process.env.CHROME_BIN = puppeteer.executablePath(); 3 | 4 | module.exports = function(params) { 5 | 'use strict'; 6 | return function(config) { 7 | return config.set({ 8 | basePath: '', 9 | frameworks: ['mocha', 'sinon-chai'], 10 | files: Array.prototype.concat( 11 | params.vendor, 12 | 13 | params.src || 'bootbox.js', 14 | 15 | ['tests/**/*.test.js'] 16 | ), 17 | exclude: [], 18 | reporters: ['dots', 'coverage', 'junit', 'progress', 'html'], 19 | port: 9876, 20 | colors: true, 21 | logLevel: config.LOG_INFO, 22 | autoWatch: true, 23 | browsers: ['ChromeHeadlessNoSandbox'], 24 | captureTimeout: 60000, 25 | singleRun: true, 26 | 27 | customLaunchers: { 28 | ChromeHeadlessNoSandbox: { 29 | base: 'ChromeHeadless', 30 | flags: ['--no-sandbox'] 31 | } 32 | }, 33 | 34 | coverageReporter: { 35 | type: 'html', 36 | dir: 'tests/coverage' 37 | }, 38 | 39 | junitReporter: { 40 | outputDir: 'tests/reports' 41 | }, 42 | 43 | htmlReporter: { 44 | outputFile: 'tests/unit-test-reports/units.html', 45 | 46 | // Optional 47 | pageTitle: 'Bootbox', 48 | subPageTitle: 'Unit test results, Grouped', 49 | groupSuites: true, 50 | useCompactStyle: true, 51 | useLegacyStyle: false, 52 | showOnlyFailed: false 53 | } 54 | }); 55 | }; 56 | }; -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | var baseConfig = require('./karma-base.conf'); 2 | 3 | module.exports = baseConfig({ 4 | vendor: [ 5 | 'tests/vendor/jquery-3.3.1.slim.min.js', 6 | 'tests/vendor/bootstrap-5.1.3.bundle.min.js' 7 | ], 8 | src: ['dist/bootbox.js', 'dist/bootbox.locales.js'] 9 | }); 10 | -------------------------------------------------------------------------------- /karma.conf.legacy.js: -------------------------------------------------------------------------------- 1 | var baseConfig = require('./karma-base.conf'); 2 | 3 | module.exports = baseConfig({ 4 | vendor: [ 5 | 'tests/vendor/jquery-3.3.1.slim.min.js', 6 | 'tests/vendor/bootstrap-4.6.1.bundle.min.js' 7 | ], 8 | src: ['dist/bootbox.js', 'dist/bootbox.locales.js'] 9 | }); 10 | -------------------------------------------------------------------------------- /locales/ar.js: -------------------------------------------------------------------------------- 1 | // locale : Arabic 2 | // author : Emad Omar 3 | bootbox.addLocale('ar', { 4 | OK: 'موافق', 5 | CANCEL: 'الغاء', 6 | CONFIRM: 'تأكيد' 7 | }); -------------------------------------------------------------------------------- /locales/az.js: -------------------------------------------------------------------------------- 1 | // locale : Azerbaijani 2 | // author : Valentin Belousov 3 | bootbox.addLocale('az', { 4 | OK: 'OK', 5 | CANCEL: 'İmtina et', 6 | CONFIRM: 'Təsdiq et' 7 | }); -------------------------------------------------------------------------------- /locales/bg_BG.js: -------------------------------------------------------------------------------- 1 | // locale : Bulgarian 2 | // author : mraiur 3 | bootbox.addLocale('bg-BG', { 4 | OK : 'Ок', 5 | CANCEL : 'Отказ', 6 | CONFIRM : 'Потвърждавам' 7 | }); -------------------------------------------------------------------------------- /locales/cs.js: -------------------------------------------------------------------------------- 1 | // locale : Czech 2 | // author : Lukáš Fryč 3 | bootbox.addLocale('cs', { 4 | OK : 'OK', 5 | CANCEL : 'Zrušit', 6 | CONFIRM : 'Potvrdit' 7 | }); -------------------------------------------------------------------------------- /locales/da.js: -------------------------------------------------------------------------------- 1 | // locale : Danish 2 | // author : Frederik Alkærsig 3 | bootbox.addLocale('da', { 4 | OK : 'OK', 5 | CANCEL : 'Annuller', 6 | CONFIRM : 'Accepter' 7 | }); -------------------------------------------------------------------------------- /locales/de.js: -------------------------------------------------------------------------------- 1 | // locale : German 2 | // author : Nick Payne 3 | bootbox.addLocale('de', { 4 | OK : 'OK', 5 | CANCEL : 'Abbrechen', 6 | CONFIRM : 'Akzeptieren' 7 | }); -------------------------------------------------------------------------------- /locales/el.js: -------------------------------------------------------------------------------- 1 | // locale : Greek 2 | // author : Tolis Emmanouilidis 3 | bootbox.addLocale('el', { 4 | OK : 'Εντάξει', 5 | CANCEL : 'Ακύρωση', 6 | CONFIRM : 'Επιβεβαίωση' 7 | }); -------------------------------------------------------------------------------- /locales/en.js: -------------------------------------------------------------------------------- 1 | // locale : English 2 | // author : Nick Payne 3 | bootbox.addLocale('en', { 4 | OK : 'OK', 5 | CANCEL : 'Cancel', 6 | CONFIRM : 'OK' 7 | }); -------------------------------------------------------------------------------- /locales/es.js: -------------------------------------------------------------------------------- 1 | // locale : Spanish 2 | // author : Ian Leckey 3 | bootbox.addLocale('es', { 4 | OK : 'OK', 5 | CANCEL : 'Cancelar', 6 | CONFIRM : 'Aceptar' 7 | }); -------------------------------------------------------------------------------- /locales/et.js: -------------------------------------------------------------------------------- 1 | // locale : Estonian 2 | // author : Pavel Krõlov 3 | bootbox.addLocale('et', { 4 | OK : 'OK', 5 | CANCEL : 'Katkesta', 6 | CONFIRM : 'OK' 7 | }); -------------------------------------------------------------------------------- /locales/eu.js: -------------------------------------------------------------------------------- 1 | // locale : Basque 2 | // author : Iker Ibarguren 3 | bootbox.addLocale('eu', { 4 | OK : 'OK', 5 | CANCEL : 'Ezeztatu', 6 | CONFIRM : 'Onartu' 7 | }); -------------------------------------------------------------------------------- /locales/fa.js: -------------------------------------------------------------------------------- 1 | // locale : Persian 2 | // author : Touhid Arastu 3 | bootbox.addLocale('fa', { 4 | OK : 'قبول', 5 | CANCEL : 'لغو', 6 | CONFIRM : 'تایید' 7 | }); -------------------------------------------------------------------------------- /locales/fi.js: -------------------------------------------------------------------------------- 1 | // locale : Finnish 2 | // author : Nick Payne 3 | bootbox.addLocale('fi', { 4 | OK : 'OK', 5 | CANCEL : 'Peruuta', 6 | CONFIRM : 'OK' 7 | }); -------------------------------------------------------------------------------- /locales/fr.js: -------------------------------------------------------------------------------- 1 | // locale : French 2 | // author : Nick Payne, Sebastien Andary 3 | bootbox.addLocale('fr', { 4 | OK : 'OK', 5 | CANCEL : 'Annuler', 6 | CONFIRM : 'Confirmer' 7 | }); -------------------------------------------------------------------------------- /locales/he.js: -------------------------------------------------------------------------------- 1 | // locale : Hebrew 2 | // author : Chen Alon 3 | bootbox.addLocale('he', { 4 | OK : 'אישור', 5 | CANCEL : 'ביטול', 6 | CONFIRM : 'אישור' 7 | }); -------------------------------------------------------------------------------- /locales/hr.js: -------------------------------------------------------------------------------- 1 | // locale : Croatian 2 | // author : Mario Bašić 3 | bootbox.addLocale('hr', { 4 | OK : 'OK', 5 | CANCEL : 'Odustani', 6 | CONFIRM : 'Potvrdi' 7 | }); -------------------------------------------------------------------------------- /locales/hu.js: -------------------------------------------------------------------------------- 1 | // locale : Hungarian 2 | // author : Márk Sági-Kazár 3 | bootbox.addLocale('hu', { 4 | OK : 'OK', 5 | CANCEL : 'Mégsem', 6 | CONFIRM : 'Megerősít' 7 | }); -------------------------------------------------------------------------------- /locales/id.js: -------------------------------------------------------------------------------- 1 | // locale : Indonesian 2 | // author : Budi Irawan 3 | bootbox.addLocale('id', { 4 | OK : 'OK', 5 | CANCEL : 'Batal', 6 | CONFIRM : 'OK' 7 | }); -------------------------------------------------------------------------------- /locales/it.js: -------------------------------------------------------------------------------- 1 | // locale : Italian 2 | // author : Mauro 3 | bootbox.addLocale('it', { 4 | OK : 'OK', 5 | CANCEL : 'Annulla', 6 | CONFIRM : 'Conferma' 7 | }); -------------------------------------------------------------------------------- /locales/ja.js: -------------------------------------------------------------------------------- 1 | // locale : Japanese 2 | // author : ms183 3 | bootbox.addLocale('ja', { 4 | OK : 'OK', 5 | CANCEL : 'キャンセル', 6 | CONFIRM : 'OK' 7 | }); 8 | -------------------------------------------------------------------------------- /locales/ka.js: -------------------------------------------------------------------------------- 1 | // locale : Georgian 2 | // author : Avtandil Kikabidze aka LONGMAN (@akalongman) 3 | bootbox.addLocale('ka', { 4 | OK : 'OK', 5 | CANCEL : 'გაუქმება', 6 | CONFIRM : 'დადასტურება' 7 | }); -------------------------------------------------------------------------------- /locales/ko.js: -------------------------------------------------------------------------------- 1 | // locale : Korean 2 | // author : rigning 3 | bootbox.addLocale('ko', { 4 | OK: 'OK', 5 | CANCEL: '취소', 6 | CONFIRM: '확인' 7 | }); -------------------------------------------------------------------------------- /locales/lt.js: -------------------------------------------------------------------------------- 1 | // locale : Lithuanian 2 | // author : Tomas 3 | bootbox.addLocale('lt', { 4 | OK : 'Gerai', 5 | CANCEL : 'Atšaukti', 6 | CONFIRM : 'Patvirtinti' 7 | }); -------------------------------------------------------------------------------- /locales/lv.js: -------------------------------------------------------------------------------- 1 | // locale : Latvian 2 | // author : Dmitry Bogatykh, Lauris BH 3 | bootbox.addLocale('lv', { 4 | OK : 'Labi', 5 | CANCEL : 'Atcelt', 6 | CONFIRM : 'Apstiprināt' 7 | }); -------------------------------------------------------------------------------- /locales/nl.js: -------------------------------------------------------------------------------- 1 | // locale : Dutch 2 | // author : Bas ter Vrugt 3 | bootbox.addLocale('nl', { 4 | OK : 'OK', 5 | CANCEL : 'Annuleren', 6 | CONFIRM : 'Accepteren' 7 | }); -------------------------------------------------------------------------------- /locales/no.js: -------------------------------------------------------------------------------- 1 | // locale : Norwegian 2 | // author : Nils Magnus Englund 3 | bootbox.addLocale('no', { 4 | OK : 'OK', 5 | CANCEL : 'Avbryt', 6 | CONFIRM : 'OK' 7 | }); -------------------------------------------------------------------------------- /locales/pl.js: -------------------------------------------------------------------------------- 1 | // locale : Polish 2 | // author : Szczepan Cieślik 3 | bootbox.addLocale('pl', { 4 | OK : 'OK', 5 | CANCEL : 'Anuluj', 6 | CONFIRM : 'Potwierdź' 7 | }); -------------------------------------------------------------------------------- /locales/pt.js: -------------------------------------------------------------------------------- 1 | // locale : Portuguese 2 | // author : Cláudio Medina 3 | bootbox.addLocale('pt', { 4 | OK : 'OK', 5 | CANCEL : 'Cancelar', 6 | CONFIRM : 'Confirmar' 7 | }); -------------------------------------------------------------------------------- /locales/pt_BR.js: -------------------------------------------------------------------------------- 1 | // locale : Portuguese (Brasil) 2 | // author : Nick Payne 3 | bootbox.addLocale('pt-BR', { 4 | OK : 'OK', 5 | CANCEL : 'Cancelar', 6 | CONFIRM : 'Sim' 7 | }); -------------------------------------------------------------------------------- /locales/ru.js: -------------------------------------------------------------------------------- 1 | // locale : Russian 2 | // author : ionian-wind 3 | bootbox.addLocale('ru', { 4 | OK : 'OK', 5 | CANCEL : 'Отмена', 6 | CONFIRM : 'Применить' 7 | }); -------------------------------------------------------------------------------- /locales/sk.js: -------------------------------------------------------------------------------- 1 | // locale : Slovak 2 | // author : Stano Paška 3 | bootbox.addLocale('sk', { 4 | OK : 'OK', 5 | CANCEL : 'Zrušiť', 6 | CONFIRM : 'Potvrdiť' 7 | }); -------------------------------------------------------------------------------- /locales/sl.js: -------------------------------------------------------------------------------- 1 | // locale : Slovenian 2 | // author : @metalcamp 3 | bootbox.addLocale('sl', { 4 | OK : 'OK', 5 | CANCEL : 'Prekliči', 6 | CONFIRM : 'Potrdi' 7 | }); -------------------------------------------------------------------------------- /locales/sq.js: -------------------------------------------------------------------------------- 1 | // locale : Albanian 2 | // author : Knut Hühne 3 | bootbox.addLocale('sq', { 4 | OK : 'OK', 5 | CANCEL : 'Anulo', 6 | CONFIRM : 'Prano' 7 | }); -------------------------------------------------------------------------------- /locales/sv.js: -------------------------------------------------------------------------------- 1 | // locale : Swedish 2 | // author : Mattias Reichel 3 | bootbox.addLocale('sv', { 4 | OK : 'OK', 5 | CANCEL : 'Avbryt', 6 | CONFIRM : 'OK' 7 | }); -------------------------------------------------------------------------------- /locales/sw.js: -------------------------------------------------------------------------------- 1 | // locale : Swahili 2 | // author : Timothy Anyona 3 | bootbox.addLocale('sw', { 4 | OK : 'Sawa', 5 | CANCEL : 'Ghairi', 6 | CONFIRM : 'Thibitisha' 7 | }); -------------------------------------------------------------------------------- /locales/ta.js: -------------------------------------------------------------------------------- 1 | // locale : Tamil 2 | // author : Kolappan Nathan 3 | bootbox.addLocale('ta', { 4 | OK : 'சரி', 5 | CANCEL : 'ரத்து செய்', 6 | CONFIRM : 'உறுதி செய்' 7 | }); -------------------------------------------------------------------------------- /locales/th.js: -------------------------------------------------------------------------------- 1 | // locale : Thai 2 | // author : Ishmael๛ 3 | bootbox.addLocale('th', { 4 | OK : 'ตกลง', 5 | CANCEL : 'ยกเลิก', 6 | CONFIRM : 'ยืนยัน' 7 | }); -------------------------------------------------------------------------------- /locales/tr.js: -------------------------------------------------------------------------------- 1 | // locale : Turkish 2 | // author : Enes Karaca 3 | bootbox.addLocale('tr', { 4 | OK : 'Tamam', 5 | CANCEL : 'İptal', 6 | CONFIRM : 'Onayla' 7 | }); -------------------------------------------------------------------------------- /locales/uk.js: -------------------------------------------------------------------------------- 1 | // locale : Ukrainian 2 | // author : OlehBoiko 3 | bootbox.addLocale('uk', { 4 | OK : 'OK', 5 | CANCEL : 'Відміна', 6 | CONFIRM : 'Прийняти' 7 | }); -------------------------------------------------------------------------------- /locales/vi.js: -------------------------------------------------------------------------------- 1 | // locale : Vietnamese 2 | // author : Anh Tu Nguyen 3 | bootbox.addLocale('vi', { 4 | OK : 'OK', 5 | CANCEL : 'Hủy bỏ', 6 | CONFIRM : 'Xác nhận' 7 | }); -------------------------------------------------------------------------------- /locales/zh_CN.js: -------------------------------------------------------------------------------- 1 | // locale : Chinese (China / People's Republic of China) 2 | // author : Nick Payne 3 | bootbox.addLocale('zh-CN', { 4 | OK : 'OK', 5 | CANCEL : '取消', 6 | CONFIRM : '确认' 7 | }); -------------------------------------------------------------------------------- /locales/zh_TW.js: -------------------------------------------------------------------------------- 1 | // locale : Chinese (Taiwan / Republic of China) 2 | // author : Nick Payne 3 | bootbox.addLocale('zh-TW', { 4 | OK : 'OK', 5 | CANCEL : '取消', 6 | CONFIRM : '確認' 7 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootbox", 3 | "version": "6.0.4", 4 | "description": "Wrappers for JavaScript alert(), confirm(), prompt(), and other flexible dialogs using the Bootstrap framework", 5 | "directories": { 6 | "test": "tests" 7 | }, 8 | "scripts": { 9 | "test": "karma start karma.conf.legacy.js && karma start karma.conf.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/bootboxjs/bootbox.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/bootboxjs/bootbox/issues" 17 | }, 18 | "author": "Nick Payne ", 19 | "license": "MIT", 20 | "readmeFilename": "README.md", 21 | "main": "dist/bootbox.js", 22 | "files": [ 23 | "dist/*.js" 24 | ], 25 | "devDependencies": { 26 | "@popperjs/core": "^2.11.8", 27 | "@sinonjs/fake-timers": "^10.3.0", 28 | "bootstrap": "^5.3.3", 29 | "chai": "^4.5.0", 30 | "eslint": "^8.57.1", 31 | "eslint-config-standard": "^17.1.0", 32 | "eslint-plugin-import": "^2.31.0", 33 | "eslint-plugin-node": "^11.1.0", 34 | "eslint-plugin-promise": "^6.6.0", 35 | "grunt": "^1.6.1", 36 | "grunt-contrib-concat": "^2.1.0", 37 | "grunt-contrib-jshint": "^3.2.0", 38 | "grunt-contrib-uglify": "^5.2.2", 39 | "grunt-jsbeautifier": "^0.2.13", 40 | "grunt-karma": "^4.0.2", 41 | "jquery": "^3.7.1", 42 | "karma": "^6.4.4", 43 | "karma-chai": "^0.1.0", 44 | "karma-chrome-launcher": "^3.2.0", 45 | "karma-coverage": "^2.2.1", 46 | "karma-htmlfile-reporter": "^0.3.8", 47 | "karma-junit-reporter": "^2.0.1", 48 | "karma-mocha": "^2.0.1", 49 | "karma-sinon-chai": "^2.0.2", 50 | "karma-webpack": "^5.0.1", 51 | "mocha": "^10.8.2", 52 | "puppeteer": "^23.11.1", 53 | "sinon": "^15.2.0", 54 | "sinon-chai": "^3.7.0", 55 | "uglify-js": "^3.19.3", 56 | "webpack": "^5.98.0" 57 | }, 58 | "peerDependencies": { 59 | "@popperjs/core": "^2.0.0", 60 | "bootstrap": "^4.4.0 || ^5.0.0", 61 | "jquery": "^3.5.1" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /templates/umd-footer.txt: -------------------------------------------------------------------------------- 1 | 2 | })); -------------------------------------------------------------------------------- /templates/umd-header-locales.txt: -------------------------------------------------------------------------------- 1 | /*! @preserve 2 | * bootbox.locales.js 3 | * version: 6.0.4 4 | * author: Nick Payne 5 | * license: MIT 6 | * http://bootboxjs.com/ 7 | */ 8 | (function (global, factory) { 9 | 'use strict'; 10 | if (typeof define === 'function' && define.amd) { 11 | define(['bootbox'], factory); 12 | } else if (typeof module === 'object' && module.exports) { 13 | factory(require('./bootbox')); 14 | } else { 15 | factory(global.bootbox); 16 | } 17 | }(this, function (bootbox) { 18 | 'use strict'; 19 | -------------------------------------------------------------------------------- /templates/umd-header.txt: -------------------------------------------------------------------------------- 1 | /*! @preserve 2 | * bootbox.js 3 | * version: 6.0.4 4 | * author: Nick Payne 5 | * license: MIT 6 | * http://bootboxjs.com/ 7 | */ 8 | (function (root, factory) { 9 | 'use strict'; 10 | if (typeof define === 'function' && define.amd) { 11 | // AMD 12 | define(['jquery'], factory); 13 | } else if (typeof exports === 'object') { 14 | // Node, CommonJS-like 15 | module.exports = factory(require('jquery')); 16 | } else { 17 | // Browser globals (root is window) 18 | root.bootbox = factory(root.jQuery); 19 | } 20 | }(this, function init($, undefined) { 21 | 'use strict'; 22 | -------------------------------------------------------------------------------- /tests/alert.test.js: -------------------------------------------------------------------------------- 1 | describe('bootbox.alert', function() { 2 | 'use strict'; 3 | var self; 4 | var _this = this; 5 | _this.bootstrapVersion = function() { 6 | let fullVersion = $.fn.modal.Constructor.VERSION; 7 | let i = fullVersion.indexOf('.'); 8 | return fullVersion.substring(0, i); 9 | }; 10 | 11 | beforeEach(function() { 12 | self = this; 13 | 14 | this.text = function(selector) { 15 | return this.find(selector).text(); 16 | }; 17 | 18 | this.find = function(selector) { 19 | return this.dialog.find(selector); 20 | }; 21 | }); 22 | 23 | describe('basic usage tests', function() { 24 | describe('with no arguments', function() { 25 | beforeEach(function() { 26 | this.create = function() { 27 | bootbox.alert(); 28 | }; 29 | }); 30 | 31 | it('throws an error regarding argument length', function() { 32 | expect(this.create).to.throw(/argument length/); 33 | }); 34 | }); 35 | 36 | describe('with one argument', function() { 37 | describe('where the argument is a string', function() { 38 | beforeEach(function() { 39 | this.dialog = bootbox.alert('Hello world!'); 40 | }); 41 | 42 | it('applies the bootbox-alert class to the dialog', function() { 43 | expect(this.dialog.hasClass('bootbox-alert')).to.be.true; 44 | }); 45 | 46 | it('shows the expected body copy', function() { 47 | expect(this.text('.bootbox-body')).to.equal('Hello world!'); 48 | }); 49 | 50 | it('shows an OK button', function() { 51 | expect(this.text('.modal-footer button:first')).to.equal('OK'); 52 | }); 53 | 54 | it('applies the primary class to the button', function() { 55 | expect(this.find('.modal-footer button:first').hasClass('btn-primary')).to.be.true; 56 | }); 57 | 58 | it('applies the bootbox-accept class to the button', function() { 59 | expect(this.find('.modal-footer button:first').hasClass('bootbox-accept')).to.be.true; 60 | }); 61 | 62 | it('shows a close button inside the header', function() { 63 | if(_this.bootstrapVersion() >= 5) { 64 | expect(this.text('.modal-header button')).to.equal(''); 65 | } 66 | else { 67 | expect(this.text('.modal-header button')).to.equal('×'); 68 | } 69 | }); 70 | 71 | it('applies the close class to the close button', function() { 72 | if(_this.bootstrapVersion() >= 5) { 73 | expect(this.find('.modal-header button').hasClass('btn-close')).to.be.true; 74 | } 75 | expect(this.find('.modal-header button').hasClass('close')).to.be.true; 76 | }); 77 | 78 | it('applies the correct aria-hidden attribute to the close button', function() { 79 | expect(this.find('button.close').attr('aria-hidden')).to.equal('true'); 80 | }); 81 | 82 | it('applies the correct class to the body', function() { 83 | expect($('body').hasClass('modal-open')).to.be.true; 84 | }); 85 | }); 86 | }); 87 | 88 | describe('with two arguments', function() { 89 | describe('where the second argument is not a function', function() { 90 | beforeEach(function() { 91 | this.create = function() { 92 | bootbox.alert('Hello world!', 'not a callback'); 93 | }; 94 | }); 95 | 96 | it('throws an error requiring a callback', function() { 97 | expect(this.create).to.throw(/alert requires the "callback" property to be a function when provided/); 98 | }); 99 | }); 100 | 101 | describe('where the second argument is a function', function() { 102 | beforeEach(function() { 103 | this.create = function() { 104 | self.dialog = bootbox.alert('Hello world!', function() {}); 105 | }; 106 | }); 107 | 108 | it('does not throw an error', function() { 109 | expect(this.create).not.to.throw(Error); 110 | }); 111 | }); 112 | }); 113 | 114 | describe('with three arguments', function() { 115 | beforeEach(function() { 116 | this.create = function() { 117 | bootbox.alert(1, 2, 3); 118 | }; 119 | }); 120 | 121 | it('throws an error regarding argument length', function() { 122 | expect(this.create).to.throw(/argument length/); 123 | }); 124 | }); 125 | 126 | }); 127 | 128 | describe('configuration options tests', function() { 129 | beforeEach(function() { 130 | this.options = { 131 | message: 'Hello world', 132 | callback: function() {} 133 | }; 134 | 135 | this.create = function() { 136 | self.dialog = bootbox.alert(this.options); 137 | }; 138 | 139 | describe('with a custom ok button', function() { 140 | beforeEach(function() { 141 | this.options.buttons = { 142 | ok: { 143 | label: 'Custom OK', 144 | className: 'btn-danger' 145 | } 146 | }; 147 | 148 | this.create(); 149 | 150 | this.button = this.dialog.find('.btn:first'); 151 | }); 152 | 153 | it('adds the correct ok button', function() { 154 | expect(this.button.text()).to.equal('Custom OK'); 155 | expect(this.button.hasClass('btn-danger')).to.be.true; 156 | expect(this.button.hasClass('bootbox-accept')).to.be.true; 157 | }); 158 | }); 159 | 160 | describe('with an unrecognised button key', function() { 161 | beforeEach(function() { 162 | this.options.buttons = { 163 | 'Another key': { 164 | label: 'Custom OK', 165 | className: 'btn-danger' 166 | } 167 | }; 168 | }); 169 | 170 | it('throws an error', function() { 171 | expect(this.create).to.throw('button key "Another key" is not allowed (options are ok)'); 172 | }); 173 | }); 174 | 175 | describe('with a custom title', function() { 176 | beforeEach(function() { 177 | this.options.title = 'Hello?'; 178 | this.create(); 179 | }); 180 | 181 | it('shows the correct title', function() { 182 | expect(this.text('.modal-title')).to.equal('Hello?'); 183 | }); 184 | }); 185 | }); 186 | }); 187 | 188 | describe('callback tests', function() { 189 | 190 | describe('with no callback', function() { 191 | beforeEach(function() { 192 | this.dialog = bootbox.alert({ 193 | message:'Hello!' 194 | }); 195 | 196 | this.hidden = sinon.spy(this.dialog, 'modal'); 197 | }); 198 | 199 | describe('when dismissing the dialog by clicking OK', function() { 200 | beforeEach(function() { 201 | this.dialog.find('.bootbox-accept').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 clicking the close button', function() { 210 | beforeEach(function() { 211 | this.dialog.find('.close').trigger('click'); 212 | }); 213 | 214 | it('should hide the modal', function() { 215 | expect(this.hidden).to.have.been.calledWithExactly('hide'); 216 | }); 217 | }); 218 | 219 | describe('when triggering the escape event', function() { 220 | beforeEach(function() { 221 | this.dialog.trigger('escape.close.bb'); 222 | }); 223 | 224 | it('should hide the modal', function() { 225 | expect(this.hidden).to.have.been.calledWithExactly('hide'); 226 | }); 227 | }); 228 | }); 229 | 230 | describe('with a simple callback', function() { 231 | beforeEach(function() { 232 | this.callback = sinon.spy(); 233 | 234 | this.dialog = bootbox.alert({ 235 | message:'Hello!', 236 | callback: this.callback 237 | }); 238 | 239 | this.hidden = sinon.spy(this.dialog, 'modal'); 240 | }); 241 | 242 | describe('when dismissing the dialog by clicking OK', function() { 243 | beforeEach(function() { 244 | this.dialog.find('.bootbox-accept').trigger('click'); 245 | }); 246 | 247 | it('should invoke the callback', function() { 248 | expect(this.callback).to.have.been.called; 249 | }); 250 | 251 | it('should pass the dialog as "this"', function() { 252 | expect(this.callback.thisValues[0]).to.equal(this.dialog); 253 | }); 254 | 255 | it('should hide the modal', function() { 256 | expect(this.hidden).to.have.been.calledWithExactly('hide'); 257 | }); 258 | }); 259 | 260 | describe('when clicking the close button', function() { 261 | beforeEach(function() { 262 | this.dialog.find('.close').trigger('click'); 263 | }); 264 | 265 | it('should invoke the callback', function() { 266 | expect(this.callback).to.have.been.called; 267 | }); 268 | 269 | it('should pass the dialog as "this"', function() { 270 | expect(this.callback.thisValues[0]).to.equal(this.dialog); 271 | }); 272 | 273 | it('should hide the modal', function() { 274 | expect(this.hidden).to.have.been.calledWithExactly('hide'); 275 | }); 276 | }); 277 | 278 | describe('when triggering the escape event', function() { 279 | beforeEach(function() { 280 | this.dialog.trigger('escape.close.bb'); 281 | }); 282 | 283 | it('should invoke the callback', function() { 284 | expect(this.callback).to.have.been.called; 285 | }); 286 | 287 | it('should pass the dialog as "this"', function() { 288 | expect(this.callback.thisValues[0]).to.equal(this.dialog); 289 | }); 290 | 291 | it('should hide the modal', function() { 292 | expect(this.hidden).to.have.been.calledWithExactly('hide'); 293 | }); 294 | }); 295 | }); 296 | 297 | describe('with a callback which returns false', function() { 298 | beforeEach(function() { 299 | this.callback = sinon.stub(); 300 | this.callback.returns(false); 301 | 302 | this.dialog = bootbox.alert({ 303 | message:'Hello!', 304 | callback: this.callback 305 | }); 306 | 307 | this.hidden = sinon.spy(this.dialog, 'modal'); 308 | }); 309 | 310 | describe('when dismissing the dialog by clicking OK', function() { 311 | beforeEach(function() { 312 | this.dialog.find('.bootbox-accept').trigger('click'); 313 | }); 314 | 315 | it('should invoke the callback', function() { 316 | expect(this.callback).to.have.been.called; 317 | }); 318 | 319 | it('should pass the dialog as "this"', function() { 320 | expect(this.callback.thisValues[0]).to.equal(this.dialog); 321 | }); 322 | 323 | it('should not hide the modal', function() { 324 | expect(this.hidden).not.to.have.been.called; 325 | }); 326 | }); 327 | 328 | describe('when clicking the close button', function() { 329 | beforeEach(function() { 330 | this.dialog.find('.close').trigger('click'); 331 | }); 332 | 333 | it('should invoke the callback', function() { 334 | expect(this.callback).to.have.been.called; 335 | }); 336 | 337 | it('should pass the dialog as "this"', function() { 338 | expect(this.callback.thisValues[0]).to.equal(this.dialog); 339 | }); 340 | 341 | it('should not hide the modal', function() { 342 | expect(this.hidden).not.to.have.been.called; 343 | }); 344 | }); 345 | 346 | describe('when triggering the escape event', function() { 347 | beforeEach(function() { 348 | this.dialog.trigger('escape.close.bb'); 349 | }); 350 | 351 | it('should invoke the callback', function() { 352 | expect(this.callback).to.have.been.called; 353 | }); 354 | 355 | it('should pass the dialog as "this"', function() { 356 | expect(this.callback.thisValues[0]).to.equal(this.dialog); 357 | }); 358 | 359 | it('should not hide the modal', function() { 360 | expect(this.hidden).not.to.have.been.called; 361 | }); 362 | }); 363 | }); 364 | }); 365 | }); 366 | -------------------------------------------------------------------------------- /tests/bootbox.test.js: -------------------------------------------------------------------------------- 1 | describe('Bootbox', function() { 2 | 'use strict'; 3 | 4 | it('is attached to the window object', function() { 5 | expect(window.bootbox).to.be.an('object'); 6 | }); 7 | 8 | it('exposes the correct public API', function() { 9 | expect(bootbox.alert).to.be.a('function'); 10 | expect(bootbox.confirm).to.be.a('function'); 11 | expect(bootbox.dialog).to.be.a('function'); 12 | expect(bootbox.setDefaults).to.be.a('function'); 13 | expect(bootbox.setLocale).to.be.a('function'); 14 | expect(bootbox.removeLocale).to.be.a('function'); 15 | expect(bootbox.locales).to.be.a('function'); 16 | expect(bootbox.hideAll).to.be.a('function'); 17 | }); 18 | 19 | describe('hideAll', function() { 20 | beforeEach(function() { 21 | this.hidden = sinon.spy($.fn, 'modal'); 22 | bootbox.hideAll(); 23 | }); 24 | 25 | afterEach(function() { 26 | this.hidden.restore(); 27 | }); 28 | 29 | it('should hide all .bootbox modals', function() { 30 | expect(this.hidden).to.have.been.calledWithExactly('hide'); 31 | }); 32 | }); 33 | 34 | describe('event listeners', function() { 35 | describe('hidden.bs.modal', function() { 36 | beforeEach(function() { 37 | this.dialog = bootbox.alert('hi'); 38 | 39 | this.removed = sinon.stub(this.dialog, 'remove'); 40 | 41 | this.e = function(target) { 42 | 43 | $(this.dialog).trigger($.Event('hidden.bs.modal', { 44 | target: target 45 | })); 46 | }; 47 | }); 48 | 49 | afterEach(function() { 50 | this.removed.restore(); 51 | }); 52 | 53 | describe('when triggered with the wrong target', function() { 54 | beforeEach(function() { 55 | this.e({an: 'object'}); 56 | }); 57 | 58 | it('does not remove the dialog', function() { 59 | expect(this.removed).not.to.have.been.called; 60 | }); 61 | }); 62 | 63 | describe('when triggered with the correct target', function() { 64 | beforeEach(function() { 65 | this.e(this.dialog.get(0)); 66 | }); 67 | 68 | it('removes the dialog', function() { 69 | expect(this.removed).to.have.been.called; 70 | }); 71 | }); 72 | }); 73 | }); 74 | 75 | describe('onHide option', function() { 76 | describe('hide.bs.modal', function() { 77 | beforeEach(function() { 78 | this.callback = sinon.spy(); 79 | this.dialog = bootbox.alert({ 80 | message: 'hi', 81 | onHide: this.callback 82 | }); 83 | 84 | this.e = function(target) { 85 | $(this.dialog).trigger($.Event('hide.bs.modal', { 86 | target: target 87 | })); 88 | }; 89 | }); 90 | 91 | describe('when triggered with the correct target', function() { 92 | beforeEach(function() { 93 | this.e(this.dialog.get(0)); 94 | }); 95 | 96 | it('has triggered onHide function', function() { 97 | expect(this.callback).to.have.been.called; 98 | }); 99 | }); 100 | }); 101 | }); 102 | 103 | describe('onHidden option', function() { 104 | describe('hidden.bs.modal', function() { 105 | beforeEach(function() { 106 | this.callback = sinon.spy(); 107 | this.dialog = bootbox.alert({ 108 | message: 'hi', 109 | onHidden: this.callback 110 | }); 111 | 112 | this.e = function(target) { 113 | $(this.dialog).trigger($.Event('hidden.bs.modal', { 114 | target: target 115 | })); 116 | }; 117 | }); 118 | 119 | describe('when triggered with the correct target', function() { 120 | beforeEach(function() { 121 | this.e(this.dialog.get(0)); 122 | }); 123 | 124 | it('has triggered onHidden function', function() { 125 | expect(this.callback).to.have.been.called; 126 | }); 127 | }); 128 | }); 129 | }); 130 | 131 | describe('onShow option', function() { 132 | describe('show.bs.modal', function() { 133 | beforeEach(function() { 134 | this.callback = sinon.spy(); 135 | this.dialog = bootbox.alert({ 136 | message: 'hi', 137 | onShow: this.callback 138 | }); 139 | }); 140 | 141 | describe('when triggered with the correct target', function() { 142 | it('has triggered onShow function', function() { 143 | expect(this.callback).to.have.been.called; 144 | }); 145 | }); 146 | }); 147 | }); 148 | 149 | describe('relatedTarget option', function() { 150 | describe('show.bs.modal', function() { 151 | var options; 152 | 153 | beforeEach(function() { 154 | this.callback = sinon.spy(); 155 | options = { 156 | message: 'hi', 157 | onShow: this.callback 158 | }; 159 | }); 160 | 161 | describe('when triggered with no related target', function() { 162 | it('has passed no related target to the callback', function() { 163 | bootbox.dialog(options); 164 | expect(this.callback.args[0][0].relatedTarget).to.equal(null); 165 | }); 166 | }); 167 | 168 | describe('when triggered with an invalid related target', function() { 169 | it('has passed an invalid related target to the callback', function() { 170 | options.relatedTarget = false; 171 | bootbox.dialog(options); 172 | expect(this.callback.args[0][0].relatedTarget).to.equal(false); 173 | }); 174 | }); 175 | 176 | describe('when triggered with a valid related target', function() { 177 | it('has passed the valid related target to the callback', function() { 178 | options.relatedTarget = $('').get(0); 179 | bootbox.dialog(options); 180 | expect(this.callback.args[0][0].relatedTarget.id).to.equal('trigger'); 181 | }); 182 | }); 183 | }); 184 | }); 185 | 186 | describe('If $.fn.modal is undefined', function() { 187 | beforeEach(function() { 188 | this.oldModal = window.jQuery.fn.modal; 189 | window.jQuery.fn.modal = undefined; 190 | }); 191 | 192 | afterEach(function() { 193 | window.jQuery.fn.modal = this.oldModal; 194 | }); 195 | 196 | describe('When invoking a dialog', function() { 197 | beforeEach(function() { 198 | try { 199 | bootbox.alert('Hi', function() {}); 200 | } catch (e) { 201 | this.e = e; 202 | } 203 | }); 204 | 205 | it('throws the correct error', function() { 206 | expect(this.e.message).to.contain('"$.fn.modal" is not defined'); 207 | }); 208 | }); 209 | }); 210 | 211 | describe('adding and removing locales', function() { 212 | 213 | describe('bootbox.addLocale', function() { 214 | describe('with invalid values', function() { 215 | beforeEach(function() { 216 | try { 217 | bootbox.addLocale('xy', { 218 | OK: 'BTN1' 219 | }); 220 | } catch (e) { 221 | this.e = e; 222 | } 223 | }); 224 | 225 | it('throws the expected error', function() { 226 | expect(this.e.message).to.equal('Please supply a translation for "CANCEL"'); 227 | }); 228 | }); 229 | 230 | describe('with valid values', function() { 231 | beforeEach(function() { 232 | bootbox 233 | .addLocale('xy', { 234 | OK: 'BTN1', 235 | CANCEL: 'BTN2', 236 | CONFIRM: 'BTN3' 237 | }) 238 | .setLocale('xy'); 239 | 240 | var d1 = bootbox.alert('foo'); 241 | var d2 = bootbox.confirm('foo', function() { return true; }); 242 | this.labels = { 243 | ok: d1.find('.btn:first').text(), 244 | cancel: d2.find('.btn:first').text(), 245 | confirm: d2.find('.btn:last').text() 246 | }; 247 | }); 248 | 249 | it('shows the expected OK translation', function() { 250 | expect(this.labels.ok).to.equal('BTN1'); 251 | }); 252 | it('shows the expected CANCEL translation', function() { 253 | expect(this.labels.cancel).to.equal('BTN2'); 254 | }); 255 | it('shows the expected PROMPT translation', function() { 256 | expect(this.labels.confirm).to.equal('BTN3'); 257 | }); 258 | }); 259 | }); 260 | 261 | describe('bootbox.removeLocale', function () { 262 | beforeEach(function () { 263 | bootbox.removeLocale('xy'); 264 | 265 | var d1 = bootbox.alert('foo'); 266 | var d2 = bootbox.confirm('foo', function () { return true; }); 267 | this.labels = { 268 | ok: d1.find('.btn:first').text(), 269 | cancel: d2.find('.btn:first').text(), 270 | confirm: d2.find('.btn:last').text() 271 | }; 272 | }); 273 | 274 | it('falls back to the default OK translation', function () { 275 | expect(this.labels.ok).to.equal('OK'); 276 | }); 277 | it('falls back to the default CANCEL translation', function () { 278 | expect(this.labels.cancel).to.equal('Cancel'); 279 | }); 280 | it('falls back to the default PROMPT translation', function () { 281 | expect(this.labels.confirm).to.equal('OK'); 282 | }); 283 | }); 284 | }); 285 | 286 | describe('backdrop variations', function() { 287 | beforeEach(function() { 288 | this.e = function(target) { 289 | $(this.dialog).trigger($.Event('click.dismiss.bs.modal', { 290 | target: target 291 | })); 292 | }; 293 | }); 294 | 295 | describe('with the default value', function() { 296 | beforeEach(function() { 297 | this.callback = sinon.spy(); 298 | this.dialog = bootbox.alert('hi', this.callback); 299 | }); 300 | 301 | describe('When triggering the backdrop click dismiss event', function() { 302 | beforeEach(function() { 303 | this.e({an: 'object'}); 304 | }); 305 | 306 | it('does not invoke the callback', function() { 307 | expect(this.callback).not.to.have.been.called; 308 | }); 309 | }); 310 | }); 311 | 312 | describe('when set to false', function() { 313 | beforeEach(function() { 314 | this.callback = sinon.spy(); 315 | this.dialog = bootbox.alert({ 316 | message: 'hi', 317 | callback: this.callback, 318 | backdrop: false 319 | }); 320 | }); 321 | 322 | describe('When triggering the backdrop click dismiss event', function() { 323 | describe('With the wrong target', function() { 324 | beforeEach(function() { 325 | this.e({an: 'object'}); 326 | }); 327 | 328 | it('does not invoke the callback', function() { 329 | expect(this.callback).not.to.have.been.called; 330 | }); 331 | }); 332 | 333 | describe('With the correct target', function() { 334 | beforeEach(function() { 335 | this.e(this.dialog.get(0)); 336 | }); 337 | 338 | it('invokes the callback', function() { 339 | expect(this.callback).not.to.have.been.called; 340 | }); 341 | }); 342 | }); 343 | }); 344 | 345 | describe('when set to true', function() { 346 | beforeEach(function() { 347 | this.callback = sinon.spy(); 348 | this.dialog = bootbox.alert({ 349 | message: 'hi', 350 | callback: this.callback, 351 | backdrop: true 352 | }); 353 | }); 354 | 355 | describe('When triggering the backdrop click dismiss event', function() { 356 | describe('With the wrong target', function() { 357 | beforeEach(function() { 358 | this.e({an: 'object'}); 359 | }); 360 | 361 | it('does not invoke the callback', function() { 362 | expect(this.callback).not.to.have.been.called; 363 | }); 364 | }); 365 | 366 | describe('With the correct target', function() { 367 | beforeEach(function() { 368 | this.e(this.dialog.children('.modal-backdrop').get(0)); 369 | }); 370 | 371 | it('invokes the callback', function() { 372 | expect(this.callback).to.have.been.called; 373 | }); 374 | 375 | it('should pass the dialog as "this"', function() { 376 | expect(this.callback.thisValues[0]).to.equal(this.dialog); 377 | }); 378 | }); 379 | }); 380 | }); 381 | }); 382 | 383 | describe('resuable: true dialog', function() { 384 | describe('hidden.bs.modal', function() { 385 | beforeEach(function() { 386 | this.dialog = bootbox.alert({ message: 'hi', reusable: true }); 387 | 388 | this.removed = sinon.stub(this.dialog, 'remove'); 389 | 390 | this.e = function(target) { 391 | $(this.dialog).trigger($.Event('hidden.bs.modal', { 392 | target: target 393 | })); 394 | }; 395 | }); 396 | 397 | afterEach(function() { 398 | this.removed.restore(); 399 | }); 400 | 401 | describe('when triggered with `reusable: true`', function() { 402 | beforeEach(function() { 403 | this.e({an: 'object'}); 404 | }); 405 | 406 | it('does not remove the dialog', function() { 407 | expect(this.removed).not.to.have.been.called; 408 | }); 409 | }); 410 | }); 411 | }); 412 | 413 | describe('resuable: false dialog', function() { 414 | describe('hidden.bs.modal', function() { 415 | beforeEach(function() { 416 | this.dialog = bootbox.alert({ message: 'hi', reusable: false }); 417 | 418 | this.removed = sinon.stub(this.dialog, 'remove'); 419 | 420 | this.e = function(target) { 421 | $(this.dialog).trigger($.Event('hidden.bs.modal', { 422 | target: target 423 | })); 424 | }; 425 | }); 426 | 427 | afterEach(function() { 428 | this.removed.restore(); 429 | }); 430 | 431 | describe('when triggered with `reusable: false`', function() { 432 | beforeEach(function() { 433 | this.e(this.dialog.get(0)); 434 | }); 435 | 436 | it('removes the dialog', function() { 437 | expect(this.removed).to.have.been.called; 438 | }); 439 | }); 440 | }); 441 | }); 442 | 443 | describe('resuable not set dialog', function() { 444 | describe('hidden.bs.modal', function() { 445 | beforeEach(function() { 446 | this.dialog = bootbox.alert({ message: 'hi' }); 447 | 448 | this.removed = sinon.stub(this.dialog, 'remove'); 449 | 450 | this.e = function(target) { 451 | $(this.dialog).trigger($.Event('hidden.bs.modal', { 452 | target: target 453 | })); 454 | }; 455 | }); 456 | 457 | afterEach(function() { 458 | this.removed.restore(); 459 | }); 460 | 461 | describe('when triggered with `reusable` not set', function() { 462 | beforeEach(function() { 463 | this.e(this.dialog.get(0)); 464 | }); 465 | 466 | it('removes the dialog', function() { 467 | expect(this.removed).to.have.been.called; 468 | }); 469 | }); 470 | }); 471 | }); 472 | }); 473 | -------------------------------------------------------------------------------- /tests/confirm.test.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | describe('bootbox.confirm', function() { 3 | 'use strict'; 4 | describe('basic usage tests', function() { 5 | describe('with one argument', function() { 6 | 7 | describe('where the argument is not an object', function() { 8 | beforeEach(function() { 9 | return this.create = function() { 10 | return bootbox.confirm('Are you sure?'); 11 | }; 12 | }); 13 | return it('throws an error', function() { 14 | return expect(this.create).throw('confirm requires a callback'); 15 | }); 16 | }); 17 | 18 | 19 | describe('where the argument is an object', function() { 20 | beforeEach(function() { 21 | this.options = {}; 22 | return this.create = (function(_this) { 23 | return function() { 24 | return _this.dialog = bootbox.confirm(_this.options); 25 | }; 26 | })(this); 27 | }); 28 | 29 | 30 | describe('with a message property', function() { 31 | beforeEach(function() { 32 | return this.options.message = 'Are you sure?'; 33 | }); 34 | return it('throws an error requiring a callback', function() { 35 | return expect(this.create).throw(/confirm requires a callback/); 36 | }); 37 | }); 38 | 39 | 40 | describe('with a callback property', function() { 41 | describe('where the callback is not a function', function() { 42 | beforeEach(function() { 43 | return this.options.callback = 'Are you sure?'; 44 | }); 45 | return it('throws an error requiring a callback', function() { 46 | return expect(this.create).throw(/confirm requires a callback/); 47 | }); 48 | }); 49 | 50 | describe('where the callback is a function', function() { 51 | beforeEach(function() { 52 | return this.options.callback = function() { 53 | return true; 54 | }; 55 | }); 56 | return it('throws an error requiring a message', function() { 57 | return expect(this.create).to.throw(/"message" option must not be null or an empty string./); 58 | }); 59 | }); 60 | }); 61 | 62 | 63 | describe('with a message and a callback', function() { 64 | beforeEach(function() { 65 | var _this = this; 66 | this.options = { 67 | callback: function() { 68 | return true; 69 | }, 70 | message: 'Are you sure?' 71 | }; 72 | 73 | return this.create = function() { 74 | _this.dialog = bootbox.confirm(_this.options); 75 | }; 76 | }); 77 | 78 | it('does not throw an error', function() { 79 | return expect(this.create).not.throw(Error); 80 | }); 81 | it('creates a dialog object', function() { 82 | return expect(this.dialog).to.be.an('object'); 83 | }); 84 | it('adds the correct button labels', function() { 85 | expect(this.dialog.find('.btn:first').text()).to.equal('Cancel'); 86 | return expect(this.dialog.find('.btn:last').text()).to.equal('OK'); 87 | }); 88 | return it('adds the correct button classes', function() { 89 | expect(this.dialog.find('.btn:first').hasClass('btn-default')).to.be.true; 90 | expect(this.dialog.find('.btn:first').hasClass('btn-secondary')).to.true; 91 | expect(this.dialog.find('.btn:first').hasClass('bootbox-cancel')).to.true; 92 | 93 | expect(this.dialog.find('.btn:last').hasClass('btn-primary')).to.true; 94 | return expect(this.dialog.find('.btn:last').hasClass('bootbox-accept')).to.true; 95 | }); 96 | }); 97 | }); 98 | }); 99 | 100 | 101 | describe('with two arguments', function() { 102 | describe('where the second argument is not a function', function() { 103 | beforeEach(function() { 104 | return this.create = (function(_this) { 105 | return function() { 106 | return _this.dialog = bootbox.confirm('Are you sure?', 'callback here'); 107 | }; 108 | })(this); 109 | }); 110 | return it('throws an error requiring a callback', function() { 111 | return expect(this.create).throw(/confirm requires a callback/); 112 | }); 113 | }); 114 | 115 | describe('where the second argument is a function', function() { 116 | beforeEach(function() { 117 | return this.create = (function(_this) { 118 | return function() { 119 | return _this.dialog = bootbox.confirm('Are you sure?', function() { 120 | return true; 121 | }); 122 | }; 123 | })(this); 124 | }); 125 | it('does not throw an error', function() { 126 | return expect(this.create).not.throw(Error); 127 | }); 128 | it('creates a dialog object', function() { 129 | return expect(this.dialog).to.be.an('object'); 130 | }); 131 | it('applies the bootbox-confirm class to the dialog', function() { 132 | return expect(this.dialog.hasClass('bootbox-confirm')).to.true; 133 | }); 134 | it('adds the correct button labels', function() { 135 | expect(this.dialog.find('.btn:first').text()).to.equal('Cancel'); 136 | return expect(this.dialog.find('.btn:last').text()).to.equal('OK'); 137 | }); 138 | it('adds the correct button classes', function() { 139 | expect(this.dialog.find('.btn:first').hasClass('btn-default')).to.true; 140 | expect(this.dialog.find('.btn:first').hasClass('btn-secondary')).to.true; 141 | expect(this.dialog.find('.btn:first').hasClass('bootbox-cancel')).to.true; 142 | 143 | expect(this.dialog.find('.btn:last').hasClass('btn-primary')).to.true; 144 | return expect(this.dialog.find('.btn:last').hasClass('bootbox-accept')).to.true; 145 | }); 146 | return it('shows the dialog', function() { 147 | return expect(this.dialog.is(':visible')).to.true; 148 | }); 149 | }); 150 | 151 | }); 152 | }); 153 | 154 | 155 | describe('configuration options tests', function() { 156 | beforeEach(function() { 157 | this.options = { 158 | message: 'Are you sure?', 159 | callback: function() { 160 | return true; 161 | } 162 | }; 163 | return this.create = (function(_this) { 164 | return function() { 165 | return _this.dialog = bootbox.confirm(_this.options); 166 | }; 167 | })(this); 168 | }); 169 | 170 | describe('with a custom cancel button', function() { 171 | beforeEach(function() { 172 | this.options.buttons = { 173 | cancel: { 174 | label: 'Custom cancel', 175 | className: 'btn-danger' 176 | } 177 | }; 178 | this.create(); 179 | return this.button = this.dialog.find('.btn:first'); 180 | }); 181 | return it('adds the correct cancel button', function() { 182 | expect(this.button.text()).to.equal('Custom cancel'); 183 | return expect(this.button.hasClass('btn-danger')).to.true; 184 | }); 185 | }); 186 | 187 | describe('with a custom confirm button', function() { 188 | beforeEach(function() { 189 | this.options.buttons = { 190 | confirm: { 191 | label: 'Custom confirm', 192 | className: 'btn-warning' 193 | } 194 | }; 195 | this.create(); 196 | return this.button = this.dialog.find('.btn:last'); 197 | }); 198 | return it('adds the correct confirm button', function() { 199 | expect(this.button.text()).to.equal('Custom confirm'); 200 | return expect(this.button.hasClass('btn-warning')).to.true; 201 | }); 202 | }); 203 | 204 | describe('with an unrecognised button key', function() { 205 | beforeEach(function() { 206 | return this.options.buttons = { 207 | 'Bad key': { 208 | label: 'Custom confirm', 209 | className: 'btn-warning' 210 | } 211 | }; 212 | }); 213 | return it('throws an error', function() { 214 | return expect(this.create).throw('button key "Bad key" is not allowed (options are cancel confirm)'); 215 | }); 216 | }); 217 | }); 218 | 219 | 220 | describe('callback tests', function() { 221 | describe('with a simple callback', function() { 222 | beforeEach(function() { 223 | this.callback = sinon.spy(); 224 | this.dialog = bootbox.confirm({ 225 | message: 'Are you sure?', 226 | callback: this.callback 227 | }); 228 | return this.hidden = sinon.spy(this.dialog, 'modal'); 229 | }); 230 | 231 | describe('when dismissing the dialog by clicking OK', function() { 232 | beforeEach(function() { 233 | return this.dialog.find('.bootbox-accept').trigger('click'); 234 | }); 235 | it('should invoke the callback', function() { 236 | return expect(this.callback).to.have.been.called; 237 | }); 238 | it('should pass the dialog as "this"', function() { 239 | return expect(this.callback.thisValues[0]).to.equal(this.dialog); 240 | }); 241 | it('with the correct value', function() { 242 | return expect(this.callback).to.have.been.calledWithExactly(true); 243 | }); 244 | return it('should hide the modal', function() { 245 | return expect(this.hidden).to.have.been.calledWithExactly('hide'); 246 | }); 247 | }); 248 | 249 | describe('when dismissing the dialog by clicking Cancel', function() { 250 | beforeEach(function() { 251 | return this.dialog.find('.bootbox-cancel').trigger('click'); 252 | }); 253 | it('should invoke the callback', function() { 254 | return expect(this.callback).to.have.been.called; 255 | }); 256 | it('should pass the dialog as "this"', function() { 257 | return expect(this.callback.thisValues[0]).to.equal(this.dialog); 258 | }); 259 | it('with the correct value', function() { 260 | return expect(this.callback).to.have.been.calledWithExactly(false); 261 | }); 262 | return it('should hide the modal', function() { 263 | return expect(this.hidden).to.have.been.calledWithExactly('hide'); 264 | }); 265 | }); 266 | 267 | describe('when triggering the escape event', function() { 268 | beforeEach(function() { 269 | return this.dialog.trigger('escape.close.bb'); 270 | }); 271 | it('should invoke the callback', function() { 272 | return expect(this.callback).to.have.been.called; 273 | }); 274 | it('should pass the dialog as "this"', function() { 275 | return expect(this.callback.thisValues[0]).to.equal(this.dialog); 276 | }); 277 | it('with the correct value', function() { 278 | return expect(this.callback).to.have.been.calledWithExactly(false); 279 | }); 280 | return it('should hide the modal', function() { 281 | return expect(this.hidden).to.have.been.calledWithExactly('hide'); 282 | }); 283 | }); 284 | }); 285 | 286 | 287 | describe('with a callback which returns false', function() { 288 | beforeEach(function() { 289 | this.callback = sinon.stub(); 290 | this.callback.returns(false); 291 | this.dialog = bootbox.confirm({ 292 | message: 'Are you sure?', 293 | callback: this.callback 294 | }); 295 | return this.hidden = sinon.spy(this.dialog, 'modal'); 296 | }); 297 | 298 | describe('when dismissing the dialog by clicking OK', function() { 299 | beforeEach(function() { 300 | return this.dialog.find('.bootbox-accept').trigger('click'); 301 | }); 302 | it('should invoke the callback', function() { 303 | return expect(this.callback).to.have.been.called; 304 | }); 305 | it('should pass the dialog as "this"', function() { 306 | return expect(this.callback.thisValues[0]).to.equal(this.dialog); 307 | }); 308 | it('with the correct value', function() { 309 | return expect(this.callback).to.have.been.calledWithExactly(true); 310 | }); 311 | return it('should not hide the modal', function() { 312 | return expect(this.hidden).not.to.have.been.called; 313 | }); 314 | }); 315 | 316 | describe('when dismissing the dialog by clicking Cancel', function() { 317 | beforeEach(function() { 318 | return this.dialog.find('.bootbox-cancel').trigger('click'); 319 | }); 320 | it('should invoke the callback', function() { 321 | return expect(this.callback).to.have.been.called; 322 | }); 323 | it('should pass the dialog as "this"', function() { 324 | return expect(this.callback.thisValues[0]).to.equal(this.dialog); 325 | }); 326 | it('with the correct value', function() { 327 | return expect(this.callback).to.have.been.calledWithExactly(false); 328 | }); 329 | return it('should not hide the modal', function() { 330 | return expect(this.hidden).not.to.have.been.called; 331 | }); 332 | }); 333 | 334 | describe('when triggering the escape event', function() { 335 | beforeEach(function() { 336 | return this.dialog.trigger('escape.close.bb'); 337 | }); 338 | it('should invoke the callback', function() { 339 | return expect(this.callback).to.have.been.called; 340 | }); 341 | it('should pass the dialog as "this"', function() { 342 | return expect(this.callback.thisValues[0]).to.equal(this.dialog); 343 | }); 344 | it('with the correct value', function() { 345 | return expect(this.callback).to.have.been.calledWithExactly(false); 346 | }); 347 | return it('should not hide the modal', function() { 348 | return expect(this.hidden).not.to.have.been.called; 349 | }); 350 | }); 351 | 352 | }); 353 | }); 354 | }); 355 | -------------------------------------------------------------------------------- /tests/defaults.test.js: -------------------------------------------------------------------------------- 1 | describe('bootbox.setDefaults', function() { 2 | 'use strict'; 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 extra-large', function() { 76 | beforeEach(function() { 77 | bootbox.setDefaults({ 78 | size: 'extra-large' 79 | }); 80 | 81 | this.dialog = bootbox.dialog({ 82 | message: 'test' 83 | }); 84 | }); 85 | 86 | it('adds the extra-large class to the innerDialog', function() { 87 | expect(this.dialog.children('.modal-dialog').hasClass('modal-xl')).to.be.true; 88 | }); 89 | }); 90 | describe('when set to xl', function() { 91 | beforeEach(function() { 92 | bootbox.setDefaults({ 93 | size: 'xl' 94 | }); 95 | 96 | this.dialog = bootbox.dialog({ 97 | message: 'test' 98 | }); 99 | }); 100 | 101 | it('adds the extra-large class to the innerDialog', function() { 102 | expect(this.dialog.children('.modal-dialog').hasClass('modal-xl')).to.be.true; 103 | }); 104 | }); 105 | 106 | describe('when set to extra-large', function() { 107 | beforeEach(function() { 108 | bootbox.setDefaults({ 109 | size: 'extra-large' 110 | }); 111 | 112 | this.dialog = bootbox.dialog({ 113 | message: 'test' 114 | }); 115 | }); 116 | 117 | it('adds the extra-large class to the innerDialog', function() { 118 | expect(this.dialog.children('.modal-dialog').hasClass('modal-xl')).to.be.true; 119 | }); 120 | }); 121 | describe('when set to xl', function() { 122 | beforeEach(function() { 123 | bootbox.setDefaults({ 124 | size: 'xl' 125 | }); 126 | 127 | this.dialog = bootbox.dialog({ 128 | message: 'test' 129 | }); 130 | }); 131 | 132 | it('adds the extra-large class to the innerDialog', function() { 133 | expect(this.dialog.children('.modal-dialog').hasClass('modal-xl')).to.be.true; 134 | }); 135 | }); 136 | 137 | describe('when set to large', function() { 138 | beforeEach(function() { 139 | bootbox.setDefaults({ 140 | size: 'large' 141 | }); 142 | 143 | this.dialog = bootbox.dialog({ 144 | message: 'test' 145 | }); 146 | }); 147 | 148 | it('adds the large class to the innerDialog', function() { 149 | expect(this.dialog.children('.modal-dialog').hasClass('modal-lg')).to.be.true; 150 | }); 151 | }); 152 | describe('when set to lg', function() { 153 | beforeEach(function() { 154 | bootbox.setDefaults({ 155 | size: 'lg' 156 | }); 157 | 158 | this.dialog = bootbox.dialog({ 159 | message: 'test' 160 | }); 161 | }); 162 | 163 | it('adds the large class to the innerDialog', function() { 164 | expect(this.dialog.children('.modal-dialog').hasClass('modal-lg')).to.be.true; 165 | }); 166 | }); 167 | 168 | describe('when set to small', function() { 169 | beforeEach(function() { 170 | bootbox.setDefaults({ 171 | size: 'small' 172 | }); 173 | 174 | this.dialog = bootbox.dialog({ 175 | message: 'test' 176 | }); 177 | }); 178 | 179 | it('adds the small class to the innerDialog', function() { 180 | expect(this.dialog.children('.modal-dialog').hasClass('modal-sm')).to.be.true; 181 | }); 182 | }); 183 | describe('when set to sm', function() { 184 | beforeEach(function() { 185 | bootbox.setDefaults({ 186 | size: 'sm' 187 | }); 188 | 189 | this.dialog = bootbox.dialog({ 190 | message: 'test' 191 | }); 192 | }); 193 | 194 | it('adds the small class to the innerDialog', function() { 195 | expect(this.dialog.children('.modal-dialog').hasClass('modal-sm')).to.be.true; 196 | }); 197 | }); 198 | }); 199 | 200 | describe('backdrop', function() { 201 | describe('when set to false', function() { 202 | beforeEach(function() { 203 | bootbox.setDefaults({ 204 | backdrop: false 205 | }); 206 | 207 | this.dialog = bootbox.dialog({ 208 | message: 'test' 209 | }); 210 | }); 211 | 212 | it('does not show a backdrop', function() { 213 | expect(this.dialog.next('.modal-backdrop').length).to.equal(0); 214 | }); 215 | }); 216 | }); 217 | 218 | describe('centerVertical', function() { 219 | describe('when set to true', function() { 220 | beforeEach(function() { 221 | bootbox.setDefaults({ 222 | centerVertical: true 223 | }); 224 | 225 | this.dialog = bootbox.dialog({ 226 | message: 'test' 227 | }); 228 | }); 229 | 230 | it('adds the modal-dialog-centered class to the innerDialog', function() { 231 | expect(this.dialog.children('.modal-dialog').hasClass('modal-dialog-centered')).to.be.true; 232 | }); 233 | }); 234 | }); 235 | 236 | describe('scrollable', function() { 237 | describe('when set to true', function() { 238 | beforeEach(function() { 239 | bootbox.setDefaults({ 240 | scrollable: true 241 | }); 242 | 243 | this.dialog = bootbox.dialog({ 244 | message: 'test' 245 | }); 246 | }); 247 | 248 | it('adds the modal-dialog-scrollable class to the innerDialog', function() { 249 | expect(this.dialog.children('.modal-dialog').hasClass('modal-dialog-scrollable')).to.be.true; 250 | }); 251 | }); 252 | }); 253 | 254 | describe('when passed two arguments', function() { 255 | beforeEach(function() { 256 | bootbox.setDefaults('className', 'my-class'); 257 | this.dialog = bootbox.dialog({ 258 | message: 'test' 259 | }); 260 | }); 261 | 262 | it('applies the arguments as a key/value pair', function() { 263 | expect(this.dialog.hasClass('bootbox')).to.be.true; 264 | expect(this.dialog.hasClass('my-class')).to.be.true; 265 | }); 266 | }); 267 | 268 | describe('container', function () { 269 | describe('when not explicitly set', function() { 270 | beforeEach(function() { 271 | this.dialog = bootbox.dialog({ 272 | message: 'test' 273 | }); 274 | }); 275 | 276 | it('defaults to the body element', function() { 277 | expect(this.dialog.parent().is('body')).to.be.true; 278 | }); 279 | }); 280 | 281 | describe('when explicitly set to body', function() { 282 | beforeEach(function() { 283 | bootbox.setDefaults({ 284 | container: 'body' 285 | }); 286 | 287 | this.dialog = bootbox.dialog({ 288 | message: 'test' 289 | }); 290 | }); 291 | 292 | it('sets the correct parent element', function() { 293 | expect(this.dialog.parent().is('body')).to.be.true; 294 | }); 295 | }); 296 | 297 | describe('when set to another dom element', function() { 298 | 299 | beforeEach(function() { 300 | this.container = $('
'); 301 | bootbox.setDefaults({ 302 | container: this.container 303 | }); 304 | 305 | this.dialog = bootbox.dialog({ 306 | message: 'test' 307 | }); 308 | }); 309 | 310 | it('sets the correct parent element', function() { 311 | expect(this.dialog.parent().is(this.container)).to.be.true; 312 | }); 313 | }); 314 | }); 315 | }); 316 | -------------------------------------------------------------------------------- /tests/dialog.test.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | describe('bootbox.dialog', function() { 3 | 'use strict'; 4 | beforeEach(function() { 5 | this.find = function(s) { 6 | return this.dialog.find(s); 7 | }; 8 | this.exists = function(s) { 9 | return this.find(s).length !== 0; 10 | }; 11 | this['class'] = function(s, c) { 12 | return this.find(s).hasClass(c); 13 | }; 14 | this.invoke = function(s, m) { 15 | return this.find(s)[m](); 16 | }; 17 | this.text = function(s) { 18 | return this.invoke(s, 'text'); 19 | }; 20 | return this.html = function(s) { 21 | return this.invoke(s, 'html'); 22 | }; 23 | }); 24 | describe('invalid usage tests', function() { 25 | describe('with no arguments', function() { 26 | beforeEach(function() { 27 | return this.create = function() { 28 | return bootbox.dialog(); 29 | }; 30 | }); 31 | return it('throws an error', function() { 32 | return expect(this.create).to.throw(/supply an object/); 33 | }); 34 | }); 35 | return describe('with one argument', function() { 36 | describe('where the argument is not an object', function() { 37 | beforeEach(function() { 38 | return this.create = function() { 39 | return bootbox.dialog('test'); 40 | }; 41 | }); 42 | return it('throws an error', function() { 43 | return expect(this.create).to.throw(/supply an object/); 44 | }); 45 | }); 46 | describe('where the argument has no message property', function() { 47 | beforeEach(function() { 48 | return this.create = function() { 49 | return bootbox.dialog({ 50 | invalid: 'options' 51 | }); 52 | }; 53 | }); 54 | return it('throws an error', function() { 55 | return expect(this.create).to.throw('"message" option must not be null or an empty string.'); 56 | }); 57 | }); 58 | return describe('where the argument has a button with an invalid value', function() { 59 | beforeEach(function() { 60 | return this.create = function() { 61 | return bootbox.dialog({ 62 | message: 'test', 63 | buttons: { 64 | ok: 'foo' 65 | } 66 | }); 67 | }; 68 | }); 69 | return it('throws an error', function() { 70 | return expect(this.create).to.throw('button with key "ok" must be an object'); 71 | }); 72 | }); 73 | }); 74 | }); 75 | describe('when creating a minimal dialog', function() { 76 | beforeEach(function() { 77 | return this.dialog = bootbox.dialog({ 78 | message: 'test' 79 | }); 80 | }); 81 | it('adds the bootbox class to the dialog', function() { 82 | return expect(this.dialog.hasClass('bootbox')).to.be.true; 83 | }); 84 | it('adds the bootstrap modal class to the dialog', function() { 85 | return expect(this.dialog.hasClass('modal')).to.be.true; 86 | }); 87 | it('adds the fade class to the dialog', function() { 88 | return expect(this.dialog.hasClass('fade')).to.be.true; 89 | }); 90 | it('shows the expected message', function() { 91 | return expect(this.text('.bootbox-body')).to.equal('test'); 92 | }); 93 | it('has a header', function() { 94 | return expect(this.exists('.modal-header')).to.be.ok; 95 | }); 96 | it('has a close button inside the header', function() { 97 | return expect(this.exists('.modal-header .close')).to.be.ok; 98 | }); 99 | it('does not have a footer', function() { 100 | return expect(this.exists('.modal-footer')).not.to.be.ok; 101 | }); 102 | }); 103 | describe('when creating a dialog with a button', function() { 104 | beforeEach(function() { 105 | return this.create = (function(_this) { 106 | return function(button) { 107 | if (button === null) { 108 | button = {}; 109 | } 110 | return _this.dialog = bootbox.dialog({ 111 | message: 'test', 112 | buttons: { 113 | one: button 114 | } 115 | }); 116 | }; 117 | })(this); 118 | }); 119 | describe('when the button has no callback', function() { 120 | beforeEach(function() { 121 | this.create({ 122 | label: 'My Label' 123 | }); 124 | return this.hidden = sinon.spy(this.dialog, 'modal'); 125 | }); 126 | it('shows a footer', function() { 127 | return expect(this.exists('.modal-footer')).to.be.ok; 128 | }); 129 | it('shows one button', function() { 130 | return expect(this.find('.btn').length).to.equal(1); 131 | }); 132 | it('shows the correct button text', function() { 133 | return expect(this.text('.btn')).to.equal('My Label'); 134 | }); 135 | it('applies the correct button class', function() { 136 | return expect(this['class']('.btn', 'btn-primary')).to.be.true; 137 | }); 138 | describe('when triggering the escape event', function() { 139 | beforeEach(function() { 140 | return this.dialog.trigger('escape.close.bb'); 141 | }); 142 | return it('should not hide the modal', function() { 143 | return expect(this.hidden).not.to.have.been.called; 144 | }); 145 | }); 146 | return describe('when clicking the close button', function() { 147 | beforeEach(function() { 148 | return this.dialog.find('.close').trigger('click'); 149 | }); 150 | return it('should hide the modal', function() { 151 | return expect(this.hidden).to.have.been.calledWithExactly('hide'); 152 | }); 153 | }); 154 | }); 155 | describe('when the button has a label and callback', function() { 156 | beforeEach(function() { 157 | this.callback = sinon.spy(); 158 | this.create({ 159 | label: 'Another Label', 160 | callback: this.callback 161 | }); 162 | return this.hidden = sinon.spy(this.dialog, 'modal'); 163 | }); 164 | it('shows a footer', function() { 165 | return expect(this.exists('.modal-footer')).to.be.ok; 166 | }); 167 | it('shows the correct button text', function() { 168 | return expect(this.text('.btn')).to.equal('Another Label'); 169 | }); 170 | describe('when dismissing the dialog by clicking OK', function() { 171 | beforeEach(function() { 172 | return this.dialog.find('.btn-primary').trigger('click'); 173 | }); 174 | it('should invoke the callback', function() { 175 | return expect(this.callback).to.have.been.called; 176 | }); 177 | it('should pass the dialog as "this"', function() { 178 | return expect(this.callback.thisValues[0]).to.equal(this.dialog); 179 | }); 180 | return it('should hide the modal', function() { 181 | return expect(this.hidden).to.have.been.calledWithExactly('hide'); 182 | }); 183 | }); 184 | describe('when triggering the escape event', function() { 185 | beforeEach(function() { 186 | return this.dialog.trigger('escape.close.bb'); 187 | }); 188 | it('should not invoke the callback', function() { 189 | return expect(this.callback).not.to.have.been.called; 190 | }); 191 | return it('should not hide the modal', function() { 192 | return expect(this.hidden).not.to.have.been.called; 193 | }); 194 | }); 195 | return describe('when clicking the close button', function() { 196 | beforeEach(function() { 197 | return this.dialog.find('.close').trigger('click'); 198 | }); 199 | it('should not invoke the callback', function() { 200 | return expect(this.callback).not.to.have.been.called; 201 | }); 202 | return it('should hide the modal', function() { 203 | return expect(this.hidden).to.have.been.called; 204 | }); 205 | }); 206 | }); 207 | describe('when the button has a custom class', function() { 208 | beforeEach(function() { 209 | return this.create({ 210 | label: 'Test Label', 211 | className: 'btn-custom' 212 | }); 213 | }); 214 | it('shows the correct button text', function() { 215 | return expect(this.text('.btn')).to.equal('Test Label'); 216 | }); 217 | return it('adds the custom class to the button', function() { 218 | return expect(this['class']('.btn', 'btn-custom')).to.be.true; 219 | }); 220 | }); 221 | return describe('when the button has no explicit label', function() { 222 | beforeEach(function() { 223 | return this.create = function(buttons) { 224 | return this.dialog = bootbox.dialog({ 225 | message: 'test', 226 | buttons: buttons 227 | }); 228 | }; 229 | }); 230 | describe('when its value is an object', function() { 231 | beforeEach(function() { 232 | return this.create({ 233 | 'Short form': { 234 | className: 'btn-custom', 235 | callback: function() { 236 | return true; 237 | } 238 | } 239 | }); 240 | }); 241 | it('uses the key name as the button text', function() { 242 | return expect(this.text('.btn')).to.equal('Short form'); 243 | }); 244 | return it('adds the custom class to the button', function() { 245 | return expect(this['class']('.btn', 'btn-custom')).to.be.true; 246 | }); 247 | }); 248 | describe('when its value is a function', function() { 249 | beforeEach(function() { 250 | this.callback = sinon.spy(); 251 | return this.create({ 252 | my_label: this.callback 253 | }); 254 | }); 255 | it('uses the key name as the button text', function() { 256 | return expect(this.text('.btn')).to.equal('my_label'); 257 | }); 258 | return describe('when dismissing the dialog by clicking the button', function() { 259 | beforeEach(function() { 260 | return this.dialog.find('.btn-primary').trigger('click'); 261 | }); 262 | it('should invoke the callback', function() { 263 | return expect(this.callback).to.have.been.called; 264 | }); 265 | return it('should pass the dialog as "this"', function() { 266 | return expect(this.callback.thisValues[0]).to.equal(this.dialog); 267 | }); 268 | }); 269 | }); 270 | return describe('when its value is not an object or function', function() { 271 | beforeEach(function() { 272 | return this.badCreate = (function(_this) { 273 | return function() { 274 | return _this.create({ 275 | 'Short form': 'hello world' 276 | }); 277 | }; 278 | })(this); 279 | }); 280 | return it('throws an error', function() { 281 | return expect(this.badCreate).to.throw('button with key "Short form" must be an object'); 282 | }); 283 | }); 284 | }); 285 | }); 286 | describe('when creating a dialog with a title', function() { 287 | beforeEach(function() { 288 | return this.dialog = bootbox.dialog({ 289 | title: 'My Title', 290 | message: 'test' 291 | }); 292 | }); 293 | it('has a header', function() { 294 | return expect(this.exists('.modal-header')).to.be.ok; 295 | }); 296 | it('shows the correct title text', function() { 297 | return expect(this.text('.modal-title')).to.equal('My Title'); 298 | }); 299 | return it('has a close button inside the header', function() { 300 | return expect(this.exists('.modal-header .close')).to.be.ok; 301 | }); 302 | }); 303 | describe('when creating a dialog with no backdrop', function() { 304 | beforeEach(function() { 305 | return this.dialog = bootbox.dialog({ 306 | message: 'No backdrop in sight', 307 | backdrop: false 308 | }); 309 | }); 310 | return it('does not have a backdrop', function() { 311 | return expect(this.dialog.next('.modal-backdrop').length).to.equal(0); 312 | }); 313 | }); 314 | describe('when creating a dialog with no close button', function() { 315 | beforeEach(function() { 316 | return this.dialog = bootbox.dialog({ 317 | message: 'No backdrop in sight', 318 | closeButton: false 319 | }); 320 | }); 321 | return it('does not have a close button inside the body', function() { 322 | return expect(this.exists('.modal-body .close')).not.to.be.ok; 323 | }); 324 | }); 325 | describe('when creating a dialog with an onEscape handler', function() { 326 | beforeEach(function() { 327 | return this.e = function(keyCode) { 328 | return $(this.dialog).trigger($.Event('keyup', { 329 | which: keyCode 330 | })); 331 | }; 332 | }); 333 | describe('with a simple callback', function() { 334 | beforeEach(function() { 335 | this.callback = sinon.spy(); 336 | this.dialog = bootbox.dialog({ 337 | message: 'Are you sure?', 338 | onEscape: this.callback 339 | }); 340 | this.hidden = sinon.spy(this.dialog, 'modal'); 341 | return this.trigger = sinon.spy(this.dialog, 'trigger').withArgs('escape.close.bb'); 342 | }); 343 | return describe('when triggering the keyup event', function() { 344 | describe('when the key is not the escape key', function() { 345 | beforeEach(function() { 346 | return this.e(15); 347 | }); 348 | it('does not trigger the escape event', function() { 349 | return expect(this.trigger).not.to.have.been.called; 350 | }); 351 | return it('should not hide the modal', function() { 352 | return expect(this.hidden).not.to.have.been.called; 353 | }); 354 | }); 355 | return describe('when the key is the escape key', function() { 356 | beforeEach(function() { 357 | return this.e(27); 358 | }); 359 | it('triggers the escape event', function() { 360 | return expect(this.trigger).to.have.been.calledWithExactly('escape.close.bb'); 361 | }); 362 | it('should invoke the callback', function() { 363 | return expect(this.callback).to.have.been.called; 364 | }); 365 | it('should pass the dialog as "this"', function() { 366 | return expect(this.callback.thisValues[0]).to.equal(this.dialog); 367 | }); 368 | return it('should hide the modal', function() { 369 | return expect(this.hidden).to.have.been.calledWithExactly('hide'); 370 | }); 371 | }); 372 | }); 373 | }); 374 | return describe('with a callback which returns false', function() { 375 | beforeEach(function() { 376 | this.callback = sinon.stub().returns(false); 377 | this.dialog = bootbox.dialog({ 378 | message: 'Are you sure?', 379 | onEscape: this.callback 380 | }); 381 | return this.hidden = sinon.spy(this.dialog, 'modal'); 382 | }); 383 | describe('when triggering the escape keyup event', function() { 384 | beforeEach(function() { 385 | return this.e(27); 386 | }); 387 | it('should invoke the callback', function() { 388 | return expect(this.callback).to.have.been.called; 389 | }); 390 | it('should pass the dialog as "this"', function() { 391 | return expect(this.callback.thisValues[0]).to.equal(this.dialog); 392 | }); 393 | return it('should not hide the modal', function() { 394 | return expect(this.hidden).not.to.have.been.called; 395 | }); 396 | }); 397 | return describe('when clicking the escape button', function() { 398 | beforeEach(function() { 399 | return this.dialog.find('.close').trigger('click'); 400 | }); 401 | it('should invoke the callback', function() { 402 | return expect(this.callback).to.have.been.called; 403 | }); 404 | it('should pass the dialog as "this"', function() { 405 | return expect(this.callback.thisValues[0]).to.equal(this.dialog); 406 | }); 407 | return it('should not hide the modal', function() { 408 | return expect(this.hidden).not.to.have.been.called; 409 | }); 410 | }); 411 | }); 412 | }); 413 | return describe('with size option', function() { 414 | describe('when the size option is set to large', function() { 415 | beforeEach(function() { 416 | return this.dialog = bootbox.dialog({ 417 | message: 'test', 418 | size: 'large' 419 | }); 420 | }); 421 | return it('adds the large class to the innerDialog', function() { 422 | return expect(this.dialog.children('.modal-dialog').hasClass('modal-lg')).to.be.true; 423 | }); 424 | }); 425 | return describe('when the size option is set to small', function() { 426 | beforeEach(function() { 427 | return this.dialog = bootbox.dialog({ 428 | message: 'test', 429 | size: 'small' 430 | }); 431 | }); 432 | return it('adds the large class to the innerDialog', function() { 433 | return expect(this.dialog.children('.modal-dialog').hasClass('modal-sm')).to.be.true; 434 | }); 435 | }); 436 | }); 437 | }); 438 | -------------------------------------------------------------------------------- /tests/locales.test.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | describe('bootbox locales', function() { 3 | 'use strict'; 4 | beforeEach(function() { 5 | return this.setLocale = function(locale) { 6 | var d1, d2; 7 | bootbox.setLocale(locale); 8 | d1 = bootbox.alert('foo'); 9 | d2 = bootbox.confirm('foo', function() { 10 | return true; 11 | }); 12 | return this.labels = { 13 | ok: d1.find('.btn:first').text(), 14 | cancel: d2.find('.btn:first').text(), 15 | confirm: d2.find('.btn:last').text() 16 | }; 17 | }; 18 | }); 19 | describe('Invalid locale', function() { 20 | beforeEach(function() { 21 | return this.setLocale('xx'); 22 | }); 23 | it('shows the default OK translation', function() { 24 | return expect(this.labels.ok).to.equal('OK'); 25 | }); 26 | it('shows the default CANCEL translation', function() { 27 | return expect(this.labels.cancel).to.equal('Cancel'); 28 | }); 29 | return it('shows the default CONFIRM translation', function() { 30 | return expect(this.labels.confirm).to.equal('OK'); 31 | }); 32 | }); 33 | describe('Arabic', function() { 34 | beforeEach(function() { 35 | return this.setLocale('ar'); 36 | }); 37 | it('shows the correct OK translation', function() { 38 | return expect(this.labels.ok).to.equal('موافق'); 39 | }); 40 | it('shows the correct CANCEL translation', function() { 41 | return expect(this.labels.cancel).to.equal('الغاء'); 42 | }); 43 | return it('shows the correct CONFIRM translation', function() { 44 | return expect(this.labels.confirm).to.equal('تأكيد'); 45 | }); 46 | }); 47 | describe('Azerbaijani', function() { 48 | beforeEach(function() { 49 | return this.setLocale('az'); 50 | }); 51 | it('shows the correct OK translation', function() { 52 | return expect(this.labels.ok).to.equal('OK'); 53 | }); 54 | it('shows the correct CANCEL translation', function() { 55 | return expect(this.labels.cancel).to.equal('İmtina et'); 56 | }); 57 | return it('shows the correct CONFIRM translation', function() { 58 | return expect(this.labels.confirm).to.equal('Təsdiq et'); 59 | }); 60 | }); 61 | describe('English', function() { 62 | beforeEach(function() { 63 | return this.setLocale('en'); 64 | }); 65 | it('shows the correct OK translation', function() { 66 | return expect(this.labels.ok).to.equal('OK'); 67 | }); 68 | it('shows the correct CANCEL translation', function() { 69 | return expect(this.labels.cancel).to.equal('Cancel'); 70 | }); 71 | return it('shows the correct CONFIRM translation', function() { 72 | return expect(this.labels.confirm).to.equal('OK'); 73 | }); 74 | }); 75 | describe('French', function() { 76 | beforeEach(function() { 77 | return this.setLocale('fr'); 78 | }); 79 | it('shows the correct OK translation', function() { 80 | return expect(this.labels.ok).to.equal('OK'); 81 | }); 82 | it('shows the correct CANCEL translation', function() { 83 | return expect(this.labels.cancel).to.equal('Annuler'); 84 | }); 85 | return it('shows the correct CONFIRM translation', function() { 86 | return expect(this.labels.confirm).to.equal('Confirmer'); 87 | }); 88 | }); 89 | describe('German', function() { 90 | beforeEach(function() { 91 | return this.setLocale('de'); 92 | }); 93 | it('shows the correct OK translation', function() { 94 | return expect(this.labels.ok).to.equal('OK'); 95 | }); 96 | it('shows the correct CANCEL translation', function() { 97 | return expect(this.labels.cancel).to.equal('Abbrechen'); 98 | }); 99 | return it('shows the correct CONFIRM translation', function() { 100 | return expect(this.labels.confirm).to.equal('Akzeptieren'); 101 | }); 102 | }); 103 | describe('Spanish', function() { 104 | beforeEach(function() { 105 | return this.setLocale('es'); 106 | }); 107 | it('shows the correct OK translation', function() { 108 | return expect(this.labels.ok).to.equal('OK'); 109 | }); 110 | it('shows the correct CANCEL translation', function() { 111 | return expect(this.labels.cancel).to.equal('Cancelar'); 112 | }); 113 | return it('shows the correct CONFIRM translation', function() { 114 | return expect(this.labels.confirm).to.equal('Aceptar'); 115 | }); 116 | }); 117 | describe('Basque', function() { 118 | beforeEach(function() { 119 | return this.setLocale('eu'); 120 | }); 121 | it('shows the correct OK translation', function() { 122 | return expect(this.labels.ok).to.equal('OK'); 123 | }); 124 | it('shows the correct CANCEL translation', function() { 125 | return expect(this.labels.cancel).to.equal('Ezeztatu'); 126 | }); 127 | return it('shows the correct CONFIRM translation', function() { 128 | return expect(this.labels.confirm).to.equal('Onartu'); 129 | }); 130 | }); 131 | describe('Portuguese', function() { 132 | beforeEach(function() { 133 | return this.setLocale('pt'); 134 | }); 135 | it('shows the correct OK translation', function() { 136 | return expect(this.labels.ok).to.equal('OK'); 137 | }); 138 | it('shows the correct CANCEL translation', function() { 139 | return expect(this.labels.cancel).to.equal('Cancelar'); 140 | }); 141 | return it('shows the correct CONFIRM translation', function() { 142 | return expect(this.labels.confirm).to.equal('Confirmar'); 143 | }); 144 | }); 145 | describe('Portuguese (Brasil)', function() { 146 | beforeEach(function() { 147 | return this.setLocale('pt-BR'); 148 | }); 149 | it('shows the correct OK translation', function() { 150 | return expect(this.labels.ok).to.equal('OK'); 151 | }); 152 | it('shows the correct CANCEL translation', function() { 153 | return expect(this.labels.cancel).to.equal('Cancelar'); 154 | }); 155 | return it('shows the correct CONFIRM translation', function() { 156 | return expect(this.labels.confirm).to.equal('Sim'); 157 | }); 158 | }); 159 | describe('Dutch', function() { 160 | beforeEach(function() { 161 | return this.setLocale('nl'); 162 | }); 163 | it('shows the correct OK translation', function() { 164 | return expect(this.labels.ok).to.equal('OK'); 165 | }); 166 | it('shows the correct CANCEL translation', function() { 167 | return expect(this.labels.cancel).to.equal('Annuleren'); 168 | }); 169 | return it('shows the correct CONFIRM translation', function() { 170 | return expect(this.labels.confirm).to.equal('Accepteren'); 171 | }); 172 | }); 173 | describe('Russian', function() { 174 | beforeEach(function() { 175 | return this.setLocale('ru'); 176 | }); 177 | it('shows the correct OK translation', function() { 178 | return expect(this.labels.ok).to.equal('OK'); 179 | }); 180 | it('shows the correct CANCEL translation', function() { 181 | return expect(this.labels.cancel).to.equal('Отмена'); 182 | }); 183 | return it('shows the correct CONFIRM translation', function() { 184 | return expect(this.labels.confirm).to.equal('Применить'); 185 | }); 186 | }); 187 | describe('Indonesian', function() { 188 | beforeEach(function() { 189 | return this.setLocale('id'); 190 | }); 191 | it('shows the correct OK translation', function() { 192 | return expect(this.labels.ok).to.equal('OK'); 193 | }); 194 | it('shows the correct CANCEL translation', function() { 195 | return expect(this.labels.cancel).to.equal('Batal'); 196 | }); 197 | return it('shows the correct CONFIRM translation', function() { 198 | return expect(this.labels.confirm).to.equal('OK'); 199 | }); 200 | }); 201 | describe('Italian', function() { 202 | beforeEach(function() { 203 | return this.setLocale('it'); 204 | }); 205 | it('shows the correct OK translation', function() { 206 | return expect(this.labels.ok).to.equal('OK'); 207 | }); 208 | it('shows the correct CANCEL translation', function() { 209 | return expect(this.labels.cancel).to.equal('Annulla'); 210 | }); 211 | return it('shows the correct CONFIRM translation', function() { 212 | return expect(this.labels.confirm).to.equal('Conferma'); 213 | }); 214 | }); 215 | describe('Polish', function() { 216 | beforeEach(function() { 217 | return this.setLocale('pl'); 218 | }); 219 | it('shows the correct OK translation', function() { 220 | return expect(this.labels.ok).to.equal('OK'); 221 | }); 222 | it('shows the correct CANCEL translation', function() { 223 | return expect(this.labels.cancel).to.equal('Anuluj'); 224 | }); 225 | return it('shows the correct CONFIRM translation', function() { 226 | return expect(this.labels.confirm).to.equal('Potwierdź'); 227 | }); 228 | }); 229 | describe('Danish', function() { 230 | beforeEach(function() { 231 | return this.setLocale('da'); 232 | }); 233 | it('shows the correct OK translation', function() { 234 | return expect(this.labels.ok).to.equal('OK'); 235 | }); 236 | it('shows the correct CANCEL translation', function() { 237 | return expect(this.labels.cancel).to.equal('Annuller'); 238 | }); 239 | return it('shows the correct CONFIRM translation', function() { 240 | return expect(this.labels.confirm).to.equal('Accepter'); 241 | }); 242 | }); 243 | describe('Chinese', function() { 244 | describe('Taiwan', function() { 245 | beforeEach(function() { 246 | return this.setLocale('zh-TW'); 247 | }); 248 | it('shows the correct OK translation', function() { 249 | return expect(this.labels.ok).to.equal('OK'); 250 | }); 251 | it('shows the correct CANCEL translation', function() { 252 | return expect(this.labels.cancel).to.equal('取消'); 253 | }); 254 | return it('shows the correct CONFIRM translation', function() { 255 | return expect(this.labels.confirm).to.equal('確認'); 256 | }); 257 | }); 258 | return describe('China', function() { 259 | beforeEach(function() { 260 | return this.setLocale('zh-CN'); 261 | }); 262 | it('shows the correct OK translation', function() { 263 | return expect(this.labels.ok).to.equal('OK'); 264 | }); 265 | it('shows the correct CANCEL translation', function() { 266 | return expect(this.labels.cancel).to.equal('取消'); 267 | }); 268 | return it('shows the correct CONFIRM translation', function() { 269 | return expect(this.labels.confirm).to.equal('确认'); 270 | }); 271 | }); 272 | }); 273 | describe('Norwegian', function() { 274 | beforeEach(function() { 275 | return this.setLocale('no'); 276 | }); 277 | it('shows the correct OK translation', function() { 278 | return expect(this.labels.ok).to.equal('OK'); 279 | }); 280 | it('shows the correct CANCEL translation', function() { 281 | return expect(this.labels.cancel).to.equal('Avbryt'); 282 | }); 283 | return it('shows the correct CONFIRM translation', function() { 284 | return expect(this.labels.confirm).to.equal('OK'); 285 | }); 286 | }); 287 | describe('Swedish', function() { 288 | beforeEach(function() { 289 | return this.setLocale('sv'); 290 | }); 291 | it('shows the correct OK translation', function() { 292 | return expect(this.labels.ok).to.equal('OK'); 293 | }); 294 | it('shows the correct CANCEL translation', function() { 295 | return expect(this.labels.cancel).to.equal('Avbryt'); 296 | }); 297 | return it('shows the correct CONFIRM translation', function() { 298 | return expect(this.labels.confirm).to.equal('OK'); 299 | }); 300 | }); 301 | describe('Latvian', function() { 302 | beforeEach(function() { 303 | return this.setLocale('lv'); 304 | }); 305 | it('shows the correct OK translation', function() { 306 | return expect(this.labels.ok).to.equal('Labi'); 307 | }); 308 | it('shows the correct CANCEL translation', function() { 309 | return expect(this.labels.cancel).to.equal('Atcelt'); 310 | }); 311 | return it('shows the correct CONFIRM translation', function() { 312 | return expect(this.labels.confirm).to.equal('Apstiprināt'); 313 | }); 314 | }); 315 | describe('Lithuanian', function() { 316 | beforeEach(function() { 317 | return this.setLocale('lt'); 318 | }); 319 | it('shows the correct OK translation', function() { 320 | return expect(this.labels.ok).to.equal('Gerai'); 321 | }); 322 | it('shows the correct CANCEL translation', function() { 323 | return expect(this.labels.cancel).to.equal('Atšaukti'); 324 | }); 325 | return it('shows the correct CONFIRM translation', function() { 326 | return expect(this.labels.confirm).to.equal('Patvirtinti'); 327 | }); 328 | }); 329 | describe('Turkish', function() { 330 | beforeEach(function() { 331 | return this.setLocale('tr'); 332 | }); 333 | it('shows the correct OK translation', function() { 334 | return expect(this.labels.ok).to.equal('Tamam'); 335 | }); 336 | it('shows the correct CANCEL translation', function() { 337 | return expect(this.labels.cancel).to.equal('İptal'); 338 | }); 339 | return it('shows the correct CONFIRM translation', function() { 340 | return expect(this.labels.confirm).to.equal('Onayla'); 341 | }); 342 | }); 343 | describe('Hebrew', function() { 344 | beforeEach(function() { 345 | return this.setLocale('he'); 346 | }); 347 | it('shows the correct OK translation', function() { 348 | return expect(this.labels.ok).to.equal('אישור'); 349 | }); 350 | it('shows the correct CANCEL translation', function() { 351 | return expect(this.labels.cancel).to.equal('ביטול'); 352 | }); 353 | return it('shows the correct CONFIRM translation', function() { 354 | return expect(this.labels.confirm).to.equal('אישור'); 355 | }); 356 | }); 357 | describe('Greek', function() { 358 | beforeEach(function() { 359 | return this.setLocale('el'); 360 | }); 361 | it('shows the correct OK translation', function() { 362 | return expect(this.labels.ok).to.equal('Εντάξει'); 363 | }); 364 | it('shows the correct CANCEL translation', function() { 365 | return expect(this.labels.cancel).to.equal('Ακύρωση'); 366 | }); 367 | return it('shows the correct CONFIRM translation', function() { 368 | return expect(this.labels.confirm).to.equal('Επιβεβαίωση'); 369 | }); 370 | }); 371 | describe('Japanese', function() { 372 | beforeEach(function() { 373 | return this.setLocale('ja'); 374 | }); 375 | it('shows the correct OK translation', function() { 376 | return expect(this.labels.ok).to.equal('OK'); 377 | }); 378 | it('shows the correct CANCEL translation', function() { 379 | return expect(this.labels.cancel).to.equal('キャンセル'); 380 | }); 381 | return it('shows the correct CONFIRM translation', function() { 382 | return expect(this.labels.confirm).to.equal('OK'); 383 | }); 384 | }); 385 | describe('Hungarian', function() { 386 | beforeEach(function() { 387 | return this.setLocale('hu'); 388 | }); 389 | it('shows the correct OK translation', function() { 390 | return expect(this.labels.ok).to.equal('OK'); 391 | }); 392 | it('shows the correct CANCEL translation', function() { 393 | return expect(this.labels.cancel).to.equal('Mégsem'); 394 | }); 395 | return it('shows the correct CONFIRM translation', function() { 396 | return expect(this.labels.confirm).to.equal('Megerősít'); 397 | }); 398 | }); 399 | describe('Croatian', function() { 400 | beforeEach(function() { 401 | return this.setLocale('hr'); 402 | }); 403 | it('shows the correct OK translation', function() { 404 | return expect(this.labels.ok).to.equal('OK'); 405 | }); 406 | it('shows the correct CANCEL translation', function() { 407 | return expect(this.labels.cancel).to.equal('Odustani'); 408 | }); 409 | return it('shows the correct CONFIRM translation', function() { 410 | return expect(this.labels.confirm).to.equal('Potvrdi'); 411 | }); 412 | }); 413 | describe('Bulgarian', function() { 414 | beforeEach(function() { 415 | return this.setLocale('bg-BG'); 416 | }); 417 | it('shows the correct OK translation', function() { 418 | return expect(this.labels.ok).to.equal('Ок'); 419 | }); 420 | it('shows the correct CANCEL translation', function() { 421 | return expect(this.labels.cancel).to.equal('Отказ'); 422 | }); 423 | return it('shows the correct CONFIRM translation', function() { 424 | return expect(this.labels.confirm).to.equal('Потвърждавам'); 425 | }); 426 | }); 427 | describe('Thai', function() { 428 | beforeEach(function() { 429 | return this.setLocale('th'); 430 | }); 431 | it('shows the correct OK translation', function() { 432 | return expect(this.labels.ok).to.equal('ตกลง'); 433 | }); 434 | it('shows the correct CANCEL translation', function() { 435 | return expect(this.labels.cancel).to.equal('ยกเลิก'); 436 | }); 437 | return it('shows the correct CONFIRM translation', function() { 438 | return expect(this.labels.confirm).to.equal('ยืนยัน'); 439 | }); 440 | }); 441 | describe('Persian', function() { 442 | beforeEach(function() { 443 | return this.setLocale('fa'); 444 | }); 445 | it('shows the correct OK translation', function() { 446 | return expect(this.labels.ok).to.equal('قبول'); 447 | }); 448 | it('shows the correct CANCEL translation', function() { 449 | return expect(this.labels.cancel).to.equal('لغو'); 450 | }); 451 | return it('shows the correct CONFIRM translation', function() { 452 | return expect(this.labels.confirm).to.equal('تایید'); 453 | }); 454 | }); 455 | describe('Ukrainian', function() { 456 | beforeEach(function() { 457 | return this.setLocale('uk'); 458 | }); 459 | it('shows the correct OK translation', function() { 460 | return expect(this.labels.ok).to.equal('OK'); 461 | }); 462 | it('shows the correct CANCEL translation', function() { 463 | return expect(this.labels.cancel).to.equal('Відміна'); 464 | }); 465 | return it('shows the correct CONFIRM translation', function() { 466 | return expect(this.labels.confirm).to.equal('Прийняти'); 467 | }); 468 | }); 469 | describe('Albanian', function() { 470 | beforeEach(function() { 471 | return this.setLocale('sq'); 472 | }); 473 | it('shows the correct OK translation', function() { 474 | return expect(this.labels.ok).to.equal('OK'); 475 | }); 476 | it('shows the correct CANCEL translation', function() { 477 | return expect(this.labels.cancel).to.equal('Anulo'); 478 | }); 479 | return it('shows the correct CONFIRM translation', function() { 480 | return expect(this.labels.confirm).to.equal('Prano'); 481 | }); 482 | }); 483 | describe('Slovenian', function() { 484 | beforeEach(function() { 485 | return this.setLocale('sl'); 486 | }); 487 | it('shows the correct OK translation', function() { 488 | return expect(this.labels.ok).to.equal('OK'); 489 | }); 490 | it('shows the correct CANCEL translation', function() { 491 | return expect(this.labels.cancel).to.equal('Prekliči'); 492 | }); 493 | return it('shows the correct CONFIRM translation', function() { 494 | return expect(this.labels.confirm).to.equal('Potrdi'); 495 | }); 496 | }); 497 | describe('Slovak', function() { 498 | beforeEach(function() { 499 | return this.setLocale('sk'); 500 | }); 501 | it('shows the correct OK translation', function() { 502 | return expect(this.labels.ok).to.equal('OK'); 503 | }); 504 | it('shows the correct CANCEL translation', function() { 505 | return expect(this.labels.cancel).to.equal('Zrušiť'); 506 | }); 507 | return it('shows the correct CONFIRM translation', function() { 508 | return expect(this.labels.confirm).to.equal('Potvrdiť'); 509 | }); 510 | }); 511 | describe('Tamil', function() { 512 | beforeEach(function() { 513 | return this.setLocale('ta'); 514 | }); 515 | it('shows the correct OK translation', function() { 516 | return expect(this.labels.ok).to.equal('சரி'); 517 | }); 518 | it('shows the correct CANCEL translation', function() { 519 | return expect(this.labels.cancel).to.equal('ரத்து செய்'); 520 | }); 521 | return it('shows the correct CONFIRM translation', function() { 522 | return expect(this.labels.confirm).to.equal('உறுதி செய்'); 523 | }); 524 | }); 525 | describe('Swahili', function() { 526 | beforeEach(function() { 527 | return this.setLocale('sw'); 528 | }); 529 | it('shows the correct OK translation', function() { 530 | return expect(this.labels.ok).to.equal('Sawa'); 531 | }); 532 | it('shows the correct CANCEL translation', function() { 533 | return expect(this.labels.cancel).to.equal('Ghairi'); 534 | }); 535 | return it('shows the correct CONFIRM translation', function() { 536 | return expect(this.labels.confirm).to.equal('Thibitisha'); 537 | }); 538 | }); 539 | describe('Georgian', function() { 540 | beforeEach(function() { 541 | return this.setLocale('ka'); 542 | }); 543 | it('shows the correct OK translation', function() { 544 | return expect(this.labels.ok).to.equal('OK'); 545 | }); 546 | it('shows the correct CANCEL translation', function() { 547 | return expect(this.labels.cancel).to.equal('გაუქმება'); 548 | }); 549 | return it('shows the correct CONFIRM translation', function() { 550 | return expect(this.labels.confirm).to.equal('დადასტურება'); 551 | }); 552 | }); 553 | describe('Vietnamese', function() { 554 | beforeEach(function() { 555 | return this.setLocale('vi'); 556 | }); 557 | it('shows the correct OK translation', function() { 558 | return expect(this.labels.ok).to.equal('OK'); 559 | }); 560 | it('shows the correct CANCEL translation', function() { 561 | return expect(this.labels.cancel).to.equal('Hủy bỏ'); 562 | }); 563 | return it('shows the correct CONFIRM translation', function() { 564 | return expect(this.labels.confirm).to.equal('Xác nhận'); 565 | }); 566 | }); 567 | }); 568 | --------------------------------------------------------------------------------