Repository: blasten/turn.js
Branch: master
Commit: 08c1f6599a14
Files: 7
Total size: 53.6 KB
Directory structure:
gitextract_61qrn7na/
├── changelog.txt
├── demos/
│ ├── bible/
│ │ └── index.html
│ ├── magazine/
│ │ └── index.html
│ └── magazine_single/
│ └── index.html
├── license.txt
├── readme.md
└── turn.js
================================================
FILE CONTENTS
================================================
================================================
FILE: changelog.txt
================================================
TURN.JS
------------------------------------------------------
Github: https://github.com/blasten/turn.js
Reference: https://github.com/blasten/turn.js/wiki/Reference
Website: www.turnjs.com
------------------------------------------------------
Release 3 - 2012/03/01
------------------------------------------------------
- Added 'range'
- Added 'addPage'
- Added 'removePage'
- Added 'hasPage'
- Added 'pages'
- Added 'display'
- Added 'when' to the initial configuration
- Added 'pages' to the initial configuration
- Added 'inclination' to the initial configuration
- Added 'first' event
- Added 'last' event
- Added gradients for non-webkit browsers
------------------------------------------------------
Release 2 - 2012/02/15
------------------------------------------------------
- Added 'size'.
- Bug in Chrome 17-18 Beta about losing background-image was fixed.
------------------------------------------------------
Release 1 - 2012/02/05
------------------------------------------------------
- First alpha release
================================================
FILE: demos/bible/index.html
================================================
<!doctype html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="../../turn.min.js"></script>
<style type="text/css">
body{
background:#ccc;
}
#book{
width:800px;
height:500px;
}
#book .turn-page{
background-color:white;
}
#book .cover{
background:#333;
}
#book .cover h1{
color:white;
text-align:center;
font-size:50px;
line-height:500px;
margin:0px;
}
#book .loader{
background-image:url(loader.gif);
width:24px;
height:24px;
display:block;
position:absolute;
top:238px;
left:188px;
}
#book .data{
text-align:center;
font-size:40px;
color:#999;
line-height:500px;
}
#controls{
width:800px;
text-align:center;
margin:20px 0px;
font:30px arial;
}
#controls input, #controls label{
font:30px arial;
}
#book .odd{
background-image:-webkit-linear-gradient(left, #FFF 95%, #ddd 100%);
background-image:-moz-linear-gradient(left, #FFF 95%, #ddd 100%);
background-image:-o-linear-gradient(left, #FFF 95%, #ddd 100%);
background-image:-ms-linear-gradient(left, #FFF 95%, #ddd 100%);
}
#book .even{
background-image:-webkit-linear-gradient(right, #FFF 95%, #ddd 100%);
background-image:-moz-linear-gradient(right, #FFF 95%, #ddd 100%);
background-image:-o-linear-gradient(right, #FFF 95%, #ddd 100%);
background-image:-ms-linear-gradient(right, #FFF 95%, #ddd 100%);
}
</style>
</head>
<body>
<div id="book">
<div class="cover"><h1>The Bible</h1></div>
</div>
<div id="controls">
<label for="page-number">Page:</label> <input type="text" size="3" id="page-number"> of <span id="number-pages"></span>
</div>
<script type="text/javascript">
// Sample using dynamic pages with turn.js
var numberOfPages = 1000;
// Adds the pages that the book will need
function addPage(page, book) {
// First check if the page is already in the book
if (!book.turn('hasPage', page)) {
// Create an element for this page
var element = $('<div />', {'class': 'page '+((page%2==0) ? 'odd' : 'even'), 'id': 'page-'+page}).html('<i class="loader"></i>');
// If not then add the page
book.turn('addPage', element, page);
// Let's assum that the data is comming from the server and the request takes 1s.
setTimeout(function(){
element.html('<div class="data">Data for page '+page+'</div>');
}, 1000);
}
}
$(window).ready(function(){
$('#book').turn({acceleration: true,
pages: numberOfPages,
elevation: 50,
gradients: !$.isTouch,
when: {
turning: function(e, page, view) {
// Gets the range of pages that the book needs right now
var range = $(this).turn('range', page);
// Check if each page is within the book
for (page = range[0]; page<=range[1]; page++)
addPage(page, $(this));
},
turned: function(e, page) {
$('#page-number').val(page);
}
}
});
$('#number-pages').html(numberOfPages);
$('#page-number').keydown(function(e){
if (e.keyCode==13)
$('#book').turn('page', $('#page-number').val());
});
});
$(window).bind('keydown', function(e){
if (e.target && e.target.tagName.toLowerCase()!='input')
if (e.keyCode==37)
$('#book').turn('previous');
else if (e.keyCode==39)
$('#book').turn('next');
});
</script>
</body>
</html>
================================================
FILE: demos/magazine/index.html
================================================
<!doctype html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="../../turn.min.js"></script>
<style type="text/css">
body{
background:#ccc;
}
#magazine{
width:1152px;
height:752px;
}
#magazine .turn-page{
background-color:#ccc;
background-size:100% 100%;
}
</style>
</head>
<body>
<div id="magazine">
<div style="background-image:url(pages/01.jpg);"></div>
<div style="background-image:url(pages/02.jpg);"></div>
<div style="background-image:url(pages/03.jpg);"></div>
<div style="background-image:url(pages/04.jpg);"></div>
<div style="background-image:url(pages/05.jpg);"></div>
<div style="background-image:url(pages/06.jpg);"></div>
</div>
<script type="text/javascript">
$(window).ready(function() {
$('#magazine').turn({
display: 'double',
acceleration: true,
gradients: !$.isTouch,
elevation:50,
when: {
turned: function(e, page) {
/*console.log('Current view: ', $(this).turn('view'));*/
}
}
});
});
$(window).bind('keydown', function(e){
if (e.keyCode==37)
$('#magazine').turn('previous');
else if (e.keyCode==39)
$('#magazine').turn('next');
});
</script>
</body>
</html>
================================================
FILE: demos/magazine_single/index.html
================================================
<!doctype html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="../../turn.min.js"></script>
<style type="text/css">
body{
background:#ccc;
}
#magazine{
width:576px;
height:752px;
}
#magazine .turn-page{
background-color:#ccc;
background-size:100% 100%;
}
</style>
</head>
<body>
<div id="magazine">
<div style="background-image:url(pages/01.jpg);"></div>
<div style="background-image:url(pages/02.jpg);"></div>
<div style="background-image:url(pages/03.jpg);"></div>
<div style="background-image:url(pages/04.jpg);"></div>
<div style="background-image:url(pages/05.jpg);"></div>
<div style="background-image:url(pages/06.jpg);"></div>
</div>
<script type="text/javascript">
$(window).ready(function() {
$('#magazine').turn({
display: 'single',
acceleration: true,
gradients: !$.isTouch,
elevation:50,
when: {
turned: function(e, page) {
/*console.log('Current view: ', $(this).turn('view'));*/
}
}
});
});
$(window).bind('keydown', function(e){
if (e.keyCode==37)
$('#magazine').turn('previous');
else if (e.keyCode==39)
$('#magazine').turn('next');
});
</script>
</body>
</html>
================================================
FILE: license.txt
================================================
turn.js 3rd release
www.turnjs.com
Copyright (c) 2012, Emmanuel Garcia
All rights reserved.
Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Any redistribution, use, or modification is done solely for personal
benefit and not for any commercial purpose or for monetary gain.
THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ''AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
================================================
FILE: readme.md
================================================

**Get the turn.js 4th release on [turnjs.com](http://www.turnjs.com/)**
### What's new in turn.js 4th release?
- Added option `autoCenter`
- Added option `zoom`
- Added property `animating`
- Added property `zoom`
- Added method `center`
- Added method `destroy`
- Added method `is`
- Added method `zoom`
- Added event `missing`
- Added event `zooming`
- Added class `.even`
- Added class `.fixed`
- Added class `.hard`
- Added class `.odd`
- Added class `.own-size`
- Added class `.sheet`
- Added the ignore attribute
- New turn.html4.js
- New scissors.js
- Changed the class `.turn-page` to `.page`
- Improved the animation frame generator with requestAnimationFrame
- Improved the animation speed for hard pages with CSS3 transitions
- Redesigned the event sequence to listen to only three events
- Fixed issue #79
- Fixed issue #91
- Fixed issue about the event order turning + turned
- Fixed issue about appending pages in wrong locations
Available only on [turnjs.com](http://www.turnjs.com/)
* * *
turn.js 3rd release
=========
### Make a flip book with HTML5
Turn.js is a plugin for jQuery that adds a beautiful transition similar to real pages in a book or magazine. It works in all modern browsers including touch devices.
### What's new?
- New `addPage` for creating pages dynamically.
- New `display` for single and double pages.
- Gradients for non-webkit browsers.
#### Usage
**CSS code:**
```css
#magazine{
width: 800px;
height: 400px;
}
#magazine .turn-page{
background-color:#ccc;
}
```
**HTML code:**
```html
<div id="magazine">
<div><span class="text">Page 1</span></div>
<div><span class="text">Page 2</span></div>
<div><span class="text">Page 3</span></div>
</div>
```
**JavaScript code:**
```javascript
$('#magazine').turn({gradients: true, acceleration: true});
```
#### Requirements
jQuery 1.7 or later
#### Browser support
* Chrome 12, Safari 5, Firefox 10, IE 9
#### License
Released under a non-commercial BSD license
[Full documentation](https://github.com/blasten/turn.js/wiki/Reference)
* * *
[turnjs.com](http://www.turnjs.com/)
================================================
FILE: turn.js
================================================
/**
* turn.js 3rd release
* www.turnjs.com
*
* Copyright (C) 2012, Emmanuel Garcia.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Any redistribution, use, or modification is done solely for personal
* benefit and not for any commercial purpose or for monetary gain.
*
**/
(function($) {
'use strict';
var has3d,
vendor ='',
PI = Math.PI,
A90 = PI/2,
isTouch = 'ontouchstart' in window,
events = (isTouch) ? {start: 'touchstart', move: 'touchmove', end: 'touchend'}
: {start: 'mousedown', move: 'mousemove', end: 'mouseup'},
// Contansts used for each corner
// tl * tr
// * *
// bl * br
corners = {
backward: ['bl', 'tl'],
forward: ['br', 'tr'],
all: ['tl', 'bl', 'tr', 'br']
},
displays = ['single', 'double'],
// Default options
turnOptions = {
// First page
page: 1,
// Enables gradients
gradients: true,
// Duration of transition in milliseconds
duration: 600,
// Enables hardware acceleration
acceleration: true,
// Display
display: 'double',
// Events
when: null
},
flipOptions = {
// Back page
folding: null,
// Corners
// backward: Activates both tl and bl corners
// forward: Activates both tr and br corners
// all: Activates all the corners
corners: 'forward',
// Size of the active zone of each corner
cornerSize: 100,
// Enables gradients
gradients: true,
// Duration of transition in milliseconds
duration: 600,
// Enables hardware acceleration
acceleration: true
},
// Number of pages in the DOM, minimum value: 6
pagesInDOM = 6,
pagePosition = {0: {top: 0, left: 0, right: 'auto', bottom: 'auto'},
1: {top: 0, right: 0, left: 'auto', bottom: 'auto'}},
// Gets basic attributes for a layer
divAtt = function(top, left, zIndex, overf) {
return {'css': {
position: 'absolute',
top: top,
left: left,
'overflow': overf || 'hidden',
'z-index': zIndex || 'auto'
}
};
},
// Gets a 2D point from a bezier curve of four points
bezier = function(p1, p2, p3, p4, t) {
var mum1 = 1 - t,
mum13 = mum1 * mum1 * mum1,
mu3 = t * t * t;
return point2D(Math.round(mum13*p1.x + 3*t*mum1*mum1*p2.x + 3*t*t*mum1*p3.x + mu3*p4.x),
Math.round(mum13*p1.y + 3*t*mum1*mum1*p2.y + 3*t*t*mum1*p3.y + mu3*p4.y));
},
// Converts an angle from degrees to radians
rad = function(degrees) {
return degrees/180*PI;
},
// Converts an angle from radians to degrees
deg = function(radians) {
return radians/PI*180;
},
// Gets a 2D point
point2D = function(x, y) {
return {x: x, y: y};
},
// Returns the traslate value
translate = function(x, y, use3d) {
return (has3d && use3d) ? ' translate3d(' + x + 'px,' + y + 'px, 0px) ' : ' translate(' + x + 'px, ' + y + 'px) ';
},
// Returns the rotation value
rotate = function(degrees) {
return ' rotate(' + degrees + 'deg) ';
},
// Checks if a property belongs to an object
has = function(property, object) {
return Object.prototype.hasOwnProperty.call(object, property);
},
// Gets the CSS3 vendor prefix
getPrefix = function() {
var vendorPrefixes = ['Moz','Webkit','Khtml','O','ms'],
len = vendorPrefixes.length,
vendor = '';
while (len--)
if ((vendorPrefixes[len] + 'Transform') in document.body.style)
vendor='-'+vendorPrefixes[len].toLowerCase()+'-';
return vendor;
},
// Adds gradients
gradient = function(obj, p0, p1, colors, numColors) {
var j, cols = [];
if (vendor=='-webkit-') {
for (j = 0; j<numColors; j++)
cols.push('color-stop('+colors[j][0]+', '+colors[j][1]+')');
obj.css({'background-image': '-webkit-gradient(linear, '+p0.x+'% '+p0.y+'%, '+p1.x+'% '+p1.y+'%, '+ cols.join(',') +' )'});
} else {
// This procedure makes the gradients for non-webkit browsers
// It will be reduced to one unique way for gradients in next versions
p0 = {x:p0.x/100 * obj.width(), y:p0.y/100 * obj.height()};
p1 = {x:p1.x/100 * obj.width(), y:p1.y/100 * obj.height()};
var dx = p1.x-p0.x,
dy = p1.y-p0.y,
angle = Math.atan2(dy, dx),
angle2 = angle - Math.PI/2,
diagonal = Math.abs(obj.width()*Math.sin(angle2)) + Math.abs(obj.height()*Math.cos(angle2)),
gradientDiagonal = Math.sqrt(dy*dy + dx*dx),
corner = point2D((p1.x<p0.x) ? obj.width() : 0, (p1.y<p0.y) ? obj.height() : 0),
slope = Math.tan(angle),
inverse = -1/slope,
x = (inverse*corner.x - corner.y - slope*p0.x + p0.y) / (inverse-slope),
c = {x: x, y: inverse*x - inverse*corner.x + corner.y},
segA = (Math.sqrt( Math.pow(c.x-p0.x,2) + Math.pow(c.y-p0.y,2)));
for (j = 0; j<numColors; j++)
cols.push(' '+colors[j][1]+' '+(( segA + gradientDiagonal*colors[j][0] )*100/diagonal)+'%');
obj.css({'background-image': vendor+'linear-gradient(' + (-angle) + 'rad,' + cols.join(',') + ')'});
}
},
turnMethods = {
// Singleton constructor
// $('#selector').turn([options]);
init: function(opts) {
// Define constants
if (has3d===undefined) {
has3d = 'WebKitCSSMatrix' in window || 'MozPerspective' in document.body.style;
vendor = getPrefix();
}
var i, data = this.data(), ch = this.children();
opts = $.extend({width: this.width(), height: this.height()}, turnOptions, opts);
data.opts = opts;
data.pageObjs = {};
data.pages = {};
data.pageWrap = {};
data.pagePlace = {};
data.pageMv = [];
data.totalPages = opts.pages || 0;
if (opts.when)
for (i in opts.when)
if (has(i, opts.when))
this.bind(i, opts.when[i]);
this.css({position: 'relative', width: opts.width, height: opts.height});
this.turn('display', opts.display);
if (has3d && !isTouch && opts.acceleration)
this.transform(translate(0, 0, true));
for (i = 0; i<ch.length; i++)
this.turn('addPage', ch[i], i+1);
this.turn('page', opts.page);
// allow setting active corners as an option
corners = $.extend({}, corners, opts.corners);
// Event listeners
$(this).bind(events.start, function(e) {
for (var page in data.pages)
if (has(page, data.pages) && flipMethods._eventStart.call(data.pages[page], e)===false)
return false;
});
$(document).bind(events.move, function(e) {
for (var page in data.pages)
if (has(page, data.pages))
flipMethods._eventMove.call(data.pages[page], e);
}).
bind(events.end, function(e) {
for (var page in data.pages)
if (has(page, data.pages))
flipMethods._eventEnd.call(data.pages[page], e);
});
data.done = true;
return this;
},
// Adds a page from external data
addPage: function(element, page) {
var incPages = false,
data = this.data(),
lastPage = data.totalPages+1;
if (page) {
if (page==lastPage) {
page = lastPage;
incPages = true;
} else if (page>lastPage)
throw new Error ('It is impossible to add the page "'+page+'", the maximum value is: "'+lastPage+'"');
} else {
page = lastPage;
incPages = true;
}
if (page>=1 && page<=lastPage) {
// Stop animations
if (data.done) this.turn('stop');
// Move pages if it's necessary
if (page in data.pageObjs)
turnMethods._movePages.call(this, page, 1);
// Update number of pages
if (incPages)
data.totalPages = lastPage;
// Add element
data.pageObjs[page] = $(element).addClass('turn-page p' + page);
// Add page
turnMethods._addPage.call(this, page);
// Update view
if (data.done)
this.turn('update');
turnMethods._removeFromDOM.call(this);
}
return this;
},
// Adds a page from internal data
_addPage: function(page) {
var data = this.data(),
element = data.pageObjs[page];
if (element)
if (turnMethods._necessPage.call(this, page)) {
if (!data.pageWrap[page]) {
var pageWidth = (data.display=='double') ? this.width()/2 : this.width(),
pageHeight = this.height();
element.css({width:pageWidth, height:pageHeight});
// Place
data.pagePlace[page] = page;
// Wrapper
data.pageWrap[page] = $('<div/>', {'class': 'turn-page-wrapper',
page: page,
css: {position: 'absolute',
overflow: 'hidden',
width: pageWidth,
height: pageHeight}}).
css(pagePosition[(data.display=='double') ? page%2 : 0]);
// Append to this
this.append(data.pageWrap[page]);
// Move data.pageObjs[page] (element) to wrapper
data.pageWrap[page].prepend(data.pageObjs[page]);
}
// If the page is in the current view, create the flip effect
if (!page || turnMethods._setPageLoc.call(this, page)==1)
turnMethods._makeFlip.call(this, page);
} else {
// Place
data.pagePlace[page] = 0;
// Remove element from the DOM
if (data.pageObjs[page])
data.pageObjs[page].remove();
}
},
// Checks if a page is in memory
hasPage: function(page) {
return page in this.data().pageObjs;
},
// Prepares the flip effect for a page
_makeFlip: function(page) {
var data = this.data();
if (!data.pages[page] && data.pagePlace[page]==page) {
var single = data.display=='single',
even = page%2;
data.pages[page] = data.pageObjs[page].
css({width: (single) ? this.width() : this.width()/2, height: this.height()}).
flip({page: page,
next: (single && page === data.totalPages) ? page -1 : ((even || single) ? page+1 : page-1),
turn: this,
duration: data.opts.duration,
acceleration : data.opts.acceleration,
corners: (single) ? 'all' : ((even) ? 'forward' : 'backward'),
backGradient: data.opts.gradients,
frontGradient: data.opts.gradients
}).
flip('disable', data.disabled).
bind('pressed', turnMethods._pressed).
bind('released', turnMethods._released).
bind('start', turnMethods._start).
bind('end', turnMethods._end).
bind('flip', turnMethods._flip);
}
return data.pages[page];
},
// Makes pages within a range
_makeRange: function() {
var page,
data = this.data(),
range = this.turn('range');
for (page = range[0]; page<=range[1]; page++)
turnMethods._addPage.call(this, page);
},
// Returns a range of `pagesInDOM` pages that should be in the DOM
// Example:
// - page of the current view, return true
// * page is in the range, return true
// 0 page is not in the range, return false
//
// 1 2-3 4-5 6-7 8-9 10-11 12-13
// ** ** -- ** **
range: function(page) {
var remainingPages, left, right,
data = this.data();
page = page || data.tpage || data.page;
var view = turnMethods._view.call(this, page);
if (page<1 || page>data.totalPages)
throw new Error ('"'+page+'" is not a page for range');
view[1] = view[1] || view[0];
if (view[0]>=1 && view[1]<=data.totalPages) {
remainingPages = Math.floor((pagesInDOM-2)/2);
if (data.totalPages-view[1] > view[0]) {
left = Math.min(view[0]-1, remainingPages);
right = 2*remainingPages-left;
} else {
right = Math.min(data.totalPages-view[1], remainingPages);
left = 2*remainingPages-right;
}
} else {
left = pagesInDOM-1;
right = pagesInDOM-1;
}
return [Math.max(1, view[0]-left), Math.min(data.totalPages, view[1]+right)];
},
// Detects if a page is within the range of `pagesInDOM` from the current view
_necessPage: function(page) {
if (page===0)
return true;
var range = this.turn('range');
return page>=range[0] && page<=range[1];
},
// Releases memory by removing pages from the DOM
_removeFromDOM: function() {
var page, data = this.data();
for (page in data.pageWrap)
if (has(page, data.pageWrap) && !turnMethods._necessPage.call(this, page))
turnMethods._removePageFromDOM.call(this, page);
},
// Removes a page from DOM and its internal references
_removePageFromDOM: function(page) {
var data = this.data();
if (data.pages[page]) {
var dd = data.pages[page].data();
if (dd.f && dd.f.fwrapper)
dd.f.fwrapper.remove();
data.pages[page].remove();
delete data.pages[page];
}
if (data.pageObjs[page])
data.pageObjs[page].remove();
if (data.pageWrap[page]) {
data.pageWrap[page].remove();
delete data.pageWrap[page];
}
delete data.pagePlace[page];
},
// Removes a page
removePage: function(page) {
var data = this.data();
if (data.pageObjs[page]) {
// Stop animations
this.turn('stop');
// Remove `page`
turnMethods._removePageFromDOM.call(this, page);
delete data.pageObjs[page];
// Move the pages behind `page`
turnMethods._movePages.call(this, page, -1);
// Resize the size of this magazine
data.totalPages = data.totalPages-1;
turnMethods._makeRange.call(this);
// Check the current view
if (data.page>data.totalPages)
this.turn('page', data.totalPages);
}
return this;
},
// Moves pages
_movePages: function(from, change) {
var page,
data = this.data(),
single = data.display=='single',
move = function(page) {
var next = page + change,
odd = next%2;
if (data.pageObjs[page])
data.pageObjs[next] = data.pageObjs[page].removeClass('page' + page).addClass('page' + next);
if (data.pagePlace[page] && data.pageWrap[page]) {
data.pagePlace[next] = next;
data.pageWrap[next] = data.pageWrap[page].css(pagePosition[(single) ? 0 : odd]).attr('page', next);
if (data.pages[page])
data.pages[next] = data.pages[page].flip('options', {
page: next,
next: (single || odd) ? next+1 : next-1,
corners: (single) ? 'all' : ((odd) ? 'forward' : 'backward')
});
if (change) {
delete data.pages[page];
delete data.pagePlace[page];
delete data.pageObjs[page];
delete data.pageWrap[page];
delete data.pageObjs[page];
}
}
};
if (change>0)
for (page=data.totalPages; page>=from; page--) move(page);
else
for (page=from; page<=data.totalPages; page++) move(page);
},
// Sets or Gets the display mode
display: function(display) {
var data = this.data(),
currentDisplay = data.display;
if (display) {
if ($.inArray(display, displays)==-1)
throw new Error ('"'+display + '" is not a value for display');
if (display=='single') {
if (!data.pageObjs[0]) {
this.turn('stop').
css({'overflow': 'hidden'});
data.pageObjs[0] = $('<div />', {'class': 'turn-page p-temporal'}).
css({width: this.width(), height: this.height()}).
appendTo(this);
}
} else {
if (data.pageObjs[0]) {
this.turn('stop').
css({'overflow': ''});
data.pageObjs[0].remove();
delete data.pageObjs[0];
}
}
data.display = display;
if (currentDisplay) {
var size = this.turn('size');
turnMethods._movePages.call(this, 1, 0);
this.turn('size', size.width, size.height).
turn('update');
}
return this;
} else
return currentDisplay;
},
// Detects if the pages are being animated
animating: function() {
return this.data().pageMv.length>0;
},
// Disables and enables the effect
disable: function(bool) {
var page,
data = this.data(),
view = this.turn('view');
data.disabled = bool===undefined || bool===true;
for (page in data.pages)
if (has(page, data.pages))
data.pages[page].flip('disable', bool ? $.inArray(page, view) : false );
return this;
},
// Gets and sets the size
size: function(width, height) {
if (width && height) {
var data = this.data(), pageWidth = (data.display=='double') ? width/2 : width, page;
this.css({width: width, height: height});
if (data.pageObjs[0])
data.pageObjs[0].css({width: pageWidth, height: height});
for (page in data.pageWrap) {
if (!has(page, data.pageWrap)) continue;
data.pageObjs[page].css({width: pageWidth, height: height});
data.pageWrap[page].css({width: pageWidth, height: height});
if (data.pages[page])
data.pages[page].css({width: pageWidth, height: height});
}
this.turn('resize');
return this;
} else {
return {width: this.width(), height: this.height()};
}
},
// Resizes each page
resize: function() {
var page, data = this.data();
if (data.pages[0]) {
data.pageWrap[0].css({left: -this.width()});
data.pages[0].flip('resize', true);
}
for (page = 1; page <= data.totalPages; page++)
if (data.pages[page])
data.pages[page].flip('resize', true);
},
// Removes an animation from the cache
_removeMv: function(page) {
var i, data = this.data();
for (i=0; i<data.pageMv.length; i++)
if (data.pageMv[i]==page) {
data.pageMv.splice(i, 1);
return true;
}
return false;
},
// Adds an animation to the cache
_addMv: function(page) {
var data = this.data();
turnMethods._removeMv.call(this, page);
data.pageMv.push(page);
},
// Gets indexes for a view
_view: function(page) {
var data = this.data();
page = page || data.page;
if (data.display=='double')
return (page%2) ? [page-1, page] : [page, page+1];
else
return [page];
},
// Gets a view
view: function(page) {
var data = this.data(), view = turnMethods._view.call(this, page);
return (data.display=='double') ? [(view[0]>0) ? view[0] : 0, (view[1]<=data.totalPages) ? view[1] : 0]
: [(view[0]>0 && view[0]<=data.totalPages) ? view[0] : 0];
},
// Stops animations
stop: function(ok) {
var i, opts, data = this.data(), pages = data.pageMv;
data.pageMv = [];
if (data.tpage) {
data.page = data.tpage;
delete data['tpage'];
}
for (i in pages) {
if (!has(i, pages)) continue;
opts = data.pages[pages[i]].data().f.opts;
flipMethods._moveFoldingPage.call(data.pages[pages[i]], null);
data.pages[pages[i]].flip('hideFoldedPage');
data.pagePlace[opts.next] = opts.next;
if (opts.force) {
opts.next = (opts.page%2===0) ? opts.page-1 : opts.page+1;
delete opts['force'];
}
}
this.turn('update');
return this;
},
// Gets and sets the number of pages
pages: function(pages) {
var data = this.data();
if (pages) {
if (pages<data.totalPages) {
for (var page = pages+1; page<=data.totalPages; page++)
this.turn('removePage', page);
if (this.turn('page')>pages)
this.turn('page', pages);
}
data.totalPages = pages;
return this;
} else
return data.totalPages;
},
// Sets a page without effect
_fitPage: function(page, ok) {
var data = this.data(), newView = this.turn('view', page);
if (data.page!=page) {
this.trigger('turning', [page, newView]);
if ($.inArray(1, newView)!=-1) this.trigger('first');
if ($.inArray(data.totalPages, newView)!=-1) this.trigger('last');
}
if (!data.pageObjs[page])
return;
data.tpage = page;
this.turn('stop', ok);
turnMethods._removeFromDOM.call(this);
turnMethods._makeRange.call(this);
this.trigger('turned', [page, newView]);
},
// Turns to a page
_turnPage: function(page) {
var current, next,
data = this.data(),
view = this.turn('view'),
newView = this.turn('view', page);
if (data.page!=page) {
this.trigger('turning', [page, newView]);
if ($.inArray(1, newView)!=-1) this.trigger('first');
if ($.inArray(data.totalPages, newView)!=-1) this.trigger('last');
}
if (!data.pageObjs[page])
return;
data.tpage = page;
this.turn('stop');
turnMethods._makeRange.call(this);
if (data.display=='single') {
current = view[0];
next = newView[0];
} else if (view[1] && page>view[1]) {
current = view[1];
next = newView[0];
} else if (view[0] && page<view[0]) {
current = view[0];
next = newView[1];
}
if (data.pages[current]) {
var opts = data.pages[current].data().f.opts;
data.tpage = next;
if (opts.next!=next) {
opts.next = next;
data.pagePlace[next] = opts.page;
opts.force = true;
}
if (data.display=='single')
data.pages[current].flip('turnPage', (newView[0] > view[0]) ? 'br' : 'bl');
else
data.pages[current].flip('turnPage');
}
},
// Gets and sets a page
page: function(page) {
page = parseInt(page, 10);
var data = this.data();
if (page>0 && page<=data.totalPages) {
if (!data.done || $.inArray(page, this.turn('view'))!=-1)
turnMethods._fitPage.call(this, page);
else
turnMethods._turnPage.call(this, page);
return this;
} else
return data.page;
},
// Turns to the next view
next: function() {
var data = this.data();
return this.turn('page', turnMethods._view.call(this, data.page).pop() + 1);
},
// Turns to the previous view
previous: function() {
var data = this.data();
return this.turn('page', turnMethods._view.call(this, data.page).shift() - 1);
},
// Adds a motion to the internal list
_addMotionPage: function() {
var opts = $(this).data().f.opts,
turn = opts.turn,
dd = turn.data();
opts.pageMv = opts.page;
turnMethods._addMv.call(turn, opts.pageMv);
dd.pagePlace[opts.next] = opts.page;
turn.turn('update');
},
// This event is called in context of flip
_start: function(e, opts, corner) {
var data = opts.turn.data(),
event = $.Event('start');
e.stopPropagation();
opts.turn.trigger(event, [opts, corner]);
if (event.isDefaultPrevented()) {
e.preventDefault();
return;
}
if (data.display=='single') {
var left = corner.charAt(1)=='l';
if ((opts.page==1 && left) || (opts.page==data.totalPages && !left))
e.preventDefault();
else {
if (left) {
opts.next = (opts.next<opts.page) ? opts.next : opts.page-1;
opts.force = true;
} else
opts.next = (opts.next>opts.page) ? opts.next : opts.page+1;
}
}
turnMethods._addMotionPage.call(this);
},
// This event is called in context of flip
_end: function(e, turned) {
var that = $(this),
data = that.data().f,
opts = data.opts,
turn = opts.turn,
dd = turn.data();
e.stopPropagation();
if (turned || dd.tpage) {
if (dd.tpage==opts.next || dd.tpage==opts.page) {
delete dd['tpage'];
turnMethods._fitPage.call(turn, dd.tpage || opts.next, true);
}
} else {
turnMethods._removeMv.call(turn, opts.pageMv);
turn.turn('update');
}
},
// This event is called in context of flip
_pressed: function() {
var page,
that = $(this),
data = that.data().f,
turn = data.opts.turn,
pages = turn.data().pages;
for (page in pages)
if (page!=data.opts.page)
pages[page].flip('disable', true);
return data.time = new Date().getTime();
},
// This event is called in context of flip
_released: function(e, point) {
var that = $(this),
data = that.data().f;
e.stopPropagation();
if ((new Date().getTime())-data.time<200 || point.x<0 || point.x>$(this).width()) {
e.preventDefault();
data.opts.turn.data().tpage = data.opts.next;
data.opts.turn.turn('update');
$(that).flip('turnPage');
}
},
// This event is called in context of flip
_flip: function() {
var opts = $(this).data().f.opts;
opts.turn.trigger('turn', [opts.next]);
},
// Calculate the z-index value for pages during the animation
calculateZ: function(mv) {
var i, page, nextPage, placePage, dpage,
that = this,
data = this.data(),
view = this.turn('view'),
currentPage = view[0] || view[1],
r = {pageZ: {}, partZ: {}, pageV: {}},
addView = function(page) {
var view = that.turn('view', page);
if (view[0]) r.pageV[view[0]] = true;
if (view[1]) r.pageV[view[1]] = true;
};
for (i = 0; i<mv.length; i++) {
page = mv[i];
nextPage = data.pages[page].data().f.opts.next;
placePage = data.pagePlace[page];
addView(page);
addView(nextPage);
dpage = (data.pagePlace[nextPage]==nextPage) ? nextPage : page;
r.pageZ[dpage] = data.totalPages - Math.abs(currentPage-dpage);
r.partZ[placePage] = data.totalPages*2 + Math.abs(currentPage-dpage);
}
return r;
},
// Updates the z-index and display property of every page
update: function() {
var page,
data = this.data();
if (data.pageMv.length && data.pageMv[0]!==0) {
// Update motion
var apage,
pos = this.turn('calculateZ', data.pageMv),
view = this.turn('view', data.tpage);
if (data.pagePlace[view[0]]==view[0]) apage = view[0];
else if (data.pagePlace[view[1]]==view[1]) apage = view[1];
for (page in data.pageWrap) {
if (!has(page, data.pageWrap)) continue;
data.pageWrap[page].css({display: (pos.pageV[page]) ? '' : 'none', 'z-index': pos.pageZ[page] || 0});
if (data.pages[page]) {
data.pages[page].flip('z', pos.partZ[page] || null);
if (pos.pageV[page])
data.pages[page].flip('resize');
if (data.tpage)
data.pages[page].flip('disable', true); // data.disabled || page!=apage
}
}
} else {
// Update static pages
for (page in data.pageWrap) {
if (!has(page, data.pageWrap)) continue;
var pageLocation = turnMethods._setPageLoc.call(this, page);
if (data.pages[page])
data.pages[page].flip('disable', data.disabled || pageLocation!=1).flip('z', null);
}
}
},
// Sets the z-index and display property of a page
// It depends on the current view
_setPageLoc: function(page) {
var data = this.data(),
view = this.turn('view');
if (page==view[0] || page==view[1]) {
data.pageWrap[page].css({'z-index': data.totalPages, display: ''});
return 1;
} else if ((data.display=='single' && page==view[0]+1) || (data.display=='double' && page==view[0]-2 || page==view[1]+2)) {
data.pageWrap[page].css({'z-index': data.totalPages-1, display: ''});
return 2;
} else {
data.pageWrap[page].css({'z-index': 0, display: 'none'});
return 0;
}
}
},
// Methods and properties for the flip page effect
flipMethods = {
// Constructor
init: function(opts) {
if (opts.gradients) {
opts.frontGradient = true;
opts.backGradient = true;
}
this.data({f: {}});
this.flip('options', opts);
flipMethods._addPageWrapper.call(this);
return this;
},
setData: function(d) {
var data = this.data();
data.f = $.extend(data.f, d);
return this;
},
options: function(opts) {
var data = this.data().f;
if (opts) {
flipMethods.setData.call(this, {opts: $.extend({}, data.opts || flipOptions, opts) });
return this;
} else
return data.opts;
},
z: function(z) {
var data = this.data().f;
data.opts['z-index'] = z;
data.fwrapper.css({'z-index': z || parseInt(data.parent.css('z-index'), 10) || 0});
return this;
},
_cAllowed: function() {
return corners[this.data().f.opts.corners] || this.data().f.opts.corners;
},
_cornerActivated: function(e) {
if (e.originalEvent === undefined) {
return false;
}
e = (isTouch) ? e.originalEvent.touches : [e];
var data = this.data().f,
pos = data.parent.offset(),
width = this.width(),
height = this.height(),
c = {x: Math.max(0, e[0].pageX-pos.left), y: Math.max(0, e[0].pageY-pos.top)},
csz = data.opts.cornerSize,
allowedCorners = flipMethods._cAllowed.call(this);
if (c.x<=0 || c.y<=0 || c.x>=width || c.y>=height) return false;
if (c.y<csz) c.corner = 't';
else if (c.y>=height-csz) c.corner = 'b';
else return false;
if (c.x<=csz) c.corner+= 'l';
else if (c.x>=width-csz) c.corner+= 'r';
else return false;
return ($.inArray(c.corner, allowedCorners)==-1) ? false : c;
},
_c: function(corner, opts) {
opts = opts || 0;
return ({tl: point2D(opts, opts),
tr: point2D(this.width()-opts, opts),
bl: point2D(opts, this.height()-opts),
br: point2D(this.width()-opts, this.height()-opts)})[corner];
},
_c2: function(corner) {
return {tl: point2D(this.width()*2, 0),
tr: point2D(-this.width(), 0),
bl: point2D(this.width()*2, this.height()),
br: point2D(-this.width(), this.height())}[corner];
},
_foldingPage: function(corner) {
var opts = this.data().f.opts;
if (opts.folding) return opts.folding;
else if(opts.turn) {
var data = opts.turn.data();
if (data.display == 'single')
return (data.pageObjs[opts.next]) ? data.pageObjs[0] : null;
else
return data.pageObjs[opts.next];
}
},
_backGradient: function() {
var data = this.data().f,
turn = data.opts.turn,
gradient = data.opts.backGradient &&
(!turn || turn.data().display=='single' || (data.opts.page!=2 && data.opts.page!=turn.data().totalPages-1) );
if (gradient && !data.bshadow)
data.bshadow = $('<div/>', divAtt(0, 0, 1)).
css({'position': '', width: this.width(), height: this.height()}).
appendTo(data.parent);
return gradient;
},
resize: function(full) {
var data = this.data().f,
width = this.width(),
height = this.height(),
size = Math.round(Math.sqrt(Math.pow(width, 2)+Math.pow(height, 2)));
if (full) {
data.wrapper.css({width: size, height: size});
data.fwrapper.css({width: size, height: size}).
children(':first-child').
css({width: width, height: height});
data.fpage.css({width: height, height: width});
if (data.opts.frontGradient)
data.ashadow.css({width: height, height: width});
if (flipMethods._backGradient.call(this))
data.bshadow.css({width: width, height: height});
}
if (data.parent.is(':visible')) {
data.fwrapper.css({top: data.parent.offset().top,
left: data.parent.offset().left});
if (data.opts.turn)
data.fparent.css({top: -data.opts.turn.offset().top, left: -data.opts.turn.offset().left});
}
this.flip('z', data.opts['z-index']);
},
// Prepares the page by adding a general wrapper and another objects
_addPageWrapper: function() {
var att,
data = this.data().f,
parent = this.parent();
if (!data.wrapper) {
var left = this.css('left'),
top = this.css('top'),
width = this.width(),
height = this.height(),
size = Math.round(Math.sqrt(Math.pow(width, 2)+Math.pow(height, 2)));
data.parent = parent;
data.fparent = (data.opts.turn) ? data.opts.turn.data().fparent : $('#turn-fwrappers');
if (!data.fparent) {
var fparent = $('<div/>', {css: {'pointer-events': 'none'}}).hide();
fparent.data().flips = 0;
if (data.opts.turn) {
fparent.css(divAtt(-data.opts.turn.offset().top, -data.opts.turn.offset().left, 'auto', 'visible').css).
appendTo(data.opts.turn);
data.opts.turn.data().fparent = fparent;
} else {
fparent.css(divAtt(0, 0, 'auto', 'visible').css).
attr('id', 'turn-fwrappers').
appendTo($('body'));
}
data.fparent = fparent;
}
this.css({position: 'absolute', top: 0, left: 0, bottom: 'auto', right: 'auto'});
data.wrapper = $('<div/>', divAtt(0, 0, this.css('z-index'))).
appendTo(parent).
prepend(this);
data.fwrapper = $('<div/>', divAtt(parent.offset().top, parent.offset().left)).
hide().
appendTo(data.fparent);
data.fpage = $('<div/>', {css: {cursor: 'default'}}).
appendTo($('<div/>', divAtt(0, 0, 0, 'visible')).
appendTo(data.fwrapper));
if (data.opts.frontGradient)
data.ashadow = $('<div/>', divAtt(0, 0, 1)).
appendTo(data.fpage);
// Save data
flipMethods.setData.call(this, data);
// Set size
flipMethods.resize.call(this, true);
}
},
// Takes a 2P point from the screen and applies the transformation
_fold: function(point) {
var that = this,
a = 0,
alpha = 0,
beta,
px,
gradientEndPointA,
gradientEndPointB,
gradientStartV,
gradientSize,
gradientOpacity,
mv = point2D(0, 0),
df = point2D(0, 0),
tr = point2D(0, 0),
width = this.width(),
height = this.height(),
folding = flipMethods._foldingPage.call(this),
tan = Math.tan(alpha),
data = this.data().f,
ac = data.opts.acceleration,
h = data.wrapper.height(),
o = flipMethods._c.call(this, point.corner),
top = point.corner.substr(0, 1) == 't',
left = point.corner.substr(1, 1) == 'l',
compute = function() {
var rel = point2D((o.x) ? o.x - point.x : point.x, (o.y) ? o.y - point.y : point.y),
tan = (Math.atan2(rel.y, rel.x)),
middle;
alpha = A90 - tan;
a = deg(alpha);
middle = point2D((left) ? width - rel.x/2 : point.x + rel.x/2, rel.y/2);
var gamma = alpha - Math.atan2(middle.y, middle.x),
distance = Math.max(0, Math.sin(gamma) * Math.sqrt(Math.pow(middle.x, 2) + Math.pow(middle.y, 2)));
tr = point2D(distance * Math.sin(alpha), distance * Math.cos(alpha));
if (alpha > A90) {
tr.x = tr.x + Math.abs(tr.y * Math.tan(tan));
tr.y = 0;
if (Math.round(tr.x*Math.tan(PI-alpha)) < height) {
point.y = Math.sqrt(Math.pow(height, 2)+2 * middle.x * rel.x);
if (top) point.y = height - point.y;
return compute();
}
}
if (alpha>A90) {
var beta = PI-alpha, dd = h - height/Math.sin(beta);
mv = point2D(Math.round(dd*Math.cos(beta)), Math.round(dd*Math.sin(beta)));
if (left) mv.x = - mv.x;
if (top) mv.y = - mv.y;
}
px = Math.round(tr.y/Math.tan(alpha) + tr.x);
var side = width - px,
sideX = side*Math.cos(alpha*2),
sideY = side*Math.sin(alpha*2);
df = point2D(Math.round( (left ? side -sideX : px+sideX)), Math.round((top) ? sideY : height - sideY));
// GRADIENTS
gradientSize = side*Math.sin(alpha);
var endingPoint = flipMethods._c2.call(that, point.corner),
far = Math.sqrt(Math.pow(endingPoint.x-point.x, 2)+Math.pow(endingPoint.y-point.y, 2));
gradientOpacity = (far<width) ? far/width : 1;
if (data.opts.frontGradient) {
gradientStartV = gradientSize>100 ? (gradientSize-100)/gradientSize : 0;
gradientEndPointA = point2D(gradientSize*Math.sin(A90-alpha)/height*100, gradientSize*Math.cos(A90-alpha)/width*100);
if (top) gradientEndPointA.y = 100-gradientEndPointA.y;
if (left) gradientEndPointA.x = 100-gradientEndPointA.x;
}
if (flipMethods._backGradient.call(that)) {
gradientEndPointB = point2D(gradientSize*Math.sin(alpha)/width*100, gradientSize*Math.cos(alpha)/height*100);
if (!left) gradientEndPointB.x = 100-gradientEndPointB.x;
if (!top) gradientEndPointB.y = 100-gradientEndPointB.y;
}
//
tr.x = Math.round(tr.x);
tr.y = Math.round(tr.y);
return true;
},
transform = function(tr, c, x, a) {
var f = ['0', 'auto'], mvW = (width-h)*x[0]/100, mvH = (height-h)*x[1]/100,
v = {left: f[c[0]], top: f[c[1]], right: f[c[2]], bottom: f[c[3]]},
aliasingFk = (a!=90 && a!=-90) ? (left ? -1 : 1) : 0;
x = x[0] + '% ' + x[1] + '%';
that.css(v).transform(rotate(a) + translate(tr.x + aliasingFk, tr.y, ac), x);
data.fpage.parent().css(v);
data.wrapper.transform(translate(-tr.x + mvW-aliasingFk, -tr.y + mvH, ac) + rotate(-a), x);
data.fwrapper.transform(translate(-tr.x + mv.x + mvW, -tr.y + mv.y + mvH, ac) + rotate(-a), x);
data.fpage.parent().transform(rotate(a) + translate(tr.x + df.x - mv.x, tr.y + df.y - mv.y, ac), x);
if (data.opts.frontGradient)
gradient(data.ashadow,
point2D(left?100:0, top?100:0),
point2D(gradientEndPointA.x, gradientEndPointA.y),
[[gradientStartV, 'rgba(0,0,0,0)'],
[((1-gradientStartV)*0.8)+gradientStartV, 'rgba(0,0,0,'+(0.2*gradientOpacity)+')'],
[1, 'rgba(255,255,255,'+(0.2*gradientOpacity)+')']],
3,
alpha);
if (flipMethods._backGradient.call(that))
gradient(data.bshadow,
point2D(left?0:100, top?0:100),
point2D(gradientEndPointB.x, gradientEndPointB.y),
[[0.8, 'rgba(0,0,0,0)'],
[1, 'rgba(0,0,0,'+(0.3*gradientOpacity)+')'],
[1, 'rgba(0,0,0,0)']],
3);
};
switch (point.corner) {
case 'tl' :
point.x = Math.max(point.x, 1);
compute();
transform(tr, [1,0,0,1], [100, 0], a);
data.fpage.transform(translate(-height, -width, ac) + rotate(90-a*2) , '100% 100%');
folding.transform(rotate(90) + translate(0, -height, ac), '0% 0%');
break;
case 'tr' :
point.x = Math.min(point.x, width-1);
compute();
transform(point2D(-tr.x, tr.y), [0,0,0,1], [0, 0], -a);
data.fpage.transform(translate(0, -width, ac) + rotate(-90+a*2) , '0% 100%');
folding.transform(rotate(270) + translate(-width, 0, ac), '0% 0%');
break;
case 'bl' :
point.x = Math.max(point.x, 1);
compute();
transform(point2D(tr.x, -tr.y), [1,1,0,0], [100, 100], -a);
data.fpage.transform(translate(-height, 0, ac) + rotate(-90+a*2), '100% 0%');
folding.transform(rotate(270) + translate(-width, 0, ac), '0% 0%');
break;
case 'br' :
point.x = Math.min(point.x, width-1);
compute();
transform(point2D(-tr.x, -tr.y), [0,1,1,0], [0, 100], a);
data.fpage.transform(rotate(90-a*2), '0% 0%');
folding.transform(rotate(90) + translate(0, -height, ac), '0% 0%');
break;
}
data.point = point;
},
_moveFoldingPage: function(bool) {
var data = this.data().f,
folding = flipMethods._foldingPage.call(this);
if (folding) {
if (bool) {
if (!data.fpage.children()[data.ashadow? '1' : '0']) {
flipMethods.setData.call(this, {backParent: folding.parent()});
data.fpage.prepend(folding);
}
} else {
if (data.backParent)
data.backParent.prepend(folding);
}
}
},
_showFoldedPage: function(c, animate) {
var folding = flipMethods._foldingPage.call(this),
dd = this.data(),
data = dd.f;
if (!data.point || data.point.corner!=c.corner) {
var event = $.Event('start');
this.trigger(event, [data.opts, c.corner]);
if (event.isDefaultPrevented())
return false;
}
if (folding) {
if (animate) {
var that = this, point = (data.point && data.point.corner==c.corner) ? data.point : flipMethods._c.call(this, c.corner, 1);
this.animatef({from: [point.x, point.y], to:[c.x, c.y], duration: 500, frame: function(v) {
c.x = Math.round(v[0]);
c.y = Math.round(v[1]);
flipMethods._fold.call(that, c);
}});
} else {
flipMethods._fold.call(this, c);
if (dd.effect && !dd.effect.turning)
this.animatef(false);
}
if (!data.fwrapper.is(':visible')) {
data.fparent.show().data().flips++;
flipMethods._moveFoldingPage.call(this, true);
data.fwrapper.show();
if (data.bshadow)
data.bshadow.show();
}
return true;
}
return false;
},
hide: function() {
var data = this.data().f,
folding = flipMethods._foldingPage.call(this);
if ((--data.fparent.data().flips)===0)
data.fparent.hide();
this.css({left: 0, top: 0, right: 'auto', bottom: 'auto'}).transform('', '0% 100%');
data.wrapper.transform('', '0% 100%');
data.fwrapper.hide();
if (data.bshadow)
data.bshadow.hide();
folding.transform('', '0% 0%');
return this;
},
hideFoldedPage: function(animate) {
var data = this.data().f;
if (!data.point) return;
var that = this,
p1 = data.point,
hide = function() {
data.point = null;
that.flip('hide');
that.trigger('end', [false]);
};
if (animate) {
var p4 = flipMethods._c.call(this, p1.corner),
top = (p1.corner.substr(0,1)=='t'),
delta = (top) ? Math.min(0, p1.y-p4.y)/2 : Math.max(0, p1.y-p4.y)/2,
p2 = point2D(p1.x, p1.y+delta),
p3 = point2D(p4.x, p4.y-delta);
this.animatef({
from: 0,
to: 1,
frame: function(v) {
var np = bezier(p1, p2, p3, p4, v);
p1.x = np.x;
p1.y = np.y;
flipMethods._fold.call(that, p1);
},
complete: hide,
duration: 800,
hiding: true
});
} else {
this.animatef(false);
hide();
}
},
turnPage: function(corner) {
var that = this,
data = this.data().f;
corner = {corner: (data.corner) ? data.corner.corner : corner || flipMethods._cAllowed.call(this)[0]};
var p1 = data.point || flipMethods._c.call(this, corner.corner, (data.opts.turn) ? data.opts.turn.data().opts.elevation : 0),
p4 = flipMethods._c2.call(this, corner.corner);
this.trigger('flip').
animatef({
from: 0,
to: 1,
frame: function(v) {
var np = bezier(p1, p1, p4, p4, v);
corner.x = np.x;
corner.y = np.y;
flipMethods._showFoldedPage.call(that, corner);
},
complete: function() {
that.trigger('end', [true]);
},
duration: data.opts.duration,
turning: true
});
data.corner = null;
},
moving: function() {
return 'effect' in this.data();
},
isTurning: function() {
return this.flip('moving') && this.data().effect.turning;
},
_eventStart: function(e) {
var data = this.data().f;
if (!data.disabled && !this.flip('isTurning')) {
data.corner = flipMethods._cornerActivated.call(this, e);
if (data.corner && flipMethods._foldingPage.call(this, data.corner)) {
flipMethods._moveFoldingPage.call(this, true);
this.trigger('pressed', [data.point]);
return false;
} else
data.corner = null;
}
},
_eventMove: function(e) {
var data = this.data().f;
if (!data.disabled) {
e = (isTouch) ? e.originalEvent.touches : [e];
if (data.corner) {
var pos = data.parent.offset();
data.corner.x = e[0].pageX-pos.left;
data.corner.y = e[0].pageY-pos.top;
flipMethods._showFoldedPage.call(this, data.corner);
} else if (!this.data().effect && this.is(':visible')) { // roll over
var corner = flipMethods._cornerActivated.call(this, e[0]);
if (corner) {
var origin = flipMethods._c.call(this, corner.corner, data.opts.cornerSize/2);
corner.x = origin.x;
corner.y = origin.y;
flipMethods._showFoldedPage.call(this, corner, true);
} else
flipMethods.hideFoldedPage.call(this, true);
}
}
},
_eventEnd: function() {
var data = this.data().f;
if (!data.disabled && data.point) {
var event = $.Event('released');
this.trigger(event, [data.point]);
if (!event.isDefaultPrevented())
flipMethods.hideFoldedPage.call(this, true);
}
data.corner = null;
},
disable: function(disable) {
flipMethods.setData.call(this, {'disabled': disable});
return this;
}
},
cla = function(that, methods, args) {
if (!args[0] || typeof(args[0])=='object')
return methods.init.apply(that, args);
else if(methods[args[0]] && args[0].toString().substr(0, 1)!='_')
return methods[args[0]].apply(that, Array.prototype.slice.call(args, 1));
else
throw args[0] + ' is an invalid value';
};
$.extend($.fn, {
flip: function(req, opts) {
return cla(this, flipMethods, arguments);
},
turn: function(req) {
return cla(this, turnMethods, arguments);
},
transform: function(transform, origin) {
var properties = {};
if (origin)
properties[vendor+'transform-origin'] = origin;
properties[vendor+'transform'] = transform;
return this.css(properties);
},
animatef: function(point) {
var data = this.data();
if (data.effect)
clearInterval(data.effect.handle);
if (point) {
if (!point.to.length) point.to = [point.to];
if (!point.from.length) point.from = [point.from];
if (!point.easing) point.easing = function (x, t, b, c, data) { return c * Math.sqrt(1 - (t=t/data-1)*t) + b; };
var j, diff = [],
len = point.to.length,
that = this,
fps = point.fps || 30,
time = - fps,
f = function() {
var j, v = [];
time = Math.min(point.duration, time + fps);
for (j = 0; j < len; j++)
v.push(point.easing(1, time, point.from[j], diff[j], point.duration));
point.frame((len==1) ? v[0] : v);
if (time==point.duration) {
clearInterval(data.effect.handle);
delete data['effect'];
that.data(data);
if (point.complete)
point.complete();
}
};
for (j = 0; j < len; j++)
diff.push(point.to[j] - point.from[j]);
data.effect = point;
data.effect.handle = setInterval(f, fps);
this.data(data);
f();
} else {
delete data['effect'];
}
}
});
$.isTouch = isTouch;
})(jQuery);
gitextract_61qrn7na/ ├── changelog.txt ├── demos/ │ ├── bible/ │ │ └── index.html │ ├── magazine/ │ │ └── index.html │ └── magazine_single/ │ └── index.html ├── license.txt ├── readme.md └── turn.js
Condensed preview — 7 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (62K chars).
[
{
"path": "changelog.txt",
"chars": 1033,
"preview": "TURN.JS\n------------------------------------------------------\nGithub: https://github.com/blasten/turn.js\nReference: htt"
},
{
"path": "demos/bible/index.html",
"chars": 3361,
"preview": "<!doctype html>\n<html>\n<head>\n<script type=\"text/javascript\" src=\"http://code.jquery.com/jquery-1.7.1.min.js\"></script>\n"
},
{
"path": "demos/magazine/index.html",
"chars": 1292,
"preview": "<!doctype html>\n<html>\n<head>\n<script type=\"text/javascript\" src=\"http://code.jquery.com/jquery-1.7.1.min.js\"></script>\n"
},
{
"path": "demos/magazine_single/index.html",
"chars": 1291,
"preview": "<!doctype html>\n<html>\n<head>\n<script type=\"text/javascript\" src=\"http://code.jquery.com/jquery-1.7.1.min.js\"></script>\n"
},
{
"path": "license.txt",
"chars": 1247,
"preview": "turn.js 3rd release\nwww.turnjs.com\n\nCopyright (c) 2012, Emmanuel Garcia\nAll rights reserved.\n\nRedistribution and use in "
},
{
"path": "readme.md",
"chars": 2184,
"preview": "\n\n\n**Get the turn.js 4th release on [turnjs.com](http:"
},
{
"path": "turn.js",
"chars": 44461,
"preview": "/**\n * turn.js 3rd release\n * www.turnjs.com\n *\n * Copyright (C) 2012, Emmanuel Garcia.\n * All rights reserved.\n *\n * Re"
}
]
About this extraction
This page contains the full source code of the blasten/turn.js GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 7 files (53.6 KB), approximately 16.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.