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.

674 lines
20 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);
  60. if(instance === undefined) {
  61. module.instantiate();
  62. }
  63. if(methodInvoked) {
  64. methodInvoked = module.invoke(query);
  65. }
  66. // no internal method was found matching query or query not made
  67. if(methodInvoked === false) {
  68. module.animate();
  69. }
  70. },
  71. instantiate: function() {
  72. module.verbose('Storing instance of module', module);
  73. instance = module;
  74. $module
  75. .data(moduleNamespace, instance)
  76. ;
  77. },
  78. destroy: function() {
  79. module.verbose('Destroying previous module for', element);
  80. $module
  81. .removeData(moduleNamespace)
  82. ;
  83. },
  84. animate: function(overrideSettings) {
  85. settings = overrideSettings || settings;
  86. if(!module.is.supported()) {
  87. module.error(error.support);
  88. return false;
  89. }
  90. module.debug('Preparing animation', settings.animation);
  91. if(module.is.animating()) {
  92. if(settings.queue) {
  93. module.queue(settings.animation);
  94. }
  95. return false;
  96. }
  97. module.save.conditions();
  98. module.set.duration(settings.duration);
  99. module.set.animating();
  100. module.repaint();
  101. $module
  102. .addClass(className.transition)
  103. .addClass(settings.animation)
  104. .one(animationEnd, module.complete)
  105. ;
  106. if(!module.has.direction() && module.can.transition()) {
  107. module.set.direction();
  108. }
  109. if( !module.has.transitionAvailable() ) {
  110. module.restore.conditions();
  111. module.error(error.noAnimation, settings.animation);
  112. return false;
  113. }
  114. module.show();
  115. module.debug('Starting tween', settings.animation, $module.attr('class'));
  116. },
  117. queue: function(animation) {
  118. module.debug('Queueing animation of', animation);
  119. instance.queuing = true;
  120. $module
  121. .one(animationEnd, function() {
  122. instance.queuing = false;
  123. module.animate.apply(this, settings);
  124. })
  125. ;
  126. },
  127. complete: function () {
  128. module.verbose('CSS animation complete', settings.animation);
  129. if(!module.is.looping()) {
  130. if($module.hasClass(className.outward)) {
  131. module.restore.conditions();
  132. module.hide();
  133. $.proxy(settings.onHide, this)();
  134. }
  135. else if($module.hasClass(className.inward)) {
  136. module.restore.conditions();
  137. module.show();
  138. $.proxy(settings.onShow, this)();
  139. }
  140. else {
  141. module.restore.conditions();
  142. }
  143. module.remove.animating();
  144. }
  145. $.proxy(settings.complete, this)();
  146. },
  147. forceRepaint: function() {
  148. module.verbose('Forcing element repaint');
  149. var
  150. $parentElement = $module.parent(),
  151. $nextElement = $module.next()
  152. ;
  153. if($nextElement.size() === 0) {
  154. $module.detach().appendTo($parentElement);
  155. }
  156. else {
  157. $module.detach().insertBefore($nextElement);
  158. }
  159. },
  160. repaint: function() {
  161. module.verbose('Repainting element');
  162. var
  163. fakeAssignment = element.offsetWidth
  164. ;
  165. },
  166. has: {
  167. direction: function(animation) {
  168. animation = animation || settings.animation;
  169. if( $module.hasClass(className.inward) || $module.hasClass(className.outward) ) {
  170. return true;
  171. }
  172. },
  173. transitionAvailable: function() {
  174. if($module.css(animationName) !== 'none') {
  175. module.debug('CSS definition found');
  176. return true;
  177. }
  178. else {
  179. module.debug('Unable to find css definition');
  180. return false;
  181. }
  182. }
  183. },
  184. set: {
  185. animating: function() {
  186. $module.addClass(className.animating);
  187. },
  188. direction: function() {
  189. if($module.is(':visible')) {
  190. module.debug('Automatically determining the direction of animation', 'Outward');
  191. $module
  192. .addClass(className.outward)
  193. .removeClass(className.inward)
  194. ;
  195. }
  196. else {
  197. module.debug('Automatically determining the direction of animation', 'Inward');
  198. $module
  199. .addClass(className.inward)
  200. .removeClass(className.outward)
  201. ;
  202. }
  203. },
  204. looping: function() {
  205. module.debug('Transition set to loop');
  206. $module
  207. .addClass(className.looping)
  208. ;
  209. },
  210. duration: function(duration) {
  211. duration = duration || settings.duration;
  212. duration = (typeof duration == 'number')
  213. ? duration + 'ms'
  214. : duration
  215. ;
  216. module.verbose('Setting animation duration', duration);
  217. $module
  218. .css({
  219. '-webkit-animation-duration': duration,
  220. '-moz-animation-duration': duration,
  221. '-ms-animation-duration': duration,
  222. '-o-animation-duration': duration,
  223. 'animation-duration': duration
  224. })
  225. ;
  226. }
  227. },
  228. save: {
  229. conditions: function() {
  230. module.cache = {
  231. className : $module.attr('class'),
  232. style : $module.attr('style')
  233. };
  234. module.verbose('Saving original attributes', module.cache);
  235. }
  236. },
  237. restore: {
  238. conditions: function() {
  239. if(typeof module.cache === undefined) {
  240. module.error(error.cache);
  241. return false;
  242. }
  243. if(module.cache.className) {
  244. $module.attr('class', module.cache.className);
  245. }
  246. else {
  247. $module.removeAttr('class');
  248. }
  249. if(module.cache.style) {
  250. $module.attr('style', module.cache.style);
  251. }
  252. else {
  253. $module.removeAttr('style');
  254. }
  255. if(module.is.looping()) {
  256. module.remove.looping();
  257. }
  258. module.verbose('Restoring original attributes', module.cache);
  259. }
  260. },
  261. remove: {
  262. animating: function() {
  263. $module.removeClass(className.animating);
  264. },
  265. looping: function() {
  266. module.debug('Transitions are no longer looping');
  267. $module
  268. .removeClass(className.looping)
  269. ;
  270. module.forceRepaint();
  271. }
  272. },
  273. get: {
  274. settings: function(animation, duration, complete) {
  275. // single settings object
  276. if($.isPlainObject(animation)) {
  277. return $.extend(true, {}, $.fn.transition.settings, animation);
  278. }
  279. // all arguments provided
  280. else if(typeof complete == 'function') {
  281. return $.extend(true, {}, $.fn.transition.settings, {
  282. animation : animation,
  283. complete : complete,
  284. duration : duration
  285. });
  286. }
  287. // only duration provided
  288. else if(typeof duration == 'string' || typeof duration == 'number') {
  289. return $.extend(true, {}, $.fn.transition.settings, {
  290. animation : animation,
  291. duration : duration
  292. });
  293. }
  294. // duration is actually settings object
  295. else if(typeof duration == 'object') {
  296. return $.extend(true, {}, $.fn.transition.settings, duration, {
  297. animation : animation
  298. });
  299. }
  300. // duration is actually callback
  301. else if(typeof duration == 'function') {
  302. return $.extend(true, {}, $.fn.transition.settings, {
  303. animation : animation,
  304. complete : duration
  305. });
  306. }
  307. // only animation provided
  308. else {
  309. return $.extend(true, {}, $.fn.transition.settings, {
  310. animation : animation
  311. });
  312. }
  313. return $.extend({}, $.fn.transition.settings);
  314. },
  315. animationName: function() {
  316. var
  317. element = document.createElement('div'),
  318. animations = {
  319. 'animation' :'animationName',
  320. 'OAnimation' :'oAnimationName',
  321. 'MozAnimation' :'mozAnimationName',
  322. 'WebkitAnimation' :'webkitAnimationName'
  323. },
  324. animation
  325. ;
  326. for(animation in animations){
  327. if( element.style[animation] !== undefined ){
  328. module.verbose('Determined animation vendor name property', animations[animation]);
  329. return animations[animation];
  330. }
  331. }
  332. return false;
  333. },
  334. animationEvent: function() {
  335. var
  336. element = document.createElement('div'),
  337. animations = {
  338. 'animation' :'animationend',
  339. 'OAnimation' :'oAnimationEnd',
  340. 'MozAnimation' :'mozAnimationEnd',
  341. 'WebkitAnimation' :'webkitAnimationEnd'
  342. },
  343. animation
  344. ;
  345. for(animation in animations){
  346. if( element.style[animation] !== undefined ){
  347. module.verbose('Determined animation vendor end event', animations[animation]);
  348. return animations[animation];
  349. }
  350. }
  351. return false;
  352. }
  353. },
  354. can: {
  355. transition: function() {
  356. var
  357. $clone = $('<div>').addClass( $module.attr('class') ).appendTo($('body')),
  358. currentAnimation = $clone.css(animationName),
  359. inAnimation = $clone.addClass(className.inward).css(animationName)
  360. ;
  361. if(currentAnimation != inAnimation) {
  362. module.debug('In/out transitions exist');
  363. $clone.remove();
  364. return true;
  365. }
  366. else {
  367. module.debug('Static animation found');
  368. $clone.remove();
  369. return false;
  370. }
  371. }
  372. },
  373. is: {
  374. animating: function() {
  375. return $module.hasClass(className.animating);
  376. },
  377. looping: function() {
  378. return $module.hasClass(className.looping);
  379. },
  380. visible: function() {
  381. return $module.is(':visible');
  382. },
  383. supported: function() {
  384. return(animationName !== false && animationEnd !== false);
  385. }
  386. },
  387. hide: function() {
  388. module.verbose('Hiding element');
  389. $module
  390. .removeClass(className.visible)
  391. .addClass(className.transition)
  392. .addClass(className.hidden)
  393. ;
  394. module.repaint();
  395. },
  396. show: function() {
  397. module.verbose('Showing element');
  398. $module
  399. .removeClass(className.hidden)
  400. .addClass(className.transition)
  401. .addClass(className.visible)
  402. ;
  403. module.repaint();
  404. },
  405. start: function() {
  406. module.verbose('Starting animation');
  407. $module.removeClass(className.disabled);
  408. },
  409. stop: function() {
  410. module.debug('Stopping animation');
  411. $module.addClass(className.disabled);
  412. },
  413. toggle: function() {
  414. module.debug('Toggling play status');
  415. $module.toggleClass(className.disabled);
  416. },
  417. setting: function(name, value) {
  418. if( $.isPlainObject(name) ) {
  419. $.extend(true, settings, name);
  420. }
  421. else if(value !== undefined) {
  422. settings[name] = value;
  423. }
  424. else {
  425. return settings[name];
  426. }
  427. },
  428. internal: function(name, value) {
  429. if( $.isPlainObject(name) ) {
  430. $.extend(true, module, name);
  431. }
  432. else if(value !== undefined) {
  433. module[name] = value;
  434. }
  435. else {
  436. return module[name];
  437. }
  438. },
  439. debug: function() {
  440. if(settings.debug) {
  441. if(settings.performance) {
  442. module.performance.log(arguments);
  443. }
  444. else {
  445. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  446. module.debug.apply(console, arguments);
  447. }
  448. }
  449. },
  450. verbose: function() {
  451. if(settings.verbose && settings.debug) {
  452. if(settings.performance) {
  453. module.performance.log(arguments);
  454. }
  455. else {
  456. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  457. module.verbose.apply(console, arguments);
  458. }
  459. }
  460. },
  461. error: function() {
  462. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  463. module.error.apply(console, arguments);
  464. },
  465. performance: {
  466. log: function(message) {
  467. var
  468. currentTime,
  469. executionTime,
  470. previousTime
  471. ;
  472. if(settings.performance) {
  473. currentTime = new Date().getTime();
  474. previousTime = time || currentTime;
  475. executionTime = currentTime - previousTime;
  476. time = currentTime;
  477. performance.push({
  478. 'Element' : element,
  479. 'Name' : message[0],
  480. 'Arguments' : [].slice.call(message, 1) || '',
  481. 'Execution Time' : executionTime
  482. });
  483. }
  484. clearTimeout(module.performance.timer);
  485. module.performance.timer = setTimeout(module.performance.display, 100);
  486. },
  487. display: function() {
  488. var
  489. title = settings.name + ':',
  490. totalTime = 0
  491. ;
  492. time = false;
  493. clearTimeout(module.performance.timer);
  494. $.each(performance, function(index, data) {
  495. totalTime += data['Execution Time'];
  496. });
  497. title += ' ' + totalTime + 'ms';
  498. if(moduleSelector) {
  499. title += ' \'' + moduleSelector + '\'';
  500. }
  501. if($allModules.size() > 1) {
  502. title += ' ' + '(' + $allModules.size() + ')';
  503. }
  504. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  505. console.groupCollapsed(title);
  506. if(console.table) {
  507. console.table(performance);
  508. }
  509. else {
  510. $.each(performance, function(index, data) {
  511. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  512. });
  513. }
  514. console.groupEnd();
  515. }
  516. performance = [];
  517. }
  518. },
  519. invoke: function(query, passedArguments, context) {
  520. var
  521. maxDepth,
  522. found,
  523. response
  524. ;
  525. passedArguments = passedArguments || queryArguments;
  526. context = element || context;
  527. if(typeof query == 'string' && instance !== undefined) {
  528. query = query.split(/[\. ]/);
  529. maxDepth = query.length - 1;
  530. $.each(query, function(depth, value) {
  531. var camelCaseValue = (depth != maxDepth)
  532. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  533. : query
  534. ;
  535. if( $.isPlainObject( instance[camelCaseValue] ) && (depth != maxDepth) ) {
  536. instance = instance[camelCaseValue];
  537. }
  538. else if( instance[camelCaseValue] !== undefined ) {
  539. found = instance[camelCaseValue];
  540. return false;
  541. }
  542. else if( $.isPlainObject( instance[value] ) && (depth != maxDepth) ) {
  543. instance = instance[value];
  544. }
  545. else if( instance[value] !== undefined ) {
  546. found = instance[value];
  547. return false;
  548. }
  549. else {
  550. return false;
  551. }
  552. });
  553. }
  554. if ( $.isFunction( found ) ) {
  555. response = found.apply(context, passedArguments);
  556. }
  557. else if(found !== undefined) {
  558. response = found;
  559. }
  560. if($.isArray(returnedValue)) {
  561. returnedValue.push(response);
  562. }
  563. else if(returnedValue !== undefined) {
  564. returnedValue = [returnedValue, response];
  565. }
  566. else if(response !== undefined) {
  567. returnedValue = response;
  568. }
  569. return found || false;
  570. }
  571. };
  572. module.initialize();
  573. })
  574. ;
  575. return (returnedValue !== undefined)
  576. ? returnedValue
  577. : this
  578. ;
  579. };
  580. $.fn.transition.settings = {
  581. // module info
  582. name : 'Transition',
  583. // debug content outputted to console
  584. debug : false,
  585. // verbose debug output
  586. verbose : true,
  587. // performance data output
  588. performance : true,
  589. // event namespace
  590. namespace : 'transition',
  591. // animation complete event
  592. complete : function() {},
  593. onShow : function() {},
  594. onHide : function() {},
  595. // animation duration
  596. animation : 'fade',
  597. duration : '700ms',
  598. // new animations will occur after previous ones
  599. queue : true,
  600. className : {
  601. animating : 'animating',
  602. disabled : 'disabled',
  603. hidden : 'hidden',
  604. inward : 'in',
  605. loading : 'loading',
  606. looping : 'looping',
  607. outward : 'out',
  608. transition : 'ui transition',
  609. visible : 'visible'
  610. },
  611. // possible errors
  612. error: {
  613. noAnimation : 'There is no css animation matching the one you specified.',
  614. method : 'The method you called is not defined',
  615. support : 'This browser does not support CSS animations'
  616. }
  617. };
  618. })( jQuery, window , document );