turn.html4.js 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394
  1. /**
  2. * turn.js 4th release HTML4 version
  3. * turnjs.com
  4. * turnjs.com/license.txt
  5. *
  6. * Copyright (C) 2012 Emmanuel Garcia
  7. * All rights reserved
  8. **/
  9. (function($) {
  10. 'use strict';
  11. var has3d,
  12. vendor = '',
  13. version = '4.1.0',
  14. isTouch = false,
  15. mouseEvents =
  16. {
  17. down: 'mousedown',
  18. move: 'mousemove',
  19. up: 'mouseup',
  20. over: 'mouseover',
  21. out: 'mouseout'
  22. },
  23. corners = {
  24. backward: ['l'],
  25. forward: ['r'],
  26. all: ['l', 'r']
  27. },
  28. // Display values
  29. displays = ['single', 'double'],
  30. // Direction values
  31. directions = ['ltr', 'rtl'],
  32. // Default options
  33. turnOptions = {
  34. // Enables hardware acceleration
  35. acceleration: true,
  36. // Display
  37. display: 'double',
  38. // Duration of transition in milliseconds
  39. duration: 600,
  40. // First page
  41. page: 1,
  42. // Enables gradients
  43. gradients: true,
  44. // Events
  45. when: null
  46. },
  47. flipOptions = {
  48. // Enables hardware acceleration
  49. acceleration: true,
  50. // Corners
  51. // backward: Activates both tl and bl corners
  52. // forward: Activates both tr and br corners
  53. // all: Activates all the corners
  54. corners: 'forward',
  55. // Size of the active zone of each corner
  56. cornerSize: 100,
  57. // Duration of transition in milliseconds
  58. duration: 600,
  59. // Enables gradients
  60. gradients: true
  61. },
  62. // Number of pages in the DOM, minimum value: 6
  63. pagesInDOM = 6,
  64. turnMethods = {
  65. // Singleton constructor
  66. // $('#selector').turn([options]);
  67. init: function(opts) {
  68. if (this.length>1)
  69. throw turnError('This selector has more than 1 element');
  70. // Define constants
  71. vendor = getPrefix();
  72. var i, pageNum = 0, data = this.data(), ch = this.children();
  73. // Set initial configuration
  74. opts = $.extend({
  75. width: this.width(),
  76. height: this.height(),
  77. direction: this.attr('dir') || this.css('direction') || 'ltr'
  78. }, turnOptions, opts);
  79. data.opts = opts;
  80. data.pageObjs = {};
  81. data.pages = {};
  82. data.pageWrap = {};
  83. data.pagePlace = {};
  84. data.pageMv = [];
  85. data.zoom = 1;
  86. data.totalPages = opts.pages || 0;
  87. data.docEvents = {
  88. mouseStart: function(e) {
  89. for (var page in data.pages)
  90. if (has(page, data.pages) &&
  91. flipMethods._eventStart.call(data.pages[page], e)===false)
  92. return false;
  93. },
  94. mouseMove: function(e) {
  95. for (var page in data.pages)
  96. if (has(page, data.pages))
  97. flipMethods._eventMove.call(data.pages[page], e);
  98. },
  99. mouseEnd: function(e) {
  100. for (var page in data.pages)
  101. if (has(page, data.pages))
  102. flipMethods._eventEnd.call(data.pages[page], e);
  103. }
  104. };
  105. // Add event listeners
  106. if (opts.when)
  107. for (i in opts.when)
  108. if (has(i, opts.when))
  109. this.bind(i, opts.when[i]);
  110. // Set the css
  111. this.css({position: 'relative', width: opts.width, height: opts.height});
  112. // Set the initial display
  113. this.turn('display', opts.display);
  114. // Set the direction
  115. if (opts.direction!=='')
  116. this.turn('direction', opts.direction);
  117. // Add pages from the DOM
  118. for (i = 0; i<ch.length; i++) {
  119. if ($(ch[i]).attr('ignore')!='1') {
  120. this.turn('addPage', ch[i], ++pageNum);
  121. }
  122. }
  123. // Event listeners
  124. $(this).bind(mouseEvents.down, data.docEvents.mouseStart).
  125. bind('start', turnMethods._start).
  126. bind('end', turnMethods._end).
  127. bind('pressed', turnMethods._pressed).
  128. bind('released', turnMethods._released).
  129. bind('flip', turnMethods._flip);
  130. $(document).bind(mouseEvents.move, data.docEvents.mouseMove).
  131. bind(mouseEvents.up, data.docEvents.mouseEnd);
  132. // Set the initial page
  133. this.turn('page', opts.page);
  134. data.done = true;
  135. return this;
  136. },
  137. // Adds a page from external data
  138. addPage: function(element, page) {
  139. var currentPage,
  140. className,
  141. incPages = false,
  142. data = this.data(),
  143. lastPage = data.totalPages+1;
  144. if (data.destroying)
  145. return false;
  146. // Read the page number from the className of `element` - format: p[0-9]+
  147. if ((currentPage = /\bp([0-9]+)\b/.exec($(element).attr('class'))))
  148. page = parseInt(currentPage[1], 10);
  149. if (page) {
  150. if (page==lastPage)
  151. incPages = true;
  152. else if (page>lastPage)
  153. throw turnError('Page "'+page+'" cannot be inserted');
  154. } else {
  155. page = lastPage;
  156. incPages = true;
  157. }
  158. if (page>=1 && page<=lastPage) {
  159. if (data.display=='double')
  160. className = (page%2) ? ' odd' : ' even';
  161. else
  162. className = '';
  163. // Stop animations
  164. if (data.done)
  165. this.turn('stop');
  166. // Move pages if it's necessary
  167. if (page in data.pageObjs)
  168. turnMethods._movePages.call(this, page, 1);
  169. // Increase the number of pages
  170. if (incPages)
  171. data.totalPages = lastPage;
  172. // Add element
  173. data.pageObjs[page] = $(element).
  174. css({'float': 'left'}).
  175. addClass('page p' + page + className);
  176. // Add page
  177. turnMethods._addPage.call(this, page);
  178. // Update view
  179. if (data.done)
  180. this.turn('update');
  181. // Remove pages out of range
  182. turnMethods._removeFromDOM.call(this);
  183. }
  184. return this;
  185. },
  186. // Adds a page
  187. _addPage: function(page) {
  188. var data = this.data(),
  189. element = data.pageObjs[page];
  190. if (element)
  191. if (turnMethods._necessPage.call(this, page)) {
  192. if (!data.pageWrap[page]) {
  193. var prop = turnMethods._pageSize.call(this, page, true);
  194. element.css({width: prop.width, height: prop.height});
  195. // Place
  196. data.pagePlace[page] = page;
  197. // Wrapper
  198. data.pageWrap[page] = $('<div/>',
  199. {'class': 'turn-page-wrapper',
  200. page: page,
  201. css: {position: 'absolute',
  202. overflow: 'hidden'}}).
  203. css(prop);
  204. // Append to this
  205. this.append(data.pageWrap[page]);
  206. // Move element to wrapper
  207. data.pageWrap[page].prepend(data.pageObjs[page]);
  208. }
  209. // If the page is in the current view, create the flip effect
  210. if (!page || turnMethods._setPageLoc.call(this, page)==1)
  211. turnMethods._makeFlip.call(this, page);
  212. } else {
  213. // Place
  214. data.pagePlace[page] = 0;
  215. // Remove element from the DOM
  216. if (data.pageObjs[page])
  217. data.pageObjs[page].remove();
  218. }
  219. },
  220. // Checks if a page is in memory
  221. hasPage: function(page) {
  222. return has(page, this.data().pageObjs);
  223. },
  224. // Centers the flipbook
  225. center: function(page) {
  226. var data = this.data(),
  227. size = $(this).turn('size'),
  228. left = size.width/(data.zoom*2) -size.width/2;
  229. if (data.display=='double') {
  230. var view = this.turn('view', page || data.tpage || data.page);
  231. if (data.direction=='ltr') {
  232. if (!view[0])
  233. left -= size.width/4;
  234. else if (!view[1])
  235. left += size.width/4;
  236. } else {
  237. if (!view[0])
  238. left += size.width/4;
  239. else if (!view[1])
  240. left -= size.width/4;
  241. }
  242. }
  243. $(this).css({marginLeft: left});
  244. return this;
  245. },
  246. // Destroys the flipbook
  247. destroy: function () {
  248. var page,
  249. data = this.data();
  250. data.destroying = true;
  251. $(this).unbind(mouseEvents.down)
  252. .unbind('end')
  253. .unbind('first')
  254. .unbind('flip')
  255. .unbind('last')
  256. .unbind('pressed')
  257. .unbind('released')
  258. .unbind('start')
  259. .unbind('turning')
  260. .unbind('turned')
  261. .unbind('zooming');
  262. $(document).unbind(mouseEvents.move, data.docEvents.mouseMove).
  263. unbind(mouseEvents.up, data.docEvents.mouseEnd);
  264. while (data.totalPages!==0) {
  265. this.turn('removePage', data.totalPages);
  266. }
  267. if (data.fparent)
  268. data.fparent.remove();
  269. if (data.shadow)
  270. data.shadow.remove();
  271. this.removeData();
  272. data = null;
  273. return this;
  274. },
  275. // Checks if this element is a flipbook
  276. is: function() {
  277. return typeof(this.data().pages)=='object';
  278. },
  279. // Sets and gets the zoom value
  280. zoom: function(newZoom) {
  281. var data = this.data();
  282. if (typeof(newZoom)=='number') {
  283. if (newZoom<0.001 || newZoom>100)
  284. throw turnError(newZoom+ ' is not a value for zoom');
  285. var event = $.Event('zooming');
  286. this.trigger(event, [newZoom, data.zoom]);
  287. if (event.isDefaultPrevented())
  288. return this;
  289. var size = $(this).turn('size'),
  290. iz = 1/data.zoom,
  291. newWidth = Math.round(size.width * iz * newZoom),
  292. newHeight = Math.round(size.height * iz * newZoom);
  293. data.zoom = newZoom;
  294. $(this).turn('stop').
  295. turn('size', newWidth, newHeight).
  296. css({marginTop: size.height * iz / 2 - newHeight / 2});
  297. if (data.opts.autoCenter)
  298. this.turn('center');
  299. else
  300. $(this).css({marginLeft: size.width * iz / 2 - newWidth / 2});
  301. turnMethods._updateShadow.call(this);
  302. return this;
  303. } else
  304. return data.zoom;
  305. },
  306. // Gets the size of a page
  307. _pageSize: function(page, position) {
  308. var data = this.data(),
  309. prop = {};
  310. if (data.display=='single') {
  311. prop.width = this.width();
  312. prop.height = this.height();
  313. if (position) {
  314. prop.top = 0;
  315. prop.left = 0;
  316. prop.right = 'auto';
  317. }
  318. } else {
  319. var pageWidth = this.width()/2,
  320. pageHeight = this.height();
  321. if (data.pageObjs[page].hasClass('own-size')) {
  322. prop.width = data.pageObjs[page].width();
  323. prop.height = data.pageObjs[page].height();
  324. } else {
  325. prop.width = pageWidth;
  326. prop.height = pageHeight;
  327. }
  328. if (position) {
  329. var odd = page%2;
  330. prop.top = (pageHeight-prop.height)/2;
  331. if (data.direction=='ltr') {
  332. prop[(odd) ? 'right' : 'left'] = pageWidth-prop.width;
  333. prop[(odd) ? 'left' : 'right'] = 'auto';
  334. } else {
  335. prop[(odd) ? 'left' : 'right'] = pageWidth-prop.width;
  336. prop[(odd) ? 'right' : 'left'] = 'auto';
  337. }
  338. }
  339. }
  340. return prop;
  341. },
  342. // Prepares the flip effect for a page
  343. _makeFlip: function(page) {
  344. var data = this.data();
  345. if (!data.pages[page] && data.pagePlace[page]==page) {
  346. var corner,
  347. single = data.display=='single',
  348. odd = page%2;
  349. data.pages[page] = data.pageObjs[page].
  350. css(turnMethods._pageSize.call(this, page)).
  351. flip({page: page,
  352. next: (odd || single) ? page+1 : page-1,
  353. turn: this,
  354. duration: data.opts.duration,
  355. acceleration : data.opts.acceleration,
  356. gradients: data.opts.gradients
  357. }).
  358. flip('disable', data.disabled);
  359. }
  360. return data.pages[page];
  361. },
  362. // Makes pages within a range
  363. _makeRange: function() {
  364. var page, range,
  365. data = this.data();
  366. if (data.totalPages<1)
  367. return;
  368. range = this.turn('range');
  369. for (page = range[0]; page<=range[1]; page++)
  370. turnMethods._addPage.call(this, page);
  371. },
  372. // Returns a range of pages that should be in the DOM
  373. // Example:
  374. // - page in the current view, return true
  375. // * page is in the range, return true
  376. // Otherwise, return false
  377. //
  378. // 1 2-3 4-5 6-7 8-9 10-11 12-13
  379. // ** ** -- ** **
  380. range: function(page) {
  381. var remainingPages, left, right, view,
  382. data = this.data();
  383. page = page || data.tpage || data.page || 1;
  384. view = turnMethods._view.call(this, page);
  385. if (page<1 || page>data.totalPages)
  386. throw turnError('"'+page+'" is not a page for range');
  387. view[1] = view[1] || view[0];
  388. if (view[0]>=1 && view[1]<=data.totalPages) {
  389. remainingPages = Math.floor((pagesInDOM-2)/2);
  390. if (data.totalPages-view[1] > view[0]) {
  391. left = Math.min(view[0]-1, remainingPages);
  392. right = 2*remainingPages-left;
  393. } else {
  394. right = Math.min(data.totalPages-view[1], remainingPages);
  395. left = 2*remainingPages-right;
  396. }
  397. } else {
  398. left = pagesInDOM-1;
  399. right = pagesInDOM-1;
  400. }
  401. return [Math.max(1, view[0]-left),
  402. Math.min(data.totalPages, view[1]+right)];
  403. },
  404. // Detects if a page is within the range of `pagesInDOM` from the current view
  405. _necessPage: function(page) {
  406. if (page===0)
  407. return true;
  408. var data = this.data(),
  409. range = this.turn('range');
  410. return data.pageObjs[page].hasClass('fixed') ||
  411. (page>=range[0] && page<=range[1]);
  412. },
  413. // Releases memory by removing pages from the DOM
  414. _removeFromDOM: function() {
  415. var page, data = this.data();
  416. for (page in data.pageWrap)
  417. if (has(page, data.pageWrap) &&
  418. !turnMethods._necessPage.call(this, page))
  419. turnMethods._removePageFromDOM.call(this, page);
  420. },
  421. // Removes a page from DOM and its internal references
  422. _removePageFromDOM: function(page) {
  423. var data = this.data();
  424. if (data.pages[page]) {
  425. var dd = data.pages[page].data();
  426. flipMethods._moveFoldingPage.call(data.pages[page], false);
  427. if (dd.f && dd.f.fwrapper)
  428. dd.f.fwrapper.remove();
  429. data.pages[page].removeData();
  430. data.pages[page].remove();
  431. delete data.pages[page];
  432. }
  433. if (data.pageObjs[page])
  434. data.pageObjs[page].remove();
  435. if (data.pageWrap[page]) {
  436. data.pageWrap[page].remove();
  437. delete data.pageWrap[page];
  438. }
  439. delete data.pagePlace[page];
  440. },
  441. // Removes a page
  442. removePage: function(page) {
  443. var data = this.data();
  444. if (page<1 || page>data.totalPages)
  445. throw turnError('The page '+ page + ' doesn\'t exist');
  446. if (data.pageObjs[page]) {
  447. // Stop animations
  448. this.turn('stop');
  449. // Remove `page`
  450. turnMethods._removePageFromDOM.call(this, page);
  451. delete data.pageObjs[page];
  452. }
  453. // Move the pages behind `page`
  454. turnMethods._movePages.call(this, page, -1);
  455. // Resize the size of this flipbook
  456. data.totalPages = data.totalPages-1;
  457. // Check the current view
  458. if (data.page>data.totalPages)
  459. this.turn('page', data.totalPages);
  460. else
  461. turnMethods._makeRange.call(this);
  462. return this;
  463. },
  464. // Moves pages
  465. _movePages: function(from, change) {
  466. var page,
  467. that = this,
  468. data = this.data(),
  469. single = data.display=='single',
  470. move = function(page) {
  471. var next = page + change,
  472. odd = next%2,
  473. className = (odd) ? ' odd ' : ' even ';
  474. if (data.pageObjs[page])
  475. data.pageObjs[next] = data.pageObjs[page]
  476. .removeClass('p' + page + ' odd even')
  477. .addClass('p' + next + className);
  478. if (data.pagePlace[page] && data.pageWrap[page]) {
  479. data.pagePlace[next] = next;
  480. if (data.pageObjs[next].hasClass('fixed'))
  481. data.pageWrap[next] = data.pageWrap[page]
  482. .attr('page', next);
  483. else
  484. data.pageWrap[next] = data.pageWrap[page].
  485. css(turnMethods._pageSize.call(that, next, true)).
  486. attr('page', next);
  487. if (data.pages[page])
  488. data.pages[next] = data.pages[page]
  489. .flip('options', {
  490. page: next,
  491. next: (single || odd) ? next+1 : next-1,
  492. corners: (single) ? 'all' :
  493. ((odd) ? 'forward' : 'backward')
  494. });
  495. if (change) {
  496. delete data.pages[page];
  497. delete data.pagePlace[page];
  498. delete data.pageObjs[page];
  499. delete data.pageWrap[page];
  500. delete data.pageObjs[page];
  501. }
  502. }
  503. };
  504. if (change>0)
  505. for (page=data.totalPages; page>=from; page--)
  506. move(page);
  507. else
  508. for (page=from; page<=data.totalPages; page++)
  509. move(page);
  510. },
  511. // Sets or Gets the display mode
  512. display: function(display) {
  513. var data = this.data(),
  514. currentDisplay = data.display;
  515. if (display) {
  516. if ($.inArray(display, displays)==-1)
  517. throw turnError('"'+display + '" is not a value for display');
  518. if (display=='single') {
  519. if (!data.pageObjs[0]) {
  520. this.turn('stop').
  521. css({'overflow': 'hidden'});
  522. data.pageObjs[0] = $('<div />',
  523. {'class': 'page p-temporal'}).
  524. css({width: this.width(), height: this.height()}).
  525. appendTo(this);
  526. }
  527. } else {
  528. if (data.pageObjs[0]) {
  529. this.turn('stop').css({'overflow': ''});
  530. data.pageObjs[0].remove();
  531. delete data.pageObjs[0];
  532. }
  533. }
  534. data.display = display;
  535. if (currentDisplay) {
  536. var size = this.turn('size');
  537. turnMethods._movePages.call(this, 1, 0);
  538. this.turn('size', size.width, size.height).
  539. turn('update');
  540. }
  541. return this;
  542. } else
  543. return currentDisplay;
  544. },
  545. // Gets and sets the direction of the flipbook
  546. direction: function(dir) {
  547. var data = this.data();
  548. if (typeof(dir)=='undefined') {
  549. return data.direction;
  550. } else {
  551. dir = dir.toLowerCase();
  552. if ($.inArray(dir, directions)==-1)
  553. throw turnError('"' + dir + '" is not a value for direction');
  554. if (dir=='rtl') {
  555. $(this).attr('dir', 'ltr').
  556. css({direction: 'ltr'});
  557. }
  558. data.direction = dir;
  559. if (data.done)
  560. this.turn('size', $(this).width(), $(this).height());
  561. return this;
  562. }
  563. },
  564. // Detects if the pages are being animated
  565. animating: function() {
  566. return this.data().pageMv.length>0;
  567. },
  568. // Disables and enables the effect
  569. disable: function(bool) {
  570. var page,
  571. data = this.data(),
  572. view = this.turn('view');
  573. data.disabled = bool===undefined || bool===true;
  574. for (page in data.pages)
  575. if (has(page, data.pages))
  576. data.pages[page].flip('disable', (bool) ? $.inArray(page, view) : false);
  577. return this;
  578. },
  579. // Disables and enables the effect
  580. disabled: function(disable) {
  581. if (disable===undefined) {
  582. return this.data().disabled===true;
  583. } else {
  584. return this.turn('disable', disable);
  585. }
  586. },
  587. // Gets and sets the size
  588. size: function(width, height) {
  589. if (width && height) {
  590. var page, prop,
  591. data = this.data(),
  592. pageWidth = (data.display=='double') ? width/2 : width;
  593. this.css({width: width, height: height});
  594. if (data.pageObjs[0])
  595. data.pageObjs[0].css({width: pageWidth, height: height});
  596. for (page in data.pageWrap) {
  597. if (!has(page, data.pageWrap)) continue;
  598. prop = turnMethods._pageSize.call(this, page, true);
  599. data.pageObjs[page].css({width: prop.width, height: prop.height});
  600. data.pageWrap[page].css(prop);
  601. if (data.pages[page])
  602. data.pages[page].css({width: prop.width, height: prop.height});
  603. }
  604. this.turn('resize');
  605. return this;
  606. } else {
  607. return {width: this.width(), height: this.height()};
  608. }
  609. },
  610. // Resizes each page
  611. resize: function() {
  612. var page, data = this.data();
  613. if (data.pages[0]) {
  614. data.pageWrap[0].css({left: -this.width()});
  615. data.pages[0].flip('resize', true);
  616. }
  617. for (page = 1; page <= data.totalPages; page++)
  618. if (data.pages[page])
  619. data.pages[page].flip('resize', true);
  620. },
  621. // Removes an animation from the cache
  622. _removeMv: function(page) {
  623. var i, data = this.data();
  624. for (i=0; i<data.pageMv.length; i++)
  625. if (data.pageMv[i]==page) {
  626. data.pageMv.splice(i, 1);
  627. return true;
  628. }
  629. return false;
  630. },
  631. // Adds an animation to the cache
  632. _addMv: function(page) {
  633. var data = this.data();
  634. turnMethods._removeMv.call(this, page);
  635. data.pageMv.push(page);
  636. },
  637. // Gets indexes for a view
  638. _view: function(page) {
  639. var data = this.data();
  640. page = page || data.page;
  641. if (data.display=='double')
  642. return (page%2) ? [page-1, page] : [page, page+1];
  643. else
  644. return [page];
  645. },
  646. // Gets a view
  647. view: function(page) {
  648. var data = this.data(),
  649. view = turnMethods._view.call(this, page);
  650. if (data.display=='double')
  651. return [(view[0]>0) ? view[0] : 0,
  652. (view[1]<=data.totalPages) ? view[1] : 0];
  653. else
  654. return [(view[0]>0 && view[0]<=data.totalPages) ? view[0] : 0];
  655. },
  656. // Stops animations
  657. stop: function(ignore, animate) {
  658. if (this.turn('animating')) {
  659. var i, opts, page,
  660. data = this.data(),
  661. pages = data.pageMv;
  662. data.pageMv = [];
  663. if (data.tpage) {
  664. data.page = data.tpage;
  665. delete data['tpage'];
  666. }
  667. for (i = 0; i<pages.length; i++) {
  668. page = data.pages[pages[i]];
  669. opts = page.data().f.opts;
  670. page.flip('hideFoldedPage', false);
  671. flipMethods._moveFoldingPage.call(page, false);
  672. data.pagePlace[opts.next] = opts.next;
  673. if (opts.force) {
  674. opts.next = (opts.page%2===0) ? opts.page-1 : opts.page+1;
  675. delete opts['force'];
  676. }
  677. }
  678. }
  679. this.turn('update');
  680. return this;
  681. },
  682. // Gets and sets the number of pages
  683. pages: function(pages) {
  684. var data = this.data();
  685. if (pages) {
  686. if (pages<data.totalPages) {
  687. for (var page = pages+1; page<=data.totalPages; page++)
  688. this.turn('removePage', page);
  689. if (this.turn('page')>pages)
  690. this.turn('page', pages);
  691. }
  692. data.totalPages = pages;
  693. return this;
  694. } else
  695. return data.totalPages;
  696. },
  697. // Checks missing pages
  698. _missing : function(page) {
  699. var p,
  700. data = this.data(),
  701. range = this.turn('range', page),
  702. missing = [];
  703. for (p = range[0]; p<=range[1]; p++) {
  704. if (!data.pageObjs[p])
  705. missing.push(p);
  706. }
  707. if (missing.length>0)
  708. this.trigger('missing', [missing]);
  709. },
  710. // Sets a page without effect
  711. _fitPage: function(page) {
  712. var data = this.data(),
  713. newView = this.turn('view', page);
  714. turnMethods._missing.call(this, page);
  715. if (!data.pageObjs[page])
  716. return;
  717. data.page = page;
  718. this.turn('stop');
  719. turnMethods._removeFromDOM.call(this);
  720. turnMethods._makeRange.call(this);
  721. turnMethods._updateShadow.call(this);
  722. this.trigger('turned', [page, newView]);
  723. if (data.opts.autoCenter)
  724. this.turn('center');
  725. },
  726. // Turns to a page
  727. _turnPage: function(page, fromMouseAction) {
  728. var current, next,
  729. data = this.data(),
  730. place = data.pagePlace[page],
  731. view = this.turn('view'),
  732. newView = this.turn('view', page);
  733. if (data.page!=page) {
  734. var event = $.Event('turning');
  735. this.trigger(event, [page, newView]);
  736. if (event.isDefaultPrevented())
  737. return;
  738. if ($.inArray(1, newView)!=-1)
  739. this.trigger('first');
  740. if ($.inArray(data.totalPages, newView)!=-1)
  741. this.trigger('last');
  742. }
  743. if (fromMouseAction) {
  744. this.turn('stop', place);
  745. } else {
  746. turnMethods._missing.call(this, page);
  747. if (!data.pageObjs[page])
  748. return;
  749. this.turn('stop');
  750. data.page = page;
  751. }
  752. turnMethods._makeRange.call(this);
  753. if (data.display=='single') {
  754. current = view[0];
  755. next = newView[0];
  756. } else if (view[1] && page>view[1]) {
  757. current = view[1];
  758. next = newView[0];
  759. } else if (view[0] && page<view[0]) {
  760. current = view[0];
  761. next = newView[1];
  762. }
  763. if (data.pages[current]) {
  764. var opts = data.pages[current].data().f.opts;
  765. data.tpage = next;
  766. if (opts.next!=next) {
  767. opts.next = next;
  768. data.pagePlace[next] = opts.page;
  769. opts.force = true;
  770. }
  771. if (data.display=='single') {
  772. if (data.direction=='ltr') {
  773. data.pages[current].flip('turnPage',
  774. (newView[0] > view[0]) ? 'r' : 'l');
  775. } else {
  776. data.pages[current].flip('turnPage',
  777. (newView[0] > view[0]) ? 'l' : 'r');
  778. }
  779. } else {
  780. data.pages[current].flip('turnPage');
  781. }
  782. }
  783. },
  784. // Gets and sets a page
  785. page: function(page) {
  786. page = parseInt(page, 10);
  787. var data = this.data();
  788. if (page>0 && page<=data.totalPages) {
  789. if (!data.done || $.inArray(page, this.turn('view'))!=-1)
  790. turnMethods._fitPage.call(this, page);
  791. else
  792. turnMethods._turnPage.call(this, page);
  793. return this;
  794. } else
  795. return data.page;
  796. },
  797. // Turns to the next view
  798. next: function() {
  799. return this.turn('page',
  800. turnMethods._view.call(this, this.data().page).pop() + 1);
  801. },
  802. // Turns to the previous view
  803. previous: function() {
  804. return this.turn('page',
  805. turnMethods._view.call(this, this.data().page).shift() - 1);
  806. },
  807. peel: function(corner, animate) {
  808. return this;
  809. },
  810. // Adds a motion to the internal list
  811. // This event is called in context of flip
  812. _addMotionPage: function() {
  813. var opts = $(this).data().f.opts,
  814. turn = opts.turn,
  815. dd = turn.data();
  816. turnMethods._addMv.call(turn, opts.page);
  817. dd.pagePlace[opts.next] = opts.page;
  818. turn.turn('update');
  819. },
  820. // This event is called in context of flip
  821. _start: function(e, opts, corner) {
  822. var data = opts.turn.data();
  823. if (e.isDefaultPrevented()) {
  824. turnMethods._updateShadow.call(opts.turn);
  825. return;
  826. }
  827. if (data.display=='single' && corner) {
  828. if ((corner=='l' && data.direction=='ltr')
  829. ||
  830. (corner=='r' && data.direction=='rtl'))
  831. {
  832. opts.next = (opts.next<opts.page) ? opts.next : opts.page-1;
  833. opts.force = true;
  834. } else {
  835. opts.next = (opts.next>opts.page) ? opts.next : opts.page+1;
  836. }
  837. }
  838. turnMethods._addMotionPage.call(e.target);
  839. turnMethods._updateShadow.call(opts.turn);
  840. },
  841. // This event is called in context of flip
  842. _end: function(e, opts, turned) {
  843. var that = $(e.target),
  844. data = that.data().f,
  845. turn = opts.turn,
  846. dd = turn.data();
  847. if (turned || dd.tpage) {
  848. if (dd.tpage==opts.next || dd.tpage==opts.page) {
  849. delete dd['tpage'];
  850. turnMethods._fitPage.call(turn, dd.tpage || opts.next, true);
  851. }
  852. } else {
  853. turnMethods._removeMv.call(turn, opts.page);
  854. turnMethods._updateShadow.call(turn);
  855. turn.turn('update');
  856. }
  857. },
  858. // This event is called in context of flip
  859. _pressed: function(e) {
  860. e.stopPropagation();
  861. var page,
  862. data = $(e.target).data().f,
  863. pages = data.opts.turn.data().pages;
  864. for (page in pages)
  865. if (page!=data.opts.page)
  866. pages[page].flip('disable', true);
  867. return data.time = new Date().getTime();
  868. },
  869. // This event is called in context of flip
  870. _released: function(e, point) {
  871. e.stopPropagation();
  872. var outArea,
  873. page = $(e.target),
  874. data = page.data().f,
  875. turn = data.opts.turn,
  876. turnData = turn.data();
  877. if (turnData.display=='single') {
  878. outArea = (point.corner=='r') ?
  879. point.x<page.width()/2:
  880. point.x>page.width()/2;
  881. } else {
  882. outArea = point.x<0 || point.x>page.width();
  883. }
  884. if ((new Date()).getTime()-data.time<200 || outArea) {
  885. e.preventDefault();
  886. turnMethods._turnPage.call(
  887. turn,
  888. data.opts.next,
  889. flipMethods._cornerActivated.call(page, point, 1) === false
  890. );
  891. }
  892. turnData.mouseAction = false;
  893. },
  894. // This event is called in context of flip
  895. _flip: function(e) {
  896. e.stopPropagation();
  897. var opts = $(e.target).data().f.opts;
  898. opts.turn.trigger('turn', [opts.next]);
  899. if (opts.turn.data().opts.autoCenter)
  900. opts.turn.turn('center', opts.next);
  901. },
  902. // Calculate the z-index value for pages during the animation
  903. calculateZ: function(mv) {
  904. var i, page, nextPage, placePage, dpage,
  905. that = this,
  906. data = this.data(),
  907. view = this.turn('view'),
  908. currentPage = view[0] || view[1],
  909. r = {pageZ: {}, partZ: {}, pageV: {}},
  910. addView = function(page) {
  911. var view = that.turn('view', page);
  912. if (view[0]) r.pageV[view[0]] = true;
  913. if (view[1]) r.pageV[view[1]] = true;
  914. };
  915. for (i = 0; i<mv.length; i++) {
  916. page = mv[i];
  917. nextPage = data.pages[page].data().f.opts.next;
  918. placePage = data.pagePlace[page];
  919. addView(page);
  920. addView(nextPage);
  921. dpage = (data.pagePlace[nextPage]==nextPage) ? nextPage : page;
  922. r.pageZ[dpage] = data.totalPages - Math.abs(currentPage-dpage);
  923. r.partZ[placePage] = data.totalPages*2 + Math.abs(currentPage-dpage);
  924. }
  925. return r;
  926. },
  927. // Updates the z-index and display property of every page
  928. update: function() {
  929. var page,
  930. data = this.data();
  931. if (data.pageMv.length && data.pageMv[0]!==0) {
  932. // Update motion
  933. var p,
  934. fixed,
  935. pos = this.turn('calculateZ', data.pageMv),
  936. view = this.turn('view', data.tpage);
  937. for (page in data.pageWrap) {
  938. if (!has(page, data.pageWrap))
  939. continue;
  940. fixed = data.pageObjs[page].hasClass('fixed');
  941. data.pageWrap[page].css({
  942. display: (pos.pageV[page] || fixed) ? '' : 'none',
  943. 'z-index': pos.pageZ[page] || ((fixed) ? -1 : 0)
  944. });
  945. if ((p = data.pages[page])) {
  946. p.flip('z', pos.partZ[page] || null);
  947. if (pos.pageV[page])
  948. p.flip('resize');
  949. if (data.tpage)
  950. p.flip('disable', true); // data.disabled || page!=apage
  951. }
  952. }
  953. } else {
  954. // Update static pages
  955. for (page in data.pageWrap) {
  956. if (!has(page, data.pageWrap))
  957. continue;
  958. var pageLocation = turnMethods._setPageLoc.call(this, page);
  959. if (data.pages[page])
  960. data.pages[page].flip('disable',
  961. data.disabled || pageLocation!=1).flip('z', null);
  962. }
  963. }
  964. },
  965. // Updates the position and size of the flipbook's shadow
  966. _updateShadow: function() {
  967. var view, view2, shadow,
  968. data = this.data(),
  969. width = this.width(),
  970. height = this.height(),
  971. pageWidth = (data.display=='single') ? width : width/2;
  972. view = this.turn('view');
  973. if (!data.shadow) {
  974. data.shadow = $('<div />',
  975. {
  976. 'class': 'shadow',
  977. 'css': divAtt(0, 0, 0).css
  978. }).
  979. appendTo(this);
  980. }
  981. for (var i = 0; i<data.pageMv.length; i++) {
  982. if (!view[0] || !view[1])
  983. break;
  984. view = this.turn('view', data.pages[data.pageMv[i]].data().f.opts.next);
  985. view2 = this.turn('view', data.pageMv[i]);
  986. view[0] = view[0] && view2[0];
  987. view[1] = view[1] && view2[1];
  988. }
  989. if (!view[0]) shadow = (data.direction=='ltr') ? 1 : 2;
  990. else if (!view[1]) shadow = (data.direction=='ltr') ? 2 : 1;
  991. else shadow = 3;
  992. switch (shadow) {
  993. case 1:
  994. data.shadow.css({
  995. width: pageWidth,
  996. height: height,
  997. top: 0,
  998. left: pageWidth
  999. });
  1000. break;
  1001. case 2:
  1002. data.shadow.css({
  1003. width: pageWidth,
  1004. height: height,
  1005. top: 0,
  1006. left: 0
  1007. });
  1008. break;
  1009. case 3:
  1010. data.shadow.css({
  1011. width: width,
  1012. height: height,
  1013. top: 0,
  1014. left: 0
  1015. });
  1016. break;
  1017. }
  1018. },
  1019. // Sets the z-index and display property of a page
  1020. // It depends on the current view
  1021. _setPageLoc: function(page) {
  1022. var data = this.data(),
  1023. view = this.turn('view');
  1024. if (page==view[0] || page==view[1]) {
  1025. data.pageWrap[page].css({zIndex: data.totalPages, display: ''});
  1026. return 1;
  1027. } else if ((data.display=='single' && page==view[0]+1) ||
  1028. (data.display=='double' && page==view[0]-2 ||
  1029. page==view[1]+2)) {
  1030. data.pageWrap[page].css({zIndex: data.totalPages-1, display: ''});
  1031. return 2;
  1032. } else {
  1033. data.pageWrap[page].css({zIndex: 0,
  1034. display: (data.pageObjs[page].hasClass('fixed')) ? '' : 'none'}
  1035. );
  1036. return 0;
  1037. }
  1038. },
  1039. // Gets and sets the options
  1040. options: function(options) {
  1041. if (options===undefined) {
  1042. return this.data().opts;
  1043. } else {
  1044. var data = this.data();
  1045. // Set new values
  1046. $.extend(data.opts, options);
  1047. // Set pages
  1048. if (options.pages)
  1049. this.turn('pages', options.pages);
  1050. // Set page
  1051. if (options.page)
  1052. this.turn('page', options.page);
  1053. // Set display
  1054. if (options.display)
  1055. this.turn('display', options.display);
  1056. // Set direction
  1057. if (options.direction)
  1058. this.turn('direction', options.direction);
  1059. // Set size
  1060. if (options.width && options.height)
  1061. this.turn('size', options.width, options.height);
  1062. // Add event listeners
  1063. if (options.when)
  1064. for (var eventName in options.when)
  1065. if (has(eventName, options.when)) {
  1066. this.unbind(eventName).
  1067. bind(eventName, options.when[eventName]);
  1068. }
  1069. return this;
  1070. }
  1071. },
  1072. // Gets the current version
  1073. version: function() {
  1074. return version;
  1075. }
  1076. },
  1077. // Methods and properties for the flip page effect
  1078. flipMethods = {
  1079. // Constructor
  1080. init: function(opts) {
  1081. this.data({f:
  1082. {effect: (opts.corners=='r' || opts.corners=='l') ? 'hard' : 'sheet'}}
  1083. );
  1084. this.flip('options', opts);
  1085. flipMethods._addPageWrapper.call(this);
  1086. return this;
  1087. },
  1088. setData: function(d) {
  1089. var data = this.data();
  1090. data.f = $.extend(data.f, d);
  1091. return this;
  1092. },
  1093. options: function(opts) {
  1094. var data = this.data().f;
  1095. if (opts) {
  1096. flipMethods.setData.call(this,
  1097. {opts: $.extend({}, data.opts || flipOptions, opts)});
  1098. return this;
  1099. } else
  1100. return data.opts;
  1101. },
  1102. z: function(z) {
  1103. var data = this.data().f;
  1104. if (data.fwrapper) {
  1105. data.opts['z-index'] = z;
  1106. data.fwrapper.css(
  1107. {'z-index': z || parseInt(data.parent.css('z-index'), 10) || 0}
  1108. );
  1109. }
  1110. return this;
  1111. },
  1112. _cAllowed: function() {
  1113. var data = this.data().f,
  1114. turnData = data.opts.turn.data(),
  1115. page = data.opts.page,
  1116. odd = page%2;
  1117. if (turnData.display=='single') {
  1118. if (page==1)
  1119. return (turnData.direction=='ltr') ?
  1120. corners['forward'] : corners['backward'];
  1121. else if (page==turnData.totalPages)
  1122. return (turnData.direction=='ltr') ?
  1123. corners['backward'] : corners['forward'];
  1124. else
  1125. return corners['all'];
  1126. } else {
  1127. return (turnData.direction=='ltr') ?
  1128. corners[(odd) ? 'forward' : 'backward']
  1129. :
  1130. corners[(odd) ? 'backward' : 'forward'];
  1131. }
  1132. },
  1133. _cornerActivated: function(e) {
  1134. var data = this.data().f,
  1135. pos = data.parent.offset(),
  1136. width = this.width(),
  1137. height = this.height(),
  1138. c = {x: Math.max(0, e.pageX-pos.left), y: Math.max(0, e.pageY-pos.top)},
  1139. csz = data.opts.cornerSize;
  1140. if (c.x<=0 || c.y<=0 || c.x>=width || c.y>=height)
  1141. return false;
  1142. var allowedCorners = flipMethods._cAllowed.call(this);
  1143. if (c.x>width-csz)
  1144. c.corner = 'r';
  1145. else if (c.x<csz)
  1146. c.corner = 'l';
  1147. else
  1148. return false;
  1149. return ($.inArray(c.corner, allowedCorners)==-1) ? false : c;
  1150. },
  1151. _c: function(corner, opts) {
  1152. opts = opts || 0;
  1153. switch (corner) {
  1154. case 'l':
  1155. return point2D(opts, 0);
  1156. case 'r':
  1157. return point2D(this.width()-opts, 0);
  1158. }
  1159. },
  1160. _c2: function(corner) {
  1161. switch (corner) {
  1162. case 'l':
  1163. return point2D(this.width()*2, 0);
  1164. case 'r':
  1165. return point2D(-this.width(), 0);
  1166. }
  1167. },
  1168. _foldingPage: function(corner) {
  1169. var data = this.data().f,
  1170. opts = data.opts;
  1171. if (data.folding) return data.folding;
  1172. else if(opts.turn) {
  1173. data = opts.turn.data();
  1174. if (data.display == 'single')
  1175. return (data.pageObjs[opts.next]) ? data.pageObjs[0] : null;
  1176. else
  1177. return data.pageObjs[opts.next];
  1178. }
  1179. },
  1180. _backGradient: function() {
  1181. var data = this.data().f,
  1182. turn = data.opts.turn,
  1183. gradient = data.opts.gradients &&
  1184. (!turn ||
  1185. turn.data().display=='single' ||
  1186. (data.opts.page!=2 && data.opts.page!=turn.data().totalPages-1));
  1187. return gradient;
  1188. },
  1189. resize: function(full) {
  1190. var data = this.data().f,
  1191. width = this.width(),
  1192. height = this.height();
  1193. if (full) {
  1194. data.wrapper.css({width: width, height: height});
  1195. data.fpage.css({width: width, height: height});
  1196. }
  1197. },
  1198. // Prepares the page by adding a general wrapper and another objects
  1199. _addPageWrapper: function() {
  1200. var att,
  1201. data = this.data().f,
  1202. parent = this.parent();
  1203. data.parent = parent;
  1204. if (!data.wrapper) {
  1205. var cssProperties = {};
  1206. data.wrapper = $('<div/>', divAtt(0, 0, 2)).
  1207. css(cssProperties).
  1208. appendTo(parent).
  1209. prepend(this);
  1210. data.fpage = $('<div/>', divAtt(0, 0, 1)).
  1211. css(cssProperties).
  1212. appendTo(parent);
  1213. }
  1214. // Set size
  1215. flipMethods.resize.call(this, true);
  1216. },
  1217. // Takes a 2P point from the screen and applies the transformation
  1218. _fold: function(point) {
  1219. var data = this.data().f,
  1220. o = flipMethods._c.call(this, point.corner),
  1221. relX = (o.x) ? o.x - point.x : point.x,
  1222. width = this.width(),
  1223. height = this.height();
  1224. relX = Math.min(width*2, Math.max(0, relX));
  1225. switch(point.corner) {
  1226. case 'r' :
  1227. data.wrapper.css({
  1228. width: Math.max(0, width-relX)
  1229. });
  1230. this.css({
  1231. position: 'relative',
  1232. left: -relX
  1233. });
  1234. data.fpage.css({
  1235. left: -relX + width,
  1236. width: Math.max(0, relX-width)
  1237. });
  1238. break;
  1239. case 'l' :
  1240. data.wrapper.css({
  1241. width: width
  1242. });
  1243. this.css({
  1244. position: 'relative',
  1245. left: relX
  1246. });
  1247. data.fpage.css({
  1248. left: width,
  1249. width: Math.max(0, relX-width)
  1250. });
  1251. if (data.folding)
  1252. data.folding.css({
  1253. position: 'relative',
  1254. left: -width*2 + relX
  1255. });
  1256. break;
  1257. }
  1258. data.parent.css({'overflow': 'visible'});
  1259. data.point = point;
  1260. },
  1261. _moveFoldingPage: function(bool) {
  1262. var data = this.data().f;
  1263. if (bool) {
  1264. var folding = flipMethods._foldingPage.call(this),
  1265. turn = data.opts.turn;
  1266. if (folding) {
  1267. if (data.folding) {
  1268. if (data.folding===folding)
  1269. return;
  1270. flipMethods._moveFoldingPage.call(this, false);
  1271. }
  1272. flipMethods.setData.call(this,
  1273. {backParent: folding.parent(),
  1274. folding: folding});
  1275. data.fpage.prepend(folding);
  1276. }
  1277. turn.turn('update');
  1278. } else {
  1279. if (data.backParent) {
  1280. data.backParent.prepend(data.folding);
  1281. delete data.backParent;
  1282. delete data.folding;
  1283. }
  1284. }
  1285. },
  1286. _showFoldedPage: function(c, animate) {
  1287. var folding = flipMethods._foldingPage.call(this),
  1288. dd = this.data(),
  1289. data = dd.f,
  1290. visible = data.visible;
  1291. if (!visible || !data.point || data.point.corner!=c.corner) {
  1292. var mAction = data.opts.turn.data().mouseAction;
  1293. var event = $.Event('start');
  1294. this.trigger(event, [data.opts, c.corner]);
  1295. visible = false;
  1296. if (event.isDefaultPrevented())
  1297. return false;
  1298. }
  1299. if (folding) {
  1300. if (animate) {
  1301. var that = this,
  1302. point = (data.point && data.point.corner==c.corner) ?
  1303. data.point : flipMethods._c.call(this, c.corner, 1);
  1304. this.animatef({from: [point.x, point.y],
  1305. to: [c.x, c.y],
  1306. duration: 500,
  1307. frame: function(v) {
  1308. c.x = Math.round(v[0]);
  1309. c.y = Math.round(v[1]);
  1310. flipMethods._fold.call(that, c);
  1311. }});
  1312. } else {
  1313. flipMethods._fold.call(this, c);
  1314. if (dd.effect && !dd.effect.turning)
  1315. this.animatef(false);
  1316. }
  1317. if (!visible) {
  1318. data.visible = true;
  1319. flipMethods._moveFoldingPage.call(this, true);
  1320. data.fpage.show();
  1321. }
  1322. return true;
  1323. }
  1324. return false;
  1325. },
  1326. hide: function() {
  1327. var data = this.data().f,
  1328. folding = flipMethods._foldingPage.call(this);
  1329. this.css({
  1330. position: '',
  1331. left: 'auto'
  1332. });
  1333. data.wrapper.css({
  1334. width: this.width()
  1335. });
  1336. data.fpage.css({
  1337. width: this.width()
  1338. });
  1339. if (data.folding)
  1340. data.folding.css({
  1341. position: '',
  1342. left: 'auto'
  1343. });
  1344. data.fpage.hide();
  1345. data.visible = false;
  1346. return this;
  1347. },
  1348. hideFoldedPage: function(animate) {
  1349. var data = this.data().f;
  1350. if (!data.point) return;
  1351. var that = this,
  1352. p1 = data.point,
  1353. hide = function() {
  1354. data.point = null;
  1355. that.flip('hide');
  1356. that.trigger('end', [data.opts, false]);
  1357. };
  1358. if (animate) {
  1359. var p4 = flipMethods._c.call(this, p1.corner),
  1360. top = (p1.corner.substr(0,1)=='t'),
  1361. delta = (top) ? Math.min(0, p1.y-p4.y)/2 : Math.max(0, p1.y-p4.y)/2,
  1362. p2 = point2D(p1.x, p1.y+delta),
  1363. p3 = point2D(p4.x, p4.y-delta);
  1364. this.animatef({
  1365. from: 0,
  1366. to: 1,
  1367. frame: function(v) {
  1368. var np = bezier(p1, p2, p3, p4, v);
  1369. p1.x = np.x;
  1370. p1.y = np.y;
  1371. flipMethods._fold.call(that, p1);
  1372. },
  1373. complete: hide,
  1374. duration: 800,
  1375. hiding: true
  1376. });
  1377. } else {
  1378. this.animatef(false);
  1379. hide();
  1380. }
  1381. },
  1382. turnPage: function(corner) {
  1383. var that = this,
  1384. data = this.data().f;
  1385. corner = {corner: (data.corner) ?
  1386. data.corner.corner :
  1387. corner || flipMethods._cAllowed.call(this)[0]};
  1388. var p1 = data.point ||
  1389. flipMethods._c.call(this,
  1390. corner.corner,
  1391. (data.opts.turn) ? data.opts.turn.data().opts.elevation : 0),
  1392. p4 = flipMethods._c2.call(this, corner.corner);
  1393. this.trigger('flip').
  1394. animatef({
  1395. from: 0,
  1396. to: 1,
  1397. frame: function(v) {
  1398. var np = bezier(p1, p1, p4, p4, v);
  1399. corner.x = np.x;
  1400. corner.y = np.y;
  1401. flipMethods._showFoldedPage.call(that, corner);
  1402. },
  1403. complete: function() {
  1404. that.trigger('end', [data.opts, true]);
  1405. },
  1406. duration: data.opts.duration,
  1407. turning: true
  1408. });
  1409. data.corner = null;
  1410. },
  1411. moving: function() {
  1412. return 'effect' in this.data();
  1413. },
  1414. isTurning: function() {
  1415. return this.flip('moving') && this.data().effect.turning;
  1416. },
  1417. _eventStart: function(e) {
  1418. var data = this.data().f;
  1419. if (!data.disabled && !this.flip('isTurning')) {
  1420. data.corner = flipMethods._cornerActivated.call(this, e);
  1421. if (data.corner && flipMethods._foldingPage.call(this, data.corner)) {
  1422. if (flipMethods._showFoldedPage.call(this, data.corner))
  1423. this.trigger('pressed', [data.point]);
  1424. return false;
  1425. } else
  1426. data.corner = null;
  1427. }
  1428. },
  1429. _eventMove: function(e) {
  1430. var data = this.data().f;
  1431. if (!data.disabled) {
  1432. e = (isTouch) ? e.originalEvent.touches : [e];
  1433. if (data.corner) {
  1434. var pos = data.parent.offset();
  1435. data.corner.x = e[0].pageX-pos.left;
  1436. data.corner.y = e[0].pageY-pos.top;
  1437. flipMethods._showFoldedPage.call(this, data.corner);
  1438. } else if (!this.data().effect && this.is(':visible')) {
  1439. var corner = flipMethods._cornerActivated.call(this, e[0]);
  1440. if (corner) {
  1441. var origin = flipMethods._c.call(this, corner.corner, data.opts.cornerSize/2);
  1442. corner.x = origin.x;
  1443. corner.y = origin.y;
  1444. flipMethods._showFoldedPage.call(this, corner, true);
  1445. } else
  1446. flipMethods.hideFoldedPage.call(this, true);
  1447. }
  1448. }
  1449. },
  1450. _eventEnd: function() {
  1451. var data = this.data().f;
  1452. if (!data.disabled && data.point) {
  1453. var event = $.Event('released');
  1454. this.trigger(event, [data.point]);
  1455. if (!event.isDefaultPrevented())
  1456. flipMethods.hideFoldedPage.call(this, true);
  1457. }
  1458. data.corner = null;
  1459. },
  1460. disable: function(disable) {
  1461. flipMethods.setData.call(this, {'disabled': disable});
  1462. return this;
  1463. }
  1464. };
  1465. // Processes classes
  1466. function decorator(that, methods, args) {
  1467. if (!args[0] || typeof(args[0])=='object')
  1468. return methods.init.apply(that, args);
  1469. else if (methods[args[0]])
  1470. return methods[args[0]].apply(that, Array.prototype.slice.call(args, 1));
  1471. else
  1472. throw turnError(args[0] + ' is an invalid value');
  1473. }
  1474. // Attributes for a layer
  1475. function divAtt(top, left, zIndex, overf) {
  1476. return {'css': {
  1477. position: 'absolute',
  1478. top: top,
  1479. left: left,
  1480. 'overflow': overf || 'hidden',
  1481. 'z-index': zIndex || 'auto'
  1482. }
  1483. };
  1484. }
  1485. // Gets a 2D point from a bezier curve of four points
  1486. function bezier(p1, p2, p3, p4, t) {
  1487. var a = 1 - t,
  1488. b = a * a * a,
  1489. c = t * t * t;
  1490. return point2D(Math.round(b*p1.x + 3*t*a*a*p2.x + 3*t*t*a*p3.x + c*p4.x),
  1491. Math.round(b*p1.y + 3*t*a*a*p2.y + 3*t*t*a*p3.y + c*p4.y));
  1492. }
  1493. // Gets a 2D point
  1494. function point2D(x, y) {
  1495. return {x: x, y: y};
  1496. }
  1497. // Checks if a property belongs to an object
  1498. function has(property, object) {
  1499. return Object.prototype.hasOwnProperty.call(object, property);
  1500. }
  1501. // Gets the CSS3 vendor prefix
  1502. function getPrefix() {
  1503. var vendorPrefixes = ['Moz','Webkit','Khtml','O','ms'],
  1504. len = vendorPrefixes.length,
  1505. vendor = '';
  1506. while (len--)
  1507. if ((vendorPrefixes[len] + 'Transform') in document.body.style)
  1508. vendor='-'+vendorPrefixes[len].toLowerCase()+'-';
  1509. return vendor;
  1510. }
  1511. // JS Errors
  1512. function turnError(message) {
  1513. function TurnJsError(message) {
  1514. this.name = "TurnJsError";
  1515. this.message = message;
  1516. }
  1517. TurnJsError.prototype = new Error();
  1518. TurnJsError.prototype.constructor = TurnJsError;
  1519. return new TurnJsError(message);
  1520. }
  1521. // Find the offset of an element discarding its transformation
  1522. function findPos(obj) {
  1523. var offset = {top: 0, left: 0};
  1524. do{
  1525. offset.left += obj.offsetLeft;
  1526. offset.top += obj.offsetTop;
  1527. } while ((obj = obj.offsetParent));
  1528. return offset;
  1529. }
  1530. // Request an animation
  1531. window.requestAnim = function(callback) {
  1532. window.setTimeout(callback, 1000 / 60);
  1533. }
  1534. function emptyFunction() {
  1535. return '';
  1536. }
  1537. // Extend $.fn
  1538. $.extend($.fn, {
  1539. flip: function(req, opts) {
  1540. return decorator(this, flipMethods, arguments);
  1541. },
  1542. turn: function(req) {
  1543. return decorator(this, turnMethods, arguments);
  1544. },
  1545. transform: function(transform, origin) {
  1546. var properties = {};
  1547. if (origin)
  1548. properties[vendor+'transform-origin'] = origin;
  1549. properties[vendor+'transform'] = transform;
  1550. return this.css(properties);
  1551. },
  1552. animatef: function(point) {
  1553. var data = this.data();
  1554. if (data.effect)
  1555. data.effect.stop();
  1556. if (point) {
  1557. if (!point.to.length) point.to = [point.to];
  1558. if (!point.from.length) point.from = [point.from];
  1559. var diff = [],
  1560. len = point.to.length,
  1561. animating = true,
  1562. that = this,
  1563. time = (new Date()).getTime(),
  1564. frame = function() {
  1565. if (!data.effect || !animating)
  1566. return;
  1567. var v = [],
  1568. timeDiff = Math.min(point.duration, (new Date()).getTime() - time);
  1569. for (var i = 0; i < len; i++)
  1570. v.push(data.effect.easing(1, timeDiff, point.from[i], diff[i], point.duration));
  1571. point.frame((len==1) ? v[0] : v);
  1572. if (timeDiff==point.duration) {
  1573. delete data['effect'];
  1574. that.data(data);
  1575. if (point.complete)
  1576. point.complete();
  1577. } else {
  1578. window.requestAnim(frame);
  1579. }
  1580. };
  1581. for (var i = 0; i < len; i++)
  1582. diff.push(point.to[i] - point.from[i]);
  1583. data.effect = $.extend({
  1584. stop: function() {
  1585. animating = false;
  1586. },
  1587. easing: function (x, t, b, c, data) {
  1588. return c * Math.sqrt(1 - (t=t/data-1)*t) + b;
  1589. }
  1590. }, point);
  1591. this.data(data);
  1592. frame();
  1593. } else {
  1594. delete data['effect'];
  1595. }
  1596. }
  1597. });
  1598. // Export some globals
  1599. $.isTouch = isTouch;
  1600. $.mouseEvents = mouseEvents;
  1601. $.cssPrefix = emptyFunction;
  1602. $.cssTransitionEnd = emptyFunction;
  1603. $.findPos = findPos;
  1604. })(jQuery);