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.

810 lines
25 KiB

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