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.

875 lines
27 KiB

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