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.

829 lines
25 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /*
  2. * # Semantic - Shape
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Copyright 2014 Contributor
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ( $, window, document, undefined ) {
  12. "use strict";
  13. $.fn.shape = function(parameters) {
  14. var
  15. $allModules = $(this),
  16. $body = $('body'),
  17. time = new Date().getTime(),
  18. performance = [],
  19. query = arguments[0],
  20. methodInvoked = (typeof query == 'string'),
  21. queryArguments = [].slice.call(arguments, 1),
  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. moduleSelector = $allModules.selector || '',
  33. settings = $.extend(true, {}, $.fn.shape.settings, parameters),
  34. // internal aliases
  35. namespace = settings.namespace,
  36. selector = settings.selector,
  37. error = settings.error,
  38. className = settings.className,
  39. // define namespaces for modules
  40. eventNamespace = '.' + namespace,
  41. moduleNamespace = 'module-' + namespace,
  42. // selector cache
  43. $module = $(this),
  44. $sides = $module.find(selector.sides),
  45. $side = $module.find(selector.side),
  46. // private variables
  47. nextIndex = false,
  48. $activeSide,
  49. $nextSide,
  50. // standard module
  51. element = this,
  52. instance = $module.data(moduleNamespace),
  53. module
  54. ;
  55. module = {
  56. initialize: function() {
  57. module.verbose('Initializing module for', element);
  58. module.set.defaultSide();
  59. module.instantiate();
  60. },
  61. instantiate: function() {
  62. module.verbose('Storing instance of module', module);
  63. instance = module;
  64. $module
  65. .data(moduleNamespace, instance)
  66. ;
  67. },
  68. destroy: function() {
  69. module.verbose('Destroying previous module for', element);
  70. $module
  71. .removeData(moduleNamespace)
  72. .off(eventNamespace)
  73. ;
  74. },
  75. refresh: function() {
  76. module.verbose('Refreshing selector cache for', element);
  77. $module = $(element);
  78. $sides = $(this).find(selector.shape);
  79. $side = $(this).find(selector.side);
  80. },
  81. repaint: function() {
  82. module.verbose('Forcing repaint event');
  83. var
  84. shape = $sides.get(0) || document.createElement('div'),
  85. fakeAssignment = shape.offsetWidth
  86. ;
  87. },
  88. animate: function(propertyObject, callback) {
  89. module.verbose('Animating box with properties', propertyObject);
  90. callback = callback || function(event) {
  91. module.verbose('Executing animation callback');
  92. if(event !== undefined) {
  93. event.stopPropagation();
  94. }
  95. module.reset();
  96. module.set.active();
  97. };
  98. $.proxy(settings.beforeChange, $nextSide[0])();
  99. if(module.get.transitionEvent()) {
  100. module.verbose('Starting CSS animation');
  101. $module
  102. .addClass(className.animating)
  103. ;
  104. $sides
  105. .css(propertyObject)
  106. .one(module.get.transitionEvent(), callback)
  107. ;
  108. module.set.duration(settings.duration);
  109. requestAnimationFrame(function() {
  110. $module
  111. .addClass(className.animating)
  112. ;
  113. $activeSide
  114. .addClass(className.hidden)
  115. ;
  116. });
  117. }
  118. else {
  119. callback();
  120. }
  121. },
  122. queue: function(method) {
  123. module.debug('Queueing animation of', method);
  124. $sides
  125. .one(module.get.transitionEvent(), function() {
  126. module.debug('Executing queued animation');
  127. setTimeout(function(){
  128. $module.shape(method);
  129. }, 0);
  130. })
  131. ;
  132. },
  133. reset: function() {
  134. module.verbose('Animating states reset');
  135. $module
  136. .removeClass(className.animating)
  137. .attr('style', '')
  138. .removeAttr('style')
  139. ;
  140. // removeAttr style does not consistently work in safari
  141. $sides
  142. .attr('style', '')
  143. .removeAttr('style')
  144. ;
  145. $side
  146. .attr('style', '')
  147. .removeAttr('style')
  148. .removeClass(className.hidden)
  149. ;
  150. $nextSide
  151. .removeClass(className.animating)
  152. .attr('style', '')
  153. .removeAttr('style')
  154. ;
  155. },
  156. is: {
  157. complete: function() {
  158. return ($side.filter('.' + className.active)[0] == $nextSide[0]);
  159. },
  160. animating: function() {
  161. return $module.hasClass(className.animating);
  162. }
  163. },
  164. set: {
  165. defaultSide: function() {
  166. $activeSide = $module.find('.' + settings.className.active);
  167. $nextSide = ( $activeSide.next(selector.side).size() > 0 )
  168. ? $activeSide.next(selector.side)
  169. : $module.find(selector.side).first()
  170. ;
  171. nextIndex = false;
  172. module.verbose('Active side set to', $activeSide);
  173. module.verbose('Next side set to', $nextSide);
  174. },
  175. duration: function(duration) {
  176. duration = duration || settings.duration;
  177. duration = (typeof duration == 'number')
  178. ? duration + 'ms'
  179. : duration
  180. ;
  181. module.verbose('Setting animation duration', duration);
  182. $sides.add($side)
  183. .css({
  184. '-webkit-transition-duration': duration,
  185. '-moz-transition-duration': duration,
  186. '-ms-transition-duration': duration,
  187. '-o-transition-duration': duration,
  188. 'transition-duration': duration
  189. })
  190. ;
  191. },
  192. stageSize: function() {
  193. var
  194. $clone = $module.clone().addClass(className.loading),
  195. $activeSide = $clone.find('.' + settings.className.active),
  196. $nextSide = (nextIndex)
  197. ? $clone.find(selector.side).eq(nextIndex)
  198. : ( $activeSide.next(selector.side).size() > 0 )
  199. ? $activeSide.next(selector.side)
  200. : $clone.find(selector.side).first(),
  201. newSize = {}
  202. ;
  203. $activeSide.removeClass(className.active);
  204. $nextSide.addClass(className.active);
  205. $clone.insertAfter($module);
  206. newSize = {
  207. width : $nextSide.outerWidth(),
  208. height : $nextSide.outerHeight()
  209. };
  210. $clone.remove();
  211. $module
  212. .css(newSize)
  213. ;
  214. module.verbose('Resizing stage to fit new content', newSize);
  215. },
  216. nextSide: function(selector) {
  217. nextIndex = selector;
  218. $nextSide = $side.filter(selector);
  219. nextIndex = $side.index($nextSide);
  220. if($nextSide.size() === 0) {
  221. module.set.defaultSide();
  222. module.error(error.side);
  223. }
  224. module.verbose('Next side manually set to', $nextSide);
  225. },
  226. active: function() {
  227. module.verbose('Setting new side to active', $nextSide);
  228. $side
  229. .removeClass(className.active)
  230. ;
  231. $nextSide
  232. .addClass(className.active)
  233. ;
  234. $.proxy(settings.onChange, $nextSide[0])();
  235. module.set.defaultSide();
  236. }
  237. },
  238. flip: {
  239. up: function() {
  240. if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) {
  241. console.log('Side already visible', $nextSide);
  242. return;
  243. }
  244. if( !module.is.animating()) {
  245. module.debug('Flipping up', $nextSide);
  246. module.set.stageSize();
  247. module.stage.above();
  248. module.animate( module.get.transform.up() );
  249. }
  250. else {
  251. module.queue('flip up');
  252. }
  253. },
  254. down: function() {
  255. if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) {
  256. console.log('Side already visible', $nextSide);
  257. return;
  258. }
  259. if( !module.is.animating()) {
  260. module.debug('Flipping down', $nextSide);
  261. module.set.stageSize();
  262. module.stage.below();
  263. module.animate( module.get.transform.down() );
  264. }
  265. else {
  266. module.queue('flip down');
  267. }
  268. },
  269. left: function() {
  270. if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) {
  271. console.log('Side already visible', $nextSide);
  272. return;
  273. }
  274. if( !module.is.animating()) {
  275. module.debug('Flipping left', $nextSide);
  276. module.set.stageSize();
  277. module.stage.left();
  278. module.animate(module.get.transform.left() );
  279. }
  280. else {
  281. module.queue('flip left');
  282. }
  283. },
  284. right: function() {
  285. if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) {
  286. console.log('Side already visible', $nextSide);
  287. return;
  288. }
  289. if( !module.is.animating()) {
  290. module.debug('Flipping right', $nextSide);
  291. module.set.stageSize();
  292. module.stage.right();
  293. module.animate(module.get.transform.right() );
  294. }
  295. else {
  296. module.queue('flip right');
  297. }
  298. },
  299. over: function() {
  300. if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) {
  301. console.log('Side already visible', $nextSide);
  302. return;
  303. }
  304. if( !module.is.animating()) {
  305. module.debug('Flipping over', $nextSide);
  306. module.set.stageSize();
  307. module.stage.behind();
  308. module.animate(module.get.transform.over() );
  309. }
  310. else {
  311. module.queue('flip over');
  312. }
  313. },
  314. back: function() {
  315. if(module.is.complete() && !module.is.animating() && !settings.allowRepeats) {
  316. console.log('Side already visible', $nextSide);
  317. return;
  318. }
  319. if( !module.is.animating()) {
  320. module.debug('Flipping back', $nextSide);
  321. module.set.stageSize();
  322. module.stage.behind();
  323. module.animate(module.get.transform.back() );
  324. }
  325. else {
  326. module.queue('flip back');
  327. }
  328. }
  329. },
  330. get: {
  331. transform: {
  332. up: function() {
  333. var
  334. translate = {
  335. y: -(($activeSide.outerHeight() - $nextSide.outerHeight()) / 2),
  336. z: -($activeSide.outerHeight() / 2)
  337. }
  338. ;
  339. return {
  340. transform: 'translateY(' + translate.y + 'px) translateZ('+ translate.z + 'px) rotateX(-90deg)'
  341. };
  342. },
  343. down: function() {
  344. var
  345. translate = {
  346. y: -(($activeSide.outerHeight() - $nextSide.outerHeight()) / 2),
  347. z: -($activeSide.outerHeight() / 2)
  348. }
  349. ;
  350. return {
  351. transform: 'translateY(' + translate.y + 'px) translateZ('+ translate.z + 'px) rotateX(90deg)'
  352. };
  353. },
  354. left: function() {
  355. var
  356. translate = {
  357. x : -(($activeSide.outerWidth() - $nextSide.outerWidth()) / 2),
  358. z : -($activeSide.outerWidth() / 2)
  359. }
  360. ;
  361. return {
  362. transform: 'translateX(' + translate.x + 'px) translateZ(' + translate.z + 'px) rotateY(90deg)'
  363. };
  364. },
  365. right: function() {
  366. var
  367. translate = {
  368. x : -(($activeSide.outerWidth() - $nextSide.outerWidth()) / 2),
  369. z : -($activeSide.outerWidth() / 2)
  370. }
  371. ;
  372. return {
  373. transform: 'translateX(' + translate.x + 'px) translateZ(' + translate.z + 'px) rotateY(-90deg)'
  374. };
  375. },
  376. over: function() {
  377. var
  378. translate = {
  379. x : -(($activeSide.outerWidth() - $nextSide.outerWidth()) / 2)
  380. }
  381. ;
  382. return {
  383. transform: 'translateX(' + translate.x + 'px) rotateY(180deg)'
  384. };
  385. },
  386. back: function() {
  387. var
  388. translate = {
  389. x : -(($activeSide.outerWidth() - $nextSide.outerWidth()) / 2)
  390. }
  391. ;
  392. return {
  393. transform: 'translateX(' + translate.x + 'px) rotateY(-180deg)'
  394. };
  395. }
  396. },
  397. transitionEvent: function() {
  398. var
  399. element = document.createElement('element'),
  400. transitions = {
  401. 'transition' :'transitionend',
  402. 'OTransition' :'oTransitionEnd',
  403. 'MozTransition' :'transitionend',
  404. 'WebkitTransition' :'webkitTransitionEnd'
  405. },
  406. transition
  407. ;
  408. for(transition in transitions){
  409. if( element.style[transition] !== undefined ){
  410. return transitions[transition];
  411. }
  412. }
  413. },
  414. nextSide: function() {
  415. return ( $activeSide.next(selector.side).size() > 0 )
  416. ? $activeSide.next(selector.side)
  417. : $module.find(selector.side).first()
  418. ;
  419. }
  420. },
  421. stage: {
  422. above: function() {
  423. var
  424. box = {
  425. origin : (($activeSide.outerHeight() - $nextSide.outerHeight()) / 2),
  426. depth : {
  427. active : ($nextSide.outerHeight() / 2),
  428. next : ($activeSide.outerHeight() / 2)
  429. }
  430. }
  431. ;
  432. module.verbose('Setting the initial animation position as above', $nextSide, box);
  433. $activeSide
  434. .css({
  435. 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)'
  436. })
  437. ;
  438. $nextSide
  439. .addClass(className.animating)
  440. .css({
  441. 'display' : 'block',
  442. 'top' : box.origin + 'px',
  443. 'transform' : 'rotateX(90deg) translateZ(' + box.depth.next + 'px)'
  444. })
  445. ;
  446. },
  447. below: function() {
  448. var
  449. box = {
  450. origin : (($activeSide.outerHeight() - $nextSide.outerHeight()) / 2),
  451. depth : {
  452. active : ($nextSide.outerHeight() / 2),
  453. next : ($activeSide.outerHeight() / 2)
  454. }
  455. }
  456. ;
  457. module.verbose('Setting the initial animation position as below', $nextSide, box);
  458. $activeSide
  459. .css({
  460. 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)'
  461. })
  462. ;
  463. $nextSide
  464. .addClass(className.animating)
  465. .css({
  466. 'display' : 'block',
  467. 'top' : box.origin + 'px',
  468. 'transform' : 'rotateX(-90deg) translateZ(' + box.depth.next + 'px)'
  469. })
  470. ;
  471. },
  472. left: function() {
  473. var
  474. box = {
  475. origin : ( ( $activeSide.outerWidth() - $nextSide.outerWidth() ) / 2),
  476. depth : {
  477. active : ($nextSide.outerWidth() / 2),
  478. next : ($activeSide.outerWidth() / 2)
  479. }
  480. }
  481. ;
  482. module.verbose('Setting the initial animation position as left', $nextSide, box);
  483. $activeSide
  484. .css({
  485. 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)'
  486. })
  487. ;
  488. $nextSide
  489. .addClass(className.animating)
  490. .css({
  491. 'display' : 'block',
  492. 'left' : box.origin + 'px',
  493. 'transform' : 'rotateY(-90deg) translateZ(' + box.depth.next + 'px)'
  494. })
  495. ;
  496. },
  497. right: function() {
  498. var
  499. box = {
  500. origin : ( ( $activeSide.outerWidth() - $nextSide.outerWidth() ) / 2),
  501. depth : {
  502. active : ($nextSide.outerWidth() / 2),
  503. next : ($activeSide.outerWidth() / 2)
  504. }
  505. }
  506. ;
  507. module.verbose('Setting the initial animation position as left', $nextSide, box);
  508. $activeSide
  509. .css({
  510. 'transform' : 'rotateY(0deg) translateZ(' + box.depth.active + 'px)'
  511. })
  512. ;
  513. $nextSide
  514. .addClass(className.animating)
  515. .css({
  516. 'display' : 'block',
  517. 'left' : box.origin + 'px',
  518. 'transform' : 'rotateY(90deg) translateZ(' + box.depth.next + 'px)'
  519. })
  520. ;
  521. },
  522. behind: function() {
  523. var
  524. box = {
  525. origin : ( ( $activeSide.outerWidth() - $nextSide.outerWidth() ) / 2),
  526. depth : {
  527. active : ($nextSide.outerWidth() / 2),
  528. next : ($activeSide.outerWidth() / 2)
  529. }
  530. }
  531. ;
  532. module.verbose('Setting the initial animation position as behind', $nextSide, box);
  533. $activeSide
  534. .css({
  535. 'transform' : 'rotateY(0deg)'
  536. })
  537. ;
  538. $nextSide
  539. .addClass(className.animating)
  540. .css({
  541. 'display' : 'block',
  542. 'left' : box.origin + 'px',
  543. 'transform' : 'rotateY(-180deg)'
  544. })
  545. ;
  546. }
  547. },
  548. setting: function(name, value) {
  549. module.debug('Changing setting', name, value);
  550. if( $.isPlainObject(name) ) {
  551. $.extend(true, settings, name);
  552. }
  553. else if(value !== undefined) {
  554. settings[name] = value;
  555. }
  556. else {
  557. return settings[name];
  558. }
  559. },
  560. internal: function(name, value) {
  561. if( $.isPlainObject(name) ) {
  562. $.extend(true, module, name);
  563. }
  564. else if(value !== undefined) {
  565. module[name] = value;
  566. }
  567. else {
  568. return module[name];
  569. }
  570. },
  571. debug: function() {
  572. if(settings.debug) {
  573. if(settings.performance) {
  574. module.performance.log(arguments);
  575. }
  576. else {
  577. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  578. module.debug.apply(console, arguments);
  579. }
  580. }
  581. },
  582. verbose: function() {
  583. if(settings.verbose && settings.debug) {
  584. if(settings.performance) {
  585. module.performance.log(arguments);
  586. }
  587. else {
  588. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  589. module.verbose.apply(console, arguments);
  590. }
  591. }
  592. },
  593. error: function() {
  594. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  595. module.error.apply(console, arguments);
  596. },
  597. performance: {
  598. log: function(message) {
  599. var
  600. currentTime,
  601. executionTime,
  602. previousTime
  603. ;
  604. if(settings.performance) {
  605. currentTime = new Date().getTime();
  606. previousTime = time || currentTime;
  607. executionTime = currentTime - previousTime;
  608. time = currentTime;
  609. performance.push({
  610. 'Name' : message[0],
  611. 'Arguments' : [].slice.call(message, 1) || '',
  612. 'Element' : element,
  613. 'Execution Time' : executionTime
  614. });
  615. }
  616. clearTimeout(module.performance.timer);
  617. module.performance.timer = setTimeout(module.performance.display, 100);
  618. },
  619. display: function() {
  620. var
  621. title = settings.name + ':',
  622. totalTime = 0
  623. ;
  624. time = false;
  625. clearTimeout(module.performance.timer);
  626. $.each(performance, function(index, data) {
  627. totalTime += data['Execution Time'];
  628. });
  629. title += ' ' + totalTime + 'ms';
  630. if(moduleSelector) {
  631. title += ' \'' + moduleSelector + '\'';
  632. }
  633. if($allModules.size() > 1) {
  634. title += ' ' + '(' + $allModules.size() + ')';
  635. }
  636. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  637. console.groupCollapsed(title);
  638. if(console.table) {
  639. console.table(performance);
  640. }
  641. else {
  642. $.each(performance, function(index, data) {
  643. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  644. });
  645. }
  646. console.groupEnd();
  647. }
  648. performance = [];
  649. }
  650. },
  651. invoke: function(query, passedArguments, context) {
  652. var
  653. object = instance,
  654. maxDepth,
  655. found,
  656. response
  657. ;
  658. passedArguments = passedArguments || queryArguments;
  659. context = element || context;
  660. if(typeof query == 'string' && object !== undefined) {
  661. query = query.split(/[\. ]/);
  662. maxDepth = query.length - 1;
  663. $.each(query, function(depth, value) {
  664. var camelCaseValue = (depth != maxDepth)
  665. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  666. : query
  667. ;
  668. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  669. object = object[camelCaseValue];
  670. }
  671. else if( object[camelCaseValue] !== undefined ) {
  672. found = object[camelCaseValue];
  673. return false;
  674. }
  675. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  676. object = object[value];
  677. }
  678. else if( object[value] !== undefined ) {
  679. found = object[value];
  680. return false;
  681. }
  682. else {
  683. return false;
  684. }
  685. });
  686. }
  687. if ( $.isFunction( found ) ) {
  688. response = found.apply(context, passedArguments);
  689. }
  690. else if(found !== undefined) {
  691. response = found;
  692. }
  693. if($.isArray(returnedValue)) {
  694. returnedValue.push(response);
  695. }
  696. else if(returnedValue !== undefined) {
  697. returnedValue = [returnedValue, response];
  698. }
  699. else if(response !== undefined) {
  700. returnedValue = response;
  701. }
  702. return found;
  703. }
  704. };
  705. if(methodInvoked) {
  706. if(instance === undefined) {
  707. module.initialize();
  708. }
  709. module.invoke(query);
  710. }
  711. else {
  712. if(instance !== undefined) {
  713. module.destroy();
  714. }
  715. module.initialize();
  716. }
  717. })
  718. ;
  719. return (returnedValue !== undefined)
  720. ? returnedValue
  721. : this
  722. ;
  723. };
  724. $.fn.shape.settings = {
  725. // module info
  726. name : 'Shape',
  727. // debug content outputted to console
  728. debug : false,
  729. // verbose debug output
  730. verbose : true,
  731. // performance data output
  732. performance: true,
  733. // event namespace
  734. namespace : 'shape',
  735. // callback occurs on side change
  736. beforeChange : function() {},
  737. onChange : function() {},
  738. // allow animation to same side
  739. allowRepeats: false,
  740. // animation duration
  741. duration : 700,
  742. // possible errors
  743. error: {
  744. side : 'You tried to switch to a side that does not exist.',
  745. method : 'The method you called is not defined'
  746. },
  747. // classnames used
  748. className : {
  749. animating : 'animating',
  750. hidden : 'hidden',
  751. loading : 'loading',
  752. active : 'active'
  753. },
  754. // selectors used
  755. selector : {
  756. sides : '.sides',
  757. side : '.side'
  758. }
  759. };
  760. })( jQuery, window , document );