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.

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