You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2387 lines
80 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
9 years ago
10 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
9 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /*!
  2. * # Semantic UI 2.0.0 - Dropdown
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Copyright 2014 Contributors
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ( $, window, document, undefined ) {
  12. "use strict";
  13. $.fn.dropdown = function(parameters) {
  14. var
  15. $allModules = $(this),
  16. $document = $(document),
  17. moduleSelector = $allModules.selector || '',
  18. hasTouch = ('ontouchstart' in document.documentElement),
  19. time = new Date().getTime(),
  20. performance = [],
  21. query = arguments[0],
  22. methodInvoked = (typeof query == 'string'),
  23. queryArguments = [].slice.call(arguments, 1),
  24. returnedValue
  25. ;
  26. $allModules
  27. .each(function() {
  28. var
  29. settings = ( $.isPlainObject(parameters) )
  30. ? $.extend(true, {}, $.fn.dropdown.settings, parameters)
  31. : $.extend({}, $.fn.dropdown.settings),
  32. className = settings.className,
  33. metadata = settings.metadata,
  34. namespace = settings.namespace,
  35. selector = settings.selector,
  36. error = settings.error,
  37. templates = settings.templates,
  38. eventNamespace = '.' + namespace,
  39. moduleNamespace = 'module-' + namespace,
  40. $module = $(this),
  41. $text = $module.find(selector.text),
  42. $search = $module.find(selector.search),
  43. $input = $module.find(selector.input),
  44. $icon = $module.find(selector.icon),
  45. $combo = ($module.prev().find(selector.text).length > 0)
  46. ? $module.prev().find(selector.text)
  47. : $module.prev(),
  48. $menu = $module.children(selector.menu),
  49. $item = $menu.find(selector.item),
  50. activated = false,
  51. itemActivated = false,
  52. element = this,
  53. instance = $module.data(moduleNamespace),
  54. elementNamespace,
  55. id,
  56. selectObserver,
  57. menuObserver,
  58. module
  59. ;
  60. module = {
  61. initialize: function() {
  62. module.debug('Initializing dropdown', settings);
  63. if( module.is.alreadySetup() ) {
  64. module.setup.reference();
  65. }
  66. else {
  67. module.setup.layout();
  68. module.save.defaults();
  69. module.set.selected();
  70. module.create.id();
  71. if(hasTouch) {
  72. module.bind.touchEvents();
  73. }
  74. module.bind.mouseEvents();
  75. module.bind.keyboardEvents();
  76. module.observeChanges();
  77. module.instantiate();
  78. }
  79. },
  80. instantiate: function() {
  81. module.verbose('Storing instance of dropdown', module);
  82. instance = module;
  83. $module
  84. .data(moduleNamespace, module)
  85. ;
  86. },
  87. destroy: function() {
  88. module.verbose('Destroying previous dropdown for', $module);
  89. module.remove.tabbable();
  90. $module
  91. .off(eventNamespace)
  92. .removeData(moduleNamespace)
  93. ;
  94. $menu
  95. .off(eventNamespace)
  96. ;
  97. $document
  98. .off(elementNamespace)
  99. ;
  100. if(selectObserver) {
  101. selectObserver.disconnect();
  102. }
  103. if(menuObserver) {
  104. menuObserver.disconnect();
  105. }
  106. },
  107. observeChanges: function() {
  108. if('MutationObserver' in window) {
  109. selectObserver = new MutationObserver(function(mutations) {
  110. module.debug('<select> modified, recreating menu');
  111. module.setup.select();
  112. });
  113. menuObserver = new MutationObserver(function(mutations) {
  114. module.debug('Menu modified, updating selector cache');
  115. module.refresh();
  116. });
  117. if(module.has.input()) {
  118. selectObserver.observe($input[0], {
  119. childList : true,
  120. subtree : true
  121. });
  122. }
  123. if(module.has.menu()) {
  124. menuObserver.observe($menu[0], {
  125. childList : true,
  126. subtree : true
  127. });
  128. }
  129. module.debug('Setting up mutation observer', selectObserver, menuObserver);
  130. }
  131. },
  132. create: {
  133. id: function() {
  134. id = (Math.random().toString(16) + '000000000').substr(2, 8);
  135. elementNamespace = '.' + id;
  136. module.verbose('Creating unique id for element', id);
  137. }
  138. },
  139. search: function() {
  140. var
  141. query
  142. ;
  143. query = $search.val();
  144. module.verbose('Searching for query', query);
  145. module.filter(query);
  146. if(module.is.searchSelection() && module.can.show() ) {
  147. module.show();
  148. }
  149. },
  150. select: {
  151. firstUnfiltered: function() {
  152. module.remove.selectedItem();
  153. $item
  154. .not('.' + className.filtered)
  155. .eq(0)
  156. .addClass(className.selected)
  157. ;
  158. },
  159. nextAvailable: function($selected) {
  160. $selected = $selected.eq(0);
  161. var
  162. $nextAvailable = $selected.nextAll(selector.item).not('.' + className.filtered).eq(0),
  163. $prevAvailable = $selected.prevAll(selector.item).not('.' + className.filtered).eq(0),
  164. hasNext = ($nextAvailable.length > 0)
  165. ;
  166. if(hasNext) {
  167. module.verbose('Moving selection to', $nextAvailable);
  168. $nextAvailable.addClass(className.selected);
  169. }
  170. else {
  171. module.verbose('Moving selection to', $prevAvailable);
  172. $prevAvailable.addClass(className.selected);
  173. }
  174. }
  175. },
  176. setup: {
  177. layout: function() {
  178. if( $module.is('select') ) {
  179. module.setup.select();
  180. }
  181. if( module.is.search() && !module.has.search() ) {
  182. $search = $('<input />')
  183. .addClass(className.search)
  184. .insertBefore($text)
  185. ;
  186. }
  187. if(settings.allowTab) {
  188. module.set.tabbable();
  189. }
  190. },
  191. select: function() {
  192. var
  193. selectValues = module.get.selectValues()
  194. ;
  195. module.debug('Dropdown initialized on a select', selectValues);
  196. if( $module.is('select') ) {
  197. $input = $module;
  198. }
  199. // see if select is placed correctly already
  200. if($input.parent(selector.dropdown).length > 0) {
  201. module.debug('UI dropdown already exists. Creating dropdown menu only');
  202. $module = $input.closest(selector.dropdown);
  203. $menu = $module.children(selector.menu);
  204. if($menu.length === 0) {
  205. $menu = $('<div />')
  206. .addClass(className.menu)
  207. .appendTo($module)
  208. ;
  209. }
  210. $menu.html( templates.menu( selectValues ));
  211. }
  212. else {
  213. module.debug('Creating entire dropdown from select');
  214. $module = $('<div />')
  215. .attr('class', $input.attr('class') )
  216. .addClass(className.selection)
  217. .addClass(className.dropdown)
  218. .html( templates.dropdown(selectValues) )
  219. .insertBefore($input)
  220. ;
  221. $input
  222. .removeAttr('class')
  223. .detach()
  224. .prependTo($module)
  225. ;
  226. }
  227. if($input.is('[multiple]')) {
  228. module.set.multiple();
  229. }
  230. module.refresh();
  231. },
  232. reference: function() {
  233. var
  234. index = $allModules.index($module),
  235. $firstModules,
  236. $lastModules
  237. ;
  238. module.debug('Dropdown behavior was called on select, replacing with closest dropdown');
  239. // replace module reference
  240. $module = $module.parent(selector.dropdown);
  241. module.refresh();
  242. // adjust all modules
  243. $firstModules = $allModules.slice(0, index);
  244. $lastModules = $allModules.slice(index + 1);
  245. $allModules = $firstModules.add($module).add($lastModules);
  246. // invoke method in context of current instance
  247. if(methodInvoked) {
  248. instance = module;
  249. module.invoke(query);
  250. }
  251. }
  252. },
  253. refresh: function() {
  254. module.verbose('Refreshing selector cache');
  255. $text = $module.find(selector.text);
  256. $search = $module.find(selector.search);
  257. $input = $module.find(selector.input);
  258. $icon = $module.find(selector.icon);
  259. $combo = ($module.prev().find(selector.text).length > 0)
  260. ? $module.prev().find(selector.text)
  261. : $module.prev()
  262. ;
  263. $menu = $module.children(selector.menu);
  264. $item = $menu.find(selector.item);
  265. },
  266. toggle: function() {
  267. module.verbose('Toggling menu visibility');
  268. if( !module.is.active() ) {
  269. module.show();
  270. }
  271. else {
  272. module.hide();
  273. }
  274. },
  275. show: function(callback) {
  276. callback = $.isFunction(callback)
  277. ? callback
  278. : function(){}
  279. ;
  280. if( module.is.searchSelection() && module.is.allFiltered() ) {
  281. return;
  282. }
  283. if( module.can.show() && !module.is.active() ) {
  284. module.debug('Showing dropdown');
  285. if(module.is.multiple() && module.is.searchSelection()) {
  286. module.filterActive();
  287. }
  288. module.animate.show(function() {
  289. if( module.can.click() ) {
  290. module.bind.intent();
  291. }
  292. module.set.visible();
  293. callback.call(element);
  294. });
  295. settings.onShow.call(element);
  296. }
  297. },
  298. hide: function(callback) {
  299. callback = $.isFunction(callback)
  300. ? callback
  301. : function(){}
  302. ;
  303. if( module.is.active() ) {
  304. module.debug('Hiding dropdown');
  305. module.animate.hide(function() {
  306. module.remove.visible();
  307. callback.call(element);
  308. });
  309. settings.onHide.call(element);
  310. }
  311. },
  312. hideOthers: function() {
  313. module.verbose('Finding other dropdowns to hide');
  314. $allModules
  315. .not($module)
  316. .has(selector.menu + ':visible:not(.' + className.animating + ')')
  317. .dropdown('hide')
  318. ;
  319. },
  320. hideMenu: function() {
  321. module.verbose('Hiding menu instantaneously');
  322. module.remove.active();
  323. module.remove.visible();
  324. $menu.transition('hide');
  325. },
  326. hideSubMenus: function() {
  327. var
  328. $subMenus = $menu.children(selector.item).find(selector.menu)
  329. ;
  330. module.verbose('Hiding sub menus', $subMenus);
  331. $subMenus.transition('hide');
  332. },
  333. bind: {
  334. keyboardEvents: function() {
  335. module.debug('Binding keyboard events');
  336. $module
  337. .on('keydown' + eventNamespace, module.event.keydown)
  338. ;
  339. if( module.has.search() ) {
  340. $module
  341. .on(module.get.inputEvent() + eventNamespace, selector.search, module.event.input)
  342. ;
  343. }
  344. if( module.is.multiple() ) {
  345. $document
  346. .on('keydown' + elementNamespace, module.event.document.keydown)
  347. ;
  348. }
  349. },
  350. touchEvents: function() {
  351. module.debug('Touch device detected binding additional touch events');
  352. if( module.is.searchSelection() ) {
  353. // do nothing special yet
  354. }
  355. else {
  356. $module
  357. .on('touchstart' + eventNamespace, module.event.test.toggle)
  358. ;
  359. }
  360. $menu
  361. .on('touchstart' + eventNamespace, selector.item, module.event.item.mouseenter)
  362. ;
  363. },
  364. mouseEvents: function() {
  365. module.verbose('Mouse detected binding mouse events');
  366. if(module.is.multiple()) {
  367. $module
  368. .on('click' + eventNamespace, selector.label, module.event.label.click)
  369. .on('click' + eventNamespace, selector.remove, module.event.remove.click)
  370. ;
  371. }
  372. if( module.is.searchSelection() ) {
  373. $module
  374. .on('mousedown' + eventNamespace, selector.menu, module.event.menu.mousedown)
  375. .on('mouseup' + eventNamespace, selector.menu, module.event.menu.mouseup)
  376. .on('click' + eventNamespace, selector.search, module.show)
  377. .on('focus' + eventNamespace, selector.search, module.event.search.focus)
  378. .on('blur' + eventNamespace, selector.search, module.event.search.blur)
  379. .on('click' + eventNamespace, selector.text, module.event.text.focus)
  380. ;
  381. if(module.is.multiple()) {
  382. $module
  383. .on('click' + eventNamespace, module.event.click)
  384. ;
  385. }
  386. }
  387. else {
  388. if(settings.on == 'click') {
  389. $module
  390. .on('click' + eventNamespace, module.event.test.toggle)
  391. ;
  392. }
  393. else if(settings.on == 'hover') {
  394. $module
  395. .on('mouseenter' + eventNamespace, module.delay.show)
  396. .on('mouseleave' + eventNamespace, module.delay.hide)
  397. ;
  398. }
  399. else {
  400. $module
  401. .on(settings.on + eventNamespace, module.toggle)
  402. ;
  403. }
  404. $module
  405. .on('mousedown' + eventNamespace, module.event.mousedown)
  406. .on('mouseup' + eventNamespace, module.event.mouseup)
  407. .on('focus' + eventNamespace, module.event.focus)
  408. .on('blur' + eventNamespace, module.event.blur)
  409. ;
  410. }
  411. $menu
  412. .on('mouseenter' + eventNamespace, selector.item, module.event.item.mouseenter)
  413. .on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave)
  414. .on('click' + eventNamespace, selector.item, module.event.item.click)
  415. ;
  416. },
  417. intent: function() {
  418. module.verbose('Binding hide intent event to document');
  419. if(hasTouch) {
  420. $document
  421. .on('touchstart' + elementNamespace, module.event.test.touch)
  422. .on('touchmove' + elementNamespace, module.event.test.touch)
  423. ;
  424. }
  425. $document
  426. .on('click' + elementNamespace, module.event.test.hide)
  427. ;
  428. }
  429. },
  430. unbind: {
  431. intent: function() {
  432. module.verbose('Removing hide intent event from document');
  433. if(hasTouch) {
  434. $document
  435. .off('touchstart' + elementNamespace)
  436. .off('touchmove' + elementNamespace)
  437. ;
  438. }
  439. $document
  440. .off('click' + elementNamespace)
  441. ;
  442. }
  443. },
  444. filter: function(searchTerm) {
  445. var
  446. $results = $(),
  447. escapedTerm = module.escape.regExp(searchTerm),
  448. exactRegExp = new RegExp('^' + escapedTerm, 'igm'),
  449. fullTextRegExp = new RegExp(escapedTerm, 'ig'),
  450. allItemsFiltered
  451. ;
  452. module.verbose('Searching for matching values');
  453. $item
  454. .each(function(){
  455. var
  456. $choice = $(this),
  457. text,
  458. value
  459. ;
  460. if(settings.match == 'both' || settings.match == 'text') {
  461. text = String(module.get.choiceText($choice, false));
  462. if(text.match(exactRegExp)) {
  463. $results = $results.add($choice);
  464. return true;
  465. }
  466. else if(settings.fullTextSearch && text.match(fullTextRegExp)) {
  467. $results = $results.add($choice);
  468. return true;
  469. }
  470. }
  471. if(settings.match == 'both' || settings.match == 'value') {
  472. value = String(module.get.choiceValue($choice, text));
  473. if(value.match(exactRegExp)) {
  474. $results = $results.add($choice);
  475. return true;
  476. }
  477. else if(settings.fullTextSearch && value.match(fullTextRegExp)) {
  478. $results = $results.add($choice);
  479. return true;
  480. }
  481. }
  482. })
  483. ;
  484. module.debug('Setting filter', searchTerm);
  485. module.remove.filteredItem();
  486. $item
  487. .not($results)
  488. .addClass(className.filtered)
  489. ;
  490. if(module.is.multiple()) {
  491. module.filterActive();
  492. }
  493. module.verbose('Selecting first non-filtered element');
  494. module.select.firstUnfiltered();
  495. if( module.is.allFiltered() ) {
  496. module.debug('All items filtered, hiding dropdown', searchTerm);
  497. if(module.is.searchSelection()) {
  498. module.hideMenu();
  499. }
  500. settings.onNoResults.call(element, searchTerm);
  501. }
  502. },
  503. filterActive: function() {
  504. if(settings.hideSelections) {
  505. $item.filter('.' + className.active)
  506. .addClass(className.filtered)
  507. ;
  508. }
  509. },
  510. focusSearch: function() {
  511. if( module.is.search() && !module.is.focusedOnSearch() ) {
  512. $search[0].focus();
  513. }
  514. },
  515. forceSelection: function() {
  516. var
  517. $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
  518. $activeItem = $item.not(className.filtered).filter('.' + className.active).eq(0),
  519. $selectedItem = ($currentlySelected.length > 0)
  520. ? $currentlySelected
  521. : $activeItem,
  522. hasSelected = ($selectedItem.size() > 0)
  523. ;
  524. if(hasSelected) {
  525. module.debug('Forcing partial selection to selected item', $selectedItem);
  526. module.event.item.click.call($selectedItem);
  527. module.remove.filteredItem();
  528. }
  529. else {
  530. module.hide();
  531. }
  532. },
  533. event: {
  534. focus: function() {
  535. if(settings.showOnFocus && !activated && module.is.hidden()) {
  536. module.show();
  537. }
  538. },
  539. click: function(event) {
  540. var
  541. $target = $(event.target)
  542. ;
  543. // focus search
  544. if(($target.is($module) || $target.is($icon)) && !module.is.focusedOnSearch()) {
  545. $search.focus();
  546. }
  547. },
  548. blur: function(event) {
  549. var
  550. pageLostFocus = (document.activeElement === this)
  551. ;
  552. if(!activated && !pageLostFocus) {
  553. module.remove.activeLabel();
  554. module.hide();
  555. }
  556. },
  557. // prevents focus callback from occuring on mousedown
  558. mousedown: function() {
  559. activated = true;
  560. },
  561. mouseup: function() {
  562. activated = false;
  563. },
  564. search: {
  565. focus: function() {
  566. activated = true;
  567. if(module.is.multiple()) {
  568. module.remove.activeLabel();
  569. }
  570. if(settings.showOnFocus) {
  571. module.show();
  572. }
  573. },
  574. blur: function(event) {
  575. var
  576. pageLostFocus = (document.activeElement === this)
  577. ;
  578. if(!itemActivated && !pageLostFocus) {
  579. if(module.is.multiple()) {
  580. module.remove.activeLabel();
  581. }
  582. else if(settings.forceSelection) {
  583. module.forceSelection();
  584. }
  585. else {
  586. module.hide();
  587. }
  588. }
  589. }
  590. },
  591. text: {
  592. focus: function(event) {
  593. activated = true;
  594. $search.focus();
  595. }
  596. },
  597. input: function(event) {
  598. if(module.is.multiple() || module.is.searchSelection()) {
  599. module.set.filtered();
  600. }
  601. clearTimeout(module.timer);
  602. module.timer = setTimeout(module.search, settings.delay.search);
  603. },
  604. label: {
  605. click: function(event) {
  606. var
  607. $label = $(this),
  608. $labels = $module.find(selector.label),
  609. $activeLabels = $labels.filter('.' + className.active),
  610. $nextActive = $label.nextAll('.' + className.active),
  611. $prevActive = $label.prevAll('.' + className.active),
  612. $range = ($nextActive.length > 0)
  613. ? $label.nextUntil($nextActive).add($activeLabels).add($label)
  614. : $label.prevUntil($prevActive).add($activeLabels).add($label)
  615. ;
  616. ;
  617. if(event.shiftKey) {
  618. $activeLabels.removeClass(className.active);
  619. $range.addClass(className.active);
  620. }
  621. else if(event.ctrlKey) {
  622. $label.toggleClass(className.active);
  623. }
  624. else {
  625. $activeLabels.removeClass(className.active);
  626. $label.addClass(className.active);
  627. }
  628. settings.onLabelSelect.apply(this, $labels.filter('.' + className.active));
  629. }
  630. },
  631. remove: {
  632. click: function() {
  633. var
  634. $label = $(this).parent()
  635. ;
  636. if( $label.hasClass(className.active) ) {
  637. // remove all selected labels
  638. module.remove.labels();
  639. }
  640. else {
  641. // remove this label only
  642. module.remove.labels( $label );
  643. }
  644. }
  645. },
  646. test: {
  647. toggle: function(event) {
  648. var
  649. toggleBehavior = (module.is.multiple())
  650. ? module.show
  651. : module.toggle
  652. ;
  653. if( module.determine.eventOnElement(event, toggleBehavior) ) {
  654. event.preventDefault();
  655. }
  656. },
  657. touch: function(event) {
  658. module.determine.eventOnElement(event, function() {
  659. if(event.type == 'touchstart') {
  660. module.timer = setTimeout(module.hide, settings.delay.touch);
  661. }
  662. else if(event.type == 'touchmove') {
  663. clearTimeout(module.timer);
  664. }
  665. });
  666. event.stopPropagation();
  667. },
  668. hide: function(event) {
  669. module.determine.eventInModule(event, module.hide);
  670. }
  671. },
  672. menu: {
  673. mousedown: function() {
  674. itemActivated = true;
  675. },
  676. mouseup: function() {
  677. itemActivated = false;
  678. }
  679. },
  680. item: {
  681. mouseenter: function(event) {
  682. var
  683. $subMenu = $(this).children(selector.menu),
  684. $otherMenus = $(this).siblings(selector.item).children(selector.menu)
  685. ;
  686. if( $subMenu.length > 0 ) {
  687. clearTimeout(module.itemTimer);
  688. module.itemTimer = setTimeout(function() {
  689. module.verbose('Showing sub-menu', $subMenu);
  690. $.each($otherMenus, function() {
  691. module.animate.hide(false, $(this));
  692. });
  693. module.animate.show(false, $subMenu);
  694. }, settings.delay.show);
  695. event.preventDefault();
  696. }
  697. },
  698. mouseleave: function(event) {
  699. var
  700. $subMenu = $(this).children(selector.menu)
  701. ;
  702. if($subMenu.length > 0) {
  703. clearTimeout(module.itemTimer);
  704. module.itemTimer = setTimeout(function() {
  705. module.verbose('Hiding sub-menu', $subMenu);
  706. module.animate.hide(false, $subMenu);
  707. }, settings.delay.hide);
  708. }
  709. },
  710. click: function (event) {
  711. var
  712. $choice = $(this),
  713. $target = (event)
  714. ? $(event.target)
  715. : $(''),
  716. $subMenu = $choice.find(selector.menu),
  717. text = module.get.choiceText($choice),
  718. value = module.get.choiceValue($choice, text),
  719. hasSubMenu = ($subMenu.length > 0),
  720. isBubbledEvent = ($subMenu.find($target).length > 0)
  721. ;
  722. if(!isBubbledEvent && (!hasSubMenu || settings.allowCategorySelection)) {
  723. module.determine.selectAction.call(this, text, value);
  724. }
  725. }
  726. },
  727. document: {
  728. // label selection should occur even when element has no focus
  729. keydown: function(event) {
  730. var
  731. pressedKey = event.which,
  732. keys = module.get.shortcutKeys(),
  733. isShortcutKey = module.is.inObject(pressedKey, keys)
  734. ;
  735. if(isShortcutKey) {
  736. var
  737. $label = $module.find(selector.label),
  738. $activeLabel = $label.filter('.' + className.active),
  739. activeValue = $activeLabel.data('value'),
  740. labelIndex = $label.index($activeLabel),
  741. labelCount = $label.length,
  742. hasActiveLabel = ($activeLabel.length > 0),
  743. hasMultipleActive = ($activeLabel.length > 1),
  744. isFirstLabel = (labelIndex == 0),
  745. isLastLabel = (labelIndex + 1 == labelCount),
  746. isSearch = module.is.searchSelection(),
  747. isFocusedOnSearch = module.is.focusedOnSearch(),
  748. isFocused = module.is.focused(),
  749. caretAtStart = (isFocusedOnSearch && module.get.caretPosition() == 0),
  750. $nextLabel
  751. ;
  752. if(isFocusedOnSearch && (pressedKey == keys.delimiter)) {
  753. // tokenize on comma
  754. if(module.is.visible()) {
  755. module.verbose('Delimiter key pressed. Tokenizing');
  756. // do tokenize
  757. event.preventDefault();
  758. }
  759. }
  760. else if(pressedKey == keys.leftArrow) {
  761. // activate previous label
  762. if((isFocused || caretAtStart) && !hasActiveLabel) {
  763. module.verbose('Selecting previous label');
  764. $label.last().addClass(className.active);
  765. }
  766. else if(hasActiveLabel) {
  767. if(!event.shiftKey) {
  768. module.verbose('Selecting previous label');
  769. $label.removeClass(className.active)
  770. }
  771. else {
  772. module.verbose('Adding previous label to selection');
  773. }
  774. if(isFirstLabel && !hasMultipleActive) {
  775. $activeLabel.addClass(className.active);
  776. }
  777. else {
  778. $activeLabel.prev(selector.siblingLabel)
  779. .addClass(className.active)
  780. .end()
  781. ;
  782. }
  783. event.preventDefault();
  784. }
  785. }
  786. else if(pressedKey == keys.rightArrow) {
  787. // activate first label
  788. if(isFocused && !hasActiveLabel) {
  789. $label.first().addClass(className.active);
  790. }
  791. // activate next label
  792. if(hasActiveLabel) {
  793. if(!event.shiftKey) {
  794. module.verbose('Selecting next label');
  795. $label.removeClass(className.active)
  796. }
  797. else {
  798. module.verbose('Adding next label to selection');
  799. }
  800. if(isLastLabel) {
  801. if(isSearch && !isFocusedOnSearch) {
  802. module.focusSearch();
  803. }
  804. else if(hasMultipleActive) {
  805. $activeLabel.next(selector.siblingLabel).addClass(className.active);
  806. }
  807. else {
  808. $activeLabel.addClass(className.active);
  809. }
  810. }
  811. else {
  812. $activeLabel.next(selector.siblingLabel).addClass(className.active);
  813. }
  814. event.preventDefault();
  815. }
  816. }
  817. else if(pressedKey == keys.deleteKey || pressedKey == keys.backspace) {
  818. if(hasActiveLabel) {
  819. module.verbose('Removing active labels');
  820. if(isLastLabel) {
  821. if(isSearch && !isFocusedOnSearch) {
  822. module.focusSearch();
  823. }
  824. }
  825. $activeLabel.last().next(selector.siblingLabel).addClass(className.active);
  826. module.remove.labels($activeLabel);
  827. event.preventDefault();
  828. }
  829. else if(caretAtStart && !hasActiveLabel && pressedKey == keys.backspace) {
  830. module.verbose('Removing last label on input backspace');
  831. $activeLabel = $label.last().addClass(className.active);
  832. activeValue = $activeLabel.data('value');
  833. module.remove.selected(activeValue);
  834. event.preventDefault();
  835. }
  836. }
  837. else {
  838. $activeLabel.removeClass(className.active);
  839. }
  840. }
  841. }
  842. },
  843. keydown: function(event) {
  844. var
  845. pressedKey = event.which,
  846. keys = module.get.shortcutKeys(),
  847. isShortcutKey = module.is.inObject(pressedKey, keys)
  848. ;
  849. if(isShortcutKey) {
  850. var
  851. $currentlySelected = $item.not('.' + className.filtered).filter('.' + className.selected).eq(0),
  852. $activeItem = $menu.children('.' + className.active).eq(0),
  853. $selectedItem = ($currentlySelected.length > 0)
  854. ? $currentlySelected
  855. : $activeItem,
  856. $visibleItems = ($selectedItem.length > 0)
  857. ? $selectedItem.siblings(':not(.' + className.filtered +')').andSelf()
  858. : $menu.children(':not(.' + className.filtered +')'),
  859. $subMenu = $selectedItem.children(selector.menu),
  860. $parentMenu = $selectedItem.closest(selector.menu),
  861. inVisibleMenu = ($parentMenu.hasClass(className.visible) || $parentMenu.hasClass(className.animating)),
  862. hasSubMenu = ($subMenu.length> 0),
  863. hasSelectedItem = ($selectedItem.length > 0),
  864. selectedIsVisible = ($selectedItem.not('.' + selector.filtered).length > 0),
  865. $nextItem,
  866. isSubMenuItem,
  867. newIndex
  868. ;
  869. // visible menu keyboard shortcuts
  870. if( module.is.visible() ) {
  871. // enter (select or open sub-menu)
  872. if(pressedKey == keys.enter && hasSelectedItem) {
  873. if(hasSubMenu && !settings.allowCategorySelection) {
  874. module.verbose('Pressed enter on unselectable category, opening sub menu');
  875. pressedKey = keys.rightArrow;
  876. }
  877. else if(selectedIsVisible) {
  878. module.verbose('Enter key pressed, choosing selected item');
  879. module.event.item.click.call($selectedItem, event);
  880. event.stopImmediatePropagation();
  881. }
  882. event.preventDefault();
  883. }
  884. // left arrow (hide sub-menu)
  885. if(pressedKey == keys.leftArrow) {
  886. isSubMenuItem = ($parentMenu[0] !== $menu[0]);
  887. if(isSubMenuItem) {
  888. module.verbose('Left key pressed, closing sub-menu');
  889. module.animate.hide(false, $parentMenu);
  890. $selectedItem
  891. .removeClass(className.selected)
  892. ;
  893. $parentMenu
  894. .closest(selector.item)
  895. .addClass(className.selected)
  896. ;
  897. event.preventDefault();
  898. }
  899. }
  900. // right arrow (show sub-menu)
  901. if(pressedKey == keys.rightArrow) {
  902. if(hasSubMenu) {
  903. module.verbose('Right key pressed, opening sub-menu');
  904. module.animate.show(false, $subMenu);
  905. $selectedItem
  906. .removeClass(className.selected)
  907. ;
  908. $subMenu
  909. .find(selector.item).eq(0)
  910. .addClass(className.selected)
  911. ;
  912. event.preventDefault();
  913. }
  914. }
  915. // up arrow (traverse menu up)
  916. if(pressedKey == keys.upArrow) {
  917. $nextItem = (hasSelectedItem && inVisibleMenu)
  918. ? $selectedItem.prevAll(selector.item + ':not(.' + className.filtered + ')').eq(0)
  919. : $item.eq(0)
  920. ;
  921. if($visibleItems.index( $nextItem ) < 0) {
  922. module.verbose('Up key pressed but reached top of current menu');
  923. event.preventDefault();
  924. return;
  925. }
  926. else {
  927. module.verbose('Up key pressed, changing active item');
  928. $selectedItem
  929. .removeClass(className.selected)
  930. ;
  931. $nextItem
  932. .addClass(className.selected)
  933. ;
  934. module.set.scrollPosition($nextItem);
  935. }
  936. event.preventDefault();
  937. }
  938. // down arrow (traverse menu down)
  939. if(pressedKey == keys.downArrow) {
  940. $nextItem = (hasSelectedItem && inVisibleMenu)
  941. ? $nextItem = $selectedItem.nextAll(selector.item + ':not(.' + className.filtered + ')').eq(0)
  942. : $item.eq(0)
  943. ;
  944. if($nextItem.length === 0) {
  945. module.verbose('Down key pressed but reached bottom of current menu');
  946. event.preventDefault();
  947. return;
  948. }
  949. else {
  950. module.verbose('Down key pressed, changing active item');
  951. $item
  952. .removeClass(className.selected)
  953. ;
  954. $nextItem
  955. .addClass(className.selected)
  956. ;
  957. module.set.scrollPosition($nextItem);
  958. }
  959. event.preventDefault();
  960. }
  961. // escape (close menu)
  962. if(pressedKey == keys.escape) {
  963. module.verbose('Escape key pressed, closing dropdown');
  964. module.hide();
  965. }
  966. }
  967. else {
  968. // enter (open menu)
  969. if(pressedKey == keys.enter) {
  970. module.verbose('Enter key pressed, showing dropdown');
  971. module.show();
  972. event.preventDefault();
  973. }
  974. // down arrow (open menu)
  975. if(pressedKey == keys.downArrow) {
  976. module.verbose('Down key pressed, showing dropdown');
  977. module.show();
  978. event.preventDefault();
  979. }
  980. }
  981. }
  982. }
  983. },
  984. determine: {
  985. selectAction: function(text, value) {
  986. module.verbose('Determining action', settings.action);
  987. if( $.isFunction( module.action[settings.action] ) ) {
  988. module.verbose('Triggering preset action', settings.action, text, value);
  989. module.action[ settings.action ].call(this, text, value);
  990. }
  991. else if( $.isFunction(settings.action) ) {
  992. module.verbose('Triggering user action', settings.action, text, value);
  993. settings.action.call(this, text, value);
  994. }
  995. else {
  996. module.error(error.action, settings.action);
  997. }
  998. },
  999. eventInModule: function(event, callback) {
  1000. callback = $.isFunction(callback)
  1001. ? callback
  1002. : function(){}
  1003. ;
  1004. if( $(event.target).closest($module).length === 0 ) {
  1005. module.verbose('Triggering event', callback);
  1006. callback();
  1007. return true;
  1008. }
  1009. else {
  1010. module.verbose('Event occurred in dropdown, canceling callback');
  1011. return false;
  1012. }
  1013. },
  1014. eventOnElement: function(event, callback) {
  1015. var
  1016. $target = $(event.target)
  1017. ;
  1018. callback = $.isFunction(callback)
  1019. ? callback
  1020. : function(){}
  1021. ;
  1022. if($target.closest($menu).length == 0) {
  1023. module.verbose('Triggering event', callback);
  1024. callback();
  1025. return true;
  1026. }
  1027. else {
  1028. module.verbose('Event occurred in dropdown menu, canceling callback');
  1029. return false;
  1030. }
  1031. }
  1032. },
  1033. action: {
  1034. nothing: function() {},
  1035. activate: function(text, value) {
  1036. value = (value !== undefined)
  1037. ? value
  1038. : text
  1039. ;
  1040. module.set.selected(value, $(this));
  1041. if(module.is.multiple() && !module.is.allFiltered()) {
  1042. return;
  1043. }
  1044. else {
  1045. module.hideAndClear();
  1046. }
  1047. },
  1048. select: function(text, value) {
  1049. // mimics action.activate but does not select text
  1050. module.action.activate.call(this);
  1051. },
  1052. combo: function(text, value) {
  1053. value = (value !== undefined)
  1054. ? value
  1055. : text
  1056. ;
  1057. module.set.selected(value, $(this));
  1058. module.hideAndClear();
  1059. },
  1060. hide: function() {
  1061. module.hideAndClear();
  1062. }
  1063. },
  1064. get: {
  1065. id: function() {
  1066. return id;
  1067. },
  1068. text: function() {
  1069. return $text.text();
  1070. },
  1071. uniqueArray: function(array) {
  1072. return $.grep(array, function (value, index) {
  1073. return $.inArray(value, array) === index;
  1074. });
  1075. },
  1076. caretPosition: function() {
  1077. var
  1078. input = $search.get(0),
  1079. range,
  1080. rangeLength
  1081. ;
  1082. if ('selectionStart' in input) {
  1083. return input.selectionStart;
  1084. }
  1085. else if (document.selection) {
  1086. input.focus();
  1087. range = document.selection.createRange();
  1088. rangeLength = range.text.length;
  1089. range.moveStart('character', -input.value.length);
  1090. return range.text.length - rangeLength;
  1091. }
  1092. },
  1093. shortcutKeys: function() {
  1094. return {
  1095. backspace : 8,
  1096. delimiter : 188, // comma
  1097. deleteKey : 46,
  1098. enter : 13,
  1099. escape : 27,
  1100. leftArrow : 37,
  1101. upArrow : 38,
  1102. rightArrow : 39,
  1103. downArrow : 40
  1104. };
  1105. },
  1106. value: function() {
  1107. return ($input.length > 0)
  1108. ? $input.val()
  1109. : $module.data(metadata.value)
  1110. ;
  1111. },
  1112. values: function() {
  1113. var
  1114. value = module.get.value()
  1115. ;
  1116. if(value == '') {
  1117. return '';
  1118. }
  1119. return (!$input.is('select') && module.is.multiple())
  1120. ? value.split(settings.delimiter)
  1121. : value
  1122. ;
  1123. },
  1124. choiceText: function($choice, preserveHTML) {
  1125. preserveHTML = (preserveHTML !== undefined)
  1126. ? preserveHTML
  1127. : settings.preserveHTML
  1128. ;
  1129. if($choice) {
  1130. if($choice.find(selector.menu).length > 0) {
  1131. module.verbose('Retreiving text of element with sub-menu');
  1132. $choice = $choice.clone();
  1133. $choice.find(selector.menu).remove();
  1134. $choice.find(selector.menuIcon).remove();
  1135. }
  1136. return ($choice.data(metadata.text) !== undefined)
  1137. ? $choice.data(metadata.text)
  1138. : (preserveHTML)
  1139. ? $choice.html().trim()
  1140. : $choice.text().trim()
  1141. ;
  1142. }
  1143. },
  1144. choiceValue: function($choice, choiceText) {
  1145. choiceText = choiceText || module.get.choiceText($choice);
  1146. if(!$choice) {
  1147. return false;
  1148. }
  1149. return ($choice.data(metadata.value) !== undefined)
  1150. ? $choice.data(metadata.value)
  1151. : (typeof choiceText === 'string')
  1152. ? choiceText.toLowerCase().trim()
  1153. : choiceText
  1154. ;
  1155. },
  1156. inputEvent: function() {
  1157. var
  1158. input = $search[0]
  1159. ;
  1160. if(input) {
  1161. return (input.oninput !== undefined)
  1162. ? 'input'
  1163. : (input.onpropertychange !== undefined)
  1164. ? 'propertychange'
  1165. : 'keyup'
  1166. ;
  1167. }
  1168. return false;
  1169. },
  1170. selectValues: function() {
  1171. var
  1172. select = {}
  1173. ;
  1174. select.values = (settings.sortSelect)
  1175. ? {} // properties will be sorted in object when re-accessed
  1176. : [] // properties will keep original order in array
  1177. ;
  1178. $module
  1179. .find('option')
  1180. .each(function() {
  1181. var
  1182. name = $(this).html(),
  1183. value = ( $(this).attr('value') !== undefined )
  1184. ? $(this).attr('value')
  1185. : name
  1186. ;
  1187. if(settings.placeholder === 'auto' && value === '') {
  1188. select.placeholder = name;
  1189. }
  1190. else {
  1191. if(settings.sortSelect) {
  1192. select.values[value] = {
  1193. name : name,
  1194. value : value
  1195. };
  1196. }
  1197. else {
  1198. select.values.push({
  1199. name: name,
  1200. value: value
  1201. });
  1202. }
  1203. }
  1204. })
  1205. ;
  1206. if(settings.placeholder && settings.placeholder !== 'auto') {
  1207. module.debug('Setting placeholder value to', settings.placeholder);
  1208. select.placeholder = settings.placeholder;
  1209. }
  1210. if(settings.sortSelect) {
  1211. module.debug('Retrieved and sorted values from select', select);
  1212. }
  1213. else {
  1214. module.debug('Retreived values from select', select);
  1215. }
  1216. return select;
  1217. },
  1218. activeItem: function() {
  1219. return $item.filter('.' + className.active);
  1220. },
  1221. item: function(value, strict) {
  1222. var
  1223. $selectedItem = false,
  1224. isMultiple
  1225. ;
  1226. value = (value !== undefined)
  1227. ? value
  1228. : ( module.get.values() !== undefined)
  1229. ? module.get.values()
  1230. : module.get.text()
  1231. ;
  1232. isMultiple = (module.is.multiple() && $.isArray(value));
  1233. strict = (value === '' || value === 0)
  1234. ? true
  1235. : strict || false
  1236. ;
  1237. if(value !== undefined) {
  1238. $item
  1239. .each(function() {
  1240. var
  1241. $choice = $(this),
  1242. optionText = module.get.choiceText($choice),
  1243. optionValue = module.get.choiceValue($choice, optionText)
  1244. ;
  1245. if(isMultiple) {
  1246. if($.inArray(optionValue.toString(), value) !== -1 || $.inArray(optionText, value) !== -1) {
  1247. $selectedItem = ($selectedItem)
  1248. ? $selectedItem.add($choice)
  1249. : $choice
  1250. ;
  1251. }
  1252. }
  1253. else if(strict) {
  1254. module.verbose('Ambiguous dropdown value using strict type check', $choice, value);
  1255. if( optionValue === value || optionText === value) {
  1256. $selectedItem = $choice;
  1257. return true;
  1258. }
  1259. }
  1260. else {
  1261. if( optionValue == value || optionText == value) {
  1262. module.verbose('Found select item by value', optionValue, value);
  1263. $selectedItem = $choice;
  1264. return true;
  1265. }
  1266. }
  1267. })
  1268. ;
  1269. }
  1270. return $selectedItem;
  1271. }
  1272. },
  1273. restore: {
  1274. defaults: function() {
  1275. module.restore.defaultText();
  1276. module.restore.defaultValue();
  1277. },
  1278. defaultText: function() {
  1279. var
  1280. defaultText = $module.data(metadata.defaultText)
  1281. ;
  1282. module.debug('Restoring default text', defaultText);
  1283. module.set.text(defaultText);
  1284. $text.addClass(className.placeholder);
  1285. },
  1286. defaultValue: function() {
  1287. var
  1288. defaultValue = $module.data(metadata.defaultValue)
  1289. ;
  1290. if(defaultValue !== undefined) {
  1291. module.debug('Restoring default value', defaultValue);
  1292. if(defaultValue.length) {
  1293. module.set.selected(defaultValue);
  1294. }
  1295. else {
  1296. module.remove.activeItem();
  1297. module.remove.selectedItem();
  1298. }
  1299. }
  1300. }
  1301. },
  1302. save: {
  1303. defaults: function() {
  1304. module.save.defaultText();
  1305. module.save.placeholderText();
  1306. module.save.defaultValue();
  1307. },
  1308. defaultValue: function() {
  1309. $module.data(metadata.defaultValue, module.get.value());
  1310. },
  1311. defaultText: function() {
  1312. $module.data(metadata.defaultText, $text.text() );
  1313. },
  1314. placeholderText: function() {
  1315. if($text.hasClass(className.placeholder)) {
  1316. $module.data(metadata.placeholderText, $text.text());
  1317. }
  1318. }
  1319. },
  1320. clear: function() {
  1321. module.set.placeholderText();
  1322. module.clearValue();
  1323. module.remove.activeItem();
  1324. module.remove.selectedItem();
  1325. },
  1326. clearValue: function() {
  1327. module.set.value('');
  1328. },
  1329. set: {
  1330. filtered: function() {
  1331. var
  1332. isMultiple = module.is.multiple(),
  1333. isSearch = module.is.searchSelection(),
  1334. isSearchMultiple = (isMultiple && isSearch),
  1335. searchValue = (isSearch)
  1336. ? $search.val()
  1337. : '',
  1338. hasSearchValue = (typeof searchValue === 'string' && searchValue.length > 0),
  1339. searchWidth = (searchValue.length * settings.glyphWidth) + 'em',
  1340. valueIsSet = $input.val() != ''
  1341. ;
  1342. if(isMultiple && hasSearchValue) {
  1343. module.verbose('Adjusting input width', searchWidth, settings.glyphWidth)
  1344. $search.css('width', searchWidth);
  1345. }
  1346. if(hasSearchValue || (isSearchMultiple && valueIsSet)) {
  1347. module.verbose('Hiding placeholder text');
  1348. $text.addClass(className.filtered);
  1349. }
  1350. else if(!isMultiple || (isSearchMultiple && !valueIsSet)) {
  1351. module.verbose('Showing placeholder text');
  1352. $text.removeClass(className.filtered);
  1353. }
  1354. },
  1355. placeholderText: function(text) {
  1356. module.debug('Restoring placeholder text');
  1357. text = text || $module.data(metadata.placeholderText);
  1358. module.set.text(placeholderText);
  1359. $text.addClass(className.placeholder);
  1360. },
  1361. tabbable: function() {
  1362. if( module.has.search() ) {
  1363. module.debug('Searchable dropdown initialized');
  1364. $search
  1365. .val('')
  1366. .attr('tabindex', 0)
  1367. ;
  1368. $menu
  1369. .attr('tabindex', '-1')
  1370. ;
  1371. }
  1372. else {
  1373. module.debug('Simple selection dropdown initialized');
  1374. if(!$module.attr('tabindex') ) {
  1375. $module
  1376. .attr('tabindex', 0)
  1377. ;
  1378. $menu
  1379. .attr('tabindex', '-1')
  1380. ;
  1381. }
  1382. }
  1383. },
  1384. scrollPosition: function($item, forceScroll) {
  1385. var
  1386. edgeTolerance = 5,
  1387. hasActive,
  1388. offset,
  1389. itemHeight,
  1390. itemOffset,
  1391. menuOffset,
  1392. menuScroll,
  1393. menuHeight,
  1394. abovePage,
  1395. belowPage
  1396. ;
  1397. $item = $item || module.get.activeItem();
  1398. hasActive = ($item && $item.length > 0);
  1399. forceScroll = (forceScroll !== undefined)
  1400. ? forceScroll
  1401. : false
  1402. ;
  1403. if($item && hasActive) {
  1404. if(!$menu.hasClass(className.visible)) {
  1405. $menu.addClass(className.loading);
  1406. }
  1407. menuHeight = $menu.height();
  1408. itemHeight = $item.height();
  1409. menuScroll = $menu.scrollTop();
  1410. menuOffset = $menu.offset().top;
  1411. itemOffset = $item.offset().top;
  1412. offset = menuScroll - menuOffset + itemOffset;
  1413. belowPage = menuScroll + menuHeight < (offset + edgeTolerance);
  1414. abovePage = ((offset - edgeTolerance) < menuScroll);
  1415. module.debug('Scrolling to active item', offset);
  1416. if(abovePage || belowPage || forceScroll) {
  1417. $menu
  1418. .scrollTop(offset)
  1419. .removeClass(className.loading)
  1420. ;
  1421. }
  1422. }
  1423. },
  1424. text: function(text) {
  1425. if(settings.action !== 'select') {
  1426. module.debug('Changing text', text, $text);
  1427. $text
  1428. .removeClass(className.filtered)
  1429. .removeClass(className.placeholder)
  1430. ;
  1431. if(settings.preserveHTML) {
  1432. $text.html(text);
  1433. }
  1434. else {
  1435. $text.text(text);
  1436. }
  1437. }
  1438. else if(settings.action == 'combo') {
  1439. module.debug('Changing combo button text', text, $combo);
  1440. if(settings.preserveHTML) {
  1441. $combo.html(text);
  1442. }
  1443. else {
  1444. $combo.text(text);
  1445. }
  1446. }
  1447. },
  1448. value: function(value, text, $selected) {
  1449. var
  1450. hasInput = ($input.length > 0),
  1451. currentValue = module.get.values()
  1452. ;
  1453. if($input.length > 0) {
  1454. if( module.is.multiple() ) {
  1455. // extend currently selected values
  1456. value = [value];
  1457. if($.isArray(currentValue)) {
  1458. value = currentValue.concat(value);
  1459. value = module.get.uniqueArray(value);
  1460. }
  1461. // set values
  1462. if( $input.is('select') ) {
  1463. module.debug('Setting multiple <select> values', value, $input);
  1464. }
  1465. else {
  1466. value = value.join(settings.delimiter);
  1467. module.debug('Setting hidden input to delimited values', value, $input);
  1468. }
  1469. }
  1470. if(value == currentValue) {
  1471. module.verbose('Skipping value update already same value', value, currentValue);
  1472. return;
  1473. }
  1474. module.debug('Updating input value', value, currentValue);
  1475. $input
  1476. .val(value)
  1477. .trigger('change')
  1478. ;
  1479. settings.onChange.call(element, value, text, $selected);
  1480. }
  1481. else {
  1482. module.verbose('Storing value in metadata', value, $input);
  1483. if(value !== currentValue) {
  1484. $module.data(metadata.value, value);
  1485. settings.onChange.call(element, value, text, $selected);
  1486. }
  1487. }
  1488. },
  1489. active: function() {
  1490. $module
  1491. .addClass(className.active)
  1492. ;
  1493. },
  1494. multiple: function() {
  1495. $module.addClass(className.multiple);
  1496. },
  1497. visible: function() {
  1498. $module.addClass(className.visible);
  1499. },
  1500. selected: function(value, $selectedItem) {
  1501. var
  1502. isMultiple = module.is.multiple(),
  1503. selectedText,
  1504. selectedValue
  1505. ;
  1506. $selectedItem = $selectedItem || module.get.item(value);
  1507. if($selectedItem) {
  1508. module.debug('Setting selected menu item to', $selectedItem);
  1509. if(module.is.single()) {
  1510. module.remove.activeItem();
  1511. module.remove.selectedItem();
  1512. }
  1513. else if(settings.hideSelections) {
  1514. module.remove.selectedItem();
  1515. }
  1516. $selectedItem
  1517. .each(function() {
  1518. var
  1519. $selected = $(this),
  1520. isNotActive = (!$selected.hasClass(className.active)),
  1521. isVisible = (!$selected.is('.' + className.filtered)),
  1522. shouldAnimate = (isMultiple && $selectedItem.length == 1)
  1523. ;
  1524. selectedText = module.get.choiceText($selected);
  1525. selectedValue = module.get.choiceValue($selected, selectedText);
  1526. if(isMultiple) {
  1527. if(isNotActive) {
  1528. module.add.label(selectedValue, selectedText, shouldAnimate);
  1529. module.set.value(selectedValue, selectedText, $selected);
  1530. $selected.addClass(className.active);
  1531. // move keyboard pointer to next element
  1532. module.filterActive();
  1533. module.select.nextAvailable($selectedItem);
  1534. }
  1535. else if(isVisible) {
  1536. module.remove.selected(selectedValue);
  1537. }
  1538. }
  1539. else {
  1540. module.set.value(selectedValue, selectedText, $selected);
  1541. module.set.text(selectedText);
  1542. $selected
  1543. .addClass(className.active)
  1544. .addClass(className.selected)
  1545. ;
  1546. }
  1547. })
  1548. ;
  1549. }
  1550. }
  1551. },
  1552. add: {
  1553. label: function(value, text, shouldAnimate) {
  1554. var
  1555. $label = $('<a />')
  1556. .addClass(className.label)
  1557. .attr('data-value', value)
  1558. .html(templates.label(value, text)),
  1559. $next = module.is.searchSelection()
  1560. ? $search
  1561. : $text
  1562. ;
  1563. if(settings.label.variation) {
  1564. $label.addClass(settings.label.variation);
  1565. }
  1566. if(shouldAnimate == true) {
  1567. module.debug('Animating in label', $label);
  1568. $label
  1569. .addClass(className.hidden)
  1570. .insertBefore($next)
  1571. .transition(settings.label.transition, settings.label.duration)
  1572. ;
  1573. }
  1574. else {
  1575. module.debug('Adding selection label', $label);
  1576. $label
  1577. .insertBefore($next)
  1578. ;
  1579. }
  1580. }
  1581. },
  1582. remove: {
  1583. active: function() {
  1584. $module.removeClass(className.active);
  1585. },
  1586. activeLabel: function() {
  1587. $module.find(selector.label).removeClass(className.active);
  1588. },
  1589. visible: function() {
  1590. $module.removeClass(className.visible);
  1591. },
  1592. activeItem: function() {
  1593. $item.removeClass(className.active);
  1594. },
  1595. filteredItem: function() {
  1596. if(settings.hideSelections) {
  1597. $item.not('.' + className.active).removeClass(className.filtered);
  1598. }
  1599. else {
  1600. $item.removeClass(className.filtered);
  1601. }
  1602. },
  1603. searchTerm: function() {
  1604. module.verbose('Cleared search term');
  1605. $search.val('');
  1606. },
  1607. selected: function(value) {
  1608. var
  1609. $selectedItem = module.get.item(value),
  1610. $option,
  1611. values = $input.val(),
  1612. selectedValue = module.get.choiceValue($selectedItem)
  1613. ;
  1614. if($selectedItem) {
  1615. if( $input.is('select') ) {
  1616. module.verbose('Input is <select> removing selected');
  1617. $input
  1618. .find('option[value="' + selectedValue + '"]')
  1619. .prop('selected', false)
  1620. ;
  1621. }
  1622. else {
  1623. module.verbose('Input is csv removing value');
  1624. values = module.remove.delimitedValue(selectedValue, values);
  1625. $input
  1626. .val(values)
  1627. .trigger('change')
  1628. ;
  1629. }
  1630. if(module.is.multiple()) {
  1631. module.remove.label(selectedValue);
  1632. }
  1633. $selectedItem
  1634. .removeClass(className.filtered)
  1635. .removeClass(className.active)
  1636. ;
  1637. if(settings.hideSelections) {
  1638. $selectedItem.removeClass(className.selected);
  1639. }
  1640. }
  1641. },
  1642. selectedItem: function() {
  1643. $item.removeClass(className.selected);
  1644. },
  1645. delimitedValue: function(removedValue, values) {
  1646. if(typeof values != 'string') {
  1647. return false;
  1648. }
  1649. values = values.split(settings.delimiter);
  1650. values = $.grep(values, function(value){
  1651. return (removedValue != value);
  1652. });
  1653. values = values.join(settings.delimiter);
  1654. module.verbose('Removed value from delimited string', removedValue, values);
  1655. return values;
  1656. },
  1657. label: function(value) {
  1658. var
  1659. $labels = $module.find(selector.label),
  1660. $removedLabel = $labels.filter('[data-value="' + value +'"]'),
  1661. labelCount = $labels.length,
  1662. isLastLabel = ($labels.index($removedLabel) + 1 == labelCount),
  1663. isOnlyLabel = (labelCount == 1),
  1664. shouldAnimate = (isOnlyLabel || isLastLabel)
  1665. ;
  1666. if(shouldAnimate) {
  1667. module.verbose('Animating and removing label', $removedLabel);
  1668. $removedLabel
  1669. .transition(settings.label.transition, settings.label.duration, function() {
  1670. $removedLabel.remove();
  1671. })
  1672. ;
  1673. }
  1674. else {
  1675. module.verbose('Removing label', $removedLabel);
  1676. $removedLabel.remove();
  1677. }
  1678. },
  1679. labels: function($activeLabels) {
  1680. $activeLabels = $activeLabels || $module.find(selector.label).filter('.' + className.active);
  1681. module.verbose('Removing active labels', $activeLabels);
  1682. $activeLabels
  1683. .each(function(){
  1684. module.remove.selected($(this).data('value'));
  1685. })
  1686. ;
  1687. },
  1688. tabbable: function() {
  1689. if( module.has.search() ) {
  1690. module.debug('Searchable dropdown initialized');
  1691. $search
  1692. .attr('tabindex', '-1')
  1693. ;
  1694. $menu
  1695. .attr('tabindex', '-1')
  1696. ;
  1697. }
  1698. else {
  1699. module.debug('Simple selection dropdown initialized');
  1700. $module
  1701. .attr('tabindex', '-1')
  1702. ;
  1703. $menu
  1704. .attr('tabindex', '-1')
  1705. ;
  1706. }
  1707. }
  1708. },
  1709. has: {
  1710. search: function() {
  1711. return ($search.length > 0);
  1712. },
  1713. input: function() {
  1714. return ($input.length > 0);
  1715. },
  1716. menu: function() {
  1717. return ($menu.length > 0);
  1718. }
  1719. },
  1720. is: {
  1721. active: function() {
  1722. return $module.hasClass(className.active);
  1723. },
  1724. alreadySetup: function() {
  1725. return ($module.is('select') && $module.parent(selector.dropdown).length > 0);
  1726. },
  1727. animating: function($subMenu) {
  1728. return ($subMenu)
  1729. ? $subMenu.transition && $subMenu.transition('is animating')
  1730. : $menu.transition && $menu.transition('is animating')
  1731. ;
  1732. },
  1733. focused: function() {
  1734. return (document.activeElement === $module[0]);
  1735. },
  1736. focusedOnSearch: function() {
  1737. return (document.activeElement === $search[0]);
  1738. },
  1739. allFiltered: function() {
  1740. return ($item.filter('.' + className.filtered).length === $item.length);
  1741. },
  1742. hidden: function($subMenu) {
  1743. return ($subMenu)
  1744. ? $subMenu.is(':hidden')
  1745. : $menu.is(':hidden')
  1746. ;
  1747. },
  1748. inObject: function(needle, object) {
  1749. var
  1750. found = false
  1751. ;
  1752. $.each(object, function(index, property) {
  1753. if(property == needle) {
  1754. found = true;
  1755. return true;
  1756. }
  1757. });
  1758. return found;
  1759. },
  1760. multiple: function() {
  1761. return $module.hasClass(className.multiple);
  1762. },
  1763. single: function() {
  1764. return !module.is.multiple();
  1765. },
  1766. selectMutation: function(mutations) {
  1767. var
  1768. selectChanged = false
  1769. ;
  1770. $.each(mutations, function(index, mutation) {
  1771. if(mutation.target && $(mutation.target).is('select')) {
  1772. selectChanged = true;
  1773. return true;
  1774. }
  1775. });
  1776. return selectChanged;
  1777. },
  1778. search: function() {
  1779. return $module.hasClass(className.search);
  1780. },
  1781. searchSelection: function() {
  1782. return ( module.has.search() && $search.closest(selector.menu).length == 0 );
  1783. },
  1784. selection: function() {
  1785. return $module.hasClass(className.selection);
  1786. },
  1787. upward: function() {
  1788. return $module.hasClass(className.upward);
  1789. },
  1790. visible: function($subMenu) {
  1791. return ($subMenu)
  1792. ? $subMenu.is(':visible')
  1793. : $menu.is(':visible')
  1794. ;
  1795. }
  1796. },
  1797. can: {
  1798. click: function() {
  1799. return (hasTouch || settings.on == 'click');
  1800. },
  1801. show: function() {
  1802. return !$module.hasClass(className.disabled);
  1803. }
  1804. },
  1805. animate: {
  1806. show: function(callback, $subMenu) {
  1807. var
  1808. $currentMenu = $subMenu || $menu,
  1809. start = ($subMenu)
  1810. ? function() {}
  1811. : function() {
  1812. module.hideSubMenus();
  1813. module.hideOthers();
  1814. module.set.active();
  1815. }
  1816. ;
  1817. callback = $.isFunction(callback)
  1818. ? callback
  1819. : function(){}
  1820. ;
  1821. if(module.is.single()) {
  1822. module.set.scrollPosition(module.get.activeItem(), true);
  1823. }
  1824. module.verbose('Doing menu show animation', $currentMenu);
  1825. if( module.is.hidden($currentMenu) || module.is.animating($currentMenu) ) {
  1826. if(settings.transition == 'auto') {
  1827. settings.transition = module.is.upward()
  1828. ? 'slide up'
  1829. : 'slide down'
  1830. ;
  1831. module.verbose('Automatically determining animation based on animation direction', settings.transition);
  1832. }
  1833. if(settings.transition == 'none') {
  1834. start();
  1835. $currentMenu.transition('show');
  1836. callback.call(element);
  1837. }
  1838. else if($.fn.transition !== undefined && $module.transition('is supported')) {
  1839. $currentMenu
  1840. .transition({
  1841. animation : settings.transition + ' in',
  1842. debug : settings.debug,
  1843. verbose : settings.verbose,
  1844. duration : settings.duration,
  1845. queue : true,
  1846. onStart : start,
  1847. onComplete : function() {
  1848. callback.call(element);
  1849. }
  1850. })
  1851. ;
  1852. }
  1853. else {
  1854. module.error(error.noTransition, settings.transition);
  1855. }
  1856. }
  1857. },
  1858. hide: function(callback, $subMenu) {
  1859. var
  1860. $currentMenu = $subMenu || $menu,
  1861. duration = ($subMenu)
  1862. ? (settings.duration * 0.9)
  1863. : settings.duration,
  1864. start = ($subMenu)
  1865. ? function() {}
  1866. : function() {
  1867. if( module.can.click() ) {
  1868. module.unbind.intent();
  1869. }
  1870. module.focusSearch();
  1871. module.remove.active();
  1872. }
  1873. ;
  1874. callback = $.isFunction(callback)
  1875. ? callback
  1876. : function(){}
  1877. ;
  1878. if( module.is.visible($currentMenu) || module.is.animating($currentMenu) ) {
  1879. module.verbose('Doing menu hide animation', $currentMenu);
  1880. if(settings.transition == 'auto') {
  1881. settings.transition = module.is.upward()
  1882. ? 'slide up'
  1883. : 'slide down'
  1884. ;
  1885. }
  1886. if(settings.transition == 'none') {
  1887. start();
  1888. $currentMenu.transition('hide');
  1889. callback.call(element);
  1890. }
  1891. else if($.fn.transition !== undefined && $module.transition('is supported')) {
  1892. $currentMenu
  1893. .transition({
  1894. animation : settings.transition + ' out',
  1895. duration : settings.duration,
  1896. debug : settings.debug,
  1897. verbose : settings.verbose,
  1898. queue : true,
  1899. onStart : start,
  1900. onComplete : function() {
  1901. callback.call(element);
  1902. }
  1903. })
  1904. ;
  1905. }
  1906. else {
  1907. module.error(error.transition);
  1908. }
  1909. }
  1910. }
  1911. },
  1912. hideAndClear: function() {
  1913. if(module.is.searchSelection()) {
  1914. module.remove.searchTerm();
  1915. module.hide(function() {
  1916. module.remove.filteredItem();
  1917. });
  1918. }
  1919. else {
  1920. module.hide();
  1921. }
  1922. },
  1923. delay: {
  1924. show: function() {
  1925. module.verbose('Delaying show event to ensure user intent');
  1926. clearTimeout(module.timer);
  1927. module.timer = setTimeout(module.show, settings.delay.show);
  1928. },
  1929. hide: function() {
  1930. module.verbose('Delaying hide event to ensure user intent');
  1931. clearTimeout(module.timer);
  1932. module.timer = setTimeout(module.hide, settings.delay.hide);
  1933. }
  1934. },
  1935. escape: {
  1936. regExp: function(text) {
  1937. text = String(text);
  1938. return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
  1939. }
  1940. },
  1941. setting: function(name, value) {
  1942. module.debug('Changing setting', name, value);
  1943. if( $.isPlainObject(name) ) {
  1944. $.extend(true, settings, name);
  1945. }
  1946. else if(value !== undefined) {
  1947. settings[name] = value;
  1948. }
  1949. else {
  1950. return settings[name];
  1951. }
  1952. },
  1953. internal: function(name, value) {
  1954. if( $.isPlainObject(name) ) {
  1955. $.extend(true, module, name);
  1956. }
  1957. else if(value !== undefined) {
  1958. module[name] = value;
  1959. }
  1960. else {
  1961. return module[name];
  1962. }
  1963. },
  1964. debug: function() {
  1965. if(settings.debug) {
  1966. if(settings.performance) {
  1967. module.performance.log(arguments);
  1968. }
  1969. else {
  1970. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  1971. module.debug.apply(console, arguments);
  1972. }
  1973. }
  1974. },
  1975. verbose: function() {
  1976. if(settings.verbose && settings.debug) {
  1977. if(settings.performance) {
  1978. module.performance.log(arguments);
  1979. }
  1980. else {
  1981. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  1982. module.verbose.apply(console, arguments);
  1983. }
  1984. }
  1985. },
  1986. error: function() {
  1987. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  1988. module.error.apply(console, arguments);
  1989. },
  1990. performance: {
  1991. log: function(message) {
  1992. var
  1993. currentTime,
  1994. executionTime,
  1995. previousTime
  1996. ;
  1997. if(settings.performance) {
  1998. currentTime = new Date().getTime();
  1999. previousTime = time || currentTime;
  2000. executionTime = currentTime - previousTime;
  2001. time = currentTime;
  2002. performance.push({
  2003. 'Name' : message[0],
  2004. 'Arguments' : [].slice.call(message, 1) || '',
  2005. 'Element' : element,
  2006. 'Execution Time' : executionTime
  2007. });
  2008. }
  2009. clearTimeout(module.performance.timer);
  2010. module.performance.timer = setTimeout(module.performance.display, 500);
  2011. },
  2012. display: function() {
  2013. var
  2014. title = settings.name + ':',
  2015. totalTime = 0
  2016. ;
  2017. time = false;
  2018. clearTimeout(module.performance.timer);
  2019. $.each(performance, function(index, data) {
  2020. totalTime += data['Execution Time'];
  2021. });
  2022. title += ' ' + totalTime + 'ms';
  2023. if(moduleSelector) {
  2024. title += ' \'' + moduleSelector + '\'';
  2025. }
  2026. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  2027. console.groupCollapsed(title);
  2028. if(console.table) {
  2029. console.table(performance);
  2030. }
  2031. else {
  2032. $.each(performance, function(index, data) {
  2033. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  2034. });
  2035. }
  2036. console.groupEnd();
  2037. }
  2038. performance = [];
  2039. }
  2040. },
  2041. invoke: function(query, passedArguments, context) {
  2042. var
  2043. object = instance,
  2044. maxDepth,
  2045. found,
  2046. response
  2047. ;
  2048. passedArguments = passedArguments || queryArguments;
  2049. context = element || context;
  2050. if(typeof query == 'string' && object !== undefined) {
  2051. query = query.split(/[\. ]/);
  2052. maxDepth = query.length - 1;
  2053. $.each(query, function(depth, value) {
  2054. var camelCaseValue = (depth != maxDepth)
  2055. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  2056. : query
  2057. ;
  2058. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  2059. object = object[camelCaseValue];
  2060. }
  2061. else if( object[camelCaseValue] !== undefined ) {
  2062. found = object[camelCaseValue];
  2063. return false;
  2064. }
  2065. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  2066. object = object[value];
  2067. }
  2068. else if( object[value] !== undefined ) {
  2069. found = object[value];
  2070. return false;
  2071. }
  2072. else {
  2073. module.error(error.method, query);
  2074. return false;
  2075. }
  2076. });
  2077. }
  2078. if ( $.isFunction( found ) ) {
  2079. response = found.apply(context, passedArguments);
  2080. }
  2081. else if(found !== undefined) {
  2082. response = found;
  2083. }
  2084. if($.isArray(returnedValue)) {
  2085. returnedValue.push(response);
  2086. }
  2087. else if(returnedValue !== undefined) {
  2088. returnedValue = [returnedValue, response];
  2089. }
  2090. else if(response !== undefined) {
  2091. returnedValue = response;
  2092. }
  2093. return found;
  2094. }
  2095. };
  2096. if(methodInvoked) {
  2097. if(instance === undefined) {
  2098. module.initialize();
  2099. }
  2100. module.invoke(query);
  2101. }
  2102. else {
  2103. if(instance !== undefined) {
  2104. instance.invoke('destroy');
  2105. }
  2106. module.initialize();
  2107. }
  2108. })
  2109. ;
  2110. return (returnedValue !== undefined)
  2111. ? returnedValue
  2112. : $allModules
  2113. ;
  2114. };
  2115. $.fn.dropdown.settings = {
  2116. debug : false,
  2117. verbose : false,
  2118. performance : true,
  2119. on : 'click',
  2120. // what event should show menu
  2121. action : 'activate',
  2122. // action on item selection
  2123. allowTab : true,
  2124. // add tabindex to element
  2125. showOnFocus : true,
  2126. // show menu on focus
  2127. fullTextSearch : false,
  2128. // search anywhere in value
  2129. placeholder : 'auto',
  2130. // whether to convert blank <select> values to placeholder text
  2131. preserveHTML : true,
  2132. // preserve html when selecting value
  2133. sortSelect : false,
  2134. // sort selection on init
  2135. match : 'both',
  2136. // what to match against with search selection (both, text, or label)
  2137. allowCategorySelection : false,
  2138. // allow elements with sub-menus to be selected
  2139. forceSelection : true,
  2140. // force a value selection on search selection
  2141. transition : 'auto',
  2142. duration : 250,
  2143. // menu transiton
  2144. delay : {
  2145. hide : 300,
  2146. show : 200,
  2147. search : 50,
  2148. touch : 50
  2149. },
  2150. // delay before event
  2151. glyphWidth : 1.0714,
  2152. // widest glyph width in em (W is 1.0714 em) used to calculate multiselect input width
  2153. allowAdditions : true,
  2154. // whether multiple select should allow user added values
  2155. delimiter : ',',
  2156. // multi select delimiting key
  2157. label: {
  2158. transition : 'horizontal flip',
  2159. duration : 150,
  2160. variation : false
  2161. },
  2162. // label settings on multi-select
  2163. hideSelections : true,
  2164. // whether multiple selects should filter active selections from menu
  2165. /* Callbacks */
  2166. onLabelSelect : function($selectedLabels){},
  2167. onNoResults : function(searchTerm){},
  2168. onChange : function(value, text, $selected){},
  2169. onShow : function(){},
  2170. onHide : function(){},
  2171. /* Component */
  2172. name : 'Dropdown',
  2173. namespace : 'dropdown',
  2174. error : {
  2175. action : 'You called a dropdown action that was not defined',
  2176. alreadySetup : 'Once a select has been initialized behaviors must be called on the created ui dropdown',
  2177. method : 'The method you called is not defined.',
  2178. noTransition : 'This module requires ui transitions <https://github.com/Semantic-Org/UI-Transition>'
  2179. },
  2180. metadata: {
  2181. defaultText : 'defaultText',
  2182. defaultValue : 'defaultValue',
  2183. placeholderText : 'placeholderText',
  2184. text : 'text',
  2185. value : 'value'
  2186. },
  2187. selector : {
  2188. dropdown : '.ui.dropdown',
  2189. icon : '> .dropdown.icon',
  2190. input : '> input[type="hidden"], > select',
  2191. item : '.item',
  2192. label : '> .label',
  2193. remove : '> .label > .delete.icon',
  2194. siblingLabel : '.label',
  2195. menu : '.menu',
  2196. menuIcon : '.dropdown.icon',
  2197. search : 'input.search, .menu > .search > input',
  2198. text : '> .text:not(.icon)'
  2199. },
  2200. className : {
  2201. active : 'active',
  2202. animating : 'animating',
  2203. disabled : 'disabled',
  2204. dropdown : 'ui dropdown',
  2205. filtered : 'filtered',
  2206. hidden : 'hidden transition',
  2207. label : 'ui label',
  2208. loading : 'loading',
  2209. menu : 'menu',
  2210. multiple : 'multiple',
  2211. placeholder : 'default',
  2212. search : 'search',
  2213. selected : 'selected',
  2214. selection : 'selection',
  2215. upward : 'upward',
  2216. visible : 'visible'
  2217. }
  2218. };
  2219. /* Templates */
  2220. $.fn.dropdown.settings.templates = {
  2221. menu: function(select) {
  2222. var
  2223. placeholder = select.placeholder || false,
  2224. values = select.values || {},
  2225. html = ''
  2226. ;
  2227. $.each(select.values, function(index, option) {
  2228. html += '<div class="item" data-value="' + option.value + '">' + option.name + '</div>';
  2229. });
  2230. return html;
  2231. },
  2232. label: function(value, text) {
  2233. return text + '<i class="delete icon"></i>';
  2234. },
  2235. dropdown: function(select) {
  2236. var
  2237. placeholder = select.placeholder || false,
  2238. values = select.values || {},
  2239. html = ''
  2240. ;
  2241. html += '<i class="dropdown icon"></i>';
  2242. if(select.placeholder) {
  2243. html += '<div class="default text">' + placeholder + '</div>';
  2244. }
  2245. else {
  2246. html += '<div class="text"></div>';
  2247. }
  2248. html += '<div class="menu">';
  2249. $.each(select.values, function(index, option) {
  2250. html += '<div class="item" data-value="' + option.value + '">' + option.name + '</div>';
  2251. });
  2252. html += '</div>';
  2253. return html;
  2254. }
  2255. };
  2256. /* Dependencies */
  2257. $.extend( $.easing, {
  2258. easeOutQuad: function (x, t, b, c, d) {
  2259. return -c *(t/=d)*(t-2) + b;
  2260. },
  2261. });
  2262. })( jQuery, window , document );