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.

1893 lines
61 KiB

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
9 years ago
9 years ago
10 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
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
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
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
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
10 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
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
9 years ago
10 years ago
10 years ago
10 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
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
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
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
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
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
  1. /*!
  2. * # Semantic UI 1.11.4 - 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. eventNamespace = '.' + namespace,
  38. moduleNamespace = 'module-' + namespace,
  39. $module = $(this),
  40. $text = $module.find(selector.text),
  41. $search = $module.find(selector.search),
  42. $labels = $module.find(selector.labels),
  43. $input = $module.find(selector.input),
  44. $combo = ($module.prev().find(selector.text).length > 0)
  45. ? $module.prev().find(selector.text)
  46. : $module.prev(),
  47. $menu = $module.children(selector.menu),
  48. $item = $menu.find(selector.item),
  49. activated = false,
  50. itemActivated = false,
  51. element = this,
  52. instance = $module.data(moduleNamespace),
  53. elementNamespace,
  54. id,
  55. selectObserver,
  56. menuObserver,
  57. module
  58. ;
  59. module = {
  60. initialize: function() {
  61. module.debug('Initializing dropdown', settings);
  62. if( module.is.alreadySetup() ) {
  63. module.setup.reference();
  64. }
  65. else {
  66. module.setup.layout();
  67. module.save.defaults();
  68. module.set.selected();
  69. module.create.id();
  70. if(hasTouch) {
  71. module.bind.touchEvents();
  72. }
  73. module.bind.mouseEvents();
  74. module.bind.keyboardEvents();
  75. module.observeChanges();
  76. module.instantiate();
  77. }
  78. },
  79. instantiate: function() {
  80. module.verbose('Storing instance of dropdown', module);
  81. instance = module;
  82. $module
  83. .data(moduleNamespace, module)
  84. ;
  85. },
  86. destroy: function() {
  87. module.verbose('Destroying previous dropdown for', $module);
  88. module.remove.tabbable();
  89. $module
  90. .off(eventNamespace)
  91. .removeData(moduleNamespace)
  92. ;
  93. $menu
  94. .off(eventNamespace)
  95. ;
  96. $document
  97. .off(elementNamespace)
  98. ;
  99. if(selectObserver) {
  100. selectObserver.disconnect();
  101. }
  102. if(menuObserver) {
  103. menuObserver.disconnect();
  104. }
  105. },
  106. observeChanges: function() {
  107. if('MutationObserver' in window) {
  108. selectObserver = new MutationObserver(function(mutations) {
  109. module.debug('<select> modified, recreating menu');
  110. module.setup.select();
  111. });
  112. menuObserver = new MutationObserver(function(mutations) {
  113. module.debug('Menu modified, updating selector cache');
  114. module.refresh();
  115. });
  116. if(module.has.input()) {
  117. selectObserver.observe($input[0], {
  118. childList : true,
  119. subtree : true
  120. });
  121. }
  122. if(module.has.menu()) {
  123. menuObserver.observe($menu[0], {
  124. childList : true,
  125. subtree : true
  126. });
  127. }
  128. module.debug('Setting up mutation observer', selectObserver, menuObserver);
  129. }
  130. },
  131. create: {
  132. id: function() {
  133. id = (Math.random().toString(16) + '000000000').substr(2,8);
  134. elementNamespace = '.' + id;
  135. module.verbose('Creating unique id for element', id);
  136. }
  137. },
  138. search: function() {
  139. var
  140. query
  141. ;
  142. query = $search.val();
  143. module.verbose('Searching for query', query);
  144. module.filter(query);
  145. if(module.is.searchSelection() && module.can.show() ) {
  146. module.show();
  147. }
  148. },
  149. setup: {
  150. layout: function() {
  151. if( $module.is('select') ) {
  152. module.setup.select();
  153. }
  154. if( module.is.search() && !module.has.search() ) {
  155. $search = $('<input />')
  156. .addClass(className.search)
  157. .insertBefore($text)
  158. ;
  159. }
  160. if( module.is.multiple() && !module.has.labels()) {
  161. $labels = $('<div />').addClass(className.labels).insertBefore($search);
  162. }
  163. if(settings.allowTab) {
  164. module.set.tabbable();
  165. }
  166. },
  167. select: function() {
  168. var
  169. selectValues = module.get.selectValues()
  170. ;
  171. module.debug('Dropdown initialized on a select', selectValues);
  172. if( $module.is('select') ) {
  173. $input = $module;
  174. }
  175. // see if select is placed correctly already
  176. if($input.parent(selector.dropdown).length > 0) {
  177. module.debug('UI dropdown already exists. Creating dropdown menu only');
  178. $module = $input.closest(selector.dropdown);
  179. $menu = $module.children(selector.menu);
  180. if($menu.length === 0) {
  181. $menu = $('<div />')
  182. .addClass(className.menu)
  183. .appendTo($module)
  184. ;
  185. }
  186. $menu.html( settings.templates.menu( selectValues ));
  187. }
  188. else {
  189. module.debug('Creating entire dropdown from select');
  190. $module = $('<div />')
  191. .attr('class', $input.attr('class') )
  192. .addClass(className.selection)
  193. .addClass(className.dropdown)
  194. .html( settings.templates.dropdown(selectValues) )
  195. .insertBefore($input)
  196. ;
  197. $input
  198. .removeAttr('class')
  199. .prependTo($module)
  200. ;
  201. }
  202. if($input.is('[multiple]')) {
  203. module.set.multiple();
  204. }
  205. module.refresh();
  206. },
  207. reference: function() {
  208. var
  209. index = $allModules.index($module),
  210. $firstModules,
  211. $lastModules
  212. ;
  213. module.debug('Dropdown behavior was called on select, replacing with closest dropdown');
  214. // replace module reference
  215. $module = $module.parent(selector.dropdown);
  216. module.refresh();
  217. // adjust all modules
  218. $firstModules = $allModules.slice(0, index);
  219. $lastModules = $allModules.slice(index + 1);
  220. $allModules = $firstModules.add($module).add($lastModules);
  221. }
  222. },
  223. refresh: function() {
  224. module.verbose('Refreshing selector cache');
  225. $text = $module.find(selector.text);
  226. $search = $module.find(selector.search);
  227. $input = $module.find(selector.input);
  228. $combo = ($module.prev().find(selector.text).length > 0)
  229. ? $module.prev().find(selector.text)
  230. : $module.prev()
  231. ;
  232. $menu = $module.children(selector.menu);
  233. $item = $menu.find(selector.item);
  234. // multiple
  235. $labels = $module.find(selector.labels);
  236. },
  237. toggle: function() {
  238. module.verbose('Toggling menu visibility');
  239. if( !module.is.active() ) {
  240. module.show();
  241. }
  242. else {
  243. module.hide();
  244. }
  245. },
  246. show: function(callback) {
  247. callback = $.isFunction(callback)
  248. ? callback
  249. : function(){}
  250. ;
  251. if( module.is.searchSelection() && module.is.allFiltered() ) {
  252. return;
  253. }
  254. if( module.can.show() && !module.is.active() ) {
  255. module.debug('Showing dropdown');
  256. module.animate.show(function() {
  257. if( module.can.click() ) {
  258. module.bind.intent();
  259. }
  260. module.set.visible();
  261. callback.call(element);
  262. });
  263. settings.onShow.call(element);
  264. }
  265. },
  266. hide: function(callback) {
  267. callback = $.isFunction(callback)
  268. ? callback
  269. : function(){}
  270. ;
  271. if( module.is.active() ) {
  272. module.debug('Hiding dropdown');
  273. module.animate.hide(function() {
  274. module.remove.visible();
  275. callback.call(element);
  276. });
  277. settings.onHide.call(element);
  278. }
  279. },
  280. hideOthers: function() {
  281. module.verbose('Finding other dropdowns to hide');
  282. $allModules
  283. .not($module)
  284. .has(selector.menu + ':visible:not(.' + className.animating + ')')
  285. .dropdown('hide')
  286. ;
  287. },
  288. hideSubMenus: function() {
  289. var
  290. $subMenus = $menu.find(selector.menu)
  291. ;
  292. $subMenus.transition('hide');
  293. },
  294. bind: {
  295. keyboardEvents: function() {
  296. module.debug('Binding keyboard events');
  297. $module
  298. .on('keydown' + eventNamespace, module.event.keydown)
  299. ;
  300. if( module.has.search() ) {
  301. $module
  302. .on(module.get.inputEvent(), selector.search, module.event.input)
  303. ;
  304. }
  305. },
  306. touchEvents: function() {
  307. module.debug('Touch device detected binding additional touch events');
  308. if( module.is.searchSelection() ) {
  309. // do nothing special yet
  310. }
  311. else {
  312. $module
  313. .on('touchstart' + eventNamespace, module.event.test.toggle)
  314. ;
  315. }
  316. $menu
  317. .on('touchstart' + eventNamespace, selector.item, module.event.item.mouseenter)
  318. ;
  319. },
  320. mouseEvents: function() {
  321. module.verbose('Mouse detected binding mouse events');
  322. if( module.is.searchSelection() ) {
  323. $module
  324. .on('mousedown' + eventNamespace, selector.menu, module.event.menu.activate)
  325. .on('mouseup' + eventNamespace, selector.menu, module.event.menu.deactivate)
  326. .on('click' + eventNamespace, selector.search, module.show)
  327. .on('focus' + eventNamespace, selector.search, module.event.searchFocus)
  328. .on('blur' + eventNamespace, selector.search, module.event.searchBlur)
  329. .on('click' + eventNamespace, selector.text, module.event.searchTextFocus)
  330. ;
  331. }
  332. else {
  333. if(settings.on == 'click') {
  334. $module
  335. .on('click' + eventNamespace, module.event.test.toggle)
  336. ;
  337. }
  338. else if(settings.on == 'hover') {
  339. $module
  340. .on('mouseenter' + eventNamespace, module.delay.show)
  341. .on('mouseleave' + eventNamespace, module.delay.hide)
  342. ;
  343. }
  344. else {
  345. $module
  346. .on(settings.on + eventNamespace, module.toggle)
  347. ;
  348. }
  349. $module
  350. .on('mousedown' + eventNamespace, module.event.mousedown)
  351. .on('mouseup' + eventNamespace, module.event.mouseup)
  352. .on('focus' + eventNamespace, module.event.focus)
  353. .on('blur' + eventNamespace, module.event.blur)
  354. ;
  355. }
  356. $menu
  357. .on('mouseenter' + eventNamespace, selector.item, module.event.item.mouseenter)
  358. .on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave)
  359. .on('click' + eventNamespace, selector.item, module.event.item.click)
  360. ;
  361. },
  362. intent: function() {
  363. module.verbose('Binding hide intent event to document');
  364. if(hasTouch) {
  365. $document
  366. .on('touchstart' + elementNamespace, module.event.test.touch)
  367. .on('touchmove' + elementNamespace, module.event.test.touch)
  368. ;
  369. }
  370. $document
  371. .on('click' + elementNamespace, module.event.test.hide)
  372. ;
  373. }
  374. },
  375. unbind: {
  376. intent: function() {
  377. module.verbose('Removing hide intent event from document');
  378. if(hasTouch) {
  379. $document
  380. .off('touchstart' + elementNamespace)
  381. .off('touchmove' + elementNamespace)
  382. ;
  383. }
  384. $document
  385. .off('click' + elementNamespace)
  386. ;
  387. }
  388. },
  389. filter: function(searchTerm) {
  390. var
  391. $results = $(),
  392. escapedTerm = module.escape.regExp(searchTerm),
  393. exactRegExp = new RegExp('^' + escapedTerm, 'igm'),
  394. fullTextRegExp = new RegExp(escapedTerm, 'ig'),
  395. allItemsFiltered
  396. ;
  397. module.verbose('Searching for matching values');
  398. $item
  399. .each(function(){
  400. var
  401. $choice = $(this),
  402. text = String(module.get.choiceText($choice, false)),
  403. value = String(module.get.choiceValue($choice, text))
  404. ;
  405. if( text.match(exactRegExp) || value.match(exactRegExp) ) {
  406. $results = $results.add($choice);
  407. }
  408. else if(settings.fullTextSearch) {
  409. if( text.match(fullTextRegExp) || value.match(fullTextRegExp) ) {
  410. $results = $results.add($choice);
  411. }
  412. }
  413. })
  414. ;
  415. module.debug('Setting filter', searchTerm);
  416. module.remove.filteredItem();
  417. $item
  418. .not($results)
  419. .addClass(className.filtered)
  420. ;
  421. module.verbose('Selecting first non-filtered element');
  422. module.remove.selectedItem();
  423. $item
  424. .not('.' + className.filtered)
  425. .eq(0)
  426. .addClass(className.selected)
  427. ;
  428. if( module.is.allFiltered() ) {
  429. module.debug('All items filtered, hiding dropdown', searchTerm);
  430. if(module.is.searchSelection()) {
  431. module.hide();
  432. }
  433. settings.onNoResults.call(element, searchTerm);
  434. }
  435. },
  436. focusSearch: function() {
  437. if( module.is.search() ) {
  438. $search
  439. .focus()
  440. ;
  441. }
  442. },
  443. forceSelection: function() {
  444. var
  445. $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
  446. $activeItem = $item.filter('.' + className.active).eq(0),
  447. $selectedItem = ($currentlySelected.length > 0)
  448. ? $currentlySelected
  449. : $activeItem,
  450. hasSelected = ($selectedItem.size() > 0)
  451. ;
  452. if(hasSelected) {
  453. module.event.item.click.call($selectedItem);
  454. module.remove.filteredItem();
  455. }
  456. else {
  457. module.hide();
  458. }
  459. },
  460. event: {
  461. // prevents focus callback from occuring on mousedown
  462. mousedown: function() {
  463. activated = true;
  464. },
  465. mouseup: function() {
  466. activated = false;
  467. },
  468. focus: function() {
  469. if(!activated && module.is.hidden()) {
  470. module.show();
  471. }
  472. },
  473. blur: function(event) {
  474. var
  475. pageLostFocus = (document.activeElement === this)
  476. ;
  477. if(!activated && !pageLostFocus) {
  478. module.hide();
  479. }
  480. },
  481. searchFocus: function() {
  482. activated = true;
  483. module.show();
  484. },
  485. searchBlur: function(event) {
  486. var
  487. pageLostFocus = (document.activeElement === this)
  488. ;
  489. if(!itemActivated && !pageLostFocus) {
  490. if(settings.forceSelection) {
  491. module.forceSelection();
  492. }
  493. else {
  494. module.hide();
  495. }
  496. }
  497. },
  498. searchTextFocus: function(event) {
  499. activated = true;
  500. $search.focus();
  501. },
  502. input: function(event) {
  503. if(module.is.searchSelection()) {
  504. module.set.filtered();
  505. }
  506. clearTimeout(module.timer);
  507. module.timer = setTimeout(module.search, settings.delay.search);
  508. },
  509. keydown: function(event) {
  510. var
  511. $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
  512. $activeItem = $menu.children('.' + className.active).eq(0),
  513. $selectedItem = ($currentlySelected.length > 0)
  514. ? $currentlySelected
  515. : $activeItem,
  516. $visibleItems = ($selectedItem.length > 0)
  517. ? $selectedItem.siblings(':not(.' + className.filtered +')').andSelf()
  518. : $menu.children(':not(.' + className.filtered +')'),
  519. $subMenu = $selectedItem.children(selector.menu),
  520. $parentMenu = $selectedItem.closest(selector.menu),
  521. isSubMenuItem = $parentMenu[0] !== $menu[0],
  522. inVisibleMenu = $parentMenu.is(':visible'),
  523. pressedKey = event.which,
  524. keys = {
  525. enter : 13,
  526. escape : 27,
  527. leftArrow : 37,
  528. upArrow : 38,
  529. rightArrow : 39,
  530. downArrow : 40
  531. },
  532. hasSubMenu = ($subMenu.length> 0),
  533. hasSelectedItem = ($selectedItem.length > 0),
  534. lastVisibleIndex = ($visibleItems.size() - 1),
  535. $nextItem,
  536. newIndex
  537. ;
  538. // visible menu keyboard shortcuts
  539. if(module.is.visible()) {
  540. // enter (select or sub-menu)
  541. if(pressedKey == keys.enter && hasSelectedItem) {
  542. if(hasSubMenu && !settings.allowCategorySelection) {
  543. module.verbose('Pressed enter on unselectable category, opening sub menu');
  544. pressedKey = keys.rightArrow;
  545. }
  546. else {
  547. module.verbose('Enter key pressed, choosing selected item');
  548. module.event.item.click.call($selectedItem, event);
  549. }
  550. }
  551. // left arrow (hide sub-menu)
  552. if(pressedKey == keys.leftArrow) {
  553. if(isSubMenuItem) {
  554. module.verbose('Left key pressed, closing sub-menu');
  555. module.animate.hide(false, $parentMenu);
  556. $selectedItem
  557. .removeClass(className.selected)
  558. ;
  559. $parentMenu
  560. .closest(selector.item)
  561. .addClass(className.selected)
  562. ;
  563. }
  564. event.preventDefault();
  565. }
  566. // right arrow (show sub-menu)
  567. if(pressedKey == keys.rightArrow) {
  568. if(hasSubMenu) {
  569. module.verbose('Right key pressed, opening sub-menu');
  570. module.animate.show(false, $subMenu);
  571. $selectedItem
  572. .removeClass(className.selected)
  573. ;
  574. $subMenu
  575. .find(selector.item).eq(0)
  576. .addClass(className.selected)
  577. ;
  578. }
  579. event.preventDefault();
  580. }
  581. // up arrow (traverse menu up)
  582. if(pressedKey == keys.upArrow) {
  583. $nextItem = (hasSelectedItem && inVisibleMenu)
  584. ? $selectedItem.prevAll(selector.item + ':not(.' + className.filtered + ')').eq(0)
  585. : $item.eq(0)
  586. ;
  587. if($visibleItems.index( $nextItem ) < 0) {
  588. module.verbose('Up key pressed but reached top of current menu');
  589. return;
  590. }
  591. else {
  592. module.verbose('Up key pressed, changing active item');
  593. $selectedItem
  594. .removeClass(className.selected)
  595. ;
  596. $nextItem
  597. .addClass(className.selected)
  598. ;
  599. module.set.scrollPosition($nextItem);
  600. }
  601. event.preventDefault();
  602. }
  603. // down arrow (traverse menu down)
  604. if(pressedKey == keys.downArrow) {
  605. $nextItem = (hasSelectedItem && inVisibleMenu)
  606. ? $nextItem = $selectedItem.nextAll(selector.item + ':not(.' + className.filtered + ')').eq(0)
  607. : $item.eq(0)
  608. ;
  609. if($nextItem.length === 0) {
  610. module.verbose('Down key pressed but reached bottom of current menu');
  611. return;
  612. }
  613. else {
  614. module.verbose('Down key pressed, changing active item');
  615. $item
  616. .removeClass(className.selected)
  617. ;
  618. $nextItem
  619. .addClass(className.selected)
  620. ;
  621. module.set.scrollPosition($nextItem);
  622. }
  623. event.preventDefault();
  624. }
  625. }
  626. else {
  627. // enter (open menu)
  628. if(pressedKey == keys.enter) {
  629. module.verbose('Enter key pressed, showing dropdown');
  630. module.show();
  631. }
  632. // escape (close menu)
  633. if(pressedKey == keys.escape) {
  634. module.verbose('Escape key pressed, closing dropdown');
  635. module.hide();
  636. }
  637. // down arrow (open menu)
  638. if(pressedKey == keys.downArrow) {
  639. module.verbose('Down key pressed, showing dropdown');
  640. module.show();
  641. }
  642. }
  643. },
  644. test: {
  645. toggle: function(event) {
  646. if( module.determine.eventInMenu(event, module.toggle) ) {
  647. event.preventDefault();
  648. }
  649. },
  650. touch: function(event) {
  651. module.determine.eventInMenu(event, function() {
  652. if(event.type == 'touchstart') {
  653. module.timer = setTimeout(module.hide, settings.delay.touch);
  654. }
  655. else if(event.type == 'touchmove') {
  656. clearTimeout(module.timer);
  657. }
  658. });
  659. event.stopPropagation();
  660. },
  661. hide: function(event) {
  662. module.determine.eventInModule(event, module.hide);
  663. }
  664. },
  665. menu: {
  666. activate: function() {
  667. itemActivated = true;
  668. },
  669. deactivate: function() {
  670. itemActivated = false;
  671. }
  672. },
  673. item: {
  674. mouseenter: function(event) {
  675. var
  676. $subMenu = $(this).children(selector.menu),
  677. $otherMenus = $(this).siblings(selector.item).children(selector.menu)
  678. ;
  679. if( $subMenu.length > 0 ) {
  680. clearTimeout(module.itemTimer);
  681. module.itemTimer = setTimeout(function() {
  682. module.verbose('Showing sub-menu', $subMenu);
  683. $.each($otherMenus, function() {
  684. module.animate.hide(false, $(this));
  685. });
  686. module.animate.show(false, $subMenu);
  687. }, settings.delay.show);
  688. event.preventDefault();
  689. }
  690. },
  691. mouseleave: function(event) {
  692. var
  693. $subMenu = $(this).children(selector.menu)
  694. ;
  695. if($subMenu.length > 0) {
  696. clearTimeout(module.itemTimer);
  697. module.itemTimer = setTimeout(function() {
  698. module.verbose('Hiding sub-menu', $subMenu);
  699. module.animate.hide(false, $subMenu);
  700. }, settings.delay.hide);
  701. }
  702. },
  703. click: function (event) {
  704. var
  705. $choice = $(this),
  706. $target = (event)
  707. ? $(event.target)
  708. : $(''),
  709. $subMenu = $choice.find(selector.menu),
  710. text = module.get.choiceText($choice),
  711. value = module.get.choiceValue($choice, text),
  712. callback = function() {
  713. module.remove.searchTerm();
  714. module.determine.selectAction(text, value);
  715. },
  716. hasSubMenu = ($subMenu.length > 0),
  717. isBubbledEvent = ($subMenu.find($target).length > 0)
  718. ;
  719. if(!isBubbledEvent && (!hasSubMenu || settings.allowCategorySelection)) {
  720. callback();
  721. }
  722. }
  723. },
  724. resetStyle: function() {
  725. $(this).removeAttr('style');
  726. }
  727. },
  728. determine: {
  729. selectAction: function(text, value) {
  730. module.verbose('Determining action', settings.action);
  731. if( $.isFunction( module.action[settings.action] ) ) {
  732. module.verbose('Triggering preset action', settings.action, text, value);
  733. module.action[ settings.action ](text, value);
  734. }
  735. else if( $.isFunction(settings.action) ) {
  736. module.verbose('Triggering user action', settings.action, text, value);
  737. settings.action(text, value);
  738. }
  739. else {
  740. module.error(error.action, settings.action);
  741. }
  742. },
  743. eventInModule: function(event, callback) {
  744. callback = $.isFunction(callback)
  745. ? callback
  746. : function(){}
  747. ;
  748. if( $(event.target).closest($module).length === 0 ) {
  749. module.verbose('Triggering event', callback);
  750. callback();
  751. return true;
  752. }
  753. else {
  754. module.verbose('Event occurred in dropdown, canceling callback');
  755. return false;
  756. }
  757. },
  758. eventInMenu: function(event, callback) {
  759. callback = $.isFunction(callback)
  760. ? callback
  761. : function(){}
  762. ;
  763. if( $(event.target).closest($menu).length === 0 ) {
  764. module.verbose('Triggering event', callback);
  765. callback();
  766. return true;
  767. }
  768. else {
  769. module.verbose('Event occurred in dropdown menu, canceling callback');
  770. return false;
  771. }
  772. }
  773. },
  774. action: {
  775. nothing: function() {},
  776. activate: function(text, value) {
  777. value = (value !== undefined)
  778. ? value
  779. : text
  780. ;
  781. module.set.selected(value);
  782. module.hide(function() {
  783. module.remove.filteredItem();
  784. });
  785. },
  786. select: function(text, value) {
  787. value = (value !== undefined)
  788. ? value
  789. : text
  790. ;
  791. module.set.selected(value);
  792. module.hide(function() {
  793. module.remove.filteredItem();
  794. });
  795. },
  796. combo: function(text, value) {
  797. value = (value !== undefined)
  798. ? value
  799. : text
  800. ;
  801. module.set.selected(value);
  802. module.hide(function() {
  803. module.remove.filteredItem();
  804. });
  805. },
  806. hide: function() {
  807. module.hide(function() {
  808. module.remove.filteredItem();
  809. });
  810. }
  811. },
  812. get: {
  813. id: function() {
  814. return id;
  815. },
  816. text: function() {
  817. return $text.text();
  818. },
  819. uniqueArray: function(array) {
  820. return $.grep(array, function (value, index) {
  821. return $.inArray(value, array) === index;
  822. });
  823. },
  824. value: function() {
  825. return ($input.length > 0)
  826. ? $input.val()
  827. : $module.data(metadata.value)
  828. ;
  829. },
  830. values: function() {
  831. var
  832. value = module.get.value()
  833. ;
  834. return $.isArray(value)
  835. ? value
  836. : [value]
  837. ;
  838. },
  839. choiceText: function($choice, preserveHTML) {
  840. preserveHTML = (preserveHTML !== undefined)
  841. ? preserveHTML
  842. : settings.preserveHTML
  843. ;
  844. if($choice !== undefined) {
  845. if($choice.find(selector.menu).length > 0) {
  846. module.verbose('Retreiving text of element with sub-menu');
  847. $choice = $choice.clone();
  848. $choice.find(selector.menu).remove();
  849. $choice.find(selector.menuIcon).remove();
  850. }
  851. return ($choice.data(metadata.text) !== undefined)
  852. ? $choice.data(metadata.text)
  853. : (preserveHTML)
  854. ? $choice.html().trim()
  855. : $choice.text().trim()
  856. ;
  857. }
  858. },
  859. choiceValue: function($choice, choiceText) {
  860. choiceText = choiceText || module.get.choiceText($choice);
  861. return ($choice.data(metadata.value) !== undefined)
  862. ? $choice.data(metadata.value)
  863. : (typeof choiceText === 'string')
  864. ? choiceText.toLowerCase().trim()
  865. : choiceText.trim()
  866. ;
  867. },
  868. inputEvent: function() {
  869. var
  870. input = $search[0]
  871. ;
  872. if(input) {
  873. return (input.oninput !== undefined)
  874. ? 'input'
  875. : (input.onpropertychange !== undefined)
  876. ? 'propertychange'
  877. : 'keyup'
  878. ;
  879. }
  880. return false;
  881. },
  882. selectValues: function() {
  883. var
  884. select = {}
  885. ;
  886. select.values = (settings.sortSelect)
  887. ? {} // properties will be sorted in object when re-accessed
  888. : [] // properties will keep original order in array
  889. ;
  890. $module
  891. .find('option')
  892. .each(function() {
  893. var
  894. name = $(this).html(),
  895. value = ( $(this).attr('value') !== undefined )
  896. ? $(this).attr('value')
  897. : name
  898. ;
  899. if(value === '') {
  900. select.placeholder = name;
  901. }
  902. else {
  903. if(settings.sortSelect) {
  904. select.values[value] = {
  905. name : name,
  906. value : value
  907. };
  908. }
  909. else {
  910. select.values.push({
  911. name: name,
  912. value: value
  913. });
  914. }
  915. }
  916. })
  917. ;
  918. if(settings.sortSelect) {
  919. module.debug('Retrieved and sorted values from select', select);
  920. }
  921. else {
  922. module.debug('Retreived values from select', select);
  923. }
  924. return select;
  925. },
  926. activeItem: function() {
  927. return $item.filter('.' + className.active);
  928. },
  929. item: function(value, strict) {
  930. var
  931. isMultiple = module.is.multiple(),
  932. $selectedItem = false
  933. ;
  934. value = (value !== undefined)
  935. ? value
  936. : ( module.get.value() !== undefined)
  937. ? module.get.value()
  938. : module.get.text()
  939. ;
  940. strict = (value === '' || value === 0)
  941. ? true
  942. : strict || false
  943. ;
  944. if(value !== undefined) {
  945. $item
  946. .each(function() {
  947. var
  948. $choice = $(this),
  949. optionText = module.get.choiceText($choice),
  950. optionValue = module.get.choiceValue($choice, optionText)
  951. ;
  952. if(strict) {
  953. module.verbose('Ambiguous dropdown value using strict type check', $choice, value);
  954. if( optionValue === value ) {
  955. $selectedItem = $(this);
  956. return true;
  957. }
  958. else if( !$selectedItem && optionText === value ) {
  959. $selectedItem = $(this);
  960. return true;
  961. }
  962. }
  963. else {
  964. if( optionValue == value ) {
  965. module.verbose('Found select item by value', optionValue, value);
  966. $selectedItem = $(this);
  967. return true;
  968. }
  969. else if( !$selectedItem && optionText == value ) {
  970. module.verbose('Found select item by text', optionText, value);
  971. $selectedItem = $(this);
  972. return true;
  973. }
  974. }
  975. })
  976. ;
  977. }
  978. else {
  979. value = module.get.text();
  980. }
  981. return $selectedItem || false;
  982. }
  983. },
  984. restore: {
  985. defaults: function() {
  986. module.restore.defaultText();
  987. module.restore.defaultValue();
  988. },
  989. defaultText: function() {
  990. var
  991. defaultText = $module.data(metadata.defaultText)
  992. ;
  993. module.debug('Restoring default text', defaultText);
  994. module.set.text(defaultText);
  995. $text.addClass(className.placeholder);
  996. },
  997. defaultValue: function() {
  998. var
  999. defaultValue = $module.data(metadata.defaultValue)
  1000. ;
  1001. if(defaultValue !== undefined) {
  1002. module.debug('Restoring default value', defaultValue);
  1003. if(defaultValue.length) {
  1004. module.set.selected(defaultValue);
  1005. }
  1006. else {
  1007. module.remove.activeItem();
  1008. module.remove.selectedItem();
  1009. }
  1010. }
  1011. }
  1012. },
  1013. save: {
  1014. defaults: function() {
  1015. module.save.defaultText();
  1016. module.save.placeholderText();
  1017. module.save.defaultValue();
  1018. },
  1019. defaultValue: function() {
  1020. $module.data(metadata.defaultValue, module.get.value());
  1021. },
  1022. defaultText: function() {
  1023. $module.data(metadata.defaultText, $text.text() );
  1024. },
  1025. placeholderText: function() {
  1026. if($text.hasClass(className.placeholder)) {
  1027. $module.data(metadata.placeholderText, $text.text());
  1028. }
  1029. }
  1030. },
  1031. clear: function() {
  1032. var
  1033. placeholderText = $module.data(metadata.placeholderText)
  1034. ;
  1035. module.set.text(placeholderText);
  1036. module.clearValue();
  1037. module.remove.activeItem();
  1038. module.remove.selectedItem();
  1039. $text.addClass(className.placeholder);
  1040. },
  1041. clearValue: function() {
  1042. module.set.value('');
  1043. },
  1044. set: {
  1045. filtered: function() {
  1046. var
  1047. searchValue = $search.val(),
  1048. hasSearchValue = (typeof searchValue === 'string' && searchValue.length > 0)
  1049. ;
  1050. if(hasSearchValue) {
  1051. $text.addClass(className.filtered);
  1052. }
  1053. else {
  1054. $text.removeClass(className.filtered);
  1055. }
  1056. },
  1057. tabbable: function() {
  1058. if( module.has.search() ) {
  1059. module.debug('Searchable dropdown initialized');
  1060. $search
  1061. .val('')
  1062. .attr('tabindex', 0)
  1063. ;
  1064. $menu
  1065. .attr('tabindex', '-1')
  1066. ;
  1067. }
  1068. else {
  1069. module.debug('Simple selection dropdown initialized');
  1070. if(!$module.attr('tabindex') ) {
  1071. $module
  1072. .attr('tabindex', 0)
  1073. ;
  1074. $menu
  1075. .attr('tabindex', '-1')
  1076. ;
  1077. }
  1078. }
  1079. },
  1080. scrollPosition: function($item, forceScroll) {
  1081. var
  1082. edgeTolerance = 5,
  1083. hasActive,
  1084. offset,
  1085. itemHeight,
  1086. itemOffset,
  1087. menuOffset,
  1088. menuScroll,
  1089. menuHeight,
  1090. abovePage,
  1091. belowPage
  1092. ;
  1093. $item = $item || module.get.activeItem();
  1094. hasActive = ($item && $item.length > 0);
  1095. forceScroll = (forceScroll !== undefined)
  1096. ? forceScroll
  1097. : false
  1098. ;
  1099. if($item && hasActive) {
  1100. if(!$menu.hasClass(className.visible)) {
  1101. $menu.addClass(className.loading);
  1102. }
  1103. menuHeight = $menu.height();
  1104. itemHeight = $item.height();
  1105. menuScroll = $menu.scrollTop();
  1106. menuOffset = $menu.offset().top;
  1107. itemOffset = $item.offset().top;
  1108. offset = menuScroll - menuOffset + itemOffset;
  1109. belowPage = menuScroll + menuHeight < (offset + edgeTolerance);
  1110. abovePage = ((offset - edgeTolerance) < menuScroll);
  1111. module.debug('Scrolling to active item', offset);
  1112. if(abovePage || belowPage || forceScroll) {
  1113. $menu
  1114. .scrollTop(offset)
  1115. .removeClass(className.loading)
  1116. ;
  1117. }
  1118. }
  1119. },
  1120. text: function(text) {
  1121. if(settings.action == 'combo') {
  1122. module.debug('Changing combo button text', text, $combo);
  1123. if(settings.preserveHTML) {
  1124. $combo.html(text);
  1125. }
  1126. else {
  1127. $combo.text(text);
  1128. }
  1129. }
  1130. else if(settings.action !== 'select') {
  1131. module.debug('Changing text', text, $text);
  1132. $text
  1133. .removeClass(className.filtered)
  1134. .removeClass(className.placeholder)
  1135. ;
  1136. if(settings.preserveHTML) {
  1137. $text.html(text);
  1138. }
  1139. else {
  1140. $text.text(text);
  1141. }
  1142. }
  1143. },
  1144. value: function(value) {
  1145. module.debug('Adding selected value to hidden input', value, $input);
  1146. if($input.length > 0) {
  1147. if( module.is.multiple() ) {
  1148. var
  1149. values = module.get.values()
  1150. ;
  1151. if($.isArray(values)) {
  1152. values.push(value);
  1153. values = module.get.uniqueArray(values);
  1154. }
  1155. else {
  1156. values = [value];
  1157. }
  1158. module.debug('Adding value to multiple', value, values);
  1159. module.set.values(values);
  1160. }
  1161. else {
  1162. module.debug('Updating input value', value);
  1163. $input
  1164. .val(value)
  1165. .trigger('change')
  1166. ;
  1167. }
  1168. }
  1169. else {
  1170. $module.data(metadata.value, value);
  1171. }
  1172. },
  1173. values: function(values) {
  1174. if( $input.is('select') ) {
  1175. $input.val(values);
  1176. module.debug('Setting mutiple select values', values, $input);
  1177. }
  1178. else {
  1179. values = values.join(',');
  1180. $input.val(values);
  1181. module.debug('Setting hidden input to comma separatd values', values, $input);
  1182. }
  1183. },
  1184. active: function() {
  1185. $module
  1186. .addClass(className.active)
  1187. ;
  1188. },
  1189. multiple: function() {
  1190. $module.addClass(className.multiple);
  1191. },
  1192. visible: function() {
  1193. $module.addClass(className.visible);
  1194. },
  1195. selected: function(value) {
  1196. var
  1197. $selectedItem = module.get.item(value),
  1198. selectedText,
  1199. selectedValue
  1200. ;
  1201. if($selectedItem && !$selectedItem.hasClass(className.active) ) {
  1202. module.debug('Setting selected menu item to', $selectedItem);
  1203. module.remove.activeItem();
  1204. module.remove.selectedItem();
  1205. $selectedItem
  1206. .addClass(className.active)
  1207. .addClass(className.selected)
  1208. ;
  1209. selectedText = module.get.choiceText($selectedItem);
  1210. selectedValue = module.get.choiceValue($selectedItem, selectedText);
  1211. module.set.text(selectedText);
  1212. module.set.value(selectedValue);
  1213. settings.onChange.call(element, value, selectedText, $selectedItem);
  1214. }
  1215. }
  1216. },
  1217. remove: {
  1218. active: function() {
  1219. $module.removeClass(className.active);
  1220. },
  1221. visible: function() {
  1222. $module.removeClass(className.visible);
  1223. },
  1224. activeItem: function() {
  1225. $item.removeClass(className.active);
  1226. },
  1227. filteredItem: function() {
  1228. $item.removeClass(className.filtered);
  1229. },
  1230. searchTerm: function() {
  1231. $search.val('');
  1232. },
  1233. selectedItem: function() {
  1234. $item.removeClass(className.selected);
  1235. },
  1236. tabbable: function() {
  1237. if( module.has.search() ) {
  1238. module.debug('Searchable dropdown initialized');
  1239. $search
  1240. .attr('tabindex', '-1')
  1241. ;
  1242. $menu
  1243. .attr('tabindex', '-1')
  1244. ;
  1245. }
  1246. else {
  1247. module.debug('Simple selection dropdown initialized');
  1248. $module
  1249. .attr('tabindex', '-1')
  1250. ;
  1251. $menu
  1252. .attr('tabindex', '-1')
  1253. ;
  1254. }
  1255. }
  1256. },
  1257. has: {
  1258. labels: function() {
  1259. return ($labels.length > 0);
  1260. },
  1261. search: function() {
  1262. return ($search.length > 0);
  1263. },
  1264. input: function() {
  1265. return ($input.length > 0);
  1266. },
  1267. menu: function() {
  1268. return ($menu.length > 0);
  1269. }
  1270. },
  1271. is: {
  1272. active: function() {
  1273. return $module.hasClass(className.active);
  1274. },
  1275. alreadySetup: function() {
  1276. return ($module.is('select') && $module.parent(selector.dropdown).length > 0);
  1277. },
  1278. animating: function($subMenu) {
  1279. return ($subMenu)
  1280. ? $subMenu.is(':animated') || $subMenu.transition && $subMenu.transition('is animating')
  1281. : $menu.is(':animated') || $menu.transition && $menu.transition('is animating')
  1282. ;
  1283. },
  1284. allFiltered: function() {
  1285. return ($item.filter('.' + className.filtered).length === $item.length);
  1286. },
  1287. hidden: function($subMenu) {
  1288. return ($subMenu)
  1289. ? $subMenu.is(':hidden')
  1290. : $menu.is(':hidden')
  1291. ;
  1292. },
  1293. multiple: function() {
  1294. return $module.hasClass(className.multiple);
  1295. },
  1296. selectMutation: function(mutations) {
  1297. var
  1298. selectChanged = false
  1299. ;
  1300. $.each(mutations, function(index, mutation) {
  1301. if(mutation.target && $(mutation.target).is('select')) {
  1302. selectChanged = true;
  1303. return true;
  1304. }
  1305. });
  1306. return selectChanged;
  1307. },
  1308. search: function() {
  1309. return $module.hasClass(className.search);
  1310. },
  1311. searchSelection: function() {
  1312. return ( module.has.search() && $search.closest(selector.menu).length == 0 );
  1313. },
  1314. selection: function() {
  1315. return $module.hasClass(className.selection);
  1316. },
  1317. upward: function() {
  1318. return $module.hasClass(className.upward);
  1319. },
  1320. visible: function($subMenu) {
  1321. return ($subMenu)
  1322. ? $subMenu.is(':visible')
  1323. : $menu.is(':visible')
  1324. ;
  1325. }
  1326. },
  1327. can: {
  1328. click: function() {
  1329. return (hasTouch || settings.on == 'click');
  1330. },
  1331. show: function() {
  1332. return !$module.hasClass(className.disabled);
  1333. }
  1334. },
  1335. animate: {
  1336. show: function(callback, $subMenu) {
  1337. var
  1338. $currentMenu = $subMenu || $menu,
  1339. start = ($subMenu)
  1340. ? function() {}
  1341. : function() {
  1342. module.hideSubMenus();
  1343. module.hideOthers();
  1344. module.set.active();
  1345. }
  1346. ;
  1347. callback = $.isFunction(callback)
  1348. ? callback
  1349. : function(){}
  1350. ;
  1351. module.set.scrollPosition(module.get.activeItem(), true);
  1352. module.verbose('Doing menu show animation', $currentMenu);
  1353. if( module.is.hidden($currentMenu) || module.is.animating($currentMenu) ) {
  1354. if(settings.transition == 'auto') {
  1355. settings.transition = module.is.upward()
  1356. ? 'slide up'
  1357. : 'slide down'
  1358. ;
  1359. module.verbose('Automatically determining animation based on animation direction', settings.transition);
  1360. }
  1361. if(settings.transition == 'none') {
  1362. callback.call(element);
  1363. }
  1364. else if($.fn.transition !== undefined && $module.transition('is supported')) {
  1365. $currentMenu
  1366. .transition({
  1367. animation : settings.transition + ' in',
  1368. debug : settings.debug,
  1369. verbose : settings.verbose,
  1370. duration : settings.duration,
  1371. queue : true,
  1372. onStart : start,
  1373. onComplete : function() {
  1374. callback.call(element);
  1375. }
  1376. })
  1377. ;
  1378. }
  1379. else if(settings.transition == 'slide down') {
  1380. start();
  1381. $currentMenu
  1382. .hide()
  1383. .clearQueue()
  1384. .children()
  1385. .clearQueue()
  1386. .css('opacity', 0)
  1387. .delay(50)
  1388. .animate({
  1389. opacity : 1
  1390. }, settings.duration, 'easeOutQuad', module.event.resetStyle)
  1391. .end()
  1392. .slideDown(100, 'easeOutQuad', function() {
  1393. module.event.resetStyle.call(this);
  1394. callback.call(element);
  1395. })
  1396. ;
  1397. }
  1398. else if(settings.transition == 'fade') {
  1399. start();
  1400. $currentMenu
  1401. .hide()
  1402. .clearQueue()
  1403. .fadeIn(settings.duration, function() {
  1404. module.event.resetStyle.call(this);
  1405. callback.call(element);
  1406. })
  1407. ;
  1408. }
  1409. else {
  1410. module.error(error.transition, settings.transition);
  1411. }
  1412. }
  1413. },
  1414. hide: function(callback, $subMenu) {
  1415. var
  1416. $currentMenu = $subMenu || $menu,
  1417. duration = ($subMenu)
  1418. ? (settings.duration * 0.9)
  1419. : settings.duration,
  1420. start = ($subMenu)
  1421. ? function() {}
  1422. : function() {
  1423. if( module.can.click() ) {
  1424. module.unbind.intent();
  1425. }
  1426. module.focusSearch();
  1427. module.remove.active();
  1428. }
  1429. ;
  1430. callback = $.isFunction(callback)
  1431. ? callback
  1432. : function(){}
  1433. ;
  1434. if( module.is.visible($currentMenu) || module.is.animating($currentMenu) ) {
  1435. module.verbose('Doing menu hide animation', $currentMenu);
  1436. if(settings.transition == 'auto') {
  1437. settings.transition = module.is.upward()
  1438. ? 'slide up'
  1439. : 'slide down'
  1440. ;
  1441. }
  1442. $input.trigger('blur');
  1443. if(settings.transition == 'none') {
  1444. callback.call(element);
  1445. }
  1446. else if($.fn.transition !== undefined && $module.transition('is supported')) {
  1447. $currentMenu
  1448. .transition({
  1449. animation : settings.transition + ' out',
  1450. duration : settings.duration,
  1451. debug : settings.debug,
  1452. verbose : settings.verbose,
  1453. queue : true,
  1454. onStart : start,
  1455. onComplete : function() {
  1456. callback.call(element);
  1457. }
  1458. })
  1459. ;
  1460. }
  1461. else if(settings.transition == 'slide down') {
  1462. start();
  1463. $currentMenu
  1464. .show()
  1465. .clearQueue()
  1466. .children()
  1467. .clearQueue()
  1468. .css('opacity', 1)
  1469. .animate({
  1470. opacity : 0
  1471. }, 100, 'easeOutQuad', module.event.resetStyle)
  1472. .end()
  1473. .delay(50)
  1474. .slideUp(100, 'easeOutQuad', function() {
  1475. module.event.resetStyle.call(this);
  1476. callback.call(element);
  1477. })
  1478. ;
  1479. }
  1480. else if(settings.transition == 'fade') {
  1481. start();
  1482. $currentMenu
  1483. .show()
  1484. .clearQueue()
  1485. .fadeOut(150, function() {
  1486. module.event.resetStyle.call(this);
  1487. callback.call(element);
  1488. })
  1489. ;
  1490. }
  1491. else {
  1492. module.error(error.transition);
  1493. }
  1494. }
  1495. }
  1496. },
  1497. delay: {
  1498. show: function() {
  1499. module.verbose('Delaying show event to ensure user intent');
  1500. clearTimeout(module.timer);
  1501. module.timer = setTimeout(module.show, settings.delay.show);
  1502. },
  1503. hide: function() {
  1504. module.verbose('Delaying hide event to ensure user intent');
  1505. clearTimeout(module.timer);
  1506. module.timer = setTimeout(module.hide, settings.delay.hide);
  1507. }
  1508. },
  1509. escape: {
  1510. regExp: function(text) {
  1511. text = String(text);
  1512. return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
  1513. }
  1514. },
  1515. setting: function(name, value) {
  1516. module.debug('Changing setting', name, value);
  1517. if( $.isPlainObject(name) ) {
  1518. $.extend(true, settings, name);
  1519. }
  1520. else if(value !== undefined) {
  1521. settings[name] = value;
  1522. }
  1523. else {
  1524. return settings[name];
  1525. }
  1526. },
  1527. internal: function(name, value) {
  1528. if( $.isPlainObject(name) ) {
  1529. $.extend(true, module, name);
  1530. }
  1531. else if(value !== undefined) {
  1532. module[name] = value;
  1533. }
  1534. else {
  1535. return module[name];
  1536. }
  1537. },
  1538. debug: function() {
  1539. if(settings.debug) {
  1540. if(settings.performance) {
  1541. module.performance.log(arguments);
  1542. }
  1543. else {
  1544. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  1545. module.debug.apply(console, arguments);
  1546. }
  1547. }
  1548. },
  1549. verbose: function() {
  1550. if(settings.verbose && settings.debug) {
  1551. if(settings.performance) {
  1552. module.performance.log(arguments);
  1553. }
  1554. else {
  1555. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  1556. module.verbose.apply(console, arguments);
  1557. }
  1558. }
  1559. },
  1560. error: function() {
  1561. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  1562. module.error.apply(console, arguments);
  1563. },
  1564. performance: {
  1565. log: function(message) {
  1566. var
  1567. currentTime,
  1568. executionTime,
  1569. previousTime
  1570. ;
  1571. if(settings.performance) {
  1572. currentTime = new Date().getTime();
  1573. previousTime = time || currentTime;
  1574. executionTime = currentTime - previousTime;
  1575. time = currentTime;
  1576. performance.push({
  1577. 'Name' : message[0],
  1578. 'Arguments' : [].slice.call(message, 1) || '',
  1579. 'Element' : element,
  1580. 'Execution Time' : executionTime
  1581. });
  1582. }
  1583. clearTimeout(module.performance.timer);
  1584. module.performance.timer = setTimeout(module.performance.display, 500);
  1585. },
  1586. display: function() {
  1587. var
  1588. title = settings.name + ':',
  1589. totalTime = 0
  1590. ;
  1591. time = false;
  1592. clearTimeout(module.performance.timer);
  1593. $.each(performance, function(index, data) {
  1594. totalTime += data['Execution Time'];
  1595. });
  1596. title += ' ' + totalTime + 'ms';
  1597. if(moduleSelector) {
  1598. title += ' \'' + moduleSelector + '\'';
  1599. }
  1600. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  1601. console.groupCollapsed(title);
  1602. if(console.table) {
  1603. console.table(performance);
  1604. }
  1605. else {
  1606. $.each(performance, function(index, data) {
  1607. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  1608. });
  1609. }
  1610. console.groupEnd();
  1611. }
  1612. performance = [];
  1613. }
  1614. },
  1615. invoke: function(query, passedArguments, context) {
  1616. var
  1617. object = instance,
  1618. maxDepth,
  1619. found,
  1620. response
  1621. ;
  1622. passedArguments = passedArguments || queryArguments;
  1623. context = element || context;
  1624. if(typeof query == 'string' && object !== undefined) {
  1625. query = query.split(/[\. ]/);
  1626. maxDepth = query.length - 1;
  1627. $.each(query, function(depth, value) {
  1628. var camelCaseValue = (depth != maxDepth)
  1629. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  1630. : query
  1631. ;
  1632. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  1633. object = object[camelCaseValue];
  1634. }
  1635. else if( object[camelCaseValue] !== undefined ) {
  1636. found = object[camelCaseValue];
  1637. return false;
  1638. }
  1639. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  1640. object = object[value];
  1641. }
  1642. else if( object[value] !== undefined ) {
  1643. found = object[value];
  1644. return false;
  1645. }
  1646. else {
  1647. module.error(error.method, query);
  1648. return false;
  1649. }
  1650. });
  1651. }
  1652. if ( $.isFunction( found ) ) {
  1653. response = found.apply(context, passedArguments);
  1654. }
  1655. else if(found !== undefined) {
  1656. response = found;
  1657. }
  1658. if($.isArray(returnedValue)) {
  1659. returnedValue.push(response);
  1660. }
  1661. else if(returnedValue !== undefined) {
  1662. returnedValue = [returnedValue, response];
  1663. }
  1664. else if(response !== undefined) {
  1665. returnedValue = response;
  1666. }
  1667. return found;
  1668. }
  1669. };
  1670. if(methodInvoked) {
  1671. if(instance === undefined) {
  1672. module.initialize();
  1673. }
  1674. module.invoke(query);
  1675. }
  1676. else {
  1677. if(instance !== undefined) {
  1678. instance.invoke('destroy');
  1679. }
  1680. module.initialize();
  1681. }
  1682. })
  1683. ;
  1684. return (returnedValue !== undefined)
  1685. ? returnedValue
  1686. : $allModules
  1687. ;
  1688. };
  1689. $.fn.dropdown.settings = {
  1690. debug : false,
  1691. verbose : true,
  1692. performance : true,
  1693. on : 'click',
  1694. action : 'activate',
  1695. allowTab : true,
  1696. fullTextSearch : false,
  1697. preserveHTML : true,
  1698. sortSelect : false,
  1699. allowCategorySelection : false,
  1700. delay : {
  1701. hide : 300,
  1702. show : 200,
  1703. search : 50,
  1704. touch : 50
  1705. },
  1706. forceSelection: true,
  1707. transition : 'auto',
  1708. duration : 250,
  1709. /* Callbacks */
  1710. onNoResults : function(searchTerm){},
  1711. onChange : function(value, text){},
  1712. onShow : function(){},
  1713. onHide : function(){},
  1714. /* Component */
  1715. name : 'Dropdown',
  1716. namespace : 'dropdown',
  1717. error : {
  1718. action : 'You called a dropdown action that was not defined',
  1719. alreadySetup : 'Once a select has been initialized behaviors must be called on the created ui dropdown',
  1720. method : 'The method you called is not defined.',
  1721. transition : 'The requested transition was not found'
  1722. },
  1723. metadata: {
  1724. defaultText : 'defaultText',
  1725. defaultValue : 'defaultValue',
  1726. placeholderText : 'placeholderText',
  1727. text : 'text',
  1728. value : 'value'
  1729. },
  1730. selector : {
  1731. dropdown : '.ui.dropdown',
  1732. input : '> input[type="hidden"], > select',
  1733. item : '.item',
  1734. labels : '> .labels',
  1735. menu : '.menu',
  1736. menuIcon : '.dropdown.icon',
  1737. search : 'input.search, .menu > .search > input',
  1738. text : '> .text:not(.icon)'
  1739. },
  1740. className : {
  1741. active : 'active',
  1742. animating : 'animating',
  1743. disabled : 'disabled',
  1744. dropdown : 'ui dropdown',
  1745. filtered : 'filtered',
  1746. labels : 'labels',
  1747. loading : 'loading',
  1748. menu : 'menu',
  1749. multiple : 'multiple',
  1750. placeholder : 'default',
  1751. search : 'search',
  1752. selected : 'selected',
  1753. selection : 'selection',
  1754. upward : 'upward',
  1755. visible : 'visible'
  1756. }
  1757. };
  1758. /* Templates */
  1759. $.fn.dropdown.settings.templates = {
  1760. menu: function(select) {
  1761. var
  1762. placeholder = select.placeholder || false,
  1763. values = select.values || {},
  1764. html = ''
  1765. ;
  1766. $.each(select.values, function(index, option) {
  1767. html += '<div class="item" data-value="' + option.value + '">' + option.name + '</div>';
  1768. });
  1769. return html;
  1770. },
  1771. dropdown: function(select) {
  1772. var
  1773. placeholder = select.placeholder || false,
  1774. values = select.values || {},
  1775. html = ''
  1776. ;
  1777. html += '<i class="dropdown icon"></i>';
  1778. if(select.placeholder) {
  1779. html += '<div class="default text">' + placeholder + '</div>';
  1780. }
  1781. else {
  1782. html += '<div class="text"></div>';
  1783. }
  1784. html += '<div class="menu">';
  1785. $.each(select.values, function(index, option) {
  1786. html += '<div class="item" data-value="' + option.value + '">' + option.name + '</div>';
  1787. });
  1788. html += '</div>';
  1789. return html;
  1790. }
  1791. };
  1792. /* Dependencies */
  1793. $.extend( $.easing, {
  1794. easeOutQuad: function (x, t, b, c, d) {
  1795. return -c *(t/=d)*(t-2) + b;
  1796. },
  1797. });
  1798. })( jQuery, window , document );