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.

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