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.

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