ui_bibz 1.2.5.3 → 2.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +10 -7
  5. data/README.md +8 -1042
  6. data/app/assets/javascripts/multi_column.coffee +45 -0
  7. data/app/assets/javascripts/{ui_bibz.js.coffee → ui_bibz.coffee} +16 -3
  8. data/app/assets/stylesheets/fix-bootstrap-4.sass +2 -0
  9. data/app/assets/stylesheets/ui_bibz.sass +22 -9
  10. data/app/inputs/custom_inputs/date_picker_input.rb +10 -0
  11. data/app/inputs/custom_inputs/date_picker_input_old.rb +61 -0
  12. data/app/inputs/custom_inputs/multi_column_input.rb +18 -0
  13. data/app/inputs/custom_inputs/multi_select_input.rb +15 -0
  14. data/app/inputs/custom_inputs/surround_input.rb +10 -0
  15. data/doc/UiBibz.html +2 -2
  16. data/doc/UiBibz/Concerns.html +1 -1
  17. data/doc/UiBibz/Concerns/Models.html +1 -1
  18. data/doc/UiBibz/Concerns/Models/Searchable.html +1 -1
  19. data/doc/UiBibz/Concerns/Models/Searchable/ClassMethods.html +5 -5
  20. data/doc/UiBibz/Helpers.html +1 -1
  21. data/doc/UiBibz/Helpers/MetaHelper.html +1 -1
  22. data/doc/UiBibz/Helpers/UiCoreHelper.html +1 -1
  23. data/doc/UiBibz/Helpers/UiUxHelper.html +1 -1
  24. data/doc/UiBibz/Helpers/UtilsHelper.html +1 -1
  25. data/doc/UiBibz/Rails.html +1 -1
  26. data/doc/UiBibz/Rails/Engine.html +1 -1
  27. data/doc/UiBibz/Ui.html +1 -1
  28. data/doc/UiBibz/Ui/Base.html +1 -1
  29. data/doc/UiBibz/Ui/Core.html +1 -1
  30. data/doc/UiBibz/Ui/Core/Alert.html +1 -1
  31. data/doc/UiBibz/Ui/Core/Bar.html +1 -1
  32. data/doc/UiBibz/Ui/Core/Breadcrumb.html +1 -1
  33. data/doc/UiBibz/Ui/Core/BreadcrumbLink.html +1 -1
  34. data/doc/UiBibz/Ui/Core/Button.html +1 -1
  35. data/doc/UiBibz/Ui/Core/ButtonDropdown.html +1 -1
  36. data/doc/UiBibz/Ui/Core/ButtonGroup.html +1 -1
  37. data/doc/UiBibz/Ui/Core/ButtonLink.html +1 -1
  38. data/doc/UiBibz/Ui/Core/ButtonSplitDropdown.html +1 -1
  39. data/doc/UiBibz/Ui/Core/Col.html +1 -1
  40. data/doc/UiBibz/Ui/Core/Component.html +1 -1
  41. data/doc/UiBibz/Ui/Core/Dropdown.html +1 -1
  42. data/doc/UiBibz/Ui/Core/DropdownDivider.html +1 -1
  43. data/doc/UiBibz/Ui/Core/DropdownHeader.html +1 -1
  44. data/doc/UiBibz/Ui/Core/DropdownLink.html +1 -1
  45. data/doc/UiBibz/Ui/Core/Glyph.html +1 -1
  46. data/doc/UiBibz/Ui/Core/Jumbotron.html +1 -1
  47. data/doc/UiBibz/Ui/Core/Label.html +1 -1
  48. data/doc/UiBibz/Ui/Core/List.html +1 -1
  49. data/doc/UiBibz/Ui/Core/ListGroup.html +1 -1
  50. data/doc/UiBibz/Ui/Core/Modal.html +1 -1
  51. data/doc/UiBibz/Ui/Core/ModalBody.html +1 -1
  52. data/doc/UiBibz/Ui/Core/ModalFooter.html +1 -1
  53. data/doc/UiBibz/Ui/Core/ModalHeader.html +1 -1
  54. data/doc/UiBibz/Ui/Core/Nav.html +1 -1
  55. data/doc/UiBibz/Ui/Core/NavDropdown.html +1 -1
  56. data/doc/UiBibz/Ui/Core/NavLink.html +1 -1
  57. data/doc/UiBibz/Ui/Core/Navbar.html +1 -1
  58. data/doc/UiBibz/Ui/Core/NavbarForm.html +1 -1
  59. data/doc/UiBibz/Ui/Core/NavbarNav.html +1 -1
  60. data/doc/UiBibz/Ui/Core/NavbarText.html +1 -1
  61. data/doc/UiBibz/Ui/Core/Panel.html +1 -1
  62. data/doc/UiBibz/Ui/Core/ProgressBar.html +1 -1
  63. data/doc/UiBibz/Ui/Core/Row.html +1 -1
  64. data/doc/UiBibz/Ui/Core/Stars.html +1 -1
  65. data/doc/UiBibz/Ui/Ux.html +1 -1
  66. data/doc/UiBibz/Ui/Ux/Actionable.html +1 -1
  67. data/doc/UiBibz/Ui/Ux/Actions.html +1 -1
  68. data/doc/UiBibz/Ui/Ux/Column.html +1 -1
  69. data/doc/UiBibz/Ui/Ux/Columns.html +1 -1
  70. data/doc/UiBibz/Ui/Ux/Grid.html +1 -1
  71. data/doc/UiBibz/Ui/Ux/Paginable.html +1 -1
  72. data/doc/UiBibz/Ui/Ux/Searchable.html +1 -1
  73. data/doc/UiBibz/Ui/Ux/Sortable.html +1 -1
  74. data/doc/UiBibz/Ui/Ux/Store.html +18 -70
  75. data/doc/UiBibz/Ui/Ux/Table.html +1 -1
  76. data/doc/UiBibz/Ui/Ux/TablePagination.html +1 -1
  77. data/doc/UiBibz/Ui/Ux/TablePaginationPerPage.html +1 -1
  78. data/doc/UiBibz/Ui/Ux/TablePanel.html +1 -1
  79. data/doc/UiBibz/Ui/Ux/TableSearchField.html +1 -1
  80. data/doc/UiBibz/Utils.html +1 -1
  81. data/doc/UiBibz/Utils/Internationalization.html +1 -1
  82. data/doc/_index.html +1 -1
  83. data/doc/file.README.html +47 -51
  84. data/doc/index.html +47 -51
  85. data/doc/method_list.html +69 -75
  86. data/doc/top-level-namespace.html +1 -1
  87. data/lib/ui_bibz.rb +14 -7
  88. data/lib/ui_bibz/helpers/ui_core_helper.rb +46 -11
  89. data/lib/ui_bibz/helpers/ui_ux_helper.rb +7 -3
  90. data/lib/ui_bibz/rails/engine.rb +2 -2
  91. data/lib/ui_bibz/ui/base.rb +5 -0
  92. data/lib/ui_bibz/ui/core/alert.rb +15 -6
  93. data/lib/ui_bibz/ui/core/breadcrumb/breadcrumb.rb +37 -2
  94. data/lib/ui_bibz/ui/core/breadcrumb/components/breadcrumb_link.rb +3 -3
  95. data/lib/ui_bibz/ui/core/button/button.rb +39 -6
  96. data/lib/ui_bibz/ui/core/button/button_choice.rb +104 -0
  97. data/lib/ui_bibz/ui/core/button/button_dropdown.rb +15 -2
  98. data/lib/ui_bibz/ui/core/button/button_group.rb +17 -5
  99. data/lib/ui_bibz/ui/core/button/button_link.rb +8 -11
  100. data/lib/ui_bibz/ui/core/button/button_split_dropdown.rb +15 -5
  101. data/lib/ui_bibz/ui/core/card/card.rb +158 -0
  102. data/lib/ui_bibz/ui/core/card/card_column.rb +60 -0
  103. data/lib/ui_bibz/ui/core/card/card_deck.rb +62 -0
  104. data/lib/ui_bibz/ui/core/card/card_group.rb +60 -0
  105. data/lib/ui_bibz/ui/core/card/components/block/card_block_link.rb +51 -0
  106. data/lib/ui_bibz/ui/core/card/components/block/card_block_text.rb +51 -0
  107. data/lib/ui_bibz/ui/core/card/components/block/card_block_title.rb +51 -0
  108. data/lib/ui_bibz/ui/core/card/components/card_block.rb +67 -0
  109. data/lib/ui_bibz/ui/core/card/components/card_footer.rb +55 -0
  110. data/lib/ui_bibz/ui/core/card/components/card_header.rb +51 -0
  111. data/lib/ui_bibz/ui/core/card/components/card_image.rb +57 -0
  112. data/lib/ui_bibz/ui/core/card/components/card_list_group.rb +47 -0
  113. data/lib/ui_bibz/ui/core/component.rb +78 -32
  114. data/lib/ui_bibz/ui/core/dropdown/components/dropdown_divider.rb +1 -1
  115. data/lib/ui_bibz/ui/core/dropdown/components/dropdown_header.rb +11 -1
  116. data/lib/ui_bibz/ui/core/dropdown/components/dropdown_link.rb +11 -4
  117. data/lib/ui_bibz/ui/core/dropdown/dropdown.rb +14 -6
  118. data/lib/ui_bibz/ui/core/glyph.rb +22 -4
  119. data/lib/ui_bibz/ui/core/input/date_picker_field.rb +125 -0
  120. data/lib/ui_bibz/ui/core/input/multi_column_field.rb +77 -0
  121. data/lib/ui_bibz/ui/core/input/multi_select_field.rb +89 -0
  122. data/lib/ui_bibz/ui/core/input/surround_field.rb +74 -0
  123. data/lib/ui_bibz/ui/core/jumbotron.rb +10 -6
  124. data/lib/ui_bibz/ui/core/label.rb +13 -3
  125. data/lib/ui_bibz/ui/core/{col.rb → layout/col.rb} +38 -18
  126. data/lib/ui_bibz/ui/core/layout/container.rb +65 -0
  127. data/lib/ui_bibz/ui/core/{row.rb → layout/row.rb} +7 -1
  128. data/lib/ui_bibz/ui/core/list/components/list.rb +26 -11
  129. data/lib/ui_bibz/ui/core/list/components/list_body.rb +36 -0
  130. data/lib/ui_bibz/ui/core/list/components/list_header.rb +36 -0
  131. data/lib/ui_bibz/ui/core/list/list_group.rb +7 -3
  132. data/lib/ui_bibz/ui/core/modal/components/modal_body.rb +7 -1
  133. data/lib/ui_bibz/ui/core/modal/components/modal_footer.rb +7 -1
  134. data/lib/ui_bibz/ui/core/modal/components/modal_header.rb +5 -2
  135. data/lib/ui_bibz/ui/core/modal/modal.rb +13 -2
  136. data/lib/ui_bibz/ui/core/nav/components/nav_dropdown.rb +6 -2
  137. data/lib/ui_bibz/ui/core/nav/components/nav_link.rb +7 -18
  138. data/lib/ui_bibz/ui/core/nav/components/nav_link_link.rb +45 -0
  139. data/lib/ui_bibz/ui/core/nav/components/nav_link_list.rb +35 -0
  140. data/lib/ui_bibz/ui/core/nav/components/navbar_brand.rb +40 -0
  141. data/lib/ui_bibz/ui/core/nav/components/navbar_form.rb +2 -2
  142. data/lib/ui_bibz/ui/core/nav/components/navbar_nav.rb +1 -1
  143. data/lib/ui_bibz/ui/core/nav/components/navbar_text.rb +6 -2
  144. data/lib/ui_bibz/ui/core/nav/nav.rb +22 -4
  145. data/lib/ui_bibz/ui/core/nav/navbar.rb +26 -17
  146. data/lib/ui_bibz/ui/core/progress.rb +98 -0
  147. data/lib/ui_bibz/ui/core/stars.rb +13 -5
  148. data/lib/ui_bibz/ui/ux/table/components/actions.rb +4 -0
  149. data/lib/ui_bibz/ui/ux/table/components/store.rb +4 -4
  150. data/lib/ui_bibz/ui/ux/table/extensions/actionable.rb +15 -13
  151. data/lib/ui_bibz/ui/ux/table/extensions/paginable.rb +7 -6
  152. data/lib/ui_bibz/ui/ux/table/extensions/searchable.rb +6 -5
  153. data/lib/ui_bibz/ui/ux/table/table.rb +14 -7
  154. data/lib/ui_bibz/ui/ux/table/{table_panel.rb → table_card.rb} +34 -36
  155. data/lib/ui_bibz/ui/ux/table/table_search_field.rb +19 -11
  156. data/lib/ui_bibz/version.rb +1 -1
  157. data/test/ui/breadcrumb_test.rb +25 -0
  158. data/test/ui/button_test.rb +44 -4
  159. data/test/ui/card_test.rb +106 -0
  160. data/test/ui/col_test.rb +22 -0
  161. data/test/ui/dropdown_test.rb +1 -1
  162. data/test/ui/inputs_test.rb +81 -0
  163. data/test/ui/list_group_test.rb +1 -1
  164. data/test/ui/nav_test.rb +4 -5
  165. data/test/ui/table_test.rb +11 -11
  166. data/test/ui_helper_test.rb +29 -18
  167. data/ui_bibz.gemspec +3 -3
  168. data/vendor/assets/images/switch.png +0 -0
  169. data/vendor/assets/javascripts/bootstrap-4.0.0-alpha.min.js +7 -0
  170. data/vendor/assets/javascripts/bootstrap-multiselect.min.js +1180 -0
  171. data/vendor/assets/javascripts/jquery.multi-select.min.js +1 -0
  172. data/vendor/assets/javascripts/jquery.quicksearch.min.js +1 -0
  173. data/vendor/assets/stylesheets/bootstrap-4.0.0-alpha.min.css +6 -0
  174. data/vendor/assets/stylesheets/bootstrap-datepicker3.min.css +1 -1
  175. data/vendor/assets/stylesheets/bootstrap-multiselect.min.css +1 -0
  176. data/vendor/assets/stylesheets/multi-select.min.css +1 -0
  177. metadata +70 -24
  178. data/.yardoc/checksums +0 -59
  179. data/.yardoc/object_types +0 -0
  180. data/.yardoc/objects/root.dat +0 -0
  181. data/.yardoc/proxy_types +0 -0
  182. data/lib/ui_bibz/helpers/meta_helper.rb +0 -32
  183. data/lib/ui_bibz/ui/core/panel.rb +0 -138
  184. data/lib/ui_bibz/ui/core/progress_bar/components/bar.rb +0 -105
  185. data/lib/ui_bibz/ui/core/progress_bar/progress_bar.rb +0 -94
  186. data/lib/ui_bibz/ui/ux/grid.rb +0 -121
  187. data/test/ui/grid_test.rb +0 -46
  188. data/test/ui/panel_test.rb +0 -52
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ include UiBibz::Helpers
4
+ class ColTest < ActionView::TestCase
5
+
6
+ test "col" do
7
+ actual = UiBibz::Ui::Core::Col.new('test', { num: 1, push: 2, offset: 3, pull: 4 }, { class: 'test' }).render
8
+ expected = "<div class=\"test col-md-1 col-md-push-2 col-md-offset-3 col-md-pull-4\">test</div>"
9
+
10
+ assert_equal expected, actual
11
+ end
12
+
13
+ test "col with several cols" do
14
+ actual = UiBibz::Ui::Core::Col.new(md: { num: 1, push: 2, pull: 4 }, xl: { num: 6, offset: 5 }, class: 'test') do
15
+ "test"
16
+ end.render
17
+ expected = "<div class=\"test col-md-1 col-md-push-2 col-md-pull-4 col-xl-6 col-xl-offset-5\">test</div>"
18
+
19
+ assert_equal expected, actual
20
+ end
21
+
22
+ end
@@ -10,7 +10,7 @@ class DropdownTest < ActionView::TestCase
10
10
  d.divider
11
11
  d.link 'Link3', url: '#link3'
12
12
  end.render
