rails_admin 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rails_admin might be problematic. Click here for more details.

Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/rails_admin/jquery.migrate.js +3 -0
  3. data/app/assets/javascripts/rails_admin/ra.filter-box.js +1 -4
  4. data/app/assets/javascripts/rails_admin/ra.nested-form-hooks.js +3 -3
  5. data/app/assets/javascripts/rails_admin/ra.widgets.js +1 -1
  6. data/app/assets/javascripts/rails_admin/rails_admin.js +2 -1
  7. data/app/assets/javascripts/rails_admin/ui.js +2 -2
  8. data/app/helpers/rails_admin/form_builder.rb +2 -0
  9. data/lib/rails_admin/extensions/paper_trail/auditing_adapter.rb +1 -1
  10. data/lib/rails_admin/version.rb +2 -2
  11. data/vendor/assets/javascripts/rails_admin/bootstrap-datetimepicker.js +317 -150
  12. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-affix.js +50 -28
  13. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-alert.js +10 -7
  14. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-button.js +35 -20
  15. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-carousel.js +48 -25
  16. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-collapse.js +70 -28
  17. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-dropdown.js +56 -42
  18. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-modal.js +118 -40
  19. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-popover.js +26 -16
  20. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-scrollspy.js +29 -27
  21. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-tab.js +46 -19
  22. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-tooltip.js +280 -60
  23. data/vendor/assets/javascripts/rails_admin/bootstrap/bootstrap-transition.js +5 -5
  24. data/vendor/assets/javascripts/rails_admin/jquery.pjax.js +317 -160
  25. data/vendor/assets/javascripts/rails_admin/moment-with-locales.js +11210 -9290
  26. metadata +3 -3
  27. data/config/initializers/devise_patch.rb +0 -9
@@ -1,8 +1,8 @@
1
1
  /* ========================================================================
2
- * Bootstrap: affix.js v3.2.0
3
- * http://getbootstrap.com/javascript/#affix
2
+ * Bootstrap: affix.js v3.4.1
3
+ * https://getbootstrap.com/docs/3.4/javascript/#affix
4
4
  * ========================================================================
5
- * Copyright 2011-2014 Twitter, Inc.
5
+ * Copyright 2011-2019 Twitter, Inc.
6
6
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
7
7
  * ======================================================================== */
8
8
 
@@ -16,19 +16,21 @@
16
16
  var Affix = function (element, options) {
17
17
  this.options = $.extend({}, Affix.DEFAULTS, options)
18
18
 
19
- this.$target = $(this.options.target)
19
+ var target = this.options.target === Affix.DEFAULTS.target ? $(this.options.target) : $(document).find(this.options.target)
20
+
21
+ this.$target = target
20
22
  .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
21
23
  .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
22
24
 
23
25
  this.$element = $(element)
24
- this.affixed =
25
- this.unpin =
26
+ this.affixed = null
27
+ this.unpin = null
26
28
  this.pinnedOffset = null
27
29
 
28
30
  this.checkPosition()
29
31
  }
30
32
 
31
- Affix.VERSION = '3.2.0'
33
+ Affix.VERSION = '3.4.1'
32
34
 
33
35
  Affix.RESET = 'affix affix-top affix-bottom'
34
36
 
@@ -37,6 +39,28 @@
37
39
  target: window
38
40
  }
39
41
 
