1/** 2 * turn.js 4th release 3 * turnjs.com 4 * turnjs.com/license.txt 5 * 6 * Copyright (C) 2012 Emmanuel Garcia 7 * All rights reserved 8 **/ 9 10(function($) { 11 12'use strict'; 13 14var has3d, 15 16 hasRot, 17 18 vendor = '', 19 20 version = '4.1.0', 21 22 PI = Math.PI, 23 24 A90 = PI/2, 25 26 isTouch = 'ontouchstart' in window, 27 28 mouseEvents = (isTouch) ? 29 { 30 down: 'touchstart', 31 move: 'touchmove', 32 up: 'touchend', 33 over: 'touchstart', 34 out: 'touchend' 35 } 36 : 37 { 38 down: 'mousedown', 39 move: 'mousemove', 40 up: 'mouseup', 41 over: 'mouseover', 42 out: 'mouseout' 43 }, 44 45 // Contansts used for each corner 46 // | tl * tr | 47 // l | * * | r 48 // | bl * br | 49 50 corners = { 51 backward: ['bl', 'tl'], 52 forward: ['br', 'tr'], 53 all: ['tl', 'bl', 'tr', 'br', 'l', 'r'] 54 }, 55 56 // Display values 57 58 displays = ['single', 'double'], 59 60 // Direction values 61 62 directions = ['ltr', 'rtl'], 63 64 // Default options 65 66 turnOptions = { 67 68 // Enables hardware acceleration 69 70 acceleration: true, 71 72 // Display 73 74 display: 'double', 75 76 // Duration of transition in milliseconds 77 78 duration: 600, 79 80 // First page 81 82 page: 1, 83 84 // Enables gradients 85 86 gradients: true, 87 88 // Corners used when turning the page 89 90 turnCorners: 'bl,br', 91 92 // Events 93 94 when: null 95 }, 96 97 flipOptions = { 98 99 // Size of the active zone of each corner 100 101 cornerSize: 100 102 103 }, 104 105 // Number of pages in the DOM, minimum value: 6 106 107 pagesInDOM = 6, 108 109 110turnMethods = { 111 112 // Singleton constructor 113 // $('#selector').turn([options]); 114 115 init: function(options) { 116 117 // Define constants 118 119 has3d = 'WebKitCSSMatrix' in window || 'MozPerspective' in document.body.style; 120 hasRot = rotationAvailable(); 121 vendor = getPrefix(); 122 123 var i, that = this, pageNum = 0, data = this.data(), ch = this.children(); 124 125 // Set initial configuration 126 127 options = $.extend({ 128 width: this.width(), 129 height: this.height(), 130 direction: this.attr('dir') || this.css('direction') || 'ltr' 131 }, turnOptions, options); 132 133 data.opts = options; 134 data.pageObjs = {}; 135 data.pages = {}; 136 data.pageWrap = {}; 137 data.pageZoom = {}; 138 data.pagePlace = {}; 139 data.pageMv = []; 140 data.zoom = 1; 141 data.totalPages = options.pages || 0; 142 data.eventHandlers = { 143 touchStart: $.proxy(turnMethods._touchStart, this), 144 touchMove: $.proxy(turnMethods._touchMove, this), 145 touchEnd: $.proxy(turnMethods._touchEnd, this), 146 start: $.proxy(turnMethods._eventStart, this) 147 }; 148 149 150 151 // Add event listeners 152 153 if (options.when) 154 for (i in options.when) 155 if (has(i, options.when)) 156 this.bind(i, options.when[i]); 157 158 // Set the css 159 160 this.css({position: 'relative', width: options.width, height: options.height}); 161 162 // Set the initial display 163 164 this.turn('display', options.display); 165 166 // Set the direction 167 168 if (options.direction!=='') 169 this.turn('direction', options.direction); 170 171 // Prevent blue screen problems of switching to hardware acceleration mode 172 // By forcing hardware acceleration for ever 173 174 if (has3d && !isTouch && options.acceleration) 175 this.transform(translate(0, 0, true)); 176 177 // Add pages from the DOM 178 179 for (i = 0; i<ch.length; i++) { 180 if ($(ch[i]).attr('ignore')!='1') { 181 this.turn('addPage', ch[i], ++pageNum); 182 } 183 } 184 185 // Event listeners 186 187 $(this).bind(mouseEvents.down, data.eventHandlers.touchStart). 188 bind('end', turnMethods._eventEnd). 189 bind('pressed', turnMethods._eventPressed). 190 bind('released', turnMethods._eventReleased). 191 bind('flip', turnMethods._flip); 192 193 $(this).parent().bind('start', data.eventHandlers.start); 194 195 $(document).bind(mouseEvents.move, data.eventHandlers.touchMove). 196 bind(mouseEvents.up, data.eventHandlers.touchEnd); 197 198 // Set the initial page 199 200 this.turn('page', options.page); 201 202 // This flipbook is ready 203 204 data.done = true; 205 206 return this; 207 }, 208 209 // Adds a page from external data 210 211 addPage: function(element, page) { 212 213 var currentPage, 214 className, 215 incPages = false, 216 data = this.data(), 217 lastPage = data.totalPages+1; 218 219 if (data.destroying) 220 return false;
221 222 // Read the page number from the className of `element` - format: p[0-9]+ 223 224 if ((currentPage = /\bp([0-9]+)\b/.exec($(element).attr('class')))) 225 page = parseInt(currentPage[1], 10); 226 227 if (page) { 228 229 if (page==lastPage) 230 incPages = true; 231 else if (page>lastPage) 232 throw turnError('Page "'+page+'" cannot be inserted'); 233 234 } else { 235 236 page = lastPage; 237 incPages = true; 238 239 } 240 241 if (page>=1 && page<=lastPage) { 242 243 if (data.display=='double') 244 className = (page%2) ? ' odd' : ' even'; 245 else 246 className = ''; 247 248 // Stop animations 249 if (data.done) 250 this.turn('stop'); 251 252 // Move pages if it's necessary 253 if (page in data.pageObjs) 254 turnMethods._movePages.call(this, page, 1); 255 256 // Increase the number of pages 257 if (incPages) 258 data.totalPages = lastPage; 259 260 // Add element 261 data.pageObjs[page] = $(element). 262 css({'float': 'left'}). 263 addClass('page p' + page + className); 264 265 if (!hasHardPage() && data.pageObjs[page].hasClass('hard')) { 266 data.pageObjs[page].removeClass('hard'); 267 } 268 269 // Add page 270 turnMethods._addPage.call(this, page); 271 272 // Remove pages out of range 273 turnMethods._removeFromDOM.call(this); 274 } 275 276 return this; 277 }, 278 279 // Adds a page 280 281 _addPage: function(page) { 282 283 var data = this.data(), 284 element = data.pageObjs[page]; 285 286 if (element) 287 if (turnMethods._necessPage.call(this, page)) { 288 289 if (!data.pageWrap[page]) { 290 291 // Wrapper 292 data.pageWrap[page] = $('<div/>', 293 {'class': 'page-wrapper', 294 page: page, 295 css: {position: 'absolute', 296 overflow: 'hidden'}}); 297 298 // Append to this flipbook 299 this.append(data.pageWrap[page]); 300 301 if (!data.pagePlace[page]) { 302 303 data.pagePlace[page] = page; 304 // Move `pageObjs[page]` to wrapper 305 data.pageObjs[page].appendTo(data.pageWrap[page]); 306 307 } 308 309 // Set the size of the page 310 var prop = turnMethods._pageSize.call(this, page, true); 311 element.css({width: prop.width, height: prop.height}); 312 data.pageWrap[page].css(prop); 313 314 } 315 316 if (data.pagePlace[page] == page) { 317 318 // If the page isn't in another place, create the flip effect 319 turnMethods._makeFlip.call(this, page); 320 321 } 322 323 } else { 324 325 // Place 326 data.pagePlace[page] = 0; 327 328 // Remove element from the DOM 329 if (data.pageObjs[page]) 330 data.pageObjs[page].remove(); 331 332 } 333 334 }, 335 336 // Checks if a page is in memory 337 338 hasPage: function(page) { 339 340 return has(page, this.data().pageObjs); 341 342 }, 343 344 // Centers the flipbook 345 346 center: function(page) { 347 348 var data = this.data(), 349 size = $(this).turn('size'), 350 left = 0; 351 352 if (!data.noCenter) { 353 if (data.display=='double') { 354 var view = this.turn('view', page || data.tpage || data.page); 355 356 if (data.direction=='ltr') { 357 if (!view[0]) 358 left -= size.width/4; 359 else if (!view[1]) 360 left += size.width/4; 361 } else { 362 if (!view[0]) 363 left += size.width/4; 364 else if (!view[1]) 365 left -= size.width/4; 366 } 367 368 } 369 370 $(this).css({marginLeft: left}); 371 } 372 373 return this; 374 375 }, 376 377 // Destroys the flipbook 378 379 destroy: function () { 380 381 var page, 382 flipbook = this, 383 data = this.data(), 384 events = [ 385 'end', 'first', 'flip', 'last', 'pressed', 386 'released', 'start', 'turning', 'turned', 387 'zooming', 'missing']; 388 389 if (trigger('destroying', this)=='prevented') 390 return; 391 392 data.destroying = true; 393 394 $.each(events, function(index, eventName) { 395 flipbook.unbind(eventName); 396 }); 397 398 this.parent().unbind('start', data.eventHandlers.start); 399 400 $(document).unbind(mouseEvents.move, data.eventHandlers.touchMove). 401 unbind(mouseEvents.up, data.eventHandlers.touchEnd); 402 403 while (data.totalPages!==0) { 404 this.turn('removePage', data.totalPages); 405 } 406 407 if (data.fparent) 408 data.fparent.remove(); 409 410 if (data.shadow) 411 data.shadow.remove(); 412 413 this.removeData(); 414 data = null; 415 416 return this; 417 418 }, 419 420 // Checks if this element is a flipbook 421 422 is: function() { 423 424 return typeof(this.data().pages)=='object'; 425 426 }, 427 428 // Sets and gets the zoom value 429 430 zoom: function(newZoom) { 431 432 var data = this.data(); 433 434 if (typeof(newZoom)=='number') { 435
436 if (newZoom<0.001 || newZoom>100) 437 throw turnError(newZoom+ ' is not a value for zoom'); 438 439 if (trigger('zooming', this, [newZoom, data.zoom])=='prevented') 440 return this; 441 442 var size = this.turn('size'), 443 currentView = this.turn('view'), 444 iz = 1/data.zoom, 445 newWidth = Math.round(size.width * iz * newZoom), 446 newHeight = Math.round(size.height * iz * newZoom); 447 448 data.zoom = newZoom; 449 450 $(this).turn('stop'). 451 turn('size', newWidth, newHeight); 452 /*. 453 css({marginTop: size.height * iz / 2 - newHeight / 2});*/ 454 455 if (data.opts.autoCenter) 456 this.turn('center'); 457 /*else 458 $(this).css({marginLeft: size.width * iz / 2 - newWidth / 2});*/ 459 460 turnMethods._updateShadow.call(this); 461 462 for (var i = 0; i<currentView.length; i++) { 463 if (currentView[i] && data.pageZoom[currentView[i]]!=data.zoom) { 464 465 this.trigger('zoomed',[ 466 currentView[i], 467 currentView, 468 data.pageZoom[currentView[i]], 469 data.zoom]); 470 471 data.pageZoom[currentView[i]] = data.zoom; 472 } 473 } 474 475 return this; 476 477 } else 478 return data.zoom; 479 480 }, 481 482 // Gets the size of a page 483 484 _pageSize: function(page, position) { 485 486 var data = this.data(), 487 prop = {}; 488 489 if (data.display=='single') { 490 491 prop.width = this.width(); 492 prop.height = this.height(); 493 494 if (position) { 495 prop.top = 0; 496 prop.left = 0; 497 prop.right = 'auto'; 498 } 499 500 } else { 501 502 var pageWidth = this.width()/2, 503 pageHeight = this.height(); 504 505 if (data.pageObjs[page].hasClass('own-size')) { 506 prop.width = data.pageObjs[page].width(); 507 prop.height = data.pageObjs[page].height(); 508 } else { 509 prop.width = pageWidth; 510 prop.height = pageHeight; 511 } 512 513 if (position) { 514 var odd = page%2; 515 prop.top = (pageHeight-prop.height)/2; 516 517 if (data.direction=='ltr') { 518 519 prop[(odd) ? 'right' : 'left'] = pageWidth-prop.width; 520 prop[(odd) ? 'left' : 'right'] = 'auto'; 521 522 } else { 523 524 prop[(odd) ? 'left' : 'right'] = pageWidth-prop.width; 525 prop[(odd) ? 'right' : 'left'] = 'auto'; 526 527 } 528 529 } 530 } 531 532 return prop; 533 534 }, 535 536 // Prepares the flip effect for a page 537 538 _makeFlip: function(page) { 539 540 var data = this.data(); 541 542 if (!data.pages[page] && data.pagePlace[page]==page) { 543 544 var single = data.display=='single', 545 odd = page%2; 546 547 data.pages[page] = data.pageObjs[page]. 548 css(turnMethods._pageSize.call(this, page)). 549 flip({ 550 page: page, 551 next: (odd || single) ? page+1 : page-1, 552 turn: this 553 }). 554 flip('disable', data.disabled); 555 556 // Issue about z-index 557 turnMethods._setPageLoc.call(this, page); 558 559 data.pageZoom[page] = data.zoom; 560 561 } 562 563 return data.pages[page]; 564 }, 565 566 // Makes pages within a range 567 568 _makeRange: function() { 569 570 var page, range, 571 data = this.data(); 572 573 if (data.totalPages<1) 574 return; 575 576 range = this.turn('range'); 577 578 for (page = range[0]; page<=range[1]; page++) 579 turnMethods._addPage.call(this, page); 580 581 }, 582 583 // Returns a range of pages that should be in the DOM 584 // Example: 585 // - page in the current view, return true 586 // * page is in the range, return true 587 // Otherwise, return false 588 // 589 // 1 2-3 4-5 6-7 8-9 10-11 12-13 590 // ** ** -- ** ** 591 592 range: function(page) { 593 594 var remainingPages, left, right, view, 595 data = this.data(); 596 597 page = page || data.tpage || data.page || 1; 598 view = turnMethods._view.call(this, page); 599 600 if (page<1 || page>data.totalPages) 601 throw turnError('"'+page+'" is not a valid page'); 602 603 604 view[1] = view[1] || view[0]; 605 606 if (view[0]>=1 && view[1]<=data.totalPages) { 607 608 remainingPages = Math.floor((pagesInDOM-2)/2); 609 610 if (data.totalPages-view[1] > view[0]) { 611 left = Math.min(view[0]-1, remainingPages); 612 right = 2*remainingPages-left; 613 } else { 614 right = Math.min(data.totalPages-view[1], remainingPages); 615 left = 2*remainingPages-right; 616 } 617 618 } else { 619 left = pagesInDOM-1; 620 right = pagesInDOM-1; 621 } 622 623 return [Math.max(1, view[0]-left), 624 Math.min(data.totalPages, view[1]+right)]; 625 626 }, 627 628 // Detects if a page is within the range of `pagesInDOM` from the current view 629 630 _necessPage: function(page) { 631 632 if (page===0) 633 return true; 634 635 var range = this.turn('range'); 636 637 return this.data().pageObjs[page].hasClass('fixed') || 638 (page>=range[0] && page<=range[1]); 639 640 }, 641 642 // Releases memory by removing pages from the DOM 643 644 _removeFromDOM: function() { 645 646 var page, data = this.data(); 647 648 for (page in data.pageWrap) 649 if (has(page, data.pageWrap) && 650 !turnMethods._necessPage.call(this, page)) 651 turnMethods._removePageFromDOM.call(this, page); 652 653 }, 654 655 // Removes a page from DOM and its internal references 656 657 _removePageFromDOM: function(page) { 658 659 var data = this.data(); 660 661 if (data.pages[page]) { 662 var dd = data.pages[page].data(); 663 664 flipMethods._moveFoldingPage.call(data.pages[page], false); 665 666 if (dd.f && dd.f.fwrapper) 667 dd.f.fwrapper.remove(); 668 669 data.pages[page].removeData(); 670 data.pages[page].remove(); 671 delete data.pages[page]; 672 } 673 674 if (data.pageObjs[page]) 675 data.pageObjs[page].remove(); 676 677 if (data.pageWrap[page]) { 678 data.pageWrap[page].remove(); 679 delete data.pageWrap[page]; 680 } 681 682 turnMethods._removeMv.call(this, page); 683 684 delete data.pagePlace[page]; 685 delete data.pageZoom[page]; 686 687 }, 688 689 // Removes a page 690 691 removePage: function(page) { 692 693 var data = this.data(); 694 695 // Delete all the pages 696 if (page=='*') { 697 698 while (data.totalPages!==0) { 699 this.turn('removePage', data.totalPages); 700 } 701 702 } else { 703 704 if (page<1 || page>data.totalPages) 705 throw turnError('The page '+ page + ' doesn\'t exist'); 706 707 if (data.pageObjs[page]) { 708 709 // Stop animations 710 this.turn('stop'); 711 712 // Remove `page` 713 turnMethods._removePageFromDOM.call(this, page); 714 715 delete data.pageObjs[page]; 716 717 } 718 719 // Move the pages 720 turnMethods._movePages.call(this, page, -1); 721 722 // Resize the size of this flipbook 723 data.totalPages = data.totalPages-1; 724 725 // Check the current view 726 727 if (data.page>data.totalPages) { 728 729 data.page = null; 730 turnMethods._fitPage.call(this, data.totalPages); 731 732 } else { 733 734 turnMethods._makeRange.call(this); 735 this.turn('update'); 736 737 } 738 } 739 740 return this; 741 742 }, 743 744 // Moves pages 745 746 _movePages: function(from, change) { 747 748 var page, 749 that = this, 750 data = this.data(), 751 single = data.display=='single', 752 move = function(page) { 753 754 var next = page + change, 755 odd = next%2, 756 className = (odd) ? ' odd ' : ' even '; 757 758 if (data.pageObjs[page]) 759 data.pageObjs[next] = data.pageObjs[page]. 760 removeClass('p' + page + ' odd even'). 761 addClass('p' + next + className); 762 763 if (data.pagePlace[page] && data.pageWrap[page]) { 764 765 data.pagePlace[next] = next; 766 767 if (data.pageObjs[next].hasClass('fixed')) 768 data.pageWrap[next] = data.pageWrap[page]. 769 attr('page', next); 770 else 771 data.pageWrap[next] = data.pageWrap[page]. 772 css(turnMethods._pageSize.call(that, next, true)). 773 attr('page', next); 774 775 if (data.pages[page]) 776 data.pages[next] = data.pages[page]. 777 flip('options', { 778 page: next, 779 next: (single || odd) ? next+1 : next-1 780 }); 781 782 if (change) { 783 delete data.pages[page]; 784 delete data.pagePlace[page]; 785 delete data.pageZoom[page]; 786 delete data.pageObjs[page]; 787 delete data.pageWrap[page]; 788 } 789 790 } 791 792 }; 793 794 if (change>0) 795 for (page=data.totalPages; page>=from; page--) 796 move(page); 797 else 798 for (page=from; page<=data.totalPages; page++) 799 move(page); 800 801 }, 802 803 // Sets or Gets the display mode 804 805 display: function(display) { 806 807 var data = this.data(), 808 currentDisplay = data.display; 809 810 if (display===undefined) { 811 812 return currentDisplay; 813 814 } else { 815 816 if ($.inArray(display, displays)==-1) 817 throw turnError('"'+display + '" is not a value for display'); 818 819 switch(display) { 820 case 'single': 821 822 // Create a temporal page to use as folded page 823 824 if (!data.pageObjs[0]) { 825 this.turn('stop'). 826 css({'overflow': 'hidden'}); 827 828 data.pageObjs[0] = $('<div />', 829 {'class': 'page p-temporal'}). 830 css({width: this.width(), height: this.height()}). 831 appendTo(this); 832 } 833 834 this.addClass('shadow'); 835 836 break; 837 case 'double': 838 839 // Remove the temporal page 840 841 if (data.pageObjs[0]) { 842 this.turn('stop').css({'overflow': ''}); 843 data.pageObjs[0].remove(); 844 delete data.pageObjs[0]; 845 } 846 847 this.removeClass('shadow'); 848 849 break; 850 } 851 852 853 data.display = display; 854 855 if (currentDisplay) { 856 var size = this.turn('size'); 857 turnMethods._movePages.call(this, 1, 0); 858 this.turn('size', size.width, size.height). 859 turn('update'); 860 } 861 862 return this; 863 864 } 865 866 }, 867
868 // Gets and sets the direction of the flipbook 869 870 direction: function(dir) { 871 872 var data = this.data(); 873 874 if (dir===undefined) { 875 876 return data.direction; 877 878 } else { 879 880 dir = dir.toLowerCase(); 881 882 if ($.inArray(dir, directions)==-1) 883 throw turnError('"' + dir + '" is not a value for direction'); 884 885 if (dir=='rtl') { 886 $(this).attr('dir', 'ltr'). 887 css({direction: 'ltr'}); 888 } 889 890 data.direction = dir; 891 892 if (data.done) 893 this.turn('size', $(this).width(), $(this).height()); 894 895 return this; 896 } 897 898 }, 899 900 // Detects animation 901 902 animating: function() { 903 904 return this.data().pageMv.length>0; 905 906 }, 907 908 // Gets the current activated corner 909 910 corner: function() { 911 912 var corner, 913 page, 914 data = this.data(); 915 916 for (page in data.pages) { 917 if (has(page, data.pages)) 918 if ((corner = data.pages[page].flip('corner'))) { 919 return corner; 920 } 921 } 922 923 return false; 924 }, 925 926 // Gets the data stored in the flipbook 927 928 data: function() { 929 930 return this.data(); 931 932 }, 933 934 // Disables and enables the effect 935 936 disable: function(disable) { 937 938 var page, 939 data = this.data(), 940 view = this.turn('view'); 941 942 data.disabled = disable===undefined || disable===true; 943 944 for (page in data.pages) { 945 if (has(page, data.pages)) 946 data.pages[page].flip('disable', 947 (data.disabled) ? true : $.inArray(parseInt(page, 10), view)==-1); 948 } 949 950 return this; 951 952 }, 953 954 // Disables and enables the effect 955 956 disabled: function(disable) { 957 958 if (disable===undefined) { 959 return this.data().disabled===true; 960 } else { 961 return this.turn('disable', disable); 962 } 963 964 }, 965 966 // Gets and sets the size 967 968 size: function(width, height) { 969 970 if (width===undefined || height===undefined) { 971 972 return {width: this.width(), height: this.height()}; 973 974 } else { 975 976 this.turn('stop'); 977 978 var page, prop, 979 data = this.data(), 980 pageWidth = (data.display=='double') ? width/2 : width; 981 982 this.css({width: width, height: height}); 983 984 if (data.pageObjs[0]) 985 data.pageObjs[0].css({width: pageWidth, height: height}); 986 987 for (page in data.pageWrap) { 988 if (!has(page, data.pageWrap)) continue; 989 990 prop = turnMethods._pageSize.call(this, page, true); 991 992 data.pageObjs[page].css({width: prop.width, height: prop.height}); 993 data.pageWrap[page].css(prop); 994 995 if (data.pages[page]) 996 data.pages[page].css({width: prop.width, height: prop.height}); 997 } 998 999 this.turn('resize'); 1000 1001 return this; 1002 1003 } 1004 }, 1005 1006 // Resizes each page 1007 1008 resize: function() { 1009 1010 var page, data = this.data(); 1011 1012 if (data.pages[0]) { 1013 data.pageWrap[0].css({left: -this.width()}); 1014 data.pages[0].flip('resize', true); 1015 } 1016 1017 for (page = 1; page <= data.totalPages; page++) 1018 if (data.pages[page]) 1019 data.pages[page].flip('resize', true); 1020 1021 turnMethods._updateShadow.call(this); 1022 1023 if (data.opts.autoCenter) 1024 this.turn('center'); 1025 1026 }, 1027 1028 // Removes an animation from the cache 1029 1030 _removeMv: function(page) { 1031 1032 var i, data = this.data(); 1033 1034 for (i=0; i<data.pageMv.length; i++) 1035 if (data.pageMv[i]==page) { 1036 data.pageMv.splice(i, 1); 1037 return true; 1038 } 1039 1040 return false; 1041 1042 }, 1043 1044 // Adds an animation to the cache 1045 1046 _addMv: function(page) { 1047 1048 var data = this.data(); 1049 1050 turnMethods._removeMv.call(this, page); 1051 data.pageMv.push(page); 1052 1053 }, 1054 1055 // Gets indexes for a view 1056 1057 _view: function(page) { 1058 1059 var data = this.data(); 1060 1061 page = page || data.page; 1062 1063 if (data.display=='double') 1064 return (page%2) ? [page-1, page] : [page, page+1]; 1065 else 1066 return [page]; 1067 1068 }, 1069 1070 // Gets a view 1071 1072 view: function(page) { 1073 1074 var data = this.data(), 1075 view = turnMethods._view.call(this, page); 1076 1077 if (data.display=='double') 1078 return [(view[0]>0) ? view[0] : 0, 1079 (view[1]<=data.totalPages) ? view[1] : 0]; 1080 else 1081 return [(view[0]>0 && view[0]<=data.totalPages) ? view[0] : 0]; 1082 1083 }, 1084 1085 // Stops animations 1086 1087 stop: function(ignore, animate) { 1088 1089 if (this.turn('animating')) { 1090 1091 var i, opts, page, 1092 data = this.data(); 1093 1094 if (data.tpage) { 1095 data.page = data.tpage; 1096 delete data['tpage']; 1097 } 1098 1099 for (i = 0; i<data.pageMv.length; i++) { 1100 1101 if (!data.pageMv[i] || data.pageMv[i]===ignore) 1102 continue; 1103 1104 page = data.pages[data.pageMv[i]]; 1105 opts = page.data().f.opts; 1106 1107 page.flip('hideFoldedPage', animate); 1108 1109 if (!animate) 1110 flipMethods._moveFoldingPage.call(page, false); 1111 1112 if (opts.force) { 1113 opts.next = (opts.page%2===0) ? opts.page-1 : opts.page+1; 1114 delete opts['force']; 1115 } 1116 1117 } 1118 } 1119 1120 this.turn('update'); 1121 1122 return this; 1123 }, 1124 1125 // Gets and sets the number of pages 1126 1127 pages: function(pages) { 1128 1129 var data = this.data(); 1130 1131 if (pages) { 1132 1133 if (pages<data.totalPages) { 1134 1135 for (var page = data.totalPages; page>pages; page--) 1136 this.turn('removePage', page); 1137 1138 } 1139 1140 data.totalPages = pages; 1141 turnMethods._fitPage.call(this, data.page); 1142 1143 return this; 1144 1145 } else 1146 return data.totalPages; 1147 1148 }, 1149 1150 // Checks missing pages 1151 1152 _missing : function(page) { 1153 1154 var data = this.data(); 1155 1156 if (data.totalPages<1) 1157 return; 1158 1159 var p, 1160 range = this.turn('range', page), 1161 missing = []; 1162 1163 for (p = range[0]; p<=range[1]; p++) { 1164 if (!data.pageObjs[p]) 1165 missing.push(p); 1166 } 1167 1168 if (missing.length>0) 1169 this.trigger('missing', [missing]); 1170 1171 }, 1172 1173 // Sets a page without effect
1174 1175 _fitPage: function(page) { 1176 1177 var data = this.data(), 1178 newView = this.turn('view', page); 1179 1180 turnMethods._missing.call(this, page); 1181 1182 if (!data.pageObjs[page]) 1183 return; 1184 1185 data.page = page; 1186 1187 this.turn('stop'); 1188 1189 for (var i = 0; i<newView.length; i++) { 1190 1191 if (newView[i] && data.pageZoom[newView[i]]!=data.zoom) { 1192 1193 this.trigger('zoomed',[ 1194 newView[i], 1195 newView, 1196 data.pageZoom[newView[i]], 1197 data.zoom]); 1198 1199 data.pageZoom[newView[i]] = data.zoom; 1200 1201 } 1202 } 1203 1204 turnMethods._removeFromDOM.call(this); 1205 turnMethods._makeRange.call(this); 1206 turnMethods._updateShadow.call(this); 1207 this.trigger('turned', [page, newView]); 1208 this.turn('update'); 1209 1210 if (data.opts.autoCenter) 1211 this.turn('center'); 1212 1213 }, 1214 1215 // Turns the page 1216 1217 _turnPage: function(page) { 1218 1219 var current, 1220 next, 1221 data = this.data(), 1222 place = data.pagePlace[page], 1223 view = this.turn('view'), 1224 newView = this.turn('view', page); 1225 1226 1227 if (data.page!=page) { 1228 1229 var currentPage = data.page; 1230 1231 if (trigger('turning', this, [page, newView])=='prevented') { 1232 1233 if (currentPage==data.page && $.inArray(place, data.pageMv)!=-1) 1234 data.pages[place].flip('hideFoldedPage', true); 1235 1236 return; 1237 1238 } 1239 1240 if ($.inArray(1, newView)!=-1) 1241 this.trigger('first'); 1242 if ($.inArray(data.totalPages, newView)!=-1) 1243 this.trigger('last'); 1244 1245 } 1246 1247 if (data.display=='single') { 1248 current = view[0]; 1249 next = newView[0]; 1250 } else if (view[1] && page>view[1]) {
1251 current = view[1]; 1252 next = newView[0]; 1253 } else if (view[0] && page<view[0]) { 1254 current = view[0]; 1255 next = newView[1]; 1256 } 1257 1258 var optsCorners = data.opts.turnCorners.split(','), 1259 flipData = data.pages[current].data().f, 1260 opts = flipData.opts, 1261 actualPoint = flipData.point; 1262 1263 turnMethods._missing.call(this, page); 1264 1265 if (!data.pageObjs[page]) 1266 return; 1267 1268 this.turn('stop'); 1269 1270 data.page = page; 1271 1272 turnMethods._makeRange.call(this); 1273 1274 data.tpage = next; 1275 1276 if (opts.next!=next) { 1277 opts.next = next; 1278 opts.force = true; 1279 } 1280 1281 this.turn('update'); 1282 1283 flipData.point = actualPoint; 1284 1285 if (flipData.effect=='hard') 1286 if (data.direction=='ltr') 1287 data.pages[current].flip('turnPage', 1288 (page>current) ? 'r' : 'l'); 1289 else 1290 data.pages[current].flip('turnPage', 1291 (page>current) ? 'l' : 'r'); 1292 else { 1293 if (data.direction=='ltr') 1294 data.pages[current].flip('turnPage', 1295 optsCorners[(page>current) ? 1 : 0]); 1296 else 1297 data.pages[current].flip('turnPage', 1298 optsCorners[(page>current) ? 0 : 1]); 1299 } 1300 1301 }, 1302 1303 // Gets and sets a page 1304 1305 page: function(page) { 1306 1307 var data = this.data(); 1308 1309 if (page===undefined) { 1310 1311 return data.page; 1312 1313 } else { 1314 1315 if (!data.disabled && !data.destroying) { 1316 1317 page = parseInt(page, 10); 1318 1319 if (page>0 && page<=data.totalPages) { 1320 1321 if (page!=data.page) { 1322 if (!data.done || $.inArray(page, this.turn('view'))!=-1) 1323 turnMethods._fitPage.call(this, page); 1324 else 1325 turnMethods._turnPage.call(this, page); 1326 } 1327 1328 return this; 1329 1330 } else { 1331 1332 throw turnError('The page ' + page + ' does not exist'); 1333 1334 } 1335 1336 } 1337 1338 } 1339 1340 }, 1341 1342 // Turns to the next view 1343 1344 next: function() { 1345 1346 return this.turn('page', Math.min(this.data().totalPages, 1347 turnMethods._view.call(this, this.data().page).pop() + 1)); 1348 1349 }, 1350 1351 // Turns to the previous view 1352 1353 previous: function() { 1354 1355 return this.turn('page', Math.max(1, 1356 turnMethods._view.call(this, this.data().page).shift() - 1)); 1357 1358 }, 1359 1360 // Shows a peeling corner 1361 1362 peel: function(corner, animate) { 1363 1364 var data = this.data(), 1365 view = this.turn('view'); 1366 1367 animate = (animate===undefined) ? true : animate===true; 1368 1369 if (corner===false) { 1370 1371 this.turn('stop', null, animate); 1372 1373 } else { 1374 1375 if (data.display=='single') { 1376 1377 data.pages[data.page].flip('peel', corner, animate); 1378 1379 } else { 1380 1381 var page; 1382 1383 if (data.direction=='ltr') { 1384 1385 page = (corner.indexOf('l')!=-1) ? view[0] : view[1]; 1386 1387 } else { 1388 1389 page = (corner.indexOf('l')!=-1) ? view[1] : view[0]; 1390 1391 } 1392 1393 if (data.pages[page]) 1394 data.pages[page].flip('peel', corner, animate); 1395 1396 } 1397 } 1398 1399 return this; 1400 1401 }, 1402 1403 // Adds a motion to the internal list 1404 // This event is called in context of flip 1405 1406 _addMotionPage: function() { 1407 1408 var opts = $(this).data().f.opts, 1409 turn = opts.turn, 1410 dd = turn.data(); 1411 1412 turnMethods._addMv.call(turn, opts.page); 1413 1414 }, 1415 1416 // This event is called in context of flip 1417 1418 _eventStart: function(e, opts, corner) { 1419 1420 var data = opts.turn.data(), 1421 actualZoom = data.pageZoom[opts.page]; 1422 1423 if (e.isDefaultPrevented()) { 1424 turnMethods._updateShadow.call(opts.turn); 1425 return; 1426 } 1427 1428 if (actualZoom && actualZoom!=data.zoom) { 1429 1430 opts.turn.trigger('zoomed',[ 1431 opts.page, 1432 opts.turn.turn('view', opts.page), 1433 actualZoom, 1434 data.zoom]); 1435 1436 data.pageZoom[opts.page] = data.zoom; 1437 1438 } 1439 1440 if (data.display=='single' && corner) { 1441 1442 if ((corner.charAt(1)=='l' && data.direction=='ltr') || 1443 (corner.charAt(1)=='r' && data.direction=='rtl')) 1444 { 1445 1446 opts.next = (opts.next<opts.page) ? opts.next : opts.page-1; 1447 opts.force = true; 1448 1449 } else { 1450 1451 opts.next = (opts.next>opts.page) ? opts.next : opts.page+1; 1452 1453 } 1454 1455 } 1456 1457 turnMethods._addMotionPage.call(e.target); 1458 turnMethods._updateShadow.call(opts.turn); 1459 }, 1460
1461 // This event is called in context of flip 1462 1463 _eventEnd: function(e, opts, turned) { 1464 1465 var that = $(e.target), 1466 data = that.data().f, 1467 turn = opts.turn, 1468 dd = turn.data(); 1469 1470 if (turned) { 1471 1472 var tpage = dd.tpage || dd.page; 1473 1474 if (tpage==opts.next || tpage==opts.page) { 1475 delete dd.tpage; 1476 1477 turnMethods._fitPage.call(turn, tpage || opts.next, true); 1478 } 1479 1480 } else { 1481 1482 turnMethods._removeMv.call(turn, opts.page); 1483 turnMethods._updateShadow.call(turn); 1484 turn.turn('update'); 1485 1486 } 1487 1488 }, 1489 1490 // This event is called in context of flip 1491 1492 _eventPressed: function(e) { 1493 1494 var page, 1495 data = $(e.target).data().f, 1496 turn = data.opts.turn, 1497 turnData = turn.data(), 1498 pages = turnData.pages; 1499 1500 turnData.mouseAction = true; 1501 1502 turn.turn('update'); 1503 1504 return data.time = new Date().getTime(); 1505 1506 }, 1507 1508 // This event is called in context of flip 1509 1510 _eventReleased: function(e, point) { 1511 1512 var outArea, 1513 page = $(e.target), 1514 data = page.data().f, 1515 turn = data.opts.turn, 1516 turnData = turn.data(); 1517 1518 if (turnData.display=='single') { 1519 outArea = (point.corner=='br' || point.corner=='tr') ? 1520 point.x<page.width()/2: 1521 point.x>page.width()/2; 1522 } else { 1523 outArea = point.x<0 || point.x>page.width(); 1524 } 1525 1526 if ((new Date()).getTime()-data.time<200 || outArea) { 1527 1528 e.preventDefault(); 1529 turnMethods._turnPage.call(turn, data.opts.next); 1530 1531 } 1532 1533 turnData.mouseAction = false; 1534 1535 }, 1536 1537 // This event is called in context of flip 1538 1539 _flip: function(e) { 1540 1541 e.stopPropagation(); 1542 1543 var opts = $(e.target).data().f.opts; 1544 1545 opts.turn.trigger('turn', [opts.next]); 1546 1547 if (opts.turn.data().opts.autoCenter) { 1548 opts.turn.turn('center', opts.next); 1549 } 1550 1551 }, 1552 1553 // 1554 _touchStart: function() { 1555 var data = this.data(); 1556 for (var page in data.pages) { 1557 if (has(page, data.pages) && 1558 flipMethods._eventStart.apply(data.pages[page], arguments)===false) { 1559 return false; 1560 } 1561 } 1562 }, 1563 1564 // 1565 _touchMove: function() { 1566 var data = this.data(); 1567 for (var page in data.pages) { 1568 if (has(page, data.pages)) { 1569 flipMethods._eventMove.apply(data.pages[page], arguments); 1570 } 1571 } 1572 }, 1573 1574 // 1575 _touchEnd: function() { 1576 var data = this.data(); 1577 for (var page in data.pages) { 1578 if (has(page, data.pages)) { 1579 flipMethods._eventEnd.apply(data.pages[page], arguments); 1580 } 1581 } 1582 }, 1583 1584 // Calculate the z-index value for pages during the animation 1585 1586 calculateZ: function(mv) { 1587 1588 var i, page, nextPage, placePage, dpage, 1589 that = this, 1590 data = this.data(), 1591 view = this.turn('view'), 1592 currentPage = view[0] || view[1], 1593 total = mv.length-1, 1594 r = {pageZ: {}, partZ: {}, pageV: {}}, 1595 1596 addView = function(page) { 1597 var view = that.turn('view', page); 1598 if (view[0]) r.pageV[view[0]] = true; 1599 if (view[1]) r.pageV[view[1]] = true; 1600 }; 1601 1602 for (i = 0; i<=total; i++) { 1603 page = mv[i]; 1604 nextPage = data.pages[page].data().f.opts.next; 1605 placePage = data.pagePlace[page]; 1606 addView(page); 1607 addView(nextPage); 1608 dpage = (data.pagePlace[nextPage]==nextPage) ? nextPage : page; 1609 r.pageZ[dpage] = data.totalPages - Math.abs(currentPage-dpage); 1610 r.partZ[placePage] = data.totalPages*2 - total + i; 1611 } 1612 1613 return r; 1614 }, 1615 1616 // Updates the z-index and display property of every page 1617 1618 update: function() { 1619 1620 var page, 1621 data = this.data(); 1622 1623 if (this.turn('animating') && data.pageMv[0]!==0) { 1624 1625 // Update motion 1626 1627 var p, apage, fixed, 1628 pos = this.turn('calculateZ', data.pageMv), 1629 corner = this.turn('corner'), 1630 actualView = this.turn('view'), 1631 newView = this.turn('view', data.tpage); 1632 1633 for (page in data.pageWrap) { 1634 1635 if (!has(page, data.pageWrap)) 1636 continue; 1637 1638 fixed = data.pageObjs[page].hasClass('fixed'); 1639 1640 data.pageWrap[page].css({ 1641 display: (pos.pageV[page] || fixed) ? '' : 'none', 1642 zIndex: 1643 (data.pageObjs[page].hasClass('hard') ? 1644 pos.partZ[page] 1645 : 1646 pos.pageZ[page] 1647 ) || (fixed ? -1 : 0) 1648 }); 1649 1650 if ((p = data.pages[page])) { 1651 1652 p.flip('z', pos.partZ[page] || null); 1653 1654 if (pos.pageV[page]) 1655 p.flip('resize'); 1656 1657 if (data.tpage) { // Is it turning the page to `tpage`? 1658 1659 p.flip('hover', false). 1660 flip('disable', 1661 $.inArray(parseInt(page, 10), data.pageMv)==-1 && 1662 page!=newView[0] && 1663 page!=newView[1]); 1664 1665 } else { 1666 1667 p.flip('hover', corner===false). 1668 flip('disable', page!=actualView[0] && page!=actualView[1]); 1669 1670 } 1671 1672 } 1673 1674 } 1675 1676 } else { 1677 1678 // Update static pages 1679 1680 for (page in data.pageWrap) { 1681 1682 if (!has(page, data.pageWrap)) 1683 continue; 1684 1685 var pageLocation = turnMethods._setPageLoc.call(this, page); 1686 1687 if (data.pages[page]) { 1688 data.pages[page]. 1689 flip('disable', data.disabled || pageLocation!=1). 1690 flip('hover', true). 1691 flip('z', null); 1692 } 1693 } 1694 } 1695 1696 return this; 1697 }, 1698 1699 // Updates the position and size of the flipbook's shadow 1700 1701 _updateShadow: function() { 1702 1703 var view, view2, shadow, 1704 data = this.data(), 1705 width = this.width(), 1706 height = this.height(), 1707 pageWidth = (data.display=='single') ? width : width/2; 1708 1709 view = this.turn('view'); 1710 1711 if (!data.shadow) { 1712 data.shadow = $('<div />', { 1713 'class': 'shadow', 1714 'css': divAtt(0, 0, 0).css 1715 }). 1716 appendTo(this); 1717 } 1718 1719 for (var i = 0; i<data.pageMv.length; i++) { 1720 if (!view[0] || !view[1]) 1721 break; 1722 1723 view = this.turn('view', data.pages[data.pageMv[i]].data().f.opts.next); 1724 view2 = this.turn('view', data.pageMv[i]); 1725 1726 view[0] = view[0] && view2[0]; 1727 view[1] = view[1] && view2[1]; 1728 } 1729 1730 if (!view[0]) shadow = (data.direction=='ltr') ? 1 : 2; 1731 else if (!view[1]) shadow = (data.direction=='ltr') ? 2 : 1; 1732 else shadow = 3; 1733 1734 switch (shadow) { 1735 case 1: 1736 data.shadow.css({ 1737 width: pageWidth, 1738 height: height, 1739 top: 0, 1740 left: pageWidth 1741 }); 1742 break; 1743 case 2: 1744 data.shadow.css({ 1745 width: pageWidth, 1746 height: height, 1747 top: 0, 1748 left: 0 1749 }); 1750 break; 1751 case 3: 1752 data.shadow.css({ 1753 width: width, 1754 height: height, 1755 top: 0, 1756 left: 0 1757 }); 1758 break; 1759 } 1760 1761 }, 1762 1763 // Sets the z-index and display property of a page 1764 // It depends on the current view 1765 1766 _setPageLoc: function(page) { 1767 1768 var data = this.data(), 1769 view = this.turn('view'), 1770 loc = 0; 1771 1772 1773 if (page==view[0] || page==view[1]) 1774 loc = 1; 1775 else if ( 1776 (data.display=='single' && page==view[0]+1) || 1777 (data.display=='double' && page==view[0]-2 || page==view[1]+2) 1778 ) 1779 loc = 2; 1780 1781 if (!this.turn('animating')) 1782 switch (loc) { 1783 case 1: 1784 data.pageWrap[page].css( 1785 { 1786 zIndex: data.totalPages, 1787 display: '' 1788 }); 1789 break; 1790 case 2: 1791 data.pageWrap[page].css( 1792 { 1793 zIndex: data.totalPages-1, 1794 display: '' 1795 }); 1796 break; 1797 case 0: 1798 data.pageWrap[page].css( 1799 { 1800 zIndex: 0, 1801 display: (data.pageObjs[page].hasClass('fixed')) ? '' : 'none'} 1802 ); 1803 break; 1804 } 1805 1806 return loc; 1807 }, 1808 1809 // Gets and sets the options 1810 1811 options: function(options) { 1812 1813 if (options===undefined) { 1814 1815 return this.data().opts; 1816 1817 } else { 1818 1819 var data = this.data(); 1820 1821 // Set new values 1822 1823 $.extend(data.opts, options); 1824 1825 // Set pages 1826 1827 if (options.pages) 1828 this.turn('pages', options.pages); 1829 1830 // Set page 1831 1832 if (options.page) 1833 this.turn('page', options.page); 1834 1835 // Set display 1836 1837 if (options.display) 1838 this.turn('display', options.display); 1839 1840 // Set direction 1841 1842 if (options.direction) 1843 this.turn('direction', options.direction); 1844 1845 // Set size 1846 1847 if (options.width && options.height) 1848 this.turn('size', options.width, options.height); 1849 1850 // Add event listeners 1851 1852 if (options.when) 1853 for (var eventName in options.when) 1854 if (has(eventName, options.when)) { 1855 this.unbind(eventName). 1856 bind(eventName, options.when[eventName]); 1857 } 1858 1859 return this; 1860 } 1861 1862 }, 1863 1864 // Gets the current version 1865 1866 version: function() { 1867 1868 return version; 1869 1870 } 1871}, 1872 1873// Methods and properties for the flip page effect 1874 1875flipMethods = { 1876 1877 // Constructor 1878 1879 init: function(opts) { 1880 1881 this.data({f: { 1882 disabled: false, 1883 hover: false, 1884 effect: (this.hasClass('hard')) ? 'hard' : 'sheet' 1885 }}); 1886 1887 this.flip('options', opts); 1888 1889 flipMethods._addPageWrapper.call(this); 1890 1891 return this; 1892 }, 1893 1894 setData: function(d) { 1895 1896 var data = this.data(); 1897 1898 data.f = $.extend(data.f, d); 1899 1900 return this; 1901 }, 1902 1903 options: function(opts) { 1904 1905 var data = this.data().f; 1906 1907 if (opts) { 1908 flipMethods.setData.call(this, 1909 {opts: $.extend({}, data.opts || flipOptions, opts)}); 1910 return this; 1911 } else 1912 return data.opts; 1913 1914 }, 1915 1916 z: function(z) { 1917 1918 var data = this.data().f; 1919 1920 data.opts['z-index'] = z; 1921 1922 if (data.fwrapper) 1923 data.fwrapper.css({ 1924 zIndex: z || parseInt(data.parent.css('z-index'), 10) || 0 1925 }); 1926 1927 return this; 1928 }, 1929 1930 _cAllowed: function() { 1931 1932 var data = this.data().f, 1933 page = data.opts.page, 1934 turnData = data.opts.turn.data(), 1935 odd = page%2; 1936 1937 if (data.effect=='hard') { 1938 1939 return (turnData.direction=='ltr') ? 1940 [(odd) ? 'r' : 'l'] : 1941 [(odd) ? 'l' : 'r']; 1942 1943 } else { 1944 1945 if (turnData.display=='single') { 1946 1947 if (page==1) 1948 return (turnData.direction=='ltr') ? 1949 corners['forward'] : corners['backward']; 1950 else if (page==turnData.totalPages) 1951 return (turnData.direction=='ltr') ? 1952 corners['backward'] : corners['forward']; 1953 else 1954 return corners['all']; 1955 1956 } else { 1957 1958 return (turnData.direction=='ltr') ? 1959 corners[(odd) ? 'forward' : 'backward'] 1960 : 1961 corners[(odd) ? 'backward' : 'forward']; 1962 1963 } 1964 1965 } 1966 1967 }, 1968 1969 _cornerActivated: function(p) { 1970 1971 var data = this.data().f, 1972 width = this.width(), 1973 height = this.height(), 1974 point = {x: p.x, y: p.y, corner: ''}, 1975 csz = data.opts.cornerSize; 1976 1977 if (point.x<=0 || point.y<=0 || point.x>=width || point.y>=height) 1978 return false;
1979 1980 var allowedCorners = flipMethods._cAllowed.call(this); 1981 1982 switch (data.effect) { 1983 case 'hard': 1984 1985 if (point.x>width-csz) 1986 point.corner = 'r'; 1987 else if (point.x<csz) 1988 point.corner = 'l'; 1989 else 1990 return false; 1991 1992 break; 1993 1994 case 'sheet': 1995 1996 if (point.y<csz) 1997 point.corner+= 't'; 1998 else if (point.y>=height-csz) 1999 point.corner+= 'b'; 2000 else 2001 return false; 2002 2003 if (point.x<=csz) 2004 point.corner+= 'l'; 2005 else if (point.x>=width-csz) 2006 point.corner+= 'r'; 2007 else 2008 return false; 2009 2010 break; 2011 } 2012 2013 return (!point.corner || $.inArray(point.corner, allowedCorners)==-1) ? 2014 false : point; 2015 2016 }, 2017 2018 _isIArea: function(e) { 2019 2020 var pos = this.data().f.parent.offset(); 2021 2022 e = (isTouch && e.originalEvent) ? e.originalEvent.touches[0] : e; 2023 2024 return flipMethods._cornerActivated.call(this, 2025 { 2026 x: e.pageX-pos.left, 2027 y: e.pageY-pos.top 2028 }); 2029 2030 }, 2031 2032 _c: function(corner, opts) { 2033 2034 opts = opts || 0; 2035 2036 switch (corner) { 2037 case 'tl': 2038 return point2D(opts, opts); 2039 case 'tr': 2040 return point2D(this.width()-opts, opts); 2041 case 'bl': 2042 return point2D(opts, this.height()-opts); 2043 case 'br': 2044 return point2D(this.width()-opts, this.height()-opts); 2045 case 'l': 2046 return point2D(opts, 0); 2047 case 'r': 2048 return point2D(this.width()-opts, 0); 2049 } 2050 2051 }, 2052 2053 _c2: function(corner) { 2054 2055 switch (corner) { 2056 case 'tl': 2057 return point2D(this.width()*2, 0); 2058 case 'tr': 2059 return point2D(-this.width(), 0); 2060 case 'bl': 2061 return point2D(this.width()*2, this.height()); 2062 case 'br': 2063 return point2D(-this.width(), this.height()); 2064 case 'l': 2065 return point2D(this.width()*2, 0); 2066 case 'r': 2067 return point2D(-this.width(), 0); 2068 } 2069 2070 }, 2071 2072 _foldingPage: function() { 2073 2074 var data = this.data().f; 2075 2076 if (!data) 2077 return; 2078 2079 var opts = data.opts; 2080 2081 if (opts.turn) { 2082 data = opts.turn.data(); 2083 if (data.display == 'single') 2084 return (opts.next>1 || opts.page>1) ? data.pageObjs[0] : null; 2085 else 2086 return data.pageObjs[opts.next]; 2087 } 2088 2089 }, 2090 2091 _backGradient: function() { 2092 2093 var data = this.data().f, 2094 turnData = data.opts.turn.data(), 2095 gradient = turnData.opts.gradients && (turnData.display=='single' || 2096 (data.opts.page!=2 && data.opts.page!=turnData.totalPages-1)); 2097 2098 if (gradient && !data.bshadow) 2099 data.bshadow = $('<div/>', divAtt(0, 0, 1)). 2100 css({'position': '', width: this.width(), height: this.height()}). 2101 appendTo(data.parent); 2102 2103 return gradient; 2104 2105 }, 2106 2107 type: function () { 2108 2109 return this.data().f.effect; 2110 2111 }, 2112 2113 resize: function(full) { 2114 2115 var data = this.data().f, 2116 turnData = data.opts.turn.data(), 2117 width = this.width(), 2118 height = this.height(); 2119 2120 switch (data.effect) { 2121 case 'hard': 2122 2123 if (full) { 2124 data.wrapper.css({width: width, height: height}); 2125 data.fpage.css({width: width, height: height}); 2126 if (turnData.opts.gradients) { 2127 data.ashadow.css({width: width, height: height}); 2128 data.bshadow.css({width: width, height: height}); 2129 } 2130 } 2131 2132 break; 2133 case 'sheet': 2134 2135 if (full) { 2136 var size = Math.round(Math.sqrt(Math.pow(width, 2)+Math.pow(height, 2))); 2137 2138 data.wrapper.css({width: size, height: size}); 2139 data.fwrapper.css({width: size, height: size}). 2140 children(':first-child'). 2141 css({width: width, height: height}); 2142 2143 data.fpage.css({width: width, height: height}); 2144 2145 if (turnData.opts.gradients) 2146 data.ashadow.css({width: width, height: height}); 2147 2148 if (flipMethods._backGradient.call(this)) 2149 data.bshadow.css({width: width, height: height}); 2150 } 2151 2152 if (data.parent.is(':visible')) { 2153 var offset = findPos(data.parent[0]); 2154 2155 data.fwrapper.css({top: offset.top, 2156 left: offset.left}); 2157 2158 //if (data.opts.turn) { 2159 offset = findPos(data.opts.turn[0]); 2160 data.fparent.css({top: -offset.top, left: -offset.left}); 2161 //} 2162 } 2163 2164 this.flip('z', data.opts['z-index']); 2165 2166 break; 2167 } 2168 2169 }, 2170
2171 // Prepares the page by adding a general wrapper and another objects 2172 2173 _addPageWrapper: function() { 2174 2175 var att, 2176 data = this.data().f, 2177 turnData = data.opts.turn.data(), 2178 parent = this.parent(); 2179 2180 data.parent = parent; 2181 2182 if (!data.wrapper) 2183 switch (data.effect) { 2184 case 'hard': 2185 2186 var cssProperties = {}; 2187 cssProperties[vendor + 'transform-style'] = 'preserve-3d'; 2188 cssProperties[vendor + 'backface-visibility'] = 'hidden'; 2189 2190 data.wrapper = $('<div/>', divAtt(0, 0, 2)). 2191 css(cssProperties). 2192 appendTo(parent). 2193 prepend(this); 2194 2195 data.fpage = $('<div/>', divAtt(0, 0, 1)). 2196 css(cssProperties). 2197 appendTo(parent); 2198 2199 if (turnData.opts.gradients) { 2200 data.ashadow = $('<div/>', divAtt(0, 0, 0)). 2201 hide(). 2202 appendTo(parent); 2203 2204 data.bshadow = $('<div/>', divAtt(0, 0, 0)); 2205 } 2206 2207 break; 2208 case 'sheet': 2209 2210 var width = this.width(), 2211 height = this.height(), 2212 size = Math.round(Math.sqrt(Math.pow(width, 2)+Math.pow(height, 2))); 2213 2214 data.fparent = data.opts.turn.data().fparent; 2215 2216 if (!data.fparent) { 2217 var fparent = $('<div/>', {css: {'pointer-events': 'none'}}).hide(); 2218 fparent.data().flips = 0; 2219 fparent.css(divAtt(0, 0, 'auto', 'visible').css). 2220 appendTo(data.opts.turn); 2221 2222 data.opts.turn.data().fparent = fparent; 2223 data.fparent = fparent; 2224 } 2225 2226 this.css({position: 'absolute', top: 0, left: 0, bottom: 'auto', right: 'auto'}); 2227 2228 data.wrapper = $('<div/>', divAtt(0, 0, this.css('z-index'))). 2229 appendTo(parent). 2230 prepend(this); 2231 2232 data.fwrapper = $('<div/>', divAtt(parent.offset().top, parent.offset().left)). 2233 hide(). 2234 appendTo(data.fparent); 2235 2236 data.fpage = $('<div/>', divAtt(0, 0, 0, 'visible')). 2237 css({cursor: 'default'}). 2238 appendTo(data.fwrapper); 2239 2240 if (turnData.opts.gradients) 2241 data.ashadow = $('<div/>', divAtt(0, 0, 1)). 2242 appendTo(data.fpage); 2243 2244 flipMethods.setData.call(this, data); 2245 2246 break; 2247 } 2248 2249 // Set size 2250 flipMethods.resize.call(this, true); 2251 2252 }, 2253 2254 // Takes a 2P point from the screen and applies the transformation 2255 2256 _fold: function(point) { 2257 2258 var data = this.data().f, 2259 turnData = data.opts.turn.data(), 2260 o = flipMethods._c.call(this, point.corner), 2261 width = this.width(), 2262 height = this.height(); 2263 2264 switch (data.effect) { 2265 2266 case 'hard': 2267 2268 if (point.corner=='l') 2269 point.x = Math.min(Math.max(point.x, 0), width*2); 2270 else 2271 point.x = Math.max(Math.min(point.x, width), -width); 2272 2273 var leftPos, 2274 shadow, 2275 gradientX, 2276 fpageOrigin, 2277 parentOrigin, 2278 totalPages = turnData.totalPages, 2279 zIndex = data.opts['z-index'] || totalPages, 2280 parentCss = {'overflow': 'visible'}, 2281 relX = (o.x) ? (o.x - point.x)/width : point.x/width, 2282 angle = relX * 90, 2283 half = angle<90; 2284 2285 switch (point.corner) { 2286 case 'l': 2287 2288 fpageOrigin = '0% 50%'; 2289 parentOrigin = '100% 50%'; 2290 2291 if (half) { 2292 leftPos = 0; 2293 shadow = data.opts.next-1>0; 2294 gradientX = 1; 2295 } else { 2296 leftPos = '100%'; 2297 shadow = data.opts.page+1<totalPages; 2298 gradientX = 0; 2299 } 2300 2301 break; 2302 case 'r': 2303 2304 fpageOrigin = '100% 50%'; 2305 parentOrigin = '0% 50%'; 2306 angle = -angle; 2307 width = -width; 2308 2309 if (half) { 2310 leftPos = 0; 2311 shadow = data.opts.next+1<totalPages; 2312 gradientX = 0; 2313 } else { 2314 leftPos = '-100%'; 2315 shadow = data.opts.page!=1; 2316 gradientX = 1; 2317 } 2318 2319 break; 2320 } 2321 2322 parentCss[vendor+'perspective-origin'] = parentOrigin; 2323 2324 data.wrapper.transform('rotateY('+angle+'deg)' + 2325 'translate3d(0px, 0px, '+(this.attr('depth')||0)+'px)', parentOrigin); 2326 2327 data.fpage.transform('translateX('+width+'px) rotateY('+(180+angle)+'deg)', fpageOrigin); 2328 2329 data.parent.css(parentCss); 2330 2331 if (half) { 2332 relX = -relX+1; 2333 data.wrapper.css({zIndex: zIndex+1}); 2334 data.fpage.css({zIndex: zIndex}); 2335 } else { 2336 relX = relX-1; 2337 data.wrapper.css({zIndex: zIndex}); 2338 data.fpage.css({zIndex: zIndex+1}); 2339 } 2340 2341 if (turnData.opts.gradients) { 2342 if (shadow) 2343 data.ashadow.css({ 2344 display: '', 2345 left: leftPos, 2346 backgroundColor: 'rgba(0,0,0,'+(0.5*relX)+')' 2347 }). 2348 transform('rotateY(0deg)'); 2349 else 2350 data.ashadow.hide(); 2351 2352 data.bshadow.css({opacity:-relX + 1}); 2353 2354 if (half) { 2355 if (data.bshadow.parent()[0]!=data.wrapper[0]) { 2356 data.bshadow.appendTo(data.wrapper); 2357 } 2358 } else { 2359 if (data.bshadow.parent()[0]!=data.fpage[0]) { 2360 data.bshadow.appendTo(data.fpage); 2361 } 2362 } 2363 /*data.bshadow.css({ 2364 backgroundColor: 'rgba(0,0,0,'+(0.1)+')' 2365 })*/ 2366 gradient(data.bshadow, point2D(gradientX * 100, 0), point2D((-gradientX + 1)*100, 0), 2367 [[0, 'rgba(0,0,0,0.3)'],[1, 'rgba(0,0,0,0)']],2); 2368 2369 } 2370 2371 break; 2372 case 'sheet': 2373 2374 var that = this, 2375 a = 0, 2376 alpha = 0, 2377 beta, 2378 px, 2379 gradientEndPointA, 2380 gradientEndPointB, 2381 gradientStartVal, 2382 gradientSize, 2383 gradientOpacity, 2384 shadowVal, 2385 mv = point2D(0, 0), 2386 df = point2D(0, 0), 2387 tr = point2D(0, 0), 2388 folding = flipMethods._foldingPage.call(this), 2389 tan = Math.tan(alpha), 2390 ac = turnData.opts.acceleration, 2391 h = data.wrapper.height(), 2392 top = point.corner.substr(0, 1) == 't', 2393 left = point.corner.substr(1, 1) == 'l', 2394 2395 compute = function() { 2396 2397 var rel = point2D(0, 0); 2398 var middle = point2D(0, 0); 2399 2400 rel.x = (o.x) ? o.x - point.x : point.x; 2401 2402 if (!hasRot) { 2403 rel.y = 0; 2404 } else { 2405 rel.y = (o.y) ? o.y - point.y : point.y; 2406 } 2407 2408 middle.x = (left)? width - rel.x/2 : point.x + rel.x/2; 2409 middle.y = rel.y/2; 2410 2411 var alpha = A90-Math.atan2(rel.y, rel.x), 2412 gamma = alpha - Math.atan2(middle.y, middle.x), 2413 distance = Math.max(0, Math.sin(gamma) * Math.sqrt(Math.pow(middle.x, 2) + Math.pow(middle.y, 2))); 2414 2415 a = deg(alpha); 2416 2417 tr = point2D(distance * Math.sin(alpha), distance * Math.cos(alpha)); 2418 2419 if (alpha > A90) {
2420 tr.x = tr.x + Math.abs(tr.y * rel.y/rel.x); 2421 tr.y = 0; 2422 if (Math.round(tr.x*Math.tan(PI-alpha)) < height) { 2423 point.y = Math.sqrt(Math.pow(height, 2)+2 * middle.x * rel.x); 2424 if (top) point.y = height - point.y; 2425 return compute(); 2426 } 2427 } 2428 2429 if (alpha>A90) { 2430 var beta = PI-alpha, dd = h - height/Math.sin(beta); 2431 mv = point2D(Math.round(dd*Math.cos(beta)), Math.round(dd*Math.sin(beta))); 2432 if (left) mv.x = - mv.x; 2433 if (top) mv.y = - mv.y; 2434 } 2435 2436 px = Math.round(tr.y/Math.tan(alpha) + tr.x); 2437 2438 var side = width - px, 2439 sideX = side*Math.cos(alpha*2), 2440 sideY = side*Math.sin(alpha*2); 2441 df = point2D( 2442 Math.round((left ? side -sideX : px+sideX)), 2443 Math.round((top) ? sideY : height - sideY)); 2444 2445 // Gradients 2446 if (turnData.opts.gradients) { 2447 2448 gradientSize = side*Math.sin(alpha); 2449 2450 var endingPoint = flipMethods._c2.call(that, point.corner), 2451 far = Math.sqrt(Math.pow(endingPoint.x-point.x, 2)+Math.pow(endingPoint.y-point.y, 2))/width; 2452 2453 shadowVal = Math.sin(A90*((far>1) ? 2 - far : far)); 2454 2455 gradientOpacity = Math.min(far, 1); 2456 2457 2458 gradientStartVal = gradientSize>100 ? (gradientSize-100)/gradientSize : 0; 2459 2460 gradientEndPointA = point2D( 2461 gradientSize*Math.sin(alpha)/width*100, 2462 gradientSize*Math.cos(alpha)/height*100); 2463 2464 2465 if (flipMethods._backGradient.call(that)) { 2466 2467 gradientEndPointB = point2D( 2468 gradientSize*1.2*Math.sin(alpha)/width*100, 2469 gradientSize*1.2*Math.cos(alpha)/height*100); 2470 2471 if (!left) gradientEndPointB.x = 100-gradientEndPointB.x; 2472 if (!top) gradientEndPointB.y = 100-gradientEndPointB.y; 2473 2474 } 2475 2476 } 2477 2478 tr.x = Math.round(tr.x); 2479 tr.y = Math.round(tr.y); 2480 2481 return true; 2482 }, 2483 2484 transform = function(tr, c, x, a) { 2485 2486 var f = ['0', 'auto'], mvW = (width-h)*x[0]/100, mvH = (height-h)*x[1]/100, 2487 cssA = {left: f[c[0]], top: f[c[1]], right: f[c[2]], bottom: f[c[3]]}, 2488 cssB = {}, 2489 aliasingFk = (a!=90 && a!=-90) ? (left ? -1 : 1) : 0, 2490 origin = x[0] + '% ' + x[1] + '%'; 2491 2492 that.css(cssA). 2493 transform(rotate(a) + translate(tr.x + aliasingFk, tr.y, ac), origin); 2494 2495 data.fpage.css(cssA).transform( 2496 rotate(a) + 2497 translate(tr.x + df.x - mv.x - width*x[0]/100, tr.y + df.y - mv.y - height*x[1]/100, ac) + 2498 rotate((180/a - 2)*a), 2499 origin); 2500 2501 data.wrapper.transform(translate(-tr.x + mvW-aliasingFk, -tr.y + mvH, ac) + rotate(-a), origin); 2502 2503 data.fwrapper.transform(translate(-tr.x + mv.x + mvW, -tr.y + mv.y + mvH, ac) + rotate(-a), origin); 2504 2505 if (turnData.opts.gradients) { 2506 2507 if (x[0]) 2508 gradientEndPointA.x = 100-gradientEndPointA.x; 2509 2510 if (x[1]) 2511 gradientEndPointA.y = (100-gradientEndPointA.y); 2512 2513 cssB['box-shadow'] = '0 0 20px rgba(0,0,0,'+(0.5*shadowVal)+')'; 2514 folding.css(cssB); 2515 2516 gradient(data.ashadow, 2517 point2D(left?100:0, top?0:100), 2518 point2D(gradientEndPointA.x, gradientEndPointA.y), 2519 [[gradientStartVal, 'rgba(0,0,0,0)'], 2520 [((1-gradientStartVal)*0.8)+gradientStartVal, 'rgba(0,0,0,'+(0.2*gradientOpacity)+')'], 2521 [1, 'rgba(255,255,255,'+(0.2*gradientOpacity)+')']], 2522 3, 2523 alpha); 2524 2525 if (flipMethods._backGradient.call(that)) 2526 gradient(data.bshadow, 2527 point2D(left?0:100, top?0:100), 2528 point2D(gradientEndPointB.x, gradientEndPointB.y), 2529 [[0.6, 'rgba(0,0,0,0)'], 2530 [0.8, 'rgba(0,0,0,'+(0.3*gradientOpacity)+')'], 2531 [1, 'rgba(0,0,0,0)'] 2532 ], 2533 3); 2534 } 2535 2536 }; 2537 2538 switch (point.corner) { 2539 case 'l' : 2540 2541 2542 break; 2543 case 'r' : 2544 2545 2546 break; 2547 case 'tl' : 2548 point.x = Math.max(point.x, 1); 2549 compute(); 2550 transform(tr, [1,0,0,1], [100, 0], a); 2551 break; 2552 case 'tr' : 2553 point.x = Math.min(point.x, width-1); 2554 compute(); 2555 transform(point2D(-tr.x, tr.y), [0,0,0,1], [0, 0], -a); 2556 break; 2557 case 'bl' : 2558 point.x = Math.max(point.x, 1); 2559 compute(); 2560 transform(point2D(tr.x, -tr.y), [1,1,0,0], [100, 100], -a); 2561 break; 2562 case 'br' : 2563 point.x = Math.min(point.x, width-1); 2564 compute(); 2565 transform(point2D(-tr.x, -tr.y), [0,1,1,0], [0, 100], a); 2566 break; 2567 } 2568 2569 break; 2570 } 2571 2572 data.point = point; 2573 2574 }, 2575 2576 _moveFoldingPage: function(move) { 2577 2578 var data = this.data().f; 2579 2580 if (!data) 2581 return; 2582 2583 var turn = data.opts.turn, 2584 turnData = turn.data(), 2585 place = turnData.pagePlace; 2586 2587 if (move) { 2588 2589 var nextPage = data.opts.next; 2590 2591 if (place[nextPage]!=data.opts.page) { 2592 2593 if (data.folding) 2594 flipMethods._moveFoldingPage.call(this, false); 2595 2596 var folding = flipMethods._foldingPage.call(this); 2597 2598 folding.appendTo(data.fpage); 2599 place[nextPage] = data.opts.page; 2600 data.folding = nextPage; 2601 } 2602 2603 turn.turn('update'); 2604 2605 } else { 2606 2607 if (data.folding) { 2608 2609 if (turnData.pages[data.folding]) { 2610 2611 // If we have flip available 2612 2613 var flipData = turnData.pages[data.folding].data().f; 2614 2615 turnData.pageObjs[data.folding]. 2616 appendTo(flipData.wrapper); 2617 2618 } else if (turnData.pageWrap[data.folding]) { 2619 2620 // If we have the pageWrapper 2621 2622 turnData.pageObjs[data.folding]. 2623 appendTo(turnData.pageWrap[data.folding]); 2624 2625 } 2626 2627 if (data.folding in place) { 2628 place[data.folding] = data.folding; 2629 } 2630 2631 delete data.folding; 2632 2633 } 2634 } 2635 }, 2636 2637 _showFoldedPage: function(c, animate) { 2638 2639 var folding = flipMethods._foldingPage.call(this), 2640 dd = this.data(), 2641 data = dd.f, 2642 visible = data.visible; 2643 2644 if (folding) { 2645 2646 if (!visible || !data.point || data.point.corner!=c.corner) { 2647 2648 var corner = ( 2649 data.status=='hover' || 2650 data.status=='peel' || 2651 data.opts.turn.data().mouseAction) ? 2652 c.corner : null; 2653 2654 visible = false;
2655 2656 if (trigger('start', this, [data.opts, corner])=='prevented') 2657 return false; 2658 2659 } 2660 2661 if (animate) { 2662 2663 var that = this, 2664 point = (data.point && data.point.corner==c.corner) ? 2665 data.point : flipMethods._c.call(this, c.corner, 1); 2666 2667 this.animatef({ 2668 from: [point.x, point.y], 2669 to: [c.x, c.y], 2670 duration: 500, 2671 frame: function(v) { 2672 c.x = Math.round(v[0]); 2673 c.y = Math.round(v[1]); 2674 flipMethods._fold.call(that, c); 2675 } 2676 }); 2677 2678 } else { 2679 2680 flipMethods._fold.call(this, c); 2681 2682 if (dd.effect && !dd.effect.turning) 2683 this.animatef(false); 2684 2685 } 2686 2687 if (!visible) { 2688 2689 switch(data.effect) { 2690 case 'hard': 2691 2692 data.visible = true; 2693 flipMethods._moveFoldingPage.call(this, true); 2694 data.fpage.show(); 2695 if (data.opts.shadows) 2696 data.bshadow.show(); 2697 2698 break; 2699 case 'sheet': 2700 2701 data.visible = true; 2702 data.fparent.show().data().flips++; 2703 flipMethods._moveFoldingPage.call(this, true); 2704 data.fwrapper.show(); 2705 if (data.bshadow) 2706 data.bshadow.show(); 2707 2708 break; 2709 } 2710 2711 } 2712 2713 return true; 2714 2715 } 2716 2717 return false; 2718 }, 2719 2720 hide: function() { 2721 2722 var data = this.data().f, 2723 turnData = data.opts.turn.data(), 2724 folding = flipMethods._foldingPage.call(this); 2725 2726 switch (data.effect) { 2727 case 'hard': 2728 2729 if (turnData.opts.gradients) { 2730 data.bshadowLoc = 0; 2731 data.bshadow.remove(); 2732 data.ashadow.hide(); 2733 } 2734 2735 data.wrapper.transform(''); 2736 data.fpage.hide(); 2737 2738 break; 2739 case 'sheet': 2740 2741 if ((--data.fparent.data().flips)===0) 2742 data.fparent.hide(); 2743 2744 this.css({left: 0, top: 0, right: 'auto', bottom: 'auto'}). 2745 transform(''); 2746 2747 data.wrapper.transform(''); 2748 2749 data.fwrapper.hide(); 2750 2751 if (data.bshadow) 2752 data.bshadow.hide(); 2753 2754 folding.transform(''); 2755 2756 break; 2757 } 2758 2759 data.visible = false; 2760 2761 return this; 2762 }, 2763 2764 hideFoldedPage: function(animate) { 2765 2766 var data = this.data().f; 2767 2768 if (!data.point) return; 2769 2770 var that = this, 2771 p1 = data.point, 2772 hide = function() { 2773 data.point = null; 2774 data.status = ''; 2775 that.flip('hide'); 2776 that.trigger('end', [data.opts, false]); 2777 }; 2778 2779 if (animate) { 2780 2781 var p4 = flipMethods._c.call(this, p1.corner), 2782 top = (p1.corner.substr(0,1)=='t'), 2783 delta = (top) ? Math.min(0, p1.y-p4.y)/2 : Math.max(0, p1.y-p4.y)/2, 2784 p2 = point2D(p1.x, p1.y+delta), 2785 p3 = point2D(p4.x, p4.y-delta); 2786 2787 this.animatef({ 2788 from: 0, 2789 to: 1, 2790 frame: function(v) { 2791 var np = bezier(p1, p2, p3, p4, v); 2792 p1.x = np.x; 2793 p1.y = np.y; 2794 flipMethods._fold.call(that, p1); 2795 }, 2796 complete: hide, 2797 duration: 800, 2798 hiding: true 2799 }); 2800 2801 } else { 2802 2803 this.animatef(false); 2804 hide(); 2805 2806 } 2807 }, 2808 2809 turnPage: function(corner) { 2810 2811 var that = this, 2812 data = this.data().f, 2813 turnData = data.opts.turn.data(); 2814 2815 corner = {corner: (data.corner) ? 2816 data.corner.corner : 2817 corner || flipMethods._cAllowed.call(this)[0]}; 2818 2819 var p1 = data.point || 2820 flipMethods._c.call(this, 2821 corner.corner, 2822 (data.opts.turn) ? turnData.opts.elevation : 0), 2823 p4 = flipMethods._c2.call(this, corner.corner); 2824 2825 this.trigger('flip'). 2826 animatef({ 2827 from: 0, 2828 to: 1, 2829 frame: function(v) { 2830 2831 var np = bezier(p1, p1, p4, p4, v); 2832 corner.x = np.x; 2833 corner.y = np.y; 2834 flipMethods._showFoldedPage.call(that, corner); 2835 2836 }, 2837 complete: function() { 2838 2839 that.trigger('end', [data.opts, true]); 2840 2841 }, 2842 duration: turnData.opts.duration, 2843 turning: true 2844 }); 2845 2846 data.corner = null; 2847 }, 2848 2849 moving: function() { 2850 2851 return 'effect' in this.data(); 2852 2853 }, 2854 2855 isTurning: function() { 2856 2857 return this.flip('moving') && this.data().effect.turning; 2858 2859 }, 2860 2861 corner: function() { 2862 2863 return this.data().f.corner; 2864 2865 }, 2866 2867 _eventStart: function(e) { 2868 2869 var data = this.data().f, 2870 turn = data.opts.turn; 2871 2872 if (!data.corner && !data.disabled && !this.flip('isTurning') && 2873 data.opts.page==turn.data().pagePlace[data.opts.page]) 2874 { 2875 2876 data.corner = flipMethods._isIArea.call(this, e); 2877 2878 if (data.corner && flipMethods._foldingPage.call(this)) { 2879 2880 this.trigger('pressed', [data.point]); 2881 flipMethods._showFoldedPage.call(this, data.corner); 2882 2883 return false;
2884 2885 } else 2886 data.corner = null; 2887 2888 } 2889 2890 }, 2891 2892 _eventMove: function(e) { 2893 2894 var data = this.data().f; 2895 2896 if (!data.disabled) { 2897 2898 e = (isTouch) ? e.originalEvent.touches : [e]; 2899 2900 if (data.corner) { 2901 2902 var pos = data.parent.offset(); 2903 data.corner.x = e[0].pageX-pos.left; 2904 data.corner.y = e[0].pageY-pos.top; 2905 flipMethods._showFoldedPage.call(this, data.corner); 2906 2907 } else if (data.hover && !this.data().effect && this.is(':visible')) { 2908 2909 var point = flipMethods._isIArea.call(this, e[0]); 2910 2911 if (point) { 2912 2913 if ((data.effect=='sheet' && point.corner.length==2) || data.effect=='hard') { 2914 data.status = 'hover'; 2915 var origin = flipMethods._c.call(this, point.corner, data.opts.cornerSize/2); 2916 point.x = origin.x; 2917 point.y = origin.y; 2918 flipMethods._showFoldedPage.call(this, point, true); 2919 } 2920 2921 } else { 2922 2923 if (data.status=='hover') { 2924 data.status = ''; 2925 flipMethods.hideFoldedPage.call(this, true); 2926 } 2927 2928 } 2929 2930 } 2931 2932 } 2933 2934 }, 2935 2936 _eventEnd: function() { 2937 2938 var data = this.data().f, 2939 corner = data.corner; 2940 2941 if (!data.disabled && corner) { 2942 if (trigger('released', this, [data.point || corner])!='prevented') { 2943 flipMethods.hideFoldedPage.call(this, true); 2944 } 2945 } 2946 2947 data.corner = null; 2948 2949 }, 2950 2951 disable: function(disable) { 2952 2953 flipMethods.setData.call(this, {'disabled': disable}); 2954 return this; 2955 2956 }, 2957 2958 hover: function(hover) { 2959 2960 flipMethods.setData.call(this, {'hover': hover}); 2961 return this; 2962 2963 }, 2964 2965 peel: function (corner, animate) { 2966 2967 var data = this.data().f; 2968 2969 if (corner) { 2970 2971 if ($.inArray(corner, corners.all)==-1) 2972 throw turnError('Corner '+corner+' is not permitted'); 2973 2974 if ($.inArray(corner, flipMethods._cAllowed.call(this))!=-1) { 2975 2976 var point = flipMethods._c.call(this, corner, data.opts.cornerSize/2); 2977 2978 data.status = 'peel'; 2979 2980 flipMethods._showFoldedPage.call(this, 2981 { 2982 corner: corner, 2983 x: point.x, 2984 y: point.y 2985 }, animate); 2986 2987 } 2988 2989 2990 } else { 2991 2992 data.status = ''; 2993 2994 flipMethods.hideFoldedPage.call(this, animate); 2995 2996 } 2997 2998 return this; 2999 } 3000}; 3001 3002 3003// Processes classes 3004 3005function dec(that, methods, args) { 3006 3007 if (!args[0] || typeof(args[0])=='object') 3008 return methods.init.apply(that, args); 3009 3010 else if (methods[args[0]]) 3011 return methods[args[0]].apply(that, Array.prototype.slice.call(args, 1)); 3012 3013 else 3014 throw turnError(args[0] + ' is not a method or property'); 3015 3016} 3017 3018 3019// Attributes for a layer 3020 3021function divAtt(top, left, zIndex, overf) { 3022 3023 return {'css': { 3024 position: 'absolute', 3025 top: top, 3026 left: left, 3027 'overflow': overf || 'hidden', 3028 zIndex: zIndex || 'auto' 3029 } 3030}; 3031 3032} 3033 3034// Gets a 2D point from a bezier curve of four points 3035 3036function bezier(p1, p2, p3, p4, t) { 3037 3038 var a = 1 - t, 3039 b = a * a * a, 3040 c = t * t * t; 3041 3042 return point2D(Math.round(b*p1.x + 3*t*a*a*p2.x + 3*t*t*a*p3.x + c*p4.x), 3043 Math.round(b*p1.y + 3*t*a*a*p2.y + 3*t*t*a*p3.y + c*p4.y)); 3044 3045} 3046 3047// Converts an angle from degrees to radians 3048 3049function rad(degrees) { 3050 3051 return degrees/180*PI; 3052 3053} 3054 3055// Converts an angle from radians to degrees 3056 3057function deg(radians) { 3058 3059 return radians/PI*180; 3060 3061} 3062 3063// Gets a 2D point 3064 3065function point2D(x, y) { 3066 3067 return {x: x, y: y}; 3068 3069} 3070 3071// Webkit 534.3 on Android wrongly repaints elements that use overflow:hidden + rotation 3072 3073function rotationAvailable() { 3074 var parts; 3075 3076 if ((parts = /AppleWebkit\/([0-9\.]+)/i.exec(navigator.userAgent))) { 3077 var webkitVersion = parseFloat(parts[1]); 3078 return (webkitVersion>534.3); 3079 } else { 3080 return true; 3081 } 3082} 3083 3084// Returns the traslate value 3085 3086function translate(x, y, use3d) { 3087 3088 return (has3d && use3d) ? ' translate3d(' + x + 'px,' + y + 'px, 0px) ' 3089 : ' translate(' + x + 'px, ' + y + 'px) '; 3090 3091} 3092 3093// Returns the rotation value 3094 3095function rotate(degrees) { 3096 3097 return ' rotate(' + degrees + 'deg) '; 3098 3099} 3100 3101// Checks if a property belongs to an object 3102 3103function has(property, object) { 3104 3105 return Object.prototype.hasOwnProperty.call(object, property); 3106 3107} 3108 3109// Gets the CSS3 vendor prefix 3110 3111function getPrefix() { 3112 3113 var vendorPrefixes = ['Moz','Webkit','Khtml','O','ms'],
3114 len = vendorPrefixes.length, 3115 vendor = ''; 3116 3117 while (len--) 3118 if ((vendorPrefixes[len] + 'Transform') in document.body.style) 3119 vendor='-'+vendorPrefixes[len].toLowerCase()+'-'; 3120 3121 return vendor; 3122 3123} 3124 3125// Detects the transitionEnd Event 3126 3127function getTransitionEnd() { 3128 3129 var t, 3130 el = document.createElement('fakeelement'), 3131 transitions = { 3132 'transition':'transitionend', 3133 'OTransition':'oTransitionEnd', 3134 'MSTransition':'transitionend', 3135 'MozTransition':'transitionend', 3136 'WebkitTransition':'webkitTransitionEnd' 3137 }; 3138 3139 for (t in transitions) { 3140 if (el.style[t] !== undefined) { 3141 return transitions[t]; 3142 } 3143 } 3144} 3145 3146// Gradients 3147 3148function gradient(obj, p0, p1, colors, numColors) { 3149 3150 var j, cols = []; 3151 3152 if (vendor=='-webkit-') { 3153 3154 for (j = 0; j<numColors; j++) 3155 cols.push('color-stop('+colors[j][0]+', '+colors[j][1]+')'); 3156 3157 obj.css({'background-image': 3158 '-webkit-gradient(linear, '+ 3159 p0.x+'% '+ 3160 p0.y+'%,'+ 3161 p1.x+'% '+ 3162 p1.y+'%, '+ 3163 cols.join(',') + ' )'}); 3164 } else { 3165 3166 p0 = {x:p0.x/100 * obj.width(), y:p0.y/100 * obj.height()}; 3167 p1 = {x:p1.x/100 * obj.width(), y:p1.y/100 * obj.height()}; 3168 3169 var dx = p1.x-p0.x, 3170 dy = p1.y-p0.y, 3171 angle = Math.atan2(dy, dx), 3172 angle2 = angle - Math.PI/2, 3173 diagonal = Math.abs(obj.width()*Math.sin(angle2))+Math.abs(obj.height()*Math.cos(angle2)), 3174 gradientDiagonal = Math.sqrt(dy*dy + dx*dx), 3175 corner = point2D((p1.x<p0.x) ? obj.width() : 0, (p1.y<p0.y) ? obj.height() : 0), 3176 slope = Math.tan(angle), 3177 inverse = -1/slope, 3178 x = (inverse*corner.x - corner.y - slope*p0.x + p0.y)/(inverse-slope), 3179 c = {x: x, y: inverse*x - inverse*corner.x + corner.y}, 3180 segA = (Math.sqrt( Math.pow(c.x-p0.x,2) + Math.pow(c.y-p0.y,2))); 3181 3182 for (j = 0; j<numColors; j++) 3183 cols.push(' '+colors[j][1]+' '+((segA + gradientDiagonal*colors[j][0])*100/diagonal)+'%'); 3184 3185 obj.css({'background-image': vendor+'linear-gradient(' + (-angle) + 'rad,' + cols.join(',') + ')'}); 3186 } 3187} 3188 3189 3190// Triggers an event 3191 3192function trigger(eventName, context, args) { 3193 3194 var event = $.Event(eventName); 3195 context.trigger(event, args); 3196 if (event.isDefaultPrevented()) 3197 return 'prevented'; 3198 else if (event.isPropagationStopped()) 3199 return 'stopped'; 3200 else 3201 return ''; 3202} 3203 3204// JS Errors 3205 3206function turnError(message) { 3207 3208 function TurnJsError(message) { 3209 this.name = "TurnJsError"; 3210 this.message = message; 3211 } 3212 3213 TurnJsError.prototype = new Error(); 3214 TurnJsError.prototype.constructor = TurnJsError; 3215 return new TurnJsError(message); 3216 3217} 3218 3219// Find the offset of an element ignoring its transformation 3220 3221function findPos(obj) { 3222 3223 var offset = {top: 0, left: 0}; 3224 3225 do{ 3226 offset.left += obj.offsetLeft; 3227 offset.top += obj.offsetTop; 3228 } while ((obj = obj.offsetParent)); 3229 3230 return offset; 3231 3232} 3233 3234// Checks if there's hard page compatibility 3235// IE9 is the only browser that does not support hard pages 3236 3237function hasHardPage() { 3238 return (navigator.userAgent.indexOf('MSIE 9.0')==-1); 3239} 3240 3241// Request an animation 3242 3243window.requestAnim = (function() { 3244 return window.requestAnimationFrame || 3245 window.webkitRequestAnimationFrame || 3246 window.mozRequestAnimationFrame || 3247 window.oRequestAnimationFrame || 3248 window.msRequestAnimationFrame || 3249 function(callback) { 3250 window.setTimeout(callback, 1000 / 60); 3251 }; 3252 3253})(); 3254 3255// Extend $.fn 3256 3257$.extend($.fn, { 3258 3259 flip: function() { 3260 return dec($(this[0]), flipMethods, arguments); 3261 }, 3262 3263 turn: function() { 3264 return dec($(this[0]), turnMethods, arguments); 3265 }, 3266 3267 transform: function(transform, origin) { 3268 3269 var properties = {}; 3270 3271 if (origin) 3272 properties[vendor+'transform-origin'] = origin; 3273 3274 properties[vendor+'transform'] = transform; 3275 3276 return this.css(properties); 3277 3278 }, 3279 3280 animatef: function(point) { 3281 3282 var data = this.data(); 3283 3284 if (data.effect) 3285 data.effect.stop(); 3286 3287 if (point) { 3288 3289 if (!point.to.length) point.to = [point.to]; 3290 if (!point.from.length) point.from = [point.from]; 3291 3292 var diff = [], 3293 len = point.to.length, 3294 animating = true, 3295 that = this, 3296 time = (new Date()).getTime(), 3297 frame = function() { 3298 3299 if (!data.effect || !animating) 3300 return; 3301 3302 var v = [], 3303 timeDiff = Math.min(point.duration, (new Date()).getTime() - time); 3304 3305 for (var i = 0; i < len; i++) 3306 v.push(data.effect.easing(1, timeDiff, point.from[i], diff[i], point.duration)); 3307 3308 point.frame((len==1) ? v[0] : v); 3309 3310 if (timeDiff==point.duration) { 3311 delete data['effect']; 3312 that.data(data); 3313 if (point.complete) 3314 point.complete(); 3315 } else { 3316 window.requestAnim(frame); 3317 } 3318 }; 3319 3320 for (var i = 0; i < len; i++) 3321 diff.push(point.to[i] - point.from[i]); 3322 3323 data.effect = $.extend({ 3324 stop: function() { 3325 animating = false;
3326 }, 3327 easing: function (x, t, b, c, data) { 3328 return c * Math.sqrt(1 - (t=t/data-1)*t) + b; 3329 } 3330 }, point); 3331 3332 this.data(data); 3333 3334 frame(); 3335 3336 } else { 3337 3338 delete data['effect']; 3339 3340 } 3341 } 3342}); 3343 3344// Export some globals 3345 3346$.isTouch = isTouch; 3347$.mouseEvents = mouseEvents; 3348$.cssPrefix = getPrefix; 3349$.cssTransitionEnd = getTransitionEnd; 3350$.findPos = findPos; 3351 3352})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.