13
- expected = "<div class=\"dropup\"><button class=\"btn btn-success dropdown-toggle\" type=\"button\" data-toggle=\"dropdown\" aria-expanded=\"false\">Dropdown <span class=\"caret\"></span></button><ul class=\"dropdown-menu dropdown-menu-left\" role=\"menu\"><li class=\"\" role=\"presentation\"><a href=\"#link1\"><i class=\"glyph fa fa-eye\"></i> Link 1</a></li><li class=\"dropdown-header\" role=\"presentation\">header</li><li class=\"\" role=\"presentation\"><a href=\"#link2\">Link 2</a></li><li class=\"divider\" role=\"separator\"></li><li class=\"\" role=\"presentation\"><a href=\"#link3\">Link3</a></li></ul></div>"
13
+ expected = "<div class=\"dropup btn-group\"><button class=\"btn btn-success dropdown-toggle\" type=\"button\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"false\">Dropdown <span class=\"caret\"></span></button><div class=\"dropdown-menu dropdown-menu-left\"><a class=\"dropdown-item\" href=\"#link1\"><i class=\"glyph fa fa-eye\"></i> Link 1</a><h6 class=\"dropdown-header\" role=\"presentation\">header</h6><a class=\"dropdown-item\" href=\"#link2\">Link 2</a><div class=\"dropdown-divider\"></div><a class=\"dropdown-item\" href=\"#link3\">Link3</a></div></div>"
14
14
 
15
15
  assert_equal expected, actual
16
16
  end
@@ -0,0 +1,81 @@
1
+ require 'test_helper'
2
+
3
+ include UiBibz::Helpers
4
+ class InputsTest < ActionView::TestCase
5
+
6
+ test "Date Picker Field" do
7
+ actual = UiBibz::Ui::Core::DatePickerField.new('date', { append: 'a', prepend: 'b', range: 'u' }, { class: 'datepicker-test' }).render
8
+ expected = "<div class=\"input-group input-daterange\"><span class=\"input-group-addon\">a</span><input type=\"text\" name=\"date\" id=\"date\" class=\"datepicker-test date_picker form-control\" data-date-locale=\"en\" data-provide=\"datepicker\" data-date-format=\"dd/mm/yyyy\" data-date-today-btn=\"linked\" /><span class=\"input-group-addon input-group-range\">u</span><input type=\"text\" name=\"date\" id=\"date\" class=\"datepicker-test date_picker form-control\" data-date-locale=\"en\" data-provide=\"datepicker\" data-date-format=\"dd/mm/yyyy\" data-date-today-btn=\"linked\" /><span class=\"input-group-addon\">b</span></div>"
9
+
10
+ assert_equal expected, actual
11
+ end
12
+
13
+ test "Date Picker Field data html options" do
14
+ actual = UiBibz::Ui::Core::DatePickerField.new('date', { date_today_highlight: true, calendar_weeks: true, autoclose: true, dates_disabled: ["11/01/2016", "12/01/2016"] }, { class: 'datepicker-test' }).render
15
+ expected = "<input type=\"text\" name=\"date\" id=\"date\" class=\"datepicker-test date_picker form-control\" data-date-locale=\"en\" data-provide=\"datepicker\" data-date-format=\"dd/mm/yyyy\" data-date-today-btn=\"linked\" data-date-today-highlight=\"true\" data-calendar-weeks=\"true\" data-autoclose=\"true\" data-dates-disabled=\"[&quot;11/01/2016&quot;,&quot;12/01/2016&quot;]\" />"
16
+
17
+ assert_equal expected, actual
18
+ end
19
+
20
+ test "Multi Column Field" do
21
+ options = options_for_select(2.times.map{ |i| "option #{i}" })
22
+ actual = UiBibz::Ui::Core::MultiColumnField.new('example', option_tags: options).render
23
+ expected = "<select name=\"example[]\" id=\"example\" class=\"multi-column\" multiple=\"multiple\"><option value=\"option 0\">option 0</option>
24
+ <option value=\"option 1\">option 1</option></select>"
25
+
26
+ assert_equal expected, actual
27
+ end
28
+
29
+ test "Multi Column Field data html options" do
30
+ grouped_options = { 'North America' => [['United States','US'], 'Canada'], 'Europe' => ['Denmark','Germany','France'] }
31
+ actual = UiBibz::Ui::Core::MultiColumnField.new('example', { option_tags: grouped_options_for_select(grouped_options), searchable: true, selectable_opt_group: true }).render
32
+ expected = "<select name=\"example[]\" id=\"example\" data-searchable=\"true\" data-selectable-optgroup=\"true\" class=\"multi-column\" multiple=\"multiple\"><optgroup label=\"North America\"><option value=\"US\">United States</option>
33
+ <option value=\"Canada\">Canada</option></optgroup><optgroup label=\"Europe\"><option value=\"Denmark\">Denmark</option>
34
+ <option value=\"Germany\">Germany</option>
35
+ <option value=\"France\">France</option></optgroup></select>"
36
+
37
+ assert_equal expected, actual
38
+ end
39
+
40
+ test "Multi Select Field" do
41
+ options = options_for_select(2.times.map{ |i| "option #{i}" })
42
+ actual = UiBibz::Ui::Core::MultiSelectField.new('example', option_tags: options).render
43
+ expected = "<select name=\"example[]\" id=\"example\" class=\"btn-primary btn multi-select\" multiple=\"multiple\"><option value=\"option 0\">option 0</option>
44
+ <option value=\"option 1\">option 1</option></select>"
45
+
46
+ assert_equal expected, actual
47
+ end
48
+
49
+ test "Multi Select Field data html options" do
50
+ grouped_options = { 'North America' => [['United States','US'], 'Canada'], 'Europe' => ['Denmark','Germany','France'] }
51
+ actual = UiBibz::Ui::Core::MultiSelectField.new('example', { option_tags: grouped_options_for_select(grouped_options), clickable_opt_group: true, collapsible_opt_group: true, searchable: true, select_all_option: true, state: :danger }).render
52
+ expected = "<select name=\"example[]\" id=\"example\" data-enable-clickable-opt-groups=\"true\" data-enable-collapsible-opt-groups=\"true\" data-enable-filtering=\"true\" data-include-select-all-option=\"true\" class=\"btn-danger btn multi-select\" multiple=\"multiple\"><optgroup label=\"North America\"><option value=\"US\">United States</option>
53
+ <option value=\"Canada\">Canada</option></optgroup><optgroup label=\"Europe\"><option value=\"Denmark\">Denmark</option>
54
+ <option value=\"Germany\">Germany</option>
55
+ <option value=\"France\">France</option></optgroup></select>"
56
+
57
+ assert_equal expected, actual
58
+ end
59
+
60
+ test 'surround_field without prepend and append' do
61
+ actual = UiBibz::Ui::Core::SurroundField.new('test').render
62
+ expected = "<input type=\"text\" name=\"test\" id=\"test\" class=\"form-control\" />"
63
+
64
+ assert_equal expected, actual
65
+ end
66
+
67
+ test 'surround_field with prepend and append' do
68
+ actual = UiBibz::Ui::Core::SurroundField.new('test', append: '1', prepend: '2').render
69
+ expected = "<div class=\"input-group\"><span class=\"input-group-addon\">1</span><input type=\"text\" name=\"test\" id=\"test\" class=\"form-control\" /><span class=\"input-group-addon\">2</span></div>"
70
+
71
+ assert_equal expected, actual
72
+ end
73
+
74
+ test 'surround_field size' do
75
+ actual = UiBibz::Ui::Core::SurroundField.new('test', size: :xs, append: '1', prepend: '2').render
76
+ expected = "<div class=\"input-group input-group-xs\"><span class=\"input-group-addon\">1</span><input type=\"text\" name=\"test\" id=\"test\" class=\"form-control\" /><span class=\"input-group-addon\">2</span></div>"
77
+
78
+ assert_equal expected, actual
79
+ end
80
+
81
+ end
@@ -10,7 +10,7 @@ class ListGroupTest < ActionView::TestCase
10
10
  l.body 'My body'
11
11
  end
12
12
  end.render
13
- expected = "<div class=\"list-group\"><a href=\"#momo\" class=\"list-group-item-success list-group-item\">Momo</a><a type=\"link\" href=\"#toto\" class=\"list-group-item\"><h4 class=\"list-group-item-heading\">My title</h4><p class=\"list-group-item-text\">My body</p></a></div>"
13
+ expected = "<div class=\"list-group\"><a class=\"list-group-item-success list-group-item\" href=\"#momo\">Momo</a><a type=\"link\" class=\"list-group-item\" href=\"#toto\"><h4 class=\"list-group-item-heading\">My title</h4><p class=\"list-group-item-text\">My body</p></a></div>"
14
14
 
15
15
  assert_equal expected, actual
16
16
  end
data/test/ui/nav_test.rb CHANGED
@@ -3,23 +3,22 @@ include UiBibz::Helpers
3
3
  class NavTest < ActionView::TestCase
4
4
 
5
5
  test 'Nav with tab' do
6
- actual = UiBibz::Ui::Core::Nav.new().tap do |n|
6
+ actual = UiBibz::Ui::Core::Nav.new(type: :tabs).tap do |n|
7
7
  n.link 'Home', status: :active, url: "#Home", selector: 'home'
8
8
  n.link 'Profile', url: "#profile", selector: 'profile'
9
9
  n.link 'Messages', url: "#messages", selector: 'messages'
10
10
  end.render
11
- expected = "<ul class=\"nav nav-tabs\"><li class=\"active\" role=\"presentation\"><a aria-controls=\"home\" role=\"tab\" data-toggle=\"tab\" href=\"#Home\">Home</a></li><li role=\"presentation\"><a aria-controls=\"profile\" role=\"tab\" data-toggle=\"tab\" href=\"#profile\">Profile</a></li><li role=\"presentation\"><a aria-controls=\"messages\" role=\"tab\" data-toggle=\"tab\" href=\"#messages\">Messages</a></li></ul>"
12
-
11
+ expected = "<ul class=\"nav nav-tabs\"><li class=\"nav-item\"><a class=\"active nav-link\" data-toggle=\"tab\" role=\"tab\" href=\"#Home\">Home</a></li><li class=\"nav-item\"><a class=\"nav-link\" data-toggle=\"tab\" role=\"tab\" href=\"#profile\">Profile</a></li><li class=\"nav-item\"><a class=\"nav-link\" data-toggle=\"tab\" role=\"tab\" href=\"#messages\">Messages</a></li></ul>"
13
12
  assert_equal expected, actual
14
13
  end
15
14
 
16
15
  test 'Nav with pills' do
17
16
  actual = UiBibz::Ui::Core::Nav.new(type: :pills, position: :justified).tap do |n|
18
17
  n.link 'Home', status: :active, url: "#Home", selector: 'home'
19
- n.link 'Profile', url: "#profile", selector: 'profile', badge: 16
18
+ n.link 'Profile', url: "#profile", selector: 'profile', label: 16
20
19
  n.link 'Messages', url: "#messages", selector: 'messages', status: :disabled
21
20
  end.render
22
- expected = "<ul class=\"nav nav-pills nav-justified\"><li class=\"active\" role=\"presentation\"><a href=\"#Home\">Home</a></li><li role=\"presentation\"><a href=\"#profile\">Profile<span class=\"badge\">16</span></a></li><li class=\"disabled\" role=\"presentation\"><a href=\"#messages\">Messages</a></li></ul>"
21
+ expected = "<ul class=\"nav nav-pills pull-justified\"><li class=\"nav-item\"><a class=\"active nav-link\" href=\"#Home\">Home</a></li><li class=\"nav-item\"><a class=\"nav-link\" href=\"#profile\">Profile<span class=\"label-default pull-right label label-pill\">16</span></a></li><li class=\"nav-item\"><a class=\"disabled nav-link\" href=\"#messages\">Messages</a></li></ul>"
23
22
 
24
23
  assert_equal expected, actual
25
24
  end
@@ -24,7 +24,7 @@ class TableTest < ActionView::TestCase
24
24
 
25
25
  test 'table search field' do
26
26
  actual = UiBibz::Ui::Ux::TableSearchField.new({ store: @users}).render
27
- expected = "<form class=\"form-table-search-field\" action=\"/users?direction=asc&amp;page=1&amp;per_page=2&amp;search=Name+fr&amp;sort=users.name_fr\" accept-charset=\"UTF-8\" method=\"get\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><div class=\"input-group input-group-sm table-search-field\"><span class=\"input-group-addon\"><i class=\"glyph fa fa-search\"></i></span><input type=\"hidden\" name=\"controller\" value=\"users\" /><input type=\"hidden\" name=\"action\" value=\"index\" /><input type=\"hidden\" name=\"sort\" value=\"users.name_fr\" /><input type=\"hidden\" name=\"direction\" value=\"asc\" /><input type=\"hidden\" name=\"search\" value=\"Name fr\" /><input type=\"hidden\" name=\"per_page\" value=\"2\" /><input type=\"hidden\" name=\"page\" value=\"1\" /><input type=\"search\" value=\"Name fr\" name=\"search\" class=\"form-control\" placeholder=\"Search by Name fr and Name en...\" /><span class=\"clear-search-btn input-group-addon\"><i class=\"glyph fa fa-times-circle\"></i></span></div></form>"
27
+ expected = "<form class=\"form-table-search-field\" action=\"/users?direction=asc&amp;page=1&amp;per_page=2&amp;search=Name+fr&amp;sort=users.name_fr\" accept-charset=\"UTF-8\" method=\"get\"><input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /><div class=\"input-group input-group-sm table-search-field\"><span class=\"input-group-addon\"><i class=\"glyph fa fa-search\"></i></span><input type=\"hidden\" name=\"controller\" value=\"users\" /><input type=\"hidden\" name=\"action\" value=\"index\" /><input type=\"hidden\" name=\"sort\" value=\"users.name_fr\" /><input type=\"hidden\" name=\"direction\" value=\"asc\" /><input type=\"hidden\" name=\"search\" value=\"Name fr\" /><input type=\"hidden\" name=\"per_page\" value=\"2\" /><input type=\"hidden\" name=\"page\" value=\"1\" /><input type=\"search\" value=\"Name fr\" name=\"search\" class=\"form-control\" placeholder=\"Search by Name fr and Name en...\" /><span class=\"input-group-btn\"><button type=\"button\" class=\"btn btn-secondary\"><i class=\"glyph fa fa-times-circle\"></i></button></span></div></form>"
28
28
 
29
29
  assert_equal expected, actual
30
30
  end
@@ -114,7 +114,7 @@ class TableTest < ActionView::TestCase
114
114
  options = { actionable: true }
115
115
  action = UiBibz::Ui::Ux::Actionable.new(@store, options)
116
116
  actual = action.body @store.records.first, []
117
- expected = ["<td><div class=\"btn-group-xs dropdown-action dropdown\"><button class=\"btn btn-default dropdown-toggle\" type=\"button\" data-toggle=\"dropdown\" aria-expanded=\"false\"><i class=\"glyph fa fa-ellipsis-v fa-fw\"></i> Actions <span class=\"caret\"></span></button><ul class=\"dropdown-menu dropdown-menu-right\" role=\"menu\"></ul></div></td>"]
117
+ expected = ["<td></td>"]
118
118
 
119
119
  assert_equal expected, actual
120
120
  end
@@ -146,23 +146,23 @@ class TableTest < ActionView::TestCase
146
146
  assert_equal expected, actual
147
147
  end
148
148
 
149
- test 'simple table_panel' do
150
- actual = UiBibz::Ui::Ux::TablePanel.new(store: @users, tap: true).render
149
+ test 'simple table_card' do
150
+ actual = UiBibz::Ui::Ux::TableCard.new(store: @users, tap: true).render
151
151
  end
152
152
 
153
- test 'complex table_panel' do
154
- actual = UiBibz::Ui::Ux::TablePanel.new({ store: @users, tap: true }, { class: 'toto' }).tap do |pane|
153
+ test 'complex table_card' do
154
+ actual = UiBibz::Ui::Ux::TableCard.new({ store: @users, tap: true }, { class: 'toto' }).tap do |pane|
155
155
  pane.header 'Test header'
156
- pane.body class: 'ui' do
156
+ pane.block class: 'ui' do
157
157
  'Test body'
158
158
  end
159
159
  end.render
160
160
  end
161
161
 
162
- test 'complex table_panel with custom actions' do
163
- actual = UiBibz::Ui::Ux::TablePanel.new({ store: @users, tap: true }, { class: 'toto'}).tap do |pane|
162
+ test 'complex table_card with custom actions' do
163
+ actual = UiBibz::Ui::Ux::TableCard.new({ store: @users, tap: true }, { class: 'toto'}).tap do |pane|
164
164
  pane.header 'Test header'
