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.

1396 lines
44 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /*
  2. * # Semantic - Dropdown
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Copyright 2014 Contributor
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ( $, window, document, undefined ) {
  12. "use strict";
  13. $.fn.dropdown = function(parameters) {
  14. var
  15. $allModules = $(this),
  16. $document = $(document),
  17. moduleSelector = $allModules.selector || '',
  18. hasTouch = ('ontouchstart' in document.documentElement),
  19. time = new Date().getTime(),
  20. performance = [],
  21. query = arguments[0],
  22. methodInvoked = (typeof query == 'string'),
  23. queryArguments = [].slice.call(arguments, 1),
  24. returnedValue
  25. ;
  26. $allModules
  27. .each(function() {
  28. var
  29. settings = ( $.isPlainObject(parameters) )
  30. ? $.extend(true, {}, $.fn.dropdown.settings, parameters)
  31. : $.extend({}, $.fn.dropdown.settings),
  32. className = settings.className,
  33. metadata = settings.metadata,
  34. namespace = settings.namespace,
  35. selector = settings.selector,
  36. error = settings.error,
  37. eventNamespace = '.' + namespace,
  38. moduleNamespace = 'module-' + namespace,
  39. $module = $(this),
  40. $text = $module.find(selector.text),
  41. $search = $module.find(selector.search),
  42. $input = $module.find(selector.input),
  43. $combo = ($module.prev().find(selector.text).size() > 0)
  44. ? $module.prev().find(selector.text)
  45. : $module.prev(),
  46. $menu = $module.children(selector.menu),
  47. $item = $menu.find(selector.item),
  48. activated = false,
  49. selectionCache = false,
  50. element = this,
  51. instance = $module.data(moduleNamespace),
  52. observer,
  53. module
  54. ;
  55. module = {
  56. initialize: function() {
  57. module.debug('Initializing dropdown', settings);
  58. module.setup.layout();
  59. module.save.defaults();
  60. module.set.selected();
  61. if(hasTouch) {
  62. module.bind.touchEvents();
  63. }
  64. module.observeChanges();
  65. module.bind.mouseEvents();
  66. module.bind.keyboardEvents();
  67. module.instantiate();
  68. },
  69. instantiate: function() {
  70. module.verbose('Storing instance of dropdown', module);
  71. instance = module;
  72. $module
  73. .data(moduleNamespace, module)
  74. ;
  75. },
  76. destroy: function() {
  77. module.verbose('Destroying previous dropdown for', $module);
  78. $module
  79. .off(eventNamespace)
  80. .removeData(moduleNamespace)
  81. ;
  82. },
  83. observeChanges: function() {
  84. if(MutationObserver !== undefined) {
  85. observer = new MutationObserver(function(mutations) {
  86. module.debug('DOM tree modified, updating selector cache');
  87. module.refresh();
  88. });
  89. observer.observe(element, {
  90. childList : true,
  91. subtree : true
  92. });
  93. module.debug('Setting up mutation observer', observer);
  94. }
  95. },
  96. setup: {
  97. layout: function() {
  98. if( $module.is('select') ) {
  99. module.setup.select();
  100. }
  101. if( module.is.search() && !module.is.searchable() ) {
  102. $search = $('<input />')
  103. .addClass(className.search)
  104. .insertBefore($text)
  105. ;
  106. }
  107. if(settings.allowTab) {
  108. if( module.is.searchable() ) {
  109. module.debug('Searchable dropdown initialized');
  110. $search
  111. .val('')
  112. .attr('tabindex', 0)
  113. ;
  114. $menu
  115. .attr('tabindex', '-1')
  116. ;
  117. }
  118. else {
  119. module.debug('Simple selection dropdown initialized');
  120. if(!$module.attr('tabindex') ) {
  121. $module
  122. .attr('tabindex', 0)
  123. ;
  124. $menu
  125. .attr('tabindex', '-1')
  126. ;
  127. }
  128. }
  129. }
  130. },
  131. select: function() {
  132. var
  133. selectValues = module.get.selectValues()
  134. ;
  135. module.debug('Dropdown initialized on a select', selectValues);
  136. // see if select exists inside a dropdown
  137. $input = $module;
  138. if($input.parents(selector.dropdown).size() > 0) {
  139. module.debug('Creating dropdown menu only from template');
  140. $module = $input.closest(selector.dropdown);
  141. if($module.find('.' + className.dropdown).size() === 0) {
  142. $('<div />')
  143. .addClass(className.menu)
  144. .html( settings.templates.menu( selectValues ))
  145. .appendTo($module)
  146. ;
  147. }
  148. }
  149. else {
  150. module.debug('Creating entire dropdown from template');
  151. $module = $('<div />')
  152. .attr('class', $input.attr('class') )
  153. .addClass(className.selection)
  154. .addClass(className.dropdown)
  155. .html( settings.templates.dropdown(selectValues) )
  156. .insertBefore($input)
  157. ;
  158. $input
  159. .removeAttr('class')
  160. .prependTo($module)
  161. ;
  162. }
  163. module.refresh();
  164. }
  165. },
  166. refresh: function() {
  167. $text = $module.find(selector.text);
  168. $search = $module.find(selector.search);
  169. $input = $module.find(selector.input);
  170. $menu = $module.children(selector.menu);
  171. $item = $menu.find(selector.item);
  172. },
  173. bind: {
  174. keyboardEvents: function() {
  175. module.debug('Binding keyboard events');
  176. $module
  177. .on('keydown' + eventNamespace, module.event.keydown)
  178. ;
  179. if( module.is.searchable() ) {
  180. $module
  181. .on(module.get.inputEvent(), selector.search, module.event.input)
  182. ;
  183. }
  184. if( module.is.searchSelection() ) {
  185. $module
  186. .on('focus' + eventNamespace, selector.search, module.event.searchFocus)
  187. .on('blur' + eventNamespace, selector.search, module.event.blur)
  188. ;
  189. }
  190. else {
  191. $module
  192. .on('mousedown', module.event.mousedown)
  193. .on('mouseup', module.event.mouseup)
  194. .on('focus' + eventNamespace, module.event.focus)
  195. .on('blur' + eventNamespace, module.event.blur)
  196. ;
  197. }
  198. },
  199. touchEvents: function() {
  200. module.debug('Touch device detected binding touch events');
  201. if( !module.is.searchSelection() ) {
  202. $module
  203. .on('touchstart' + eventNamespace, module.event.test.toggle)
  204. ;
  205. }
  206. $module
  207. .on('touchstart' + eventNamespace, selector.item, module.event.item.mouseenter)
  208. .on('touchstart' + eventNamespace, selector.item, module.event.item.click)
  209. ;
  210. },
  211. mouseEvents: function() {
  212. module.verbose('Mouse detected binding mouse events');
  213. if( !module.is.searchSelection() ) {
  214. if(settings.on == 'click') {
  215. $module
  216. .on('click' + eventNamespace, module.event.test.toggle)
  217. ;
  218. }
  219. else if(settings.on == 'hover') {
  220. $module
  221. .on('mouseenter' + eventNamespace, module.delay.show)
  222. .on('mouseleave' + eventNamespace, module.delay.hide)
  223. ;
  224. }
  225. else {
  226. $module
  227. .on(settings.on + eventNamespace, module.toggle)
  228. ;
  229. }
  230. }
  231. $module
  232. .on('mouseenter' + eventNamespace, selector.item, module.event.item.mouseenter)
  233. .on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave)
  234. .on('click' + eventNamespace, selector.item, module.event.item.click)
  235. ;
  236. },
  237. intent: function() {
  238. module.verbose('Binding hide intent event to document');
  239. if(hasTouch) {
  240. $document
  241. .on('touchstart' + eventNamespace, module.event.test.touch)
  242. .on('touchmove' + eventNamespace, module.event.test.touch)
  243. ;
  244. }
  245. $document
  246. .on('click' + eventNamespace, module.event.test.hide)
  247. ;
  248. }
  249. },
  250. unbind: {
  251. intent: function() {
  252. module.verbose('Removing hide intent event from document');
  253. if(hasTouch) {
  254. $document
  255. .off('touchstart' + eventNamespace)
  256. .off('touchmove' + eventNamespace)
  257. ;
  258. }
  259. $document
  260. .off('click' + eventNamespace)
  261. ;
  262. }
  263. },
  264. filter: function(searchTerm) {
  265. var
  266. $results = $(),
  267. exactRegExp = new RegExp('(?:\s|^)' + searchTerm, 'i'),
  268. fullTextRegExp = new RegExp(searchTerm, 'i'),
  269. $filteredItems
  270. ;
  271. $item
  272. .each(function(){
  273. var
  274. $choice = $(this),
  275. text = ( $choice.data(metadata.text) !== undefined )
  276. ? $choice.data(metadata.text)
  277. : (settings.preserveHTML)
  278. ? $choice.html()
  279. : $choice.text(),
  280. value = ( $choice.data(metadata.value) !== undefined)
  281. ? $choice.data(metadata.value)
  282. : (typeof text === 'string')
  283. ? text.toLowerCase()
  284. : text
  285. ;
  286. if( exactRegExp.test( text ) || exactRegExp.test( value ) ) {
  287. $results = $results.add($choice);
  288. }
  289. else if(settings.fullTextSearch) {
  290. if( fullTextRegExp.test( text ) || fullTextRegExp.test( value ) ) {
  291. $results = $results.add($choice);
  292. }
  293. }
  294. })
  295. ;
  296. $filteredItems = $item.not($results);
  297. $item
  298. .removeClass(className.filtered)
  299. .removeClass(className.selected)
  300. ;
  301. $filteredItems
  302. .addClass(className.filtered)
  303. ;
  304. $item
  305. .not('.' + className.filtered)
  306. .eq(0)
  307. .addClass(className.selected)
  308. ;
  309. },
  310. event: {
  311. // prevents focus from occuring on mousedown
  312. mousedown: function() {
  313. activated = true;
  314. },
  315. mouseup: function() {
  316. activated = false;
  317. },
  318. focus: function() {
  319. if(!activated) {
  320. module.show();
  321. }
  322. },
  323. searchFocus: function() {
  324. activated = true;
  325. module.show();
  326. },
  327. blur: function(event) {
  328. if(!activated) {
  329. module.hide();
  330. }
  331. },
  332. input: function(event) {
  333. var
  334. query = $search.val()
  335. ;
  336. $text.addClass(className.filtered);
  337. module.filter(query);
  338. },
  339. keydown: function(event) {
  340. var
  341. $selectedItem = $item.not(className.filtered).filter('.' + className.selected).eq(0),
  342. $visibleItems = $item.not('.' + className.filtered),
  343. pressedKey = event.which,
  344. keys = {
  345. enter : 13,
  346. escape : 27,
  347. upArrow : 38,
  348. downArrow : 40
  349. },
  350. selectedClass = className.selected,
  351. currentIndex = $visibleItems.index( $selectedItem ),
  352. hasSelectedItem = ($selectedItem.size() > 0),
  353. $nextItem,
  354. newIndex
  355. ;
  356. // default to activated choice if no selection present
  357. if(!hasSelectedItem) {
  358. $selectedItem = $item.filter('.' + className.active).eq(0);
  359. hasSelectedItem = ($selectedItem.size() > 0);
  360. }
  361. // close shortcuts
  362. if(pressedKey == keys.escape) {
  363. module.verbose('Escape key pressed, closing dropdown');
  364. $search.blur();
  365. module.hide();
  366. }
  367. // result shortcuts
  368. if(module.is.visible()) {
  369. if(pressedKey == keys.enter && hasSelectedItem) {
  370. module.verbose('Enter key pressed, choosing selected item');
  371. if(module.is.searchable()) {
  372. module.verbose('Removing focus from search input');
  373. $search.blur();
  374. }
  375. $.proxy(module.event.item.click, $selectedItem)(event);
  376. event.preventDefault();
  377. return false;
  378. }
  379. else if(pressedKey == keys.upArrow) {
  380. if(!hasSelectedItem) {
  381. $nextItem = $visibleItems.eq(0);
  382. }
  383. else {
  384. $nextItem = $selectedItem.prevAll(selector.item + ':not(.' + className.filtered + ')').eq(0);
  385. }
  386. if(currentIndex !== 0) {
  387. module.verbose('Up key pressed, changing active item');
  388. $item
  389. .removeClass(selectedClass)
  390. ;
  391. $nextItem
  392. .addClass(selectedClass)
  393. ;
  394. module.set.scrollPosition($nextItem);
  395. }
  396. event.preventDefault();
  397. }
  398. else if(pressedKey == keys.downArrow) {
  399. if(!hasSelectedItem) {
  400. $nextItem = $visibleItems.eq(0);
  401. }
  402. else {
  403. $nextItem = $selectedItem.nextAll(selector.item + ':not(.' + className.filtered + ')').eq(0);
  404. }
  405. if(currentIndex + 1 < $visibleItems.size() ) {
  406. module.verbose('Down key pressed, changing active item');
  407. $item
  408. .removeClass(selectedClass)
  409. ;
  410. $nextItem
  411. .addClass(selectedClass)
  412. ;
  413. module.set.scrollPosition($nextItem);
  414. }
  415. event.preventDefault();
  416. }
  417. }
  418. else {
  419. if(pressedKey == keys.enter) {
  420. module.show();
  421. }
  422. }
  423. },
  424. test: {
  425. toggle: function(event) {
  426. if( module.determine.eventInMenu(event, module.toggle) ) {
  427. event.preventDefault();
  428. }
  429. },
  430. touch: function(event) {
  431. module.determine.eventInMenu(event, function() {
  432. if(event.type == 'touchstart') {
  433. module.timer = setTimeout(module.hide, settings.delay.touch);
  434. }
  435. else if(event.type == 'touchmove') {
  436. clearTimeout(module.timer);
  437. }
  438. });
  439. event.stopPropagation();
  440. },
  441. hide: function(event) {
  442. module.determine.eventInModule(event, module.hide);
  443. }
  444. },
  445. item: {
  446. mouseenter: function(event) {
  447. var
  448. $currentMenu = $(this).find(selector.menu),
  449. $otherMenus = $(this).siblings(selector.item).children(selector.menu)
  450. ;
  451. if( $currentMenu.size() > 0 ) {
  452. clearTimeout(module.itemTimer);
  453. module.itemTimer = setTimeout(function() {
  454. module.animate.hide(false, $otherMenus);
  455. module.verbose('Showing sub-menu', $currentMenu);
  456. module.animate.show(false, $currentMenu);
  457. }, settings.delay.show * 2);
  458. event.preventDefault();
  459. }
  460. },
  461. mouseleave: function(event) {
  462. var
  463. $currentMenu = $(this).find(selector.menu)
  464. ;
  465. if($currentMenu.size() > 0) {
  466. clearTimeout(module.itemTimer);
  467. module.itemTimer = setTimeout(function() {
  468. module.verbose('Hiding sub-menu', $currentMenu);
  469. module.animate.hide(false, $currentMenu);
  470. }, settings.delay.hide);
  471. }
  472. },
  473. click: function (event) {
  474. var
  475. $choice = $(this),
  476. text = ( $choice.data(metadata.text) !== undefined )
  477. ? $choice.data(metadata.text)
  478. : (settings.preserveHTML)
  479. ? $choice.html()
  480. : $choice.text(),
  481. value = ( $choice.data(metadata.value) !== undefined)
  482. ? $choice.data(metadata.value)
  483. : (typeof text === 'string')
  484. ? text.toLowerCase()
  485. : text,
  486. callback = function() {
  487. $search.val('');
  488. module.determine.selectAction(text, value);
  489. $.proxy(settings.onChange, element)(value, text, $choice);
  490. },
  491. openingSubMenu = ($choice.find(selector.menu).size() > 0)
  492. ;
  493. if( !openingSubMenu ) {
  494. if(event.type == 'touchstart') {
  495. $choice.one('click', callback);
  496. }
  497. else {
  498. callback();
  499. }
  500. }
  501. }
  502. },
  503. resetStyle: function() {
  504. $(this).removeAttr('style');
  505. }
  506. },
  507. determine: {
  508. selectAction: function(text, value) {
  509. module.verbose('Determining action', settings.action);
  510. if( $.isFunction( module.action[settings.action] ) ) {
  511. module.verbose('Triggering preset action', settings.action, text, value);
  512. module.action[ settings.action ](text, value);
  513. }
  514. else if( $.isFunction(settings.action) ) {
  515. module.verbose('Triggering user action', settings.action, text, value);
  516. settings.action(text, value);
  517. }
  518. else {
  519. module.error(error.action, settings.action);
  520. }
  521. },
  522. eventInModule: function(event, callback) {
  523. callback = callback || function(){};
  524. if( $(event.target).closest($module).size() === 0 ) {
  525. module.verbose('Triggering event', callback);
  526. callback();
  527. return true;
  528. }
  529. else {
  530. module.verbose('Event occurred in dropdown, canceling callback');
  531. return false;
  532. }
  533. },
  534. eventInMenu: function(event, callback) {
  535. callback = callback || function(){};
  536. if( $(event.target).closest($menu).size() === 0 ) {
  537. module.verbose('Triggering event', callback);
  538. callback();
  539. return true;
  540. }
  541. else {
  542. module.verbose('Event occurred in dropdown menu, canceling callback');
  543. return false;
  544. }
  545. }
  546. },
  547. action: {
  548. nothing: function() {},
  549. hide: function() {
  550. module.hide();
  551. },
  552. select: function(text, value) {
  553. value = (value !== undefined)
  554. ? value
  555. : text
  556. ;
  557. module.set.selected(value);
  558. module.set.value(value);
  559. module.hide();
  560. },
  561. activate: function(text, value) {
  562. value = (value !== undefined)
  563. ? value
  564. : text
  565. ;
  566. module.set.selected(value);
  567. module.set.value(value);
  568. module.hide();
  569. },
  570. combo: function(text, value) {
  571. value = (value !== undefined)
  572. ? value
  573. : text
  574. ;
  575. module.set.selected(value);
  576. module.set.value(value);
  577. module.hide();
  578. }
  579. },
  580. get: {
  581. text: function() {
  582. return $text.text();
  583. },
  584. value: function() {
  585. return ($input.size() > 0)
  586. ? $input.val()
  587. : $module.data(metadata.value)
  588. ;
  589. },
  590. inputEvent: function() {
  591. var
  592. input = $search[0]
  593. ;
  594. if(input) {
  595. return (input.oninput !== undefined)
  596. ? 'input'
  597. : (input.onpropertychange !== undefined)
  598. ? 'propertychange'
  599. : 'keyup'
  600. ;
  601. }
  602. return false;
  603. },
  604. selectValues: function() {
  605. var
  606. select = {
  607. values : {}
  608. }
  609. ;
  610. $module
  611. .find('option')
  612. .each(function() {
  613. var
  614. name = $(this).html(),
  615. value = ( $(this).attr('value') !== undefined )
  616. ? $(this).attr('value')
  617. : name
  618. ;
  619. if(value === '') {
  620. select.placeholder = name;
  621. }
  622. else {
  623. select.values[value] = name;
  624. }
  625. })
  626. ;
  627. module.debug('Retrieved values from select', select);
  628. return select;
  629. },
  630. item: function(value, strict) {
  631. var
  632. $selectedItem = false
  633. ;
  634. value = (value !== undefined)
  635. ? value
  636. : ( module.get.value() !== undefined)
  637. ? module.get.value()
  638. : module.get.text()
  639. ;
  640. strict = (value === '' || value === 0)
  641. ? true
  642. : strict || false
  643. ;
  644. if(value !== undefined) {
  645. $item
  646. .each(function() {
  647. var
  648. $choice = $(this),
  649. optionText = ( $choice.data(metadata.text) !== undefined )
  650. ? $choice.data(metadata.text)
  651. : (settings.preserveHTML)
  652. ? $choice.html()
  653. : $choice.text(),
  654. optionValue = ( $choice.data(metadata.value) !== undefined )
  655. ? $choice.data(metadata.value)
  656. : (typeof optionText === 'string')
  657. ? optionText.toLowerCase()
  658. : optionText
  659. ;
  660. if(strict) {
  661. module.debug('Ambiguous dropdown value using strict type check', value);
  662. if( optionValue === value ) {
  663. $selectedItem = $(this);
  664. }
  665. else if( !$selectedItem && optionText === value ) {
  666. $selectedItem = $(this);
  667. }
  668. }
  669. else {
  670. if( optionValue == value ) {
  671. module.verbose('Found select item by value', optionValue, value);
  672. $selectedItem = $(this);
  673. }
  674. else if( !$selectedItem && optionText == value ) {
  675. module.verbose('Found select item by text', optionText, value);
  676. $selectedItem = $(this);
  677. }
  678. }
  679. })
  680. ;
  681. }
  682. else {
  683. value = module.get.text();
  684. }
  685. return $selectedItem || false;
  686. }
  687. },
  688. restore: {
  689. defaults: function() {
  690. module.restore.defaultText();
  691. module.restore.defaultValue();
  692. },
  693. defaultText: function() {
  694. var
  695. defaultText = $module.data(metadata.defaultText)
  696. ;
  697. module.debug('Restoring default text', defaultText);
  698. module.set.text(defaultText);
  699. },
  700. defaultValue: function() {
  701. var
  702. defaultValue = $module.data(metadata.defaultValue)
  703. ;
  704. if(defaultValue !== undefined) {
  705. module.debug('Restoring default value', defaultValue);
  706. module.set.selected(defaultValue);
  707. module.set.value(defaultValue);
  708. }
  709. }
  710. },
  711. save: {
  712. defaults: function() {
  713. module.save.defaultText();
  714. module.save.defaultValue();
  715. },
  716. defaultValue: function() {
  717. $module.data(metadata.defaultValue, module.get.value() );
  718. },
  719. defaultText: function() {
  720. $module.data(metadata.defaultText, $text.text() );
  721. }
  722. },
  723. set: {
  724. scrollPosition: function($item) {
  725. var
  726. $item = $item || module.get.item(),
  727. hasActive = ($item && $item.size() > 0),
  728. edgeTolerance = 5,
  729. offset,
  730. itemHeight,
  731. itemOffset,
  732. menuOffset,
  733. menuScroll,
  734. menuHeight,
  735. abovePage,
  736. belowPage
  737. ;
  738. if($item && hasActive) {
  739. menuHeight = $menu.height();
  740. itemHeight = $item.height();
  741. menuScroll = $menu.scrollTop();
  742. menuOffset = $menu.offset().top;
  743. itemOffset = $item.offset().top;
  744. offset = menuScroll - menuOffset + itemOffset;
  745. belowPage = menuScroll + menuHeight < (offset + edgeTolerance);
  746. abovePage = ((offset - edgeTolerance) < menuScroll);
  747. if(abovePage || belowPage) {
  748. module.debug('Scrolling to active item');
  749. $menu
  750. .scrollTop(offset)
  751. ;
  752. }
  753. }
  754. },
  755. text: function(text) {
  756. if(settings.action == 'combo') {
  757. module.debug('Changing combo button text', text, $combo);
  758. if(settings.preserveHTML) {
  759. $combo.html(text);
  760. }
  761. else {
  762. $combo.text(text);
  763. }
  764. }
  765. else if(settings.action !== 'select') {
  766. module.debug('Changing text', text, $text);
  767. $text
  768. .removeClass(className.filtered)
  769. .removeClass(className.placeholder)
  770. ;
  771. if(settings.preserveHTML) {
  772. $text.html(text);
  773. }
  774. else {
  775. $text.text(text);
  776. }
  777. }
  778. },
  779. value: function(value) {
  780. module.debug('Adding selected value to hidden input', value, $input);
  781. if($input.size() > 0) {
  782. $input
  783. .val(value)
  784. .trigger('change')
  785. ;
  786. }
  787. else {
  788. $module.data(metadata.value, value);
  789. }
  790. },
  791. active: function() {
  792. $module
  793. .addClass(className.active)
  794. ;
  795. },
  796. visible: function() {
  797. $module.addClass(className.visible);
  798. },
  799. selected: function(value) {
  800. var
  801. $selectedItem = module.get.item(value),
  802. selectedText
  803. ;
  804. if($selectedItem) {
  805. module.debug('Setting selected menu item to', $selectedItem);
  806. selectedText = ($selectedItem.data(metadata.text) !== undefined)
  807. ? $selectedItem.data(metadata.text)
  808. : (settings.preserveHTML)
  809. ? $selectedItem.html()
  810. : $selectedItem.text()
  811. ;
  812. $item
  813. .removeClass(className.active)
  814. .removeClass(className.selected)
  815. ;
  816. $selectedItem
  817. .addClass(className.active)
  818. .addClass(className.selected)
  819. ;
  820. module.set.text(selectedText);
  821. }
  822. }
  823. },
  824. remove: {
  825. active: function() {
  826. $module.removeClass(className.active);
  827. },
  828. visible: function() {
  829. $module.removeClass(className.visible);
  830. }
  831. },
  832. is: {
  833. search: function() {
  834. return $module.hasClass(className.search);
  835. },
  836. searchable: function() {
  837. return ($search.size() > 0);
  838. },
  839. searchSelection: function() {
  840. return ( module.is.searchable() && $search.parent().is($module) );
  841. },
  842. selection: function() {
  843. return $module.hasClass(className.selection);
  844. },
  845. animated: function($subMenu) {
  846. return ($subMenu)
  847. ? $subMenu.is(':animated') || $subMenu.transition && $subMenu.transition('is animating')
  848. : $menu.is(':animated') || $menu.transition && $menu.transition('is animating')
  849. ;
  850. },
  851. visible: function($subMenu) {
  852. return ($subMenu)
  853. ? $subMenu.is(':visible')
  854. : $menu.is(':visible')
  855. ;
  856. },
  857. hidden: function($subMenu) {
  858. return ($subMenu)
  859. ? $subMenu.is(':hidden')
  860. : $menu.is(':hidden')
  861. ;
  862. }
  863. },
  864. can: {
  865. click: function() {
  866. return (hasTouch || settings.on == 'click');
  867. },
  868. show: function() {
  869. return !$module.hasClass(className.disabled);
  870. }
  871. },
  872. animate: {
  873. show: function(callback, $subMenu) {
  874. var
  875. $currentMenu = $subMenu || $menu
  876. ;
  877. callback = callback || function(){};
  878. if( module.is.hidden($currentMenu) ) {
  879. module.verbose('Doing menu show animation', $currentMenu);
  880. if(settings.transition == 'none') {
  881. $.proxy(callback, element)();
  882. }
  883. else if($.fn.transition !== undefined && $module.transition('is supported')) {
  884. $currentMenu
  885. .transition({
  886. animation : settings.transition + ' in',
  887. duration : settings.duration,
  888. queue : true,
  889. complete : function() {
  890. $.proxy(callback, element)();
  891. }
  892. })
  893. ;
  894. }
  895. else if(settings.transition == 'slide down') {
  896. $currentMenu
  897. .hide()
  898. .clearQueue()
  899. .children()
  900. .clearQueue()
  901. .css('opacity', 0)
  902. .delay(50)
  903. .animate({
  904. opacity : 1
  905. }, settings.duration, 'easeOutQuad', module.event.resetStyle)
  906. .end()
  907. .slideDown(100, 'easeOutQuad', function() {
  908. $.proxy(module.event.resetStyle, this)();
  909. $.proxy(callback, element)();
  910. })
  911. ;
  912. }
  913. else if(settings.transition == 'fade') {
  914. $currentMenu
  915. .hide()
  916. .clearQueue()
  917. .fadeIn(settings.duration, function() {
  918. $.proxy(module.event.resetStyle, this)();
  919. $.proxy(callback, element)();
  920. })
  921. ;
  922. }
  923. else {
  924. module.error(error.transition, settings.transition);
  925. }
  926. }
  927. },
  928. hide: function(callback, $subMenu) {
  929. var
  930. $currentMenu = $subMenu || $menu
  931. ;
  932. callback = callback || function(){};
  933. if( module.is.visible($currentMenu) ) {
  934. module.verbose('Doing menu hide animation', $currentMenu);
  935. if(settings.transition == 'none') {
  936. $.proxy(callback, element)();
  937. }
  938. else if($.fn.transition !== undefined && $module.transition('is supported')) {
  939. $currentMenu
  940. .transition({
  941. animation : settings.transition + ' out',
  942. duration : settings.duration,
  943. queue : true,
  944. complete : function() {
  945. $.proxy(callback, element)();
  946. }
  947. })
  948. ;
  949. }
  950. else if(settings.transition == 'slide down') {
  951. $currentMenu
  952. .show()
  953. .clearQueue()
  954. .children()
  955. .clearQueue()
  956. .css('opacity', 1)
  957. .animate({
  958. opacity : 0
  959. }, 100, 'easeOutQuad', module.event.resetStyle)
  960. .end()
  961. .delay(50)
  962. .slideUp(100, 'easeOutQuad', function() {
  963. $.proxy(module.event.resetStyle, this)();
  964. $.proxy(callback, element)();
  965. })
  966. ;
  967. }
  968. else if(settings.transition == 'fade') {
  969. $currentMenu
  970. .show()
  971. .clearQueue()
  972. .fadeOut(150, function() {
  973. $.proxy(module.event.resetStyle, this)();
  974. $.proxy(callback, element)();
  975. })
  976. ;
  977. }
  978. else {
  979. module.error(error.transition);
  980. }
  981. }
  982. }
  983. },
  984. show: function() {
  985. module.debug('Checking if dropdown can show');
  986. if( module.is.hidden() ) {
  987. module.hideOthers();
  988. module.set.active();
  989. module.set.scrollPosition();
  990. module.animate.show(function() {
  991. if( module.can.click() ) {
  992. module.bind.intent();
  993. }
  994. module.set.visible();
  995. });
  996. $.proxy(settings.onShow, element)();
  997. }
  998. },
  999. hide: function() {
  1000. if( !module.is.animated() && module.is.visible() ) {
  1001. module.debug('Hiding dropdown');
  1002. if( module.can.click() ) {
  1003. module.unbind.intent();
  1004. }
  1005. module.remove.active();
  1006. module.animate.hide(function() {
  1007. $item
  1008. .removeClass(className.filtered)
  1009. ;
  1010. module.remove.visible();
  1011. });
  1012. $.proxy(settings.onHide, element)();
  1013. }
  1014. },
  1015. delay: {
  1016. show: function() {
  1017. module.verbose('Delaying show event to ensure user intent');
  1018. clearTimeout(module.timer);
  1019. module.timer = setTimeout(module.show, settings.delay.show);
  1020. },
  1021. hide: function() {
  1022. module.verbose('Delaying hide event to ensure user intent');
  1023. clearTimeout(module.timer);
  1024. module.timer = setTimeout(module.hide, settings.delay.hide);
  1025. }
  1026. },
  1027. hideOthers: function() {
  1028. module.verbose('Finding other dropdowns to hide');
  1029. $allModules
  1030. .not($module)
  1031. .has(selector.menu + ':visible')
  1032. .dropdown('hide')
  1033. ;
  1034. },
  1035. toggle: function() {
  1036. module.verbose('Toggling menu visibility');
  1037. if( module.is.hidden() ) {
  1038. module.show();
  1039. }
  1040. else {
  1041. module.hide();
  1042. }
  1043. },
  1044. setting: function(name, value) {
  1045. module.debug('Changing setting', name, value);
  1046. if( $.isPlainObject(name) ) {
  1047. $.extend(true, settings, name);
  1048. }
  1049. else if(value !== undefined) {
  1050. settings[name] = value;
  1051. }
  1052. else {
  1053. return settings[name];
  1054. }
  1055. },
  1056. internal: function(name, value) {
  1057. if( $.isPlainObject(name) ) {
  1058. $.extend(true, module, name);
  1059. }
  1060. else if(value !== undefined) {
  1061. module[name] = value;
  1062. }
  1063. else {
  1064. return module[name];
  1065. }
  1066. },
  1067. debug: function() {
  1068. if(settings.debug) {
  1069. if(settings.performance) {
  1070. module.performance.log(arguments);
  1071. }
  1072. else {
  1073. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  1074. module.debug.apply(console, arguments);
  1075. }
  1076. }
  1077. },
  1078. verbose: function() {
  1079. if(settings.verbose && settings.debug) {
  1080. if(settings.performance) {
  1081. module.performance.log(arguments);
  1082. }
  1083. else {
  1084. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  1085. module.verbose.apply(console, arguments);
  1086. }
  1087. }
  1088. },
  1089. error: function() {
  1090. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  1091. module.error.apply(console, arguments);
  1092. },
  1093. performance: {
  1094. log: function(message) {
  1095. var
  1096. currentTime,
  1097. executionTime,
  1098. previousTime
  1099. ;
  1100. if(settings.performance) {
  1101. currentTime = new Date().getTime();
  1102. previousTime = time || currentTime;
  1103. executionTime = currentTime - previousTime;
  1104. time = currentTime;
  1105. performance.push({
  1106. 'Name' : message[0],
  1107. 'Arguments' : [].slice.call(message, 1) || '',
  1108. 'Element' : element,
  1109. 'Execution Time' : executionTime
  1110. });
  1111. }
  1112. clearTimeout(module.performance.timer);
  1113. module.performance.timer = setTimeout(module.performance.display, 100);
  1114. },
  1115. display: function() {
  1116. var
  1117. title = settings.name + ':',
  1118. totalTime = 0
  1119. ;
  1120. time = false;
  1121. clearTimeout(module.performance.timer);
  1122. $.each(performance, function(index, data) {
  1123. totalTime += data['Execution Time'];
  1124. });
  1125. title += ' ' + totalTime + 'ms';
  1126. if(moduleSelector) {
  1127. title += ' \'' + moduleSelector + '\'';
  1128. }
  1129. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  1130. console.groupCollapsed(title);
  1131. if(console.table) {
  1132. console.table(performance);
  1133. }
  1134. else {
  1135. $.each(performance, function(index, data) {
  1136. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  1137. });
  1138. }
  1139. console.groupEnd();
  1140. }
  1141. performance = [];
  1142. }
  1143. },
  1144. invoke: function(query, passedArguments, context) {
  1145. var
  1146. object = instance,
  1147. maxDepth,
  1148. found,
  1149. response
  1150. ;
  1151. passedArguments = passedArguments || queryArguments;
  1152. context = element || context;
  1153. if(typeof query == 'string' && object !== undefined) {
  1154. query = query.split(/[\. ]/);
  1155. maxDepth = query.length - 1;
  1156. $.each(query, function(depth, value) {
  1157. var camelCaseValue = (depth != maxDepth)
  1158. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  1159. : query
  1160. ;
  1161. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  1162. object = object[camelCaseValue];
  1163. }
  1164. else if( object[camelCaseValue] !== undefined ) {
  1165. found = object[camelCaseValue];
  1166. return false;
  1167. }
  1168. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  1169. object = object[value];
  1170. }
  1171. else if( object[value] !== undefined ) {
  1172. found = object[value];
  1173. return false;
  1174. }
  1175. else {
  1176. module.error(error.method, query);
  1177. return false;
  1178. }
  1179. });
  1180. }
  1181. if ( $.isFunction( found ) ) {
  1182. response = found.apply(context, passedArguments);
  1183. }
  1184. else if(found !== undefined) {
  1185. response = found;
  1186. }
  1187. if($.isArray(returnedValue)) {
  1188. returnedValue.push(response);
  1189. }
  1190. else if(returnedValue !== undefined) {
  1191. returnedValue = [returnedValue, response];
  1192. }
  1193. else if(response !== undefined) {
  1194. returnedValue = response;
  1195. }
  1196. return found;
  1197. }
  1198. };
  1199. if(methodInvoked) {
  1200. if(instance === undefined) {
  1201. module.initialize();
  1202. }
  1203. module.invoke(query);
  1204. }
  1205. else {
  1206. if(instance !== undefined) {
  1207. module.destroy();
  1208. }
  1209. module.initialize();
  1210. }
  1211. })
  1212. ;
  1213. return (returnedValue !== undefined)
  1214. ? returnedValue
  1215. : this
  1216. ;
  1217. };
  1218. $.fn.dropdown.settings = {
  1219. name : 'Dropdown',
  1220. namespace : 'dropdown',
  1221. debug : false,
  1222. verbose : true,
  1223. performance : true,
  1224. type : false,
  1225. on : 'click',
  1226. action : 'activate',
  1227. allowTab : true,
  1228. fullTextSearch : true,
  1229. preserveHTML : true,
  1230. delay : {
  1231. show : 200,
  1232. hide : 300,
  1233. touch : 50
  1234. },
  1235. transition : 'slide down',
  1236. duration : 250,
  1237. onChange : function(value, text){},
  1238. onShow : function(){},
  1239. onHide : function(){},
  1240. error : {
  1241. action : 'You called a dropdown action that was not defined',
  1242. method : 'The method you called is not defined.',
  1243. transition : 'The requested transition was not found'
  1244. },
  1245. metadata: {
  1246. defaultText : 'defaultText',
  1247. defaultValue : 'defaultValue',
  1248. text : 'text',
  1249. value : 'value'
  1250. },
  1251. selector : {
  1252. dropdown : '.ui.dropdown',
  1253. text : '> .text:not(.icon)',
  1254. input : '> input[type="hidden"], > select',
  1255. search : '> .search, .menu > .search > input, .menu > input.search',
  1256. menu : '.menu',
  1257. item : '.item'
  1258. },
  1259. className : {
  1260. active : 'active',
  1261. disabled : 'disabled',
  1262. dropdown : 'ui dropdown',
  1263. filtered : 'filtered',
  1264. menu : 'menu',
  1265. placeholder : 'default',
  1266. search : 'search',
  1267. selected : 'selected',
  1268. selection : 'selection',
  1269. visible : 'visible'
  1270. }
  1271. };
  1272. $.fn.dropdown.settings.templates = {
  1273. menu: function(select) {
  1274. var
  1275. placeholder = select.placeholder || false,
  1276. values = select.values || {},
  1277. html = ''
  1278. ;
  1279. $.each(select.values, function(value, name) {
  1280. if(value === name) {
  1281. html += '<div class="item">' + name + '</div>';
  1282. }
  1283. else {
  1284. html += '<div class="item" data-value="' + value + '">' + name + '</div>';
  1285. }
  1286. });
  1287. return html;
  1288. },
  1289. dropdown: function(select) {
  1290. var
  1291. placeholder = select.placeholder || false,
  1292. values = select.values || {},
  1293. html = ''
  1294. ;
  1295. html += '<i class="dropdown icon"></i>';
  1296. if(select.placeholder) {
  1297. html += '<div class="default text">' + placeholder + '</div>';
  1298. }
  1299. else {
  1300. html += '<div class="text"></div>';
  1301. }
  1302. html += '<div class="menu">';
  1303. $.each(select.values, function(value, name) {
  1304. if(value === name) {
  1305. html += '<div class="item">' + name + '</div>';
  1306. }
  1307. else {
  1308. html += '<div class="item" data-value="' + value + '">' + name + '</div>';
  1309. }
  1310. });
  1311. html += '</div>';
  1312. return html;
  1313. }
  1314. };
  1315. // Adds easing
  1316. $.extend( $.easing, {
  1317. easeOutQuad: function (x, t, b, c, d) {
  1318. return -c *(t/=d)*(t-2) + b;
  1319. },
  1320. });
  1321. })( jQuery, window , document );