42
+ Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
43
+ var scrollTop = this.$target.scrollTop()
44
+ var position = this.$element.offset()
45
+ var targetHeight = this.$target.height()
46
+
47
+ if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
48
+
49
+ if (this.affixed == 'bottom') {
50
+ if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
51
+ return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
52
+ }
53
+
54
+ var initializing = this.affixed == null
55
+ var colliderTop = initializing ? scrollTop : position.top
56
+ var colliderHeight = initializing ? targetHeight : height
57
+
58
+ if (offsetTop != null && scrollTop <= offsetTop) return 'top'
59
+ if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
60
+
61
+ return false
62
+ }
63
+
40
64
  Affix.prototype.getPinnedOffset = function () {
41
65
  if (this.pinnedOffset) return this.pinnedOffset
42
66
  this.$element.removeClass(Affix.RESET).addClass('affix')
@@ -52,42 +76,40 @@
52
76
  Affix.prototype.checkPosition = function () {
53
77
  if (!this.$element.is(':visible')) return
54
78
 
55
- var scrollHeight = $(document).height()
56
- var scrollTop = this.$target.scrollTop()
57
- var position = this.$element.offset()
79
+ var height = this.$element.height()
58
80
  var offset = this.options.offset
59
81
  var offsetTop = offset.top
60
82
  var offsetBottom = offset.bottom
83
+ var scrollHeight = Math.max($(document).height(), $(document.body).height())
61
84
 
62
85
  if (typeof offset != 'object') offsetBottom = offsetTop = offset
63
86
  if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
64
87
  if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
65
88
 
66
- var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false :
67
- offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
68
- offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false
89
+ var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
69
90
 
70
- if (this.affixed === affix) return
71
- if (this.unpin != null) this.$element.css('top', '')
91
+ if (this.affixed != affix) {
92
+ if (this.unpin != null) this.$element.css('top', '')
72
93
 
73
- var affixType = 'affix' + (affix ? '-' + affix : '')
74
- var e = $.Event(affixType + '.bs.affix')
94
+ var affixType = 'affix' + (affix ? '-' + affix : '')
95
+ var e = $.Event(affixType + '.bs.affix')
75
96
 
76
- this.$element.trigger(e)
97
+ this.$element.trigger(e)
77
98
 
78
- if (e.isDefaultPrevented()) return
99
+ if (e.isDefaultPrevented()) return
79
100
 
80
- this.affixed = affix
81
- this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
101
+ this.affixed = affix
102
+ this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
82
103
 
83
- this.$element
84
- .removeClass(Affix.RESET)
85
- .addClass(affixType)
86
- .trigger($.Event(affixType.replace('affix', 'affixed')))
104
+ this.$element
105
+ .removeClass(Affix.RESET)
106
+ .addClass(affixType)
107
+ .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
108
+ }
87
109
 
88
110
  if (affix == 'bottom') {
89
111
  this.$element.offset({
90
- top: scrollHeight - this.$element.height() - offsetBottom
112
+ top: scrollHeight - height - offsetBottom
91
113
  })
92
114
  }
93
115
  }
@@ -132,8 +154,8 @@
132
154
 
133
155
  data.offset = data.offset || {}
134
156
 
135
- if (data.offsetBottom) data.offset.bottom = data.offsetBottom
136
- if (data.offsetTop) data.offset.top = data.offsetTop
157
+ if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
158
+ if (data.offsetTop != null) data.offset.top = data.offsetTop
137
159
 
138
160
  Plugin.call($spy, data)
139
161
  })
@@ -1,8 +1,8 @@
1
1
  /* ========================================================================
2
- * Bootstrap: alert.js v3.2.0
3
- * http://getbootstrap.com/javascript/#alerts
2
+ * Bootstrap: alert.js v3.4.1
3
+ * https://getbootstrap.com/docs/3.4/javascript/#alerts
4
4
  * ========================================================================
5
- * Copyright 2011-2014 Twitter, Inc.
5
+ * Copyright 2011-2019 Twitter, Inc.
6
6
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
7
7
  * ======================================================================== */
8
8
 
@@ -18,7 +18,9 @@
18
18
  $(el).on('click', dismiss, this.close)
19
19
  }
20
20
 
21
- Alert.VERSION = '3.2.0'
21
+ Alert.VERSION = '3.4.1'
22
+
23
+ Alert.TRANSITION_DURATION = 150
22
24
 
23
25
  Alert.prototype.close = function (e) {
24
26
  var $this = $(this)
@@ -29,12 +31,13 @@
29
31
  selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
30
32
  }
31
33
 
32
- var $parent = $(selector)
34
+ selector = selector === '#' ? [] : selector
35
+ var $parent = $(document).find(selector)
33
36
 
34
37
  if (e) e.preventDefault()
35
38
 
36
39
  if (!$parent.length) {
37
- $parent = $this.hasClass('alert') ? $this : $this.parent()
40
+ $parent = $this.closest('.alert')
38
41
  }