165
- pane.body cls: 'ui' do
165
+ pane.block cls: 'ui' do
166
166
  'Test body'
167
167
  end
168
168
  pane.columns do |c|
@@ -180,7 +180,7 @@ class TableTest < ActionView::TestCase
180
180
  end
181
181
 
182
182
  test 'format action' do
183
- actual = UiBibz::Ui::Ux::TablePanel.new(store: @users, tap: true).tap do |pane|
183
+ actual = UiBibz::Ui::Ux::TableCard.new(store: @users, tap: true).tap do |pane|
184
184
  pane.columns do |cls|
185
185
  cls.column :id, name: '#'
186
186
  end
@@ -5,7 +5,7 @@ include UiBibz::Helpers::UiCoreHelper
5
5
  class UiHelperTest < ActionView::TestCase
6
6
 
7
7
  test 'breadcrumb' do
8
- actual = breadcrumb do |b|
8
+ actual = breadcrumb(tap: true) do |b|
9
9
  b.link 'Home', url: '#home', glyph: 'home'
10
10
  b.link 'Toto', { url: '#toto', status: :active }
11
11
  end
@@ -14,9 +14,17 @@ class UiHelperTest < ActionView::TestCase
14
14
  assert_equal expected, actual
15
15
  end
16
16
 
17
+ test 'breadcrumb with store' do
18
+ users = create_list(:user, 3)
19
+ actual = breadcrumb store: users, link_label: :name_fr, link_url: user_path(:id)
20
+ expected = "<ol class=\"breadcrumb\"><li><a href=\"/users/1/\">Name fr</a></li><li><a href=\"/users/2/\">Name fr</a></li><li>Name fr</li></ol>"
21
+
22
+ assert_equal expected, actual
23
+ end
24
+
17
25
  test 'alert' do
18
26
  actual = notify 'toto'
19
- expected = "<div class=\"alert-info alert\" role=\"alert\">toto</div>"
27
+ expected = "<div class=\"alert-info alert alert-dismissible\" role=\"alert\">toto</div>"
20
28
 
21
29
  assert_equal expected, actual
22
30
  end
@@ -28,23 +36,23 @@ class UiHelperTest < ActionView::TestCase
28
36
  assert_equal expected, actual
29
37
  end
30
38
 
31
- test 'jumbotron with full_width' do
32
- actual = jumbotron 'toto', full_width: true
33
- expected = "<div class=\"jumbotron\"><div class=\"container\">toto</div></div>"
39
+ test 'etiquette pill' do
40
+ actual = etiquette 'toto', state: :success, type: :pill, glyph: 'pencil'
41
+ expected = "<span class=\"label-success label label-pill\"><i class=\"glyph fa fa-pencil\"></i> toto</span>"
34
42
 
35
43
  assert_equal expected, actual
36
44
  end
37
45
 
38
- test 'jumbotron' do
39
- actual = jumbotron 'toto'
46
+ test 'jumbotron with full_width' do
47
+ actual = jumbotron 'toto', full_width: true
40
48
  expected = "<div class=\"jumbotron\">toto</div>"
41
49
 
42
50
  assert_equal expected, actual
43
51
  end
44
52
 
45
- test 'progress bar' do
46
- actual = progress_bar 30
47
- expected = "<div class=\"progress\"><div class=\"progress-bar\" role=\"progressbar\" aria-valuenow=\"30\" aria-valuemin=\"0\" aria-valuemax=\"100\" style=\"width: 30%\">30%</div></div>"
53
+ test 'jumbotron' do
54
+ actual = jumbotron 'toto'
55
+ expected = "<div class=\"jumbotron\">toto</div>"
48
56
 
49
57
  assert_equal expected, actual
50
58
  end
@@ -65,13 +73,16 @@ class UiHelperTest < ActionView::TestCase
65
73
  refute_equal expected, actual
66
74
  end
67
75
 
68
- test 'progress bar with several bars' do
69
- actual = progress_bar(tap: true) do |pb|
70
- pb.bar 20, type: :animated
71
- pb.bar 30, state: :success
72
- pb.bar 10, type: :striped
73
- end
74
- expected = "<div class=\"progress\"><div class=\"progress-bar progress-bar-striped active\" role=\"progressbar\" aria-valuenow=\"20\" aria-valuemin=\"0\" aria-valuemax=\"100\" style=\"width: 20%\">20%</div><div class=\"progress-bar-success progress-bar\" role=\"progressbar\" aria-valuenow=\"30\" aria-valuemin=\"0\" aria-valuemax=\"100\" style=\"width: 30%\">30%</div><div class=\"progress-bar progress-bar-striped\" role=\"progressbar\" aria-valuenow=\"10\" aria-valuemin=\"0\" aria-valuemax=\"100\" style=\"width: 10%\">10%</div></div>"
76
+ test 'progress bar' do
77
+ actual = progress(20)
78
+ expected = "<progress class=\"progress\" max=\"100\" value=\"20\">20</progress>"
79
+
80
+ assert_equal expected, actual
81
+ end
82
+
83
+ test 'contaner' do
84
+ actual = container type: :fluid
85
+ expected = "<div class=\"container-fluid\"></div>"
75
86
 
76
87
  assert_equal expected, actual
77
88
  end
@@ -85,7 +96,7 @@ class UiHelperTest < ActionView::TestCase
85
96
  concat button_link 'Save', url: '#', state: :primary
86
97
  end
87
98
  end
88
- expected = "<div class=\"modal\"><div class=\"modal-dialog\" role=\"document\"><div class=\"modal-content\"><div class=\"modal-header\"><button class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">×</span><span class=\"sr-only\">Close</span></button><h4 class=\"modal-title\">My title</h4></div><div class=\"modal-body\">My body</div><div class=\"modal-footer\"><a class=\"btn- btn\" href=\"#\">Close</a><a class=\"btn-primary btn\" href=\"#\">Save</a></div></div></div></div>"
99
+ expected = "<div class=\"modal\"><div class=\"modal-dialog \" role=\"document\"><div class=\"modal-content\"><div class=\"modal-header\"><button class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">×</span><span class=\"sr-only\">Close</span></button><h4 class=\"modal-title\">My title</h4></div><div class=\"modal-body\">My body</div><div class=\"modal-footer\"><a class=\"btn-link btn\" href=\"#\">Close</a><a class=\"btn-primary btn\" href=\"#\">Save</a></div></div></div></div>"
89
100
 
90
101
  assert_equal expected, actual
91
102
  end
data/ui_bibz.gemspec CHANGED
@@ -21,19 +21,19 @@ Gem::Specification.new do |s|
21
21
 
22
22
  # rails
23
23
  s.add_dependency 'rails', '>= 4.2.0'
24
- s.add_dependency 'will_paginate', '~> 3.0.7'
24
+ s.add_dependency 'will_paginate', '~> 3.1.0'
25
25
  s.add_dependency 'will_paginate-bootstrap'
26
26
  s.add_dependency 'will-paginate-i18n'
27
27
  s.add_dependency 'haml'
28
28
  s.add_dependency 'haml-rails'
29
29
  s.add_dependency 'sass-rails', '>= 5.0.0'
30
30
  s.add_dependency 'font-awesome-sass'
31
- s.add_dependency 'bootstrap-sass', '~> 3.3.5'
31
+ s.add_dependency 'bootstrap', '~> 4.0.0.alpha3'
32
+ s.add_dependency 'rails-assets-tether', '>= 1.1.0'
32
33
 
33
34
  s.add_development_dependency "minitest"
34
35
  s.add_development_dependency "rdoc"
35
36
  s.add_development_dependency "sqlite3"
36
37
  s.add_development_dependency "factory_girl_rails", "~> 4.0"
37
38
  s.add_development_dependency "codeclimate-test-reporter"
38
-
39
39
  end
