fomantic-ui-sass 2.6.4

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