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.

652 lines
19 KiB

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