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.

654 lines
20 KiB

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. 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. $.proxy(settings.onHide, this)();
  130. }
  131. else if($module.hasClass(className.inward)) {
  132. module.restore.conditions();
  133. module.show();
  134. $.proxy(settings.onShow, this)();
  135. }
  136. else {
  137. module.restore.conditions();
  138. }
  139. module.remove.animating();
  140. }
  141. $.proxy(settings.complete, this)();
  142. },
  143. repaint: function(fakeAssignment) {
  144. module.verbose('Forcing repaint event');
  145. fakeAssignment = element.offsetWidth;
  146. },
  147. has: {
  148. direction: function(animation) {
  149. animation = animation || settings.animation;
  150. if( $module.hasClass(className.inward) || $module.hasClass(className.outward) ) {
  151. return true;
  152. }
  153. }
  154. },
  155. set: {
  156. animating: function() {
  157. $module.addClass(className.animating);
  158. },
  159. direction: function() {
  160. if($module.is(':visible')) {
  161. module.debug('Automatically determining the direction of animation', 'Outward');
  162. $module
  163. .addClass(className.outward)
  164. .removeClass(className.inward)
  165. ;
  166. }
  167. else {
  168. module.debug('Automatically determining the direction of animation', 'Inward');
  169. $module
  170. .addClass(className.inward)
  171. .removeClass(className.outward)
  172. ;
  173. }
  174. },
  175. looping: function() {
  176. module.debug('Transition set to loop');
  177. $module
  178. .addClass(className.looping)
  179. ;
  180. },
  181. duration: function(duration) {
  182. duration = duration || settings.duration;
  183. duration = (typeof duration == 'number')
  184. ? duration + 'ms'
  185. : duration
  186. ;
  187. module.verbose('Setting animation duration', duration);
  188. $module
  189. .css({
  190. '-webkit-animation-duration': duration,
  191. '-moz-animation-duration': duration,
  192. '-ms-animation-duration': duration,
  193. '-o-animation-duration': duration,
  194. 'animation-duration': duration
  195. })
  196. ;
  197. }
  198. },
  199. save: {
  200. conditions: function() {
  201. module.cache = {
  202. className : $module.attr('class'),
  203. style : $module.attr('style')
  204. };
  205. module.verbose('Saving original attributes', module.cache);
  206. }
  207. },
  208. restore: {
  209. conditions: function() {
  210. if(typeof module.cache === undefined) {
  211. module.error(error.cache);
  212. return false;
  213. }
  214. if(module.cache.className) {
  215. $module.attr('class', module.cache.className);
  216. }
  217. else {
  218. $module.removeAttr('class');
  219. }
  220. if(module.cache.style) {
  221. $module.attr('style', module.cache.style);
  222. }
  223. else {
  224. $module.removeAttr('style');
  225. }
  226. if(module.is.looping()) {
  227. module.remove.looping();
  228. }
  229. module.verbose('Restoring original attributes', module.cache);
  230. }
  231. },
  232. remove: {
  233. animating: function() {
  234. $module.removeClass(className.animating);
  235. },
  236. looping: function() {
  237. module.debug('Transitions are no longer looping');
  238. $module
  239. .removeClass(className.looping)
  240. ;
  241. module.repaint();
  242. }
  243. },
  244. get: {
  245. settings: function(animation, duration, complete) {
  246. // single settings object
  247. if($.isPlainObject(animation)) {
  248. return $.extend(true, {}, $.fn.transition.settings, animation);
  249. }
  250. // all arguments provided
  251. else if(typeof complete == 'function') {
  252. return $.extend(true, {}, $.fn.transition.settings, {
  253. animation : animation,
  254. complete : complete,
  255. duration : duration
  256. });
  257. }
  258. // only duration provided
  259. else if(typeof duration == 'string' || typeof duration == 'number') {
  260. return $.extend(true, {}, $.fn.transition.settings, {
  261. animation : animation,
  262. duration : duration
  263. });
  264. }
  265. // duration is actually settings object
  266. else if(typeof duration == 'object') {
  267. return $.extend(true, {}, $.fn.transition.settings, duration, {
  268. animation : animation
  269. });
  270. }
  271. // duration is actually callback
  272. else if(typeof duration == 'function') {
  273. return $.extend(true, {}, $.fn.transition.settings, {
  274. animation : animation,
  275. complete : duration
  276. });
  277. }
  278. // only animation provided
  279. else {
  280. return $.extend(true, {}, $.fn.transition.settings, {
  281. animation : animation
  282. });
  283. }
  284. return $.extend({}, $.fn.transition.settings);
  285. },
  286. animationName: function() {
  287. var
  288. element = document.createElement('div'),
  289. animations = {
  290. 'animation' :'animationName',
  291. 'OAnimation' :'oAnimationName',
  292. 'MozAnimation' :'mozAnimationName',
  293. 'WebkitAnimation' :'webkitAnimationName'
  294. },
  295. animation
  296. ;
  297. for(animation in animations){
  298. if( element.style[animation] !== undefined ){
  299. module.verbose('Determining animation vendor name property', animations[animation]);
  300. return animations[animation];
  301. }
  302. }
  303. return false;
  304. },
  305. animationEvent: function() {
  306. var
  307. element = document.createElement('div'),
  308. animations = {
  309. 'animation' :'animationend',
  310. 'OAnimation' :'oAnimationEnd',
  311. 'MozAnimation' :'mozAnimationEnd',
  312. 'WebkitAnimation' :'webkitAnimationEnd'
  313. },
  314. animation
  315. ;
  316. for(animation in animations){
  317. if( element.style[animation] !== undefined ){
  318. module.verbose('Determining animation vendor end event', animations[animation]);
  319. return animations[animation];
  320. }
  321. }
  322. return false;
  323. }
  324. },
  325. can: {
  326. animate: function() {
  327. if($module.css(animationName) !== 'none') {
  328. module.debug('CSS definition found');
  329. return true;
  330. }
  331. else {
  332. module.debug('Unable to find css definition');
  333. return false;
  334. }
  335. },
  336. transition: function() {
  337. var
  338. $clone = $('<div>').addClass( $module.attr('class') ).appendTo($('body')),
  339. currentAnimation = $clone.css(animationName),
  340. inAnimation = $clone.addClass(className.inward).css(animationName)
  341. ;
  342. if(currentAnimation != inAnimation) {
  343. module.debug('In/out transitions exist');
  344. $clone.remove();
  345. return true;
  346. }
  347. else {
  348. module.debug('Static animation found');
  349. $clone.remove();
  350. return false;
  351. }
  352. }
  353. },
  354. is: {
  355. animating: function() {
  356. return $module.hasClass(className.animating);
  357. },
  358. looping: function() {
  359. return $module.hasClass(className.looping);
  360. },
  361. visible: function() {
  362. return $module.is(':visible');
  363. }
  364. },
  365. hide: function() {
  366. module.verbose('Hiding element');
  367. $module
  368. .removeClass(className.visible)
  369. .addClass(className.transition)
  370. .addClass(className.hidden)
  371. ;
  372. module.repaint();
  373. },
  374. show: function() {
  375. module.verbose('Showing element');
  376. $module
  377. .removeClass(className.hidden)
  378. .addClass(className.transition)
  379. .addClass(className.visible)
  380. ;
  381. module.repaint();
  382. },
  383. start: function() {
  384. module.verbose('Starting animation');
  385. $module.removeClass(className.disabled);
  386. },
  387. stop: function() {
  388. module.debug('Stopping animation');
  389. $module.addClass(className.disabled);
  390. },
  391. toggle: function() {
  392. module.debug('Toggling play status');
  393. $module.toggleClass(className.disabled);
  394. },
  395. setting: function(name, value) {
  396. if( $.isPlainObject(name) ) {
  397. $.extend(true, settings, name);
  398. }
  399. else if(value !== undefined) {
  400. settings[name] = value;
  401. }
  402. else {
  403. return settings[name];
  404. }
  405. },
  406. internal: function(name, value) {
  407. if( $.isPlainObject(name) ) {
  408. $.extend(true, module, name);
  409. }
  410. else if(value !== undefined) {
  411. module[name] = value;
  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(returnedValue)) {
  539. returnedValue.push(response);
  540. }
  541. else if(returnedValue !== undefined) {
  542. returnedValue = [returnedValue, response];
  543. }
  544. else if(response !== undefined) {
  545. returnedValue = response;
  546. }
  547. return found || false;
  548. }
  549. };
  550. module.initialize();
  551. })
  552. ;
  553. return (returnedValue !== undefined)
  554. ? returnedValue
  555. : this
  556. ;
  557. };
  558. $.fn.transition.settings = {
  559. // module info
  560. name : 'Transition',
  561. // debug content outputted to console
  562. debug : false,
  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. onShow : function() {},
  572. onHide : function() {},
  573. // animation duration
  574. animation : 'fade',
  575. duration : '700ms',
  576. // new animations will occur after previous ones
  577. queue : true,
  578. className : {
  579. animating : 'animating',
  580. disabled : 'disabled',
  581. hidden : 'hidden',
  582. inward : 'in',
  583. loading : 'loading',
  584. looping : 'looping',
  585. outward : 'out',
  586. transition : 'ui transition',
  587. visible : 'visible'
  588. },
  589. // possible errors
  590. error: {
  591. noAnimation : 'There is no css animation matching the one you specified.',
  592. method : 'The method you called is not defined'
  593. }
  594. };
  595. })( jQuery, window , document );