mongo_browser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +12 -0
  4. data/Gemfile +19 -0
  5. data/LICENSE +22 -0
  6. data/README.md +31 -0
  7. data/README.rdoc +19 -0
  8. data/Rakefile +61 -0
  9. data/app/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
  10. data/app/assets/images/bootstrap/glyphicons-halflings.png +0 -0
  11. data/app/assets/javascripts/app/table_filter.js.coffee +50 -0
  12. data/app/assets/javascripts/application.js.coffee +32 -0
  13. data/app/assets/javascripts/vendor/bootbox.js +508 -0
  14. data/app/assets/javascripts/vendor/bootstrap.js +2025 -0
  15. data/app/assets/javascripts/vendor/jquery.js +9266 -0
  16. data/app/assets/stylesheets/application.css.scss +22 -0
  17. data/app/assets/stylesheets/vendor/bootstrap-responsive.css +1088 -0
  18. data/app/assets/stylesheets/vendor/bootstrap.css +5893 -0
  19. data/app/views/collections/show.erb +61 -0
  20. data/app/views/databases/show.erb +59 -0
  21. data/app/views/index.erb +29 -0
  22. data/app/views/layout.erb +19 -0
  23. data/app/views/layout/_breadcrumb.erb +13 -0
  24. data/app/views/layout/_flash_messages.erb +10 -0
  25. data/app/views/layout/_navbar.erb +21 -0
  26. data/app/views/server_info.erb +20 -0
  27. data/app/views/shared/_filter.erb +14 -0
  28. data/bin/mongo_browser +53 -0
  29. data/config.ru +4 -0
  30. data/features/mongo_browser.feature +18 -0
  31. data/features/step_definitions/mongo_browser_steps.rb +1 -0
  32. data/features/support/env.rb +18 -0
  33. data/lib/mongo_browser.rb +32 -0
  34. data/lib/mongo_browser/application.rb +108 -0
  35. data/lib/mongo_browser/middleware/sprockets_sinatra.rb +24 -0
  36. data/lib/mongo_browser/version.rb +3 -0
  37. data/mongo_browser.gemspec +32 -0
  38. data/spec/features/collections_list_spec.rb +105 -0
  39. data/spec/features/databases_list_spec.rb +60 -0
  40. data/spec/features/documents_list_spec.rb +136 -0
  41. data/spec/features/server_info_spec.rb +17 -0
  42. data/spec/fixtures/databases.json +40 -0
  43. data/spec/spec_helper.rb +56 -0
  44. data/spec/support/have_flash_message_matcher.rb +16 -0
  45. data/spec/support/integration.rb +26 -0
  46. data/spec/support/mongo_test_server.rb +50 -0
  47. metadata +313 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .DS_Store
