vapid 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (433) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.gitmodules +6 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +133 -0
  6. data/.travis.yml +5 -0
  7. data/Gemfile +10 -0
  8. data/README.md +37 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/exe/vapid +7 -0
  13. data/lib/vapid.rb +24 -0
  14. data/lib/vapid/assets/javascripts/vapid/admin.coffee +2 -0
  15. data/lib/vapid/assets/stylesheets/vapid/admin.scss +72 -0
  16. data/lib/vapid/builder.rb +70 -0
  17. data/lib/vapid/cli.rb +96 -0
  18. data/lib/vapid/controllers/admin_controller.rb +282 -0
  19. data/lib/vapid/controllers/project_controller.rb +41 -0
  20. data/lib/vapid/db/migrate/20170202182310_create_groups.rb +12 -0
  21. data/lib/vapid/db/migrate/20170202183017_create_records.rb +11 -0
  22. data/lib/vapid/db/migrate/20170202185150_create_users.rb +11 -0
  23. data/lib/vapid/directive.rb +168 -0
  24. data/lib/vapid/directives.rb +30 -0
  25. data/lib/vapid/directives/audio.rb +27 -0
  26. data/lib/vapid/directives/group.rb +71 -0
  27. data/lib/vapid/directives/hide.rb +11 -0
  28. data/lib/vapid/directives/href.rb +11 -0
  29. data/lib/vapid/directives/image.rb +14 -0
  30. data/lib/vapid/directives/richtext.rb +15 -0
  31. data/lib/vapid/directives/show.rb +11 -0
  32. data/lib/vapid/directives/text.rb +24 -0
  33. data/lib/vapid/directives/video.rb +28 -0
  34. data/lib/vapid/generator_template/.gitignore +21 -0
  35. data/lib/vapid/generator_template/Gemfile.tt +13 -0
  36. data/lib/vapid/generator_template/README.md +0 -0
  37. data/lib/vapid/generator_template/assets/favicon.ico +0 -0
  38. data/lib/vapid/generator_template/assets/images/.keep +0 -0
  39. data/lib/vapid/generator_template/assets/javascripts/.keep +0 -0
  40. data/lib/vapid/generator_template/assets/stylesheets/.keep +0 -0
  41. data/lib/vapid/generator_template/assets/stylesheets/application.css +120 -0
  42. data/lib/vapid/generator_template/config.ru +3 -0
  43. data/lib/vapid/generator_template/config/database.yml +12 -0
  44. data/lib/vapid/generator_template/config/settings.yml.tt +11 -0
  45. data/lib/vapid/generator_template/templates/index.html.erb +25 -0
  46. data/lib/vapid/generator_template/templates/layouts/default.html.erb +22 -0
  47. data/lib/vapid/models.rb +18 -0
  48. data/lib/vapid/models/group.rb +36 -0
  49. data/lib/vapid/models/record.rb +30 -0
  50. data/lib/vapid/models/user.rb +16 -0
  51. data/lib/vapid/server.rb +93 -0
  52. data/lib/vapid/settings.rb +13 -0
  53. data/lib/vapid/template.rb +79 -0
  54. data/lib/vapid/template/node.rb +91 -0
  55. data/lib/vapid/template/parser.rb +50 -0
  56. data/lib/vapid/vendor/semantic-ui/.versions +4 -0
  57. data/lib/vapid/vendor/semantic-ui/LICENSE +22 -0
  58. data/lib/vapid/vendor/semantic-ui/README.md +7 -0
  59. data/lib/vapid/vendor/semantic-ui/components/accordion.css +252 -0
  60. data/lib/vapid/vendor/semantic-ui/components/accordion.js +610 -0
  61. data/lib/vapid/vendor/semantic-ui/components/accordion.min.css +9 -0
  62. data/lib/vapid/vendor/semantic-ui/components/accordion.min.js +10 -0
  63. data/lib/vapid/vendor/semantic-ui/components/ad.css +275 -0
  64. data/lib/vapid/vendor/semantic-ui/components/ad.min.css +10 -0
  65. data/lib/vapid/vendor/semantic-ui/components/api.js +1167 -0
  66. data/lib/vapid/vendor/semantic-ui/components/api.min.js +10 -0
  67. data/lib/vapid/vendor/semantic-ui/components/breadcrumb.css +124 -0
  68. data/lib/vapid/vendor/semantic-ui/components/breadcrumb.min.css +9 -0
  69. data/lib/vapid/vendor/semantic-ui/components/button.css +3450 -0
  70. data/lib/vapid/vendor/semantic-ui/components/button.min.css +9 -0
  71. data/lib/vapid/vendor/semantic-ui/components/card.css +964 -0
  72. data/lib/vapid/vendor/semantic-ui/components/card.min.css +9 -0
  73. data/lib/vapid/vendor/semantic-ui/components/checkbox.css +625 -0
  74. data/lib/vapid/vendor/semantic-ui/components/checkbox.js +831 -0
  75. data/lib/vapid/vendor/semantic-ui/components/checkbox.min.css +9 -0
  76. data/lib/vapid/vendor/semantic-ui/components/checkbox.min.js +10 -0
  77. data/lib/vapid/vendor/semantic-ui/components/colorize.js +274 -0
  78. data/lib/vapid/vendor/semantic-ui/components/colorize.min.js +11 -0
  79. data/lib/vapid/vendor/semantic-ui/components/comment.css +270 -0
  80. data/lib/vapid/vendor/semantic-ui/components/comment.min.css +9 -0
  81. data/lib/vapid/vendor/semantic-ui/components/container.css +147 -0
  82. data/lib/vapid/vendor/semantic-ui/components/container.min.css +9 -0
  83. data/lib/vapid/vendor/semantic-ui/components/dimmer.css +200 -0
  84. data/lib/vapid/vendor/semantic-ui/components/dimmer.js +708 -0
  85. data/lib/vapid/vendor/semantic-ui/components/dimmer.min.css +9 -0
  86. data/lib/vapid/vendor/semantic-ui/components/dimmer.min.js +10 -0
  87. data/lib/vapid/vendor/semantic-ui/components/divider.css +260 -0
  88. data/lib/vapid/vendor/semantic-ui/components/divider.min.css +9 -0
  89. data/lib/vapid/vendor/semantic-ui/components/dropdown.css +1442 -0
  90. data/lib/vapid/vendor/semantic-ui/components/dropdown.js +3767 -0
  91. data/lib/vapid/vendor/semantic-ui/components/dropdown.min.css +9 -0
  92. data/lib/vapid/vendor/semantic-ui/components/dropdown.min.js +11 -0
  93. data/lib/vapid/vendor/semantic-ui/components/embed.css +166 -0
  94. data/lib/vapid/vendor/semantic-ui/components/embed.js +696 -0
  95. data/lib/vapid/vendor/semantic-ui/components/embed.min.css +9 -0
  96. data/lib/vapid/vendor/semantic-ui/components/embed.min.js +10 -0
  97. data/lib/vapid/vendor/semantic-ui/components/feed.css +296 -0
  98. data/lib/vapid/vendor/semantic-ui/components/feed.min.css +9 -0
  99. data/lib/vapid/vendor/semantic-ui/components/flag.css +1031 -0
  100. data/lib/vapid/vendor/semantic-ui/components/flag.min.css +9 -0
  101. data/lib/vapid/vendor/semantic-ui/components/form.css +1067 -0
  102. data/lib/vapid/vendor/semantic-ui/components/form.js +1603 -0
  103. data/lib/vapid/vendor/semantic-ui/components/form.min.css +9 -0
  104. data/lib/vapid/vendor/semantic-ui/components/form.min.js +10 -0
  105. data/lib/vapid/vendor/semantic-ui/components/grid.css +2002 -0
  106. data/lib/vapid/vendor/semantic-ui/components/grid.min.css +9 -0
  107. data/lib/vapid/vendor/semantic-ui/components/header.css +721 -0
  108. data/lib/vapid/vendor/semantic-ui/components/header.min.css +9 -0
  109. data/lib/vapid/vendor/semantic-ui/components/icon.css +3292 -0
  110. data/lib/vapid/vendor/semantic-ui/components/icon.min.css +9 -0
  111. data/lib/vapid/vendor/semantic-ui/components/image.css +306 -0
  112. data/lib/vapid/vendor/semantic-ui/components/image.min.css +9 -0
  113. data/lib/vapid/vendor/semantic-ui/components/input.css +510 -0
  114. data/lib/vapid/vendor/semantic-ui/components/input.min.css +9 -0
  115. data/lib/vapid/vendor/semantic-ui/components/item.css +486 -0
  116. data/lib/vapid/vendor/semantic-ui/components/item.min.css +9 -0
  117. data/lib/vapid/vendor/semantic-ui/components/label.css +1307 -0
  118. data/lib/vapid/vendor/semantic-ui/components/label.min.css +9 -0
  119. data/lib/vapid/vendor/semantic-ui/components/list.css +951 -0
  120. data/lib/vapid/vendor/semantic-ui/components/list.min.css +9 -0
  121. data/lib/vapid/vendor/semantic-ui/components/loader.css +347 -0
  122. data/lib/vapid/vendor/semantic-ui/components/loader.min.css +9 -0
  123. data/lib/vapid/vendor/semantic-ui/components/menu.css +2002 -0
  124. data/lib/vapid/vendor/semantic-ui/components/menu.min.css +1 -0
  125. data/lib/vapid/vendor/semantic-ui/components/message.css +477 -0
  126. data/lib/vapid/vendor/semantic-ui/components/message.min.css +9 -0
  127. data/lib/vapid/vendor/semantic-ui/components/modal.css +502 -0
  128. data/lib/vapid/vendor/semantic-ui/components/modal.js +913 -0
  129. data/lib/vapid/vendor/semantic-ui/components/modal.min.css +9 -0
  130. data/lib/vapid/vendor/semantic-ui/components/modal.min.js +10 -0
  131. data/lib/vapid/vendor/semantic-ui/components/nag.css +147 -0
  132. data/lib/vapid/vendor/semantic-ui/components/nag.js +507 -0
  133. data/lib/vapid/vendor/semantic-ui/components/nag.min.css +9 -0
  134. data/lib/vapid/vendor/semantic-ui/components/nag.min.js +10 -0
  135. data/lib/vapid/vendor/semantic-ui/components/popup.css +733 -0
  136. data/lib/vapid/vendor/semantic-ui/components/popup.js +1475 -0
  137. data/lib/vapid/vendor/semantic-ui/components/popup.min.css +9 -0
  138. data/lib/vapid/vendor/semantic-ui/components/popup.min.js +10 -0
  139. data/lib/vapid/vendor/semantic-ui/components/progress.css +516 -0
  140. data/lib/vapid/vendor/semantic-ui/components/progress.js +931 -0
  141. data/lib/vapid/vendor/semantic-ui/components/progress.min.css +9 -0
  142. data/lib/vapid/vendor/semantic-ui/components/progress.min.js +10 -0
  143. data/lib/vapid/vendor/semantic-ui/components/rail.css +152 -0
  144. data/lib/vapid/vendor/semantic-ui/components/rail.min.css +9 -0
  145. data/lib/vapid/vendor/semantic-ui/components/rating.css +263 -0
  146. data/lib/vapid/vendor/semantic-ui/components/rating.js +508 -0
  147. data/lib/vapid/vendor/semantic-ui/components/rating.min.css +9 -0
  148. data/lib/vapid/vendor/semantic-ui/components/rating.min.js +10 -0
  149. data/lib/vapid/vendor/semantic-ui/components/reset.css +424 -0
  150. data/lib/vapid/vendor/semantic-ui/components/reset.min.css +9 -0
  151. data/lib/vapid/vendor/semantic-ui/components/reveal.css +284 -0
  152. data/lib/vapid/vendor/semantic-ui/components/reveal.min.css +9 -0
  153. data/lib/vapid/vendor/semantic-ui/components/search.css +408 -0
  154. data/lib/vapid/vendor/semantic-ui/components/search.js +1442 -0
  155. data/lib/vapid/vendor/semantic-ui/components/search.min.css +9 -0
  156. data/lib/vapid/vendor/semantic-ui/components/search.min.js +10 -0
  157. data/lib/vapid/vendor/semantic-ui/components/segment.css +798 -0
  158. data/lib/vapid/vendor/semantic-ui/components/segment.min.css +9 -0
  159. data/lib/vapid/vendor/semantic-ui/components/shape.css +157 -0
  160. data/lib/vapid/vendor/semantic-ui/components/shape.js +921 -0
  161. data/lib/vapid/vendor/semantic-ui/components/shape.min.css +9 -0
  162. data/lib/vapid/vendor/semantic-ui/components/shape.min.js +10 -0
  163. data/lib/vapid/vendor/semantic-ui/components/sidebar.css +634 -0
  164. data/lib/vapid/vendor/semantic-ui/components/sidebar.js +1036 -0
  165. data/lib/vapid/vendor/semantic-ui/components/sidebar.min.css +9 -0
  166. data/lib/vapid/vendor/semantic-ui/components/sidebar.min.js +10 -0
  167. data/lib/vapid/vendor/semantic-ui/components/site.css +160 -0
  168. data/lib/vapid/vendor/semantic-ui/components/site.js +487 -0
  169. data/lib/vapid/vendor/semantic-ui/components/site.min.css +9 -0
  170. data/lib/vapid/vendor/semantic-ui/components/site.min.js +10 -0
  171. data/lib/vapid/vendor/semantic-ui/components/state.js +708 -0
  172. data/lib/vapid/vendor/semantic-ui/components/state.min.js +10 -0
  173. data/lib/vapid/vendor/semantic-ui/components/statistic.css +569 -0
  174. data/lib/vapid/vendor/semantic-ui/components/statistic.min.css +9 -0
  175. data/lib/vapid/vendor/semantic-ui/components/step.css +623 -0
  176. data/lib/vapid/vendor/semantic-ui/components/step.min.css +9 -0
  177. data/lib/vapid/vendor/semantic-ui/components/sticky.css +78 -0
  178. data/lib/vapid/vendor/semantic-ui/components/sticky.js +942 -0
  179. data/lib/vapid/vendor/semantic-ui/components/sticky.min.css +9 -0
  180. data/lib/vapid/vendor/semantic-ui/components/sticky.min.js +10 -0
  181. data/lib/vapid/vendor/semantic-ui/components/tab.css +91 -0
  182. data/lib/vapid/vendor/semantic-ui/components/tab.js +952 -0
  183. data/lib/vapid/vendor/semantic-ui/components/tab.min.css +9 -0
  184. data/lib/vapid/vendor/semantic-ui/components/tab.min.js +10 -0
  185. data/lib/vapid/vendor/semantic-ui/components/table.css +1108 -0
  186. data/lib/vapid/vendor/semantic-ui/components/table.min.css +9 -0
  187. data/lib/vapid/vendor/semantic-ui/components/transition.css +1964 -0
  188. data/lib/vapid/vendor/semantic-ui/components/transition.js +1095 -0
  189. data/lib/vapid/vendor/semantic-ui/components/transition.min.css +9 -0
  190. data/lib/vapid/vendor/semantic-ui/components/transition.min.js +10 -0
  191. data/lib/vapid/vendor/semantic-ui/components/video.css +125 -0
  192. data/lib/vapid/vendor/semantic-ui/components/video.js +532 -0
  193. data/lib/vapid/vendor/semantic-ui/components/video.min.css +10 -0
  194. data/lib/vapid/vendor/semantic-ui/components/video.min.js +11 -0
  195. data/lib/vapid/vendor/semantic-ui/components/visibility.js +1288 -0
  196. data/lib/vapid/vendor/semantic-ui/components/visibility.min.js +10 -0
  197. data/lib/vapid/vendor/semantic-ui/components/visit.js +517 -0
  198. data/lib/vapid/vendor/semantic-ui/components/visit.min.js +11 -0
  199. data/lib/vapid/vendor/semantic-ui/package.js +34 -0
  200. data/lib/vapid/vendor/semantic-ui/package.json +20 -0
  201. data/lib/vapid/vendor/semantic-ui/semantic.css +36844 -0
  202. data/lib/vapid/vendor/semantic-ui/semantic.js +22617 -0
  203. data/lib/vapid/vendor/semantic-ui/semantic.min.css +364 -0
  204. data/lib/vapid/vendor/semantic-ui/semantic.min.js +19 -0
  205. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.eot +0 -0
  206. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.otf +0 -0
  207. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.svg +2671 -0
  208. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.ttf +0 -0
  209. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.woff +0 -0
  210. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.woff2 +0 -0
  211. data/lib/vapid/vendor/semantic-ui/themes/default/assets/images/flags.png +0 -0
  212. data/lib/vapid/vendor/trix/.blade.yml +45 -0
  213. data/lib/vapid/vendor/trix/.github/ISSUE_TEMPLATE.md +13 -0
  214. data/lib/vapid/vendor/trix/.gitignore +3 -0
  215. data/lib/vapid/vendor/trix/.ruby-version +1 -0
  216. data/lib/vapid/vendor/trix/.travis.yml +6 -0
  217. data/lib/vapid/vendor/trix/CONDUCT.md +79 -0
  218. data/lib/vapid/vendor/trix/Gemfile +17 -0
  219. data/lib/vapid/vendor/trix/Gemfile.lock +152 -0
  220. data/lib/vapid/vendor/trix/LICENSE +20 -0
  221. data/lib/vapid/vendor/trix/README.md +382 -0
  222. data/lib/vapid/vendor/trix/assets/index.html +55 -0
  223. data/lib/vapid/vendor/trix/assets/trix-core.coffee +2 -0
  224. data/lib/vapid/vendor/trix/assets/trix.coffee +3 -0
  225. data/lib/vapid/vendor/trix/assets/trix.scss +5 -0
  226. data/lib/vapid/vendor/trix/assets/trix/banner/index.coffee.erb +3 -0
  227. data/lib/vapid/vendor/trix/assets/trix/banner/index.scss.erb +3 -0
  228. data/lib/vapid/vendor/trix/assets/trix/banner/index.txt.erb +3 -0
  229. data/lib/vapid/vendor/trix/assets/trix/images/README.md +6 -0
  230. data/lib/vapid/vendor/trix/assets/trix/images/attach.svg +5 -0
  231. data/lib/vapid/vendor/trix/assets/trix/images/bold.svg +5 -0
  232. data/lib/vapid/vendor/trix/assets/trix/images/bullets.svg +5 -0
  233. data/lib/vapid/vendor/trix/assets/trix/images/code.svg +5 -0
  234. data/lib/vapid/vendor/trix/assets/trix/images/heading_1.svg +5 -0
  235. data/lib/vapid/vendor/trix/assets/trix/images/italic.svg +5 -0
  236. data/lib/vapid/vendor/trix/assets/trix/images/link.svg +6 -0
  237. data/lib/vapid/vendor/trix/assets/trix/images/nesting_level_decrease.svg +5 -0
  238. data/lib/vapid/vendor/trix/assets/trix/images/nesting_level_increase.svg +5 -0
  239. data/lib/vapid/vendor/trix/assets/trix/images/numbers.svg +5 -0
  240. data/lib/vapid/vendor/trix/assets/trix/images/quote.svg +5 -0
  241. data/lib/vapid/vendor/trix/assets/trix/images/redo.svg +5 -0
  242. data/lib/vapid/vendor/trix/assets/trix/images/remove.svg +4 -0
  243. data/lib/vapid/vendor/trix/assets/trix/images/strike.svg +6 -0
  244. data/lib/vapid/vendor/trix/assets/trix/images/trash.svg +5 -0
  245. data/lib/vapid/vendor/trix/assets/trix/images/undo.svg +5 -0
  246. data/lib/vapid/vendor/trix/assets/trix/stylesheets/attachments.scss +112 -0
  247. data/lib/vapid/vendor/trix/assets/trix/stylesheets/content.scss +88 -0
  248. data/lib/vapid/vendor/trix/assets/trix/stylesheets/editor.scss +8 -0
  249. data/lib/vapid/vendor/trix/assets/trix/stylesheets/icons.scss +14 -0
  250. data/lib/vapid/vendor/trix/assets/trix/stylesheets/media-queries.scss +7 -0
  251. data/lib/vapid/vendor/trix/assets/trix/stylesheets/toolbar.scss +194 -0
  252. data/lib/vapid/vendor/trix/bin/blade +16 -0
  253. data/lib/vapid/vendor/trix/bin/ci +21 -0
  254. data/lib/vapid/vendor/trix/bin/rackup +16 -0
  255. data/lib/vapid/vendor/trix/bin/rake +16 -0
  256. data/lib/vapid/vendor/trix/bin/release +139 -0
  257. data/lib/vapid/vendor/trix/bin/setup +69 -0
  258. data/lib/vapid/vendor/trix/bin/update-status-image +100 -0
  259. data/lib/vapid/vendor/trix/bower.json +34 -0
  260. data/lib/vapid/vendor/trix/config.ru +32 -0
  261. data/lib/vapid/vendor/trix/dist/trix-core.js +11 -0
  262. data/lib/vapid/vendor/trix/dist/trix.css +286 -0
  263. data/lib/vapid/vendor/trix/dist/trix.js +20 -0
  264. data/lib/vapid/vendor/trix/package.json +29 -0
  265. data/lib/vapid/vendor/trix/polyfills/polyfills.coffee +3 -0
  266. data/lib/vapid/vendor/trix/polyfills/set.coffee +24 -0
  267. data/lib/vapid/vendor/trix/polyfills/vendor/CustomElements.js +1029 -0
  268. data/lib/vapid/vendor/trix/polyfills/vendor/README.md +2 -0
  269. data/lib/vapid/vendor/trix/polyfills/vendor/promise.js +228 -0
  270. data/lib/vapid/vendor/trix/src/trix/VERSION +1 -0
  271. data/lib/vapid/vendor/trix/src/trix/config/block_attributes.coffee +37 -0
  272. data/lib/vapid/vendor/trix/src/trix/config/css.coffee +12 -0
  273. data/lib/vapid/vendor/trix/src/trix/config/file_size_formatting.coffee +22 -0
  274. data/lib/vapid/vendor/trix/src/trix/config/index.coffee +9 -0
  275. data/lib/vapid/vendor/trix/src/trix/config/lang.coffee +26 -0
  276. data/lib/vapid/vendor/trix/src/trix/config/selection_elements.coffee +26 -0
  277. data/lib/vapid/vendor/trix/src/trix/config/serialization.coffee +63 -0
  278. data/lib/vapid/vendor/trix/src/trix/config/text_attributes.coffee +25 -0
  279. data/lib/vapid/vendor/trix/src/trix/config/toolbar.coffee +41 -0
  280. data/lib/vapid/vendor/trix/src/trix/config/undo_interval.coffee +7 -0
  281. data/lib/vapid/vendor/trix/src/trix/controllers/attachment_editor_controller.coffee +104 -0
  282. data/lib/vapid/vendor/trix/src/trix/controllers/composition_controller.coffee +117 -0
  283. data/lib/vapid/vendor/trix/src/trix/controllers/controller.coffee +3 -0
  284. data/lib/vapid/vendor/trix/src/trix/controllers/editor_controller.coffee +369 -0
  285. data/lib/vapid/vendor/trix/src/trix/controllers/input/composition_input.coffee +57 -0
  286. data/lib/vapid/vendor/trix/src/trix/controllers/input_controller.coffee +473 -0
  287. data/lib/vapid/vendor/trix/src/trix/controllers/toolbar_controller.coffee +173 -0
  288. data/lib/vapid/vendor/trix/src/trix/core/basic_object.coffee +44 -0
  289. data/lib/vapid/vendor/trix/src/trix/core/collections/element_store.coffee +21 -0
  290. data/lib/vapid/vendor/trix/src/trix/core/collections/hash.coffee +98 -0
  291. data/lib/vapid/vendor/trix/src/trix/core/collections/index.coffee +4 -0
  292. data/lib/vapid/vendor/trix/src/trix/core/collections/object_group.coffee +37 -0
  293. data/lib/vapid/vendor/trix/src/trix/core/collections/object_map.coffee +10 -0
  294. data/lib/vapid/vendor/trix/src/trix/core/helpers/arrays.coffee +34 -0
  295. data/lib/vapid/vendor/trix/src/trix/core/helpers/config.coffee +23 -0
  296. data/lib/vapid/vendor/trix/src/trix/core/helpers/custom_elements.coffee +45 -0
  297. data/lib/vapid/vendor/trix/src/trix/core/helpers/dom.coffee +185 -0
  298. data/lib/vapid/vendor/trix/src/trix/core/helpers/extend.coffee +4 -0
  299. data/lib/vapid/vendor/trix/src/trix/core/helpers/functions.coffee +3 -0
  300. data/lib/vapid/vendor/trix/src/trix/core/helpers/global.coffee +23 -0
  301. data/lib/vapid/vendor/trix/src/trix/core/helpers/index.coffee +10 -0
  302. data/lib/vapid/vendor/trix/src/trix/core/helpers/objects.coffee +11 -0
  303. data/lib/vapid/vendor/trix/src/trix/core/helpers/ranges.coffee +33 -0
  304. data/lib/vapid/vendor/trix/src/trix/core/helpers/selection.coffee +25 -0
  305. data/lib/vapid/vendor/trix/src/trix/core/helpers/strings.coffee +46 -0
  306. data/lib/vapid/vendor/trix/src/trix/core/index.coffee +4 -0
  307. data/lib/vapid/vendor/trix/src/trix/core/object.coffee +33 -0
  308. data/lib/vapid/vendor/trix/src/trix/core/utilities/index.coffee +2 -0
  309. data/lib/vapid/vendor/trix/src/trix/core/utilities/operation.coffee +37 -0
  310. data/lib/vapid/vendor/trix/src/trix/core/utilities/utf16_string.coffee +93 -0
  311. data/lib/vapid/vendor/trix/src/trix/elements/trix_editor_element.coffee +166 -0
  312. data/lib/vapid/vendor/trix/src/trix/elements/trix_toolbar_element.coffee +28 -0
  313. data/lib/vapid/vendor/trix/src/trix/index.coffee.erb +14 -0
  314. data/lib/vapid/vendor/trix/src/trix/inspector/control_element.coffee +60 -0
  315. data/lib/vapid/vendor/trix/src/trix/inspector/debugger.coffee +104 -0
  316. data/lib/vapid/vendor/trix/src/trix/inspector/element.coffee +86 -0
  317. data/lib/vapid/vendor/trix/src/trix/inspector/index.coffee +17 -0
  318. data/lib/vapid/vendor/trix/src/trix/inspector/polyfills/vendor/details-element-polyfill.js +5 -0
  319. data/lib/vapid/vendor/trix/src/trix/inspector/templates/debug.jst.eco +17 -0
  320. data/lib/vapid/vendor/trix/src/trix/inspector/templates/document.jst.eco +33 -0
  321. data/lib/vapid/vendor/trix/src/trix/inspector/templates/performance.jst.eco +10 -0
  322. data/lib/vapid/vendor/trix/src/trix/inspector/templates/render.jst.eco +1 -0
  323. data/lib/vapid/vendor/trix/src/trix/inspector/templates/selection.jst.eco +5 -0
  324. data/lib/vapid/vendor/trix/src/trix/inspector/templates/undo.jst.eco +13 -0
  325. data/lib/vapid/vendor/trix/src/trix/inspector/view.coffee +58 -0
  326. data/lib/vapid/vendor/trix/src/trix/inspector/views/debug_view.coffee +33 -0
  327. data/lib/vapid/vendor/trix/src/trix/inspector/views/document_view.coffee +12 -0
  328. data/lib/vapid/vendor/trix/src/trix/inspector/views/performance_view.coffee +51 -0
  329. data/lib/vapid/vendor/trix/src/trix/inspector/views/render_view.coffee +21 -0
  330. data/lib/vapid/vendor/trix/src/trix/inspector/views/selection_view.coffee +33 -0
  331. data/lib/vapid/vendor/trix/src/trix/inspector/views/undo_view.coffee +12 -0
  332. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/deserializer.coffee +55 -0
  333. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/index.coffee +5 -0
  334. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/player.coffee +60 -0
  335. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/player_controller.coffee +43 -0
  336. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/player_element.coffee +32 -0
  337. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/player_view.coffee +133 -0
  338. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/recorder.coffee +82 -0
  339. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/recording.coffee +54 -0
  340. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/serializer.coffee +61 -0
  341. data/lib/vapid/vendor/trix/src/trix/models/attachment.coffee +157 -0
  342. data/lib/vapid/vendor/trix/src/trix/models/attachment_manager.coffee +24 -0
  343. data/lib/vapid/vendor/trix/src/trix/models/attachment_piece.coffee +47 -0
  344. data/lib/vapid/vendor/trix/src/trix/models/block.coffee +230 -0
  345. data/lib/vapid/vendor/trix/src/trix/models/composition.coffee +521 -0
  346. data/lib/vapid/vendor/trix/src/trix/models/document.coffee +538 -0
  347. data/lib/vapid/vendor/trix/src/trix/models/editor.coffee +141 -0
  348. data/lib/vapid/vendor/trix/src/trix/models/html_parser.coffee +335 -0
  349. data/lib/vapid/vendor/trix/src/trix/models/line_break_insertion.coffee +31 -0
  350. data/lib/vapid/vendor/trix/src/trix/models/location_mapper.coffee +156 -0
  351. data/lib/vapid/vendor/trix/src/trix/models/managed_attachment.coffee +28 -0
  352. data/lib/vapid/vendor/trix/src/trix/models/piece.coffee +95 -0
  353. data/lib/vapid/vendor/trix/src/trix/models/point_mapper.coffee +28 -0
  354. data/lib/vapid/vendor/trix/src/trix/models/selection_manager.coffee +129 -0
  355. data/lib/vapid/vendor/trix/src/trix/models/splittable_list.coffee +168 -0
  356. data/lib/vapid/vendor/trix/src/trix/models/string_piece.coffee +49 -0
  357. data/lib/vapid/vendor/trix/src/trix/models/text.coffee +168 -0
  358. data/lib/vapid/vendor/trix/src/trix/models/undo_manager.coffee +40 -0
  359. data/lib/vapid/vendor/trix/src/trix/observers/mutation_observer.coffee +131 -0
  360. data/lib/vapid/vendor/trix/src/trix/observers/selection_change_observer.coffee +56 -0
  361. data/lib/vapid/vendor/trix/src/trix/operations/file_verification_operation.coffee +15 -0
  362. data/lib/vapid/vendor/trix/src/trix/operations/image_preload_operation.coffee +15 -0
  363. data/lib/vapid/vendor/trix/src/trix/views/attachment_view.coffee +99 -0
  364. data/lib/vapid/vendor/trix/src/trix/views/block_view.coffee +37 -0
  365. data/lib/vapid/vendor/trix/src/trix/views/document_view.coffee +68 -0
  366. data/lib/vapid/vendor/trix/src/trix/views/object_group_view.coffee +23 -0
  367. data/lib/vapid/vendor/trix/src/trix/views/object_view.coffee +86 -0
  368. data/lib/vapid/vendor/trix/src/trix/views/piece_view.coffee +97 -0
  369. data/lib/vapid/vendor/trix/src/trix/views/previewable_attachment_view.coffee +49 -0
  370. data/lib/vapid/vendor/trix/src/trix/views/text_view.coffee +30 -0
  371. data/lib/vapid/vendor/trix/test/.gitignore +1 -0
  372. data/lib/vapid/vendor/trix/test/src/system/attachment_test.coffee +70 -0
  373. data/lib/vapid/vendor/trix/test/src/system/basic_input_test.coffee +68 -0
  374. data/lib/vapid/vendor/trix/test/src/system/block_formatting_test.coffee +602 -0
  375. data/lib/vapid/vendor/trix/test/src/system/caching_test.coffee +21 -0
  376. data/lib/vapid/vendor/trix/test/src/system/composition_input_test.coffee +138 -0
  377. data/lib/vapid/vendor/trix/test/src/system/cursor_movement_test.coffee +54 -0
  378. data/lib/vapid/vendor/trix/test/src/system/custom_element_test.coffee +314 -0
  379. data/lib/vapid/vendor/trix/test/src/system/html_loading_test.coffee +62 -0
  380. data/lib/vapid/vendor/trix/test/src/system/html_replacement_test.coffee +101 -0
  381. data/lib/vapid/vendor/trix/test/src/system/installation_process_test.coffee +59 -0
  382. data/lib/vapid/vendor/trix/test/src/system/list_formatting_test.coffee +70 -0
  383. data/lib/vapid/vendor/trix/test/src/system/mutation_input_test.coffee +73 -0
  384. data/lib/vapid/vendor/trix/test/src/system/pasting_test.coffee +215 -0
  385. data/lib/vapid/vendor/trix/test/src/system/text_formatting_test.coffee +151 -0
  386. data/lib/vapid/vendor/trix/test/src/system/undo_test.coffee +48 -0
  387. data/lib/vapid/vendor/trix/test/src/test.coffee +16 -0
  388. data/lib/vapid/vendor/trix/test/src/test_helpers/assertions.coffee +38 -0
  389. data/lib/vapid/vendor/trix/test/src/test_helpers/editor_helpers.coffee +37 -0
  390. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_empty.jst.eco +1 -0
  391. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_html.jst.eco +2 -0
  392. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_in_table.jst.eco +7 -0
  393. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_with_block_styles.jst.eco +6 -0
  394. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_with_bold_styles.jst.eco +7 -0
  395. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_with_image.jst.eco +2 -0
  396. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_with_styled_content.jst.eco +7 -0
  397. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_with_toolbar_and_input.jst.eco +5 -0
  398. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/fixtures.coffee +536 -0
  399. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/logo.png +0 -0
  400. data/lib/vapid/vendor/trix/test/src/test_helpers/index.coffee +18 -0
  401. data/lib/vapid/vendor/trix/test/src/test_helpers/input_helpers.coffee +183 -0
  402. data/lib/vapid/vendor/trix/test/src/test_helpers/selection_helpers.coffee +91 -0
  403. data/lib/vapid/vendor/trix/test/src/test_helpers/test_helpers.coffee +72 -0
  404. data/lib/vapid/vendor/trix/test/src/test_helpers/test_stubs.coffee +31 -0
  405. data/lib/vapid/vendor/trix/test/src/test_helpers/toolbar_helpers.coffee +45 -0
  406. data/lib/vapid/vendor/trix/test/src/unit/attachment_test.coffee +23 -0
  407. data/lib/vapid/vendor/trix/test/src/unit/block_test.coffee +22 -0
  408. data/lib/vapid/vendor/trix/test/src/unit/composition_test.coffee +13 -0
  409. data/lib/vapid/vendor/trix/test/src/unit/document_test.coffee +11 -0
  410. data/lib/vapid/vendor/trix/test/src/unit/document_view_test.coffee +6 -0
  411. data/lib/vapid/vendor/trix/test/src/unit/html_parser_test.coffee +162 -0
  412. data/lib/vapid/vendor/trix/test/src/unit/location_mapper_test.coffee +185 -0
  413. data/lib/vapid/vendor/trix/test/src/unit/mutation_observer_test.coffee +84 -0
  414. data/lib/vapid/vendor/trix/test/src/unit/serialization_test.coffee +7 -0
  415. data/lib/vapid/vendor/trix/test/src/unit/string_change_summary_test.coffee +57 -0
  416. data/lib/vapid/vendor/trix/test/src/unit/text_test.coffee +19 -0
  417. data/lib/vapid/vendor/trix/test/vendor/rangy-core.js +3845 -0
  418. data/lib/vapid/vendor/trix/test/vendor/rangy-textrange.js +1930 -0
  419. data/lib/vapid/version.rb +4 -0
  420. data/lib/vapid/views/admin/install.html.erb +19 -0
  421. data/lib/vapid/views/admin/login.html.erb +11 -0
  422. data/lib/vapid/views/admin/overview/index.html.erb +1 -0
  423. data/lib/vapid/views/admin/records/_form.erb +9 -0
  424. data/lib/vapid/views/admin/records/edit.html.erb +5 -0
  425. data/lib/vapid/views/admin/records/index.html.erb +38 -0
  426. data/lib/vapid/views/admin/records/new.html.erb +3 -0
  427. data/lib/vapid/views/errors/404.html.erb +1 -0
  428. data/lib/vapid/views/errors/500.html.erb +1 -0
  429. data/lib/vapid/views/layouts/admin.html.erb +41 -0
  430. data/lib/vapid/views/layouts/admin_form.html.erb +22 -0
  431. data/lib/vapid/views/layouts/error.html.erb +11 -0
  432. data/vapid.gemspec +48 -0
  433. metadata +811 -0
