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.

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