├── .bowerrc ├── .gitignore ├── bower.json ├── .jshintrc ├── demo ├── flip-with-swipebox │ ├── style.css │ └── index.html ├── index.html └── index2.html ├── LICENSE ├── package.json ├── Gruntfile.js ├── dist ├── jquery.flip.min.js ├── jquery.flip.min.js.map └── jquery.flip.js ├── README.md └── src └── flip.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /bower_components/ 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flip", 3 | "version": "1.0.11", 4 | "dependencies": {}, 5 | "devDependencies": { 6 | "jquery": "~2.1.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "boss": true, 3 | "curly": true, 4 | "eqeqeq": false, 5 | "eqnull": true, 6 | "es3": true, 7 | "expr": true, 8 | "immed": true, 9 | "noarg": true, 10 | "onevar": true, 11 | "quotmark": false, 12 | "trailing": true, 13 | "undef": true, 14 | "unused": true, 15 | "newcap": false, 16 | "browser": true, 17 | "shadow": true, 18 | "devel": true, 19 | 20 | "globals": { 21 | "jQuery": false 22 | } 23 | } -------------------------------------------------------------------------------- /demo/flip-with-swipebox/style.css: -------------------------------------------------------------------------------- 1 | html, body, div, ul { 2 | box-sizing: border-box; 3 | } 4 | 5 | html, body { 6 | margin: 0; 7 | padding: 0; 8 | } 9 | 10 | .card { 11 | width: 320px; 12 | height: 460px; 13 | padding: 30px; 14 | margin: 0 auto; 15 | } 16 | 17 | .card a.thumb { 18 | float: left; 19 | width: 100px; 20 | height: 60px; 21 | margin: 0 5px 10px 0; 22 | } 23 | 24 | .card a.thumb img { 25 | max-width: 100%; 26 | max-height: 100%; 27 | } 28 | 29 | .front, .back { 30 | width: 100%; 31 | height: 80%; 32 | padding: 15px; 33 | background-color: white; 34 | border-radius: 5px; 35 | box-shadow: 0px 1px 6px rgba(0,0,0, 0.4); 36 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Nattawat Nonsung 4 | 2015 Jemar Jones, Nattawat Nonsung and Stijn de Witt 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flip", 3 | "version": "1.0.11", 4 | "description": "jQuery Plugin - 3d Flip Content", 5 | "keywords": [ 6 | "jquery-plugin" 7 | ], 8 | "homepage": "https://github.com/nnattawat/flip", 9 | "bugs": "https://github.com/nnattawat/flip/issues", 10 | "author": { 11 | "name": "Nattawat Nonsung", 12 | "email": "armmer1@gmail.com", 13 | "url": "https://github.com/nnattawat" 14 | }, 15 | "contributors": [ 16 | { 17 | "name": "Carlos Fagiani Junior", 18 | "email": "fagianijunior@gmail.com", 19 | "url": "https://github.com/fagianijunior" 20 | }, 21 | { 22 | "name": "Jemar Jones", 23 | "email": "jemarkjones@gmail.com", 24 | "url": "http://JKJones.me/" 25 | }, 26 | { 27 | "name": "Stijn de Witt", 28 | "email": "StijnDeWitt@hotmail.com", 29 | "url": "http://StijnDeWitt.com" 30 | } 31 | ], 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/nnattawat/flip.git" 35 | }, 36 | "licenses": [ 37 | { 38 | "type": "MIT" 39 | } 40 | ], 41 | "devDependencies": { 42 | "grunt-contrib-concat": "~0.3.0", 43 | "grunt-contrib-uglify": "~0.2.7", 44 | "grunt-contrib-watch": "~0.5.3", 45 | "grunt-contrib-clean": "~0.5.0", 46 | "grunt-contrib-connect": "~0.5.0", 47 | "time-grunt": "~0.2.3", 48 | "load-grunt-tasks": "~0.2.0", 49 | "grunt": "~0.4.2", 50 | "grunt-contrib-jshint": "~0.11.2" 51 | }, 52 | "scripts": { 53 | "test": "grunt test" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (grunt) { 4 | // Load all grunt tasks 5 | require('load-grunt-tasks')(grunt); 6 | // Show elapsed time at the end 7 | require('time-grunt')(grunt); 8 | 9 | // Project configuration. 10 | grunt.initConfig({ 11 | // Metadata. 12 | pkg: grunt.file.readJSON('package.json'), 13 | banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' + 14 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 15 | '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' + 16 | '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + 17 | ' Licensed MIT */\n', 18 | // Task configuration. 19 | clean: { 20 | files: ['dist'] 21 | }, 22 | jshint: { 23 | options : { 24 | jshintrc : '.jshintrc' 25 | }, 26 | all: [ 'src/<%= pkg.name %>.js' ] 27 | }, 28 | concat: { 29 | options: { 30 | banner: '<%= banner %>', 31 | stripBanners: true 32 | }, 33 | dist: { 34 | src: ['src/<%= pkg.name %>.js'], 35 | dest: 'dist/jquery.<%= pkg.name %>.js' 36 | } 37 | }, 38 | uglify: { 39 | options: { 40 | sourceMap: 'dist/jquery.<%= pkg.name %>.min.js.map', 41 | sourceMappingURL : 'jquery.<%= pkg.name %>.min.js.map', 42 | banner: '<%= banner %>' 43 | }, 44 | dist: { 45 | src: '<%= concat.dist.src %>', 46 | dest: 'dist/jquery.<%= pkg.name %>.min.js' 47 | } 48 | }, 49 | connect: { 50 | server: { 51 | options: { 52 | hostname: '*', 53 | port: 9000 54 | } 55 | } 56 | } 57 | }); 58 | 59 | // Default task. 60 | grunt.registerTask('default', ['connect', 'clean', 'jshint', 'concat', 'uglify']); 61 | }; 62 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | jQuery plugin 8 | 25 | 26 | 27 |
28 |
29 | Front: Cursus aliquet mus et sociis, placerat adipiscing a placerat magnis integer nisi lacus in, turpis porttitor? Lectus nunc dis in porta, montes lacus. Tortor. Pid sit nisi eu nec aenean. 30 |
31 |
32 | link 33 | Back: Dolor scelerisque ridiculus! Mus? Augue, montes, montes proin rhoncus vel a parturient dapibus eros? Penatibus nascetur. In turpis nisi elementum nascetur habitasse augue egestas, in ac rhoncus odio porttitor turpis. 34 |
35 |
36 | 37 |
38 |
39 | Front: Cursus aliquet mus et sociis, placerat adipiscing a placerat magnis integer nisi lacus in, turpis porttitor? Lectus nunc dis in porta, montes lacus. Tortor. Pid sit nisi eu nec aenean. 40 |
41 |
42 | link 43 | Back: Dolor scelerisque ridiculus! Mus? Augue, montes, montes proin rhoncus vel a parturient dapibus eros? Penatibus nascetur. In turpis nisi elementum nascetur habitasse augue egestas, in ac rhoncus odio porttitor turpis. 44 | 45 |
46 |
47 | 48 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /demo/flip-with-swipebox/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |

Flip with Swipebox

13 |
14 | 15 | image 16 | 17 | 18 | image 19 | 20 | 21 | image 22 | 23 | 24 | image 25 | 26 |
27 |

When a Swipebox is used on 28 | a card animated with Flip, the Swipebox fails, as can be seen in this demo when 29 | we use version 1.0.1 of Flip. If we apply the patch the issue is fixed.

30 |

Click on the thumbnails. A Swipebox should open.

31 | 32 |
33 |
34 |

Flip with Swipebox

35 |

The problem is caused by Flip disabling the bubbling of click 36 | events on any links that are contained inside the element to be flipped, 37 | as can be seen in lines 80-82 of the 38 | source code.

39 |

This code was written to prevent the card from flipping when buttons and 40 | links on the card are pressed. However, preventing event bubling is 41 | dangerous.

42 |

The better solution is to ignore those events that you are not interested in.

43 |
44 |
45 | 46 | 47 | 48 | 49 | 55 | 56 | -------------------------------------------------------------------------------- /dist/jquery.flip.min.js: -------------------------------------------------------------------------------- 1 | /*! flip - v1.0.11 - 2015-07-13 2 | * https://github.com/nnattawat/flip 3 | * Copyright (c) 2015 Nattawat Nonsung; Licensed MIT */ 4 | 5 | !function(a){var b=function(a){a.data("flipped",!0);var b="rotate"+a.data("axis");a.find(a.data("front")).css({transform:b+(a.data("reverse")?"(-180deg)":"(180deg)"),"z-index":"0"}),a.find(a.data("back")).css({transform:b+"(0deg)","z-index":"1"})},c=function(a){a.data("flipped",!1);var b="rotate"+a.data("axis");a.find(a.data("front")).css({transform:b+"(0deg)","z-index":"1"}),a.find(a.data("back")).css({transform:b+(a.data("reverse")?"(180deg)":"(-180deg)"),"z-index":"0"})},d=function(){var a,b=document.createElement("fakeelement"),c={transition:"transitionend",OTransition:"oTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(a in c)if(void 0!==b.style[a])return c[a]};a.fn.flip=function(f,g){return"function"==typeof f&&(g=f),this.each(function(){var h=a(this);if(void 0===f||"boolean"!=typeof f&&"string"!=typeof f)if(h.data("initiated"))void 0!==f.axis&&e.call(this,f.axis,g);else{h.data("initiated",!0);var i=a.extend({axis:"y",reverse:!1,trigger:"click",speed:500,forceHeight:!1,forceWidth:!1,autoSize:!0,front:"auto",back:"auto"},f);"auto"==i.front?i.front=h.find(".front").length>0?".front":"div:first-child":"autostrict"==i.front&&(i.front="div:first-child"),"auto"==i.back?i.back=h.find(".back").length>0?".back":"div:first-child + div":"autostrict"==i.back&&(i.back="div:first-child + div"),h.data("reverse",i.reverse),h.data("axis",i.axis),h.data("front",i.front),h.data("back",i.back);var j="rotate"+("x"==i.axis.toLowerCase()?"x":"y"),k=2*h["outer"+("rotatex"==j?"Height":"Width")]();h.find(h.data("back")).css({transform:j+"("+(i.reverse?"180deg":"-180deg")+")"}),h.css({perspective:k,position:"relative"});var l=i.speed/1e3||.5,m=h.find(i.front).add(i.back,h);if(i.forceHeight?m.outerHeight(h.height()):i.autoSize&&m.css({height:"100%"}),i.forceWidth?m.outerWidth(h.width()):i.autoSize&&m.css({width:"100%"}),m.css({"backface-visibility":"hidden","transform-style":"preserve-3d",position:"absolute","z-index":"1"}),h.find(h.data("back")).css({transform:j+"("+(i.reverse?"180deg":"-180deg")+")","z-index":"0"}),setTimeout(function(){m.css({transition:"all "+l+"s ease-out"}),void 0!==g&&g.call(this)},20),"click"==i.trigger.toLowerCase())h.on(a.fn.tap?"tap":"click",function(){h.find(a(event.target).closest('button, a, input[type="submit"]')).length||(h.data("flipped")?c(h):b(h))});else if("hover"==i.trigger.toLowerCase()){var n=function(){h.unbind("mouseleave",o),b(h),setTimeout(function(){h.bind("mouseleave",o),h.is(":hover")||c(h)},i.speed+150)},o=function(){c(h)};h.mouseenter(n),h.mouseleave(o)}}else"toggle"==f&&(f=!h.data("flipped")),f?b(h):c(h),void 0!==g&&a(this).one(d(),function(){g.call(this)})}),this};var e=function(b,c){if(a(this).data("axis")!=b.toLowerCase()){var d=a(this).find(a(this).data("front")).add(a(this).data("back"),a(this)),e=d.css("transition");d.css({transition:"none"}),b=b.toLowerCase(),a(this).data("axis",b);var f="rotate"+b;a(this).data("flipped")?a(this).find(a(this).data("front")).css({transform:f+(a(this).data("reverse")?"(-180deg)":"(180deg)"),"z-index":"0"}):a(this).find(a(this).data("back")).css({transform:f+"("+(a(this).data("reverse")?"180deg":"-180deg")+")","z-index":"0"}),setTimeout(function(){d.css({transition:e}),void 0!==c&&c.call(this)},0)}else void 0!==c&&setTimeout(c.bind(this),0)}}(jQuery); 6 | //# sourceMappingURL=jquery.flip.min.js.map -------------------------------------------------------------------------------- /demo/index2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | jQuery plugin 9 | 31 | 32 | 33 |
34 |
35 | Front 36 |
37 |
38 | Back 39 |
40 |
41 | 42 |
43 |
44 | Front 45 |
46 |
47 | Back 48 |
49 |
50 | 51 |
52 |
53 | Front 54 |
55 |
56 | Back 57 |
58 |
59 | 60 |
61 |
62 | Front 63 |
64 |
65 | Back 66 |
67 |
68 | 69 |
70 |
71 | Front 72 |
73 |
74 | Back 75 |
76 |
77 | 78 |
79 |
80 | Front 81 |
82 |
83 | Back 84 |
85 |
86 | 87 |
88 |
89 | Front 90 |
91 |
92 | Back 93 |
94 |
95 | 96 |
97 |
98 | Front 99 |
100 |
101 | Back 102 |
103 |
104 | 105 |
106 |
107 | Front 108 |
109 |
110 | Back 111 |
112 |
113 | 114 |
115 |
116 | Front 117 |
118 |
119 | Back 120 |
121 |
122 | 123 |
124 |
125 | Front 126 |
127 |
128 | Back 129 |
130 |
131 | 132 |
133 |
134 | Front 135 |
136 |
137 | Back 138 |
139 |
140 | 141 |
142 |
143 | Front 144 |
145 |
146 | Back 147 |
148 |
149 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /dist/jquery.flip.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"dist/jquery.flip.min.js","sources":["src/flip.js"],"names":["$","flip","$dom","data","rotateAxis","find","css","transform","z-index","unflip","whichTransitionEvent","t","el","document","createElement","transitions","transition","OTransition","MozTransition","WebkitTransition","undefined","style","fn","options","callback","this","each","axis","setAxis","call","settings","extend","reverse","trigger","speed","forceHeight","forceWidth","autoSize","front","back","length","toLowerCase","perspective","position","speedInSec","faces","add","outerHeight","height","outerWidth","width","backface-visibility","transform-style","setTimeout","on","tap","event","target","closest","performFlip","unbind","performUnflip","bind","is","mouseenter","mouseleave","one","savedTrans","jQuery"],"mappings":"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"} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jQuery Flip v1.0.11 2 | 3 | **A lightweight jQuery plugin to create 3d flip animation.** 4 | See the [project page](http://nnattawat.github.io/flip/) 5 | 6 | > This project was forked from [nnattawat/flip](https://github.com/nnatawat/flip) and has since 7 | > been merged back into that project. Please use the original project. 8 | 9 | 10 | ## What's new 11 | * **Flip v1.0.11** 12 | * [Added a callback that fires when flip animation is finished](https://github.com/Download/flip/commit/61b57a3d6c9a8f0dd116ca5b4444cb5356374702) 13 | 14 | * **Flip v1.0.10** 15 | * [Removed log statement](https://github.com/Download/flip/commit/60a0df340b17036978a9b26b23be71204755c928) 16 | * [Updated license and credits](https://github.com/Download/flip/commit/9b8d218506f6b279d04a52642a8ca2fd9455d9b4) 17 | * [Fixed indentation](https://github.com/Download/flip/commit/acc64f52e176e7fdb5124b797b4d28cdc5bedf95) 18 | 19 | * **Flip v1.0.9** 20 | * [Added support for custom front/back face selectors #27](https://github.com/nnattawat/flip/issues/27) 21 | 22 | * **Flip v1.0.8** 23 | * [Added support for the mobile `tap` event](https://github.com/nnattawat/flip/issues/26) 24 | * [Dynamic sized content by default while maintaining backward compatibility](https://github.com/Download/flip/commit/8a6d1b3626a3c1e0e5d71fb4786c44244bf33eac) 25 | * Now available with [source map](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/) for better debugging support. 26 | 27 | * **Flip v1.0.7** 28 | * [Improved backward compatibility](https://github.com/Download/flip/commit/b27588b1e5340ec2a6bfc5afca80a6e52b6f833f) 29 | 30 | * **Flip v1.0.6** 31 | This version adds new features and fixes some issues compared to the original flip: 32 | * [Added support for flippable content with dynamic height/width #17](https://github.com/nnattawat/flip/issues/17) 33 | * [Added ability to change axis arbitrarily #1](https://github.com/download/flip/pull/1) 34 | * [Flip prevents bubbling of click on 'button, a, input[type="submit"], breaking e.g. Swipebox #14](https://github.com/nnattawat/flip/issues/14) 35 | * [Flickering animations of elements in flipped content. #16](https://github.com/nnattawat/flip/issues/16) 36 | 37 | * **Flip v1.0.2 - v1.0.5** 38 | Chaotic period of development culminating in v1.0.6 see above. 39 | 40 | ## Getting Started 41 | 42 | ### CDN 43 | https://cdn.rawgit.com/nnattawat/flip/v1.0.11/dist/jquery.flip.min.js 44 | 45 | ### Download 46 | * [jquery.flip.js][max] (development version, commented ~9kB) 47 | * [jquery.flip.min.js][min] (production version, minified ~4kB, gzipped ~2kB) 48 | * [jquery.flip.min.js.map][map] (source map, ~5kB) 49 | [max]: https://cdn.rawgit.com/nnattawat/flip/v1.0.11/dist/jquery.flip.js 50 | [min]: https://cdn.rawgit.com/nnattawat/flip/v1.0.11/dist/jquery.flip.min.js 51 | [map]: https://cdn.rawgit.com/nnattawat/flip/v1.0.11/dist/jquery.flip.min.js.map 52 | 53 | ### Bower 54 |
bower install flip
55 | 56 | ### Usage 57 | In your web page: 58 | 59 | ```html 60 | 61 |
62 |
63 | Front content 64 |
65 |
66 | Back content 67 |
68 |
69 | 70 | 71 | 72 | 77 | ``` 78 | 79 | ## Documentation and Example 80 | 81 | Please refer to [the project website](http://nnattawat.github.io/flip/) 82 | 83 | ## Development 84 | Ensure that you have the latest [Node.js](http://nodejs.org/) and [npm](http://npmjs.org/) installed. 85 | 86 | Test that Grunt's CLI and Bower are installed by running `grunt --version` and `bower --version`. If the commands aren't found, run `npm install -g grunt-cli bower`. For more information about installing the tools, see the [getting started with Grunt guide](http://gruntjs.com/getting-started) or [bower.io](http://bower.io/) respectively. 87 | 88 | To run the demo locally, do the following. 89 |
90 | npm install
91 | bower install
92 | 
93 | 94 | And run grunt command to create files in /dist folder. 95 |
grunt
96 | -------------------------------------------------------------------------------- /src/flip.js: -------------------------------------------------------------------------------- 1 | (function( $ ) { 2 | var flip = function($dom) { 3 | $dom.data("flipped", true); 4 | 5 | var rotateAxis = "rotate" + $dom.data("axis"); 6 | $dom.find($dom.data("front")).css({ 7 | transform: rotateAxis + ($dom.data("reverse") ? "(-180deg)" : "(180deg)"), 8 | "z-index": "0" 9 | }); 10 | 11 | $dom.find($dom.data("back")).css({ 12 | transform: rotateAxis + "(0deg)", 13 | "z-index": "1" 14 | }); 15 | }; 16 | 17 | var unflip = function($dom) { 18 | $dom.data("flipped", false); 19 | 20 | var rotateAxis = "rotate" + $dom.data("axis"); 21 | $dom.find($dom.data("front")).css({ 22 | transform: rotateAxis + "(0deg)", 23 | "z-index": "1" 24 | }); 25 | 26 | $dom.find($dom.data("back")).css({ 27 | transform: rotateAxis + ($dom.data("reverse") ? "(180deg)" : "(-180deg)"), 28 | "z-index": "0" 29 | }); 30 | }; 31 | // Function from David Walsh: http://davidwalsh.name/css-animation-callback licensed with http://opensource.org/licenses/MIT 32 | var whichTransitionEvent = function(){ 33 | var t, 34 | el = document.createElement("fakeelement"), 35 | transitions = { 36 | "transition" : "transitionend", 37 | "OTransition" : "oTransitionEnd", 38 | "MozTransition" : "transitionend", 39 | "WebkitTransition": "webkitTransitionEnd" 40 | }; 41 | 42 | for (t in transitions){ 43 | if (el.style[t] !== undefined){ 44 | return transitions[t]; 45 | } 46 | } 47 | }; 48 | $.fn.flip = function(options, callback) { 49 | if (typeof options == 'function'){ 50 | //This allows flip to be called for setup with only a callback (default settings) 51 | callback = options; 52 | } 53 | this.each(function(){ 54 | var $dom = $(this); 55 | 56 | if (options !== undefined && (typeof(options) == "boolean" || typeof(options) == "string")) { // Force flip the DOM 57 | if (options == "toggle"){ 58 | options = !$dom.data("flipped"); 59 | } 60 | if (options) { 61 | flip($dom); 62 | } else { 63 | unflip($dom); 64 | } 65 | //Providing a nicely wrapped up callback because transform is essentially async 66 | if (callback !== undefined){ 67 | $(this).one(whichTransitionEvent(), callback); 68 | } 69 | } else if (!$dom.data("initiated")){ //Init flipable DOM 70 | $dom.data("initiated", true); 71 | 72 | var settings = $.extend({ 73 | axis: "y", 74 | reverse: false, 75 | trigger: "click", 76 | speed: 500, 77 | forceHeight: false, 78 | forceWidth: false, 79 | autoSize: true, 80 | front: 'auto', 81 | back: 'auto' 82 | }, options ); 83 | 84 | //By defualt we first check for the old front and back selectors for backward compatibility 85 | //if they arent there we fall back to auto selecting the first and second div 86 | if (settings.front == "auto"){ 87 | settings.front = ($dom.find('.front').length > 0)? '.front' : 'div:first-child'; 88 | }else if (settings.front == "autostrict"){ 89 | settings.front = 'div:first-child'; 90 | } 91 | if (settings.back == "auto"){ 92 | //Note, we must use the old 'div:first-child + div' for IE compatibility 93 | settings.back = ($dom.find('.back').length > 0)? '.back' : 'div:first-child + div'; 94 | }else if (settings.back == "autostrict"){ 95 | settings.back = 'div:first-child + div'; 96 | } 97 | // save reverse and axis css to DOM for performing flip 98 | $dom.data("reverse", settings.reverse); 99 | $dom.data("axis", settings.axis); 100 | $dom.data("front", settings.front); 101 | $dom.data("back", settings.back); 102 | 103 | var rotateAxis = "rotate" + (settings.axis.toLowerCase() == "x" ? "x" : "y"), 104 | perspective = $dom["outer" + (rotateAxis == "rotatex" ? "Height" : "Width")]() * 2; 105 | 106 | $dom.find($dom.data("back")).css({ 107 | transform: rotateAxis + "(" + (settings.reverse? "180deg" : "-180deg") + ")" 108 | }); 109 | 110 | $dom.css({ 111 | perspective: perspective, 112 | position: "relative" 113 | }); 114 | 115 | var speedInSec = settings.speed / 1000 || 0.5; 116 | var faces = $dom.find(settings.front).add(settings.back, $dom); 117 | if (settings.forceHeight) {faces.outerHeight($dom.height());} else if (settings.autoSize) {faces.css({'height': '100%'});} 118 | if (settings.forceWidth) {faces.outerWidth($dom.width());} else if (settings.autoSize) {faces.css({'width': '100%'});} 119 | faces.css({ 120 | "backface-visibility": "hidden", 121 | "transform-style": "preserve-3d", 122 | position: "absolute", 123 | "z-index": "1" 124 | }); 125 | $dom.find($dom.data("back")).css({ 126 | transform: rotateAxis + "(" + (settings.reverse? "180deg" : "-180deg") + ")", 127 | "z-index": "0" 128 | }); 129 | // not forcing width/height may cause an initial flip to show up on 130 | // page load when we apply the style to reverse the backface... 131 | // To prevent this we first apply the basic styles and then give the 132 | // browser a moment to apply them. Only afterwards do we add the transition. 133 | setTimeout(function(){ 134 | // By now the browser should have applied the styles, so the transition 135 | // will only affect subsequent flips. 136 | faces.css({ 137 | transition: "all " + speedInSec + "s ease-out" 138 | }); 139 | if (callback !== undefined){ 140 | callback.call(this); 141 | } 142 | //While this used to work with a setTimeout of zero, at some point that became 143 | //unstable and the initial flip returned. The reason for this is unknown but we 144 | //will temporarily use a short delay of 20 to mitigate this issue. 145 | }, 20); 146 | 147 | if (settings.trigger.toLowerCase() == "click") { 148 | $dom.on($.fn.tap ? "tap" : "click", function(event) { 149 | if( !event ) event = window.event; 150 | if ($dom.find($(event.target).closest('button, a, input[type="submit"]')).length) { 151 | return; 152 | } 153 | 154 | if ($dom.data("flipped")) { 155 | unflip($dom); 156 | } else { 157 | flip($dom); 158 | } 159 | }); 160 | } 161 | else if (settings.trigger.toLowerCase() == "hover") { 162 | var performFlip = function() { 163 | $dom.unbind('mouseleave', performUnflip); 164 | 165 | flip($dom); 166 | 167 | setTimeout(function() { 168 | $dom.bind('mouseleave', performUnflip); 169 | if (!$dom.is(":hover")) { 170 | unflip($dom); 171 | } 172 | }, (settings.speed + 150)); 173 | }; 174 | 175 | var performUnflip = function() { 176 | unflip($dom); 177 | }; 178 | 179 | $dom.mouseenter(performFlip); 180 | $dom.mouseleave(performUnflip); 181 | } 182 | }else{ 183 | //The element has been initiated, all we have to do is change applicable settings 184 | if (options.axis !== undefined){ 185 | setAxis.call(this,options.axis,callback); 186 | } 187 | } 188 | }); 189 | 190 | return this; 191 | }; 192 | var setAxis = function(axis,callback){ 193 | if ($(this).data("axis") != axis.toLowerCase()){ 194 | var faces = $(this).find($(this).data("front")).add($(this).data("back"), $(this)); 195 | var savedTrans = faces.css("transition"); 196 | faces.css({ 197 | transition: "none" 198 | }); 199 | //Only setting the axis if it needs to be 200 | 201 | axis = axis.toLowerCase(); 202 | $(this).data("axis", axis); 203 | 204 | //This sets up the first flip in the new direction automatically 205 | var rotateAxis = "rotate" + axis; 206 | if ($(this).data("flipped")){ 207 | $(this).find($(this).data("front")).css({ 208 | transform: rotateAxis + ($(this).data("reverse") ? "(-180deg)" : "(180deg)"), 209 | "z-index": "0" 210 | }); 211 | }else{ 212 | $(this).find($(this).data("back")).css({ 213 | transform: rotateAxis + "(" + ($(this).data("reverse")? "180deg" : "-180deg") + ")", 214 | "z-index": "0" 215 | }); 216 | } 217 | //Providing a nicely wrapped up callback because transform is essentially async 218 | setTimeout(function(){ 219 | faces.css({ 220 | transition: savedTrans 221 | }); 222 | if (callback !== undefined){ 223 | callback.call(this); 224 | } 225 | },0); 226 | }else{ 227 | //If we didnt have to set the axis we can just call back. 228 | if (callback !== undefined){ 229 | setTimeout(callback.bind(this), 0); 230 | } 231 | } 232 | }; 233 | }( jQuery )); -------------------------------------------------------------------------------- /dist/jquery.flip.js: -------------------------------------------------------------------------------- 1 | /*! flip - v1.0.11 - 2015-07-13 2 | * https://github.com/nnattawat/flip 3 | * Copyright (c) 2015 Nattawat Nonsung; Licensed MIT */ 4 | (function( $ ) { 5 | var flip = function($dom) { 6 | $dom.data("flipped", true); 7 | 8 | var rotateAxis = "rotate" + $dom.data("axis"); 9 | $dom.find($dom.data("front")).css({ 10 | transform: rotateAxis + ($dom.data("reverse") ? "(-180deg)" : "(180deg)"), 11 | "z-index": "0" 12 | }); 13 | 14 | $dom.find($dom.data("back")).css({ 15 | transform: rotateAxis + "(0deg)", 16 | "z-index": "1" 17 | }); 18 | }; 19 | 20 | var unflip = function($dom) { 21 | $dom.data("flipped", false); 22 | 23 | var rotateAxis = "rotate" + $dom.data("axis"); 24 | $dom.find($dom.data("front")).css({ 25 | transform: rotateAxis + "(0deg)", 26 | "z-index": "1" 27 | }); 28 | 29 | $dom.find($dom.data("back")).css({ 30 | transform: rotateAxis + ($dom.data("reverse") ? "(180deg)" : "(-180deg)"), 31 | "z-index": "0" 32 | }); 33 | }; 34 | // Function from David Walsh: http://davidwalsh.name/css-animation-callback licensed with http://opensource.org/licenses/MIT 35 | var whichTransitionEvent = function(){ 36 | var t, 37 | el = document.createElement("fakeelement"), 38 | transitions = { 39 | "transition" : "transitionend", 40 | "OTransition" : "oTransitionEnd", 41 | "MozTransition" : "transitionend", 42 | "WebkitTransition": "webkitTransitionEnd" 43 | }; 44 | 45 | for (t in transitions){ 46 | if (el.style[t] !== undefined){ 47 | return transitions[t]; 48 | } 49 | } 50 | }; 51 | $.fn.flip = function(options, callback) { 52 | if (typeof options == 'function'){ 53 | //This allows flip to be called for setup with only a callback (default settings) 54 | callback = options; 55 | } 56 | this.each(function(){ 57 | var $dom = $(this); 58 | 59 | if (options !== undefined && (typeof(options) == "boolean" || typeof(options) == "string")) { // Force flip the DOM 60 | if (options == "toggle"){ 61 | options = !$dom.data("flipped"); 62 | } 63 | if (options) { 64 | flip($dom); 65 | } else { 66 | unflip($dom); 67 | } 68 | //Providing a nicely wrapped up callback because transform is essentially async 69 | if (callback !== undefined){ 70 | $(this).one(whichTransitionEvent(), function(){ 71 | callback.call(this); 72 | }); 73 | } 74 | } else if (!$dom.data("initiated")){ //Init flipable DOM 75 | $dom.data("initiated", true); 76 | 77 | var settings = $.extend({ 78 | axis: "y", 79 | reverse: false, 80 | trigger: "click", 81 | speed: 500, 82 | forceHeight: false, 83 | forceWidth: false, 84 | autoSize: true, 85 | front: 'auto', 86 | back: 'auto' 87 | }, options ); 88 | 89 | //By defualt we first check for the old front and back selectors for backward compatibility 90 | //if they arent there we fall back to auto selecting the first and second div 91 | if (settings.front == "auto"){ 92 | settings.front = ($dom.find('.front').length > 0)? '.front' : 'div:first-child'; 93 | }else if (settings.front == "autostrict"){ 94 | settings.front = 'div:first-child'; 95 | } 96 | if (settings.back == "auto"){ 97 | //Note, we must use the old 'div:first-child + div' for IE compatibility 98 | settings.back = ($dom.find('.back').length > 0)? '.back' : 'div:first-child + div'; 99 | }else if (settings.back == "autostrict"){ 100 | settings.back = 'div:first-child + div'; 101 | } 102 | // save reverse and axis css to DOM for performing flip 103 | $dom.data("reverse", settings.reverse); 104 | $dom.data("axis", settings.axis); 105 | $dom.data("front", settings.front); 106 | $dom.data("back", settings.back); 107 | 108 | var rotateAxis = "rotate" + (settings.axis.toLowerCase() == "x" ? "x" : "y"), 109 | perspective = $dom["outer" + (rotateAxis == "rotatex" ? "Height" : "Width")]() * 2; 110 | 111 | $dom.find($dom.data("back")).css({ 112 | transform: rotateAxis + "(" + (settings.reverse? "180deg" : "-180deg") + ")" 113 | }); 114 | 115 | $dom.css({ 116 | perspective: perspective, 117 | position: "relative" 118 | }); 119 | 120 | var speedInSec = settings.speed / 1000 || 0.5; 121 | var faces = $dom.find(settings.front).add(settings.back, $dom); 122 | if (settings.forceHeight) {faces.outerHeight($dom.height());} else if (settings.autoSize) {faces.css({'height': '100%'});} 123 | if (settings.forceWidth) {faces.outerWidth($dom.width());} else if (settings.autoSize) {faces.css({'width': '100%'});} 124 | faces.css({ 125 | "backface-visibility": "hidden", 126 | "transform-style": "preserve-3d", 127 | position: "absolute", 128 | "z-index": "1" 129 | }); 130 | $dom.find($dom.data("back")).css({ 131 | transform: rotateAxis + "(" + (settings.reverse? "180deg" : "-180deg") + ")", 132 | "z-index": "0" 133 | }); 134 | // not forcing width/height may cause an initial flip to show up on 135 | // page load when we apply the style to reverse the backface... 136 | // To prevent this we first apply the basic styles and then give the 137 | // browser a moment to apply them. Only afterwards do we add the transition. 138 | setTimeout(function(){ 139 | // By now the browser should have applied the styles, so the transition 140 | // will only affect subsequent flips. 141 | faces.css({ 142 | transition: "all " + speedInSec + "s ease-out" 143 | }); 144 | if (callback !== undefined){ 145 | callback.call(this); 146 | } 147 | //While this used to work with a setTimeout of zero, at some point that became 148 | //unstable and the initial flip returned. The reason for this is unknown but we 149 | //will temporarily use a short delay of 20 to mitigate this issue. 150 | }, 20); 151 | 152 | if (settings.trigger.toLowerCase() == "click") { 153 | $dom.on($.fn.tap ? "tap" : "click", function() { 154 | if ($dom.find($(event.target).closest('button, a, input[type="submit"]')).length) { 155 | return; 156 | } 157 | 158 | if ($dom.data("flipped")) { 159 | unflip($dom); 160 | } else { 161 | flip($dom); 162 | } 163 | }); 164 | } 165 | else if (settings.trigger.toLowerCase() == "hover") { 166 | var performFlip = function() { 167 | $dom.unbind('mouseleave', performUnflip); 168 | 169 | flip($dom); 170 | 171 | setTimeout(function() { 172 | $dom.bind('mouseleave', performUnflip); 173 | if (!$dom.is(":hover")) { 174 | unflip($dom); 175 | } 176 | }, (settings.speed + 150)); 177 | }; 178 | 179 | var performUnflip = function() { 180 | unflip($dom); 181 | }; 182 | 183 | $dom.mouseenter(performFlip); 184 | $dom.mouseleave(performUnflip); 185 | } 186 | }else{ 187 | //The element has been initiated, all we have to do is change applicable settings 188 | if (options.axis !== undefined){ 189 | setAxis.call(this,options.axis,callback); 190 | } 191 | } 192 | }); 193 | 194 | return this; 195 | }; 196 | var setAxis = function(axis,callback){ 197 | if ($(this).data("axis") != axis.toLowerCase()){ 198 | var faces = $(this).find($(this).data("front")).add($(this).data("back"), $(this)); 199 | var savedTrans = faces.css("transition"); 200 | faces.css({ 201 | transition: "none" 202 | }); 203 | //Only setting the axis if it needs to be 204 | 205 | axis = axis.toLowerCase(); 206 | $(this).data("axis", axis); 207 | 208 | //This sets up the first flip in the new direction automatically 209 | var rotateAxis = "rotate" + axis; 210 | if ($(this).data("flipped")){ 211 | $(this).find($(this).data("front")).css({ 212 | transform: rotateAxis + ($(this).data("reverse") ? "(-180deg)" : "(180deg)"), 213 | "z-index": "0" 214 | }); 215 | }else{ 216 | $(this).find($(this).data("back")).css({ 217 | transform: rotateAxis + "(" + ($(this).data("reverse")? "180deg" : "-180deg") + ")", 218 | "z-index": "0" 219 | }); 220 | } 221 | //Providing a nicely wrapped up callback because transform is essentially async 222 | setTimeout(function(){ 223 | faces.css({ 224 | transition: savedTrans 225 | }); 226 | if (callback !== undefined){ 227 | callback.call(this); 228 | } 229 | },0); 230 | }else{ 231 | //If we didnt have to set the axis we can just call back. 232 | if (callback !== undefined){ 233 | setTimeout(callback.bind(this), 0); 234 | } 235 | } 236 | }; 237 | }( jQuery )); --------------------------------------------------------------------------------