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.

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