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.

1186 lines
38 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
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
  1. /*
  2. * # Semantic - Popup
  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.popup = 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.popup.settings, parameters)
  31. : $.extend({}, $.fn.popup.settings),
  32. selector = settings.selector,
  33. className = settings.className,
  34. error = settings.error,
  35. metadata = settings.metadata,
  36. namespace = settings.namespace,
  37. eventNamespace = '.' + settings.namespace,
  38. moduleNamespace = 'module-' + namespace,
  39. $module = $(this),
  40. $context = $(settings.context),
  41. $target = (settings.target)
  42. ? $(settings.target)
  43. : $module,
  44. $window = $(window),
  45. $body = $('body'),
  46. $popup,
  47. $offsetParent,
  48. searchDepth = 0,
  49. triedPositions = false,
  50. element = this,
  51. instance = $module.data(moduleNamespace),
  52. module
  53. ;
  54. module = {
  55. // binds events
  56. initialize: function() {
  57. module.debug('Initializing module', $module);
  58. if(settings.on == 'click') {
  59. $module
  60. .on('click' + eventNamespace, module.toggle)
  61. ;
  62. }
  63. else if( module.get.startEvent() ) {
  64. $module
  65. .on(module.get.startEvent() + eventNamespace, module.event.start)
  66. .on(module.get.endEvent() + eventNamespace, module.event.end)
  67. ;
  68. }
  69. if(settings.target) {
  70. module.debug('Target set to element', $target);
  71. }
  72. $window
  73. .on('resize' + eventNamespace, module.event.resize)
  74. ;
  75. if( !module.exists() && settings.preserve) {
  76. module.create();
  77. }
  78. module.instantiate();
  79. },
  80. instantiate: function() {
  81. module.verbose('Storing instance of module', module);
  82. instance = module;
  83. $module
  84. .data(moduleNamespace, instance)
  85. ;
  86. },
  87. refresh: function() {
  88. if(settings.popup) {
  89. $popup = $(settings.popup);
  90. }
  91. else {
  92. if(settings.inline) {
  93. $popup = $target.next(selector.popup);
  94. }
  95. }
  96. if(settings.popup) {
  97. $popup.addClass(className.loading);
  98. $offsetParent = module.get.offsetParent();
  99. $popup.removeClass(className.loading);
  100. if(module.has.popup() && module.get.offsetParent($popup)[0] !== $offsetParent[0]) {
  101. module.debug('Moving popup to the same offset parent as activating element');
  102. $popup
  103. .detach()
  104. .appendTo($offsetParent)
  105. ;
  106. }
  107. }
  108. else {
  109. $offsetParent = (settings.inline)
  110. ? module.get.offsetParent($target)
  111. : module.has.popup()
  112. ? module.get.offsetParent($popup)
  113. : $body
  114. ;
  115. }
  116. if( $offsetParent.is('html') ) {
  117. module.debug('Setting page as offset parent');
  118. $offsetParent = $body;
  119. }
  120. },
  121. reposition: function() {
  122. module.refresh();
  123. module.set.position();
  124. },
  125. destroy: function() {
  126. module.debug('Destroying previous module');
  127. if($popup && !settings.preserve) {
  128. module.removePopup();
  129. }
  130. clearTimeout(module.hideTimer);
  131. clearTimeout(module.showTimer);
  132. $module
  133. .off(eventNamespace)
  134. .removeData(moduleNamespace)
  135. ;
  136. },
  137. event: {
  138. start: function(event) {
  139. var
  140. delay = ($.isPlainObject(settings.delay))
  141. ? settings.delay.show
  142. : settings.delay
  143. ;
  144. clearTimeout(module.hideTimer);
  145. module.showTimer = setTimeout(function() {
  146. if(module.is.hidden() && !( module.is.active() && module.is.dropdown()) ) {
  147. module.show();
  148. }
  149. }, delay);
  150. },
  151. end: function() {
  152. var
  153. delay = ($.isPlainObject(settings.delay))
  154. ? settings.delay.hide
  155. : settings.delay
  156. ;
  157. clearTimeout(module.showTimer);
  158. module.hideTimer = setTimeout(function() {
  159. if(module.is.visible() ) {
  160. module.hide();
  161. }
  162. }, delay);
  163. },
  164. resize: function() {
  165. if( module.is.visible() ) {
  166. module.set.position();
  167. }
  168. }
  169. },
  170. // generates popup html from metadata
  171. create: function() {
  172. var
  173. html = $module.data(metadata.html) || settings.html,
  174. variation = $module.data(metadata.variation) || settings.variation,
  175. title = $module.data(metadata.title) || settings.title,
  176. content = $module.data(metadata.content) || $module.attr('title') || settings.content
  177. ;
  178. if(html || content || title) {
  179. module.debug('Creating pop-up html');
  180. if(!html) {
  181. html = settings.templates.popup({
  182. title : title,
  183. content : content
  184. });
  185. }
  186. $popup = $('<div/>')
  187. .addClass(className.popup)
  188. .addClass(variation)
  189. .html(html)
  190. ;
  191. if(variation) {
  192. $popup
  193. .addClass(variation)
  194. ;
  195. }
  196. if(settings.inline) {
  197. module.verbose('Inserting popup element inline', $popup);
  198. $popup
  199. .insertAfter($module)
  200. ;
  201. }
  202. else {
  203. module.verbose('Appending popup element to body', $popup);
  204. $popup
  205. .appendTo( $context )
  206. ;
  207. }
  208. module.refresh();
  209. if(settings.hoverable) {
  210. module.bind.popup();
  211. }
  212. settings.onCreate.call($popup, element);
  213. }
  214. else if($target.next(selector.popup).length !== 0) {
  215. module.verbose('Pre-existing popup found');
  216. settings.inline = true;
  217. settings.popup = $target.next(selector.popup);
  218. module.refresh();
  219. if(settings.hoverable) {
  220. module.bind.popup();
  221. }
  222. }
  223. else if(settings.popup) {
  224. module.verbose('Used popup specified in settings');
  225. module.refresh();
  226. if(settings.hoverable) {
  227. module.bind.popup();
  228. }
  229. }
  230. else {
  231. module.debug('No content specified skipping display', element);
  232. }
  233. },
  234. // determines popup state
  235. toggle: function() {
  236. module.debug('Toggling pop-up');
  237. if( module.is.hidden() ) {
  238. module.debug('Popup is hidden, showing pop-up');
  239. module.unbind.close();
  240. module.hideAll();
  241. module.show();
  242. }
  243. else {
  244. module.debug('Popup is visible, hiding pop-up');
  245. module.hide();
  246. }
  247. },
  248. show: function(callback) {
  249. callback = $.isFunction(callback) ? callback : function(){};
  250. module.debug('Showing pop-up', settings.transition);
  251. if( !module.exists() ) {
  252. module.create();
  253. }
  254. else if(!settings.preserve && !settings.popup) {
  255. module.refresh();
  256. }
  257. if( $popup && module.set.position() ) {
  258. module.save.conditions();
  259. module.animate.show(callback);
  260. }
  261. },
  262. hide: function(callback) {
  263. callback = $.isFunction(callback) ? callback : function(){};
  264. module.remove.visible();
  265. module.unbind.close();
  266. if( module.is.visible() ) {
  267. module.restore.conditions();
  268. module.animate.hide(callback);
  269. }
  270. },
  271. hideAll: function() {
  272. $(selector.popup)
  273. .filter(':visible')
  274. .transition('hide')
  275. ;
  276. },
  277. hideGracefully: function(event) {
  278. // don't close on clicks inside popup
  279. if(event && $(event.target).closest(selector.popup).length === 0) {
  280. module.debug('Click occurred outside popup hiding popup');
  281. module.hide();
  282. }
  283. else {
  284. module.debug('Click was inside popup, keeping popup open');
  285. }
  286. },
  287. exists: function() {
  288. if(!$popup) {
  289. return false;
  290. }
  291. if(settings.inline || settings.popup) {
  292. return ( module.has.popup() );
  293. }
  294. else {
  295. return ( $popup.closest($context).length >= 1 )
  296. ? true
  297. : false
  298. ;
  299. }
  300. },
  301. removePopup: function() {
  302. module.debug('Removing popup', $popup);
  303. if( module.has.popup() && !settings.popup) {
  304. $popup.remove();
  305. $popup = undefined;
  306. }
  307. settings.onRemove.call($popup, element);
  308. },
  309. save: {
  310. conditions: function() {
  311. module.cache = {
  312. title: $module.attr('title')
  313. };
  314. if (module.cache.title) {
  315. $module.removeAttr('title');
  316. }
  317. module.verbose('Saving original attributes', module.cache.title);
  318. }
  319. },
  320. restore: {
  321. conditions: function() {
  322. if(module.cache && module.cache.title) {
  323. $module.attr('title', module.cache.title);
  324. module.verbose('Restoring original attributes', module.cache.title);
  325. }
  326. return true;
  327. }
  328. },
  329. animate: {
  330. show: function(callback) {
  331. callback = $.isFunction(callback) ? callback : function(){};
  332. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  333. module.set.visible();
  334. $popup
  335. .transition({
  336. animation : settings.transition + ' in',
  337. queue : false,
  338. debug : settings.debug,
  339. verbose : settings.verbose,
  340. duration : settings.duration,
  341. onComplete : function() {
  342. module.bind.close();
  343. callback.call($popup, element);
  344. settings.onVisible.call($popup, element);
  345. }
  346. })
  347. ;
  348. }
  349. else {
  350. module.set.visible();
  351. $popup
  352. .stop()
  353. .fadeIn(settings.duration, settings.easing, function() {
  354. module.bind.close();
  355. callback.call($popup, element);
  356. settings.onVisible.call($popup, element);
  357. })
  358. ;
  359. }
  360. settings.onShow.call($popup, element);
  361. },
  362. hide: function(callback) {
  363. callback = $.isFunction(callback) ? callback : function(){};
  364. module.debug('Hiding pop-up');
  365. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  366. $popup
  367. .transition({
  368. animation : settings.transition + ' out',
  369. queue : false,
  370. duration : settings.duration,
  371. debug : settings.debug,
  372. verbose : settings.verbose,
  373. onComplete : function() {
  374. module.reset();
  375. callback.call($popup, element);
  376. settings.onHidden.call($popup, element);
  377. }
  378. })
  379. ;
  380. }
  381. else {
  382. $popup
  383. .stop()
  384. .fadeOut(settings.duration, settings.easing, function() {
  385. module.reset();
  386. callback.call($popup, element);
  387. settings.onHidden.call($popup, element);
  388. })
  389. ;
  390. }
  391. settings.onHide.call($popup, element);
  392. }
  393. },
  394. get: {
  395. startEvent: function() {
  396. if(settings.on == 'hover') {
  397. return 'mouseenter';
  398. }
  399. else if(settings.on == 'focus') {
  400. return 'focus';
  401. }
  402. return false;
  403. },
  404. endEvent: function() {
  405. if(settings.on == 'hover') {
  406. return 'mouseleave';
  407. }
  408. else if(settings.on == 'focus') {
  409. return 'blur';
  410. }
  411. return false;
  412. },
  413. offsetParent: function($target) {
  414. var
  415. element = ($target !== undefined)
  416. ? $target[0]
  417. : $module[0],
  418. parentNode = element.parentNode,
  419. $node = $(parentNode)
  420. ;
  421. if(parentNode) {
  422. var
  423. is2D = ($node.css('transform') === 'none'),
  424. isStatic = ($node.css('position') === 'static'),
  425. isHTML = $node.is('html')
  426. ;
  427. while(parentNode && !isHTML && isStatic && is2D) {
  428. parentNode = parentNode.parentNode;
  429. $node = $(parentNode);
  430. is2D = ($node.css('transform') === 'none');
  431. isStatic = ($node.css('position') === 'static');
  432. isHTML = $node.is('html');
  433. }
  434. }
  435. return ($node && $node.length > 0)
  436. ? $node
  437. : $()
  438. ;
  439. },
  440. offstagePosition: function(position) {
  441. var
  442. boundary = {
  443. top : $(window).scrollTop(),
  444. bottom : $(window).scrollTop() + $(window).height(),
  445. left : 0,
  446. right : $(window).width()
  447. },
  448. popup = {
  449. width : $popup.width(),
  450. height : $popup.height(),
  451. offset : $popup.offset()
  452. },
  453. offstage = {},
  454. offstagePositions = []
  455. ;
  456. position = position || false;
  457. if(popup.offset && position) {
  458. module.verbose('Checking if outside viewable area', popup.offset);
  459. offstage = {
  460. top : (popup.offset.top < boundary.top),
  461. bottom : (popup.offset.top + popup.height > boundary.bottom),
  462. right : (popup.offset.left + popup.width > boundary.right),
  463. left : (popup.offset.left < boundary.left)
  464. };
  465. }
  466. // return only boundaries that have been surpassed
  467. $.each(offstage, function(direction, isOffstage) {
  468. if(isOffstage) {
  469. offstagePositions.push(direction);
  470. }
  471. });
  472. return (offstagePositions.length > 0)
  473. ? offstagePositions.join(' ')
  474. : false
  475. ;
  476. },
  477. positions: function() {
  478. return {
  479. 'top left' : false,
  480. 'top center' : false,
  481. 'top right' : false,
  482. 'bottom left' : false,
  483. 'bottom center' : false,
  484. 'bottom right' : false,
  485. 'left center' : false,
  486. 'right center' : false
  487. };
  488. },
  489. nextPosition: function(position) {
  490. var
  491. positions = position.split(' '),
  492. verticalPosition = positions[0],
  493. horizontalPosition = positions[1],
  494. opposite = {
  495. top : 'bottom',
  496. bottom : 'top',
  497. left : 'right',
  498. right : 'left'
  499. },
  500. adjacent = {
  501. left : 'center',
  502. center : 'right',
  503. right : 'left'
  504. },
  505. backup = {
  506. 'top left' : 'top center',
  507. 'top center' : 'top right',
  508. 'top right' : 'right center',
  509. 'right center' : 'bottom right',
  510. 'bottom right' : 'bottom center',
  511. 'bottom center' : 'bottom left',
  512. 'bottom left' : 'left center',
  513. 'left center' : 'top left'
  514. },
  515. adjacentsAvailable = (verticalPosition == 'top' || verticalPosition == 'bottom'),
  516. oppositeTried = false,
  517. adjacentTried = false,
  518. nextPosition = false
  519. ;
  520. if(!triedPositions) {
  521. module.verbose('All available positions available');
  522. triedPositions = module.get.positions();
  523. }
  524. module.debug('Recording last position tried', position);
  525. triedPositions[position] = true;
  526. if(settings.prefer === 'opposite') {
  527. nextPosition = [opposite[verticalPosition], horizontalPosition];
  528. nextPosition = nextPosition.join(' ');
  529. oppositeTried = (triedPositions[nextPosition] === true);
  530. module.debug('Trying opposite strategy', nextPosition);
  531. }
  532. if((settings.prefer === 'adjacent') && adjacentsAvailable ) {
  533. nextPosition = [verticalPosition, adjacent[horizontalPosition]];
  534. nextPosition = nextPosition.join(' ');
  535. adjacentTried = (triedPositions[nextPosition] === true);
  536. module.debug('Trying adjacent strategy', nextPosition);
  537. }
  538. if(adjacentTried || oppositeTried) {
  539. module.debug('Using backup position', nextPosition);
  540. nextPosition = backup[position];
  541. }
  542. return nextPosition;
  543. }
  544. },
  545. set: {
  546. position: function(position, arrowOffset) {
  547. var
  548. windowWidth = $(window).width(),
  549. windowHeight = $(window).height(),
  550. targetWidth = $target.outerWidth(),
  551. targetHeight = $target.outerHeight(),
  552. popupWidth = $popup.outerWidth(),
  553. popupHeight = $popup.outerHeight(),
  554. parentWidth = $offsetParent.outerWidth(),
  555. parentHeight = $offsetParent.outerHeight(),
  556. distanceAway = settings.distanceAway,
  557. targetElement = $target[0],
  558. marginTop = (settings.inline)
  559. ? parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-top'), 10)
  560. : 0,
  561. marginLeft = (settings.inline)
  562. ? parseInt( window.getComputedStyle(targetElement).getPropertyValue(module.is.rtl() ? 'margin-right' : 'margin-left'), 10)
  563. : 0,
  564. target = (settings.inline || settings.popup)
  565. ? $target.position()
  566. : $target.offset(),
  567. computedPosition,
  568. positioning,
  569. offstagePosition
  570. ;
  571. position = position || $module.data(metadata.position) || settings.position;
  572. arrowOffset = arrowOffset || $module.data(metadata.offset) || settings.offset;
  573. if(searchDepth == settings.maxSearchDepth && settings.lastResort) {
  574. module.debug('Using last resort position to display', settings.lastResort);
  575. position = settings.lastResort;
  576. }
  577. if(settings.inline) {
  578. module.debug('Adding targets margin to calculation');
  579. if(position == 'left center' || position == 'right center') {
  580. arrowOffset += marginTop;
  581. distanceAway += -marginLeft;
  582. }
  583. else if (position == 'top left' || position == 'top center' || position == 'top right') {
  584. arrowOffset += marginLeft;
  585. distanceAway -= marginTop;
  586. }
  587. else {
  588. arrowOffset += marginLeft;
  589. distanceAway += marginTop;
  590. }
  591. }
  592. module.debug('Calculating popup positioning', position);
  593. computedPosition = position;
  594. if (module.is.rtl()) {
  595. computedPosition = computedPosition.replace(/left|right/g, function (match) {
  596. return (match == 'left')
  597. ? 'right'
  598. : 'left'
  599. ;
  600. });
  601. module.debug('RTL: Popup positioning updated', computedPosition);
  602. }
  603. switch (computedPosition) {
  604. case 'top left':
  605. positioning = {
  606. top : 'auto',
  607. bottom : parentHeight - target.top + distanceAway,
  608. left : target.left + arrowOffset,
  609. right : 'auto'
  610. };
  611. break;
  612. case 'top center':
  613. positioning = {
  614. bottom : parentHeight - target.top + distanceAway,
  615. left : target.left + (targetWidth / 2) - (popupWidth / 2) + arrowOffset,
  616. top : 'auto',
  617. right : 'auto'
  618. };
  619. break;
  620. case 'top right':
  621. positioning = {
  622. bottom : parentHeight - target.top + distanceAway,
  623. right : parentWidth - target.left - targetWidth - arrowOffset,
  624. top : 'auto',
  625. left : 'auto'
  626. };
  627. break;
  628. case 'left center':
  629. positioning = {
  630. top : target.top + (targetHeight / 2) - (popupHeight / 2) + arrowOffset,
  631. right : parentWidth - target.left + distanceAway,
  632. left : 'auto',
  633. bottom : 'auto'
  634. };
  635. break;
  636. case 'right center':
  637. positioning = {
  638. top : target.top + (targetHeight / 2) - (popupHeight / 2) + arrowOffset,
  639. left : target.left + targetWidth + distanceAway,
  640. bottom : 'auto',
  641. right : 'auto'
  642. };
  643. break;
  644. case 'bottom left':
  645. positioning = {
  646. top : target.top + targetHeight + distanceAway,
  647. left : target.left + arrowOffset,
  648. bottom : 'auto',
  649. right : 'auto'
  650. };
  651. break;
  652. case 'bottom center':
  653. positioning = {
  654. top : target.top + targetHeight + distanceAway,
  655. left : target.left + (targetWidth / 2) - (popupWidth / 2) + arrowOffset,
  656. bottom : 'auto',
  657. right : 'auto'
  658. };
  659. break;
  660. case 'bottom right':
  661. positioning = {
  662. top : target.top + targetHeight + distanceAway,
  663. right : parentWidth - target.left - targetWidth - arrowOffset,
  664. left : 'auto',
  665. bottom : 'auto'
  666. };
  667. break;
  668. }
  669. if(positioning === undefined) {
  670. module.error(error.invalidPosition, position);
  671. }
  672. module.debug('Calculated popup positioning values', positioning);
  673. // tentatively place on stage
  674. $popup
  675. .css(positioning)
  676. .removeClass(className.position)
  677. .addClass(position)
  678. .addClass(className.loading)
  679. ;
  680. // check if is offstage
  681. offstagePosition = module.get.offstagePosition(position);
  682. // recursively find new positioning
  683. if(offstagePosition) {
  684. module.debug('Popup cant fit into viewport', offstagePosition);
  685. if(searchDepth < settings.maxSearchDepth) {
  686. searchDepth++;
  687. position = module.get.nextPosition(position);
  688. module.debug('Trying new position', position);
  689. return ($popup)
  690. ? module.set.position(position)
  691. : false
  692. ;
  693. }
  694. else if(!settings.lastResort) {
  695. module.debug('Popup could not find a position in view', $popup);
  696. module.error(error.cannotPlace);
  697. module.remove.attempts();
  698. module.remove.loading();
  699. module.reset();
  700. return false;
  701. }
  702. }
  703. module.debug('Position is on stage', position);
  704. module.remove.attempts();
  705. module.set.fluidWidth();
  706. module.remove.loading();
  707. return true;
  708. },
  709. fluidWidth: function() {
  710. if( settings.setFluidWidth && $popup.hasClass(className.fluid) ) {
  711. $popup.css('width', $offsetParent.width());
  712. }
  713. },
  714. visible: function() {
  715. $module.addClass(className.visible);
  716. }
  717. },
  718. remove: {
  719. loading: function() {
  720. $popup.removeClass(className.loading);
  721. },
  722. visible: function() {
  723. $module.removeClass(className.visible);
  724. },
  725. attempts: function() {
  726. module.verbose('Resetting all searched positions');
  727. searchDepth = 0;
  728. triedPositions = false;
  729. }
  730. },
  731. bind: {
  732. popup: function() {
  733. module.verbose('Allowing hover events on popup to prevent closing');
  734. if( $popup && module.has.popup() ) {
  735. $popup
  736. .on('mouseenter' + eventNamespace, module.event.start)
  737. .on('mouseleave' + eventNamespace, module.event.end)
  738. ;
  739. }
  740. },
  741. close:function() {
  742. if(settings.hideOnScroll === true || settings.hideOnScroll == 'auto' && settings.on != 'click') {
  743. $document
  744. .one('touchmove' + eventNamespace, module.hideGracefully)
  745. .one('scroll' + eventNamespace, module.hideGracefully)
  746. ;
  747. $context
  748. .one('touchmove' + eventNamespace, module.hideGracefully)
  749. .one('scroll' + eventNamespace, module.hideGracefully)
  750. ;
  751. }
  752. if(settings.on == 'click' && settings.closable) {
  753. module.verbose('Binding popup close event to document');
  754. $document
  755. .on('click' + eventNamespace, function(event) {
  756. module.verbose('Pop-up clickaway intent detected');
  757. module.hideGracefully.call(element, event);
  758. })
  759. ;
  760. }
  761. }
  762. },
  763. unbind: {
  764. close: function() {
  765. if(settings.hideOnScroll === true || settings.hideOnScroll == 'auto' && settings.on != 'click') {
  766. $document
  767. .off('scroll' + eventNamespace, module.hide)
  768. ;
  769. $context
  770. .off('scroll' + eventNamespace, module.hide)
  771. ;
  772. }
  773. if(settings.on == 'click' && settings.closable) {
  774. module.verbose('Removing close event from document');
  775. $document
  776. .off('click' + eventNamespace)
  777. ;
  778. }
  779. }
  780. },
  781. has: {
  782. popup: function() {
  783. return ($popup && $popup.length > 0);
  784. }
  785. },
  786. is: {
  787. active: function() {
  788. return $module.hasClass(className.active);
  789. },
  790. animating: function() {
  791. return ( $popup && $popup.is(':animated') || $popup.hasClass(className.animating) );
  792. },
  793. visible: function() {
  794. return $popup && $popup.is(':visible');
  795. },
  796. dropdown: function() {
  797. return $module.hasClass(className.dropdown);
  798. },
  799. hidden: function() {
  800. return !module.is.visible();
  801. },
  802. rtl: function () {
  803. return $module.css('direction') == 'rtl';
  804. }
  805. },
  806. reset: function() {
  807. module.remove.visible();
  808. if(settings.preserve) {
  809. if($.fn.transition !== undefined) {
  810. $popup
  811. .transition('remove transition')
  812. ;
  813. }
  814. }
  815. else {
  816. module.removePopup();
  817. }
  818. },
  819. setting: function(name, value) {
  820. if( $.isPlainObject(name) ) {
  821. $.extend(true, settings, name);
  822. }
  823. else if(value !== undefined) {
  824. settings[name] = value;
  825. }
  826. else {
  827. return settings[name];
  828. }
  829. },
  830. internal: function(name, value) {
  831. if( $.isPlainObject(name) ) {
  832. $.extend(true, module, name);
  833. }
  834. else if(value !== undefined) {
  835. module[name] = value;
  836. }
  837. else {
  838. return module[name];
  839. }
  840. },
  841. debug: function() {
  842. if(settings.debug) {
  843. if(settings.performance) {
  844. module.performance.log(arguments);
  845. }
  846. else {
  847. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  848. module.debug.apply(console, arguments);
  849. }
  850. }
  851. },
  852. verbose: function() {
  853. if(settings.verbose && settings.debug) {
  854. if(settings.performance) {
  855. module.performance.log(arguments);
  856. }
  857. else {
  858. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  859. module.verbose.apply(console, arguments);
  860. }
  861. }
  862. },
  863. error: function() {
  864. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  865. module.error.apply(console, arguments);
  866. },
  867. performance: {
  868. log: function(message) {
  869. var
  870. currentTime,
  871. executionTime,
  872. previousTime
  873. ;
  874. if(settings.performance) {
  875. currentTime = new Date().getTime();
  876. previousTime = time || currentTime;
  877. executionTime = currentTime - previousTime;
  878. time = currentTime;
  879. performance.push({
  880. 'Name' : message[0],
  881. 'Arguments' : [].slice.call(message, 1) || '',
  882. 'Element' : element,
  883. 'Execution Time' : executionTime
  884. });
  885. }
  886. clearTimeout(module.performance.timer);
  887. module.performance.timer = setTimeout(module.performance.display, 100);
  888. },
  889. display: function() {
  890. var
  891. title = settings.name + ':',
  892. totalTime = 0
  893. ;
  894. time = false;
  895. clearTimeout(module.performance.timer);
  896. $.each(performance, function(index, data) {
  897. totalTime += data['Execution Time'];
  898. });
  899. title += ' ' + totalTime + 'ms';
  900. if(moduleSelector) {
  901. title += ' \'' + moduleSelector + '\'';
  902. }
  903. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  904. console.groupCollapsed(title);
  905. if(console.table) {
  906. console.table(performance);
  907. }
  908. else {
  909. $.each(performance, function(index, data) {
  910. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  911. });
  912. }
  913. console.groupEnd();
  914. }
  915. performance = [];
  916. }
  917. },
  918. invoke: function(query, passedArguments, context) {
  919. var
  920. object = instance,
  921. maxDepth,
  922. found,
  923. response
  924. ;
  925. passedArguments = passedArguments || queryArguments;
  926. context = element || context;
  927. if(typeof query == 'string' && object !== undefined) {
  928. query = query.split(/[\. ]/);
  929. maxDepth = query.length - 1;
  930. $.each(query, function(depth, value) {
  931. var camelCaseValue = (depth != maxDepth)
  932. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  933. : query
  934. ;
  935. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  936. object = object[camelCaseValue];
  937. }
  938. else if( object[camelCaseValue] !== undefined ) {
  939. found = object[camelCaseValue];
  940. return false;
  941. }
  942. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  943. object = object[value];
  944. }
  945. else if( object[value] !== undefined ) {
  946. found = object[value];
  947. return false;
  948. }
  949. else {
  950. return false;
  951. }
  952. });
  953. }
  954. if ( $.isFunction( found ) ) {
  955. response = found.apply(context, passedArguments);
  956. }
  957. else if(found !== undefined) {
  958. response = found;
  959. }
  960. if($.isArray(returnedValue)) {
  961. returnedValue.push(response);
  962. }
  963. else if(returnedValue !== undefined) {
  964. returnedValue = [returnedValue, response];
  965. }
  966. else if(response !== undefined) {
  967. returnedValue = response;
  968. }
  969. return found;
  970. }
  971. };
  972. if(methodInvoked) {
  973. if(instance === undefined) {
  974. module.initialize();
  975. }
  976. module.invoke(query);
  977. }
  978. else {
  979. if(instance !== undefined) {
  980. module.destroy();
  981. }
  982. module.initialize();
  983. }
  984. })
  985. ;
  986. return (returnedValue !== undefined)
  987. ? returnedValue
  988. : this
  989. ;
  990. };
  991. $.fn.popup.settings = {
  992. name : 'Popup',
  993. debug : false,
  994. verbose : true,
  995. performance : true,
  996. namespace : 'popup',
  997. onCreate : function(){},
  998. onRemove : function(){},
  999. onShow : function(){},
  1000. onVisible : function(){},
  1001. onHide : function(){},
  1002. onHidden : function(){},
  1003. variation : '',
  1004. content : false,
  1005. html : false,
  1006. title : false,
  1007. on : 'hover',
  1008. closable : true,
  1009. hideOnScroll : 'auto',
  1010. context : 'body',
  1011. position : 'top left',
  1012. prefer : 'opposite',
  1013. lastResort : false,
  1014. delay : {
  1015. show : 30,
  1016. hide : 0
  1017. },
  1018. setFluidWidth : true,
  1019. target : false,
  1020. popup : false,
  1021. inline : false,
  1022. preserve : false,
  1023. hoverable : false,
  1024. duration : 200,
  1025. easing : 'easeOutQuint',
  1026. transition : 'scale',
  1027. distanceAway : 0,
  1028. offset : 0,
  1029. maxSearchDepth : 20,
  1030. error: {
  1031. invalidPosition : 'The position you specified is not a valid position',
  1032. cannotPlace : 'No visible position could be found for the popup',
  1033. method : 'The method you called is not defined.'
  1034. },
  1035. metadata: {
  1036. content : 'content',
  1037. html : 'html',
  1038. offset : 'offset',
  1039. position : 'position',
  1040. title : 'title',
  1041. variation : 'variation'
  1042. },
  1043. className : {
  1044. active : 'active',
  1045. animating : 'animating',
  1046. dropdown : 'dropdown',
  1047. fluid : 'fluid',
  1048. loading : 'loading',
  1049. popup : 'ui popup',
  1050. position : 'top left center bottom right',
  1051. visible : 'visible'
  1052. },
  1053. selector : {
  1054. popup : '.ui.popup'
  1055. },
  1056. templates: {
  1057. escape: function(string) {
  1058. var
  1059. badChars = /[&<>"'`]/g,
  1060. shouldEscape = /[&<>"'`]/,
  1061. escape = {
  1062. "&": "&amp;",
  1063. "<": "&lt;",
  1064. ">": "&gt;",
  1065. '"': "&quot;",
  1066. "'": "&#x27;",
  1067. "`": "&#x60;"
  1068. },
  1069. escapedChar = function(chr) {
  1070. return escape[chr];
  1071. }
  1072. ;
  1073. if(shouldEscape.test(string)) {
  1074. return string.replace(badChars, escapedChar);
  1075. }
  1076. return string;
  1077. },
  1078. popup: function(text) {
  1079. var
  1080. html = '',
  1081. escape = $.fn.popup.settings.templates.escape
  1082. ;
  1083. if(typeof text !== undefined) {
  1084. if(typeof text.title !== undefined && text.title) {
  1085. text.title = escape(text.title);
  1086. html += '<div class="header">' + text.title + '</div>';
  1087. }
  1088. if(typeof text.content !== undefined && text.content) {
  1089. text.content = escape(text.content);
  1090. html += '<div class="content">' + text.content + '</div>';
  1091. }
  1092. }
  1093. return html;
  1094. }
  1095. }
  1096. };
  1097. // Adds easing
  1098. $.extend( $.easing, {
  1099. easeOutQuad: function (x, t, b, c, d) {
  1100. return -c *(t/=d)*(t-2) + b;
  1101. }
  1102. });
  1103. })( jQuery, window , document );