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.

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