@@ -0,0 +1,3767 @@
1
+ /*!
2
+ * # Semantic UI 2.2.9 - Dropdown
3
+ * http://github.com/semantic-org/semantic-ui/
4
+ *
5
+ *
6
+ * Released under the MIT license
7
+ * http://opensource.org/licenses/MIT
8
+ *
9
+ */
10
+
11
+ ;(function ($, window, document, undefined) {
12
+
13
+ "use strict";
14
+
15
+ window = (typeof window != 'undefined' && window.Math == Math)
16
+ ? window
17
+ : (typeof self != 'undefined' && self.Math == Math)
18
+ ? self
19
+ : Function('return this')()
20
+ ;
21
+
22
+ $.fn.dropdown = function(parameters) {
23
+ var
24
+ $allModules = $(this),
25
+ $document = $(document),
26
+
27
+ moduleSelector = $allModules.selector || '',
28
+
29
+ hasTouch = ('ontouchstart' in document.documentElement),
30
+ time = new Date().getTime(),
31
+ performance = [],
32
+
33
+ query = arguments[0],
34
+ methodInvoked = (typeof query == 'string'),
35
+ queryArguments = [].slice.call(arguments, 1),
36
+ returnedValue
37
+ ;
38
+
39
+ $allModules
40
+ .each(function(elementIndex) {
41
+ var
42
+ settings = ( $.isPlainObject(parameters) )
43
+ ? $.extend(true, {}, $.fn.dropdown.settings, parameters)
44
+ : $.extend({}, $.fn.dropdown.settings),
45
+
46
+ className = settings.className,
47
+ message = settings.message,
48
+ fields = settings.fields,
49
+ keys = settings.keys,
50
+ metadata = settings.metadata,
51
+ namespace = settings.namespace,
52
+ regExp = settings.regExp,
53
+ selector = settings.selector,
54
+ error = settings.error,
55
+ templates = settings.templates,
56
+
57
+ eventNamespace = '.' + namespace,
58
+ moduleNamespace = 'module-' + namespace,
59
+
60
+ $module = $(this),
61
+ $context = $(settings.context),
62
+ $text = $module.find(selector.text),
63
+ $search = $module.find(selector.search),
64
+ $sizer = $module.find(selector.sizer),
65
+ $input = $module.find(selector.input),
66
+ $icon = $module.find(selector.icon),
67
+
68
+ $combo = ($module.prev().find(selector.text).length > 0)
69
+ ? $module.prev().find(selector.text)
70
+ : $module.prev(),
71
+
72
+ $menu = $module.children(selector.menu),
73
+ $item = $menu.find(selector.item),
74
+
75
+ activated = false,
76
+ itemActivated = false,
77
+ internalChange = false,
78
+ element = this,
79
+ instance = $module.data(moduleNamespace),
80
+
81
+ initialLoad,
82
+ pageLostFocus,
83
+ willRefocus,
84
+ elementNamespace,
85
+ id,
86
+ selectObserver,
87
+ menuObserver,
88
+ module
89
+ ;
90
+
91
+ module = {
92
+
93
+ initialize: function() {
94
+ module.debug('Initializing dropdown', settings);
95
+
96
+ if( module.is.alreadySetup() ) {
97
+ module.setup.reference();
98
+ }
99
+ else {
100
+ module.setup.layout();
101
+ module.refreshData();
102
+
103
+ module.save.defaults();
104
+ module.restore.selected();
105
+
106
+ module.create.id();
107
+ module.bind.events();
108
+
109
+ module.observeChanges();
110
+ module.instantiate();
111
+ }
112
+
113
+ },
114
+
115
+ instantiate: function() {
116
+ module.verbose('Storing instance of dropdown', module);
117
+ instance = module;
118
+ $module
119
+ .data(moduleNamespace, module)
120
+ ;
121
+ },
122
+
123
+ destroy: function() {
124
+ module.verbose('Destroying previous dropdown', $module);
125
+ module.remove.tabbable();
126
+ $module
127
+ .off(eventNamespace)
128
+ .removeData(moduleNamespace)
129
+ ;
130
+ $menu
131
+ .off(eventNamespace)
132
+ ;
133
+ $document
134
+ .off(elementNamespace)
135
+ ;
136
+ module.disconnect.menuObserver();
137
+ module.disconnect.selectObserver();
138
+ },
139
+
140
+ observeChanges: function() {
141
+ if('MutationObserver' in window) {
142
+ selectObserver = new MutationObserver(module.event.select.mutation);
143
+ menuObserver = new MutationObserver(module.event.menu.mutation);
144
+ module.debug('Setting up mutation observer', selectObserver, menuObserver);
145
+ module.observe.select();
146
+ module.observe.menu();
147
+ }
148
+ },
149
+
150
+ disconnect: {
151
+ menuObserver: function() {
152
+ if(menuObserver) {
153
+ menuObserver.disconnect();
154
+ }
155
+ },
156
+ selectObserver: function() {
157
+ if(selectObserver) {
158
+ selectObserver.disconnect();
159
+ }
160
+ }
161
+ },
162
+ observe: {
163
+ select: function() {
164
+ if(module.has.input()) {
165
+ selectObserver.observe($input[0], {
166
+ childList : true,
167
+ subtree : true
168
+ });
169
+ }
170
+ },
171
+ menu: function() {
172
+ if(module.has.menu()) {
173
+ menuObserver.observe($menu[0], {
174
+ childList : true,
175
+ subtree : true
176
+ });
177
+ }
178
+ }
179
+ },
180
+
181
+ create: {
182
+ id: function() {
183
+ id = (Math.random().toString(16) + '000000000').substr(2, 8);
184
+ elementNamespace = '.' + id;
185
+ module.verbose('Creating unique id for element', id);
186
+ },
187
+ userChoice: function(values) {
188
+ var
189
+ $userChoices,
190
+ $userChoice,
191
+ isUserValue,
192
+ html
193
+ ;
194
+ values = values || module.get.userValues();
195
+ if(!values) {
196
+ return false;
197
+ }
198
+ values = $.isArray(values)
199
+ ? values
200
+ : [values]
201
+ ;
202
+ $.each(values, function(index, value) {
203
+ if(module.get.item(value) === false) {
204
+ html = settings.templates.addition( module.add.variables(message.addResult, value) );
205
+ $userChoice = $('<div />')
206
+ .html(html)
207
+ .attr('data-' + metadata.value, value)
208
+ .attr('data-' + metadata.text, value)
209
+ .addClass(className.addition)
210
+ .addClass(className.item)
211
+ ;
212
+ if(settings.hideAdditions) {
213
+ $userChoice.addClass(className.hidden);
214
+ }
215
+ $userChoices = ($userChoices === undefined)
216
+ ? $userChoice
217
+ : $userChoices.add($userChoice)
218
+ ;
219
+ module.verbose('Creating user choices for value', value, $userChoice);
220
+ }
221
+ });
222
+ return $userChoices;
223
+ },
224
+ userLabels: function(value) {
225
+ var
226
+ userValues = module.get.userValues()
227
+ ;
228
+ if(userValues) {
229
+ module.debug('Adding user labels', userValues);
230
+ $.each(userValues, function(index, value) {
231
+ module.verbose('Adding custom user value');
232
+ module.add.label(value, value);
233
+ });
234
+ }
235
+ },
236
+ menu: function() {
237
+ $menu = $('<div />')
238
+ .addClass(className.menu)
239
+ .appendTo($module)
240
+ ;
241
+ },
242
+ sizer: function() {
243
+ $sizer = $('<span />')
244
+ .addClass(className.sizer)
245
+ .insertAfter($search)
246
+ ;
247
+ }
248
+ },
249
+
250
+ search: function(query) {
251
+ query = (query !== undefined)
252
+ ? query
253
+ : module.get.query()
254
+ ;
255
+ module.verbose('Searching for query', query);
256
+ if(module.has.minCharacters(query)) {
257
+ module.filter(query);
258
+ }
259
+ else {
260
+ module.hide();
261
+ }
262
+ },
263
+
264
+ select: {
265
+ firstUnfiltered: function() {
266
+ module.verbose('Selecting first non-filtered element');
267
+ module.remove.selectedItem();
268
+ $item
269
+ .not(selector.unselectable)
270
+ .not(selector.addition + selector.hidden)
271
+ .eq(0)
272
+ .addClass(className.selected)
273
+ ;
274
+ },
275
+ nextAvailable: function($selected) {
276
+ $selected = $selected.eq(0);
277
+ var
278
+ $nextAvailable = $selected.nextAll(selector.item).not(selector.unselectable).eq(0),
279
+ $prevAvailable = $selected.prevAll(selector.item).not(selector.unselectable).eq(0),
280
+ hasNext = ($nextAvailable.length > 0)
281
+ ;
282
+ if(hasNext) {
283
+ module.verbose('Moving selection to', $nextAvailable);
284
+ $nextAvailable.addClass(className.selected);
285
+ }
286
+ else {
287
+ module.verbose('Moving selection to', $prevAvailable);
288
+ $prevAvailable.addClass(className.selected);
289
+ }
290
+ }
291
+ },
292
+
293
+ setup: {
294
+ api: function() {
295
+ var
296
+ apiSettings = {
297
+ debug : settings.debug,
298
+ urlData : {
299
+ value : module.get.value(),
300
+ query : module.get.query()
301
+ },
302
+ on : false
303
+ }
304
+ ;
305
+ module.verbose('First request, initializing API');
306
+ $module
307
+ .api(apiSettings)
308
+ ;
309
+ },
310
+ layout: function() {
311
+ if( $module.is('select') ) {
312
+ module.setup.select();
313
+ module.setup.returnedObject();
314
+ }
315
+ if( !module.has.menu() ) {
316
+ module.create.menu();
317
+ }
318
+ if( module.is.search() && !module.has.search() ) {
319
+ module.verbose('Adding search input');
320
+ $search = $('<input />')
321
+ .addClass(className.search)
322
+ .prop('autocomplete', 'off')
323
+ .insertBefore($text)
324
+ ;
325
+ }
326
+ if( module.is.multiple() && module.is.searchSelection() && !module.has.sizer()) {
327
+ module.create.sizer();
328
+ }
329
+ if(settings.allowTab) {
330
+ module.set.tabbable();
331
+ }
332
+ },
333
+ select: function() {
334
+ var
335
+ selectValues = module.get.selectValues()
336
+ ;
337
+ module.debug('Dropdown initialized on a select', selectValues);
338
+ if( $module.is('select') ) {
339
+ $input = $module;
340
+ }
341
+ // see if select is placed correctly already
342
+ if($input.parent(selector.dropdown).length > 0) {
343
+ module.debug('UI dropdown already exists. Creating dropdown menu only');
344
+ $module = $input.closest(selector.dropdown);
345
+ if( !module.has.menu() ) {
346
+ module.create.menu();
347
+ }
348
+ $menu = $module.children(selector.menu);
349
+ module.setup.menu(selectValues);
350
+ }
351
+ else {
352
+ module.debug('Creating entire dropdown from select');
353
+ $module = $('<div />')
354
+ .attr('class', $input.attr('class') )
355
+ .addClass(className.selection)
356
+ .addClass(className.dropdown)
357
+ .html( templates.dropdown(selectValues) )
358
+ .insertBefore($input)
359
+ ;
360
+ if($input.hasClass(className.multiple) && $input.prop('multiple') === false) {
361
+ module.error(error.missingMultiple);
362
+ $input.prop('multiple', true);
363
+ }
364
+ if($input.is('[multiple]')) {
365
+ module.set.multiple();
366
+ }
367
+ if ($input.prop('disabled')) {
368
+ module.debug('Disabling dropdown');
369
+ $module.addClass(className.disabled);
370
+ }
371
+ $input
372
+ .removeAttr('class')
373
+ .detach()
374
+ .prependTo($module)
375
+ ;
376
+ }
377
+ module.refresh();
378
+ },
379
+ menu: function(values) {
380
+ $menu.html( templates.menu(values, fields));
381
+ $item = $menu.find(selector.item);
382
+ },
383
+ reference: function() {
384
+ module.debug('Dropdown behavior was called on select, replacing with closest dropdown');
385
+ // replace module reference
386
+ $module = $module.parent(selector.dropdown);
387
+ module.refresh();
388
+ module.setup.returnedObject();
389
+ // invoke method in context of current instance
390
+ if(methodInvoked) {
391
+ instance = module;
392
+ module.invoke(query);
393
+ }
394
+ },
395
+ returnedObject: function() {
396
+ var
397
+ $firstModules = $allModules.slice(0, elementIndex),
398
+ $lastModules = $allModules.slice(elementIndex + 1)
399
+ ;
400
+ // adjust all modules to use correct reference
401
+ $allModules = $firstModules.add($module).add($lastModules);
402
+ }
403
+ },
404
+
405
+ refresh: function() {
406
+ module.refreshSelectors();
407
+ module.refreshData();
408
+ },
409
+
410
+ refreshItems: function() {
411
+ $item = $menu.find(selector.item);
412
+ },
413
+
414
+ refreshSelectors: function() {
415
+ module.verbose('Refreshing selector cache');
416
+ $text = $module.find(selector.text);
417
+ $search = $module.find(selector.search);
418
+ $input = $module.find(selector.input);
419
+ $icon = $module.find(selector.icon);
420
+ $combo = ($module.prev().find(selector.text).length > 0)
421
+ ? $module.prev().find(selector.text)
422
+ : $module.prev()
423
+ ;
424
+ $menu = $module.children(selector.menu);
425
+ $item = $menu.find(selector.item);
426
+ },
427
+
428
+ refreshData: function() {
429
+ module.verbose('Refreshing cached metadata');
430
+ $item
431
+ .removeData(metadata.text)
432
+ .removeData(metadata.value)
433
+ ;
434
+ },
435
+
436
+ clearData: function() {
437
+ module.verbose('Clearing metadata');
438
+ $item
439
+ .removeData(metadata.text)
440
+ .removeData(metadata.value)
441
+ ;
442
+ $module
443
+ .removeData(metadata.defaultText)
444
+ .removeData(metadata.defaultValue)
445
+ .removeData(metadata.placeholderText)
446
+ ;
447
+ },
448
+
449
+ toggle: function() {
450
+ module.verbose('Toggling menu visibility');
451
+ if( !module.is.active() ) {
452
+ module.show();
453
+ }
454
+ else {
455
+ module.hide();
456
+ }
457
+ },
458
+
459
+ show: function(callback) {
460
+ callback = $.isFunction(callback)
461
+ ? callback
462
+ : function(){}
463
+ ;
464
+ if(!module.can.show() && module.is.remote()) {
465
+ module.debug('No API results retrieved, searching before show');
466
+ module.queryRemote(module.get.query(), module.show);
467
+ }
468
+ if( module.can.show() && !module.is.active() ) {
469
+ module.debug('Showing dropdown');
470
+ if(module.has.message() && !(module.has.maxSelections() || module.has.allResultsFiltered()) ) {
471
+ module.remove.message();
472
+ }
473
+ if(module.is.allFiltered()) {
474
+ return true;
475
+ }
476
+ if(settings.onShow.call(element) !== false) {
477
+ module.animate.show(function() {
478
+ if( module.can.click() ) {
479
+ module.bind.intent();
480
+ }
481
+ if(module.has.menuSearch()) {
482
+ module.focusSearch();
483
+ }
484
+ module.set.visible();
485
+ callback.call(element);
486
+ });
487
+ }
488
+ }
489
+ },
490
+
491
+ hide: function(callback) {
492
+ callback = $.isFunction(callback)
493
+ ? callback
494
+ : function(){}
495
+ ;
496
+ if( module.is.active() ) {
497
+ module.debug('Hiding dropdown');
498
+ if(settings.onHide.call(element) !== false) {
499
+ module.animate.hide(function() {
500
+ module.remove.visible();
501
+ callback.call(element);
502
+ });
503
+ }
504
+ }
505
+ },
506
+
507
+ hideOthers: function() {
508
+ module.verbose('Finding other dropdowns to hide');
509
+ $allModules
510
+ .not($module)
511
+ .has(selector.menu + '.' + className.visible)
512
+ .dropdown('hide')
513
+ ;
514
+ },
515
+
516
+ hideMenu: function() {
517
+ module.verbose('Hiding menu instantaneously');
518
+ module.remove.active();
519
+ module.remove.visible();
520
+ $menu.transition('hide');
521
+ },
522
+
523
+ hideSubMenus: function() {
524
+ var
525
+ $subMenus = $menu.children(selector.item).find(selector.menu)
526
+ ;
527
+ module.verbose('Hiding sub menus', $subMenus);
528
+ $subMenus.transition('hide');
529
+ },
530
+
531
+ bind: {
532
+ events: function() {
533
+ if(hasTouch) {
534
+ module.bind.touchEvents();
535
+ }
536
+ module.bind.keyboardEvents();
537
+ module.bind.inputEvents();
538
+ module.bind.mouseEvents();
539
+ },
540
+ touchEvents: function() {
541
+ module.debug('Touch device detected binding additional touch events');
542
+ if( module.is.searchSelection() ) {
543
+ // do nothing special yet
544
+ }
545
+ else if( module.is.single() ) {
546
+ $module
547
+ .on('touchstart' + eventNamespace, module.event.test.toggle)
548
+ ;
549
+ }
550
+ $menu
551
+ .on('touchstart' + eventNamespace, selector.item, module.event.item.mouseenter)
552
+ ;
553
+ },
554
+ keyboardEvents: function() {
555
+ module.verbose('Binding keyboard events');
556
+ $module
557
+ .on('keydown' + eventNamespace, module.event.keydown)
558
+ ;
559
+ if( module.has.search() ) {
560
+ $module
561
+ .on(module.get.inputEvent() + eventNamespace, selector.search, module.event.input)
562
+ ;
563
+ }
564
+ if( module.is.multiple() ) {
565
+ $document
566
+ .on('keydown' + elementNamespace, module.event.document.keydown)
567
+ ;
568
+ }
569
+ },
570
+ inputEvents: function() {
571
+ module.verbose('Binding input change events');
572
+ $module
573
+ .on('change' + eventNamespace, selector.input, module.event.change)
574
+ ;
575
+ },
576
+ mouseEvents: function() {
577
+ module.verbose('Binding mouse events');
578
+ if(module.is.multiple()) {
579
+ $module
580
+ .on('click' + eventNamespace, selector.label, module.event.label.click)
581
+ .on('click' + eventNamespace, selector.remove, module.event.remove.click)
582
+ ;
583
+ }
584
+ if( module.is.searchSelection() ) {
585
+ $module
586
+ .on('mousedown' + eventNamespace, module.event.mousedown)
587
+ .on('mouseup' + eventNamespace, module.event.mouseup)
588
+ .on('mousedown' + eventNamespace, selector.menu, module.event.menu.mousedown)
589
+ .on('mouseup' + eventNamespace, selector.menu, module.event.menu.mouseup)
590
+ .on('click' + eventNamespace, selector.icon, module.event.icon.click)
591
+ .on('focus' + eventNamespace, selector.search, module.event.search.focus)
592
+ .on('click' + eventNamespace, selector.search, module.event.search.focus)
593
+ .on('blur' + eventNamespace, selector.search, module.event.search.blur)
594
+ .on('click' + eventNamespace, selector.text, module.event.text.focus)
595
+ ;
596
+ if(module.is.multiple()) {
597
+ $module
598
+ .on('click' + eventNamespace, module.event.click)
599
+ ;
600
+ }
601
+ }
602
+ else {
603
+ if(settings.on == 'click') {
604
+ $module
605
+ .on('click' + eventNamespace, selector.icon, module.event.icon.click)
606
+ .on('click' + eventNamespace, module.event.test.toggle)
607
+ ;
608
+ }
609
+ else if(settings.on == 'hover') {
610
+ $module
611
+ .on('mouseenter' + eventNamespace, module.delay.show)
612
+ .on('mouseleave' + eventNamespace, module.delay.hide)
613
+ ;
614
+ }
615
+ else {
616
+ $module
617
+ .on(settings.on + eventNamespace, module.toggle)
618
+ ;
619
+ }
620
+ $module
621
+ .on('mousedown' + eventNamespace, module.event.mousedown)
622
+ .on('mouseup' + eventNamespace, module.event.mouseup)
623
+ .on('focus' + eventNamespace, module.event.focus)
624
+ ;
625
+ if(module.has.menuSearch() ) {
626
+ $module
627
+ .on('blur' + eventNamespace, selector.search, module.event.search.blur)
628
+ ;
629
+ }
630
+ else {
631
+ $module
632
+ .on('blur' + eventNamespace, module.event.blur)
633
+ ;
634
+ }
635
+ }
636
+ $menu
637
+ .on('mouseenter' + eventNamespace, selector.item, module.event.item.mouseenter)
638
+ .on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave)
639
+ .on('click' + eventNamespace, selector.item, module.event.item.click)
640
+ ;
641
+ },
642
+ intent: function() {
643
+ module.verbose('Binding hide intent event to document');
644
+ if(hasTouch) {
645
+ $document
646
+ .on('touchstart' + elementNamespace, module.event.test.touch)
647
+ .on('touchmove' + elementNamespace, module.event.test.touch)
648
+ ;
649
+ }
650
+ $document
651
+ .on('click' + elementNamespace, module.event.test.hide)
652
+ ;
653
+ }
654
+ },
655
+
656
+ unbind: {
657
+ intent: function() {
658
+ module.verbose('Removing hide intent event from document');
659
+ if(hasTouch) {
660
+ $document
661
+ .off('touchstart' + elementNamespace)
662
+ .off('touchmove' + elementNamespace)
663
+ ;
664
+ }
665
+ $document
666
+ .off('click' + elementNamespace)
667
+ ;
668
+ }
669
+ },
670
+
671
+ filter: function(query) {
672
+ var
673
+ searchTerm = (query !== undefined)
674
+ ? query
675
+ : module.get.query(),
676
+ afterFiltered = function() {
677
+ if(module.is.multiple()) {
678
+ module.filterActive();
679
+ }
680
+ module.select.firstUnfiltered();
681
+ if( module.has.allResultsFiltered() ) {
682
+ if( settings.onNoResults.call(element, searchTerm) ) {
683
+ if(settings.allowAdditions) {
684
+ if(settings.hideAdditions) {
685
+ module.verbose('User addition with no menu, setting empty style');
686
+ module.set.empty();
687
+ module.hideMenu();
688
+ }
689
+ }
690
+ else {
691
+ module.verbose('All items filtered, showing message', searchTerm);
692
+ module.add.message(message.noResults);
693
+ }
694
+ }
695
+ else {
696
+ module.verbose('All items filtered, hiding dropdown', searchTerm);
697
+ module.hideMenu();
698
+ }
699
+ }
700
+ else {
701
+ module.remove.empty();
702
+ module.remove.message();
703
+ }
704
+ if(settings.allowAdditions) {
705
+ module.add.userSuggestion(query);
706
+ }
707
+ if(module.is.searchSelection() && module.can.show() && module.is.focusedOnSearch() ) {
708
+ module.show();
709
+ }
710
+ }
711
+ ;
712
+ if(settings.useLabels && module.has.maxSelections()) {
713
+ return;
714
+ }
715
+ if(settings.apiSettings) {
716
+ if( module.can.useAPI() ) {
717
+ module.queryRemote(searchTerm, function() {
718
+ if(settings.filterRemoteData) {
719
+ module.filterItems(searchTerm);
720
+ }
721
+ afterFiltered();
722
+ });
723
+ }
724
+ else {
725
+ module.error(error.noAPI);
726
+ }
727
+ }
728
+ else {
729
+ module.filterItems(searchTerm);
730
+ afterFiltered();
731
+ }
732
+ },
733
+
734
+ queryRemote: function(query, callback) {
735
+ var
736
+ apiSettings = {
737
+ errorDuration : false,
738
+ cache : 'local',
739
+ throttle : settings.throttle,
740
+ urlData : {
741
+ query: query
742
+ },
743
+ onError: function() {
744
+ module.add.message(message.serverError);
745
+ callback();
746
+ },
747
+ onFailure: function() {
748
+ module.add.message(message.serverError);
749
+ callback();
750
+ },
751
+ onSuccess : function(response) {
752
+ module.remove.message();
753
+ module.setup.menu({
754
+ values: response[fields.remoteValues]
755
+ });
756
+ callback();
757
+ }
758
+ }
759
+ ;
760
+ if( !$module.api('get request') ) {
761
+ module.setup.api();
762
+ }
763
+ apiSettings = $.extend(true, {}, apiSettings, settings.apiSettings);
764
+ $module
765
+ .api('setting', apiSettings)
766
+ .api('query')
767
+ ;
768
+ },
769
+
770
+ filterItems: function(query) {
771
+ var
772
+ searchTerm = (query !== undefined)
773
+ ? query
774
+ : module.get.query(),
775
+ results = null,
776
+ escapedTerm = module.escape.string(searchTerm),
777
+ beginsWithRegExp = new RegExp('^' + escapedTerm, 'igm')
778
+ ;
779
+ // avoid loop if we're matching nothing
780
+ if( module.has.query() ) {
781
+ results = [];
782
+
783
+ module.verbose('Searching for matching values', searchTerm);
784
+ $item
785
+ .each(function(){
786
+ var
787
+ $choice = $(this),
788
+ text,
789
+ value
790
+ ;
791
+ if(settings.match == 'both' || settings.match == 'text') {
792
+ text = String(module.get.choiceText($choice, false));
793
+ if(text.search(beginsWithRegExp) !== -1) {
794
+ results.push(this);
795
+ return true;
796
+ }
797
+ else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text)) {
798
+ results.push(this);
799
+ return true;
800
+ }
801
+ else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, text)) {
802
+ results.push(this);
803
+ return true;
804
+ }
805
+ }
806
+ if(settings.match == 'both' || settings.match == 'value') {
807
+ value = String(module.get.choiceValue($choice, text));
808
+ if(value.search(beginsWithRegExp) !== -1) {
809
+ results.push(this);
810
+ return true;
811
+ }
812
+ else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, value)) {
813
+ results.push(this);
814
+ return true;
815
+ }
816
+ else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, value)) {
817
+ results.push(this);
818
+ return true;
819
+ }
820
+ }
821
+ })
822
+ ;
823
+ }
824
+ module.debug('Showing only matched items', searchTerm);
825
+ module.remove.filteredItem();
826
+ if(results) {
827
+ $item
828
+ .not(results)
829
+ .addClass(className.filtered)
830
+ ;
831
+ }
832
+ },
833
+
834
+ fuzzySearch: function(query, term) {
835
+ var
836
+ termLength = term.length,
837
+ queryLength = query.length
838
+ ;
839
+ query = query.toLowerCase();
840
+ term = term.toLowerCase();
841
+ if(queryLength > termLength) {
842
+ return false;
843
+ }
844
+ if(queryLength === termLength) {
845
+ return (query === term);
846
+ }
847
+ search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
848
+ var
849
+ queryCharacter = query.charCodeAt(characterIndex)
850
+ ;
851
+ while(nextCharacterIndex < termLength) {
852
+ if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
853
+ continue search;
854
+ }
855
+ }
856
+ return false;
857
+ }
858
+ return true;
859
+ },
860
+ exactSearch: function (query, term) {
861
+ query = query.toLowerCase();
862
+ term = term.toLowerCase();
863
+ if(term.indexOf(query) > -1) {
864
+ return true;
865
+ }
866
+ return false;
867
+ },
868
+ filterActive: function() {
869
+ if(settings.useLabels) {
870
+ $item.filter('.' + className.active)
871
+ .addClass(className.filtered)
872
+ ;
873
+ }
874
+ },
875
+
876
+ focusSearch: function(skipHandler) {
877
+ if( module.has.search() && !module.is.focusedOnSearch() ) {
878
+ if(skipHandler) {
879
+ $module.off('focus' + eventNamespace, selector.search);
880
+ $search.focus();
881
+ $module.on('focus' + eventNamespace, selector.search, module.event.search.focus);
882
+ }
883
+ else {
884
+ $search.focus();
885
+ }
886
+ }
887
+ },
888
+
889
+ forceSelection: function() {
890
+ var
891
+ $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
892
+ $activeItem = $item.not(className.filtered).filter('.' + className.active).eq(0),
893
+ $selectedItem = ($currentlySelected.length > 0)
894
+ ? $currentlySelected
895
+ : $activeItem,
896
+ hasSelected = ($selectedItem.length > 0)
897
+ ;
898
+ if(hasSelected && !module.is.multiple()) {
899
+ module.debug('Forcing partial selection to selected item', $selectedItem);
900
+ module.event.item.click.call($selectedItem, {}, true);
901
+ return;
902
+ }
903
+ else {
904
+ if(settings.allowAdditions) {
905
+ module.set.selected(module.get.query());
906
+ module.remove.searchTerm();
907
+ }
908
+ else {
909
+ module.remove.searchTerm();
910
+ }
911
+ }
912
+ },
913
+
914
+ event: {
915
+ change: function() {
916
+ if(!internalChange) {
917
+ module.debug('Input changed, updating selection');
918
+ module.set.selected();
919
+ }
920
+ },
921
+ focus: function() {
922
+ if(settings.showOnFocus && !activated && module.is.hidden() && !pageLostFocus) {
923
+ module.show();
924
+ }
925
+ },
926
+ blur: function(event) {
927
+ pageLostFocus = (document.activeElement === this);
928
+ if(!activated && !pageLostFocus) {
929
+ module.remove.activeLabel();
930
+ module.hide();
931
+ }
932
+ },
933
+ mousedown: function() {
934
+ if(module.is.searchSelection()) {
935
+ // prevent menu hiding on immediate re-focus
936
+ willRefocus = true;
937
+ }
938
+ else {
939
+ // prevents focus callback from occurring on mousedown
940
+ activated = true;
941
+ }
942
+ },
943
+ mouseup: function() {
944
+ if(module.is.searchSelection()) {
945
+ // prevent menu hiding on immediate re-focus
946
+ willRefocus = false;
947
+ }
948
+ else {
949
+ activated = false;
950
+ }
951
+ },
952
+ click: function(event) {
953
+ var
954
+ $target = $(event.target)
955
+ ;
956
+ // focus search
957
+ if($target.is($module)) {
958
+ if(!module.is.focusedOnSearch()) {
959
+ module.focusSearch();
960
+ }
961
+ else {
962
+ module.show();
963
+ }
964
+ }
965
+ },
966
+ search: {
967
+ focus: function() {
968
+ activated = true;
969
+ if(module.is.multiple()) {
970
+ module.remove.activeLabel();
971
+ }
972
+ if(settings.showOnFocus) {
973
+ module.search();
974
+ }
975
+ },
976
+ blur: function(event) {
977
+ pageLostFocus = (document.activeElement === this);
978
+ if(!willRefocus) {
979
+ if(!itemActivated && !pageLostFocus) {
980
+ if(settings.forceSelection) {
981
+ module.forceSelection();
982
+ }
983
+ module.hide();
984
+ }
985
+ }
986
+ willRefocus = false;
987
+ }
988
+ },
989
+ icon: {
990
+ click: function(event) {
991
+ module.toggle();
992
+ }
993
+ },
994
+ text: {
995
+ focus: function(event) {
996
+ activated = true;
997
+ module.focusSearch();
998
+ }
999
+ },
1000
+ input: function(event) {
1001
+ if(module.is.multiple() || module.is.searchSelection()) {
1002
+ module.set.filtered();
1003
+ }
1004
+ clearTimeout(module.timer);
1005
+ module.timer = setTimeout(module.search, settings.delay.search);
1006
+ },
1007
+ label: {
1008
+ click: function(event) {
1009
+ var
1010
+ $label = $(this),
1011
+ $labels = $module.find(selector.label),
1012
+ $activeLabels = $labels.filter('.' + className.active),
1013
+ $nextActive = $label.nextAll('.' + className.active),
1014
+ $prevActive = $label.prevAll('.' + className.active),
1015
+ $range = ($nextActive.length > 0)
1016
+ ? $label.nextUntil($nextActive).add($activeLabels).add($label)
1017
+ : $label.prevUntil($prevActive).add($activeLabels).add($label)
1018
+ ;
1019
+ if(event.shiftKey) {
1020
+ $activeLabels.removeClass(className.active);
1021
+ $range.addClass(className.active);
1022
+ }
1023
+ else if(event.ctrlKey) {
1024
+ $label.toggleClass(className.active);
1025
+ }
1026
+ else {
1027
+ $activeLabels.removeClass(className.active);
1028
+ $label.addClass(className.active);
1029
+ }
1030
+ settings.onLabelSelect.apply(this, $labels.filter('.' + className.active));
1031
+ }
1032
+ },
1033
+ remove: {
1034
+ click: function() {
1035
+ var
1036
+ $label = $(this).parent()
1037
+ ;
1038
+ if( $label.hasClass(className.active) ) {
1039
+ // remove all selected labels
1040
+ module.remove.activeLabels();
1041
+ }
1042
+ else {
1043
+ // remove this label only
1044
+ module.remove.activeLabels( $label );
1045
+ }
1046
+ }
1047
+ },
1048
+ test: {
1049
+ toggle: function(event) {
1050
+ var
1051
+ toggleBehavior = (module.is.multiple())
1052
+ ? module.show
1053
+ : module.toggle
1054
+ ;
1055
+ if(module.is.bubbledLabelClick(event) || module.is.bubbledIconClick(event)) {
1056
+ return;
1057
+ }
1058
+ if( module.determine.eventOnElement(event, toggleBehavior) ) {
1059
+ event.preventDefault();
1060
+ }
1061
+ },
1062
+ touch: function(event) {
1063
+ module.determine.eventOnElement(event, function() {
1064
+ if(event.type == 'touchstart') {
1065
+ module.timer = setTimeout(function() {
1066
+ module.hide();
1067
+ }, settings.delay.touch);
1068
+ }
1069
+ else if(event.type == 'touchmove') {
1070
+ clearTimeout(module.timer);
1071
+ }
1072
+ });
1073
+ event.stopPropagation();
1074
+ },
1075
+ hide: function(event) {
1076
+ module.determine.eventInModule(event, module.hide);
1077
+ }
1078
+ },
1079
+ select: {
1080
+ mutation: function(mutations) {
1081
+ module.debug('<select> modified, recreating menu');
1082
+ module.setup.select();
1083
+ }
1084
+ },
1085
+ menu: {
1086
+ mutation: function(mutations) {
1087
+ var
1088
+ mutation = mutations[0],
1089
+ $addedNode = mutation.addedNodes
1090
+ ? $(mutation.addedNodes[0])
1091
+ : $(false),
1092
+ $removedNode = mutation.removedNodes
1093
+ ? $(mutation.removedNodes[0])
1094
+ : $(false),
1095
+ $changedNodes = $addedNode.add($removedNode),
1096
+ isUserAddition = $changedNodes.is(selector.addition) || $changedNodes.closest(selector.addition).length > 0,
1097
+ isMessage = $changedNodes.is(selector.message) || $changedNodes.closest(selector.message).length > 0
1098
+ ;
1099
+ if(isUserAddition || isMessage) {
1100
+ module.debug('Updating item selector cache');
1101
+ module.refreshItems();
1102
+ }
1103
+ else {
1104
+ module.debug('Menu modified, updating selector cache');
1105
+ module.refresh();
1106
+ }
1107
+ },
1108
+ mousedown: function() {
1109
+ itemActivated = true;
1110
+ },
1111
+ mouseup: function() {
1112
+ itemActivated = false;
1113
+ }
1114
+ },
1115
+ item: {
1116
+ mouseenter: function(event) {
1117
+ var
1118
+ $target = $(event.target),
1119
+ $item = $(this),
1120
+ $subMenu = $item.children(selector.menu),
1121
+ $otherMenus = $item.siblings(selector.item).children(selector.menu),
1122
+ hasSubMenu = ($subMenu.length > 0),
1123
+ isBubbledEvent = ($subMenu.find($target).length > 0)
1124
+ ;
1125
+ if( !isBubbledEvent && hasSubMenu ) {
1126
+ clearTimeout(module.itemTimer);
1127
+ module.itemTimer = setTimeout(function() {
1128
+ module.verbose('Showing sub-menu', $subMenu);
1129
+ $.each($otherMenus, function() {
1130
+ module.animate.hide(false, $(this));
1131
+ });
1132
+ module.animate.show(false, $subMenu);
1133
+ }, settings.delay.show);
1134
+ event.preventDefault();
1135
+ }
1136
+ },
1137
+ mouseleave: function(event) {
1138
+ var
1139
+ $subMenu = $(this).children(selector.menu)
1140
+ ;
1141
+ if($subMenu.length > 0) {
1142
+ clearTimeout(module.itemTimer);
1143
+ module.itemTimer = setTimeout(function() {
1144
+ module.verbose('Hiding sub-menu', $subMenu);
1145
+ module.animate.hide(false, $subMenu);
1146
+ }, settings.delay.hide);
1147
+ }
1148
+ },
1149
+ click: function (event, skipRefocus) {
1150
+ var
1151
+ $choice = $(this),
1152
+ $target = (event)
1153
+ ? $(event.target)
1154
+ : $(''),
1155
+ $subMenu = $choice.find(selector.menu),
1156
+ text = module.get.choiceText($choice),
1157
+ value = module.get.choiceValue($choice, text),
1158
+ hasSubMenu = ($subMenu.length > 0),
1159
+ isBubbledEvent = ($subMenu.find($target).length > 0)
1160
+ ;
1161
+ if(!isBubbledEvent && (!hasSubMenu || settings.allowCategorySelection)) {
1162
+ if(module.is.searchSelection()) {
1163
+ if(settings.allowAdditions) {
1164
+ module.remove.userAddition();
1165
+ }
1166
+ module.remove.searchTerm();
1167
+ if(!module.is.focusedOnSearch() && !(skipRefocus == true)) {
1168
+ module.focusSearch(true);
1169
+ }
1170
+ }
1171
+ if(!settings.useLabels) {
1172
+ module.remove.filteredItem();
1173
+ module.set.scrollPosition($choice);
1174
+ }
1175
+ module.determine.selectAction.call(this, text, value);
1176
+ }
1177
+ }
1178
+ },
1179
+
1180
+ document: {
1181
+ // label selection should occur even when element has no focus
1182
+ keydown: function(event) {
1183
+ var
1184
+ pressedKey = event.which,
1185
+ isShortcutKey = module.is.inObject(pressedKey, keys)
1186
+ ;
1187
+ if(isShortcutKey) {
1188
+ var
1189
+ $label = $module.find(selector.label),
1190
+ $activeLabel = $label.filter('.' + className.active),
1191
+ activeValue = $activeLabel.data(metadata.value),
1192
+ labelIndex = $label.index($activeLabel),
1193
+ labelCount = $label.length,
1194
+ hasActiveLabel = ($activeLabel.length > 0),
1195
+ hasMultipleActive = ($activeLabel.length > 1),
1196
+ isFirstLabel = (labelIndex === 0),
1197
+ isLastLabel = (labelIndex + 1 == labelCount),
1198
+ isSearch = module.is.searchSelection(),
1199
+ isFocusedOnSearch = module.is.focusedOnSearch(),
1200
+ isFocused = module.is.focused(),
1201
+ caretAtStart = (isFocusedOnSearch && module.get.caretPosition() === 0),
1202
+ $nextLabel
1203
+ ;
1204
+ if(isSearch && !hasActiveLabel && !isFocusedOnSearch) {
1205
+ return;
1206
+ }
1207
+
1208
+ if(pressedKey == keys.leftArrow) {
1209
+ // activate previous label
1210
+ if((isFocused || caretAtStart) && !hasActiveLabel) {
1211
+ module.verbose('Selecting previous label');
1212
+ $label.last().addClass(className.active);
1213
+ }
1214
+ else if(hasActiveLabel) {
1215
+ if(!event.shiftKey) {
1216
+ module.verbose('Selecting previous label');
1217
+ $label.removeClass(className.active);
1218
+ }
1219
+ else {
1220
+ module.verbose('Adding previous label to selection');
1221
+ }
1222
+ if(isFirstLabel && !hasMultipleActive) {
1223
+ $activeLabel.addClass(className.active);
1224
+ }
1225
+ else {
1226
+ $activeLabel.prev(selector.siblingLabel)
1227
+ .addClass(className.active)
1228
+ .end()
1229
+ ;
1230
+ }
1231
+ event.preventDefault();
1232
+ }
1233
+ }
1234
+ else if(pressedKey == keys.rightArrow) {
1235
+ // activate first label
1236
+ if(isFocused && !hasActiveLabel) {
1237
+ $label.first().addClass(className.active);
1238
+ }
1239
+ // activate next label
1240
+ if(hasActiveLabel) {
1241
+ if(!event.shiftKey) {
1242
+ module.verbose('Selecting next label');
1243
+ $label.removeClass(className.active);
1244
+ }
1245
+ else {
1246
+ module.verbose('Adding next label to selection');
1247
+ }
1248
+ if(isLastLabel) {
1249
+ if(isSearch) {
1250
+ if(!isFocusedOnSearch) {
1251
+ module.focusSearch();
1252
+ }
1253
+ else {
1254
+ $label.removeClass(className.active);
1255
+ }
1256
+ }
1257
+ else if(hasMultipleActive) {
1258
+ $activeLabel.next(selector.siblingLabel).addClass(className.active);
1259
+ }
1260
+ else {
1261
+ $activeLabel.addClass(className.active);
1262
+ }
1263
+ }
1264
+ else {
1265
+ $activeLabel.next(selector.siblingLabel).addClass(className.active);
1266
+ }
1267
+ event.preventDefault();
1268
+ }
1269
+ }
1270
+ else if(pressedKey == keys.deleteKey || pressedKey == keys.backspace) {
1271
+ if(hasActiveLabel) {
1272
+ module.verbose('Removing active labels');
1273
+ if(isLastLabel) {
1274
+ if(isSearch && !isFocusedOnSearch) {
1275
+ module.focusSearch();
1276
+ }
1277
+ }
1278
+ $activeLabel.last().next(selector.siblingLabel).addClass(className.active);
1279
+ module.remove.activeLabels($activeLabel);
1280
+ event.preventDefault();
1281
+ }
1282
+ else if(caretAtStart && !hasActiveLabel && pressedKey == keys.backspace) {
1283
+ module.verbose('Removing last label on input backspace');
1284
+ $activeLabel = $label.last().addClass(className.active);
1285
+ module.remove.activeLabels($activeLabel);
1286
+ }
1287
+ }
1288
+ else {
1289
+ $activeLabel.removeClass(className.active);
1290
+ }
1291
+ }
1292
+ }
1293
+ },
1294
+
1295
+ keydown: function(event) {
1296
+ var
1297
+ pressedKey = event.which,
1298
+ isShortcutKey = module.is.inObject(pressedKey, keys)
1299
+ ;
1300
+ if(isShortcutKey) {
1301
+ var
1302
+ $currentlySelected = $item.not(selector.unselectable).filter('.' + className.selected).eq(0),
1303
+ $activeItem = $menu.children('.' + className.active).eq(0),
1304
+ $selectedItem = ($currentlySelected.length > 0)
1305
+ ? $currentlySelected
1306
+ : $activeItem,
1307
+ $visibleItems = ($selectedItem.length > 0)
1308
+ ? $selectedItem.siblings(':not(.' + className.filtered +')').addBack()
1309
+ : $menu.children(':not(.' + className.filtered +')'),
1310
+ $subMenu = $selectedItem.children(selector.menu),
1311
+ $parentMenu = $selectedItem.closest(selector.menu),
1312
+ inVisibleMenu = ($parentMenu.hasClass(className.visible) || $parentMenu.hasClass(className.animating) || $parentMenu.parent(selector.menu).length > 0),
1313
+ hasSubMenu = ($subMenu.length> 0),
1314
+ hasSelectedItem = ($selectedItem.length > 0),
1315
+ selectedIsSelectable = ($selectedItem.not(selector.unselectable).length > 0),
1316
+ delimiterPressed = (pressedKey == keys.delimiter && settings.allowAdditions && module.is.multiple()),
1317
+ isAdditionWithoutMenu = (settings.allowAdditions && settings.hideAdditions && (pressedKey == keys.enter || delimiterPressed) && selectedIsSelectable),
1318
+ $nextItem,
1319
+ isSubMenuItem,
1320
+ newIndex
1321
+ ;
1322
+ // allow selection with menu closed
1323
+ if(isAdditionWithoutMenu) {
1324
+ module.verbose('Selecting item from keyboard shortcut', $selectedItem);
1325
+ module.event.item.click.call($selectedItem, event);
1326
+ if(module.is.searchSelection()) {
1327
+ module.remove.searchTerm();
1328
+ }
1329
+ }
1330
+
1331
+ // visible menu keyboard shortcuts
1332
+ if( module.is.visible() ) {
1333
+
1334
+ // enter (select or open sub-menu)
1335
+ if(pressedKey == keys.enter || delimiterPressed) {
1336
+ if(pressedKey == keys.enter && hasSelectedItem && hasSubMenu && !settings.allowCategorySelection) {
1337
+ module.verbose('Pressed enter on unselectable category, opening sub menu');
1338
+ pressedKey = keys.rightArrow;
1339
+ }
1340
+ else if(selectedIsSelectable) {
1341
+ module.verbose('Selecting item from keyboard shortcut', $selectedItem);
1342
+ module.event.item.click.call($selectedItem, event);
1343
+ if(module.is.searchSelection()) {
1344
+ module.remove.searchTerm();
1345
+ }
1346
+ }
1347
+ event.preventDefault();
1348
+ }
1349
+
1350
+ // sub-menu actions
1351
+ if(hasSelectedItem) {
1352
+
1353
+ if(pressedKey == keys.leftArrow) {
1354
+
1355
+ isSubMenuItem = ($parentMenu[0] !== $menu[0]);
1356
+
1357
+ if(isSubMenuItem) {
1358
+ module.verbose('Left key pressed, closing sub-menu');
1359
+ module.animate.hide(false, $parentMenu);
1360
+ $selectedItem
1361
+ .removeClass(className.selected)
1362
+ ;
1363
+ $parentMenu
1364
+ .closest(selector.item)
1365
+ .addClass(className.selected)
1366
+ ;
1367
+ event.preventDefault();
1368
+ }
1369
+ }
1370
+
1371
+ // right arrow (show sub-menu)
1372
+ if(pressedKey == keys.rightArrow) {
1373
+ if(hasSubMenu) {
1374
+ module.verbose('Right key pressed, opening sub-menu');
1375
+ module.animate.show(false, $subMenu);
1376
+ $selectedItem
1377
+ .removeClass(className.selected)
1378
+ ;
1379
+ $subMenu
1380
+ .find(selector.item).eq(0)
1381
+ .addClass(className.selected)
1382
+ ;
1383
+ event.preventDefault();
1384
+ }
1385
+ }
1386
+ }
1387
+
1388
+ // up arrow (traverse menu up)
1389
+ if(pressedKey == keys.upArrow) {
1390
+ $nextItem = (hasSelectedItem && inVisibleMenu)
1391
+ ? $selectedItem.prevAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
1392
+ : $item.eq(0)
1393
+ ;
1394
+ if($visibleItems.index( $nextItem ) < 0) {
1395
+ module.verbose('Up key pressed but reached top of current menu');
1396
+ event.preventDefault();
1397
+ return;
1398
+ }
1399
+ else {
1400
+ module.verbose('Up key pressed, changing active item');
1401
+ $selectedItem
1402
+ .removeClass(className.selected)
1403
+ ;
1404
+ $nextItem
1405
+ .addClass(className.selected)
1406
+ ;
1407
+ module.set.scrollPosition($nextItem);
1408
+ if(settings.selectOnKeydown && module.is.single()) {
1409
+ module.set.selectedItem($nextItem);
1410
+ }
1411
+ }
1412
+ event.preventDefault();
1413
+ }
1414
+
1415
+ // down arrow (traverse menu down)
1416
+ if(pressedKey == keys.downArrow) {
1417
+ $nextItem = (hasSelectedItem && inVisibleMenu)
1418
+ ? $nextItem = $selectedItem.nextAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
1419
+ : $item.eq(0)
1420
+ ;
1421
+ if($nextItem.length === 0) {
1422
+ module.verbose('Down key pressed but reached bottom of current menu');
1423
+ event.preventDefault();
1424
+ return;
1425
+ }
1426
+ else {
1427
+ module.verbose('Down key pressed, changing active item');
1428
+ $item
1429
+ .removeClass(className.selected)
1430
+ ;
1431
+ $nextItem
1432
+ .addClass(className.selected)
1433
+ ;
1434
+ module.set.scrollPosition($nextItem);
1435
+ if(settings.selectOnKeydown && module.is.single()) {
1436
+ module.set.selectedItem($nextItem);
1437
+ }
1438
+ }
1439
+ event.preventDefault();
1440
+ }
1441
+
1442
+ // page down (show next page)
1443
+ if(pressedKey == keys.pageUp) {
1444
+ module.scrollPage('up');
1445
+ event.preventDefault();
1446
+ }
1447
+ if(pressedKey == keys.pageDown) {
1448
+ module.scrollPage('down');
1449
+ event.preventDefault();
1450
+ }
1451
+
1452
+ // escape (close menu)
1453
+ if(pressedKey == keys.escape) {
1454
+ module.verbose('Escape key pressed, closing dropdown');
1455
+ module.hide();
1456
+ }
1457
+
1458
+ }
1459
+ else {
1460
+ // delimiter key
1461
+ if(delimiterPressed) {
1462
+ event.preventDefault();
1463
+ }
1464
+ // down arrow (open menu)
1465
+ if(pressedKey == keys.downArrow && !module.is.visible()) {
1466
+ module.verbose('Down key pressed, showing dropdown');
1467
+ module.select.firstUnfiltered();
1468
+ module.show();
1469
+ event.preventDefault();
1470
+ }
1471
+ }
1472
+ }
1473
+ else {
1474
+ if( !module.has.search() ) {
1475
+ module.set.selectedLetter( String.fromCharCode(pressedKey) );
1476
+ }
1477
+ }
1478
+ }
1479
+ },
1480
+
1481
+ trigger: {
1482
+ change: function() {
1483
+ var
1484
+ events = document.createEvent('HTMLEvents'),
1485
+ inputElement = $input[0]
1486
+ ;
1487
+ if(inputElement) {
1488
+ module.verbose('Triggering native change event');
1489
+ events.initEvent('change', true, false);
1490
+ inputElement.dispatchEvent(events);
1491
+ }
1492
+ }
1493
+ },
1494
+
1495
+ determine: {
1496
+ selectAction: function(text, value) {
1497
+ module.verbose('Determining action', settings.action);
1498
+ if( $.isFunction( module.action[settings.action] ) ) {
1499
+ module.verbose('Triggering preset action', settings.action, text, value);
1500
+ module.action[ settings.action ].call(element, text, value, this);
1501
+ }
1502
+ else if( $.isFunction(settings.action) ) {
1503
+ module.verbose('Triggering user action', settings.action, text, value);
1504
+ settings.action.call(element, text, value, this);
1505
+ }
1506
+ else {
1507
+ module.error(error.action, settings.action);
1508
+ }
1509
+ },
1510
+ eventInModule: function(event, callback) {
1511
+ var
1512
+ $target = $(event.target),
1513
+ inDocument = ($target.closest(document.documentElement).length > 0),
1514
+ inModule = ($target.closest($module).length > 0)
1515
+ ;
1516
+ callback = $.isFunction(callback)
1517
+ ? callback
1518
+ : function(){}
1519
+ ;
1520
+ if(inDocument && !inModule) {
1521
+ module.verbose('Triggering event', callback);
1522
+ callback();
1523
+ return true;
1524
+ }
1525
+ else {
1526
+ module.verbose('Event occurred in dropdown, canceling callback');
1527
+ return false;
1528
+ }
1529
+ },
1530
+ eventOnElement: function(event, callback) {
1531
+ var
1532
+ $target = $(event.target),
1533
+ $label = $target.closest(selector.siblingLabel),
1534
+ inVisibleDOM = document.body.contains(event.target),
1535
+ notOnLabel = ($module.find($label).length === 0),
1536
+ notInMenu = ($target.closest($menu).length === 0)
1537
+ ;
1538
+ callback = $.isFunction(callback)
1539
+ ? callback
1540
+ : function(){}
1541
+ ;
1542
+ if(inVisibleDOM && notOnLabel && notInMenu) {
1543
+ module.verbose('Triggering event', callback);
1544
+ callback();
1545
+ return true;
1546
+ }
1547
+ else {
1548
+ module.verbose('Event occurred in dropdown menu, canceling callback');
1549
+ return false;
1550
+ }
1551
+ }
1552
+ },
1553
+
1554
+ action: {
1555
+
1556
+ nothing: function() {},
1557
+
1558
+ activate: function(text, value, element) {
1559
+ value = (value !== undefined)
1560
+ ? value
1561
+ : text
1562
+ ;
1563
+ if( module.can.activate( $(element) ) ) {
1564
+ module.set.selected(value, $(element));
1565
+ if(module.is.multiple() && !module.is.allFiltered()) {
1566
+ return;
1567
+ }
1568
+ else {
1569
+ module.hideAndClear();
1570
+ }
1571
+ }
1572
+ },
1573
+
1574
+ select: function(text, value, element) {
1575
+ value = (value !== undefined)
1576
+ ? value
1577
+ : text
1578
+ ;
1579
+ if( module.can.activate( $(element) ) ) {
1580
+ module.set.value(value, $(element));
1581
+ if(module.is.multiple() && !module.is.allFiltered()) {
1582
+ return;
1583
+ }
1584
+ else {
1585
+ module.hideAndClear();
1586
+ }
1587
+ }
1588
+ },
1589
+
1590
+ combo: function(text, value, element) {
1591
+ value = (value !== undefined)
1592
+ ? value
1593
+ : text
1594
+ ;
1595
+ module.set.selected(value, $(element));
1596
+ module.hideAndClear();
1597
+ },
1598
+
1599
+ hide: function(text, value, element) {
1600
+ module.set.value(value, text);
1601
+ module.hideAndClear();
1602
+ }
1603
+
1604
+ },
1605
+
1606
+ get: {
1607
+ id: function() {
1608
+ return id;
1609
+ },
1610
+ defaultText: function() {
1611
+ return $module.data(metadata.defaultText);
1612
+ },
1613
+ defaultValue: function() {
1614
+ return $module.data(metadata.defaultValue);
1615
+ },
1616
+ placeholderText: function() {
1617
+ return $module.data(metadata.placeholderText) || '';
1618
+ },
1619
+ text: function() {
1620
+ return $text.text();
1621
+ },
1622
+ query: function() {
1623
+ return $.trim($search.val());
1624
+ },
1625
+ searchWidth: function(value) {
1626
+ value = (value !== undefined)
1627
+ ? value
1628
+ : $search.val()
1629
+ ;
1630
+ $sizer.text(value);
1631
+ // prevent rounding issues
1632
+ return Math.ceil( $sizer.width() + 1);
1633
+ },
1634
+ selectionCount: function() {
1635
+ var
1636
+ values = module.get.values(),
1637
+ count
1638
+ ;
1639
+ count = ( module.is.multiple() )
1640
+ ? $.isArray(values)
1641
+ ? values.length
1642
+ : 0
1643
+ : (module.get.value() !== '')
1644
+ ? 1
1645
+ : 0
1646
+ ;
1647
+ return count;
1648
+ },
1649
+ transition: function($subMenu) {
1650
+ return (settings.transition == 'auto')
1651
+ ? module.is.upward($subMenu)
1652
+ ? 'slide up'
1653
+ : 'slide down'
1654
+ : settings.transition
1655
+ ;
1656
+ },
1657
+ userValues: function() {
1658
+ var
1659
+ values = module.get.values()
1660
+ ;
1661
+ if(!values) {
1662
+ return false;
1663
+ }
1664
+ values = $.isArray(values)
1665
+ ? values
1666
+ : [values]
1667
+ ;
1668
+ return $.grep(values, function(value) {
1669
+ return (module.get.item(value) === false);
1670
+ });
1671
+ },
1672
+ uniqueArray: function(array) {
1673
+ return $.grep(array, function (value, index) {
1674
+ return $.inArray(value, array) === index;
1675
+ });
1676
+ },
1677
+ caretPosition: function() {
1678
+ var
1679
+ input = $search.get(0),
1680
+ range,
1681
+ rangeLength
1682
+ ;
1683
+ if('selectionStart' in input) {
1684
+ return input.selectionStart;
1685
+ }
1686
+ else if (document.selection) {
1687
+ input.focus();
1688
+ range = document.selection.createRange();
1689
+ rangeLength = range.text.length;
1690
+ range.moveStart('character', -input.value.length);
1691
+ return range.text.length - rangeLength;
1692
+ }
1693
+ },
1694
+ value: function() {
1695
+ var
1696
+ value = ($input.length > 0)
1697
+ ? $input.val()
1698
+ : $module.data(metadata.value),
1699
+ isEmptyMultiselect = ($.isArray(value) && value.length === 1 && value[0] === '')
1700
+ ;
1701
+ // prevents placeholder element from being selected when multiple
1702
+ return (value === undefined || isEmptyMultiselect)
1703
+ ? ''
1704
+ : value
1705
+ ;
1706
+ },
1707
+ values: function() {
1708
+ var
1709
+ value = module.get.value()
1710
+ ;
1711
+ if(value === '') {
1712
+ return '';
1713
+ }
1714
+ return ( !module.has.selectInput() && module.is.multiple() )
1715
+ ? (typeof value == 'string') // delimited string
1716
+ ? value.split(settings.delimiter)
1717
+ : ''
1718
+ : value
1719
+ ;
1720
+ },
1721
+ remoteValues: function() {
1722
+ var
1723
+ values = module.get.values(),
1724
+ remoteValues = false
1725
+ ;
1726
+ if(values) {
1727
+ if(typeof values == 'string') {
1728
+ values = [values];
1729
+ }
1730
+ $.each(values, function(index, value) {
1731
+ var
1732
+ name = module.read.remoteData(value)
1733
+ ;
1734
+ module.verbose('Restoring value from session data', name, value);
1735
+ if(name) {
1736
+ if(!remoteValues) {
1737
+ remoteValues = {};
1738
+ }
1739
+ remoteValues[value] = name;
1740
+ }
1741
+ });
1742
+ }
1743
+ return remoteValues;
1744
+ },
1745
+ choiceText: function($choice, preserveHTML) {
1746
+ preserveHTML = (preserveHTML !== undefined)
1747
+ ? preserveHTML
1748
+ : settings.preserveHTML
1749
+ ;
1750
+ if($choice) {
1751
+ if($choice.find(selector.menu).length > 0) {
1752
+ module.verbose('Retrieving text of element with sub-menu');
1753
+ $choice = $choice.clone();
1754
+ $choice.find(selector.menu).remove();
1755
+ $choice.find(selector.menuIcon).remove();
1756
+ }
1757
+ return ($choice.data(metadata.text) !== undefined)
1758
+ ? $choice.data(metadata.text)
1759
+ : (preserveHTML)
1760
+ ? $.trim($choice.html())
1761
+ : $.trim($choice.text())
1762
+ ;
1763
+ }
1764
+ },
1765
+ choiceValue: function($choice, choiceText) {
1766
+ choiceText = choiceText || module.get.choiceText($choice);
1767
+ if(!$choice) {
1768
+ return false;
1769
+ }
1770
+ return ($choice.data(metadata.value) !== undefined)
1771
+ ? String( $choice.data(metadata.value) )
1772
+ : (typeof choiceText === 'string')
1773
+ ? $.trim(choiceText.toLowerCase())
1774
+ : String(choiceText)
1775
+ ;
1776
+ },
1777
+ inputEvent: function() {
1778
+ var
1779
+ input = $search[0]
1780
+ ;
1781
+ if(input) {
1782
+ return (input.oninput !== undefined)
1783
+ ? 'input'
1784
+ : (input.onpropertychange !== undefined)
1785
+ ? 'propertychange'
1786
+ : 'keyup'
1787
+ ;
1788
+ }
1789
+ return false;
1790
+ },
1791
+ selectValues: function() {
1792
+ var
1793
+ select = {}
1794
+ ;
1795
+ select.values = [];
1796
+ $module
1797
+ .find('option')
1798
+ .each(function() {
1799
+ var
1800
+ $option = $(this),
1801
+ name = $option.html(),
1802
+ disabled = $option.attr('disabled'),
1803
+ value = ( $option.attr('value') !== undefined )
1804
+ ? $option.attr('value')
1805
+ : name
1806
+ ;
1807
+ if(settings.placeholder === 'auto' && value === '') {
1808
+ select.placeholder = name;
1809
+ }
1810
+ else {
1811
+ select.values.push({
1812
+ name : name,
1813
+ value : value,
1814
+ disabled : disabled
1815
+ });
1816
+ }
1817
+ })
1818
+ ;
1819
+ if(settings.placeholder && settings.placeholder !== 'auto') {
1820
+ module.debug('Setting placeholder value to', settings.placeholder);
1821
+ select.placeholder = settings.placeholder;
1822
+ }
1823
+ if(settings.sortSelect) {
1824
+ select.values.sort(function(a, b) {
1825
+ return (a.name > b.name)
1826
+ ? 1
1827
+ : -1
1828
+ ;
1829
+ });
1830
+ module.debug('Retrieved and sorted values from select', select);
1831
+ }
1832
+ else {
1833
+ module.debug('Retrieved values from select', select);
1834
+ }
1835
+ return select;
1836
+ },
1837
+ activeItem: function() {
1838
+ return $item.filter('.' + className.active);
1839
+ },
1840
+ selectedItem: function() {
1841
+ var
1842
+ $selectedItem = $item.not(selector.unselectable).filter('.' + className.selected)
1843
+ ;
1844
+ return ($selectedItem.length > 0)
1845
+ ? $selectedItem
1846
+ : $item.eq(0)
1847
+ ;
1848
+ },
1849
+ itemWithAdditions: function(value) {
1850
+ var
1851
+ $items = module.get.item(value),
1852
+ $userItems = module.create.userChoice(value),
1853
+ hasUserItems = ($userItems && $userItems.length > 0)
1854
+ ;
1855
+ if(hasUserItems) {
1856
+ $items = ($items.length > 0)
1857
+ ? $items.add($userItems)
1858
+ : $userItems
1859
+ ;
1860
+ }
1861
+ return $items;
1862
+ },
1863
+ item: function(value, strict) {
1864
+ var
1865
+ $selectedItem = false,
1866
+ shouldSearch,
1867
+ isMultiple
1868
+ ;
1869
+ value = (value !== undefined)
1870
+ ? value
1871
+ : ( module.get.values() !== undefined)
1872
+ ? module.get.values()
1873
+ : module.get.text()
1874
+ ;
1875
+ shouldSearch = (isMultiple)
1876
+ ? (value.length > 0)
1877
+ : (value !== undefined && value !== null)
1878
+ ;
1879
+ isMultiple = (module.is.multiple() && $.isArray(value));
1880
+ strict = (value === '' || value === 0)
1881
+ ? true
1882
+ : strict || false
1883
+ ;
1884
+ if(shouldSearch) {
1885
+ $item
1886
+ .each(function() {
1887
+ var
1888
+ $choice = $(this),
1889
+ optionText = module.get.choiceText($choice),
1890
+ optionValue = module.get.choiceValue($choice, optionText)
1891
+ ;
1892
+ // safe early exit
1893
+ if(optionValue === null || optionValue === undefined) {
1894
+ return;
1895
+ }
1896
+ if(isMultiple) {
1897
+ if($.inArray( String(optionValue), value) !== -1 || $.inArray(optionText, value) !== -1) {
1898
+ $selectedItem = ($selectedItem)
1899
+ ? $selectedItem.add($choice)
1900
+ : $choice
1901
+ ;
1902
+ }
1903
+ }
1904
+ else if(strict) {
1905
+ module.verbose('Ambiguous dropdown value using strict type check', $choice, value);
1906
+ if( optionValue === value || optionText === value) {
1907
+ $selectedItem = $choice;
1908
+ return true;
1909
+ }
1910
+ }
1911
+ else {
1912
+ if( String(optionValue) == String(value) || optionText == value) {
1913
+ module.verbose('Found select item by value', optionValue, value);
1914
+ $selectedItem = $choice;
1915
+ return true;
1916
+ }
1917
+ }
1918
+ })
1919
+ ;
1920
+ }
1921
+ return $selectedItem;
1922
+ }
1923
+ },
1924
+
1925
+ check: {
1926
+ maxSelections: function(selectionCount) {
1927
+ if(settings.maxSelections) {
1928
+ selectionCount = (selectionCount !== undefined)
1929
+ ? selectionCount
1930
+ : module.get.selectionCount()
1931
+ ;
1932
+ if(selectionCount >= settings.maxSelections) {
1933
+ module.debug('Maximum selection count reached');
1934
+ if(settings.useLabels) {
1935
+ $item.addClass(className.filtered);
1936
+ module.add.message(message.maxSelections);
1937
+ }
1938
+ return true;
1939
+ }
1940
+ else {
1941
+ module.verbose('No longer at maximum selection count');
1942
+ module.remove.message();
1943
+ module.remove.filteredItem();
1944
+ if(module.is.searchSelection()) {
1945
+ module.filterItems();
1946
+ }
1947
+ return false;
1948
+ }
1949
+ }
1950
+ return true;
1951
+ }
1952
+ },
1953
+
1954
+ restore: {
1955
+ defaults: function() {
1956
+ module.clear();
1957
+ module.restore.defaultText();
1958
+ module.restore.defaultValue();
1959
+ },
1960
+ defaultText: function() {
1961
+ var
1962
+ defaultText = module.get.defaultText(),
1963
+ placeholderText = module.get.placeholderText
1964
+ ;
1965
+ if(defaultText === placeholderText) {
1966
+ module.debug('Restoring default placeholder text', defaultText);
1967
+ module.set.placeholderText(defaultText);
1968
+ }
1969
+ else {
1970
+ module.debug('Restoring default text', defaultText);
1971
+ module.set.text(defaultText);
1972
+ }
1973
+ },
1974
+ placeholderText: function() {
1975
+ module.set.placeholderText();
1976
+ },
1977
+ defaultValue: function() {
1978
+ var
1979
+ defaultValue = module.get.defaultValue()
1980
+ ;
1981
+ if(defaultValue !== undefined) {
1982
+ module.debug('Restoring default value', defaultValue);
1983
+ if(defaultValue !== '') {
1984
+ module.set.value(defaultValue);
1985
+ module.set.selected();
1986
+ }
1987
+ else {
1988
+ module.remove.activeItem();
1989
+ module.remove.selectedItem();
1990
+ }
1991
+ }
1992
+ },
1993
+ labels: function() {
1994
+ if(settings.allowAdditions) {
1995
+ if(!settings.useLabels) {
1996
+ module.error(error.labels);
1997
+ settings.useLabels = true;
1998
+ }
1999
+ module.debug('Restoring selected values');
2000
+ module.create.userLabels();
2001
+ }
2002
+ module.check.maxSelections();
2003
+ },
2004
+ selected: function() {
2005
+ module.restore.values();
2006
+ if(module.is.multiple()) {
2007
+ module.debug('Restoring previously selected values and labels');
2008
+ module.restore.labels();
2009
+ }
2010
+ else {
2011
+ module.debug('Restoring previously selected values');
2012
+ }
2013
+ },
2014
+ values: function() {
2015
+ // prevents callbacks from occurring on initial load
2016
+ module.set.initialLoad();
2017
+ if(settings.apiSettings && settings.saveRemoteData && module.get.remoteValues()) {
2018
+ module.restore.remoteValues();
2019
+ }
2020
+ else {
2021
+ module.set.selected();
2022
+ }
2023
+ module.remove.initialLoad();
2024
+ },
2025
+ remoteValues: function() {
2026
+ var
2027
+ values = module.get.remoteValues()
2028
+ ;
2029
+ module.debug('Recreating selected from session data', values);
2030
+ if(values) {
2031
+ if( module.is.single() ) {
2032
+ $.each(values, function(value, name) {
2033
+ module.set.text(name);
2034
+ });
2035
+ }
2036
+ else {
2037
+ $.each(values, function(value, name) {
2038
+ module.add.label(value, name);
2039
+ });
2040
+ }
2041
+ }
2042
+ }
2043
+ },
2044
+
2045
+ read: {
2046
+ remoteData: function(value) {
2047
+ var
2048
+ name
2049
+ ;
2050
+ if(window.Storage === undefined) {
2051
+ module.error(error.noStorage);
2052
+ return;
2053
+ }
2054
+ name = sessionStorage.getItem(value);
2055
+ return (name !== undefined)
2056
+ ? name
2057
+ : false
2058
+ ;
2059
+ }
2060
+ },
2061
+
2062
+ save: {
2063
+ defaults: function() {
2064
+ module.save.defaultText();
2065
+ module.save.placeholderText();
2066
+ module.save.defaultValue();
2067
+ },
2068
+ defaultValue: function() {
2069
+ var
2070
+ value = module.get.value()
2071
+ ;
2072
+ module.verbose('Saving default value as', value);
2073
+ $module.data(metadata.defaultValue, value);
2074
+ },
2075
+ defaultText: function() {
2076
+ var
2077
+ text = module.get.text()
2078
+ ;
2079
+ module.verbose('Saving default text as', text);
2080
+ $module.data(metadata.defaultText, text);
2081
+ },
2082
+ placeholderText: function() {
2083
+ var
2084
+ text
2085
+ ;
2086
+ if(settings.placeholder !== false && $text.hasClass(className.placeholder)) {
2087
+ text = module.get.text();
2088
+ module.verbose('Saving placeholder text as', text);
2089
+ $module.data(metadata.placeholderText, text);
2090
+ }
2091
+ },
2092
+ remoteData: function(name, value) {
2093
+ if(window.Storage === undefined) {
2094
+ module.error(error.noStorage);
2095
+ return;
2096
+ }
2097
+ module.verbose('Saving remote data to session storage', value, name);
2098
+ sessionStorage.setItem(value, name);
2099
+ }
2100
+ },
2101
+
2102
+ clear: function() {
2103
+ if(module.is.multiple() && settings.useLabels) {
2104
+ module.remove.labels();
2105
+ }
2106
+ else {
2107
+ module.remove.activeItem();
2108
+ module.remove.selectedItem();
2109
+ }
2110
+ module.set.placeholderText();
2111
+ module.clearValue();
2112
+ },
2113
+
2114
+ clearValue: function() {
2115
+ module.set.value('');
2116
+ },
2117
+
2118
+ scrollPage: function(direction, $selectedItem) {
2119
+ var
2120
+ $currentItem = $selectedItem || module.get.selectedItem(),
2121
+ $menu = $currentItem.closest(selector.menu),
2122
+ menuHeight = $menu.outerHeight(),
2123
+ currentScroll = $menu.scrollTop(),
2124
+ itemHeight = $item.eq(0).outerHeight(),
2125
+ itemsPerPage = Math.floor(menuHeight / itemHeight),
2126
+ maxScroll = $menu.prop('scrollHeight'),
2127
+ newScroll = (direction == 'up')
2128
+ ? currentScroll - (itemHeight * itemsPerPage)
2129
+ : currentScroll + (itemHeight * itemsPerPage),
2130
+ $selectableItem = $item.not(selector.unselectable),
2131
+ isWithinRange,
2132
+ $nextSelectedItem,
2133
+ elementIndex
2134
+ ;
2135
+ elementIndex = (direction == 'up')
2136
+ ? $selectableItem.index($currentItem) - itemsPerPage
2137
+ : $selectableItem.index($currentItem) + itemsPerPage
2138
+ ;
2139
+ isWithinRange = (direction == 'up')
2140
+ ? (elementIndex >= 0)
2141
+ : (elementIndex < $selectableItem.length)
2142
+ ;
2143
+ $nextSelectedItem = (isWithinRange)
2144
+ ? $selectableItem.eq(elementIndex)
2145
+ : (direction == 'up')
2146
+ ? $selectableItem.first()
2147
+ : $selectableItem.last()
2148
+ ;
2149
+ if($nextSelectedItem.length > 0) {
2150
+ module.debug('Scrolling page', direction, $nextSelectedItem);
2151
+ $currentItem
2152
+ .removeClass(className.selected)
2153
+ ;
2154
+ $nextSelectedItem
2155
+ .addClass(className.selected)
2156
+ ;
2157
+ if(settings.selectOnKeydown && module.is.single()) {
2158
+ module.set.selectedItem($nextSelectedItem);
2159
+ }
2160
+ $menu
2161
+ .scrollTop(newScroll)
2162
+ ;
2163
+ }
2164
+ },
2165
+
2166
+ set: {
2167
+ filtered: function() {
2168
+ var
2169
+ isMultiple = module.is.multiple(),
2170
+ isSearch = module.is.searchSelection(),
2171
+ isSearchMultiple = (isMultiple && isSearch),
2172
+ searchValue = (isSearch)
2173
+ ? module.get.query()
2174
+ : '',
2175
+ hasSearchValue = (typeof searchValue === 'string' && searchValue.length > 0),
2176
+ searchWidth = module.get.searchWidth(),
2177
+ valueIsSet = searchValue !== ''
2178
+ ;
2179
+ if(isMultiple && hasSearchValue) {
2180
+ module.verbose('Adjusting input width', searchWidth, settings.glyphWidth);
2181
+ $search.css('width', searchWidth);
2182
+ }
2183
+ if(hasSearchValue || (isSearchMultiple && valueIsSet)) {
2184
+ module.verbose('Hiding placeholder text');
2185
+ $text.addClass(className.filtered);
2186
+ }
2187
+ else if(!isMultiple || (isSearchMultiple && !valueIsSet)) {
2188
+ module.verbose('Showing placeholder text');
2189
+ $text.removeClass(className.filtered);
2190
+ }
2191
+ },
2192
+ empty: function() {
2193
+ $module.addClass(className.empty);
2194
+ },
2195
+ loading: function() {
2196
+ $module.addClass(className.loading);
2197
+ },
2198
+ placeholderText: function(text) {
2199
+ text = text || module.get.placeholderText();
2200
+ module.debug('Setting placeholder text', text);
2201
+ module.set.text(text);
2202
+ $text.addClass(className.placeholder);
2203
+ },
2204
+ tabbable: function() {
2205
+ if( module.is.searchSelection() ) {
2206
+ module.debug('Added tabindex to searchable dropdown');
2207
+ $search
2208
+ .val('')
2209
+ .attr('tabindex', 0)
2210
+ ;
2211
+ $menu
2212
+ .attr('tabindex', -1)
2213
+ ;
2214
+ }
2215
+ else {
2216
+ module.debug('Added tabindex to dropdown');
2217
+ if( $module.attr('tabindex') === undefined) {
2218
+ $module
2219
+ .attr('tabindex', 0)
2220
+ ;
2221
+ $menu
2222
+ .attr('tabindex', -1)
2223
+ ;
2224
+ }
2225
+ }
2226
+ },
2227
+ initialLoad: function() {
2228
+ module.verbose('Setting initial load');
2229
+ initialLoad = true;
2230
+ },
2231
+ activeItem: function($item) {
2232
+ if( settings.allowAdditions && $item.filter(selector.addition).length > 0 ) {
2233
+ $item.addClass(className.filtered);
2234
+ }
2235
+ else {
2236
+ $item.addClass(className.active);
2237
+ }
2238
+ },
2239
+ partialSearch: function(text) {
2240
+ var
2241
+ length = module.get.query().length
2242
+ ;
2243
+ $search.val( text.substr(0 , length));
2244
+ },
2245
+ scrollPosition: function($item, forceScroll) {
2246
+ var
2247
+ edgeTolerance = 5,
2248
+ $menu,
2249
+ hasActive,
2250
+ offset,
2251
+ itemHeight,
2252
+ itemOffset,
2253
+ menuOffset,
2254
+ menuScroll,
2255
+ menuHeight,
2256
+ abovePage,
2257
+ belowPage
2258
+ ;
2259
+
2260
+ $item = $item || module.get.selectedItem();
2261
+ $menu = $item.closest(selector.menu);
2262
+ hasActive = ($item && $item.length > 0);
2263
+ forceScroll = (forceScroll !== undefined)
2264
+ ? forceScroll
2265
+ : false
2266
+ ;
2267
+ if($item && $menu.length > 0 && hasActive) {
2268
+ itemOffset = $item.position().top;
2269
+
2270
+ $menu.addClass(className.loading);
2271
+ menuScroll = $menu.scrollTop();
2272
+ menuOffset = $menu.offset().top;
2273
+ itemOffset = $item.offset().top;
2274
+ offset = menuScroll - menuOffset + itemOffset;
2275
+ if(!forceScroll) {
2276
+ menuHeight = $menu.height();
2277
+ belowPage = menuScroll + menuHeight < (offset + edgeTolerance);
2278
+ abovePage = ((offset - edgeTolerance) < menuScroll);
2279
+ }
2280
+ module.debug('Scrolling to active item', offset);
2281
+ if(forceScroll || abovePage || belowPage) {
2282
+ $menu.scrollTop(offset);
2283
+ }
2284
+ $menu.removeClass(className.loading);
2285
+ }
2286
+ },
2287
+ text: function(text) {
2288
+ if(settings.action !== 'select') {
2289
+ if(settings.action == 'combo') {
2290
+ module.debug('Changing combo button text', text, $combo);
2291
+ if(settings.preserveHTML) {
2292
+ $combo.html(text);
2293
+ }
2294
+ else {
2295
+ $combo.text(text);
2296
+ }
2297
+ }
2298
+ else {
2299
+ if(text !== module.get.placeholderText()) {
2300
+ $text.removeClass(className.placeholder);
2301
+ }
2302
+ module.debug('Changing text', text, $text);
2303
+ $text
2304
+ .removeClass(className.filtered)
2305
+ ;
2306
+ if(settings.preserveHTML) {
2307
+ $text.html(text);
2308
+ }
2309
+ else {
2310
+ $text.text(text);
2311
+ }
2312
+ }
2313
+ }
2314
+ },
2315
+ selectedItem: function($item) {
2316
+ var
2317
+ value = module.get.choiceValue($item),
2318
+ searchText = module.get.choiceText($item, false),
2319
+ text = module.get.choiceText($item, true)
2320
+ ;
2321
+ module.debug('Setting user selection to item', $item);
2322
+ module.remove.activeItem();
2323
+ module.set.partialSearch(searchText);
2324
+ module.set.activeItem($item);
2325
+ module.set.selected(value, $item);
2326
+ module.set.text(text);
2327
+ },
2328
+ selectedLetter: function(letter) {
2329
+ var
2330
+ $selectedItem = $item.filter('.' + className.selected),
2331
+ alreadySelectedLetter = $selectedItem.length > 0 && module.has.firstLetter($selectedItem, letter),
2332
+ $nextValue = false,
2333
+ $nextItem
2334
+ ;
2335
+ // check next of same letter
2336
+ if(alreadySelectedLetter) {
2337
+ $nextItem = $selectedItem.nextAll($item).eq(0);
2338
+ if( module.has.firstLetter($nextItem, letter) ) {
2339
+ $nextValue = $nextItem;
2340
+ }
2341
+ }
2342
+ // check all values
2343
+ if(!$nextValue) {
2344
+ $item
2345
+ .each(function(){
2346
+ if(module.has.firstLetter($(this), letter)) {
2347
+ $nextValue = $(this);
2348
+ return false;
2349
+ }
2350
+ })
2351
+ ;
2352
+ }
2353
+ // set next value
2354
+ if($nextValue) {
2355
+ module.verbose('Scrolling to next value with letter', letter);
2356
+ module.set.scrollPosition($nextValue);
2357
+ $selectedItem.removeClass(className.selected);
2358
+ $nextValue.addClass(className.selected);
2359
+ if(settings.selectOnKeydown && module.is.single()) {
2360
+ module.set.selectedItem($nextValue);
2361
+ }
2362
+ }
2363
+ },
2364
+ direction: function($menu) {
2365
+ if(settings.direction == 'auto') {
2366
+ if(module.is.onScreen($menu)) {
2367
+ module.remove.upward($menu);
2368
+ }
2369
+ else {
2370
+ module.set.upward($menu);
2371
+ }
2372
+ }
2373
+ else if(settings.direction == 'upward') {
2374
+ module.set.upward($menu);
2375
+ }
2376
+ },
2377
+ upward: function($menu) {
2378
+ var $element = $menu || $module;
2379
+ $element.addClass(className.upward);
2380
+ },
2381
+ value: function(value, text, $selected) {
2382
+ var
2383
+ escapedValue = module.escape.value(value),
2384
+ hasInput = ($input.length > 0),
2385
+ isAddition = !module.has.value(value),
2386
+ currentValue = module.get.values(),
2387
+ stringValue = (value !== undefined)
2388
+ ? String(value)
2389
+ : value,
2390
+ newValue
2391
+ ;
2392
+ if(hasInput) {
2393
+ if(!settings.allowReselection && stringValue == currentValue) {
2394
+ module.verbose('Skipping value update already same value', value, currentValue);
2395
+ if(!module.is.initialLoad()) {
2396
+ return;
2397
+ }
2398
+ }
2399
+
2400
+ if( module.is.single() && module.has.selectInput() && module.can.extendSelect() ) {
2401
+ module.debug('Adding user option', value);
2402
+ module.add.optionValue(value);
2403
+ }
2404
+ module.debug('Updating input value', escapedValue, currentValue);
2405
+ internalChange = true;
2406
+ $input
2407
+ .val(escapedValue)
2408
+ ;
2409
+ if(settings.fireOnInit === false && module.is.initialLoad()) {
2410
+ module.debug('Input native change event ignored on initial load');
2411
+ }
2412
+ else {
2413
+ module.trigger.change();
2414
+ }
2415
+ internalChange = false;
2416
+ }
2417
+ else {
2418
+ module.verbose('Storing value in metadata', escapedValue, $input);
2419
+ if(escapedValue !== currentValue) {
2420
+ $module.data(metadata.value, stringValue);
2421
+ }
2422
+ }
2423
+ if(settings.fireOnInit === false && module.is.initialLoad()) {
2424
+ module.verbose('No callback on initial load', settings.onChange);
2425
+ }
2426
+ else {
2427
+ settings.onChange.call(element, value, text, $selected);
2428
+ }
2429
+ },
2430
+ active: function() {
2431
+ $module
2432
+ .addClass(className.active)
2433
+ ;
2434
+ },
2435
+ multiple: function() {
2436
+ $module.addClass(className.multiple);
2437
+ },
2438
+ visible: function() {
2439
+ $module.addClass(className.visible);
2440
+ },
2441
+ exactly: function(value, $selectedItem) {
2442
+ module.debug('Setting selected to exact values');
2443
+ module.clear();
2444
+ module.set.selected(value, $selectedItem);
2445
+ },
2446
+ selected: function(value, $selectedItem) {
2447
+ var
2448
+ isMultiple = module.is.multiple(),
2449
+ $userSelectedItem
2450
+ ;
2451
+ $selectedItem = (settings.allowAdditions)
2452
+ ? $selectedItem || module.get.itemWithAdditions(value)
2453
+ : $selectedItem || module.get.item(value)
2454
+ ;
2455
+ if(!$selectedItem) {
2456
+ return;
2457
+ }
2458
+ module.debug('Setting selected menu item to', $selectedItem);
2459
+ if(module.is.multiple()) {
2460
+ module.remove.searchWidth();
2461
+ }
2462
+ if(module.is.single()) {
2463
+ module.remove.activeItem();
2464
+ module.remove.selectedItem();
2465
+ }
2466
+ else if(settings.useLabels) {
2467
+ module.remove.selectedItem();
2468
+ }
2469
+ // select each item
2470
+ $selectedItem
2471
+ .each(function() {
2472
+ var
2473
+ $selected = $(this),
2474
+ selectedText = module.get.choiceText($selected),
2475
+ selectedValue = module.get.choiceValue($selected, selectedText),
2476
+
2477
+ isFiltered = $selected.hasClass(className.filtered),
2478
+ isActive = $selected.hasClass(className.active),
2479
+ isUserValue = $selected.hasClass(className.addition),
2480
+ shouldAnimate = (isMultiple && $selectedItem.length == 1)
2481
+ ;
2482
+ if(isMultiple) {
2483
+ if(!isActive || isUserValue) {
2484
+ if(settings.apiSettings && settings.saveRemoteData) {
2485
+ module.save.remoteData(selectedText, selectedValue);
2486
+ }
2487
+ if(settings.useLabels) {
2488
+ module.add.value(selectedValue, selectedText, $selected);
2489
+ module.add.label(selectedValue, selectedText, shouldAnimate);
2490
+ module.set.activeItem($selected);
2491
+ module.filterActive();
2492
+ module.select.nextAvailable($selectedItem);
2493
+ }
2494
+ else {
2495
+ module.add.value(selectedValue, selectedText, $selected);
2496
+ module.set.text(module.add.variables(message.count));
2497
+ module.set.activeItem($selected);
2498
+ }
2499
+ }
2500
+ else if(!isFiltered) {
2501
+ module.debug('Selected active value, removing label');
2502
+ module.remove.selected(selectedValue);
2503
+ }
2504
+ }
2505
+ else {
2506
+ if(settings.apiSettings && settings.saveRemoteData) {
2507
+ module.save.remoteData(selectedText, selectedValue);
2508
+ }
2509
+ module.set.text(selectedText);
2510
+ module.set.value(selectedValue, selectedText, $selected);
2511
+ $selected
2512
+ .addClass(className.active)
2513
+ .addClass(className.selected)
2514
+ ;
2515
+ }
2516
+ })
2517
+ ;
2518
+ }
2519
+ },
2520
+
2521
+ add: {
2522
+ label: function(value, text, shouldAnimate) {
2523
+ var
2524
+ $next = module.is.searchSelection()
2525
+ ? $search
2526
+ : $text,
2527
+ escapedValue = module.escape.value(value),
2528
+ $label
2529
+ ;
2530
+ $label = $('<a />')
2531
+ .addClass(className.label)
2532
+ .attr('data-' + metadata.value, escapedValue)
2533
+ .html(templates.label(escapedValue, text))
2534
+ ;
2535
+ $label = settings.onLabelCreate.call($label, escapedValue, text);
2536
+
2537
+ if(module.has.label(value)) {
2538
+ module.debug('Label already exists, skipping', escapedValue);
2539
+ return;
2540
+ }
2541
+ if(settings.label.variation) {
2542
+ $label.addClass(settings.label.variation);
2543
+ }
2544
+ if(shouldAnimate === true) {
2545
+ module.debug('Animating in label', $label);
2546
+ $label
2547
+ .addClass(className.hidden)
2548
+ .insertBefore($next)
2549
+ .transition(settings.label.transition, settings.label.duration)
2550
+ ;
2551
+ }
2552
+ else {
2553
+ module.debug('Adding selection label', $label);
2554
+ $label
2555
+ .insertBefore($next)
2556
+ ;
2557
+ }
2558
+ },
2559
+ message: function(message) {
2560
+ var
2561
+ $message = $menu.children(selector.message),
2562
+ html = settings.templates.message(module.add.variables(message))
2563
+ ;
2564
+ if($message.length > 0) {
2565
+ $message
2566
+ .html(html)
2567
+ ;
2568
+ }
2569
+ else {
2570
+ $message = $('<div/>')
2571
+ .html(html)
2572
+ .addClass(className.message)
2573
+ .appendTo($menu)
2574
+ ;
2575
+ }
2576
+ },
2577
+ optionValue: function(value) {
2578
+ var
2579
+ escapedValue = module.escape.value(value),
2580
+ $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
2581
+ hasOption = ($option.length > 0)
2582
+ ;
2583
+ if(hasOption) {
2584
+ return;
2585
+ }
2586
+ // temporarily disconnect observer
2587
+ module.disconnect.selectObserver();
2588
+ if( module.is.single() ) {
2589
+ module.verbose('Removing previous user addition');
2590
+ $input.find('option.' + className.addition).remove();
2591
+ }
2592
+ $('<option/>')
2593
+ .prop('value', escapedValue)
2594
+ .addClass(className.addition)
2595
+ .html(value)
2596
+ .appendTo($input)
2597
+ ;
2598
+ module.verbose('Adding user addition as an <option>', value);
2599
+ module.observe.select();
2600
+ },
2601
+ userSuggestion: function(value) {
2602
+ var
2603
+ $addition = $menu.children(selector.addition),
2604
+ $existingItem = module.get.item(value),
2605
+ alreadyHasValue = $existingItem && $existingItem.not(selector.addition).length,
2606
+ hasUserSuggestion = $addition.length > 0,
2607
+ html
2608
+ ;
2609
+ if(settings.useLabels && module.has.maxSelections()) {
2610
+ return;
2611
+ }
2612
+ if(value === '' || alreadyHasValue) {
2613
+ $addition.remove();
2614
+ return;
2615
+ }
2616
+ if(hasUserSuggestion) {
2617
+ $addition
2618
+ .data(metadata.value, value)
2619
+ .data(metadata.text, value)
2620
+ .attr('data-' + metadata.value, value)
2621
+ .attr('data-' + metadata.text, value)
2622
+ .removeClass(className.filtered)
2623
+ ;
2624
+ if(!settings.hideAdditions) {
2625
+ html = settings.templates.addition( module.add.variables(message.addResult, value) );
2626
+ $addition
2627
+ .html(html)
2628
+ ;
2629
+ }
2630
+ module.verbose('Replacing user suggestion with new value', $addition);
2631
+ }
2632
+ else {
2633
+ $addition = module.create.userChoice(value);
2634
+ $addition
2635
+ .prependTo($menu)
2636
+ ;
2637
+ module.verbose('Adding item choice to menu corresponding with user choice addition', $addition);
2638
+ }
2639
+ if(!settings.hideAdditions || module.is.allFiltered()) {
2640
+ $addition
2641
+ .addClass(className.selected)
2642
+ .siblings()
2643
+ .removeClass(className.selected)
2644
+ ;
2645
+ }
2646
+ module.refreshItems();
2647
+ },
2648
+ variables: function(message, term) {
2649
+ var
2650
+ hasCount = (message.search('{count}') !== -1),
2651
+ hasMaxCount = (message.search('{maxCount}') !== -1),
2652
+ hasTerm = (message.search('{term}') !== -1),
2653
+ values,
2654
+ count,
2655
+ query
2656
+ ;
2657
+ module.verbose('Adding templated variables to message', message);
2658
+ if(hasCount) {
2659
+ count = module.get.selectionCount();
2660
+ message = message.replace('{count}', count);
2661
+ }
2662
+ if(hasMaxCount) {
2663
+ count = module.get.selectionCount();
2664
+ message = message.replace('{maxCount}', settings.maxSelections);
2665
+ }
2666
+ if(hasTerm) {
2667
+ query = term || module.get.query();
2668
+ message = message.replace('{term}', query);
2669
+ }
2670
+ return message;
2671
+ },
2672
+ value: function(addedValue, addedText, $selectedItem) {
2673
+ var
2674
+ currentValue = module.get.values(),
2675
+ newValue
2676
+ ;
2677
+ if(addedValue === '') {
2678
+ module.debug('Cannot select blank values from multiselect');
2679
+ return;
2680
+ }
2681
+ // extend current array
2682
+ if($.isArray(currentValue)) {
2683
+ newValue = currentValue.concat([addedValue]);
2684
+ newValue = module.get.uniqueArray(newValue);
2685
+ }
2686
+ else {
2687
+ newValue = [addedValue];
2688
+ }
2689
+ // add values
2690
+ if( module.has.selectInput() ) {
2691
+ if(module.can.extendSelect()) {
2692
+ module.debug('Adding value to select', addedValue, newValue, $input);
2693
+ module.add.optionValue(addedValue);
2694
+ }
2695
+ }
2696
+ else {
2697
+ newValue = newValue.join(settings.delimiter);
2698
+ module.debug('Setting hidden input to delimited value', newValue, $input);
2699
+ }
2700
+
2701
+ if(settings.fireOnInit === false && module.is.initialLoad()) {
2702
+ module.verbose('Skipping onadd callback on initial load', settings.onAdd);
2703
+ }
2704
+ else {
2705
+ settings.onAdd.call(element, addedValue, addedText, $selectedItem);
2706
+ }
2707
+ module.set.value(newValue, addedValue, addedText, $selectedItem);
2708
+ module.check.maxSelections();
2709
+ }
2710
+ },
2711
+
2712
+ remove: {
2713
+ active: function() {
2714
+ $module.removeClass(className.active);
2715
+ },
2716
+ activeLabel: function() {
2717
+ $module.find(selector.label).removeClass(className.active);
2718
+ },
2719
+ empty: function() {
2720
+ $module.removeClass(className.empty);
2721
+ },
2722
+ loading: function() {
2723
+ $module.removeClass(className.loading);
2724
+ },
2725
+ initialLoad: function() {
2726
+ initialLoad = false;
2727
+ },
2728
+ upward: function($menu) {
2729
+ var $element = $menu || $module;
2730
+ $element.removeClass(className.upward);
2731
+ },
2732
+ visible: function() {
2733
+ $module.removeClass(className.visible);
2734
+ },
2735
+ activeItem: function() {
2736
+ $item.removeClass(className.active);
2737
+ },
2738
+ filteredItem: function() {
2739
+ if(settings.useLabels && module.has.maxSelections() ) {
2740
+ return;
2741
+ }
2742
+ if(settings.useLabels && module.is.multiple()) {
2743
+ $item.not('.' + className.active).removeClass(className.filtered);
2744
+ }
2745
+ else {
2746
+ $item.removeClass(className.filtered);
2747
+ }
2748
+ module.remove.empty();
2749
+ },
2750
+ optionValue: function(value) {
2751
+ var
2752
+ escapedValue = module.escape.value(value),
2753
+ $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
2754
+ hasOption = ($option.length > 0)
2755
+ ;
2756
+ if(!hasOption || !$option.hasClass(className.addition)) {
2757
+ return;
2758
+ }
2759
+ // temporarily disconnect observer
2760
+ if(selectObserver) {
2761
+ selectObserver.disconnect();
2762
+ module.verbose('Temporarily disconnecting mutation observer');
2763
+ }
2764
+ $option.remove();
2765
+ module.verbose('Removing user addition as an <option>', escapedValue);
2766
+ if(selectObserver) {
2767
+ selectObserver.observe($input[0], {
2768
+ childList : true,
2769
+ subtree : true
2770
+ });
2771
+ }
2772
+ },
2773
+ message: function() {
2774
+ $menu.children(selector.message).remove();
2775
+ },
2776
+ searchWidth: function() {
2777
+ $search.css('width', '');
2778
+ },
2779
+ searchTerm: function() {
2780
+ module.verbose('Cleared search term');
2781
+ $search.val('');
2782
+ module.set.filtered();
2783
+ },
2784
+ userAddition: function() {
2785
+ $item.filter(selector.addition).remove();
2786
+ },
2787
+ selected: function(value, $selectedItem) {
2788
+ $selectedItem = (settings.allowAdditions)
2789
+ ? $selectedItem || module.get.itemWithAdditions(value)
2790
+ : $selectedItem || module.get.item(value)
2791
+ ;
2792
+
2793
+ if(!$selectedItem) {
2794
+ return false;
2795
+ }
2796
+
2797
+ $selectedItem
2798
+ .each(function() {
2799
+ var
2800
+ $selected = $(this),
2801
+ selectedText = module.get.choiceText($selected),
2802
+ selectedValue = module.get.choiceValue($selected, selectedText)
2803
+ ;
2804
+ if(module.is.multiple()) {
2805
+ if(settings.useLabels) {
2806
+ module.remove.value(selectedValue, selectedText, $selected);
2807
+ module.remove.label(selectedValue);
2808
+ }
2809
+ else {
2810
+ module.remove.value(selectedValue, selectedText, $selected);
2811
+ if(module.get.selectionCount() === 0) {
2812
+ module.set.placeholderText();
2813
+ }
2814
+ else {
2815
+ module.set.text(module.add.variables(message.count));
2816
+ }
2817
+ }
2818
+ }
2819
+ else {
2820
+ module.remove.value(selectedValue, selectedText, $selected);
2821
+ }
2822
+ $selected
2823
+ .removeClass(className.filtered)
2824
+ .removeClass(className.active)
2825
+ ;
2826
+ if(settings.useLabels) {
2827
+ $selected.removeClass(className.selected);
2828
+ }
2829
+ })
2830
+ ;
2831
+ },
2832
+ selectedItem: function() {
2833
+ $item.removeClass(className.selected);
2834
+ },
2835
+ value: function(removedValue, removedText, $removedItem) {
2836
+ var
2837
+ values = module.get.values(),
2838
+ newValue
2839
+ ;
2840
+ if( module.has.selectInput() ) {
2841
+ module.verbose('Input is <select> removing selected option', removedValue);
2842
+ newValue = module.remove.arrayValue(removedValue, values);
2843
+ module.remove.optionValue(removedValue);
2844
+ }
2845
+ else {
2846
+ module.verbose('Removing from delimited values', removedValue);
2847
+ newValue = module.remove.arrayValue(removedValue, values);
2848
+ newValue = newValue.join(settings.delimiter);
2849
+ }
2850
+ if(settings.fireOnInit === false && module.is.initialLoad()) {
2851
+ module.verbose('No callback on initial load', settings.onRemove);
2852
+ }
2853
+ else {
2854
+ settings.onRemove.call(element, removedValue, removedText, $removedItem);
2855
+ }
2856
+ module.set.value(newValue, removedText, $removedItem);
2857
+ module.check.maxSelections();
2858
+ },
2859
+ arrayValue: function(removedValue, values) {
2860
+ if( !$.isArray(values) ) {
2861
+ values = [values];
2862
+ }
2863
+ values = $.grep(values, function(value){
2864
+ return (removedValue != value);
2865
+ });
2866
+ module.verbose('Removed value from delimited string', removedValue, values);
2867
+ return values;
2868
+ },
2869
+ label: function(value, shouldAnimate) {
2870
+ var
2871
+ $labels = $module.find(selector.label),
2872
+ $removedLabel = $labels.filter('[data-' + metadata.value + '="' + module.escape.string(value) +'"]')
2873
+ ;
2874
+ module.verbose('Removing label', $removedLabel);
2875
+ $removedLabel.remove();
2876
+ },
2877
+ activeLabels: function($activeLabels) {
2878
+ $activeLabels = $activeLabels || $module.find(selector.label).filter('.' + className.active);
2879
+ module.verbose('Removing active label selections', $activeLabels);
2880
+ module.remove.labels($activeLabels);
2881
+ },
2882
+ labels: function($labels) {
2883
+ $labels = $labels || $module.find(selector.label);
2884
+ module.verbose('Removing labels', $labels);
2885
+ $labels
2886
+ .each(function(){
2887
+ var
2888
+ $label = $(this),
2889
+ value = $label.data(metadata.value),
2890
+ stringValue = (value !== undefined)
2891
+ ? String(value)
2892
+ : value,
2893
+ isUserValue = module.is.userValue(stringValue)
2894
+ ;
2895
+ if(settings.onLabelRemove.call($label, value) === false) {
2896
+ module.debug('Label remove callback cancelled removal');
2897
+ return;
2898
+ }
2899
+ module.remove.message();
2900
+ if(isUserValue) {
2901
+ module.remove.value(stringValue);
2902
+ module.remove.label(stringValue);
2903
+ }
2904
+ else {
2905
+ // selected will also remove label
2906
+ module.remove.selected(stringValue);
2907
+ }
2908
+ })
2909
+ ;
2910
+ },
2911
+ tabbable: function() {
2912
+ if( module.is.searchSelection() ) {
2913
+ module.debug('Searchable dropdown initialized');
2914
+ $search
2915
+ .removeAttr('tabindex')
2916
+ ;
2917
+ $menu
2918
+ .removeAttr('tabindex')
2919
+ ;
2920
+ }
2921
+ else {
2922
+ module.debug('Simple selection dropdown initialized');
2923
+ $module
2924
+ .removeAttr('tabindex')
2925
+ ;
2926
+ $menu
2927
+ .removeAttr('tabindex')
2928
+ ;
2929
+ }
2930
+ }
2931
+ },
2932
+
2933
+ has: {
2934
+ menuSearch: function() {
2935
+ return (module.has.search() && $search.closest($menu).length > 0);
2936
+ },
2937
+ search: function() {
2938
+ return ($search.length > 0);
2939
+ },
2940
+ sizer: function() {
2941
+ return ($sizer.length > 0);
2942
+ },
2943
+ selectInput: function() {
2944
+ return ( $input.is('select') );
2945
+ },
2946
+ minCharacters: function(searchTerm) {
2947
+ if(settings.minCharacters) {
2948
+ searchTerm = (searchTerm !== undefined)
2949
+ ? String(searchTerm)
2950
+ : String(module.get.query())
2951
+ ;
2952
+ return (searchTerm.length >= settings.minCharacters);
2953
+ }
2954
+ return true;
2955
+ },
2956
+ firstLetter: function($item, letter) {
2957
+ var
2958
+ text,
2959
+ firstLetter
2960
+ ;
2961
+ if(!$item || $item.length === 0 || typeof letter !== 'string') {
2962
+ return false;
2963
+ }
2964
+ text = module.get.choiceText($item, false);
2965
+ letter = letter.toLowerCase();
2966
+ firstLetter = String(text).charAt(0).toLowerCase();
2967
+ return (letter == firstLetter);
2968
+ },
2969
+ input: function() {
2970
+ return ($input.length > 0);
2971
+ },
2972
+ items: function() {
2973
+ return ($item.length > 0);
2974
+ },
2975
+ menu: function() {
2976
+ return ($menu.length > 0);
2977
+ },
2978
+ message: function() {
2979
+ return ($menu.children(selector.message).length !== 0);
2980
+ },
2981
+ label: function(value) {
2982
+ var
2983
+ escapedValue = module.escape.value(value),
2984
+ $labels = $module.find(selector.label)
2985
+ ;
2986
+ return ($labels.filter('[data-' + metadata.value + '="' + module.escape.string(escapedValue) +'"]').length > 0);
2987
+ },
2988
+ maxSelections: function() {
2989
+ return (settings.maxSelections && module.get.selectionCount() >= settings.maxSelections);
2990
+ },
2991
+ allResultsFiltered: function() {
2992
+ var
2993
+ $normalResults = $item.not(selector.addition)
2994
+ ;
2995
+ return ($normalResults.filter(selector.unselectable).length === $normalResults.length);
2996
+ },
2997
+ userSuggestion: function() {
2998
+ return ($menu.children(selector.addition).length > 0);
2999
+ },
3000
+ query: function() {
3001
+ return (module.get.query() !== '');
3002
+ },
3003
+ value: function(value) {
3004
+ var
3005
+ values = module.get.values(),
3006
+ hasValue = $.isArray(values)
3007
+ ? values && ($.inArray(value, values) !== -1)
3008
+ : (values == value)
3009
+ ;
3010
+ return (hasValue)
3011
+ ? true
3012
+ : false
3013
+ ;
3014
+ }
3015
+ },
3016
+
3017
+ is: {
3018
+ active: function() {
3019
+ return $module.hasClass(className.active);
3020
+ },
3021
+ bubbledLabelClick: function(event) {
3022
+ return $(event.target).is('select, input') && $module.closest('label').length > 0;
3023
+ },
3024
+ bubbledIconClick: function(event) {
3025
+ return $(event.target).closest($icon).length > 0;
3026
+ },
3027
+ alreadySetup: function() {
3028
+ return ($module.is('select') && $module.parent(selector.dropdown).length > 0 && $module.prev().length === 0);
3029
+ },
3030
+ animating: function($subMenu) {
3031
+ return ($subMenu)
3032
+ ? $subMenu.transition && $subMenu.transition('is animating')
3033
+ : $menu.transition && $menu.transition('is animating')
3034
+ ;
3035
+ },
3036
+ disabled: function() {
3037
+ return $module.hasClass(className.disabled);
3038
+ },
3039
+ focused: function() {
3040
+ return (document.activeElement === $module[0]);
3041
+ },
3042
+ focusedOnSearch: function() {
3043
+ return (document.activeElement === $search[0]);
3044
+ },
3045
+ allFiltered: function() {
3046
+ return( (module.is.multiple() || module.has.search()) && !(settings.hideAdditions == false && module.has.userSuggestion()) && !module.has.message() && module.has.allResultsFiltered() );
3047
+ },
3048
+ hidden: function($subMenu) {
3049
+ return !module.is.visible($subMenu);
3050
+ },
3051
+ initialLoad: function() {
3052
+ return initialLoad;
3053
+ },
3054
+ onScreen: function($subMenu) {
3055
+ var
3056
+ $currentMenu = $subMenu || $menu,
3057
+ canOpenDownward = true,
3058
+ onScreen = {},
3059
+ calculations
3060
+ ;
3061
+ $currentMenu.addClass(className.loading);
3062
+ calculations = {
3063
+ context: {
3064
+ scrollTop : $context.scrollTop(),
3065
+ height : $context.outerHeight()
3066
+ },
3067
+ menu : {
3068
+ offset: $currentMenu.offset(),
3069
+ height: $currentMenu.outerHeight()
3070
+ }
3071
+ };
3072
+ onScreen = {
3073
+ above : (calculations.context.scrollTop) <= calculations.menu.offset.top - calculations.menu.height,
3074
+ below : (calculations.context.scrollTop + calculations.context.height) >= calculations.menu.offset.top + calculations.menu.height
3075
+ };
3076
+ if(onScreen.below) {
3077
+ module.verbose('Dropdown can fit in context downward', onScreen);
3078
+ canOpenDownward = true;
3079
+ }
3080
+ else if(!onScreen.below && !onScreen.above) {
3081
+ module.verbose('Dropdown cannot fit in either direction, favoring downward', onScreen);
3082
+ canOpenDownward = true;
3083
+ }
3084
+ else {
3085
+ module.verbose('Dropdown cannot fit below, opening upward', onScreen);
3086
+ canOpenDownward = false;
3087
+ }
3088
+ $currentMenu.removeClass(className.loading);
3089
+ return canOpenDownward;
3090
+ },
3091
+ inObject: function(needle, object) {
3092
+ var
3093
+ found = false
3094
+ ;
3095
+ $.each(object, function(index, property) {
3096
+ if(property == needle) {
3097
+ found = true;
3098
+ return true;
3099
+ }
3100
+ });
3101
+ return found;
3102
+ },
3103
+ multiple: function() {
3104
+ return $module.hasClass(className.multiple);
3105
+ },
3106
+ remote: function() {
3107
+ return settings.apiSettings && module.can.useAPI();
3108
+ },
3109
+ single: function() {
3110
+ return !module.is.multiple();
3111
+ },
3112
+ selectMutation: function(mutations) {
3113
+ var
3114
+ selectChanged = false
3115
+ ;
3116
+ $.each(mutations, function(index, mutation) {
3117
+ if(mutation.target && $(mutation.target).is('select')) {
3118
+ selectChanged = true;
3119
+ return true;
3120
+ }
3121
+ });
3122
+ return selectChanged;
3123
+ },
3124
+ search: function() {
3125
+ return $module.hasClass(className.search);
3126
+ },
3127
+ searchSelection: function() {
3128
+ return ( module.has.search() && $search.parent(selector.dropdown).length === 1 );
3129
+ },
3130
+ selection: function() {
3131
+ return $module.hasClass(className.selection);
3132
+ },
3133
+ userValue: function(value) {
3134
+ return ($.inArray(value, module.get.userValues()) !== -1);
3135
+ },
3136
+ upward: function($menu) {
3137
+ var $element = $menu || $module;
3138
+ return $element.hasClass(className.upward);
3139
+ },
3140
+ visible: function($subMenu) {
3141
+ return ($subMenu)
3142
+ ? $subMenu.hasClass(className.visible)
3143
+ : $menu.hasClass(className.visible)
3144
+ ;
3145
+ }
3146
+ },
3147
+
3148
+ can: {
3149
+ activate: function($item) {
3150
+ if(settings.useLabels) {
3151
+ return true;
3152
+ }
3153
+ if(!module.has.maxSelections()) {
3154
+ return true;
3155
+ }
3156
+ if(module.has.maxSelections() && $item.hasClass(className.active)) {
3157
+ return true;
3158
+ }
3159
+ return false;
3160
+ },
3161
+ click: function() {
3162
+ return (hasTouch || settings.on == 'click');
3163
+ },
3164
+ extendSelect: function() {
3165
+ return settings.allowAdditions || settings.apiSettings;
3166
+ },
3167
+ show: function() {
3168
+ return !module.is.disabled() && (module.has.items() || module.has.message());
3169
+ },
3170
+ useAPI: function() {
3171
+ return $.fn.api !== undefined;
3172
+ }
3173
+ },
3174
+
3175
+ animate: {
3176
+ show: function(callback, $subMenu) {
3177
+ var
3178
+ $currentMenu = $subMenu || $menu,
3179
+ start = ($subMenu)
3180
+ ? function() {}
3181
+ : function() {
3182
+ module.hideSubMenus();
3183
+ module.hideOthers();
3184
+ module.set.active();
3185
+ },
3186
+ transition
3187
+ ;
3188
+ callback = $.isFunction(callback)
3189
+ ? callback
3190
+ : function(){}
3191
+ ;
3192
+ module.verbose('Doing menu show animation', $currentMenu);
3193
+ module.set.direction($subMenu);
3194
+ transition = module.get.transition($subMenu);
3195
+ if( module.is.selection() ) {
3196
+ module.set.scrollPosition(module.get.selectedItem(), true);
3197
+ }
3198
+ if( module.is.hidden($currentMenu) || module.is.animating($currentMenu) ) {
3199
+ if(transition == 'none') {
3200
+ start();
3201
+ $currentMenu.transition('show');
3202
+ callback.call(element);
3203
+ }
3204
+ else if($.fn.transition !== undefined && $module.transition('is supported')) {
3205
+ $currentMenu
3206
+ .transition({
3207
+ animation : transition + ' in',
3208
+ debug : settings.debug,
3209
+ verbose : settings.verbose,
3210
+ duration : settings.duration,
3211
+ queue : true,
3212
+ onStart : start,
3213
+ onComplete : function() {
3214
+ callback.call(element);
3215
+ }
3216
+ })
3217
+ ;
3218
+ }
3219
+ else {
3220
+ module.error(error.noTransition, transition);
3221
+ }
3222
+ }
3223
+ },
3224
+ hide: function(callback, $subMenu) {
3225
+ var
3226
+ $currentMenu = $subMenu || $menu,
3227
+ duration = ($subMenu)
3228
+ ? (settings.duration * 0.9)
3229
+ : settings.duration,
3230
+ start = ($subMenu)
3231
+ ? function() {}
3232
+ : function() {
3233
+ if( module.can.click() ) {
3234
+ module.unbind.intent();
3235
+ }
3236
+ module.remove.active();
3237
+ },
3238
+ transition = module.get.transition($subMenu)
3239
+ ;
3240
+ callback = $.isFunction(callback)
3241
+ ? callback
3242
+ : function(){}
3243
+ ;
3244
+ if( module.is.visible($currentMenu) || module.is.animating($currentMenu) ) {
3245
+ module.verbose('Doing menu hide animation', $currentMenu);
3246
+
3247
+ if(transition == 'none') {
3248
+ start();
3249
+ $currentMenu.transition('hide');
3250
+ callback.call(element);
3251
+ }
3252
+ else if($.fn.transition !== undefined && $module.transition('is supported')) {
3253
+ $currentMenu
3254
+ .transition({
3255
+ animation : transition + ' out',
3256
+ duration : settings.duration,
3257
+ debug : settings.debug,
3258
+ verbose : settings.verbose,
3259
+ queue : true,
3260
+ onStart : start,
3261
+ onComplete : function() {
3262
+ if(settings.direction == 'auto') {
3263
+ module.remove.upward($subMenu);
3264
+ }
3265
+ callback.call(element);
3266
+ }
3267
+ })
3268
+ ;
3269
+ }
3270
+ else {
3271
+ module.error(error.transition);
3272
+ }
3273
+ }
3274
+ }
3275
+ },
3276
+
3277
+ hideAndClear: function() {
3278
+ module.remove.searchTerm();
3279
+ if( module.has.maxSelections() ) {
3280
+ return;
3281
+ }
3282
+ if(module.has.search()) {
3283
+ module.hide(function() {
3284
+ module.remove.filteredItem();
3285
+ });
3286
+ }
3287
+ else {
3288
+ module.hide();
3289
+ }
3290
+ },
3291
+
3292
+ delay: {
3293
+ show: function() {
3294
+ module.verbose('Delaying show event to ensure user intent');
3295
+ clearTimeout(module.timer);
3296
+ module.timer = setTimeout(module.show, settings.delay.show);
3297
+ },
3298
+ hide: function() {
3299
+ module.verbose('Delaying hide event to ensure user intent');
3300
+ clearTimeout(module.timer);
3301
+ module.timer = setTimeout(module.hide, settings.delay.hide);
3302
+ }
3303
+ },
3304
+
3305
+ escape: {
3306
+ value: function(value) {
3307
+ var
3308
+ multipleValues = $.isArray(value),
3309
+ stringValue = (typeof value === 'string'),
3310
+ isUnparsable = (!stringValue && !multipleValues),
3311
+ hasQuotes = (stringValue && value.search(regExp.quote) !== -1),
3312
+ values = []
3313
+ ;
3314
+ if(isUnparsable || !hasQuotes) {
3315
+ return value;
3316
+ }
3317
+ module.debug('Encoding quote values for use in select', value);
3318
+ if(multipleValues) {
3319
+ $.each(value, function(index, value){
3320
+ values.push(value.replace(regExp.quote, '&quot;'));
3321
+ });
3322
+ return values;
3323
+ }
3324
+ return value.replace(regExp.quote, '&quot;');
3325
+ },
3326
+ string: function(text) {
3327
+ text = String(text);
3328
+ return text.replace(regExp.escape, '\\$&');
3329
+ }
3330
+ },
3331
+
3332
+ setting: function(name, value) {
3333
+ module.debug('Changing setting', name, value);
3334
+ if( $.isPlainObject(name) ) {
3335
+ $.extend(true, settings, name);
3336
+ }
3337
+ else if(value !== undefined) {
3338
+ if($.isPlainObject(settings[name])) {
3339
+ $.extend(true, settings[name], value);
3340
+ }
3341
+ else {
3342
+ settings[name] = value;
3343
+ }
3344
+ }
3345
+ else {
3346
+ return settings[name];
3347
+ }
3348
+ },
3349
+ internal: function(name, value) {
3350
+ if( $.isPlainObject(name) ) {
3351
+ $.extend(true, module, name);
3352
+ }
3353
+ else if(value !== undefined) {
3354
+ module[name] = value;
3355
+ }
3356
+ else {
3357
+ return module[name];
3358
+ }
3359
+ },
3360
+ debug: function() {
3361
+ if(!settings.silent && settings.debug) {
3362
+ if(settings.performance) {
3363
+ module.performance.log(arguments);
3364
+ }
3365
+ else {
3366
+ module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
3367
+ module.debug.apply(console, arguments);
3368
+ }
3369
+ }
3370
+ },
3371
+ verbose: function() {
3372
+ if(!settings.silent && settings.verbose && settings.debug) {
3373
+ if(settings.performance) {
3374
+ module.performance.log(arguments);
3375
+ }
3376
+ else {
3377
+ module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
3378
+ module.verbose.apply(console, arguments);
3379
+ }
3380
+ }
3381
+ },
3382
+ error: function() {
3383
+ if(!settings.silent) {
3384
+ module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
3385
+ module.error.apply(console, arguments);
3386
+ }
3387
+ },
3388
+ performance: {
3389
+ log: function(message) {
3390
+ var
3391
+ currentTime,
3392
+ executionTime,
3393
+ previousTime
3394
+ ;
3395
+ if(settings.performance) {
3396
+ currentTime = new Date().getTime();
3397
+ previousTime = time || currentTime;
3398
+ executionTime = currentTime - previousTime;
3399
+ time = currentTime;
3400
+ performance.push({
3401
+ 'Name' : message[0],
3402
+ 'Arguments' : [].slice.call(message, 1) || '',
3403
+ 'Element' : element,
3404
+ 'Execution Time' : executionTime
3405
+ });
3406
+ }
3407
+ clearTimeout(module.performance.timer);
3408
+ module.performance.timer = setTimeout(module.performance.display, 500);
3409
+ },
3410
+ display: function() {
3411
+ var
3412
+ title = settings.name + ':',
3413
+ totalTime = 0
3414
+ ;
3415
+ time = false;
3416
+ clearTimeout(module.performance.timer);
3417
+ $.each(performance, function(index, data) {
3418
+ totalTime += data['Execution Time'];
3419
+ });
3420
+ title += ' ' + totalTime + 'ms';
3421
+ if(moduleSelector) {
3422
+ title += ' \'' + moduleSelector + '\'';
3423
+ }
3424
+ if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
3425
+ console.groupCollapsed(title);
3426
+ if(console.table) {
3427
+ console.table(performance);
3428
+ }
3429
+ else {
3430
+ $.each(performance, function(index, data) {
3431
+ console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
3432
+ });
3433
+ }
3434
+ console.groupEnd();
3435
+ }
3436
+ performance = [];
3437
+ }
3438
+ },
3439
+ invoke: function(query, passedArguments, context) {
3440
+ var
3441
+ object = instance,
3442
+ maxDepth,
3443
+ found,
3444
+ response
3445
+ ;
3446
+ passedArguments = passedArguments || queryArguments;
3447
+ context = element || context;
3448
+ if(typeof query == 'string' && object !== undefined) {
3449
+ query = query.split(/[\. ]/);
3450
+ maxDepth = query.length - 1;
3451
+ $.each(query, function(depth, value) {
3452
+ var camelCaseValue = (depth != maxDepth)
3453
+ ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
3454
+ : query
3455
+ ;
3456
+ if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
3457
+ object = object[camelCaseValue];
3458
+ }
3459
+ else if( object[camelCaseValue] !== undefined ) {
3460
+ found = object[camelCaseValue];
3461
+ return false;
3462
+ }
3463
+ else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
3464
+ object = object[value];
3465
+ }
3466
+ else if( object[value] !== undefined ) {
3467
+ found = object[value];
3468
+ return false;
3469
+ }
3470
+ else {
3471
+ module.error(error.method, query);
3472
+ return false;
3473
+ }
3474
+ });
3475
+ }
3476
+ if ( $.isFunction( found ) ) {
3477
+ response = found.apply(context, passedArguments);
3478
+ }
3479
+ else if(found !== undefined) {
3480
+ response = found;
3481
+ }
3482
+ if($.isArray(returnedValue)) {
3483
+ returnedValue.push(response);
3484
+ }
3485
+ else if(returnedValue !== undefined) {
3486
+ returnedValue = [returnedValue, response];
3487
+ }
3488
+ else if(response !== undefined) {
3489
+ returnedValue = response;
3490
+ }
3491
+ return found;
3492
+ }
3493
+ };
3494
+
3495
+ if(methodInvoked) {
3496
+ if(instance === undefined) {
3497
+ module.initialize();
3498
+ }
3499
+ module.invoke(query);
3500
+ }
3501
+ else {
3502
+ if(instance !== undefined) {
3503
+ instance.invoke('destroy');
3504
+ }
3505
+ module.initialize();
3506
+ }
3507
+ })
3508
+ ;
3509
+ return (returnedValue !== undefined)
3510
+ ? returnedValue
3511
+ : $allModules
3512
+ ;
3513
+ };
3514
+
3515
+ $.fn.dropdown.settings = {
3516
+
3517
+ silent : false,
3518
+ debug : false,
3519
+ verbose : false,
3520
+ performance : true,
3521
+
3522
+ on : 'click', // what event should show menu action on item selection
3523
+ action : 'activate', // action on item selection (nothing, activate, select, combo, hide, function(){})
3524
+
3525
+
3526
+ apiSettings : false,
3527
+ selectOnKeydown : true, // Whether selection should occur automatically when keyboard shortcuts used
3528
+ minCharacters : 0, // Minimum characters required to trigger API call
3529
+
3530
+ filterRemoteData : false, // Whether API results should be filtered after being returned for query term
3531
+ saveRemoteData : true, // Whether remote name/value pairs should be stored in sessionStorage to allow remote data to be restored on page refresh
3532
+
3533
+ throttle : 200, // How long to wait after last user input to search remotely
3534
+
3535
+ context : window, // Context to use when determining if on screen
3536
+ direction : 'auto', // Whether dropdown should always open in one direction
3537
+ keepOnScreen : true, // Whether dropdown should check whether it is on screen before showing
3538
+
3539
+ match : 'both', // what to match against with search selection (both, text, or label)
3540
+ fullTextSearch : false, // search anywhere in value (set to 'exact' to require exact matches)
3541
+
3542
+ placeholder : 'auto', // whether to convert blank <select> values to placeholder text
3543
+ preserveHTML : true, // preserve html when selecting value
3544
+ sortSelect : false, // sort selection on init
3545
+
3546
+ forceSelection : true, // force a choice on blur with search selection
3547
+
3548
+ allowAdditions : false, // whether multiple select should allow user added values
3549
+ hideAdditions : true, // whether or not to hide special message prompting a user they can enter a value
3550
+
3551
+ maxSelections : false, // When set to a number limits the number of selections to this count
3552
+ useLabels : true, // whether multiple select should filter currently active selections from choices
3553
+ delimiter : ',', // when multiselect uses normal <input> the values will be delimited with this character
3554
+
3555
+ showOnFocus : true, // show menu on focus
3556
+ allowReselection : false, // whether current value should trigger callbacks when reselected
3557
+ allowTab : true, // add tabindex to element
3558
+ allowCategorySelection : false, // allow elements with sub-menus to be selected
3559
+
3560
+ fireOnInit : false, // Whether callbacks should fire when initializing dropdown values
3561
+
3562
+ transition : 'auto', // auto transition will slide down or up based on direction
3563
+ duration : 200, // duration of transition
3564
+
3565
+ glyphWidth : 1.037, // widest glyph width in em (W is 1.037 em) used to calculate multiselect input width
3566
+
3567
+ // label settings on multi-select
3568
+ label: {
3569
+ transition : 'scale',
3570
+ duration : 200,
3571
+ variation : false
3572
+ },
3573
+
3574
+ // delay before event
3575
+ delay : {
3576
+ hide : 300,
3577
+ show : 200,
3578
+ search : 20,
3579
+ touch : 50
3580
+ },
3581
+
3582
+ /* Callbacks */
3583
+ onChange : function(value, text, $selected){},
3584
+ onAdd : function(value, text, $selected){},
3585
+ onRemove : function(value, text, $selected){},
3586
+
3587
+ onLabelSelect : function($selectedLabels){},
3588
+ onLabelCreate : function(value, text) { return $(this); },
3589
+ onLabelRemove : function(value) { return true; },
3590
+ onNoResults : function(searchTerm) { return true; },
3591
+ onShow : function(){},
3592
+ onHide : function(){},
3593
+
3594
+ /* Component */
3595
+ name : 'Dropdown',
3596
+ namespace : 'dropdown',
3597
+
3598
+ message: {
3599
+ addResult : 'Add <b>{term}</b>',
3600
+ count : '{count} selected',
3601
+ maxSelections : 'Max {maxCount} selections',
3602
+ noResults : 'No results found.',
3603
+ serverError : 'There was an error contacting the server'
3604
+ },
3605
+
3606
+ error : {
3607
+ action : 'You called a dropdown action that was not defined',
3608
+ alreadySetup : 'Once a select has been initialized behaviors must be called on the created ui dropdown',
3609
+ labels : 'Allowing user additions currently requires the use of labels.',
3610
+ missingMultiple : '<select> requires multiple property to be set to correctly preserve multiple values',
3611
+ method : 'The method you called is not defined.',
3612
+ noAPI : 'The API module is required to load resources remotely',
3613
+ noStorage : 'Saving remote data requires session storage',
3614
+ noTransition : 'This module requires ui transitions <https://github.com/Semantic-Org/UI-Transition>'
3615
+ },
3616
+
3617
+ regExp : {
3618
+ escape : /[-[\]{}()*+?.,\\^$|#\s]/g,
3619
+ quote : /"/g
3620
+ },
3621
+
3622
+ metadata : {
3623
+ defaultText : 'defaultText',
3624
+ defaultValue : 'defaultValue',
3625
+ placeholderText : 'placeholder',
3626
+ text : 'text',
3627
+ value : 'value'
3628
+ },
3629
+
3630
+ // property names for remote query
3631
+ fields: {
3632
+ remoteValues : 'results', // grouping for api results
3633
+ values : 'values', // grouping for all dropdown values
3634
+ disabled : 'disabled', // whether value should be disabled
3635
+ name : 'name', // displayed dropdown text
3636
+ value : 'value', // actual dropdown value
3637
+ text : 'text' // displayed text when selected
3638
+ },
3639
+
3640
+ keys : {
3641
+ backspace : 8,
3642
+ delimiter : 188, // comma
3643
+ deleteKey : 46,
3644
+ enter : 13,
3645
+ escape : 27,
3646
+ pageUp : 33,
3647
+ pageDown : 34,
3648
+ leftArrow : 37,
3649
+ upArrow : 38,
3650
+ rightArrow : 39,
3651
+ downArrow : 40
3652
+ },
3653
+
3654
+ selector : {
3655
+ addition : '.addition',
3656
+ dropdown : '.ui.dropdown',
3657
+ hidden : '.hidden',
3658
+ icon : '> .dropdown.icon',
3659
+ input : '> input[type="hidden"], > select',
3660
+ item : '.item',
3661
+ label : '> .label',
3662
+ remove : '> .label > .delete.icon',
3663
+ siblingLabel : '.label',
3664
+ menu : '.menu',
3665
+ message : '.message',
3666
+ menuIcon : '.dropdown.icon',
3667
+ search : 'input.search, .menu > .search > input, .menu input.search',
3668
+ sizer : '> input.sizer',
3669
+ text : '> .text:not(.icon)',
3670
+ unselectable : '.disabled, .filtered'
3671
+ },
3672
+
3673
+ className : {
3674
+ active : 'active',
3675
+ addition : 'addition',
3676
+ animating : 'animating',
3677
+ disabled : 'disabled',
3678
+ empty : 'empty',
3679
+ dropdown : 'ui dropdown',
3680
+ filtered : 'filtered',
3681
+ hidden : 'hidden transition',
3682
+ item : 'item',
3683
+ label : 'ui label',
3684
+ loading : 'loading',
3685
+ menu : 'menu',
3686
+ message : 'message',
3687
+ multiple : 'multiple',
3688
+ placeholder : 'default',
3689
+ sizer : 'sizer',
3690
+ search : 'search',
3691
+ selected : 'selected',
3692
+ selection : 'selection',
3693
+ upward : 'upward',
3694
+ visible : 'visible'
3695
+ }
3696
+
3697
+ };
3698
+
3699
+ /* Templates */
3700
+ $.fn.dropdown.settings.templates = {
3701
+
3702
+ // generates dropdown from select values
3703
+ dropdown: function(select) {
3704
+ var
3705
+ placeholder = select.placeholder || false,
3706
+ values = select.values || {},
3707
+ html = ''
3708
+ ;
3709
+ html += '<i class="dropdown icon"></i>';
3710
+ if(select.placeholder) {
3711
+ html += '<div class="default text">' + placeholder + '</div>';
3712
+ }
3713
+ else {
3714
+ html += '<div class="text"></div>';
3715
+ }
3716
+ html += '<div class="menu">';
3717
+ $.each(select.values, function(index, option) {
3718
+ html += (option.disabled)
3719
+ ? '<div class="disabled item" data-value="' + option.value + '">' + option.name + '</div>'
3720
+ : '<div class="item" data-value="' + option.value + '">' + option.name + '</div>'
3721
+ ;
3722
+ });
3723
+ html += '</div>';
3724
+ return html;
3725
+ },
3726
+
3727
+ // generates just menu from select
3728
+ menu: function(response, fields) {
3729
+ var
3730
+ values = response[fields.values] || {},
3731
+ html = ''
3732
+ ;
3733
+ $.each(values, function(index, option) {
3734
+ var
3735
+ maybeText = (option[fields.text])
3736
+ ? 'data-text="' + option[fields.text] + '"'
3737
+ : '',
3738
+ maybeDisabled = (option[fields.disabled])
3739
+ ? 'disabled '
3740
+ : ''
3741
+ ;
3742
+ html += '<div class="'+ maybeDisabled +'item" data-value="' + option[fields.value] + '"' + maybeText + '>'
3743
+ html += option[fields.name];
3744
+ html += '</div>';
3745
+ });
3746
+ return html;
3747
+ },
3748
+
3749
+ // generates label for multiselect
3750
+ label: function(value, text) {
3751
+ return text + '<i class="delete icon"></i>';
3752
+ },
3753
+
3754
+
3755
+ // generates messages like "No results"
3756
+ message: function(message) {
3757
+ return message;
3758
+ },
3759
+
3760
+ // generates user addition to selection menu
3761
+ addition: function(choice) {
3762
+ return choice;
3763
+ }
3764
+
3765
+ };
3766
+
3767
+ })( jQuery, window, document );