39
42
 
40
43
  $parent.trigger(e = $.Event('close.bs.alert'))
@@ -51,7 +54,7 @@
51
54
  $.support.transition && $parent.hasClass('fade') ?
52
55
  $parent
53
56
  .one('bsTransitionEnd', removeElement)
54
- .emulateTransitionEnd(150) :
57
+ .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
55
58
  removeElement()
56
59
  }
57
60
 
@@ -1,8 +1,8 @@
1
1
  /* ========================================================================
2
- * Bootstrap: button.js v3.2.0
3
- * http://getbootstrap.com/javascript/#buttons
2
+ * Bootstrap: button.js v3.4.1
3
+ * https://getbootstrap.com/docs/3.4/javascript/#buttons
4
4
  * ========================================================================
5
- * Copyright 2011-2014 Twitter, Inc.
5
+ * Copyright 2011-2019 Twitter, Inc.
6
6
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
7
7
  * ======================================================================== */
8
8
 
@@ -19,7 +19,7 @@
19
19
  this.isLoading = false
20
20
  }
21
21
 
22
- Button.VERSION = '3.2.0'
22
+ Button.VERSION = '3.4.1'
23
23
 
24
24
  Button.DEFAULTS = {
25
25
  loadingText: 'loading...'
@@ -31,20 +31,20 @@
31
31
  var val = $el.is('input') ? 'val' : 'html'
32
32
  var data = $el.data()
33
33
 
34
- state = state + 'Text'
34
+ state += 'Text'
35
35
 
36
36
  if (data.resetText == null) $el.data('resetText', $el[val]())
37
37
 
38
- $el[val](data[state] == null ? this.options[state] : data[state])
39
-
40
38
  // push to event loop to allow forms to submit
41
39
  setTimeout($.proxy(function () {
40
+ $el[val](data[state] == null ? this.options[state] : data[state])
41
+
42
42
  if (state == 'loadingText') {
43
43
  this.isLoading = true
44
- $el.addClass(d).attr(d, d)
44
+ $el.addClass(d).attr(d, d).prop(d, true)
45
45
  } else if (this.isLoading) {
46
46
  this.isLoading = false
47
- $el.removeClass(d).removeAttr(d)
47
+ $el.removeClass(d).removeAttr(d).prop(d, false)
48
48
  }
49
49
  }, this), 0)
50
50
  }
@@ -56,13 +56,19 @@
56
56
  if ($parent.length) {
57
57
  var $input = this.$element.find('input')
58
58
  if ($input.prop('type') == 'radio') {
59
- if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
60
- else $parent.find('.active').removeClass('active')
59
+ if ($input.prop('checked')) changed = false
60
+ $parent.find('.active').removeClass('active')
61
+ this.$element.addClass('active')
62
+ } else if ($input.prop('type') == 'checkbox') {
63
+ if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
64
+ this.$element.toggleClass('active')
61
65
  }
62
- if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
66
+ $input.prop('checked', this.$element.hasClass('active'))
67
+ if (changed) $input.trigger('change')
68
+ } else {
69
+ this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
70
+ this.$element.toggleClass('active')
63
71
  }
64
-
65
- if (changed) this.$element.toggleClass('active')
66
72
  }
67
73
 
68
74
 
@@ -100,11 +106,20 @@
100
106
  // BUTTON DATA-API
101
107
  // ===============
102
108
 
103
- $(document).on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
104
- var $btn = $(e.target)
105
- if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
106
- Plugin.call($btn, 'toggle')
107
- e.preventDefault()
108
- })
109
+ $(document)
110
+ .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
111
+ var $btn = $(e.target).closest('.btn')
112
+ Plugin.call($btn, 'toggle')
113
+ if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) {
114
+ // Prevent double click on radios, and the double selections (so cancellation) on checkboxes
115
+ e.preventDefault()
116
+ // The target component still receive the focus
117
+ if ($btn.is('input,button')) $btn.trigger('focus')
118
+ else $btn.find('input:visible,button:visible').first().trigger('focus')
119
+ }
120
+ })
121
+ .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
122
+ $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
123
+ })
109
124
 
