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.

825 lines
26 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
  1. /*
  2. * # Semantic - Transition
  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.transition = function() {
  14. var
  15. $allModules = $(this),
  16. moduleSelector = $allModules.selector || '',
  17. time = new Date().getTime(),
  18. performance = [],
  19. moduleArguments = arguments,
  20. query = moduleArguments[0],
  21. queryArguments = [].slice.call(arguments, 1),
  22. methodInvoked = (typeof query === 'string'),
  23. requestAnimationFrame = window.requestAnimationFrame
  24. || window.mozRequestAnimationFrame
  25. || window.webkitRequestAnimationFrame
  26. || window.msRequestAnimationFrame
  27. || function(callback) { setTimeout(callback, 0); },
  28. returnedValue
  29. ;
  30. $allModules
  31. .each(function() {
  32. var
  33. $module = $(this),
  34. element = this,
  35. // set at run time
  36. settings,
  37. instance,
  38. error,
  39. className,
  40. metadata,
  41. animationEnd,
  42. animationName,
  43. namespace,
  44. moduleNamespace,
  45. eventNamespace,
  46. module
  47. ;
  48. module = {
  49. initialize: function() {
  50. // get full settings
  51. moduleNamespace = 'module-' + namespace;
  52. settings = module.get.settings.apply(element, moduleArguments);
  53. className = settings.className;
  54. metadata = settings.metadata;
  55. animationEnd = module.get.animationEvent();
  56. animationName = module.get.animationName();
  57. error = settings.error;
  58. namespace = settings.namespace;
  59. eventNamespace = '.' + settings.namespace;
  60. instance = $module.data(moduleNamespace) || module;
  61. if(methodInvoked) {
  62. methodInvoked = module.invoke(query);
  63. }
  64. // no internal method was found matching query or query not made
  65. if(methodInvoked === false) {
  66. module.verbose('Converted arguments into settings object', settings);
  67. module.animate();
  68. module.instantiate();
  69. }
  70. },
  71. instantiate: function() {
  72. module.verbose('Storing instance of module', module);
  73. $module
  74. .data(moduleNamespace, instance)
  75. ;
  76. },
  77. destroy: function() {
  78. module.verbose('Destroying previous module for', element);
  79. $module
  80. .removeData(moduleNamespace)
  81. ;
  82. },
  83. refresh: function() {
  84. module.verbose('Refreshing display type on next animation');
  85. delete module.displayType;
  86. },
  87. forceRepaint: function() {
  88. module.verbose('Forcing element repaint');
  89. var
  90. $parentElement = $module.parent(),
  91. $nextElement = $module.next()
  92. ;
  93. if($nextElement.size() === 0) {
  94. $module.detach().appendTo($parentElement);
  95. }
  96. else {
  97. $module.detach().insertBefore($nextElement);
  98. }
  99. },
  100. repaint: function() {
  101. module.verbose('Repainting element');
  102. var
  103. fakeAssignment = element.offsetWidth
  104. ;
  105. },
  106. animate: function(overrideSettings) {
  107. settings = overrideSettings || settings;
  108. if(!module.is.supported()) {
  109. module.error(error.support);
  110. return false;
  111. }
  112. module.debug('Preparing animation', settings.animation);
  113. if(module.is.animating() && settings.queue) {
  114. if(!settings.allowRepeats && module.has.direction() && module.is.occuring() && module.queuing !== true) {
  115. module.error(error.repeated);
  116. }
  117. else {
  118. module.queue(settings.animation);
  119. }
  120. return false;
  121. }
  122. if(module.can.animate) {
  123. module.set.animating(settings.animation);
  124. }
  125. else {
  126. module.error(error.noAnimation, settings.animation);
  127. }
  128. },
  129. reset: function() {
  130. module.debug('Resetting animation to beginning conditions');
  131. $module.off(animationEnd + eventNamespace);
  132. module.restore.conditions();
  133. module.hide();
  134. module.remove.animating();
  135. },
  136. queue: function(animation) {
  137. module.debug('Queueing animation of', animation);
  138. module.queuing = true;
  139. $module
  140. .one(animationEnd + eventNamespace, function() {
  141. module.queuing = false;
  142. module.repaint();
  143. module.animate.apply(this, settings);
  144. })
  145. ;
  146. },
  147. complete: function () {
  148. module.verbose('CSS animation complete', settings.animation);
  149. if(!module.is.looping()) {
  150. if( module.is.outward() ) {
  151. module.verbose('Animation is outward, hiding element');
  152. module.restore.conditions();
  153. module.remove.display();
  154. module.hide();
  155. $.proxy(settings.onHide, this)();
  156. }
  157. else if( module.is.inward() ) {
  158. module.verbose('Animation is outward, showing element');
  159. module.restore.conditions();
  160. module.set.display();
  161. module.show();
  162. $.proxy(settings.onShow, this)();
  163. }
  164. else {
  165. module.restore.conditions();
  166. }
  167. module.remove.duration();
  168. module.remove.animating();
  169. }
  170. $.proxy(settings.complete, this)();
  171. },
  172. has: {
  173. direction: function(animation) {
  174. animation = animation || settings.animation;
  175. if( animation.search(className.inward) !== -1 || animation.search(className.outward) !== -1) {
  176. module.debug('Direction already set in animation');
  177. return true;
  178. }
  179. return false;
  180. }
  181. },
  182. set: {
  183. animating: function(animation) {
  184. animation = animation || settings.animation;
  185. module.save.conditions();
  186. if(module.can.transition() && !module.has.direction()) {
  187. module.set.direction();
  188. }
  189. module.remove.hidden();
  190. module.set.display();
  191. $module
  192. .addClass(className.animating)
  193. .addClass(className.transition)
  194. .addClass(animation)
  195. .one(animationEnd + eventNamespace, module.complete)
  196. ;
  197. module.set.duration(settings.duration);
  198. module.debug('Starting tween', settings.animation, $module.attr('class'));
  199. },
  200. display: function() {
  201. var
  202. style = $module.attr('style') || '',
  203. displayType = module.get.displayType(),
  204. overrideStyle = style + ';display: ' + displayType + ' !important;'
  205. ;
  206. if( $module.css('display') !== displayType ) {
  207. module.verbose('Setting inline visibility to', displayType);
  208. $module
  209. .attr('style', overrideStyle)
  210. ;
  211. }
  212. },
  213. direction: function() {
  214. if($module.is(':visible') && !module.is.hidden()) {
  215. module.debug('Automatically determining the direction of animation', 'Outward');
  216. $module
  217. .removeClass(className.inward)
  218. .addClass(className.outward)
  219. ;
  220. }
  221. else {
  222. module.debug('Automatically determining the direction of animation', 'Inward');
  223. $module
  224. .removeClass(className.outward)
  225. .addClass(className.inward)
  226. ;
  227. }
  228. },
  229. looping: function() {
  230. module.debug('Transition set to loop');
  231. $module
  232. .addClass(className.looping)
  233. ;
  234. },
  235. duration: function(duration) {
  236. duration = duration || settings.duration;
  237. duration = (typeof duration == 'number')
  238. ? duration + 'ms'
  239. : duration
  240. ;
  241. module.verbose('Setting animation duration', duration);
  242. $module
  243. .css({
  244. '-webkit-animation-duration': duration,
  245. '-moz-animation-duration': duration,
  246. '-ms-animation-duration': duration,
  247. '-o-animation-duration': duration,
  248. 'animation-duration': duration
  249. })
  250. ;
  251. },
  252. hidden: function() {
  253. if(!module.is.hidden()) {
  254. $module
  255. .addClass(className.transition)
  256. .addClass(className.hidden)
  257. ;
  258. if($module.css('display') !== 'none') {
  259. module.verbose('Overriding default display to hide element');
  260. $module
  261. .css('display', 'none')
  262. ;
  263. }
  264. }
  265. },
  266. visible: function() {
  267. $module
  268. .addClass(className.transition)
  269. .addClass(className.visible)
  270. ;
  271. }
  272. },
  273. save: {
  274. displayType: function(displayType) {
  275. module.displayType = displayType;
  276. },
  277. transitionExists: function(animation, exists) {
  278. $.fn.transition.exists[animation] = exists;
  279. module.verbose('Saving existence of transition', animation, exists);
  280. },
  281. conditions: function() {
  282. module.cache = {
  283. className : $module.attr('class'),
  284. style : $module.attr('style')
  285. };
  286. module.verbose('Saving original attributes', module.cache);
  287. }
  288. },
  289. restore: {
  290. conditions: function() {
  291. if(module.cache === undefined) {
  292. return false;
  293. }
  294. if(module.cache.className) {
  295. $module.attr('class', module.cache.className);
  296. }
  297. else {
  298. $module.removeAttr('class');
  299. }
  300. if(module.cache.style) {
  301. module.verbose('Restoring original style attribute', module.cache.style);
  302. $module.attr('style', module.cache.style);
  303. }
  304. else {
  305. if(module.get.displayType() === 'block') {
  306. module.verbose('Removing inline style override, element defaults to block');
  307. $module.removeAttr('style');
  308. }
  309. }
  310. if(module.is.looping()) {
  311. module.remove.looping();
  312. }
  313. module.verbose('Restoring original attributes', module.cache);
  314. }
  315. },
  316. remove: {
  317. animating: function() {
  318. $module.removeClass(className.animating);
  319. },
  320. display: function() {
  321. if(module.displayType !== undefined) {
  322. $module.css('display', '');
  323. }
  324. },
  325. duration: function() {
  326. $module
  327. .css({
  328. '-webkit-animation-duration' : '',
  329. '-moz-animation-duration' : '',
  330. '-ms-animation-duration' : '',
  331. '-o-animation-duration' : '',
  332. 'animation-duration' : ''
  333. })
  334. ;
  335. },
  336. hidden: function() {
  337. $module.removeClass(className.hidden);
  338. },
  339. visible: function() {
  340. $module.removeClass(className.visible);
  341. },
  342. looping: function() {
  343. module.debug('Transitions are no longer looping');
  344. $module
  345. .removeClass(className.looping)
  346. ;
  347. module.forceRepaint();
  348. },
  349. transition: function() {
  350. $module
  351. .removeClass(className.visible)
  352. .removeClass(className.hidden)
  353. ;
  354. }
  355. },
  356. get: {
  357. settings: function(animation, duration, complete) {
  358. // single settings object
  359. if(typeof animation == 'object') {
  360. return $.extend(true, {}, $.fn.transition.settings, animation);
  361. }
  362. // all arguments provided
  363. else if(typeof complete == 'function') {
  364. return $.extend({}, $.fn.transition.settings, {
  365. animation : animation,
  366. complete : complete,
  367. duration : duration
  368. });
  369. }
  370. // only duration provided
  371. else if(typeof duration == 'string' || typeof duration == 'number') {
  372. return $.extend({}, $.fn.transition.settings, {
  373. animation : animation,
  374. duration : duration
  375. });
  376. }
  377. // duration is actually settings object
  378. else if(typeof duration == 'object') {
  379. return $.extend({}, $.fn.transition.settings, duration, {
  380. animation : animation
  381. });
  382. }
  383. // duration is actually callback
  384. else if(typeof duration == 'function') {
  385. return $.extend({}, $.fn.transition.settings, {
  386. animation : animation,
  387. complete : duration
  388. });
  389. }
  390. // only animation provided
  391. else {
  392. return $.extend({}, $.fn.transition.settings, {
  393. animation : animation
  394. });
  395. }
  396. return $.fn.transition.settings;
  397. },
  398. displayType: function() {
  399. if(module.displayType === undefined) {
  400. // create fake element to determine display state
  401. module.can.transition();
  402. }
  403. return module.displayType;
  404. },
  405. transitionExists: function(animation) {
  406. return $.fn.transition.exists[animation];
  407. },
  408. animationName: function() {
  409. var
  410. element = document.createElement('div'),
  411. animations = {
  412. 'animation' :'animationName',
  413. 'OAnimation' :'oAnimationName',
  414. 'MozAnimation' :'mozAnimationName',
  415. 'WebkitAnimation' :'webkitAnimationName'
  416. },
  417. animation
  418. ;
  419. for(animation in animations){
  420. if( element.style[animation] !== undefined ){
  421. return animations[animation];
  422. }
  423. }
  424. return false;
  425. },
  426. animationEvent: function() {
  427. var
  428. element = document.createElement('div'),
  429. animations = {
  430. 'animation' :'animationend',
  431. 'OAnimation' :'oAnimationEnd',
  432. 'MozAnimation' :'mozAnimationEnd',
  433. 'WebkitAnimation' :'webkitAnimationEnd'
  434. },
  435. animation
  436. ;
  437. for(animation in animations){
  438. if( element.style[animation] !== undefined ){
  439. return animations[animation];
  440. }
  441. }
  442. return false;
  443. }
  444. },
  445. can: {
  446. animate: function() {
  447. if($module.css(settings.animation) !== 'none') {
  448. module.debug('CSS definition found', $module.css(settings.animation));
  449. return true;
  450. }
  451. else {
  452. module.debug('Unable to find css definition', $module.attr('class'));
  453. return false;
  454. }
  455. },
  456. transition: function() {
  457. var
  458. elementClass = $module.attr('class'),
  459. tagName = $module.prop('tagName'),
  460. animation = settings.animation,
  461. transitionExists = module.get.transitionExists(settings.animation),
  462. $clone,
  463. currentAnimation,
  464. inAnimation,
  465. displayType
  466. ;
  467. if( transitionExists === undefined || module.displayType === undefined) {
  468. module.verbose('Determining whether animation exists');
  469. $clone = $('<' + tagName + ' />').addClass( elementClass ).insertAfter($module);
  470. currentAnimation = $clone
  471. .removeClass(className.inward)
  472. .removeClass(className.outward)
  473. .addClass(className.animating)
  474. .addClass(className.transition)
  475. .addClass(animation)
  476. .css(animationName)
  477. ;
  478. inAnimation = $clone
  479. .addClass(className.inward)
  480. .css(animationName)
  481. ;
  482. displayType = $clone
  483. .attr('class', elementClass)
  484. .removeAttr('style')
  485. .removeClass(className.visible)
  486. .show()
  487. .css('display')
  488. ;
  489. module.verbose('Determining final display state', displayType);
  490. if(currentAnimation != inAnimation) {
  491. module.debug('Transition exists for animation', animation);
  492. transitionExists = true;
  493. }
  494. else {
  495. module.debug('Static animation found', animation, displayType);
  496. transitionExists = false;
  497. }
  498. $clone.remove();
  499. module.save.displayType(displayType);
  500. if(transitionExists === undefined) {
  501. module.save.transitionExists(animation, transitionExists);
  502. }
  503. }
  504. return transitionExists;
  505. }
  506. },
  507. is: {
  508. animating: function() {
  509. return $module.hasClass(className.animating);
  510. },
  511. inward: function() {
  512. return $module.hasClass(className.inward);
  513. },
  514. outward: function() {
  515. return $module.hasClass(className.outward);
  516. },
  517. looping: function() {
  518. return $module.hasClass(className.looping);
  519. },
  520. occuring: function(animation) {
  521. animation = animation || settings.animation;
  522. return ( $module.hasClass(animation) );
  523. },
  524. visible: function() {
  525. return $module.is(':visible');
  526. },
  527. hidden: function() {
  528. return $module.css('visibility') === 'hidden';
  529. },
  530. supported: function() {
  531. return(animationName !== false && animationEnd !== false);
  532. }
  533. },
  534. hide: function() {
  535. module.verbose('Hiding element');
  536. module.remove.visible();
  537. module.set.hidden();
  538. module.repaint();
  539. },
  540. show: function(display) {
  541. module.verbose('Showing element', display);
  542. module.remove.hidden();
  543. module.set.visible();
  544. module.repaint();
  545. },
  546. start: function() {
  547. module.verbose('Starting animation');
  548. $module.removeClass(className.disabled);
  549. },
  550. stop: function() {
  551. module.debug('Stopping animation');
  552. $module.addClass(className.disabled);
  553. },
  554. toggle: function() {
  555. module.debug('Toggling play status');
  556. $module.toggleClass(className.disabled);
  557. },
  558. setting: function(name, value) {
  559. module.debug('Changing setting', name, value);
  560. if( $.isPlainObject(name) ) {
  561. $.extend(true, settings, name);
  562. }
  563. else if(value !== undefined) {
  564. settings[name] = value;
  565. }
  566. else {
  567. return settings[name];
  568. }
  569. },
  570. internal: function(name, value) {
  571. if( $.isPlainObject(name) ) {
  572. $.extend(true, module, name);
  573. }
  574. else if(value !== undefined) {
  575. module[name] = value;
  576. }
  577. else {
  578. return module[name];
  579. }
  580. },
  581. debug: function() {
  582. if(settings.debug) {
  583. if(settings.performance) {
  584. module.performance.log(arguments);
  585. }
  586. else {
  587. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  588. module.debug.apply(console, arguments);
  589. }
  590. }
  591. },
  592. verbose: function() {
  593. if(settings.verbose && settings.debug) {
  594. if(settings.performance) {
  595. module.performance.log(arguments);
  596. }
  597. else {
  598. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  599. module.verbose.apply(console, arguments);
  600. }
  601. }
  602. },
  603. error: function() {
  604. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  605. module.error.apply(console, arguments);
  606. },
  607. performance: {
  608. log: function(message) {
  609. var
  610. currentTime,
  611. executionTime,
  612. previousTime
  613. ;
  614. if(settings.performance) {
  615. currentTime = new Date().getTime();
  616. previousTime = time || currentTime;
  617. executionTime = currentTime - previousTime;
  618. time = currentTime;
  619. performance.push({
  620. 'Name' : message[0],
  621. 'Arguments' : [].slice.call(message, 1) || '',
  622. 'Element' : element,
  623. 'Execution Time' : executionTime
  624. });
  625. }
  626. clearTimeout(module.performance.timer);
  627. module.performance.timer = setTimeout(module.performance.display, 600);
  628. },
  629. display: function() {
  630. var
  631. title = settings.name + ':',
  632. totalTime = 0
  633. ;
  634. time = false;
  635. clearTimeout(module.performance.timer);
  636. $.each(performance, function(index, data) {
  637. totalTime += data['Execution Time'];
  638. });
  639. title += ' ' + totalTime + 'ms';
  640. if(moduleSelector) {
  641. title += ' \'' + moduleSelector + '\'';
  642. }
  643. if($allModules.size() > 1) {
  644. title += ' ' + '(' + $allModules.size() + ')';
  645. }
  646. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  647. console.groupCollapsed(title);
  648. if(console.table) {
  649. console.table(performance);
  650. }
  651. else {
  652. $.each(performance, function(index, data) {
  653. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  654. });
  655. }
  656. console.groupEnd();
  657. }
  658. performance = [];
  659. }
  660. },
  661. // modified for transition to return invoke success
  662. invoke: function(query, passedArguments, context) {
  663. var
  664. object = instance,
  665. maxDepth,
  666. found,
  667. response
  668. ;
  669. passedArguments = passedArguments || queryArguments;
  670. context = element || context;
  671. if(typeof query == 'string' && object !== undefined) {
  672. query = query.split(/[\. ]/);
  673. maxDepth = query.length - 1;
  674. $.each(query, function(depth, value) {
  675. var camelCaseValue = (depth != maxDepth)
  676. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  677. : query
  678. ;
  679. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  680. object = object[camelCaseValue];
  681. }
  682. else if( object[camelCaseValue] !== undefined ) {
  683. found = object[camelCaseValue];
  684. return false;
  685. }
  686. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  687. object = object[value];
  688. }
  689. else if( object[value] !== undefined ) {
  690. found = object[value];
  691. return false;
  692. }
  693. else {
  694. return false;
  695. }
  696. });
  697. }
  698. if ( $.isFunction( found ) ) {
  699. response = found.apply(context, passedArguments);
  700. }
  701. else if(found !== undefined) {
  702. response = found;
  703. }
  704. if($.isArray(returnedValue)) {
  705. returnedValue.push(response);
  706. }
  707. else if(returnedValue !== undefined) {
  708. returnedValue = [returnedValue, response];
  709. }
  710. else if(response !== undefined) {
  711. returnedValue = response;
  712. }
  713. return (found !== undefined)
  714. ? found
  715. : false
  716. ;
  717. }
  718. };
  719. module.initialize();
  720. })
  721. ;
  722. return (returnedValue !== undefined)
  723. ? returnedValue
  724. : this
  725. ;
  726. };
  727. // Records if CSS transition is available
  728. $.fn.transition.exists = {};
  729. $.fn.transition.settings = {
  730. // module info
  731. name : 'Transition',
  732. // debug content outputted to console
  733. debug : false,
  734. // verbose debug output
  735. verbose : true,
  736. // performance data output
  737. performance : true,
  738. // event namespace
  739. namespace : 'transition',
  740. // animation complete event
  741. complete : function() {},
  742. onShow : function() {},
  743. onHide : function() {},
  744. // whether EXACT animation can occur twice in a row
  745. allowRepeats : false,
  746. // animation duration
  747. animation : 'fade',
  748. duration : '500ms',
  749. // new animations will occur after previous ones
  750. queue : true,
  751. className : {
  752. animating : 'animating',
  753. disabled : 'disabled',
  754. hidden : 'hidden',
  755. inward : 'in',
  756. loading : 'loading',
  757. looping : 'looping',
  758. outward : 'out',
  759. transition : 'transition',
  760. visible : 'visible'
  761. },
  762. // possible errors
  763. error: {
  764. noAnimation : 'There is no css animation matching the one you specified.',
  765. repeated : 'That animation is already occurring, cancelling repeated animation',
  766. method : 'The method you called is not defined',
  767. support : 'This browser does not support CSS animations'
  768. }
  769. };
  770. })( jQuery, window , document );