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.

2488 lines
84 KiB

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