110
125
  }(jQuery);
@@ -1,8 +1,8 @@
1
1
  /* ========================================================================
2
- * Bootstrap: carousel.js v3.2.0
3
- * http://getbootstrap.com/javascript/#carousel
2
+ * Bootstrap: carousel.js v3.4.1
3
+ * https://getbootstrap.com/docs/3.4/javascript/#carousel
4
4
  * ========================================================================
5
- * Copyright 2011-2014 Twitter, Inc.
5
+ * Copyright 2011-2019 Twitter, Inc.
6
6
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
7
7
  * ======================================================================== */
8
8
 
@@ -14,29 +14,35 @@
14
14
  // =========================
15
15
 
16
16
  var Carousel = function (element, options) {
17
- this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this))
17
+ this.$element = $(element)
18
18
  this.$indicators = this.$element.find('.carousel-indicators')
19
19
  this.options = options
20
- this.paused =
21
- this.sliding =
22
- this.interval =
23
- this.$active =
20
+ this.paused = null
21
+ this.sliding = null
22
+ this.interval = null
23
+ this.$active = null
24
24
  this.$items = null
25
25
 
26
- this.options.pause == 'hover' && this.$element
26
+ this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
27
+
28
+ this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
27
29
  .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
28
30
  .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
29
31
  }
30
32
 
31
- Carousel.VERSION = '3.2.0'
33
+ Carousel.VERSION = '3.4.1'
34
+
35
+ Carousel.TRANSITION_DURATION = 600
32
36
 
33
37
  Carousel.DEFAULTS = {
34
38
  interval: 5000,
35
39
  pause: 'hover',
36
- wrap: true
40
+ wrap: true,
41
+ keyboard: true
37
42
  }
38
43
 
39
44
  Carousel.prototype.keydown = function (e) {
45
+ if (/input|textarea/i.test(e.target.tagName)) return
40
46
  switch (e.which) {
41
47
  case 37: this.prev(); break
42
48
  case 39: this.next(); break
@@ -63,6 +69,16 @@
63
69
  return this.$items.index(item || this.$active)
64
70
  }
65
71
 
72
+ Carousel.prototype.getItemForDirection = function (direction, active) {
73
+ var activeIndex = this.getItemIndex(active)
74
+ var willWrap = (direction == 'prev' && activeIndex === 0)
75
+ || (direction == 'next' && activeIndex == (this.$items.length - 1))
76
+ if (willWrap && !this.options.wrap) return active
77
+ var delta = direction == 'prev' ? -1 : 1
78
+ var itemIndex = (activeIndex + delta) % this.$items.length
79
+ return this.$items.eq(itemIndex)
80
+ }
81
+
66
82
  Carousel.prototype.to = function (pos) {
67
83
  var that = this
68
84
  var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
@@ -72,7 +88,7 @@
72
88
  if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
73
89
  if (activeIndex == pos) return this.pause().cycle()
74
90
 
75
- return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
91
+ return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
76
92
  }
77
93
 
78
94
  Carousel.prototype.pause = function (e) {
@@ -100,17 +116,11 @@
100
116
 
101
117
  Carousel.prototype.slide = function (type, next) {
102
118
  var $active = this.$element.find('.item.active')
103
- var $next = next || $active[type]()
119
+ var $next = next || this.getItemForDirection(type, $active)
104
120
  var isCycling = this.interval
105
121
  var direction = type == 'next' ? 'left' : 'right'
106
- var fallback = type == 'next' ? 'first' : 'last'
107
122
  var that = this
108
123
 
109
- if (!$next.length) {
110
- if (!this.options.wrap) return
111
- $next = this.$element.find('.item')[fallback]()
112
- }
113
-
114
124
  if ($next.hasClass('active')) return (this.sliding = false)
115
125
 
116
126
  var relatedTarget = $next[0]
@@ -134,7 +144,9 @@
134
144
  var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
135
145
  if ($.support.transition && this.$element.hasClass('slide')) {
136
146
  $next.addClass(type)
137
- $next[0].offsetWidth // force reflow
147
+ if (typeof $next === 'object' && $next.length) {
148
+ $next[0].offsetWidth // force reflow
149
+ }
138
150
  $active.addClass(direction)
139
151
  $next.addClass(direction)
140
152
  $active
@@ -146,7 +158,7 @@
146
158
  that.$element.trigger(slidEvent)
147
159
  }, 0)
148
160
  })