2
+ results.html
3
+ pkg
4
+ html
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.9.3
5
+
6
+ before_install:
7
+ - "export DISPLAY=:99.0"
8
+ - "sh -e /etc/init.d/xvfb start"
9
+
10
+ script:
11
+ - "rspec spec"
12
+ - "cucumber features"
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mongo_browser.gemspec
4
+ gemspec
5
+
6
+ gem "sprockets"
7
+ gem "coffee-script"
8
+ gem "sass"
9
+ gem "less"
10
+
11
+ group :development, :test do
12
+ gem "rspec"
13
+ gem "capybara"
14
+ gem "capybara-webkit"
15
+ gem "launchy"
16
+ gem "simplecov"
17
+
18
+ gem "debugger"
19
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Łukasz Bandzarewicz
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # MongoBrowser
2
+
3
+ [![Build status](https://secure.travis-ci.org/lucassus/mongo_browser.png)](http://travis-ci.org/lucassus/mongo_browser)
4
+
5
+ TODO: Write a gem description
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'mongo_browser'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install mongo_browser
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create new Pull Request
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = mongo_browser - DESCRIBE YOUR GEM
2
+
3
+ Author:: YOUR NAME (YOUR EMAIL)
4
+ Copyright:: Copyright (c) 2012 YOUR NAME
5
+
6
+
7
+ DESCRIBE YOUR GEM HERE
8
+
9
+ == Links
10
+
11
+ * {Source on Github}[LINK TO GITHUB]
12
+ * RDoc[LINK TO RDOC.INFO]
13
+
14
+ == Install
15
+
16
+ == Examples
17
+
18
+ == Contributing
19
+
data/Rakefile ADDED
@@ -0,0 +1,61 @@
1
+ def dump_load_path
2
+ puts $LOAD_PATH.join("\n")
3
+ found = nil
4
+ $LOAD_PATH.each do |path|
5
+ if File.exists?(File.join(path,"rspec"))
6
+ puts "Found rspec in #{path}"
7
+ if File.exists?(File.join(path,"rspec","core"))
8
+ puts "Found core"
9
+ if File.exists?(File.join(path,"rspec","core","rake_task"))
10
+ puts "Found rake_task"
11
+ found = path
12
+ else
13
+ puts "!! no rake_task"
14
+ end
15
+ else
16
+ puts "!!! no core"
17
+ end
18
+ end
19
+ end
20
+ if found.nil?
21
+ puts "Didn't find rspec/core/rake_task anywhere"
22
+ else
23
+ puts "Found in #{path}"
24
+ end
25
+ end
26
+ require 'bundler'
27
+ require 'rake/clean'
28
+
29
+ require 'rake/testtask'
30
+
31
+ require 'cucumber'
32
+ require 'cucumber/rake/task'
33
+ gem 'rdoc' # we need the installed RDoc gem, not the system one
34
+ require 'rdoc/task'
35
+
36
+ include Rake::DSL
37
+
38
+ Bundler::GemHelper.install_tasks
39
+
40
+
41
+ Rake::TestTask.new do |t|
42
+ t.pattern = 'test/tc_*.rb'
43
+ end
44
+
45
+
46
+ CUKE_RESULTS = 'results.html'
47
+ CLEAN << CUKE_RESULTS
48
+ Cucumber::Rake::Task.new(:features) do |t|
49
+ t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty --no-source -x"
50
+ t.fork = false
51
+ end
52
+
53
+ Rake::RDocTask.new do |rd|
54
+
55
+ rd.main = "README.rdoc"
56
+
57
+ rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
58
+ end
59
+
60
+ task :default => [:test,:features]
61
+
@@ -0,0 +1,50 @@
1
+ window.TableFilter = class TableFilter
2
+ constructor: (@$form) ->
3
+ @$table = $("table.#{$form.data('filter-for')}")
4
+
5
+ @$form.find("input[type='text']").keyup (event) =>
6
+ Escape = 27
7
+ if event.keyCode is Escape
8
+ @clearFilter()
9
+ else
10
+ @$form.submit()
11
+
12
+ @$form.find("button.clear").click (event) =>
13
+ event?.preventDefault()
14
+ @clearFilter()
15
+
16
+ @$form.submit (event) =>
17
+ event?.preventDefault()
18
+
19
+ filterVal = @getFilterInput()
20
+ @filter(filterVal)
21
+
22
+ @filter()
23
+
24
+ filter: ->
25
+ filterVal = @getFilterInput()
26
+ @$table.find("tbody tr").each (index, row) =>
27
+ $row = $(row)
28
+ name = $row.find("td:first").text()
29
+ if filterVal is "" or name.match(new RegExp(filterVal))
30
+ $row.show()
31
+ else
32
+ $row.hide()
33
+
34
+ @$table.show()
35
+ if @$table.find("tbody tr:visible").size() == 0
36
+ @$table.hide()
37
+ $(".filter.alert").show()
38
+ else
39
+ @$table.show()
40
+ $(".filter.alert").hide()
41
+
42
+ clearFilter: ->
43
+ @setFilterInput('')
44
+ @filter()
45
+
46
+ setFilterInput: (value) ->
47
+ @$form.find("input[type='text']").val(value)
48
+
49
+ getFilterInput: ->
50
+ @$form.find("input[type='text']").val()
@@ -0,0 +1,32 @@
1
+ #= require vendor/jquery
2
+ #= require vendor/bootstrap
3
+ #= require vendor/bootbox
4
+ #
5
+ #= require app/table_filter
6
+
7
+ $(document).ready ->
8
+ $("form.filter").each (index, form) ->
9
+ $form = $(form)
10
+ filter = new TableFilter($form)
11
+
12
+ $("a[data-method]").click (event) ->
13
+ event.preventDefault()
14
+
15
+ $link = $(event.target)
16
+
17
+ createAndSubmitForm = ->
18
+ action = $link.attr("href")
19
+ $form = $("<form />").attr("method", "post").attr("action", action)
20
+
21
+ method = $link.data("method")
22
+ $metadataInput = $("<input />").attr("type", "hidden").attr("name", "_method").val(method)
23
+
24
+ $form.hide().append($metadataInput).appendTo("body");
25
+ $form.submit()
26
+
27
+ confirmationMessage = $link.data("confirm")
28
+ if confirmationMessage?
29
+ bootbox.confirm confirmationMessage, (confirmed) =>
30
+ createAndSubmitForm() if confirmed
31
+ else
32
+ createAndSubmitForm()
@@ -0,0 +1,508 @@
1
+ /**
2
+ * bootbox.js v2.4.2
3
+ *
4
+ * http://bootboxjs.com/license.txt
5
+ */
6
+ var bootbox = window.bootbox || (function($) {
7
+
8
+ var _locale = 'en',
9
+ _defaultLocale = 'en',
10
+ _animate = true,
11
+ _backdrop = 'static',
12
+ _icons = {},
13
+ /* last var should always be the public object we'll return */
14
+ that = {};
15
+
16
+ /**
17
+ * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are
18
+ * unlikely to be required. If this gets too large it can be split out into separate JS files.
19
+ */
20
+ var _locales = {
21
+ 'en' : {
22
+ OK : 'OK',
23
+ CANCEL : 'Cancel',
24
+ CONFIRM : 'OK'
25
+ },
26
+ 'fr' : {
27
+ OK : 'OK',
28
+ CANCEL : 'Annuler',
29
+ CONFIRM : 'D\'accord'
30
+ },
31
+ 'de' : {
32
+ OK : 'OK',
33
+ CANCEL : 'Abbrechen',
34
+ CONFIRM : 'Akzeptieren'
35
+ },
36
+ 'es' : {
37
+ OK : 'OK',
38
+ CANCEL : 'Cancelar',
39
+ CONFIRM : 'Aceptar'
40
+ },
41
+ 'br' : {
42
+ OK : 'OK',
43
+ CANCEL : 'Cancelar',
44
+ CONFIRM : 'Sim'
45
+ },
46
+ 'nl' : {
47
+ OK : 'OK',
48
+ CANCEL : 'Annuleren',
49
+ CONFIRM : 'Accepteren'
50
+ },
51
+ 'ru' : {
52
+ OK : 'OK',
53
+ CANCEL : 'Отмена',
54
+ CONFIRM : 'Применить'
55
+ },
56
+ 'it' : {
57
+ OK : 'OK',
58
+ CANCEL : 'Annulla',
59
+ CONFIRM : 'Conferma'
60
+ }
61
+ };
62
+
63
+ function _translate(str, locale) {
64
+ // we assume if no target locale is probided then we should take it from current setting
65
+ if (locale == null) {
66
+ locale = _locale;
67
+ }
68
+ if (typeof _locales[locale][str] == 'string') {
69
+ return _locales[locale][str];
70
+ }
71
+
72
+ // if we couldn't find a lookup then try and fallback to a default translation
73
+
74
+ if (locale != _defaultLocale) {
75
+ return _translate(str, _defaultLocale);
76
+ }
77
+
78
+ // if we can't do anything then bail out with whatever string was passed in - last resort
79
+ return str;
80
+ }
81
+
82
+ that.setLocale = function(locale) {
83
+ for (var i in _locales) {
84
+ if (i == locale) {
85
+ _locale = locale;
86
+ return;
87
+ }
88
+ }
89
+ throw new Error('Invalid locale: '+locale);
90
+ }
91
+
92
+ that.addLocale = function(locale, translations) {
93
+ if (typeof _locales[locale] == 'undefined') {
94
+ _locales[locale] = {};
95
+ }
96
+ for (var str in translations) {
97
+ _locales[locale][str] = translations[str];
98
+ }
99
+ }
100
+
101
+ that.setIcons = function(icons) {
102
+ _icons = icons;
103
+ if (typeof _icons !== 'object' || _icons == null) {
104
+ _icons = {};
105
+ }
106
+ }
107
+
108
+ that.alert = function(/*str, label, cb*/) {
109
+ var str = "",
110
+ label = _translate('OK'),
111
+ cb = null;
112
+
113
+ switch (arguments.length) {
114
+ case 1:
115
+ // no callback, default button label
116
+ str = arguments[0];
117
+ break;
118
+ case 2:
119
+ // callback *or* custom button label dependent on type
120
+ str = arguments[0];
121
+ if (typeof arguments[1] == 'function') {
122
+ cb = arguments[1];
123
+ } else {
124
+ label = arguments[1];
125
+ }
126
+ break;
127
+ case 3:
128
+ // callback and custom button label
129
+ str = arguments[0];
130
+ label = arguments[1];
131
+ cb = arguments[2];
132
+ break;
133
+ default:
134
+ throw new Error("Incorrect number of arguments: expected 1-3");
135
+ break;
136
+ }
137
+
138
+ return that.dialog(str, {
139
+ "label": label,
140
+ "icon" : _icons.OK,
141
+ "callback": cb
142
+ }, {
143
+ "onEscape": cb
144
+ });
145
+ }
146
+
147
+ that.confirm = function(/*str, labelCancel, labelOk, cb*/) {
148
+ var str = "",
149
+ labelCancel = _translate('CANCEL'),
150
+ labelOk = _translate('CONFIRM'),
151
+ cb = null;
152
+
153
+ switch (arguments.length) {
154
+ case 1:
155
+ str = arguments[0];
156
+ break;
157
+ case 2:
158
+ str = arguments[0];
159
+ if (typeof arguments[1] == 'function') {
160
+ cb = arguments[1];
161
+ } else {
162
+ labelCancel = arguments[1];
163
+ }
164
+ break;
165
+ case 3:
166
+ str = arguments[0];
167
+ labelCancel = arguments[1];
168
+ if (typeof arguments[2] == 'function') {
169
+ cb = arguments[2];
170
+ } else {
171
+ labelOk = arguments[2];
172
+ }
173
+ break;
174
+ case 4:
175
+ str = arguments[0];
176
+ labelCancel = arguments[1];
177
+ labelOk = arguments[2];
178
+ cb = arguments[3];
179
+ break;
180
+ default:
181
+ throw new Error("Incorrect number of arguments: expected 1-4");
182
+ break;
183
+ }
184
+
185
+ return that.dialog(str, [{
186
+ "label": labelCancel,
187
+ "icon" : _icons.CANCEL,
188
+ "callback": function() {
189
+ if (typeof cb == 'function') {
190
+ cb(false);
191
+ }
192
+ }
193
+ }, {
194
+ "label": labelOk,
195
+ "icon" : _icons.CONFIRM,
196
+ "callback": function() {
197
+ if (typeof cb == 'function') {
198
+ cb(true);
199
+ }
200
+ }
201
+ }]);
202
+ }
203
+
204
+ that.prompt = function(/*str, labelCancel, labelOk, cb, defaultVal*/) {
205
+ var str = "",
206
+ labelCancel = _translate('CANCEL'),
207
+ labelOk = _translate('CONFIRM'),
208
+ cb = null,
209
+ defaultVal = "";
210
+
211
+ switch (arguments.length) {
212
+ case 1:
213
+ str = arguments[0];
214
+ break;
215
+ case 2:
216
+ str = arguments[0];
217
+ if (typeof arguments[1] == 'function') {
218
+ cb = arguments[1];
219
+ } else {
220
+ labelCancel = arguments[1];
221
+ }
222
+ break;
223
+ case 3:
224
+ str = arguments[0];
225
+ labelCancel = arguments[1];
226
+ if (typeof arguments[2] == 'function') {
227
+ cb = arguments[2];
228
+ } else {
229
+ labelOk = arguments[2];
230
+ }
231
+ break;
232
+ case 4:
233
+ str = arguments[0];
234
+ labelCancel = arguments[1];
235
+ labelOk = arguments[2];
236
+ cb = arguments[3];
237
+ break;
238
+ case 5:
239
+ str = arguments[0];
240
+ labelCancel = arguments[1];
241
+ labelOk = arguments[2];
242
+ cb = arguments[3];
243
+ defaultVal = arguments[4];
244
+ break;
245
+ default:
246
+ throw new Error("Incorrect number of arguments: expected 1-5");
247
+ break;
248
+ }
249
+
250
+ var header = str;
251
+
252
+ // let's keep a reference to the form object for later
253
+ var form = $("<form></form>");
254
+ form.append("<input autocomplete=off type=text value='" + defaultVal + "' />");
255
+
256
+ var div = that.dialog(form, [{
257
+ "label": labelCancel,
258
+ "icon" : _icons.CANCEL,
259
+ "callback": function() {
260
+ if (typeof cb == 'function') {
261
+ cb(null);
262
+ }
263
+ }
264
+ }, {
265
+ "label": labelOk,
266
+ "icon" : _icons.CONFIRM,
267
+ "callback": function() {
268
+ if (typeof cb == 'function') {
269
+ cb(
270
+ form.find("input[type=text]").val()
271
+ );
272
+ }
273
+ }
274
+ }], {
275
+ "header": header
276
+ });
277
+
278
+ div.on("shown", function() {
279
+ form.find("input[type=text]").focus();
280
+
281
+ // ensure that submitting the form (e.g. with the enter key)
282
+ // replicates the behaviour of a normal prompt()
283
+ form.on("submit", function(e) {
284
+ e.preventDefault();
285
+ div.find(".btn-primary").click();
286
+ });
287
+ });
288
+
289
+ return div;
290
+ }
291
+
292
+ that.modal = function(/*str, label, options*/) {
293
+ var str;
294
+ var label;
295
+ var options;
296
+
297
+ var defaultOptions = {
298
+ "onEscape": null,
299
+ "keyboard": true,
300
+ "backdrop": _backdrop
301
+ };
302
+
303
+ switch (arguments.length) {
304
+ case 1:
305
+ str = arguments[0];
306
+ break;
307
+ case 2:
308
+ str = arguments[0];
309
+ if (typeof arguments[1] == 'object') {
310
+ options = arguments[1];
311
+ } else {
312
+ label = arguments[1];
313
+ }
314
+ break;
315
+ case 3:
316
+ str = arguments[0];
317
+ label = arguments[1];
318
+ options = arguments[2];
319
+ break;
320
+ default:
321
+ throw new Error("Incorrect number of arguments: expected 1-3");
322
+ break;
323
+ }
324
+
325
+ defaultOptions['header'] = label;
326
+
327
+ if (typeof options == 'object') {
328
+ options = $.extend(defaultOptions, options);
329
+ } else {
330
+ options = defaultOptions;
331
+ }
332
+
333
+ return that.dialog(str, [], options);
334
+ }
335
+
336
+ that.dialog = function(str, handlers, options) {
337
+ var hideSource = null,
338
+ buttons = "",
339
+ callbacks = [],
340
+ options = options || {};
341
+
342
+ // check for single object and convert to array if necessary
343
+ if (handlers == null) {
344
+ handlers = [];
345
+ } else if (typeof handlers.length == 'undefined') {
346
+ handlers = [handlers];
347
+ }
348
+
349
+ var i = handlers.length;
350
+ while (i--) {
351
+ var label = null,
352
+ _class = null,
353
+ icon = '',
354
+ callback = null;
355
+
356
+ if (typeof handlers[i]['label'] == 'undefined' &&
357
+ typeof handlers[i]['class'] == 'undefined' &&
358
+ typeof handlers[i]['callback'] == 'undefined') {
359
+ // if we've got nothing we expect, check for condensed format
360
+
361
+ var propCount = 0, // condensed will only match if this == 1
362
+ property = null; // save the last property we found
363
+
364
+ // be nicer to count the properties without this, but don't think it's possible...
365
+ for (var j in handlers[i]) {
366
+ property = j;
367
+ if (++propCount > 1) {
368
+ // forget it, too many properties
369
+ break;
370
+ }
371
+ }
372
+
373
+ if (propCount == 1 && typeof handlers[i][j] == 'function') {
374
+ // matches condensed format of label -> function
375
+ handlers[i]['label'] = property;
376
+ handlers[i]['callback'] = handlers[i][j];
377
+ }
378
+ }
379
+
380
+ if (typeof handlers[i]['callback']== 'function') {
381
+ callback = handlers[i]['callback'];
382
+ }
383
+
384
+ if (handlers[i]['class']) {
385
+ _class = handlers[i]['class'];
386
+ } else if (i == handlers.length -1 && handlers.length <= 2) {
387
+ // always add a primary to the main option in a two-button dialog
388
+ _class = 'btn-primary';
389
+ }
390
+
391
+ if (handlers[i]['label']) {
392
+ label = handlers[i]['label'];
393
+ } else {
394
+ label = "Option "+(i+1);
395
+ }
396
+
397
+ if (handlers[i]['icon']) {
398
+ icon = "<i class='"+handlers[i]['icon']+"'></i> ";
399
+ }
400
+
401
+ buttons += "<a data-handler='"+i+"' class='btn "+_class+"' href='javascript:;'>"+icon+""+label+"</a>";
402
+
403
+ callbacks[i] = callback;
404
+ }
405
+
406
+ // @see https://github.com/makeusabrew/bootbox/issues/46#issuecomment-8235302
407
+ // and https://github.com/twitter/bootstrap/issues/4474
408
+ // for an explanation of the inline overflow: hidden
409
+
410
+ var parts = ["<div class='bootbox modal' style='overflow:hidden;'>"];
411
+
412
+ if (options['header']) {
413
+ var closeButton = '';
414
+ if (typeof options['headerCloseButton'] == 'undefined' || options['headerCloseButton']) {
415
+ closeButton = "<a href='javascript:;' class='close'>&times;</a>";
416
+ }
417
+
418
+ parts.push("<div class='modal-header'>"+closeButton+"<h3>"+options['header']+"</h3></div>");
419
+ }
420
+
421
+ // push an empty body into which we'll inject the proper content later
422
+ parts.push("<div class='modal-body'></div>");
423
+
424
+ if (buttons) {
425
+ parts.push("<div class='modal-footer'>"+buttons+"</div>")
426
+ }
427
+
428
+ parts.push("</div>");
429
+
430
+ var div = $(parts.join("\n"));
431
+
432
+ // check whether we should fade in/out
433
+ var shouldFade = (typeof options.animate === 'undefined') ? _animate : options.animate;
434
+
435
+ if (shouldFade) {
436
+ div.addClass("fade");
437
+ }
438
+
439
+ // now we've built up the div properly we can inject the content whether it was a string or a jQuery object
440
+ $(".modal-body", div).html(str);
441
+
442
+ div.bind('hidden', function() {
443
+ div.remove();
444
+ });
445
+
446
+ div.bind('hide', function() {
447
+ if (hideSource == 'escape' &&
448
+ typeof options.onEscape == 'function') {
449
+ options.onEscape();
450
+ }
451
+ });
452
+
453
+ // hook into the modal's keyup trigger to check for the escape key
454
+ $(document).bind('keyup.modal', function ( e ) {
455
+ if (e.which == 27) {
456
+ hideSource = 'escape';
457
+ }
458
+ });
459
+
460
+ // well, *if* we have a primary - give the last dom element (first displayed) focus
461
+ div.bind('shown', function() {
462
+ $("a.btn-primary:last", div).focus();
463
+ });
464
+
465
+ // wire up button handlers
466
+ div.on('click', '.modal-footer a, a.close', function(e) {
467
+ var handler = $(this).data("handler"),
468
+ cb = callbacks[handler],
469
+ hideModal = null;
470
+
471
+ if (typeof cb == 'function') {
472
+ hideModal = cb();
473
+ }
474
+ if (hideModal !== false){
475
+ e.preventDefault();
476
+ hideSource = 'button';
477
+ div.modal("hide");
478
+ }
479
+ });
480
+
481
+ if (options.keyboard == null) {
482
+ options.keyboard = (typeof options.onEscape == 'function');
483
+ }
484
+
485
+ $("body").append(div);
486
+
487
+ div.modal({
488
+ "backdrop" : (typeof options.backdrop === 'undefined') ? _backdrop : options.backdrop,
489
+ "keyboard" : options.keyboard
490
+ });
491
+
492
+ return div;
493
+ }
494
+
495
+ that.hideAll = function() {
496
+ $(".bootbox").modal("hide");
497
+ }
498
+
499
+ that.animate = function(animate) {
500
+ _animate = animate;
501
+ }
502
+
503
+ that.backdrop = function(backdrop) {
504
+ _backdrop = backdrop;
505
+ }
506
+
507
+ return that;
508
+ })( window.jQuery );