Binary file
@@ -0,0 +1,7 @@
1
+ /*!
2
+ * Bootstrap v4.0.0-alpha (http://getbootstrap.com)
3
+ * Copyright 2011-2015 Twitter, Inc.
4
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5
+ */
6
+ if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(){"use strict";function a(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}function b(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}{var c=function(a,b,c){for(var d=!0;d;){var e=a,f=b,g=c;h=j=i=void 0,d=!1,null===e&&(e=Function.prototype);var h=Object.getOwnPropertyDescriptor(e,f);if(void 0!==h){if("value"in h)return h.value;var i=h.get;return void 0===i?void 0:i.call(g)}var j=Object.getPrototypeOf(e);if(null===j)return void 0;a=j,b=f,c=g,d=!0}},d=function(){function a(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1,d.configurable=!0,"value"in d&&(d.writable=!0),Object.defineProperty(a,d.key,d)}}return function(b,c,d){return c&&a(b.prototype,c),d&&a(b,d),b}}(),e=function(a){function b(a){return{}.toString.call(a).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function c(a){return(a[0]||a).nodeType}function d(){return{bindType:h.end,delegateType:h.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}}}function e(){if(window.QUnit)return!1;var a=document.createElement("bootstrap");for(var b in i)if(void 0!==a.style[b])return{end:i[b]};return!1}function f(b){var c=this,d=!1;return a(this).one(j.TRANSITION_END,function(){d=!0}),setTimeout(function(){d||j.triggerTransitionEnd(c)},b),this}function g(){h=e(),a.fn.emulateTransitionEnd=f,j.supportsTransitionEnd()&&(a.event.special[j.TRANSITION_END]=d())}var h=!1,i={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},j={TRANSITION_END:"bsTransitionEnd",getUID:function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},getSelectorFromElement:function(a){var b=a.getAttribute("data-target");return b||(b=a.getAttribute("href")||"",b=/^#[a-z]/i.test(b)?b:null),b},reflow:function(a){new Function("bs","return bs")(a.offsetHeight)},triggerTransitionEnd:function(b){a(b).trigger(h.end)},supportsTransitionEnd:function(){return Boolean(h)},typeCheckConfig:function(a,d,e){for(var f in e)if(e.hasOwnProperty(f)){var g=e[f],h=d[f],i=void 0;if(i=h&&c(h)?"element":b(h),!new RegExp(g).test(i))throw new Error(a.toUpperCase()+": "+('Option "'+f+'" provided type "'+i+'" ')+('but expected type "'+g+'".'))}}};return g(),j}(jQuery),f=(function(a){var c="alert",f="4.0.0",g="bs.alert",h="."+g,i=".data-api",j=a.fn[c],k=150,l={DISMISS:'[data-dismiss="alert"]'},m={CLOSE:"close"+h,CLOSED:"closed"+h,CLICK_DATA_API:"click"+h+i},n={ALERT:"alert",FADE:"fade",IN:"in"},o=function(){function c(a){b(this,c),this._element=a}return d(c,[{key:"close",value:function(a){a=a||this._element;var b=this._getRootElement(a),c=this._triggerCloseEvent(b);c.isDefaultPrevented()||this._removeElement(b)}},{key:"dispose",value:function(){a.removeData(this._element,g),this._element=null}},{key:"_getRootElement",value:function(b){var c=e.getSelectorFromElement(b),d=!1;return c&&(d=a(c)[0]),d||(d=a(b).closest("."+n.ALERT)[0]),d}},{key:"_triggerCloseEvent",value:function(b){var c=a.Event(m.CLOSE);return a(b).trigger(c),c}},{key:"_removeElement",value:function(b){return a(b).removeClass(n.IN),e.supportsTransitionEnd()&&a(b).hasClass(n.FADE)?void a(b).one(e.TRANSITION_END,a.proxy(this._destroyElement,this,b)).emulateTransitionEnd(k):void this._destroyElement(b)}},{key:"_destroyElement",value:function(b){a(b).detach().trigger(m.CLOSED).remove()}}],[{key:"_jQueryInterface",value:function(b){return this.each(function(){var d=a(this),e=d.data(g);e||(e=new c(this),d.data(g,e)),"close"===b&&e[b](this)})}},{key:"_handleDismiss",value:function(a){return function(b){b&&b.preventDefault(),a.close(this)}}},{key:"VERSION",get:function(){return f}}]),c}();return a(document).on(m.CLICK_DATA_API,l.DISMISS,o._handleDismiss(new o)),a.fn[c]=o._jQueryInterface,a.fn[c].Constructor=o,a.fn[c].noConflict=function(){return a.fn[c]=j,o._jQueryInterface},o}(jQuery),function(a){var c="button",e="4.0.0",f="bs.button",g="."+f,h=".data-api",i=a.fn[c],j={ACTIVE:"active",BUTTON:"btn",FOCUS:"focus"},k={DATA_TOGGLE_CARROT:'[data-toggle^="button"]',DATA_TOGGLE:'[data-toggle="buttons"]',INPUT:"input",ACTIVE:".active",BUTTON:".btn"},l={CLICK_DATA_API:"click"+g+h,FOCUS_BLUR_DATA_API:"focus"+g+h+" "+("blur"+g+h)},m=function(){function c(a){b(this,c),this._element=a}return d(c,[{key:"toggle",value:function(){var b=!0,c=a(this._element).closest(k.DATA_TOGGLE)[0];if(c){var d=a(this._element).find(k.INPUT)[0];if(d){if("radio"===d.type)if(d.checked&&a(this._element).hasClass(j.ACTIVE))b=!1;else{var e=a(c).find(k.ACTIVE)[0];e&&a(e).removeClass(j.ACTIVE)}b&&(d.checked=!a(this._element).hasClass(j.ACTIVE),a(this._element).trigger("change"))}}else this._element.setAttribute("aria-pressed",!a(this._element).hasClass(j.ACTIVE));b&&a(this._element).toggleClass(j.ACTIVE)}},{key:"dispose",value:function(){a.removeData(this._element,f),this._element=null}}],[{key:"_jQueryInterface",value:function(b){return this.each(function(){var d=a(this).data(f);d||(d=new c(this),a(this).data(f,d)),"toggle"===b&&d[b]()})}},{key:"VERSION",get:function(){return e}}]),c}();return a(document).on(l.CLICK_DATA_API,k.DATA_TOGGLE_CARROT,function(b){b.preventDefault();var c=b.target;a(c).hasClass(j.BUTTON)||(c=a(c).closest(k.BUTTON)),m._jQueryInterface.call(a(c),"toggle")}).on(l.FOCUS_BLUR_DATA_API,k.DATA_TOGGLE_CARROT,function(b){var c=a(b.target).closest(k.BUTTON)[0];a(c).toggleClass(j.FOCUS,/^focus(in)?$/.test(b.type))}),a.fn[c]=m._jQueryInterface,a.fn[c].Constructor=m,a.fn[c].noConflict=function(){return a.fn[c]=i,m._jQueryInterface},m}(jQuery),function(a){var c="carousel",f="4.0.0",g="bs.carousel",h="."+g,i=".data-api",j=a.fn[c],k=600,l={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0},m={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean"},n={NEXT:"next",PREVIOUS:"prev"},o={SLIDE:"slide"+h,SLID:"slid"+h,KEYDOWN:"keydown"+h,MOUSEENTER:"mouseenter"+h,MOUSELEAVE:"mouseleave"+h,LOAD_DATA_API:"load"+h+i,CLICK_DATA_API:"click"+h+i},p={CAROUSEL:"carousel",ACTIVE:"active",SLIDE:"slide",RIGHT:"right",LEFT:"left",ITEM:"carousel-item"},q={ACTIVE:".active",ACTIVE_ITEM:".active.carousel-item",ITEM:".carousel-item",NEXT_PREV:".next, .prev",INDICATORS:".carousel-indicators",DATA_SLIDE:"[data-slide], [data-slide-to]",DATA_RIDE:'[data-ride="carousel"]'},r=function(){function i(c,d){b(this,i),this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this._config=this._getConfig(d),this._element=a(c)[0],this._indicatorsElement=a(this._element).find(q.INDICATORS)[0],this._addEventListeners()}return d(i,[{key:"next",value:function(){this._isSliding||this._slide(n.NEXT)}},{key:"prev",value:function(){this._isSliding||this._slide(n.PREVIOUS)}},{key:"pause",value:function(b){b||(this._isPaused=!0),a(this._element).find(q.NEXT_PREV)[0]&&e.supportsTransitionEnd()&&(e.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null}},{key:"cycle",value:function(b){b||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval(a.proxy(this.next,this),this._config.interval))}},{key:"to",value:function(b){var c=this;this._activeElement=a(this._element).find(q.ACTIVE_ITEM)[0];var d=this._getItemIndex(this._activeElement);if(!(b>this._items.length-1||0>b)){if(this._isSliding)return void a(this._element).one(o.SLID,function(){return c.to(b)});if(d===b)return this.pause(),void this.cycle();var e=b>d?n.NEXT:n.PREVIOUS;this._slide(e,this._items[b])}}},{key:"dispose",value:function(){a(this._element).off(h),a.removeData(this._element,g),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null}},{key:"_getConfig",value:function(b){return b=a.extend({},l,b),e.typeCheckConfig(c,b,m),b}},{key:"_addEventListeners",value:function(){this._config.keyboard&&a(this._element).on(o.KEYDOWN,a.proxy(this._keydown,this)),"hover"!==this._config.pause||"ontouchstart"in document.documentElement||a(this._element).on(o.MOUSEENTER,a.proxy(this.pause,this)).on(o.MOUSELEAVE,a.proxy(this.cycle,this))}},{key:"_keydown",value:function(a){if(a.preventDefault(),!/input|textarea/i.test(a.target.tagName))switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}}},{key:"_getItemIndex",value:function(b){return this._items=a.makeArray(a(b).parent().find(q.ITEM)),this._items.indexOf(b)}},{key:"_getItemByDirection",value:function(a,b){var c=a===n.NEXT,d=a===n.PREVIOUS,e=this._getItemIndex(b),f=this._items.length-1,g=d&&0===e||c&&e===f;if(g&&!this._config.wrap)return b;var h=a===n.PREVIOUS?-1:1,i=(e+h)%this._items.length;return-1===i?this._items[this._items.length-1]:this._items[i]}},{key:"_triggerSlideEvent",value:function(b,c){var d=a.Event(o.SLIDE,{relatedTarget:b,direction:c});return a(this._element).trigger(d),d}},{key:"_setActiveIndicatorElement",value:function(b){if(this._indicatorsElement){a(this._indicatorsElement).find(q.ACTIVE).removeClass(p.ACTIVE);var c=this._indicatorsElement.children[this._getItemIndex(b)];c&&a(c).addClass(p.ACTIVE)}}},{key:"_slide",value:function(b,c){var d=this,f=a(this._element).find(q.ACTIVE_ITEM)[0],g=c||f&&this._getItemByDirection(b,f),h=Boolean(this._interval),i=b===n.NEXT?p.LEFT:p.RIGHT;if(g&&a(g).hasClass(p.ACTIVE))return void(this._isSliding=!1);var j=this._triggerSlideEvent(g,i);if(!j.isDefaultPrevented()&&f&&g){this._isSliding=!0,h&&this.pause(),this._setActiveIndicatorElement(g);var l=a.Event(o.SLID,{relatedTarget:g,direction:i});e.supportsTransitionEnd()&&a(this._element).hasClass(p.SLIDE)?(a(g).addClass(b),e.reflow(g),a(f).addClass(i),a(g).addClass(i),a(f).one(e.TRANSITION_END,function(){a(g).removeClass(i).removeClass(b),a(g).addClass(p.ACTIVE),a(f).removeClass(p.ACTIVE).removeClass(b).removeClass(i),d._isSliding=!1,setTimeout(function(){return a(d._element).trigger(l)},0)}).emulateTransitionEnd(k)):(a(f).removeClass(p.ACTIVE),a(g).addClass(p.ACTIVE),this._isSliding=!1,a(this._element).trigger(l)),h&&this.cycle()}}}],[{key:"_jQueryInterface",value:function(b){return this.each(function(){var c=a(this).data(g),d=a.extend({},l,a(this).data());"object"==typeof b&&a.extend(d,b);var e="string"==typeof b?b:d.slide;c||(c=new i(this,d),a(this).data(g,c)),"number"==typeof b?c.to(b):e?c[e]():d.interval&&(c.pause(),c.cycle())})}},{key:"_dataApiClickHandler",value:function(b){var c=e.getSelectorFromElement(this);if(c){var d=a(c)[0];if(d&&a(d).hasClass(p.CAROUSEL)){var f=a.extend({},a(d).data(),a(this).data()),h=this.getAttribute("data-slide-to");h&&(f.interval=!1),i._jQueryInterface.call(a(d),f),h&&a(d).data(g).to(h),b.preventDefault()}}}},{key:"VERSION",get:function(){return f}},{key:"Default",get:function(){return l}}]),i}();return a(document).on(o.CLICK_DATA_API,q.DATA_SLIDE,r._dataApiClickHandler),a(window).on(o.LOAD_DATA_API,function(){a(q.DATA_RIDE).each(function(){var b=a(this);r._jQueryInterface.call(b,b.data())})}),a.fn[c]=r._jQueryInterface,a.fn[c].Constructor=r,a.fn[c].noConflict=function(){return a.fn[c]=j,r._jQueryInterface},r}(jQuery),function(a){var c="collapse",f="4.0.0",g="bs.collapse",h="."+g,i=".data-api",j=a.fn[c],k=600,l={toggle:!0,parent:""},m={toggle:"boolean",parent:"string"},n={SHOW:"show"+h,SHOWN:"shown"+h,HIDE:"hide"+h,HIDDEN:"hidden"+h,CLICK_DATA_API:"click"+h+i},o={IN:"in",COLLAPSE:"collapse",COLLAPSING:"collapsing",COLLAPSED:"collapsed"},p={WIDTH:"width",HEIGHT:"height"},q={ACTIVES:".panel > .in, .panel > .collapsing",DATA_TOGGLE:'[data-toggle="collapse"]'},r=function(){function h(c,d){b(this,h),this._isTransitioning=!1,this._element=c,this._config=this._getConfig(d),this._triggerArray=a.makeArray(a('[data-toggle="collapse"][href="#'+c.id+'"],'+('[data-toggle="collapse"][data-target="#'+c.id+'"]'))),this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}return d(h,[{key:"toggle",value:function(){a(this._element).hasClass(o.IN)?this.hide():this.show()}},{key:"show",value:function(){var b=this;if(!this._isTransitioning&&!a(this._element).hasClass(o.IN)){var c=void 0,d=void 0;if(this._parent&&(c=a.makeArray(a(q.ACTIVES)),c.length||(c=null)),!(c&&(d=a(c).data(g),d&&d._isTransitioning))){var f=a.Event(n.SHOW);if(a(this._element).trigger(f),!f.isDefaultPrevented()){c&&(h._jQueryInterface.call(a(c),"hide"),d||a(c).data(g,null));var i=this._getDimension();a(this._element).removeClass(o.COLLAPSE).addClass(o.COLLAPSING),this._element.style[i]=0,this._element.setAttribute("aria-expanded",!0),this._triggerArray.length&&a(this._triggerArray).removeClass(o.COLLAPSED).attr("aria-expanded",!0),this.setTransitioning(!0);var j=function(){a(b._element).removeClass(o.COLLAPSING).addClass(o.COLLAPSE).addClass(o.IN),b._element.style[i]="",b.setTransitioning(!1),a(b._element).trigger(n.SHOWN)};if(!e.supportsTransitionEnd())return void j();var l=i[0].toUpperCase()+i.slice(1),m="scroll"+l;a(this._element).one(e.TRANSITION_END,j).emulateTransitionEnd(k),this._element.style[i]=this._element[m]+"px"}}}}},{key:"hide",value:function(){var b=this;if(!this._isTransitioning&&a(this._element).hasClass(o.IN)){var c=a.Event(n.HIDE);if(a(this._element).trigger(c),!c.isDefaultPrevented()){var d=this._getDimension(),f=d===p.WIDTH?"offsetWidth":"offsetHeight";this._element.style[d]=this._element[f]+"px",e.reflow(this._element),a(this._element).addClass(o.COLLAPSING).removeClass(o.COLLAPSE).removeClass(o.IN),this._element.setAttribute("aria-expanded",!1),this._triggerArray.length&&a(this._triggerArray).addClass(o.COLLAPSED).attr("aria-expanded",!1),this.setTransitioning(!0);var g=function(){b.setTransitioning(!1),a(b._element).removeClass(o.COLLAPSING).addClass(o.COLLAPSE).trigger(n.HIDDEN)};return this._element.style[d]=0,e.supportsTransitionEnd()?void a(this._element).one(e.TRANSITION_END,g).emulateTransitionEnd(k):void g()}}}},{key:"setTransitioning",value:function(a){this._isTransitioning=a}},{key:"dispose",value:function(){a.removeData(this._element,g),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null}},{key:"_getConfig",value:function(b){return b=a.extend({},l,b),b.toggle=Boolean(b.toggle),e.typeCheckConfig(c,b,m),b}},{key:"_getDimension",value:function(){var b=a(this._element).hasClass(p.WIDTH);return b?p.WIDTH:p.HEIGHT}},{key:"_getParent",value:function(){var b=this,c=a(this._config.parent)[0],d='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]';return a(c).find(d).each(function(a,c){b._addAriaAndCollapsedClass(h._getTargetFromElement(c),[c])}),c}},{key:"_addAriaAndCollapsedClass",value:function(b,c){if(b){var d=a(b).hasClass(o.IN);b.setAttribute("aria-expanded",d),c.length&&a(c).toggleClass(o.COLLAPSED,!d).attr("aria-expanded",d)}}}],[{key:"_getTargetFromElement",value:function(b){var c=e.getSelectorFromElement(b);return c?a(c)[0]:null}},{key:"_jQueryInterface",value:function(b){return this.each(function(){var c=a(this),d=c.data(g),e=a.extend({},l,c.data(),"object"==typeof b&&b);!d&&e.toggle&&/show|hide/.test(b)&&(e.toggle=!1),d||(d=new h(this,e),c.data(g,d)),"string"==typeof b&&d[b]()})}},{key:"VERSION",get:function(){return f}},{key:"Default",get:function(){return l}}]),h}();return a(document).on(n.CLICK_DATA_API,q.DATA_TOGGLE,function(b){b.preventDefault();var c=r._getTargetFromElement(this),d=a(c).data(g),e=d?"toggle":a(this).data();r._jQueryInterface.call(a(c),e)}),a.fn[c]=r._jQueryInterface,a.fn[c].Constructor=r,a.fn[c].noConflict=function(){return a.fn[c]=j,r._jQueryInterface},r}(jQuery),function(a){var c="dropdown",f="4.0.0",g="bs.dropdown",h="."+g,i=".data-api",j=a.fn[c],k={HIDE:"hide"+h,HIDDEN:"hidden"+h,SHOW:"show"+h,SHOWN:"shown"+h,CLICK:"click"+h,CLICK_DATA_API:"click"+h+i,KEYDOWN_DATA_API:"keydown"+h+i},l={BACKDROP:"dropdown-backdrop",DISABLED:"disabled",OPEN:"open"},m={BACKDROP:".dropdown-backdrop",DATA_TOGGLE:'[data-toggle="dropdown"]',FORM_CHILD:".dropdown form",ROLE_MENU:'[role="menu"]',ROLE_LISTBOX:'[role="listbox"]',NAVBAR_NAV:".navbar-nav",VISIBLE_ITEMS:'[role="menu"] li:not(.disabled) a, [role="listbox"] li:not(.disabled) a'},n=function(){function c(a){b(this,c),this._element=a,this._addEventListeners()}return d(c,[{key:"toggle",value:function(){if(this.disabled||a(this).hasClass(l.DISABLED))return!1;var b=c._getParentFromElement(this),d=a(b).hasClass(l.OPEN);if(c._clearMenus(),d)return!1;if("ontouchstart"in document.documentElement&&!a(b).closest(m.NAVBAR_NAV).length){var e=document.createElement("div");e.className=l.BACKDROP,a(e).insertBefore(this),a(e).on("click",c._clearMenus)}var f={relatedTarget:this},g=a.Event(k.SHOW,f);return a(b).trigger(g),g.isDefaultPrevented()?!1:(this.focus(),this.setAttribute("aria-expanded","true"),a(b).toggleClass(l.OPEN),a(b).trigger(a.Event(k.SHOWN,f)),!1)}},{key:"dispose",value:function(){a.removeData(this._element,g),a(this._element).off(h),this._element=null}},{key:"_addEventListeners",value:function(){a(this._element).on(k.CLICK,this.toggle)}}],[{key:"_jQueryInterface",value:function(b){return this.each(function(){var d=a(this).data(g);d||a(this).data(g,d=new c(this)),"string"==typeof b&&d[b].call(this)})}},{key:"_clearMenus",value:function(b){if(!b||3!==b.which){var d=a(m.BACKDROP)[0];d&&d.parentNode.removeChild(d);for(var e=a.makeArray(a(m.DATA_TOGGLE)),f=0;f<e.length;f++){var g=c._getParentFromElement(e[f]),h={relatedTarget:e[f]};if(a(g).hasClass(l.OPEN)&&!(b&&"click"===b.type&&/input|textarea/i.test(b.target.tagName)&&a.contains(g,b.target))){var i=a.Event(k.HIDE,h);a(g).trigger(i),i.isDefaultPrevented()||(e[f].setAttribute("aria-expanded","false"),a(g).removeClass(l.OPEN).trigger(a.Event(k.HIDDEN,h)))}}}}},{key:"_getParentFromElement",value:function(b){var c=void 0,d=e.getSelectorFromElement(b);return d&&(c=a(d)[0]),c||b.parentNode}},{key:"_dataApiKeydownHandler",value:function(b){if(/(38|40|27|32)/.test(b.which)&&!/input|textarea/i.test(b.target.tagName)&&(b.preventDefault(),b.stopPropagation(),!this.disabled&&!a(this).hasClass(l.DISABLED))){var d=c._getParentFromElement(this),e=a(d).hasClass(l.OPEN);if(!e&&27!==b.which||e&&27===b.which){if(27===b.which){var f=a(d).find(m.DATA_TOGGLE)[0];a(f).trigger("focus")}return void a(this).trigger("click")}var g=a.makeArray(a(m.VISIBLE_ITEMS));if(g=g.filter(function(a){return a.offsetWidth||a.offsetHeight}),g.length){var h=g.indexOf(b.target);38===b.which&&h>0&&h--,40===b.which&&h<g.length-1&&h++,~h||(h=0),g[h].focus()}}}},{key:"VERSION",get:function(){return f}}]),c}();return a(document).on(k.KEYDOWN_DATA_API,m.DATA_TOGGLE,n._dataApiKeydownHandler).on(k.KEYDOWN_DATA_API,m.ROLE_MENU,n._dataApiKeydownHandler).on(k.KEYDOWN_DATA_API,m.ROLE_LISTBOX,n._dataApiKeydownHandler).on(k.CLICK_DATA_API,n._clearMenus).on(k.CLICK_DATA_API,m.DATA_TOGGLE,n.prototype.toggle).on(k.CLICK_DATA_API,m.FORM_CHILD,function(a){a.stopPropagation()}),a.fn[c]=n._jQueryInterface,a.fn[c].Constructor=n,a.fn[c].noConflict=function(){return a.fn[c]=j,n._jQueryInterface},n}(jQuery),function(a){var c="modal",f="4.0.0",g="bs.modal",h="."+g,i=".data-api",j=a.fn[c],k=300,l=150,m={backdrop:!0,keyboard:!0,focus:!0,show:!0},n={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},o={HIDE:"hide"+h,HIDDEN:"hidden"+h,SHOW:"show"+h,SHOWN:"shown"+h,FOCUSIN:"focusin"+h,RESIZE:"resize"+h,CLICK_DISMISS:"click.dismiss"+h,KEYDOWN_DISMISS:"keydown.dismiss"+h,MOUSEUP_DISMISS:"mouseup.dismiss"+h,MOUSEDOWN_DISMISS:"mousedown.dismiss"+h,CLICK_DATA_API:"click"+h+i},p={SCROLLBAR_MEASURER:"modal-scrollbar-measure",BACKDROP:"modal-backdrop",OPEN:"modal-open",FADE:"fade",IN:"in"},q={DIALOG:".modal-dialog",DATA_TOGGLE:'[data-toggle="modal"]',DATA_DISMISS:'[data-dismiss="modal"]',FIXED_CONTENT:".navbar-fixed-top, .navbar-fixed-bottom, .is-fixed"},r=function(){function i(c,d){b(this,i),this._config=this._getConfig(d),this._element=c,this._dialog=a(c).find(q.DIALOG)[0],this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._originalBodyPadding=0,this._scrollbarWidth=0}return d(i,[{key:"toggle",value:function(a){return this._isShown?this.hide():this.show(a)}},{key:"show",value:function(b){var c=this,d=a.Event(o.SHOW,{relatedTarget:b});a(this._element).trigger(d),this._isShown||d.isDefaultPrevented()||(this._isShown=!0,this._checkScrollbar(),this._setScrollbar(),a(document.body).addClass(p.OPEN),this._setEscapeEvent(),this._setResizeEvent(),a(this._element).on(o.CLICK_DISMISS,q.DATA_DISMISS,a.proxy(this.hide,this)),a(this._dialog).on(o.MOUSEDOWN_DISMISS,function(){a(c._element).one(o.MOUSEUP_DISMISS,function(b){a(b.target).is(c._element)&&(that._ignoreBackdropClick=!0)})}),this._showBackdrop(a.proxy(this._showElement,this,b)))}},{key:"hide",value:function(b){b&&b.preventDefault();var c=a.Event(o.HIDE);a(this._element).trigger(c),this._isShown&&!c.isDefaultPrevented()&&(this._isShown=!1,this._setEscapeEvent(),this._setResizeEvent(),a(document).off(o.FOCUSIN),a(this._element).removeClass(p.IN),a(this._element).off(o.CLICK_DISMISS),a(this._dialog).off(o.MOUSEDOWN_DISMISS),e.supportsTransitionEnd()&&a(this._element).hasClass(p.FADE)?a(this._element).one(e.TRANSITION_END,a.proxy(this._hideModal,this)).emulateTransitionEnd(k):this._hideModal())}},{key:"dispose",value:function(){a.removeData(this._element,g),a(window).off(h),a(document).off(h),a(this._element).off(h),a(this._backdrop).off(h),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._originalBodyPadding=null,this._scrollbarWidth=null}},{key:"_getConfig",value:function(b){return b=a.extend({},m,b),e.typeCheckConfig(c,b,n),b}},{key:"_showElement",value:function(b){var c=this,d=e.supportsTransitionEnd()&&a(this._element).hasClass(p.FADE);this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.scrollTop=0,d&&e.reflow(this._element),a(this._element).addClass(p.IN),this._config.focus&&this._enforceFocus();var f=a.Event(o.SHOWN,{relatedTarget:b}),g=function(){c._config.focus&&c._element.focus(),a(c._element).trigger(f)};d?a(this._dialog).one(e.TRANSITION_END,g).emulateTransitionEnd(k):g()}},{key:"_enforceFocus",value:function(){var b=this;a(document).off(o.FOCUSIN).on(o.FOCUSIN,function(c){b._element===c.target||a(b._element).has(c.target).length||b._element.focus()})}},{key:"_setEscapeEvent",value:function(){var b=this;this._isShown&&this._config.keyboard?a(this._element).on(o.KEYDOWN_DISMISS,function(a){27===a.which&&b.hide()}):this._isShown||a(this._element).off(o.KEYDOWN_DISMISS)}},{key:"_setResizeEvent",value:function(){this._isShown?a(window).on(o.RESIZE,a.proxy(this._handleUpdate,this)):a(window).off(o.RESIZE)}},{key:"_hideModal",value:function(){var b=this;this._element.style.display="none",this._showBackdrop(function(){a(document.body).removeClass(p.OPEN),b._resetAdjustments(),b._resetScrollbar(),a(b._element).trigger(o.HIDDEN)})}},{key:"_removeBackdrop",value:function(){this._backdrop&&(a(this._backdrop).remove(),this._backdrop=null)}},{key:"_showBackdrop",value:function(b){var c=this,d=a(this._element).hasClass(p.FADE)?p.FADE:"";if(this._isShown&&this._config.backdrop){var f=e.supportsTransitionEnd()&&d;if(this._backdrop=document.createElement("div"),this._backdrop.className=p.BACKDROP,d&&a(this._backdrop).addClass(d),a(this._backdrop).appendTo(document.body),a(this._element).on(o.CLICK_DISMISS,function(a){return c._ignoreBackdropClick?void(c._ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"===c._config.backdrop?c._element.focus():c.hide()))}),f&&e.reflow(this._backdrop),a(this._backdrop).addClass(p.IN),!b)return;if(!f)return void b();a(this._backdrop).one(e.TRANSITION_END,b).emulateTransitionEnd(l)}else if(!this._isShown&&this._backdrop){a(this._backdrop).removeClass(p.IN);var g=function(){c._removeBackdrop(),b&&b()};e.supportsTransitionEnd()&&a(this._element).hasClass(p.FADE)?a(this._backdrop).one(e.TRANSITION_END,g).emulateTransitionEnd(l):g()}else b&&b()}},{key:"_handleUpdate",value:function(){this._adjustDialog()}},{key:"_adjustDialog",value:function(){var a=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&a&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!a&&(this._element.style.paddingRight=this._scrollbarWidth+"px~")}},{key:"_resetAdjustments",value:function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}},{key:"_checkScrollbar",value:function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this._isBodyOverflowing=document.body.clientWidth<a,this._scrollbarWidth=this._getScrollbarWidth()}},{key:"_setScrollbar",value:function(){var b=parseInt(a(q.FIXED_CONTENT).css("padding-right")||0,10);this._originalBodyPadding=document.body.style.paddingRight||"",this._isBodyOverflowing&&(document.body.style.paddingRight=b+(this._scrollbarWidth+"px"))}},{key:"_resetScrollbar",value:function(){document.body.style.paddingRight=this._originalBodyPadding}},{key:"_getScrollbarWidth",value:function(){var a=document.createElement("div");a.className=p.SCROLLBAR_MEASURER,document.body.appendChild(a);var b=a.offsetWidth-a.clientWidth;return document.body.removeChild(a),b}}],[{key:"_jQueryInterface",value:function(b,c){return this.each(function(){var d=a(this).data(g),e=a.extend({},i.Default,a(this).data(),"object"==typeof b&&b);d||(d=new i(this,e),a(this).data(g,d)),"string"==typeof b?d[b](c):e.show&&d.show(c)})}},{key:"VERSION",get:function(){return f}},{key:"Default",get:function(){return m}}]),i}();return a(document).on(o.CLICK_DATA_API,q.DATA_TOGGLE,function(b){var c=this,d=void 0,f=e.getSelectorFromElement(this);f&&(d=a(f)[0]);var h=a(d).data(g)?"toggle":a.extend({},a(d).data(),a(this).data());"A"===this.tagName&&b.preventDefault();var i=a(d).one(o.SHOW,function(b){b.isDefaultPrevented()||i.one(o.HIDDEN,function(){a(c).is(":visible")&&c.focus()})});r._jQueryInterface.call(a(d),h,this)}),a.fn[c]=r._jQueryInterface,a.fn[c].Constructor=r,a.fn[c].noConflict=function(){return a.fn[c]=j,r._jQueryInterface},r}(jQuery),function(a){var c="scrollspy",f="4.0.0",g="bs.scrollspy",h="."+g,i=".data-api",j=a.fn[c],k={offset:10,method:"auto",target:""},l={offset:"number",method:"string",target:"(string|element)"},m={ACTIVATE:"activate"+h,SCROLL:"scroll"+h,LOAD_DATA_API:"load"+h+i},n={DROPDOWN_ITEM:"dropdown-item",DROPDOWN_MENU:"dropdown-menu",NAV_LINK:"nav-link",NAV:"nav",ACTIVE:"active"},o={DATA_SPY:'[data-spy="scroll"]',ACTIVE:".active",LIST_ITEM:".list-item",LI:"li",LI_DROPDOWN:"li.dropdown",NAV_LINKS:".nav-link",DROPDOWN:".dropdown",DROPDOWN_ITEMS:".dropdown-item",DROPDOWN_TOGGLE:".dropdown-toggle"},p={OFFSET:"offset",POSITION:"position"},q=function(){function i(c,d){b(this,i),this._element=c,this._scrollElement="BODY"===c.tagName?window:c,this._config=this._getConfig(d),this._selector=this._config.target+" "+o.NAV_LINKS+","+(this._config.target+" "+o.DROPDOWN_ITEMS),this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,a(this._scrollElement).on(m.SCROLL,a.proxy(this._process,this)),this.refresh(),this._process()}return d(i,[{key:"refresh",value:function(){var b=this,c=this._scrollElement!==this._scrollElement.window?p.POSITION:p.OFFSET,d="auto"===this._config.method?c:this._config.method,f=d===p.POSITION?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight();var g=a.makeArray(a(this._selector));g.map(function(b){var c=void 0,g=e.getSelectorFromElement(b);return g&&(c=a(g)[0]),c&&(c.offsetWidth||c.offsetHeight)?[a(c)[d]().top+f,g]:void 0}).filter(function(a){return a}).sort(function(a,b){return a[0]-b[0]}).forEach(function(a){b._offsets.push(a[0]),b._targets.push(a[1])})}},{key:"dispose",value:function(){a.removeData(this._element,g),a(this._scrollElement).off(h),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null}},{key:"_getConfig",value:function(b){if(b=a.extend({},k,b),"string"!=typeof b.target){var d=a(b.target).attr("id");d||(d=e.getUID(c),a(b.target).attr("id",d)),b.target="#"+d}return e.typeCheckConfig(c,b,l),b}},{key:"_getScrollTop",value:function(){return this._scrollElement===window?this._scrollElement.scrollY:this._scrollElement.scrollTop}},{key:"_getScrollHeight",value:function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}},{key:"_process",value:function(){var a=this._getScrollTop()+this._config.offset,b=this._getScrollHeight(),c=this._config.offset+b-this._scrollElement.offsetHeight;if(this._scrollHeight!==b&&this.refresh(),a>=c){var d=this._targets[this._targets.length-1];this._activeTarget!==d&&this._activate(d)}if(this._activeTarget&&a<this._offsets[0])return this._activeTarget=null,void this._clear();for(var e=this._offsets.length;e--;){var f=this._activeTarget!==this._targets[e]&&a>=this._offsets[e]&&(void 0===this._offsets[e+1]||a<this._offsets[e+1]);f&&this._activate(this._targets[e])}}},{key:"_activate",value:function(b){this._activeTarget=b,this._clear();var c=this._selector.split(",");c=c.map(function(a){return a+'[data-target="'+b+'"],'+(a+'[href="'+b+'"]')});var d=a(c.join(","));d.hasClass(n.DROPDOWN_ITEM)?(d.closest(o.DROPDOWN).find(o.DROPDOWN_TOGGLE).addClass(n.ACTIVE),d.addClass(n.ACTIVE)):d.parents(o.LI).find(o.NAV_LINKS).addClass(n.ACTIVE),a(this._scrollElement).trigger(m.ACTIVATE,{relatedTarget:b})}},{key:"_clear",value:function(){a(this._selector).filter(o.ACTIVE).removeClass(n.ACTIVE)}}],[{key:"_jQueryInterface",value:function(b){return this.each(function(){var c=a(this).data(g),d="object"==typeof b&&b||null;c||(c=new i(this,d),a(this).data(g,c)),"string"==typeof b&&c[b]()})}},{key:"VERSION",get:function(){return f}},{key:"Default",get:function(){return k}}]),i}();return a(window).on(m.LOAD_DATA_API,function(){for(var b=a.makeArray(a(o.DATA_SPY)),c=b.length;c--;){var d=a(b[c]);q._jQueryInterface.call(d,d.data())}}),a.fn[c]=q._jQueryInterface,a.fn[c].Constructor=q,a.fn[c].noConflict=function(){return a.fn[c]=j,q._jQueryInterface},q}(jQuery),function(a){var c="tab",f="4.0.0",g="bs.tab",h="."+g,i=".data-api",j=a.fn[c],k=150,l={HIDE:"hide"+h,HIDDEN:"hidden"+h,SHOW:"show"+h,SHOWN:"shown"+h,CLICK_DATA_API:"click"+h+i},m={DROPDOWN_MENU:"dropdown-menu",ACTIVE:"active",FADE:"fade",IN:"in"},n={A:"a",LI:"li",DROPDOWN:".dropdown",UL:"ul:not(.dropdown-menu)",FADE_CHILD:"> .nav-item .fade, > .fade",ACTIVE:".active",ACTIVE_CHILD:"> .nav-item > .active, > .active",DATA_TOGGLE:'[data-toggle="tab"], [data-toggle="pill"]',DROPDOWN_TOGGLE:".dropdown-toggle",DROPDOWN_ACTIVE_CHILD:"> .dropdown-menu .active"},o=function(){function c(a){b(this,c),this._element=a}return d(c,[{key:"show",value:function(){var b=this;if(!this._element.parentNode||this._element.parentNode.nodeType!==Node.ELEMENT_NODE||!a(this._element).hasClass(m.ACTIVE)){var c=void 0,d=void 0,f=a(this._element).closest(n.UL)[0],g=e.getSelectorFromElement(this._element);f&&(d=a.makeArray(a(f).find(n.ACTIVE)),d=d[d.length-1]);var h=a.Event(l.HIDE,{
7
+ relatedTarget:this._element}),i=a.Event(l.SHOW,{relatedTarget:d});if(d&&a(d).trigger(h),a(this._element).trigger(i),!i.isDefaultPrevented()&&!h.isDefaultPrevented()){g&&(c=a(g)[0]),this._activate(this._element,f);var j=function(){var c=a.Event(l.HIDDEN,{relatedTarget:b._element}),e=a.Event(l.SHOWN,{relatedTarget:d});a(d).trigger(c),a(b._element).trigger(e)};c?this._activate(c,c.parentNode,j):j()}}}},{key:"dispose",value:function(){a.removeClass(this._element,g),this._element=null}},{key:"_activate",value:function(b,c,d){var f=a(c).find(n.ACTIVE_CHILD)[0],g=d&&e.supportsTransitionEnd()&&(f&&a(f).hasClass(m.FADE)||Boolean(a(c).find(n.FADE_CHILD)[0])),h=a.proxy(this._transitionComplete,this,b,f,g,d);f&&g?a(f).one(e.TRANSITION_END,h).emulateTransitionEnd(k):h(),f&&a(f).removeClass(m.IN)}},{key:"_transitionComplete",value:function(b,c,d,f){if(c){a(c).removeClass(m.ACTIVE);var g=a(c).find(n.DROPDOWN_ACTIVE_CHILD)[0];g&&a(g).removeClass(m.ACTIVE),c.setAttribute("aria-expanded",!1)}if(a(b).addClass(m.ACTIVE),b.setAttribute("aria-expanded",!0),d?(e.reflow(b),a(b).addClass(m.IN)):a(b).removeClass(m.FADE),b.parentNode&&a(b.parentNode).hasClass(m.DROPDOWN_MENU)){var h=a(b).closest(n.DROPDOWN)[0];h&&a(h).find(n.DROPDOWN_TOGGLE).addClass(m.ACTIVE),b.setAttribute("aria-expanded",!0)}f&&f()}}],[{key:"_jQueryInterface",value:function(b){return this.each(function(){var d=a(this),e=d.data(g);e||(e=e=new c(this),d.data(g,e)),"string"==typeof b&&e[b]()})}},{key:"VERSION",get:function(){return f}}]),c}();return a(document).on(l.CLICK_DATA_API,n.DATA_TOGGLE,function(b){b.preventDefault(),o._jQueryInterface.call(a(this),"show")}),a.fn[c]=o._jQueryInterface,a.fn[c].Constructor=o,a.fn[c].noConflict=function(){return a.fn[c]=j,o._jQueryInterface},o}(jQuery),function(a){var c="tooltip",f="4.0.0",g="bs.tooltip",h="."+g,i=a.fn[c],j=150,k="bs-tether",l={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:"0 0",constraints:[]},m={animation:"boolean",template:"string",title:"(string|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"string",constraints:"array"},n={TOP:"bottom center",RIGHT:"middle left",BOTTOM:"top center",LEFT:"middle right"},o={IN:"in",OUT:"out"},p={HIDE:"hide"+h,HIDDEN:"hidden"+h,SHOW:"show"+h,SHOWN:"shown"+h,INSERTED:"inserted"+h,CLICK:"click"+h,FOCUSIN:"focusin"+h,FOCUSOUT:"focusout"+h,MOUSEENTER:"mouseenter"+h,MOUSELEAVE:"mouseleave"+h},q={FADE:"fade",IN:"in"},r={TOOLTIP:".tooltip",TOOLTIP_INNER:".tooltip-inner"},s={element:!1,enabled:!1},t={HOVER:"hover",FOCUS:"focus",CLICK:"click",MANUAL:"manual"},u=function(){function i(a,c){b(this,i),this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._tether=null,this.element=a,this.config=this._getConfig(c),this.tip=null,this._setListeners()}return d(i,[{key:"enable",value:function(){this._isEnabled=!0}},{key:"disable",value:function(){this._isEnabled=!1}},{key:"toggleEnabled",value:function(){this._isEnabled=!this._isEnabled}},{key:"toggle",value:function(b){if(b){var c=this.constructor.DATA_KEY,d=a(b.currentTarget).data(c);d||(d=new this.constructor(b.currentTarget,this._getDelegateConfig()),a(b.currentTarget).data(c,d)),d._activeTrigger.click=!d._activeTrigger.click,d._isWithActiveTrigger()?d._enter(null,d):d._leave(null,d)}else{if(a(this.getTipElement()).hasClass(q.IN))return void this._leave(null,this);this._enter(null,this)}}},{key:"dispose",value:function(){clearTimeout(this._timeout),this.cleanupTether(),a.removeData(this.element,this.constructor.DATA_KEY),a(this.element).off(this.constructor.EVENT_KEY),this.tip&&a(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._tether=null,this.element=null,this.config=null,this.tip=null}},{key:"show",value:function(){var b=this,c=a.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){a(this.element).trigger(c);var d=a.contains(this.element.ownerDocument.documentElement,this.element);if(c.isDefaultPrevented()||!d)return;var f=this.getTipElement(),g=e.getUID(this.constructor.NAME);f.setAttribute("id",g),this.element.setAttribute("aria-describedby",g),this.setContent(),this.config.animation&&a(f).addClass(q.FADE);var h="function"==typeof this.config.placement?this.config.placement.call(this,f,this.element):this.config.placement,j=this._getAttachment(h);a(f).data(this.constructor.DATA_KEY,this).appendTo(document.body),a(this.element).trigger(this.constructor.Event.INSERTED),this._tether=new Tether({attachment:j,element:f,target:this.element,classes:s,classPrefix:k,offset:this.config.offset,constraints:this.config.constraints}),e.reflow(f),this._tether.position(),a(f).addClass(q.IN);var l=function(){var c=b._hoverState;b._hoverState=null,a(b.element).trigger(b.constructor.Event.SHOWN),c===o.OUT&&b._leave(null,b)};if(e.supportsTransitionEnd()&&a(this.tip).hasClass(q.FADE))return void a(this.tip).one(e.TRANSITION_END,l).emulateTransitionEnd(i._TRANSITION_DURATION);l()}}},{key:"hide",value:function(b){var c=this,d=this.getTipElement(),f=a.Event(this.constructor.Event.HIDE),g=function(){c._hoverState!==o.IN&&d.parentNode&&d.parentNode.removeChild(d),c.element.removeAttribute("aria-describedby"),a(c.element).trigger(c.constructor.Event.HIDDEN),c.cleanupTether(),b&&b()};a(this.element).trigger(f),f.isDefaultPrevented()||(a(d).removeClass(q.IN),e.supportsTransitionEnd()&&a(this.tip).hasClass(q.FADE)?a(d).one(e.TRANSITION_END,g).emulateTransitionEnd(j):g(),this._hoverState="")}},{key:"isWithContent",value:function(){return Boolean(this.getTitle())}},{key:"getTipElement",value:function(){return this.tip=this.tip||a(this.config.template)[0]}},{key:"setContent",value:function(){var b=this.getTipElement(),c=this.getTitle(),d=this.config.html?"innerHTML":"innerText";a(b).find(r.TOOLTIP_INNER)[0][d]=c,a(b).removeClass(q.FADE).removeClass(q.IN),this.cleanupTether()}},{key:"getTitle",value:function(){var a=this.element.getAttribute("data-original-title");return a||(a="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),a}},{key:"cleanupTether",value:function(){this._tether&&(this._tether.destroy(),a(this.element).removeClass(this._removeTetherClasses),a(this.tip).removeClass(this._removeTetherClasses))}},{key:"_getAttachment",value:function(a){return n[a.toUpperCase()]}},{key:"_setListeners",value:function(){var b=this,c=this.config.trigger.split(" ");c.forEach(function(c){if("click"===c)a(b.element).on(b.constructor.Event.CLICK,b.config.selector,a.proxy(b.toggle,b));else if(c!==t.MANUAL){var d=c===t.HOVER?b.constructor.Event.MOUSEENTER:b.constructor.Event.FOCUSIN,e=c===t.HOVER?b.constructor.Event.MOUSELEAVE:b.constructor.Event.FOCUSOUT;a(b.element).on(d,b.config.selector,a.proxy(b._enter,b)).on(e,b.config.selector,a.proxy(b._leave,b))}}),this.config.selector?this.config=a.extend({},this.config,{trigger:"manual",selector:""}):this._fixTitle()}},{key:"_removeTetherClasses",value:function(a,b){return((b.baseVal||b).match(new RegExp("(^|\\s)"+k+"-\\S+","g"))||[]).join(" ")}},{key:"_fixTitle",value:function(){var a=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==a)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))}},{key:"_enter",value:function(b,c){var d=this.constructor.DATA_KEY;return c=c||a(b.currentTarget).data(d),c||(c=new this.constructor(b.currentTarget,this._getDelegateConfig()),a(b.currentTarget).data(d,c)),b&&(c._activeTrigger["focusin"===b.type?t.FOCUS:t.HOVER]=!0),a(c.getTipElement()).hasClass(q.IN)||c._hoverState===o.IN?void(c._hoverState=o.IN):(clearTimeout(c._timeout),c._hoverState=o.IN,c.config.delay&&c.config.delay.show?void(c._timeout=setTimeout(function(){c._hoverState===o.IN&&c.show()},c.config.delay.show)):void c.show())}},{key:"_leave",value:function(b,c){var d=this.constructor.DATA_KEY;return c=c||a(b.currentTarget).data(d),c||(c=new this.constructor(b.currentTarget,this._getDelegateConfig()),a(b.currentTarget).data(d,c)),b&&(c._activeTrigger["focusout"===b.type?t.FOCUS:t.HOVER]=!1),c._isWithActiveTrigger()?void 0:(clearTimeout(c._timeout),c._hoverState=o.OUT,c.config.delay&&c.config.delay.hide?void(c._timeout=setTimeout(function(){c._hoverState===o.OUT&&c.hide()},c.config.delay.hide)):void c.hide())}},{key:"_isWithActiveTrigger",value:function(){for(var a in this._activeTrigger)if(this._activeTrigger[a])return!0;return!1}},{key:"_getConfig",value:function(b){return b=a.extend({},this.constructor.Default,a(this.element).data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),e.typeCheckConfig(c,b,this.constructor.DefaultType),b}},{key:"_getDelegateConfig",value:function(){var a={};if(this.config)for(var b in this.config)this.constructor.Default[b]!==this.config[b]&&(a[b]=this.config[b]);return a}}],[{key:"_jQueryInterface",value:function(b){return this.each(function(){var c=a(this).data(g),d="object"==typeof b?b:null;(c||!/destroy|hide/.test(b))&&(c||(c=new i(this,d),a(this).data(g,c)),"string"==typeof b&&c[b]())})}},{key:"VERSION",get:function(){return f}},{key:"Default",get:function(){return l}},{key:"NAME",get:function(){return c}},{key:"DATA_KEY",get:function(){return g}},{key:"Event",get:function(){return p}},{key:"EVENT_KEY",get:function(){return h}},{key:"DefaultType",get:function(){return m}}]),i}();return a.fn[c]=u._jQueryInterface,a.fn[c].Constructor=u,a.fn[c].noConflict=function(){return a.fn[c]=i,u._jQueryInterface},u}(jQuery));!function(e){var g="popover",h="4.0.0",i="bs.popover",j="."+i,k=e.fn[g],l=e.extend({},f.Default,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),m=e.extend({},f.DefaultType,{content:"(string|function)"}),n={FADE:"fade",IN:"in"},o={TITLE:".popover-title",CONTENT:".popover-content",ARROW:".popover-arrow"},p={HIDE:"hide"+j,HIDDEN:"hidden"+j,SHOW:"show"+j,SHOWN:"shown"+j,INSERTED:"inserted"+j,CLICK:"click"+j,FOCUSIN:"focusin"+j,FOCUSOUT:"focusout"+j,MOUSEENTER:"mouseenter"+j,MOUSELEAVE:"mouseleave"+j},q=function(f){function k(){b(this,k),c(Object.getPrototypeOf(k.prototype),"constructor",this).apply(this,arguments)}return a(k,f),d(k,[{key:"isWithContent",value:function(){return this.getTitle()||this._getContent()}},{key:"getTipElement",value:function(){return this.tip=this.tip||e(this.config.template)[0]}},{key:"setContent",value:function(){var a=this.getTipElement(),b=this.getTitle(),c=this._getContent(),d=e(a).find(o.TITLE)[0];d&&(d[this.config.html?"innerHTML":"innerText"]=b),e(a).find(o.CONTENT).children().detach().end()[this.config.html?"string"==typeof c?"html":"append":"text"](c),e(a).removeClass(n.FADE).removeClass(n.IN),this.cleanupTether()}},{key:"_getContent",value:function(){return this.element.getAttribute("data-content")||("function"==typeof this.config.content?this.config.content.call(this.element):this.config.content)}}],[{key:"_jQueryInterface",value:function(a){return this.each(function(){var b=e(this).data(i),c="object"==typeof a?a:null;(b||!/destroy|hide/.test(a))&&(b||(b=new k(this,c),e(this).data(i,b)),"string"==typeof a&&b[a]())})}},{key:"VERSION",get:function(){return h}},{key:"Default",get:function(){return l}},{key:"NAME",get:function(){return g}},{key:"DATA_KEY",get:function(){return i}},{key:"Event",get:function(){return p}},{key:"EVENT_KEY",get:function(){return j}},{key:"DefaultType",get:function(){return m}}]),k}(f);return e.fn[g]=q._jQueryInterface,e.fn[g].Constructor=q,e.fn[g].noConflict=function(){return e.fn[g]=k,q._jQueryInterface},q}(jQuery)}}(jQuery);
@@ -0,0 +1,1180 @@
1
+ /**
2
+ * Bootstrap Multiselect v0.9.8 (https://github.com/davidstutz/bootstrap-multiselect)
3
+ *
4
+ * Copyright 2012 - 2014 David Stutz
5
+ *
6
+ * Dual licensed under the BSD-3-Clause and the Apache License, Version 2.0.
7
+ */
8
+ !function($) {
9
+
10
+ "use strict";// jshint ;_;
11
+
12
+ if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) {
13
+ ko.bindingHandlers.multiselect = {
14
+
15
+ init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
16
+
17
+ var listOfSelectedItems = allBindingsAccessor().selectedOptions;
18
+ var config = ko.utils.unwrapObservable(valueAccessor());
19
+
20
+ $(element).multiselect(config);
21
+
22
+ if (isObservableArray(listOfSelectedItems)) {
23
+
24
+ // Set the initial selection state on the multiselect list.
25
+ $(element).multiselect('select', ko.utils.unwrapObservable(listOfSelectedItems));
26
+
27
+ // Subscribe to the selectedOptions: ko.observableArray
28
+ listOfSelectedItems.subscribe(function (changes) {
29
+ var addedArray = [], deletedArray = [];
30
+ forEach(changes, function (change) {
31
+ switch (change.status) {
32
+ case 'added':
33
+ addedArray.push(change.value);
34
+ break;
35
+ case 'deleted':
36
+ deletedArray.push(change.value);
37
+ break;
38
+ }
39
+ });
40
+
41
+ if (addedArray.length > 0) {
42
+ $(element).multiselect('select', addedArray);
43
+ }
44
+
45
+ if (deletedArray.length > 0) {
46
+ $(element).multiselect('deselect', deletedArray);
47
+ }
48
+ }, null, "arrayChange");
49
+ }
50
+ },
51
+
52
+ update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
53
+
54
+ var listOfItems = allBindingsAccessor().options,
55
+ ms = $(element).data('multiselect'),
56
+ config = ko.utils.unwrapObservable(valueAccessor());
57
+
58
+ if (isObservableArray(listOfItems)) {
59
+ // Subscribe to the options: ko.observableArray incase it changes later
60
+ listOfItems.subscribe(function (theArray) {
61
+ $(element).multiselect('rebuild');
62
+ });
63
+ }
64
+
65
+ if (!ms) {
66
+ $(element).multiselect(config);
67
+ }
68
+ else {
69
+ ms.updateOriginalOptions();
70
+ }
71
+ }
72
+ };
73
+ }
74
+
75
+ function isObservableArray(obj) {
76
+ return ko.isObservable(obj) && !(obj.destroyAll === undefined);
77
+ }
78
+
79
+ function forEach(array, callback) {
80
+ for (var index = 0; index < array.length; ++index) {
81
+ callback(array[index]);
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Constructor to create a new multiselect using the given select.
87
+ *
88
+ * @param {jQuery} select
89
+ * @param {Object} options
90
+ * @returns {Multiselect}
91
+ */
92
+ function Multiselect(select, options) {
93
+
94
+ this.options = this.mergeOptions(options);
95
+ this.$select = $(select);
96
+
97
+ // Initialization.
98
+ // We have to clone to create a new reference.
99
+ this.originalOptions = this.$select.clone()[0].options;
100
+ this.query = '';
101
+ this.searchTimeout = null;
102
+
103
+ this.options.multiple = this.$select.attr('multiple') === "multiple";
104
+ this.options.onChange = $.proxy(this.options.onChange, this);
105
+ this.options.onDropdownShow = $.proxy(this.options.onDropdownShow, this);
106
+ this.options.onDropdownHide = $.proxy(this.options.onDropdownHide, this);
107
+ this.options.onDropdownShown = $.proxy(this.options.onDropdownShown, this);
108
+ this.options.onDropdownHidden = $.proxy(this.options.onDropdownHidden, this);
109
+
110
+ // Build select all if enabled.
111
+ this.buildContainer();
112
+ this.buildButton();
113
+ this.buildDropdown();
114
+ this.buildSelectAll();
115
+ this.buildDropdownOptions();
116
+ this.buildFilter();
117
+
118
+ this.updateButtonText();
119
+ this.updateSelectAll();
120
+
121
+ if (this.options.disableIfEmpty) {
122
+ this.disableIfEmpty();
123
+ }
124
+
125
+ this.$select.hide().after(this.$container);
126
+ };
127
+
128
+ Multiselect.prototype = {
129
+
130
+ defaults: {
131
+ /**
132
+ * Default text function will either print 'None selected' in case no
133
+ * option is selected or a list of the selected options up to a length
134
+ * of 3 selected options.
135
+ *
136
+ * @param {jQuery} options
137
+ * @param {jQuery} select
138
+ * @returns {String}
139
+ */
140
+ buttonText: function(options, select) {
141
+ if (options.length === 0) {
142
+ return this.nonSelectedText + ' <b class="caret"></b>';
143
+ }
144
+ else {
145
+ if (options.length > this.numberDisplayed) {
146
+ return options.length + ' ' + this.nSelectedText + ' <b class="caret"></b>';
147
+ }
148
+ else {
149
+ var selected = '';
150
+ options.each(function() {
151
+ var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).html();
152
+
153
+ selected += label + ', ';
154
+ });
155
+ return selected.substr(0, selected.length - 2) + ' <b class="caret"></b>';
156
+ }
157
+ }
158
+ },
159
+ /**
160
+ * Updates the title of the button similar to the buttonText function.
161
+ *
162
+ * @param {jQuery} options
163
+ * @param {jQuery} select
164
+ * @returns {@exp;selected@call;substr}
165
+ */
166
+ buttonTitle: function(options, select) {
167
+ if (options.length === 0) {
168
+ return this.nonSelectedText;
169
+ }
170
+ else {
171
+ var selected = '';
172
+ options.each(function () {
173
+ selected += $(this).text() + ', ';
174
+ });
175
+ return selected.substr(0, selected.length - 2);
176
+ }
177
+ },
178
+ /**
179
+ * Create a label.
180
+ *
181
+ * @param {jQuery} element
182
+ * @returns {String}
183
+ */
184
+ label: function(element){
185
+ return $(element).attr('label') || $(element).html();
186
+ },
187
+ /**
188
+ * Triggered on change of the multiselect.
189
+ *
190
+ * Not triggered when selecting/deselecting options manually.
191
+ *
192
+ * @param {jQuery} option
193
+ * @param {Boolean} checked
194
+ */
195
+ onChange : function(option, checked) {
196
+
197
+ },
198
+ /**
199
+ * Triggered when the dropdown is shown.
200
+ *
201
+ * @param {jQuery} event
202
+ */
203
+ onDropdownShow: function(event) {
204
+
205
+ },
206
+ /**
207
+ * Triggered when the dropdown is hidden.
208
+ *
209
+ * @param {jQuery} event
210
+ */
211
+ onDropdownHide: function(event) {
212
+
213
+ },
214
+ /**
215
+ * Triggered after the dropdown is shown.
216
+ *
217
+ * @param {jQuery} event
218
+ */
219
+ onDropdownShown: function(event) {
220
+
221
+ },
222
+ /**
223
+ * Triggered after the dropdown is hidden.
224
+ *
225
+ * @param {jQuery} event
226
+ */
227
+ onDropdownHidden: function(event) {
228
+
229
+ },
230
+ buttonClass: 'btn btn-default',
231
+ dropRight: false,
232
+ selectedClass: 'active',
233
+ buttonWidth: 'auto',
234
+ buttonContainer: '<div class="btn-group" />',
235
+ // Maximum height of the dropdown menu.
236
+ // If maximum height is exceeded a scrollbar will be displayed.
237
+ maxHeight: false,
238
+ checkboxName: false,
239
+ includeSelectAllOption: false,
240
+ includeSelectAllIfMoreThan: 0,
241
+ selectAllText: ' Select all',
242
+ selectAllValue: 'multiselect-all',
243
+ enableFiltering: false,
244
+ enableCaseInsensitiveFiltering: false,
245
+ filterPlaceholder: 'Search',
246
+ // possible options: 'text', 'value', 'both'
247
+ filterBehavior: 'text',
248
+ preventInputChangeEvent: false,
249
+ nonSelectedText: 'None selected',
250
+ nSelectedText: 'selected',
251
+ numberDisplayed: 3,
252
+ disableIfEmpty: false,
253
+ templates: {
254
+ button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"></button>',
255
+ ul: '<ul class="multiselect-container dropdown-menu"></ul>',
256
+ filter: '<li class="multiselect-item filter"><div class=""><input class="form-control multiselect-search" type="text"></div></li>',
257
+ li: '<li><a href="javascript:void(0);"><label></label></a></li>',
258
+ divider: '<li class="multiselect-item divider"></li>',
259
+ liGroup: '<li class="multiselect-item group"><label class="multiselect-group"></label></li>'
260
+ }
261
+ },
262
+
263
+ constructor: Multiselect,
264
+
265
+ /**
266
+ * Builds the container of the multiselect.
267
+ */
268
+ buildContainer: function() {
269
+ this.$container = $(this.options.buttonContainer);
270
+ this.$container.on('show.bs.dropdown', this.options.onDropdownShow);
271
+ this.$container.on('hide.bs.dropdown', this.options.onDropdownHide);
272
+ this.$container.on('shown.bs.dropdown', this.options.onDropdownShown);
273
+ this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden);
274
+ },
275
+
276
+ /**
277
+ * Builds the button of the multiselect.
278
+ */
279
+ buildButton: function() {
280
+ this.$button = $(this.options.templates.button).addClass(this.options.buttonClass);
281
+
282
+ // Adopt active state.
283
+ if (this.$select.prop('disabled')) {
284
+ this.disable();
285
+ }
286
+ else {
287
+ this.enable();
288
+ }
289
+
290
+ // Manually add button width if set.
291
+ if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') {
292
+ this.$button.css({
293
+ 'width' : this.options.buttonWidth
294
+ });
295
+ this.$container.css({
296
+ 'width': this.options.buttonWidth
297
+ });
298
+ }
299
+
300
+ // Keep the tab index from the select.
301
+ var tabindex = this.$select.attr('tabindex');
302
+ if (tabindex) {
303
+ this.$button.attr('tabindex', tabindex);
304
+ }
305
+
306
+ this.$container.prepend(this.$button);
307
+ },
308
+
309
+ /**
310
+ * Builds the ul representing the dropdown menu.
311
+ */
312
+ buildDropdown: function() {
313
+
314
+ // Build ul.
315
+ this.$ul = $(this.options.templates.ul);
316
+
317
+ if (this.options.dropRight) {
318
+ this.$ul.addClass('pull-right');
319
+ }
320
+
321
+ // Set max height of dropdown menu to activate auto scrollbar.
322
+ if (this.options.maxHeight) {
323
+ // TODO: Add a class for this option to move the css declarations.
324
+ this.$ul.css({
325
+ 'max-height': this.options.maxHeight + 'px',
326
+ 'overflow-y': 'auto',
327
+ 'overflow-x': 'hidden'
328
+ });
329
+ }
330
+
331
+ this.$container.append(this.$ul);
332
+ },
333
+
334
+ /**
335
+ * Build the dropdown options and binds all nessecary events.
336
+ *
337
+ * Uses createDivider and createOptionValue to create the necessary options.
338
+ */
339
+ buildDropdownOptions: function() {
340
+
341
+ this.$select.children().each($.proxy(function(index, element) {
342
+
343
+ // Support optgroups and options without a group simultaneously.
344
+ var tag = $(element).prop('tagName')
345
+ .toLowerCase();
346
+
347
+ if ($(element).prop('value') === this.options.selectAllValue) {
348
+ return;
349
+ }
350
+
351
+ if (tag === 'optgroup') {
352
+ this.createOptgroup(element);
353
+ }
354
+ else if (tag === 'option') {
355
+
356
+ if ($(element).data('role') === 'divider') {
357
+ this.createDivider();
358
+ }
359
+ else {
360
+ this.createOptionValue(element);
361
+ }
362
+
363
+ }
364
+
365
+ // Other illegal tags will be ignored.
366
+ }, this));
367
+
368
+ // Bind the change event on the dropdown elements.
369
+ $('li input', this.$ul).on('change', $.proxy(function(event) {
370
+ var $target = $(event.target);
371
+
372
+ var checked = $target.prop('checked') || false;
373
+ var isSelectAllOption = $target.val() === this.options.selectAllValue;
374
+
375
+ // Apply or unapply the configured selected class.
376
+ if (this.options.selectedClass) {
377
+ if (checked) {
378
+ $target.parents('li')
379
+ .addClass(this.options.selectedClass);
380
+ }
381
+ else {
382
+ $target.parents('li')
383
+ .removeClass(this.options.selectedClass);
384
+ }
385
+ }
386
+
387
+ // Get the corresponding option.
388
+ var value = $target.val();
389
+ var $option = this.getOptionByValue(value);
390
+
391
+ var $optionsNotThis = $('option', this.$select).not($option);
392
+ var $checkboxesNotThis = $('input', this.$container).not($target);
393
+
394
+ if (isSelectAllOption) {
395
+ if (checked) {
396
+ this.selectAll();
397
+ }
398
+ else {
399
+ this.deselectAll();
400
+ }
401
+ }
402
+
403
+ if(!isSelectAllOption){
404
+ if (checked) {
405
+ $option.prop('selected', true);
406
+
407
+ if (this.options.multiple) {
408
+ // Simply select additional option.
409
+ $option.prop('selected', true);
410
+ }
411
+ else {
412
+ // Unselect all other options and corresponding checkboxes.
413
+ if (this.options.selectedClass) {
414
+ $($checkboxesNotThis).parents('li').removeClass(this.options.selectedClass);
415
+ }
416
+
417
+ $($checkboxesNotThis).prop('checked', false);
418
+ $optionsNotThis.prop('selected', false);
419
+
420
+ // It's a single selection, so close.
421
+ this.$button.click();
422
+ }
423
+
424
+ if (this.options.selectedClass === "active") {
425
+ $optionsNotThis.parents("a").css("outline", "");
426
+ }
427
+ }
428
+ else {
429
+ // Unselect option.
430
+ $option.prop('selected', false);
431
+ }
432
+ }
433
+
434
+ this.$select.change();
435
+
436
+ this.updateButtonText();
437
+ this.updateSelectAll();
438
+
439
+ this.options.onChange($option, checked);
440
+
441
+ if(this.options.preventInputChangeEvent) {
442
+ return false;
443
+ }
444
+ }, this));
445
+
446
+ $('li a', this.$ul).on('touchstart click', function(event) {
447
+ event.stopPropagation();
448
+
449
+ var $target = $(event.target);
450
+
451
+ if (event.shiftKey) {
452
+ var checked = $target.prop('checked') || false;
453
+
454
+ if (checked) {
455
+ var prev = $target.parents('li:last')
456
+ .siblings('li[class="active"]:first');
457
+
458
+ var currentIdx = $target.parents('li')
459
+ .index();
460
+ var prevIdx = prev.index();
461
+
462
+ if (currentIdx > prevIdx) {
463
+ $target.parents("li:last").prevUntil(prev).each(
464
+ function() {
465
+ $(this).find("input:first").prop("checked", true)
466
+ .trigger("change");
467
+ }
468
+ );
469
+ }
470
+ else {
471
+ $target.parents("li:last").nextUntil(prev).each(
472
+ function() {
473
+ $(this).find("input:first").prop("checked", true)
474
+ .trigger("change");
475
+ }
476
+ );
477
+ }
478
+ }
479
+ }
480
+
481
+ $target.blur();
482
+ });
483
+
484
+ // Keyboard support.
485
+ this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) {
486
+ if ($('input[type="text"]', this.$container).is(':focus')) {
487
+ return;
488
+ }
489
+ if ((event.keyCode === 9 || event.keyCode === 27)
490
+ && this.$container.hasClass('open')) {
491
+
492
+ // Close on tab or escape.
493
+ this.$button.click();
494
+ }
495
+ else {
496
+ var $items = $(this.$container).find("li:not(.divider):not(.disabled) a").filter(":visible");
497
+
498
+ if (!$items.length) {
499
+ return;
500
+ }
501
+
502
+ var index = $items.index($items.filter(':focus'));
503
+
504
+ // Navigation up.
505
+ if (event.keyCode === 38 && index > 0) {
506
+ index--;
507
+ }
508
+ // Navigate down.
509
+ else if (event.keyCode === 40 && index < $items.length - 1) {
510
+ index++;
511
+ }
512
+ else if (!~index) {
513
+ index = 0;
514
+ }
515
+
516
+ var $current = $items.eq(index);
517
+ $current.focus();
518
+
519
+ if (event.keyCode === 32 || event.keyCode === 13) {
520
+ var $checkbox = $current.find('input');
521
+
522
+ $checkbox.prop("checked", !$checkbox.prop("checked"));
523
+ $checkbox.change();
524
+ }
525
+
526
+ event.stopPropagation();
527
+ event.preventDefault();
528
+ }
529
+ }, this));
530
+ },
531
+
532
+ /**
533
+ * Create an option using the given select option.
534
+ *
535
+ * @param {jQuery} element
536
+ */
537
+ createOptionValue: function(element) {
538
+ if ($(element).is(':selected')) {
539
+ $(element).prop('selected', true);
540
+ }
541
+
542
+ // Support the label attribute on options.
543
+ var label = this.options.label(element);
544
+ var value = $(element).val();
545
+ var inputType = this.options.multiple ? "checkbox" : "radio";
546
+
547
+ var $li = $(this.options.templates.li);
548
+ $('label', $li).addClass(inputType);
549
+
550
+ if (this.options.checkboxName) {
551
+ $('label', $li).append('<input type="' + inputType + '" name="' + this.options.checkboxName + '" />');
552
+ }
553
+ else {
554
+ $('label', $li).append('<input type="' + inputType + '" />');
555
+ }
556
+
557
+ var selected = $(element).prop('selected') || false;
558
+ var $checkbox = $('input', $li);
559
+ $checkbox.val(value);
560
+
561
+ if (value === this.options.selectAllValue) {
562
+ $li.addClass("multiselect-item multiselect-all");
563
+ $checkbox.parent().parent()
564
+ .addClass('multiselect-all');
565
+ }
566
+
567
+ $('label', $li).append(" " + label);
568
+
569
+ this.$ul.append($li);
570
+
571
+ if ($(element).is(':disabled')) {
572
+ $checkbox.attr('disabled', 'disabled')
573
+ .prop('disabled', true)
574
+ .parents('a')
575
+ .attr("tabindex", "-1")
576
+ .parents('li')
577
+ .addClass('disabled');
578
+ }
579
+
580
+ $checkbox.prop('checked', selected);
581
+
582
+ if (selected && this.options.selectedClass) {
583
+ $checkbox.parents('li')
584
+ .addClass(this.options.selectedClass);
585
+ }
586
+ },
587
+
588
+ /**
589
+ * Creates a divider using the given select option.
590
+ *
591
+ * @param {jQuery} element
592
+ */
593
+ createDivider: function(element) {
594
+ var $divider = $(this.options.templates.divider);
595
+ this.$ul.append($divider);
596
+ },
597
+
598
+ /**
599
+ * Creates an optgroup.
600
+ *
601
+ * @param {jQuery} group
602
+ */
603
+ createOptgroup: function(group) {
604
+ var groupName = $(group).prop('label');
605
+
606
+ // Add a header for the group.
607
+ var $li = $(this.options.templates.liGroup);
608
+ $('label', $li).text(groupName);
609
+
610
+ this.$ul.append($li);
611
+
612
+ if ($(group).is(':disabled')) {
613
+ $li.addClass('disabled');
614
+ }
615
+
616
+ // Add the options of the group.
617
+ $('option', group).each($.proxy(function(index, element) {
618
+ this.createOptionValue(element);
619
+ }, this));
620
+ },
621
+
622
+ /**
623
+ * Build the selct all.
624
+ *
625
+ * Checks if a select all has already been created.
626
+ */
627
+ buildSelectAll: function() {
628
+ var alreadyHasSelectAll = this.hasSelectAll();
629
+
630
+ if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple
631
+ && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) {
632
+
633
+ // Check whether to add a divider after the select all.
634
+ if (this.options.includeSelectAllDivider) {
635
+ this.$ul.prepend($(this.options.templates.divider));
636
+ }
637
+
638
+ var $li = $(this.options.templates.li);
639
+ $('label', $li).addClass("checkbox");
640
+
641
+ if (this.options.checkboxName) {
642
+ $('label', $li).append('<input type="checkbox" name="' + this.options.checkboxName + '" />');
643
+ }
644
+ else {
645
+ $('label', $li).append('<input type="checkbox" />');
646
+ }
647
+
648
+ var $checkbox = $('input', $li);
649
+ $checkbox.val(this.options.selectAllValue);
650
+
651
+ $li.addClass("multiselect-item multiselect-all");
652
+ $checkbox.parent().parent()
653
+ .addClass('multiselect-all');
654
+
655
+ $('label', $li).append(" " + this.options.selectAllText);
656
+
657
+ this.$ul.prepend($li);
658
+
659
+ $checkbox.prop('checked', false);
660
+ }
661
+ },
662
+
663
+ /**
664
+ * Builds the filter.
665
+ */
666
+ buildFilter: function() {
667
+
668
+ // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength.
669
+ if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) {
670
+ var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering);
671
+
672
+ if (this.$select.find('option').length >= enableFilterLength) {
673
+
674
+ this.$filter = $(this.options.templates.filter);
675
+ $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder);
676
+ this.$ul.prepend(this.$filter);
677
+
678
+ this.$filter.val(this.query).on('click', function(event) {
679
+ event.stopPropagation();
680
+ }).on('input keydown', $.proxy(function(event) {
681
+ // This is useful to catch "keydown" events after the browser has updated the control.
682
+ clearTimeout(this.searchTimeout);
683
+
684
+ this.searchTimeout = this.asyncFunction($.proxy(function() {
685
+
686
+ if (this.query !== event.target.value) {
687
+ this.query = event.target.value;
688
+
689
+ $.each($('li', this.$ul), $.proxy(function(index, element) {
690
+ var value = $('input', element).val();
691
+ var text = $('label', element).text();
692
+
693
+ var filterCandidate = '';
694
+ if ((this.options.filterBehavior === 'text')) {
695
+ filterCandidate = text;
696
+ }
697
+ else if ((this.options.filterBehavior === 'value')) {
698
+ filterCandidate = value;
699
+ }
700
+ else if (this.options.filterBehavior === 'both') {
701
+ filterCandidate = text + '\n' + value;
702
+ }
703
+
704
+ if (value !== this.options.selectAllValue && text) {
705
+ // By default lets assume that element is not
706
+ // interesting for this search.
707
+ var showElement = false;
708
+
709
+ if (this.options.enableCaseInsensitiveFiltering && filterCandidate.toLowerCase().indexOf(this.query.toLowerCase()) > -1) {
710
+ showElement = true;
711
+ }
712
+ else if (filterCandidate.indexOf(this.query) > -1) {
713
+ showElement = true;
714
+ }
715
+
716
+ if (showElement) {
717
+ $(element).show().removeClass("filter-hidden");
718
+ }
719
+ else {
720
+ $(element).hide().addClass("filter-hidden");
721
+ }
722
+ }
723
+ }, this));
724
+ }
725
+
726
+ this.updateSelectAll();
727
+ }, this), 300, this);
728
+ }, this));
729
+ }
730
+ }
731
+ },
732
+
733
+ /**
734
+ * Unbinds the whole plugin.
735
+ */
736
+ destroy: function() {
737
+ this.$container.remove();
738
+ this.$select.show();
739
+ this.$select.data('multiselect', null);
740
+ },
741
+
742
+ /**
743
+ * Refreshs the multiselect based on the selected options of the select.
744
+ */
745
+ refresh: function() {
746
+ $('option', this.$select).each($.proxy(function(index, element) {
747
+ var $input = $('li input', this.$ul).filter(function() {
748
+ return $(this).val() === $(element).val();
749
+ });
750
+
751
+ if ($(element).is(':selected')) {
752
+ $input.prop('checked', true);
753
+
754
+ if (this.options.selectedClass) {
755
+ $input.parents('li')
756
+ .addClass(this.options.selectedClass);
757
+ }
758
+ }
759
+ else {
760
+ $input.prop('checked', false);
761
+
762
+ if (this.options.selectedClass) {
763
+ $input.parents('li')
764
+ .removeClass(this.options.selectedClass);
765
+ }
766
+ }
767
+
768
+ if ($(element).is(":disabled")) {
769
+ $input.attr('disabled', 'disabled')
770
+ .prop('disabled', true)
771
+ .parents('li')
772
+ .addClass('disabled');
773
+ }
774
+ else {
775
+ $input.prop('disabled', false)
776
+ .parents('li')
777
+ .removeClass('disabled');
778
+ }
779
+ }, this));
780
+
781
+ this.updateButtonText();
782
+ this.updateSelectAll();
783
+ },
784
+
785
+ /**
786
+ * Select all options of the given values.
787
+ *
788
+ * If triggerOnChange is set to true, the on change event is triggered if
789
+ * and only if one value is passed.
790
+ *
791
+ * @param {Array} selectValues
792
+ * @param {Boolean} triggerOnChange
793
+ */
794
+ select: function(selectValues, triggerOnChange) {
795
+ if(!$.isArray(selectValues)) {
796
+ selectValues = [selectValues];
797
+ }
798
+
799
+ for (var i = 0; i < selectValues.length; i++) {
800
+ var value = selectValues[i];
801
+
802
+ var $option = this.getOptionByValue(value);
803
+ var $checkbox = this.getInputByValue(value);
804
+
805
+ if($option === undefined || $checkbox === undefined) {
806
+ continue;
807
+ }
808
+
809
+ if (!this.options.multiple) {
810
+ this.deselectAll(false);
811
+ }
812
+
813
+ if (this.options.selectedClass) {
814
+ $checkbox.parents('li')
815
+ .addClass(this.options.selectedClass);
816
+ }
817
+
818
+ $checkbox.prop('checked', true);
819
+ $option.prop('selected', true);
820
+ }
821
+
822
+ this.updateButtonText();
823
+
824
+ if (triggerOnChange && selectValues.length === 1) {
825
+ this.options.onChange($option, true);
826
+ }
827
+ },
828
+
829
+ /**
830
+ * Clears all selected items.
831
+ */
832
+ clearSelection: function () {
833
+ this.deselectAll(false);
834
+ this.updateButtonText();
835
+ this.updateSelectAll();
836
+ },
837
+
838
+ /**
839
+ * Deselects all options of the given values.
840
+ *
841
+ * If triggerOnChange is set to true, the on change event is triggered, if
842
+ * and only if one value is passed.
843
+ *
844
+ * @param {Array} deselectValues
845
+ * @param {Boolean} triggerOnChange
846
+ */
847
+ deselect: function(deselectValues, triggerOnChange) {
848
+ if(!$.isArray(deselectValues)) {
849
+ deselectValues = [deselectValues];
850
+ }
851
+
852
+ for (var i = 0; i < deselectValues.length; i++) {
853
+
854
+ var value = deselectValues[i];
855
+
856
+ var $option = this.getOptionByValue(value);
857
+ var $checkbox = this.getInputByValue(value);
858
+
859
+ if($option === undefined || $checkbox === undefined) {
860
+ continue;
861
+ }
862
+
863
+ if (this.options.selectedClass) {
864
+ $checkbox.parents('li')
865
+ .removeClass(this.options.selectedClass);
866
+ }
867
+
868
+ $checkbox.prop('checked', false);
869
+ $option.prop('selected', false);
870
+ }
871
+
872
+ this.updateButtonText();
873
+
874
+ if (triggerOnChange && deselectValues.length === 1) {
875
+ this.options.onChange($option, false);
876
+ }
877
+ },
878
+
879
+ /**
880
+ * Selects all enabled & visible options.
881
+ */
882
+ selectAll: function () {
883
+ var allCheckboxes = $("li input[type='checkbox']:enabled", this.$ul);
884
+ var visibleCheckboxes = allCheckboxes.filter(":visible");
885
+ var allCheckboxesCount = allCheckboxes.length;
886
+ var visibleCheckboxesCount = visibleCheckboxes.length;
887
+
888
+ visibleCheckboxes.prop('checked', true);
889
+ $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").addClass(this.options.selectedClass);
890
+
891
+ if (allCheckboxesCount === visibleCheckboxesCount) {
892
+ $("option:enabled", this.$select).prop('selected', true);
893
+ }
894
+ else {
895
+ var values = visibleCheckboxes.map(function() {
896
+ return $(this).val();
897
+ }).get();
898
+
899
+ $("option:enabled", this.$select).filter(function(index) {
900
+ return $.inArray($(this).val(), values) !== -1;
901
+ }).prop('selected', true);
902
+ }
903
+ },
904
+
905
+ /**
906
+ * Deselects all options.
907
+ *
908
+ * If justVisible is true or not specified, only visible options are deselected.
909
+ *
910
+ * @param {Boolean} justVisible
911
+ */
912
+ deselectAll: function (justVisible) {
913
+ var justVisible = typeof justVisible === 'undefined' ? true : justVisible;
914
+
915
+ if(justVisible) {
916
+ var visibleCheckboxes = $("li input[type='checkbox']:enabled", this.$ul).filter(":visible");
917
+ visibleCheckboxes.prop('checked', false);
918
+
919
+ var values = visibleCheckboxes.map(function() {
920
+ return $(this).val();
921
+ }).get();
922
+
923
+ $("option:enabled", this.$select).filter(function(index) {
924
+ return $.inArray($(this).val(), values) !== -1;
925
+ }).prop('selected', false);
926
+
927
+ if (this.options.selectedClass) {
928
+ $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").removeClass(this.options.selectedClass);
929
+ }
930
+ }
931
+ else {
932
+ $("li input[type='checkbox']:enabled", this.$ul).prop('checked', false);
933
+ $("option:enabled", this.$select).prop('selected', false);
934
+
935
+ if (this.options.selectedClass) {
936
+ $("li:not(.divider):not(.disabled)", this.$ul).removeClass(this.options.selectedClass);
937
+ }
938
+ }
939
+ },
940
+
941
+ /**
942
+ * Rebuild the plugin.
943
+ *
944
+ * Rebuilds the dropdown, the filter and the select all option.
945
+ */
946
+ rebuild: function() {
947
+ this.$ul.html('');
948
+
949
+ // Important to distinguish between radios and checkboxes.
950
+ this.options.multiple = this.$select.attr('multiple') === "multiple";
951
+
952
+ this.buildSelectAll();
953
+ this.buildDropdownOptions();
954
+ this.buildFilter();
955
+
956
+ this.updateButtonText();
957
+ this.updateSelectAll();
958
+
959
+ if (this.options.disableIfEmpty) {
960
+ this.disableIfEmpty();
961
+ }
962
+
963
+ if (this.options.dropRight) {
964
+ this.$ul.addClass('pull-right');
965
+ }
966
+ },
967
+
968
+ /**
969
+ * The provided data will be used to build the dropdown.
970
+ *
971
+ * @param {Array} dataprovider
972
+ */
973
+ dataprovider: function(dataprovider) {
974
+ var optionDOM = "";
975
+ var groupCounter = 0;
976
+
977
+ $.each(dataprovider, function (index, option) {
978
+ if ($.isArray(option.children)) {
979
+ groupCounter++;
980
+ optionDOM += '<optgroup label="' + (option.title || 'Group ' + groupCounter) + '">';
981
+
982
+ forEach(option.children, function(subOption) {
983
+ optionDOM += '<option value="' + subOption.value + '">' + (subOption.label || subOption.value) + '</option>';
984
+ });
985
+
986
+ optionDOM += '</optgroup>';
987
+ }
988
+ else {
989
+ optionDOM += '<option value="' + option.value + '">' + (option.label || option.value) + '</option>';
990
+ }
991
+ });
992
+
993
+ this.$select.html(optionDOM);
994
+ this.rebuild();
995
+ },
996
+
997
+ /**
998
+ * Enable the multiselect.
999
+ */
1000
+ enable: function() {
1001
+ this.$select.prop('disabled', false);
1002
+ this.$button.prop('disabled', false)
1003
+ .removeClass('disabled');
1004
+ },
1005
+
1006
+ /**
1007
+ * Disable the multiselect.
1008
+ */
1009
+ disable: function() {
1010
+ this.$select.prop('disabled', true);
1011
+ this.$button.prop('disabled', true)
1012
+ .addClass('disabled');
1013
+ },
1014
+
1015
+ /**
1016
+ * Disable the multiselect if there are no options in the select.
1017
+ */
1018
+ disableIfEmpty: function () {
1019
+ if ($('option', this.$select).length <= 0) {
1020
+ this.disable();
1021
+ }
1022
+ else {
1023
+ this.enable();
1024
+ }
1025
+ },
1026
+
1027
+ /**
1028
+ * Set the options.
1029
+ *
1030
+ * @param {Array} options
1031
+ */
1032
+ setOptions: function(options) {
1033
+ this.options = this.mergeOptions(options);
1034
+ },
1035
+
1036
+ /**
1037
+ * Merges the given options with the default options.
1038
+ *
1039
+ * @param {Array} options
1040
+ * @returns {Array}
1041
+ */
1042
+ mergeOptions: function(options) {
1043
+ return $.extend(true, {}, this.defaults, options);
1044
+ },
1045
+
1046
+ /**
1047
+ * Checks whether a select all checkbox is present.
1048
+ *
1049
+ * @returns {Boolean}
1050
+ */
1051
+ hasSelectAll: function() {
1052
+ return $('li.' + this.options.selectAllValue, this.$ul).length > 0;
1053
+ },
1054
+
1055
+ /**
1056
+ * Updates the select all checkbox based on the currently displayed and selected checkboxes.
1057
+ */
1058
+ updateSelectAll: function() {
1059
+ if (this.hasSelectAll()) {
1060
+ var allBoxes = $("li:not(.multiselect-item):not(.filter-hidden) input:enabled", this.$ul);
1061
+ var allBoxesLength = allBoxes.length;
1062
+ var checkedBoxesLength = allBoxes.filter(":checked").length;
1063
+ var selectAllLi = $("li." + this.options.selectAllValue, this.$ul);
1064
+ var selectAllInput = selectAllLi.find("input");
1065
+
1066
+ if (checkedBoxesLength > 0 && checkedBoxesLength === allBoxesLength) {
1067
+ selectAllInput.prop("checked", true);
1068
+ selectAllLi.addClass(this.options.selectedClass);
1069
+ }
1070
+ else {
1071
+ selectAllInput.prop("checked", false);
1072
+ selectAllLi.removeClass(this.options.selectedClass);
1073
+ }
1074
+ }
1075
+ },
1076
+
1077
+ /**
1078
+ * Update the button text and its title based on the currently selected options.
1079
+ */
1080
+ updateButtonText: function() {
1081
+ var options = this.getSelected();
1082
+
1083
+ // First update the displayed button text.
1084
+ $('button.multiselect', this.$container).html(this.options.buttonText(options, this.$select));
1085
+
1086
+ // Now update the title attribute of the button.
1087
+ $('button.multiselect', this.$container).attr('title', this.options.buttonTitle(options, this.$select));
1088
+ },
1089
+
1090
+ /**
1091
+ * Get all selected options.
1092
+ *
1093
+ * @returns {jQUery}
1094
+ */
1095
+ getSelected: function() {
1096
+ return $('option', this.$select).filter(":selected");
1097
+ },
1098
+
1099
+ /**
1100
+ * Gets a select option by its value.
1101
+ *
1102
+ * @param {String} value
1103
+ * @returns {jQuery}
1104
+ */
1105
+ getOptionByValue: function (value) {
1106
+
1107
+ var options = $('option', this.$select);
1108
+ var valueToCompare = value.toString();
1109
+
1110
+ for (var i = 0; i < options.length; i = i + 1) {
1111
+ var option = options[i];
1112
+ if (option.value === valueToCompare) {
1113
+ return $(option);
1114
+ }
1115
+ }
1116
+ },
1117
+
1118
+ /**
1119
+ * Get the input (radio/checkbox) by its value.
1120
+ *
1121
+ * @param {String} value
1122
+ * @returns {jQuery}
1123
+ */
1124
+ getInputByValue: function (value) {
1125
+
1126
+ var checkboxes = $('li input', this.$ul);
1127
+ var valueToCompare = value.toString();
1128
+
1129
+ for (var i = 0; i < checkboxes.length; i = i + 1) {
1130
+ var checkbox = checkboxes[i];
1131
+ if (checkbox.value === valueToCompare) {
1132
+ return $(checkbox);
1133
+ }
1134
+ }
1135
+ },
1136
+
1137
+ /**
1138
+ * Used for knockout integration.
1139
+ */
1140
+ updateOriginalOptions: function() {
1141
+ this.originalOptions = this.$select.clone()[0].options;
1142
+ },
1143
+
1144
+ asyncFunction: function(callback, timeout, self) {
1145
+ var args = Array.prototype.slice.call(arguments, 3);
1146
+ return setTimeout(function() {
1147
+ callback.apply(self || window, args);
1148
+ }, timeout);
1149
+ }
1150
+ };
1151
+
1152
+ $.fn.multiselect = function(option, parameter, extraOptions) {
1153
+ return this.each(function() {
1154
+ var data = $(this).data('multiselect');
1155
+ var options = typeof option === 'object' && option;
1156
+
1157
+ // Initialize the multiselect.
1158
+ if (!data) {
1159
+ data = new Multiselect(this, options);
1160
+ $(this).data('multiselect', data);
1161
+ }
1162
+
1163
+ // Call multiselect method.
1164
+ if (typeof option === 'string') {
1165
+ data[option](parameter, extraOptions);
1166
+
1167
+ if (option === 'destroy') {
1168
+ $(this).data('multiselect', false);
1169
+ }
1170
+ }
1171
+ });
1172
+ };
1173
+
1174
+ $.fn.multiselect.Constructor = Multiselect;
1175
+
1176
+ $(function() {
1177
+ $("select[data-role=multiselect]").multiselect();
1178
+ });
1179
+
1180
+ }(window.jQuery);