149
- .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000)
161
+ .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
150
162
  } else {
151
163
  $active.removeClass('active')
152
164
  $next.addClass('active')
@@ -195,11 +207,18 @@
195
207
  // CAROUSEL DATA-API
196
208
  // =================
197
209
 
198
- $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
199
- var href
210
+ var clickHandler = function (e) {
200
211
  var $this = $(this)
201
- var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
212
+ var href = $this.attr('href')
213
+ if (href) {
214
+ href = href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
215
+ }
216
+
217
+ var target = $this.attr('data-target') || href
218
+ var $target = $(document).find(target)
219
+
202
220
  if (!$target.hasClass('carousel')) return
221
+
203
222
  var options = $.extend({}, $target.data(), $this.data())
204
223
  var slideIndex = $this.attr('data-slide-to')
205
224
  if (slideIndex) options.interval = false
@@ -211,7 +230,11 @@
211
230
  }
212
231
 
213
232
  e.preventDefault()
214
- })
233
+ }
234
+
235
+ $(document)
236
+ .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
237
+ .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
215
238
 
216
239
  $(window).on('load', function () {
217
240
  $('[data-ride="carousel"]').each(function () {
@@ -1,11 +1,12 @@
1
1
  /* ========================================================================
2
- * Bootstrap: collapse.js v3.2.0
3
- * http://getbootstrap.com/javascript/#collapse
2
+ * Bootstrap: collapse.js v3.4.1
3
+ * https://getbootstrap.com/docs/3.4/javascript/#collapse
4
4
  * ========================================================================
5
- * Copyright 2011-2014 Twitter, Inc.
5
+ * Copyright 2011-2019 Twitter, Inc.
6
6
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
7
7
  * ======================================================================== */
8
8
 
9
+ /* jshint latedef: false */
9
10
 
10
11
  +function ($) {
11
12
  'use strict';
@@ -16,13 +17,22 @@
16
17
  var Collapse = function (element, options) {
17
18
  this.$element = $(element)
18
19
  this.options = $.extend({}, Collapse.DEFAULTS, options)
20
+ this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
21
+ '[data-toggle="collapse"][data-target="#' + element.id + '"]')
19
22
  this.transitioning = null
20
23
 
21
- if (this.options.parent) this.$parent = $(this.options.parent)
24
+ if (this.options.parent) {
25
+ this.$parent = this.getParent()
26
+ } else {
27
+ this.addAriaAndCollapsedClass(this.$element, this.$trigger)
28
+ }
29
+
22
30
  if (this.options.toggle) this.toggle()
23
31
  }
24
32
 
25
- Collapse.VERSION = '3.2.0'
33
+ Collapse.VERSION = '3.4.1'
34
+
35
+ Collapse.TRANSITION_DURATION = 350
26
36
 
27
37
  Collapse.DEFAULTS = {
28
38
  toggle: true
@@ -36,17 +46,21 @@
36
46
  Collapse.prototype.show = function () {
37
47
  if (this.transitioning || this.$element.hasClass('in')) return
38
48
 
49
+ var activesData
50
+ var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
51
+
52
+ if (actives && actives.length) {
53
+ activesData = actives.data('bs.collapse')
54
+ if (activesData && activesData.transitioning) return
55
+ }
56
+
39
57
  var startEvent = $.Event('show.bs.collapse')
40
58
  this.$element.trigger(startEvent)
41
59
  if (startEvent.isDefaultPrevented()) return
42
60
 
43
- var actives = this.$parent && this.$parent.find('> .panel > .in')
44
-
45
61
  if (actives && actives.length) {
46
- var hasData = actives.data('bs.collapse')
47
- if (hasData && hasData.transitioning) return
48
62
  Plugin.call(actives, 'hide')
49
- hasData || actives.data('bs.collapse', null)
63
+ activesData || actives.data('bs.collapse', null)
50
64
  }
51
65
 
52
66
  var dimension = this.dimension()
@@ -54,6 +68,11 @@
54
68
  this.$element
55
69
  .removeClass('collapse')
56
70
  .addClass('collapsing')[dimension](0)
71
+ .attr('aria-expanded', true)
72
+
73
+ this.$trigger
74
+ .removeClass('collapsed')
75
+ .attr('aria-expanded', true)
57
76
 
58
77
  this.transitioning = 1
59
78
 
@@ -72,7 +91,7 @@
72
91
 
73
92
  this.$element
74
93
  .one('bsTransitionEnd', $.proxy(complete, this))
75
- .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize])
94
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
76
95
  }
77
96
 
78
97
  Collapse.prototype.hide = function () {
@@ -88,17 +107,21 @@
88
107
 
89
108
  this.$element
90
109
  .addClass('collapsing')
91
- .removeClass('collapse')
92
- .removeClass('in')
110
+ .removeClass('collapse in')
111
+ .attr('aria-expanded', false)
112
+
113
+ this.$trigger
114
+ .addClass('collapsed')
115
+ .attr('aria-expanded', false)
93
116
 
94
117
  this.transitioning = 1
95
118
 
96
119
  var complete = function () {
97
120
  this.transitioning = 0
98
121
  this.$element
99
- .trigger('hidden.bs.collapse')
100
122
  .removeClass('collapsing')
101
123
  .addClass('collapse')
124
+ .trigger('hidden.bs.collapse')
102
125
  }
103
126
 
104
127
  if (!$.support.transition) return complete.call(this)
@@ -106,13 +129,40 @@
106
129
  this.$element
107
130
  [dimension](0)
108
131
  .one('bsTransitionEnd', $.proxy(complete, this))
109
- .emulateTransitionEnd(350)
132
+ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
110
133
  }
111
134
 
112
135
  Collapse.prototype.toggle = function () {
113
136
  this[this.$element.hasClass('in') ? 'hide' : 'show']()
114
137
  }
115
138
 
139
+ Collapse.prototype.getParent = function () {
140
+ return $(document).find(this.options.parent)
141
+ .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
142
+ .each($.proxy(function (i, element) {
143
+ var $element = $(element)
144
+ this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
145
+ }, this))
146
+ .end()
147
+ }
148
+
149
+ Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
150
+ var isOpen = $element.hasClass('in')
151
+
152
+ $element.attr('aria-expanded', isOpen)
153
+ $trigger
154
+ .toggleClass('collapsed', !isOpen)
155
+ .attr('aria-expanded', isOpen)
156
+ }
157
+
158
+ function getTargetFromTrigger($trigger) {
159
+ var href
160
+ var target = $trigger.attr('data-target')
161
+ || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
162
+
163
+ return $(document).find(target)
164
+ }
165
+
116
166
 
117
167
  // COLLAPSE PLUGIN DEFINITION
118
168
  // ==========================
@@ -123,7 +173,7 @@
123
173
  var data = $this.data('bs.collapse')
124
174
  var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
125
175
 
126
- if (!data && options.toggle && option == 'show') option = !option
176
+ if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
127
177
  if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
128
178
  if (typeof option == 'string') data[option]()
129
179
  })
@@ -148,21 +198,13 @@
148
198
  // =================
149
199
 
150
200
  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
151
- var href
152
201
  var $this = $(this)
153
- var target = $this.attr('data-target')
154
- || e.preventDefault()
155
- || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
156
- var $target = $(target)
202
+
203
+ if (!$this.attr('data-target')) e.preventDefault()
204
+
205
+ var $target = getTargetFromTrigger($this)
157
206
  var data = $target.data('bs.collapse')
158
207
  var option = data ? 'toggle' : $this.data()
159
- var parent = $this.attr('data-parent')
160
- var $parent = parent && $(parent)
161
-
162
- if (!data || !data.transitioning) {
163
- if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed')
164
- $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
165
- }
166
208
 
167
209
  Plugin.call($target, option)
168
210
  })